Compare commits
422 Commits
wiki
...
2.5.7.2663
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d444ebe99 | |||
| 237eafed35 | |||
| fbc5069fb8 | |||
| d23c44589e | |||
| 42cc500b05 | |||
| 81760192dc | |||
| 2cb077423d | |||
| de8aaaa5e5 | |||
| b9ebd4e1d6 | |||
| 8fdf1e841c | |||
| 9df92d0262 | |||
| a07d5aa440 | |||
| 54bd222605 | |||
| 6487258136 | |||
| d1935a4439 | |||
| 026c30642e | |||
| 036d036a61 | |||
| 2092d44627 | |||
| c6e7e64ba3 | |||
| a8f5ad6435 | |||
| afa0c3a1b0 | |||
| b3132d57b2 | |||
| 0a2a6b558f | |||
| adb9926928 | |||
| 3ce25007b5 | |||
| 5690ada2a7 | |||
| 76481186e9 | |||
| 8d2d2341c8 | |||
| 4e20d282f7 | |||
| edc3ce1ba4 | |||
| b9249ff09a | |||
| c3b2ffa97d | |||
| 4e3b8ee3c2 | |||
| a749ed4837 | |||
| 67ba6be6e2 | |||
| 7a47e6617d | |||
| 4a4c6e7df2 | |||
| 5661528862 | |||
| 696e9d6b64 | |||
| c0aa465827 | |||
| a6120ae27a | |||
| ba8a165aa5 | |||
| 833d7072ed | |||
| 9829137001 | |||
| c686214f56 | |||
| 2252d7ea6a | |||
| e7fbfca2d7 | |||
| 9ca959a20a | |||
| bd8e26ecab | |||
| 451b34dceb | |||
| 02761db660 | |||
| 42b7e9fa62 | |||
| edf6c25e17 | |||
| e91aac65cc | |||
| 01d5a18af8 | |||
| 70c1142f8d | |||
| 8b6b162073 | |||
| 5199fbe0cb | |||
| 924de62dff | |||
| 4cba7d8684 | |||
| f3f9ab1360 | |||
| 682d1d85ce | |||
| a1cc9a2049 | |||
| a7f7b3e572 | |||
| 7c32a7c2c8 | |||
| e842579f25 | |||
| bdd9134a0e | |||
| a01552e88c | |||
| 824957ae85 | |||
| af335d5565 | |||
| 2f9eb51868 | |||
| aebbc17643 | |||
| 84e78e1e20 | |||
| 89bb747ee3 | |||
| 62e37dbd09 | |||
| edef9cb936 | |||
| 3ae02c3050 | |||
| a4016616a1 | |||
| b4855611c4 | |||
| 1b44f6d220 | |||
| b0f0af087b | |||
| 1344f7255d | |||
| 39fe3b0fd6 | |||
| 0ba676b5e7 | |||
| 4d6897c138 | |||
| c7c6ba09e9 | |||
| c06baa67f1 | |||
| cdb7946c00 | |||
| bdb5da8df0 | |||
| e961c8d3aa | |||
| 3eb1a9eef8 | |||
| 67aead8fcc | |||
| fd764d0576 | |||
| dad55d7922 | |||
| fb32772512 | |||
| 918ce65acd | |||
| 9f03b9ee71 | |||
| 2235de1a2d | |||
| 8804c89f04 | |||
| 2e8805015c | |||
| f435ca2961 | |||
| 71c3761b20 | |||
| e4c441043a | |||
| 8a655a5d6e | |||
| 777c21ce87 | |||
| e22ff09691 | |||
| d0f685e87c | |||
| 8f71c417a9 | |||
| b62977c494 | |||
| 8d11136c1c | |||
| 4a7ea43095 | |||
| 8fe4bd2751 | |||
| 38bb819a24 | |||
| dbe75ad18d | |||
| 760441b45a | |||
| 56645b601b | |||
| 885e4bc99f | |||
| b04e5510fd | |||
| 806000725b | |||
| 71270641d3 | |||
| bf4f2bec91 | |||
| dafad3a7a3 | |||
| 182a1cc3fb | |||
| 4b7664aaa6 | |||
| 2050aef1e5 | |||
| 390af30bf6 | |||
| 698f48b1fd | |||
| 2e5cc61ac6 | |||
| 8d97fb7633 | |||
| 8a41c393bb | |||
| 6ae38359d7 | |||
| 7ddd1e3497 | |||
| 20a0993aa8 | |||
| 57d58056de | |||
| 06c6fa4d01 | |||
| 41f884e129 | |||
| 77a74c8839 | |||
| c198788017 | |||
| 4cbfa21b52 | |||
| f3754de394 | |||
| d47ad013cd | |||
| 8c4372d0d3 | |||
| 1c7b9145c8 | |||
| c477f53ee6 | |||
| f99f03dc33 | |||
| 2ddd786819 | |||
| 6e604f98e3 | |||
| 729404d05f | |||
| de50dfdb7c | |||
| 7bda522f0a | |||
| 6c39fb0649 | |||
| a7342ac77e | |||
| 5d45b8bbdd | |||
| aa0ff38ed7 | |||
| d55aa3b569 | |||
| d86a99fb32 | |||
| c687152724 | |||
| 65ec539875 | |||
| 6dba0792d2 | |||
| df78cecb31 | |||
| 3d8687f69d | |||
| 92196897a9 | |||
| 4206edfb13 | |||
| c08e63ab80 | |||
| 03646b4f87 | |||
| d9fa860b0c | |||
| 93d8494ddc | |||
| bd982958fa | |||
| e280b62f5c | |||
| 2bb050de40 | |||
| f3ed3bf0bf | |||
| 79457536f2 | |||
| 048f930da1 | |||
| 6aa8108fce | |||
| c234f75d7e | |||
| 064b634f77 | |||
| 8d83184cd1 | |||
| 7a5112bee5 | |||
| 0c549c6bda | |||
| c48e704502 | |||
| bec66895d9 | |||
| c9f1e8a8bb | |||
| ac209e7ee2 | |||
| 525256e15c | |||
| 3b8c965f4b | |||
| 8f8da8e6ea | |||
| ac9b81abea | |||
| 1c39c55423 | |||
| ca11273b37 | |||
| b532a60c3d | |||
| 941662e9f2 | |||
| 4d1e4c3ebe | |||
| f66fd9bcae | |||
| f5c5ecd1b9 | |||
| f9b7855d19 | |||
| 418a8af99a | |||
| ce3b4661de | |||
| 4b811f38b0 | |||
| bba2823065 | |||
| 5547e9658d | |||
| e14cbb19f5 | |||
| 0613a001c5 | |||
| 2970ba69f8 | |||
| 2c6b811d4d | |||
| d5a3caf961 | |||
| 7e64778546 | |||
| 1afd0d7c28 | |||
| 3027a3c3e8 | |||
| 3d7df100ff | |||
| 4de5030196 | |||
| e3bfe368db | |||
| e45fe0aaa0 | |||
| 807d758bfa | |||
| 7c5164b9a5 | |||
| 1e15fb8e43 | |||
| ae996b4b9a | |||
| 3259a7eec9 | |||
| 39a5aa1d63 | |||
| dbe378ad82 | |||
| a316c11974 | |||
| 2fd05c2464 | |||
| 8adabb946e | |||
| 3f251b9c0e | |||
| aadd60c3ad | |||
| 99cc994865 | |||
| da0355ca88 | |||
| aaa7c0934a | |||
| 03c70f4dfa | |||
| 0704609fa5 | |||
| d26569b26f | |||
| 007e93e526 | |||
| 8feec0284d | |||
| eaa79fb3bd | |||
| 3af5102e93 | |||
| d936460d83 | |||
| f51649c59f | |||
| be1e33b555 | |||
| 059645dec7 | |||
| 6439becd7d | |||
| 917fbc1ea2 | |||
| c97fee90b7 | |||
| 35d04946b4 | |||
| d0d71d626e | |||
| 5a1b39c67e | |||
| a8cbd37697 | |||
| b2bac94009 | |||
| d88b7e2a17 | |||
| 68bf35d83d | |||
| a78e6587ac | |||
| 21f715a321 | |||
| 18a5dfd81f | |||
| 2a7b5e2efb | |||
| 0d63b0361f | |||
| 4e301ddd24 | |||
| bc182276ac | |||
| 4980523d10 | |||
| 85baf58b55 | |||
| d7a4d02564 | |||
| 0e6f4c45db | |||
| 932cadce3c | |||
| 3926ea9c69 | |||
| dd1495c881 | |||
| 8c27e6aade | |||
| ba2774eeb5 | |||
| 8e854a8d64 | |||
| 86f5ed198f | |||
| cc57520c71 | |||
| 8d9f8960b2 | |||
| f66573620b | |||
| 3544a0e7f8 | |||
| 9c9db90886 | |||
| c4bc4d22e9 | |||
| b107c70a0c | |||
| 084069441f | |||
| 8b01433e61 | |||
| b72902b8f4 | |||
| 354e455ae7 | |||
| 8aaed47e39 | |||
| c7598aaf12 | |||
| cbe2d16d9b | |||
| 953eb97513 | |||
| b340b3b699 | |||
| f9f2579904 | |||
| 3a90653edd | |||
| a8ae18f43c | |||
| c235dd934a | |||
| 3e7c2cb0c2 | |||
| 1c9398b5b9 | |||
| 6a9c818e67 | |||
| 753baf85b6 | |||
| 7685c2a6b7 | |||
| cf1203566e | |||
| 052e6a475b | |||
| 8890acef3a | |||
| 72570ee21b | |||
| 100c94ad83 | |||
| 2ea3bf20a7 | |||
| b1cb7c7259 | |||
| 7510dfc5c5 | |||
| b18bbba23f | |||
| 4e28cea2a3 | |||
| a9bafc5efd | |||
| a04ff3343b | |||
| aa09fb28d2 | |||
| e6900c18b9 | |||
| 221a17a5af | |||
| fc638c608b | |||
| 71d9d96d81 | |||
| 5a8b999509 | |||
| 720d7e9d8d | |||
| c69be5934d | |||
| dae186fb03 | |||
| 076ad78355 | |||
| 421aa3a95c | |||
| 153d186a1c | |||
| 2238835868 | |||
| e0be4542ab | |||
| fab841bc7a | |||
| 789a28a966 | |||
| 7cde652ed1 | |||
| 5359116e72 | |||
| 17edfd215d | |||
| e292b46cca | |||
| d091b20ebe | |||
| 50a53562a1 | |||
| 55a479590b | |||
| 8874bb64fb | |||
| 38afba3075 | |||
| ba48e30128 | |||
| 77397b6877 | |||
| f50fa0554a | |||
| d0dd9f629d | |||
| c82637e760 | |||
| 152cfb3f07 | |||
| 7f579181fd | |||
| 3e0f39b6f1 | |||
| 244d3b1a5b | |||
| 7c24302f7c | |||
| 6cafc3a1e8 | |||
| 1ab0d31baa | |||
| b2fadc5a90 | |||
| 38f3d85909 | |||
| 3694100265 | |||
| af44f271ab | |||
| 9984f6aef9 | |||
| 51a1debc39 | |||
| b8a68f62a0 | |||
| 5ded188f51 | |||
| 12c5dda1fa | |||
| 25146049bf | |||
| 5598ee0c78 | |||
| 6e4b0cbcbf | |||
| 572cf29974 | |||
| 5601d19002 | |||
| e81dd5df76 | |||
| e7919d5a47 | |||
| 6f634fbc21 | |||
| 7478ece1ff | |||
| cd72b6f477 | |||
| fab96de4c7 | |||
| 0ffa17cf67 | |||
| 777549a15f | |||
| c07ded004d | |||
| da3e96a9d8 | |||
| d6e8a03ddf | |||
| b13cbd1e54 | |||
| 6b2e5c154b | |||
| 137a4d1e0d | |||
| 1725550acc | |||
| bd91e173b0 | |||
| 47a11b3e64 | |||
| b5e57519ff | |||
| 20845bbcd4 | |||
| 739c10ade6 | |||
| 14ea2d72a7 | |||
| 4a9ea97ea1 | |||
| b017a94353 | |||
| 15b65dd844 | |||
| 079ea8c39d | |||
| 4b949dcd72 | |||
| 2626cf4253 | |||
| b260c8aaec | |||
| 1ece46473b | |||
| 890c3cc8b0 | |||
| 7b45c9f1c5 | |||
| 58fb2f5ea6 | |||
| a79f3e47ba | |||
| b3b9db9ff6 | |||
| 9aed245241 | |||
| aa03fdb445 | |||
| 7cb8356598 | |||
| ac347755fd | |||
| b16cb15e88 | |||
| 4989c37964 | |||
| 06849c5814 | |||
| 78b67a6f5e | |||
| acf79df4d0 | |||
| bc5a9caf63 | |||
| 7b34b07cdc | |||
| 8df1a1bf17 | |||
| 1143b0f2d2 | |||
| 86883336fd | |||
| 62d77c5811 | |||
| 8397dddbbe | |||
| 47ef94d8c3 | |||
| 8aa4a485ed | |||
| cb4ef9c9ea | |||
| 2f80852a7c | |||
| 190a580642 | |||
| 6ba85f5069 | |||
| 707b5921fb | |||
| 2e25e68444 | |||
| 034260e426 | |||
| b4eda8bbff | |||
| 93a1b7fb52 | |||
| 8ef44c3520 | |||
| 449de57fc7 | |||
| cbe29e233d | |||
| bef56ff124 | |||
| c1e13e520b | |||
| ae8473183d | |||
| 69fb328b50 |
+206
@@ -1,4 +1,210 @@
|
||||
|
||||
2.5.4.2541
|
||||
|
||||
- core: try retrieving advanced_settings.json from the path given, which may be a file path or a directory
|
||||
- menu: ignore options: fix plugin not responding, fix unicode strings; resolve #509
|
||||
- providers: addic7ed: fix usage/adapt to new show search method
|
||||
- providers: opensubtitles: properly handle responses again, re-enable automatic throttling based on those (broken since XMLRPC handler rewrite)
|
||||
|
||||
|
||||
2.5.4.2527
|
||||
|
||||
- core: bugfixes
|
||||
- core: get_item: don't fail on socket timeout; fixes #498
|
||||
- core: fix scandir encoding errors; #453 #461 #441
|
||||
- core: clamp menu history to 25 items
|
||||
- add UnRAR for aarch64 (untested), arm (armv5tel, untested), linux/i386, MacOSX/i386; fixes #311
|
||||
- add 3rd party licenses
|
||||
- menu: new debounce/history mechanism; fixes the back button usage
|
||||
- config: add custom path option for advanced_settings.json
|
||||
- providers: opensubtitles: re-add support for throttling based on HTTP response codes, which got ditched due to new connection interface
|
||||
- providers: legendastv: disable if unrar wasn't found
|
||||
- providers: addic7ed: reduce show cache to 1 week
|
||||
- advanced settings: sonarr/radarr: make ssl verification optional
|
||||
- advanced settings: opensubtitles: add configurable connection timeout
|
||||
- refiners: drone: use certifi for HTTPS connections
|
||||
- tasks: SearchAllRecentlyAddedMissing: fix ZeroDivisionError in edgecases; fixes #496
|
||||
|
||||
|
||||
2.5.3.2452
|
||||
|
||||
- core: update certifi to 2018.01.18
|
||||
- core: metadata storage: only allow one subtitle per language
|
||||
- core: metadata storage: only parse latest metadata subtitle in localmedia
|
||||
- core: metadata storage: kill existing metadata subtitles explicitly upon storing a new one
|
||||
- core: metadata storage: fix selecting current subtitle from menu
|
||||
- providers: opensubtitles: use new requests based transport by default, finally fixes ResponseNotReady properly
|
||||
- providers: opensubtitles: mask token in logs
|
||||
- providers: don't check for hash validity if it isn't verifiable (fixes napiprojekt, #478)
|
||||
- submod: common: extend non_word_only matching
|
||||
- submod: common: reduce multi spaces to one
|
||||
- submod: OCR: fix III'll=I'll
|
||||
- advanced settings: add option to use HTTP instead of HTTPS for OpenSubtitles
|
||||
|
||||
|
||||
2.5.3.2422
|
||||
|
||||
- core: don't fail on embedded subtitle streams without language code set, fixes #473
|
||||
- providers: catch ResponseNotReady in list_subtitles_provider as well (partly fixes OpenSubtitles)
|
||||
- providers: don't use retry logic in case of ResponseNotReady
|
||||
- providers: addic7ed: use new search endpoint
|
||||
|
||||
|
||||
2.5.3.2414
|
||||
|
||||
- core: expand user agent list
|
||||
- core: update subliminal to 4ad5d31
|
||||
- core: treat 23.976, 23.98, 24.0 fps as equal
|
||||
- core: correctly skip blacklist entries when iterating through currently known subs
|
||||
- core: fix unpacking of packs without asked-for-release-group
|
||||
- core: fix embedded subtitle language detection; add debug log
|
||||
- core: treat embedded subtitle containing "forced" in its title as forced
|
||||
- core: improve embedded subtitles detection
|
||||
- core: store extracted embedded forced subtitles with the "forced" suffix (e.g.: video.en.forced.srt)
|
||||
- core: don't bother trying to extract embedded subtitle if transcoder wasn't found
|
||||
- core: fix automatic extraction of unknown embedded subtitle streams
|
||||
- core: skip immediately searching for new subtitle after successfully extracting embedded
|
||||
- core: extract embedded ASS: don't transcode to SRT using ffmpeg (Plex Transcoder), do the transcoding later using pysubs2; fixes offset issues
|
||||
- core: extract embedded: let ffmpeg auto convert mov_text/tx3g to srt
|
||||
- core: fix transcoder detection; add fallback #460
|
||||
- core: remove LD_LIBRARY_PATH from environment before calling notification executable
|
||||
- core: auto extract embedded subtitles in a separate thread
|
||||
- core: reduce encoding change log spam
|
||||
- core: only allow one automatic extraction at a time; add optional advanced settings "auto_extract_multithread"
|
||||
- core: add minimum score a subtitle has to have when considered by the find better subtitles task, when the current subtitle is an extracted embedded one; add advanced_settings entries
|
||||
- core/config: automatic extraction: add config setting to indicate whether there should be an immediate search for available subtitles after extraction or not (default: off)
|
||||
- core/menu/submod: add reverse_rtl modification for Hebrew; fixes #409
|
||||
- core: scoring: assume title match on tvdb_id match
|
||||
- tasks: search all recently added missing: fix attribute access on missing stored subtitle info
|
||||
- providers: add hosszupuska (hungarian, thanks morpheus133 for the basic implementation)
|
||||
- providers: add argenteam (spanish, thanks mmiraglia for the basic implementation)
|
||||
- providers: addic7ed: use random user agent by default (enforce for existing configs)
|
||||
- providers: enable subscene by default
|
||||
- providers: opensubtitles: add fallback for dict based query response in contrast to list/array based
|
||||
- advanced settings: make text-based-subtitle-formats configurable
|
||||
- menu: submod: inverse-reverse subtitle timing time-choices for better accessibility
|
||||
- submod: reduce log spam in case of debug logs enabled
|
||||
- submod: style tags could result in no output at all
|
||||
- submod: fix empty content if only non-line-mods were used, no line-mods; fixes #449
|
||||
- submod: HI: correctly handle style tags when checking for brackets
|
||||
- submod: HI: don't remove anything that's surrounded by quotes
|
||||
- submod: HI: double or triple dash is em dash
|
||||
- submod: HI: HI_before_colon_noncaps, don't assume single quotes are sentence enders
|
||||
- submod: common: don't uppercase after abbreviations
|
||||
- submod: common: don't break phone numbers (more than one spaced number pair found)
|
||||
- submod: common: also count lines only consisting of dots as removable
|
||||
- submod: common: replace more than 3 consecutive dots with 3 dots
|
||||
- submod: OCR: "H i." = "Hi."
|
||||
|
||||
|
||||
2.5.0.2287
|
||||
|
||||
- core: reduce main icon size
|
||||
- core: fix usage on NVIDIA SHIELD (hopefully, please report back), #441
|
||||
- core: add scandir fallback to listdir in case of badly configured locale in environment, #441, #440
|
||||
- core: get subtitles from archive: don't assume an episode match
|
||||
- core: get subtitles from archive: don't assume any attributes in guess
|
||||
- core: improve release group detection for drone/filebot/file_info refiners
|
||||
- core: fix language detection for embedded subtitle streams
|
||||
- core: support extraction of embedded mov_text subtitles in mp4 video files
|
||||
- refiners: drone: add http:// to url if not given
|
||||
- providers: opensubtitles: retry/reinitialize request when encountering ResponseNotReady
|
||||
- config: clarify subscene being only enabled for TV series by default
|
||||
- menu: when encountering permission errors when scanning media files, warn in the menu about them
|
||||
- submod: common: don't break -- addic7ed --
|
||||
- submod: common: remove lines that consist only of dash, underscore
|
||||
- submod: OCR: fix Ls = Is
|
||||
- submod: OCR: fix bad HI colons (ANNOUNCER; instead of ANNOUNCER:)
|
||||
- submod: common: fix lines consisting only of bad music symbols (*#¶ = ♪)
|
||||
- submod: HI: remove music-symbol-only-lines
|
||||
- submod: HI: be less aggressive about lines ending with a colon; please re-apply all your mods via advanced menu
|
||||
- submod: OCR: fix it'sjust, isn'tjust, Iam, Ican
|
||||
|
||||
|
||||
2.5.0.2247
|
||||
- fix ignoring by-hash-matched episodes
|
||||
|
||||
|
||||
2.5.0.2241
|
||||
|
||||
- fix issue when removing crap from filenames to not accidentally remove release group #436
|
||||
- fix initialization of soft ignore list after upgrade fron 2.0
|
||||
|
||||
|
||||
2.5.0.2221
|
||||
|
||||
- refiners: add support for retrieving original filename from
|
||||
- drone derivates: sonarr, radarr
|
||||
- filebot
|
||||
- symlinks
|
||||
- file_info meta file lists (see wiki)
|
||||
|
||||
- providers: add subscene (disabled by default to not flood subscene on release)
|
||||
- normal search
|
||||
- season pack search if season has concluded
|
||||
|
||||
- core: add provider subtitle-archive/pack cache for retrieving single subtitles from previously downloaded (season-) packs (subscene)
|
||||
- core/agent: massive performance improvements over 2.0
|
||||
- core/agent/background-tasks: reduce memory usage to a fraction of 2.0
|
||||
- core/providers: add dynamic provider throttling when certain events occur (ServiceUnavailable, too many downloads, ...), to lighten the provider-load
|
||||
- core/agent/config: automatically extract embedded subtitles (and use them if no current subtitle)
|
||||
- core: fix internal subtitle info storage issues
|
||||
- core: always store internal subtitle information even if no subtitle was downloaded (fixes SearchAllRecentlyAddedMissing)
|
||||
- core: fix internal subtitle info storage on windows (gzip handling is broken there)
|
||||
- core: don't fail on missing logfile paths
|
||||
- core: fix default encoding order for non-script-serbian
|
||||
- core: improve logging
|
||||
- core: add AsRequested to cleanup garbage names
|
||||
- core: treat SDTV and HDTV the same when searching for subtitles
|
||||
- core: parse_video: trust PMS season and episode numbers
|
||||
- core: parse_video: add series year information from PMS if none found
|
||||
- core: upgrade dependencies
|
||||
- core: update subliminal to 62cdb3c
|
||||
- core: add new file based cache mechanism, rendering DBM/memory backends obsolete
|
||||
- core: treat 23.980 fps as 23.976 and vice-versa
|
||||
- core: add HTTP proxy support for querying the providers (supports credentials)
|
||||
- core: only compute file hashes for enabled providers
|
||||
- core: massive speedup; refine only when needed, exit early otherwise
|
||||
- core: store last modified timestamp in subtitle info storage
|
||||
- core: only write to subtitle info storage if we haven't had one or any subtitle was downloaded
|
||||
- core: only clean up the sub-folder if a subtitle-sub-folder has been selected, and not the parent one also
|
||||
- core: support for CP437 encoded filenames in ZIP-Archives
|
||||
- core: use scandir library instead of os.listdir if possible, reducing performance-impact
|
||||
- core: archives: support multi-episode subtitles (partly)
|
||||
- core: subtitle cleanup: add support for hi, cc, sdh secondary filename tags; don't autoclean .txt
|
||||
- core: increase request timeout by three times in case a proxy is being used
|
||||
- core: fix language=Unknown in Plex when "Restrict to one language"-setting is set
|
||||
- core: refining: re-add old detected title as alternative title after re-refining with plex metadata's title; fixes #428
|
||||
- core: implement advanced_settings.json (see advanced_settings.json.template for reference, copy to "Plug-in Support/Data/com.plexapp.agents.subzero" to use it)
|
||||
- core/tasks: fix search all recently added missing (the total number of items will change in the menu while running), reduces memory usage
|
||||
- core/menu: add support for extracting embedded subtitles using the builtin plex transcoder
|
||||
- core/menu: skip wrong season or episode in returned subtitle results
|
||||
- core/config: fix language handling if treat undefined as first language is set
|
||||
- providers: remove shooter.cn
|
||||
- providers: add support for zip/rar archives containing more than one subtitle file
|
||||
- submod: common: remove redundant interpunction ("Hello !!!" -> "Hello!")
|
||||
- submod: skip provider hashing when applying mods
|
||||
- submod: correctly drop empty line (fixing broken display)
|
||||
- submod: OCR: fix F'xxxxx -> Fxxxxx
|
||||
- submod: HI: improve bracket matching
|
||||
- submod: OCR: fix l/L instead of I more aggressively
|
||||
- submod: common: fix uppercase I's in lowercase words more aggressively
|
||||
- submod: HI: improve HI_before_colon
|
||||
- submod: common: be more aggressive when fixing numbers; correctly space out spaced ellipses; don't break spaced ellipses; handle multiple spaces in numbers
|
||||
- menu: add support for extracting embedded subtitles for a whole season
|
||||
- menu: add reapply mods to current subtitle
|
||||
- menu: pad titles for more submenus, resulting in detail view in PlexWeb
|
||||
- menu: add subtitle selection submenu (if multiple subtitles are inside the subtitle info storage; e.g. previously downloaded ones or extracted embedded)
|
||||
- menu: advanced: add skip findbettersubtitles menu item, which sets the last_run to now (for debugging purposes)
|
||||
- menu: ignore: add more natural title for seasons and episodes (kills your old ignore lists!)
|
||||
- config: skip provider hashing on low impact mode
|
||||
- config: add limit by air date setting to consider for FindBetterSubtitles task (default: 1 year)
|
||||
- advanced settings: define enabled-for media types per provider
|
||||
- advanced settings: define enabled-for languages per provider
|
||||
- advanced settings: add deep-clean option (clean up the subtitle-sub-folder and the parent one)
|
||||
|
||||
|
||||
|
||||
2.0.33.1871
|
||||
- core: normalize line endings in subtitles to LF (\n)
|
||||
- core: add subtitle storage lock to avoid race condition
|
||||
|
||||
+38
-17
@@ -2,10 +2,10 @@
|
||||
import sys
|
||||
import datetime
|
||||
|
||||
from subzero.sandbox import restore_builtins
|
||||
from subzero.sandbox import fix_environment_stuff
|
||||
|
||||
module = sys.modules['__main__']
|
||||
restore_builtins(module, {})
|
||||
fix_environment_stuff(module, {})
|
||||
|
||||
globals = getattr(module, "__builtins__")["globals"]
|
||||
for key, value in getattr(module, "__builtins__").iteritems():
|
||||
@@ -46,6 +46,8 @@ def Start():
|
||||
intent = get_intent()
|
||||
intent.cleanup()
|
||||
|
||||
#Locale.DefaultLocale = "de"
|
||||
|
||||
# clear expired menu history items
|
||||
now = datetime.datetime.now()
|
||||
if "menu_history" in Dict:
|
||||
@@ -114,32 +116,41 @@ def update_local_media(metadata, media, media_type="movies"):
|
||||
pass
|
||||
|
||||
|
||||
def agent_extract_embedded(videos):
|
||||
def agent_extract_embedded(video_part_map):
|
||||
try:
|
||||
subtitle_storage = get_subtitle_storage()
|
||||
|
||||
for video in videos:
|
||||
item = video["item"]
|
||||
stored_subs = subtitle_storage.load_or_new(item)
|
||||
to_extract = []
|
||||
item_count = 0
|
||||
|
||||
for part in get_all_parts(item):
|
||||
for scanned_video, part_info in video_part_map.iteritems():
|
||||
plexapi_item = scanned_video.plexapi_metadata["item"]
|
||||
stored_subs = subtitle_storage.load_or_new(plexapi_item)
|
||||
|
||||
for plexapi_part in get_all_parts(plexapi_item):
|
||||
item_count = item_count + 1
|
||||
for requested_language in config.lang_list:
|
||||
embedded_subs = stored_subs.get_by_provider(part.id, requested_language, "embedded")
|
||||
current = stored_subs.get_any(part.id, requested_language)
|
||||
embedded_subs = stored_subs.get_by_provider(plexapi_part.id, requested_language, "embedded")
|
||||
current = stored_subs.get_any(plexapi_part.id, requested_language)
|
||||
if not embedded_subs:
|
||||
stream_data = get_embedded_subtitle_streams(part, requested_language=requested_language,
|
||||
stream_data = get_embedded_subtitle_streams(plexapi_part, requested_language=requested_language,
|
||||
get_forced=config.forced_only)
|
||||
|
||||
if stream_data:
|
||||
stream = stream_data[0]["stream"]
|
||||
|
||||
extract_embedded_sub(rating_key=item.rating_key, part_id=part.id,
|
||||
stream_index=str(stream.index),
|
||||
language=str(requested_language), with_mods=True, refresh=False,
|
||||
set_current=not current)
|
||||
to_extract.append(({scanned_video: part_info}, plexapi_part, str(stream.index),
|
||||
str(requested_language), not current))
|
||||
|
||||
if not cast_bool(Prefs["subtitles.search_after_autoextract"]):
|
||||
scanned_video.subtitle_languages.update({requested_language})
|
||||
else:
|
||||
Log.Debug("Skipping embedded subtitle extraction for %s, already got %r from %s",
|
||||
item.rating_key, requested_language, embedded_subs[0].id)
|
||||
plexapi_item.rating_key, requested_language, embedded_subs[0].id)
|
||||
if to_extract:
|
||||
Log.Info("Triggering extraction of %d embedded subtitles of %d items", len(to_extract), item_count)
|
||||
Thread.Create(multi_extract_embedded, stream_list=to_extract, refresh=True, with_mods=True,
|
||||
single_thread=not config.advanced.auto_extract_multithread)
|
||||
except:
|
||||
Log.Error("Something went wrong when auto-extracting subtitles, continuing: %s", traceback.format_exc())
|
||||
|
||||
@@ -206,11 +217,21 @@ class SubZeroAgent(object):
|
||||
|
||||
# scanned_video_part_map = {subliminal.Video: plex_part, ...}
|
||||
providers = config.get_providers(media_type=self.agent_type)
|
||||
scanned_video_part_map = scan_videos(videos, providers=providers)
|
||||
try:
|
||||
scanned_video_part_map = scan_videos(videos, providers=providers)
|
||||
except IOError, e:
|
||||
Log.Exception("Permission error, please check your folder/file permissions. Exiting.")
|
||||
if cast_bool(Prefs["check_permissions"]):
|
||||
config.permissions_ok = False
|
||||
config.missing_permissions = e.message
|
||||
return
|
||||
|
||||
# auto extract embedded
|
||||
if config.embedded_auto_extract:
|
||||
agent_extract_embedded(videos)
|
||||
if config.plex_transcoder:
|
||||
agent_extract_embedded(scanned_video_part_map)
|
||||
else:
|
||||
Log.Warning("Plex Transcoder not found, can't auto extract")
|
||||
|
||||
# clear missing subtitles menu data
|
||||
if not scheduler.is_task_running("MissingSubtitles"):
|
||||
|
||||
@@ -20,86 +20,98 @@ from support.lib import Plex
|
||||
from support.storage import reset_storage, log_storage, get_subtitle_storage
|
||||
from support.scheduler import scheduler
|
||||
from support.items import set_mods_for_part, get_item_kind_from_rating_key
|
||||
from support.i18n import _
|
||||
|
||||
|
||||
@route(PREFIX + '/advanced')
|
||||
def AdvancedMenu(randomize=None, header=None, message=None):
|
||||
oc = SubFolderObjectContainer(header=header or "Internal stuff, pay attention!", message=message, no_cache=True,
|
||||
no_history=True,
|
||||
replace_parent=False, title2="Advanced")
|
||||
oc = SubFolderObjectContainer(
|
||||
header=header or _("Internal stuff, pay attention!"),
|
||||
message=message,
|
||||
no_cache=True,
|
||||
no_history=True,
|
||||
replace_parent=False,
|
||||
title2=_("Advanced"))
|
||||
|
||||
if config.lock_advanced_menu and not config.pin_correct:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(PinMenu, randomize=timestamp(), success_go_to="advanced"),
|
||||
title=pad_title("Enter PIN"),
|
||||
summary="The owner has restricted the access to this menu. Please enter the correct pin",
|
||||
key=Callback(
|
||||
PinMenu,
|
||||
randomize=timestamp(),
|
||||
success_go_to=_("advanced")),
|
||||
title=pad_title(_("Enter PIN")),
|
||||
summary=_("The owner has restricted the access to this menu. Please enter the correct pin"),
|
||||
))
|
||||
return oc
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(TriggerRestart, randomize=timestamp()),
|
||||
title=pad_title("Restart the plugin"),
|
||||
title=pad_title(_("Restart the plugin")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(GetLogsLink),
|
||||
title="Get my logs (copy the appearing link and open it in your browser, please)",
|
||||
summary="Copy the appearing link and open it in your browser, please",
|
||||
title=_("Get my logs (copy the appearing link and open it in your browser, please)"),
|
||||
summary=_("Copy the appearing link and open it in your browser, please"),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(TriggerBetterSubtitles, randomize=timestamp()),
|
||||
title=pad_title("Trigger find better subtitles"),
|
||||
title=pad_title(_("Trigger find better subtitles")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SkipFindBetterSubtitles, randomize=timestamp()),
|
||||
title=pad_title("Skip next find better subtitles (sets last run to now)"),
|
||||
title=pad_title(_("Skip next find better subtitles (sets last run to now)")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(TriggerStorageMaintenance, randomize=timestamp()),
|
||||
title=pad_title("Trigger subtitle storage maintenance"),
|
||||
title=pad_title(_("Trigger subtitle storage maintenance")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(TriggerStorageMigration, randomize=timestamp()),
|
||||
title=pad_title("Trigger subtitle storage migration (expensive)"),
|
||||
title=pad_title(_("Trigger subtitle storage migration (expensive)")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(TriggerCacheMaintenance, randomize=timestamp()),
|
||||
title=pad_title("Trigger cache maintenance (refiners, providers and packs/archives)"),
|
||||
title=pad_title(_("Trigger cache maintenance (refiners, providers and packs/archives)")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ApplyDefaultMods, randomize=timestamp()),
|
||||
title=pad_title("Apply configured default subtitle mods to all (active) stored subtitles"),
|
||||
title=pad_title(_("Apply configured default subtitle mods to all (active) stored subtitles")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ReApplyMods, randomize=timestamp()),
|
||||
title=pad_title("Re-Apply mods of all stored subtitles"),
|
||||
title=pad_title(_("Re-Apply mods of all stored subtitles")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(LogStorage, key="tasks", randomize=timestamp()),
|
||||
title=pad_title("Log the plugin's scheduled tasks state storage"),
|
||||
title=pad_title(_("Log the plugin's scheduled tasks state storage")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(LogStorage, key="ignore", randomize=timestamp()),
|
||||
title=pad_title("Log the plugin's internal ignorelist storage"),
|
||||
title=pad_title(_("Log the plugin's internal ignorelist storage")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(LogStorage, key=None, randomize=timestamp()),
|
||||
title=pad_title("Log the plugin's complete state storage"),
|
||||
title=pad_title(_("Log the plugin's complete state storage")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ResetStorage, key="tasks", randomize=timestamp()),
|
||||
title=pad_title("Reset the plugin's scheduled tasks state storage"),
|
||||
title=pad_title(_("Reset the plugin's scheduled tasks state storage")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ResetStorage, key="ignore", randomize=timestamp()),
|
||||
title=pad_title("Reset the plugin's internal ignorelist storage"),
|
||||
title=pad_title(_("Reset the plugin's internal ignorelist storage")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ResetStorage, key="menu_history", randomize=timestamp()),
|
||||
title=pad_title("Reset the plugin's menu history storage"),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(InvalidateCache, randomize=timestamp()),
|
||||
title=pad_title("Invalidate Sub-Zero metadata caches (subliminal)"),
|
||||
title=pad_title(_("Invalidate Sub-Zero metadata caches (subliminal)")),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ResetProviderThrottle, randomize=timestamp()),
|
||||
title=pad_title("Reset provider throttle states"),
|
||||
title=pad_title(_("Reset provider throttle states")),
|
||||
))
|
||||
return oc
|
||||
|
||||
@@ -111,15 +123,20 @@ def DispatchRestart():
|
||||
@route(PREFIX + '/advanced/restart/trigger')
|
||||
@debounce
|
||||
def TriggerRestart(randomize=None):
|
||||
set_refresh_menu_state("Restarting the plugin")
|
||||
set_refresh_menu_state(_("Restarting the plugin"))
|
||||
DispatchRestart()
|
||||
return fatality(header="Restart triggered, please wait about 5 seconds", force_title=" ", only_refresh=True,
|
||||
replace_parent=True,
|
||||
no_history=True, randomize=timestamp())
|
||||
return fatality(
|
||||
header=_("Restart triggered, please wait about 5 seconds"),
|
||||
force_title=" ",
|
||||
only_refresh=True,
|
||||
replace_parent=True,
|
||||
no_history=True,
|
||||
randomize=timestamp())
|
||||
|
||||
|
||||
@route(PREFIX + '/advanced/restart/execute')
|
||||
def Restart():
|
||||
@debounce
|
||||
def Restart(randomize=None):
|
||||
Plex[":/plugins"].restart(PLUGIN_IDENTIFIER)
|
||||
|
||||
|
||||
@@ -127,10 +144,17 @@ def Restart():
|
||||
@debounce
|
||||
def ResetStorage(key, randomize=None, sure=False):
|
||||
if not sure:
|
||||
oc = SubFolderObjectContainer(no_history=True, title1="Reset subtitle storage", title2="Are you sure?")
|
||||
oc = SubFolderObjectContainer(
|
||||
no_history=True,
|
||||
title1=_("Reset subtitle storage"),
|
||||
title2=_("Are you sure?"))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ResetStorage, key=key, sure=True, randomize=timestamp()),
|
||||
title=pad_title("Are you really sure?"),
|
||||
key=Callback(
|
||||
ResetStorage,
|
||||
key=key,
|
||||
sure=True,
|
||||
randomize=timestamp()),
|
||||
title=pad_title(_("Are you really sure?")),
|
||||
|
||||
))
|
||||
return oc
|
||||
@@ -144,8 +168,8 @@ def ResetStorage(key, randomize=None, sure=False):
|
||||
|
||||
return AdvancedMenu(
|
||||
randomize=timestamp(),
|
||||
header='Success',
|
||||
message='Information Storage (%s) reset' % key
|
||||
header=_("Success"),
|
||||
message=_("Information Storage (%s) reset", key)
|
||||
)
|
||||
|
||||
|
||||
@@ -154,8 +178,8 @@ def LogStorage(key, randomize=None):
|
||||
log_storage(key)
|
||||
return AdvancedMenu(
|
||||
randomize=timestamp(),
|
||||
header='Success',
|
||||
message='Information Storage (%s) logged' % key
|
||||
header=_("Success"),
|
||||
message=_("Information Storage (%s) logged", key)
|
||||
)
|
||||
|
||||
|
||||
@@ -165,12 +189,11 @@ def TriggerBetterSubtitles(randomize=None):
|
||||
scheduler.dispatch_task("FindBetterSubtitles")
|
||||
return AdvancedMenu(
|
||||
randomize=timestamp(),
|
||||
header='Success',
|
||||
message='FindBetterSubtitles triggered'
|
||||
header=_("Success"),
|
||||
message=_("FindBetterSubtitles triggered")
|
||||
)
|
||||
|
||||
|
||||
|
||||
@route(PREFIX + '/skipbetter')
|
||||
@debounce
|
||||
def SkipFindBetterSubtitles(randomize=None):
|
||||
@@ -179,8 +202,8 @@ def SkipFindBetterSubtitles(randomize=None):
|
||||
|
||||
return AdvancedMenu(
|
||||
randomize=timestamp(),
|
||||
header='Success',
|
||||
message='FindBetterSubtitles skipped'
|
||||
header=_("Success"),
|
||||
message=_("FindBetterSubtitles skipped")
|
||||
)
|
||||
|
||||
|
||||
@@ -190,8 +213,8 @@ def TriggerStorageMaintenance(randomize=None):
|
||||
scheduler.dispatch_task("SubtitleStorageMaintenance")
|
||||
return AdvancedMenu(
|
||||
randomize=timestamp(),
|
||||
header='Success',
|
||||
message='SubtitleStorageMaintenance triggered'
|
||||
header=_("Success"),
|
||||
message=_("SubtitleStorageMaintenance triggered")
|
||||
)
|
||||
|
||||
|
||||
@@ -201,8 +224,8 @@ def TriggerStorageMigration(randomize=None):
|
||||
scheduler.dispatch_task("MigrateSubtitleStorage")
|
||||
return AdvancedMenu(
|
||||
randomize=timestamp(),
|
||||
header='Success',
|
||||
message='MigrateSubtitleStorage triggered'
|
||||
header=_("Success"),
|
||||
message=_("MigrateSubtitleStorage triggered")
|
||||
)
|
||||
|
||||
|
||||
@@ -212,8 +235,8 @@ def TriggerCacheMaintenance(randomize=None):
|
||||
scheduler.dispatch_task("CacheMaintenance")
|
||||
return AdvancedMenu(
|
||||
randomize=timestamp(),
|
||||
header='Success',
|
||||
message='TriggerCacheMaintenance triggered'
|
||||
header=_("Success"),
|
||||
message=_("TriggerCacheMaintenance triggered")
|
||||
)
|
||||
|
||||
|
||||
@@ -265,8 +288,8 @@ def ApplyDefaultMods(randomize=None):
|
||||
Thread.CreateTimer(1.0, apply_default_mods)
|
||||
return AdvancedMenu(
|
||||
randomize=timestamp(),
|
||||
header='Success',
|
||||
message='This may take some time ...'
|
||||
header=_("Success"),
|
||||
message=_("This may take some time ...")
|
||||
)
|
||||
|
||||
|
||||
@@ -276,17 +299,20 @@ def ReApplyMods(randomize=None):
|
||||
Thread.CreateTimer(1.0, apply_default_mods, reapply_current=True)
|
||||
return AdvancedMenu(
|
||||
randomize=timestamp(),
|
||||
header='Success',
|
||||
message='This may take some time ...'
|
||||
header=_("Success"),
|
||||
message=_("This may take some time ...")
|
||||
)
|
||||
|
||||
|
||||
@route(PREFIX + '/get_logs_link')
|
||||
def GetLogsLink():
|
||||
if not config.plex_token:
|
||||
oc = ObjectContainer(title2="Download Logs", no_cache=True, no_history=True,
|
||||
header="Sorry, feature unavailable",
|
||||
message="Universal Plex token not available")
|
||||
oc = ObjectContainer(
|
||||
title2=_("Download Logs"),
|
||||
no_cache=True,
|
||||
no_history=True,
|
||||
header=_("Sorry, feature unavailable"),
|
||||
message=_("Universal Plex token not available"))
|
||||
return oc
|
||||
|
||||
# try getting the link base via the request in context, first, otherwise use the public ip
|
||||
@@ -311,9 +337,12 @@ def GetLogsLink():
|
||||
Log.Debug("Using ip-based fallback link_base")
|
||||
|
||||
logs_link = "%s%s?X-Plex-Token=%s" % (link_base, PREFIX + '/logs', config.plex_token)
|
||||
oc = ObjectContainer(title2=logs_link, no_cache=True, no_history=True,
|
||||
header="Copy this link and open this in your browser, please",
|
||||
message=logs_link)
|
||||
oc = ObjectContainer(
|
||||
title2=logs_link,
|
||||
no_cache=True,
|
||||
no_history=True,
|
||||
header=_("Copy this link and open this in your browser, please"),
|
||||
message=logs_link)
|
||||
return oc
|
||||
|
||||
|
||||
@@ -343,32 +372,45 @@ def InvalidateCache(randomize=None):
|
||||
region.invalidate()
|
||||
return AdvancedMenu(
|
||||
randomize=timestamp(),
|
||||
header='Success',
|
||||
message='Cache invalidated'
|
||||
header=_("Success"),
|
||||
message=_("Cache invalidated")
|
||||
)
|
||||
|
||||
|
||||
@route(PREFIX + '/pin')
|
||||
def PinMenu(pin="", randomize=None, success_go_to="channel"):
|
||||
oc = ObjectContainer(title2="Enter PIN number %s" % (len(pin) + 1), no_cache=True, no_history=True,
|
||||
skip_pin_lock=True)
|
||||
oc = ObjectContainer(
|
||||
title2=_("Enter PIN number ") + str(len(pin) + 1),
|
||||
no_cache=True,
|
||||
no_history=True,
|
||||
skip_pin_lock=True)
|
||||
|
||||
if pin == config.pin:
|
||||
Dict["pin_correct_time"] = datetime.datetime.now()
|
||||
config.locked = False
|
||||
if success_go_to == "channel":
|
||||
return fatality(force_title="PIN correct", header="PIN correct", no_history=True)
|
||||
return fatality(
|
||||
force_title=_("PIN correct"),
|
||||
header=_("PIN correct"),
|
||||
no_history=True)
|
||||
elif success_go_to == "advanced":
|
||||
return AdvancedMenu(randomize=timestamp())
|
||||
|
||||
for i in range(10):
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(PinMenu, randomize=timestamp(), pin=pin + str(i), success_go_to=success_go_to),
|
||||
key=Callback(
|
||||
PinMenu,
|
||||
randomize=timestamp(),
|
||||
pin=pin + str(i),
|
||||
success_go_to=success_go_to),
|
||||
title=pad_title(str(i)),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(PinMenu, randomize=timestamp(), success_go_to=success_go_to),
|
||||
title=pad_title("Reset"),
|
||||
key=Callback(
|
||||
PinMenu,
|
||||
randomize=timestamp(),
|
||||
success_go_to=success_go_to),
|
||||
title=pad_title(_("Reset")),
|
||||
))
|
||||
return oc
|
||||
|
||||
@@ -377,7 +419,7 @@ def PinMenu(pin="", randomize=None, success_go_to="channel"):
|
||||
def ClearPin(randomize=None):
|
||||
Dict["pin_correct_time"] = None
|
||||
config.locked = True
|
||||
return fatality(force_title="Menu locked", header=" ", no_history=True)
|
||||
return fatality(force_title=_("Menu locked"), header=" ", no_history=True)
|
||||
|
||||
|
||||
@route(PREFIX + '/reset_throttle')
|
||||
@@ -386,6 +428,6 @@ def ResetProviderThrottle(randomize=None):
|
||||
Dict.Save()
|
||||
return AdvancedMenu(
|
||||
randomize=timestamp(),
|
||||
header='Success',
|
||||
message='Provider throttles reset'
|
||||
)
|
||||
header=_("Success"),
|
||||
message=_("Provider throttles reset")
|
||||
)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# coding=utf-8
|
||||
|
||||
import datetime
|
||||
import operator
|
||||
|
||||
from support.config import config
|
||||
from support.helpers import timestamp
|
||||
|
||||
|
||||
def enable_channel_wrapper(func):
|
||||
@@ -25,3 +29,157 @@ def enable_channel_wrapper(func):
|
||||
return (func if (config.enable_channel or enforce_route) else noop)(*args, **kwargs)
|
||||
|
||||
return wrap
|
||||
|
||||
|
||||
ROUTE_REGISTRY = {}
|
||||
|
||||
|
||||
def get_func_name(args):
|
||||
return list(args).pop(0).__name__
|
||||
|
||||
|
||||
def get_lookup_key(f, args, kwargs):
|
||||
return tuple([f.__name__, tuple(args), tuple([(key, value) for key, value in kwargs.iteritems()])])
|
||||
|
||||
|
||||
def should_debounce(f, key, kw):
|
||||
return getattr(f, "debounce", False) and "randomize" in kw and key in Dict["menu_history"]
|
||||
|
||||
|
||||
def register_route_function(f):
|
||||
fn = f.__name__
|
||||
if fn != "ValidatePrefs" and fn not in ROUTE_REGISTRY:
|
||||
ROUTE_REGISTRY[fn] = f
|
||||
return f
|
||||
|
||||
|
||||
def main_menu_fallback():
|
||||
key = get_lookup_key(ROUTE_REGISTRY["fatality"], [], {})
|
||||
Dict["last_menu_item"] = key
|
||||
add_to_menu_history(key)
|
||||
|
||||
return ROUTE_REGISTRY["fatality"](randomize=timestamp())
|
||||
|
||||
|
||||
def add_to_menu_history(key):
|
||||
# add function to menu history
|
||||
mh = Dict["menu_history"]
|
||||
if key in mh:
|
||||
del mh[key]
|
||||
|
||||
mh[key] = datetime.datetime.now() + datetime.timedelta(hours=6)
|
||||
|
||||
# limit to 25 items
|
||||
Dict["menu_history"] = dict(sorted(sorted(mh.items(), key=operator.itemgetter(1),
|
||||
reverse=True)[:25]))
|
||||
|
||||
try:
|
||||
Dict.Save()
|
||||
except TypeError:
|
||||
Log.Error("Can't save menu history for: %r", key)
|
||||
del Dict["menu_history"][key]
|
||||
|
||||
|
||||
def route_wrapper(*args, **kwargs):
|
||||
def wrap(f):
|
||||
already_wrapped = getattr(f, "orig_f", False)
|
||||
|
||||
register_route_function(f)
|
||||
|
||||
def inner(*a, **kw):
|
||||
if "menu_history" not in Dict:
|
||||
Dict["menu_history"] = {}
|
||||
|
||||
if "last_menu_item" not in Dict:
|
||||
Dict["last_menu_item"] = None
|
||||
|
||||
key = get_lookup_key(f, list(a), kw)
|
||||
|
||||
ret_f = f
|
||||
ret_a = a
|
||||
ret_kw = kw
|
||||
# mh = Dict["menu_history"]
|
||||
# mh_keys = [k for k, v in sorted(mh.items(), key=operator.itemgetter(1))]
|
||||
#
|
||||
# fallback_needed = False
|
||||
# fallback_found = False
|
||||
|
||||
if should_debounce(ret_f, key, kw):
|
||||
# special case for TriggerRestart
|
||||
if ret_f.__name__ in ("TriggerRestart", "Restart"):
|
||||
Log.Debug("Don't trigger a re-restart, falling back to main menu")
|
||||
else:
|
||||
Log.Debug("not triggering %s twice with %s, %s, returning to main menu" %
|
||||
(f.__name__, a, kw))
|
||||
|
||||
return main_menu_fallback()
|
||||
#
|
||||
# fallback_needed = True
|
||||
#
|
||||
# # try to find a suitable fallback route in case we've encountered an already visited
|
||||
# # debounced route
|
||||
# fallbacks = []
|
||||
# current_last_visit = mh[key]
|
||||
# last_menu_item = Dict["last_menu_item"]
|
||||
# direction_backwards = True
|
||||
#
|
||||
# if last_menu_item and last_menu_item in mh and key in mh:
|
||||
# last_mi_pos = mh_keys.index(last_menu_item)
|
||||
# current_mi_pos = mh_keys.index(key)
|
||||
# if current_mi_pos > -1 and last_mi_pos > -1:
|
||||
# print "SHEKEL", current_mi_pos, last_mi_pos, current_mi_pos < last_mi_pos
|
||||
|
||||
# only consider items in menu history that have an older timestamp than the current
|
||||
# for key_, last_visit in sorted(mh.items(), key=operator.itemgetter(1),
|
||||
# reverse=True):
|
||||
# if last_visit < current_last_visit:
|
||||
# fallbacks.append(key_)
|
||||
#
|
||||
# for key_ in fallbacks:
|
||||
# # old data structure
|
||||
# if not len(key_) == 3 or not (isinstance(key_[1], tuple) and isinstance(key_[2], tuple)):
|
||||
# continue
|
||||
#
|
||||
# old_f, old_a, old_kw = key_
|
||||
# if old_f == "ValidatePrefs":
|
||||
# continue
|
||||
#
|
||||
# possible_fallback = ROUTE_REGISTRY[old_f]
|
||||
#
|
||||
# # non-debounced function found
|
||||
# if not getattr(possible_fallback, "debounce", False):
|
||||
# ret_kw = dict(old_kw)
|
||||
# ret_a = old_a
|
||||
# if "randomize" in ret_kw:
|
||||
# ret_kw["randomize"] = timestamp()
|
||||
#
|
||||
# ret_f = possible_fallback
|
||||
# key = get_lookup_key(ret_f, list(ret_a), ret_kw)
|
||||
# fallback_found = True
|
||||
#
|
||||
# Log.Debug("not triggering %s twice with %s, %s, returning to %s, %s, %s" %
|
||||
# (f.__name__, a, kw, ret_f.__name__, ret_a, ret_kw))
|
||||
#
|
||||
# break
|
||||
#
|
||||
# if not fallback_found:
|
||||
# Log.Debug("No fallback found in menu history for %s, falling back to main menu", f)
|
||||
# return main_menu_fallback()
|
||||
|
||||
# if not fallback_needed:
|
||||
# add_to_menu_history(key)
|
||||
# if ret_f.__name__ != "ValidatePrefs":
|
||||
# Dict["last_menu_item"] = key
|
||||
#
|
||||
add_to_menu_history(key)
|
||||
Dict["last_menu_item"] = key
|
||||
return ret_f(*ret_a, **ret_kw)
|
||||
|
||||
# @route may be used multiple times
|
||||
if not already_wrapped:
|
||||
inner.orig_f = f
|
||||
|
||||
return enable_channel_wrapper(route(*args, **kwargs))(inner)
|
||||
return enable_channel_wrapper(route(*args, **kwargs))(f)
|
||||
|
||||
return wrap
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# coding=utf-8
|
||||
import os
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
from subzero.language import Language
|
||||
|
||||
@@ -12,19 +10,20 @@ from menu_helpers import debounce, SubFolderObjectContainer, default_thumb, add_
|
||||
from refresh_item import RefreshItem
|
||||
from subzero.constants import PREFIX
|
||||
from support.config import config, TEXT_SUBTITLE_EXTS
|
||||
from support.helpers import timestamp, df, get_language, display_language, quote_args, get_language_from_stream
|
||||
from support.helpers import timestamp, df, get_language, display_language, get_language_from_stream
|
||||
from support.items import get_item_kind_from_rating_key, get_item, get_current_sub, get_item_title, save_stored_sub
|
||||
from support.plex_media import get_plex_metadata, get_part, get_embedded_subtitle_streams
|
||||
from support.scanning import scan_videos
|
||||
from support.scheduler import scheduler
|
||||
from support.storage import get_subtitle_storage
|
||||
from support.i18n import _
|
||||
|
||||
|
||||
# fixme: needs kwargs cleanup
|
||||
|
||||
@route(PREFIX + '/item/{rating_key}/actions')
|
||||
@debounce
|
||||
def ItemDetailsMenu(rating_key, title=None, base_title=None, item_title=None, randomize=None, header=None):
|
||||
def ItemDetailsMenu(rating_key, title=None, base_title=None, item_title=None, randomize=None, header=None,
|
||||
message=None):
|
||||
"""
|
||||
displays the item details menu of an item that doesn't contain any deeper tree, such as a movie or an episode
|
||||
:param rating_key:
|
||||
@@ -42,14 +41,23 @@ def ItemDetailsMenu(rating_key, title=None, base_title=None, item_title=None, ra
|
||||
|
||||
timeout = 30
|
||||
|
||||
oc = SubFolderObjectContainer(title2=title, replace_parent=True, header=header)
|
||||
oc = SubFolderObjectContainer(
|
||||
title2=title,
|
||||
replace_parent=True,
|
||||
header=header,
|
||||
message=message)
|
||||
|
||||
if not item:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ItemDetailsMenu, rating_key=rating_key, title=title, base_title=base_title,
|
||||
item_title=item_title, randomize=timestamp()),
|
||||
title=u"Item not found: %s!" % item_title,
|
||||
summary="Plex didn't return any information about the item, please refresh it and come back later",
|
||||
key=Callback(
|
||||
ItemDetailsMenu,
|
||||
rating_key=rating_key,
|
||||
title=title,
|
||||
base_title=base_title,
|
||||
item_title=item_title,
|
||||
randomize=timestamp()),
|
||||
title=_(u"Item not found: %s!", item_title),
|
||||
summary=_("Plex didn't return any information about the item, please refresh it and come back later"),
|
||||
thumb=default_thumb
|
||||
))
|
||||
return oc
|
||||
@@ -61,26 +69,37 @@ def ItemDetailsMenu(rating_key, title=None, base_title=None, item_title=None, ra
|
||||
season = get_item(item.season.rating_key)
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(MetadataMenu, rating_key=season.rating_key, title=season.title, base_title=show.title,
|
||||
previous_item_type="show", previous_rating_key=show.rating_key,
|
||||
display_items=True, randomize=timestamp()),
|
||||
title=u"< Back to %s" % season.title,
|
||||
summary="Back to %s > %s" % (show.title, season.title),
|
||||
key=Callback(
|
||||
MetadataMenu,
|
||||
rating_key=season.rating_key,
|
||||
title=season.title,
|
||||
base_title=show.title,
|
||||
previous_item_type="show",
|
||||
previous_rating_key=show.rating_key,
|
||||
display_items=True,
|
||||
randomize=timestamp()),
|
||||
title=_(u"< Back to %s", season.title),
|
||||
summary=_("Back to %s > %s", show.title, season.title),
|
||||
thumb=season.thumb or default_thumb
|
||||
))
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(RefreshItem, rating_key=rating_key, item_title=item_title, randomize=timestamp(),
|
||||
timeout=timeout * 1000),
|
||||
title=u"Refresh: %s" % item_title,
|
||||
summary="Refreshes the %s, possibly searching for missing and picking up new subtitles on disk" % current_kind,
|
||||
key=Callback(
|
||||
RefreshItem,
|
||||
rating_key=rating_key,
|
||||
item_title=item_title,
|
||||
randomize=timestamp(),
|
||||
timeout=timeout * 1000),
|
||||
title=_(u"Refresh: %s", item_title),
|
||||
summary=_("Refreshes %(the_movie_series_season_episode)s, possibly searching for missing and picking up "
|
||||
"new subtitles on disk", the_movie_series_season_episode=_(u"the %s" % current_kind)),
|
||||
thumb=item.thumb or default_thumb
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(RefreshItem, rating_key=rating_key, item_title=item_title, force=True, randomize=timestamp(),
|
||||
timeout=timeout * 1000),
|
||||
title=u"Force-find subtitles: %s" % item_title,
|
||||
summary="Issues a forced refresh, ignoring known subtitles and searching for new ones",
|
||||
title=_(u"Force-find subtitles: %(item_title)s", item_title=item_title),
|
||||
summary=_("Issues a forced refresh, ignoring known subtitles and searching for new ones"),
|
||||
thumb=item.thumb or default_thumb
|
||||
))
|
||||
|
||||
@@ -100,11 +119,11 @@ def ItemDetailsMenu(rating_key, title=None, base_title=None, item_title=None, ra
|
||||
part_id = str(part.id)
|
||||
part_index += 1
|
||||
|
||||
part_index_addon = ""
|
||||
part_summary_addon = ""
|
||||
part_index_addon = u""
|
||||
part_summary_addon = u""
|
||||
if has_multiple_parts:
|
||||
part_index_addon = u"File %s: " % part_index
|
||||
part_summary_addon = "%s " % filename
|
||||
part_index_addon = _(u"File %(file_part_index)s: ", file_part_index=part_index)
|
||||
part_summary_addon = u"%s " % filename
|
||||
|
||||
# iterate through all configured languages
|
||||
for lang in config.lang_list:
|
||||
@@ -113,17 +132,22 @@ def ItemDetailsMenu(rating_key, title=None, base_title=None, item_title=None, ra
|
||||
current_sub_id = None
|
||||
current_sub_provider_name = None
|
||||
|
||||
summary = u"%sNo current subtitle in storage" % part_summary_addon
|
||||
summary = _(u"%(part_summary)sNo current subtitle in storage", part_summary=part_summary_addon)
|
||||
current_score = None
|
||||
if current_sub:
|
||||
current_sub_id = current_sub.id
|
||||
current_sub_provider_name = current_sub.provider_name
|
||||
current_score = current_sub.score
|
||||
|
||||
summary = u"%sCurrent subtitle: %s (added: %s, %s), Language: %s, Score: %i, Storage: %s" % \
|
||||
(part_summary_addon, current_sub.provider_name, df(current_sub.date_added),
|
||||
current_sub.mode_verbose, display_language(lang), current_sub.score,
|
||||
current_sub.storage_type)
|
||||
summary = _(u"%(part_summary)sCurrent subtitle: %(provider_name)s (added: %(date_added)s, "
|
||||
u"%(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s",
|
||||
part_summary=part_summary_addon,
|
||||
provider_name=current_sub.provider_name,
|
||||
date_added=df(current_sub.date_added),
|
||||
mode=current_sub.mode_verbose,
|
||||
language=display_language(lang),
|
||||
score=current_sub.score,
|
||||
storage_type=current_sub.storage_type)
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleOptionsMenu, rating_key=rating_key, part_id=part_id, title=title,
|
||||
@@ -132,7 +156,8 @@ def ItemDetailsMenu(rating_key, title=None, base_title=None, item_title=None, ra
|
||||
item_type=plex_item.type, filename=filename, current_data=summary,
|
||||
randomize=timestamp(), current_provider=current_sub_provider_name,
|
||||
current_score=current_score),
|
||||
title=u"%sManage %s subtitle" % (part_index_addon, display_language(lang)),
|
||||
title=_(u"%(part_summary)sManage %(language)s subtitle", part_summary=part_index_addon,
|
||||
language=display_language(lang)),
|
||||
summary=summary
|
||||
))
|
||||
else:
|
||||
@@ -143,7 +168,8 @@ def ItemDetailsMenu(rating_key, title=None, base_title=None, item_title=None, ra
|
||||
item_type=plex_item.type, filename=filename, current_data=summary,
|
||||
randomize=timestamp(), current_provider=current_sub_provider_name,
|
||||
current_score=current_score),
|
||||
title=u"%sList %s subtitles" % (part_index_addon, display_language(lang)),
|
||||
title=_(u"%(part_summary)sList %(language)s subtitles", part_summary=part_index_addon,
|
||||
language=display_language(lang)),
|
||||
summary=summary
|
||||
))
|
||||
|
||||
@@ -168,9 +194,10 @@ def ItemDetailsMenu(rating_key, title=None, base_title=None, item_title=None, ra
|
||||
key=Callback(ListEmbeddedSubsForItemMenu, rating_key=rating_key, part_id=part_id, title=title,
|
||||
item_type=plex_item.type, item_title=item_title, base_title=base_title,
|
||||
randomize=timestamp()),
|
||||
title=u"%sEmbedded subtitles (%s)" % (part_index_addon, ", ".join(display_language(l) for l in
|
||||
set(embedded_langs))),
|
||||
summary=u"Extract and activate embedded subtitle streams"
|
||||
title=_(u"%(part_summary)sEmbedded subtitles (%(languages)s)",
|
||||
part_summary=part_index_addon,
|
||||
languages=", ".join(display_language(l) for l in set(embedded_langs))),
|
||||
summary=_(u"Extract and activate embedded subtitle streams")
|
||||
))
|
||||
|
||||
ignore_title = item_title
|
||||
@@ -189,7 +216,7 @@ def SubtitleOptionsMenu(**kwargs):
|
||||
rating_key = kwargs["rating_key"]
|
||||
part_id = kwargs["part_id"]
|
||||
language = kwargs["language"]
|
||||
current_data = kwargs["current_data"]
|
||||
current_data = unicode(kwargs["current_data"])
|
||||
|
||||
current_sub, stored_subs, storage = get_current_sub(rating_key, part_id, language)
|
||||
subs_count = stored_subs.count(part_id, language)
|
||||
@@ -198,33 +225,35 @@ def SubtitleOptionsMenu(**kwargs):
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ItemDetailsMenu, rating_key=kwargs["rating_key"], item_title=kwargs["item_title"],
|
||||
title=kwargs["title"], randomize=timestamp()),
|
||||
title=u"< Back to %s" % kwargs["title"],
|
||||
summary=kwargs["current_data"],
|
||||
title=_(u"< Back to %s", kwargs["title"]),
|
||||
summary=current_data,
|
||||
thumb=default_thumb
|
||||
))
|
||||
if subs_count:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ListStoredSubsForItemMenu, randomize=timestamp(), **kwargs),
|
||||
title=u"Select active %s subtitle" % kwargs["language_name"],
|
||||
summary=u"%d subtitles in storage" % subs_count
|
||||
title=_(u"Select active %(language)s subtitle", language=kwargs["language_name"]),
|
||||
summary=_(u"%(count)d subtitles in storage", count=subs_count)
|
||||
))
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ListAvailableSubsForItemMenu, randomize=timestamp(), **kwargs),
|
||||
title=u"List available %s subtitles" % kwargs["language_name"],
|
||||
summary=kwargs["current_data"]
|
||||
title=_(u"List available %(language)s subtitles", language=kwargs["language_name"]),
|
||||
summary=current_data
|
||||
))
|
||||
if current_sub:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleModificationsMenu, randomize=timestamp(), **kwargs),
|
||||
title=u"Modify current %s subtitle" % kwargs["language_name"],
|
||||
summary=u"Currently applied mods: %s" % (", ".join(current_sub.mods) if current_sub.mods else "none")
|
||||
title=_(u"Modify current %(language)s subtitle", language=kwargs["language_name"]),
|
||||
summary=_(u"Currently applied mods: %(mod_list)s",
|
||||
mod_list=(", ".join(current_sub.mods) if current_sub.mods else "none"))
|
||||
))
|
||||
|
||||
if current_sub.provider_name != "embedded":
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(BlacklistSubtitleMenu, randomize=timestamp(), **kwargs),
|
||||
title=u"Blacklist current %s subtitle and search for a new one" % kwargs["language_name"],
|
||||
title=_(u"Blacklist current %(language)s subtitle and search for a new one",
|
||||
language=kwargs["language_name"]),
|
||||
summary=current_data
|
||||
))
|
||||
|
||||
@@ -232,8 +261,8 @@ def SubtitleOptionsMenu(**kwargs):
|
||||
if current_bl:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ManageBlacklistMenu, randomize=timestamp(), **kwargs),
|
||||
title=u"Manage blacklist (%s contained)" % len(current_bl),
|
||||
summary=u"Inspect currently blacklisted subtitles"
|
||||
title=_(u"Manage blacklist (%(amount)s contained)", amount=len(current_bl)),
|
||||
summary=_(u"Inspect currently blacklisted subtitles")
|
||||
))
|
||||
|
||||
storage.destroy()
|
||||
@@ -251,14 +280,17 @@ def ListStoredSubsForItemMenu(**kwargs):
|
||||
all_subs = stored_subs.get_all(part_id, language)
|
||||
kwargs.pop("randomize")
|
||||
|
||||
for key, subtitle in sorted(filter(lambda x: x[0] != "current", all_subs.items()),
|
||||
for key, subtitle in sorted(filter(lambda x: x[0] not in ("current", "blacklist"), all_subs.items()),
|
||||
key=lambda x: x[1].date_added, reverse=True):
|
||||
is_current = key == all_subs["current"]
|
||||
|
||||
summary = u"added: %s, %s, Language: %s, Score: %i, Storage: %s" % \
|
||||
(df(subtitle.date_added),
|
||||
subtitle.mode_verbose, display_language(language), subtitle.score,
|
||||
subtitle.storage_type)
|
||||
summary = _(u"added: %(date_added)s, %(mode)s, Language: %(language)s, Score: %(score)i, Storage: "
|
||||
u"%(storage_type)s",
|
||||
date_added=df(subtitle.date_added),
|
||||
mode=subtitle.mode_verbose,
|
||||
language=display_language(language),
|
||||
score=subtitle.score,
|
||||
storage_type=subtitle.storage_type)
|
||||
|
||||
sub_name = subtitle.provider_name
|
||||
if sub_name == "embedded":
|
||||
@@ -266,8 +298,10 @@ def ListStoredSubsForItemMenu(**kwargs):
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SelectStoredSubForItemMenu, randomize=timestamp(), sub_key="__".join(key), **kwargs),
|
||||
title=u"%s%s, Score: %s" % ("Current: " if is_current else "Stored: ", sub_name,
|
||||
subtitle.score),
|
||||
title=_(u"%(current_state)s%(subtitle_name)s, Score: %(score)s",
|
||||
current_state=_("Current: ") if is_current else _("Stored: "),
|
||||
subtitle_name=sub_name,
|
||||
score=subtitle.score),
|
||||
summary=summary
|
||||
))
|
||||
|
||||
@@ -299,8 +333,8 @@ def SelectStoredSubForItemMenu(**kwargs):
|
||||
|
||||
kwargs.pop("randomize")
|
||||
|
||||
kwargs["header"] = 'Success'
|
||||
kwargs["message"] = 'Subtitle saved to disk'
|
||||
kwargs["header"] = _("Success")
|
||||
kwargs["message"] = _("Subtitle saved to disk")
|
||||
|
||||
return SubtitleOptionsMenu(randomize=timestamp(), **kwargs)
|
||||
|
||||
@@ -389,6 +423,7 @@ def ManageBlacklistMenu(**kwargs):
|
||||
part_id = kwargs["part_id"]
|
||||
language = kwargs["language"]
|
||||
remove_sub_key = kwargs.pop("remove_sub_key", None)
|
||||
current_data = unicode(kwargs["current_data"])
|
||||
|
||||
current_sub, stored_subs, storage = get_current_sub(rating_key, part_id, language)
|
||||
current_bl, subs = stored_subs.get_blacklist(part_id, language)
|
||||
@@ -404,8 +439,8 @@ def ManageBlacklistMenu(**kwargs):
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ItemDetailsMenu, rating_key=kwargs["rating_key"], item_title=kwargs["item_title"],
|
||||
title=kwargs["title"], randomize=timestamp()),
|
||||
title=u"< Back to %s" % kwargs["title"],
|
||||
summary=kwargs["current_data"],
|
||||
title=_(u"< Back to %s", kwargs["title"]),
|
||||
summary=current_data,
|
||||
thumb=default_thumb
|
||||
))
|
||||
|
||||
@@ -415,15 +450,19 @@ def ManageBlacklistMenu(**kwargs):
|
||||
|
||||
for sub_key, data in sorted(current_bl.iteritems(), key=sorter, reverse=True):
|
||||
provider_name, subtitle_id = sub_key
|
||||
title = u"%s, %s (added: %s, %s), Language: " \
|
||||
u"%s, Score: %i, Storage: %s" % (provider_name, subtitle_id, df(data["date_added"]),
|
||||
current_sub.get_mode_verbose(data["mode"]),
|
||||
display_language(Language.fromietf(language)), data["score"],
|
||||
data["storage_type"])
|
||||
title = _(u"%(provider_name)s, %(subtitle_id)s (added: %(date_added)s, %(mode)s), Language: %(language)s, "
|
||||
u"Score: %(score)i, Storage: %(storage_type)s",
|
||||
provider_name=provider_name,
|
||||
subtitle_id=subtitle_id,
|
||||
date_added=df(data["date_added"]),
|
||||
mode=current_sub.get_mode_verbose(data["mode"]),
|
||||
language=display_language(Language.fromietf(language)),
|
||||
score=data["score"],
|
||||
storage_type=data["storage_type"])
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ManageBlacklistMenu, remove_sub_key="__".join(sub_key), randomize=timestamp(), **kwargs),
|
||||
title=title,
|
||||
summary=u"Remove subtitle from blacklist"
|
||||
summary=_(u"Remove subtitle from blacklist")
|
||||
))
|
||||
|
||||
storage.destroy()
|
||||
@@ -432,7 +471,6 @@ def ManageBlacklistMenu(**kwargs):
|
||||
|
||||
|
||||
@route(PREFIX + '/item/search/{rating_key}/{part_id}', force=bool)
|
||||
@debounce
|
||||
def ListAvailableSubsForItemMenu(rating_key=None, part_id=None, title=None, item_title=None, filename=None,
|
||||
item_type="episode", language=None, language_name=None, force=False, current_id=None,
|
||||
current_data=None,
|
||||
@@ -442,6 +480,8 @@ def ListAvailableSubsForItemMenu(rating_key=None, part_id=None, title=None, item
|
||||
running = scheduler.is_task_running("AvailableSubsForItem")
|
||||
search_results = get_item_task_data("AvailableSubsForItem", rating_key, language)
|
||||
|
||||
current_data = unicode(current_data) if current_data else None
|
||||
|
||||
if (search_results is None or force) and not running:
|
||||
scheduler.dispatch_task("AvailableSubsForItem", rating_key=rating_key, item_type=item_type, part_id=part_id,
|
||||
language=language)
|
||||
@@ -450,7 +490,7 @@ def ListAvailableSubsForItemMenu(rating_key=None, part_id=None, title=None, item
|
||||
oc = SubFolderObjectContainer(title2=unicode(title), replace_parent=True)
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ItemDetailsMenu, rating_key=rating_key, item_title=item_title, title=title, randomize=timestamp()),
|
||||
title=u"< Back to %s" % title,
|
||||
title=_(u"< Back to %s", title),
|
||||
summary=current_data,
|
||||
thumb=default_thumb
|
||||
))
|
||||
@@ -468,20 +508,24 @@ def ListAvailableSubsForItemMenu(rating_key=None, part_id=None, title=None, item
|
||||
|
||||
video_display_data = [video.format] if video.format else []
|
||||
if video.release_group:
|
||||
video_display_data.append(u"by %s" % video.release_group)
|
||||
video_display_data.append(unicode(_(u"by %(release_group)s", release_group=video.release_group)))
|
||||
video_display_data = " ".join(video_display_data)
|
||||
else:
|
||||
video_display_data = metadata["filename"]
|
||||
|
||||
current_display = (u"Current: %s (%s) " % (current_provider, current_score) if current_provider else "")
|
||||
current_display = (_(u"Current: %(provider_name)s (%(score)s) ",
|
||||
provider_name=current_provider,
|
||||
score=current_score if current_provider else ""))
|
||||
if not running:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ListAvailableSubsForItemMenu, rating_key=rating_key, item_title=item_title, language=language,
|
||||
filename=filename, part_id=part_id, title=title, current_id=current_id, force=True,
|
||||
current_provider=current_provider, current_score=current_score,
|
||||
current_data=current_data, item_type=item_type, randomize=timestamp()),
|
||||
title=u"Search for %s subs (%s)" % (get_language(language).name, video_display_data),
|
||||
summary=u"%sFilename: %s" % (current_display, filename),
|
||||
title=_(u"Search for %(language)s subs (%(video_data)s)",
|
||||
language=get_language(language).name,
|
||||
video_data=video_display_data),
|
||||
summary=_(u"%(current_info)sFilename: %(filename)s", current_info=current_display, filename=filename),
|
||||
thumb=default_thumb
|
||||
))
|
||||
|
||||
@@ -492,8 +536,8 @@ def ListAvailableSubsForItemMenu(rating_key=None, part_id=None, title=None, item
|
||||
part_id=part_id, title=title, current_id=current_id, item_type=item_type,
|
||||
current_provider=current_provider, current_score=current_score,
|
||||
randomize=timestamp()),
|
||||
title=u"No subtitles found",
|
||||
summary=u"%sFilename: %s" % (current_display, filename),
|
||||
title=_(u"No subtitles found"),
|
||||
summary=_(u"%(current_info)sFilename: %(filename)s", current_info=current_display, filename=filename),
|
||||
thumb=default_thumb
|
||||
))
|
||||
else:
|
||||
@@ -503,9 +547,10 @@ def ListAvailableSubsForItemMenu(rating_key=None, part_id=None, title=None, item
|
||||
part_id=part_id, title=title, current_id=current_id, item_type=item_type,
|
||||
current_provider=current_provider, current_score=current_score,
|
||||
randomize=timestamp()),
|
||||
title=u"Searching for %s subs (%s), refresh here ..." % (display_language(get_language(language)),
|
||||
video_display_data),
|
||||
summary=u"%sFilename: %s" % (current_display, filename),
|
||||
title=_(u"Searching for %(language)s subs (%(video_data)s), refresh here ...",
|
||||
language=display_language(get_language(language)),
|
||||
video_data=video_display_data),
|
||||
summary=_(u"%(current_info)sFilename: %(filename)s", current_info=current_display, filename=filename),
|
||||
thumb=default_thumb
|
||||
))
|
||||
|
||||
@@ -527,16 +572,25 @@ def ListAvailableSubsForItemMenu(rating_key=None, part_id=None, title=None, item
|
||||
wrong_fps_addon = ""
|
||||
if subtitle.wrong_fps:
|
||||
if plex_part:
|
||||
wrong_fps_addon = " (wrong FPS, sub: %s, media: %s)" % (subtitle.fps, plex_part.fps)
|
||||
wrong_fps_addon = _(" (wrong FPS, sub: %(subtitle_fps)s, media: %(media_fps)s)",
|
||||
subtitle_fps=subtitle.fps,
|
||||
media_fps=plex_part.fps)
|
||||
else:
|
||||
wrong_fps_addon = " (wrong FPS, sub: %s, media: unknown, low impact mode)" % subtitle.fps
|
||||
wrong_fps_addon = _(" (wrong FPS, sub: %(subtitle_fps)s, media: unknown, low impact mode)",
|
||||
subtitle_fps=subtitle.fps)
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(TriggerDownloadSubtitle, rating_key=rating_key, randomize=timestamp(), item_title=item_title,
|
||||
subtitle_id=str(subtitle.id), language=language),
|
||||
title=u"%s%s: %s, score: %s%s" % (bl_addon, "Available" if current_id != subtitle.id else "Current",
|
||||
subtitle.provider_name, subtitle.score, wrong_fps_addon),
|
||||
summary=u"Release: %s, Matches: %s" % (subtitle.release_info, ", ".join(subtitle.matches)),
|
||||
title=_(u"%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s",
|
||||
blacklisted_state=bl_addon,
|
||||
current_state=_("Available") if current_id != subtitle.id else _("Current"),
|
||||
provider_name=subtitle.provider_name,
|
||||
score=subtitle.score,
|
||||
wrong_fps_state=wrong_fps_addon),
|
||||
summary=_(u"Release: %(release_info)s, Matches: %(matches)s",
|
||||
release_info=subtitle.release_info,
|
||||
matches=", ".join(subtitle.matches)),
|
||||
thumb=default_thumb
|
||||
))
|
||||
|
||||
@@ -550,7 +604,7 @@ def ListAvailableSubsForItemMenu(rating_key=None, part_id=None, title=None, item
|
||||
def TriggerDownloadSubtitle(rating_key=None, subtitle_id=None, item_title=None, language=None, randomize=None):
|
||||
from interface.main import fatality
|
||||
|
||||
set_refresh_menu_state("Downloading subtitle for %s" % item_title or rating_key)
|
||||
set_refresh_menu_state(_("Downloading subtitle for %(title_or_id)s", title_or_id=item_title or rating_key))
|
||||
search_results = get_item_task_data("AvailableSubsForItem", rating_key, language)
|
||||
|
||||
download_subtitle = None
|
||||
@@ -581,7 +635,7 @@ def ListEmbeddedSubsForItemMenu(**kwargs):
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ItemDetailsMenu, rating_key=kwargs["rating_key"], item_title=kwargs["item_title"],
|
||||
base_title=kwargs["base_title"], title=kwargs["item_title"], randomize=timestamp()),
|
||||
title=u"< Back to %s" % kwargs["title"],
|
||||
title=_("< Back to %s", kwargs["title"]),
|
||||
thumb=default_thumb
|
||||
))
|
||||
|
||||
@@ -593,25 +647,30 @@ def ListEmbeddedSubsForItemMenu(**kwargs):
|
||||
language = stream_data["language"]
|
||||
is_unknown = stream_data["is_unknown"]
|
||||
stream = stream_data["stream"]
|
||||
is_forced = stream_data["is_forced"]
|
||||
|
||||
if language:
|
||||
forced = stream.forced
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(TriggerExtractEmbeddedSubForItemMenu, randomize=timestamp(),
|
||||
stream_index=str(stream.index), language=language, with_mods=True, **kwargs),
|
||||
title=u"Extract stream %s, "
|
||||
u"%s%s%s%s with default mods" % (stream.index, display_language(language),
|
||||
" (unknown)" if is_unknown else "",
|
||||
" (forced)" if forced else "",
|
||||
" (\"%s\")" % stream.title if stream.title else ""),
|
||||
title=_(u"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s"
|
||||
u"%(stream_title)s with default mods",
|
||||
stream_index=stream.index,
|
||||
language=display_language(language),
|
||||
unknown_state=_(" (unknown)") if is_unknown else "",
|
||||
forced_state=_(" (forced)") if is_forced else "",
|
||||
stream_title=" (\"%s\")" % stream.title if stream.title else ""),
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(TriggerExtractEmbeddedSubForItemMenu, randomize=timestamp(),
|
||||
stream_index=str(stream.index), language=language, **kwargs),
|
||||
title=u"Extract stream %s, %s%s%s%s" % (stream.index, display_language(language),
|
||||
" (unknown)" if is_unknown else "",
|
||||
" (forced)" if forced else "",
|
||||
" (\"%s\")" % stream.title if stream.title else ""),
|
||||
title=_(u"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s"
|
||||
u"%(stream_title)s",
|
||||
stream_index=stream.index,
|
||||
language=display_language(language),
|
||||
unknown_state=_(" (unknown)") if is_unknown else "",
|
||||
forced_state=_(" (forced)") if is_forced else "",
|
||||
stream_title=" (\"%s\")" % stream.title if stream.title else ""),
|
||||
))
|
||||
return oc
|
||||
|
||||
@@ -624,7 +683,8 @@ def TriggerExtractEmbeddedSubForItemMenu(**kwargs):
|
||||
stream_index = kwargs.get("stream_index")
|
||||
|
||||
Thread.Create(extract_embedded_sub, **kwargs)
|
||||
header = u"Extracting of embedded subtitle %s of part %s:%s triggered" % (stream_index, rating_key, part_id)
|
||||
header = _(u"Extracting of embedded subtitle %s of part %s:%s triggered",
|
||||
stream_index, rating_key, part_id)
|
||||
|
||||
kwargs.pop("randomize")
|
||||
kwargs.pop("item_type")
|
||||
@@ -634,6 +694,7 @@ def TriggerExtractEmbeddedSubForItemMenu(**kwargs):
|
||||
kwargs.pop("language")
|
||||
kwargs["title"] = kwargs["item_title"]
|
||||
kwargs["header"] = header
|
||||
kwargs["message"] = header
|
||||
|
||||
return ItemDetailsMenu(randomize=timestamp(), **kwargs)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from support.ignore import ignore_list
|
||||
from support.items import get_item_thumb, get_on_deck_items, get_all_items, get_items_info, get_item, get_item_title
|
||||
from menu_helpers import main_icon, debounce, SubFolderObjectContainer, default_thumb, dig_tree, add_ignore_options, \
|
||||
ObjectContainer, route, handler
|
||||
from support.i18n import _
|
||||
from item_details import ItemDetailsMenu
|
||||
|
||||
|
||||
@@ -35,25 +36,34 @@ def fatality(randomize=None, force_title=None, header=None, message=None, only_r
|
||||
if config.lock_menu and not config.pin_correct:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(PinMenu, randomize=timestamp()),
|
||||
title=pad_title("Enter PIN"),
|
||||
summary="The owner has restricted the access to this menu. Please enter the correct pin",
|
||||
title=pad_title(_("Enter PIN")),
|
||||
summary=_("The owner has restricted the access to this menu. Please enter the correct pin"),
|
||||
))
|
||||
return oc
|
||||
|
||||
if not config.permissions_ok and config.missing_permissions:
|
||||
for title, path in config.missing_permissions:
|
||||
if not isinstance(config.missing_permissions, list):
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, randomize=timestamp()),
|
||||
title=pad_title("Insufficient permissions"),
|
||||
summary="Insufficient permissions on library %s, folder: %s" % (title, path),
|
||||
title=pad_title(_("Insufficient permissions")),
|
||||
summary=config.missing_permissions,
|
||||
))
|
||||
else:
|
||||
for title, path in config.missing_permissions:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, randomize=timestamp()),
|
||||
title=pad_title(_("Insufficient permissions")),
|
||||
summary=_("Insufficient permissions on library %(title)s, folder: %(path)s",
|
||||
title=title,
|
||||
path=path),
|
||||
))
|
||||
return oc
|
||||
|
||||
if not config.enabled_sections:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, randomize=timestamp()),
|
||||
title=pad_title("I'm not enabled!"),
|
||||
summary="Please enable me for some of your libraries in your server settings; currently I do nothing",
|
||||
title=pad_title(_("I'm not enabled!")),
|
||||
summary=_("Please enable me for some of your libraries in your server settings; currently I do nothing"),
|
||||
))
|
||||
return oc
|
||||
|
||||
@@ -61,46 +71,42 @@ def fatality(randomize=None, force_title=None, header=None, message=None, only_r
|
||||
if Dict["current_refresh_state"]:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, force_title=" ", randomize=timestamp()),
|
||||
title=pad_title("Working ... refresh here"),
|
||||
summary="Current state: %s; Last state: %s" % (
|
||||
(Dict["current_refresh_state"] or "Idle") if "current_refresh_state" in Dict else "Idle",
|
||||
(Dict["last_refresh_state"] or "None") if "last_refresh_state" in Dict else "None"
|
||||
title=pad_title(_("Working ... refresh here")),
|
||||
summary=_("Current state: %s; Last state: %s",
|
||||
(Dict["current_refresh_state"] or _("Idle")) if "current_refresh_state" in Dict else _("Idle"),
|
||||
(Dict["last_refresh_state"] or _("None")) if "last_refresh_state" in Dict else _("None")
|
||||
)
|
||||
))
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(OnDeckMenu),
|
||||
title="On-deck items",
|
||||
summary="Shows the current on deck items and allows you to individually (force-) refresh their metadata/"
|
||||
"subtitles.",
|
||||
title=_("On-deck items"),
|
||||
summary=_("Shows the current on deck items and allows you to individually (force-) refresh their metadata/subtitles."),
|
||||
thumb=R("icon-ondeck.jpg")
|
||||
))
|
||||
if "last_played_items" in Dict and Dict["last_played_items"]:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(RecentlyPlayedMenu),
|
||||
title=pad_title("Recently played items"),
|
||||
summary="Shows the %i recently played items and allows you to individually (force-) refresh their "
|
||||
"metadata/subtitles." % config.store_recently_played_amount,
|
||||
title=pad_title(_("Recently played items")),
|
||||
summary=_("Shows the %s recently played items and allows you to individually (force-) refresh their metadata/subtitles.", config.store_recently_played_amount),
|
||||
thumb=R("icon-played.jpg")
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(RecentlyAddedMenu),
|
||||
title="Recently-added items",
|
||||
summary="Shows the recently added items per section.",
|
||||
title=_("Recently-added items"),
|
||||
summary=_("Shows the recently added items per section."),
|
||||
thumb=R("icon-added.jpg")
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(RecentMissingSubtitlesMenu, randomize=timestamp()),
|
||||
title="Show recently added items with missing subtitles",
|
||||
summary="Lists items with missing subtitles. Click on \"Find recent items with missing subs\" "
|
||||
"to update list",
|
||||
title=_("Show recently added items with missing subtitles"),
|
||||
summary=_("Lists items with missing subtitles. Click on Find recent items with missing subs to update list"),
|
||||
thumb=R("icon-missing.jpg")
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SectionsMenu),
|
||||
title="Browse all items",
|
||||
summary="Go through your whole library and manage your ignore list. You can also "
|
||||
"(force-) refresh the metadata/subtitles of individual items.",
|
||||
title=_("Browse all items"),
|
||||
summary=_("Go through your whole library and manage your ignore list. You can also (force-) refresh the metadata/subtitles of individual items."),
|
||||
thumb=R("icon-browse.jpg")
|
||||
))
|
||||
|
||||
@@ -108,43 +114,46 @@ def fatality(randomize=None, force_title=None, header=None, message=None, only_r
|
||||
task = scheduler.task(task_name)
|
||||
|
||||
if task.ready_for_display:
|
||||
task_state = "Running: %s/%s (%s%%)" % (task.items_done, task.items_searching, task.percentage)
|
||||
task_state = _("Running: %(items_done)s/%(items_searching)s (%(percentage)s%%)",
|
||||
items_done=task.items_done,
|
||||
items_searching=task.items_searching,
|
||||
percentage=task.percentage)
|
||||
else:
|
||||
lr = scheduler.last_run(task_name)
|
||||
nr = scheduler.next_run(task_name)
|
||||
task_state = "Last run: %s; Next scheduled run: %s; Last runtime: %s" % (
|
||||
task_state = _("Last run: %s; Next scheduled run: %s; Last runtime: %s",
|
||||
df(scheduler.last_run(task_name)) if lr else "never",
|
||||
df(scheduler.next_run(task_name)) if nr else "never",
|
||||
str(task.last_run_time).split(".")[0])
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(RefreshMissing, randomize=timestamp()),
|
||||
title="Search for missing subtitles (in recently-added items, max-age: %s)" % Prefs[
|
||||
"scheduler.item_is_recent_age"],
|
||||
summary="Automatically run periodically by the scheduler, if configured. %s" % task_state,
|
||||
title=_("Search for missing subtitles (in recently-added items, max-age: %s)", Prefs[
|
||||
"scheduler.item_is_recent_age"]),
|
||||
summary=_("Automatically run periodically by the scheduler, if configured. %s", task_state),
|
||||
thumb=R("icon-search.jpg")
|
||||
))
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(IgnoreListMenu),
|
||||
title="Display ignore list (%d)" % len(ignore_list),
|
||||
summary="Show the current ignore list (mainly used for the automatic tasks)",
|
||||
title=_("Display ignore list (%(ignored_count)d)", ignored_count=len(ignore_list)),
|
||||
summary=_("Show the current ignore list (mainly used for the automatic tasks)"),
|
||||
thumb=R("icon-ignore.jpg")
|
||||
))
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(HistoryMenu),
|
||||
title="History",
|
||||
summary="Show the last %i downloaded subtitles" % int(Prefs["history_size"]),
|
||||
title=_("History"),
|
||||
summary=_("Show the last %i downloaded subtitles", int(Prefs["history_size"])),
|
||||
thumb=R("icon-history.jpg")
|
||||
))
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, force_title=" ", randomize=timestamp()),
|
||||
title=pad_title("Refresh"),
|
||||
summary="Current state: %s; Last state: %s" % (
|
||||
(Dict["current_refresh_state"] or "Idle") if "current_refresh_state" in Dict else "Idle",
|
||||
(Dict["last_refresh_state"] or "None") if "last_refresh_state" in Dict else "None"
|
||||
title=pad_title(_("Refresh")),
|
||||
summary=_("Current state: %s; Last state: %s",
|
||||
(Dict["current_refresh_state"] or _("Idle")) if "current_refresh_state" in Dict else _("Idle"),
|
||||
(Dict["last_refresh_state"] or _("None")) if "last_refresh_state" in Dict else _("None")
|
||||
),
|
||||
thumb=R("icon-refresh.jpg")
|
||||
))
|
||||
@@ -153,8 +162,8 @@ def fatality(randomize=None, force_title=None, header=None, message=None, only_r
|
||||
if config.pin:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(ClearPin, randomize=timestamp()),
|
||||
title=pad_title("Re-lock menu(s)"),
|
||||
summary="Enabled the PIN again for menu(s)"
|
||||
title=pad_title(_("Re-lock menu(s)")),
|
||||
summary=_("Enabled the PIN again for menu(s)")
|
||||
))
|
||||
|
||||
if not only_refresh:
|
||||
@@ -162,19 +171,22 @@ def fatality(randomize=None, force_title=None, header=None, message=None, only_r
|
||||
summary_data = []
|
||||
for provider, data in Dict["provider_throttle"].iteritems():
|
||||
reason, until, desc = data
|
||||
summary_data.append("%s until %s (%s)" % (provider, until.strftime("%y/%m/%d %H:%M"), reason))
|
||||
summary_data.append(unicode(_("%(throttled_provider)s until %(until_date)s (%(reason)s)",
|
||||
throttled_provider=provider,
|
||||
until_date=until.strftime("%y/%m/%d %H:%M"),
|
||||
reason=reason)))
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(fatality, force_title=" ", randomize=timestamp()),
|
||||
title=pad_title("Throttled providers: %s" % ", ".join(Dict["provider_throttle"].keys())),
|
||||
title=pad_title(_("Throttled providers: %s", ", ".join(Dict["provider_throttle"].keys()))),
|
||||
summary=", ".join(summary_data),
|
||||
thumb=R("icon-throttled.jpg")
|
||||
))
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(AdvancedMenu),
|
||||
title=pad_title("Advanced functions"),
|
||||
summary="Use at your own risk",
|
||||
title=pad_title(_("Advanced functions")),
|
||||
summary=_("Use at your own risk"),
|
||||
thumb=R("icon-advanced.jpg")
|
||||
))
|
||||
|
||||
@@ -188,12 +200,12 @@ def OnDeckMenu(message=None):
|
||||
:param message:
|
||||
:return:
|
||||
"""
|
||||
return mergedItemsMenu(title="Items On Deck", base_title="Items On Deck", itemGetter=get_on_deck_items)
|
||||
return mergedItemsMenu(title=_("Items On Deck"), base_title=_("Items On Deck"), itemGetter=get_on_deck_items)
|
||||
|
||||
|
||||
@route(PREFIX + '/recently_played')
|
||||
def RecentlyPlayedMenu():
|
||||
base_title = "Recently Played"
|
||||
base_title = _("Recently Played")
|
||||
oc = SubFolderObjectContainer(title2=base_title, replace_parent=True)
|
||||
|
||||
for item in [get_item(rating_key) for rating_key in Dict["last_played_items"]]:
|
||||
@@ -221,13 +233,13 @@ def RecentlyAddedMenu(message=None):
|
||||
:param message:
|
||||
:return:
|
||||
"""
|
||||
return SectionsMenu(base_title="Recently added", section_items_key="recently_added", ignore_options=False)
|
||||
return SectionsMenu(base_title=_("Recently added"), section_items_key="recently_added", ignore_options=False)
|
||||
|
||||
|
||||
@route(PREFIX + '/recent', force=bool)
|
||||
@debounce
|
||||
def RecentMissingSubtitlesMenu(force=False, randomize=None):
|
||||
title = "Items with missing subtitles"
|
||||
title = _("Items with missing subtitles")
|
||||
oc = SubFolderObjectContainer(title2=title, no_cache=True, no_history=True)
|
||||
|
||||
running = scheduler.is_task_running("MissingSubtitles")
|
||||
@@ -241,13 +253,13 @@ def RecentMissingSubtitlesMenu(force=False, randomize=None):
|
||||
if not running:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(RecentMissingSubtitlesMenu, force=True, randomize=timestamp()),
|
||||
title=u"Find recent items with missing subtitles",
|
||||
title=_(u"Find recent items with missing subtitles"),
|
||||
thumb=default_thumb
|
||||
))
|
||||
else:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(RecentMissingSubtitlesMenu, force=False, randomize=timestamp()),
|
||||
title=u"Updating, refresh here ...",
|
||||
title=_(u"Updating, refresh here ..."),
|
||||
thumb=default_thumb
|
||||
))
|
||||
|
||||
@@ -257,7 +269,7 @@ def RecentMissingSubtitlesMenu(force=False, randomize=None):
|
||||
key=Callback(ItemDetailsMenu, title=title + " > " + item_title, item_title=item_title,
|
||||
rating_key=item_id),
|
||||
title=item_title,
|
||||
summary="Missing: %s" % ", ".join(display_language(l) for l in missing_languages),
|
||||
summary=_("Missing: %s", ", ".join(display_language(l) for l in missing_languages)),
|
||||
thumb=get_item_thumb(item) or default_thumb
|
||||
))
|
||||
|
||||
@@ -315,18 +327,25 @@ def IgnoreMenu(kind, rating_key, title=None, sure=False, todo="not_set"):
|
||||
"""
|
||||
is_ignored = rating_key in ignore_list[kind]
|
||||
if not sure:
|
||||
oc = SubFolderObjectContainer(no_history=True, replace_parent=True, title1="%s %s %s %s the ignore list" % (
|
||||
"Add" if not is_ignored else "Remove", ignore_list.verbose(kind), title,
|
||||
"to" if not is_ignored else "from"), title2="Are you sure?")
|
||||
t = u"Add %(kind)s %(title)s to the ignore list"
|
||||
if is_ignored:
|
||||
t = u"Remove %(kind)s %(title)s from the ignore list"
|
||||
oc = SubFolderObjectContainer(no_history=True, replace_parent=True,
|
||||
title1=_(t,
|
||||
kind=ignore_list.verbose(kind),
|
||||
title=title
|
||||
),
|
||||
title2=_("Are you sure?"))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(IgnoreMenu, kind=kind, rating_key=rating_key, title=title, sure=True,
|
||||
todo="add" if not is_ignored else "remove"),
|
||||
title=pad_title("Are you sure?"),
|
||||
title=pad_title(_("Are you sure?")),
|
||||
))
|
||||
return oc
|
||||
|
||||
rel = ignore_list[kind]
|
||||
dont_change = False
|
||||
state = None
|
||||
if todo == "remove":
|
||||
if not is_ignored:
|
||||
dont_change = True
|
||||
@@ -335,7 +354,6 @@ def IgnoreMenu(kind, rating_key, title=None, sure=False, todo="not_set"):
|
||||
Log.Info("Removed %s (%s) from the ignore list", title, rating_key)
|
||||
ignore_list.remove_title(kind, rating_key)
|
||||
ignore_list.save()
|
||||
state = "removed from"
|
||||
elif todo == "add":
|
||||
if is_ignored:
|
||||
dont_change = True
|
||||
@@ -344,25 +362,29 @@ def IgnoreMenu(kind, rating_key, title=None, sure=False, todo="not_set"):
|
||||
Log.Info("Added %s (%s) to the ignore list", title, rating_key)
|
||||
ignore_list.add_title(kind, rating_key, title)
|
||||
ignore_list.save()
|
||||
state = "added to"
|
||||
else:
|
||||
dont_change = True
|
||||
|
||||
if dont_change:
|
||||
return fatality(force_title=" ", header="Didn't change the ignore list", no_history=True)
|
||||
return fatality(force_title=" ", header=_("Didn't change the ignore list"), no_history=True)
|
||||
|
||||
return fatality(force_title=" ", header="%s %s the ignore list" % (title, state), no_history=True)
|
||||
t = "%(title)s added to the ignore list"
|
||||
if todo == "remove":
|
||||
t = "%(title)s removed from the ignore list"
|
||||
return fatality(force_title=" ", header=_(t,
|
||||
title=title,),
|
||||
no_history=True)
|
||||
|
||||
|
||||
@route(PREFIX + '/sections')
|
||||
def SectionsMenu(base_title="Sections", section_items_key="all", ignore_options=True):
|
||||
def SectionsMenu(base_title=_("Sections"), section_items_key="all", ignore_options=True):
|
||||
"""
|
||||
displays the menu for all sections
|
||||
:return:
|
||||
"""
|
||||
items = get_all_items("sections")
|
||||
|
||||
return dig_tree(SubFolderObjectContainer(title2="Sections", no_cache=True, no_history=True), items, None,
|
||||
return dig_tree(SubFolderObjectContainer(title2=_("Sections"), no_cache=True, no_history=True), items, None,
|
||||
menu_determination_callback=determine_section_display, pass_kwargs={"base_title": base_title,
|
||||
"section_items_key": section_items_key,
|
||||
"ignore_options": ignore_options},
|
||||
@@ -423,7 +445,7 @@ def SectionFirstLetterMenu(rating_key, title=None, base_title=None, section_titl
|
||||
add_ignore_options(oc, "sections", title=section_title, rating_key=rating_key, callback_menu=IgnoreMenu)
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SectionMenu, title="All", base_title=title, rating_key=rating_key, ignore_options=False),
|
||||
key=Callback(SectionMenu, title=_("All"), base_title=title, rating_key=rating_key, ignore_options=False),
|
||||
title="All"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -24,6 +24,7 @@ from support.helpers import timestamp, df, display_language
|
||||
from support.ignore import ignore_list
|
||||
from support.items import get_all_items, get_items_info, get_item_kind_from_rating_key, get_item, MI_KEY, get_item_title
|
||||
from support.storage import get_subtitle_storage
|
||||
from support.i18n import _
|
||||
|
||||
# init GUI
|
||||
ObjectContainer.art = R(ART)
|
||||
@@ -58,7 +59,7 @@ def FirstLetterMetadataMenu(rating_key, key, title=None, base_title=None, displa
|
||||
|
||||
@route(PREFIX + '/section/contents', display_items=bool)
|
||||
def MetadataMenu(rating_key, title=None, base_title=None, display_items=False, previous_item_type=None,
|
||||
previous_rating_key=None, header=None, randomize=None):
|
||||
previous_rating_key=None, message=None, header=None, randomize=None):
|
||||
"""
|
||||
displays the contents of a section based on whether it has a deeper tree or not (movies->movie (item) list; series->series list)
|
||||
:param rating_key:
|
||||
@@ -72,7 +73,7 @@ def MetadataMenu(rating_key, title=None, base_title=None, display_items=False, p
|
||||
title = unicode(title)
|
||||
item_title = title
|
||||
title = base_title + " > " + title
|
||||
oc = SubFolderObjectContainer(title2=title, no_cache=True, no_history=True, header=header,
|
||||
oc = SubFolderObjectContainer(title2=title, no_cache=True, no_history=True, header=header, message=message,
|
||||
view_group="full_details")
|
||||
|
||||
current_kind = get_item_kind_from_rating_key(rating_key)
|
||||
@@ -89,7 +90,7 @@ def MetadataMenu(rating_key, title=None, base_title=None, display_items=False, p
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(MetadataMenu, rating_key=show.rating_key, title=show.title, base_title=show.section.title,
|
||||
previous_item_type="section", display_items=True, randomize=timestamp()),
|
||||
title=u"< Back to %s" % show.title,
|
||||
title=_(u"< Back to %s", show.title),
|
||||
thumb=show.thumb or default_thumb
|
||||
))
|
||||
elif current_kind == "series":
|
||||
@@ -117,33 +118,35 @@ def MetadataMenu(rating_key, title=None, base_title=None, display_items=False, p
|
||||
title=title,
|
||||
previous_item_type=previous_item_type, with_mods=True,
|
||||
previous_rating_key=previous_rating_key, randomize=timestamp()),
|
||||
title=u"Extract missing %s embedded subtitles with default mods" % display_language(lang),
|
||||
summary="Extracts the not yet extracted embedded subtitles of all episodes for the current season "
|
||||
"with all configured default modifications"
|
||||
title=_(u"Extract missing %(language)s embedded subtitles", language=display_language(lang)),
|
||||
summary=_("Extracts the not yet extracted embedded subtitles of all episodes for the current "
|
||||
"season with all configured default modifications")
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SeasonExtractEmbedded, rating_key=rating_key, language=lang,
|
||||
base_title=show.section.title, display_items=display_items, item_title=item_title,
|
||||
title=title,
|
||||
previous_item_type=previous_item_type, with_mods=False,
|
||||
title=title, force=True,
|
||||
previous_item_type=previous_item_type, with_mods=True,
|
||||
previous_rating_key=previous_rating_key, randomize=timestamp()),
|
||||
title=u"Extract missing %s embedded subtitles" % display_language(lang),
|
||||
summary="Extracts the not yet extracted embedded subtitles of all episodes for the current season"
|
||||
title=_(u"Extract and activate %(language)s embedded subtitles", language=display_language(lang)),
|
||||
summary=_("Extracts embedded subtitles of all episodes for the current season "
|
||||
"with all configured default modifications")
|
||||
))
|
||||
|
||||
# add refresh
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(RefreshItem, rating_key=rating_key, item_title=title, refresh_kind=current_kind,
|
||||
previous_rating_key=previous_rating_key, timeout=timeout * 1000, randomize=timestamp()),
|
||||
title=u"Refresh: %s" % item_title,
|
||||
summary="Refreshes the %s, possibly searching for missing and picking up new subtitles on disk" % current_kind
|
||||
title=_(u"Refresh: %s", item_title),
|
||||
summary=_("Refreshes %(the_movie_series_season_episode)s, possibly searching for missing and picking up "
|
||||
"new subtitles on disk", the_movie_series_season_episode=_(u"the %s" % current_kind))
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(RefreshItem, rating_key=rating_key, item_title=title, force=True,
|
||||
refresh_kind=current_kind, previous_rating_key=previous_rating_key, timeout=timeout * 1000,
|
||||
randomize=timestamp()),
|
||||
title=u"Auto-Find subtitles: %s" % item_title,
|
||||
summary="Issues a forced refresh, ignoring known subtitles and searching for new ones"
|
||||
title=_(u"Auto-Find subtitles: %s", item_title),
|
||||
summary=_("Issues a forced refresh, ignoring known subtitles and searching for new ones")
|
||||
))
|
||||
else:
|
||||
return ItemDetailsMenu(rating_key=rating_key, title=title, item_title=item_title)
|
||||
@@ -158,18 +161,36 @@ def SeasonExtractEmbedded(**kwargs):
|
||||
with_mods = kwargs.pop("with_mods")
|
||||
item_title = kwargs.pop("item_title")
|
||||
title = kwargs.pop("title")
|
||||
force = kwargs.pop("force", False)
|
||||
|
||||
Thread.Create(season_extract_embedded, **{"rating_key": rating_key, "requested_language": requested_language,
|
||||
"with_mods": with_mods})
|
||||
"with_mods": with_mods, "force": force})
|
||||
|
||||
kwargs["header"] = 'Success'
|
||||
kwargs["message"] = u"Extracting of embedded subtitles for %s triggered" % title
|
||||
kwargs["header"] = _("Success")
|
||||
kwargs["message"] = _(u"Extracting of embedded subtitles for %s triggered", title)
|
||||
|
||||
kwargs.pop("randomize")
|
||||
return MetadataMenu(randomize=timestamp(), title=item_title, **kwargs)
|
||||
|
||||
|
||||
def season_extract_embedded(rating_key, requested_language, with_mods=False):
|
||||
def multi_extract_embedded(stream_list, refresh=False, with_mods=False, single_thread=True):
|
||||
def execute():
|
||||
for video_part_map, plexapi_part, stream_index, language, set_current in stream_list:
|
||||
plexapi_item = video_part_map.keys()[0].plexapi_metadata["item"]
|
||||
|
||||
extract_embedded_sub(rating_key=plexapi_item.rating_key, part_id=plexapi_part.id,
|
||||
plex_item=plexapi_item, part=plexapi_part, scanned_videos=video_part_map,
|
||||
stream_index=stream_index, set_current=set_current,
|
||||
language=language, with_mods=with_mods, refresh=refresh)
|
||||
|
||||
if single_thread:
|
||||
with Thread.Lock(key="extract_embedded"):
|
||||
execute()
|
||||
else:
|
||||
execute()
|
||||
|
||||
|
||||
def season_extract_embedded(rating_key, requested_language, with_mods=False, force=False):
|
||||
# get stored subtitle info for item id
|
||||
subtitle_storage = get_subtitle_storage()
|
||||
|
||||
@@ -180,15 +201,19 @@ def season_extract_embedded(rating_key, requested_language, with_mods=False):
|
||||
stored_subs = subtitle_storage.load_or_new(item)
|
||||
for part in get_all_parts(item):
|
||||
embedded_subs = stored_subs.get_by_provider(part.id, requested_language, "embedded")
|
||||
if not embedded_subs:
|
||||
current = stored_subs.get_any(part.id, requested_language)
|
||||
if not embedded_subs or force:
|
||||
stream_data = get_embedded_subtitle_streams(part, requested_language=requested_language,
|
||||
get_forced=config.forced_only)
|
||||
if stream_data:
|
||||
stream = stream_data[0]["stream"]
|
||||
|
||||
set_current = not current or force
|
||||
refresh = not current
|
||||
|
||||
extract_embedded_sub(rating_key=item.rating_key, part_id=part.id,
|
||||
stream_index=str(stream.index),
|
||||
language=requested_language, with_mods=with_mods)
|
||||
stream_index=str(stream.index), set_current=set_current,
|
||||
refresh=refresh, language=requested_language, with_mods=with_mods)
|
||||
finally:
|
||||
subtitle_storage.destroy()
|
||||
|
||||
@@ -208,7 +233,7 @@ def IgnoreListMenu():
|
||||
def HistoryMenu():
|
||||
from support.history import get_history
|
||||
history = get_history()
|
||||
oc = SubFolderObjectContainer(title2="History", replace_parent=True)
|
||||
oc = SubFolderObjectContainer(title2=_("History"), replace_parent=True)
|
||||
|
||||
for item in history.items:
|
||||
possible_language = item.language
|
||||
@@ -218,7 +243,7 @@ def HistoryMenu():
|
||||
key=Callback(ItemDetailsMenu, title=item.title, item_title=item.item_title,
|
||||
rating_key=item.rating_key),
|
||||
title=u"%s (%s)" % (item.item_title, item.mode_verbose),
|
||||
summary=u"%s in %s (%s, score: %s), %s" % (language_display, item.section_title,
|
||||
summary=_(u"%s in %s (%s, score: %s), %s", language_display, item.section_title,
|
||||
item.provider_name, item.score, df(item.time))
|
||||
))
|
||||
|
||||
@@ -267,20 +292,20 @@ def ValidatePrefs():
|
||||
update_dict = True
|
||||
|
||||
elif Dict["channel_enabled"] != config.enable_channel:
|
||||
Log.Debug("Channel features %s, restarting plugin", "enabled" if config.enable_channel else "disabled")
|
||||
Log.Debug("Interface features %s, restarting plugin", "enabled" if config.enable_channel else "disabled")
|
||||
update_dict = True
|
||||
restart = True
|
||||
|
||||
if "plugin_pin_mode" not in Dict:
|
||||
if "plugin_pin_mode2" not in Dict:
|
||||
update_dict = True
|
||||
|
||||
elif Dict["plugin_pin_mode"] != Prefs["plugin_pin_mode"]:
|
||||
elif Dict["plugin_pin_mode2"] != Prefs["plugin_pin_mode2"]:
|
||||
update_dict = True
|
||||
restart = True
|
||||
|
||||
if update_dict:
|
||||
Dict["channel_enabled"] = config.enable_channel
|
||||
Dict["plugin_pin_mode"] = Prefs["plugin_pin_mode"]
|
||||
Dict["plugin_pin_mode2"] = Prefs["plugin_pin_mode2"]
|
||||
Dict.Save()
|
||||
|
||||
if restart:
|
||||
@@ -300,7 +325,7 @@ def ValidatePrefs():
|
||||
"version", "app_support_path", "data_path", "data_items_path", "enable_agent",
|
||||
"enable_channel", "permissions_ok", "missing_permissions", "fs_encoding",
|
||||
"subtitle_destination_folder", "new_style_cache", "dbm_supported", "lang_list", "providers",
|
||||
"plex_transcoder", "refiner_settings"]:
|
||||
"plex_transcoder", "refiner_settings", "unrar", "adv_cfg_path"]:
|
||||
|
||||
value = getattr(config, attr)
|
||||
if isinstance(value, dict):
|
||||
@@ -332,24 +357,27 @@ def ValidatePrefs():
|
||||
# debug drone
|
||||
if "sonarr" in config.refiner_settings or "radarr" in config.refiner_settings:
|
||||
Log.Debug("----- Connections -----")
|
||||
from subliminal_patch.refiners.drone import SonarrClient, RadarrClient
|
||||
for key, cls in [("sonarr", SonarrClient), ("radarr", RadarrClient)]:
|
||||
if key in config.refiner_settings:
|
||||
cname = key.capitalize()
|
||||
try:
|
||||
status = cls(**config.refiner_settings[key]).status()
|
||||
except HTTPError, e:
|
||||
if e.response.status_code == 401:
|
||||
Log.Debug("%s: NOT WORKING - BAD API KEY", cname)
|
||||
else:
|
||||
try:
|
||||
from subliminal_patch.refiners.drone import SonarrClient, RadarrClient
|
||||
for key, cls in [("sonarr", SonarrClient), ("radarr", RadarrClient)]:
|
||||
if key in config.refiner_settings:
|
||||
cname = key.capitalize()
|
||||
try:
|
||||
status = cls(**config.refiner_settings[key]).status()
|
||||
except HTTPError, e:
|
||||
if e.response.status_code == 401:
|
||||
Log.Debug("%s: NOT WORKING - BAD API KEY", cname)
|
||||
else:
|
||||
Log.Debug("%s: NOT WORKING - %s", cname, traceback.format_exc())
|
||||
except:
|
||||
Log.Debug("%s: NOT WORKING - %s", cname, traceback.format_exc())
|
||||
except:
|
||||
Log.Debug("%s: NOT WORKING - %s", cname, traceback.format_exc())
|
||||
else:
|
||||
if status["version"]:
|
||||
Log.Debug("%s: OK - %s", cname, status["version"])
|
||||
else:
|
||||
Log.Debug("%s: NOT WORKING - %s", cname)
|
||||
if status and status["version"]:
|
||||
Log.Debug("%s: OK - %s", cname, status["version"])
|
||||
else:
|
||||
Log.Debug("%s: NOT WORKING - %s", cname)
|
||||
except:
|
||||
Log.Debug("Something went really wrong when evaluating Sonarr/Radarr: %s", traceback.format_exc())
|
||||
|
||||
# fixme: check existance of and os access of logs
|
||||
Log.Debug("----- Environment -----")
|
||||
|
||||
@@ -4,11 +4,13 @@ import types
|
||||
import datetime
|
||||
import subprocess
|
||||
import os
|
||||
import operator
|
||||
|
||||
from func import enable_channel_wrapper
|
||||
from func import enable_channel_wrapper, route_wrapper, register_route_function
|
||||
from subzero.language import Language
|
||||
from support.i18n import is_localized_string, _
|
||||
from support.items import get_kind, get_item_thumb, get_item, get_item_kind_from_item, refresh_item
|
||||
from support.helpers import get_video_display_title, pad_title, display_language, quote_args
|
||||
from support.helpers import get_video_display_title, pad_title, display_language, quote_args, is_stream_forced
|
||||
from support.ignore import ignore_list
|
||||
from support.lib import get_intent
|
||||
from support.config import config
|
||||
@@ -24,7 +26,7 @@ default_thumb = R(ICON_SUB)
|
||||
main_icon = ICON if not config.is_development else "icon-dev.jpg"
|
||||
|
||||
# noinspection PyUnboundLocalVariable
|
||||
route = enable_channel_wrapper(route)
|
||||
route = route_wrapper
|
||||
# noinspection PyUnboundLocalVariable
|
||||
handler = enable_channel_wrapper(handler)
|
||||
|
||||
@@ -48,10 +50,15 @@ def add_ignore_options(oc, kind, callback_menu=None, title=None, rating_key=None
|
||||
|
||||
in_list = rating_key in ignore_list[use_kind]
|
||||
|
||||
t = u"Ignore %(kind)s \"%(title)s\""
|
||||
if in_list:
|
||||
t = u"Un-ignore %(kind)s \"%(title)s\""
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(callback_menu, kind=use_kind, rating_key=rating_key, title=title),
|
||||
title=u"%s %s \"%s\"" % (
|
||||
"Un-Ignore" if in_list else "Ignore", ignore_list.verbose(kind) if add_kind else "", unicode(title))
|
||||
key=Callback(callback_menu, kind=use_kind, sure=False, todo="not_set", rating_key=rating_key, title=title),
|
||||
title=_(t,
|
||||
kind=ignore_list.verbose(kind) if add_kind else "",
|
||||
title=unicode(title))
|
||||
)
|
||||
)
|
||||
|
||||
@@ -91,8 +98,8 @@ def set_refresh_menu_state(state_or_media, media_type="movies"):
|
||||
Dict["current_refresh_state"] = None
|
||||
return
|
||||
|
||||
if isinstance(state_or_media, types.StringTypes):
|
||||
Dict["current_refresh_state"] = state_or_media
|
||||
if isinstance(state_or_media, types.StringTypes) or is_localized_string(state_or_media):
|
||||
Dict["current_refresh_state"] = unicode(state_or_media)
|
||||
return
|
||||
|
||||
media = state_or_media
|
||||
@@ -103,14 +110,19 @@ def set_refresh_menu_state(state_or_media, media_type="movies"):
|
||||
for episode in media.seasons[season].episodes:
|
||||
ep = media.seasons[season].episodes[episode]
|
||||
media_id = ep.id
|
||||
title = get_video_display_title("show", ep.title, parent_title=media.title, season=int(season), episode=int(episode))
|
||||
title = get_video_display_title(_("show"), ep.title, parent_title=media.title, season=int(season), episode=int(episode))
|
||||
else:
|
||||
title = get_video_display_title("movie", media.title)
|
||||
title = get_video_display_title(_("movie"), media.title)
|
||||
|
||||
intent = get_intent()
|
||||
force_refresh = intent.get("force", media_id)
|
||||
|
||||
Dict["current_refresh_state"] = u"%sRefreshing %s" % ("Force-" if force_refresh else "", unicode(title))
|
||||
t = u"Refreshing %(title)s"
|
||||
if force_refresh:
|
||||
t = u"Force-refreshing %(title)s"
|
||||
|
||||
Dict["current_refresh_state"] = unicode(_(t,
|
||||
title=unicode(title)))
|
||||
|
||||
|
||||
def get_item_task_data(task_name, rating_key, language):
|
||||
@@ -125,30 +137,10 @@ def debounce(func):
|
||||
:param func:
|
||||
:return:
|
||||
"""
|
||||
def get_lookup_key(args, kwargs):
|
||||
func_name = list(args).pop(0).__name__
|
||||
return tuple([func_name] + [(key, value) for key, value in kwargs.iteritems()])
|
||||
|
||||
def wrap(*args, **kwargs):
|
||||
if "randomize" in kwargs:
|
||||
if "menu_history" not in Dict:
|
||||
Dict["menu_history"] = {}
|
||||
func.debounce = True
|
||||
|
||||
key = get_lookup_key([func] + list(args), kwargs)
|
||||
if key in Dict["menu_history"]:
|
||||
Log.Debug("not triggering %s twice with %s, %s" % (func, args, kwargs))
|
||||
return ObjectContainer()
|
||||
else:
|
||||
Dict["menu_history"][key] = datetime.datetime.now() + datetime.timedelta(hours=6)
|
||||
try:
|
||||
Dict.Save()
|
||||
except TypeError:
|
||||
Log.Error("Can't save menu history for: %r", key)
|
||||
del Dict["menu_history"][key]
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrap
|
||||
return func
|
||||
|
||||
|
||||
def extract_embedded_sub(**kwargs):
|
||||
@@ -160,24 +152,33 @@ def extract_embedded_sub(**kwargs):
|
||||
refresh = kwargs.pop("refresh", True)
|
||||
set_current = kwargs.pop("set_current", True)
|
||||
|
||||
plex_item = get_item(rating_key)
|
||||
plex_item = kwargs.pop("plex_item", get_item(rating_key))
|
||||
item_type = get_item_kind_from_item(plex_item)
|
||||
part = get_part(plex_item, part_id)
|
||||
part = kwargs.pop("part", get_part(plex_item, part_id))
|
||||
scanned_videos = kwargs.pop("scanned_videos", None)
|
||||
|
||||
any_successful = False
|
||||
|
||||
if part:
|
||||
metadata = get_plex_metadata(rating_key, part_id, item_type, plex_item=plex_item)
|
||||
scanned_parts = scan_videos([metadata], ignore_all=True, skip_hashing=True)
|
||||
if not scanned_videos:
|
||||
metadata = get_plex_metadata(rating_key, part_id, item_type, plex_item=plex_item)
|
||||
scanned_videos = scan_videos([metadata], ignore_all=True, skip_hashing=True)
|
||||
|
||||
for stream in part.streams:
|
||||
# subtitle stream
|
||||
if str(stream.index) == stream_index:
|
||||
forced = stream.forced
|
||||
is_forced = is_stream_forced(stream)
|
||||
bn = os.path.basename(part.file)
|
||||
|
||||
set_refresh_menu_state(u"Extracting subtitle %s of %s" % (stream_index, bn))
|
||||
set_refresh_menu_state(_(u"Extracting subtitle %(stream_index)s of %(filename)s",
|
||||
stream_index=stream_index,
|
||||
filename=bn))
|
||||
Log.Info(u"Extracting stream %s (%s) of %s", stream_index, display_language(language), bn)
|
||||
|
||||
out_codec = stream.codec if stream.codec != "mov_text" else "srt"
|
||||
|
||||
args = [
|
||||
config.plex_transcoder, "-i", part.file, "-map", "0:%s" % stream_index, "-f", "srt", "-"
|
||||
config.plex_transcoder, "-i", part.file, "-map", "0:%s" % stream_index, "-f", out_codec, "-"
|
||||
]
|
||||
output = None
|
||||
try:
|
||||
@@ -194,13 +195,17 @@ def extract_embedded_sub(**kwargs):
|
||||
subtitle.set_encoding("utf-8")
|
||||
|
||||
# fixme: speedup video; only video.name is needed
|
||||
save_successful = save_subtitles(scanned_parts, {scanned_parts.keys()[0]: [subtitle]}, mode="m",
|
||||
set_current=set_current)
|
||||
save_successful = save_subtitles(scanned_videos, {scanned_videos.keys()[0]: [subtitle]}, mode="m",
|
||||
set_current=set_current, is_forced=is_forced)
|
||||
set_refresh_menu_state(None)
|
||||
|
||||
if save_successful and refresh:
|
||||
refresh_item(rating_key)
|
||||
|
||||
any_successful = True
|
||||
|
||||
return any_successful
|
||||
|
||||
|
||||
class SZObjectContainer(ObjectContainer):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -233,10 +238,10 @@ class SubFolderObjectContainer(ObjectContainer):
|
||||
from support.helpers import pad_title, timestamp
|
||||
self.add(DirectoryObject(
|
||||
key=Callback(fatality, force_title=" ", randomize=timestamp()),
|
||||
title=pad_title("<< Back to home"),
|
||||
summary="Current state: %s; Last state: %s" % (
|
||||
(Dict["current_refresh_state"] or "Idle") if "current_refresh_state" in Dict else "Idle",
|
||||
(Dict["last_refresh_state"] or "None") if "last_refresh_state" in Dict else "None"
|
||||
title=pad_title(_("<< Back to home")),
|
||||
summary=_("Current state: %s; Last state: %s",
|
||||
(Dict["current_refresh_state"] or _("Idle")) if "current_refresh_state" in Dict else _("Idle"),
|
||||
(Dict["last_refresh_state"] or _("None")) if "last_refresh_state" in Dict else _("None")
|
||||
)
|
||||
))
|
||||
|
||||
@@ -254,4 +259,4 @@ class ZipObject(ObjectClass):
|
||||
self.SetHeader("Content-Disposition",
|
||||
'attachment; filename="' + datetime.datetime.now().strftime("Logs_%y%m%d_%H-%M-%S.zip")
|
||||
+ '"')
|
||||
return self.zipdata
|
||||
return self.zipdata
|
||||
|
||||
@@ -4,6 +4,7 @@ from subzero.constants import PREFIX
|
||||
from menu_helpers import debounce, set_refresh_menu_state, route
|
||||
from support.items import refresh_item
|
||||
from support.helpers import timestamp
|
||||
from support.i18n import _
|
||||
|
||||
|
||||
@route(PREFIX + '/item/refresh/{rating_key}/force', force=True)
|
||||
@@ -15,9 +16,17 @@ def RefreshItem(rating_key=None, came_from="/recent", item_title=None, force=Fal
|
||||
from interface.main import fatality
|
||||
header = " "
|
||||
if trigger:
|
||||
set_refresh_menu_state(u"Triggering %sRefresh for %s" % ("Force-" if force else "", item_title))
|
||||
t = u"Triggering refresh for %(title)s"
|
||||
if force:
|
||||
u"Triggering forced refresh for %(title)s"
|
||||
set_refresh_menu_state(_(t,
|
||||
title=item_title))
|
||||
Thread.Create(refresh_item, rating_key=rating_key, force=force, refresh_kind=refresh_kind,
|
||||
parent_rating_key=previous_rating_key, timeout=int(timeout))
|
||||
|
||||
header = u"%s of item %s triggered" % ("Refresh" if not force else "Forced-refresh", rating_key)
|
||||
t = u"Refresh of item %(item_id)s triggered"
|
||||
if force:
|
||||
t = u"Forced refresh of item %(item_id)s triggered"
|
||||
header = _(t,
|
||||
item_id=rating_key)
|
||||
return fatality(randomize=timestamp(), header=header, replace_parent=True)
|
||||
|
||||
@@ -12,14 +12,15 @@ from support.plex_media import get_plex_metadata
|
||||
from support.scanning import scan_videos
|
||||
from support.helpers import timestamp, pad_title
|
||||
from support.items import get_current_sub, set_mods_for_part
|
||||
from support.i18n import _
|
||||
|
||||
|
||||
@route(PREFIX + '/item/sub_mods/{rating_key}/{part_id}', force=bool)
|
||||
@debounce
|
||||
def SubtitleModificationsMenu(**kwargs):
|
||||
rating_key = kwargs["rating_key"]
|
||||
part_id = kwargs["part_id"]
|
||||
language = kwargs["language"]
|
||||
lang_instance = Language.fromietf(language)
|
||||
current_sub, stored_subs, storage = get_current_sub(rating_key, part_id, language)
|
||||
kwargs.pop("randomize")
|
||||
|
||||
@@ -30,8 +31,8 @@ def SubtitleModificationsMenu(**kwargs):
|
||||
from interface.item_details import SubtitleOptionsMenu
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleOptionsMenu, randomize=timestamp(), **kwargs),
|
||||
title=u"< Back to subtitle options for: %s" % kwargs["title"],
|
||||
summary=kwargs["current_data"],
|
||||
title=_(u"< Back to subtitle options for: %s", kwargs["title"]),
|
||||
summary=unicode(kwargs["current_data"]),
|
||||
thumb=default_thumb
|
||||
))
|
||||
|
||||
@@ -42,50 +43,53 @@ def SubtitleModificationsMenu(**kwargs):
|
||||
if mod.exclusive and identifier in current_mods:
|
||||
continue
|
||||
|
||||
if mod.languages and lang_instance not in mod.languages:
|
||||
continue
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleSetMods, mods=identifier, mode="add", randomize=timestamp(), **kwargs),
|
||||
title=pad_title(mod.description), summary=mod.long_description or ""
|
||||
title=pad_title(_(mod.description)), summary=_(mod.long_description) or ""
|
||||
))
|
||||
|
||||
fps_mod = SubtitleModifications.get_mod_class("change_FPS")
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleFPSModMenu, randomize=timestamp(), **kwargs),
|
||||
title=pad_title(fps_mod.description), summary=fps_mod.long_description or ""
|
||||
title=pad_title(_(fps_mod.description)), summary=_(fps_mod.long_description) or ""
|
||||
))
|
||||
|
||||
shift_mod = SubtitleModifications.get_mod_class("shift_offset")
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleShiftModUnitMenu, randomize=timestamp(), **kwargs),
|
||||
title=pad_title(shift_mod.description), summary=shift_mod.long_description or ""
|
||||
title=pad_title(_(shift_mod.description)), summary=_(shift_mod.long_description) or ""
|
||||
))
|
||||
|
||||
color_mod = SubtitleModifications.get_mod_class("color")
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleColorModMenu, randomize=timestamp(), **kwargs),
|
||||
title=pad_title(color_mod.description), summary=color_mod.long_description or ""
|
||||
title=pad_title(_(color_mod.description)), summary=_(color_mod.long_description) or ""
|
||||
))
|
||||
|
||||
if current_mods:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleSetMods, mods=None, mode="remove_last", randomize=timestamp(), **kwargs),
|
||||
title=pad_title("Remove last applied mod (%s)" % current_mods[-1]),
|
||||
summary=u"Currently applied mods: %s" % (", ".join(current_mods) if current_mods else "none")
|
||||
title=pad_title(_("Remove last applied mod (%s)", current_mods[-1])),
|
||||
summary=_(u"Currently applied mods: %(mod_list)s", mod_list=", ".join(current_mods) if current_mods else _("none"))
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleListMods, randomize=timestamp(), **kwargs),
|
||||
title=pad_title("Manage applied mods"),
|
||||
summary=u"Currently applied mods: %s" % (", ".join(current_mods))
|
||||
title=pad_title(_("Manage applied mods")),
|
||||
summary=_(u"Currently applied mods: %(mod_list)s", mod_list=", ".join(current_mods))
|
||||
))
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleReapplyMods, randomize=timestamp(), **kwargs),
|
||||
title=pad_title("Reapply applied mods"),
|
||||
summary=u"Currently applied mods: %s" % (", ".join(current_mods) if current_mods else "none")
|
||||
title=pad_title(_("Reapply applied mods")),
|
||||
summary=_(u"Currently applied mods: %(mod_list)s", mod_list=", ".join(current_mods) if current_mods else _("none"))
|
||||
))
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleSetMods, mods=None, mode="clear", randomize=timestamp(), **kwargs),
|
||||
title=pad_title("Restore original version"),
|
||||
summary=u"Currently applied mods: %s" % (", ".join(current_mods) if current_mods else "none")
|
||||
title=pad_title(_("Restore original version")),
|
||||
summary=_(u"Currently applied mods: %(mod_list)s", mod_list=", ".join(current_mods) if current_mods else _("none"))
|
||||
))
|
||||
|
||||
storage.destroy()
|
||||
@@ -105,7 +109,7 @@ def SubtitleFPSModMenu(**kwargs):
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleModificationsMenu, randomize=timestamp(), **kwargs),
|
||||
title="< Back to subtitle modification menu"
|
||||
title=_("< Back to subtitle modification menu")
|
||||
))
|
||||
|
||||
metadata = get_plex_metadata(rating_key, part_id, item_type)
|
||||
@@ -119,14 +123,17 @@ def SubtitleFPSModMenu(**kwargs):
|
||||
continue
|
||||
|
||||
if float(fps) > float(target_fps):
|
||||
indicator = "subs constantly getting faster"
|
||||
indicator = _("subs constantly getting faster")
|
||||
else:
|
||||
indicator = "subs constantly getting slower"
|
||||
indicator = _("subs constantly getting slower")
|
||||
|
||||
mod_ident = SubtitleModifications.get_mod_signature("change_FPS", **{"from": fps, "to": target_fps})
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleSetMods, mods=mod_ident, mode="add", randomize=timestamp(), **kwargs),
|
||||
title="%s fps -> %s fps (%s)" % (fps, target_fps, indicator)
|
||||
title=_("%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)",
|
||||
from_fps=fps,
|
||||
to_fps=target_fps,
|
||||
slower_or_faster_indicator=indicator)
|
||||
))
|
||||
|
||||
return oc
|
||||
@@ -144,13 +151,13 @@ def SubtitleShiftModUnitMenu(**kwargs):
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleModificationsMenu, randomize=timestamp(), **kwargs),
|
||||
title="< Back to subtitle modifications"
|
||||
title=_("< Back to subtitle modifications")
|
||||
))
|
||||
|
||||
for unit, title in POSSIBLE_UNITS:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleShiftModMenu, unit=unit, randomize=timestamp(), **kwargs),
|
||||
title="Adjust by %s" % title
|
||||
title=_("Adjust by %(time_and_unit)s", time_and_unit=title)
|
||||
))
|
||||
|
||||
return oc
|
||||
@@ -167,16 +174,16 @@ def SubtitleShiftModMenu(unit=None, **kwargs):
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleShiftModUnitMenu, randomize=timestamp(), **kwargs),
|
||||
title="< Back to unit selection"
|
||||
title=_("< Back to unit selection")
|
||||
))
|
||||
|
||||
rng = []
|
||||
if unit == "h":
|
||||
rng = range(-10, 11)
|
||||
rng = list(reversed(range(-10, 0))) + list(reversed(range(1, 11)))
|
||||
elif unit in ("m", "s"):
|
||||
rng = range(-15, 15)
|
||||
rng = list(reversed(range(-15, 0))) + list(reversed(range(1, 16)))
|
||||
elif unit == "ms":
|
||||
rng = range(-900, 1000, 100)
|
||||
rng = list(reversed(range(-900, 0, 100))) + list(reversed(range(100, 1000, 100)))
|
||||
|
||||
for i in rng:
|
||||
if i == 0:
|
||||
@@ -201,7 +208,7 @@ def SubtitleColorModMenu(**kwargs):
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleModificationsMenu, randomize=timestamp(), **kwargs),
|
||||
title="< Back to subtitle modification menu"
|
||||
title=_("< Back to subtitle modification menu")
|
||||
))
|
||||
|
||||
for color, code in color_mod.colors.iteritems():
|
||||
@@ -263,13 +270,13 @@ def SubtitleListMods(**kwargs):
|
||||
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleModificationsMenu, randomize=timestamp(), **kwargs),
|
||||
title="< Back to subtitle modifications"
|
||||
title=_("< Back to subtitle modifications")
|
||||
))
|
||||
|
||||
for identifier in current_sub.mods:
|
||||
oc.add(DirectoryObject(
|
||||
key=Callback(SubtitleSetMods, mods=identifier, mode="remove", randomize=timestamp(), **kwargs),
|
||||
title="Remove: %s" % identifier
|
||||
title=_("Remove: %(mod_name)s", mod_name=identifier)
|
||||
))
|
||||
|
||||
storage.destroy()
|
||||
|
||||
@@ -13,6 +13,12 @@ import lib
|
||||
|
||||
sys.modules["support.lib"] = lib
|
||||
|
||||
import i18n
|
||||
|
||||
sys.modules["support.i18n"] = i18n
|
||||
|
||||
helpers._ = i18n._
|
||||
|
||||
import plex_media
|
||||
sys.modules["support.plex_media"] = plex_media
|
||||
|
||||
|
||||
+126
-27
@@ -12,7 +12,7 @@ import subliminal
|
||||
import subliminal_patch
|
||||
import subzero.constants
|
||||
import lib
|
||||
from subliminal.exceptions import ServiceUnavailable, DownloadLimitExceeded
|
||||
from subliminal.exceptions import ServiceUnavailable, DownloadLimitExceeded, AuthenticationError
|
||||
|
||||
from subliminal_patch.core import is_windows_special_path
|
||||
from whichdb import whichdb
|
||||
@@ -35,7 +35,7 @@ SUBTITLE_EXTS_BASE = ['utf', 'utf8', 'utf-8', 'srt', 'smi', 'rt', 'ssa', 'aqt',
|
||||
'vtt']
|
||||
SUBTITLE_EXTS = SUBTITLE_EXTS_BASE + ["txt"]
|
||||
|
||||
TEXT_SUBTITLE_EXTS = ("srt", "ass", "ssa", "vtt")
|
||||
TEXT_SUBTITLE_EXTS = ("srt", "ass", "ssa", "vtt", "mov_text")
|
||||
VIDEO_EXTS = ['3g2', '3gp', 'asf', 'asx', 'avc', 'avi', 'avs', 'bivx', 'bup', 'divx', 'dv', 'dvr-ms', 'evo', 'fli',
|
||||
'flv',
|
||||
'm2t', 'm2ts', 'm2v', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg', 'mts', 'nsv', 'nuv', 'ogm', 'ogv', 'tp',
|
||||
@@ -69,7 +69,8 @@ PROVIDER_THROTTLE_MAP = {
|
||||
DownloadLimitExceeded: (datetime.timedelta(hours=6), "6 hours"),
|
||||
},
|
||||
"addic7ed": {
|
||||
DownloadLimitExceeded: (datetime.timedelta(hours=24), "24 hours"),
|
||||
DownloadLimitExceeded: (datetime.timedelta(hours=3), "3 hours"),
|
||||
TooManyRequests: (datetime.timedelta(minutes=5), "5 minutes"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +94,7 @@ class Config(object):
|
||||
new_style_cache = False
|
||||
pack_cache_dir = None
|
||||
advanced = None
|
||||
debug_i18n = False
|
||||
|
||||
enable_channel = True
|
||||
enable_agent = True
|
||||
@@ -116,6 +118,7 @@ class Config(object):
|
||||
remove_tags = False
|
||||
fix_ocr = False
|
||||
fix_common = False
|
||||
reverse_rtl = False
|
||||
colors = ""
|
||||
chmod = None
|
||||
forced_only = False
|
||||
@@ -134,6 +137,8 @@ class Config(object):
|
||||
only_one = False
|
||||
embedded_auto_extract = False
|
||||
ietf_as_alpha3 = False
|
||||
unrar = None
|
||||
adv_cfg_path = None
|
||||
|
||||
store_recently_played_amount = 40
|
||||
|
||||
@@ -164,6 +169,7 @@ class Config(object):
|
||||
self.new_style_cache = cast_bool(Prefs['new_style_cache'])
|
||||
self.pack_cache_dir = self.get_pack_cache_dir()
|
||||
self.advanced = self.get_advanced_config()
|
||||
self.debug_i18n = self.advanced.debug_i18n
|
||||
|
||||
os.environ["SZ_USER_AGENT"] = self.get_user_agent()
|
||||
|
||||
@@ -188,6 +194,7 @@ class Config(object):
|
||||
self.remove_tags = cast_bool(Prefs['subtitles.remove_tags'])
|
||||
self.fix_ocr = cast_bool(Prefs['subtitles.fix_ocr'])
|
||||
self.fix_common = cast_bool(Prefs['subtitles.fix_common'])
|
||||
self.reverse_rtl = cast_bool(Prefs['subtitles.reverse_rtl'])
|
||||
self.colors = Prefs['subtitles.colors'] if Prefs['subtitles.colors'] != "don't change" else None
|
||||
self.chmod = self.check_chmod()
|
||||
self.exotic_ext = cast_bool(Prefs["subtitles.scan.exotic_ext"])
|
||||
@@ -204,16 +211,44 @@ class Config(object):
|
||||
self.initialized = True
|
||||
|
||||
def init_libraries(self):
|
||||
try_executables = []
|
||||
custom_unrar = os.environ.get("SZ_UNRAR_TOOL")
|
||||
if custom_unrar:
|
||||
if os.path.isfile(custom_unrar):
|
||||
try_executables.append(custom_unrar)
|
||||
|
||||
unrar_exe = None
|
||||
if Core.runtime.os == "Windows":
|
||||
unrar_exe = os.path.abspath(os.path.join(self.libraries_root, "Windows", "i386", "UnRAR", "UnRAR.exe"))
|
||||
if os.path.isfile(unrar_exe):
|
||||
rarfile.UNRAR_TOOL = unrar_exe
|
||||
Log.Info("Using UnRAR from: %s", unrar_exe)
|
||||
|
||||
custom_unrar = os.environ.get("SZ_UNRAR_TOOL")
|
||||
if custom_unrar and os.path.isfile(custom_unrar):
|
||||
rarfile.UNRAR_TOOL = custom_unrar
|
||||
Log.Info("Using UnRAR from: %s", custom_unrar)
|
||||
elif Core.runtime.os == "MacOSX":
|
||||
unrar_exe = os.path.abspath(os.path.join(self.libraries_root, "MacOSX", "i386", "UnRAR", "unrar"))
|
||||
|
||||
elif Core.runtime.os == "Linux":
|
||||
unrar_exe = os.path.abspath(os.path.join(self.libraries_root, "Linux", Core.runtime.cpu, "UnRAR", "unrar"))
|
||||
|
||||
if unrar_exe and os.path.isfile(unrar_exe):
|
||||
try_executables.append(unrar_exe)
|
||||
|
||||
try_executables.append("unrar")
|
||||
|
||||
for exe in try_executables:
|
||||
rarfile.UNRAR_TOOL = exe
|
||||
rarfile.ORIG_UNRAR_TOOL = exe
|
||||
try:
|
||||
rarfile.custom_check([rarfile.UNRAR_TOOL], True)
|
||||
except:
|
||||
Log.Debug("custom check failed for: %s", exe)
|
||||
continue
|
||||
|
||||
rarfile.OPEN_ARGS = rarfile.ORIG_OPEN_ARGS
|
||||
rarfile.EXTRACT_ARGS = rarfile.ORIG_EXTRACT_ARGS
|
||||
rarfile.TEST_ARGS = rarfile.ORIG_TEST_ARGS
|
||||
Log.Info("Using UnRAR from: %s", exe)
|
||||
self.unrar = exe
|
||||
return
|
||||
|
||||
Log.Warn("UnRAR not found")
|
||||
|
||||
def init_cache(self):
|
||||
if self.new_style_cache:
|
||||
@@ -282,11 +317,23 @@ class Config(object):
|
||||
return pack_cache_dir
|
||||
|
||||
def get_advanced_config(self):
|
||||
path = os.path.join(config.data_path, "advanced_settings.json")
|
||||
if os.path.isfile(path):
|
||||
data = FileIO.read(path, "r")
|
||||
paths = []
|
||||
if Prefs['path_to_advanced_settings']:
|
||||
paths = [
|
||||
Prefs['path_to_advanced_settings'],
|
||||
os.path.join(Prefs['path_to_advanced_settings'], "advanced_settings.json")
|
||||
]
|
||||
|
||||
return Dicked(**jstyleson.loads(data))
|
||||
paths.append(os.path.join(config.data_path, "advanced_settings.json"))
|
||||
|
||||
for path in paths:
|
||||
if os.path.isfile(path):
|
||||
data = FileIO.read(path, "r")
|
||||
|
||||
d = Dicked(**jstyleson.loads(data))
|
||||
self.adv_cfg_path = path
|
||||
Log.Info(u"Using advanced settings from: %s", path)
|
||||
return d
|
||||
|
||||
return Dicked()
|
||||
|
||||
@@ -329,16 +376,16 @@ class Config(object):
|
||||
if not self.providers:
|
||||
self.enable_agent = False
|
||||
self.enable_channel = False
|
||||
Log.Warn("No providers enabled, disabling agent and channel!")
|
||||
Log.Warn("No providers enabled, disabling agent and interface!")
|
||||
return
|
||||
|
||||
if Prefs["plugin_mode"] == "only agent":
|
||||
if Prefs["plugin_mode2"] == "only agent":
|
||||
self.enable_channel = False
|
||||
elif Prefs["plugin_mode"] == "only channel":
|
||||
elif Prefs["plugin_mode2"] == "only interface":
|
||||
self.enable_agent = False
|
||||
|
||||
def set_plugin_lock(self):
|
||||
if Prefs["plugin_pin_mode"] in ("channel menu", "advanced menu"):
|
||||
if Prefs["plugin_pin_mode2"] in ("interface", "advanced menu"):
|
||||
# check pin
|
||||
pin = Prefs["plugin_pin"]
|
||||
if not pin or not len(pin):
|
||||
@@ -351,8 +398,8 @@ class Config(object):
|
||||
except ValueError:
|
||||
Log.Warn("PIN has to be an integer (0-9)")
|
||||
self.pin = pin
|
||||
self.lock_advanced_menu = Prefs["plugin_pin_mode"] == "advanced menu"
|
||||
self.lock_menu = Prefs["plugin_pin_mode"] == "channel menu"
|
||||
self.lock_advanced_menu = Prefs["plugin_pin_mode2"] == "advanced menu"
|
||||
self.lock_menu = Prefs["plugin_pin_mode2"] == "interface"
|
||||
|
||||
try:
|
||||
self.pin_valid_minutes = int(Prefs["plugin_pin_valid_for"].strip())
|
||||
@@ -458,12 +505,30 @@ class Config(object):
|
||||
if not fn:
|
||||
return
|
||||
|
||||
splitted_fn = fn.split()
|
||||
exe_fn = splitted_fn[0]
|
||||
arguments = [arg.strip() for arg in splitted_fn[1:]]
|
||||
got_args = "%(" in fn
|
||||
if got_args:
|
||||
first_arg_pos = fn.index("%(")
|
||||
exe_fn = fn[:first_arg_pos].strip()
|
||||
arguments = [arg.strip() for arg in fn[first_arg_pos:].split()]
|
||||
else:
|
||||
exe_fn = fn
|
||||
arguments = []
|
||||
|
||||
if os.path.isfile(exe_fn) and os.access(exe_fn, os.X_OK):
|
||||
return exe_fn, arguments
|
||||
|
||||
# try finding the executable itself, the path might contain spaces and there might have been other arguments
|
||||
fn_split = exe_fn.split(u" ")
|
||||
tmp_exe_fn = fn_split[0]
|
||||
|
||||
for offset in range(1, len(fn_split)+1):
|
||||
if os.path.isfile(tmp_exe_fn) and os.access(tmp_exe_fn, os.X_OK):
|
||||
exe_fn = tmp_exe_fn.strip()
|
||||
arguments = [arg.strip() for arg in fn_split[offset:]] + arguments
|
||||
return exe_fn, arguments
|
||||
|
||||
tmp_exe_fn = u" ".join(fn_split[:offset+1])
|
||||
|
||||
Log.Error("Notify executable not existing or not executable: %s" % exe_fn)
|
||||
|
||||
def refresh_enabled_sections(self):
|
||||
@@ -576,9 +641,13 @@ class Config(object):
|
||||
'tvsubtitles': cast_bool(Prefs['provider.tvsubtitles.enabled']),
|
||||
'legendastv': cast_bool(Prefs['provider.legendastv.enabled']),
|
||||
'napiprojekt': cast_bool(Prefs['provider.napiprojekt.enabled']),
|
||||
'hosszupuska': cast_bool(Prefs['provider.hosszupuska.enabled']),
|
||||
'supersubtitles': cast_bool(Prefs['provider.supersubtitles.enabled']),
|
||||
'shooter': False,
|
||||
'subscene': cast_bool(Prefs['provider.subscene.enabled']),
|
||||
'argenteam': cast_bool(Prefs['provider.argenteam.enabled']),
|
||||
'subscenter': False,
|
||||
'assrt': cast_bool(Prefs['provider.assrt.enabled']),
|
||||
}
|
||||
|
||||
providers_by_prefs = copy.deepcopy(providers)
|
||||
@@ -594,7 +663,15 @@ class Config(object):
|
||||
providers["legendastv"] = False
|
||||
providers["napiprojekt"] = False
|
||||
providers["shooter"] = False
|
||||
providers["hosszupuska"] = False
|
||||
providers["supersubtitles"] = False
|
||||
providers["titlovi"] = False
|
||||
providers["argenteam"] = False
|
||||
providers["assrt"] = False
|
||||
|
||||
if not self.unrar and providers["legendastv"]:
|
||||
providers["legendastv"] = False
|
||||
Log.Info("Disabling LegendasTV, because UnRAR wasn't found")
|
||||
|
||||
# advanced settings
|
||||
if media_type and self.advanced.providers:
|
||||
@@ -630,22 +707,27 @@ class Config(object):
|
||||
providers = property(get_providers)
|
||||
|
||||
def get_provider_settings(self):
|
||||
os_use_https = self.advanced.providers.opensubtitles.use_https \
|
||||
if self.advanced.providers.opensubtitles.use_https != None else True
|
||||
provider_settings = {'addic7ed': {'username': Prefs['provider.addic7ed.username'],
|
||||
'password': Prefs['provider.addic7ed.password'],
|
||||
'use_random_agents': cast_bool(Prefs['provider.addic7ed.use_random_agents']),
|
||||
'use_random_agents': cast_bool(Prefs['provider.addic7ed.use_random_agents1']),
|
||||
},
|
||||
'opensubtitles': {'username': Prefs['provider.opensubtitles.username'],
|
||||
'password': Prefs['provider.opensubtitles.password'],
|
||||
'use_tag_search': self.exact_filenames,
|
||||
'only_foreign': self.forced_only,
|
||||
'is_vip': cast_bool(Prefs['provider.opensubtitles.is_vip'])
|
||||
'is_vip': cast_bool(Prefs['provider.opensubtitles.is_vip']),
|
||||
'use_ssl': os_use_https,
|
||||
'timeout': self.advanced.providers.opensubtitles.timeout or 15
|
||||
},
|
||||
'podnapisi': {
|
||||
'only_foreign': self.forced_only,
|
||||
},
|
||||
'legendastv': {'username': Prefs['provider.legendastv.username'],
|
||||
'password': Prefs['provider.legendastv.password'],
|
||||
}
|
||||
},
|
||||
'assrt': {'token': Prefs['provider.assrt.token'], }
|
||||
}
|
||||
|
||||
return provider_settings
|
||||
@@ -743,6 +825,8 @@ class Config(object):
|
||||
mods.append("common")
|
||||
if self.colors:
|
||||
mods.append("color(name=%s)" % self.colors)
|
||||
if self.reverse_rtl:
|
||||
mods.append("reverse_rtl")
|
||||
|
||||
return mods
|
||||
|
||||
@@ -786,6 +870,11 @@ class Config(object):
|
||||
if os.path.isfile(fn):
|
||||
return fn
|
||||
|
||||
# look inside Resources folder as fallback, as well
|
||||
fn = os.path.join(base_path, "Resources", "Plex Transcoder")
|
||||
if os.path.isfile(fn):
|
||||
return fn
|
||||
|
||||
def parse_rename_mode(self):
|
||||
# fixme: exact_filenames should be determined via callback combined with info about the current video
|
||||
# (original_name)
|
||||
@@ -816,8 +905,11 @@ class Config(object):
|
||||
if Prefs["drone_api.sonarr.url"] and Prefs["drone_api.sonarr.api_key"]:
|
||||
self.refiner_settings["sonarr"] = {
|
||||
"base_url": Prefs["drone_api.sonarr.url"],
|
||||
"api_key": Prefs["drone_api.sonarr.api_key"]
|
||||
"api_key": Prefs["drone_api.sonarr.api_key"],
|
||||
}
|
||||
if self.advanced.refiners.sonarr:
|
||||
self.refiner_settings["sonarr"].update(self.advanced.refiners.sonarr)
|
||||
|
||||
self.exact_filenames = True
|
||||
|
||||
if Prefs["drone_api.radarr.url"] and Prefs["drone_api.radarr.api_key"]:
|
||||
@@ -825,8 +917,15 @@ class Config(object):
|
||||
"base_url": Prefs["drone_api.radarr.url"],
|
||||
"api_key": Prefs["drone_api.radarr.api_key"]
|
||||
}
|
||||
if self.advanced.refiners.radarr:
|
||||
self.refiner_settings["radarr"].update(self.advanced.refiners.radarr)
|
||||
|
||||
self.exact_filenames = True
|
||||
|
||||
@property
|
||||
def text_based_formats(self):
|
||||
return self.advanced.text_subtitle_formats or TEXT_SUBTITLE_EXTS
|
||||
|
||||
def init_subliminal_patches(self):
|
||||
# configure custom subtitle destination folders for scanning pre-existing subs
|
||||
Log.Debug("Patching subliminal ...")
|
||||
|
||||
@@ -292,7 +292,6 @@ def notify_executable(exe_info, videos, subtitles, storage):
|
||||
prepared_arguments = [arg % prepared_data for arg in arguments]
|
||||
|
||||
Log.Debug(u"Calling %s with arguments: %s" % (exe, prepared_arguments))
|
||||
env = os.environ
|
||||
if not mswindows:
|
||||
env_path = {"PATH": os.pathsep.join(
|
||||
[
|
||||
@@ -303,14 +302,30 @@ def notify_executable(exe_info, videos, subtitles, storage):
|
||||
)
|
||||
}
|
||||
env = dict(os.environ, **env_path)
|
||||
env.pop("LD_LIBRARY_PATH", None)
|
||||
else:
|
||||
env = dict(os.environ)
|
||||
|
||||
# clean out any Plex-PYTHONPATH that may bleed through the spawned process
|
||||
if "PYTHONPATH" in env and "plex" in env["PYTHONPATH"].lower():
|
||||
del env["PYTHONPATH"]
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(quote_args([exe] + prepared_arguments),
|
||||
stderr=subprocess.STDOUT, shell=True, env=env)
|
||||
except subprocess.CalledProcessError:
|
||||
Log.Error(u"Calling %s failed: %s" % (exe, traceback.format_exc()))
|
||||
proc = subprocess.Popen(quote_args([exe] + prepared_arguments), stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, shell=True, env=env, cwd=os.path.dirname(exe))
|
||||
output, errors = proc.communicate()
|
||||
|
||||
if proc.returncode == 1:
|
||||
Log.Error(u"Calling %s with args %s failed: output:\n%s, error:\n%s", exe, prepared_arguments,
|
||||
output, errors)
|
||||
return
|
||||
|
||||
output = output.decode()
|
||||
|
||||
except:
|
||||
Log.Error(u"Calling %s failed: %s", exe, traceback.format_exc())
|
||||
else:
|
||||
Log.Debug(u"Process output: %s" % output)
|
||||
Log.Debug(u"Process output: %s", output)
|
||||
|
||||
|
||||
def track_usage(category=None, action=None, label=None, value=None):
|
||||
@@ -368,13 +383,16 @@ def get_language(lang_short):
|
||||
|
||||
|
||||
def display_language(l):
|
||||
addons = []
|
||||
if l.country:
|
||||
addons.append(l.country.alpha2)
|
||||
if l.script:
|
||||
addons.append(l.script.code)
|
||||
return _(str(l).lower())
|
||||
|
||||
return l.name if not addons else "%s (%s)" % (l.name, ", ".join(addons))
|
||||
|
||||
def is_stream_forced(stream):
|
||||
stream_title = getattr(stream, "title", "") or ""
|
||||
forced = getattr(stream, "forced", False)
|
||||
if not forced and stream_title and "forced" in stream_title.strip().lower():
|
||||
forced = True
|
||||
|
||||
return forced
|
||||
|
||||
|
||||
class PartUnknownException(Exception):
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# coding=utf-8
|
||||
|
||||
import inspect
|
||||
|
||||
from support.config import config
|
||||
|
||||
|
||||
core = getattr(Data, "_core")
|
||||
|
||||
|
||||
# get original localization module in order to access its base classes later on
|
||||
def get_localization_module():
|
||||
cls = getattr(core.localization, "__class__")
|
||||
return inspect.getmodule(cls)
|
||||
|
||||
|
||||
plex_i18n_module = get_localization_module()
|
||||
|
||||
|
||||
def old_style_placeholders_count(s):
|
||||
# fixme: incomplete, use regex
|
||||
return sum(s.count(c) for c in ["%s", "%d", "%r", "%f", "%i"])
|
||||
|
||||
|
||||
def check_old_style_placeholders(k, args):
|
||||
# replace escaped %'s?
|
||||
k = k.__str__().replace("%%", "")
|
||||
|
||||
if "%(" in k:
|
||||
Log.Error(u"%r defines named placeholders for formatting" % k)
|
||||
return "NEEDS NAMED ARGUMENTS"
|
||||
|
||||
placeholders_found = old_style_placeholders_count(k)
|
||||
if placeholders_found and not args:
|
||||
Log.Error(u"%r requires a arguments for formatting" % k)
|
||||
return "NEEDS FORMAT ARGUMENTS"
|
||||
|
||||
elif not placeholders_found and args:
|
||||
Log.Error(u"%r doesn't define placeholders for formatting" % k)
|
||||
return "HAS NO FORMAT ARGUMENTS"
|
||||
|
||||
elif placeholders_found and placeholders_found != len(args):
|
||||
Log.Error(u"%r wrong amount of arguments supplied for formatting" % k)
|
||||
return "WRONG FORMAT ARGUMENT COUNT"
|
||||
|
||||
|
||||
class SmartLocalStringFormatter(plex_i18n_module.LocalStringFormatter):
|
||||
"""
|
||||
this allows the use of dictionaries for string formatting, also does some sanity checking on the keys and values
|
||||
"""
|
||||
def __init__(self, string1, string2, locale=None):
|
||||
if isinstance(string2, tuple):
|
||||
# dictionary passed
|
||||
if len(string2) == 1 and hasattr(string2[0], "iteritems"):
|
||||
string2 = string2[0]
|
||||
if config.debug_i18n:
|
||||
if "%(" not in string1.__str__().replace("%%", ""):
|
||||
Log.Error(u"%r: dictionary for non-named format string supplied" % string1.__str__())
|
||||
string1 = "%s"
|
||||
string2 = "NO NAMED ARGUMENTS"
|
||||
|
||||
# arguments
|
||||
elif len(string2) >= 1 and config.debug_i18n:
|
||||
msg = check_old_style_placeholders(string1, string2)
|
||||
if msg:
|
||||
string1 = "%s"
|
||||
string2 = msg
|
||||
|
||||
setattr(self, "_string1", string1)
|
||||
setattr(self, "_string2", string2)
|
||||
setattr(self, "_locale", locale)
|
||||
|
||||
|
||||
def local_string_with_optional_format(key, *args, **kwargs):
|
||||
if kwargs:
|
||||
args = (kwargs,)
|
||||
else:
|
||||
args = tuple(args)
|
||||
|
||||
if args:
|
||||
# fixme: may not be the best idea as this evaluates the string early
|
||||
try:
|
||||
return unicode(SmartLocalStringFormatter(plex_i18n_module.LocalString(core, key, Locale.CurrentLocale), args))
|
||||
except TypeError:
|
||||
Log.Exception("Broken translation!")
|
||||
return unicode(SmartLocalStringFormatter(plex_i18n_module.LocalString(core, key, "en"), args))
|
||||
|
||||
# check string instances for arguments
|
||||
if config.debug_i18n:
|
||||
msg = check_old_style_placeholders(key, args)
|
||||
if msg:
|
||||
return msg
|
||||
|
||||
try:
|
||||
return unicode(plex_i18n_module.LocalString(core, key, Locale.CurrentLocale))
|
||||
|
||||
except TypeError:
|
||||
Log.Exception("Broken translation!")
|
||||
return unicode(plex_i18n_module.LocalString(core, key, "en"))
|
||||
|
||||
|
||||
_ = local_string_with_optional_format
|
||||
|
||||
|
||||
def is_localized_string(s):
|
||||
return hasattr(s, "localize")
|
||||
@@ -16,6 +16,7 @@ from lib import Plex, get_intent
|
||||
from config import config, IGNORE_FN
|
||||
from subliminal_patch.subtitle import ModifiedSubtitle
|
||||
from subzero.modification import registry as mod_registry, SubtitleModifications
|
||||
from socket import timeout
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -30,7 +31,11 @@ def get_item(key):
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
item_container = Plex["library"].metadata(item_id)
|
||||
try:
|
||||
item_container = Plex["library"].metadata(item_id)
|
||||
except timeout:
|
||||
Log.Debug("PMS API timed out when querying information about item %d", item_id)
|
||||
return
|
||||
|
||||
try:
|
||||
return list(item_container)[0]
|
||||
@@ -348,16 +353,30 @@ def get_current_sub(rating_key, part_id, language, plex_item=None):
|
||||
|
||||
def save_stored_sub(stored_subtitle, rating_key, part_id, language, item_type, plex_item=None, storage=None,
|
||||
stored_subs=None):
|
||||
"""
|
||||
in order for this to work, if the calling supplies stored_subs and storage, it has to trigger its saving and
|
||||
destruction explicitly
|
||||
:param stored_subtitle:
|
||||
:param rating_key:
|
||||
:param part_id:
|
||||
:param language:
|
||||
:param item_type:
|
||||
:param plex_item:
|
||||
:param storage:
|
||||
:param stored_subs:
|
||||
:return:
|
||||
"""
|
||||
from support.plex_media import get_plex_metadata
|
||||
from support.scanning import scan_videos
|
||||
from support.storage import save_subtitles, get_subtitle_storage
|
||||
|
||||
plex_item = plex_item or get_item(rating_key)
|
||||
storage = storage or get_subtitle_storage()
|
||||
|
||||
cleanup = not storage
|
||||
|
||||
stored_subs = stored_subs or storage.load(plex_item.rating_key)
|
||||
stored_subs_was_provided = True
|
||||
if not stored_subs or not storage:
|
||||
storage = get_subtitle_storage()
|
||||
stored_subs = storage.load(plex_item.rating_key)
|
||||
stored_subs_was_provided = False
|
||||
|
||||
if not all([plex_item, stored_subs]):
|
||||
return
|
||||
@@ -396,9 +415,9 @@ def save_stored_sub(stored_subtitle, rating_key, part_id, language, item_type, p
|
||||
|
||||
if subtitle.storage_path:
|
||||
stored_subtitle.last_mod = datetime.datetime.fromtimestamp(os.path.getmtime(subtitle.storage_path))
|
||||
storage.save(stored_subs)
|
||||
|
||||
if cleanup:
|
||||
if not stored_subs_was_provided:
|
||||
storage.save(stored_subs)
|
||||
storage.destroy()
|
||||
|
||||
|
||||
@@ -436,9 +455,9 @@ def set_mods_for_part(rating_key, part_id, language, item_type, mods, mode="add"
|
||||
current_sub.mods.pop()
|
||||
else:
|
||||
raise NotImplementedError("Wrong mode given")
|
||||
storage.save(stored_subs)
|
||||
|
||||
save_stored_sub(current_sub, rating_key, part_id, language, item_type, plex_item=plex_item, storage=storage,
|
||||
stored_subs=stored_subs)
|
||||
|
||||
storage.save(stored_subs)
|
||||
storage.destroy()
|
||||
|
||||
@@ -180,9 +180,10 @@ def get_embedded_subtitle_streams(part, requested_language=None, skip_duplicate_
|
||||
language = helpers.get_language_from_stream(stream.language_code)
|
||||
is_unknown = False
|
||||
found_requested_language = requested_language and requested_language == language
|
||||
is_forced = helpers.is_stream_forced(stream)
|
||||
|
||||
if get_forced is not None:
|
||||
if (get_forced and not stream.forced) or (not get_forced and stream.forced):
|
||||
if (get_forced and not is_forced) or (not get_forced and is_forced):
|
||||
continue
|
||||
|
||||
if not language and config.treat_und_as_first:
|
||||
@@ -194,8 +195,9 @@ def get_embedded_subtitle_streams(part, requested_language=None, skip_duplicate_
|
||||
is_unknown = True
|
||||
has_unknown = True
|
||||
|
||||
if not requested_language or found_requested_language:
|
||||
streams.append({"stream": stream, "is_unknown": is_unknown, "language": language})
|
||||
if not requested_language or found_requested_language or has_unknown:
|
||||
streams.append({"stream": stream, "is_unknown": is_unknown, "language": language,
|
||||
"is_forced": is_forced})
|
||||
|
||||
if found_requested_language:
|
||||
break
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# coding=utf-8
|
||||
import traceback
|
||||
import helpers
|
||||
from babelfish.exceptions import LanguageError
|
||||
|
||||
from support.lib import Plex, get_intent
|
||||
from support.plex_media import get_stream_fps
|
||||
@@ -8,6 +9,7 @@ from support.storage import get_subtitle_storage
|
||||
from support.config import config, TEXT_SUBTITLE_EXTS
|
||||
|
||||
from subzero.video import parse_video, set_existing_languages
|
||||
from subzero.language import language_from_stream
|
||||
|
||||
|
||||
def scan_video(pms_video_info, ignore_all=False, hints=None, rating_key=None, providers=None, skip_hashing=False):
|
||||
@@ -41,39 +43,44 @@ def scan_video(pms_video_info, ignore_all=False, hints=None, rating_key=None, pr
|
||||
plexpy_part = part
|
||||
|
||||
# embedded subtitles
|
||||
# fixme: skip the whole scanning process if known_embedded == wanted languages?
|
||||
if plexpy_part:
|
||||
for stream in plexpy_part.streams:
|
||||
# subtitle stream
|
||||
if stream.stream_type == 3:
|
||||
if (config.forced_only and getattr(stream, "forced")) or \
|
||||
(not config.forced_only and not getattr(stream, "forced")):
|
||||
if embedded_subtitles:
|
||||
for stream in plexpy_part.streams:
|
||||
# subtitle stream
|
||||
if stream.stream_type == 3:
|
||||
is_forced = helpers.is_stream_forced(stream)
|
||||
|
||||
# embedded subtitle
|
||||
# fixme: tap into external subtitles here instead of scanning for ourselves later?
|
||||
if not stream.stream_key and stream.codec:
|
||||
if config.exotic_ext or stream.codec.lower() in TEXT_SUBTITLE_EXTS:
|
||||
lang = helpers.get_language_from_stream(stream.language_code)
|
||||
if (config.forced_only and is_forced) or \
|
||||
(not config.forced_only and not is_forced):
|
||||
|
||||
# treat unknown language as lang1?
|
||||
if not lang and config.treat_und_as_first:
|
||||
lang = list(config.lang_list)[0]
|
||||
# embedded subtitle
|
||||
# fixme: tap into external subtitles here instead of scanning for ourselves later?
|
||||
if stream.codec and getattr(stream, "index", None):
|
||||
if config.exotic_ext or stream.codec.lower() in config.text_based_formats:
|
||||
lang = None
|
||||
try:
|
||||
lang = language_from_stream(stream.language_code)
|
||||
except LanguageError:
|
||||
Log.Debug("Couldn't detect embedded subtitle stream language: %s", stream.language_code)
|
||||
|
||||
if lang:
|
||||
known_embedded.append(lang.alpha3)
|
||||
# treat unknown language as lang1?
|
||||
if not lang and config.treat_und_as_first:
|
||||
lang = list(config.lang_list)[0]
|
||||
|
||||
if lang:
|
||||
known_embedded.append(lang.alpha3)
|
||||
else:
|
||||
Log.Warn("Part %s missing of %s, not able to scan internal streams", plex_part.id, rating_key)
|
||||
|
||||
Log.Debug("Known embedded: %r", known_embedded)
|
||||
|
||||
subtitle_storage = get_subtitle_storage()
|
||||
stored_subs = subtitle_storage.load(rating_key)
|
||||
subtitle_storage.destroy()
|
||||
|
||||
try:
|
||||
# get basic video info scan (filename)
|
||||
# video = parse_video(plex_part.file, pms_video_info, hints, external_subtitles=external_subtitles,
|
||||
# embedded_subtitles=embedded_subtitles, known_embedded=known_embedded,
|
||||
# forced_only=config.forced_only, no_refining=no_refining, ignore_all=ignore_all,
|
||||
# stored_subs=stored_subs, refiner_settings=config.refiner_settings, providers=providers,
|
||||
# skip_hashing=config.low_impact_mode)
|
||||
video = parse_video(plex_part.file, hints, skip_hashing=config.low_impact_mode or skip_hashing,
|
||||
providers=providers)
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ def store_subtitle_info(scanned_video_part_map, downloaded_subtitles, storage_ty
|
||||
part_id = str(part.id)
|
||||
video_id = str(video.id)
|
||||
plex_item = get_item(video_id)
|
||||
if not plex_item:
|
||||
Log.Warning("Plex item not found: %s", video_id)
|
||||
continue
|
||||
|
||||
metadata = video.plexapi_metadata
|
||||
title = get_title_for_video_metadata(metadata)
|
||||
|
||||
@@ -127,7 +131,7 @@ def save_subtitles_to_file(subtitles, tags=None, forced_tag=None):
|
||||
return True
|
||||
|
||||
|
||||
def save_subtitles_to_metadata(videos, subtitles):
|
||||
def save_subtitles_to_metadata(videos, subtitles, is_forced=False):
|
||||
for video, video_subtitles in subtitles.items():
|
||||
mediaPart = videos[video]
|
||||
for subtitle in video_subtitles:
|
||||
@@ -139,12 +143,15 @@ def save_subtitles_to_metadata(videos, subtitles):
|
||||
mp = PMSMediaProxy(video.id).get_part(mediaPart.id)
|
||||
else:
|
||||
mp = mediaPart
|
||||
mp.subtitles[Locale.Language.Match(subtitle.language.alpha2)][subtitle.id] = Proxy.Media(content, ext="srt")
|
||||
pm = Proxy.Media(content, ext="srt", forced="1" if is_forced else None)
|
||||
lang = Locale.Language.Match(subtitle.language.alpha2)
|
||||
mp.subtitles[lang].validate_keys({})
|
||||
mp.subtitles[lang]["subzero"] = pm
|
||||
return True
|
||||
|
||||
|
||||
def save_subtitles(scanned_video_part_map, downloaded_subtitles, mode="a", bare_save=False, mods=None,
|
||||
set_current=True):
|
||||
set_current=True, is_forced=False):
|
||||
"""
|
||||
|
||||
:param set_current: save the subtitle as the current one
|
||||
@@ -179,7 +186,7 @@ def save_subtitles(scanned_video_part_map, downloaded_subtitles, mode="a", bare_
|
||||
if save_to_fs:
|
||||
try:
|
||||
Log.Debug("Using filesystem as subtitle storage")
|
||||
save_subtitles_to_file(downloaded_subtitles)
|
||||
save_subtitles_to_file(downloaded_subtitles, forced_tag=is_forced)
|
||||
except OSError:
|
||||
if cast_bool(Prefs["subtitles.save.metadata_fallback"]):
|
||||
meta_fallback = True
|
||||
@@ -194,12 +201,13 @@ def save_subtitles(scanned_video_part_map, downloaded_subtitles, mode="a", bare_
|
||||
Log.Debug("Using metadata as subtitle storage, because filesystem storage failed")
|
||||
else:
|
||||
Log.Debug("Using metadata as subtitle storage")
|
||||
save_successful = save_subtitles_to_metadata(scanned_video_part_map, downloaded_subtitles)
|
||||
save_successful = save_subtitles_to_metadata(scanned_video_part_map, downloaded_subtitles,
|
||||
is_forced=is_forced)
|
||||
|
||||
if not bare_save and save_successful and config.notify_executable:
|
||||
notify_executable(config.notify_executable, scanned_video_part_map, downloaded_subtitles, storage)
|
||||
|
||||
if not bare_save and (save_successful or not set_current):
|
||||
if not bare_save and save_successful or not set_current:
|
||||
store_subtitle_info(scanned_video_part_map, downloaded_subtitles, storage, mode=mode, set_current=set_current)
|
||||
|
||||
return save_successful
|
||||
|
||||
@@ -196,7 +196,7 @@ def get_subtitles_from_metadata(part):
|
||||
if p_type == "Media":
|
||||
# metadata subtitle
|
||||
Log.Debug(u"Found metadata subtitle: %s, %s" % (language, repr(proxy)))
|
||||
subs[language].append(key)
|
||||
subs[language] = [key]
|
||||
return subs
|
||||
|
||||
|
||||
|
||||
@@ -20,11 +20,9 @@ from support.items import get_recent_items, get_item, is_ignored, get_item_title
|
||||
from support.helpers import track_usage, get_title_for_video_metadata, cast_bool, PartUnknownException
|
||||
from support.plex_media import get_plex_metadata
|
||||
from support.scanning import scan_videos
|
||||
from support.i18n import _
|
||||
from download import download_best_subtitles, pre_download_hook, post_download_hook, language_hook
|
||||
|
||||
PROVIDER_SLACK = 30
|
||||
DL_PROVIDER_SLACK = 30
|
||||
|
||||
|
||||
class Task(object):
|
||||
name = None
|
||||
@@ -34,6 +32,9 @@ class Task(object):
|
||||
time_start = None
|
||||
data = None
|
||||
|
||||
PROVIDER_SLACK = 30
|
||||
DL_PROVIDER_SLACK = 30
|
||||
|
||||
stored_attributes = ("last_run", "last_run_time", "running")
|
||||
default_data = {"last_run": None, "last_run_time": None, "running": False, "data": {}}
|
||||
|
||||
@@ -158,9 +159,14 @@ class SubtitleListingMixin(object):
|
||||
continue
|
||||
|
||||
# skip wrong season/episodes
|
||||
if item_type == "episode" and not {"series", "season", "episode"}.issubset(matches):
|
||||
Log.Debug(u"%s: Skipping %s, because it doesn't match our series/episode", self.name, s)
|
||||
continue
|
||||
if item_type == "episode":
|
||||
can_verify_series = True
|
||||
if not s.hash_verifiable and "hash" in matches:
|
||||
can_verify_series = False
|
||||
|
||||
if can_verify_series and not {"series", "season", "episode"}.issubset(matches):
|
||||
Log.Debug(u"%s: Skipping %s, because it doesn't match our series/episode", self.name, s)
|
||||
continue
|
||||
|
||||
unsorted_subtitles.append(
|
||||
(s, compute_score(matches, s, video, hearing_impaired=use_hearing_impaired), matches))
|
||||
@@ -237,7 +243,9 @@ class DownloadSubtitleMixin(object):
|
||||
if not scheduler.is_task_running("MissingSubtitles"):
|
||||
scheduler.clear_task_data("MissingSubtitles")
|
||||
else:
|
||||
set_refresh_menu_state(u"%s: Subtitle download failed (%s)" % (self.name, rating_key))
|
||||
set_refresh_menu_state(_(u"%(class_name)s: Subtitle download failed (%(item_id)s)",
|
||||
class_name=self.name,
|
||||
item_id=rating_key))
|
||||
return download_successful
|
||||
|
||||
|
||||
@@ -379,18 +387,19 @@ class SearchAllRecentlyAddedMissing(Task):
|
||||
|
||||
def skip_item():
|
||||
self.items_searching = self.items_searching - 1
|
||||
self.percentage = int(self.items_done * 100 / self.items_searching)
|
||||
self.percentage = int(self.items_done * 100 / self.items_searching) if self.items_searching > 0 else 100
|
||||
|
||||
# search for subtitles in viable items
|
||||
try:
|
||||
for fn in recent_files:
|
||||
stored_subs = subtitle_storage.load(filename=fn)
|
||||
video_id = stored_subs.video_id
|
||||
if not stored_subs:
|
||||
Log.Debug("Skipping item %s because storage is empty", video_id)
|
||||
Log.Debug("Skipping item %s because storage is empty", fn)
|
||||
skip_item()
|
||||
continue
|
||||
|
||||
video_id = stored_subs.video_id
|
||||
|
||||
# added_date <= max_search_days?
|
||||
if stored_subs.added_at + datetime.timedelta(days=max_search_days) <= now:
|
||||
Log.Debug("Skipping item %s because it's too old", video_id)
|
||||
@@ -476,8 +485,8 @@ class SearchAllRecentlyAddedMissing(Task):
|
||||
except:
|
||||
Log.Error(u"%s: DEBUG HIT: %s", self.name, traceback.format_exc())
|
||||
|
||||
Log.Debug(u"%s: Waiting %s seconds before continuing", self.name, PROVIDER_SLACK)
|
||||
Thread.Sleep(PROVIDER_SLACK)
|
||||
Log.Debug(u"%s: Waiting %s seconds before continuing", self.name, self.PROVIDER_SLACK)
|
||||
Thread.Sleep(self.PROVIDER_SLACK)
|
||||
|
||||
download_count += downloads_per_video
|
||||
|
||||
@@ -485,18 +494,18 @@ class SearchAllRecentlyAddedMissing(Task):
|
||||
videos_with_downloads += 1
|
||||
|
||||
self.items_done = self.items_done + 1
|
||||
self.percentage = int(self.items_done * 100 / self.items_searching)
|
||||
self.percentage = int(self.items_done * 100 / self.items_searching) if self.items_searching > 0 else 100
|
||||
|
||||
stored_subs = None
|
||||
|
||||
if downloads_per_video:
|
||||
Log.Debug(u"%s: Subtitles have been downloaded, "
|
||||
u"waiting %s seconds before continuing", self.name, DL_PROVIDER_SLACK)
|
||||
Thread.Sleep(DL_PROVIDER_SLACK)
|
||||
u"waiting %s seconds before continuing", self.name, self.DL_PROVIDER_SLACK)
|
||||
Thread.Sleep(self.DL_PROVIDER_SLACK)
|
||||
else:
|
||||
if hit_providers:
|
||||
Log.Debug(u"%s: Waiting %s seconds before continuing", self.name, PROVIDER_SLACK)
|
||||
Thread.Sleep(PROVIDER_SLACK)
|
||||
Log.Debug(u"%s: Waiting %s seconds before continuing", self.name, self.PROVIDER_SLACK)
|
||||
Thread.Sleep(self.PROVIDER_SLACK)
|
||||
finally:
|
||||
subtitle_storage.destroy()
|
||||
history.destroy()
|
||||
@@ -574,7 +583,7 @@ class LegacySearchAllRecentlyAddedMissing(Task):
|
||||
while 1:
|
||||
if item_id in self.items_done:
|
||||
items_done_count += 1
|
||||
self.percentage = int(items_done_count * 100 / missing_count)
|
||||
self.percentage = int(items_done_count * 100 / missing_count) if missing_count > 0 else 100
|
||||
Log.Debug(u"Task: %s, item %s done (%s%%, %s/%s)", self.name, item_id, self.percentage,
|
||||
items_done_count, missing_count)
|
||||
break
|
||||
@@ -641,6 +650,8 @@ class FindBetterSubtitles(DownloadSubtitleMixin, SubtitleListingMixin, Task):
|
||||
now = datetime.datetime.now()
|
||||
min_score_series = int(Prefs["subtitles.search.minimumTVScore2"].strip())
|
||||
min_score_movies = int(Prefs["subtitles.search.minimumMovieScore2"].strip())
|
||||
min_score_extracted_series = config.advanced.find_better_as_extracted_tv_score or 352
|
||||
min_score_extracted_movies = config.advanced.find_better_as_extracted_movie_score or 82
|
||||
overwrite_manually_modified = cast_bool(
|
||||
Prefs["scheduler.tasks.FindBetterSubtitles.overwrite_manually_modified"])
|
||||
overwrite_manually_selected = cast_bool(
|
||||
@@ -666,9 +677,11 @@ class FindBetterSubtitles(DownloadSubtitleMixin, SubtitleListingMixin, Task):
|
||||
if stored_subs.item_type == "episode":
|
||||
cutoff = self.series_cutoff
|
||||
min_score = min_score_series
|
||||
min_score_extracted = min_score_extracted_series
|
||||
else:
|
||||
cutoff = self.movies_cutoff
|
||||
min_score = min_score_movies
|
||||
min_score_extracted = min_score_extracted_movies
|
||||
|
||||
# don't search for better subtitles until at least 30 minutes have passed
|
||||
if stored_subs.added_at + datetime.timedelta(minutes=30) > now:
|
||||
@@ -735,6 +748,13 @@ class FindBetterSubtitles(DownloadSubtitleMixin, SubtitleListingMixin, Task):
|
||||
better_visited = 0
|
||||
for sub in subs:
|
||||
if sub.score > current_score and sub.score > min_score:
|
||||
if current.provider_name == "embedded" and sub.score < min_score_extracted:
|
||||
Log.Debug(u"%s: Not downloading subtitle for %s, we've got an active extracted "
|
||||
u"embedded sub and the min score %s isn't met (%s).",
|
||||
self.name, video_id, min_score_extracted, sub.score)
|
||||
better_visited += 1
|
||||
break
|
||||
|
||||
Log.Debug(u"%s: Better subtitle found for %s, downloading", self.name, video_id)
|
||||
better_tried_download += 1
|
||||
ret = self.download_subtitle(sub, video_id, mode="b")
|
||||
@@ -746,8 +766,8 @@ class FindBetterSubtitles(DownloadSubtitleMixin, SubtitleListingMixin, Task):
|
||||
Log.Debug(u"%s: Couldn't download/save subtitle. "
|
||||
u"Continuing to the next one", self.name)
|
||||
Log.Debug(u"%s: Waiting %s seconds before continuing",
|
||||
self.name, DL_PROVIDER_SLACK)
|
||||
Thread.Sleep(DL_PROVIDER_SLACK)
|
||||
self.name, self.DL_PROVIDER_SLACK)
|
||||
Thread.Sleep(self.DL_PROVIDER_SLACK)
|
||||
better_visited += 1
|
||||
|
||||
if better_tried_download and not better_downloaded:
|
||||
@@ -758,19 +778,19 @@ class FindBetterSubtitles(DownloadSubtitleMixin, SubtitleListingMixin, Task):
|
||||
Log.Debug(u"%s: Better subtitle downloaded for %s", self.name, video_id)
|
||||
|
||||
if better_tried_download or better_downloaded:
|
||||
Log.Debug(u"%s: Waiting %s seconds before continuing", self.name, DL_PROVIDER_SLACK)
|
||||
Thread.Sleep(DL_PROVIDER_SLACK)
|
||||
Log.Debug(u"%s: Waiting %s seconds before continuing", self.name, self.DL_PROVIDER_SLACK)
|
||||
Thread.Sleep(self.DL_PROVIDER_SLACK)
|
||||
|
||||
elif better_visited:
|
||||
Log.Debug(u"%s: Waiting %s seconds before continuing", self.name, PROVIDER_SLACK)
|
||||
Thread.Sleep(PROVIDER_SLACK)
|
||||
Log.Debug(u"%s: Waiting %s seconds before continuing", self.name, self.PROVIDER_SLACK)
|
||||
Thread.Sleep(self.PROVIDER_SLACK)
|
||||
|
||||
subs = None
|
||||
|
||||
elif hit_providers:
|
||||
# hit the providers but didn't try downloading? wait.
|
||||
Log.Debug(u"%s: Waiting %s seconds before continuing", self.name, PROVIDER_SLACK)
|
||||
Thread.Sleep(PROVIDER_SLACK)
|
||||
Log.Debug(u"%s: Waiting %s seconds before continuing", self.name, self.PROVIDER_SLACK)
|
||||
Thread.Sleep(self.PROVIDER_SLACK)
|
||||
|
||||
if ditch_parts:
|
||||
for part_id in ditch_parts:
|
||||
|
||||
+67
-13
@@ -50,7 +50,9 @@
|
||||
"tr",
|
||||
"uk",
|
||||
"vi",
|
||||
"hr"
|
||||
"hr",
|
||||
"zh-hans",
|
||||
"zh-hant"
|
||||
],
|
||||
"default": "en"
|
||||
},
|
||||
@@ -106,7 +108,9 @@
|
||||
"tr",
|
||||
"uk",
|
||||
"vi",
|
||||
"hr"
|
||||
"hr",
|
||||
"zh-hans",
|
||||
"zh-hant"
|
||||
],
|
||||
"default": "None"
|
||||
},
|
||||
@@ -162,7 +166,9 @@
|
||||
"tr",
|
||||
"uk",
|
||||
"vi",
|
||||
"hr"
|
||||
"hr",
|
||||
"zh-hans",
|
||||
"zh-hant"
|
||||
],
|
||||
"default": "None"
|
||||
},
|
||||
@@ -337,10 +343,10 @@
|
||||
"default": "19"
|
||||
},
|
||||
{
|
||||
"id": "provider.addic7ed.use_random_agents",
|
||||
"id": "provider.addic7ed.use_random_agents1",
|
||||
"label": "Addic7ed: Use random user agents",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
"default": "true"
|
||||
},
|
||||
{
|
||||
"id": "provider.legendastv.enabled",
|
||||
@@ -376,10 +382,40 @@
|
||||
},
|
||||
{
|
||||
"id": "provider.subscene.enabled",
|
||||
"label": "Provider: Enable SubScene",
|
||||
"label": "Provider: Enable SubScene (TV shows)",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
},
|
||||
{
|
||||
"id": "provider.supersubtitles.enabled",
|
||||
"label": "Provider: Enable feliratok.info (Hungarian)",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"id": "provider.hosszupuska.enabled",
|
||||
"label": "Provider: Enable hosszupuskasub.com (Hungarian)",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"id": "provider.argenteam.enabled",
|
||||
"label": "Provider: Enable aRGENTeaM (Spanish)",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"id": "provider.assrt.enabled",
|
||||
"label": "Provider: Enable assrt.net (Chinese)",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"id": "provider.assrt.token",
|
||||
"label": "Assrt API Token",
|
||||
"type": "text",
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"id": "providers.multithreading",
|
||||
"label": "Search enabled providers simultaneously (multithreading)",
|
||||
@@ -392,6 +428,12 @@
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"id": "subtitles.search_after_autoextract",
|
||||
"label": "After automatic extraction of embedded subtitles, also immediately search for available subtitles?",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"id": "subtitles.scan.embedded",
|
||||
"label": "Don't search for subtitles of a language if there are embedded subtitles inside the media file (MKV/MP4)?",
|
||||
@@ -459,7 +501,7 @@
|
||||
},
|
||||
{
|
||||
"id": "subtitles.fix_common",
|
||||
"label": "Fix common whitespace/punctuation issues in subtitles",
|
||||
"label": "Fix common issues in subtitles",
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
},
|
||||
@@ -469,6 +511,12 @@
|
||||
"type": "bool",
|
||||
"default": "true"
|
||||
},
|
||||
{
|
||||
"id": "subtitles.reverse_rtl",
|
||||
"label": "Reverse punctuation in RTL languages (heb)",
|
||||
"type": "bool",
|
||||
"default": "false"
|
||||
},
|
||||
{
|
||||
"id": "subtitles.colors",
|
||||
"label": "Change colors of subtitles to",
|
||||
@@ -685,15 +733,15 @@
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"id": "plugin_mode",
|
||||
"id": "plugin_mode2",
|
||||
"label": "Sub-Zero mode",
|
||||
"type": "enum",
|
||||
"values": [
|
||||
"agent + channel",
|
||||
"agent + interface",
|
||||
"only agent",
|
||||
"only channel"
|
||||
"only interface"
|
||||
],
|
||||
"default": "agent + channel"
|
||||
"default": "agent + interface"
|
||||
},
|
||||
{
|
||||
"id": "plugin_pin",
|
||||
@@ -710,12 +758,12 @@
|
||||
"default": "10"
|
||||
},
|
||||
{
|
||||
"id": "plugin_pin_mode",
|
||||
"id": "plugin_pin_mode2",
|
||||
"label": "Use PIN to restrict access to (needs plugin or PMS restart)",
|
||||
"type": "enum",
|
||||
"values": [
|
||||
"disabled",
|
||||
"channel menu",
|
||||
"interface",
|
||||
"advanced menu"
|
||||
],
|
||||
"default": "disabled"
|
||||
@@ -756,6 +804,12 @@
|
||||
"type": "text",
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"id": "path_to_advanced_settings",
|
||||
"label": "Custom path to advanced_settings.json",
|
||||
"type": "text",
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"id": "log_level",
|
||||
"label": "How verbose should the logging be?",
|
||||
|
||||
+6
-4
@@ -9,11 +9,11 @@
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.5.0</string>
|
||||
<string>2.5.4</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.5.0.2241</string>
|
||||
<string>2.5.7.2663</string>
|
||||
<key>PlexFrameworkVersion</key>
|
||||
<string>2</string>
|
||||
<key>PlexPluginClass</key>
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<h1>Sub-Zero for Plex</h1><i>Subtitles done right</i>
|
||||
|
||||
Version 2.5.0.2241
|
||||
Version 2.5.7.2663
|
||||
|
||||
Originally based on @bramwalet's awesome <a href="https://github.com/bramwalet/Subliminal.bundle">Subliminal.bundle</a>
|
||||
|
||||
@@ -44,7 +44,9 @@ Score info: <a href="http://v.ht/szscores">http://v.ht/szscores&
|
||||
Plex thread: <a href="https://forums.plex.tv/discussion/186575">https://forums.plex.tv/discussion/186575</a>
|
||||
Github: <a href="https://github.com/pannal/Sub-Zero.bundle">https://github.com/pannal/Sub-Zero</a>
|
||||
|
||||
panni, 2017
|
||||
3rd party licenses: <a href="https://github.com/pannal/Sub-Zero.bundle/tree/master/Licenses">https://github.com/pannal/Sub-Zero.bundle/tree/master/Licenses</a>
|
||||
|
||||
panni, 2018
|
||||
</div>
|
||||
</string>
|
||||
</dict>
|
||||
|
||||
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -1,3 +1,3 @@
|
||||
from .core import where, old_where
|
||||
|
||||
__version__ = "2017.11.05"
|
||||
__version__ = "2018.01.18"
|
||||
|
||||
@@ -573,78 +573,6 @@ Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2
|
||||
ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
|
||||
# Subject: CN=Chambers of Commerce Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
|
||||
# Label: "Camerfirma Chambers of Commerce Root"
|
||||
# Serial: 0
|
||||
# MD5 Fingerprint: b0:01:ee:14:d9:af:29:18:94:76:8e:f1:69:33:2a:84
|
||||
# SHA1 Fingerprint: 6e:3a:55:a4:19:0c:19:5c:93:84:3c:c0:db:72:2e:31:30:61:f0:b1
|
||||
# SHA256 Fingerprint: 0c:25:8a:12:a5:67:4a:ef:25:f2:8b:a7:dc:fa:ec:ee:a3:48:e5:41:e6:f5:cc:4e:e6:3b:71:b3:61:60:6a:c3
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn
|
||||
MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
|
||||
ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg
|
||||
b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa
|
||||
MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB
|
||||
ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw
|
||||
IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb
|
||||
unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d
|
||||
BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq
|
||||
7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3
|
||||
0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX
|
||||
roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG
|
||||
A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j
|
||||
aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p
|
||||
26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA
|
||||
BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud
|
||||
EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN
|
||||
BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
|
||||
aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB
|
||||
AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd
|
||||
p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi
|
||||
1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc
|
||||
XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0
|
||||
eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu
|
||||
tGWaIZDgqtCYvDi1czyL+Nw=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
|
||||
# Subject: CN=Global Chambersign Root O=AC Camerfirma SA CIF A82743287 OU=http://www.chambersign.org
|
||||
# Label: "Camerfirma Global Chambersign Root"
|
||||
# Serial: 0
|
||||
# MD5 Fingerprint: c5:e6:7b:bf:06:d0:4f:43:ed:c4:7a:65:8a:fb:6b:19
|
||||
# SHA1 Fingerprint: 33:9b:6b:14:50:24:9b:55:7a:01:87:72:84:d9:e0:2f:c3:d2:d8:e9
|
||||
# SHA256 Fingerprint: ef:3c:b4:17:fc:8e:bf:6f:97:87:6c:9e:4e:ce:39:de:1e:a5:fe:64:91:41:d1:02:8b:7d:11:c0:b2:29:8c:ed
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn
|
||||
MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
|
||||
ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo
|
||||
YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9
|
||||
MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy
|
||||
NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G
|
||||
A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA
|
||||
A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0
|
||||
Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s
|
||||
QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV
|
||||
eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795
|
||||
B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh
|
||||
z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T
|
||||
AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i
|
||||
ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w
|
||||
TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH
|
||||
MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD
|
||||
VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE
|
||||
VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
|
||||
bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B
|
||||
AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM
|
||||
bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi
|
||||
ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG
|
||||
VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c
|
||||
ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/
|
||||
AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
|
||||
# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com
|
||||
# Label: "XRamp Global CA Root"
|
||||
@@ -931,38 +859,6 @@ JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
|
||||
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES
|
||||
# Subject: CN=DST ACES CA X6 O=Digital Signature Trust OU=DST ACES
|
||||
# Label: "DST ACES CA X6"
|
||||
# Serial: 17771143917277623872238992636097467865
|
||||
# MD5 Fingerprint: 21:d8:4c:82:2b:99:09:33:a2:eb:14:24:8d:8e:5f:e8
|
||||
# SHA1 Fingerprint: 40:54:da:6f:1c:3f:40:74:ac:ed:0f:ec:cd:db:79:d1:53:fb:90:1d
|
||||
# SHA256 Fingerprint: 76:7c:95:5a:76:41:2c:89:af:68:8e:90:a1:c7:0f:55:6c:fd:6b:60:25:db:ea:10:41:6d:7e:b6:83:1f:8c:40
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb
|
||||
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx
|
||||
ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w
|
||||
MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD
|
||||
VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx
|
||||
FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu
|
||||
ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7
|
||||
gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH
|
||||
fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a
|
||||
ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT
|
||||
ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk
|
||||
c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto
|
||||
dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt
|
||||
aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI
|
||||
hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk
|
||||
QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/
|
||||
h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
|
||||
nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR
|
||||
rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2
|
||||
9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG
|
||||
# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG
|
||||
# Label: "SwissSign Gold CA - G2"
|
||||
@@ -1291,35 +1187,6 @@ fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
|
||||
GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1
|
||||
# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication EV RootCA1
|
||||
# Label: "Security Communication EV RootCA1"
|
||||
# Serial: 0
|
||||
# MD5 Fingerprint: 22:2d:a6:01:ea:7c:0a:f7:f0:6c:56:43:3f:77:76:d3
|
||||
# SHA1 Fingerprint: fe:b8:c4:32:dc:f9:76:9a:ce:ae:3d:d8:90:8f:fd:28:86:65:64:7d
|
||||
# SHA256 Fingerprint: a2:2d:ba:68:1e:97:37:6e:2d:39:7d:72:8a:ae:3a:9b:62:96:b9:fd:ba:60:bc:2e:11:f6:47:f2:c6:75:fb:37
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl
|
||||
MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh
|
||||
U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz
|
||||
MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N
|
||||
IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11
|
||||
bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||
ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE
|
||||
RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO
|
||||
zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5
|
||||
bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF
|
||||
MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1
|
||||
VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC
|
||||
OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G
|
||||
CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW
|
||||
tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ
|
||||
q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb
|
||||
EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+
|
||||
Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O
|
||||
VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed
|
||||
# Subject: CN=OISTE WISeKey Global Root GA CA O=WISeKey OU=Copyright (c) 2005/OISTE Foundation Endorsed
|
||||
# Label: "OISTE WISeKey Global Root GA CA"
|
||||
@@ -2673,45 +2540,6 @@ xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX
|
||||
KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=CA Disig Root R1 O=Disig a.s.
|
||||
# Subject: CN=CA Disig Root R1 O=Disig a.s.
|
||||
# Label: "CA Disig Root R1"
|
||||
# Serial: 14052245610670616104
|
||||
# MD5 Fingerprint: be:ec:11:93:9a:f5:69:21:bc:d7:c1:c0:67:89:cc:2a
|
||||
# SHA1 Fingerprint: 8e:1c:74:f8:a6:20:b9:e5:8a:f4:61:fa:ec:2b:47:56:51:1a:52:c6
|
||||
# SHA256 Fingerprint: f9:6f:23:f4:c3:e7:9c:07:7a:46:98:8d:5a:f5:90:06:76:a0:f0:39:cb:64:5d:d1:75:49:b2:16:c8:24:40:ce
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV
|
||||
BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu
|
||||
MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQy
|
||||
MDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx
|
||||
EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjEw
|
||||
ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy3QRk
|
||||
D2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/o
|
||||
OI7bm+V8u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3A
|
||||
fQ+lekLZWnDZv6fXARz2m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJe
|
||||
IgpFy4QxTaz+29FHuvlglzmxZcfe+5nkCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8n
|
||||
oc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTaYVKvJrT1cU/J19IG32PK
|
||||
/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6vpmumwKj
|
||||
rckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD
|
||||
3AjLLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE
|
||||
7cderVC6xkGbrPAXZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkC
|
||||
yC2fg69naQanMVXVz0tv/wQFx1isXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLd
|
||||
qvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
|
||||
DwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ04IwDQYJKoZI
|
||||
hvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR
|
||||
xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaA
|
||||
SfX8MPWbTx9BLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXo
|
||||
HqJPYNcHKfyyo6SdbhWSVhlMCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpB
|
||||
emOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5GfbVSUZP/3oNn6z4eGBrxEWi1CXYBmC
|
||||
AMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85YmLLW1AL14FABZyb
|
||||
7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKSds+x
|
||||
DzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvk
|
||||
F7mGnjixlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqF
|
||||
a3qdnom2piiZk4hA9z7NUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsT
|
||||
Q6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJa7+h89n07eLw4+1knj0vllJPgFOL
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Issuer: CN=CA Disig Root R2 O=Disig a.s.
|
||||
# Subject: CN=CA Disig Root R2 O=Disig a.s.
|
||||
# Label: "CA Disig Root R2"
|
||||
|
||||
@@ -26,12 +26,12 @@ def where():
|
||||
|
||||
def old_where():
|
||||
warnings.warn(
|
||||
"The weak security bundle is being deprecated. It will be removed in "
|
||||
"2018.",
|
||||
"The weak security bundle has been removed. certifi.old_where() is now an alias "
|
||||
"of certifi.where(). Please update your code to use certifi.where() instead. "
|
||||
"certifi.old_where() will be removed in 2018.",
|
||||
DeprecatedBundleWarning
|
||||
)
|
||||
f = os.path.dirname(__file__)
|
||||
return os.path.join(f, 'weak.pem')
|
||||
return where()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(where())
|
||||
|
||||
@@ -7,11 +7,14 @@ import logging
|
||||
import re
|
||||
import binascii
|
||||
import types
|
||||
import os
|
||||
|
||||
from pipes import quote
|
||||
from lib import find_executable
|
||||
|
||||
mswindows = False
|
||||
if sys.platform == "win32":
|
||||
mswindows = True
|
||||
from pyads import ADS
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -23,7 +26,10 @@ def quote_args(seq):
|
||||
|
||||
def win32_xattr(fn):
|
||||
handler = ADS(fn)
|
||||
return handler.get_stream_content("net.filebot.filename")
|
||||
try:
|
||||
return handler.get_stream_content("net.filebot.filename")
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
|
||||
def default_xattr(fn):
|
||||
@@ -45,18 +51,25 @@ XATTR_MAP = {
|
||||
lambda result: re.search('(?um)(net\.filebot\.filename(?=="|: )[=:" ]+|Attribute.+:\s)([^"\n\r\0]+)',
|
||||
result).group(2)
|
||||
),
|
||||
"darwin": {
|
||||
lambda fn: ["xattr", "-p", "net.filebot.filename", fn],
|
||||
lambda result: binascii.unhexlify(result.replace(' ', '').replace('\n', '')).strip("\x00")
|
||||
},
|
||||
"win32": {
|
||||
# "darwin": (
|
||||
# lambda fn: ["xattr", "-p", "net.filebot.filename", fn],
|
||||
# lambda result: binascii.unhexlify(result.strip().replace(' ', '').replace('\r\n', '').replace('\r', '')
|
||||
# .replace('\n', '')).strip("\x00")
|
||||
# ),
|
||||
"darwin": (
|
||||
lambda fn: ["filebot", "-script", "fn:xattr", fn],
|
||||
lambda result: re.search('(?um)(net\.filebot\.filename(?=="|: )[=:" ]+|Attribute.+:\s)([^"\n\r\0]+)',
|
||||
result).group(2)
|
||||
),
|
||||
"win32": (
|
||||
lambda fn: fn,
|
||||
win32_xattr,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if sys.platform not in XATTR_MAP:
|
||||
default_xattr_bin = find_executable("getfattr") or find_executable("attr") or find_executable("filebot")
|
||||
default_xattr_bin = find_executable("getfattr") or find_executable("attr") or find_executable("filebot") \
|
||||
or "filebot"
|
||||
|
||||
|
||||
def get_filebot_attrs(fn):
|
||||
@@ -76,12 +89,35 @@ def get_filebot_attrs(fn):
|
||||
args = args_func(fn)
|
||||
if isinstance(args, types.ListType):
|
||||
try:
|
||||
output = subprocess.check_output(quote_args(args), stderr=subprocess.PIPE, shell=True)
|
||||
except subprocess.CalledProcessError, e:
|
||||
if e.returncode == 1:
|
||||
logger.info(u"%s: Couldn't get filebot original filename", fn)
|
||||
else:
|
||||
logger.error(u"%s: Unexpected error while getting filebot original filename: %s", fn,
|
||||
env = dict(os.environ)
|
||||
if not mswindows:
|
||||
env_path = {"PATH": os.pathsep.join(
|
||||
[
|
||||
"/usr/local/bin",
|
||||
"/usr/bin",
|
||||
"/usr/local/sbin",
|
||||
"/usr/sbin",
|
||||
os.environ.get("PATH", "")
|
||||
]
|
||||
)
|
||||
}
|
||||
env = dict(os.environ, **env_path)
|
||||
|
||||
env.pop("LD_LIBRARY_PATH", None)
|
||||
|
||||
proc = subprocess.Popen(quote_args(args), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True,
|
||||
env=env)
|
||||
output, errors = proc.communicate()
|
||||
|
||||
if proc.returncode == 1:
|
||||
logger.info(u"%s: Couldn't get filebot original filename, args: %r, output: %r, error: %r", fn, args,
|
||||
output, errors)
|
||||
return
|
||||
|
||||
output = output.decode()
|
||||
|
||||
except:
|
||||
logger.error(u"%s: Unexpected error while getting filebot original filename: %s", fn,
|
||||
traceback.format_exc())
|
||||
return
|
||||
else:
|
||||
@@ -92,6 +128,7 @@ def get_filebot_attrs(fn):
|
||||
return orig_fn.strip()
|
||||
except:
|
||||
logger.info(u"%s: Couldn't get filebot original filename" % fn)
|
||||
logger.debug(u"%s: Result: %r" % (fn, output))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
# addic7ed
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); import subliminal_patch, subliminal; subliminal.region.configure('dogpile.cache.memory'); from subliminal_patch.core import SZProviderPool; ProviderPool(providers=['addic7ed'], provider_configs={'addic7ed': {'use_random_agents': True}})['addic7ed'].query('Game of Thrones', 2)"
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import os; os.environ['U1pfT01EQl9LRVk'] = '789CF30DAC2C8B0AF433F5C9AD34290A712DF30D7135F12D0FB3E502006FDE081E'; import subliminal_patch, subliminal; from subliminal_patch.core import SZProviderPool; from babelfish import Language; subliminal.region.configure('dogpile.cache.memory'); from subzero.video import parse_video; video = parse_video('FILE_NAME', hints={'type': 'episode'}, dry_run=True); provider = SZProviderPool(providers=['addic7ed'], provider_configs={'addic7ed': {'use_random_agents': True}} )['addic7ed']; subtitle = provider.list_subtitles(video, languages=[Language('fra')]); provider.download_subtitle(subtitle[0]);"
|
||||
|
||||
# opensubtitles
|
||||
# addic7ed download_best
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import os; os.environ['U1pfT01EQl9LRVk'] = '789CF30DAC2C8B0AF433F5C9AD34290A712DF30D7135F12D0FB3E502006FDE081E'; import subliminal_patch, subliminal; from subliminal_patch.core import SZProviderPool, download_best_subtitles; from subliminal_patch.score import compute_score; from babelfish import Language; subliminal.region.configure('dogpile.cache.memory'); from subzero.video import parse_video; video = parse_video('FILE_NAME', hints={'type': 'episode'}, dry_run=True); download_best_subtitles([video], pool_class=SZProviderPool, compute_score=compute_score, providers=['addic7ed'], provider_configs={'addic7ed': {'use_random_agents': True}}, languages={Language('fra')} )"
|
||||
|
||||
# opensubtitles:list
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import subliminal_patch, subliminal; subliminal.region.configure('dogpile.cache.memory'); from subliminal_patch.core import SZProviderPool; from babelfish import Language; from subzero.video import parse_video; SZProviderPool(providers=['opensubtitles'], )['opensubtitles'].list_subtitles(parse_video('FULL_PATH', {}, {'type': 'episode'}), languages=[Language('eng')])"
|
||||
|
||||
# opensubtitles:download
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import subliminal_patch, subliminal; subliminal.region.configure('dogpile.cache.memory'); from subliminal_patch.core import SZProviderPool, download_best_subtitles; from subliminal_patch.score import compute_score; from babelfish import Language; from subzero.video import parse_video; video = parse_video('FILE_NAME', hints={'type': 'movie'}, dry_run=True); download_best_subtitles([video], pool_class=SZProviderPool, compute_score=compute_score, providers=['opensubtitles'], languages={Language('srp')} )"
|
||||
|
||||
# podnapisi
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); import subliminal_patch, subliminal; subliminal.region.configure('dogpile.cache.memory'); from subliminal_patch.core import SZProviderPool; from babelfish import Language; SZProviderPool(providers=['podnapisi'], )['podnapisi'].query([Language('eng')], 'Game of Thrones', season=2, episode=1)"
|
||||
|
||||
@@ -16,6 +22,17 @@ python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.get
|
||||
# napiprojekt:download
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import subliminal_patch, subliminal; subliminal.region.configure('dogpile.cache.memory'); from subliminal_patch.core import SZProviderPool; from subliminal_patch.score import compute_score; from subliminal import download_best_subtitles; from babelfish import Language; from subliminal.core import scan_video; subs = download_best_subtitles([scan_video('FULL_PATH')], languages={Language('eng')}, providers=['napiprojekt'], pool_class=SZProviderPool, compute_score=compute_score); print subs.values()[0][0].is_valid()"
|
||||
|
||||
# argenteam:list
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import subliminal_patch, subliminal; subliminal.region.configure('dogpile.cache.memory'); from subliminal_patch.core import SZProviderPool; from babelfish import Language; from subzero.video import parse_video; video = parse_video('FILE_NAME', hints={'type': 'movie'}, dry_run=True); print SZProviderPool(providers=['argenteam'], )['argenteam'].list_subtitles(video, languages=[Language('spa')])"
|
||||
|
||||
# argenteam:download
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import subliminal_patch, subliminal; subliminal.region.configure('dogpile.cache.memory'); from subliminal_patch.core import SZProviderPool, download_best_subtitles; from subliminal_patch.score import compute_score; from babelfish import Language; from subzero.video import parse_video; video = parse_video('FILE_NAME', hints={'type': 'movie'}, dry_run=True); download_best_subtitles([video], pool_class=SZProviderPool, compute_score=compute_score, providers=['argenteam'], languages={Language('spa')} )"
|
||||
|
||||
# hosszupuska:list
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import subliminal_patch, subliminal; subliminal.region.configure('dogpile.cache.memory'); from subliminal_patch.core import SZProviderPool; from babelfish import Language; from subzero.video import parse_video; video = parse_video('FILE_NAME', hints={'type': 'episode'}, dry_run=True); print SZProviderPool(providers=['hosszupuska'], )['hosszupuska'].list_subtitles(video, languages=[Language('hun')])"
|
||||
|
||||
# hosszupuska:download
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import subliminal_patch, subliminal; subliminal.region.configure('dogpile.cache.memory'); from subliminal_patch.core import SZProviderPool, download_best_subtitles; from subliminal_patch.score import compute_score; from babelfish import Language; from subzero.video import parse_video; video = parse_video('FILE_INFO', hints={'type': 'episode'}, dry_run=True); download_best_subtitles([video], pool_class=SZProviderPool, compute_score=compute_score, providers=['hosszupuska'], languages={Language('hun')} )"
|
||||
|
||||
# shooter:list
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import subliminal_patch, subliminal; subliminal.region.configure('dogpile.cache.memory'); from subliminal_patch.core import SZProviderPool; from babelfish import Language; from subliminal.core import scan_video; print SZProviderPool(providers=['shooter'], )['shooter'].list_subtitles(scan_video('FULL_PATH'), languages=[Language('zho')])"
|
||||
@@ -28,4 +45,4 @@ python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.get
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import os; os.environ['U1pfT01EQl9LRVk'] = '789CF30DAC2C8B0AF433F5C9AD34290A712DF30D7135F12D0FB3E502006FDE081E'; import subliminal_patch, subliminal; subliminal.region.configure('dogpile.cache.memory'); from subzero.video import parse_video, refine_video; video = parse_video('FILE_NAME', {'type': 'episode'}, dry_run=True); print refine_video(video)"
|
||||
|
||||
# full test with download
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import os; os.environ['U1pfT01EQl9LRVk'] = '789CF30DAC2C8B0AF433F5C9AD34290A712DF30D7135F12D0FB3E502006FDE081E'; import subliminal_patch, subliminal; from subliminal_patch.core import SZProviderPool; from babelfish import Language; subliminal.region.configure('dogpile.cache.memory'); from subzero.video import parse_video; video = parse_video('FILE_NAME', {}, hints={'type': 'episode'}, dry_run=True); subtitle = SZProviderPool(providers=['titlovi'], )['titlovi'].list_subtitles(video, languages=[Language('hrv')]); SZProviderPool(providers=['titlovi'], )['titlovi'].download_subtitle(subtitle[0]);"
|
||||
python -c "import logging; logging.basicConfig(level=logging.DEBUG); logging.getLogger('rebulk').setLevel(logging.WARNING); import os; os.environ['U1pfT01EQl9LRVk'] = '789CF30DAC2C8B0AF433F5C9AD34290A712DF30D7135F12D0FB3E502006FDE081E'; import subliminal_patch, subliminal; from subliminal_patch.core import SZProviderPool; from babelfish import Language; subliminal.region.configure('dogpile.cache.memory'); from subzero.video import parse_video; video = parse_video('FILE_NAME', hints={'type': 'episode'}, dry_run=True); subtitle = SZProviderPool(providers=['titlovi'], )['titlovi'].list_subtitles(video, languages=[Language('hrv')]); SZProviderPool(providers=['titlovi'], )['titlovi'].download_subtitle(subtitle[0]);"
|
||||
@@ -170,6 +170,9 @@ else: # pragma: no cover
|
||||
unicode = str
|
||||
_byte_code = int # noqa
|
||||
|
||||
# don't break 2.6 completely
|
||||
if sys.hexversion < 0x2070000:
|
||||
memoryview = lambda x: x # noqa
|
||||
|
||||
__version__ = '3.0'
|
||||
|
||||
@@ -215,6 +218,12 @@ ALT_EXTRACT_ARGS = ('-x', '-f')
|
||||
ALT_TEST_ARGS = ('-t', '-f')
|
||||
ALT_CHECK_ARGS = ('--help',)
|
||||
|
||||
#ALT_TOOL = 'unar'
|
||||
#ALT_OPEN_ARGS = ('-o', '-')
|
||||
#ALT_EXTRACT_ARGS = ()
|
||||
#ALT_TEST_ARGS = ('-test',) # does not work
|
||||
#ALT_CHECK_ARGS = ('-v',)
|
||||
|
||||
#: whether to speed up decompression by using tmp archive
|
||||
USE_EXTRACT_HACK = 1
|
||||
|
||||
@@ -2525,6 +2534,53 @@ class Blake2SP(object):
|
||||
"""Hexadecimal digest."""
|
||||
return tohex(self.digest())
|
||||
|
||||
|
||||
class Rar3Sha1(object):
|
||||
"""Bug-compat for SHA1
|
||||
"""
|
||||
digest_size = 20
|
||||
block_size = 64
|
||||
|
||||
_BLK_BE = struct.Struct(b'>16L')
|
||||
_BLK_LE = struct.Struct(b'<16L')
|
||||
|
||||
__slots__ = ('_nbytes', '_md', '_rarbug')
|
||||
|
||||
def __init__(self, data=b'', rarbug=False):
|
||||
self._md = sha1()
|
||||
self._nbytes = 0
|
||||
self._rarbug = rarbug
|
||||
self.update(data)
|
||||
|
||||
def update(self, data):
|
||||
"""Process more data."""
|
||||
self._md.update(data)
|
||||
bufpos = self._nbytes & 63
|
||||
self._nbytes += len(data)
|
||||
|
||||
if self._rarbug and len(data) > 64:
|
||||
dpos = self.block_size - bufpos
|
||||
while dpos + self.block_size <= len(data):
|
||||
self._corrupt(data, dpos)
|
||||
dpos += self.block_size
|
||||
|
||||
def digest(self):
|
||||
"""Return final state."""
|
||||
return self._md.digest()
|
||||
|
||||
def hexdigest(self):
|
||||
"""Return final state as hex string."""
|
||||
return self._md.hexdigest()
|
||||
|
||||
def _corrupt(self, data, dpos):
|
||||
"""Corruption from SHA1 core."""
|
||||
ws = list(self._BLK_BE.unpack_from(data, dpos))
|
||||
for t in range(16, 80):
|
||||
tmp = ws[(t - 3) & 15] ^ ws[(t - 8) & 15] ^ ws[(t - 14) & 15] ^ ws[(t - 16) & 15]
|
||||
ws[t & 15] = ((tmp << 1) | (tmp >> (32 - 1))) & 0xFFFFFFFF
|
||||
self._BLK_LE.pack_into(data, dpos, *ws)
|
||||
|
||||
|
||||
##
|
||||
## Utility functions
|
||||
##
|
||||
@@ -2686,13 +2742,14 @@ def rar3_s2k(psw, salt):
|
||||
"""
|
||||
if not isinstance(psw, unicode):
|
||||
psw = psw.decode('utf8')
|
||||
seed = psw.encode('utf-16le') + salt
|
||||
seed = bytearray(psw.encode('utf-16le') + salt)
|
||||
h = Rar3Sha1(rarbug=True)
|
||||
iv = EMPTY
|
||||
h = sha1()
|
||||
for i in range(16):
|
||||
for j in range(0x4000):
|
||||
cnt = S_LONG.pack(i * 0x4000 + j)
|
||||
h.update(seed + cnt[:3])
|
||||
h.update(seed)
|
||||
h.update(cnt[:3])
|
||||
if j == 0:
|
||||
iv += h.digest()[19:20]
|
||||
key_be = h.digest()[:16]
|
||||
@@ -2814,6 +2871,8 @@ def custom_popen(cmd):
|
||||
except OSError as ex:
|
||||
if ex.errno == errno.ENOENT:
|
||||
raise RarCannotExec("Unrar not installed? (rarfile.UNRAR_TOOL=%r)" % UNRAR_TOOL)
|
||||
if ex.errno == errno.EACCES or ex.errno == errno.EPERM:
|
||||
raise RarCannotExec("Cannot execute unrar (rarfile.UNRAR_TOOL=%r)" % UNRAR_TOOL)
|
||||
raise
|
||||
return p
|
||||
|
||||
@@ -2945,7 +3004,9 @@ def _check_unrar_tool():
|
||||
TEST_ARGS = ALT_TEST_ARGS
|
||||
except RarCannotExec:
|
||||
# no usable tool, only uncompressed archives work
|
||||
pass
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
try:
|
||||
_check_unrar_tool()
|
||||
|
||||
@@ -556,7 +556,13 @@ elif sys.platform.startswith(('linux', 'darwin', 'sunos5')) or 'bsd' in sys.plat
|
||||
name = entry.d_name
|
||||
if name not in (b'.', b'..'):
|
||||
if not is_bytes:
|
||||
name = name.decode(file_system_encoding)
|
||||
try:
|
||||
name = name.decode(file_system_encoding)
|
||||
except UnicodeDecodeError:
|
||||
try:
|
||||
name = name.decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
yield PosixDirEntry(path, name, entry.d_type, entry.d_ino)
|
||||
finally:
|
||||
if closedir(dir_p):
|
||||
@@ -667,5 +673,8 @@ else:
|
||||
|
||||
def walk(top, topdown=True, onerror=None, followlinks=False):
|
||||
if isinstance(top, bytes):
|
||||
top = top.decode(file_system_encoding)
|
||||
try:
|
||||
top = top.decode(file_system_encoding)
|
||||
except UnicodeDecodeError:
|
||||
top = top.decode("utf-8")
|
||||
return _walk(top, topdown, onerror, followlinks)
|
||||
|
||||
@@ -15,6 +15,7 @@ if not is_windows_special_path:
|
||||
else:
|
||||
ThreadPoolExecutor = object
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
import io
|
||||
import itertools
|
||||
|
||||
@@ -231,18 +231,12 @@ class Addic7edProvider(Provider):
|
||||
|
||||
# search as last resort
|
||||
if not show_id:
|
||||
logger.warning('Series not found in show ids')
|
||||
logger.warning('Series %s not found in show ids', series)
|
||||
show_id = self._search_show_id(series)
|
||||
|
||||
return show_id
|
||||
|
||||
def query(self, series, season, year=None, country=None):
|
||||
# get the show id
|
||||
show_id = self.get_show_id(series, year, country)
|
||||
if show_id is None:
|
||||
logger.error('No show id found for %r (%r)', series, {'year': year, 'country': country})
|
||||
return []
|
||||
|
||||
def query(self, show_id, series, season, year=None, country=None):
|
||||
# get the page of the season of the show
|
||||
logger.info('Getting the page of show id %d, season %d', show_id, season)
|
||||
r = self.session.get(self.server_url + 'show/%d' % show_id, params={'season': season}, timeout=10)
|
||||
@@ -288,12 +282,22 @@ class Addic7edProvider(Provider):
|
||||
return subtitles
|
||||
|
||||
def list_subtitles(self, video, languages):
|
||||
# lookup show_id
|
||||
titles = [video.series] + video.alternative_series
|
||||
show_id = None
|
||||
for title in titles:
|
||||
subtitles = [s for s in self.query(title, video.season, video.year)
|
||||
show_id = self.get_show_id(title, video.year)
|
||||
if show_id is not None:
|
||||
break
|
||||
|
||||
# query for subtitles with the show_id
|
||||
if show_id is not None:
|
||||
subtitles = [s for s in self.query(show_id, title, video.season, video.year)
|
||||
if s.language in languages and s.episode == video.episode]
|
||||
if subtitles:
|
||||
return subtitles
|
||||
else:
|
||||
logger.error('No show id found for %r (%r)', video.series, {'year': video.year})
|
||||
|
||||
return []
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ class OpenSubtitlesProvider(Provider):
|
||||
if isinstance(video, Episode):
|
||||
query = video.series
|
||||
season = video.season
|
||||
episode = min(video.episode) if isinstance(video.episode, list) else video.episode
|
||||
episode = video.episode
|
||||
else:
|
||||
query = video.title
|
||||
|
||||
|
||||
@@ -163,18 +163,9 @@ class TVsubtitlesProvider(Provider):
|
||||
|
||||
return episode_ids
|
||||
|
||||
def query(self, series, season, episode, year=None):
|
||||
# search the show id
|
||||
show_id = self.search_show_id(series, year)
|
||||
if show_id is None:
|
||||
logger.error('No show id found for %r (%r)', series, {'year': year})
|
||||
return []
|
||||
|
||||
def query(self, show_id, series, season, episode, year=None):
|
||||
# get the episode ids
|
||||
episode_ids = self.get_episode_ids(show_id, season)
|
||||
# Provider doesn't store multi episode information
|
||||
episode = min(episode) if episode and isinstance(episode, list) else episode
|
||||
|
||||
if episode not in episode_ids:
|
||||
logger.error('Episode %d not found', episode)
|
||||
return []
|
||||
@@ -202,12 +193,22 @@ class TVsubtitlesProvider(Provider):
|
||||
return subtitles
|
||||
|
||||
def list_subtitles(self, video, languages):
|
||||
# lookup show_id
|
||||
titles = [video.series] + video.alternative_series
|
||||
show_id = None
|
||||
for title in titles:
|
||||
subtitles = [s for s in self.query(title, video.season, video.episode, video.year)
|
||||
if s.language in languages]
|
||||
show_id = self.search_show_id(title, video.year)
|
||||
if show_id is not None:
|
||||
break
|
||||
|
||||
# query for subtitles with the show_id
|
||||
if show_id is not None:
|
||||
subtitles = [s for s in self.query(show_id, title, video.season, video.episode, video.year)
|
||||
if s.language in languages and s.episode == video.episode]
|
||||
if subtitles:
|
||||
return subtitles
|
||||
else:
|
||||
logger.error('No show id found for %r (%r)', video.series, {'year': video.year})
|
||||
|
||||
return []
|
||||
|
||||
|
||||
@@ -208,8 +208,14 @@ def guess_matches(video, guess, partial=False):
|
||||
if video.season and 'season' in guess and guess['season'] == video.season:
|
||||
matches.add('season')
|
||||
# episode
|
||||
if video.episode and 'episode' in guess and guess['episode'] == video.episode:
|
||||
matches.add('episode')
|
||||
# Currently we only have single-ep support (guessit returns a multi-ep as a list with int values)
|
||||
# Most providers only support single-ep, so make sure it contains only 1 episode
|
||||
# In case of multi-ep, take the lowest episode (subtitles will normally be available on lowest episode number)
|
||||
if video.episode and 'episode' in guess:
|
||||
episode_guess = guess['episode']
|
||||
episode = min(episode_guess) if episode_guess and isinstance(episode_guess, list) else episode_guess
|
||||
if episode == video.episode:
|
||||
matches.add('episode')
|
||||
# year
|
||||
if video.year and 'year' in guess and guess['year'] == video.year:
|
||||
matches.add('year')
|
||||
|
||||
@@ -169,7 +169,13 @@ class Episode(Video):
|
||||
if 'title' not in guess or 'episode' not in guess:
|
||||
raise ValueError('Insufficient data to process the guess')
|
||||
|
||||
return cls(name, guess['title'], guess.get('season', 1), guess['episode'], title=guess.get('episode_title'),
|
||||
# Currently we only have single-ep support (guessit returns a multi-ep as a list with int values)
|
||||
# Most providers only support single-ep, so make sure it contains only 1 episode
|
||||
# In case of multi-ep, take the lowest episode (subtitles will normally be available on lowest episode number)
|
||||
episode_guess = guess.get('episode')
|
||||
episode = min(episode_guess) if episode_guess and isinstance(episode_guess, list) else episode_guess
|
||||
|
||||
return cls(name, guess['title'], guess.get('season', 1), episode, title=guess.get('episode_title'),
|
||||
year=guess.get('year'), format=guess.get('format'), original_series='year' not in guess,
|
||||
release_group=guess.get('release_group'), resolution=guess.get('screen_size'),
|
||||
video_codec=guess.get('video_codec'), audio_codec=guess.get('audio_codec'))
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from babelfish import LanguageReverseConverter
|
||||
from subliminal.exceptions import ConfigurationError
|
||||
|
||||
class AssrtConverter(LanguageReverseConverter):
|
||||
def __init__(self):
|
||||
self.from_assrt = { u'简体': ('zho', None, 'Hans'), u'繁体': ('zho', None, 'Hant'),
|
||||
u'簡體': ('zho', None, 'Hans'), u'繁體': ('zho', None, 'Hant'),
|
||||
u'英文': ('eng',),
|
||||
u'chs': ('zho', None, 'Hans'), u'cht': ('zho', None, 'Hant'),
|
||||
u'chn': ('zho', None, 'Hans'), u'twn': ('zho', None, 'Hant')}
|
||||
self.to_assrt = { ('zho', None, 'Hans'): u'chs', ('zho', None, 'Hant'): u'cht', ('eng',) : u'eng' }
|
||||
self.codes = set(self.from_assrt.keys())
|
||||
|
||||
def convert(self, alpha3, country=None, script=None):
|
||||
if (alpha3, country, script) in self.to_assrt:
|
||||
return self.to_assrt[(alpha3, country, script)]
|
||||
|
||||
raise ConfigurationError('Unsupported language for assrt: %s, %s, %s' % (alpha3, country, script))
|
||||
|
||||
def reverse(self, assrt):
|
||||
if assrt in self.from_assrt:
|
||||
return self.from_assrt[assrt]
|
||||
|
||||
raise ConfigurationError('Unsupported language code for assrt: %s' % assrt)
|
||||
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from babelfish import LanguageReverseConverter, language_converters
|
||||
|
||||
|
||||
class HosszupuskaConverter(LanguageReverseConverter):
|
||||
def __init__(self):
|
||||
self.alpha2_converter = language_converters['alpha2']
|
||||
self.from_hosszupuska = {'hu': ('hun', ), 'en': ('eng',)}
|
||||
self.to_hosszupuska = {v: k for k, v in self.from_hosszupuska.items()}
|
||||
self.codes = self.alpha2_converter.codes | set(self.from_hosszupuska.keys())
|
||||
|
||||
def convert(self, alpha3, country=None, script=None):
|
||||
if (alpha3, country) in self.to_hosszupuska:
|
||||
return self.to_hosszupuska[(alpha3, country)]
|
||||
if (alpha3,) in self.to_hosszupuska:
|
||||
return self.to_hosszupuska[(alpha3,)]
|
||||
|
||||
return self.alpha2_converter.convert(alpha3, country, script)
|
||||
|
||||
def reverse(self, hosszupuska):
|
||||
if hosszupuska in self.from_hosszupuska:
|
||||
return self.from_hosszupuska[hosszupuska]
|
||||
|
||||
return self.alpha2_converter.reverse(hosszupuska)
|
||||
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from babelfish import LanguageReverseConverter, language_converters
|
||||
|
||||
|
||||
class SuperSubtitlesConverter(LanguageReverseConverter):
|
||||
def __init__(self):
|
||||
self.alpha2_converter = language_converters['alpha2']
|
||||
self.from_supersubtitles = {'hu': ('hun', ), 'en': ('eng',)}
|
||||
self.to_supersubtitles = {v: k for k, v in self.from_supersubtitles.items()}
|
||||
self.codes = self.alpha2_converter.codes | set(self.from_supersubtitles.keys())
|
||||
|
||||
def convert(self, alpha3, country=None, script=None):
|
||||
if (alpha3, country) in self.to_supersubtitles:
|
||||
return self.to_supersubtitles[(alpha3, country)]
|
||||
if (alpha3,) in self.to_supersubtitles:
|
||||
return self.to_supersubtitles[(alpha3,)]
|
||||
|
||||
return self.alpha2_converter.convert(alpha3, country, script)
|
||||
|
||||
def reverse(self, supersubtitles):
|
||||
if supersubtitles in self.from_supersubtitles:
|
||||
return self.from_supersubtitles[supersubtitles]
|
||||
|
||||
return self.alpha2_converter.reverse(supersubtitles)
|
||||
@@ -10,6 +10,7 @@ import time
|
||||
import operator
|
||||
|
||||
import itertools
|
||||
from httplib import ResponseNotReady
|
||||
|
||||
import rarfile
|
||||
import requests
|
||||
@@ -18,7 +19,6 @@ from collections import defaultdict
|
||||
from bs4 import UnicodeDammit
|
||||
from babelfish import LanguageReverseError
|
||||
from guessit.jsonutils import GuessitEncoder
|
||||
from scandir import scandir
|
||||
from subliminal import ProviderError, refiner_manager
|
||||
|
||||
from extensions import provider_registry
|
||||
@@ -31,6 +31,7 @@ from subliminal.core import guessit, ProviderPool, io, is_windows_special_path,
|
||||
from subliminal_patch.exceptions import TooManyRequests
|
||||
|
||||
from subzero.language import Language
|
||||
from scandir import scandir
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -43,11 +44,20 @@ DOWNLOAD_RETRY_SLEEP = 6
|
||||
|
||||
# fixme: this may be overkill
|
||||
REMOVE_CRAP_FROM_FILENAME = re.compile(r"(?i)(?:([\s_-]+(?:obfuscated|scrambled|nzbgeek|chamele0n|buymore|xpost|postbot"
|
||||
r"|asrequested)(?:\[.+\])?)|[\s_-]\w{2,}(\[.+\]))(?=\.\w+$|$)")
|
||||
r"|asrequested)(?:\[.+\])?)|([\s_-]\w{2,})(\[.+\]))(?=\.\w+$|$)")
|
||||
|
||||
SUBTITLE_EXTENSIONS = ('.srt', '.sub', '.smi', '.txt', '.ssa', '.ass', '.mpl', '.vtt')
|
||||
|
||||
|
||||
def remove_crap_from_fn(fn):
|
||||
# in case of the second regex part, the legit release group name will be in group(2), if it's followed by [string]
|
||||
# otherwise replace fully, because the first part matched
|
||||
def repl(m):
|
||||
return m.group(2) if len(m.groups()) == 3 else ""
|
||||
|
||||
return REMOVE_CRAP_FROM_FILENAME.sub(repl, fn)
|
||||
|
||||
|
||||
class SZProviderPool(ProviderPool):
|
||||
def __init__(self, providers=None, provider_configs=None, blacklist=None, throttle_callback=None,
|
||||
pre_download_hook=None, post_download_hook=None, language_hook=None):
|
||||
@@ -121,7 +131,10 @@ class SZProviderPool(ProviderPool):
|
||||
:rtype: list of :class:`~subliminal.subtitle.Subtitle` or None
|
||||
|
||||
"""
|
||||
languages_search_base = self.language_hook(provider)
|
||||
if self.language_hook:
|
||||
languages_search_base = self.language_hook(provider)
|
||||
else:
|
||||
languages_search_base = languages
|
||||
|
||||
# check video validity
|
||||
if not provider_registry[provider].check(video):
|
||||
@@ -143,8 +156,19 @@ class SZProviderPool(ProviderPool):
|
||||
|
||||
# list subtitles
|
||||
logger.info('Listing subtitles with provider %r and languages %r', provider, provider_languages)
|
||||
results = []
|
||||
try:
|
||||
results = self[provider].list_subtitles(video, provider_languages)
|
||||
try:
|
||||
results = self[provider].list_subtitles(video, provider_languages)
|
||||
except ResponseNotReady:
|
||||
logger.error('Provider %r response error, reinitializing', provider)
|
||||
try:
|
||||
self[provider].terminate()
|
||||
self[provider].initialize()
|
||||
results = self[provider].list_subtitles(video, provider_languages)
|
||||
except:
|
||||
logger.error('Provider %r reinitialization error: %s', provider, traceback.format_exc())
|
||||
|
||||
seen = []
|
||||
out = []
|
||||
for s in results:
|
||||
@@ -245,8 +269,18 @@ class SZProviderPool(ProviderPool):
|
||||
socket.timeout):
|
||||
logger.error('Provider %r connection error', subtitle.provider_name)
|
||||
|
||||
except ResponseNotReady:
|
||||
logger.error('Provider %r response error, reinitializing', subtitle.provider_name)
|
||||
try:
|
||||
self[subtitle.provider_name].terminate()
|
||||
self[subtitle.provider_name].initialize()
|
||||
except:
|
||||
logger.error('Provider %r reinitialization error: %s', subtitle.provider_name,
|
||||
traceback.format_exc())
|
||||
|
||||
except rarfile.BadRarFile:
|
||||
logger.error('Malformed RAR file from provider %r, skipping subtitle.', subtitle.provider_name)
|
||||
logger.debug("RAR Traceback: %s", traceback.format_exc())
|
||||
return False
|
||||
|
||||
except (TooManyRequests, DownloadLimitExceeded, ServiceUnavailable), e:
|
||||
@@ -319,16 +353,18 @@ class SZProviderPool(ProviderPool):
|
||||
logger.error("%r: Match computation failed: %s", s, traceback.format_exc())
|
||||
continue
|
||||
|
||||
orig_matches = matches.copy()
|
||||
|
||||
logger.debug('%r: Found matches %r', s, matches)
|
||||
unsorted_subtitles.append(
|
||||
(s, compute_score(matches, s, video, hearing_impaired=use_hearing_impaired), matches))
|
||||
(s, compute_score(matches, s, video, hearing_impaired=use_hearing_impaired), matches, orig_matches))
|
||||
|
||||
# sort subtitles by score
|
||||
scored_subtitles = sorted(unsorted_subtitles, key=operator.itemgetter(1), reverse=True)
|
||||
|
||||
# download best subtitles, falling back on the next on error
|
||||
downloaded_subtitles = []
|
||||
for subtitle, score, matches in scored_subtitles:
|
||||
for subtitle, score, matches, orig_matches in scored_subtitles:
|
||||
# check score
|
||||
if score < min_score:
|
||||
logger.info('%r: Score %d is below min_score (%d)', subtitle, score, min_score)
|
||||
@@ -351,10 +387,15 @@ class SZProviderPool(ProviderPool):
|
||||
score, hearing_impaired)
|
||||
continue
|
||||
|
||||
if is_episode and not {"series", "season", "episode"}.issubset(matches):
|
||||
logger.debug("%r: Skipping subtitle with score %d, because it doesn't match our series/episode",
|
||||
subtitle, score)
|
||||
continue
|
||||
if is_episode:
|
||||
can_verify_series = True
|
||||
if not subtitle.hash_verifiable and "hash" in matches:
|
||||
can_verify_series = False
|
||||
|
||||
if can_verify_series and not {"series", "season", "episode"}.issubset(orig_matches):
|
||||
logger.debug("%r: Skipping subtitle with score %d, because it doesn't match our series/episode",
|
||||
subtitle, score)
|
||||
continue
|
||||
|
||||
# download
|
||||
logger.debug("%r: Trying to download subtitle with matches %s, score: %s; release(s): %s", subtitle, matches,
|
||||
@@ -457,15 +498,15 @@ def scan_video(path, dont_use_actual_file=False, hints=None, providers=None, ski
|
||||
# remove crap from folder names
|
||||
if video_type == "episode":
|
||||
if len(split_path) > 2:
|
||||
split_path[-3] = REMOVE_CRAP_FROM_FILENAME.sub("", split_path[-3])
|
||||
split_path[-3] = remove_crap_from_fn(split_path[-3])
|
||||
else:
|
||||
if len(split_path) > 1:
|
||||
split_path[-2] = REMOVE_CRAP_FROM_FILENAME.sub("", split_path[-2])
|
||||
split_path[-2] = remove_crap_from_fn(split_path[-2])
|
||||
|
||||
guess_from = os.path.join(*split_path)
|
||||
|
||||
# remove crap from file name
|
||||
guess_from = REMOVE_CRAP_FROM_FILENAME.sub("", guess_from)
|
||||
guess_from = remove_crap_from_fn(guess_from)
|
||||
|
||||
# guess
|
||||
hints["single_value"] = True
|
||||
@@ -777,7 +818,7 @@ def save_subtitles(file_path, subtitles, single=False, directory=None, chmod=Non
|
||||
subtitle_path = os.path.splitext(subtitle_path)[0] + (u".%s" % format)
|
||||
|
||||
logger.debug(u"Saving %r to %r", subtitle, subtitle_path)
|
||||
content = subtitle.get_modified_content(format=format)
|
||||
content = subtitle.get_modified_content(format=format, debug=debug_mods)
|
||||
if content:
|
||||
with open(subtitle_path, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# coding=utf-8
|
||||
from xmlrpclib import SafeTransport, ProtocolError, Fault, Transport
|
||||
|
||||
import certifi
|
||||
import ssl
|
||||
import os
|
||||
import socket
|
||||
import logging
|
||||
import requests
|
||||
import xmlrpclib
|
||||
|
||||
from xmlrpclib import SafeTransport, Transport
|
||||
from requests import Session, exceptions
|
||||
from retry.api import retry_call
|
||||
|
||||
@@ -21,11 +22,17 @@ except AttributeError:
|
||||
default_ssl_context = None
|
||||
|
||||
|
||||
class RetryingSession(Session):
|
||||
class CertifiSession(Session):
|
||||
def __init__(self):
|
||||
super(CertifiSession, self).__init__()
|
||||
self.verify = pem_file
|
||||
|
||||
|
||||
class RetryingSession(CertifiSession):
|
||||
proxied_functions = ("get", "post")
|
||||
|
||||
def __init__(self):
|
||||
super(RetryingSession, self).__init__()
|
||||
super(CertifiSession, self).__init__()
|
||||
self.verify = pem_file
|
||||
|
||||
proxy = os.environ.get('SZ_HTTP_PROXY')
|
||||
@@ -40,7 +47,7 @@ class RetryingSession(Session):
|
||||
# fixme: may be a little loud
|
||||
logger.debug("Using proxy %s for: %s", self.proxies["http"], args[0])
|
||||
|
||||
return retry_call(getattr(super(RetryingSession, self), method), fargs=args, fkwargs=kwargs, tries=3, delay=5,
|
||||
return retry_call(getattr(super(CertifiSession, self), method), fargs=args, fkwargs=kwargs, tries=3, delay=5,
|
||||
exceptions=(exceptions.ConnectionError,
|
||||
exceptions.ProxyError,
|
||||
exceptions.SSLError,
|
||||
@@ -60,55 +67,56 @@ class RetryingSession(Session):
|
||||
return self.retry_method("post", *args, **kwargs)
|
||||
|
||||
|
||||
class TimeoutTransport(Transport):
|
||||
"""Timeout support for ``xmlrpc.client.SafeTransport``."""
|
||||
def __init__(self, timeout, *args, **kwargs):
|
||||
Transport.__init__(self, *args, **kwargs)
|
||||
self.timeout = timeout
|
||||
|
||||
def make_connection(self, host):
|
||||
c = Transport.make_connection(self, host)
|
||||
c.timeout = self.timeout
|
||||
|
||||
return c
|
||||
|
||||
|
||||
class SubZeroTransport(SafeTransport):
|
||||
class SubZeroRequestsTransport(xmlrpclib.SafeTransport):
|
||||
"""
|
||||
Timeout and proxy support for ``xmlrpc.client.(Safe)Transport``
|
||||
Drop in Transport for xmlrpclib that uses Requests instead of httplib
|
||||
|
||||
Based on: https://gist.github.com/chrisguitarguy/2354951#gistcomment-2388906
|
||||
|
||||
"""
|
||||
def __init__(self, timeout, url, *args, **kwargs):
|
||||
SafeTransport.__init__(self, *args, **kwargs)
|
||||
# change our user agent to reflect Requests
|
||||
user_agent = "Python XMLRPC with Requests (python-requests.org)"
|
||||
proxies = None
|
||||
|
||||
def __init__(self, use_https=True, verify=None, user_agent=None, timeout=10, *args, **kwargs):
|
||||
self.verify = pem_file if verify is None else verify
|
||||
self.use_https = use_https
|
||||
self.user_agent = user_agent if user_agent is not None else self.user_agent
|
||||
self.timeout = timeout
|
||||
self.host = None
|
||||
self.proxy = None
|
||||
self.scheme = url.split('://', 1)[0]
|
||||
self.https = url.startswith('https')
|
||||
self.proxy = os.environ.get('SZ_HTTP_PROXY')
|
||||
proxy = os.environ.get('SZ_HTTP_PROXY')
|
||||
if proxy:
|
||||
self.proxies = {
|
||||
"http": proxy,
|
||||
"https": proxy
|
||||
}
|
||||
|
||||
if self.https:
|
||||
self.context = default_ssl_context
|
||||
xmlrpclib.SafeTransport.__init__(self, *args, **kwargs)
|
||||
|
||||
if self.proxy:
|
||||
logger.debug("Using proxy %s for: %s", self.proxy, url)
|
||||
self.https = self.proxy.startswith('https')
|
||||
|
||||
if self.timeout:
|
||||
self.timeout = self.timeout * 3
|
||||
|
||||
def make_connection(self, host):
|
||||
self.host = host
|
||||
if self.proxy:
|
||||
host = self.proxy.split('://', 1)[-1]
|
||||
if self.https:
|
||||
c = SafeTransport.make_connection(self, host)
|
||||
def request(self, host, handler, request_body, verbose=0):
|
||||
"""
|
||||
Make an xmlrpc request.
|
||||
"""
|
||||
headers = {'User-Agent': self.user_agent}
|
||||
url = self._build_url(host, handler)
|
||||
try:
|
||||
resp = requests.post(url, data=request_body, headers=headers,
|
||||
stream=True, timeout=self.timeout, proxies=self.proxies,
|
||||
verify=self.verify)
|
||||
except ValueError:
|
||||
raise
|
||||
except Exception:
|
||||
raise # something went wrong
|
||||
else:
|
||||
c = Transport.make_connection(self, host)
|
||||
resp.raise_for_status()
|
||||
|
||||
c.timeout = self.timeout
|
||||
self.verbose = verbose
|
||||
return self.parse_response(resp.raw)
|
||||
|
||||
return c
|
||||
|
||||
def send_request(self, connection, handler, request_body):
|
||||
handler = '%s://%s%s' % (self.scheme, self.host, handler)
|
||||
Transport.send_request(self, connection, handler, request_body)
|
||||
def _build_url(self, host, handler):
|
||||
"""
|
||||
Build a url for our request based on the host, handler and use_http
|
||||
property
|
||||
"""
|
||||
scheme = 'https' if self.use_https else 'http'
|
||||
handler = handler[1:] if handler and handler[0] == "/" else handler
|
||||
return '%s://%s/%s' % (scheme, host, handler)
|
||||
|
||||
@@ -20,6 +20,10 @@ class PatchedOpenSubtitlesConverter(OpenSubtitlesConverter):
|
||||
self.to_opensubtitles.update({
|
||||
('srp', None, "Latn"): 'scc',
|
||||
('srp', None, "Cyrl"): 'scc',
|
||||
('chi', None, 'Hant'): 'zht'
|
||||
})
|
||||
self.from_opensubtitles.update({
|
||||
'zht': ('zho', None, 'Hant')
|
||||
})
|
||||
|
||||
def convert(self, alpha3, country=None, script=None):
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
# coding=utf-8
|
||||
import logging
|
||||
import re
|
||||
|
||||
import datetime
|
||||
import subliminal
|
||||
import time
|
||||
from random import randint
|
||||
from dogpile.cache.api import NO_VALUE
|
||||
from requests import Session
|
||||
|
||||
from subliminal.exceptions import ServiceUnavailable, DownloadLimitExceeded
|
||||
from subliminal.exceptions import ServiceUnavailable, DownloadLimitExceeded, AuthenticationError
|
||||
from subliminal.providers.addic7ed import Addic7edProvider as _Addic7edProvider, \
|
||||
Addic7edSubtitle as _Addic7edSubtitle, ParserBeautifulSoup, show_cells_re
|
||||
from subliminal.cache import SHOW_EXPIRATION_TIME, region
|
||||
from subliminal.cache import region
|
||||
from subliminal.subtitle import fix_line_ending
|
||||
from subliminal_patch.utils import sanitize
|
||||
from subliminal_patch.exceptions import TooManyRequests
|
||||
@@ -20,6 +23,8 @@ logger = logging.getLogger(__name__)
|
||||
#: Series header parsing regex
|
||||
series_year_re = re.compile(r'^(?P<series>[ \w\'.:(),*&!?-]+?)(?: \((?P<year>\d{4})\))?$')
|
||||
|
||||
SHOW_EXPIRATION_TIME = datetime.timedelta(weeks=1).total_seconds()
|
||||
|
||||
|
||||
class Addic7edSubtitle(_Addic7edSubtitle):
|
||||
hearing_impaired_verifiable = True
|
||||
@@ -67,14 +72,46 @@ class Addic7edProvider(_Addic7edProvider):
|
||||
self.USE_ADDICTED_RANDOM_AGENTS = use_random_agents
|
||||
|
||||
def initialize(self):
|
||||
# patch: add optional user agent randomization
|
||||
super(Addic7edProvider, self).initialize()
|
||||
self.session = Session()
|
||||
self.session.headers['User-Agent'] = 'Subliminal/%s' % subliminal.__short_version__
|
||||
|
||||
if self.USE_ADDICTED_RANDOM_AGENTS:
|
||||
from .utils import FIRST_THOUSAND_OR_SO_USER_AGENTS as AGENT_LIST
|
||||
logger.debug("addic7ed: using random user agents")
|
||||
logger.debug("Addic7ed: using random user agents")
|
||||
self.session.headers['User-Agent'] = AGENT_LIST[randint(0, len(AGENT_LIST) - 1)]
|
||||
self.session.headers['Referer'] = self.server_url
|
||||
|
||||
# login
|
||||
if self.username and self.password:
|
||||
ccks = region.get("addic7ed_cookies", expiration_time=86400)
|
||||
do_login = False
|
||||
if ccks != NO_VALUE:
|
||||
self.session.cookies.update(ccks)
|
||||
r = self.session.get(self.server_url + 'panel.php', allow_redirects=False, timeout=10)
|
||||
if r.status_code == 302:
|
||||
logger.info('Addic7ed: Login expired')
|
||||
do_login = True
|
||||
else:
|
||||
logger.info('Addic7ed: Reusing old login')
|
||||
self.logged_in = True
|
||||
|
||||
if do_login:
|
||||
logger.info('Addic7ed: Logging in')
|
||||
data = {'username': self.username, 'password': self.password, 'Submit': 'Log in'}
|
||||
r = self.session.post(self.server_url + 'dologin.php', data, allow_redirects=False, timeout=10)
|
||||
|
||||
if "relax, slow down" in r.content:
|
||||
raise TooManyRequests(self.username)
|
||||
|
||||
if r.status_code != 302:
|
||||
raise AuthenticationError(self.username)
|
||||
|
||||
region.set("addic7ed_cookies", r.cookies)
|
||||
|
||||
logger.debug('Addic7ed: Logged in')
|
||||
self.logged_in = True
|
||||
|
||||
|
||||
@region.cache_on_arguments(expiration_time=SHOW_EXPIRATION_TIME)
|
||||
def _get_show_ids(self):
|
||||
"""Get the ``dict`` of show ids per series by querying the `shows.php` page.
|
||||
@@ -140,10 +177,26 @@ class Addic7edProvider(_Addic7edProvider):
|
||||
|
||||
# make the search
|
||||
logger.info('Searching show ids with %r', params)
|
||||
r = self.session.get(self.server_url + 'search.php', params=params, timeout=10)
|
||||
r.raise_for_status()
|
||||
|
||||
# currently addic7ed searches via srch.php from the front page, then a re-search is needed which calls
|
||||
# search.php
|
||||
for endpoint in ("srch.php", "search.php",):
|
||||
headers = None
|
||||
if endpoint == "search.php":
|
||||
headers = {
|
||||
"referer": self.server_url + "srch.php"
|
||||
}
|
||||
r = self.session.get(self.server_url + endpoint, params=params, timeout=10, headers=headers)
|
||||
r.raise_for_status()
|
||||
|
||||
if r.content and "Sorry, your search" not in r.content:
|
||||
break
|
||||
|
||||
time.sleep(4)
|
||||
|
||||
if r.status_code == 304:
|
||||
raise TooManyRequests()
|
||||
|
||||
soup = ParserBeautifulSoup(r.content, ['lxml', 'html.parser'])
|
||||
|
||||
suggestion = None
|
||||
@@ -167,17 +220,20 @@ class Addic7edProvider(_Addic7edProvider):
|
||||
soup.decompose()
|
||||
soup = None
|
||||
|
||||
def query(self, series, season, year=None, country=None):
|
||||
def query(self, show_id, series, season, year=None, country=None):
|
||||
# patch: fix logging
|
||||
# get the show id
|
||||
show_id = self.get_show_id(series, year, country)
|
||||
if show_id is None:
|
||||
logger.info('No show id found for %r (%r)', series, {'year': year, 'country': country})
|
||||
return []
|
||||
|
||||
# get the page of the season of the show
|
||||
logger.info('Getting the page of show id %d, season %d', show_id, season)
|
||||
r = self.session.get(self.server_url + 'show/%d' % show_id, params={'season': season}, timeout=10)
|
||||
r = self.session.get(self.server_url + 'ajax_loadShow.php',
|
||||
params={'show': show_id, 'season': season},
|
||||
timeout=10,
|
||||
headers={
|
||||
"referer": "%sshow/%s" % (self.server_url, show_id),
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
}
|
||||
)
|
||||
|
||||
r.raise_for_status()
|
||||
|
||||
if r.status_code == 304:
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
# coding=utf-8
|
||||
import logging
|
||||
import os
|
||||
import io
|
||||
import time
|
||||
|
||||
from zipfile import ZipFile
|
||||
from guessit import guessit
|
||||
from requests import Session
|
||||
from subliminal import Episode, Movie
|
||||
from subliminal.score import get_equivalent_release_groups
|
||||
from subliminal.utils import sanitize_release_group, sanitize
|
||||
from subliminal_patch.providers import Provider
|
||||
from subliminal_patch.subtitle import Subtitle, guess_matches
|
||||
from subliminal_patch.providers.mixins import ProviderSubtitleArchiveMixin
|
||||
from subzero.language import Language
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ArgenteamSubtitle(Subtitle):
|
||||
provider_name = 'argenteam'
|
||||
hearing_impaired_verifiable = False
|
||||
_release_info = None
|
||||
|
||||
def __init__(self, language, download_link, movie_kind, title, season, episode, year, release, version, source,
|
||||
video_codec, tvdb_id, imdb_id, asked_for_episode=None, asked_for_release_group=None, *args, **kwargs):
|
||||
super(ArgenteamSubtitle, self).__init__(language, download_link, *args, **kwargs)
|
||||
self.download_link = download_link
|
||||
self.movie_kind = movie_kind
|
||||
self.title = title
|
||||
self.year = year
|
||||
self.season = season
|
||||
self.episode = episode
|
||||
self.release = release
|
||||
self.version = version
|
||||
self.asked_for_release_group = asked_for_release_group
|
||||
self.asked_for_episode = asked_for_episode
|
||||
self.matches = None
|
||||
self.format = source
|
||||
self.video_codec = video_codec
|
||||
self.tvdb_id = tvdb_id
|
||||
self.imdb_id = "tt" + imdb_id if imdb_id else None
|
||||
self.releases = self.release_info
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.download_link
|
||||
|
||||
@property
|
||||
def release_info(self):
|
||||
if self._release_info:
|
||||
return self._release_info
|
||||
|
||||
combine = []
|
||||
for attr in ("format", "version", "video_codec"):
|
||||
value = getattr(self, attr)
|
||||
if value:
|
||||
combine.append(value)
|
||||
|
||||
self._release_info = u".".join(combine) + (u"-"+self.release if self.release else "")
|
||||
return self._release_info
|
||||
|
||||
def __repr__(self):
|
||||
ep_addon = (" S%02dE%02d" % (self.season, self.episode)) if self.episode else ""
|
||||
return '<%s %r [%s]>' % (
|
||||
self.__class__.__name__, u"%s%s%s." % (self.title, " (%s)" % self.year if self.year else "", ep_addon) +
|
||||
self.release_info, self.language)
|
||||
|
||||
def get_matches(self, video):
|
||||
matches = set()
|
||||
# series
|
||||
if isinstance(video, Episode) and self.movie_kind == 'episode':
|
||||
if video.series and (sanitize(self.title) in (
|
||||
sanitize(name) for name in [video.series] + video.alternative_series)):
|
||||
matches.add('series')
|
||||
# season
|
||||
if video.season and self.season == video.season:
|
||||
matches.add('season')
|
||||
# episode
|
||||
if video.episode and self.episode == video.episode:
|
||||
matches.add('episode')
|
||||
|
||||
# tvdb_id
|
||||
if video.tvdb_id and str(self.tvdb_id) == str(video.tvdb_id):
|
||||
matches.add('tvdb_id')
|
||||
|
||||
elif isinstance(video, Movie) and self.movie_kind == 'movie':
|
||||
# title
|
||||
if video.title and (sanitize(self.title) in (
|
||||
sanitize(name) for name in [video.title] + video.alternative_titles)):
|
||||
matches.add('title')
|
||||
|
||||
# imdb_id
|
||||
if video.imdb_id and self.imdb_id and str(self.imdb_id) == str(video.imdb_id):
|
||||
matches.add('imdb_id')
|
||||
|
||||
# year
|
||||
if video.year and self.year == video.year:
|
||||
matches.add('year')
|
||||
else:
|
||||
logger.info('%r is not a valid movie_kind', self.movie_kind)
|
||||
return matches
|
||||
|
||||
# release_group
|
||||
if video.release_group and self.release:
|
||||
rg = sanitize_release_group(video.release_group)
|
||||
if any(r in sanitize_release_group(self.release) for r in get_equivalent_release_groups(rg)):
|
||||
matches.add('release_group')
|
||||
|
||||
# blatantly assume we've got a matching format if the release group matches
|
||||
# fixme: smart?
|
||||
#matches.add('format')
|
||||
|
||||
# resolution
|
||||
if video.resolution and self.version and str(video.resolution) in self.version.lower():
|
||||
matches.add('resolution')
|
||||
# format
|
||||
if video.format and self.format:
|
||||
formats = [video.format]
|
||||
if video.format == "WEB-DL":
|
||||
formats.append("WEB")
|
||||
|
||||
for fmt in formats:
|
||||
if fmt.lower() in self.format.lower():
|
||||
matches.add('format')
|
||||
break
|
||||
|
||||
matches |= guess_matches(video, guessit(self.release_info), partial=True)
|
||||
self.matches = matches
|
||||
return matches
|
||||
|
||||
|
||||
class ArgenteamProvider(Provider, ProviderSubtitleArchiveMixin):
|
||||
provider_name = 'argenteam'
|
||||
languages = {Language.fromalpha2(l) for l in ['es']}
|
||||
video_types = (Episode, Movie)
|
||||
API_URL = "http://argenteam.net/api/v1/"
|
||||
subtitle_class = ArgenteamSubtitle
|
||||
hearing_impaired_verifiable = False
|
||||
language_list = list(languages)
|
||||
|
||||
multi_result_throttle = 2 # seconds
|
||||
|
||||
def __init__(self):
|
||||
self.session = None
|
||||
|
||||
def initialize(self):
|
||||
self.session = Session()
|
||||
self.session.headers = {'User-Agent': os.environ.get("SZ_USER_AGENT", "Sub-Zero/2")}
|
||||
|
||||
def terminate(self):
|
||||
self.session.close()
|
||||
|
||||
def search_ids(self, title, year=None, imdb_id=None, season=None, episode=None, titles=None):
|
||||
"""Search movie or episode id from the `title`, `season` and `episode`.
|
||||
|
||||
:param imdb_id: imdb id of the given movie
|
||||
:param titles: all titles of the given series or movie
|
||||
:param year: release year of the given movie
|
||||
:param str title: series of the episode or movie name
|
||||
:param int season: season of the episode.
|
||||
:param int episode: episode number.
|
||||
:return: list of ids
|
||||
:rtype: list
|
||||
|
||||
"""
|
||||
# make the search
|
||||
query = title
|
||||
titles = titles or []
|
||||
|
||||
is_episode = False
|
||||
if season and episode:
|
||||
is_episode = True
|
||||
query = '%s S%#02dE%#02d' % (title, season, episode)
|
||||
|
||||
logger.info(u'Searching %s ID for %r', "episode" if is_episode else "movie", query)
|
||||
r = self.session.get(self.API_URL + 'search', params={'q': query}, timeout=10)
|
||||
r.raise_for_status()
|
||||
results = r.json()
|
||||
match_ids = []
|
||||
if results['total'] >= 1:
|
||||
for result in results["results"]:
|
||||
if (result['type'] == "episode" and not is_episode) or (result['type'] == "movie" and is_episode):
|
||||
continue
|
||||
|
||||
# shortcut in case of matching imdb id
|
||||
if not is_episode and imdb_id and "imdb" in result and "tt%s" % result["imdb"] == str(imdb_id):
|
||||
logger.debug("Movie matched by IMDB ID %s, taking shortcut", imdb_id)
|
||||
match_ids = [result['id']]
|
||||
break
|
||||
|
||||
# advanced title check in case of multiple movie results
|
||||
if results['total'] > 1:
|
||||
if not is_episode and year:
|
||||
if result["title"] and not (sanitize(result["title"]) in (u"%s %s" % (sanitize(name), year)
|
||||
for name in titles)):
|
||||
continue
|
||||
|
||||
match_ids.append(result['id'])
|
||||
else:
|
||||
logger.error(u'No episode ID found for %r', query)
|
||||
|
||||
if match_ids:
|
||||
logger.debug(u"Found matching IDs: %s", ", ".join(str(id) for id in match_ids))
|
||||
|
||||
return match_ids
|
||||
|
||||
def query(self, title, video, titles=None):
|
||||
is_episode = isinstance(video, Episode)
|
||||
season = episode = None
|
||||
url = self.API_URL + 'movie'
|
||||
if is_episode:
|
||||
season = video.season
|
||||
episode = video.episode
|
||||
url = self.API_URL + 'episode'
|
||||
argenteam_ids = self.search_ids(title, season=season, episode=episode, titles=titles)
|
||||
|
||||
else:
|
||||
argenteam_ids = self.search_ids(title, year=video.year, imdb_id=video.imdb_id, titles=titles)
|
||||
|
||||
if not argenteam_ids:
|
||||
return []
|
||||
|
||||
language = self.language_list[0]
|
||||
subtitles = []
|
||||
has_multiple_ids = len(argenteam_ids) > 1
|
||||
for aid in argenteam_ids:
|
||||
response = self.session.get(url, params={'id': aid}, timeout=10)
|
||||
|
||||
response.raise_for_status()
|
||||
content = response.json()
|
||||
|
||||
imdb_id = year = None
|
||||
returned_title = title
|
||||
if not is_episode and "info" in content:
|
||||
imdb_id = content["info"].get("imdb")
|
||||
year = content["info"].get("year")
|
||||
returned_title = content["info"].get("title", title)
|
||||
|
||||
for r in content['releases']:
|
||||
for s in r['subtitles']:
|
||||
sub = ArgenteamSubtitle(language, s['uri'], "episode" if is_episode else "movie", returned_title,
|
||||
season, episode, year, r.get('team'), r.get('tags'),
|
||||
r.get('source'), r.get('codec'), content.get("tvdb"), imdb_id,
|
||||
asked_for_release_group=video.release_group,
|
||||
asked_for_episode=episode
|
||||
)
|
||||
subtitles.append(sub)
|
||||
|
||||
if has_multiple_ids:
|
||||
time.sleep(self.multi_result_throttle)
|
||||
|
||||
return subtitles
|
||||
|
||||
def list_subtitles(self, video, languages):
|
||||
if isinstance(video, Episode):
|
||||
titles = [video.series] + video.alternative_series
|
||||
else:
|
||||
titles = [video.title] + video.alternative_titles
|
||||
|
||||
for title in titles:
|
||||
subs = self.query(title, video, titles=titles)
|
||||
if subs:
|
||||
return subs
|
||||
|
||||
time.sleep(self.multi_result_throttle)
|
||||
|
||||
return []
|
||||
|
||||
def download_subtitle(self, subtitle):
|
||||
# download as a zip
|
||||
logger.info('Downloading subtitle %r', subtitle)
|
||||
r = self.session.get(subtitle.download_link, timeout=10)
|
||||
r.raise_for_status()
|
||||
|
||||
# open the zip
|
||||
with ZipFile(io.BytesIO(r.content)) as zf:
|
||||
subtitle.content = self.get_subtitle_from_archive(subtitle, zf)
|
||||
@@ -0,0 +1,166 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from babelfish import language_converters
|
||||
from guessit import guessit
|
||||
from requests import Session
|
||||
|
||||
from subliminal import Movie, Episode, ProviderError, __short_version__
|
||||
from subliminal.exceptions import AuthenticationError, ConfigurationError, DownloadLimitExceeded, ProviderError
|
||||
from subliminal_patch.subtitle import Subtitle, guess_matches
|
||||
from subliminal.subtitle import fix_line_ending
|
||||
from subliminal_patch.providers import Provider
|
||||
from subzero.language import Language
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
language_converters.register('assrt = subliminal_patch.converters.assrt:AssrtConverter')
|
||||
|
||||
server_url = 'https://api.assrt.net/v1'
|
||||
supported_languages = language_converters['assrt'].to_assrt.keys()
|
||||
|
||||
|
||||
class AssrtSubtitle(Subtitle):
|
||||
"""Assrt Sbutitle."""
|
||||
provider_name = 'assrt'
|
||||
guessit_options = {
|
||||
'allowed_languages': [ l[0] for l in supported_languages ],
|
||||
'allowed_countries': [ l[1] for l in supported_languages if len(l) > 1 ],
|
||||
'enforce_list': True
|
||||
}
|
||||
|
||||
def __init__(self, language, subtitle_id, video_name, session, token):
|
||||
super(AssrtSubtitle, self).__init__(language)
|
||||
self.session = session
|
||||
self.token = token
|
||||
self.subtitle_id = subtitle_id
|
||||
self.video_name = video_name
|
||||
self.url = None
|
||||
self._detail = None
|
||||
|
||||
def _get_detail(self):
|
||||
if self._detail:
|
||||
return self._detail
|
||||
params = {'token': self.token, 'id': self.id}
|
||||
r = self.session.get(server_url + '/sub/detail', params=params, timeout=10)
|
||||
r.raise_for_status()
|
||||
|
||||
result = r.json()
|
||||
sub = result['sub']['subs'][0]
|
||||
files = sub['filelist']
|
||||
|
||||
# first pass: guessit
|
||||
for f in files:
|
||||
logger.info('File %r', f)
|
||||
guess = guessit(f['f'], self.guessit_options)
|
||||
logger.info('GuessIt %r', guess)
|
||||
langs = set()
|
||||
if 'language' in guess:
|
||||
langs.update(guess['language'])
|
||||
if 'subtitle_language' in guess:
|
||||
langs.update(guess['subtitle_language'])
|
||||
if self.language in langs:
|
||||
self._defail = f
|
||||
return f
|
||||
|
||||
# second pass: keyword matching
|
||||
codes = language_converters['assrt'].codes
|
||||
for f in files:
|
||||
langs = set([ Language.fromassrt(k) for k in codes if k in f['f'] ])
|
||||
logger.info('%s: %r', f['f'], langs)
|
||||
if self.language in langs:
|
||||
self._defail = f
|
||||
return f
|
||||
|
||||
# fallback: pick up first file if nothing matches
|
||||
return files[0]
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.subtitle_id
|
||||
|
||||
@property
|
||||
def download_link(self):
|
||||
detail = self._get_detail()
|
||||
return detail['url']
|
||||
|
||||
def get_matches(self, video):
|
||||
matches = guess_matches(video, guessit(self.video_name))
|
||||
return matches
|
||||
|
||||
|
||||
class AssrtProvider(Provider):
|
||||
"""Assrt Provider."""
|
||||
languages = {Language(*l) for l in supported_languages}
|
||||
|
||||
def __init__(self, token=None):
|
||||
if not token:
|
||||
raise ConfigurationError('Token must be specified')
|
||||
self.token = token
|
||||
|
||||
def initialize(self):
|
||||
self.session = Session()
|
||||
self.session.headers = {'User-Agent': os.environ.get("SZ_USER_AGENT", "Sub-Zero/2")}
|
||||
|
||||
def terminate(self):
|
||||
self.session.close()
|
||||
|
||||
def query(self, languages, video):
|
||||
# query the server
|
||||
keywords = []
|
||||
if isinstance(video, Movie):
|
||||
if video.title:
|
||||
keywords.append(video.title)
|
||||
if video.year:
|
||||
keywords.append(str(video.year))
|
||||
elif isinstance(video, Episode):
|
||||
if video.series:
|
||||
keywords.append(video.series)
|
||||
if video.season and video.episode:
|
||||
keywords.append('S%02dE%02d' % (video.season, video.episode))
|
||||
elif video.episode:
|
||||
keywords.append('E%02d' % video.episode)
|
||||
query = ' '.join(keywords)
|
||||
|
||||
params = {'token': self.token, 'q': query, 'is_file': 1}
|
||||
logger.debug('Searching subtitles %r', params)
|
||||
res = self.session.get(server_url + '/sub/search', params=params, timeout=10)
|
||||
res.raise_for_status()
|
||||
result = res.json()
|
||||
|
||||
if result['status'] != 0:
|
||||
logger.error('status error: %r', r)
|
||||
return []
|
||||
|
||||
if not result['sub']['subs']:
|
||||
logger.debug('No subtitle found')
|
||||
|
||||
# parse the subtitles
|
||||
pattern = re.compile(ur'lang(?P<code>\w+)')
|
||||
subtitles = []
|
||||
for sub in result['sub']['subs']:
|
||||
if 'lang' not in sub:
|
||||
continue
|
||||
for key in sub['lang']['langlist'].keys():
|
||||
match = pattern.match(key)
|
||||
try:
|
||||
language = Language.fromassrt(match.group('code'))
|
||||
if language in languages:
|
||||
subtitles.append(AssrtSubtitle(language, sub['id'], sub['videoname'], self.session, self.token))
|
||||
except:
|
||||
pass
|
||||
|
||||
return subtitles
|
||||
|
||||
def list_subtitles(self, video, languages):
|
||||
return self.query(languages, video)
|
||||
|
||||
def download_subtitle(self, subtitle):
|
||||
logger.info('Downloading subtitle %r', subtitle)
|
||||
r = self.session.get(subtitle.download_link, timeout=10)
|
||||
r.raise_for_status()
|
||||
|
||||
subtitle.content = fix_line_ending(r.content)
|
||||
@@ -0,0 +1,244 @@
|
||||
# coding: utf-8
|
||||
|
||||
import io
|
||||
import six
|
||||
import logging
|
||||
import re
|
||||
import os
|
||||
import time
|
||||
|
||||
from babelfish import Language, language_converters
|
||||
from requests import Session
|
||||
|
||||
from subliminal_patch.providers import Provider
|
||||
from subliminal_patch.providers.mixins import ProviderSubtitleArchiveMixin
|
||||
from subliminal.providers import ParserBeautifulSoup
|
||||
from subliminal_patch.exceptions import ProviderError
|
||||
from subliminal.score import get_equivalent_release_groups
|
||||
from subliminal_patch.subtitle import Subtitle, guess_matches
|
||||
from subliminal.utils import sanitize, sanitize_release_group
|
||||
from subliminal.video import Episode
|
||||
from zipfile import ZipFile, is_zipfile
|
||||
from rarfile import RarFile, is_rarfile
|
||||
from subliminal_patch.utils import sanitize, fix_inconsistent_naming as _fix_inconsistent_naming
|
||||
from guessit import guessit
|
||||
|
||||
|
||||
def fix_inconsistent_naming(title):
|
||||
"""Fix titles with inconsistent naming using dictionary and sanitize them.
|
||||
|
||||
:param str title: original title.
|
||||
:return: new title.
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
return _fix_inconsistent_naming(title, {"DC's Legends of Tomorrow": "Legends of Tomorrow",
|
||||
"Marvel's Jessica Jones": "Jessica Jones"})
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
language_converters.register('hosszupuska = subliminal_patch.converters.hosszupuska:HosszupuskaConverter')
|
||||
|
||||
|
||||
class HosszupuskaSubtitle(Subtitle):
|
||||
"""Hosszupuska Subtitle."""
|
||||
provider_name = 'hosszupuska'
|
||||
|
||||
def __str__(self):
|
||||
subtit = "Subtitle id: " + str(self.subtitle_id) \
|
||||
+ " Series: " + self.series \
|
||||
+ " Season: " + str(self.season) \
|
||||
+ " Episode: " + str(self.episode) \
|
||||
+ " Releases: " + str(self.releases)
|
||||
if self.year:
|
||||
subtit = subtit + " Year: " + str(self.year)
|
||||
if six.PY3:
|
||||
return subtit
|
||||
return subtit.encode('utf-8')
|
||||
|
||||
def __init__(self, language, page_link, subtitle_id, series, season, episode, version,
|
||||
releases, year, asked_for_release_group=None, asked_for_episode=None):
|
||||
super(HosszupuskaSubtitle, self).__init__(language, page_link=page_link)
|
||||
self.subtitle_id = subtitle_id
|
||||
self.series = series
|
||||
self.season = season
|
||||
self.episode = episode
|
||||
self.version = version
|
||||
self.releases = releases
|
||||
self.year = year
|
||||
if year:
|
||||
self.year = int(year)
|
||||
|
||||
self.release_info = u", ".join(releases)
|
||||
self.page_link = page_link
|
||||
self.asked_for_release_group = asked_for_release_group
|
||||
self.asked_for_episode = asked_for_episode
|
||||
|
||||
def __repr__(self):
|
||||
ep_addon = (" S%02dE%02d" % (self.season, self.episode)) if self.episode else ""
|
||||
return '<%s %r [%s]>' % (
|
||||
self.__class__.__name__, u"%s%s%s [%s]" % (self.series, " (%s)" % self.year if self.year else "", ep_addon,
|
||||
self.release_info), self.language)
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return str(self.subtitle_id)
|
||||
|
||||
def get_matches(self, video):
|
||||
matches = set()
|
||||
# series
|
||||
if video.series and sanitize(self.series) == sanitize(video.series):
|
||||
matches.add('series')
|
||||
# season
|
||||
if video.season and self.season == video.season:
|
||||
matches.add('season')
|
||||
# episode
|
||||
if video.episode and self.episode == video.episode:
|
||||
matches.add('episode')
|
||||
# year
|
||||
if ('series' in matches and video.original_series and self.year is None or
|
||||
video.year and video.year == self.year):
|
||||
matches.add('year')
|
||||
|
||||
# release_group
|
||||
if (video.release_group and self.version and
|
||||
any(r in sanitize_release_group(self.version)
|
||||
for r in get_equivalent_release_groups(sanitize_release_group(video.release_group)))):
|
||||
matches.add('release_group')
|
||||
# resolution
|
||||
if video.resolution and self.version and video.resolution in self.version.lower():
|
||||
matches.add('resolution')
|
||||
# format
|
||||
if video.format and self.version and video.format.lower() in self.version.lower():
|
||||
matches.add('format')
|
||||
# other properties
|
||||
matches |= guess_matches(video, guessit(self.release_info.encode("utf-8")))
|
||||
|
||||
return matches
|
||||
|
||||
|
||||
class HosszupuskaProvider(Provider, ProviderSubtitleArchiveMixin):
|
||||
"""Hosszupuska Provider."""
|
||||
languages = {Language('hun', 'HU')} | {Language(l) for l in [
|
||||
'hun', 'eng'
|
||||
]}
|
||||
video_types = (Episode,)
|
||||
server_url = 'http://hosszupuskasub.com/'
|
||||
subtitle_class = HosszupuskaSubtitle
|
||||
hearing_impaired_verifiable = False
|
||||
multi_result_throttle = 2 # seconds
|
||||
|
||||
def initialize(self):
|
||||
self.session = Session()
|
||||
self.session.headers = {'User-Agent': os.environ.get("SZ_USER_AGENT", "Sub-Zero/2")}
|
||||
|
||||
def terminate(self):
|
||||
self.session.close()
|
||||
|
||||
def get_language(self, text):
|
||||
if text == '1.gif':
|
||||
return Language.fromhosszupuska('hu')
|
||||
if text == '2.gif':
|
||||
return Language.fromhosszupuska('en')
|
||||
return None
|
||||
|
||||
def query(self, series, season, episode, year=None, video=None):
|
||||
|
||||
# Search for s01e03 instead of s1e3
|
||||
seasona = "%02d" % season
|
||||
episodea = "%02d" % episode
|
||||
series = fix_inconsistent_naming(series)
|
||||
seriesa = series.replace(' ', '+').replace('\'', '')
|
||||
|
||||
# get the episode page
|
||||
logger.info('Getting the page for episode %s', episode)
|
||||
url = self.server_url + "sorozatok.php?cim=" + seriesa + "&evad="+str(seasona) + \
|
||||
"&resz="+str(episodea)+"&nyelvtipus=%25&x=24&y=8"
|
||||
logger.info('Url %s', url)
|
||||
|
||||
r = self.session.get(url, timeout=10).content
|
||||
|
||||
i = 0
|
||||
soup = ParserBeautifulSoup(r, ['lxml'])
|
||||
|
||||
table = soup.find_all("table")[9]
|
||||
|
||||
subtitles = []
|
||||
# loop over subtitles rows
|
||||
for row in table.find_all("tr"):
|
||||
i = i + 1
|
||||
if "this.style.backgroundImage='url(css/over2.jpg)" in str(row) and i > 5:
|
||||
datas = row.find_all("td")
|
||||
|
||||
# Currently subliminal not use these params, but maybe later will come in handy
|
||||
# hunagrian_name = re.split('s(\d{1,2})', datas[1].find_all('b')[0].getText())[0]
|
||||
# Translator of subtitle
|
||||
# sub_translator = datas[3].getText()
|
||||
# Posting date of subtitle
|
||||
# sub_date = datas[4].getText()
|
||||
|
||||
sub_year = sub_english_name = sub_version = None
|
||||
# Handle the case when '(' in subtitle
|
||||
if datas[1].getText().count('(') == 2:
|
||||
sub_english_name = re.split('s(\d{1,2})e(\d{1,2})', datas[1].getText())[3]
|
||||
if datas[1].getText().count('(') == 3:
|
||||
sub_year = re.findall(r"(?<=\()(\d{4})(?=\))", datas[1].getText().strip())[0]
|
||||
sub_english_name = re.split('s(\d{1,2})e(\d{1,2})', datas[1].getText().split('(')[0])[0]
|
||||
|
||||
if not sub_english_name:
|
||||
continue
|
||||
|
||||
sub_season = int((re.findall('s(\d{1,2})', datas[1].find_all('b')[0].getText(), re.VERBOSE)[0])
|
||||
.lstrip('0'))
|
||||
sub_episode = int((re.findall('e(\d{1,2})', datas[1].find_all('b')[0].getText(), re.VERBOSE)[0])
|
||||
.lstrip('0'))
|
||||
|
||||
if sub_season == season and sub_episode == episode:
|
||||
sub_language = self.get_language(datas[2].find_all('img')[0]['src'].split('/')[1])
|
||||
sub_downloadlink = datas[6].find_all('a')[1]['href']
|
||||
sub_id = sub_downloadlink.split('=')[1].split('.')[0]
|
||||
|
||||
if datas[1].getText().count('(') == 2:
|
||||
sub_version = datas[1].getText().split('(')[1].split(')')[0]
|
||||
if datas[1].getText().count('(') == 3:
|
||||
sub_version = datas[1].getText().split('(')[2].split(')')[0]
|
||||
|
||||
# One subtitle can be used for several releases
|
||||
sub_releases = [s.strip() for s in sub_version.split(',')]
|
||||
subtitle = self.subtitle_class(sub_language, sub_downloadlink, sub_id, sub_english_name.strip(),
|
||||
sub_season, sub_episode, sub_version, sub_releases, sub_year,
|
||||
asked_for_release_group=video.release_group,
|
||||
asked_for_episode=episode)
|
||||
|
||||
logger.debug('Found subtitle: %r', subtitle)
|
||||
subtitles.append(subtitle)
|
||||
|
||||
return subtitles
|
||||
|
||||
def list_subtitles(self, video, languages):
|
||||
titles = [video.series] + video.alternative_series
|
||||
|
||||
for title in titles:
|
||||
subs = self.query(title, video.season, video.episode, video.year, video=video)
|
||||
if subs:
|
||||
return subs
|
||||
|
||||
time.sleep(self.multi_result_throttle)
|
||||
|
||||
def download_subtitle(self, subtitle):
|
||||
r = self.session.get(subtitle.page_link, timeout=10)
|
||||
r.raise_for_status()
|
||||
|
||||
# open the archive
|
||||
archive_stream = io.BytesIO(r.content)
|
||||
if is_rarfile(archive_stream):
|
||||
logger.debug('Archive identified as rar')
|
||||
archive = RarFile(archive_stream)
|
||||
elif is_zipfile(archive_stream):
|
||||
logger.debug('Archive identified as zip')
|
||||
archive = ZipFile(archive_stream)
|
||||
else:
|
||||
raise ProviderError('Unidentified archive type')
|
||||
|
||||
subtitle.content = self.get_subtitle_from_archive(subtitle, archive)
|
||||
@@ -1,5 +1,7 @@
|
||||
# coding=utf-8
|
||||
import logging
|
||||
import rarfile
|
||||
from subliminal.exceptions import ConfigurationError
|
||||
|
||||
from subliminal.providers.legendastv import LegendasTVSubtitle as _LegendasTVSubtitle, \
|
||||
LegendasTVProvider as _LegendasTVProvider, Episode, Movie, guess_matches, guessit, sanitize
|
||||
@@ -60,6 +62,22 @@ class LegendasTVSubtitle(_LegendasTVSubtitle):
|
||||
class LegendasTVProvider(_LegendasTVProvider):
|
||||
subtitle_class = LegendasTVSubtitle
|
||||
|
||||
def __init__(self, username=None, password=None):
|
||||
|
||||
# Provider needs UNRAR installed. If not available raise ConfigurationError
|
||||
try:
|
||||
rarfile.custom_check([rarfile.UNRAR_TOOL], True)
|
||||
except rarfile.RarExecError:
|
||||
raise ConfigurationError('UNRAR tool not available')
|
||||
|
||||
if any((username, password)) and not all((username, password)):
|
||||
raise ConfigurationError('Username and password must be specified')
|
||||
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.logged_in = False
|
||||
self.session = None
|
||||
|
||||
def download_subtitle(self, subtitle):
|
||||
super(LegendasTVProvider, self).download_subtitle(subtitle)
|
||||
subtitle.archive.content = None
|
||||
|
||||
@@ -5,6 +5,7 @@ import time
|
||||
import logging
|
||||
import traceback
|
||||
import types
|
||||
from httplib import ResponseNotReady
|
||||
|
||||
from guessit import guessit
|
||||
from subliminal import ProviderError
|
||||
@@ -42,7 +43,7 @@ class ProviderRetryMixin(object):
|
||||
while i <= amount:
|
||||
try:
|
||||
return f()
|
||||
except (Unauthorized, ServiceUnavailable, TooManyRequests, DownloadLimitExceeded):
|
||||
except (Unauthorized, ServiceUnavailable, TooManyRequests, DownloadLimitExceeded, ResponseNotReady):
|
||||
raise
|
||||
except exc:
|
||||
formatted_exc = traceback.format_exc()
|
||||
@@ -56,8 +57,8 @@ class ProviderRetryMixin(object):
|
||||
|
||||
class ProviderSubtitleArchiveMixin(object):
|
||||
"""
|
||||
handled ZipFile and RarFile archives
|
||||
needs subtitle.episode, subtitle.season, subtitle.matches and subtitle.releases to work
|
||||
handles ZipFile and RarFile archives
|
||||
needs subtitle.episode, subtitle.season, subtitle.matches, subtitle.releases and subtitle.asked_for_episode to work
|
||||
"""
|
||||
def get_subtitle_from_archive(self, subtitle, archive):
|
||||
# extract subtitle's content
|
||||
@@ -84,7 +85,7 @@ class ProviderSubtitleArchiveMixin(object):
|
||||
# - release group matches (and we asked for one and it was matched, or it was not matched)
|
||||
is_episode = subtitle.asked_for_episode
|
||||
|
||||
episodes = guess["episode"]
|
||||
episodes = guess.get("episode")
|
||||
if is_episode and episodes and not isinstance(episodes, list):
|
||||
episodes = [episodes]
|
||||
|
||||
@@ -92,7 +93,7 @@ class ProviderSubtitleArchiveMixin(object):
|
||||
(
|
||||
subtitle.episode in episodes
|
||||
or (subtitle.is_pack and subtitle.asked_for_episode in episodes)
|
||||
) and guess["season"] == subtitle.season):
|
||||
) and guess.get("season") == subtitle.season):
|
||||
|
||||
format_matches = True
|
||||
wanted_format_but_not_found = False
|
||||
@@ -122,12 +123,13 @@ class ProviderSubtitleArchiveMixin(object):
|
||||
("release_group" in subtitle.matches or
|
||||
"hash" in subtitle.matches)):
|
||||
|
||||
asked_for_rlsgrp = subtitle.asked_for_release_group.lower()
|
||||
if subtitle.asked_for_release_group:
|
||||
asked_for_rlsgrp = subtitle.asked_for_release_group.lower()
|
||||
|
||||
if asked_for_rlsgrp:
|
||||
release_group_matches = False
|
||||
if asked_for_rlsgrp in sub_name_lower:
|
||||
release_group_matches = True
|
||||
if asked_for_rlsgrp:
|
||||
release_group_matches = False
|
||||
if asked_for_rlsgrp in sub_name_lower:
|
||||
release_group_matches = True
|
||||
|
||||
if release_group_matches and format_matches:
|
||||
matching_sub = sub_name
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
# coding=utf-8
|
||||
|
||||
import base64
|
||||
import logging
|
||||
import os
|
||||
import traceback
|
||||
import zlib
|
||||
|
||||
import requests
|
||||
|
||||
from babelfish import language_converters
|
||||
from dogpile.cache.api import NO_VALUE
|
||||
@@ -10,7 +14,8 @@ from subliminal.providers.opensubtitles import OpenSubtitlesProvider as _OpenSub
|
||||
OpenSubtitlesSubtitle as _OpenSubtitlesSubtitle, Episode, ServerProxy, Unauthorized, NoSession, \
|
||||
DownloadLimitReached, InvalidImdbid, UnknownUserAgent, DisabledUserAgent, OpenSubtitlesError
|
||||
from mixins import ProviderRetryMixin
|
||||
from subliminal_patch.http import SubZeroTransport
|
||||
from subliminal.subtitle import fix_line_ending
|
||||
from subliminal_patch.http import SubZeroRequestsTransport
|
||||
from subliminal.cache import region
|
||||
from subliminal_patch.score import framerate_equal
|
||||
from subzero.language import Language
|
||||
@@ -74,15 +79,17 @@ class OpenSubtitlesProvider(ProviderRetryMixin, _OpenSubtitlesProvider):
|
||||
hearing_impaired_verifiable = True
|
||||
skip_wrong_fps = True
|
||||
is_vip = False
|
||||
use_ssl = True
|
||||
timeout = 15
|
||||
|
||||
default_url = "https://api.opensubtitles.org/xml-rpc"
|
||||
vip_url = "https://vip-api.opensubtitles.org/xml-rpc"
|
||||
default_url = "//api.opensubtitles.org/xml-rpc"
|
||||
vip_url = "//vip-api.opensubtitles.org/xml-rpc"
|
||||
|
||||
languages = {Language.fromopensubtitles(l) for l in language_converters['szopensubtitles'].codes}# | {
|
||||
#Language.fromietf("sr-latn"), Language.fromietf("sr-cyrl")}
|
||||
|
||||
def __init__(self, username=None, password=None, use_tag_search=False, only_foreign=False, skip_wrong_fps=True,
|
||||
is_vip=False):
|
||||
is_vip=False, use_ssl=True, timeout=15):
|
||||
if any((username, password)) and not all((username, password)):
|
||||
raise ConfigurationError('Username and password must be specified')
|
||||
|
||||
@@ -93,12 +100,16 @@ class OpenSubtitlesProvider(ProviderRetryMixin, _OpenSubtitlesProvider):
|
||||
self.skip_wrong_fps = skip_wrong_fps
|
||||
self.token = None
|
||||
self.is_vip = is_vip
|
||||
self.use_ssl = use_ssl
|
||||
self.timeout = timeout
|
||||
|
||||
if is_vip:
|
||||
self.server = self.get_server_proxy(self.vip_url)
|
||||
logger.info("Using VIP server")
|
||||
else:
|
||||
self.server = self.get_server_proxy(self.default_url)
|
||||
logger.debug("Using timeout: %d", timeout)
|
||||
|
||||
if use_ssl:
|
||||
logger.debug("Using HTTPS connection")
|
||||
|
||||
self.default_url = ("https:" if use_ssl else "http:") + self.default_url
|
||||
self.vip_url = ("https:" if use_ssl else "http:") + self.vip_url
|
||||
|
||||
if use_tag_search:
|
||||
logger.info("Using tag/exact filename search")
|
||||
@@ -106,8 +117,9 @@ class OpenSubtitlesProvider(ProviderRetryMixin, _OpenSubtitlesProvider):
|
||||
if only_foreign:
|
||||
logger.info("Only searching for foreign/forced subtitles")
|
||||
|
||||
def get_server_proxy(self, url, timeout=10):
|
||||
return ServerProxy(url, SubZeroTransport(timeout, url))
|
||||
def get_server_proxy(self, url, timeout=None):
|
||||
return ServerProxy(url, SubZeroRequestsTransport(use_https=self.use_ssl, timeout=timeout or self.timeout,
|
||||
user_agent=os.environ.get("SZ_USER_AGENT", "Sub-Zero/2")))
|
||||
|
||||
def log_in(self, server_url=None):
|
||||
if server_url:
|
||||
@@ -117,13 +129,13 @@ class OpenSubtitlesProvider(ProviderRetryMixin, _OpenSubtitlesProvider):
|
||||
|
||||
response = self.retry(
|
||||
lambda: checked(
|
||||
self.server.LogIn(self.username, self.password, 'eng',
|
||||
os.environ.get("SZ_USER_AGENT", "Sub-Zero/2"))
|
||||
lambda: self.server.LogIn(self.username, self.password, 'eng',
|
||||
os.environ.get("SZ_USER_AGENT", "Sub-Zero/2"))
|
||||
)
|
||||
)
|
||||
|
||||
self.token = response['token']
|
||||
logger.debug('Logged in with token %r', self.token)
|
||||
logger.debug('Logged in with token %r', self.token[:10]+"X"*(len(self.token)-10))
|
||||
|
||||
region.set("os_token", self.token)
|
||||
|
||||
@@ -138,14 +150,21 @@ class OpenSubtitlesProvider(ProviderRetryMixin, _OpenSubtitlesProvider):
|
||||
return func()
|
||||
|
||||
def initialize(self):
|
||||
if self.is_vip:
|
||||
self.server = self.get_server_proxy(self.vip_url)
|
||||
logger.info("Using VIP server")
|
||||
else:
|
||||
self.server = self.get_server_proxy(self.default_url)
|
||||
|
||||
logger.info('Logging in')
|
||||
|
||||
token = region.get("os_token", expiration_time=3600)
|
||||
if token is not NO_VALUE:
|
||||
try:
|
||||
checked(self.server.NoOperation(token))
|
||||
logger.debug('Trying previous token')
|
||||
checked(lambda: self.server.NoOperation(token))
|
||||
self.token = token
|
||||
logger.info("Using previous login token: %s", self.token)
|
||||
logger.debug("Using previous login token: %s", self.token)
|
||||
return
|
||||
except:
|
||||
pass
|
||||
@@ -163,12 +182,18 @@ class OpenSubtitlesProvider(ProviderRetryMixin, _OpenSubtitlesProvider):
|
||||
logger.error("Login failed, please check your credentials")
|
||||
|
||||
def terminate(self):
|
||||
try:
|
||||
if self.server:
|
||||
self.server.close()
|
||||
except:
|
||||
pass
|
||||
if self.token:
|
||||
try:
|
||||
checked(lambda: self.server.LogOut(self.token))
|
||||
except:
|
||||
logger.error("Logout failed: %s", traceback.format_exc())
|
||||
|
||||
try:
|
||||
self.server.close()
|
||||
except:
|
||||
logger.error("Logout failed (server close): %s", traceback.format_exc())
|
||||
|
||||
self.server = None
|
||||
self.token = None
|
||||
|
||||
def list_subtitles(self, video, languages):
|
||||
@@ -227,7 +252,7 @@ class OpenSubtitlesProvider(ProviderRetryMixin, _OpenSubtitlesProvider):
|
||||
# query the server
|
||||
logger.info('Searching subtitles %r', criteria)
|
||||
response = self.use_token_or_login(
|
||||
lambda: self.retry(lambda: checked(self.server.SearchSubtitles(self.token, criteria)))
|
||||
lambda: self.retry(lambda: checked(lambda: self.server.SearchSubtitles(self.token, criteria)))
|
||||
)
|
||||
|
||||
subtitles = []
|
||||
@@ -239,24 +264,30 @@ class OpenSubtitlesProvider(ProviderRetryMixin, _OpenSubtitlesProvider):
|
||||
|
||||
# loop over subtitle items
|
||||
for subtitle_item in response['data']:
|
||||
_subtitle_item = subtitle_item
|
||||
|
||||
# in case OS messes their API results up again, check whether we've got a dict or a string as subtitle_item
|
||||
if hasattr(_subtitle_item, "startswith"):
|
||||
_subtitle_item = response["data"][subtitle_item]
|
||||
|
||||
# read the item
|
||||
language = Language.fromopensubtitles(subtitle_item['SubLanguageID'])
|
||||
hearing_impaired = bool(int(subtitle_item['SubHearingImpaired']))
|
||||
page_link = subtitle_item['SubtitlesLink']
|
||||
subtitle_id = int(subtitle_item['IDSubtitleFile'])
|
||||
matched_by = subtitle_item['MatchedBy']
|
||||
movie_kind = subtitle_item['MovieKind']
|
||||
hash = subtitle_item['MovieHash']
|
||||
movie_name = subtitle_item['MovieName']
|
||||
movie_release_name = subtitle_item['MovieReleaseName']
|
||||
movie_year = int(subtitle_item['MovieYear']) if subtitle_item['MovieYear'] else None
|
||||
movie_imdb_id = 'tt' + subtitle_item['IDMovieImdb']
|
||||
movie_fps = subtitle_item.get('MovieFPS')
|
||||
series_season = int(subtitle_item['SeriesSeason']) if subtitle_item['SeriesSeason'] else None
|
||||
series_episode = int(subtitle_item['SeriesEpisode']) if subtitle_item['SeriesEpisode'] else None
|
||||
filename = subtitle_item['SubFileName']
|
||||
encoding = subtitle_item.get('SubEncoding') or None
|
||||
foreign_parts_only = bool(int(subtitle_item.get('SubForeignPartsOnly', 0)))
|
||||
language = Language.fromopensubtitles(_subtitle_item['SubLanguageID'])
|
||||
hearing_impaired = bool(int(_subtitle_item['SubHearingImpaired']))
|
||||
page_link = _subtitle_item['SubtitlesLink']
|
||||
subtitle_id = int(_subtitle_item['IDSubtitleFile'])
|
||||
matched_by = _subtitle_item['MatchedBy']
|
||||
movie_kind = _subtitle_item['MovieKind']
|
||||
hash = _subtitle_item['MovieHash']
|
||||
movie_name = _subtitle_item['MovieName']
|
||||
movie_release_name = _subtitle_item['MovieReleaseName']
|
||||
movie_year = int(_subtitle_item['MovieYear']) if _subtitle_item['MovieYear'] else None
|
||||
movie_imdb_id = 'tt' + _subtitle_item['IDMovieImdb']
|
||||
movie_fps = _subtitle_item.get('MovieFPS')
|
||||
series_season = int(_subtitle_item['SeriesSeason']) if _subtitle_item['SeriesSeason'] else None
|
||||
series_episode = int(_subtitle_item['SeriesEpisode']) if _subtitle_item['SeriesEpisode'] else None
|
||||
filename = _subtitle_item['SubFileName']
|
||||
encoding = _subtitle_item.get('SubEncoding') or None
|
||||
foreign_parts_only = bool(int(_subtitle_item.get('SubForeignPartsOnly', 0)))
|
||||
|
||||
# foreign/forced subtitles only wanted
|
||||
if only_foreign and not foreign_parts_only:
|
||||
@@ -266,7 +297,7 @@ class OpenSubtitlesProvider(ProviderRetryMixin, _OpenSubtitlesProvider):
|
||||
if not only_foreign and foreign_parts_only:
|
||||
continue
|
||||
|
||||
query_parameters = subtitle_item.get("QueryParameters")
|
||||
query_parameters = _subtitle_item.get("QueryParameters")
|
||||
|
||||
subtitle = self.subtitle_class(language, hearing_impaired, page_link, subtitle_id, matched_by,
|
||||
movie_kind,
|
||||
@@ -279,18 +310,31 @@ class OpenSubtitlesProvider(ProviderRetryMixin, _OpenSubtitlesProvider):
|
||||
return subtitles
|
||||
|
||||
def download_subtitle(self, subtitle):
|
||||
return self.use_token_or_login(lambda: super(OpenSubtitlesProvider, self).download_subtitle(subtitle))
|
||||
logger.info('Downloading subtitle %r', subtitle)
|
||||
response = self.use_token_or_login(
|
||||
lambda: checked(
|
||||
lambda: self.server.DownloadSubtitles(self.token, [str(subtitle.subtitle_id)])
|
||||
)
|
||||
)
|
||||
subtitle.content = fix_line_ending(zlib.decompress(base64.b64decode(response['data'][0]['data']), 47))
|
||||
|
||||
|
||||
def checked(response):
|
||||
"""Check a response status before returning it.
|
||||
def checked(fn):
|
||||
"""Run :fn: and check the response status before returning it.
|
||||
|
||||
:param response: a response from a XMLRPC call to OpenSubtitles.
|
||||
:param fn: the function to make an XMLRPC call to OpenSubtitles.
|
||||
:return: the response.
|
||||
:raise: :class:`OpenSubtitlesError`
|
||||
|
||||
"""
|
||||
status_code = int(response['status'][:3])
|
||||
response = None
|
||||
try:
|
||||
response = fn()
|
||||
except requests.RequestException as e:
|
||||
status_code = e.response.status_code
|
||||
else:
|
||||
status_code = int(response['status'][:3])
|
||||
|
||||
if status_code == 401:
|
||||
raise Unauthorized
|
||||
if status_code == 406:
|
||||
@@ -310,4 +354,4 @@ def checked(response):
|
||||
if status_code != 200:
|
||||
raise OpenSubtitlesError(response['status'])
|
||||
|
||||
return response
|
||||
return response
|
||||
|
||||
@@ -0,0 +1,390 @@
|
||||
# coding=utf-8
|
||||
import io
|
||||
import six
|
||||
import os
|
||||
from pkg_resources import require
|
||||
import logging
|
||||
import re
|
||||
import os
|
||||
import time
|
||||
|
||||
from babelfish import Language, language_converters
|
||||
from requests import Session
|
||||
|
||||
from subliminal.subtitle import fix_line_ending
|
||||
from subliminal_patch.providers import Provider
|
||||
from subliminal_patch.providers.mixins import ProviderSubtitleArchiveMixin
|
||||
from subliminal.providers import ParserBeautifulSoup
|
||||
from subliminal_patch.exceptions import ProviderError
|
||||
from subliminal.score import get_equivalent_release_groups
|
||||
from subliminal_patch.subtitle import Subtitle, guess_matches
|
||||
from subliminal.utils import sanitize, sanitize_release_group
|
||||
from subliminal.video import Episode, Movie
|
||||
from zipfile import ZipFile, is_zipfile
|
||||
from rarfile import RarFile, is_rarfile
|
||||
from subliminal_patch.utils import sanitize, fix_inconsistent_naming as _fix_inconsistent_naming
|
||||
from guessit import guessit
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
language_converters.register('supersubtitles = subliminal_patch.converters.supersubtitles:SuperSubtitlesConverter')
|
||||
|
||||
|
||||
class SuperSubtitlesSubtitle(Subtitle):
|
||||
"""SuperSubtitles Subtitle."""
|
||||
provider_name = 'supersubtitles'
|
||||
|
||||
def __str__(self):
|
||||
subtit = "Subtitle id: " + str(self.subtitle_id) \
|
||||
+ " Series: " + self.series \
|
||||
+ " Season: " + str(self.season) \
|
||||
+ " Episode: " + str(self.episode) \
|
||||
+ " Version: " + str(self.version) \
|
||||
+ " Releases: " + str(self.releases) \
|
||||
+ " DownloadLink: " + str(self.page_link) \
|
||||
+ " Matches: " + str(self.matches)
|
||||
if self.year:
|
||||
subtit = subtit + " Year: " + str(self.year)
|
||||
return subtit.encode('utf-8')
|
||||
|
||||
def __init__(self, language, page_link, subtitle_id, series, season, episode, version,
|
||||
releases, year, imdb_id, asked_for_episode=None, asked_for_release_group=None):
|
||||
super(SuperSubtitlesSubtitle, self).__init__(language, page_link=page_link)
|
||||
self.subtitle_id = subtitle_id
|
||||
self.series = series
|
||||
self.season = season
|
||||
self.episode = episode
|
||||
self.version = version
|
||||
self.releases = releases
|
||||
self.year = year
|
||||
if year:
|
||||
self.year = int(year)
|
||||
|
||||
self.release_info = u", ".join(releases)
|
||||
self.page_link = page_link
|
||||
self.asked_for_release_group = asked_for_release_group
|
||||
self.asked_for_episode = asked_for_episode
|
||||
self.imdb_id = imdb_id
|
||||
self.is_pack = True
|
||||
|
||||
def numeric_id(self):
|
||||
return self.subtitle_id
|
||||
|
||||
def __repr__(self):
|
||||
ep_addon = (" S%02dE%02d" % (self.season, self.episode)) if self.episode else ""
|
||||
return '<%s %r [%s]>' % (
|
||||
self.__class__.__name__, u"%s%s%s [%s]" % (self.series, " (%s)" % self.year if self.year else "", ep_addon,
|
||||
self.release_info), self.language)
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return str(self.subtitle_id)
|
||||
|
||||
def get_matches(self, video):
|
||||
matches = set()
|
||||
|
||||
# episode
|
||||
if isinstance(video, Episode):
|
||||
# series
|
||||
if video.series and sanitize(self.series) == sanitize(video.series):
|
||||
matches.add('series')
|
||||
# season
|
||||
if video.season and self.season == video.season:
|
||||
matches.add('season')
|
||||
# episode
|
||||
if video.episode and self.episode == video.episode:
|
||||
matches.add('episode')
|
||||
# imdb_id
|
||||
if video.series_imdb_id and self.imdb_id and str(self.imdb_id) == str(video.series_imdb_id):
|
||||
matches.add('series_imdb_id')
|
||||
matches.add('series')
|
||||
matches.add('year')
|
||||
# year
|
||||
if ('series' in matches and video.original_series and self.year is None or
|
||||
video.year and video.year == self.year):
|
||||
matches.add('year')
|
||||
# movie
|
||||
elif isinstance(video, Movie):
|
||||
# title
|
||||
if video.title and (sanitize(self.series) in (
|
||||
sanitize(name) for name in [video.title] + video.alternative_titles)):
|
||||
matches.add('title')
|
||||
# imdb_id
|
||||
if video.imdb_id and self.imdb_id == video.imdb_id:
|
||||
matches.add('imdb_id')
|
||||
matches.add('title')
|
||||
matches.add('year')
|
||||
# year
|
||||
if video.year and self.year == video.year:
|
||||
matches.add('year')
|
||||
|
||||
# release_group
|
||||
if (video.release_group and self.version and
|
||||
any(r in sanitize_release_group(self.version)
|
||||
for r in get_equivalent_release_groups(sanitize_release_group(video.release_group)))):
|
||||
matches.add('release_group')
|
||||
# resolution
|
||||
if video.resolution and self.version and video.resolution in self.version.lower():
|
||||
matches.add('resolution')
|
||||
# format
|
||||
if video.format and self.version and video.format.lower() in self.version.lower():
|
||||
matches.add('format')
|
||||
# other properties
|
||||
# matches |= guess_matches(video, guessit(self.release_info.encode("utf-8")))
|
||||
|
||||
self.matches = matches
|
||||
return matches
|
||||
|
||||
|
||||
class SuperSubtitlesProvider(Provider, ProviderSubtitleArchiveMixin):
|
||||
"""SuperSubtitles Provider."""
|
||||
languages = {Language('hun', 'HU')} | {Language(l) for l in [
|
||||
'hun', 'eng'
|
||||
]}
|
||||
video_types = (Episode, Movie)
|
||||
# https://www.feliratok.info/?search=&soriSorszam=&nyelv=&sorozatnev=The+Flash+%282014%29&sid=3212&complexsearch=true&knyelv=0&evad=4&epizod1=1&cimke=0&minoseg=0&rlsr=0&tab=all
|
||||
server_url = 'https://www.feliratok.info/'
|
||||
subtitle_class = SuperSubtitlesSubtitle
|
||||
hearing_impaired_verifiable = False
|
||||
multi_result_throttle = 2 # seconds
|
||||
|
||||
def initialize(self):
|
||||
self.session = Session()
|
||||
self.session.headers = {'User-Agent': os.environ.get("SZ_USER_AGENT", "Sub-Zero/2")}
|
||||
|
||||
def terminate(self):
|
||||
self.session.close()
|
||||
|
||||
def get_language(self, text):
|
||||
if text == 'Magyar':
|
||||
return Language.fromsupersubtitles('hu')
|
||||
if text == 'Angol':
|
||||
return Language.fromsupersubtitles('en')
|
||||
return None
|
||||
|
||||
def find_imdb_id(self, sub_id):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
url = self.server_url + "index.php?tipus=adatlap&azon=a_" + sub_id
|
||||
# url = https://www.feliratok.info/index.php?tipus=adatlap&azon=a_1518600916
|
||||
logger.info('Get IMDB id from URL %s', url)
|
||||
r = self.session.get(url, timeout=10).content
|
||||
|
||||
soup = ParserBeautifulSoup(r, ['lxml'])
|
||||
links = soup.find_all("a")
|
||||
|
||||
for value in links:
|
||||
if "imdb.com" in str(value):
|
||||
# <a alt="iMDB" href="http://www.imdb.com/title/tt2357547/" target="_blank"><img alt="iMDB" src="img/adatlap/imdb.png"/></a>
|
||||
imdb_id = re.findall(r'(?<=www\.imdb\.com/title/).*(?=/")', str(value))[0]
|
||||
return imdb_id
|
||||
|
||||
return None
|
||||
|
||||
def find_id(self, series, year, original_title):
|
||||
"""
|
||||
We need to find the id of the series at the following url:
|
||||
https://www.feliratok.info/index.php?term=SERIESNAME&nyelv=0&action=autoname
|
||||
Where SERIESNAME is a searchable string.
|
||||
The result will be something like this:
|
||||
[{"name":"DC\u2019s Legends of Tomorrow (2016)","ID":"3725"},{"name":"Miles from Tomorrowland (2015)","ID":"3789"}
|
||||
,{"name":"No Tomorrow (2016)","ID":"4179"}]
|
||||
|
||||
"""
|
||||
|
||||
# Search for exact name
|
||||
url = self.server_url + "index.php?term=" + series + "&nyelv=0&action=autoname"
|
||||
# url = self.server_url + "index.php?term=" + "fla"+ "&nyelv=0&action=autoname"
|
||||
logger.info('Get series id from URL %s', url)
|
||||
r = self.session.get(url, timeout=10)
|
||||
|
||||
# r is something like this:
|
||||
# [{"name":"DC\u2019s Legends of Tomorrow (2016)","ID":"3725"},{"name":"Miles from Tomorrowland (2015)","ID":"3789"}
|
||||
# ,{"name":"No Tomorrow (2016)","ID":"4179"}]
|
||||
|
||||
results = r.json()
|
||||
|
||||
# check all of the results:
|
||||
for result in results:
|
||||
try:
|
||||
# "name":"Miles from Tomorrowland (2015)","ID":"3789"
|
||||
result_year = re.findall(r"(?<=\()\d\d\d\d(?=\))", result['name'])[0]
|
||||
except IndexError:
|
||||
result_year = ""
|
||||
|
||||
try:
|
||||
# "name":"Miles from Tomorrowland (2015)","ID":"3789"
|
||||
result_title = re.findall(r".*(?=\(\d\d\d\d\))", result['name'])[0]
|
||||
result_id = result['ID']
|
||||
except IndexError:
|
||||
continue
|
||||
|
||||
result_title = result_title.strip().replace("�", "").replace(" ", ".")
|
||||
|
||||
guessable = result_title.strip() + ".s01e01." + result_year
|
||||
guess = guessit(guessable, {'type': "episode"})
|
||||
|
||||
if sanitize(original_title) == sanitize(guess['title']) and year and guess['year'] and year == guess['year']:
|
||||
# Return the founded id
|
||||
return result_id
|
||||
|
||||
return None
|
||||
|
||||
def query(self, series, video=None):
|
||||
year = video.year
|
||||
subtitle = None
|
||||
if isinstance(video, Episode):
|
||||
series = video.series
|
||||
season = video.season
|
||||
episode = video.episode
|
||||
#seriesa = series.replace(' ', '+')
|
||||
|
||||
# Get ID of series with original name
|
||||
series_id = self.find_id(series, year, series)
|
||||
if not series_id:
|
||||
# If not founded try without ' char
|
||||
modified_series = series.replace(' ', '+').replace('\'', '')
|
||||
series_id = self.find_id(modified_series, year, series)
|
||||
if not series_id and modified_series:
|
||||
# If still not founded try with the longest word is series title
|
||||
modified_series = modified_series.split('+')
|
||||
modified_series = max(modified_series, key=len)
|
||||
series_id = self.find_id(modified_series, year, series)
|
||||
|
||||
if not series_id:
|
||||
return None
|
||||
|
||||
# https://www.feliratok.info/index.php?search=&soriSorszam=&nyelv=&sorozatnev=&sid=2075&complexsearch=true&knyelv=0&evad=6&epizod1=16&cimke=0&minoseg=0&rlsr=0&tab=all
|
||||
url = self.server_url + "index.php?search=&soriSorszam=&nyelv=&sorozatnev=&sid=" + \
|
||||
str(series_id) + "&complexsearch=true&knyelv=0&evad=" + str(season) + "&epizod1=" + str(
|
||||
episode) + "&cimke=0&minoseg=0&rlsr=0&tab=all"
|
||||
subtitle = self.process_subs(series, video, url)
|
||||
|
||||
if not subtitle:
|
||||
# No Subtitle found. Maybe already archived to season pack
|
||||
url = self.server_url + "index.php?search=&soriSorszam=&nyelv=&sorozatnev=&sid=" + \
|
||||
str(series_id) + "&complexsearch=true&knyelv=0&evad=" + str(
|
||||
season) + "&epizod1=&evadpakk=on&cimke=0&minoseg=0&rlsr=0&tab=all"
|
||||
subtitle = self.process_subs(series, video, url)
|
||||
|
||||
if isinstance(video, Movie):
|
||||
title = series.replace(" ", "+")
|
||||
|
||||
# https://www.feliratok.info/index.php?search=The+Hitman%27s+BodyGuard&soriSorszam=&nyelv=&tab=film
|
||||
url = self.server_url + "index.php?search=" + title + "&soriSorszam=&nyelv=&tab=film"
|
||||
subtitle = self.process_subs(series, video, url)
|
||||
|
||||
return subtitle
|
||||
|
||||
def process_subs(self, series, video, url):
|
||||
|
||||
subtitles = []
|
||||
|
||||
logger.info('URL for subtitles %s', url)
|
||||
r = self.session.get(url, timeout=10).content
|
||||
|
||||
soup = ParserBeautifulSoup(r, ['lxml'])
|
||||
tables = soup.find_all("table")
|
||||
tables = tables[0].find_all("tr")
|
||||
i = 0
|
||||
series_imdb_id = None
|
||||
for table in tables:
|
||||
if "vilagit" in str(table) and i > 1:
|
||||
try:
|
||||
sub_hun_name = table.findAll("div", {"class": "magyar"})[0]
|
||||
if isinstance(video, Episode):
|
||||
if "vad)" not in str(sub_hun_name):
|
||||
# <div class="magyar">A pletykaf�szek (3. �vad)</div>
|
||||
sub_hun_name = re.findall(r'(?<=<div class="magyar">).*(?= -)', str(sub_hun_name))[0]
|
||||
else:
|
||||
# <div class="magyar">A holnap legend�i - 3x11</div>
|
||||
sub_hun_name = re.findall(r'(?<=<div class="magyar">).*(?= \()', str(sub_hun_name))[0]
|
||||
if isinstance(video, Movie):
|
||||
sub_hun_name = re.findall(r'(?<=<div class="magyar">).*(?=</div)', str(sub_hun_name))[0]
|
||||
except IndexError:
|
||||
sub_hun_name = ""
|
||||
|
||||
asked_for_episode = None
|
||||
sub_season = None
|
||||
sub_episode = None
|
||||
sub_english = table.findAll("div", {"class": "eredeti"})
|
||||
if isinstance(video, Episode):
|
||||
asked_for_episode = video.episode
|
||||
if "Season" not in str(sub_english):
|
||||
# [<div class="eredeti">Gossip Girl (Season 3) (DVDRip-REWARD)</div>]
|
||||
sub_english_name = re.findall(r'(?<=<div class="eredeti">).*?(?= -)', str(sub_english))[0]
|
||||
sub_season = int((re.findall(r"(?<=- ).*?(?= - )", str(sub_english))[0].split('x')[0]).strip())
|
||||
sub_episode = int((re.findall(r"(?<=- ).*?(?= - )", str(sub_english))[0].split('x')[1]).strip())
|
||||
|
||||
else:
|
||||
# [<div class="eredeti">DC's Legends of Tomorrow - 3x11 - Here I Go Again (HDTV-AFG, HDTV-RMX, 720p-SVA, 720p-PSA </div>]
|
||||
sub_english_name = \
|
||||
re.findall(r'(?<=<div class="eredeti">).*?(?=\(Season)', str(sub_english))[0]
|
||||
sub_season = int(re.findall(r"(?<=Season )\d+(?=\))", str(sub_english))[0])
|
||||
sub_episode = int(video.episode)
|
||||
if isinstance(video, Movie):
|
||||
sub_english_name = re.findall(r'(?<=<div class="eredeti">).*?(?=\()', str(sub_english))[0]
|
||||
|
||||
sub_version = (str(sub_english).split('(')[len(str(sub_english).split('(')) - 1]).split(')')[0]
|
||||
# <small>Angol</small>
|
||||
lang = table.findAll("small")[0]
|
||||
sub_language = self.get_language(re.findall(r"(?<=<small>).*(?=</small>)", str(lang))[0])
|
||||
|
||||
# <a href="/index.php?action=letolt&fnev=DCs Legends of Tomorrow - 03x11 - Here I Go Again.SVA.English.C.orig.Addic7ed.com.srt&felirat=1519162191">
|
||||
link = str(table.findAll("a")[len(table.findAll("a")) - 1]).replace("amp;", "")
|
||||
sub_downloadlink = self.server_url + re.findall(r'(?<=href="/).*(?=">)', link)[0]
|
||||
|
||||
sub_id = re.findall(r"(?<=felirat\=).*(?=\"\>)", link)[0]
|
||||
sub_year = video.year
|
||||
sub_releases = [s.strip() for s in sub_version.split(',')]
|
||||
|
||||
# For episodes we open the series page so all subtitles imdb_id must be the same. no need to check all
|
||||
if isinstance(video, Episode) and series_imdb_id is not None:
|
||||
sub_imdb_id = series_imdb_id
|
||||
else:
|
||||
sub_imdb_id = self.find_imdb_id(sub_id)
|
||||
series_imdb_id = sub_imdb_id
|
||||
|
||||
subtitle = SuperSubtitlesSubtitle(sub_language, sub_downloadlink, sub_id, sub_english_name.strip(), sub_season,
|
||||
sub_episode, sub_version, sub_releases, sub_year, sub_imdb_id,
|
||||
asked_for_episode, asked_for_release_group=video.release_group )
|
||||
subtitles.append(subtitle)
|
||||
i = i + 1
|
||||
return subtitles
|
||||
|
||||
def list_subtitles(self, video, languages):
|
||||
if isinstance(video, Episode):
|
||||
titles = [video.series] + video.alternative_series
|
||||
elif isinstance(video, Movie):
|
||||
titles = [video.title] + video.alternative_titles
|
||||
|
||||
for title in titles:
|
||||
subs = self.query(title, video=video)
|
||||
if subs:
|
||||
return subs
|
||||
|
||||
time.sleep(self.multi_result_throttle)
|
||||
return []
|
||||
|
||||
def download_subtitle(self, subtitle):
|
||||
|
||||
# download as a zip
|
||||
logger.info('Downloading subtitle %r', subtitle.subtitle_id)
|
||||
r = self.session.get(subtitle.page_link, timeout=10)
|
||||
r.raise_for_status()
|
||||
|
||||
if ".rar" in subtitle.page_link:
|
||||
logger.debug('Archive identified as rar')
|
||||
archive_stream = io.BytesIO(r.content)
|
||||
archive = RarFile(archive_stream)
|
||||
subtitle.content = self.get_subtitle_from_archive(subtitle, archive)
|
||||
elif ".zip" in subtitle.page_link:
|
||||
logger.debug('Archive identified as zip')
|
||||
archive_stream = io.BytesIO(r.content)
|
||||
archive = ZipFile(archive_stream)
|
||||
subtitle.content = self.get_subtitle_from_archive(subtitle, archive)
|
||||
else:
|
||||
subtitle.content = fix_line_ending(r.content)
|
||||
@@ -97,13 +97,7 @@ class TVsubtitlesProvider(_TVsubtitlesProvider):
|
||||
|
||||
return episode_ids
|
||||
|
||||
def query(self, series, season, episode, year=None):
|
||||
# search the show id
|
||||
show_id = self.search_show_id(series, year)
|
||||
if show_id is None:
|
||||
logger.info('No show id found for %r (%r)', series, {'year': year})
|
||||
return []
|
||||
|
||||
def query(self, show_id, series, season, episode, year=None):
|
||||
# get the episode ids
|
||||
episode_ids = self.get_episode_ids(show_id, season)
|
||||
# Provider doesn't store multi episode information
|
||||
|
||||
@@ -82,4 +82,702 @@ FIRST_THOUSAND_OR_SO_USER_AGENTS = [
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/527 (KHTML, like Gecko, Safari/419.3) Arora/0.6 (Change: )",
|
||||
"Avant Browser/1.2.789rel1 (http://www.avantbrowser.com)",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.0 Safari/532.5",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.310.0 Safari/532.9",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.514.0 Safari/534.7",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/9.0.601.0 Safari/534.14",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/10.0.601.0 Safari/534.14",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.27 (KHTML, like Gecko) Chrome/12.0.712.0 Safari/534.27",
|
||||
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.120 Safari/535.2",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.36 Safari/535.7",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/18.6.872.0 Safari/535.2 UNTRUSTED/1.0 3gpp-gba UNTRUSTED/1.0",
|
||||
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
|
||||
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.0 x64; en-US; rv:1.9pre) Gecko/2008072421 Minefield/3.0.2pre",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.0; en-GB; rv:1.9.0.11) Gecko/2009060215 Firefox/3.0.11 (.NET CLR 3.5.30729)",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 GTB5",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; tr; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 ( .NET CLR 3.5.30729; .NET4.0E)",
|
||||
"Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0a2) Gecko/20110622 Firefox/6.0a2",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.1) Gecko/20100101 Firefox/10.0.1",
|
||||
"Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20120403211507 Firefox/12.0",
|
||||
"Mozilla/5.0 (Windows NT 6.0; rv:14.0) Gecko/20100101 Firefox/14.0.1",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0) Gecko/20120427 Firefox/15.0a1",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0) Gecko/16.0 Firefox/16.0",
|
||||
"Mozilla/5.0 (Windows NT 6.2; rv:19.0) Gecko/20121129 Firefox/19.0",
|
||||
"Mozilla/5.0 (Windows NT 6.2; rv:20.0) Gecko/20121202 Firefox/20.0",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Maxthon 2.0)",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0b4pre) Gecko/20100815 Minefield/4.0b4pre",
|
||||
"Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0 )",
|
||||
"Opera/9.80 (Windows NT 5.2; U; en) Presto/2.2.15 Version/10.10",
|
||||
"Opera/9.80 (Windows NT 5.1; U; zh-tw) Presto/2.8.131 Version/11.10",
|
||||
"Opera/9.80 (Windows NT 6.1; U; en) Presto/2.7.62 Version/11.01",
|
||||
"Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00",
|
||||
"Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14",
|
||||
"Mozilla/5.0 (Windows; U; WinNT4.0; en-US; rv:1.2b) Gecko/20021001 Phoenix/0.2",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.23) Gecko/20090825 SeaMonkey/1.1.18",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1.17) Gecko/20110123 (like Firefox/3.x) SeaMonkey/2.0.12",
|
||||
"Mozilla/5.0 (Windows NT 5.2; rv:10.0.1) Gecko/20100101 Firefox/10.0.1 SeaMonkey/2.7.1",
|
||||
"Mozilla/5.0 (Windows; U; ; en-NZ) AppleWebKit/527 (KHTML, like Gecko, Safari/419.3) Arora/0.8.0",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Avant Browser; Avant Browser; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.8 (KHTML, like Gecko) Beamrise/17.2.0.9 Chrome/17.0.939.0 Safari/535.8",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML like Gecko) Chrome/28.0.1469.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML like Gecko) Chrome/28.0.1469.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2869.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 AOL/11.0 AOLBUILD/11.0.1305 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3191.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240",
|
||||
"Mozilla/5.0 (MSIE 9.0; Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14931",
|
||||
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063",
|
||||
"Mozilla/5.0 (Windows NT 6.1; rv:21.0) Gecko/20130401 Firefox/21.0",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/29.0",
|
||||
"Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0",
|
||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:35.0) Gecko/20100101 Firefox/35.0",
|
||||
"Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0",
|
||||
"Mozilla/5.0 (Windows NT 6.0; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0",
|
||||
"Mozilla/5.0 (compatible; Konqueror/4.5; Windows) KHTML/4.5.4 (like Gecko)",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.1 (KHTML, like Gecko) Maxthon/3.0.8.2 Safari/533.1",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML like Gecko) Maxthon/4.0.0.2000 Chrome/22.0.1229.79 Safari/537.1",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.4.6.1000 Chrome/30.0.1599.101 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/5.0.4.3000 Chrome/47.0.2526.73 Safari/537.36",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
|
||||
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)",
|
||||
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0)",
|
||||
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/5.0)",
|
||||
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)",
|
||||
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.2; Trident/5.0)",
|
||||
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.2; WOW64; Trident/5.0)",
|
||||
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8; Zune 4.7)",
|
||||
"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0)",
|
||||
"Mozilla/5.0 (compatible; MSIE 10.6; Windows NT 6.1; Trident/5.0; InfoPath.2; SLCC1; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727) 3gpp-gba UNTRUSTED/1.0",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
|
||||
"Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)",
|
||||
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) MxBrowser/4.5.10.7000 Chrome/30.0.1551.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; MATBJS; rv:11.0) like Gecko",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; MALNJS; rv:11.0) like Gecko",
|
||||
"Opera/9.80 (Windows NT 6.1; WOW64) Presto/2.12.388 Version/12.16",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.12 Safari/537.36 OPR/14.0.1116.4",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.29 Safari/537.36 OPR/15.0.1147.24 (Edition Next)",
|
||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36 OPR/18.0.1284.49",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.76 Safari/537.36 OPR/19.0.1326.56",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36 OPR/20.0.1387.91",
|
||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36 OPR/28.0.1750.40",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.155 Safari/537.36 OPR/31.0.1889.174",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36 OPR/36.0.2130.46",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36 OPR/47.0.2631.55",
|
||||
"Mozilla/5.0 (Windows NT 10.0; rv:45.9) Gecko/20100101 Goanna/3.2 Firefox/45.9 PaleMoon/27.4.0",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.2; es-US ) AppleWebKit/540.0 (KHTML like Gecko) Version/6.0 Safari/8900.00",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.71 (KHTML like Gecko) WebVideo/1.0.1.10 Version/7.0 Safari/537.71",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20120422 Firefox/12.0 SeaMonkey/2.9",
|
||||
"Mozilla/5.0 (Windows NT 6.0; rv:36.0) Gecko/20100101 Firefox/36.0 SeaMonkey/2.33.1",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 UBrowser/5.6.13705.206 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.89 Vivaldi/1.0.94.2 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.90 Safari/537.36 Vivaldi/1.4.589.11",
|
||||
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.91 Safari/537.36 Vivaldi/1.92.917.39",
|
||||
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 YaBrowser/17.3.0.1785 Yowser/2.5 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 Camino/2.2.1",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0b6pre) Gecko/20100907 Firefox/4.0b6pre Camino/2.2a1pre",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-US) AppleWebKit/532.8 (KHTML, like Gecko) Chrome/4.0.302.2 Safari/532.8",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.464.0 Safari/534.3",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.15 Safari/534.13",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.186 Safari/535.1",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.54 Safari/535.2",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.36 Safari/535.7",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.4 (KHTML like Gecko) Chrome/22.0.1229.79 Safari/537.4",
|
||||
"Mozilla/5.0 (Macintosh; U; Mac OS X Mach-O; en-US; rv:2.0a) Gecko/20040614 Firefox/3.0.0",
|
||||
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.0.3) Gecko/2008092414 Firefox/3.0.3",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.14) Gecko/20110218 AlexaToolbar/alxf-2.0 Firefox/3.6.14",
|
||||
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:5.0) Gecko/20100101 Firefox/5.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2; rv:10.0.1) Gecko/20100101 Firefox/10.0.1",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20120813 Firefox/16.0",
|
||||
"Mozilla/4.0 (compatible; MSIE 5.15; Mac_PowerPC)",
|
||||
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-US) AppleWebKit/125.4 (KHTML, like Gecko, Safari) OmniWeb/v563.15",
|
||||
"Opera/9.0 (Macintosh; PPC Mac OS X; U; en)",
|
||||
"Opera/9.20 (Macintosh; Intel Mac OS X; U; en)",
|
||||
"Opera/9.64 (Macintosh; PPC Mac OS X; U; en) Presto/2.1.1",
|
||||
"Opera/9.80 (Macintosh; Intel Mac OS X; U; en) Presto/2.6.30 Version/10.61",
|
||||
"Opera/9.80 (Macintosh; Intel Mac OS X 10.4.11; U; en) Presto/2.7.62 Version/11.00",
|
||||
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/85.8",
|
||||
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8",
|
||||
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; fr-fr) AppleWebKit/312.5 (KHTML, like Gecko) Safari/312.3",
|
||||
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418.8 (KHTML, like Gecko) Safari/419.3",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.31 (KHTML like Gecko) Chrome/26.0.1410.63 Safari/537.31",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 1083) AppleWebKit/537.36 (KHTML like Gecko) Chrome/28.0.1469.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1664.3 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1944.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2859.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.49 Safari/537.36",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:20.0) Gecko/20100101 Firefox/20.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:21.0) Gecko/20100101 Firefox/21.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:25.0) Gecko/20100101 Firefox/25.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:35.0) Gecko/20100101 Firefox/35.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:40.0) Gecko/20100101 Firefox/40.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:47.0) Gecko/20100101 Firefox/47.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:49.0) Gecko/20100101 Firefox/49.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:55.0) Gecko/20100101 Firefox/55.0",
|
||||
"iTunes/4.2 (Macintosh; U; PPC Mac OS X 10.2)",
|
||||
"iTunes/9.0.3 (Macintosh; U; Intel Mac OS X 10_6_2; en-ca)",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/600.8.9 (KHTML, like Gecko) Maxthon/4.5.2",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US) AppleWebKit/528.16 (KHTML, like Gecko, Safari/528.16) OmniWeb/v622.8.0.112941",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-US) AppleWebKit/528.16 (KHTML, like Gecko, Safari/528.16) OmniWeb/v622.8.0",
|
||||
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36 OPR/28.0.1750.51",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.82 Safari/537.36 OPR/29.0.1795.41",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; de-de) AppleWebKit/534.15 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-us) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_7; en-us) AppleWebKit/534.20.8 (KHTML, like Gecko) Version/5.1 Safari/534.20.8",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/536.26.17 (KHTML like Gecko) Version/6.0.2 Safari/536.26.17",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.78.1 (KHTML like Gecko) Version/7.0.6 Safari/537.78.1",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/600.8.9 (KHTML, like Gecko) Version/8.0.8 Safari/600.8.9",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11) AppleWebKit/601.1.56 (KHTML, like Gecko) Version/9.0 Safari/601.1.56",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/601.7.8 (KHTML, like Gecko) Version/10.1 Safari/603.1.30",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Safari/602.1.50",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.5; rv:10.0.1) Gecko/20100101 Firefox/10.0.1 SeaMonkey/2.7.1",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.0.13.81_10003810) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=true",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.105 Safari/537.36 Vivaldi/1.0.162.9",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.4 (KHTML, like Gecko) Chrome/4.0.237.0 Safari/532.4 Debian",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.8 (KHTML, like Gecko) Chrome/4.0.277.0 Safari/532.8",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.309.0 Safari/532.9",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.514.0 Safari/534.7",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/540.0 (KHTML, like Gecko) Ubuntu/10.10 Chrome/9.1.0.0 Safari/540.0",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Chrome/10.0.613.0 Safari/534.15",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.15 (KHTML, like Gecko) Ubuntu/10.10 Chromium/10.0.613.0 Chrome/10.0.613.0 Safari/534.15",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/10.10 Chromium/12.0.703.0 Chrome/12.0.703.0 Safari/534.24",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.20 Safari/535.1",
|
||||
"Mozilla/5.0 Slackware/13.37 (X11; U; Linux x86_64; en-US) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Ubuntu/11.04 Chromium/14.0.825.0 Chrome/14.0.825.0 Safari/535.1",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.2 (KHTML, like Gecko) Ubuntu/11.10 Chromium/15.0.874.120 Chrome/15.0.874.120 Safari/535.2",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
|
||||
"Mozilla/5.0 (X11; U; Linux; i686; en-US; rv:1.6) Gecko Epiphany/1.2.5",
|
||||
"Mozilla/5.0 (X11; U; Linux i586; en-US; rv:1.7.3) Gecko/20040924 Epiphany/1.4.4 (Ubuntu)",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040614 Firefox/0.8",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; sv-SE; rv:1.8.1.12) Gecko/20080207 Ubuntu/7.10 (gutsy) Firefox/2.0.0.12",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.11) Gecko/2009060309 Ubuntu/9.10 (karmic) Firefox/3.0.11",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.2) Gecko/20090803 Ubuntu/9.04 (jaunty) Shiretoko/3.5.2",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.5) Gecko/20091107 Firefox/3.5.5",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3) Gecko/20091020 Linux Mint/8 (Helena) Firefox/3.5.3",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.9) Gecko/20100915 Gentoo Firefox/3.6.9",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; pl-PL; rv:1.9.0.2) Gecko/20121223 Ubuntu/9.25 (jaunty) Firefox/3.8",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:2.0b6pre) Gecko/20100907 Firefox/4.0b6pre",
|
||||
"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:2.2a1pre) Gecko/20100101 Firefox/4.2a1pre",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:5.0) Gecko/20100101 Firefox/5.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:6.0) Gecko/20100101 Firefox/6.0",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:7.0a1) Gecko/20110623 Firefox/7.0a1",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:8.0) Gecko/20100101 Firefox/8.0",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:10.0.1) Gecko/20100101 Firefox/10.0.1",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.16) Gecko/20120421 Gecko Firefox/11.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:12.0) Gecko/20100101 Firefox/12.0",
|
||||
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1",
|
||||
"Mozilla/5.0 (X11; U; Linux; i686; en-US; rv:1.6) Gecko Galeon/1.3.14",
|
||||
"Mozilla/5.0 (X11; U; Linux ppc; en-US; rv:1.8.1.13) Gecko/20080313 Iceape/1.1.9 (Debian-1.1.9-5)",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; pt-PT; rv:1.9.2.3) Gecko/20100402 Iceweasel/3.6.3 (like Firefox/3.6.3) GTB7.0",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:5.0) Gecko/20100101 Firefox/5.0 Iceweasel/5.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:6.0a2) Gecko/20110615 Firefox/6.0a2 Iceweasel/6.0a2",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:14.0) Gecko/20100101 Firefox/14.0.1 Iceweasel/14.0.1",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20120724 Debian Iceweasel/15.02",
|
||||
"Konqueror/3.0-rc4; (Konqueror/3.0-rc4; i686 Linux;;datecode)",
|
||||
"Mozilla/5.0 (compatible; Konqueror/3.3; Linux 2.6.8-gentoo-r3; X11;",
|
||||
"Mozilla/5.0 (compatible; Konqueror/3.5; Linux 2.6.30-7.dmz.1-liquorix-686; X11) KHTML/3.5.10 (like Gecko) (Debian package 4:3.5.10.dfsg.1-1 b1)",
|
||||
"Mozilla/5.0 (compatible; Konqueror/3.5; Linux; en_US) KHTML/3.5.6 (like Gecko) (Kubuntu)",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; en-US; rv:2.0b2pre) Gecko/20100712 Minefield/4.0b2pre",
|
||||
"Mozilla/5.0 (X11; U; Linux; i686; en-US; rv:1.6) Gecko Debian/1.6-7",
|
||||
"MSIE (MSIE 6.0; X11; Linux; i686) Opera 7.23",
|
||||
"Opera/9.64 (X11; Linux i686; U; Linux Mint; nb) Presto/2.1.1",
|
||||
"Opera/9.80 (X11; Linux i686; U; en) Presto/2.2.15 Version/10.10",
|
||||
"Opera/9.80 (X11; Linux x86_64; U; pl) Presto/2.7.62 Version/11.00",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.34 (KHTML, like Gecko) QupZilla/1.2.0 Safari/534.34",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.17) Gecko/20110123 SeaMonkey/2.0.12",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0 (Swiftfox)",
|
||||
"Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527 (KHTML, like Gecko, Safari/419.3) Arora/0.10.1",
|
||||
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.4 (KHTML like Gecko) Chrome/22.0.1229.56 Safari/537.4",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1478.0 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; CrOS x86_64 5841.83.0) AppleWebKit/537.36 (KHTML like Gecko) Chrome/36.0.1985.138 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML like Gecko) Chrome/36.0.1985.125 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2166.2 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux i686 (x86_64)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.130 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2876.0 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux i686 (x86_64)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3187.0 Safari/537.366",
|
||||
"Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3178.0 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML like Gecko) Ubuntu Chromium/25.0.1364.160 Chrome/25.0.1364.160 Safari/537.22",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/33.0.1750.152 Chrome/33.0.1750.152 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/51.0.2704.79 Chrome/51.0.2704.79 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/60.0.3112.78 Chrome/60.0.3112.78 Safari/537.36",
|
||||
"Mozilla/4.0 (compatible; Dillo 3.0)",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-us) AppleWebKit/528.5 (KHTML, like Gecko, Safari/528.5 ) lt-GtkLauncher",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.32 (KHTML, like Gecko) Chromium/25.0.1349.2 Chrome/25.0.1349.2 Safari/537.32 Epiphany/3.8.2",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/604.1 (KHTML, like Gecko) Version/11.0 Safari/604.1 Ubuntu/17.04 (3.24.1-0ubuntu1) Epiphany/3.24.1",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:16.0) Gecko/20100101 Firefox/16.0",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; rv:19.0) Gecko/20100101 Slackware/13 Firefox/19.0",
|
||||
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:20.0) Gecko/20100101 Firefox/20.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:20.0) Gecko/20100101 Firefox/20.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:25.0) Gecko/20100101 Firefox/25.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:28.0) Gecko/20100101 Firefox/28.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:32.0) Gecko/20100101 Firefox/32.0",
|
||||
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0",
|
||||
"Mozilla/5.0 (X11; CentOS; Linux x86_64; rv:36.0) Gecko/20100101 Firefox/36.0",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:40.0) Gecko/20100101 Firefox/40.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:43.0) Gecko/20100101 Firefox/43.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:46.0) Gecko/20100101 Firefox/46.0",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:49.0) Gecko/20100101 Firefox/49.0",
|
||||
"Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0",
|
||||
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.0",
|
||||
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.0",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Galeon/2.0.6 (Ubuntu 2.0.6-2)",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.16) Gecko/20080716 (Gentoo) Galeon/2.0.6",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.13) Gecko/20100916 Iceape/2.0.8",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0 Iceweasel/19.0.2",
|
||||
"Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.2.1",
|
||||
"Mozilla/5.0 (compatible; Konqueror/4.2; Linux) KHTML/4.2.4 (like Gecko) Slackware/13.0",
|
||||
"Mozilla/5.0 (compatible; Konqueror/4.3; Linux) KHTML/4.3.1 (like Gecko) Fedora/4.3.1-3.fc11",
|
||||
"Mozilla/5.0 (compatible; Konqueror/4.4; Linux) KHTML/4.4.1 (like Gecko) Fedora/4.4.1-1.fc12",
|
||||
"Mozilla/5.0 (compatible; Konqueror/4.4; Linux 2.6.32-22-generic; X11; en_US) KHTML/4.4.3 (like Gecko) Kubuntu",
|
||||
"Mozilla/5.0 (compatible; Konqueror/4.4; Linux 2.6.32-22-generic; X11; en_US) KHTML/4.4.3 (like Gecko) Kubuntu",
|
||||
"Mozilla/5.0 (X11; Linux 3.8-6.dmz.1-liquorix-686) KHTML/4.8.4 (like Gecko) Konqueror/4.8",
|
||||
"Mozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.21 (KHTML, like Gecko) konqueror/4.14.10 Safari/537.21",
|
||||
"Midori/0.1.10 (X11; Linux i686; U; en-us) WebKit/(531).(2)",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.3) Gecko/2008092814 (Debian-3.0.1-1)",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9a3pre) Gecko/20070330",
|
||||
"Opera/9.80 (X11; Linux i686) Presto/2.12.388 Version/12.16",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.166 Safari/537.36 OPR/20.0.1396.73172",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.166 Safari/537.36 OPR/20.0.1396.73172",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36 OPR/32.0.1948.25",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.101 Safari/537.36 OPR/40.0.2308.62",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.114 Safari/537.36 Puffin/4.8.0.2965AT",
|
||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/538.1 (KHTML, like Gecko) QupZilla/1.8.6 Safari/538.1",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) QupZilla/1.9.0 Safari/538.1",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:10.0.1) Gecko/20100101 Firefox/10.0.1 SeaMonkey/2.7.1",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:12.0) Gecko/20120502 Firefox/12.0 SeaMonkey/2.9.1",
|
||||
"Mozilla/5.0 (Windows NT 5.1; rv:38.0) Gecko/20100101 Firefox/38.0 SeaMonkey/2.35",
|
||||
"Mozilla/5.0 (X11; Linux i686; rv:49.0) Gecko/20100101 Firefox/49.0 SeaMonkey/2.46",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; us; rv:1.9.1.19) Gecko/20110430 shadowfox/7.0 (like Firefox/7.0",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; it; rv:1.9.2.3) Gecko/20100406 Firefox/3.6.3 (Swiftfox)",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36 Vivaldi/1.0.344.37",
|
||||
"Mozilla/5.0 (X11; U; FreeBSD i386; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.207.0 Safari/532.0",
|
||||
"Mozilla/5.0 (X11; U; OpenBSD i386; en-US) AppleWebKit/533.3 (KHTML, like Gecko) Chrome/5.0.359.0 Safari/533.3",
|
||||
"Mozilla/5.0 (X11; U; FreeBSD x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16",
|
||||
"Mozilla/5.0 (X11; U; SunOS sun4m; en-US; rv:1.4b) Gecko/20030517 Mozilla Firebird/0.6",
|
||||
"Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.9.1b3) Gecko/20090429 Firefox/3.1b3",
|
||||
"Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.9.1) Gecko/20090702 Firefox/3.5",
|
||||
"Mozilla/5.0 (X11; U; FreeBSD i386; de-CH; rv:1.9.2.8) Gecko/20100729 Firefox/3.6.8",
|
||||
"Mozilla/5.0 (X11; FreeBSD amd64; rv:5.0) Gecko/20100101 Firefox/5.0",
|
||||
"Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.6) Gecko/20040406 Galeon/1.3.15",
|
||||
"Mozilla/5.0 (compatible; Konqueror/3.5; NetBSD 4.0_RC3; X11) KHTML/3.5.7 (like Gecko)",
|
||||
"Mozilla/5.0 (compatible; Konqueror/3.5; SunOS) KHTML/3.5.1 (like Gecko)",
|
||||
"Mozilla/5.0 (X11; U; FreeBSD; i386; en-US; rv:1.7) Gecko",
|
||||
"Mozilla/4.77 [en] (X11; I; IRIX;64 6.5 IP30)",
|
||||
"Mozilla/4.8 [en] (X11; U; SunOS; 5.7 sun4u)",
|
||||
"Mozilla/5.0 (Unknown; U; UNIX BSD/SYSV system; C -) AppleWebKit/527 (KHTML, like Gecko, Safari/419.3) Arora/0.10.2",
|
||||
"Mozilla/5.0 (X11; FreeBSD amd64) AppleWebKit/536.5 (KHTML like Gecko) Chrome/19.0.1084.56 Safari/536.5",
|
||||
"Mozilla/5.0 (X11; FreeBSD amd64) AppleWebKit/537.4 (KHTML like Gecko) Chrome/22.0.1229.79 Safari/537.4",
|
||||
"Mozilla/5.0 (X11; NetBSD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; OpenBSD i386) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; NetBSD x86; en-us) AppleWebKit/666.6+ (KHTML, like Gecko) Chromium/20.0.0000.00 Chrome/20.0.0000.00 Safari/666.6+",
|
||||
"Mozilla/5.0 (X11; FreeBSD amd64) AppleWebKit/535.22+ (KHTML, like Gecko) Chromium/17.0.963.56 Chrome/17.0.963.56 Safari/535.22+ Epiphany/2.30.6",
|
||||
"Mozilla/5.0 (X11; U; OpenBSD arm; en-us) AppleWebKit/531.2 (KHTML, like Gecko) Safari/531.2 Epiphany/2.30.0",
|
||||
"Mozilla/5.0 (X11; NetBSD amd64; rv:16.0) Gecko/20121102 Firefox/16.0",
|
||||
"Mozilla/5.0 (X11; OpenBSD amd64; rv:28.0) Gecko/20100101 Firefox/28.0",
|
||||
"Mozilla/5.0 (X11; NetBSD amd64; rv:30.0) Gecko/20100101 Firefox/30.0",
|
||||
"Mozilla/5.0 (X11; OpenBSD amd64; rv:30.0) Gecko/20100101 Firefox/30.0",
|
||||
"Mozilla/5.0 (X11; FreeBSD amd64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36",
|
||||
"Mozilla/5.0 (X11; FreeBSD amd64; rv:54.0) Gecko/20100101 Firefox/54.0",
|
||||
"Mozilla/5.0 (compatible; Konqueror/4.1; DragonFly) KHTML/4.1.4 (like Gecko)",
|
||||
"Mozilla/5.0 (compatible; Konqueror/4.1; OpenBSD) KHTML/4.1.4 (like Gecko)",
|
||||
"Mozilla/5.0 (compatible; Konqueror/4.5; NetBSD 5.0.2; X11; amd64; en_US) KHTML/4.5.4 (like Gecko)",
|
||||
"Mozilla/5.0 (compatible; Konqueror/4.5; FreeBSD) KHTML/4.5.4 (like Gecko)",
|
||||
"Mozilla/5.0 (X11; U; NetBSD amd64; en-US; rv:1.9.2.15) Gecko/20110308 Namoroka/3.6.15",
|
||||
"NetSurf/1.2 (NetBSD; amd64)",
|
||||
"Opera/9.80 (X11; FreeBSD 8.1-RELEASE i386; Edition Next) Presto/2.12.388 Version/12.10",
|
||||
"Mozilla/5.0 (Unknown; UNIX BSD/SYSV system) AppleWebKit/538.1 (KHTML, like Gecko) QupZilla/1.7.0 Safari/538.1",
|
||||
"Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.8.1.12) Gecko/20080303 SeaMonkey/1.1.8",
|
||||
"Mozilla/5.0 (X11; FreeBSD i386; rv:28.0) Gecko/20100101 Firefox/28.0 SeaMonkey/2.25",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; BOLT/2.800) AppleWebKit/534.6 (KHTML, like Gecko) Version/5.0 Safari/534.6.3",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.2; SAMSUNG-SM-T537A Build/KOT49H) AppleWebKit/537.36 (KHTML like Gecko) Chrome/35.0.1916.141 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; Pixel XL Build/OPR6.170623.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.107 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; DEVICE INFO) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Mobile Safari/537.36 Edge/12.0",
|
||||
"Mozilla/5.0 (Android; Mobile; rv:35.0) Gecko/35.0 Firefox/35.0",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 6.12; Microsoft ZuneHD 4.3)",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11)",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0) Asus;Galaxy6",
|
||||
"Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0)",
|
||||
"Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0)",
|
||||
"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch)",
|
||||
"Mozilla/1.22 (compatible; MSIE 5.01; PalmOS 3.0) EudoraWeb 2.1",
|
||||
"Mozilla/5.0 (WindowsCE 6.0; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (X11; U; Linux armv61; en-US; rv:1.9.1b2pre) Gecko/20081015 Fennec/1.0a1",
|
||||
"Mozilla/5.0 (Maemo; Linux armv7l; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 Fennec/2.0.1",
|
||||
"Mozilla/5.0 (Maemo; Linux armv7l; rv:10.0.1) Gecko/20100101 Firefox/10.0.1 Fennec/10.0.1",
|
||||
"Mozilla/5.0 (Android 6.0.1; Mobile; rv:48.0) Gecko/48.0 Firefox/48.0",
|
||||
"Mozilla/5.0 (Windows; U; Windows CE 5.1; rv:1.8.1a3) Gecko/20060610 Minimo/0.016",
|
||||
"Mozilla/5.0 (X11; U; Linux armv6l; rv 1.8.1.5pre) Gecko/20070619 Minimo/0.020",
|
||||
"Mozilla/5.0 (X11; U; Linux arm7tdmi; rv:1.8.1.11) Gecko/20071130 Minimo/0.025",
|
||||
"Mozilla/4.0 (PDA; PalmOS/sony/model prmr/Revision:1.1.54 (en)) NetFront/3.0",
|
||||
"Opera/9.51 Beta (Microsoft Windows; PPC; Opera Mobi/1718; U; en)",
|
||||
"Opera/9.60 (J2ME/MIDP; Opera Mini/4.1.11320/608; U; en) Presto/2.2.0",
|
||||
"Opera/9.60 (J2ME/MIDP; Opera Mini/4.2.14320/554; U; cs) Presto/2.2.0",
|
||||
"Opera/9.80 (S60; SymbOS; Opera Mobi/499; U; ru) Presto/2.4.18 Version/10.00",
|
||||
"Opera/10.61 (J2ME/MIDP; Opera Mini/5.1.21219/19.999; en-US; rv:1.9.3a5) WebKit/534.5 Presto/2.6.30",
|
||||
"Opera/9.80 (Android; Opera Mini/7.5.33361/31.1543; U; en) Presto/2.8.119 Version/11.1010",
|
||||
"Opera/9.80 (J2ME/MIDP; Opera Mini/8.0.35626/37.8918; U; en) Presto/2.12.423 Version/12.16",
|
||||
"Mozilla/5.0 (Linux; Android 5.1.1; Nexus 7 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.78 Safari/537.36 OPR/30.0.1856.93524",
|
||||
"Opera/9.80 (Android; Opera Mini/9.0.1829/66.318; U; en) Presto/2.12.423 Version/12.16",
|
||||
"Opera/9.80 (Linux i686; Opera Mobi/1040; U; en) Presto/2.5.24 Version/10.00",
|
||||
"POLARIS/6.01 (BREW 3.1.5; U; en-us; LG; LX265; POLARIS/6.01/WAP) MMP/2.0 profile/MIDP-2.1 Configuration/CLDC-1.1",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-gb) AppleWebKit/534.35 (KHTML, like Gecko) Chrome/11.0.696.65 Safari/534.35 Puffin/2.9174AP",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit/534.35 (KHTML, like Gecko) Chrome/11.0.696.65 Safari/534.35 Puffin/2.9174AT",
|
||||
"Mozilla/5.0 (iPod; U; CPU iPhone OS 6_1 like Mac OS X; en-HK) AppleWebKit/534.35 (KHTML, like Gecko) Chrome/11.0.696.65 Safari/534.35 Puffin/3.9174IP Mobile",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-AU) AppleWebKit/534.35 (KHTML, like Gecko) Chrome/11.0.696.65 Safari/534.35 Puffin/3.9174IT",
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-gb) AppleWebKit/534.35 (KHTML, like Gecko) Chrome/11.0.696.65 Safari/534.35 Puffin/2.0.5603M",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.114 Safari/537.36 Puffin/4.5.0IT",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.0; en-us; Droid Build/ESD20) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||
"Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
|
||||
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; da-dk) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
|
||||
"Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25",
|
||||
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; XBLWP7; ZuneWP7) UCBrowser/2.9.0.263",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.3.3; en-us ; LS670 Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1/UCBrowser/8.6.1.262/145/355",
|
||||
"Mozilla/5.0 (Linux; U; Android 3.0.1; fr-fr; A500 Build/HRI66) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.1; en-us; sdk Build/MR1) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.1 Safari/534.30",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.2; en-us; sdk Build/MR1) AppleWebKit/535.19 (KHTML, like Gecko) Version/4.2 Safari/535.19",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit/534.35 (KHTML, like Gecko) Chrome/11.0.696.65 Safari/534.35 Puffin/2.9174AT",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-AU) AppleWebKit/534.35 (KHTML, like Gecko) Chrome/11.0.696.65 Safari/534.35 Puffin/3.9174IT",
|
||||
"Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10",
|
||||
"Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
|
||||
"Mozilla/5.0 (iPad; U; CPU OS 4_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8F190 Safari/6533.18.5",
|
||||
"Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3",
|
||||
"Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25",
|
||||
"Mozilla/5.0 (iPad; CPU OS 8_0_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML like Gecko) Mobile/12A405 Version/7.0 Safari/9537.53",
|
||||
"Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4",
|
||||
"Mozilla/5.0 (iPad; CPU OS 9_3_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13F69 Safari/601.1",
|
||||
"Mozilla/5.0 (iPad; CPU OS 10_0 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/49.0.2623.109 Mobile/14A5335b Safari/601.1.46",
|
||||
"Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A5362a Safari/604.1",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.114 Safari/537.36 Puffin/4.5.0IT",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7;en-us) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Safari/530.17",
|
||||
"Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.2; U; de-DE) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/234.40.1 Safari/534.6 TouchPad/1.0",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; KFTT Build/IML74K) AppleWebKit/535.19 (KHTML, like Gecko) Silk/2.1 Mobile Safari/535.19 Silk-Accelerated=true",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.2; LG-V410 Build/KOT49I.V41010d) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.103 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
|
||||
"Mozilla/5.0 (Linux; Android 4.0.4; BNTV400 Build/IMM76L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.111 Safari/537.36",
|
||||
"Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.5; de-de; Galaxy Build/CUPCAKE) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.2; en-ca; GT-P1000M Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; SCH-I800 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; GT-P5210 Build/KOT49H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
|
||||
"Mozilla/5.0 (Linux; U; Android 3.0.1; en-us; GT-P7100 Build/HRI83) AppleWebkit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
|
||||
"Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-T530NU Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/3.2 Chrome/38.0.2125.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; U; Android 3.0.1; fr-fr; A500 Build/HRI66) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
|
||||
"Mozilla/4.0 (compatible; Linux 2.6.22) NetFront/3.4 Kindle/2.0 (screen 600x800)",
|
||||
"Mozilla/5.0 (Linux U; en-US) AppleWebKit/528.5 (KHTML, like Gecko, Safari/528.5 ) Version/4.0 Kindle/3.0 (screen 600x800; rotate)",
|
||||
"Mozilla/5.0 (X11; U; Linux armv7l like Android; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/533.2+ Kindle/3.0+",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; KFTT Build/IML74K) AppleWebKit/535.19 (KHTML, like Gecko) Silk/2.1 Mobile Safari/535.19 Silk-Accelerated=true",
|
||||
"Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10",
|
||||
"Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
|
||||
"Mozilla/5.0 (iPad; U; CPU OS 4_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8F190 Safari/6533.18.5",
|
||||
"Mozilla/5.0 (iPad; U; CPU iPad OS 5_0_1 like Mac OS X; en-us) AppleWebKit/535.1+ (KHTML like Gecko) Version/7.2.0.0 Safari/6533.18.5",
|
||||
"Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25",
|
||||
"Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) CriOS/30.0.1599.12 Mobile/11A465 Safari/8536.25 (3B92C18B-D9DE-4CB7-A02A-22FD2AF17C8F)",
|
||||
"Mozilla/5.0 (iPad; CPU OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53",
|
||||
"Mozilla/5.0 (iPad; CPU OS 8_0_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML like Gecko) Mobile/12A405 Version/7.0 Safari/9537.53",
|
||||
"Mozilla/5.0 (iPad; CPU OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12H321 Safari/600.1.4",
|
||||
"Mozilla/5.0 (iPad; CPU OS 9_3_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13F69 Safari/601.1",
|
||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.114 Safari/537.36 Puffin/4.5.0IT",
|
||||
"Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420 (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3",
|
||||
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_0 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5A347 Safari/525.200",
|
||||
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16",
|
||||
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/531.22.7",
|
||||
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; da-dk) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
|
||||
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; da-dk) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A403 Safari/8536.25",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12F70 Safari/600.1.4",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) GSA/8.0.57838 Mobile/12H321 Safari/600.1.4",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 9_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13C75 Safari/601.1",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/14A346 Safari/602.1",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) GSA/18.0.130791545 Mobile/14A5345a Safari/600.1.4",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A5362a Safari/604.1",
|
||||
"Mozilla/5.0 (iPod; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11a Safari/525.20",
|
||||
"Mozilla/5.0 (iPod; U; CPU iPhone OS 3_1_1 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Mobile/7C145",
|
||||
"Mozilla/5.0 (iPod touch; CPU iPhone OS 7_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML like Gecko) Version/7.0 Mobile/11D167 Safari/123E71C",
|
||||
"Mozilla/5.0 (iPod; CPU iPhone OS 8_4 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) CriOS/44.0.2403.67 Mobile/12H143 Safari/600.1.4",
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7;en-us) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Safari/530.17",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; BNTV250 Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Safari/533.1",
|
||||
"Mozilla/5.0 (Linux; Android 4.0.4; BNTV400 Build/IMM76L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.111 Safari/537.36",
|
||||
"BlackBerry7100i/4.1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/103",
|
||||
"BlackBerry8300/4.2.2 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/107 UP.Link/6.2.3.15.0",
|
||||
"BlackBerry8320/4.2.2 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/100",
|
||||
"BlackBerry8330/4.3.0 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/105",
|
||||
"BlackBerry9000/4.6.0.167 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/102",
|
||||
"BlackBerry9530/4.7.0.167 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/102 UP.Link/6.3.1.20.0",
|
||||
"BlackBerry9700/5.0.0.351 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/123",
|
||||
"Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1 (KHTML, Like Gecko) Version/6.0.0.141 Mobile Safari/534.1",
|
||||
"Mozilla/5.0 (BlackBerry; U; BlackBerry 9930; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.1.0.267 Mobile Safari/534.11+",
|
||||
"Mozilla/5.0 (Linux; Android 7.1.1; BBB100-1 Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+",
|
||||
"Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.1.0.2342 Mobile Safari/537.10+",
|
||||
"Mozilla/5.0 (Linux; Android 5.1.1; Coolpad 3622A Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.1.1; Coolpad 3632A Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.5; en-us; sdk Build/CUPCAKE) AppleWebkit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5X Build/MDB08L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.1.2; Nexus 6P Build/N2G48C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.107 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.4; Nexus 7 Build/KTU84P) AppleWebKit/537.36 (KHTML like Gecko) Chrome/36.0.1985.135 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.1.1; Nexus 7 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.78 Safari/537.36 OPR/30.0.1856.93524",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; Nexus 9 Build/NRD90R) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.1.2; Pixel Build/NHG47N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; Pixel XL Build/OPR6.170623.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.107 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.2; U; de-DE) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/234.40.1 Safari/534.6 TouchPad/1.0",
|
||||
"Mozilla/5.0 (Linux; webOS/2.2.4; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) webOSBrowser/221.56 Safari/534.6 Pre/3.0",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11) Sprint:PPC6800",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11) XV6800",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.5; en-us; htc_bahamas Build/CRB17) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.1-update1; de-de; HTC Desire 1.19.161.5 Build/ERE27) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||
"HTC_Dream Mozilla/5.0 (Linux; U; Android 1.5; en-ca; Build/CUPCAKE) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; Sprint APA9292KT Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.5; de-ch; HTC Hero Build/CUPCAKE) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; ADR6300 Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.1; en-us; HTC Legend Build/cupcake) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.5; de-de; HTC Magic Build/PLAT-RC33) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1 FirePHP/0.3",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; HTC One M9 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.0.3; de-ch; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
|
||||
"HTC-ST7377/1.59.502.3 (67150) Opera/9.50 (Windows NT 5.1; U; en) UP.Link/6.3.1.17.0",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.6; en-us; HTC_TATTOO_A3288 Build/DRC79) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; ALE-L21 Build/HuaweiALE-L21) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.89 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.1; C6740N Build/LMY47O) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.111 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.1.2; en-us; LG-P870/P87020d Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
|
||||
"LG-LX550 AU-MIC-LX550/2.0 MMP/2.0 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; LG-D850 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.97 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; LG-H918 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; LGL84VL Build/NRD90U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; LGUS997 Build/NRD90U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.2; LGMS323 Build/KOT49I.MS32310b) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.103 Mobile Safari/537.36",
|
||||
"POLARIS/6.01(BREW 3.1.5;U;en-us;LG;LX265;POLARIS/6.01/WAP;)MMP/2.0 profile/MIDP-201 Configuration /CLDC-1.1",
|
||||
"LG-GC900/V10a Obigo/WAP2.0 Profile/MIDP-2.1 Configuration/CLDC-1.1",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.2; LG-V410 Build/KOT49I.V41010d) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.103 Safari/537.36",
|
||||
"Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; MDA Pro/1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1)",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.5; en-us; T-Mobile G1 Build/CRB43) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari 525.20.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.5; en-gb; T-Mobile_G2_Touch Build/CUPCAKE) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.0; en-us; Droid Build/ESD20) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; Droid Build/FRG22D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"MOT-L7v/08.B7.5DR MIB/2.2.1 Profile/MIDP-2.0 Configuration/CLDC-1.1 UP.Link/6.3.0.0.0",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.0; en-us; Milestone Build/ SHOLS_U2_01.03.1) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.0.1; de-de; Milestone Build/SHOLS_U2_01.14.0) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; Moto G (5) Plus Build/NPNS25.137-35-5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.107 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.1.1; XT1710-02 Build/NDS26.74-36) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36",
|
||||
"MOT-V9mm/00.62 UP.Browser/6.2.3.4.c.1.123 (GUI) MMP/2.0",
|
||||
"MOTORIZR-Z8/46.00.00 Mozilla/4.0 (compatible; MSIE 6.0; Symbian OS; 356) Opera 8.65 [it] UP.Link/6.3.0.0.0",
|
||||
"MOT-V177/0.1.75 UP.Browser/6.2.3.9.c.12 (GUI) MMP/2.0 UP.Link/6.3.1.13.0",
|
||||
"Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.4; XT1032 Build/KXB21.14-L1.61) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.94 Mobile Safari/537.36",
|
||||
"portalmmm/2.0 N410i(c20;TB)",
|
||||
"Nokia3230/2.0 (5.0614.0) SymbianOS/7.0s Series60/2.1 Profile/MIDP-2.0 Configuration/CLDC-1.0",
|
||||
"Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 Nokia5700/3.27; Profile/MIDP-2.0 Configuration/CLDC-1.1) AppleWebKit/413 (KHTML, like Gecko) Safari/413",
|
||||
"Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 Nokia6120c/3.70; Profile/MIDP-2.0 Configuration/CLDC-1.1) AppleWebKit/413 (KHTML, like Gecko) Safari/413",
|
||||
"Nokia6230/2.0 (04.44) Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"Nokia6230i/2.0 (03.80) Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"Mozilla/4.1 (compatible; MSIE 5.0; Symbian OS; Nokia 6600;452) Opera 6.20 [en-US]",
|
||||
"Nokia6630/1.0 (2.39.15) SymbianOS/8.0 Series60/2.6 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"Nokia7250/1.0 (3.14) Profile/MIDP-1.0 Configuration/CLDC-1.0",
|
||||
"Mozilla/4.0 (compatible; MSIE 5.0; Series80/2.0 Nokia9500/4.51 Profile/MIDP-2.0 Configuration/CLDC-1.1)",
|
||||
"Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaC6-01/011.010; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/525 (KHTML, like Gecko) Version/3.0 BrowserNG/7.2.7.2 3gpp-gba",
|
||||
"Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaC7-00/012.003; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/525 (KHTML, like Gecko) Version/3.0 BrowserNG/7.2.7.3 3gpp-gba",
|
||||
"Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413 (KHTML, like Gecko) Safari/413 es50",
|
||||
"Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaE6-00/021.002; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.1.16 Mobile Safari/533.4 3gpp-gba",
|
||||
"UCWEB/8.8 (SymbianOS/9.2; U; en-US; NokiaE63) AppleWebKit/534.1 UCBrowser/8.8.0.245 Mobile",
|
||||
"Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413 (KHTML, like Gecko) Safari/413 es65",
|
||||
"Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaE7-00/010.016; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/525 (KHTML, like Gecko) Version/3.0 BrowserNG/7.2.7.3 3gpp-gba",
|
||||
"Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413 (KHTML, like Gecko) Safari/413 es70",
|
||||
"Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 NokiaE90-1/07.24.0.3; Profile/MIDP-2.0 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413 UP.Link/6.2.3.18.0",
|
||||
"Mozilla/5.0 (Windows Phone 8.1; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 530) like Gecko",
|
||||
"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 920)",
|
||||
"Mozilla/5.0 (Windows Phone 8.1; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 630) like Gecko",
|
||||
"Mozilla/5.0 (Windows NT 6.2; ARM; Trident/7.0; Touch; rv:11.0; WPDesktop; NOKIA; Lumia 635) like Gecko",
|
||||
"Mozilla/5.0 (Windows NT 6.2; ARM; Trident/7.0; Touch; rv:11.0; WPDesktop; NOKIA; Lumia 920) like Geckoo",
|
||||
"Mozilla/5.0 (Windows Phone 8.1; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 920) like Gecko",
|
||||
"NokiaN70-1/5.0609.2.0.1 Series60/2.8 Profile/MIDP-2.0 Configuration/CLDC-1.1 UP.Link/6.3.1.13.0",
|
||||
"Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413 (KHTML, like Gecko) Safari/413",
|
||||
"NokiaN73-1/3.0649.0.0.1 Series60/3.0 Profile/MIDP2.0 Configuration/CLDC-1.1",
|
||||
"Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaN8-00/014.002; Profile/MIDP-2.1 Configuration/CLDC-1.1; en-us) AppleWebKit/525 (KHTML, like Gecko) Version/3.0 BrowserNG/7.2.6.4 3gpp-gba",
|
||||
"Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413 (KHTML, like Gecko) Safari/413",
|
||||
"Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13",
|
||||
"Mozilla/5.0 (SymbianOS/9.1; U; de) AppleWebKit/413 (KHTML, like Gecko) Safari/413",
|
||||
"Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 NokiaN95/10.0.018; Profile/MIDP-2.0 Configuration/CLDC-1.1) AppleWebKit/413 (KHTML, like Gecko) Safari/413 UP.Link/6.3.0.0.0",
|
||||
"Mozilla/5.0 (MeeGo; NokiaN950-00/00) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13",
|
||||
"Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/10.0.012; Profile/MIDP-2.1 Configuration/CLDC-1.1; en-us) AppleWebKit/525 (KHTML, like Gecko) WicKed/7.1.12344",
|
||||
"Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaX7-00/021.004; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.1.21 Mobile Safari/533.4 3gpp-gba",
|
||||
"Mozilla/5.0 (webOS/1.3; U; en-US) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/1.0 Safari/525.27.1 Desktop/1.0",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; PalmSource/hspr-H102; Blazer/4.0) 16;320x320",
|
||||
"SEC-SGHE900/1.0 NetFront/3.2 Profile/MIDP-2.0 Configuration/CLDC-1.1 Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1378; nl; U; ssr)",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.5; de-de; Galaxy Build/CUPCAKE) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.2; en-ca; GT-P1000M Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; SCH-I800 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.0.3; de-de; Galaxy S II Build/GRJ22) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
|
||||
"Mozilla/5.0 (Linux; Android 4.3; SPH-L710 Build/JSS15J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.0.1; SCH-R970 Build/LRX22C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.84 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.2; SAMSUNG-SM-G900A Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.94 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; GT-P5210 Build/KOT49H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
|
||||
"Mozilla/5.0 (Linux; U; Android 3.0.1; en-us; GT-P7100 Build/HRI83) AppleWebkit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
|
||||
"SAMSUNG-S8000/S8000XXIF3 SHP/VPP/R5 Jasmine/1.0 Nextreaming SMM-MMS/1.2.0 profile/MIDP-2.1 configuration/CLDC-1.1 FirePHP/0.3",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.5; en-us; SPH-M900 Build/CUPCAKE) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"SAMSUNG-SGH-A867/A867UCHJ3 SHP/VPP/R5 NetFront/35 SMM-MMS/1.2.0 profile/MIDP-2.0 configuration/CLDC-1.1 UP.Link/6.3.0.0.0",
|
||||
"SEC-SGHX210/1.0 UP.Link/6.3.1.13.0",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-G900H Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; SAMSUNG SM-G925R6 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/5.4 Chrome/51.0.2704.106 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.2; SAMSUNG-SM-T537A Build/KOT49H) AppleWebKit/537.36 (KHTML like Gecko) Chrome/35.0.1916.141 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.5; fr-fr; GT-I5700 Build/CUPCAKE) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"SEC-SGHX820/1.0 NetFront/3.2 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"SonyEricssonK310iv/R4DA Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1 UP.Link/6.3.1.13.0",
|
||||
"SonyEricssonK550i/R1JD Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"SonyEricssonK610i/R1CB Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"SonyEricssonK750i/R1CA Browser/SEMC-Browser/4.2 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"Opera/9.80 (J2ME/MIDP; Opera Mini/5.0.16823/1428; U; en) Presto/2.2.0",
|
||||
"SonyEricssonK800i/R1CB Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1 UP.Link/6.3.0.0.0",
|
||||
"SonyEricssonK810i/R1KG Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"Opera/8.01 (J2ME/MIDP; Opera Mini/1.0.1479/HiFi; SonyEricsson P900; no; U; ssr)",
|
||||
"SonyEricssonS500i/R6BC Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0 SonyEricssonP100/01; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) Version/3.0 Safari/525",
|
||||
"SonyEricssonT68/R201A",
|
||||
"SonyEricssonT100/R101",
|
||||
"SonyEricssonT610/R201 Profile/MIDP-1.0 Configuration/CLDC-1.0",
|
||||
"SonyEricssonT650i/R7AA Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"SonyEricssonW580i/R6BC Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"SonyEricssonW660i/R6AD Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"SonyEricssonW810i/R4EA Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1 UP.Link/6.3.0.0.0",
|
||||
"SonyEricssonW850i/R1ED Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"SonyEricssonW950i/R100 Mozilla/4.0 (compatible; MSIE 6.0; Symbian OS; 323) Opera 8.60 [en-US]",
|
||||
"SonyEricssonW995/R1EA Profile/MIDP-2.1 Configuration/CLDC-1.1 UNTRUSTED/1.0",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.6; es-es; SonyEricssonX10i Build/R1FA016) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.6; en-us; SonyEricssonX10i Build/R1AA056) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"Opera/9.5 (Microsoft Windows; PPC; Opera Mobi; U) SonyEricssonX1i/R2AA Profile/MIDP-2.0 Configuration/CLDC-1.1",
|
||||
"SonyEricssonZ800/R1Y Browser/SEMC-Browser/4.1 Profile/MIDP-2.0 Configuration/CLDC-1.1 UP.Link/6.3.0.0.0",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 6.12; Microsoft ZuneHD 4.3)",
|
||||
"Opera/9.80 (Android; Opera Mini/7.5.33361/31.1543; U; en) Presto/2.8.119 Version/11.1010",
|
||||
"Mozilla/5.0 (Android; Mobile; rv:35.0) Gecko/35.0 Firefox/35.0",
|
||||
"Mozilla/5.0 (Android 6.0.1; Mobile; rv:48.0) Gecko/48.0 Firefox/48.0",
|
||||
"Mozilla/5.0 (Linux; U; Android 0.5; en-us) AppleWebKit/522 (KHTML, like Gecko) Safari/419.3",
|
||||
"Mozilla/5.0 (Linux; U; Android 1.1; en-gb; dream) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
|
||||
"HTC_Dream Mozilla/5.0 (Linux; U; Android 1.5; en-ca; Build/CUPCAKE) AppleWebKit/528.5 (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.0; en-us; Droid Build/ESD20) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; Sprint APA9292KT Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.2; en-us; ADR6300 Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.2; en-ca; GT-P1000M Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"Mozilla/5.0 (Android; Linux armv7l; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 Fennec/2.0.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 3.0.1; fr-fr; A500 Build/HRI66) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
|
||||
"Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.0.3; de-ch; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.0.3; de-de; Galaxy S II Build/GRJ22) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
|
||||
"Opera/9.80 (Android 4.0.4; Linux; Opera Mobi/ADR-1205181138; U; pl) Presto/2.10.254 Version/12.00",
|
||||
"Mozilla/5.0 (Android; Linux armv7l; rv:10.0.1) Gecko/20100101 Firefox/10.0.1 Fennec/10.0.1",
|
||||
"Mozilla/5.0 (Linux; Android 4.1.2; SHV-E250S Build/JZO54K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.82 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Android 4.2; rv:19.0) Gecko/20121129 Firefox/19.0",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.3; en-us; sdk Build/MR1) AppleWebKit/536.23 (KHTML, like Gecko) Version/4.3 Mobile Safari/536.23",
|
||||
"Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.2; SAMSUNG-SM-T537A Build/KOT49H) AppleWebKit/537.36 (KHTML like Gecko) Chrome/35.0.1916.141 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 4.4.2; SM-T230NU Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.81 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.0.1; SCH-R970 Build/LRX22C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.84 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-T530NU Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/3.2 Chrome/38.0.2125.102 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 5.1.1; Nexus 7 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.78 Safari/537.36 OPR/30.0.1856.93524",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; HTC One M9 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; LG-D850 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.97 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5X Build/MDB08L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-G900H Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; Nexus 9 Build/NRD90R) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 7.0; LG-H918 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; Pixel XL Build/OPR6.170623.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.107 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (Linux; Android 8.0.0; Pixel XL Build/OPR6.170623.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.107 Mobile Safari/537.36",
|
||||
"Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420 (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3",
|
||||
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_0 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5A347 Safari/525.200",
|
||||
"Mozilla/5.0 (iPod; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11a Safari/525.20",
|
||||
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16",
|
||||
"Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10",
|
||||
"Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; ja-jp) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
|
||||
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; da-dk) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
|
||||
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3 like Mac OS X; de-de) AppleWebKit/533.17.9 (KHTML, like Gecko) Mobile/8F190",
|
||||
"MobileSafari/600.1.4 CFNetwork/711.1.12 Darwin/14.0.0",
|
||||
"Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; da-dk) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 9_2 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13C75 Safari/601.1",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A5362a Safari/604.1",
|
||||
"Mozilla/5.0 (X11; Linux i686 on x86_64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 Fennec/2.0.1",
|
||||
"Mozilla/5.0 (Maemo; Linux armv7l; rv:2.0.1) Gecko/20100101 Firefox/4.0.1 Fennec/2.0.1",
|
||||
"Mozilla/5.0 (webOS/1.3; U; en-US) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/1.0 Safari/525.27.1 Desktop/1.0",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; PalmSource/hspr-H102; Blazer/4.0) 16;320x320",
|
||||
"Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaN8-00/014.002; Profile/MIDP-2.1 Configuration/CLDC-1.1; en-us) AppleWebKit/525 (KHTML, like Gecko) Version/3.0 BrowserNG/7.2.6.4 3gpp-gba",
|
||||
"Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaX7-00/021.004; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.1.21 Mobile Safari/533.4 3gpp-gba",
|
||||
"Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 NokiaE90-1/07.24.0.3; Profile/MIDP-2.0 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413 UP.Link/6.2.3.18.0",
|
||||
"Mozilla/5.0 (SymbianOS 9.4; Series60/5.0 NokiaN97-1/10.0.012; Profile/MIDP-2.1 Configuration/CLDC-1.1; en-us) AppleWebKit/525 (KHTML, like Gecko) WicKed/7.1.12344",
|
||||
"Opera/9.80 (S60; SymbOS; Opera Mobi/499; U; ru) Presto/2.4.18 Version/10.00",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 6.12; Microsoft ZuneHD 4.3)",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11)",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11) Sprint:PPC6800",
|
||||
"Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 8.12; MSIEMobile6.0)",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0) Asus;Galaxy6",
|
||||
"Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0)",
|
||||
"Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0)",
|
||||
"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch)",
|
||||
"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 920)",
|
||||
"Mozilla/5.0 (Windows Phone 8.1; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 530) like Gecko",
|
||||
"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 920)",
|
||||
"Mozilla/5.0 (Windows Phone 8.1; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 630) like Gecko",
|
||||
"Mozilla/5.0 (Windows NT 6.2; ARM; Trident/7.0; Touch; rv:11.0; WPDesktop; NOKIA; Lumia 635) like Gecko",
|
||||
"Mozilla/5.0 (Windows NT 6.2; ARM; Trident/7.0; Touch; rv:11.0; WPDesktop; NOKIA; Lumia 920) like Geckoo",
|
||||
"Mozilla/5.0 (Windows Phone 8.1; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 920) like Gecko",
|
||||
"Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 929) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537",
|
||||
"Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; DEVICE INFO) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Mobile Safari/537.36 Edge/12.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; ARM; Lumia 950 Dual SIM) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393",
|
||||
|
||||
]
|
||||
|
||||
@@ -5,13 +5,13 @@ import os
|
||||
|
||||
from guessit import guessit
|
||||
from subliminal import Episode
|
||||
from subliminal_patch.core import REMOVE_CRAP_FROM_FILENAME
|
||||
from subliminal_patch.core import remove_crap_from_fn
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def update_video(video, fn):
|
||||
guess_from = REMOVE_CRAP_FROM_FILENAME.sub("", fn)
|
||||
guess_from = remove_crap_from_fn(fn)
|
||||
|
||||
logger.debug(u"Got original filename: %s", guess_from)
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@ import logging
|
||||
import types
|
||||
import os
|
||||
import datetime
|
||||
import requests
|
||||
|
||||
from guessit import guessit
|
||||
from requests.compat import urljoin, quote
|
||||
from subliminal import Episode, Movie, region
|
||||
from subliminal_patch.core import REMOVE_CRAP_FROM_FILENAME
|
||||
from subliminal_patch.core import remove_crap_from_fn
|
||||
from subliminal_patch.http import CertifiSession
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -18,17 +18,24 @@ class DroneAPIClient(object):
|
||||
api_url = None
|
||||
_fill_attrs = None
|
||||
|
||||
def __init__(self, version=1, session=None, headers=None, timeout=10, base_url=None, api_key=None):
|
||||
def __init__(self, version=1, session=None, headers=None, timeout=10, base_url=None, api_key=None,
|
||||
ssl_no_verify=False):
|
||||
headers = dict(headers or {}, **{"X-Api-Key": api_key})
|
||||
|
||||
#: Session for the requests
|
||||
self.session = session or requests.Session()
|
||||
self.session = session or CertifiSession()
|
||||
if ssl_no_verify:
|
||||
self.session.verify = False
|
||||
|
||||
self.session.timeout = timeout
|
||||
self.session.headers.update(headers or {})
|
||||
|
||||
if not base_url.endswith("/"):
|
||||
base_url += "/"
|
||||
|
||||
if not base_url.startswith("http"):
|
||||
base_url = "http://%s" % base_url
|
||||
|
||||
if not base_url.endswith("api/"):
|
||||
self.api_url = urljoin(base_url, "api/")
|
||||
|
||||
@@ -171,7 +178,7 @@ class SonarrClient(DroneAPIClient):
|
||||
:return:
|
||||
"""
|
||||
ext = os.path.splitext(video.name)[1]
|
||||
guess_from = REMOVE_CRAP_FROM_FILENAME.sub("", scene_name + ext)
|
||||
guess_from = remove_crap_from_fn(scene_name + ext)
|
||||
|
||||
# guess
|
||||
hints = {
|
||||
@@ -260,7 +267,7 @@ class RadarrClient(DroneAPIClient):
|
||||
:return:
|
||||
"""
|
||||
ext = os.path.splitext(video.name)[1]
|
||||
guess_from = REMOVE_CRAP_FROM_FILENAME.sub("", scene_name + ext)
|
||||
guess_from = remove_crap_from_fn(scene_name + ext)
|
||||
|
||||
# guess
|
||||
hints = {
|
||||
@@ -308,4 +315,4 @@ def refine(video, **kwargs):
|
||||
client.update_video(video, os.path.splitext(additional_data["original_filepath"])[0])
|
||||
|
||||
if "release_group" in additional_data and not video.release_group:
|
||||
video.release_group = REMOVE_CRAP_FROM_FILENAME.sub("", additional_data["release_group"])
|
||||
video.release_group = remove_crap_from_fn(additional_data["release_group"])
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# coding=utf-8
|
||||
|
||||
import logging
|
||||
from libfilebot import get_filebot_attrs
|
||||
from common import update_video
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def refine(video, **kwargs):
|
||||
"""
|
||||
@@ -11,7 +14,12 @@ def refine(video, **kwargs):
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
orig_fn = get_filebot_attrs(video.name)
|
||||
try:
|
||||
orig_fn = get_filebot_attrs(video.name)
|
||||
|
||||
if orig_fn:
|
||||
update_video(video, orig_fn)
|
||||
if orig_fn:
|
||||
update_video(video, orig_fn)
|
||||
else:
|
||||
logger.info(u"%s: Filebot didn't return an original filename", video.name)
|
||||
except:
|
||||
logger.exception(u"%s: Something went wrong when retrieving filebot attributes:", video.name)
|
||||
|
||||
@@ -53,7 +53,7 @@ def refine(video, **kwargs):
|
||||
"""
|
||||
# only deal with Episode videos
|
||||
if not isinstance(video, Episode):
|
||||
logger.error('Cannot refine episodes')
|
||||
logger.error('Can only refine episodes')
|
||||
return
|
||||
|
||||
# exit if the information is complete
|
||||
|
||||
@@ -8,10 +8,9 @@ from subliminal.score import get_scores
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
FPS_EQUALITY = {
|
||||
23.976: 23.98
|
||||
}
|
||||
FPS_EQUALITY.update({value: key for key, value in FPS_EQUALITY.iteritems()})
|
||||
FPS_EQUALITY = (
|
||||
(23.976, 23.98, 24.0),
|
||||
)
|
||||
|
||||
|
||||
def framerate_equal(source, check):
|
||||
@@ -23,8 +22,9 @@ def framerate_equal(source, check):
|
||||
if source == check:
|
||||
return True
|
||||
|
||||
if check in FPS_EQUALITY and FPS_EQUALITY[check] == source:
|
||||
return True
|
||||
for equal_fps_tuple in FPS_EQUALITY:
|
||||
if check in equal_fps_tuple and source in equal_fps_tuple:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@@ -95,7 +95,7 @@ def compute_score(matches, subtitle, video, hearing_impaired=None):
|
||||
matches |= {'series', 'year', 'season', 'episode'}
|
||||
if 'tvdb_id' in matches:
|
||||
logger.debug('Adding tvdb_id match equivalents')
|
||||
matches |= {'series', 'year', 'season', 'episode'}
|
||||
matches |= {'series', 'year', 'season', 'episode', 'title'}
|
||||
if 'series_tvdb_id' in matches:
|
||||
logger.debug('Adding series_tvdb_id match equivalents')
|
||||
matches |= {'series', 'year'}
|
||||
|
||||
@@ -82,12 +82,11 @@ class Subtitle(Subtitle_):
|
||||
|
||||
def set_encoding(self, encoding):
|
||||
ge = self.guess_encoding()
|
||||
logger.debug("Encoding change requested: to %s, from %s", encoding, ge)
|
||||
if encoding == ge:
|
||||
logger.debug("Encoding already is %s", encoding)
|
||||
return
|
||||
|
||||
unicontent = self.text
|
||||
logger.debug("Changing encoding: to %s, from %s", encoding, ge)
|
||||
self.content = unicontent.encode(encoding)
|
||||
self._guessed_encoding = encoding
|
||||
|
||||
@@ -109,7 +108,6 @@ class Subtitle(Subtitle_):
|
||||
|
||||
"""
|
||||
if self._guessed_encoding:
|
||||
logger.debug('Encoding already guessed: %s', self._guessed_encoding)
|
||||
return self._guessed_encoding
|
||||
|
||||
logger.info('Guessing encoding for language %s', self.language)
|
||||
@@ -311,7 +309,7 @@ class Subtitle(Subtitle_):
|
||||
:return: string
|
||||
"""
|
||||
if not self.mods:
|
||||
return fix_text(self.content.decode("utf-8"), **ftfy_defaults)
|
||||
return fix_text(self.content.decode("utf-8"), **ftfy_defaults).encode(encoding="utf-8")
|
||||
|
||||
submods = SubtitleModifications(debug=debug)
|
||||
if submods.load(content=self.text, language=self.language):
|
||||
@@ -359,8 +357,14 @@ def guess_matches(video, guess, partial=False):
|
||||
if video.season and 'season' in guess and guess['season'] == video.season:
|
||||
matches.add('season')
|
||||
# episode
|
||||
if video.episode and 'episode' in guess and guess['episode'] == video.episode:
|
||||
matches.add('episode')
|
||||
# Currently we only have single-ep support (guessit returns a multi-ep as a list with int values)
|
||||
# Most providers only support single-ep, so make sure it contains only 1 episode
|
||||
# In case of multi-ep, take the lowest episode (subtitles will normally be available on lowest episode number)
|
||||
if video.episode and 'episode' in guess:
|
||||
episode_guess = guess['episode']
|
||||
episode = min(episode_guess) if episode_guess and isinstance(episode_guess, list) else episode_guess
|
||||
if episode == video.episode:
|
||||
matches.add('episode')
|
||||
# year
|
||||
if video.year and 'year' in guess and guess['year'] == video.year:
|
||||
matches.add('year')
|
||||
|
||||
@@ -20,7 +20,7 @@ debug = "--debug" in sys.argv
|
||||
if debug:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
sub = Subtitle(Language.fromietf("eng"), mods=["common", "remove_HI", "remove_tags", "OCR_fixes"])
|
||||
sub = Subtitle(Language.fromietf("eng"), mods=["common", "remove_HI", "OCR_fixes"])
|
||||
sub.content = open(fn).read()
|
||||
sub.normalize()
|
||||
content = sub.get_modified_content(debug=True)
|
||||
|
||||
@@ -45,4 +45,7 @@ class SZFileBackend(CacheBackend):
|
||||
|
||||
def clear(self):
|
||||
self._cache.clear()
|
||||
if not hasattr(self._cache, "_buffer") or self._cache._sync:
|
||||
self._cache._sync = False
|
||||
self._cache._buffer = {}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
OS_PLEX_USERAGENT = 'plexapp.com v9.0'
|
||||
|
||||
DEPENDENCY_MODULE_NAMES = ['subliminal', 'subliminal_patch', 'enzyme', 'guessit', 'subzero']
|
||||
DEPENDENCY_MODULE_NAMES = ['subliminal', 'subliminal_patch', 'enzyme', 'guessit', 'subzero', 'libfilebot']
|
||||
PERSONAL_MEDIA_IDENTIFIER = "com.plexapp.agents.none"
|
||||
PLUGIN_IDENTIFIER_SHORT = "subzero"
|
||||
PLUGIN_IDENTIFIER = "com.plexapp.agents.%s" % PLUGIN_IDENTIFIER_SHORT
|
||||
|
||||
@@ -1,17 +1,40 @@
|
||||
# coding=utf-8
|
||||
from babelfish.exceptions import LanguageError
|
||||
|
||||
from babelfish import Language as Language_
|
||||
|
||||
|
||||
repl_map = {
|
||||
"dk": "da",
|
||||
"nld": "nl",
|
||||
"english": "en",
|
||||
}
|
||||
|
||||
|
||||
def language_from_stream(l):
|
||||
if not l:
|
||||
raise LanguageError()
|
||||
for method in ("fromietf", "fromalpha3t", "fromalpha3b"):
|
||||
try:
|
||||
return getattr(Language, method)(l)
|
||||
except (LanguageError, ValueError):
|
||||
pass
|
||||
raise LanguageError()
|
||||
|
||||
|
||||
class Language(Language_):
|
||||
@classmethod
|
||||
def fromietf(cls, ietf):
|
||||
if ietf in repl_map:
|
||||
ietf = repl_map[ietf]
|
||||
ietf_lower = ietf.lower()
|
||||
if ietf_lower in repl_map:
|
||||
ietf = repl_map[ietf_lower]
|
||||
|
||||
return Language_.fromietf(ietf)
|
||||
|
||||
@classmethod
|
||||
def fromalpha3b(cls, s):
|
||||
if s in repl_map:
|
||||
s = repl_map[s]
|
||||
return Language_.fromietf(s)
|
||||
|
||||
return Language_.fromalpha3b(s)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
import dict, geezip, httpfake, io, json, rar, which
|
||||
@@ -123,6 +123,9 @@ class Dicked(object):
|
||||
return self._entries <= d
|
||||
|
||||
def __eq__(self, d):
|
||||
if d is None and not self._entries:
|
||||
return True
|
||||
|
||||
return self._entries == d
|
||||
|
||||
def __ne__(self, d):
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# coding=utf-8
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from scandir import scandir as _scandir
|
||||
|
||||
# thanks @ plex trakt scrobbler: https://github.com/trakt/Plex-Trakt-Scrobbler/blob/master/Trakttv.bundle/Contents/Libraries/Shared/plugin/core/io.py
|
||||
|
||||
|
||||
@@ -34,3 +38,62 @@ def get_viable_encoding():
|
||||
encoding = sys.getfilesystemencoding()
|
||||
return "utf-8" if not encoding or encoding.lower() not in VALID_ENCODINGS else encoding
|
||||
|
||||
|
||||
class ScandirListdirEntryStub(object):
|
||||
"""
|
||||
A class which mimics the entries returned by scandir, for fallback purposes when using listdir instead.
|
||||
"""
|
||||
__slots__ = ('name', '_d_type', '_stat', '_lstat', '_scandir_path', '_path', '_inode')
|
||||
|
||||
def __init__(self, scandir_path, name, d_type, inode):
|
||||
self._scandir_path = scandir_path
|
||||
self.name = name
|
||||
self._d_type = d_type
|
||||
self._inode = inode
|
||||
self._stat = None
|
||||
self._lstat = None
|
||||
self._path = None
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
if self._path is None:
|
||||
self._path = os.path.join(self._scandir_path, self.name)
|
||||
return self._path
|
||||
|
||||
def stat(self, follow_symlinks=True):
|
||||
path = self.path
|
||||
if follow_symlinks and self.is_symlink():
|
||||
path = os.path.realpath(path)
|
||||
|
||||
return os.stat(path)
|
||||
|
||||
def is_dir(self, follow_symlinks=True):
|
||||
path = self.path
|
||||
if follow_symlinks and self.is_symlink():
|
||||
path = os.path.realpath(path)
|
||||
|
||||
return os.path.isdir(path)
|
||||
|
||||
def is_file(self, follow_symlinks=True):
|
||||
path = self.path
|
||||
if follow_symlinks and self.is_symlink():
|
||||
path = os.path.realpath(path)
|
||||
|
||||
return os.path.isfile(path)
|
||||
|
||||
def is_symlink(self):
|
||||
return os.path.islink(self.path)
|
||||
|
||||
|
||||
def scandir_listdir_fallback(path):
|
||||
for fn in os.listdir(path):
|
||||
yield ScandirListdirEntryStub(path, fn, None, None)
|
||||
|
||||
|
||||
def scandir(path):
|
||||
try:
|
||||
return _scandir(path)
|
||||
|
||||
# fallback for systems where sys.getfilesystemencoding() returns the "wrong" value
|
||||
except UnicodeDecodeError:
|
||||
return scandir_listdir_fallback(path)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# coding=utf-8
|
||||
|
||||
import logging
|
||||
import rarfile
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RarFile(rarfile.RarFile):
|
||||
def read(self, fname, psw=None):
|
||||
"""
|
||||
read specific content of rarfile without parsing
|
||||
:param fname:
|
||||
:param psw:
|
||||
:return:
|
||||
"""
|
||||
cmd = [rarfile.UNRAR_TOOL] + list(rarfile.ORIG_OPEN_ARGS)
|
||||
|
||||
with rarfile.XTempFile(self._rarfile) as rf:
|
||||
log.debug(u"RAR CMD: %s", cmd + [rf, fname])
|
||||
p = rarfile.custom_popen(cmd + [rf, fname])
|
||||
output = p.communicate()[0]
|
||||
rarfile.check_returncode(p, output)
|
||||
|
||||
return output
|
||||
File diff suppressed because one or more lines are too long
@@ -34,9 +34,12 @@ SZ_FIX_DATA = {
|
||||
u"lljust": u"ll just",
|
||||
u" L ": u" I ",
|
||||
u" l ": u" I ",
|
||||
u"'sjust": u"'s just",
|
||||
u"'tjust": u"'t just",
|
||||
},
|
||||
"WholeWords": {
|
||||
u"I'11": u"I'll",
|
||||
u"III'll": u"I'll",
|
||||
u"Tun": u"Run",
|
||||
u"pan'": u"part",
|
||||
u"al'": u"at",
|
||||
@@ -53,6 +56,9 @@ SZ_FIX_DATA = {
|
||||
u" 're ": u"'re ",
|
||||
u"LAst": u"Last",
|
||||
u"forthis": u"for this",
|
||||
u"Ls": u"Is",
|
||||
u"Iam": u"I am",
|
||||
u"Ican": u"I can",
|
||||
},
|
||||
"PartialLines": {
|
||||
u"L know": u"I know",
|
||||
@@ -80,6 +86,7 @@ SZ_FIX_DATA = {
|
||||
u"L haven't": u"I haven't",
|
||||
u"L couldn't": u"I couldn't",
|
||||
u"L won't": u"I won't",
|
||||
u"H i": u"Hi",
|
||||
},
|
||||
"BeginLines": {
|
||||
u"l ": u"I ",
|
||||
|
||||
@@ -88,6 +88,13 @@ class SubtitleModifications(object):
|
||||
if identifier in final_mods and mod_cls.exclusive:
|
||||
final_mods.pop(identifier)
|
||||
|
||||
# language-specific mod, check validity
|
||||
if mod_cls.languages and self.language not in mod_cls.languages:
|
||||
if self.debug:
|
||||
logger.debug("Skipping %s, because %r is not a valid language for this mod",
|
||||
identifier, self.language)
|
||||
continue
|
||||
|
||||
# merge args of duplicate mods if possible
|
||||
elif identifier in final_mods and mod_cls.args_mergeable:
|
||||
final_mods[identifier] = mod_cls.merge_args(final_mods[identifier], args)
|
||||
@@ -132,7 +139,8 @@ class SubtitleModifications(object):
|
||||
if self.debug:
|
||||
logger.debug("Line mods took %ss", time.time() - line_mods_start)
|
||||
|
||||
self.f.events = new_entries
|
||||
if new_entries:
|
||||
self.f.events = new_entries
|
||||
|
||||
# apply last file mods
|
||||
if non_line_mods:
|
||||
@@ -194,16 +202,17 @@ class SubtitleModifications(object):
|
||||
mod = self.initialized_mods[identifier]
|
||||
|
||||
try:
|
||||
line = mod.modify(line.strip(), entry=entry.text, debug=self.debug, parent=self, **args)
|
||||
line = mod.modify(line.strip(), entry=entry.text, debug=self.debug, parent=self, index=index,
|
||||
**args)
|
||||
except EmptyEntryError:
|
||||
if self.debug:
|
||||
logger.debug(u"%s: %r -> ''", identifier, entry.text)
|
||||
logger.debug(u"%d: %s: %r -> ''", index, identifier, entry.text)
|
||||
skip_entry = True
|
||||
break
|
||||
|
||||
if not line:
|
||||
if self.debug:
|
||||
logger.debug(u"%s: %r -> ''", identifier, old_line)
|
||||
logger.debug(u"%d: %s: %r -> ''", index, identifier, old_line)
|
||||
skip_line = True
|
||||
break
|
||||
|
||||
@@ -235,12 +244,12 @@ class SubtitleModifications(object):
|
||||
lines.append(cleaned_line)
|
||||
else:
|
||||
if self.debug:
|
||||
logger.debug(u"Ditching now empty line (%r)", line)
|
||||
logger.debug(u"%d: Ditching now empty line (%r)", index, line)
|
||||
|
||||
if not lines:
|
||||
# don't bother logging when the entry only had one line
|
||||
if self.debug and line_count > 1:
|
||||
logger.debug(u"%r -> ''", entry.text)
|
||||
logger.debug(u"%d: %r -> ''", index, entry.text)
|
||||
continue
|
||||
|
||||
new_text = ur"\N".join(lines)
|
||||
@@ -263,6 +272,8 @@ class SubtitleModifications(object):
|
||||
if self.debug:
|
||||
logger.debug(u"Fixing tags: %s (%r -> %r)", str(add_start_tags+add_end_tags), new_text,
|
||||
entry.text)
|
||||
else:
|
||||
entry.text = new_text
|
||||
else:
|
||||
entry.text = new_text
|
||||
|
||||
|
||||
@@ -20,11 +20,12 @@ class SubtitleModification(object):
|
||||
pre_processors = []
|
||||
processors = []
|
||||
post_processors = []
|
||||
languages = []
|
||||
|
||||
def __init__(self, parent):
|
||||
return
|
||||
|
||||
def _process(self, content, processors, debug=False, parent=None, **kwargs):
|
||||
def _process(self, content, processors, debug=False, parent=None, index=None, **kwargs):
|
||||
if not content:
|
||||
return
|
||||
|
||||
@@ -41,12 +42,13 @@ class SubtitleModification(object):
|
||||
new_content = processor.process(new_content, debug=debug, **kwargs)
|
||||
if not new_content:
|
||||
if debug:
|
||||
logger.debug("Processor returned empty line: %s", processor)
|
||||
logger.debug("Processor returned empty line: %s", processor.name)
|
||||
break
|
||||
if debug:
|
||||
if old_content == new_content:
|
||||
continue
|
||||
logger.debug("%s: %s -> %s", processor, repr(old_content), repr(new_content))
|
||||
logger.debug("%d: %s: %s -> %s", index, processor.name, repr(old_content), repr(new_content))
|
||||
|
||||
return new_content
|
||||
|
||||
def pre_process(self, content, debug=False, parent=None, **kwargs):
|
||||
@@ -84,6 +86,7 @@ class SubtitleTextModification(SubtitleModification):
|
||||
pass
|
||||
|
||||
|
||||
TAG = ur"(?:\s*{\\[iusb][0-1]}\s*)*"
|
||||
EMPTY_TAG_PROCESSOR = ReProcessor(re.compile(r'({\\\w1})[\s.,-_!?]*({\\\w0})'), "", name="empty_tag")
|
||||
|
||||
empty_line_post_processors = [
|
||||
|
||||
@@ -39,9 +39,7 @@ class Color(SubtitleModification):
|
||||
|
||||
colors = COLOR_MAP
|
||||
|
||||
long_description = """\
|
||||
Adds the requested color to every line of the subtitle. Support depends on player.
|
||||
"""
|
||||
long_description = "Adds the requested color to every line of the subtitle. Support depends on player."
|
||||
|
||||
def modify(self, content, debug=False, parent=None, **kwargs):
|
||||
color = self.colors.get(kwargs.get("name"))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import re
|
||||
|
||||
from subzero.language import Language
|
||||
from subzero.modification.mods import SubtitleTextModification, empty_line_post_processors, SubtitleModification
|
||||
from subzero.modification.processors.string_processor import StringProcessor
|
||||
from subzero.modification.processors.re_processor import NReProcessor
|
||||
@@ -14,13 +15,20 @@ class CommonFixes(SubtitleTextModification):
|
||||
exclusive = True
|
||||
order = 40
|
||||
|
||||
long_description = """\
|
||||
Fix common and whitespace/punctuation issues in subtitles
|
||||
"""
|
||||
long_description = "Fix common and whitespace/punctuation issues in subtitles"
|
||||
|
||||
processors = [
|
||||
# -- = ...
|
||||
StringProcessor("-- ", '... ', name="CM_doubledash"),
|
||||
# -- = em dash
|
||||
NReProcessor(re.compile(r'(?u)(\w|\b|\s|^)(-\s?-{1,2})'), ur"\1—", name="CM_multidash"),
|
||||
|
||||
# line = _/-/\s
|
||||
NReProcessor(re.compile(r'(?u)(^\W*[-_.]+\W*$)'), "", name="CM_non_word_only"),
|
||||
|
||||
# multi space
|
||||
NReProcessor(re.compile(r'(?u)(\s{2,})'), " ", name="CM_multi_space"),
|
||||
|
||||
# fix music symbols
|
||||
NReProcessor(re.compile(ur'(?u)(^[*#¶\s]*[*#¶]+[*#¶\s]*$)'), u"♪", name="CM_music_symbols"),
|
||||
|
||||
# '' = "
|
||||
StringProcessor("''", '"', name="CM_double_apostrophe"),
|
||||
@@ -40,6 +48,9 @@ class CommonFixes(SubtitleTextModification):
|
||||
# multiple spaces
|
||||
NReProcessor(re.compile(r'(?u)[\s]{2,}'), " ", name="CM_multiple_spaces"),
|
||||
|
||||
# more than 3 dots
|
||||
NReProcessor(re.compile(r'(?u)\.{3,}'), "...", name="CM_dots"),
|
||||
|
||||
# no space after starting dash
|
||||
NReProcessor(re.compile(r'(?u)^-(?![\s-])'), "- ", name="CM_dash_space"),
|
||||
|
||||
@@ -67,11 +78,11 @@ class CommonFixes(SubtitleTextModification):
|
||||
# countdowns otherwise); don't break up ellipses
|
||||
NReProcessor(
|
||||
re.compile(r'(?u)(\b[0-9]+[0-9:\']*(?<!\.\.)\s+(?!\.\.)[0-9,.:\'\s]*(?=[0-9]+)[0-9,.:\'])'),
|
||||
lambda match: match.group(1).replace(" ", ""),
|
||||
lambda match: match.group(1).replace(" ", "") if match.group(1).count(" ") == 1 else match.group(1),
|
||||
name="CM_spaces_in_numbers"),
|
||||
|
||||
# uppercase after dot
|
||||
NReProcessor(re.compile(ur'(?u)((?:[^.\s])+\.\s+)([a-zà-ž])'),
|
||||
NReProcessor(re.compile(ur'(?u)((?<!(?=\s*[A-ZÀ-Ž-_0-9.]\s*))(?:[^.\s])+\.\s+)([a-zà-ž])'),
|
||||
lambda match: ur'%s%s' % (match.group(1), match.group(2).upper()), name="CM_uppercase_after_dot"),
|
||||
|
||||
# remove double interpunction
|
||||
@@ -92,9 +103,7 @@ class RemoveTags(SubtitleModification):
|
||||
exclusive = True
|
||||
modifies_whole_file = True
|
||||
|
||||
long_description = """\
|
||||
Removes all possible style tags from the subtitle, such as font, bold, color etc.
|
||||
"""
|
||||
long_description = "Removes all possible style tags from the subtitle, such as font, bold, color etc."
|
||||
|
||||
def modify(self, content, debug=False, parent=None, **kwargs):
|
||||
for entry in parent.f:
|
||||
@@ -106,12 +115,21 @@ class ReverseRTL(SubtitleModification):
|
||||
identifier = "reverse_rtl"
|
||||
description = "Reverse punctuation in RTL languages"
|
||||
exclusive = True
|
||||
order = 50
|
||||
languages = [Language("heb")]
|
||||
|
||||
long_description = "Some playback devices don't properly handle right-to-left markers for punctuation. " \
|
||||
"Physically swap punctuation. Applicable to languages: hebrew"
|
||||
|
||||
processors = [
|
||||
NReProcessor(re.compile(ur"(?u)((?=(?<=\b|^)|(?<=\s))([.!?-]+)([^.!?-]+)(?=\b|$|\s))"), r"\3\2",
|
||||
# new? (?u)(^([\s.!?]*)(.+?)(\s*)(-?\s*)$); \5\4\3\2
|
||||
#NReProcessor(re.compile(ur"(?u)((?=(?<=\b|^)|(?<=\s))([.!?-]+)([^.!?-]+)(?=\b|$|\s))"), r"\3\2",
|
||||
# name="CM_RTL_reverse")
|
||||
NReProcessor(re.compile(ur"(?u)(^([\s.!?]*)(.+?)(\s*)(-?\s*)$)"), r"\5\4\3\2",
|
||||
name="CM_RTL_reverse")
|
||||
]
|
||||
|
||||
|
||||
registry.register(CommonFixes)
|
||||
registry.register(RemoveTags)
|
||||
registry.register(ReverseRTL)
|
||||
|
||||
@@ -15,9 +15,7 @@ class ChangeFPS(SubtitleModification):
|
||||
advanced = True
|
||||
modifies_whole_file = True
|
||||
|
||||
long_description = """\
|
||||
Re-syncs the subtitle to the framerate of the current media file.
|
||||
"""
|
||||
long_description = "Re-syncs the subtitle to the framerate of the current media file."
|
||||
|
||||
def modify(self, content, debug=False, parent=None, **kwargs):
|
||||
fps_from = kwargs.get("from")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# coding=utf-8
|
||||
import re
|
||||
|
||||
from subzero.modification.mods import SubtitleTextModification, empty_line_post_processors, EmptyEntryError
|
||||
from subzero.modification.mods import SubtitleTextModification, empty_line_post_processors, EmptyEntryError, TAG
|
||||
from subzero.modification.processors.re_processor import NReProcessor
|
||||
from subzero.modification import registry
|
||||
|
||||
@@ -22,22 +22,21 @@ class HearingImpaired(SubtitleTextModification):
|
||||
exclusive = True
|
||||
order = 20
|
||||
|
||||
long_description = """\
|
||||
Removes tags, text and characters from subtitles that are meant for hearing impaired people
|
||||
"""
|
||||
long_description = "Removes tags, text and characters from subtitles that are meant for hearing impaired people"
|
||||
|
||||
processors = [
|
||||
# full bracket entry, single or multiline; starting with brackets and ending with brackets
|
||||
FullBracketEntryProcessor(re.compile(ur'(?sux)^-?\s?[([].+(?=[^)\]]{3,}).+[)\]]$'), "",
|
||||
name="HI_brackets_full"),
|
||||
FullBracketEntryProcessor(re.compile(ur'(?sux)^-?%(t)s[([].+(?=[^)\]]{3,}).+[)\]]%(t)s$' % {"t": TAG}),
|
||||
"", name="HI_brackets_full"),
|
||||
|
||||
# brackets (only remove if at least 3 chars in brackets)
|
||||
NReProcessor(re.compile(ur'(?sux)-?\s*[([][^([)\]]+?(?=[A-zÀ-ž"\']{3,})[^([)\]]+[)\]][\s:]*'), "",
|
||||
name="HI_brackets"),
|
||||
NReProcessor(re.compile(ur'(?sux)-?%(t)s[([][^([)\]]+?(?=[A-zÀ-ž"\'.]{3,})[^([)\]]+[)\]][\s:]*%(t)s' %
|
||||
{"t": TAG}), "", name="HI_brackets"),
|
||||
|
||||
NReProcessor(re.compile(ur'(?sux)-?\s*[([]\s*(?=[A-zÀ-ž"\']{3,})[^([)\]]+$'), "", name="HI_bracket_open_start"),
|
||||
NReProcessor(re.compile(ur'(?sux)-?%(t)s[([]%(t)s(?=[A-zÀ-ž"\'.]{3,})[^([)\]]+%(t)s$' % {"t": TAG}),
|
||||
"", name="HI_bracket_open_start"),
|
||||
|
||||
NReProcessor(re.compile(ur'(?sux)-?\s*(?=[A-zÀ-ž"\']{3,})[^([)\]]+[)\]][\s:]*'), "",
|
||||
NReProcessor(re.compile(ur'(?sux)-?%(t)s(?=[A-zÀ-ž"\'.]{3,})[^([)\]]+[)\]][\s:]*%(t)s' % {"t": TAG}), "",
|
||||
name="HI_bracket_open_end"),
|
||||
|
||||
# text before colon (and possible dash in front), max 11 chars after the first whitespace (if any)
|
||||
@@ -47,10 +46,21 @@ class HearingImpaired(SubtitleTextModification):
|
||||
#NReProcessor(re.compile(ur'(?u)(\b|^)([\s-]*(?=[A-zÀ-ž-_0-9"\']{3,})[A-zÀ-ž-_0-9"\']+:\s*)'), "",
|
||||
# name="HI_before_colon"),
|
||||
|
||||
# text before colon (at least 3 chars); at start or after a sentence, possibly with a dash in front
|
||||
NReProcessor(re.compile(ur'(?u)(?:(?<=^)|(?<=[.\-!?"\']))'
|
||||
ur'([\s-]*(?=[A-zÀ-ž-_0-9\s"\']{3,})[A-zÀ-ž-_0-9\s"\']+:\s*)(?![0-9])'), "",
|
||||
name="HI_before_colon"),
|
||||
# uppercase text before colon (at least 3 uppercase chars); at start or after a sentence,
|
||||
# possibly with a dash in front; ignore anything ending with a quote
|
||||
NReProcessor(re.compile(ur'(?u)(?:(?<=^)|(?<=[.\-!?\"\']))([\s-]*(?=[A-ZÀ-Ž&+]\s*[A-ZÀ-Ž&+]\s*[A-ZÀ-Ž&+])'
|
||||
ur'[A-ZÀ-Ž-_0-9\s\"\'&+]+:(?![\"\'’ʼ❜‘‛”“‟„])\s*)(?![0-9])'), "",
|
||||
name="HI_before_colon_caps"),
|
||||
|
||||
# any text before colon (at least 3 chars); at start or after a sentence,
|
||||
# possibly with a dash in front; try not breaking actual sentences with a colon at the end by not matching if
|
||||
# a space is inside the text; ignore anything ending with a quote
|
||||
NReProcessor(re.compile(ur'(?u)(?:(?<=^)|(?<=[.\-!?\"]))([\s-]*((?=[A-zÀ-ž&+]\s*[A-zÀ-ž&+]\s*[A-zÀ-ž&+])'
|
||||
ur'[A-zÀ-ž-_0-9\s\"\'&+]+:)(?![\"’ʼ❜‘‛”“‟„])\s*)(?![0-9])'),
|
||||
lambda match:
|
||||
match.group(1) if (match.group(2).count(" ") > 0 or match.group(1).count("-") > 0)
|
||||
else "" if not match.group(1).startswith(" ") else " ",
|
||||
name="HI_before_colon_noncaps"),
|
||||
|
||||
# text in brackets at start, after optional dash, before colon or at end of line
|
||||
# fixme: may be too aggressive
|
||||
@@ -58,7 +68,7 @@ class HearingImpaired(SubtitleTextModification):
|
||||
# name="HI_brackets_special"),
|
||||
|
||||
# all caps line (at least 4 consecutive uppercase chars)
|
||||
NReProcessor(re.compile(ur'(?u)(^(?=.*[A-ZÀ-Ž]{4,})[A-ZÀ-Ž-_\s]+$)'), "", name="HI_all_caps"),
|
||||
NReProcessor(re.compile(ur'(?u)(^(?=.*[A-ZÀ-Ž&+]{4,})[A-ZÀ-Ž-_\s&+]+$)'), "", name="HI_all_caps"),
|
||||
|
||||
# dash in front
|
||||
# NReProcessor(re.compile(r'(?u)^\s*-\s*'), "", name="HI_starting_dash"),
|
||||
@@ -66,6 +76,10 @@ class HearingImpaired(SubtitleTextModification):
|
||||
# all caps at start before new sentence
|
||||
NReProcessor(re.compile(ur'(?u)^(?=[A-ZÀ-Ž]{4,})[A-ZÀ-Ž-_\s]+\s([A-ZÀ-Ž][a-zà-ž].+)'), r"\1",
|
||||
name="HI_starting_upper_then_sentence"),
|
||||
|
||||
# remove music symbols
|
||||
NReProcessor(re.compile(ur'(?u)(^%(t)s[*#¶♫♪\s]*%(t)s[*#¶♫♪\s]+%(t)s[*#¶♫♪\s]*%(t)s$)' % {"t": TAG}),
|
||||
"", name="HI_music_symbols_only"),
|
||||
]
|
||||
|
||||
post_processors = empty_line_post_processors
|
||||
|
||||
@@ -19,9 +19,7 @@ class FixOCR(SubtitleTextModification):
|
||||
order = 10
|
||||
data_dict = None
|
||||
|
||||
long_description = """\
|
||||
Fix issues that happen when a subtitle gets converted from bitmap to text through OCR
|
||||
"""
|
||||
long_description = "Fix issues that happen when a subtitle gets converted from bitmap to text through OCR"
|
||||
|
||||
def __init__(self, parent):
|
||||
super(FixOCR, self).__init__(parent)
|
||||
@@ -39,8 +37,10 @@ class FixOCR(SubtitleTextModification):
|
||||
|
||||
return [
|
||||
# remove broken HI tag colons (ANNOUNCER'., ". instead of :) after at least 3 uppercase chars
|
||||
NReProcessor(re.compile(ur'(?u)(^.*(?<=[A-ZÀ-Ž]{3})[A-ZÀ-Ž-_\s0-9"\']+["\'’ʼ❜‘‛”“‟„][.,‚،⹁、]\s*)'), "",
|
||||
name="OCR_fix_HI_colons"),
|
||||
# don't modify stuff inside quotes
|
||||
NReProcessor(re.compile(ur'(?u)(^[^"\'’ʼ❜‘‛”“‟„]*(?<=[A-ZÀ-Ž]{3})[A-ZÀ-Ž-_\s0-9]+)'
|
||||
ur'(["\'’ʼ❜‘‛”“‟„]*[.,‚،⹁、;]+)(\s*)(?!["\'’ʼ❜‘‛”“‟„])'),
|
||||
r"\1:\3", name="OCR_fix_HI_colons"),
|
||||
# fix F'bla
|
||||
NReProcessor(re.compile(ur'(?u)(\bF)(\')([A-zÀ-ž]*\b)'), r"\1\3", name="OCR_fix_F"),
|
||||
WholeLineProcessor(self.data_dict["WholeLines"], name="OCR_replace_line"),
|
||||
|
||||
@@ -16,9 +16,7 @@ class ShiftOffset(SubtitleModification):
|
||||
args_mergeable = True
|
||||
modifies_whole_file = True
|
||||
|
||||
long_description = """\
|
||||
Adds or substracts a certain amount of time from the whole subtitle to match your media
|
||||
"""
|
||||
long_description = "Adds or substracts a certain amount of time from the whole subtitle to match your media"
|
||||
|
||||
@classmethod
|
||||
def merge_args(cls, args1, args2):
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
# coding=utf-8
|
||||
|
||||
# restore builtins
|
||||
import sys
|
||||
|
||||
|
||||
def restore_builtins(module, base):
|
||||
def fix_environment_stuff(module, base):
|
||||
# restore builtins
|
||||
module.__builtins__ = [x for x in base.__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__
|
||||
|
||||
# patch getfilesystemencoding for NVIDIA Shield
|
||||
getfilesystemencoding_orig = sys.getfilesystemencoding
|
||||
|
||||
def getfilesystemencoding():
|
||||
return getfilesystemencoding_orig() or "utf-8"
|
||||
|
||||
sys.getfilesystemencoding = getfilesystemencoding
|
||||
|
||||
@@ -212,7 +212,7 @@ class JSONStoredVideoSubtitles(object):
|
||||
return out
|
||||
|
||||
for key, subtitle in all_subs.iteritems():
|
||||
if key == "current":
|
||||
if key in ("current", "blacklist"):
|
||||
continue
|
||||
|
||||
if subtitle.provider_name == provider_name:
|
||||
@@ -226,7 +226,7 @@ class JSONStoredVideoSubtitles(object):
|
||||
return 0
|
||||
|
||||
subs = part.get(str(lang))
|
||||
return len(filter(lambda key: key != "current", subs.keys()))
|
||||
return len(filter(lambda key: key not in ("current", "blacklist"), subs.keys()))
|
||||
|
||||
def get_sub_key(self, provider_name, id):
|
||||
return provider_name, str(id)
|
||||
|
||||
@@ -4,7 +4,7 @@ import logging
|
||||
import os
|
||||
|
||||
from babelfish.exceptions import LanguageError
|
||||
from subzero.language import Language
|
||||
from subzero.language import Language, language_from_stream
|
||||
from subliminal_patch import scan_video, refine, search_external_subtitles
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -44,7 +44,8 @@ def set_existing_languages(video, video_info, external_subtitles=False, embedded
|
||||
# mp4 and stuff, check burned in
|
||||
for language in known_embedded:
|
||||
try:
|
||||
embedded_subtitle_languages.add(Language.fromalpha3b(language))
|
||||
embedded_subtitle_languages.add(language_from_stream(language))
|
||||
|
||||
except LanguageError:
|
||||
logger.error('Embedded subtitle track language %r is not a valid language', language)
|
||||
embedded_subtitle_languages.add(Language('und'))
|
||||
@@ -114,6 +115,7 @@ def refine_video(video, no_refining=False, refiner_settings=None):
|
||||
video.year = year
|
||||
|
||||
refine(video, **refine_kwargs)
|
||||
logger.info(u"Using filename: %s", video.original_name)
|
||||
|
||||
if hints["type"] == "movie" and not video.imdb_id:
|
||||
if plex_title:
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
1
|
||||
00:00:01,250 --> 00:00:04,419
|
||||
(WHOOSHING)
|
||||
This is a phone number: 123 4325 123
|
||||
H i. But not MATCH hindrance.
|
||||
Ja, det är väl det. Tack och lov
|
||||
att HD:s beslut var så solklart.
|
||||
But this should. Peter: Yello!
|
||||
|
||||
2
|
||||
00:00:10,759 --> 00:00:12,678
|
||||
ROSE: (Help us. Please. . .help us.)
|
||||
What's "wrong"? over 9, 000!
|
||||
I can't keep running. L can't!
|
||||
|
||||
3
|
||||
00:00:12,679 --> 00:00:16,097
|
||||
<b>I don't know. Some kind of wrong "1 00" number
|
||||
of signal, drawing the Tardis off course.</b>
|
||||
<b>I don't know. Some kind of wrong "1 00" number---
|
||||
of signal, drawing the Tardis off.... course.</b>
|
||||
|
||||
4
|
||||
00:00:16,099 --> 00:00:17,224
|
||||
@@ -34,28 +40,39 @@ But fix this: 81 ,00
|
||||
00:00:21,103 --> 00:00:23,603
|
||||
<i>(laughing): lrn gonna And when are we? (chuckles)
|
||||
lrn gonna And when are we?</i>
|
||||
"I luv butts".
|
||||
|
||||
8
|
||||
00:00:24,274 --> 00:00:26,649
|
||||
...2012. weII it's 1 2:00 o'clock
|
||||
like "MAMASBMW,"
|
||||
"LEXUS4LIZ," or "BOOBGUY"?
|
||||
|
||||
9
|
||||
00:00:26,650 --> 00:00:29,370
|
||||
(BIG BROTHER
|
||||
oh god multiline! THEME MUSIC)
|
||||
takeaway from today, it's this:
|
||||
|
||||
10
|
||||
00:00:30,612 --> 00:00:33,112
|
||||
(SWITCH CLICKING)
|
||||
<i> ( intercom buzzes )</i>
|
||||
- woman:<i> Ken's here</i>
|
||||
<i> for the meeting.</i>
|
||||
|
||||
11
|
||||
00:00:33,658 --> 00:00:34,783
|
||||
(WHOO
|
||||
SHING) >>geil
|
||||
A.K.A. being nice
|
||||
[J.J.] alte. nice. being nice
|
||||
|
||||
12
|
||||
00:00:34,783 --> 00:00:36,826
|
||||
-- Blimey.
|
||||
remove this hi you no, please: is that ok?
|
||||
here also no hi removal:
|
||||
butremove this:
|
||||
|
||||
13
|
||||
00:00:36,828 --> 00:00:39,328
|
||||
@@ -83,7 +100,7 @@ pepipi</i>
|
||||
|
||||
18
|
||||
00:00:51,926 --> 00:00:55,304
|
||||
That's the milometer
|
||||
That's the milometer well
|
||||
from the Roswell spaceship.
|
||||
|
||||
19
|
||||
@@ -104,15 +121,19 @@ Ah, look at you!
|
||||
|
||||
22
|
||||
00:01:13,489 --> 00:01:15,073
|
||||
What is it?
|
||||
- _
|
||||
_
|
||||
*
|
||||
♫♫
|
||||
|
||||
|
||||
23
|
||||
00:01:15,075 --> 00:01:18,076
|
||||
An old friend of mine. Well, enemy.
|
||||
-- www.Addic7ed.com --
|
||||
|
||||
24
|
||||
00:01:19,037 --> 00:01:22,367
|
||||
The stuff of nightmares,
|
||||
-- The stuff of nightmares,
|
||||
reduced to an exhibit.
|
||||
|
||||
25
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 177 KiB After Width: | Height: | Size: 49 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 177 KiB After Width: | Height: | Size: 50 KiB |
@@ -0,0 +1,400 @@
|
||||
{
|
||||
"sq": "Albansk",
|
||||
"ar": "Arabisk",
|
||||
"be": "Hviderussisk",
|
||||
"bs": "Bosnisk",
|
||||
"bg": "Bulgarisk",
|
||||
"ca": "Catalansk",
|
||||
"zh": "Kinesisk",
|
||||
"hr": "Kroatisk",
|
||||
"cs": "Tjekkisk",
|
||||
"da": "Dansk",
|
||||
"nl": "Hollansk",
|
||||
"en": "Engelsk",
|
||||
"et": "Estisk",
|
||||
"fa": "Persisk",
|
||||
"fi": "Finsk",
|
||||
"fr": "Fransk",
|
||||
"de": "Tysk",
|
||||
"el": "Græsk",
|
||||
"he": "Hebrarisk",
|
||||
"hi": "Hindi",
|
||||
"hu": "Ungarsk",
|
||||
"is": "Islansk",
|
||||
"id": "Indonesisk",
|
||||
"it": "Italiensk",
|
||||
"ja": "Japansk",
|
||||
"ko": "Koreansk",
|
||||
"lv": "Lettisk",
|
||||
"lt": "Litauisk",
|
||||
"mk": "Makedonsk",
|
||||
"ms": "Malajsisk",
|
||||
"no": "Norsk",
|
||||
"pl": "Polsk",
|
||||
"pt": "Portugisisk",
|
||||
"pt-br": "Portugisisk (Brasiliansk)",
|
||||
"ro": "Romansk",
|
||||
"ru": "Russisk",
|
||||
"sr": "Serbisk",
|
||||
"sr-cyrl": "Serbisk (Cyrillic)",
|
||||
"sr-latn": "Serbisk (Latin)",
|
||||
"sk": "Slovakisk",
|
||||
"sl": "Slovensk",
|
||||
"es": "Spansk",
|
||||
"sv": "Svensk",
|
||||
"th": "Thaisk",
|
||||
"tr": "Tyrkisk",
|
||||
"uk": "Ukrainsk",
|
||||
"vi": "Vietnamesisk",
|
||||
"Internal stuff, pay attention!": "Interne ting, vær opmærksom!",
|
||||
"Advanced": "Avanceret",
|
||||
"advanced": "avanceret",
|
||||
"Enter PIN": "Indtast Pin",
|
||||
"The owner has restricted the access to this menu. Please enter the correct pin": "Ejeren har begrænset adgangen til denne menu. Venligst indtast den rigtige PIN kode",
|
||||
"Restart the plugin": "Genstart Plugin",
|
||||
"Get my logs (copy the appearing link and open it in your browser, please)": "Hent mine logs (kopier det viste link og åben det i din browser)",
|
||||
"Copy the appearing link and open it in your browser, please": "Kopier det viste link og åben det i din browser",
|
||||
"Trigger find better subtitles": "Trigger find bedre undertekster",
|
||||
"Skip next find better subtitles (sets last run to now)": "Spring næste Find bedre undertekster over (sæt sidste kørsel til nu)",
|
||||
"Trigger subtitle storage maintenance": "Start undertekst lager vedligehold",
|
||||
"Trigger subtitle storage migration (expensive)": "Start migration af undertekst lagre (dyrt?)",
|
||||
"Trigger cache maintenance (refiners, providers and packs/archives)": "Start cache vedligehold (for refiners, udbydere og pakker/arkiver)",
|
||||
"Apply configured default subtitle mods to all (active) stored subtitles": "Brug de standard konfigurede undertekst-mods til alle (aktive) gemte undertekster",
|
||||
"Re-Apply mods of all stored subtitles": "Genindsæt alle mods for alle gemte undertekster",
|
||||
"Log the plugin's scheduled tasks state storage": "Log plugins planlagte aktivitets lagerstatus",
|
||||
"Log the plugin's internal ignorelist storage": "Log plugins interne ignoreliste lager",
|
||||
"Log the plugin's complete state storage": "Log plugins komplette lager status",
|
||||
"Reset the plugin's scheduled tasks state storage": "Nulstil plugins planlagte aktivitets lagerstatus",
|
||||
"Reset the plugin's internal ignorelist storage": "Nulstil plugins internet ignoreliste lager",
|
||||
"Invalidate Sub-Zero metadata caches (subliminal)": "Afkræft Sub-Zero metadata cache (subliminale)",
|
||||
"Reset provider throttle states": "Nulstil udbyders throttle status",
|
||||
"Restarting the plugin": "Genstarter plugin",
|
||||
"Restart triggered, please wait about 5 seconds": "Genstart aktiveret, vent venligst ca. 5 sekunder",
|
||||
"Reset subtitle storage": "Nulstil undertekst lager",
|
||||
"Are you sure?": "Er du sikker?",
|
||||
"Are you really sure?": "Er du helt sikker?",
|
||||
"Success": "Succes",
|
||||
"FindBetterSubtitles triggered": "FindBedreUndertekster startet",
|
||||
"FindBetterSubtitles skipped": "FindBedreUndertekster sprunget over",
|
||||
"SubtitleStorageMaintenance triggered": "Undertekst lager vedligehold startet",
|
||||
"MigrateSubtitleStorage triggered": "Migration af undertekst lager startet",
|
||||
"TriggerCacheMaintenance triggered": "Udløseren for Cache vedligehold startet",
|
||||
"This may take some time ...": "Dette kan tage et stykke tid ...",
|
||||
"Download Logs": "Download logs",
|
||||
"Sorry, feature unavailable": "Beklager, men ikke tilgængelig",
|
||||
"Universal Plex token not available": "Universal Plex token er ikke tilgængelig",
|
||||
"Copy this link and open this in your browser, please": "Kopier venligst dette link, og åben i en browser",
|
||||
"Cache invalidated": "Cache ugyldig",
|
||||
"Enter PIN number ": "Indtast PIN nummer ",
|
||||
"PIN correct": "PIN korrekt",
|
||||
"Reset": "Nulstil",
|
||||
"Menu locked": "Menu låst",
|
||||
"Provider throttles reset": "Udbyders throttle nulstilles",
|
||||
"Information Storage (%s) reset": "Informationslager (%s) nulstilles",
|
||||
"Information Storage (%s) logged": "Informationslager (%s) logget",
|
||||
"Plex didn't return any information about the item, please refresh it and come back later": "Plex retunerede ingen information vedr. mediet. Venligst opdater det i Plex, og forsøg igen",
|
||||
"Item not found: %s!": "Media ikke fundet: %s!",
|
||||
"< Back to %s": "< Tilbage til %s",
|
||||
"Back to %s > %s": "Tilbage til %s > %s",
|
||||
"Refresh: %s": "Opdater: %s",
|
||||
"Issues a forced refresh, ignoring known subtitles and searching for new ones": "Start en tvungen opdatering, ignorer kendte undertekster, og søg for nye",
|
||||
"Extract and activate embedded subtitle streams": "Extrakt og aktiver undertekster fra selve mediet",
|
||||
"Inspect currently blacklisted subtitles": "Undersøg nuværende undertekster i karantæne",
|
||||
"Subtitle saved to disk": "Undertekster gemt på disk",
|
||||
"Remove subtitle from blacklist": "Fjern undertekst fra karantæne",
|
||||
"No subtitles found": "Ingen undertekster fundet",
|
||||
" (unknown)": " (Ukendt)",
|
||||
" (forced)": " (Tvunget)",
|
||||
"Extracting of embedded subtitle %s of part %s:%s triggered": "Udpakker indlejrede undertekster %s som del af %s:%s startet",
|
||||
"Insufficient permissions": "Utilstrækkelig rettigheder",
|
||||
"I'm not enabled!": "Jeg er ikke aktiveret!",
|
||||
"Please enable me for some of your libraries in your server settings; currently I do nothing": "Venligst aktiver mig til nogle af dine biblioteker i dine server indstillinger; lige nu gør jeg ingenting",
|
||||
"Working ... refresh here": "Arbejder ... Opdater her",
|
||||
"Current state: %s; Last state: %s": "Nuværende status: %s; Sidste status: %s",
|
||||
"On-deck items": "Skrivebords medier",
|
||||
"Shows the %s recently played items and allows you to individually (force-) refresh their metadata/subtitles.": "Viser de %s sidste afspillede titler og giver dig mulighed for individuelt (gennem tvinge) at opdatere deres metadata/undertekster. ",
|
||||
"Recently-added items": "Nylig tilføjet media",
|
||||
"Recently played items": "Nylig afspillet media",
|
||||
"Shows the recently added items per section.": "Vis seneste tilføjede media pr. sektion",
|
||||
"Show recently added items with missing subtitles": "Vis seneste tilføjede hvor undertekster mangler",
|
||||
"Browse all items": "Gennemse alle media",
|
||||
"Go through your whole library and manage your ignore list. You can also (force-) refresh the metadata/subtitles of individual items.": "Gå igennem hele dit bibliotek og håndter din ignorer-liste. Du kan også (gennemtvinge) en opdatering af metadata/undertekster og de enkelte elementer.",
|
||||
"Last run: %s; Next scheduled run: %s; Last runtime: %s": "Sidste gennemgang: %s; Næste planlagte gennemgang: %s; Sidste gennemløbs tid: %s",
|
||||
"Search for missing subtitles (in recently-added items, max-age: %s)": "Søg efter manglende undertekster (blandt de sidste tilføjede titler, maksimal alder: %s)",
|
||||
"Automatically run periodically by the scheduler, if configured. %s": "Automatisk periodisk gennemløb af jobplanlæggeren, såfremt konfigureret. %s.",
|
||||
"Show the current ignore list (mainly used for the automatic tasks)": "Hvis den nuværende ignorer liste (hovedsageligt brugt for de automatiske opgaver)",
|
||||
"History": "Historie",
|
||||
"Show the last %i downloaded subtitles": "Vis de sidste %i hentede undertekster",
|
||||
"Refresh": "Opdater",
|
||||
"Re-lock menu(s)": "Gen aflås meny(er)",
|
||||
"Enabled the PIN again for menu(s)": "Aktiver PIN igen for meny(er)",
|
||||
"Throttled providers: %s": "Throttled udbydere: %s",
|
||||
"Advanced functions": "Avancerede funktioner",
|
||||
"Use at your own risk": "Brug for egen risiko",
|
||||
"Items On Deck": "Elementer på skrivebord",
|
||||
"Recently Played": "Nyeligt afspillede",
|
||||
"Items with missing subtitles": "Titler som mangler undertekster",
|
||||
"Find recent items with missing subtitles": "Find seneste medias hvor undertekster mangler",
|
||||
"Updating, refresh here ...": "Opdaterer, klik her for ny status ...",
|
||||
"Missing: %s": "Mangler: %s",
|
||||
"Didn't change the ignore list": "Ændre ikke ignorer-listen",
|
||||
"Sections": "Sektioner",
|
||||
"All": "Alle",
|
||||
"show": "vis",
|
||||
"movie": "film",
|
||||
"<< Back to home": "<< Tilbage til hoved menu",
|
||||
"Auto-Find subtitles: %s": "Auto-Find undertekster: %s",
|
||||
"Extracting of embedded subtitles for %s triggered": "Udpakning af indlejrede undertekster for %s fundne",
|
||||
"< Back to subtitle options for: %s": "< Tilbage til undertekst muligheder for: %s",
|
||||
"Remove last applied mod (%s)": "Fjern sidste aktive",
|
||||
"none": "ingen",
|
||||
"Manage applied mods": "Håndter de gældende mods",
|
||||
"Reapply applied mods": "Genaktiver de pågældende mods",
|
||||
"Restore original version": "Gendan orginal version",
|
||||
"< Back to subtitle modification menu": "< Tilbage til undertekst modifikations meny",
|
||||
"subs constantly getting faster": "Undertekster bliver konstant hurtigere",
|
||||
"subs constantly getting slower": "Undertekster bliver konstant langsommere",
|
||||
"< Back to subtitle modifications": "< Tilbage til undertekst ændringer",
|
||||
"< Back to unit selection": "< Tilbage til enheds valg ",
|
||||
"Subtitle Language (1)": "Undertekst sprog (1)",
|
||||
"Subtitle Language (2)": "Undertekst sprog (2)",
|
||||
"Subtitle Language (3)": "Undertekst sprog (3)",
|
||||
"Additional Subtitle Languages (use ISO-639-1 codes; comma-separated)": "Flere Undertekst sprog (brug ISO-639-1 koder; kommasepareret)",
|
||||
"Only download foreign/forced subtitles": "Download kun fremmede/gennemtvungne undertekster",
|
||||
"Display languages with country attribute as ISO 639-1 (e.g. pt-BR = pt)": "Vis sprog med lande attributer som i ISO639-1 (f.eks. pt-BR = pt)",
|
||||
"Treat languages with country attribute as ISO 639-1 (e.g. don't download pt-BR if pt subtitle exists)": "Behandel sprog med lande attributer som i ISO 639-1 (f.eks hent ikke pt-BR hvis pt undertekster findes)",
|
||||
"Restrict to one language (skips adding \".lang.\" to the subtitle filename; only uses \"Subtitle Language (1)\")": "Indskrænk til et sprog (tilføj ikke \".lang\" til underteksters filnavn; brug kun \"Undertekst sprog (1)\")",
|
||||
"Embedded subtitles: Treat \"Undefined\" (und) as language 1": "Indlejrede undertekster: Behandel \"Udefineret\" som sprog 1",
|
||||
"I rename my files using": "Jeg omdøber mine filer med",
|
||||
"Retrieve original filename from .file_info/file_info index files (see wiki)": "Genskab det originale filnavn fra .file_info/file_info indeks filer (se wiki)",
|
||||
"Sonarr URL (add URL base if configured)": "Sonarr URL (tilføj URL databse hvis konfigureret)",
|
||||
"Sonarr API key": "Sonarr API key",
|
||||
"Radarr URL (add URL base if configured, min. version: 0.2.0.897)": "Radarr URL (tilføj URL database hvos konfigireret, min. version: 0.2.0.897)",
|
||||
"Radarr API key": "Radarr API key",
|
||||
"Provider: Enable OpenSubtitles": "Provider: Aktiver OpenSubtitles",
|
||||
"Opensubtitles Username": "Opensubtitles Brugernavn",
|
||||
"Opensubtitles Password": "Opensubtitles Kodeord",
|
||||
"OpenSubtitles VIP? (ad-free subs, 1000 subs/day, no-cache VIP server: http://v.ht/osvip)": "OpenSubtitles VIP? (reklamefri undertekster, 1000 undertekster/dag, no-cache VIP server: http://v.ht/osvip)",
|
||||
"Provider: Enable Podnapisi.NET": "Provider: Aktiver Podnapisi.NET",
|
||||
"Provider: Enable Titlovi.com": "Provider: Aktiver Titlovi.com",
|
||||
"Provider: Enable Addic7ed": "Provider: Aktiver Addic7ed",
|
||||
"Addic7ed Username": "Addic7ed Brugernavn",
|
||||
"Addic7ed Password": "Addic7ed Kodeord",
|
||||
"Addic7ed: boost score (if requirements met)": "Addic7ed: boost score (hvis betingelser er opfyldt)",
|
||||
"Addic7ed: Use random user agents": "Addic7ed: Brug tilfældig bruger agent",
|
||||
"Provider: Enable Legendas TV (mostly pt-BR; UNRAR NEEDED)": "Provider: Aktiver Legendas TV (for det meste pt-BR; UNRAR skal være tilgængelig)",
|
||||
"Legendas TV Username": "Legendas TV Brugernavn",
|
||||
"Legendas TV Password": "Legendas TV Kodeord",
|
||||
"Provider: Enable TVsubtitles.net": "Provider: Aktiver TVsubtitles.net",
|
||||
"Provider: Enable NapiProjekt.pl (Polish)": "Provider: Aktiver NapiProjekt.pl (Polsk)",
|
||||
"Provider: Enable SubScene (TV shows)": "Provider: Aktiver SubScene (TV shows)",
|
||||
"Provider: Enable hosszupuskasub.com (Hungarian)": "Provider: Aktiver hosszupuskasub.com (Ungaren)",
|
||||
"Provider: Enable aRGENTeaM (Spanish)": "Provider: Aktiver aRGENTeaM (Spansk)",
|
||||
"Search enabled providers simultaneously (multithreading)": "Søg flere providers samtidigt (multithreading)",
|
||||
"Automatically extract and use embedded subtitles upon media addition (with configured default mods)": "Udpak automatisk og brug indlejrede undertekster når nyt indlægges (med konfigurerede default mods)",
|
||||
"After automatic extraction of embedded subtitles, also immediately search for available subtitles?": "Efter automatisk udpakning af indlejrede undertekster skal der straks søges efter tilgængelige undertekster?",
|
||||
"Don't search for subtitles of a language if there are embedded subtitles inside the media file (MKV/MP4)?": "Søg ikke efter undertekster for et sprog når der er indlejrede undertekster i et medies fil (MKV/MP4)?",
|
||||
"Don't search for subtitles of a language if they already exist on the filesystem (metadata/filesystem)?": "Søg ikke efter undertekster for et sprog hvis det allerede eksisterer i filsystemet (metadata/filsystem)?",
|
||||
"How strict should these subtitles existing on the filesystem be detected?": "Hvor eksakt skal undertekster på fil systemet blive fundet?",
|
||||
"Include non-text subtitle formats (anything else than .srt/.ssa/.ass/.vtt; embedded or external) in the above?": "Inkluder ikke-tekst undertekst formater (alt andet end .srt/.ssa/.ass/.vtt; indlejret eller eksternt) i det ovenstående?",
|
||||
"Minimum score for TV (min: 240, def/sane: 337, min-ideal: 352; see http://v.ht/szscores)": "Minimum score for TV (min: 240, def/sane: 337, min-ideal: 352; læs http://v.ht/szscores)",
|
||||
"Minimum score for movies (min: 60, def/sane: 69, min-ideal: 82; see http://v.ht/szscores)": "Minimum score for film(min: 60, def/sane: 69, min-ideal: 82; læs http://v.ht/szscores)",
|
||||
"Download hearing impaired subtitles.": "Hent undertekster for hørehæmmet",
|
||||
"Remove Hearing Impaired tags from downloaded subtitles": "Fjern hørehæmmet dele fra undertekst",
|
||||
"Remove style tags from downloaded subtitles (bold, italic, underline, colors, ...)": "Fjern formatering fra hentede undertekster (fremhævet, kursiv, understreget, farver ...)",
|
||||
"Fix common issues in subtitles": "Fix std. fejl med undertekster",
|
||||
"Fix common OCR errors in downloaded subtitles": "Fix almindelige OCR fejl i downloadede undertekster",
|
||||
"Reverse punctuation in RTL languages (heb)": "Genindkald tegnsætning i RTL sprog (hebræisk)",
|
||||
"Change colors of subtitles to": "Slå farve på undertekster til",
|
||||
"Store subtitles next to media files (instead of metadata)": "Gem undertekster sammen med medie filer (Istedet for internt i Plex)",
|
||||
"Subtitle formats to save (non-SRT only works if the previous option is enabled)": "Undertekster formater der gemmes (non-SRT virker kun såfremt forrige option er aktiveret)",
|
||||
"Subtitle Folder (\"current folder\" is the folder the current media file lives in)": "Undertekst bibliotek (\"Nuværede bibliotek\" er det bibliotek som det aktive medies fil lever i)",
|
||||
"Custom Subtitle folder (overrides \"Subtitle Folder\"; computes to real paths)": "Brugertilpas Undertekst bibliotek (overskriver \"Undertekst bibliotek\"; udregner den rette sti)",
|
||||
"Fall back to metadata storage if filesystem storage failed": "Fald tilbage til metadata lager såfremt filsystem lager fejlede",
|
||||
"Set subtitle file permissions to (integer, e.g.: 0775)": "Sæt undertekst fils egenskaber til (integer, f.eks: 0775)",
|
||||
"Automatically delete leftover/unused (externally saved) subtitles": "Slet automatisk overskuds/ubrugte (eksternt gemte) undertekster",
|
||||
"On media playback: search for missing subtitles (refresh item)": "Ved mediets playback: søg efter manglende undertekster (genindlæs dem)",
|
||||
"Scheduler: Periodically search for recent items with missing subtitles": "Jobplanlægger: Periodisk søgning efter titler med manglende undertekster",
|
||||
"Scheduler: Item age to be considered recent": "Jobplanlægger: En titles alder anses som fornylig",
|
||||
"Scheduler: Recent items to consider per library": "Jobplanlægger: Fornylige titler at blive undersøgt per bibliotek",
|
||||
"Scheduler: Periodically search for better subtitles": "Jobplanlægger:Periodisk søgning efter bedre undertekster",
|
||||
"Scheduler: Days to search for better subtitles (max: 30 days)": "Jobplanlægger: Dage til søgning efter bedre undertekster (max: 30 dage)",
|
||||
"Scheduler: Don't search for better subtitles if the item's air date is older than": "Jobplanlægger: Søg ikke efter bedre undertekster såfremt titlens dato er ældre end",
|
||||
"Scheduler: Overwrite manually selected subtitles when better found": "Jobplanlægger: Overskriv manuelt valgte undertekster når bedre er fundet",
|
||||
"Scheduler: Overwrite subtitles with non-default subtitle modifications when better found": "Jobplanlægger: Overskriv undertekster når ikke-default undertekster ændringer såfremt bedre er fundet",
|
||||
"History: amount of items to store historical data for": "Historie: antallet af titler som der skal gemmes historiske data for",
|
||||
"How many download tries per subtitle (on timeout or error)": "Hvormange downloads forsøg per titel (på timeout eller fejl)",
|
||||
"Ignore folders (with \"subzero.ignore/.subzero.ignore/.nosz\" files in them)": "Ignorer biblioteker (med \"subzero.ignore/.subzero.ignore/.nosz\" filer i dem)",
|
||||
"Ignore anything in the following paths (comma-separated)": "Ignorer alting i det følgende bibliotek (kommesepareret)",
|
||||
"Sub-Zero mode": "Sub-Zero mode",
|
||||
"Access PIN (any amount of numbers, 0-9)": "PIN (tilfældig antal af nummere , 0-9)",
|
||||
"Access PIN valid for minutes": "Pin er gyldig i minutter",
|
||||
"Use PIN to restrict access to (needs plugin or PMS restart)": "Brug PIN til at hindre adgang til (kræver plugin eller PMS genstart)",
|
||||
"Call this executable upon successful subtitle download (see Wiki for details)": "Kald denne eksekverebare såfremt undertekst download (se i WIKI for detaljer)",
|
||||
"Check for correct folder permissions of every library on plugin start": "Undersøg for de korrekte biblioteks tilladelser på hvert bibliotek når plugin stater op",
|
||||
"Use new style caching (for subliminal)": "Brug ny stil cacing (til sublimiral)",
|
||||
"Low impact mode (for remote filesystems)": "Lille indvirkrning tilstand (for remote filsystemer)",
|
||||
"Timeout for API requests sent to the PMS": "Timeout for kommunikation med Plex server",
|
||||
"HTTP proxy to use for providers (supports credentials)": "HTTP proxy til brug for udbydere (understøtter legitimation)",
|
||||
"How verbose should the logging be?": "Hvor ordrig/omfattende skal logning være?",
|
||||
"How many log backups to keep?": "Hvormange log backups skal gemmes?",
|
||||
"Log subtitle modification (debug)": "Log til modifikation af undertekster (debug)",
|
||||
"Log to console (for development/debugging)": "Log til console (for udvikling/fejlfinding)",
|
||||
"Collect anonymous usage statistics": "Indsaml anonymt brugs statistik",
|
||||
"Sonarr/Radarr (fill api info below)": "Sonarr/Radarr (indsæt api information nedenfor)",
|
||||
"Filebot": "Filebot",
|
||||
"Sonarr/Radarr/Filebot": "Sonarr/Radarr/Filebot",
|
||||
"Symlink to original file": "Symlink til original fil",
|
||||
"I keep the original filenames": "Jeg beholder de orginale fil navne",
|
||||
"none of the above": "Ingen af ovenstående",
|
||||
"exact: media filename match": "eksakt: mediets filnavn stemmer",
|
||||
"loose: filename contains media filename": "løs: filnavn indeholder mediets filnavn",
|
||||
"any": "alle",
|
||||
"prefer": "foretræk",
|
||||
"don't prefer": "foretræk ikke",
|
||||
"force HI": "brug kun hørehæmmet",
|
||||
"force non-HI": "brug ikke hørehæmmet",
|
||||
"don't change": "skift ikke",
|
||||
"white": "hvid",
|
||||
"light-grey": "lysegrå",
|
||||
"red": "rød",
|
||||
"green": "grøn",
|
||||
"yellow": "gul",
|
||||
"blue": "blå",
|
||||
"magenta": "magenta",
|
||||
"cyan": "cyan",
|
||||
"black": "sort",
|
||||
"dark-red": "mørkerød",
|
||||
"dark-green": "mørkegrøn",
|
||||
"dark-yellow": "mørkegul",
|
||||
"dark-blue": "mørkeblå",
|
||||
"dark-magenta": "mørkemagenta",
|
||||
"dark-cyan": "mørkecyan",
|
||||
"dark-grey": "mørkegrå",
|
||||
"current folder": "nuværende folder",
|
||||
"never": "aldrig",
|
||||
"current media item": "nuværende titel",
|
||||
"next episode (series)": "næste episode (serier)",
|
||||
"hybrid: current item or next episode": "hybrid: nuværende titel eller næste afsnit",
|
||||
"hybrid-plus: current item and next episode": "hybrid-plus: nuværende titel eller næste afsnit",
|
||||
"every 6 hours": "hver 6 time",
|
||||
"every 12 hours": "hver 12 time",
|
||||
"every 24 hours": "hver 24 time",
|
||||
"1 days": "hver dag",
|
||||
"2 days": "hver 2 dag",
|
||||
"3 days": "hver 3 dag",
|
||||
"4 days": "hver 4 dag",
|
||||
"1 weeks": "hver uge",
|
||||
"2 weeks": "hver 2 uge",
|
||||
"3 weeks": "hver 3 uge",
|
||||
"4 weeks": "hver 4 uge",
|
||||
"5 weeks": "hver 5 uge",
|
||||
"6 weeks": "hver 6 uge",
|
||||
"12 weeks": "hver 12 uge",
|
||||
"don't limit": "ingen begrænsning",
|
||||
"1 year": "1 år",
|
||||
"2 years": "2 år",
|
||||
"3 years": "3 år",
|
||||
"4 years": "4 år",
|
||||
"5 years": "5 år",
|
||||
"6 years": "6 år",
|
||||
"7 years": "7 år",
|
||||
"8 years": "8 år",
|
||||
"9 years": "9 år",
|
||||
"10 years": "10 år",
|
||||
"only agent": "kun agent",
|
||||
"disabled": "ikke aktiv",
|
||||
"advanced menu": "advanceret menu",
|
||||
"CRITICAL": "KRITISK",
|
||||
"ERROR": "FEJL",
|
||||
"WARNING": "ADVARSEL",
|
||||
"INFO": "INFO",
|
||||
"DEBUG": "DEBUG",
|
||||
"Force-find subtitles: %(item_title)s": "Gennentving-find undertekster: %s (titler)",
|
||||
"File %(file_part_index)s: ": "Fil %(file_part_index)s:␣",
|
||||
"%(part_summary)sNo current subtitle in storage": "%(part_summary)s Ingen undertekster for nuværende gemt",
|
||||
"%(part_summary)sCurrent subtitle: %(provider_name)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "%(part_summary)sNuværende undertekst: %(provider_name)s (tilføjet: %(date_added)s, %(mode)s), Sprog: %(language)s, Score: %(score)i, Lager: %(storage_type)s",
|
||||
"%(part_summary)sManage %(language)s subtitle": "%(part_summary)sAdministrer %(language)s undertekster",
|
||||
"%(part_summary)sList %(language)s subtitles": "%(part_summary)sVis %(language)s undertekster",
|
||||
"%(part_summary)sEmbedded subtitles (%(languages)s)": "%(part_summary)sIndlejrede undertekster (%(language)s)",
|
||||
"Select active %(language)s subtitle": "Vælg aktive %(language)s undertekster",
|
||||
"%(count)d subtitles in storage": "%(count)d undertekster gemt",
|
||||
"List available %(language)s subtitles": "Vis tilgængelige %(language)s undertekster",
|
||||
"Modify current %(language)s subtitle": "Modificer nuværende %(language)s undertekst",
|
||||
"Currently applied mods: %(mod_list)s": "Nuværende gældende mods: %(mod_list)s",
|
||||
"Blacklist current %(language)s subtitle and search for a new one": "Blacklist nuværende %(language)s undertekst og søg efter en ny",
|
||||
"Manage blacklist (%(amount)s contained)": "Administrer blacklist (%(amount)s indeholdt)",
|
||||
"%(current_state)s%(subtitle_name)s, Score: %(score)s": "%(current_state)s%(subtitle_name)s, Score: %(score)s",
|
||||
"Current: ": "Nuværende: ",
|
||||
"Stored: ": "Gemt: ",
|
||||
"by %(release_group)s": "af %(release_group)s",
|
||||
"Current: %(provider_name)s (%(score)s) ": "Nuværende: %(provider_name)s (%(score)s)␣",
|
||||
"Search for %(language)s subs (%(video_data)s)": "Søg efter %(language)s undertekster (%(video_data)s)",
|
||||
"%(current_info)sFilename: %(filename)s": "%(current_info)sFilenavn: %(filename)s",
|
||||
"Searching for %(language)s subs (%(video_data)s), refresh here ...": "Søger efter %(language)s undertekster (%(video_data)s), opdater her...",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: %(media_fps)s)": " ␣(forkert FPS, undertekst: %(subtitle_fps)s, medie: %(media_fps)s)",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: unknown, low impact mode)": "␣(forkert FPS, undertekst: %(subtitle_fps)s, medie: ukendt, lav betydnings modus)",
|
||||
"%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s": "%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s",
|
||||
"Release: %(release_info)s, Matches: %(matches)s": "Udgivelse: %(release_info)s, Stemmer: %(matches)s",
|
||||
"Downloading subtitle for %(title_or_id)s": "Henter undertekster for %(title_or_id)s",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s with default mods": "Udpak stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s med gældende mods",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s": "Udpak stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s",
|
||||
"%(provider_name)s, %(subtitle_id)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "%(provider_name)s, %(subtitle_id)s (added: %(date_added)s, %(mode)s), Sprog: %(language)s, Score: %(score)i, Lager: %(storage_type)s",
|
||||
"Insufficient permissions on library %(title)s, folder: %(path)s": "Utilstrækkelig tilladelser for bibliotek %(title)s, Sti: %(path)s",
|
||||
"Running: %(items_done)s/%(items_searching)s (%(percentage)s%%)": "Kører: %(items_done)s/%(items_searching)s (%(percentage)s%%)",
|
||||
"Display ignore list (%(ignored_count)d)": "Vis ignorer listen (%(ignored_count)d)",
|
||||
"%(throttled_provider)s until %(until_date)s (%(reason)s)": "%(throttled_provider)s indtil %(until_date)s (%(reason)s)",
|
||||
"Extracting subtitle %(stream_index)s of %(filename)s": "Udpakker undertekst %(stream_index)s af %(filename)s",
|
||||
"Extract missing %(language)s embedded subtitles": "Udpakker manglende %(language)s indlejrede undertekster",
|
||||
"Extract and activate %(language)s embedded subtitles": "Udpak og aktiver %(language)s indlejrede undertekster",
|
||||
"None": "Ingen",
|
||||
"Idle": "Venter",
|
||||
"%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)": "%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)",
|
||||
"Adjust by %(time_and_unit)s": "Justeret med %(time_and_unit)s",
|
||||
"added: %(date_added)s, %(mode)s, Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "tilføjet: %(date_added)s, %(mode)s, Sprog: %(language)s, Score: %(score)i, Lager: %(storage_type)s",
|
||||
"Remove: %(mod_name)s": "Fjern: %(mod_name)s",
|
||||
"%(class_name)s: Subtitle download failed (%(item_id)s)": "%(class_name)s: Undertekst download fejlede (%(item_id)s)",
|
||||
"agent + interface": "agent + interface",
|
||||
"only interface": "kun interface",
|
||||
"interface": "interface",
|
||||
"Shows the current on deck items and allows you to individually (force-) refresh their metadata/subtitles.": "Vis nuværende desktop titler og du kan individuelt (gennemtvinge) en opdatering af metadata/undertekster.",
|
||||
"Lists items with missing subtitles. Click on \"Find recent items with missing subs\" to update list": "Vis titler med manglende undertekster. Klik på \"Find seneste titler med manglende undertekster\" for at opdatere listen",
|
||||
"Add %(kind)s %(title)s to the ignore list": "Tilføj %(kind)s %(title)s til ignorer listen",
|
||||
"Remove %(kind)s %(title)s from the ignore list": "Fjern %(kind)s %(title)s fra ignorer listen",
|
||||
"%(title)s added to the ignore list": "%(title)s tilføjet til ignorer listen",
|
||||
"%(title)s removed from the ignore list": "%(title)s fjernet fra ignorer listen",
|
||||
"Triggering refresh for %(title)s": "Aktiver genindlæsning for %(title)s",
|
||||
"Triggering forced refresh for %(title)s": "Aktiver gennemtvunget opdatering for %(title)s",
|
||||
"Refresh of item %(item_id)s triggered": "Opdatering af %(item_id)s titler er aktiveret",
|
||||
"Forced refresh of item %(item_id)s triggered": "Gennemtvunget opdatering af %(item_id)s titler er aktiveret",
|
||||
"Ignore %(kind)s \"%(title)s\"": "Ignorer %(kind)s \"%(title)s\"",
|
||||
"Un-ignore %(kind)s \"%(title)s\"": "Ikke-ignorer %(kind)s \"%(title)s\"",
|
||||
"Refreshing %(title)s": "Genindlæser %(title)s",
|
||||
"Force-refreshing %(title)s": "Gennentvunget-opdatering %(title)s",
|
||||
"Extracts the not yet extracted embedded subtitles of all episodes for the current season with all configured default modifications": "Udpakker de endnu ikke udpakkede indlejrede undertekster fra alle afsnit for nuværende sæson ved brug af de konfigurede default indstillede rettelser \n",
|
||||
"Extracts embedded subtitles of all episodes for the current season with all configured default modifications": "Udpakker indlejrede undertekster fra alle afsnit for nuværende sæson ved brug af de konfigurede default indstillede rettelser",
|
||||
"Refreshes %(the_movie_series_season_episode)s, possibly searching for missing and picking up new subtitles on disk": "Genindlæser %(the_movie_series_season_episode)s, sandsynligvis søges der efter manglende undertekster ellers anvendes nye fra disken",
|
||||
"the movie": "filmen",
|
||||
"the series": "serien",
|
||||
"the episode": "episoden",
|
||||
"the season": "sæsonen",
|
||||
"Change the color of the subtitle": "Skift farve på underteksten",
|
||||
"Adds the requested color to every line of the subtitle. Support depends on player.": "Tilføjer den valgte farve til hver linje i underteksten. Support afhænger af afspiller.",
|
||||
"Basic common fixes": "Basale fælles rettelser",
|
||||
"Fix common and whitespace/punctuation issues in subtitles": "Ret fælles og whitespace/tegnsætnings problemer i undertekster",
|
||||
"Remove all style tags": "Fjern alle style tags",
|
||||
"Removes all possible style tags from the subtitle, such as font, bold, color etc.": "Fjern alle mulige style tags fra underteksten såsom font, fed, farve osv.",
|
||||
"Reverse punctuation in RTL languages": "Omvend tegnsætning i RTL sprog",
|
||||
"Some playback devices don't properly handle right-to-left markers for punctuation. Physically swap punctuation. Applicable to languages: hebrew": "Nogle afspille håndterer ikke højre-til-venstre tegnsætning korrekt. Fysisk ændring af tegnsætning. Gældende for sprog som: hebræisk",
|
||||
"Change the FPS of the subtitle": "Ændre FPS for undertekst",
|
||||
"Re-syncs the subtitle to the framerate of the current media file.": "Gensynkroniser underteksten med frameraten på den nuværende medie fil.",
|
||||
"Remove Hearing Impaired tags": "Fjern Høresvækkede tags",
|
||||
"Removes tags, text and characters from subtitles that are meant for hearing impaired people": "Fjern tags, tekst og tegn fra underteksten som er tiltænkt høresvækkede mennesker",
|
||||
"Fix common OCR issues": "Ret fælles OCR problemer",
|
||||
"Fix issues that happen when a subtitle gets converted from bitmap to text through OCR": "Ret problemer når en undertekst bliver oversat fra bitmap eller gennem OCR",
|
||||
"Change the timing of the subtitle": "Ændre timingen på underteksten",
|
||||
"Adds or substracts a certain amount of time from the whole subtitle to match your media": "Tilføj eller fjern en vis tid fra underteksten så den matcher din medie",
|
||||
"Available": "Tilgængelig",
|
||||
"Current": "Nuværende",
|
||||
"Custom path to advanced_settings.json": "Bruger defineret sti til advanced_settings.json"
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
{
|
||||
"sq": "Albanisch",
|
||||
"ar": "Arabisch",
|
||||
"be": "Weißrussisch",
|
||||
"bs": "Bosnisch",
|
||||
"bg": "Bulgarisch",
|
||||
"ca": "Katalanisch",
|
||||
"zh": "Chinesisch",
|
||||
"hr": "Kroatisch",
|
||||
"cs": "Tschechisch",
|
||||
"da": "Dänisch",
|
||||
"nl": "Holländisch",
|
||||
"en": "Englisch",
|
||||
"et": "Estnisch",
|
||||
"fa": "Persisch",
|
||||
"fi": "Finnisch",
|
||||
"fr": "Französisch",
|
||||
"de": "Deutsch",
|
||||
"el": "Griechisch",
|
||||
"he": "Hebräisch",
|
||||
"hi": "Hindi",
|
||||
"hu": "Ungarisch",
|
||||
"is": "Isländisch",
|
||||
"id": "Indonesisch",
|
||||
"it": "Italienisch",
|
||||
"ja": "Japanisch",
|
||||
"ko": "Koreanisch",
|
||||
"lv": "Lettisch",
|
||||
"lt": "Litauisch",
|
||||
"mk": "Mazedonisch",
|
||||
"ms": "Malaiisch",
|
||||
"no": "Norwegisch",
|
||||
"pl": "Polnisch",
|
||||
"pt": "Portugiesisch",
|
||||
"pt-br": "Portigiesisch (Brasilien)",
|
||||
"ro": "Rumänisch",
|
||||
"ru": "Russisch",
|
||||
"sr": "Serbisch",
|
||||
"sr-cyrl": "Serbisch (Kyrillisch)",
|
||||
"sr-latn": "Serbisch (Lateinisch)",
|
||||
"sk": "Slowakisch",
|
||||
"sl": "Slowenisch",
|
||||
"es": "Spanisch",
|
||||
"sv": "Schwedisch",
|
||||
"th": "Thailändisch",
|
||||
"tr": "Türkisch",
|
||||
"uk": "Ukrainisch",
|
||||
"vi": "Vietnamesisch",
|
||||
"Internal stuff, pay attention!": "Internes, bitte aufpassen!",
|
||||
"Advanced": "Erweitert",
|
||||
"advanced": "erweitert",
|
||||
"Enter PIN": "PIN eingeben",
|
||||
"The owner has restricted the access to this menu. Please enter the correct pin": "Der Inhaber hat den Zugriff zu diesem Menü eingeschränkt, bitte die korrekte PIN eingeben",
|
||||
"Restart the plugin": "Plugin neustarten",
|
||||
"Get my logs (copy the appearing link and open it in your browser, please)": "Hole meine Logs (bitte den erscheinenden Link kopieren und im Browser öffnen)",
|
||||
"Copy the appearing link and open it in your browser, please": "Bitte den erscheinenden Link kopieren und im Browser öffnen",
|
||||
"Trigger find better subtitles": "Bessere Untertitel finden anstoßen",
|
||||
"Skip next find better subtitles (sets last run to now)": "Das nächste Bessere-Untertitel-Finden überspringen (setzt den letzten Laufzeitpunkt auf jetzt)",
|
||||
"Trigger subtitle storage maintenance": "Unterstitelspeicher-Wartung anstoßen",
|
||||
"Trigger subtitle storage migration (expensive)": "Untertitelspeicher-Migration anstoßen (teuer)",
|
||||
"Trigger cache maintenance (refiners, providers and packs/archives)": "Cache-Wartung anstoßen (Refiner, Anbieter, Pakete/Archive)",
|
||||
"Apply configured default subtitle mods to all (active) stored subtitles": "Alle Standard-Untertitel-Mods auf alle (aktiven) gespeicherten Untertitel anwenden",
|
||||
"Re-Apply mods of all stored subtitles": "Alle Mods aller gespeicherten Untertitel abermals anwenden",
|
||||
"Log the plugin's scheduled tasks state storage": "Den Geplante-Aufgaben-Status-Speicher protokollieren",
|
||||
"Log the plugin's internal ignorelist storage": "Den internen Ignore-Listen-Speicher protokollieren",
|
||||
"Log the plugin's complete state storage": "Den kompletten internen State-Speicher protokollieren",
|
||||
"Reset the plugin's scheduled tasks state storage": "Den Geplante-Aufgaben-Status-Speicher zurücksetzen",
|
||||
"Reset the plugin's internal ignorelist storage": "Den internen Ignore-Listen-Speicher zurücksetzen",
|
||||
"Invalidate Sub-Zero metadata caches (subliminal)": "Die Sub-Zero Metadaten-Caches invalidieren (subliminal)",
|
||||
"Reset provider throttle states": "Den Anbieter-Drosselungsstatus zurücksetzen",
|
||||
"Restarting the plugin": "Starte das Plugin neu",
|
||||
"Restart triggered, please wait about 5 seconds": "Neustart angestoßen, bitte circa 5 Sekunden warten",
|
||||
"Reset subtitle storage": "Untertitelspeicher zurücksetzen",
|
||||
"Are you sure?": "Sicher?",
|
||||
"Are you really sure?": "Wirklich sicher?",
|
||||
"Success": "Erfolg",
|
||||
"FindBetterSubtitles triggered": "FindBetterSubtitles angestoßen",
|
||||
"FindBetterSubtitles skipped": "FindBetterSubtitles übersprungen",
|
||||
"SubtitleStorageMaintenance triggered": "SubtitleStorageMaintenance angestoßen",
|
||||
"MigrateSubtitleStorage triggered": "MigrateSubtitleStorage angestoßen",
|
||||
"TriggerCacheMaintenance triggered": "TriggerCacheMaintenance angestoßen",
|
||||
"This may take some time ...": "Dies könnte ein wenig dauern ...",
|
||||
"Download Logs": "Logs herunterladen",
|
||||
"Sorry, feature unavailable": "Entschuldigung, Funktion nicht verfügbar",
|
||||
"Universal Plex token not available": "Universeller Plex-Token nicht verfügbar",
|
||||
"Copy this link and open this in your browser, please": "Bitte diesen Link kopieren und im Browser öffnen",
|
||||
"Cache invalidated": "Cache invalidiert",
|
||||
"Enter PIN number ": "PIN eingeben",
|
||||
"PIN correct": "PIN ist korrekt",
|
||||
"Reset": "Zurücksetzen",
|
||||
"Menu locked": "Menü gesperrt",
|
||||
"Provider throttles reset": "Anbieter-Drosselung zurückgesetzt",
|
||||
"Information Storage (%s) reset": "Informationsspeicher (%s) zurückgesetzt",
|
||||
"Information Storage (%s) logged": "Informationsspeicher (%s) protokolliert",
|
||||
"Plex didn't return any information about the item, please refresh it and come back later": "Plex hat leider keine Informationen über dieses Element, bitte dieses aktualisieren und noch mal probieren",
|
||||
"Item not found: %s!": "Element nicht gefunden: %s!",
|
||||
"< Back to %s": "< Zurück zu %s",
|
||||
"Back to %s > %s": "Zurück zu %s > %s",
|
||||
"Refresh: %s": "Aktualisieren: %s",
|
||||
"Issues a forced refresh, ignoring known subtitles and searching for new ones": "Startet eine erzwungene Aktualisierung; ignoriert dabei bereits bekannte Untertitel und sucht nach neuen",
|
||||
"Extract and activate embedded subtitle streams": "Eingebettete Untertitel extrahieren und aktivieren",
|
||||
"Inspect currently blacklisted subtitles": "Untertitel-Sperrliste ansehen",
|
||||
"Subtitle saved to disk": "Untertitel gespeichert",
|
||||
"Remove subtitle from blacklist": "Untertitel von der Sperrliste entfernen",
|
||||
"No subtitles found": "Keine Untertitel gefunden",
|
||||
" (unknown)": " (unbekannt)",
|
||||
" (forced)": " (erzwungen)",
|
||||
"Extracting of embedded subtitle %s of part %s:%s triggered": "Extraktion des eingebetteten Untertitels %s des Teils %s:%s angestoßen",
|
||||
"Insufficient permissions": "Unzureichende Berechtigungen",
|
||||
"I'm not enabled!": "Ich bin nicht aktiv!",
|
||||
"Please enable me for some of your libraries in your server settings; currently I do nothing": "Bitte für wenigstens eine Medienbibliothek aktivieren, aktuell tue ich nichts",
|
||||
"Working ... refresh here": "In Arbeit ... hier aktualisieren",
|
||||
"Current state: %s; Last state: %s": "Aktueller Status: %s; Letzter Status: %s",
|
||||
"On-deck items": "Aktuelle Medien",
|
||||
"Shows the %s recently played items and allows you to individually (force-) refresh their metadata/subtitles.": "Zeigt die letzten %s abgespielten Medien und ermöglicht die (erzwungene) Aktualisierung derer Metadaten/Untertitel",
|
||||
"Recently-added items": "Zuletzt hinzugefügte Medien",
|
||||
"Recently played items": "Zuletzt abgespielte Medien",
|
||||
"Shows the recently added items per section.": "Zeigt die zuletzt hinzugefügten Medien pro Bereich",
|
||||
"Show recently added items with missing subtitles": "Zeigt zuletzt hinzugefügte Medien mit fehlenden Untertiteln",
|
||||
"Browse all items": "Alle Medien durchsuchen",
|
||||
"Go through your whole library and manage your ignore list. You can also (force-) refresh the metadata/subtitles of individual items.": "Die gesamten Medienbibliothek durchsuchen und die Ignore-Listen verwalten. Hier können ebenfalls die Metadaten/Untertitel einzelner Medien (erzwungen) aktualisiert werden",
|
||||
"Last run: %s; Next scheduled run: %s; Last runtime: %s": "Letzter Durchlauf: %s; Nächster geplanter Durchlauf: %s; Letzte Laufzeit: %s",
|
||||
"Search for missing subtitles (in recently-added items, max-age: %s)": "Nach fehlenden Untertiteln suchen (für zuletzt hinzugefügte Medien, maximales Alter: %s)",
|
||||
"Automatically run periodically by the scheduler, if configured. %s": "Automatisch regelmäßig vom Scheduler ausgeführt, wenn konfiguriert. %s",
|
||||
"Show the current ignore list (mainly used for the automatic tasks)": "Zeige die aktuelle Ignore-Liste (hauptsächlich für Hintergrundaufgaben genutzt)",
|
||||
"History": "Verlauf",
|
||||
"Show the last %i downloaded subtitles": "Zeige die letzten %i heruntergeladenen Untertitel",
|
||||
"Refresh": "Aktualisieren",
|
||||
"Re-lock menu(s)": "Menüs wieder sperren",
|
||||
"Enabled the PIN again for menu(s)": "PIN für Menüs wieder aktiviert",
|
||||
"Throttled providers: %s": "Gedrosselte Anbieter: %s",
|
||||
"Advanced functions": "Erweiterte Funktionen",
|
||||
"Use at your own risk": "Benutzung auf eigene Gefahr",
|
||||
"Items On Deck": "Aktuelle Medien",
|
||||
"Recently Played": "Zuletzt abgespielt",
|
||||
"Items with missing subtitles": "Medien mit fehlenden Untertiteln",
|
||||
"Find recent items with missing subtitles": "Finde zuletzt hinzugefügte Medien mit fehlenden Untertiteln",
|
||||
"Updating, refresh here ...": "Update, hier aktualisieren ...",
|
||||
"Missing: %s": "Fehlt: %s",
|
||||
"Didn't change the ignore list": "Ignore-Liste wurde nicht verändert",
|
||||
"Sections": "Bereiche",
|
||||
"All": "Alle",
|
||||
"show": "Serie",
|
||||
"movie": "Film",
|
||||
"<< Back to home": "<< Zurück zum Anfang",
|
||||
"Auto-Find subtitles: %s": "Untertitel automatisch finden: %s",
|
||||
"Extracting of embedded subtitles for %s triggered": "Extraktion eingebetteter Untertitel für %s angestoßen",
|
||||
"< Back to subtitle options for: %s": "< Zurück zu den Untertiteloptionen für: %s",
|
||||
"Remove last applied mod (%s)": "Die zuletzt angewandte Modifikation entfernen (%s)",
|
||||
"none": "keine",
|
||||
"Manage applied mods": "Angewandte Modifikationen verwalten",
|
||||
"Reapply applied mods": "Angewandte Modifikationen erneut anwenden",
|
||||
"Restore original version": "Originalversion wiederherstellen",
|
||||
"< Back to subtitle modification menu": "< Zurück zum Untertitel-Modifikations-Menü",
|
||||
"subs constantly getting faster": "Untertitel werden konstant schneller",
|
||||
"subs constantly getting slower": "Untertitel werden konstant langsamer",
|
||||
"< Back to subtitle modifications": "< Zurück zu den Untertitel-Modifikationen",
|
||||
"< Back to unit selection": "< Zurück zur Einheiten-Auswahl",
|
||||
"Subtitle Language (1)": "Untertitel-Sprache (1)",
|
||||
"Subtitle Language (2)": "Untertitel-Sprache (2)",
|
||||
"Subtitle Language (3)": "Untertitel-Sprache (3)",
|
||||
"Additional Subtitle Languages (use ISO-639-1 codes; comma-separated)": "Weitere Untertitel-Sprachen (ISO-639-1 Kodierung benutzen, kommagetrennt)",
|
||||
"Only download foreign/forced subtitles": "Nur erzwungende/fremdsprachige Untertitel herunterladen",
|
||||
"Display languages with country attribute as ISO 639-1 (e.g. pt-BR = pt)": "Sprachen mit Landesattribut als ISO 639-1 anzeigen (z. B. pt-BR = pt)",
|
||||
"Treat languages with country attribute as ISO 639-1 (e.g. don't download pt-BR if pt subtitle exists)": "Sprachen mit Landesattribut als ISO 639-1 behandeln (pt-BR nicht herunterladen, wenn ein pt Untertitel existiert)",
|
||||
"Restrict to one language (skips adding \".lang.\" to the subtitle filename; only uses \"Subtitle Language (1)\")": "Auf eine Sprache beschränken (fügt dem Dateinamen kein \".lang.\"-Suffix hinzu; benutzt nur \"Untertitel Sprache (1)\")",
|
||||
"Embedded subtitles: Treat \"Undefined\" (und) as language 1": "Eingebettete Untertitel: behandle Unbekannte Sprache als Sprache 1",
|
||||
"I rename my files using": "Ich benenne meine Dateien um, mit:",
|
||||
"Retrieve original filename from .file_info/file_info index files (see wiki)": "Originale Dateinamen von .file_info/file_info Indexdateien beziehen (siehe Wiki)",
|
||||
"Sonarr URL (add URL base if configured)": "Sonarr URL (URL-Basis hinzufügen, wenn konfiguriert)",
|
||||
"Sonarr API key": "Sonarr API-Schlüssel",
|
||||
"Radarr URL (add URL base if configured, min. version: 0.2.0.897)": "Radarr URL (URL-Basis hinzufügen, wenn konfiguriert; minimale Version: 0.2.0.897)",
|
||||
"Radarr API key": "Radarr API-Schlüssel",
|
||||
"Provider: Enable OpenSubtitles": "Anbieter: OpenSubtitles aktivieren",
|
||||
"Opensubtitles Username": "OpenSubtitles Benutzername",
|
||||
"Opensubtitles Password": "OpenSubtitles Passwort",
|
||||
"OpenSubtitles VIP? (ad-free subs, 1000 subs/day, no-cache VIP server: http://v.ht/osvip)": "OpenSubtitles VIP? (werbefreie Untertitel, 1000 Untertitel/Tag, no-cache VIP-Server: http://v.ht/osvip)",
|
||||
"Provider: Enable Podnapisi.NET": "Anbieter: Podnapisi.NET aktivieren",
|
||||
"Provider: Enable Titlovi.com": "Anbieter: Titlovi.com aktivieren",
|
||||
"Provider: Enable Addic7ed": "Anbieter: Addic7ed aktivieren",
|
||||
"Addic7ed Username": "Addic7ed Benutzername",
|
||||
"Addic7ed Password": "Addic7ed Passwort",
|
||||
"Addic7ed: boost score (if requirements met)": "Addic7ed: Punktzahl anheben (wenn Voraussetzungen erfüllt)",
|
||||
"Addic7ed: Use random user agents": "Addic7ed: Zufällige User-Agents benutzen",
|
||||
"Provider: Enable Legendas TV (mostly pt-BR; UNRAR NEEDED)": "Anbieter: Legendas TV aktivieren (meist pt-BR; UNRAR erforderlich!)",
|
||||
"Legendas TV Username": "Legendas TV Benutzername",
|
||||
"Legendas TV Password": "Legendas TV Passwort",
|
||||
"Provider: Enable TVsubtitles.net": "Anbieter: TVsubtitles.net aktivieren",
|
||||
"Provider: Enable NapiProjekt.pl (Polish)": "Anbieter: NapiProjekt.pl aktivieren (polnisch)",
|
||||
"Provider: Enable SubScene (TV shows)": "Anbieter: SubScene aktivieren (Serien)",
|
||||
"Provider: Enable hosszupuskasub.com (Hungarian)": "Anbieter: Hosszupuskasub.com aktivieren (ungarisch)",
|
||||
"Provider: Enable aRGENTeaM (Spanish)": "Anbieter: aRGENTeaM aktivieren (spanisch)",
|
||||
"Search enabled providers simultaneously (multithreading)": "Durchsuche aktive Anbieter gleichzeitig (Multithreading)",
|
||||
"Automatically extract and use embedded subtitles upon media addition (with configured default mods)": "Extrahiere und aktiviere eingebettete Untertitel automatisch nach dem Hinzufügen der Medien (mit den konfigurierten Standard-Mods)",
|
||||
"After automatic extraction of embedded subtitles, also immediately search for available subtitles?": "Soll nach der automatischen Extraktion eingebetteter Untertitel zusätzlich auch sofort nach verfügbaren gesucht werden?",
|
||||
"Don't search for subtitles of a language if there are embedded subtitles inside the media file (MKV/MP4)?": "Nicht nach Untertiteln einer Sprache suchen, wenn bereits einer als eingebetteter Untertitel in der Mediendatei existiert (MKV/MP4)?",
|
||||
"Don't search for subtitles of a language if they already exist on the filesystem (metadata/filesystem)?": "Nicht nach Untertiteln einer Sprache suchen, wenn bereits einer im Dateisystem existiert (Metadaten/Dateisystem)?",
|
||||
"How strict should these subtitles existing on the filesystem be detected?": "Wie genau soll die Erkennung existierender Untertitel auf dem Dateisystem sein?",
|
||||
"Include non-text subtitle formats (anything else than .srt/.ssa/.ass/.vtt; embedded or external) in the above?": "Dabei nicht-textbasierte Untertitelformate einbeziehen? (Alles außer .srt/.ssa/.ass/.vtt; eingebettet oder extern)",
|
||||
"Minimum score for TV (min: 240, def/sane: 337, min-ideal: 352; see http://v.ht/szscores)": "Minimale Punktzahl für Serien (min: 240, sinnvoll: 337, min-ideal: 352; see http://v.ht/szscores)",
|
||||
"Minimum score for movies (min: 60, def/sane: 69, min-ideal: 82; see http://v.ht/szscores)": "Minimale Punktzahl für Filme (min: 60, def/sane: 69, sinnvoll: 82; see http://v.ht/szscores)",
|
||||
"Download hearing impaired subtitles.": "Untertitel für Hörgeschädigte herunterladen.",
|
||||
"Remove Hearing Impaired tags from downloaded subtitles": "Inhalte für Hörgeschädigte von heruntergeladenen Untertiteln entfernen",
|
||||
"Remove style tags from downloaded subtitles (bold, italic, underline, colors, ...)": "Textformatierungen von heruntergeladenen Untertiteln entfernen (fett, kursiv, unterstrichen, Farben, ...)",
|
||||
"Fix common issues in subtitles": "Häufige Probleme in Untertiteln beheben",
|
||||
"Fix common OCR errors in downloaded subtitles": "Häufige Texterkennungsfehler in Untertiteln beheben",
|
||||
"Reverse punctuation in RTL languages (heb)": "Zeichensetzung in linksläufigen Schriften umkehren (heb)",
|
||||
"Change colors of subtitles to": "Farbe der Untertitel ändern zu",
|
||||
"Store subtitles next to media files (instead of metadata)": "Speichere Untertitel neben den Medien (anstatt im Metadatenspeicher)",
|
||||
"Subtitle formats to save (non-SRT only works if the previous option is enabled)": "Zu speichernde Untertitelformate (Nicht-SRT funktioniert nur, wenn die vorherige Option aktiv ist)",
|
||||
"Subtitle Folder (\"current folder\" is the folder the current media file lives in)": "Untertitel-Ordner (\"aktueller Ordner\" ist der, in dem die Mediendatei liegt)",
|
||||
"Custom Subtitle folder (overrides \"Subtitle Folder\"; computes to real paths)": "Benutzerdefinierter Untertitelordner (überschreibt \"Untertitel-Ordner\"; wird zu echten Pfaden aufgelöst)",
|
||||
"Fall back to metadata storage if filesystem storage failed": "Im Notfall im Metadatenspeicher speichern, wenn nicht auf dem Dateisystem gespeichert werden konnte",
|
||||
"Set subtitle file permissions to (integer, e.g.: 0775)": "Untertitel-Berechtigungen setzen auf (Ganzzahl, z. B.: 0775)",
|
||||
"Automatically delete leftover/unused (externally saved) subtitles": "Automatisch übrig gebliebene/ungenutzte (extern gespeicherter) Untertitel löschen",
|
||||
"On media playback: search for missing subtitles (refresh item)": "Beim Abspielen von Median nach fehlenden Untertiteln suchen (Element aktualisieren)",
|
||||
"Scheduler: Periodically search for recent items with missing subtitles": "Hintergrundaufgaben: Regelmäßig nach aktuellen Medien mit fehlenden Untertiteln suchen",
|
||||
"Scheduler: Item age to be considered recent": "Hintergrundaufgaben: Medien-Alter um als \"aktuell\" zu gelten",
|
||||
"Scheduler: Recent items to consider per library": "Hintergrundaufgaben: Anzahl an zu berücksichtigenden Medien pro Bibliothek",
|
||||
"Scheduler: Periodically search for better subtitles": "Hintergrundaufgaben: Regelmäßig nach besseren Untertiteln suchen",
|
||||
"Scheduler: Days to search for better subtitles (max: 30 days)": "Hintergrundaufgaben: Wie viele Tage soll nach besseren Untertiteln gesucht werden (max: 30 Tage)",
|
||||
"Scheduler: Don't search for better subtitles if the item's air date is older than": "Hintergrundaufgaben: Nicht nach besseren Untertiteln suchen, wenn der Ausstrahlungszeitpunkt älter ist als",
|
||||
"Scheduler: Overwrite manually selected subtitles when better found": "Hintergrundaufgaben: Manuell ausgewählte Untertitel überschreiben, wenn bessere gefunden wurden",
|
||||
"Scheduler: Overwrite subtitles with non-default subtitle modifications when better found": "Hintergrundaufgaben: Untertitel mit Nicht-Standard-Modifikationen überschreiben, wenn bessere gefunden wurden",
|
||||
"History: amount of items to store historical data for": "Verlauf: Anzahl der Elemente",
|
||||
"How many download tries per subtitle (on timeout or error)": "Wie oft soll der Download pro Untertitel versucht werden (bei Fehlern)",
|
||||
"Ignore folders (with \"subzero.ignore/.subzero.ignore/.nosz\" files in them)": "Ignoriere Ordner mit \"subzero.ignore/.subzero.ignore/.nosz\"-Dateien",
|
||||
"Ignore anything in the following paths (comma-separated)": "Ignoriere folgende Pfade",
|
||||
"Sub-Zero mode": "Sub-Zero Modus",
|
||||
"Access PIN (any amount of numbers, 0-9)": "Zugriffs-PIN (beliebige Anzahl Zahlen, 0-9)",
|
||||
"Access PIN valid for minutes": "Zugriffs-PIN gültig für Minuten",
|
||||
"Use PIN to restrict access to (needs plugin or PMS restart)": "Benutze PIN um den Zugriff einzuschränken für (benötigt Plugin- oder PMS-Neustart)",
|
||||
"Call this executable upon successful subtitle download (see Wiki for details)": "Diese ausführbare Datei nach erfolgreichem Untertitel-Download aufrufen (siehe Wiki für Details)",
|
||||
"Check for correct folder permissions of every library on plugin start": "Korrekte Ordnerberechtigungen bei jedem Plugin-Start überprüfen",
|
||||
"Use new style caching (for subliminal)": "Modernes Caching benutzen (für subliminal)",
|
||||
"Low impact mode (for remote filesystems)": "Belastungsarmer Modus (für rechnerferne Dateisysteme)",
|
||||
"Timeout for API requests sent to the PMS": "Timeout für Plex-API-Anfragen",
|
||||
"HTTP proxy to use for providers (supports credentials)": "Benutze HTTP-Proxy für Untertitel-Anbieter (unterstützt Anmeldeinformationen)",
|
||||
"How verbose should the logging be?": "Wie ausführlich soll die Protokollierung sein?",
|
||||
"How many log backups to keep?": "Wie viele Protokoll-Sicherungen sollen behalten werden?",
|
||||
"Log subtitle modification (debug)": "Untertitelmodifikationen protokollieren (debug)",
|
||||
"Log to console (for development/debugging)": "Protokollausgabe in der Konsole (für Entwicklung/Debugging)",
|
||||
"Collect anonymous usage statistics": "Anonyme Nutzungsstatistiken sammeln",
|
||||
"Sonarr/Radarr (fill api info below)": "Sonarr/Radarr (API info unten ergänzen)",
|
||||
"Filebot": "Filebot",
|
||||
"Sonarr/Radarr/Filebot": "Sonarr/Radarr/Filebot",
|
||||
"Symlink to original file": "Symbolischer Link zur Originaldatei",
|
||||
"I keep the original filenames": "Ich behalte die originalen Dateinamen",
|
||||
"none of the above": "keine dieser Optionen",
|
||||
"exact: media filename match": "Exakt: Dateiname stimmt überein",
|
||||
"loose: filename contains media filename": "Locker: Untertiteldateiname enthält Mediendateiname",
|
||||
"any": "alle",
|
||||
"prefer": "bevorzugen",
|
||||
"don't prefer": "nicht bevorzugen",
|
||||
"force HI": "HI erzwingen",
|
||||
"force non-HI": "Non-HI erzwingen",
|
||||
"don't change": "nicht ändern",
|
||||
"white": "weiß",
|
||||
"light-grey": "hellgrau",
|
||||
"red": "rot",
|
||||
"green": "grün",
|
||||
"yellow": "gelb",
|
||||
"blue": "blau",
|
||||
"magenta": "Magentarot",
|
||||
"cyan": "türkis",
|
||||
"black": "schwarz",
|
||||
"dark-red": "dunkelrot",
|
||||
"dark-green": "dunkelgrün",
|
||||
"dark-yellow": "ocker",
|
||||
"dark-blue": "dunkelblau",
|
||||
"dark-magenta": "dunkles Magentarot",
|
||||
"dark-cyan": "dunkles Türkis",
|
||||
"dark-grey": "dunkelgrau",
|
||||
"current folder": "aktueller Ordner",
|
||||
"never": "nie",
|
||||
"current media item": "aktuelle Mediendatei",
|
||||
"next episode (series)": "nächste Episode (Serien)",
|
||||
"hybrid: current item or next episode": "hybrid: aktuelle Mediendatei oder nächste Episode",
|
||||
"hybrid-plus: current item and next episode": "hybrid-plus: aktuelle Mediendatei und nächste Episode",
|
||||
"every 6 hours": "alle 6 Stunden",
|
||||
"every 12 hours": "alle 12 Stunden",
|
||||
"every 24 hours": "alle 24 Stunden",
|
||||
"1 days": "1 Tag",
|
||||
"2 days": "2 Tage",
|
||||
"3 days": "3 Tage",
|
||||
"4 days": "4 Tage",
|
||||
"1 weeks": "1 Woche",
|
||||
"2 weeks": "2 Wochen",
|
||||
"3 weeks": "3 Wochen",
|
||||
"4 weeks": "4 Wochen",
|
||||
"5 weeks": "5 Wochen",
|
||||
"6 weeks": "6 Wochen",
|
||||
"12 weeks": "12 Wochen",
|
||||
"don't limit": "keine Limitierung",
|
||||
"1 year": "1 Jahr",
|
||||
"2 years": "2 Jahre",
|
||||
"3 years": "3 Jahre",
|
||||
"4 years": "4 Jahre",
|
||||
"5 years": "5 Jahre",
|
||||
"6 years": "6 Jahre",
|
||||
"7 years": "7 Jahre",
|
||||
"8 years": "8 Jahre",
|
||||
"9 years": "9 Jahre",
|
||||
"10 years": "10 Jahre",
|
||||
"only agent": "nur Metadaten-Agent",
|
||||
"disabled": "deaktiviert",
|
||||
"advanced menu": "erweitertes Menü",
|
||||
"CRITICAL": "CRITICAL",
|
||||
"ERROR": "ERROR",
|
||||
"WARNING": "WARNING",
|
||||
"INFO": "INFO",
|
||||
"DEBUG": "DEBUG",
|
||||
"Force-find subtitles: %(item_title)s": "Erzwinge Suche nach Untertiteln: %(item_title)s",
|
||||
"File %(file_part_index)s: ": "Datei %(file_part_index)s: ",
|
||||
"%(part_summary)sNo current subtitle in storage": "%(part_summary)sKein aktueller Untertitel im Speicher",
|
||||
"%(part_summary)sCurrent subtitle: %(provider_name)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "%(part_summary)sAktueller Untertitel: %(provider_name)s (hinzugefügt: %(date_added)s, %(mode)s), Sprache: %(language)s, Punktzahl: %(score)i, Speicher: %(storage_type)s",
|
||||
"%(part_summary)sManage %(language)s subtitle": "%(part_summary)s%(language)s: Untertitel verwalten",
|
||||
"%(part_summary)sList %(language)s subtitles": "%(part_summary)s%(language)s: Untertitel auflisten",
|
||||
"%(part_summary)sEmbedded subtitles (%(languages)s)": "%(part_summary)sEingebettete Untertitel (%(languages)s)",
|
||||
"Select active %(language)s subtitle": "%(language)s: Aktiven Untertitel auswählen",
|
||||
"%(count)d subtitles in storage": "%(count)d Untertitel im Speicher",
|
||||
"List available %(language)s subtitles": "%(language)s: Verfügbare Untertitel auflisten",
|
||||
"Modify current %(language)s subtitle": "%(language)s: Aktiven Untertitel modifizieren",
|
||||
"Currently applied mods: %(mod_list)s": "Aktuell angewandte Modifikationen: %(mod_list)s",
|
||||
"Blacklist current %(language)s subtitle and search for a new one": "%(language)s: Aktuellen Untertitel zur Sperrliste hinzufügen und nach einem neuen suchen",
|
||||
"Manage blacklist (%(amount)s contained)": "Sperrliste verwalten (enthält %(amount)s)",
|
||||
"%(current_state)s%(subtitle_name)s, Score: %(score)s": "%(current_state)s%(subtitle_name)s, Punktzahl: %(score)s",
|
||||
"Current: ": "Aktuell: ",
|
||||
"Stored: ": "Gespeichert: ",
|
||||
"by %(release_group)s": "von %(release_group)s",
|
||||
"Current: %(provider_name)s (%(score)s) ": "Aktuell: %(provider_name)s (%(score)s) ",
|
||||
"Search for %(language)s subs (%(video_data)s)": "%(language)s: Nach Untertiteln suchen (%(video_data)s)",
|
||||
"%(current_info)sFilename: %(filename)s": "%(current_info)sDateiname: %(filename)s",
|
||||
"Searching for %(language)s subs (%(video_data)s), refresh here ...": "%(language)s: Suche nach Untertiteln (%(video_data)s), hier aktualisieren ...",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: %(media_fps)s)": " (falsche FPS, Untertitel: %(subtitle_fps)s, Video: %(media_fps)s)",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: unknown, low impact mode)": " (falsche FPS, Untertitel: %(subtitle_fps)s, Video: Unbekannt, belastungsarmer Modus aktiv)",
|
||||
"%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s": "%(blacklisted_state)s%(current_state)s: %(provider_name)s, Punktzahl: %(score)s%(wrong_fps_state)s",
|
||||
"Release: %(release_info)s, Matches: %(matches)s": "Release: %(release_info)s, Übereinstimmungen: %(matches)s",
|
||||
"Downloading subtitle for %(title_or_id)s": "Lade Untertitel für %(title_or_id)s herunter",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s with default mods": "Stream mit Standard-Mods extrahieren: %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s": "Stream extrahieren: %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s",
|
||||
"%(provider_name)s, %(subtitle_id)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "%(provider_name)s, %(subtitle_id)s (hinzugefügt: %(date_added)s, %(mode)s), Sprache: %(language)s, Punktzahl: %(score)i, Speicher: %(storage_type)s",
|
||||
"Insufficient permissions on library %(title)s, folder: %(path)s": "Ungenügende Berechtigungen der Bibliothek %(title)s, Ordner: %(path)s",
|
||||
"Running: %(items_done)s/%(items_searching)s (%(percentage)s%%)": "Läuft: %(items_done)s/%(items_searching)s (%(percentage)s%%)",
|
||||
"Display ignore list (%(ignored_count)d)": "Ignore-Liste anzeigen (%(ignored_count)d)",
|
||||
"%(throttled_provider)s until %(until_date)s (%(reason)s)": "%(throttled_provider)s bis %(until_date)s (%(reason)s)",
|
||||
"Extracting subtitle %(stream_index)s of %(filename)s": "Extrahiere Untertitel %(stream_index)s von %(filename)s",
|
||||
"Extract missing %(language)s embedded subtitles": "%(language)s: Extrahiere fehlende, eingebettete Untertitel",
|
||||
"Extract and activate %(language)s embedded subtitles": "%(language)s: Extrahiere und aktiviere eingebettete Untertitel",
|
||||
"None": "Nichts",
|
||||
"Idle": "Inaktiv",
|
||||
"%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)": "%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)",
|
||||
"Adjust by %(time_and_unit)s": "Verschieben um %(time_and_unit)s",
|
||||
"added: %(date_added)s, %(mode)s, Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "hinzugefügt: %(date_added)s, %(mode)s, Sprache: %(language)s, Punktzahl: %(score)i, Speicher: %(storage_type)s",
|
||||
"Remove: %(mod_name)s": "%(mod_name)s entfernen",
|
||||
"%(class_name)s: Subtitle download failed (%(item_id)s)": "%(class_name)s: Herunterladen von Untertitel fehlgeschlagen (%(item_id)s)",
|
||||
"agent + interface": "Metadatenagent + Benutzeroberfläche",
|
||||
"only interface": "nur Benutzeroberfläche",
|
||||
"interface": "Benutzeroberfläche",
|
||||
"Shows the current on deck items and allows you to individually (force-) refresh their metadata/subtitles.": "Zeigt die aktuellen Medien und ermöglicht die (erzwungene) Aktualisierung derer Metadaten/Untertitel",
|
||||
"Lists items with missing subtitles. Click on \"Find recent items with missing subs\" to update list": "Zeigt Medien mit fehlenden Untertiteln. Auf \"Finde zuletzt hinzugefügte Medien mit fehlenden Untertiteln\" klicken, um die Liste zu aktualisieren",
|
||||
"Add %(kind)s %(title)s to the ignore list": "Füge %(kind)s %(title)s zur Ignore-Liste hinzu",
|
||||
"Remove %(kind)s %(title)s from the ignore list": "Entferne %(kind)s %(title)s von der Ignore-Liste",
|
||||
"%(title)s added to the ignore list": "%(title)s zur Ignore-Liste hinzugefügt",
|
||||
"%(title)s removed from the ignore list": "%(title)s von der Ignore-Liste entfernt",
|
||||
"Triggering refresh for %(title)s": "Stoße Aktualisierung an, für: %(title)s",
|
||||
"Triggering forced refresh for %(title)s": "Stoße erzwungene Aktualisieren an, für %(title)s",
|
||||
"Refresh of item %(item_id)s triggered": "Aktualisierung von %(item_id)s angestoßen",
|
||||
"Forced refresh of item %(item_id)s triggered": "Erzwungene Aktualisierung von %(item_id)s angestoßen",
|
||||
"Ignore %(kind)s \"%(title)s\"": "%(kind)s \"%(title)s\" ignorieren",
|
||||
"Un-ignore %(kind)s \"%(title)s\"": "%(kind)s \"%(title)s\" nicht mehr ignorieren",
|
||||
"Refreshing %(title)s": "Aktualisiere %(title)s",
|
||||
"Force-refreshing %(title)s": "Erzwinge Aktualisierung von %(title)s",
|
||||
"Extracts the not yet extracted embedded subtitles of all episodes for the current season with all configured default modifications": "Extrahiert die noch nicht extrahierten, eingebetteten Untertitel aller Episoden der aktuellen Staffel, mit allen konfigurierten Standard-Modifikationen",
|
||||
"Extracts embedded subtitles of all episodes for the current season with all configured default modifications": "Extrahiert eingebettete Untertitel aller Episoden der aktuellen Staffel, mit allen konfigurierten Standard-Modifikationen",
|
||||
"Refreshes %(the_movie_series_season_episode)s, possibly searching for missing and picking up new subtitles on disk": "Aktualisiert %(the_movie_series_season_episode)s, möglicherweise mit einer Suche nach fehlenden Untertiteln, sowie Auffinden von Untertiteln im Dateisystem",
|
||||
"the movie": "den Film",
|
||||
"the series": "die Serie",
|
||||
"the episode": "die Episode",
|
||||
"the season": "die Staffel",
|
||||
"Change the color of the subtitle": "Untertitel-Farben ändern",
|
||||
"Adds the requested color to every line of the subtitle. Support depends on player.": "Fügt die gewählte Farbe zu jeder Untertitelzeile hinzu. Wird nicht von jedem Abspielgerät unterstützt",
|
||||
"Basic common fixes": "Behebe häufige Probleme in Untertiteln",
|
||||
"Fix common and whitespace/punctuation issues in subtitles": "Behebe häufige und Leerraum/Zeichensetzungs-Probleme in Untertiteln",
|
||||
"Remove all style tags": "Entferne alle Textformatierungen",
|
||||
"Removes all possible style tags from the subtitle, such as font, bold, color etc.": "Entfernt alle möglichen Textformatierungen von Untertiteln, wie z. B. Schriftart, Fettschrift, Farben etc.",
|
||||
"Reverse punctuation in RTL languages": "Zeichensetzung in linksläufigen Schriften umkehren",
|
||||
"Some playback devices don't properly handle right-to-left markers for punctuation. Physically swap punctuation. Applicable to languages: hebrew": "Einige Abspielgeräte verarbeiten nötige Marker für linksläufige Schriften nicht korrekt. Invertiere die Zeichensetzung, um dies zu beheben. Anwendbar auf: hebräisch",
|
||||
"Change the FPS of the subtitle": "Ändern der Bildwiederholrate des Untertitels",
|
||||
"Re-syncs the subtitle to the framerate of the current media file.": "Synchronisiert den Untertitel zur Bildwiederholrate der Mediendatei.",
|
||||
"Remove Hearing Impaired tags": "Inhalte für Hörgeschädigte von heruntergeladenen Untertiteln entfernen",
|
||||
"Removes tags, text and characters from subtitles that are meant for hearing impaired people": "Entfernt Markierungen, Text und Zeichen von Untertiteln, die Inhalte für Hörgeschädigte enthalten",
|
||||
"Fix common OCR issues": "Häufige Texterkennungsfehler in Untertiteln beheben",
|
||||
"Fix issues that happen when a subtitle gets converted from bitmap to text through OCR": "Behebt Fehler, die auftreten, wenn ein Untertitel vom Bitmap-Format zu Text konvertiert werden (OCR).",
|
||||
"Change the timing of the subtitle": "Die zeitliche Abstimmung des Untertitels ändern",
|
||||
"Adds or substracts a certain amount of time from the whole subtitle to match your media": "Verschiebt den Untertitel um eine bestimmte Zeiteinheit",
|
||||
"Available": "Verfügbar",
|
||||
"Current": "Aktuell",
|
||||
"Custom path to advanced_settings.json": "Spezifischer Pfad zu advanced_settings.json",
|
||||
"zh-hant": "Chinesisch (traditionell)",
|
||||
"zh-hans": "Chinesisch (vereinfacht)"
|
||||
}
|
||||
Executable → Regular
+354
-1
@@ -6,6 +6,8 @@
|
||||
"bg":"Bulgarian",
|
||||
"ca":"Catalan",
|
||||
"zh":"Chinese",
|
||||
"zh-hant":"Chinese (Traditional)",
|
||||
"zh-hans":"Chinese (Simplified)",
|
||||
"hr":"Croatian",
|
||||
"cs":"Czech",
|
||||
"da":"Danish",
|
||||
@@ -45,5 +47,356 @@
|
||||
"th":"Thai",
|
||||
"tr":"Turkish",
|
||||
"uk":"Ukranian",
|
||||
"vi":"Vietnamese"
|
||||
"vi":"Vietnamese",
|
||||
"Internal stuff, pay attention!":"Internal stuff, pay attention!",
|
||||
"Advanced":"Advanced",
|
||||
"advanced":"advanced",
|
||||
"Enter PIN":"Enter PIN",
|
||||
"The owner has restricted the access to this menu. Please enter the correct pin":"The owner has restricted the access to this menu. Please enter the correct pin",
|
||||
"Restart the plugin":"Restart the plugin",
|
||||
"Get my logs (copy the appearing link and open it in your browser, please)":"Get my logs (copy the appearing link and open it in your browser, please)",
|
||||
"Copy the appearing link and open it in your browser, please":"Copy the appearing link and open it in your browser, please",
|
||||
"Trigger find better subtitles":"Trigger find better subtitles",
|
||||
"Skip next find better subtitles (sets last run to now)":"Skip next find better subtitles (sets last run to now)",
|
||||
"Trigger subtitle storage maintenance":"Trigger subtitle storage maintenance",
|
||||
"Trigger subtitle storage migration (expensive)":"Trigger subtitle storage migration (expensive)",
|
||||
"Trigger cache maintenance (refiners, providers and packs/archives)":"Trigger cache maintenance (refiners, providers and packs/archives)",
|
||||
"Apply configured default subtitle mods to all (active) stored subtitles":"Apply configured default subtitle mods to all (active) stored subtitles",
|
||||
"Re-Apply mods of all stored subtitles":"Re-Apply mods of all stored subtitles",
|
||||
"Log the plugin's scheduled tasks state storage":"Log the plugin's scheduled tasks state storage",
|
||||
"Log the plugin's internal ignorelist storage":"Log the plugin's internal ignorelist storage",
|
||||
"Log the plugin's complete state storage":"Log the plugin's complete state storage",
|
||||
"Reset the plugin's scheduled tasks state storage":"Reset the plugin's scheduled tasks state storage",
|
||||
"Reset the plugin's internal ignorelist storage":"Reset the plugin's internal ignorelist storage",
|
||||
"Invalidate Sub-Zero metadata caches (subliminal)":"Invalidate Sub-Zero metadata caches (subliminal)",
|
||||
"Reset provider throttle states":"Reset provider throttle states",
|
||||
"Restarting the plugin":"Restarting the plugin",
|
||||
"Restart triggered, please wait about 5 seconds":"Restart triggered, please wait about 5 seconds",
|
||||
"Reset subtitle storage":"Reset subtitle storage",
|
||||
"Are you sure?":"Are you sure?",
|
||||
"Are you really sure?":"Are you really sure?",
|
||||
"Success":"Success",
|
||||
"Information Storage (%s) reset":"Information Storage (%s) reset",
|
||||
"Information Storage (%s) logged":"Information Storage (%s) logged",
|
||||
"FindBetterSubtitles triggered":"FindBetterSubtitles triggered",
|
||||
"FindBetterSubtitles skipped":"FindBetterSubtitles skipped",
|
||||
"SubtitleStorageMaintenance triggered":"SubtitleStorageMaintenance triggered",
|
||||
"MigrateSubtitleStorage triggered":"MigrateSubtitleStorage triggered",
|
||||
"TriggerCacheMaintenance triggered":"TriggerCacheMaintenance triggered",
|
||||
"This may take some time ...":"This may take some time ...",
|
||||
"Download Logs":"Download Logs",
|
||||
"Sorry, feature unavailable":"Sorry, feature unavailable",
|
||||
"Universal Plex token not available":"Universal Plex token not available",
|
||||
"Copy this link and open this in your browser, please":"Copy this link and open this in your browser, please",
|
||||
"Cache invalidated":"Cache invalidated",
|
||||
"Enter PIN number ":"Enter PIN number ",
|
||||
"PIN correct":"PIN correct",
|
||||
"Reset":"Reset",
|
||||
"Menu locked":"Menu locked",
|
||||
"Provider throttles reset":"Provider throttles reset",
|
||||
"Plex didn't return any information about the item, please refresh it and come back later":"Plex didn't return any information about the item, please refresh it and come back later",
|
||||
"Item not found: %s!":"Item not found: %s!",
|
||||
"< Back to %s":"< Back to %s",
|
||||
"Back to %s > %s":"Back to %s > %s",
|
||||
"Refresh: %s":"Refresh: %s",
|
||||
"Refreshes %(the_movie_series_season_episode)s, possibly searching for missing and picking up new subtitles on disk":"Refreshes %(the_movie_series_season_episode)s, possibly searching for missing and picking up new subtitles on disk",
|
||||
"the movie":"the movie",
|
||||
"the series":"the series",
|
||||
"the episode":"the episode",
|
||||
"the season":"the season",
|
||||
"Force-find subtitles: %(item_title)s":"Force-find subtitles: %(item_title)s",
|
||||
"Issues a forced refresh, ignoring known subtitles and searching for new ones":"Issues a forced refresh, ignoring known subtitles and searching for new ones",
|
||||
"File %(file_part_index)s: ":"File %(file_part_index)s: ",
|
||||
"%(part_summary)sNo current subtitle in storage":"%(part_summary)sNo current subtitle in storage",
|
||||
"%(part_summary)sCurrent subtitle: %(provider_name)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s":"%(part_summary)sCurrent subtitle: %(provider_name)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s",
|
||||
"%(part_summary)sManage %(language)s subtitle":"%(part_summary)sManage %(language)s subtitle",
|
||||
"%(part_summary)sList %(language)s subtitles":"%(part_summary)sList %(language)s subtitles",
|
||||
"%(part_summary)sEmbedded subtitles (%(languages)s)":"%(part_summary)sEmbedded subtitles (%(languages)s)",
|
||||
"Extract and activate embedded subtitle streams":"Extract and activate embedded subtitle streams",
|
||||
"Select active %(language)s subtitle":"Select active %(language)s subtitle",
|
||||
"%(count)d subtitles in storage":"%(count)d subtitles in storage",
|
||||
"List available %(language)s subtitles":"List available %(language)s subtitles",
|
||||
"Modify current %(language)s subtitle":"Modify current %(language)s subtitle",
|
||||
"Currently applied mods: %(mod_list)s":"Currently applied mods: %(mod_list)s",
|
||||
"Blacklist current %(language)s subtitle and search for a new one":"Blacklist current %(language)s subtitle and search for a new one",
|
||||
"Manage blacklist (%(amount)s contained)":"Manage blacklist (%(amount)s contained)",
|
||||
"Inspect currently blacklisted subtitles":"Inspect currently blacklisted subtitles",
|
||||
"%(current_state)s%(subtitle_name)s, Score: %(score)s":"%(current_state)s%(subtitle_name)s, Score: %(score)s",
|
||||
"Current: ":"Current: ",
|
||||
"Stored: ":"Stored: ",
|
||||
"Subtitle saved to disk":"Subtitle saved to disk",
|
||||
"Remove subtitle from blacklist":"Remove subtitle from blacklist",
|
||||
"by %(release_group)s":"by %(release_group)s",
|
||||
"Current: %(provider_name)s (%(score)s) ":"Current: %(provider_name)s (%(score)s) ",
|
||||
"Search for %(language)s subs (%(video_data)s)":"Search for %(language)s subs (%(video_data)s)",
|
||||
"%(current_info)sFilename: %(filename)s":"%(current_info)sFilename: %(filename)s",
|
||||
"No subtitles found":"No subtitles found",
|
||||
"Searching for %(language)s subs (%(video_data)s), refresh here ...":"Searching for %(language)s subs (%(video_data)s), refresh here ...",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: %(media_fps)s)":" (wrong FPS, sub: %(subtitle_fps)s, media: %(media_fps)s)",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: unknown, low impact mode)":" (wrong FPS, sub: %(subtitle_fps)s, media: unknown, low impact mode)",
|
||||
"%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s":"%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s",
|
||||
"Release: %(release_info)s, Matches: %(matches)s":"Release: %(release_info)s, Matches: %(matches)s",
|
||||
"Downloading subtitle for %(title_or_id)s":"Downloading subtitle for %(title_or_id)s",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s with default mods":"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s with default mods",
|
||||
" (unknown)":" (unknown)",
|
||||
" (forced)":" (forced)",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s":"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s",
|
||||
"Extracting of embedded subtitle %s of part %s:%s triggered":"Extracting of embedded subtitle %s of part %s:%s triggered",
|
||||
"%(provider_name)s, %(subtitle_id)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s":"%(provider_name)s, %(subtitle_id)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s",
|
||||
"Insufficient permissions":"Insufficient permissions",
|
||||
"Insufficient permissions on library %(title)s, folder: %(path)s":"Insufficient permissions on library %(title)s, folder: %(path)s",
|
||||
"I'm not enabled!":"I'm not enabled!",
|
||||
"Please enable me for some of your libraries in your server settings; currently I do nothing":"Please enable me for some of your libraries in your server settings; currently I do nothing",
|
||||
"Working ... refresh here":"Working ... refresh here",
|
||||
"Current state: %s; Last state: %s":"Current state: %s; Last state: %s",
|
||||
"On-deck items":"On-deck items",
|
||||
"Shows the current on deck items and allows you to individually (force-) refresh their metadata/subtitles.":"Shows the current on deck items and allows you to individually (force-) refresh their metadata/subtitles.",
|
||||
"Shows the %s recently played items and allows you to individually (force-) refresh their metadata/subtitles.":"Shows the %s recently played items and allows you to individually (force-) refresh their metadata/subtitles.",
|
||||
"Recently-added items":"Recently-added items",
|
||||
"Recently played items":"Recently played items",
|
||||
"Shows the recently added items per section.":"Shows the recently added items per section.",
|
||||
"Show recently added items with missing subtitles":"Show recently added items with missing subtitles",
|
||||
"Lists items with missing subtitles. Click on \"Find recent items with missing subs\" to update list":"Lists items with missing subtitles. Click on \"Find recent items with missing subs\" to update list",
|
||||
"Browse all items":"Browse all items",
|
||||
"Go through your whole library and manage your ignore list. You can also (force-) refresh the metadata/subtitles of individual items.":"Go through your whole library and manage your ignore list. You can also (force-) refresh the metadata/subtitles of individual items.",
|
||||
"Running: %(items_done)s/%(items_searching)s (%(percentage)s%%)":"Running: %(items_done)s/%(items_searching)s (%(percentage)s%%)",
|
||||
"Last run: %s; Next scheduled run: %s; Last runtime: %s":"Last run: %s; Next scheduled run: %s; Last runtime: %s",
|
||||
"Search for missing subtitles (in recently-added items, max-age: %s)":"Search for missing subtitles (in recently-added items, max-age: %s)",
|
||||
"Automatically run periodically by the scheduler, if configured. %s":"Automatically run periodically by the scheduler, if configured. %s",
|
||||
"Display ignore list (%(ignored_count)d)":"Display ignore list (%(ignored_count)d)",
|
||||
"Show the current ignore list (mainly used for the automatic tasks)":"Show the current ignore list (mainly used for the automatic tasks)",
|
||||
"History":"History",
|
||||
"Show the last %i downloaded subtitles":"Show the last %i downloaded subtitles",
|
||||
"Refresh":"Refresh",
|
||||
"Re-lock menu(s)":"Re-lock menu(s)",
|
||||
"Enabled the PIN again for menu(s)":"Enabled the PIN again for menu(s)",
|
||||
"%(throttled_provider)s until %(until_date)s (%(reason)s)":"%(throttled_provider)s until %(until_date)s (%(reason)s)",
|
||||
"Throttled providers: %s":"Throttled providers: %s",
|
||||
"Advanced functions":"Advanced functions",
|
||||
"Use at your own risk":"Use at your own risk",
|
||||
"Items On Deck":"Items On Deck",
|
||||
"Recently Played":"Recently Played",
|
||||
"Items with missing subtitles":"Items with missing subtitles",
|
||||
"Find recent items with missing subtitles":"Find recent items with missing subtitles",
|
||||
"Updating, refresh here ...":"Updating, refresh here ...",
|
||||
"Missing: %s":"Missing: %s",
|
||||
"Add %(kind)s %(title)s to the ignore list":"Add %(kind)s %(title)s to the ignore list",
|
||||
"Remove %(kind)s %(title)s from the ignore list":"Remove %(kind)s %(title)s from the ignore list",
|
||||
"Didn't change the ignore list":"Didn't change the ignore list",
|
||||
"%(title)s added to the ignore list":"%(title)s added to the ignore list",
|
||||
"%(title)s removed from the ignore list":"%(title)s removed from the ignore list",
|
||||
"Sections":"Sections",
|
||||
"All":"All",
|
||||
"Ignore %(kind)s \"%(title)s\"":"Ignore %(kind)s \"%(title)s\"",
|
||||
"Un-ignore %(kind)s \"%(title)s\"":"Un-ignore %(kind)s \"%(title)s\"",
|
||||
"show":"show",
|
||||
"movie":"movie",
|
||||
"Refreshing %(title)s":"Refreshing %(title)s",
|
||||
"Force-refreshing %(title)s":"Force-refreshing %(title)s",
|
||||
"Extracting subtitle %(stream_index)s of %(filename)s":"Extracting subtitle %(stream_index)s of %(filename)s",
|
||||
"<< Back to home":"<< Back to home",
|
||||
"Extract missing %(language)s embedded subtitles":"Extract missing %(language)s embedded subtitles",
|
||||
"Extracts the not yet extracted embedded subtitles of all episodes for the current season with all configured default modifications":"Extracts the not yet extracted embedded subtitles of all episodes for the current season with all configured default modifications",
|
||||
"Extract and activate %(language)s embedded subtitles":"Extract and activate %(language)s embedded subtitles",
|
||||
"Extracts embedded subtitles of all episodes for the current season with all configured default modifications":"Extracts embedded subtitles of all episodes for the current season with all configured default modifications",
|
||||
"Auto-Find subtitles: %s":"Auto-Find subtitles: %s",
|
||||
"Extracting of embedded subtitles for %s triggered":"Extracting of embedded subtitles for %s triggered",
|
||||
"Triggering refresh for %(title)s":"Triggering refresh for %(title)s",
|
||||
"Triggering forced refresh for %(title)s":"Triggering forced refresh for %(title)s",
|
||||
"Refresh of item %(item_id)s triggered":"Refresh of item %(item_id)s triggered",
|
||||
"Forced refresh of item %(item_id)s triggered":"Forced refresh of item %(item_id)s triggered",
|
||||
"< Back to subtitle options for: %s":"< Back to subtitle options for: %s",
|
||||
"Remove last applied mod (%s)":"Remove last applied mod (%s)",
|
||||
"none":"none",
|
||||
"None":"None",
|
||||
"Idle":"Idle",
|
||||
"Manage applied mods":"Manage applied mods",
|
||||
"Reapply applied mods":"Reapply applied mods",
|
||||
"Restore original version":"Restore original version",
|
||||
"< Back to subtitle modification menu":"< Back to subtitle modification menu",
|
||||
"subs constantly getting faster":"subs constantly getting faster",
|
||||
"subs constantly getting slower":"subs constantly getting slower",
|
||||
"%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)":"%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)",
|
||||
"< Back to subtitle modifications":"< Back to subtitle modifications",
|
||||
"Adjust by %(time_and_unit)s":"Adjust by %(time_and_unit)s",
|
||||
"< Back to unit selection":"< Back to unit selection",
|
||||
"added: %(date_added)s, %(mode)s, Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s":"added: %(date_added)s, %(mode)s, Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s",
|
||||
"Remove: %(mod_name)s":"Remove: %(mod_name)s",
|
||||
"%(class_name)s: Subtitle download failed (%(item_id)s)": "%(class_name)s: Subtitle download failed (%(item_id)s)",
|
||||
"Subtitle Language (1)":"Subtitle Language (1)",
|
||||
"Subtitle Language (2)":"Subtitle Language (2)",
|
||||
"Subtitle Language (3)":"Subtitle Language (3)",
|
||||
"Additional Subtitle Languages (use ISO-639-1 codes; comma-separated)":"Additional Subtitle Languages (use ISO-639-1 codes; comma-separated)",
|
||||
"Only download foreign/forced subtitles":"Only download foreign/forced subtitles",
|
||||
"Display languages with country attribute as ISO 639-1 (e.g. pt-BR = pt)":"Display languages with country attribute as ISO 639-1 (e.g. pt-BR = pt)",
|
||||
"Treat languages with country attribute as ISO 639-1 (e.g. don't download pt-BR if pt subtitle exists)":"Treat languages with country attribute as ISO 639-1 (e.g. don't download pt-BR if pt subtitle exists)",
|
||||
"Restrict to one language (skips adding \".lang.\" to the subtitle filename; only uses \"Subtitle Language (1)\")":"Restrict to one language (skips adding \".lang.\" to the subtitle filename; only uses \"Subtitle Language (1)\")",
|
||||
"Embedded subtitles: Treat \"Undefined\" (und) as language 1":"Embedded subtitles: Treat \"Undefined\" (und) as language 1",
|
||||
"I rename my files using":"I rename my files using",
|
||||
"Retrieve original filename from .file_info/file_info index files (see wiki)":"Retrieve original filename from .file_info/file_info index files (see wiki)",
|
||||
"Sonarr URL (add URL base if configured)":"Sonarr URL (add URL base if configured)",
|
||||
"Sonarr API key":"Sonarr API key",
|
||||
"Radarr URL (add URL base if configured, min. version: 0.2.0.897)":"Radarr URL (add URL base if configured, min. version: 0.2.0.897)",
|
||||
"Radarr API key":"Radarr API key",
|
||||
"Provider: Enable OpenSubtitles":"Provider: Enable OpenSubtitles",
|
||||
"Opensubtitles Username":"Opensubtitles Username",
|
||||
"Opensubtitles Password":"Opensubtitles Password",
|
||||
"OpenSubtitles VIP? (ad-free subs, 1000 subs/day, no-cache VIP server: http://v.ht/osvip)":"OpenSubtitles VIP? (ad-free subs, 1000 subs/day, no-cache VIP server: http://v.ht/osvip)",
|
||||
"Provider: Enable Podnapisi.NET":"Provider: Enable Podnapisi.NET",
|
||||
"Provider: Enable Titlovi.com":"Provider: Enable Titlovi.com",
|
||||
"Provider: Enable Addic7ed":"Provider: Enable Addic7ed",
|
||||
"Addic7ed Username":"Addic7ed Username",
|
||||
"Addic7ed Password":"Addic7ed Password",
|
||||
"Addic7ed: boost score (if requirements met)":"Addic7ed: boost score (if requirements met)",
|
||||
"Addic7ed: Use random user agents":"Addic7ed: Use random user agents",
|
||||
"Provider: Enable Legendas TV (mostly pt-BR; UNRAR NEEDED)":"Provider: Enable Legendas TV (mostly pt-BR; UNRAR NEEDED)",
|
||||
"Legendas TV Username":"Legendas TV Username",
|
||||
"Legendas TV Password":"Legendas TV Password",
|
||||
"Provider: Enable TVsubtitles.net":"Provider: Enable TVsubtitles.net",
|
||||
"Provider: Enable NapiProjekt.pl (Polish)":"Provider: Enable NapiProjekt.pl (Polish)",
|
||||
"Provider: Enable SubScene (TV shows)":"Provider: Enable SubScene (TV shows)",
|
||||
"Provider: Enable hosszupuskasub.com (Hungarian)":"Provider: Enable hosszupuskasub.com (Hungarian)",
|
||||
"Provider: Enable aRGENTeaM (Spanish)":"Provider: Enable aRGENTeaM (Spanish)",
|
||||
"Search enabled providers simultaneously (multithreading)":"Search enabled providers simultaneously (multithreading)",
|
||||
"Automatically extract and use embedded subtitles upon media addition (with configured default mods)":"Automatically extract and use embedded subtitles upon media addition (with configured default mods)",
|
||||
"After automatic extraction of embedded subtitles, also immediately search for available subtitles?":"After automatic extraction of embedded subtitles, also immediately search for available subtitles?",
|
||||
"Don't search for subtitles of a language if there are embedded subtitles inside the media file (MKV/MP4)?":"Don't search for subtitles of a language if there are embedded subtitles inside the media file (MKV/MP4)?",
|
||||
"Don't search for subtitles of a language if they already exist on the filesystem (metadata/filesystem)?":"Don't search for subtitles of a language if they already exist on the filesystem (metadata/filesystem)?",
|
||||
"How strict should these subtitles existing on the filesystem be detected?":"How strict should these subtitles existing on the filesystem be detected?",
|
||||
"Include non-text subtitle formats (anything else than .srt/.ssa/.ass/.vtt; embedded or external) in the above?":"Include non-text subtitle formats (anything else than .srt/.ssa/.ass/.vtt; embedded or external) in the above?",
|
||||
"Minimum score for TV (min: 240, def/sane: 337, min-ideal: 352; see http://v.ht/szscores)":"Minimum score for TV (min: 240, def/sane: 337, min-ideal: 352; see http://v.ht/szscores)",
|
||||
"Minimum score for movies (min: 60, def/sane: 69, min-ideal: 82; see http://v.ht/szscores)":"Minimum score for movies (min: 60, def/sane: 69, min-ideal: 82; see http://v.ht/szscores)",
|
||||
"Download hearing impaired subtitles.":"Download hearing impaired subtitles.",
|
||||
"Remove Hearing Impaired tags from downloaded subtitles":"Remove Hearing Impaired tags from downloaded subtitles",
|
||||
"Remove style tags from downloaded subtitles (bold, italic, underline, colors, ...)":"Remove style tags from downloaded subtitles (bold, italic, underline, colors, ...)",
|
||||
"Fix common issues in subtitles":"Fix common issues in subtitles",
|
||||
"Fix common OCR errors in downloaded subtitles":"Fix common OCR errors in downloaded subtitles",
|
||||
"Reverse punctuation in RTL languages (heb)":"Reverse punctuation in RTL languages (heb)",
|
||||
"Change colors of subtitles to":"Change colors of subtitles to",
|
||||
"Store subtitles next to media files (instead of metadata)":"Store subtitles next to media files (instead of metadata)",
|
||||
"Subtitle formats to save (non-SRT only works if the previous option is enabled)":"Subtitle formats to save (non-SRT only works if the previous option is enabled)",
|
||||
"Subtitle Folder (\"current folder\" is the folder the current media file lives in)":"Subtitle Folder (\"current folder\" is the folder the current media file lives in)",
|
||||
"Custom Subtitle folder (overrides \"Subtitle Folder\"; computes to real paths)":"Custom Subtitle folder (overrides \"Subtitle Folder\"; computes to real paths)",
|
||||
"Fall back to metadata storage if filesystem storage failed":"Fall back to metadata storage if filesystem storage failed",
|
||||
"Set subtitle file permissions to (integer, e.g.: 0775)":"Set subtitle file permissions to (integer, e.g.: 0775)",
|
||||
"Automatically delete leftover/unused (externally saved) subtitles":"Automatically delete leftover/unused (externally saved) subtitles",
|
||||
"On media playback: search for missing subtitles (refresh item)":"On media playback: search for missing subtitles (refresh item)",
|
||||
"Scheduler: Periodically search for recent items with missing subtitles":"Scheduler: Periodically search for recent items with missing subtitles",
|
||||
"Scheduler: Item age to be considered recent":"Scheduler: Item age to be considered recent",
|
||||
"Scheduler: Recent items to consider per library":"Scheduler: Recent items to consider per library",
|
||||
"Scheduler: Periodically search for better subtitles":"Scheduler: Periodically search for better subtitles",
|
||||
"Scheduler: Days to search for better subtitles (max: 30 days)":"Scheduler: Days to search for better subtitles (max: 30 days)",
|
||||
"Scheduler: Don't search for better subtitles if the item's air date is older than":"Scheduler: Don't search for better subtitles if the item's air date is older than",
|
||||
"Scheduler: Overwrite manually selected subtitles when better found":"Scheduler: Overwrite manually selected subtitles when better found",
|
||||
"Scheduler: Overwrite subtitles with non-default subtitle modifications when better found":"Scheduler: Overwrite subtitles with non-default subtitle modifications when better found",
|
||||
"History: amount of items to store historical data for":"History: amount of items to store historical data for",
|
||||
"How many download tries per subtitle (on timeout or error)":"How many download tries per subtitle (on timeout or error)",
|
||||
"Ignore folders (with \"subzero.ignore/.subzero.ignore/.nosz\" files in them)":"Ignore folders (with \"subzero.ignore/.subzero.ignore/.nosz\" files in them)",
|
||||
"Ignore anything in the following paths (comma-separated)":"Ignore anything in the following paths (comma-separated)",
|
||||
"Sub-Zero mode":"Sub-Zero mode",
|
||||
"Access PIN (any amount of numbers, 0-9)":"Access PIN (any amount of numbers, 0-9)",
|
||||
"Access PIN valid for minutes":"Access PIN valid for minutes",
|
||||
"Use PIN to restrict access to (needs plugin or PMS restart)":"Use PIN to restrict access to (needs plugin or PMS restart)",
|
||||
"Call this executable upon successful subtitle download (see Wiki for details)":"Call this executable upon successful subtitle download (see Wiki for details)",
|
||||
"Check for correct folder permissions of every library on plugin start":"Check for correct folder permissions of every library on plugin start",
|
||||
"Use new style caching (for subliminal)":"Use new style caching (for subliminal)",
|
||||
"Low impact mode (for remote filesystems)":"Low impact mode (for remote filesystems)",
|
||||
"Timeout for API requests sent to the PMS":"Timeout for API requests sent to the PMS",
|
||||
"HTTP proxy to use for providers (supports credentials)":"HTTP proxy to use for providers (supports credentials)",
|
||||
"How verbose should the logging be?":"How verbose should the logging be?",
|
||||
"How many log backups to keep?":"How many log backups to keep?",
|
||||
"Log subtitle modification (debug)":"Log subtitle modification (debug)",
|
||||
"Log to console (for development/debugging)":"Log to console (for development/debugging)",
|
||||
"Collect anonymous usage statistics":"Collect anonymous usage statistics",
|
||||
"Sonarr/Radarr (fill api info below)":"Sonarr/Radarr (fill api info below)",
|
||||
"Filebot":"Filebot",
|
||||
"Sonarr/Radarr/Filebot":"Sonarr/Radarr/Filebot",
|
||||
"Symlink to original file":"Symlink to original file",
|
||||
"I keep the original filenames":"I keep the original filenames",
|
||||
"none of the above":"none of the above",
|
||||
"exact: media filename match":"exact: media filename match",
|
||||
"loose: filename contains media filename":"loose: filename contains media filename",
|
||||
"any":"any",
|
||||
"prefer":"prefer",
|
||||
"don't prefer":"don't prefer",
|
||||
"force HI":"force HI",
|
||||
"force non-HI":"force non-HI",
|
||||
"don't change":"don't change",
|
||||
"white":"white",
|
||||
"light-grey":"light-grey",
|
||||
"red":"red",
|
||||
"green":"green",
|
||||
"yellow":"yellow",
|
||||
"blue":"blue",
|
||||
"magenta":"magenta",
|
||||
"cyan":"cyan",
|
||||
"black":"black",
|
||||
"dark-red":"dark-red",
|
||||
"dark-green":"dark-green",
|
||||
"dark-yellow":"dark-yellow",
|
||||
"dark-blue":"dark-blue",
|
||||
"dark-magenta":"dark-magenta",
|
||||
"dark-cyan":"dark-cyan",
|
||||
"dark-grey":"dark-grey",
|
||||
"current folder":"current folder",
|
||||
"never":"never",
|
||||
"current media item":"current media item",
|
||||
"next episode (series)":"next episode (series)",
|
||||
"hybrid: current item or next episode":"hybrid: current item or next episode",
|
||||
"hybrid-plus: current item and next episode":"hybrid-plus: current item and next episode",
|
||||
"every 6 hours":"every 6 hours",
|
||||
"every 12 hours":"every 12 hours",
|
||||
"every 24 hours":"every 24 hours",
|
||||
"1 days":"1 days",
|
||||
"2 days":"2 days",
|
||||
"3 days":"3 days",
|
||||
"4 days":"4 days",
|
||||
"1 weeks":"1 weeks",
|
||||
"2 weeks":"2 weeks",
|
||||
"3 weeks":"3 weeks",
|
||||
"4 weeks":"4 weeks",
|
||||
"5 weeks":"5 weeks",
|
||||
"6 weeks":"6 weeks",
|
||||
"12 weeks":"12 weeks",
|
||||
"don't limit":"don't limit",
|
||||
"1 year":"1 year",
|
||||
"2 years":"2 years",
|
||||
"3 years":"3 years",
|
||||
"4 years":"4 years",
|
||||
"5 years":"5 years",
|
||||
"6 years":"6 years",
|
||||
"7 years":"7 years",
|
||||
"8 years":"8 years",
|
||||
"9 years":"9 years",
|
||||
"10 years":"10 years",
|
||||
"agent + interface":"agent + interface",
|
||||
"only agent":"only agent",
|
||||
"only interface":"only interface",
|
||||
"disabled":"disabled",
|
||||
"interface":"interface",
|
||||
"advanced menu":"advanced menu",
|
||||
"CRITICAL":"CRITICAL",
|
||||
"ERROR":"ERROR",
|
||||
"WARNING":"WARNING",
|
||||
"INFO":"INFO",
|
||||
"DEBUG":"DEBUG",
|
||||
"Change the color of the subtitle":"Change the color of the subtitle",
|
||||
"Adds the requested color to every line of the subtitle. Support depends on player.":"Adds the requested color to every line of the subtitle. Support depends on player.",
|
||||
"Basic common fixes":"Basic common fixes",
|
||||
"Fix common and whitespace/punctuation issues in subtitles":"Fix common and whitespace/punctuation issues in subtitles",
|
||||
"Remove all style tags":"Remove all style tags",
|
||||
"Removes all possible style tags from the subtitle, such as font, bold, color etc.":"Removes all possible style tags from the subtitle, such as font, bold, color etc.",
|
||||
"Reverse punctuation in RTL languages":"Reverse punctuation in RTL languages",
|
||||
"Some playback devices don't properly handle right-to-left markers for punctuation. Physically swap punctuation. Applicable to languages: hebrew":"Some playback devices don't properly handle right-to-left markers for punctuation. Physically swap punctuation. Applicable to languages: hebrew",
|
||||
"Change the FPS of the subtitle":"Change the FPS of the subtitle",
|
||||
"Re-syncs the subtitle to the framerate of the current media file.":"Re-syncs the subtitle to the framerate of the current media file.",
|
||||
"Remove Hearing Impaired tags":"Remove Hearing Impaired tags",
|
||||
"Removes tags, text and characters from subtitles that are meant for hearing impaired people":"Removes tags, text and characters from subtitles that are meant for hearing impaired people",
|
||||
"Fix common OCR issues":"Fix common OCR issues",
|
||||
"Fix issues that happen when a subtitle gets converted from bitmap to text through OCR":"Fix issues that happen when a subtitle gets converted from bitmap to text through OCR",
|
||||
"Change the timing of the subtitle":"Change the timing of the subtitle",
|
||||
"Adds or substracts a certain amount of time from the whole subtitle to match your media":"Adds or substracts a certain amount of time from the whole subtitle to match your media",
|
||||
"Available":"Available",
|
||||
"Current":"Current",
|
||||
"Custom path to advanced_settings.json":"Custom path to advanced_settings.json"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,400 @@
|
||||
{
|
||||
"sq": "Albanés",
|
||||
"ar": "Árabe",
|
||||
"be": "Bieloruso",
|
||||
"bs": "Bosnio",
|
||||
"bg": "Búlgaro",
|
||||
"ca": "Catalán",
|
||||
"zh": "Chino",
|
||||
"hr": "Croata",
|
||||
"cs": "Checo",
|
||||
"da": "Danés",
|
||||
"nl": "Neerlandés",
|
||||
"en": "Inglés",
|
||||
"et": "Estonio",
|
||||
"fa": "Persa",
|
||||
"fi": "Finés",
|
||||
"fr": "Francés",
|
||||
"de": "Alemán",
|
||||
"el": "Griego",
|
||||
"he": "Hebreo",
|
||||
"hi": "Hindi",
|
||||
"hu": "Húngaro",
|
||||
"is": "Islandés",
|
||||
"id": "Indonesio",
|
||||
"it": "Italiano",
|
||||
"ja": "Japonés",
|
||||
"ko": "Coreano",
|
||||
"lv": "Letonio",
|
||||
"lt": "Lituano",
|
||||
"mk": "Macedonio",
|
||||
"ms": "Malayo",
|
||||
"no": "Noruego",
|
||||
"pl": "Polaco",
|
||||
"pt": "Portugués",
|
||||
"pt-br": "Portugués (Brasil)",
|
||||
"ro": "Rumano",
|
||||
"ru": "Ruso",
|
||||
"sr": "Serbio",
|
||||
"sr-cyrl": "Serbio (Cirílico)",
|
||||
"sr-latn": "Serbio (Latín)",
|
||||
"sk": "Eslovaco",
|
||||
"sl": "Slovaco",
|
||||
"es": "Español",
|
||||
"sv": "Sueco",
|
||||
"th": "Tailandés",
|
||||
"tr": "Turco",
|
||||
"uk": "Ucraniano",
|
||||
"vi": "Vietnamita",
|
||||
"Internal stuff, pay attention!": "Asunto interno, ¡prestar atención!",
|
||||
"Advanced": "Avanzado",
|
||||
"advanced": "avanzado",
|
||||
"Enter PIN": "Ingresar PIN",
|
||||
"The owner has restricted the access to this menu. Please enter the correct pin": "El dueño ha restringido el acceso a este menú. Por favor ingrese el PIN adecuado",
|
||||
"Restart the plugin": "Reiniciar el plugin",
|
||||
"Get my logs (copy the appearing link and open it in your browser, please)": "Obtener mis bitácoras (copia el enlace que aparece y ábrelo en tu navegador, por favor)",
|
||||
"Copy the appearing link and open it in your browser, please": "Copia el enlace que aparece y ábrelo en tu navegador, por favor",
|
||||
"Trigger find better subtitles": "Provocar búsqueda de Mejores Subtítulos",
|
||||
"Skip next find better subtitles (sets last run to now)": "Evitar la próxima búsqueda de Mejores Subtítulos (fija a Ahora la última búsqueda)",
|
||||
"Trigger subtitle storage maintenance": "Provocar el mantenimiento del almacenamiento de subtítulos",
|
||||
"Trigger subtitle storage migration (expensive)": "Provocar la migración del almacenamiento de subtítulos (lento)",
|
||||
"Trigger cache maintenance (refiners, providers and packs/archives)": "Provocar el mantenimiento de la caché (refinadores, proveedores y paquetes)",
|
||||
"Apply configured default subtitle mods to all (active) stored subtitles": "Aplicar las modificaciones de la configuración de subtítulos a todos los almacenados y activos",
|
||||
"Re-Apply mods of all stored subtitles": "Reaplicar modificaciones a todos los subtítulos almacenados",
|
||||
"Log the plugin's scheduled tasks state storage": "Anotar en el log las tareas de las descargar programadas del plugin",
|
||||
"Log the plugin's internal ignorelist storage": "Anotar en el log la lista a ignorar en las descargas",
|
||||
"Log the plugin's complete state storage": "Anotar en el log el estado completo de las descargas",
|
||||
"Reset the plugin's scheduled tasks state storage": "Reiniciar el plugin de descargas programadas",
|
||||
"Reset the plugin's internal ignorelist storage": "Reiniciar el plugin de la lista a ignorar en las descargas",
|
||||
"Invalidate Sub-Zero metadata caches (subliminal)": "Invalidar el caché de metadata Sub-Zero (subliminal)",
|
||||
"Reset provider throttle states": "Reiniciar estado en proveedores ralentizados",
|
||||
"Restarting the plugin": "Reiniciar el plugin",
|
||||
"Restart triggered, please wait about 5 seconds": "Reinicio provocado, por favor espere 5 segundos",
|
||||
"Reset subtitle storage": "Limpiar carpeta de almacenamiento de subtítulos",
|
||||
"Are you sure?": "¿Estás seguro?",
|
||||
"Are you really sure?": "¿Estás realmente seguro?",
|
||||
"Success": "Éxito",
|
||||
"FindBetterSubtitles triggered": "Buscar Mejores Subtítulos provocado",
|
||||
"FindBetterSubtitles skipped": "Buscar Mejores Subtítulos evitado",
|
||||
"SubtitleStorageMaintenance triggered": "Mantenimiento del almacenamiento de subtítulos provocado",
|
||||
"MigrateSubtitleStorage triggered": "Migración del almacenamiento de subtítulos provocada",
|
||||
"TriggerCacheMaintenance triggered": "Mantenimiento de la caché provocado",
|
||||
"This may take some time ...": "Esto podría tomar algún tiempo...",
|
||||
"Download Logs": "Descargar logs",
|
||||
"Sorry, feature unavailable": "Lo sentimos, característica no disponible",
|
||||
"Universal Plex token not available": "Token universal Plex no disponible",
|
||||
"Copy this link and open this in your browser, please": "Copia este enlace y ábrelo en tu navegador, por favor",
|
||||
"Cache invalidated": "Caché invalidado",
|
||||
"Enter PIN number ": "Ingrese número PIN␣",
|
||||
"PIN correct": "PIN correcto",
|
||||
"Reset": "Resetear",
|
||||
"Menu locked": "Menú bloqueado",
|
||||
"Provider throttles reset": "Reiniciar proveedores ralentizados",
|
||||
"Information Storage (%s) reset": "Almacén de Información (%s) reiniciado",
|
||||
"Information Storage (%s) logged": "Almacén de Información (%s) logueado",
|
||||
"Plex didn't return any information about the item, please refresh it and come back later": "Plex no retornó información del elemento, por favor actualice y vuelva más tarde",
|
||||
"Item not found: %s!": "¡Elemento no encontrado: %s!",
|
||||
"< Back to %s": "< Regresar a %s",
|
||||
"Back to %s > %s": "Regresar a %s > %s",
|
||||
"Refresh: %s": "Actualizar: %s",
|
||||
"Issues a forced refresh, ignoring known subtitles and searching for new ones": "Ejecuta una actualización forzada, ignorando subtítulos presentes y buscando nuevos",
|
||||
"Extract and activate embedded subtitle streams": "Extraer y activar subtítulos integrados",
|
||||
"Inspect currently blacklisted subtitles": "Inspeccionar la actual lista de bloqueados",
|
||||
"Subtitle saved to disk": "Subtítulo guardado en disco",
|
||||
"Remove subtitle from blacklist": "Eliminar subtítulo de la lista de bloqueados",
|
||||
"No subtitles found": "No se han encontrado subtítulos",
|
||||
" (unknown)": "␣(desconocido)",
|
||||
" (forced)": "␣(forzado)",
|
||||
"Extracting of embedded subtitle %s of part %s:%s triggered": "Extracción del subtítulo integrado %s de la parte %s:%s lanzada",
|
||||
"Insufficient permissions": "Permisos insuficientes",
|
||||
"I'm not enabled!": "¡No estoy habilitado!",
|
||||
"Please enable me for some of your libraries in your server settings; currently I do nothing": "Por favor, habilítame para alguna biblioteca en tu servidor; ahora mismo no puedo hacer nada",
|
||||
"Working ... refresh here": "Trabajando... refresque aquí",
|
||||
"Current state: %s; Last state: %s": "Estado actual: %s; Último estado: %s",
|
||||
"On-deck items": "Archivos On-deck",
|
||||
"Shows the %s recently played items and allows you to individually (force-) refresh their metadata/subtitles.": "Muestra los últimos %s elementos reproducidos y permite actualizar individualmente sus metadatos y subtítulos.",
|
||||
"Recently-added items": "Elementos recién añadidos",
|
||||
"Recently played items": "Elementos recién reproducidos",
|
||||
"Shows the recently added items per section.": "Muestra los elementos recién añadidos por sección.",
|
||||
"Show recently added items with missing subtitles": "Muestra los elementos recién añadidos sin subtítulos",
|
||||
"Browse all items": "Ver todos los elementos",
|
||||
"Go through your whole library and manage your ignore list. You can also (force-) refresh the metadata/subtitles of individual items.": "Navega a través de la colección completa y gestiona la Lista de Ignorados. Puede además actualizar individualmente sus metadatos y subtítulos",
|
||||
"Last run: %s; Next scheduled run: %s; Last runtime: %s": "Última ejecución: %s; Próxima ejecución: %s; Duración de la última ejecución: %s",
|
||||
"Search for missing subtitles (in recently-added items, max-age: %s)": "Buscar subtítulos faltantes (en elementos recién añadidos, antigüedad máxima: %s)",
|
||||
"Automatically run periodically by the scheduler, if configured. %s": "Ejecuta automáticamente las tareas programadas, si están configuradas. %s",
|
||||
"Show the current ignore list (mainly used for the automatic tasks)": "Muestra la Lista de Ignorados actual (principalmente usado por tareas automáticas)",
|
||||
"History": "Historial",
|
||||
"Show the last %i downloaded subtitles": "Muestra lo últimos %i subtítulos descargados",
|
||||
"Refresh": "Actualizar",
|
||||
"Re-lock menu(s)": "Bloquear menús",
|
||||
"Enabled the PIN again for menu(s)": "Habilitado el PIN de nuevo en los menús",
|
||||
"Throttled providers: %s": "Proveedores ralentizados: %s",
|
||||
"Advanced functions": "Funciones avanzadas",
|
||||
"Use at your own risk": "Use bajo su responsabilidad",
|
||||
"Items On Deck": "Elementos On Deck",
|
||||
"Recently Played": "Recién Reproducidos",
|
||||
"Items with missing subtitles": "Elementos sin subtítulos",
|
||||
"Find recent items with missing subtitles": "Buscar elementos recientes sin subtítulos",
|
||||
"Updating, refresh here ...": "Actualizando, refresque aquí...",
|
||||
"Missing: %s": "Falta: %s",
|
||||
"Didn't change the ignore list": "La Lista de Ignorados no ha cambiado",
|
||||
"Sections": "Secciones",
|
||||
"All": "Todo",
|
||||
"show": "serie",
|
||||
"movie": "película",
|
||||
"<< Back to home": "<< Volver a Inicio",
|
||||
"Auto-Find subtitles: %s": "Auto-búsqueda de subtítulos: %s ",
|
||||
"Extracting of embedded subtitles for %s triggered": "Extracción lanzada para los subtítulos integrados de %s",
|
||||
"< Back to subtitle options for: %s": "< Regresar a las opciones de subtítulos para: %s",
|
||||
"Remove last applied mod (%s)": "Quitar la última modificación realizada (%s)",
|
||||
"none": "ninguno",
|
||||
"Manage applied mods": "Gestionar modificaciones realizadas",
|
||||
"Reapply applied mods": "Reaplicar modificaciones realizadas",
|
||||
"Restore original version": "Restaurar versión original",
|
||||
"< Back to subtitle modification menu": "< Regresar al menú de modificación de subtítulos",
|
||||
"subs constantly getting faster": "los subtítulos van más rápido constantemente",
|
||||
"subs constantly getting slower": "los subtítulos van más lento constantemente",
|
||||
"< Back to subtitle modifications": "< Regresar a las modificaciones de subtítulos",
|
||||
"< Back to unit selection": "< Regresar a la selección",
|
||||
"Subtitle Language (1)": "Idioma del Subtítulo (1)",
|
||||
"Subtitle Language (2)": "Idioma del Subtítulo (2)",
|
||||
"Subtitle Language (3)": "Idioma del Subtítulo (3)",
|
||||
"Additional Subtitle Languages (use ISO-639-1 codes; comma-separated)": "Idiomas adicionales de subtítulos (use códigos ISO-639-1 separados por comas)",
|
||||
"Only download foreign/forced subtitles": "Descargar sólo subtítulos extranjeros o forzados",
|
||||
"Display languages with country attribute as ISO 639-1 (e.g. pt-BR = pt)": "Mostar idiomas con atributos de país tipo ISO 639-1 (p. ej. pt-BR = pt)",
|
||||
"Treat languages with country attribute as ISO 639-1 (e.g. don't download pt-BR if pt subtitle exists)": "Tratar idiomas con atributos de país como ISO 639-1 (es decir, no descargar pt-BR si existe un subtítulo pt)",
|
||||
"Restrict to one language (skips adding \".lang.\" to the subtitle filename; only uses \"Subtitle Language (1)\")": "Restringir a un idioma (evita añadir \".lang\" al nombre del fichero del subtítulo; solo usa el \"Idioma del Subtítulo (1)\"",
|
||||
"Embedded subtitles: Treat \"Undefined\" (und) as language 1": "Subtítulos integrados: tratar \"Undefined\" (und) como primer idioma",
|
||||
"I rename my files using": "Renombro los archivos usando",
|
||||
"Retrieve original filename from .file_info/file_info index files (see wiki)": "Obtener el nombre original del archivo del los archivos tipo \"file_info\" (ver wiki)",
|
||||
"Sonarr URL (add URL base if configured)": "URL de Sonarr (añadir la URL base si está configurada)",
|
||||
"Sonarr API key": "Clave API de Sonarr",
|
||||
"Radarr URL (add URL base if configured, min. version: 0.2.0.897)": "URL de Radar (añadir la URL base si está configurada, versión mínima: 0.2.0.897)",
|
||||
"Radarr API key": "Clave API de Radarr",
|
||||
"Provider: Enable OpenSubtitles": "Proveedor: Habilitar OpenSubtitles",
|
||||
"Opensubtitles Username": "Usuario de OpenSubtitles",
|
||||
"Opensubtitles Password": "Contraseña de OpenSubtitles",
|
||||
"OpenSubtitles VIP? (ad-free subs, 1000 subs/day, no-cache VIP server: http://v.ht/osvip)": "¿Es usuario VIP de OpenSubtitles? (http://v.ht/osvip)",
|
||||
"Provider: Enable Podnapisi.NET": "Proveedor: Habilitar Podnapisi.net",
|
||||
"Provider: Enable Titlovi.com": "Proveedor: Habilitar Titlovi.com",
|
||||
"Provider: Enable Addic7ed": "Proveedor: Habilitar Addic7ed",
|
||||
"Addic7ed Username": "Usuario de Addic7ed",
|
||||
"Addic7ed Password": "Contraseña de Addic7ed",
|
||||
"Addic7ed: boost score (if requirements met)": "Addic7ed: puntuación incentivada si cumple los requisitos",
|
||||
"Addic7ed: Use random user agents": "Addic7ed: Usar agentes aleatorios",
|
||||
"Provider: Enable Legendas TV (mostly pt-BR; UNRAR NEEDED)": "Proveedor: Habilitar Legendas TV (sobre todo pt-BR, requiere UNRAR)",
|
||||
"Legendas TV Username": "Usuario de Legendas TV",
|
||||
"Legendas TV Password": "Contraseña de Legendas TV",
|
||||
"Provider: Enable TVsubtitles.net": "Proveedor: Habilitar TVsubtitles.net",
|
||||
"Provider: Enable NapiProjekt.pl (Polish)": "Proveedor: Habilitar NapiProjekt.pl (polaco)",
|
||||
"Provider: Enable SubScene (TV shows)": "Proveedor: Habilitar SubScene (series de tv)",
|
||||
"Provider: Enable hosszupuskasub.com (Hungarian)": "Proveedor: Habilitar hosszupuskasub.com (húngaro)",
|
||||
"Provider: Enable aRGENTeaM (Spanish)": "Proveedor: Habilitar aRGENTeaM (español)",
|
||||
"Search enabled providers simultaneously (multithreading)": "Buscar proveedores habilitados simultáneamente (multihebra)",
|
||||
"Automatically extract and use embedded subtitles upon media addition (with configured default mods)": "Extraer automáticamente y usar subtítulos integrados al añadir contenidos (con las modificaciones configuradas por defecto)",
|
||||
"After automatic extraction of embedded subtitles, also immediately search for available subtitles?": "Después de extraer automáticamente subtítulos integrados ¿buscar inmediatamente subtítulos disponibles?",
|
||||
"Don't search for subtitles of a language if there are embedded subtitles inside the media file (MKV/MP4)?": "¿No buscar subtítulos de un idioma si existen subtítulos integrados dentro del archivo (MKV/MP4)?",
|
||||
"Don't search for subtitles of a language if they already exist on the filesystem (metadata/filesystem)?": "¿No buscar subtítulos de un idioma si ya existen en el sistema de ficheros o en los metadatos?",
|
||||
"How strict should these subtitles existing on the filesystem be detected?": "¿Cómo de estricta debe ser la detección de estos subtítulos en el sistema de archivos?",
|
||||
"Include non-text subtitle formats (anything else than .srt/.ssa/.ass/.vtt; embedded or external) in the above?": "¿Incluir subtítulos que no sean tipo texto (diferentes a .srt/.ssa/.ass/.vtt; integrados o externos) en lo anterior?",
|
||||
"Minimum score for TV (min: 240, def/sane: 337, min-ideal: 352; see http://v.ht/szscores)": "Puntuación mínima para Series de TV (mínimo: 240, sano: 337, recomendable: 352; ver http://v.ht/szscores)",
|
||||
"Minimum score for movies (min: 60, def/sane: 69, min-ideal: 82; see http://v.ht/szscores)": "Puntuación mínima para Películas (mínimo: 60, sano: 69, recomendado: 82; ver \nhttp://v.ht/szscores)",
|
||||
"Download hearing impaired subtitles.": "Descargar subtítulos para sordos",
|
||||
"Remove Hearing Impaired tags from downloaded subtitles": "Eliminar etiquetas para sordos de los subtítulos descargados",
|
||||
"Remove style tags from downloaded subtitles (bold, italic, underline, colors, ...)": "Eliminar etiquetas y estilos de los subtítulos descargados (negrita, cursiva, subrayado, colores, ...)",
|
||||
"Fix common issues in subtitles": "Arreglar problemas comunes en los subtítullos",
|
||||
"Fix common OCR errors in downloaded subtitles": "Arreglar errores comunes de OCR en los subtítulos descargados",
|
||||
"Reverse punctuation in RTL languages (heb)": "Invertir puntuación en idiomas RTL (heb)",
|
||||
"Change colors of subtitles to": "Cambiar color de los subtítulos a",
|
||||
"Store subtitles next to media files (instead of metadata)": "Almacenar los subtítulos junto a los archivos de video (en lugar de en los metadatos)",
|
||||
"Subtitle formats to save (non-SRT only works if the previous option is enabled)": "Formatos de subtítulos a guardar (lo que no sean SRT sólo funcionan si la opción anterior está habilitada)",
|
||||
"Subtitle Folder (\"current folder\" is the folder the current media file lives in)": "Carpeta de subtítulos (\"carpeta actual\" es la carpeta donde los archivos de video están guardados)",
|
||||
"Custom Subtitle folder (overrides \"Subtitle Folder\"; computes to real paths)": "Carpeta de subtítulos personalizada (sustituye a la \"Carpeta de subtítulos\", calculada con las rutas reales)",
|
||||
"Fall back to metadata storage if filesystem storage failed": "Activar como segunda opción el almacenamiento en los metadatos si falla en el sistema de almacenamiento",
|
||||
"Set subtitle file permissions to (integer, e.g.: 0775)": "Fijar los permisos de los subtítulos a (nº entero, p. ej.: 0775)",
|
||||
"Automatically delete leftover/unused (externally saved) subtitles": "Borrar automáticamente los subtítulos sin uso (grabados externamente)",
|
||||
"On media playback: search for missing subtitles (refresh item)": "Al reproducir un vídeo: buscar subtítulos automáticamente",
|
||||
"Scheduler: Periodically search for recent items with missing subtitles": "Programador: Buscar subtítulos periódicamente para elementos recién añadidos que no tengan",
|
||||
"Scheduler: Item age to be considered recent": "Programador: Antigüedad del elemento para ser considerado como Reciente",
|
||||
"Scheduler: Recent items to consider per library": "Programador: Elementos recientes a considerar por biblioteca",
|
||||
"Scheduler: Periodically search for better subtitles": "Programador: Buscar periódicamente Mejores Subtítulos",
|
||||
"Scheduler: Days to search for better subtitles (max: 30 days)": "Programador: Días a buscar Mejores Subtítulos (máximo 30)",
|
||||
"Scheduler: Don't search for better subtitles if the item's air date is older than": "Programador: No buscar Mejores Subtítulos si la fecha de emisión del elemento es mayor que",
|
||||
"Scheduler: Overwrite manually selected subtitles when better found": "Programador: Sobreescribir subtítulos seleccionados manualmente cuando se encuentren Mejores Subtítulos",
|
||||
"Scheduler: Overwrite subtitles with non-default subtitle modifications when better found": "Programador: Sobreecribir subtítulos con modificaciones que no sean por defecto cuando se encuentren Mejores Subtítulos",
|
||||
"History: amount of items to store historical data for": "Historial: Cantidad de elementos de los que se guardará datos históricos",
|
||||
"How many download tries per subtitle (on timeout or error)": "¿Cuántos intentos se deben realizar por subtítulos, cuando se produzca un error o supere el tiempo de espera?",
|
||||
"Ignore folders (with \"subzero.ignore/.subzero.ignore/.nosz\" files in them)": "Ignorar carpetas (con archivos tipo \"subzero.ignore/.subzero.ignore/.nosz\" en ellas)",
|
||||
"Ignore anything in the following paths (comma-separated)": "Ignorar cualquier cosa en las siguientes rutas, separadas por comas",
|
||||
"Sub-Zero mode": "Modo Sub-Zero",
|
||||
"Access PIN (any amount of numbers, 0-9)": "PIN de acceso (cualquier cantidad de números, 0-9)",
|
||||
"Access PIN valid for minutes": "Minutos de validez para el PIN de acceso ",
|
||||
"Use PIN to restrict access to (needs plugin or PMS restart)": "Usar PIN para restringir el acceso (necesitar reinicio)",
|
||||
"Call this executable upon successful subtitle download (see Wiki for details)": "Llamar a este ejecutable tras las descarga correcta de un subtítulo (ver detalles en la Wiki)",
|
||||
"Check for correct folder permissions of every library on plugin start": "Comprobar permisos de carpeta para cada biblioteca al iniciar el plugin",
|
||||
"Use new style caching (for subliminal)": "Usar nuevo estilo de caché (para Subliminal)",
|
||||
"Low impact mode (for remote filesystems)": "Modo de bajo impacto (para sistemas de archivo remotos)",
|
||||
"Timeout for API requests sent to the PMS": "Tiempo de espera para peticiones API enviadas al PMS",
|
||||
"HTTP proxy to use for providers (supports credentials)": "Proxy HTTP a usar para proveedores (permite credenciales)",
|
||||
"How verbose should the logging be?": "¿Cómo de extenso debe ser el detalle del log?",
|
||||
"How many log backups to keep?": "¿Cuántas copias del log quiere mantener?",
|
||||
"Log subtitle modification (debug)": "Anotar en el log las modificaciones de subtítulos (en pruebas)",
|
||||
"Log to console (for development/debugging)": "Anotar log en consola (para desarrollar o depurar)",
|
||||
"Collect anonymous usage statistics": "Recolectar estadísticas anónimas de uso",
|
||||
"Sonarr/Radarr (fill api info below)": "Sonarr/Radarr (rellenar información de la API)",
|
||||
"Filebot": "Filebot",
|
||||
"Sonarr/Radarr/Filebot": "Sonarr/Radarr/Filebot",
|
||||
"Symlink to original file": "Enlace simbólico al archivo original",
|
||||
"I keep the original filenames": "Mantener los nombres de archivo originales",
|
||||
"none of the above": "ninguno de los anteriores",
|
||||
"exact: media filename match": "exacta: coincidencia por nombre de archivo del vídeo",
|
||||
"loose: filename contains media filename": "parcial: el nombre del vídeo aparece en el nombre del archivo",
|
||||
"any": "cualquiera",
|
||||
"prefer": "preferir",
|
||||
"don't prefer": "no preferir",
|
||||
"force HI": "forzar Alta",
|
||||
"force non-HI": "forzar No Alta",
|
||||
"don't change": "sin cambios",
|
||||
"white": "blanco",
|
||||
"light-grey": "gris claro",
|
||||
"red": "rojo",
|
||||
"green": "verde",
|
||||
"yellow": "amarillo",
|
||||
"blue": "azul",
|
||||
"magenta": "magenta",
|
||||
"cyan": "cyan",
|
||||
"black": "negro",
|
||||
"dark-red": "rojo-oscuro",
|
||||
"dark-green": "verde-oscuro",
|
||||
"dark-yellow": "amarillo-oscuro",
|
||||
"dark-blue": "azul-oscuro",
|
||||
"dark-magenta": "magenta-oscuro",
|
||||
"dark-cyan": "cyan-oscuro",
|
||||
"dark-grey": "gris-oscuro",
|
||||
"current folder": "carpeta actual",
|
||||
"never": "nunca",
|
||||
"current media item": "elemento multimedia actual",
|
||||
"next episode (series)": "siguiente episodio (serie)",
|
||||
"hybrid: current item or next episode": "híbrido: elemento actual o siguiente episodio",
|
||||
"hybrid-plus: current item and next episode": "híbrido-plus: elemento actual y siguiente episodio",
|
||||
"every 6 hours": "cada 6 horas",
|
||||
"every 12 hours": "cada 12 horas",
|
||||
"every 24 hours": "cada 24 horas",
|
||||
"1 days": "1 día",
|
||||
"2 days": "2 días",
|
||||
"3 days": "3 días",
|
||||
"4 days": "4 días",
|
||||
"1 weeks": "1 semana",
|
||||
"2 weeks": "2 semanas",
|
||||
"3 weeks": "3 semanas",
|
||||
"4 weeks": "4 semanas",
|
||||
"5 weeks": "5 semanas",
|
||||
"6 weeks": "6 semanas",
|
||||
"12 weeks": "12 semanas",
|
||||
"don't limit": "no limitar",
|
||||
"1 year": "1 año",
|
||||
"2 years": "2 años",
|
||||
"3 years": "3 años",
|
||||
"4 years": "4 años",
|
||||
"5 years": "5 años",
|
||||
"6 years": "5 años",
|
||||
"7 years": "7 años",
|
||||
"8 years": "8 años",
|
||||
"9 years": "9 años",
|
||||
"10 years": "10 años",
|
||||
"only agent": "sólo agente",
|
||||
"disabled": "deshabilitado",
|
||||
"advanced menu": "menú avanzado",
|
||||
"CRITICAL": "CRÍTICO",
|
||||
"ERROR": "ERROR",
|
||||
"WARNING": "ADVERTENCIA",
|
||||
"INFO": "INFO",
|
||||
"DEBUG": "DEPURACIÓN",
|
||||
"Force-find subtitles: %(item_title)s": "Forzar búsqueda de subtítulos: %(item_title)s",
|
||||
"File %(file_part_index)s: ": "Archivo %(file_part_index)s:␣",
|
||||
"%(part_summary)sNo current subtitle in storage": "%(part_summary)sNo hay un subtítulo guardado",
|
||||
"%(part_summary)sCurrent subtitle: %(provider_name)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "%(part_summary)sSubtítulos actual: %(provider_name)s (añadido: %(date_added)s, %(mode)s), Idioma: %(language)s, Puntuación: %(score)i, Almacenamiento: %(storage_type)s",
|
||||
"%(part_summary)sManage %(language)s subtitle": "%(part_summary)Gestiona subtítulos en %(language)s",
|
||||
"%(part_summary)sList %(language)s subtitles": "%(part_summary)Muestra subtítulos en %(language)s",
|
||||
"%(part_summary)sEmbedded subtitles (%(languages)s)": "%(part_summary)sSubtítulos integrados en (%(languages)s)",
|
||||
"Select active %(language)s subtitle": "Selecciona subtítulos activos en %(language)s subtitle",
|
||||
"%(count)d subtitles in storage": "%(count)d subtítulos almacenados",
|
||||
"List available %(language)s subtitles": "Muestra subtítulos disponibles en %(language)s",
|
||||
"Modify current %(language)s subtitle": "Modificar el subtítulo actual en %(language)s",
|
||||
"Currently applied mods: %(mod_list)s": "Modificaciones aplicadas actualmente: %(mod_list)s",
|
||||
"Blacklist current %(language)s subtitle and search for a new one": "Bloquear el actual subtítulo en %(language)s y buscar uno nuevo ",
|
||||
"Manage blacklist (%(amount)s contained)": "Gestionar lista de bloqueados (total: %(amount)s) ",
|
||||
"%(current_state)s%(subtitle_name)s, Score: %(score)s": "%(current_state)s%(subtitle_name)s, Puntuación: %(score)s",
|
||||
"Current: ": "Actual: ",
|
||||
"Stored: ": "Almacenado: ",
|
||||
"by %(release_group)s": "por %(release_group)s",
|
||||
"Current: %(provider_name)s (%(score)s) ": "Actual: %(provider_name)s (%(score)s)␣",
|
||||
"Search for %(language)s subs (%(video_data)s)": "Buscar subtítulos en %(language)s (%(video_data)s)",
|
||||
"%(current_info)sFilename: %(filename)s": "%(current_info)sNombre de archivo: %(filename)s",
|
||||
"Searching for %(language)s subs (%(video_data)s), refresh here ...": "Buscando subtítulos en %(language)s para (%(video_data)s), actualice aquí...",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: %(media_fps)s)": "␣(FPS incorrectos, sub: %(subtitle_fps)s, archivo: %(media_fps)s)",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: unknown, low impact mode)": " ␣(FPS incorrectos, sub: %(subtitle_fps)s, archivo: desconocido, modo de bajo impacto)\n",
|
||||
"%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s": "%(blacklisted_state)s%(current_state)s: %(provider_name)s, puntuación: %(score)s%(wrong_fps_state)s",
|
||||
"Release: %(release_info)s, Matches: %(matches)s": "Release: %(release_info)s, Coincidencia: %(matches)s",
|
||||
"Downloading subtitle for %(title_or_id)s": "Descargando subtítulo para %(title_or_id)s",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s with default mods": "Extraer flujo %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s con las modificaciones por defecto",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s": "Extraer flujo %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s",
|
||||
"%(provider_name)s, %(subtitle_id)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "%(provider_name)s, %(subtitle_id)s (added: %(date_added)s, %(mode)s), Idioma: %(language)s, Puntuación: %(score)i, Almacenamiento: %(storage_type)s",
|
||||
"Insufficient permissions on library %(title)s, folder: %(path)s": "Permisos insuficientes en biblioteca %(title)s, carpeta: %(path)s",
|
||||
"Running: %(items_done)s/%(items_searching)s (%(percentage)s%%)": "Ejecutando: %(items_done)s/%(items_searching)s (%(percentage)s%%)",
|
||||
"Display ignore list (%(ignored_count)d)": "Mostrar lista de ignorados (%(ignored_count)d)",
|
||||
"%(throttled_provider)s until %(until_date)s (%(reason)s)": "%(throttled_provider)s hasta %(until_date)s (%(reason)s)",
|
||||
"Extracting subtitle %(stream_index)s of %(filename)s": "Extrayendo subtítulos %(stream_index)s de %(filename)s",
|
||||
"Extract missing %(language)s embedded subtitles": "Extraer subtítulos integrados faltantes en %(language)s",
|
||||
"Extract and activate %(language)s embedded subtitles": "Extraer y activar subtítulos integrados en %(language)s",
|
||||
"None": "Ninguno",
|
||||
"Idle": "Parado",
|
||||
"%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)": "%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)",
|
||||
"Adjust by %(time_and_unit)s": "Ajustar a %(time_and_unit)s",
|
||||
"added: %(date_added)s, %(mode)s, Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "añadido: %(date_added)s, %(mode)s, Idioma: %(language)s, Puntuación: %(score)i, Almacenamiento: %(storage_type)s\n",
|
||||
"Remove: %(mod_name)s": "Quitar: %(mod_name)s",
|
||||
"%(class_name)s: Subtitle download failed (%(item_id)s)": "%(class_name)s: Fallo la descarga del subtítulos(%(item_id)s)",
|
||||
"agent + interface": "agente + interfaz",
|
||||
"only interface": "sólo interfaz",
|
||||
"interface": "interfaz",
|
||||
"Shows the current on deck items and allows you to individually (force-) refresh their metadata/subtitles.": "Mostrar los elementos En Progreso y permite actualiza individualmente los metadatos y subtítulos",
|
||||
"Lists items with missing subtitles. Click on \"Find recent items with missing subs\" to update list": "Muestra elementos sin subtítulos. Haz clic en \"Buscar elementos recientes sin subtítulos\" para actualizar la lista",
|
||||
"Add %(kind)s %(title)s to the ignore list": "Añadir %(kind)s %(title)s a la lista de ignorados",
|
||||
"Remove %(kind)s %(title)s from the ignore list": "Eliminar %(kind)s %(title)s de la lista de ignorados",
|
||||
"%(title)s added to the ignore list": "%(title)s añadido a la lista de ignorados",
|
||||
"%(title)s removed from the ignore list": "%(title)s eliminados de la lista de ignorados",
|
||||
"Triggering refresh for %(title)s": "Actualización lanzada para %(title)s",
|
||||
"Triggering forced refresh for %(title)s": "Actualización forzada para%(title)s",
|
||||
"Refresh of item %(item_id)s triggered": "Actualización de elementos %(item_id)s lanzada",
|
||||
"Forced refresh of item %(item_id)s triggered": "Actualización de elementos %(item_id)s forzada",
|
||||
"Ignore %(kind)s \"%(title)s\"": "Ignorar %(kind)s \"%(title)s\"",
|
||||
"Un-ignore %(kind)s \"%(title)s\"": "Dejar de ignorar %(kind)s \"%(title)s\"",
|
||||
"Refreshing %(title)s": "Actualizando %(title)s",
|
||||
"Force-refreshing %(title)s": "Actualizando forzosamente %(title)s",
|
||||
"Extracts the not yet extracted embedded subtitles of all episodes for the current season with all configured default modifications": "Extraer los subtítulos integrados no extraídos de todos los episodios de la actual temporada con las modificaciones configuradas por defecto",
|
||||
"Extracts embedded subtitles of all episodes for the current season with all configured default modifications": "Extraer los subtítulos integrados de todos los episodios de la actual temporada con las modificaciones configuradas por defecto",
|
||||
"Refreshes %(the_movie_series_season_episode)s, possibly searching for missing and picking up new subtitles on disk": "Actualiza %(the_movie_series_season_episode)s, posiblemente buscando y tomando nuevos subtítulos del disco",
|
||||
"the movie": "la película",
|
||||
"the series": "la serie",
|
||||
"the episode": "el episodio",
|
||||
"the season": "la temporada",
|
||||
"Change the color of the subtitle": "cambiar el color del subtítulo",
|
||||
"Adds the requested color to every line of the subtitle. Support depends on player.": "Añade el color seleccionado a todos los subtítulos. El funcionamiento depende del reproductor.",
|
||||
"Basic common fixes": "Arreglos comunes básicos",
|
||||
"Fix common and whitespace/punctuation issues in subtitles": "Arregla problemas comunes, espacios y puntuación en los subtítulos",
|
||||
"Remove all style tags": "Elimina toda las etiquetas de estilo",
|
||||
"Removes all possible style tags from the subtitle, such as font, bold, color etc.": "Elimina todas las posibles etiquetas de estilo del subtítulo, como fuente, negrita, color, etc.",
|
||||
"Reverse punctuation in RTL languages": "Invertir puntuación en idiomas RTL",
|
||||
"Some playback devices don't properly handle right-to-left markers for punctuation. Physically swap punctuation. Applicable to languages: hebrew": "Algunos reproductores no saben tratar correctamente la puntuación de derecha a izquierda, que aplica al idioma hebreo",
|
||||
"Change the FPS of the subtitle": "Cambiar los FPS del subtítulo",
|
||||
"Re-syncs the subtitle to the framerate of the current media file.": "Resincronizar los subtítulos a la tasa de frames del archivo original",
|
||||
"Remove Hearing Impaired tags": "Eliminar etiquetas para sordos",
|
||||
"Removes tags, text and characters from subtitles that are meant for hearing impaired people": "Eliminar etiquetas, textos y caracteres de los subtítulos puestos para sordos",
|
||||
"Fix common OCR issues": "Arreglar errores OCR comunes",
|
||||
"Fix issues that happen when a subtitle gets converted from bitmap to text through OCR": "Arreglar fallos que ocurren cuando los subtítulos se convierten de imagen a texto por un OCR",
|
||||
"Change the timing of the subtitle": "Cambiar el \"timing\" de los subtítulos",
|
||||
"Adds or substracts a certain amount of time from the whole subtitle to match your media": "Añadir o quitar cierta cantidad de tiempo de todo el subtítulos para emparejarlo con el vídeo",
|
||||
"Available": "Disponible",
|
||||
"Current": "Actual",
|
||||
"Custom path to advanced_settings.json": "Ruta personalizada para advanced_settings.json"
|
||||
}
|
||||
@@ -0,0 +1,400 @@
|
||||
{
|
||||
"sq": "Albán",
|
||||
"ar": "Arab",
|
||||
"be": "Fehérorosz",
|
||||
"bs": "Bosnyák",
|
||||
"bg": "Bolgár",
|
||||
"ca": "Katalán",
|
||||
"zh": "Kínai",
|
||||
"hr": "Horváth",
|
||||
"cs": "Cseh",
|
||||
"da": "Dán",
|
||||
"nl": "Holland",
|
||||
"en": "Angol",
|
||||
"et": "Észt",
|
||||
"fa": "Perzsa",
|
||||
"fi": "Finn",
|
||||
"fr": "Francia",
|
||||
"de": "Németh",
|
||||
"el": "Görög",
|
||||
"he": "Héber",
|
||||
"hi": "Hindu",
|
||||
"hu": "Magyar",
|
||||
"is": "Izlandi",
|
||||
"id": "Indonéz",
|
||||
"it": "Olasz",
|
||||
"ja": "Japán",
|
||||
"ko": "Koreai",
|
||||
"lv": "Lett",
|
||||
"lt": "Litván",
|
||||
"mk": "Makedón",
|
||||
"ms": "Maláj",
|
||||
"no": "Norwég",
|
||||
"pl": "Lengyel",
|
||||
"pt": "Portugál",
|
||||
"pt-br": "Portugál (Brazil)",
|
||||
"ro": "Román",
|
||||
"ru": "Orosz",
|
||||
"sr": "Szerb",
|
||||
"sr-cyrl": "Szerb (Cirill)",
|
||||
"sr-latn": "Szerb (Latin)",
|
||||
"sk": "Szlovák",
|
||||
"sl": "Szlovén",
|
||||
"es": "Spanyol",
|
||||
"sv": "Svéd",
|
||||
"th": "Thai",
|
||||
"tr": "Török",
|
||||
"uk": "Ukrán",
|
||||
"vi": "Vietnámi",
|
||||
"Internal stuff, pay attention!": "Belső dolog, figyelem!",
|
||||
"Advanced": "Haladó",
|
||||
"advanced": "haladó",
|
||||
"Enter PIN": "Írd be a PIN-t",
|
||||
"The owner has restricted the access to this menu. Please enter the correct pin": "A tulajdonos korlátozta a hozzáférést a menühöz. Kérlek írd be a jó pint-t",
|
||||
"Restart the plugin": "Bővítmény újrainditása",
|
||||
"Get my logs (copy the appearing link and open it in your browser, please)": "Logok lekérése (másold ki a megjelenő linket és nyisd meg a böngészőben)",
|
||||
"Copy the appearing link and open it in your browser, please": "Másold ki a megjelenő linket és nyisd meg a böngészőben",
|
||||
"Trigger find better subtitles": "Jobban passzoló feliratok keresése",
|
||||
"Skip next find better subtitles (sets last run to now)": "Ugrás a következőre, keress jobb feliratot\n",
|
||||
"Trigger subtitle storage maintenance": "Feliarttár karbantartás indítása",
|
||||
"Trigger subtitle storage migration (expensive)": "Felirattár migrálás indítása (költséges)",
|
||||
"Trigger cache maintenance (refiners, providers and packs/archives)": "Gyorsitótár karbantartás indítása (refiners, providers and packs/archives)",
|
||||
"Apply configured default subtitle mods to all (active) stored subtitles": "Alapértelmezett módosítások alkalmazása az összes (aktív) feliratra",
|
||||
"Re-Apply mods of all stored subtitles": "Módosítások újra alkalmazása az összes feliratra",
|
||||
"Log the plugin's scheduled tasks state storage": "Bővítmény ütemezett feladatainak logolása",
|
||||
"Log the plugin's internal ignorelist storage": "A bővítmény mellőzendő listájának naplózása.",
|
||||
"Log the plugin's complete state storage": "Bővítmény teljes állapotának naplózása",
|
||||
"Reset the plugin's scheduled tasks state storage": "Bővítmény ütemezett feladatainak visszaállítása",
|
||||
"Reset the plugin's internal ignorelist storage": "A bővítmény mellőzendő listájának alaphelyzetbe állítása",
|
||||
"Invalidate Sub-Zero metadata caches (subliminal)": "Sub-Zero metaadat tár érvénytelenítése (subliminal)",
|
||||
"Reset provider throttle states": "Szolgáltató túlterhelési állapotának alaphelyzetbe állítása",
|
||||
"Restarting the plugin": "Bővítmény újraindítása",
|
||||
"Restart triggered, please wait about 5 seconds": "Újraindítás folyamatban, várj körülbelül 5 másodpercet",
|
||||
"Reset subtitle storage": "Felirattár alaphelyzetbe állitása",
|
||||
"Are you sure?": "Biztos vagy benne?",
|
||||
"Are you really sure?": "Teljesen biztos vagy benne?",
|
||||
"Success": "Sikerült",
|
||||
"FindBetterSubtitles triggered": "Jobb felirat keresése elindítva",
|
||||
"FindBetterSubtitles skipped": "Jobb felirat keresése kihagyva",
|
||||
"SubtitleStorageMaintenance triggered": "Felirat tár karbantartás elindítva",
|
||||
"MigrateSubtitleStorage triggered": "Felirat tár migrálás elindítva",
|
||||
"TriggerCacheMaintenance triggered": "Gyorsítótár karbantartás elindítva",
|
||||
"This may take some time ...": "Ez egy kis időt vehet igénybe ...",
|
||||
"Download Logs": "Naplófájlok letöltése",
|
||||
"Sorry, feature unavailable": "Sajnálom, ez a funkció nem elérhető",
|
||||
"Universal Plex token not available": "Univerzális Plex Token nem elérhető",
|
||||
"Copy this link and open this in your browser, please": "Kérlek másold ki ezt a linket és nyisd meg a böngésződben",
|
||||
"Cache invalidated": "Gyorsítótár érvénytelenítse",
|
||||
"Enter PIN number ": "Írd be a PIN kódot",
|
||||
"PIN correct": "PIN elfogadva",
|
||||
"Reset": "Alaphelyzetbe állitás",
|
||||
"Menu locked": "Menü zárolva",
|
||||
"Provider throttles reset": "Szolgáltató túlterhelés védelem apahelyzetbe állítása",
|
||||
"Information Storage (%s) reset": "Információs tár ( %s) alaphelyzetbe állítása",
|
||||
"Information Storage (%s) logged": "Információs tár (%s) naplózva",
|
||||
"Plex didn't return any information about the item, please refresh it and come back later": "A plex nem adott vissza semmi információt az elemről. Frissítsd és gyere vissza később.",
|
||||
"Item not found: %s!": "Elem nem található: %s!",
|
||||
"< Back to %s": "< Vissza %s",
|
||||
"Back to %s > %s": "Vissza %s > %s",
|
||||
"Refresh: %s": "Frissítés: %s",
|
||||
"Issues a forced refresh, ignoring known subtitles and searching for new ones": "Kényszerített frissítés, figyelmen kívül hagyja az ismert feliratokat és újakat keres",
|
||||
"Extract and activate embedded subtitle streams": "Beágyazott felirat kitömörítése és aktiválása",
|
||||
"Inspect currently blacklisted subtitles": "Feketelistás feliratok megvizsgálása",
|
||||
"Subtitle saved to disk": "Felirat lemezre mentve",
|
||||
"Remove subtitle from blacklist": "Felirat eltávolítása a feketelistáról",
|
||||
"No subtitles found": "Felirat nem található",
|
||||
" (unknown)": " (ismeretlen)",
|
||||
" (forced)": " (kiegészítő felirat)",
|
||||
"Extracting of embedded subtitle %s of part %s:%s triggered": "Beágyazott felirat %s of part %s:%s kitömörítése elindítva",
|
||||
"Insufficient permissions": "Nincs elegendő engedély",
|
||||
"I'm not enabled!": "Bővitmény nincs bekapcsolva",
|
||||
"Please enable me for some of your libraries in your server settings; currently I do nothing": "Aktiválj valamelyik könyvtáradhoz a server beállításoknál.\njelenleg semmit nem csinálok.",
|
||||
"Working ... refresh here": "Folyamatban ... Frissíts it",
|
||||
"Current state: %s; Last state: %s": "Jelenlegi állapot: %s; Utolsó állapot: %s",
|
||||
"On-deck items": "Lejátszás alatt lévő elemek",
|
||||
"Shows the %s recently played items and allows you to individually (force-) refresh their metadata/subtitles.": "Mutassa a %s most játszott elemeket és adjon lehetőséget a metaadataik egyenként történő frissítésére,",
|
||||
"Recently-added items": "Mostanában hozzáadott elemek",
|
||||
"Recently played items": "Mostanában játszottak elemek",
|
||||
"Shows the recently added items per section.": "Mostanában hozzáadott elemek mutatása könyvtáranként",
|
||||
"Show recently added items with missing subtitles": "Mutasd a mostanában hozzáadott felirat nélküli elemeket",
|
||||
"Browse all items": "Összes elme böngészése",
|
||||
"Go through your whole library and manage your ignore list. You can also (force-) refresh the metadata/subtitles of individual items.": "Végig megy a teljes könyvtáron és kezeli a mellőzendők listáját. Lehetőség van az egyes metaadatok és feliratok (erőltetett-) frissítésre is az egyes elemeknél.",
|
||||
"Last run: %s; Next scheduled run: %s; Last runtime: %s": "Utolsó futás: %s, Következő időzített futás: %s, Utolsó futás hossza: %s",
|
||||
"Search for missing subtitles (in recently-added items, max-age: %s)": "Hiányzó feliratok keresése (újonnan hozzáadott elemek, max. kor: %s)",
|
||||
"Automatically run periodically by the scheduler, if configured. %s": "Automatikus futtatás az ütemező által konfigurálva. %s",
|
||||
"Show the current ignore list (mainly used for the automatic tasks)": "Mellőzendő lista megjelenése (elsősorban automatikus feladatokhoz)",
|
||||
"History": "Előzmények",
|
||||
"Show the last %i downloaded subtitles": "Mutasd az utolsó %i letöltött feliratot",
|
||||
"Refresh": "Frissités",
|
||||
"Re-lock menu(s)": "menü(k) újrazárolása",
|
||||
"Enabled the PIN again for menu(s)": "PIN újra engedélyezése a menü(k)-höz",
|
||||
"Throttled providers: %s": "Túlterhelt szolgáltatók: %s",
|
||||
"Advanced functions": "Fejlett funkciók",
|
||||
"Use at your own risk": "Csak saját felelősségre használd",
|
||||
"Items On Deck": "Lejátszás alatt álló elemek",
|
||||
"Recently Played": "Mostanában játszott elemek",
|
||||
"Items with missing subtitles": "Hiányzó felirattal rendelkező elemek",
|
||||
"Find recent items with missing subtitles": "Mostanában hozzáadott hiányzó felirattal rendelkező elemek keresése",
|
||||
"Updating, refresh here ...": "Frissítés folyamatban, frissíts itt ...",
|
||||
"Missing: %s": "Hiányzó: %s",
|
||||
"Didn't change the ignore list": "Ne változzon a mellőzendő lista ",
|
||||
"Sections": "Könyvtárak",
|
||||
"All": "Mind",
|
||||
"show": "mutasd",
|
||||
"movie": "Film",
|
||||
"<< Back to home": "<< Vissza a kezdőképernyőre",
|
||||
"Auto-Find subtitles: %s": "Felirat autó keresése: %s",
|
||||
"Extracting of embedded subtitles for %s triggered": "Beágyazott felirat kitömörítése a %s -hez elindítva",
|
||||
"< Back to subtitle options for: %s": "< Vissza a felirat beállításokhoz: %s",
|
||||
"Remove last applied mod (%s)": "Utoljára alkalmazott (%s) módosítás visszavonása",
|
||||
"none": "egyik sem",
|
||||
"Manage applied mods": "Használatban lévő módositások kezelése",
|
||||
"Reapply applied mods": "Módosítások újra alkalmazása",
|
||||
"Restore original version": "Eredeti verzió visszaállítása",
|
||||
"< Back to subtitle modification menu": "< Vissza a felirat módosítás menübe",
|
||||
"subs constantly getting faster": "feliratok folyamatosan gyorsabbak lesznek",
|
||||
"subs constantly getting slower": "feliratok folyamatosan lassabbak lesznek",
|
||||
"< Back to subtitle modifications": "< Vissza a felirat módosításokhoz: %s",
|
||||
"< Back to unit selection": "< Vissza a unit választáshoz",
|
||||
"Subtitle Language (1)": "Felirat Nyelve (1)",
|
||||
"Subtitle Language (2)": "Felirat Nyelve (2)",
|
||||
"Subtitle Language (3)": "Felirat Nyelve (3)",
|
||||
"Additional Subtitle Languages (use ISO-639-1 codes; comma-separated)": "További felirat nyelvei (ISO-639-1 kódok vesszővel elválasztva)",
|
||||
"Only download foreign/forced subtitles": "Csak külföldi / kényszerített feliratok letöltése",
|
||||
"Display languages with country attribute as ISO 639-1 (e.g. pt-BR = pt)": "Nyelvek megjelentetési az ISO 639-1 es kódolásnak megfelelő ország attribútummal (pl. pt-BR = pt)",
|
||||
"Treat languages with country attribute as ISO 639-1 (e.g. don't download pt-BR if pt subtitle exists)": "Ország attribútummal rendelkező nyelveket kezeljen ISO 639-1 esként (pl. ne töltsön le pt-BR-t ha pt már le van töltve)",
|
||||
"Restrict to one language (skips adding \".lang.\" to the subtitle filename; only uses \"Subtitle Language (1)\")": "Korlátozás egy nyelvre (nem adja hozzá a \".nyelv\"-et a felirat fájl nevéhez.)",
|
||||
"Embedded subtitles: Treat \"Undefined\" (und) as language 1": "Beágyazott feliratok: \"Ismeretlen\" nyelvet úgy tekinti mintha az elsődleges lenne",
|
||||
"I rename my files using": "Átneveztem a fájlokat a következő segitségével",
|
||||
"Retrieve original filename from .file_info/file_info index files (see wiki)": "Eredeti fájlnév meghatározása a .file_info/file_info index fájlokból. (lásd wiki)",
|
||||
"Sonarr URL (add URL base if configured)": "Sonarr URL (base url hozzáadása ha be van állítva)",
|
||||
"Sonarr API key": "Sonarr API kulcs",
|
||||
"Radarr URL (add URL base if configured, min. version: 0.2.0.897)": "Radarr URL (base url hozzáadása ha be van állítva) min. verzió: 0.2.0.897",
|
||||
"Radarr API key": "Radarr API kulcs",
|
||||
"Provider: Enable OpenSubtitles": "Szolgáltató: OpenSubtitles használata",
|
||||
"Opensubtitles Username": "Opensubtitles Felhasználónév",
|
||||
"Opensubtitles Password": "Opensubtitles Jelszó",
|
||||
"OpenSubtitles VIP? (ad-free subs, 1000 subs/day, no-cache VIP server: http://v.ht/osvip)": "OpenSubtitles VIP? (hirdetésmentes feliratok, 1000 felirat/nap, VIP server: http://v.ht/osvip)",
|
||||
"Provider: Enable Podnapisi.NET": "Szolgáltató: Podnapisi.NET használata",
|
||||
"Provider: Enable Titlovi.com": "Szolgáltató: Titlovi.com használata",
|
||||
"Provider: Enable Addic7ed": "Szolgáltató: Titlovi.com használata",
|
||||
"Addic7ed Username": "Addic7ed Felhasználónév",
|
||||
"Addic7ed Password": "Addic7ed Jelszó",
|
||||
"Addic7ed: boost score (if requirements met)": "Addic7ed: találati esély növelése (ha a feltételek teljesülnek)",
|
||||
"Addic7ed: Use random user agents": "Addic7ed: Random \"user agent\" használata",
|
||||
"Provider: Enable Legendas TV (mostly pt-BR; UNRAR NEEDED)": "Szolgáltató: Legendas TV használata (jellemzően pt-BR nyelvhez) ",
|
||||
"Legendas TV Username": "Legendas TV felhasználói név",
|
||||
"Legendas TV Password": "Legendas TV jelszó",
|
||||
"Provider: Enable TVsubtitles.net": "Szolgáltató: Tvsubtitles.net engedélyezése",
|
||||
"Provider: Enable NapiProjekt.pl (Polish)": "Szolgáltató: NapiProjekt.pl (Lengyel) engedélyezése",
|
||||
"Provider: Enable SubScene (TV shows)": "Szolgáltató: SubScene (TV sorozatok) engedélyezése",
|
||||
"Provider: Enable hosszupuskasub.com (Hungarian)": "Szolgáltató: hosszupuskasub.com (Magyar) engedélyezése",
|
||||
"Provider: Enable aRGENTeaM (Spanish)": "Szolgáltató: aRGENTeaM (Spanyol) engedélyezése",
|
||||
"Search enabled providers simultaneously (multithreading)": "Párhuzamos keresés az engedélyezett szolgáltatóknál ( multithreading )",
|
||||
"Automatically extract and use embedded subtitles upon media addition (with configured default mods)": "Automatikusan tömörítse ki és használja a beágyazott feliratot a media hozzáadásakor (a beállított alapmódosításokkal )",
|
||||
"After automatic extraction of embedded subtitles, also immediately search for available subtitles?": "A beágyazott felirat automatikus kitömörítés után azonnal keressen elérhető feliratot?",
|
||||
"Don't search for subtitles of a language if there are embedded subtitles inside the media file (MKV/MP4)?": "Ne keressen feliratot ahhoz a nyelvhez amihez van beágyazott felirat a média fájlban (MKV/MP4)?",
|
||||
"Don't search for subtitles of a language if they already exist on the filesystem (metadata/filesystem)?": "Ne keressen feliratot olyan nyelvhez ami már megtalálható a fájlrendszeren (metaadat/fájlrendszer)",
|
||||
"How strict should these subtitles existing on the filesystem be detected?": "Mennyire szigorúan nézze ezeknek a feliratoknak a meglétét a fájlrendszeren?",
|
||||
"Include non-text subtitle formats (anything else than .srt/.ssa/.ass/.vtt; embedded or external) in the above?": "Nem szöveg alapú feliratok használat (bármi más mint srt/.ssa/.ass/.vtt; beágyazott vagy külső)",
|
||||
"Minimum score for TV (min: 240, def/sane: 337, min-ideal: 352; see http://v.ht/szscores)": "Minimum pontszám Sorozatokhoz (min: 240, def/sane: 337, min-ideális: 352; bővebben: http://v.ht/szscores)",
|
||||
"Minimum score for movies (min: 60, def/sane: 69, min-ideal: 82; see http://v.ht/szscores)": "Minimum pontszám Filmekhez (min: 60, def/sane: 69, min-ideal: 82; bővebben: http://v.ht/szscores)",
|
||||
"Download hearing impaired subtitles.": "Halláskárosult feliratok letöltése",
|
||||
"Remove Hearing Impaired tags from downloaded subtitles": "Halláskárosult címke törlése a letöltött feliratokról",
|
||||
"Remove style tags from downloaded subtitles (bold, italic, underline, colors, ...)": "Stílus címkék törlése a letöltött feliratokról (vastag, dőlt, aláhúzott, színek, ...)",
|
||||
"Fix common issues in subtitles": "Gyakori hibák javítása a feliratokban",
|
||||
"Fix common OCR errors in downloaded subtitles": "Általános OCR hibák javítása a letöltött feliratokban",
|
||||
"Reverse punctuation in RTL languages (heb)": "Fordított írásjelek RTL nyelveken (heb)",
|
||||
"Change colors of subtitles to": "Felirat színének megváltoztatása erre",
|
||||
"Store subtitles next to media files (instead of metadata)": "Feliratok tárolása a videó fájlok mellet (az adatbázis helyett)",
|
||||
"Subtitle formats to save (non-SRT only works if the previous option is enabled)": "Felirat mentési formátuma (a non-SRT csak akkor működik ha az előző opció be van kapcsolva)",
|
||||
"Subtitle Folder (\"current folder\" is the folder the current media file lives in)": "Felirat mappa (az \"aktuális mappa\" az az aktuális médiafájl mappája)",
|
||||
"Custom Subtitle folder (overrides \"Subtitle Folder\"; computes to real paths)": "Egyéni felirat mappa (felülbírálja a \"Felirat mappa\" -át, valós útvonalnak megfelelően)",
|
||||
"Fall back to metadata storage if filesystem storage failed": "A fájlok tárolásának meghiúsulása esetén térjen vissza a metaadat-tároláshoz",
|
||||
"Set subtitle file permissions to (integer, e.g.: 0775)": "Felirat fájl engedélyeinek beállítása erre (egész szám, például: 0775)",
|
||||
"Automatically delete leftover/unused (externally saved) subtitles": "Elhagyott/használatlan (egyénileg mentett) feliratok automatikus törlése",
|
||||
"On media playback: search for missing subtitles (refresh item)": "Lejátszás esetén: keressen hiányzó feliratot ( elem frissítése )",
|
||||
"Scheduler: Periodically search for recent items with missing subtitles": "Ütemező: Rendszeresen keressen feliratot a felirattal nem rendelkező friss elemekhez",
|
||||
"Scheduler: Item age to be considered recent": "Ütemező: Elem frissnek tekinthető ha a kora",
|
||||
"Scheduler: Recent items to consider per library": "Ütemező: Könyvtáranként ennyi elemet vegyen figyelembe mint friss",
|
||||
"Scheduler: Periodically search for better subtitles": "Ütemező: Rendszeresen keressen jobb feliratot",
|
||||
"Scheduler: Days to search for better subtitles (max: 30 days)": "Ütemező: Ennyi napig keressen jobb feliratot (max 30 nap)",
|
||||
"Scheduler: Don't search for better subtitles if the item's air date is older than": "Ütemező: Ne keressen jobb feliratot ha az elem vetítési dátuma régebbi mint",
|
||||
"Scheduler: Overwrite manually selected subtitles when better found": "Ütemező: Manuálisan kiválasztott felirat felülírása ha van jobb találat",
|
||||
"Scheduler: Overwrite subtitles with non-default subtitle modifications when better found": "Ütemező: Írja felül a feliratot ha van jobb",
|
||||
"History: amount of items to store historical data for": "Előzmények: ennyi elemnek tárolja az előzményeit",
|
||||
"How many download tries per subtitle (on timeout or error)": "Hány letöltési próbálkozás történjen feliratonként (időtúllépés vagy hiba esetén)",
|
||||
"Ignore folders (with \"subzero.ignore/.subzero.ignore/.nosz\" files in them)": "Mappák figyelmen kívül hagyása (amik tartalmazzák a \"subzero.ignore/.subzero.ignore/.nosz\" kifejezést)",
|
||||
"Ignore anything in the following paths (comma-separated)": "Minden kihagyása a következő helyeken (vesszővel elválasztva)",
|
||||
"Sub-Zero mode": "Sub-Zero mód",
|
||||
"Access PIN (any amount of numbers, 0-9)": "PIN Hozzáférés (tetszőleges hosszuságú szám, 0-9)",
|
||||
"Access PIN valid for minutes": "PIN hozzáférés ennyi percig valid: ",
|
||||
"Use PIN to restrict access to (needs plugin or PMS restart)": "PIN használta hogy hozzáférj (Bővítmény vagy PMS újraindítás szükséges)",
|
||||
"Call this executable upon successful subtitle download (see Wiki for details)": "Ennek a programnak az indítása sikeres felirat letöltés esetén (Részletek a Wiki-n)",
|
||||
"Check for correct folder permissions of every library on plugin start": "Megfelelő mappa engedélyek ellenőrzése minden bővítmény indításnál",
|
||||
"Use new style caching (for subliminal)": "Új stílusú gyorsitótár használata (subliminalhoz) ",
|
||||
"Low impact mode (for remote filesystems)": "Alacsony hatású mód (távoli fájlrendszerek esetén)",
|
||||
"Timeout for API requests sent to the PMS": "A PMS-hez küldött API-kérelmek időtúllépése",
|
||||
"HTTP proxy to use for providers (supports credentials)": "HTTP proxy használata a szolgáltatók eléréséhez (supports credentials)",
|
||||
"How verbose should the logging be?": "Milyen mértékű legyen a naplózás?",
|
||||
"How many log backups to keep?": "Hány napló-mentés maradjon?",
|
||||
"Log subtitle modification (debug)": "Felirat módosítások naplózása (Debug esetén)",
|
||||
"Log to console (for development/debugging)": "Naplózás konzolra (fejlesztéshez/hiba kereséshez)",
|
||||
"Collect anonymous usage statistics": "Anonim használati statisztikák gyűjtése",
|
||||
"Sonarr/Radarr (fill api info below)": "Sonarr/Radarr (API infó kitöltése lent)",
|
||||
"Filebot": "Filebot",
|
||||
"Sonarr/Radarr/Filebot": "Sonarr/Radarr/Filebot",
|
||||
"Symlink to original file": "Symlink az eredeti fájlra",
|
||||
"I keep the original filenames": "Megtartottam az eredeti fájl neveket",
|
||||
"none of the above": "Egyiksem",
|
||||
"exact: media filename match": "pontos: média fájl neve egyezik",
|
||||
"loose: filename contains media filename": "gyenge: fájlnév tartalmazza a média fájl nevét.",
|
||||
"any": "bármelyik",
|
||||
"prefer": "javasolt",
|
||||
"don't prefer": "nem javasolt",
|
||||
"force HI": "HI erőltetésé",
|
||||
"force non-HI": "non-Hi eröltetése",
|
||||
"don't change": "nincs módositás",
|
||||
"white": "fehér",
|
||||
"light-grey": "világos-szürke",
|
||||
"red": "piros",
|
||||
"green": "zöld",
|
||||
"yellow": "sárga",
|
||||
"blue": "kék",
|
||||
"magenta": "magenta",
|
||||
"cyan": "cián",
|
||||
"black": "fekete",
|
||||
"dark-red": "sötét-piros",
|
||||
"dark-green": "sötét-zöld",
|
||||
"dark-yellow": "sötét-sárga",
|
||||
"dark-blue": "sötét-két",
|
||||
"dark-magenta": "sötét-magenta",
|
||||
"dark-cyan": "sötét-cián",
|
||||
"dark-grey": "sötét-szürke",
|
||||
"current folder": "jelenlegi mappa",
|
||||
"never": "soha",
|
||||
"current media item": "jelenlegi média elem",
|
||||
"next episode (series)": "következő rész (sorozat)",
|
||||
"hybrid: current item or next episode": "hybrid: jelenlegi elem vagy következő rész",
|
||||
"hybrid-plus: current item and next episode": "hybrid-plus: jelenlegi elem és következő rész",
|
||||
"every 6 hours": "6 óránként",
|
||||
"every 12 hours": "12 óránként",
|
||||
"every 24 hours": "24 óránként",
|
||||
"1 days": "1 nap",
|
||||
"2 days": "2 nap",
|
||||
"3 days": "3 nap",
|
||||
"4 days": "4 nap",
|
||||
"1 weeks": "1 hét",
|
||||
"2 weeks": "2 hét",
|
||||
"3 weeks": "3 hét",
|
||||
"4 weeks": "4 hét",
|
||||
"5 weeks": "5 hét",
|
||||
"6 weeks": "6 hét",
|
||||
"12 weeks": "12 hét",
|
||||
"don't limit": "Nem korlátozott",
|
||||
"1 year": "1 év",
|
||||
"2 years": "2 év",
|
||||
"3 years": "3 év",
|
||||
"4 years": "4 év",
|
||||
"5 years": "5 év",
|
||||
"6 years": "6 év",
|
||||
"7 years": "7 év",
|
||||
"8 years": "8 év",
|
||||
"9 years": "9 év",
|
||||
"10 years": "10 év",
|
||||
"only agent": "csak ügynök",
|
||||
"disabled": "inaktiv",
|
||||
"advanced menu": "fejlett menü",
|
||||
"CRITICAL": "CRITICAL",
|
||||
"ERROR": "ERROR",
|
||||
"WARNING": "WARNING",
|
||||
"INFO": "INFO",
|
||||
"DEBUG": "DEBUG",
|
||||
"Force-find subtitles: %(item_title)s": "Erőltetett felirat keresés: %(item_title)s",
|
||||
"File %(file_part_index)s: ": "Fájl %(file_part_index)s: ",
|
||||
"%(part_summary)sNo current subtitle in storage": "%(part_summary)sNincs jelenlegi felirat",
|
||||
"%(part_summary)sCurrent subtitle: %(provider_name)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "%(part_summary)sJelenlegi felirat: %(provider_name)s (hozzáadva: %(date_added)s, %(mode)s), Nyelv: %(language)s, Pontszám: %(score)i, Tárolás: %(storage_type)s",
|
||||
"%(part_summary)sManage %(language)s subtitle": "%(part_summary)sKezelje a(z) %(language)s feliratot",
|
||||
"%(part_summary)sList %(language)s subtitles": "%(part_summary)sListázza a(z) %(language)s feliratokat",
|
||||
"%(part_summary)sEmbedded subtitles (%(languages)s)": "%(part_summary)sBeágyazott feliratok (%(languages)s)",
|
||||
"Select active %(language)s subtitle": "Aktív %(language)s felirat kiválasztása",
|
||||
"%(count)d subtitles in storage": "%(count)d felirat tárolva",
|
||||
"List available %(language)s subtitles": "%(language)s feliratok listázása",
|
||||
"Modify current %(language)s subtitle": "%(language)s felirat módosítása",
|
||||
"Currently applied mods: %(mod_list)s": "Már alkalmazott módosítások: %(mod_list)s",
|
||||
"Blacklist current %(language)s subtitle and search for a new one": "Tegye feketelistára a mostani %(language)s feliratot és keressen újat",
|
||||
"Manage blacklist (%(amount)s contained)": "Feketelista kezelése ((%(amount)s tartalmaz))",
|
||||
"%(current_state)s%(subtitle_name)s, Score: %(score)s": "%(current_state)s%(subtitle_name)s, Pontszám: %(score)s",
|
||||
"Current: ": "Jelenlegi: ",
|
||||
"Stored: ": "Tárolt: ",
|
||||
"by %(release_group)s": "%(release_group)s alapján",
|
||||
"Current: %(provider_name)s (%(score)s) ": "Jelenlegi: %(provider_name)s (%(score)s)␣",
|
||||
"Search for %(language)s subs (%(video_data)s)": "Keressen %(language)s feliratot (%(video_data)s)",
|
||||
"%(current_info)sFilename: %(filename)s": "%(current_info)sFájlnév: %(filename)s",
|
||||
"Searching for %(language)s subs (%(video_data)s), refresh here ...": "Keresés folyamatban a %(language)s felirathoz (%(video_data)s), \nSearching for %(language)s subs (%(video_data)s), frissíts itt ...",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: %(media_fps)s)": " (rossz FPS, felirat: %(subtitle_fps)s, média: %(media_fps)s)",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: unknown, low impact mode)": " (rossz FPS, felirat: %(subtitle_fps)s, média: ismeretlen, low impact mód)",
|
||||
"%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s": "%(blacklisted_state)s%(current_state)s: %(provider_name)s, Pont: %(score)s%(wrong_fps_state)s",
|
||||
"Release: %(release_info)s, Matches: %(matches)s": "Release: %(release_info)s, Találatok: %(matches)s",
|
||||
"Downloading subtitle for %(title_or_id)s": "Felirat letöltése ehhez: %(title_or_id)s",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s with default mods": "Tömöritsd ki a stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s alap beállításokkal",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s": "Tömörítsd ki a sream %(stream_index)s -t, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s",
|
||||
"%(provider_name)s, %(subtitle_id)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "%(provider_name)s, %(subtitle_id)s (hozzáadva: %(date_added)s, %(mode)s), Nyelv: %(language)s, Pontszám: %(score)i, Tárolás: %(storage_type)s",
|
||||
"Insufficient permissions on library %(title)s, folder: %(path)s": "Nem megfelelő jogosultság ehhez %(title)s, mappa: %(path)s",
|
||||
"Running: %(items_done)s/%(items_searching)s (%(percentage)s%%)": "Folyamatban: %(items_done)s/%(items_searching)s (%(percentage)s%%)",
|
||||
"Display ignore list (%(ignored_count)d)": "Mutasd a mellőzendők listáját (%(ignored_count)d)",
|
||||
"%(throttled_provider)s until %(until_date)s (%(reason)s)": "%(throttled_provider)s %(until_date)s ig (%(reason)s)",
|
||||
"Extracting subtitle %(stream_index)s of %(filename)s": "Felirat kitömörítése %(stream_index)s a %(filename)s -ból",
|
||||
"Extract missing %(language)s embedded subtitles": "Hiányzó %(language)s beágyazott felirat kitömörítése",
|
||||
"Extract and activate %(language)s embedded subtitles": "Beágyazott %(language)s felirat kitömörítése és aktiválása",
|
||||
"None": "Egyik sem",
|
||||
"Idle": "Tétlen",
|
||||
"%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)": "%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)",
|
||||
"Adjust by %(time_and_unit)s": "Igazítás %(time_and_unit)s -val ",
|
||||
"added: %(date_added)s, %(mode)s, Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "Hozzáadva: %(date_added)s, %(mode)s, Nyelv: %(language)s, Pontszám: %(score)i, Tárolás: %(storage_type)s",
|
||||
"Remove: %(mod_name)s": "Eltávolít: %(mod_name)s",
|
||||
"%(class_name)s: Subtitle download failed (%(item_id)s)": "%(class_name)s: Felirat letöltés nem sikerült (%(item_id)s)",
|
||||
"agent + interface": "Ügynök + interfész",
|
||||
"only interface": "Csak interfész",
|
||||
"interface": "Interfész",
|
||||
"Shows the current on deck items and allows you to individually (force-) refresh their metadata/subtitles.": "Mutassa a most játszott elemeket, és adjon lehetősége egyesével történő metaadat/felirat frissítéshez",
|
||||
"Lists items with missing subtitles. Click on \"Find recent items with missing subs\" to update list": "Hiányzó felirattal rendelkező elemek listázása. Kattints a \"Mostanában hozzáadott hiányzó felirattal rendelkező elemek keresése\"-re hogy frissítsd a listát",
|
||||
"Add %(kind)s %(title)s to the ignore list": "%(kind)s %(title)s hozzáadása a listára",
|
||||
"Remove %(kind)s %(title)s from the ignore list": "%(kind)s %(title)s eltávolítása a listáról ",
|
||||
"%(title)s added to the ignore list": "%(title)s hozzáadva a listára",
|
||||
"%(title)s removed from the ignore list": "%(title)s kivéve a listáról",
|
||||
"Triggering refresh for %(title)s": "Frissítés indítása a(z) %(title)s -hez",
|
||||
"Triggering forced refresh for %(title)s": "Erőltetett frissítés indítása a(z) %(title)s -hez",
|
||||
"Refresh of item %(item_id)s triggered": "Frissítés a %(item_id)s elemhez elindítva",
|
||||
"Forced refresh of item %(item_id)s triggered": "Erőltetett frissítés a %(item_id)s elemhez elindítva",
|
||||
"Ignore %(kind)s \"%(title)s\"": "Hagyd figyelmen kívül: %(kind)s \"%(title)s\"",
|
||||
"Un-ignore %(kind)s \"%(title)s\"": "Ne hagyd figyelmen kívül: Un-ignore %(kind)s \"%(title)s\"",
|
||||
"Refreshing %(title)s": "Frissítés: %(title)s",
|
||||
"Force-refreshing %(title)s": "Erőltetett frissítés: %(title)s",
|
||||
"Extracts the not yet extracted embedded subtitles of all episodes for the current season with all configured default modifications": "A még nem kitömörített beágyazott feliratok kitömörítése a kiválasztott évad összes részéből az összes beállított alapmódosítással",
|
||||
"Extracts embedded subtitles of all episodes for the current season with all configured default modifications": "A beágyazott feliratok kitömörítése a kiválasztott évad összes részéből az összes beállított alapmódosítással",
|
||||
"Refreshes %(the_movie_series_season_episode)s, possibly searching for missing and picking up new subtitles on disk": "Frissítse %(the_movie_series_season_episode)s, új feliratot keres vagy hozzáad a meghajtón lévőkből.",
|
||||
"the movie": "a film",
|
||||
"the series": "a sorozat",
|
||||
"the episode": "az epizód",
|
||||
"the season": "az évadot",
|
||||
"Change the color of the subtitle": "Felirat színének megváltoztatása",
|
||||
"Adds the requested color to every line of the subtitle. Support depends on player.": "Kért szín hozzáadása a felirat minden sorához. Támogatása a lejátszótól függ.",
|
||||
"Basic common fixes": "Általános alap javítások",
|
||||
"Fix common and whitespace/punctuation issues in subtitles": "Általános szóköz/írásjel hibák javítása a feliratokban",
|
||||
"Remove all style tags": "Minden stílus tag eltávolítása",
|
||||
"Removes all possible style tags from the subtitle, such as font, bold, color etc.": "Eltávolítja az összes lehetséges stílust a feliratból, például betűtípus, félkövér, szín stb.",
|
||||
"Reverse punctuation in RTL languages": "Fordított írásjelek RTL nyelveken",
|
||||
"Some playback devices don't properly handle right-to-left markers for punctuation. Physically swap punctuation. Applicable to languages: hebrew": "Néhány lejátszóeszköz nem megfelelően kezeli a jobbról balra történő írásnál az írásjeleket. Fizikálisan felcseréli az írásjeleket. A következő nyelvekhez alkalmazható: héber",
|
||||
"Change the FPS of the subtitle": "Feliratok FPS-ének megváltoztatása",
|
||||
"Re-syncs the subtitle to the framerate of the current media file.": "Felirat képkocka-sebességének szinkronizálása az aktuális médiához",
|
||||
"Remove Hearing Impaired tags": "Hallássérülteknek szánt információ eltávolitása ",
|
||||
"Removes tags, text and characters from subtitles that are meant for hearing impaired people": "Hallás sérültek számára hozzáadott szövegek, karakterek, tagek eltávolítása a feliratokból.",
|
||||
"Fix common OCR issues": "Általános OCR hibák javitása",
|
||||
"Fix issues that happen when a subtitle gets converted from bitmap to text through OCR": "Felirat képből szöveggé történő átalakítása(OCR) során keletkezett hibák javítása",
|
||||
"Change the timing of the subtitle": "Felirat időzítésének módosítása",
|
||||
"Adds or substracts a certain amount of time from the whole subtitle to match your media": "Hozzáad vagy elvesz a feliratból egy bizonyos időtartamot hogy illeszkedjen a médiához.",
|
||||
"Available": "Elérhető",
|
||||
"Current": "Jelenlegi",
|
||||
"Custom path to advanced_settings.json": "Egyéni útvonal az advanced_settings.json fájlhoz"
|
||||
}
|
||||
@@ -0,0 +1,400 @@
|
||||
{
|
||||
"sq": "Albanees",
|
||||
"ar": "Arabisch",
|
||||
"be": "Wit-Russisch",
|
||||
"bs": "Bosnisch",
|
||||
"bg": "Bulgaars",
|
||||
"ca": "Catalaans\n",
|
||||
"zh": "Chinees",
|
||||
"hr": "Kroatisch",
|
||||
"cs": "Czech",
|
||||
"da": "Deens",
|
||||
"nl": "Nederlands",
|
||||
"en": "Engels",
|
||||
"et": "Estlands",
|
||||
"fa": "Perzisch (Farsi)",
|
||||
"fi": "Fins",
|
||||
"fr": "Frans",
|
||||
"de": "Duits",
|
||||
"el": "Grieks",
|
||||
"he": "Hebreeuws",
|
||||
"hi": "Hindi",
|
||||
"hu": "Hongaars",
|
||||
"is": "IJslands",
|
||||
"id": "Indonesisch",
|
||||
"it": "Italiaans",
|
||||
"ja": "Japanees",
|
||||
"ko": "Koreaans",
|
||||
"lv": "Lets",
|
||||
"lt": "Litouws",
|
||||
"mk": "Macedonisch",
|
||||
"ms": "Maleis",
|
||||
"no": "Noors",
|
||||
"pl": "Pools",
|
||||
"pt": "Portugees",
|
||||
"pt-br": "Portugees (Braziliaans)",
|
||||
"ro": "Roemeens",
|
||||
"ru": "Russisch",
|
||||
"sr": "Servisch",
|
||||
"sr-cyrl": "Servisch (Cyrillisch)",
|
||||
"sr-latn": "Servisch (Latijns)",
|
||||
"sk": "Slowaaks",
|
||||
"sl": "Sloveens",
|
||||
"es": "Spaans",
|
||||
"sv": "Zweeds",
|
||||
"th": "Thais",
|
||||
"tr": "Turks",
|
||||
"uk": "Oekraïens",
|
||||
"vi": "Vietnamees",
|
||||
"Internal stuff, pay attention!": "Interne dingen, Opletten!",
|
||||
"Advanced": "Geavanceerd",
|
||||
"advanced": "geavanceerd",
|
||||
"Enter PIN": "PIN invoeren",
|
||||
"The owner has restricted the access to this menu. Please enter the correct pin": "De eigenaar heeft de toegang beperkt tot dit menu. Vul a.u.b. de correcte PIN in.",
|
||||
"Restart the plugin": "Herstart de plugin",
|
||||
"Get my logs (copy the appearing link and open it in your browser, please)": "Haal mijn logs op (kopieer de link en open deze in je browser, a.u.b.)",
|
||||
"Copy the appearing link and open it in your browser, please": "kopieer de link en open deze in je browser, a.u.b.",
|
||||
"Trigger find better subtitles": "Activeer zoek betere ondertiteling",
|
||||
"Skip next find better subtitles (sets last run to now)": "Sla volgende zoek beter ondertiteling over (stelt de laatste uitvoering op nu)",
|
||||
"Trigger subtitle storage maintenance": "Activeer ondertiteling opslag onderhoud",
|
||||
"Trigger subtitle storage migration (expensive)": "Activeer ondertiteling opslag migratie (zwaar)",
|
||||
"Trigger cache maintenance (refiners, providers and packs/archives)": "Activeer Cache onderhoud (filters, providers en packs/archieven)",
|
||||
"Apply configured default subtitle mods to all (active) stored subtitles": "Pas geconfigureerde standaard ondertiteling modificaties toe op alle (actieve) opgeslagen ondertiteling",
|
||||
"Re-Apply mods of all stored subtitles": "Pas modificaties op alle opgeslagen ondertiteling opnieuw toe",
|
||||
"Log the plugin's scheduled tasks state storage": "Log de plugin's geplande takenstaat opslag",
|
||||
"Log the plugin's internal ignorelist storage": "Log de plugin's interne negeerlijst opslag",
|
||||
"Log the plugin's complete state storage": "Log de plugin's compleetstaat opslag",
|
||||
"Reset the plugin's scheduled tasks state storage": "Reset de plugin's geplande takenstaat opslag",
|
||||
"Reset the plugin's internal ignorelist storage": "Reset de plugin's interne negeerlijst opslag",
|
||||
"Invalidate Sub-Zero metadata caches (subliminal)": "Invalideer Sub-Zero metadata caches (subliminal)",
|
||||
"Reset provider throttle states": "Reset provider pauze staten",
|
||||
"Restarting the plugin": "Plugin herstarten",
|
||||
"Restart triggered, please wait about 5 seconds": "Herstart geactiveerd, wacht a.u.b. ongeveer 5 seconden",
|
||||
"Reset subtitle storage": "Reset ondertiteling opslag",
|
||||
"Are you sure?": "Weet je het zeker?",
|
||||
"Are you really sure?": "Weet je het heel zeker?",
|
||||
"Success": "Succes",
|
||||
"FindBetterSubtitles triggered": "ZoekBetereOndertiteling geactiveerd",
|
||||
"FindBetterSubtitles skipped": "ZoekBetereOndertiteling overgeslagen",
|
||||
"SubtitleStorageMaintenance triggered": "OndertitelingOpslagOnderhoud geactiveerd",
|
||||
"MigrateSubtitleStorage triggered": "MigreerOndertitelingOpslag geactiveerd",
|
||||
"TriggerCacheMaintenance triggered": "CacheOnderhoud geactiveerd",
|
||||
"This may take some time ...": "Dit kan een tijd duren...",
|
||||
"Download Logs": "Download Logs",
|
||||
"Sorry, feature unavailable": "Sorry, eigenschap is niet beschikbaar",
|
||||
"Universal Plex token not available": "Universele Plex token niet beschikbaar",
|
||||
"Copy this link and open this in your browser, please": "Kopieer deze link en open deze in je browser, a.u.b.",
|
||||
"Cache invalidated": "Cache is geinvalideerd",
|
||||
"Enter PIN number ": "Voer PIN nummer in ",
|
||||
"PIN correct": "PIN is correct",
|
||||
"Reset": "Reset",
|
||||
"Menu locked": "Menu geblokeerd",
|
||||
"Provider throttles reset": "Provider pauzes reset",
|
||||
"Information Storage (%s) reset": "Informatie opslag (%s) gereset",
|
||||
"Information Storage (%s) logged": "Informatie opslag (%s) geregistreerd",
|
||||
"Plex didn't return any information about the item, please refresh it and come back later": "Plex heeft geen informatie over het item gegeven, ververs a.u.b. het item en kom later terug",
|
||||
"Item not found: %s!": "Item niet gevonden: %s!",
|
||||
"< Back to %s": "< Terug naar %s",
|
||||
"Back to %s > %s": "Terug naar %s > %s",
|
||||
"Refresh: %s": "Ververs: %s",
|
||||
"Issues a forced refresh, ignoring known subtitles and searching for new ones": "Voert een geforceerde vernieuwing uit, negeert bekende ondertiteling en zoekt naar nieuwe",
|
||||
"Extract and activate embedded subtitle streams": "Geëmbedde ondertiteling streams extraheren en activeren",
|
||||
"Inspect currently blacklisted subtitles": "Inspecteer de ondertiteling op de zwarte lijst",
|
||||
"Subtitle saved to disk": "Ondertitel opgeslagen op schijf",
|
||||
"Remove subtitle from blacklist": "Verwijder ondertitel van zwarte lijst",
|
||||
"No subtitles found": "Geen ondertiteling gevonden",
|
||||
" (unknown)": " (onbekend)",
|
||||
" (forced)": " (geforceerd)",
|
||||
"Extracting of embedded subtitle %s of part %s:%s triggered": "Extraheren van geëmbedde ondertitel %s van deel %s:%s geactiveerd",
|
||||
"Insufficient permissions": "Niet genoeg rechten",
|
||||
"I'm not enabled!": "Ik ben niet ingeschakeld!",
|
||||
"Please enable me for some of your libraries in your server settings; currently I do nothing": "Schakel me alstublieft in voor enkele van uw bibliotheken in uw serverinstellingen; momenteel doe ik niets",
|
||||
"Working ... refresh here": "Bezig... ververs hier",
|
||||
"Current state: %s; Last state: %s": "Huidige staat: %s, Laatste staat: %s",
|
||||
"On-deck items": "On-deck items",
|
||||
"Shows the %s recently played items and allows you to individually (force-) refresh their metadata/subtitles.": "Toont de %s recentelijk afgespeelde items en stelt je instaat om hiervan de metadata/ondertiteling individueel (geforceerd) te verversen",
|
||||
"Recently-added items": "Recentelijk toegevoegde items",
|
||||
"Recently played items": "Recentelijk afgespeelde items",
|
||||
"Shows the recently added items per section.": "Toont de recentelijk toegevoegde items per sectie",
|
||||
"Show recently added items with missing subtitles": "Laat de recentelijk toegevoegde items met missende ondertiteling zien",
|
||||
"Browse all items": "Blader door alle items",
|
||||
"Go through your whole library and manage your ignore list. You can also (force-) refresh the metadata/subtitles of individual items.": "Ga door je hele bibliotheek heen en beheer je negeerlijst. Je kan ook de metadata/ondertiteling van individuele items (geforceerd) verversen.",
|
||||
"Last run: %s; Next scheduled run: %s; Last runtime: %s": "Laatste uitvoering: %s; Volgend geplande uitvoering: %s; Laatste uitvoeringstijd: %s",
|
||||
"Search for missing subtitles (in recently-added items, max-age: %s)": "Zoek naar missende ondertiteling (in recentelijk toegevoegde items, maximale leeftijd: %s)",
|
||||
"Automatically run periodically by the scheduler, if configured. %s": "Voer automatisch periodiek uit door de taakplanner, indien ingesteld. %s",
|
||||
"Show the current ignore list (mainly used for the automatic tasks)": "Laat de huidige negeerlijst zien (hoofdzakelijk gebruikt voor automatische taken)",
|
||||
"History": "Geschiedenis",
|
||||
"Show the last %i downloaded subtitles": "Laat de laatste %i gedownloade ondertiteling zien",
|
||||
"Refresh": "Ververs",
|
||||
"Re-lock menu(s)": "Her-blokkeer menu('s)",
|
||||
"Enabled the PIN again for menu(s)": "PIN opnieuw ingeschakeld voor menu('s)",
|
||||
"Throttled providers: %s": "Gepauzeerde providers: %s",
|
||||
"Advanced functions": "Geavanceerde functies",
|
||||
"Use at your own risk": "Gebruik op eigen risico",
|
||||
"Items On Deck": "Items On-deck",
|
||||
"Recently Played": "Recentelijk afgespeeld",
|
||||
"Items with missing subtitles": "Items met missende ondertiteling",
|
||||
"Find recent items with missing subtitles": "Zoek recente items met missende ondertiteling",
|
||||
"Updating, refresh here ...": "Updaten, ververs hier...",
|
||||
"Missing: %s": "Missend: %s",
|
||||
"Didn't change the ignore list": "De negeerlijst is niet gewijzigd",
|
||||
"Sections": "Secties",
|
||||
"All": "Alles",
|
||||
"show": "serie",
|
||||
"movie": "film",
|
||||
"<< Back to home": "<< Terug naar start",
|
||||
"Auto-Find subtitles: %s": "Auto-Vind ondertiteling: %s",
|
||||
"Extracting of embedded subtitles for %s triggered": "Extraheren van geëmbedde ondertiteling voor %s is geactiveerd",
|
||||
"< Back to subtitle options for: %s": "< Teug naar ondertitel opties voor: %s",
|
||||
"Remove last applied mod (%s)": "Verwijder laatst toegepaste modificatie (%s)",
|
||||
"none": "geen",
|
||||
"Manage applied mods": "Beheer toegepaste modificaties",
|
||||
"Reapply applied mods": "Opnieuw toepassen van toegepaste modificaties",
|
||||
"Restore original version": "Herstel originele versie",
|
||||
"< Back to subtitle modification menu": "< Terug naar ondertitel modificatie menu",
|
||||
"subs constantly getting faster": "ondertiteling wordt constant sneller",
|
||||
"subs constantly getting slower": "ondertiteling wordt constant langzamer",
|
||||
"< Back to subtitle modifications": "< Terug naar ondertitel modificaties",
|
||||
"< Back to unit selection": "< Terug naar eenheid selectie",
|
||||
"Subtitle Language (1)": "Ondertitel taal (1)",
|
||||
"Subtitle Language (2)": "Ondertitel taal (2)",
|
||||
"Subtitle Language (3)": "Ondertitel taal (3)",
|
||||
"Additional Subtitle Languages (use ISO-639-1 codes; comma-separated)": "Additionele ondertitel talen (gebruik ISO-639-1 codes; komma-gescheiden)",
|
||||
"Only download foreign/forced subtitles": "Download enkel ondertiteling voor buitenlands gesproken delen (forced)",
|
||||
"Display languages with country attribute as ISO 639-1 (e.g. pt-BR = pt)": "Geef talen weer met landattribuut als ISO 639-1 (bijvoorbeeld pt-BR = pt)",
|
||||
"Treat languages with country attribute as ISO 639-1 (e.g. don't download pt-BR if pt subtitle exists)": "Behandel talen met landattribuut als ISO 639-1 (bijvoorbeeld download geen pt-BR als pt ondertitel bestaat)",
|
||||
"Restrict to one language (skips adding \".lang.\" to the subtitle filename; only uses \"Subtitle Language (1)\")": "Beperken tot één taal (slaat het toevoegen van \".lang.\" aan de ondertitel bestandsnaam over, gebruikt alleen \"Ondertitel taal (1)\")",
|
||||
"Embedded subtitles: Treat \"Undefined\" (und) as language 1": "Geëmbedde ondertiteling: Behandel \"Ongedefineerd\" (und) als \"Ondertitel taal (1)\"",
|
||||
"I rename my files using": "Ik hernoem mijn bestanden met",
|
||||
"Retrieve original filename from .file_info/file_info index files (see wiki)": "Oorspronkelijke bestandsnaam ophalen uit .file_info / file_info indexbestanden (zie wiki)",
|
||||
"Sonarr URL (add URL base if configured)": "Sonarr URL (voeg URL-basis toe indien geconfigureerd)",
|
||||
"Sonarr API key": "Sonarr API sleutel",
|
||||
"Radarr URL (add URL base if configured, min. version: 0.2.0.897)": "Radarr URL (voeg URL-basis toe indien geconfigureerd, minimaal benodigde versie:0.2.0.897)",
|
||||
"Radarr API key": "Radarr API sleutel",
|
||||
"Provider: Enable OpenSubtitles": "Provider: Opensubtitles inschakelen",
|
||||
"Opensubtitles Username": "Opensubtitles gebruikersnaam",
|
||||
"Opensubtitles Password": "Opensubtitles wachtwoord",
|
||||
"OpenSubtitles VIP? (ad-free subs, 1000 subs/day, no-cache VIP server: http://v.ht/osvip)": "OpenSubtitles VIP? (reclame vrije ondertiteling, 1000 ondertitels/dag, geen-cache, VIP server: http://v.ht/osvip) ",
|
||||
"Provider: Enable Podnapisi.NET": "Provider: Podnapisi.NET inschakelen",
|
||||
"Provider: Enable Titlovi.com": "Provider:Titlovi.com inschakelen",
|
||||
"Provider: Enable Addic7ed": "Provider: Addic7ed inschakelen",
|
||||
"Addic7ed Username": "Addic7ed gebruikersnaam",
|
||||
"Addic7ed Password": "Addic7ed wachtwoord",
|
||||
"Addic7ed: boost score (if requirements met)": "Addic7ed: verhoog score (indien aan eisen wordt voldaan)",
|
||||
"Addic7ed: Use random user agents": "Addic7ed: gebruik willekeurige user-agents",
|
||||
"Provider: Enable Legendas TV (mostly pt-BR; UNRAR NEEDED)": "Provider: Legendas TV inschakelen (meestal pt-BR; UNRAR NODIG)",
|
||||
"Legendas TV Username": "Legendas TV gebruikersnaam",
|
||||
"Legendas TV Password": "Legendas TV wachtwoord",
|
||||
"Provider: Enable TVsubtitles.net": "Provider: Tvsubtitles.net inschakelen",
|
||||
"Provider: Enable NapiProjekt.pl (Polish)": "Provider: NapiProjekt.pl inschakelen (Pools)",
|
||||
"Provider: Enable SubScene (TV shows)": "Provider: SubScene inschakelen (TV series)",
|
||||
"Provider: Enable hosszupuskasub.com (Hungarian)": "Provider: hosszupuskasub.com inschakelen (Hongaars)",
|
||||
"Provider: Enable aRGENTeaM (Spanish)": "Provider: aRGENTeaM inschakelen (Spaans)",
|
||||
"Search enabled providers simultaneously (multithreading)": "Zoek gelijkertijd bij ingeschakelde providers (multithreading)",
|
||||
"Automatically extract and use embedded subtitles upon media addition (with configured default mods)": "Extraheer en gebruik geëmbedde ondertiteling automatisch bij media toevoeging (met geconfigureerde standaard modificaties)",
|
||||
"After automatic extraction of embedded subtitles, also immediately search for available subtitles?": "Na het automatisch extraheren van geëmbedde ondertiteling, ook gelijk zoeken naar beschikbare ondertiteling?",
|
||||
"Don't search for subtitles of a language if there are embedded subtitles inside the media file (MKV/MP4)?": "Niet zoeken naar ondertiteling van een taal als er al geëmbedde ondertiteling bestaat in het media bestand (MKV/MP4)?",
|
||||
"Don't search for subtitles of a language if they already exist on the filesystem (metadata/filesystem)?": "Niet zoeken naar ondertiteling van een taal als er ondertiteling bestaat op het bestandssysteem (metadata/bestandssysteem)?",
|
||||
"How strict should these subtitles existing on the filesystem be detected?": "Hoe strikt moet deze ondertiteling op het bestandssysteem worden gedetecteerd?",
|
||||
"Include non-text subtitle formats (anything else than .srt/.ssa/.ass/.vtt; embedded or external) in the above?": "Gebruik niet-tekst ondertiteling formaten (iets anders dan .srt / .ssa / .ass / .vtt; geëmbed of extern) in het bovenstaande?",
|
||||
"Minimum score for TV (min: 240, def/sane: 337, min-ideal: 352; see http://v.ht/szscores)": "Minimale score voor TV (minimaal: 240, verstandig: 337, minimaal ideaal: 352; zie http://v.ht/szscores)",
|
||||
"Minimum score for movies (min: 60, def/sane: 69, min-ideal: 82; see http://v.ht/szscores)": "Minimale score voor films (minimaal: 60, verstandig: 69, minimaal ideaal: 82; zie http://v.ht/szscores)",
|
||||
"Download hearing impaired subtitles.": "Download ondertiteling voor slechthorende",
|
||||
"Remove Hearing Impaired tags from downloaded subtitles": "Verwijder slecthorende-tags uit ondertiteling",
|
||||
"Remove style tags from downloaded subtitles (bold, italic, underline, colors, ...)": "Verwijder stijl-tags uit de ondertiteling (dik gedrukt, schuin, onderlijnt, kleuren, ...)",
|
||||
"Fix common issues in subtitles": "Repareer veel voorkomende fouten in ondertiteling",
|
||||
"Fix common OCR errors in downloaded subtitles": "Repareer veel voorkomende OCR fouten om gedownloade ondertiteling",
|
||||
"Reverse punctuation in RTL languages (heb)": "Draai leestekens in RTL (rechts naar links) talen om (Hebreeuws)",
|
||||
"Change colors of subtitles to": "Wijzig kleur van ondertiteling naar",
|
||||
"Store subtitles next to media files (instead of metadata)": "Sla ondertiteling naast de media bestanden op (in plaats van in de metadata)",
|
||||
"Subtitle formats to save (non-SRT only works if the previous option is enabled)": "Ondertiteling formaten om op te slaan (niet-SRT werkt alleen als de vorige optie is ingeschakeld)",
|
||||
"Subtitle Folder (\"current folder\" is the folder the current media file lives in)": "Ondertiteling map (\"huidige map\" is de map waar de huidige media is opgelsagen)",
|
||||
"Custom Subtitle folder (overrides \"Subtitle Folder\"; computes to real paths)": "Aangepaste ondertiteling map (overschrijft de \"Ondertiteling map\"; gebruikt echte paden)",
|
||||
"Fall back to metadata storage if filesystem storage failed": "Val terug naar metadata opslag indien het bestandssysteem faalt",
|
||||
"Set subtitle file permissions to (integer, e.g.: 0775)": "Stel rechten voor ondertitel bestanden in (geheel getal, bijv .: 0775)",
|
||||
"Automatically delete leftover/unused (externally saved) subtitles": "Verwijder automatisch achtergebleven/ongebruikte (extern opgeslagen) ondertiteling",
|
||||
"On media playback: search for missing subtitles (refresh item)": "Bij media afspelen: zoek naar missende ondertiteling (ververs item)",
|
||||
"Scheduler: Periodically search for recent items with missing subtitles": "Taakplanner: Zoek periodiek naar recente items met missende ondertiteling",
|
||||
"Scheduler: Item age to be considered recent": "Taakplanner: Item leeftijd die als recent moet worden beschouwd",
|
||||
"Scheduler: Recent items to consider per library": "Taakplanner: Recente items om te overwegen per bibliotheek",
|
||||
"Scheduler: Periodically search for better subtitles": "Taakplanner: Zoek periodiek naar betere ondertiteling",
|
||||
"Scheduler: Days to search for better subtitles (max: 30 days)": "Taakplanner: Dagen om te zoeken naar betere ondertiteling (maximaal 30 dagen)",
|
||||
"Scheduler: Don't search for better subtitles if the item's air date is older than": "Taakplanner: Niet zoeken naar betere ondertiteling als de uitzenddatum ouder is dan",
|
||||
"Scheduler: Overwrite manually selected subtitles when better found": "Taakplanner: Overschrijf handmatig geselecteerde ondertiteling als betere ondertiteling is gevonden",
|
||||
"Scheduler: Overwrite subtitles with non-default subtitle modifications when better found": "Taakplanner: Overschrijf ondertiteling met niet-standaard ondertitel modificaties als betere ondertiteling is gevonden",
|
||||
"History: amount of items to store historical data for": "Geschiedenis: aantal items om historische gegevens voor op te slaan",
|
||||
"How many download tries per subtitle (on timeout or error)": "Hoeveel download pogingen per ondertitel (bij time-out of fout)",
|
||||
"Ignore folders (with \"subzero.ignore/.subzero.ignore/.nosz\" files in them)": "Negeer mappen (met \"subzero.ignore/.subzero.ignore/.nosz\" bestanden in de mappen)",
|
||||
"Ignore anything in the following paths (comma-separated)": "Negeer alles in de volgende paden (komma-gescheiden)",
|
||||
"Sub-Zero mode": "Sub-Zero modus",
|
||||
"Access PIN (any amount of numbers, 0-9)": "ToegangsPIN (elk aantal cijfers, 0-9)",
|
||||
"Access PIN valid for minutes": "ToegangsPIN geldig voor minuten",
|
||||
"Use PIN to restrict access to (needs plugin or PMS restart)": "Gebruik PIN om toegang te beperken tot (hiervoor moet de plugin of PMS worden herstart)",
|
||||
"Call this executable upon successful subtitle download (see Wiki for details)": "Voer dit uitvoerbare bestand uit na succesvolle ondertitel download (zie Wiki voor details)",
|
||||
"Check for correct folder permissions of every library on plugin start": "Controleer op juiste map rechten van elke bibliotheek bij het starten van de plugin",
|
||||
"Use new style caching (for subliminal)": "Gebruik nieuwe manier van caching (voor subliminal)",
|
||||
"Low impact mode (for remote filesystems)": "Lage impact modus (voor externe bestandssystemen)",
|
||||
"Timeout for API requests sent to the PMS": "Time-out voor API-aanvragen die naar PMS worden verzonden",
|
||||
"HTTP proxy to use for providers (supports credentials)": "HTTP proxy om te gebruiken voor providers (ondersteunt inloggegevens)",
|
||||
"How verbose should the logging be?": "Hoe uitgebreid moet de logging zijn?",
|
||||
"How many log backups to keep?": "Hoeveel log bestanden moeten worden bewaard?",
|
||||
"Log subtitle modification (debug)": "Log ondertitel modificatie (debug)",
|
||||
"Log to console (for development/debugging)": "Log naar console (voor ontwikkeling/debugging)",
|
||||
"Collect anonymous usage statistics": "Verzamel anonieme gebruiksstatistieken",
|
||||
"Sonarr/Radarr (fill api info below)": "Sonarr/Radarr (vul API informatie hieronder in)",
|
||||
"Filebot": "Filebot",
|
||||
"Sonarr/Radarr/Filebot": "Sonarr/Radarr/Filebot ",
|
||||
"Symlink to original file": "Symlink naar origineel bestand",
|
||||
"I keep the original filenames": "Ik bewaar de originele bestandsnamen",
|
||||
"none of the above": "geen van de bovenstaande",
|
||||
"exact: media filename match": "Precies: media bestandsnaam overeenkomst",
|
||||
"loose: filename contains media filename": "Los: Bestandsnaam bevat media bestandsnaam",
|
||||
"any": "elk",
|
||||
"prefer": "prefereren",
|
||||
"don't prefer": "niet refereren",
|
||||
"force HI": "forceer slechthorend",
|
||||
"force non-HI": "forceer niet slechthorend",
|
||||
"don't change": "niet wijzigen",
|
||||
"white": "wit",
|
||||
"light-grey": "licht grijs",
|
||||
"red": "rood",
|
||||
"green": "groen",
|
||||
"yellow": "geel",
|
||||
"blue": "blauw",
|
||||
"magenta": "magenta",
|
||||
"cyan": "cyaan",
|
||||
"black": "zwart",
|
||||
"dark-red": "donker rood",
|
||||
"dark-green": "donker groen",
|
||||
"dark-yellow": "donker geel",
|
||||
"dark-blue": "donker blauw",
|
||||
"dark-magenta": "donker magenta",
|
||||
"dark-cyan": "donker cyaan",
|
||||
"dark-grey": "donker grijs",
|
||||
"current folder": "huidige map",
|
||||
"never": "nooit",
|
||||
"current media item": "huidig media item",
|
||||
"next episode (series)": "volgende aflevering (series)",
|
||||
"hybrid: current item or next episode": "Hybride: huidig item of volgende aflevering",
|
||||
"hybrid-plus: current item and next episode": "Hybride-plus: huidig item en volgende aflevering",
|
||||
"every 6 hours": "elke 6 uur",
|
||||
"every 12 hours": "elke 12 uur",
|
||||
"every 24 hours": "elk 24 uur",
|
||||
"1 days": "1 dag",
|
||||
"2 days": "2 dagen",
|
||||
"3 days": "3 dagen",
|
||||
"4 days": "4 dagen",
|
||||
"1 weeks": "1 week",
|
||||
"2 weeks": "2 weken",
|
||||
"3 weeks": "3 weken",
|
||||
"4 weeks": "4 weken",
|
||||
"5 weeks": "5 weken",
|
||||
"6 weeks": "6 weken",
|
||||
"12 weeks": "12 weken",
|
||||
"don't limit": "niet limiteren",
|
||||
"1 year": "1 jaar",
|
||||
"2 years": "2 jaar",
|
||||
"3 years": "3 jaar",
|
||||
"4 years": "4 jaar",
|
||||
"5 years": "5 jaar",
|
||||
"6 years": "6 jaar",
|
||||
"7 years": "7 jaar",
|
||||
"8 years": "8 jaar",
|
||||
"9 years": "9 jaar",
|
||||
"10 years": "10 jaar",
|
||||
"only agent": "enkel agent",
|
||||
"disabled": "uitgeschakeld",
|
||||
"advanced menu": "geavanceerd menu",
|
||||
"CRITICAL": "KRITISCH",
|
||||
"ERROR": "ERROR",
|
||||
"WARNING": "WAARSCHUWING",
|
||||
"INFO": "INFO",
|
||||
"DEBUG": "DEBUG",
|
||||
"Force-find subtitles: %(item_title)s": "Geforceerd ondertiteling zoeken: %(item_title)s",
|
||||
"File %(file_part_index)s: ": "Bestand %(file_part_index)s: ",
|
||||
"%(part_summary)sNo current subtitle in storage": "%(part_summary)sGeen huidige ondertitel gevonden in opslag",
|
||||
"%(part_summary)sCurrent subtitle: %(provider_name)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "%(part_summary)sHuidige ondertitel: %(provider_name)s (toegevoegd: %(date_added)s, %(mode)s), Taal: %(language)s, Score: %(score)i, Opslag: %(storage_type)s",
|
||||
"%(part_summary)sManage %(language)s subtitle": "%(part_summary)sBeheer %(language)se ondertitel",
|
||||
"%(part_summary)sList %(language)s subtitles": "%(part_summary)s %(language)se ondertiteling weergeven",
|
||||
"%(part_summary)sEmbedded subtitles (%(languages)s)": " %(part_summary)sGeëmbedde ondertiteling (%(languages)s) ",
|
||||
"Select active %(language)s subtitle": "Selecteer actieve %(language)se ondertitel",
|
||||
"%(count)d subtitles in storage": "%(count)d ondertitels gevonden in opslag",
|
||||
"List available %(language)s subtitles": "Beschikbare %(language)se ondertiteling weergeven",
|
||||
"Modify current %(language)s subtitle": "Wijzig huidige %(language)se ondertitel",
|
||||
"Currently applied mods: %(mod_list)s": "Huidig toegepaste modificaties: %(mod_list)s",
|
||||
"Blacklist current %(language)s subtitle and search for a new one": "Zet de huidige %(language)se ondertitel op de zwarte lijst en zoek naar nieuwe",
|
||||
"Manage blacklist (%(amount)s contained)": "Beheer de zwarte lijst (bevat er %(amount)s)",
|
||||
"%(current_state)s%(subtitle_name)s, Score: %(score)s": "%(current_state)s%(subtitle_name)s, Score: %(score)s",
|
||||
"Current: ": "Huidig: ",
|
||||
"Stored: ": "Opgeslagen: ",
|
||||
"by %(release_group)s": " door %(release_group)s ",
|
||||
"Current: %(provider_name)s (%(score)s) ": "Huidig: %(provider_name)s (%(score)s) ",
|
||||
"Search for %(language)s subs (%(video_data)s)": "Zoek naar %(language)se ondertiteling (%(video_data)s)",
|
||||
"%(current_info)sFilename: %(filename)s": "%(current_info)sBestandsnaam: %(filename)s",
|
||||
"Searching for %(language)s subs (%(video_data)s), refresh here ...": "Zoeken naar %(language)se ondertiteling (%(video_data)s), ververs hier",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: %(media_fps)s)": " (verkeerde FPS, ondertitel: %(subtitle_fps)s, media: %(media_fps)s)",
|
||||
" (wrong FPS, sub: %(subtitle_fps)s, media: unknown, low impact mode)": " (verkeerde FPS, ondertitel: %(subtitle_fps)s, media: onbekend, lage impact modus)",
|
||||
"%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s": "%(blacklisted_state)s%(current_state)s: %(provider_name)s, score: %(score)s%(wrong_fps_state)s",
|
||||
"Release: %(release_info)s, Matches: %(matches)s": "Uitgave: %(release_info)s, Overeenkomst: %(matches)s",
|
||||
"Downloading subtitle for %(title_or_id)s": "Ondertitel downloaden voor %(title_or_id)s",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s with default mods": "Extraheer stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s met standaard modificaties",
|
||||
"Extract stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s": "Extraheer stream %(stream_index)s, %(language)s%(unknown_state)s%(forced_state)s%(stream_title)s",
|
||||
"%(provider_name)s, %(subtitle_id)s (added: %(date_added)s, %(mode)s), Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "%(provider_name)s, %(subtitle_id)s (Toegevoegd: %(date_added)s, %(mode)s), Taal: %(language)s, Score: %(score)i, Opslag: %(storage_type)s",
|
||||
"Insufficient permissions on library %(title)s, folder: %(path)s": "Niet genoeg rechten op bibliotheek %(title)s, map: %(path)s",
|
||||
"Running: %(items_done)s/%(items_searching)s (%(percentage)s%%)": "Uitvoeren: %(items_done)s/%(items_searching)s (%(percentage)s%%)",
|
||||
"Display ignore list (%(ignored_count)d)": "Negeerlijst weergeven (%(ignored_count)d)",
|
||||
"%(throttled_provider)s until %(until_date)s (%(reason)s)": "%(throttled_provider)s tot %(until_date)s (%(reason)s)",
|
||||
"Extracting subtitle %(stream_index)s of %(filename)s": "Extraheren van ondertitel %(stream_index)s van %(filename)s",
|
||||
"Extract missing %(language)s embedded subtitles": "Extraheer missende %(language)s geëmbedde ondertiteling",
|
||||
"Extract and activate %(language)s embedded subtitles": "Extraheer en activeer %(language)s geëmbedde ondertiteling",
|
||||
"None": "Geen",
|
||||
"Idle": "Idle",
|
||||
"%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)": "%(from_fps)s fps -> %(to_fps)s fps (%(slower_or_faster_indicator)s)",
|
||||
"Adjust by %(time_and_unit)s": "Aanpassen met %(time_and_unit)s",
|
||||
"added: %(date_added)s, %(mode)s, Language: %(language)s, Score: %(score)i, Storage: %(storage_type)s": "Toegevoegd: %(date_added)s, %(mode)s, Taal: %(language)s, Score: %(score)i, Opslag: %(storage_type)s",
|
||||
"Remove: %(mod_name)s": "Verwijder: %(mod_name)s ",
|
||||
"%(class_name)s: Subtitle download failed (%(item_id)s)": "%(class_name)s: Downloaden van ondertitel mislukt (%(item_id)s)",
|
||||
"agent + interface": "agent + interface",
|
||||
"only interface": "enkel de interface",
|
||||
"interface": "interface",
|
||||
"Shows the current on deck items and allows you to individually (force-) refresh their metadata/subtitles.": "Toont de huidige on-deck items en stelt je instaat om hiervan de metadata/ondertiteling individueel (geforceerd) te verversen",
|
||||
"Lists items with missing subtitles. Click on \"Find recent items with missing subs\" to update list": "Items met missende ondertiteling weergeven. Klik op \"Zoek recente items met missende ondertiteling\" om de lijst te updaten",
|
||||
"Add %(kind)s %(title)s to the ignore list": "Voeg %(kind)s %(title)s toe aan negeerlijst",
|
||||
"Remove %(kind)s %(title)s from the ignore list": "Verwijder %(kind)s %(title)s van negeerlijst",
|
||||
"%(title)s added to the ignore list": "%(title)s toegevoegd aan negeerlijst",
|
||||
"%(title)s removed from the ignore list": "%(title)s verwijderd van negeerlijst",
|
||||
"Triggering refresh for %(title)s": "Activeer een verversing voor %(title)s",
|
||||
"Triggering forced refresh for %(title)s": "Activeer een geforceerde verversing voor %(title)s",
|
||||
"Refresh of item %(item_id)s triggered": "Verversing van item %(item_id)s geactiveerd",
|
||||
"Forced refresh of item %(item_id)s triggered": "Geforceerde verversing van item %(item_id)s geactiveerd",
|
||||
"Ignore %(kind)s \"%(title)s\"": "Negeer %(kind)s \"%(title)s\" ",
|
||||
"Un-ignore %(kind)s \"%(title)s\"": "%(kind)s \"%(title)s\" niet meer negeren",
|
||||
"Refreshing %(title)s": "%(title)s aan het verversen",
|
||||
"Force-refreshing %(title)s": "%(title)s aan het geforceerd verversen",
|
||||
"Extracts the not yet extracted embedded subtitles of all episodes for the current season with all configured default modifications": "Extraheert de nog niet geëxtraheerde geëmbedde ondertiteling van alle afleveringen voor het huidige seizoen met alle geconfigureerde standaard modificaties",
|
||||
"Extracts embedded subtitles of all episodes for the current season with all configured default modifications": "Extraheert geëmbedde ondertiteling van alle afleveringen voor het huidig seizoen met alle geconfigureerde standaard modificaties",
|
||||
"Refreshes %(the_movie_series_season_episode)s, possibly searching for missing and picking up new subtitles on disk": "Ververst %(the_movie_series_season_episode)s, mogelijk zoeken naar missende ondertiteling en nieuwe ondertiteling op schijf",
|
||||
"the movie": "de film",
|
||||
"the series": "de serie",
|
||||
"the episode": "de aflevering",
|
||||
"the season": "het zeizoen",
|
||||
"Change the color of the subtitle": "Wijzig de kleur van de ondertiteling",
|
||||
"Adds the requested color to every line of the subtitle. Support depends on player.": "Voegt de ingestelde kleur toe aan elke regel van de ondertiteling. Ondersteuning is afhankelijk van de speler.",
|
||||
"Basic common fixes": "Basis veel voorkomende reparaties",
|
||||
"Fix common and whitespace/punctuation issues in subtitles": "Repareer veel voorkomende en wit ruimte/leestekens fouten in ondertiteling",
|
||||
"Remove all style tags": "Verwijder alle stijl-tags",
|
||||
"Removes all possible style tags from the subtitle, such as font, bold, color etc.": "Verwijder alle mogelijke stijl-tags uit de ondertiteling, zoals lettertype, dik gedrukt, kleur enz.",
|
||||
"Reverse punctuation in RTL languages": "Draai leestekens in RTL (rechts naar links) talen om",
|
||||
"Some playback devices don't properly handle right-to-left markers for punctuation. Physically swap punctuation. Applicable to languages: hebrew": "Sommige afspeelapparaten kunnen de leestekens van rechts naar links niet goed verwerken. Wissel interpunctie fysiek. Van toepassing op talen: hebreeuws",
|
||||
"Change the FPS of the subtitle": "Wijzig de FPS van de ondertitel",
|
||||
"Re-syncs the subtitle to the framerate of the current media file.": "Synchroniseert de ondertitel naar de framerate van het huidige mediabestand opnieuw.",
|
||||
"Remove Hearing Impaired tags": "Verwijder slecthorende-tags",
|
||||
"Removes tags, text and characters from subtitles that are meant for hearing impaired people": "Verwijderd tags. tekst en karakters uit ondertiteling die zijn bedoeld voor slechthorende mensen",
|
||||
"Fix common OCR issues": "Repareer veel voorkomende OCR fouten",
|
||||
"Fix issues that happen when a subtitle gets converted from bitmap to text through OCR": "Repareer fouten die optreden als een ondertitel is geconverteerd van bitmap naar tekst met OCR",
|
||||
"Change the timing of the subtitle": "Wijzig de timing van de ondertitel",
|
||||
"Adds or substracts a certain amount of time from the whole subtitle to match your media": "Voegt een bepaalde hoeveelheid tijd toe of trekt een bepaalde tijd af van de hele ondertitel zodat deze overeenkomt met uw media",
|
||||
"Available": "Beschikbaar",
|
||||
"Current": "Huidig",
|
||||
"Custom path to advanced_settings.json": "Aangepast pad naar advanced_settings.json"
|
||||
}
|
||||
@@ -10,6 +10,22 @@ Don't expect support if you mess this up.
|
||||
// true: in case of a configured subtitle sub-folder, also clean up the folder containing the media item
|
||||
"thorough_cleaning": false,
|
||||
|
||||
// list of subtitle formats that should be considered as text-based. useful if your media player doesn't natively
|
||||
// support ASS/SSA for example (remove them from this list then)
|
||||
"text_subtitle_formats": ["srt", "ass", "ssa", "vtt", "mov_text"],
|
||||
|
||||
// allow multiple automatic subtitle extractions at the same time; by default, to not clog up the CPU, SZ only
|
||||
// executes one automatic extraction at a time. This does not affect manually started extractions and only affect
|
||||
// multiple agent/metadata update calls. Refreshing a season or a full TV show will still only use one thread.
|
||||
"auto_extract_multithread": false,
|
||||
|
||||
// when the find better subtitles task finds a subtitle for an item that has an active extracted embedded subtitle
|
||||
// set, what should be the minimum score that subtitle has to have in order to be considered better?
|
||||
"find_better_as_extracted_tv_score": 352,
|
||||
"find_better_as_extracted_movie_score": 82,
|
||||
|
||||
"debug_i18n": false,
|
||||
|
||||
// per-provider-config
|
||||
"providers": {
|
||||
// enabled_for: specifies which media type to enable the provider for. does not override global/throttled
|
||||
@@ -26,6 +42,8 @@ Don't expect support if you mess this up.
|
||||
},
|
||||
"opensubtitles": {
|
||||
"enabled_for": ["series", "movies"],
|
||||
"use_https": true,
|
||||
"timeout": 15,
|
||||
},
|
||||
"podnapisi": {
|
||||
"enabled_for": ["series", "movies"],
|
||||
@@ -45,5 +63,21 @@ Don't expect support if you mess this up.
|
||||
"tvsubtitles": {
|
||||
"enabled_for": ["series"],
|
||||
},
|
||||
"hosszupuska": {
|
||||
"enabled_for": ["series"],
|
||||
},
|
||||
"argenteam": {
|
||||
"enabled_for": ["series", "movies"],
|
||||
}
|
||||
},
|
||||
|
||||
"refiners": {
|
||||
"sonarr": {
|
||||
// don't verify HTTPS certificates? Set to True for self-signed certificates
|
||||
"ssl_no_verify": false,
|
||||
},
|
||||
"radarr": {
|
||||
"ssl_no_verify": false,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
MIT License
|
||||
|
||||
Modified version of The Unlicense.
|
||||
Copyright (c) 2018 Hannes Tismer
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means, as long as the author of this software is contacted beforehands
|
||||
and confirms and consents to such use of this software, as well as where
|
||||
and in which software and in which form it is used.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
Copyright for portions of project Sub-Zero are held by Bram Walet, 2014 as part of project Subliminal.bundle.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright 2013 Dean Gardiner <gardiner91@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2015, by the respective authors (see AUTHORS file).
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of the BabelFish authors nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,27 @@
|
||||
Beautiful Soup is made available under the MIT license:
|
||||
|
||||
Copyright (c) 2004-2017 Leonard Richardson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
Beautiful Soup incorporates code from the html5lib library, which is
|
||||
also made available under the MIT license. Copyright (c) 2006-2013
|
||||
James Graham and other contributors
|
||||
@@ -0,0 +1,21 @@
|
||||
This packge contains a modified version of ca-bundle.crt:
|
||||
|
||||
ca-bundle.crt -- Bundle of CA Root Certificates
|
||||
|
||||
Certificate data from Mozilla as of: Thu Nov 3 19:04:19 2011#
|
||||
This is a bundle of X.509 certificates of public Certificate Authorities
|
||||
(CA). These were automatically extracted from Mozilla's root certificates
|
||||
file (certdata.txt). This file can be found in the mozilla source tree:
|
||||
http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1#
|
||||
It contains the certificates in PEM format and therefore
|
||||
can be directly used with curl / libcurl / php_curl, or with
|
||||
an Apache+mod_ssl webserver for SSL client authentication.
|
||||
Just configure this file as the SSLCACertificateFile.#
|
||||
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain
|
||||
one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $
|
||||
@@ -0,0 +1,504 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
Copyright (c) 2014 by Armin Ronacher.
|
||||
|
||||
Click uses parts of optparse written by Gregory P. Ward and maintained by the
|
||||
Python software foundation. This is limited to code in the parser.py
|
||||
module:
|
||||
|
||||
Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved.
|
||||
Copyright (c) 2002-2006 Python Software Foundation. All rights reserved.
|
||||
|
||||
Some rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* The names of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,54 @@
|
||||
Copyright 2017- Paul Ganssle <paul@ganssle.io>
|
||||
Copyright 2017- dateutil contributors (see AUTHORS file)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
The above license applies to all contributions after 2017-12-01, as well as
|
||||
all contributions that have been re-licensed (see AUTHORS file for the list of
|
||||
contributors who have re-licensed their code).
|
||||
--------------------------------------------------------------------------------
|
||||
dateutil - Extensions to the standard Python datetime module.
|
||||
|
||||
Copyright (c) 2003-2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
|
||||
Copyright (c) 2012-2014 - Tomi Pieviläinen <tomi.pievilainen@iki.fi>
|
||||
Copyright (c) 2014-2016 - Yaron de Leeuw <me@jarondl.net>
|
||||
Copyright (c) 2015- - Paul Ganssle <paul@ganssle.io>
|
||||
Copyright (c) 2015- - dateutil contributors (see AUTHORS file)
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The above BSD License Applies to all code, even that also covered by Apache 2.0.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user