Compare commits

...

422 Commits

Author SHA1 Message Date
panni 6d444ebe99 back from dev 2018-06-15 15:51:32 +02:00
panni 237eafed35 release 2.5.7.2663 2018-06-15 15:50:39 +02:00
panni fbc5069fb8 submod: HI: be less aggressive with HI_before_colon_noncaps; fixes #510 2018-06-15 15:42:10 +02:00
panni d23c44589e assrt/supersubtitles: adjust code style 2018-06-15 15:22:11 +02:00
pannal 42cc500b05 Update de.json (POEditor.com) 2018-06-15 15:11:10 +02:00
panni 81760192dc Merge branch 'master' into develop-2.5 2018-06-15 15:06:26 +02:00
panni 2cb077423d addic7ed: use correct throttle hours 2018-06-15 15:05:28 +02:00
panni de8aaaa5e5 addic7ed: raise TooManyRequests and throttle in case of too frequent login 2018-06-15 15:05:02 +02:00
panni b9ebd4e1d6 addic7ed: reduce DownloadLimitExceeded to 3 hours 2018-06-15 15:03:03 +02:00
pannal 8fdf1e841c Merge pull request #501 from morpheus133/provider_supersubtitles
Provider supersubtitles
2018-06-15 15:02:26 +02:00
pannal 9df92d0262 Merge pull request #523 from dimotsai/support-traditional-chinese-2
Add provider assrt.net (Chinese)
2018-06-15 15:02:08 +02:00
panni a07d5aa440 addic7ed: cache login data instead of re-login per search 2018-06-15 05:38:39 +02:00
panni 54bd222605 core: fix plugin_pin_mode 2018-06-15 04:58:43 +02:00
panni 6487258136 i18n: revert to english in case of error 2018-06-15 04:58:29 +02:00
panni d1935a4439 i18n: fix danish placeholders 2018-06-15 04:58:00 +02:00
pannal 026c30642e Update hu.json (POEditor.com) 2018-06-15 04:37:12 +02:00
pannal 036d036a61 Update es.json (POEditor.com) 2018-06-15 04:37:10 +02:00
pannal 2092d44627 Update nl.json (POEditor.com) 2018-06-15 04:37:07 +02:00
pannal c6e7e64ba3 Update de.json (POEditor.com) 2018-06-15 04:37:05 +02:00
pannal a8f5ad6435 Update da.json (POEditor.com) 2018-06-15 04:37:03 +02:00
panni afa0c3a1b0 i18n: add spanish, hungarian, dutch 2018-06-15 04:33:49 +02:00
panni b3132d57b2 Merge branch 'develop-2.5' into i18n 2018-06-15 02:25:57 +02:00
pannal 0a2a6b558f Update de.json (POEditor.com) 2018-06-15 02:24:00 +02:00
pannal adb9926928 Update da.json (POEditor.com) 2018-06-15 02:23:57 +02:00
panni 3ce25007b5 core: notify executable: drop pythonpath from env altogether if it was altered by plex 2018-06-14 15:45:33 +02:00
panni 5690ada2a7 core: notify executable: log error instead of info; properly clean up PYTHONPATH environment variable 2018-06-12 04:22:05 +02:00
panni 76481186e9 core: notify executable: unset PYTHONPATH in env if given and contains Plex 2018-06-10 22:45:18 +02:00
panni 8d2d2341c8 #355 don't use explicit env for mswindows; set working directory to executable directory 2018-06-07 16:10:16 +02:00
panni 4e20d282f7 #355 fix logging 2018-06-07 15:57:03 +02:00
morpheus133 edc3ce1ba4 Correct return value 2018-06-07 11:36:56 +02:00
Dimo Tsai b9249ff09a Fix language order in preferences 2018-06-07 14:08:02 +08:00
panni c3b2ffa97d submod: HI: support "&" and "+" in hi_before_colon 2018-06-06 21:59:31 +02:00
panni 4e3b8ee3c2 opensubtitles: only try logging out if token existed 2018-06-06 18:43:27 +02:00
panni a749ed4837 core: handle "ENGLISH" 2018-06-06 18:40:29 +02:00
panni 67ba6be6e2 #355 try finding executable in path 2018-06-05 14:38:15 +02:00
morpheus133 7a47e6617d - Changed coding to UTF8
- Using .json() insted of manually parsing
2018-06-05 12:29:06 +02:00
panni 4a4c6e7df2 enable logging of notification executable's output and error streams when its exit code is 1, #355 2018-05-30 16:20:38 +02:00
Dimo Tsai 5661528862 Add assrt provider and language converter
Since shooter.cn is not available any longer, implement a new provider
for Chinese as an alternative.
2018-05-29 23:28:21 +08:00
Dimo Tsai 696e9d6b64 Support Traditional Chinese
Since 'zh'always represents simplified Chinese in opensubtitles.org, add
'zh-Hant' as an additional language option in the menu. And fix the language
converter of opensubtitles.
2018-05-29 21:06:24 +08:00
panni c0aa465827 bump dev 2018-05-26 06:07:42 +02:00
panni a6120ae27a libfilebot: use filebot instead of xattr for darwin, just to be safe 2018-05-26 06:06:58 +02:00
panni ba8a165aa5 libfilebot: filebot executable fallback 2018-05-26 05:50:07 +02:00
panni 833d7072ed libfilebot: remove native xattr handling and use filebot itself 2018-05-26 05:06:16 +02:00
panni 9829137001 libfilebot: add sbin folders to environment as well 2018-05-25 17:41:59 +02:00
panni c686214f56 bump dev 2018-05-25 17:40:34 +02:00
panni 2252d7ea6a libfilebot: set correct environment for xattr calls 2018-05-25 17:39:42 +02:00
panni e7fbfca2d7 libfilebot: log output as well in case of returncode 1 2018-05-25 17:25:48 +02:00
panni 9ca959a20a libfilebot: use subprocess.Popen directly instead of check_output 2018-05-25 17:23:42 +02:00
panni bd8e26ecab add additional debug logging in case of filebot attr retrieval error 2018-05-25 16:57:23 +02:00
panni 451b34dceb bump dev 2018-05-25 16:39:16 +02:00
panni 02761db660 and even more logging 2018-05-25 16:38:51 +02:00
panni 42b7e9fa62 add logging for the filebot refiner 2018-05-25 16:34:02 +02:00
panni edf6c25e17 add libfilebot to logged dependencies 2018-05-25 16:32:17 +02:00
panni e91aac65cc add debug info 2018-05-25 16:08:00 +02:00
panni 01d5a18af8 bump dev 2018-05-25 16:06:13 +02:00
panni 70c1142f8d #518 correctly define functions for darwin and win32 2018-05-25 16:05:50 +02:00
panni 8b6b162073 try fixing #518 2018-05-25 15:48:13 +02:00
panni 5199fbe0cb fix #520 2018-05-25 15:28:11 +02:00
panni 924de62dff fix missing string, thanks @morpheus133 2018-05-17 15:08:39 +02:00
panni 4cba7d8684 Merge branch 'develop-2.5' into i18n
# Conflicts:
#	Contents/Code/interface/main.py
#	Contents/Code/interface/menu_helpers.py
2018-05-17 15:05:31 +02:00
panni f3f9ab1360 back to dev 2018-05-17 15:01:14 +02:00
panni 682d1d85ce remove dev flag 2018-05-17 14:58:38 +02:00
panni a1cc9a2049 release 2.5.4.2541 2018-05-17 14:41:36 +02:00
panni a7f7b3e572 bump dev changelog 2018-05-17 14:40:04 +02:00
panni 7c32a7c2c8 providers: addic7ed: set correct headers for endpoints 2018-05-17 14:35:42 +02:00
panni e842579f25 providers: addic7ed: handle empty r.content 2018-05-16 19:17:15 +02:00
panni bdd9134a0e providers: addic7ed: adapt to new (broken) search handling; use new ajax show-season endpoint 2018-05-16 18:39:00 +02:00
panni a01552e88c menu: ignore options: fix plugin not responding, fix unicode strings; resolve #509 2018-05-16 18:11:27 +02:00
panni 824957ae85 bump dev 2018-04-26 06:30:13 +02:00
pannal af335d5565 Update de.json (POEditor.com) 2018-04-26 06:24:25 +02:00
panni 2f9eb51868 i18n: add custom advanced_settings.json setting 2018-04-26 06:21:55 +02:00
panni aebbc17643 Merge branch 'develop-2.5' into i18n
# Conflicts:
#	Contents/Libraries/Shared/subliminal_patch/http.py
2018-04-26 06:21:09 +02:00
panni 84e78e1e20 core: try retrieving advanced_settings.json from the path given, which may be a file path or a directory 2018-04-26 06:19:42 +02:00
panni 89bb747ee3 update provider test; providers: opensubtitles: use e.response, not response in case of http error 2018-04-26 06:13:10 +02:00
panni 62e37dbd09 bump dev 2018-04-25 16:10:23 +02:00
panni edef9cb936 providers: opensubtitles: use new response handling for DownloadSubtitles as well 2018-04-25 16:09:36 +02:00
panni 3ae02c3050 providers: opensubtitles: properly handle opensubtitles responses with the new requests xmlrpc handler 2018-04-25 16:01:26 +02:00
panni a4016616a1 log usage of advanced config; add "adv_cfg_path" config variable to debug output 2018-04-25 15:33:19 +02:00
morpheus133 b4855611c4 removed unnecessary comments 2018-04-18 20:34:39 +02:00
morpheus133 1b44f6d220 Added Hungarian provider supersubtitles
https://www.feliratok.info/

Support:
   Movies
   Series
   SeriesPacks
2018-04-18 20:28:53 +02:00
panni b0f0af087b back to dev 2018-04-17 17:25:35 +02:00
panni 1344f7255d release 2.5.4.2527 2018-04-17 17:14:11 +02:00
panni 39fe3b0fd6 back from dev 2018-04-17 17:02:24 +02:00
panni 0ba676b5e7 Merge branch 'develop-2.5' 2018-04-17 17:02:10 +02:00
panni 4d6897c138 core: get_item: don't fail on socket timeout #498 2018-04-17 14:09:50 +02:00
panni c7c6ba09e9 bump dev 2018-04-16 16:04:30 +02:00
panni c06baa67f1 config: add optional custom path to advanced_settings.json (mostly for NVIDIA SHIELD) 2018-04-16 16:03:08 +02:00
panni cdb7946c00 bump dev 2018-04-16 15:14:10 +02:00
panni bdb5da8df0 core: simplify menu handling; comment out unneeded stuff for now; return to main menu in case of debounce 2018-04-16 15:13:42 +02:00
panni e961c8d3aa fix ZeroDivisionError, resolve #496 2018-04-16 13:24:31 +02:00
panni 3eb1a9eef8 menu: new debounce/menu history implementation, WIP 2018-04-12 19:14:44 +02:00
panni 67aead8fcc providers: addic7ed: reduce show cache to 1 week 2018-04-12 16:07:00 +02:00
panni fd764d0576 core: unrar check: be less verbose 2018-04-10 10:33:29 +02:00
panni dad55d7922 refiners: tvdb: fix spelling 2018-04-10 00:14:50 +02:00
panni fb32772512 core: clamp menu history to 25 items 2018-04-10 00:14:37 +02:00
panni 918ce65acd core: fix scandir errors 2018-04-07 22:51:11 +02:00
panni 9f03b9ee71 core: http cleanup 2018-04-07 05:56:08 +02:00
panni 2235de1a2d Merge remote-tracking branch 'origin/develop-2.5' into develop-2.5 2018-04-07 04:35:00 +02:00
panni 8804c89f04 clarify ssl_no_verify 2018-04-07 04:34:49 +02:00
pannal 2e8805015c Update README.md 2018-04-07 04:32:39 +02:00
pannal f435ca2961 Update README.md 2018-04-07 04:31:04 +02:00
pannal 71c3761b20 Update README.md 2018-04-07 04:30:18 +02:00
panni e4c441043a bump dev 2018-04-06 19:40:17 +02:00
panni 8a655a5d6e don't rely on _check_unrar_tool at all, rely on custom_check 2018-04-06 19:38:51 +02:00
panni 777c21ce87 try local unrar last; explicitly custom check 2018-04-06 19:32:54 +02:00
panni e22ff09691 wow. 2018-04-06 19:23:43 +02:00
panni d0f685e87c moep 2018-04-06 19:18:21 +02:00
panni 8f71c417a9 bump dev 2018-04-06 19:14:30 +02:00
panni b62977c494 more debugging 2018-04-06 19:14:06 +02:00
panni 8d11136c1c restore ORIG_UNRAR_TOOL as well 2018-04-06 19:07:12 +02:00
panni 4a7ea43095 bump dev 2018-04-06 19:05:21 +02:00
panni 8fe4bd2751 add rarfile debug test 2018-04-06 19:04:52 +02:00
panni 38bb819a24 set ORIG_UNRAR_TOOL as well 2018-04-06 18:51:13 +02:00
panni dbe75ad18d bump dev 2018-04-06 18:32:40 +02:00
panni 760441b45a core: check custom unrar tool first, then unrar, then bundled 2018-04-06 18:32:16 +02:00
panni 56645b601b legendastv: correctly check unrar tool 2018-04-06 18:31:57 +02:00
panni 885e4bc99f legendastv: disable if unrar wasn't found 2018-04-06 18:15:10 +02:00
panni b04e5510fd damn underscore check 2018-04-06 18:10:03 +02:00
panni 806000725b explicitly call rarfile._check_unrar_tool() after setting rarfile.UNRAR_TOOL 2018-04-06 18:07:00 +02:00
panni 71270641d3 Revert "core: add own RarFile.read implementation; hopefully fixes #311"
This reverts commit 8d97fb7
2018-04-06 18:05:43 +02:00
panni bf4f2bec91 always use ORIG_OPEN_ARGS for unrar 2018-04-06 17:56:02 +02:00
panni dafad3a7a3 bump dev 2018-04-06 17:49:18 +02:00
panni 182a1cc3fb remove possibly unneeded UNRAR_TOOL fix 2018-04-06 17:48:30 +02:00
panni 4b7664aaa6 use rarfile explicitly instead of individual imports 2018-04-06 17:44:41 +02:00
panni 2050aef1e5 properly set rar.UNRAR_TOOL? 2018-04-06 17:41:51 +02:00
panni 390af30bf6 add cmd debug to rar; set rar.UNRAR_TOOL 2018-04-06 17:22:49 +02:00
panni 698f48b1fd bump dev 2018-04-06 17:03:56 +02:00
panni 2e5cc61ac6 add unrar license 2018-04-06 17:01:18 +02:00
panni 8d97fb7633 core: add own RarFile.read implementation; hopefully fixes #311 2018-04-06 16:54:37 +02:00
panni 8a41c393bb add 3rd party licenses reference to about screen 2018-04-06 14:50:28 +02:00
panni 6ae38359d7 add licenses for 3rd party modules 2018-04-06 14:48:13 +02:00
panni 7ddd1e3497 cache: explicitly reset cache sync and buffer after clear 2018-04-06 14:23:31 +02:00
panni 20a0993aa8 tasks: move provider slack vars to class 2018-04-06 14:10:40 +02:00
panni 57d58056de Merge remote-tracking branch 'origin/i18n' into i18n 2018-04-06 13:58:33 +02:00
panni 06c6fa4d01 core: due to enum value changes, add plugin_mode and plugin_pin_mode suffixes 2018-04-06 13:58:21 +02:00
panni 41f884e129 i18n: lowercase language identifier (to make it actually work) 2018-04-06 13:53:29 +02:00
ukdtom 77a74c8839 Updated danish a tad 2018-04-05 18:59:07 +02:00
panni c198788017 i18n: defaultlocale placeholder 2018-04-05 18:01:28 +02:00
panni 4cbfa21b52 i18n: debug i18n: recognize %i when checking strings 2018-04-05 17:36:34 +02:00
panni f3754de394 i18n: make mod descriptions properly translatable; fix current mods display 2018-04-05 17:34:59 +02:00
pannal d47ad013cd Update de.json (POEditor.com) 2018-04-05 17:26:09 +02:00
panni 8c4372d0d3 i18n: add missing translations; fix summary passthrough with explicit unicode casting 2018-04-05 17:11:45 +02:00
panni 1c7b9145c8 i18n: add missing modification translations 2018-04-05 16:57:07 +02:00
panni c477f53ee6 Merge remote-tracking branch 'origin/i18n' into i18n 2018-04-05 16:51:06 +02:00
panni f99f03dc33 i18n: inject _ into helpers; fix untranslated strings; display translated language name 2018-04-05 16:50:58 +02:00
pannal 2ddd786819 Update de.json (POEditor.com) 2018-04-05 16:50:24 +02:00
pannal 6e604f98e3 Update de.json (POEditor.com) 2018-04-05 16:32:33 +02:00
panni 729404d05f i18n: replace badly translatable terms 2018-04-05 16:01:46 +02:00
panni de50dfdb7c i18n: replace badly translatable terms 2018-04-05 15:09:42 +02:00
panni 7bda522f0a i18n: replace badly translatable terms 2018-04-05 15:02:13 +02:00
panni 6c39fb0649 i18n: remove obsolete translations 2018-04-05 14:56:29 +02:00
panni a7342ac77e i18n: replace badly translatable strings 2018-04-05 14:51:51 +02:00
panni 5d45b8bbdd opensubtitles: log timeout 2018-04-05 14:33:50 +02:00
panni aa0ff38ed7 opensubtitles: add advanced setting for request timeout 2018-04-05 13:03:56 +02:00
panni d55aa3b569 refiners: drone: add advanced setting to skip SSL verification 2018-04-05 13:00:55 +02:00
panni d86a99fb32 refiners: drone: use certifi pem file for https connections 2018-04-05 11:06:22 +02:00
panni c687152724 readme: rename "channel" to "interface" 2018-04-05 01:28:20 +02:00
panni 65ec539875 rename "channel" to "interface" 2018-04-05 01:25:11 +02:00
panni 6dba0792d2 i18n: unicodize the result of _() 2018-04-04 15:32:43 +02:00
pannal df78cecb31 Update de.json (POEditor.com) 2018-04-04 15:28:33 +02:00
pannal 3d8687f69d Update de.json (POEditor.com) 2018-04-04 15:25:24 +02:00
pannal 92196897a9 Update da.json (POEditor.com) 2018-04-04 00:01:03 +02:00
panni 4206edfb13 i18n: revert last commit; add blank de/da 2018-04-04 00:00:30 +02:00
panni c08e63ab80 i18n: add non-blank de.json with languages translated; add base_template.json 2018-04-03 23:56:26 +02:00
panni 03646b4f87 Merge remote-tracking branch 'origin/i18n' into i18n
# Conflicts:
#	Contents/Strings/de.json
2018-04-03 23:52:58 +02:00
panni d9fa860b0c i18n: add non-blank de.json 2018-04-03 23:52:49 +02:00
pannal 93d8494ddc Update de.json (POEditor.com) 2018-04-03 23:49:01 +02:00
panni bd982958fa i18n: add blank de.json 2018-04-03 23:47:52 +02:00
panni e280b62f5c i18n: improve debug mode improper usage detection 2018-04-03 23:27:53 +02:00
panni 2bb050de40 i18n: add optional debug mode that checks correct supply of args/kwargs for a format string 2018-04-03 22:49:26 +02:00
panni f3ed3bf0bf providers: opensubtitles: return compatible status code in case of error
(cherry picked from commit 7945753)
2018-04-03 19:05:23 +02:00
panni 79457536f2 providers: opensubtitles: return compatible status code in case of error 2018-04-03 19:03:33 +02:00
panni 048f930da1 i18n: add missing strings 2018-04-03 19:02:41 +02:00
panni 6aa8108fce i18n: string update finished 2018-04-03 18:48:37 +02:00
panni c234f75d7e i18n: mid-string-update commit WIP 2018-04-03 17:48:57 +02:00
panni 064b634f77 i18n: _: don't fail check on localized string 2018-04-03 17:48:41 +02:00
panni 8d83184cd1 i18n: _: log error instead of raising an exception, which breaks menu code 2018-04-03 17:13:51 +02:00
panni 7a5112bee5 i18n: en: add missing string 2018-04-03 17:09:18 +02:00
panni 0c549c6bda i18n: support kwargs in _ in addition to {} as first non-keyword-argument 2018-04-03 17:03:33 +02:00
panni c48e704502 i18n: replace all F and L calls with _ 2018-04-03 17:00:53 +02:00
panni bec66895d9 Merge branch 'develop-2.5' into i18n
# Conflicts:
#	Contents/Info.plist
2018-04-03 16:57:11 +02:00
panni c9f1e8a8bb core: add i18n module; implement our own version of F and L as _ 2018-04-03 16:54:46 +02:00
ukdtom ac209e7ee2 Prefs translated 2018-04-03 00:27:08 +02:00
ukdtom 525256e15c Everything in /Contents/Code/Interface done 2018-04-02 21:16:49 +02:00
ukdtom 3b8c965f4b refresh_items done 2018-04-02 20:07:08 +02:00
ukdtom 8f8da8e6ea fixed menu_helpers 2018-04-02 20:01:32 +02:00
ukdtom ac9b81abea menu.py done 2018-04-02 19:59:58 +02:00
ukdtom 1c39c55423 menu_helpers done, but look at line 3 2018-04-02 19:20:05 +02:00
ukdtom ca11273b37 menu_helpers.py done, but look at line 3 2018-04-02 19:18:41 +02:00
ukdtom b532a60c3d main.py translation, but look at line 3 for misses 2018-04-02 19:02:36 +02:00
pannal 941662e9f2 Update LICENSE 2018-04-02 17:10:47 +02:00
panni 4d1e4c3ebe core: update rarfile to 2704344 2018-04-02 02:08:47 +02:00
panni f66fd9bcae core: update unrar to 5.60b2 for MacOSX 2018-04-02 01:54:27 +02:00
panni f5c5ecd1b9 core: add rarfile.BadRarFile debug info 2018-04-02 00:59:45 +02:00
panni f9b7855d19 core: early bailout on custom unrar environment variable; try supplied unrar if found and default unrar if applicable 2018-04-01 23:49:13 +02:00
panni 418a8af99a update linux-i386 and macosx-i386 unrar binaries to 5.5.0 2018-04-01 23:43:11 +02:00
ukdtom ce3b4661de Added item_details.py 2018-04-01 23:24:02 +02:00
ukdtom 4b811f38b0 Switch i18n to dev mode :) 2018-04-01 23:19:53 +02:00
ukdtom bba2823065 Fixed nasty syntax for placeholders, as well as some PEP8 2018-04-01 21:42:11 +02:00
ukdtom 5547e9658d Advanced.py done 2018-04-01 21:32:15 +02:00
root e14cbb19f5 make unrars executable 2018-04-01 17:52:45 +02:00
panni 0613a001c5 core: log used unrar location 2018-04-01 07:40:49 +02:00
panni 2970ba69f8 add and check unrar for aarch64, arm (armv5tel), linux/i386, MacOSX/i386 2018-04-01 07:26:48 +02:00
panni 2c6b811d4d add unrar_MacOSX_10.13.2_64bit; try using supplied UnRAR on MacOSX i386 2018-03-31 17:41:06 +02:00
panni d5a3caf961 back to dev 2018-03-31 16:50:24 +02:00
panni 7e64778546 Merge branch 'develop-2.5'
# Conflicts:
#	Contents/Info.plist
#	README.md
2018-03-31 16:49:28 +02:00
panni 1afd0d7c28 add Jose to beta team 2018-03-31 16:47:35 +02:00
panni 3027a3c3e8 Merge remote-tracking branch 'origin/develop-2.5' into develop-2.5 2018-03-31 16:47:11 +02:00
panni 3d7df100ff release 2.5.3.2452 2018-03-31 16:46:59 +02:00
pannal 4de5030196 Update README.md 2018-03-31 03:43:32 +02:00
pannal e3bfe368db Update README.md 2018-03-31 03:34:01 +02:00
panni e45fe0aaa0 add doc 2018-03-30 22:09:16 +02:00
panni 807d758bfa bump dev 2018-03-30 18:43:26 +02:00
panni 7c5164b9a5 core: cleanup #2 2018-03-30 18:41:36 +02:00
panni 1e15fb8e43 core: cleanup 2018-03-30 18:07:48 +02:00
panni ae996b4b9a core: revert last fix; explicitly store subs after writing stored subs to disk 2018-03-30 18:02:43 +02:00
panni 3259a7eec9 core: also store subtitle info on bare_save with set_current 2018-03-30 17:41:37 +02:00
panni 39a5aa1d63 core: metadata storage: kill existing metadata subtitles explicitly upon storing a new one 2018-03-30 17:17:28 +02:00
panni dbe378ad82 core: metadata storage: mediaproxy doesn't support item assignment 2018-03-30 16:53:51 +02:00
panni a316c11974 core: advanced settings: fix typo 2018-03-30 16:37:24 +02:00
panni 2fd05c2464 core: metadata storage: only parse latest metadata subtitle in localmedia 2018-03-30 16:21:37 +02:00
panni 8adabb946e core: metadata storage: only allow one subtitle per language 2018-03-30 16:17:32 +02:00
panni 3f251b9c0e bump dev 2018-03-30 07:04:55 +02:00
panni aadd60c3ad providers: opensubtitles: remove use https setting; add advanced setting; add debug 2018-03-30 07:03:57 +02:00
panni 99cc994865 providers: opensubtitles: mask token 2018-03-30 06:36:59 +02:00
panni da0355ca88 bump dev 2018-03-30 06:31:59 +02:00
panni aaa7c0934a core: update certifi to 2018.01.18 2018-03-30 06:31:27 +02:00
panni 03c70f4dfa providers: opensubtitles: use new requests based transport by default; don't use keepalive 2018-03-30 06:30:01 +02:00
panni 0704609fa5 providers: opensubtitles: try new transport 2018-03-30 05:56:20 +02:00
panni d26569b26f providers: opensubtitles: more debug info; add option to disable HTTPS 2018-03-30 05:26:41 +02:00
panni 007e93e526 providers: opensubtitles: more debug info 2018-03-30 05:16:00 +02:00
panni 8feec0284d bump dev 2018-03-27 17:34:27 +02:00
panni eaa79fb3bd submod: common: reduce multi spaces to one 2018-03-27 17:27:38 +02:00
panni 3af5102e93 submod: OCR: fix III'll=I'll 2018-03-27 17:14:29 +02:00
panni d936460d83 submod: common: extend non_word_only matching 2018-03-27 17:13:05 +02:00
panni f51649c59f fix uppercase Submit
(cherry picked from commit be1e33b)
2018-03-27 00:56:52 +02:00
panni be1e33b555 fix uppercase Submit 2018-03-27 00:56:28 +02:00
panni 059645dec7 menu: list subtitles: only skip items if hash verifiable and verification fails 2018-03-26 22:01:16 +02:00
panni 6439becd7d providers: for non-hash-verifiable providers (napiprojekt in this case) don't try verifying series/season/episode; fixes #478 2018-03-26 17:51:30 +02:00
panni 917fbc1ea2 release 2.5.3.2422 2018-03-26 16:39:45 +02:00
panni c97fee90b7 Merge remote-tracking branch 'origin/master' 2018-03-26 16:39:31 +02:00
panni 35d04946b4 release 2.5.3.2422 2018-03-26 16:39:08 +02:00
panni d0d71d626e providers: opensubtitles: speedup for result format fix 2018-03-26 16:32:02 +02:00
panni 5a1b39c67e providers: addic7ed: use new search endpoint 2018-03-26 16:27:42 +02:00
panni a8cbd37697 bump dev 2018-03-25 16:07:41 +02:00
panni b2bac94009 providers: don't use retry logic in case of ResponseNotReady 2018-03-25 16:05:39 +02:00
panni d88b7e2a17 providers: catch ResponseNotReady in list_subtitles_provider as well 2018-03-25 16:04:09 +02:00
panni 68bf35d83d don't fail on stream.language_code=None, fixes #473 2018-03-25 16:01:20 +02:00
pannal a78e6587ac Update README.md 2018-03-24 06:31:00 +01:00
panni 21f715a321 back to dev 2018-03-24 03:13:12 +01:00
panni 18a5dfd81f update version to 2.5.3.2414 2018-03-24 03:12:40 +01:00
panni 2a7b5e2efb back from dev 2018-03-24 03:11:41 +01:00
panni 0d63b0361f Merge branch 'develop-2.5' 2018-03-24 03:11:28 +01:00
panni 4e301ddd24 release 2.5.3.2408 2018-03-24 03:11:04 +01:00
panni bc182276ac submod: common: replace more than 3 consecutive dots with 3 dots; also replace three dashes with em dash 2018-03-24 02:59:06 +01:00
panni 4980523d10 core: don't fail on empty plex item API result 2018-03-24 02:37:33 +01:00
panni 85baf58b55 providers: hosszupuska: improve implementation 2018-03-24 02:32:46 +01:00
panni d7a4d02564 providers: argenteam: streamline; improve subtitle repr 2018-03-24 02:31:16 +01:00
panni 0e6f4c45db submod: HI: HI_before_colon_noncaps, don't assume single quotes are sentence enders 2018-03-23 22:17:24 +01:00
panni 932cadce3c providers: opensubtitles: add fallback for dict based query response in contrast to list/array based 2018-03-23 14:17:08 +01:00
panni 3926ea9c69 providers: argenteam: add subtitle.releases 2018-03-20 21:17:55 +01:00
panni dd1495c881 update year 2018-03-20 13:35:33 +01:00
panni 8c27e6aade bump dev 2018-03-20 13:35:21 +01:00
panni ba2774eeb5 providers: argenteam: avoid unnecessary typecasting 2018-03-20 13:12:13 +01:00
panni 8e854a8d64 providers: argenteam: doc 2018-03-20 13:09:41 +01:00
panni 86f5ed198f providers: argenteam: logging consistency 2018-03-20 13:08:05 +01:00
panni cc57520c71 providers: argenteam: rename multi_id_throttle to multi_result_throttle 2018-03-20 13:06:12 +01:00
panni 8d9f8960b2 providers: argenteam: add debug output; try to be even faster with movies in case of matching imdb id 2018-03-20 12:58:27 +01:00
panni f66573620b providers: argenteam: try quick matching movies; reduce provider impact 2018-03-20 12:41:36 +01:00
panni 3544a0e7f8 providers: argenteam: improve subtitle repr #2 2018-03-20 12:00:18 +01:00
panni 9c9db90886 providers: argenteam: improve subtitle repr 2018-03-20 11:59:24 +01:00
panni c4bc4d22e9 providers: argenteam: fix empty results 2018-03-20 11:55:31 +01:00
panni b107c70a0c providers: argenteam: fix downloading; search for multiple IDs; implement multi-id-search-throttling 2018-03-20 11:54:13 +01:00
Tommy Mikkelsen 084069441f Add files via upload 2018-03-20 00:10:15 +01:00
Tommy Mikkelsen 8b01433e61 Add files via upload
Resized images
2018-03-20 00:04:30 +01:00
panni b72902b8f4 providers: argenteam: remove unnecessary json import 2018-03-19 19:32:44 +01:00
panni 354e455ae7 remove debug print 2018-03-19 19:27:48 +01:00
panni 8aaed47e39 bump dev 2018-03-19 19:23:50 +01:00
panni c7598aaf12 update default prefs and advanced settings template for argenteam 2018-03-19 19:23:28 +01:00
panni cbe2d16d9b providers: argenteam: reimplement to also support movies 2018-03-19 19:22:48 +01:00
panni 953eb97513 bump dev 2018-03-19 18:42:14 +01:00
panni b340b3b699 providers: argenteam: implement as SZ provider fully, too many changes over the original subliminal pull request 2018-03-19 18:40:52 +01:00
panni f9f2579904 providers: argenteam: identify as Sub-Zero, not subliminal 2018-03-19 18:33:43 +01:00
panni 3a90653edd providers: argenteam: cleanup 2018-03-19 18:29:38 +01:00
panni a8ae18f43c providers: argenteam: compute and parse release_info properly; bail out if returned item wasn't an episode 2018-03-19 18:22:03 +01:00
panni c235dd934a bump dev 2018-03-19 18:06:43 +01:00
panni 3e7c2cb0c2 core: scoring: assume title match on tvdb_id match 2018-03-19 18:06:02 +01:00
panni 1c9398b5b9 providers: argenteam: first working implementation 2018-03-19 18:05:47 +01:00
panni 6a9c818e67 tasks: search all recently added missing: fix attribute access on missing stored subtitle info 2018-03-19 17:26:38 +01:00
panni 753baf85b6 providers: first argenteam subzero implementation 2018-03-19 17:24:05 +01:00
panni 7685c2a6b7 providers: add argenteam provider (spanish), from PR mmiraglia/subliminal/tree/feature/add_argenteam 2018-03-19 17:02:13 +01:00
panni cf1203566e 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 2018-03-19 16:56:07 +01:00
panni 052e6a475b core: treat 23.976, 23.98, 24.0 as equal 2018-03-19 16:39:14 +01:00
panni 8890acef3a core: update patches to newest subliminal 2018-03-19 16:23:42 +01:00
panni 72570ee21b tvsubtitles: update patches to newest subliminal 2018-03-19 16:21:00 +01:00
panni 100c94ad83 addic7ed: update patches to newest subliminal 2018-03-19 16:19:19 +01:00
panni 2ea3bf20a7 subliminal: reapply threadpoolexecutor windows fix 2018-03-19 16:16:49 +01:00
panni b1cb7c7259 subliminal: reapply strptime fix 2018-03-19 16:16:11 +01:00
panni 7510dfc5c5 core: update subliminal to 4ad5d31 2018-03-19 16:15:38 +01:00
pannal b18bbba23f Update README.md 2018-03-18 04:59:53 +01:00
panni 4e28cea2a3 config: rename "Fix common whitespace/punctuation issues in subtitles" to "Fix common issues in subtitles" 2018-03-18 01:21:14 +01:00
panni a9bafc5efd advanced_settings: clarify auto_extract_multithread 2018-03-18 00:54:28 +01:00
panni a04ff3343b submod: fix empty content if only non-line-mods were used, no line-mods; fixes #449 2018-03-18 00:31:18 +01:00
panni aa09fb28d2 bump dev 2018-03-17 16:45:53 +01:00
panni e6900c18b9 core/menu/submod: add reverse_rtl modification for Hebrew; fixes #409 2018-03-17 16:41:49 +01:00
panni 221a17a5af Merge branch 'heb_test' into develop-2.5 2018-03-17 16:21:38 +01:00
panni fc638c608b core: only allow one automatic extraction at a time; add optional advanced settings "auto_extract_multithread" 2018-03-17 16:19:59 +01:00
panni 71d9d96d81 core: make download_best_subtitles testable again by making language hook optional 2018-03-17 15:46:23 +01:00
panni 5a8b999509 core: reduce encoding logging even more
menu: simplify season extract embedded; only set current if needed, only refresh item if needed
2018-03-17 03:59:46 +01:00
panni 720d7e9d8d bump dev 2018-03-17 03:16:09 +01:00
panni c69be5934d core: reduce encoding change log spam 2018-03-17 03:15:35 +01:00
panni dae186fb03 core: fix set_current regression 2018-03-17 03:12:31 +01:00
panni 076ad78355 remove comment 2018-03-17 01:55:15 +01:00
panni 421aa3a95c core: skip duplicate data aggregation when auto extracting embedded subtitles 2018-03-17 01:54:57 +01:00
panni 153d186a1c core: auto extract embedded subtitles in a separate thread 2018-03-17 01:14:24 +01:00
panni 2238835868 submod: common: also count lines only consisting of dots as removable 2018-03-16 23:46:38 +01:00
panni e0be4542ab bump dev 2018-03-16 15:47:51 +01:00
panni fab841bc7a core: automatic extraction: add config setting to indicate whether there should be an immediate search for available subtitles after extraction or not (default: off) 2018-03-16 15:10:31 +01:00
panni 789a28a966 core: don't change our environ 2018-03-16 14:50:48 +01:00
panni 7cde652ed1 core: remove LD_LIBRARY_PATH from environment before calling notification executable 2018-03-16 14:49:53 +01:00
panni 5359116e72 providers: enable subscene by default 2018-03-16 14:45:01 +01:00
panni 17edfd215d bump dev 2018-03-16 14:42:17 +01:00
panni e292b46cca core: addic7ed: use random user agent by default (enforce for existing configs) 2018-03-16 14:41:53 +01:00
panni d091b20ebe core: addic7ed: use random user agent by default 2018-03-16 14:36:35 +01:00
panni 50a53562a1 core: expand user agent list 2018-03-16 14:36:15 +01:00
panni 55a479590b core: try finding Plex Transcoder in Resources folder, as well, hopefully fixes #460 2018-03-16 14:11:36 +01:00
panni 8874bb64fb core: extract embedded: let ffmpeg auto convert mov_text/tx3g to srt 2018-03-15 17:53:46 +01:00
panni 38afba3075 core: extract embedded: don't transcode to SRT using ffmpeg (Plex Transcoder), do the transcoding later using pysubs2; fixes offset issues 2018-03-15 17:42:18 +01:00
panni ba48e30128 bump dev 2018-03-15 15:18:21 +01:00
panni 77397b6877 submod: OCR: "H i." = "Hi." 2018-03-15 15:17:42 +01:00
panni f50fa0554a submod: common: don't break phone numbers (more than one spaced number pair found) 2018-03-15 15:14:06 +01:00
panni d0dd9f629d core: correctly skip immediately searching for new subtitle after successfully extracting embedded 2018-03-15 15:07:35 +01:00
panni c82637e760 core: fix automatic extraction of unknown embedded subtitle streams 2018-03-15 15:05:52 +01:00
panni 152cfb3f07 menu: fix season extract embedded 2018-03-14 16:28:38 +01:00
panni 7f579181fd bump dev 2018-03-14 16:26:03 +01:00
panni 3e0f39b6f1 submod: HI: count dots as chars inside brackets, for abbreviated names 2018-03-14 16:24:19 +01:00
panni 244d3b1a5b submod: common: don't uppercase after abbreviations 2018-03-14 16:21:07 +01:00
panni 7c24302f7c submod: common: double dash is actually em dash; fix removal 2018-03-14 16:12:48 +01:00
panni 6cafc3a1e8 submod: OCR/HI: don't remove stuff inside quotes 2018-03-14 15:48:23 +01:00
panni 1ab0d31baa bump dev 2018-03-13 18:29:50 +01:00
panni b2fadc5a90 submod: HI: correctly handle tags inside lines when checking for brackets 2018-03-13 18:19:41 +01:00
panni 38f3d85909 submod: fix style tags in line can result in no modifications at all 2018-03-13 18:06:31 +01:00
panni 3694100265 submod: only log processor name, not the full class 2018-03-13 18:01:30 +01:00
panni af44f271ab submod: correctly use the debug mods flag 2018-03-13 17:53:41 +01:00
panni 9984f6aef9 submod: shift timing: inversely reverse value list to make it easier accessible 2018-03-13 17:32:56 +01:00
panni 51a1debc39 Merge branch 'develop-2.5' into heb_test 2018-03-13 17:25:07 +01:00
panni b8a68f62a0 #460 don't bother auto extracting subtitles if the transcoder wasn't found; warn 2018-03-13 16:57:23 +01:00
panni 5ded188f51 add hosszupuska to advanced_settings.json; make text based subtitle formats configurable resolve #464 2018-03-13 16:45:54 +01:00
panni 12c5dda1fa bump dev 2018-03-06 02:49:10 +01:00
panni 25146049bf Merge branch 'master' into develop-2.5 2018-03-06 02:48:28 +01:00
pannal 5598ee0c78 Merge pull request #445 from morpheus133/hosszupuskasub_provider
Add Hungarian provider Hosszupuska
2018-03-06 02:45:30 +01:00
pannal 6e4b0cbcbf Merge pull request #456 from Ineluctable/patch-1
Update Channels to Plugins on install instructions
2018-03-06 02:42:16 +01:00
Ineluctable 572cf29974 Update Channels to Plugins on install instructions
Plex doesn't show the option as Channels anymore, it shows Plugins.
2018-03-05 13:45:30 -06:00
morpheus133 5601d19002 - Instead of parsing release information manually use releases as visible in other providers.
- Add asked_for_episode
2018-03-04 20:41:25 +01:00
panni e81dd5df76 core: subtitle srtorage: correctly skip blacklist key 2018-03-04 17:36:53 +01:00
panni e7919d5a47 bump dev 2018-03-04 06:50:45 +01:00
panni 6f634fbc21 #454 support extracting forced embedded subtitles and storing them as such; display message when extracting via menu 2018-03-04 06:50:02 +01:00
panni 7478ece1ff use the same forced detection for extract embedded; add fixme 2018-03-04 06:23:59 +01:00
panni cd72b6f477 bump dev 2018-03-04 06:15:18 +01:00
panni fab96de4c7 add fixme 2018-03-04 06:08:40 +01:00
panni 0ffa17cf67 #454 remove debug logging; exit early if embedded scanning isn't wanted 2018-03-04 06:06:51 +01:00
panni 777549a15f #454 embedded streams have an index, which is better than checking for inexistant stream_key 2018-03-04 05:59:27 +01:00
panni c07ded004d #454 attribute check 2018-03-04 05:50:41 +01:00
panni da3e96a9d8 #454 smarter stream title detection 2018-03-04 05:47:17 +01:00
panni d6e8a03ddf #454 treat "forced" contained by stream.title = forced subtitle 2018-03-04 05:40:25 +01:00
panni b13cbd1e54 #454 also treat stream.title=="forced" as forced subtitle 2018-03-04 05:36:42 +01:00
panni 6b2e5c154b #454 add more embedded stream logging 2018-03-04 02:39:14 +01:00
panni 137a4d1e0d core: fix embedded subtitle language detection; add debug log 2018-03-03 22:14:45 +01:00
panni 1725550acc core: fix unpacking of packs without asked-for-release-group 2018-03-03 14:40:55 +01:00
panni bd91e173b0 core: expand exception handling when trying to save subtitle 2018-03-03 04:29:56 +01:00
panni 47a11b3e64 core: correctly skip blacklist entries when iterating through currently known subs 2018-03-02 21:44:25 +01:00
panni b5e57519ff back to dev 2018-03-01 16:45:44 +01:00
panni 20845bbcd4 release 2.5.0.2287 2018-03-01 16:34:45 +01:00
panni 739c10ade6 submod: common: require at least one music symbol when fixing 2018-03-01 16:30:02 +01:00
panni 14ea2d72a7 Merge branch 'develop-2.1' 2018-03-01 16:19:01 +01:00
panni 4a9ea97ea1 update doc 2018-03-01 12:51:48 +01:00
panni b017a94353 update doc 2018-03-01 12:51:39 +01:00
panni 15b65dd844 core: better embedded subtitle stream language detection 2018-03-01 12:46:19 +01:00
morpheus133 079ea8c39d - Added mixin for archive handling (also add rar support)
- Remove LXML checking  (Needed only for official subliminal)
- Added fix_inconsistent_naming handling
2018-03-01 08:09:52 +01:00
panni 4b949dcd72 core: support mov_text for embedded subtitle extraction 2018-02-28 18:42:58 +01:00
panni 2626cf4253 core: handle nld for embedded subs 2018-02-28 18:14:59 +01:00
panni b260c8aaec config: clarify subscene being only enabled for TV shows by default 2018-02-28 11:44:35 +01:00
panni 1ece46473b bump dev 2018-02-27 17:45:56 +01:00
panni 890c3cc8b0 core: fix remove crap from filename; fixes non-matched release group in refiners 2018-02-27 15:15:25 +01:00
morpheus133 7b45c9f1c5 Add Hungarian provider Hosszupuska
link: http://hosszupuskasub.com/
2018-02-27 12:53:08 +01:00
panni 58fb2f5ea6 bump dev 2018-02-27 12:48:40 +01:00
panni a79f3e47ba submod: OCR: fix it'sjust, isn'tjust, Iam, Ican 2018-02-27 12:37:15 +01:00
panni b3b9db9ff6 core: get subtitles from archive: remove redundant get 2018-02-27 12:33:14 +01:00
panni 9aed245241 core: get subtitles from archive: don't assume any attributes in guess 2018-02-27 12:32:28 +01:00
panni aa03fdb445 core: get subtitles from archive: don't assume an episode match 2018-02-27 12:31:18 +01:00
panni 7cb8356598 submod: HI: HI_before_colon_noncaps: also consider multiple dashes a sentence 2018-02-27 12:28:37 +01:00
panni ac347755fd submod: HI: separate text before colon into two checks; try not to break actual sentences before colon 2018-02-27 12:09:26 +01:00
panni b16cb15e88 submod: HI: fix remove music-symbol-only lines 2018-02-27 11:36:28 +01:00
panni 4989c37964 submod: HI: remove music-symbol-only lines 2018-02-27 11:30:55 +01:00
panni 06849c5814 submod: common: fix music symbols 2018-02-27 11:26:53 +01:00
panni 78b67a6f5e submod: OCR: correctly fix broken HI tag colons 2018-02-27 11:22:58 +01:00
panni acf79df4d0 bump dev 2018-02-26 16:45:04 +01:00
panni bc5a9caf63 submod: OCR: fix "Ls"="Is" 2018-02-26 14:56:48 +01:00
panni 7b34b07cdc hard error on IOError while scanning videos; warn about hard error in menu #444 2018-02-26 10:06:52 +01:00
panni 8df1a1bf17 bump dev 2018-02-23 17:03:54 +01:00
panni 1143b0f2d2 providers: opensubtitles: try re-initializing the provider on ResponseNotReady 2018-02-23 17:01:42 +01:00
panni 86883336fd providers: opensubtitles: catch ResponseNotReady 2018-02-23 16:51:47 +01:00
panni 62d77c5811 #441 #440 add scandir listdir fallback mechanism 2018-02-23 15:22:39 +01:00
panni 8397dddbbe #441 patch sys.getfilesystemencoding 2018-02-23 12:28:48 +01:00
panni 47ef94d8c3 submod: common: rename CM_underscore_only to CM_non_word_only 2018-02-18 00:39:47 +01:00
panni 8aa4a485ed reduce main icon size 2018-02-17 17:08:47 +01:00
panni cb4ef9c9ea submod: common: dash underscore empty 2018-02-17 03:51:01 +01:00
panni 2f80852a7c submod: add entry index to debug 2018-02-16 13:21:08 +01:00
panni 190a580642 submod: common: remove lines that consists only of underscores; update test.srt 2018-02-16 13:18:44 +01:00
panni 6ba85f5069 submod: common: don't break "-- addicted --" 2018-02-16 13:13:54 +01:00
pannal 707b5921fb Update README.md 2018-02-16 10:05:05 +01:00
panni 2e25e68444 refiners: drone: add http:// to base url if needed 2018-02-15 19:31:01 +01:00
pannal 034260e426 Update README.md 2018-02-15 16:59:11 +01:00
pannal b4eda8bbff Update README.md 2018-02-15 09:46:51 +01:00
panni 93a1b7fb52 back to dev 2018-02-15 09:45:53 +01:00
panni 8ef44c3520 release 2.5.0.2247 2018-02-15 09:45:27 +01:00
panni 449de57fc7 config: debug sonarr/radarr 2018-02-15 09:44:59 +01:00
panni cbe29e233d Merge remote-tracking branch 'origin/master' 2018-02-15 09:42:11 +01:00
panni bef56ff124 core: fix wrong episode matches on hash match 2018-02-15 09:34:31 +01:00
panni c1e13e520b back to dev 2018-02-14 16:31:02 +01:00
panni ae8473183d second try 2017-12-28 22:44:42 +01:00
panni 69fb328b50 set reverse_rtl order to 50 2017-12-28 15:09:16 +01:00
134 changed files with 9846 additions and 1057 deletions
+206
View File
@@ -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
View File
@@ -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"):
+110 -68
View File
@@ -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")
)
+158
View File
@@ -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
+154 -93
View File
@@ -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)
+84 -62
View File
@@ -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"
)
)
+72 -44
View File
@@ -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 -----")
+52 -47
View File
@@ -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
+11 -2
View File
@@ -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)
+35 -28
View File
@@ -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()
+6
View File
@@ -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
View File
@@ -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 ...")
+30 -12
View File
@@ -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):
+106
View File
@@ -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")
+27 -8
View File
@@ -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()
+5 -3
View File
@@ -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
+27 -20
View File
@@ -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)
+14 -6
View File
@@ -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
+1 -1
View File
@@ -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
+46 -26
View File
@@ -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
View File
@@ -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
View File
@@ -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 @@
&lt;h1&gt;Sub-Zero for Plex&lt;/h1&gt;&lt;i&gt;Subtitles done right&lt;/i&gt;
Version 2.5.0.2241
Version 2.5.7.2663
Originally based on @bramwalet's awesome &lt;a href=&quot;https://github.com/bramwalet/Subliminal.bundle&quot;&gt;Subliminal.bundle&lt;/a&gt;
@@ -44,7 +44,9 @@ Score info: &lt;a href=&quot;http://v.ht/szscores&quot;&gt;http://v.ht/szscores&
Plex thread: &lt;a href=&quot;https://forums.plex.tv/discussion/186575&quot;>https://forums.plex.tv/discussion/186575&lt;/a&gt;
Github: &lt;a href=&quot;https://github.com/pannal/Sub-Zero.bundle&quot;&gt;https://github.com/pannal/Sub-Zero&lt;/a&gt;
panni, 2017
3rd party licenses: &lt;a href=&quot;https://github.com/pannal/Sub-Zero.bundle/tree/master/Licenses&quot;&gt;https://github.com/pannal/Sub-Zero.bundle/tree/master/Licenses&lt;/a&gt;
panni, 2018
&lt;/div&gt;
</string>
</dict>
Binary file not shown.
Binary file not shown.
Binary file not shown.
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"
+4 -4
View File
@@ -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())
+51 -14
View File
@@ -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__":
+20 -3
View File
@@ -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]);"
+65 -4
View File
@@ -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()
+11 -2
View File
@@ -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 pletykafszek (3. vad)</div>
sub_hun_name = re.findall(r'(?<=<div class="magyar">).*(?= -)', str(sub_hun_name))[0]
else:
# <div class="magyar">A holnap legendi - 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&amp;fnev=DCs Legends of Tomorrow - 03x11 - Here I Go Again.SVA.English.C.orig.Addic7ed.com.srt&amp;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')
+1 -1
View File
@@ -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
+25 -2
View File
@@ -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):
+11 -3
View File
@@ -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 -2
View File
@@ -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:
+28 -7
View File
@@ -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

+400
View File
@@ -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"
}
+402
View File
@@ -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
View File
@@ -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"
}
+400
View File
@@ -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"
}
+400
View File
@@ -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"
}
+400
View File
@@ -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"
}
+34
View File
@@ -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,
},
}
}
+17 -24
View File
@@ -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.
+13
View File
@@ -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.
+25
View File
@@ -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.
+27
View File
@@ -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
+21
View File
@@ -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 $
+504
View File
@@ -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!
+38
View File
@@ -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.
+54
View File
@@ -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