├── .flake8 ├── .pylintrc ├── resources ├── __init__.py ├── lib │ ├── __init__.py │ ├── UniversalAnalytics │ │ ├── __init__.py │ │ ├── LICENSE │ │ └── HTTPLog.py │ ├── compat.py │ ├── ui │ │ ├── __init__.py │ │ └── xmldialogs.py │ ├── NetflixCredentials.py │ ├── playback │ │ ├── bookmarks.py │ │ ├── section_skipping.py │ │ ├── stream_continuity.py │ │ └── __init__.py │ ├── storage.py │ ├── NetflixHttpRequestHandler.py │ ├── MSLHttpRequestHandler.py │ ├── utils.py │ ├── MSLCrypto.py │ ├── NetflixCommon.py │ └── MSLMediaDrm.py ├── test │ ├── __init__.py │ ├── mocks │ │ ├── __init__.py │ │ ├── MinimalClassMocks.py │ │ └── LoggerMocks.py │ ├── test_MSL.py │ ├── test_Library.py │ ├── test_Navigation.py │ ├── test_NetflixSession.py │ ├── test_NetflixHttpRequestHandler.py │ ├── test_NetflixHttpSubRessourceHandler.py │ ├── test_KodiHelper.py │ ├── test_Utils.py │ └── test_KodiHelper_Dialogs.py ├── icon.png ├── fanart.jpg ├── media │ ├── fanart.xcf │ └── netflix_logo.xcf ├── screenshot-01.jpg ├── screenshot-02.jpg ├── screenshot-03.jpg ├── skins │ └── default │ │ ├── media │ │ └── smallbutton.png │ │ └── 1080i │ │ ├── plugin-video-netflix-Skip.xml │ │ └── plugin-video-netflix-SaveStreamSettings.xml ├── language │ ├── resource.language.sv_sv │ │ └── strings.po │ ├── resource.language.nl_nl │ │ └── strings.po │ ├── resource.language.he_il │ │ └── strings.po │ ├── resource.language.pl_pl │ │ └── strings.po │ ├── resource.language.hr_hr │ │ └── strings.po │ ├── resource.language.sk_sk │ │ └── strings.po │ ├── resource.language.ko_kr │ │ └── strings.po │ ├── resource.language.en_gb │ │ └── strings.po │ ├── resource.language.it_it │ │ └── strings.po │ ├── resource.language.fi_fi │ │ └── strings.po │ ├── resource.language.fr_fr │ │ └── strings.po │ ├── resource.language.pt_pt │ │ └── strings.po │ ├── resource.language.es_es │ │ └── strings.po │ └── resource.language.de_de │ │ └── strings.po └── settings.xml ├── .travis.yml ├── docs ├── index.rst └── conf.py ├── requirements.txt ├── .editorconfig ├── .codeclimate.yml ├── PULL_REQUEST_TEMPLATE.md ├── addon.py ├── LICENSE.txt ├── ISSUE_TEMPLATE.md ├── .gitignore ├── makefile ├── setup.py ├── Code_of_Conduct.md ├── README.md └── addon.xml /.flake8: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/test/mocks/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/lib/UniversalAnalytics/__init__.py: -------------------------------------------------------------------------------- 1 | import resources.lib.UniversalAnalytics.Tracker 2 | -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asciidisco/plugin.video.netflix/HEAD/resources/icon.png -------------------------------------------------------------------------------- /resources/fanart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asciidisco/plugin.video.netflix/HEAD/resources/fanart.jpg -------------------------------------------------------------------------------- /resources/media/fanart.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asciidisco/plugin.video.netflix/HEAD/resources/media/fanart.xcf -------------------------------------------------------------------------------- /resources/screenshot-01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asciidisco/plugin.video.netflix/HEAD/resources/screenshot-01.jpg -------------------------------------------------------------------------------- /resources/screenshot-02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asciidisco/plugin.video.netflix/HEAD/resources/screenshot-02.jpg -------------------------------------------------------------------------------- /resources/screenshot-03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asciidisco/plugin.video.netflix/HEAD/resources/screenshot-03.jpg -------------------------------------------------------------------------------- /resources/media/netflix_logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asciidisco/plugin.video.netflix/HEAD/resources/media/netflix_logo.xcf -------------------------------------------------------------------------------- /resources/skins/default/media/smallbutton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asciidisco/plugin.video.netflix/HEAD/resources/skins/default/media/smallbutton.png -------------------------------------------------------------------------------- /resources/test/test_MSL.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Module: MSL 3 | # Author: asciidisco 4 | # Created on: 11.10.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Tests for the `MSL` module""" 8 | 9 | import unittest 10 | import mock 11 | from resources.lib.MSL import MSL 12 | 13 | class MSLTestCase(unittest.TestCase): 14 | pass 15 | -------------------------------------------------------------------------------- /resources/lib/compat.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | #define >= 3 values here 4 | itername = 'items' 5 | string_encoding = 'unicode' 6 | compat_unicode = str 7 | compat_basestring = str 8 | 9 | if (sys.version_info < (3, 0)): 10 | itername = 'iteritems' 11 | string_encoding = 'utf-8' 12 | compat_unicode = unicode 13 | compat_basestring = basestring 14 | -------------------------------------------------------------------------------- /resources/test/test_Library.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Module: Library 3 | # Author: asciidisco 4 | # Created on: 11.10.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Tests for the `Library` module""" 8 | 9 | import unittest 10 | import mock 11 | from resources.lib.Library import Library 12 | 13 | class LibraryTestCase(unittest.TestCase): 14 | pass 15 | -------------------------------------------------------------------------------- /resources/test/test_Navigation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Module: Navigation 3 | # Author: asciidisco 4 | # Created on: 11.10.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Tests for the `Navigation` module""" 8 | 9 | import unittest 10 | import mock 11 | from resources.lib.Navigation import Navigation 12 | 13 | class NavigationTestCase(unittest.TestCase): 14 | pass 15 | -------------------------------------------------------------------------------- /resources/test/test_NetflixSession.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Module: NetflixSession 3 | # Author: asciidisco 4 | # Created on: 11.10.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Tests for the `NetflixSession` module""" 8 | 9 | import unittest 10 | import mock 11 | from resources.lib.NetflixSession import NetflixSession 12 | 13 | class NetflixSessionTestCase(unittest.TestCase): 14 | pass 15 | -------------------------------------------------------------------------------- /resources/test/mocks/MinimalClassMocks.py: -------------------------------------------------------------------------------- 1 | class MockClass(object): 2 | def __init__(self): 3 | pass 4 | def foo(): 5 | pass 6 | def bar(): 7 | pass 8 | 9 | class Error_resp_401(object): 10 | status_code = 401 11 | pass 12 | 13 | class Error_resp_500(object): 14 | status_code = 500 15 | pass 16 | 17 | class Success_resp(object): 18 | status_code = 200 19 | def json(self): 20 | return {'foo': 'bar'} 21 | -------------------------------------------------------------------------------- /resources/test/test_NetflixHttpRequestHandler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Module: NetflixHttpRequestHandler 3 | # Author: asciidisco 4 | # Created on: 11.10.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Tests for the `NetflixHttpRequestHandler` module""" 8 | 9 | import unittest 10 | import mock 11 | from resources.lib.NetflixHttpRequestHandler import NetflixHttpRequestHandler 12 | 13 | class NetflixHttpRequestHandlerTestCase(unittest.TestCase): 14 | pass 15 | -------------------------------------------------------------------------------- /resources/test/test_NetflixHttpSubRessourceHandler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Module: NetflixHttpSubRessourceHandler 3 | # Author: asciidisco 4 | # Created on: 11.10.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Tests for the `NetflixHttpSubRessourceHandler` module""" 8 | 9 | import unittest 10 | import mock 11 | from resources.lib.NetflixHttpSubRessourceHandler import NetflixHttpSubRessourceHandler 12 | 13 | class NetflixHttpSubRessourceHandlerTestCase(unittest.TestCase): 14 | pass 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sudo: false 3 | language: python 4 | 5 | python: 6 | - "2.7" 7 | 8 | script: 9 | - pip install pycryptodomex 10 | - make all 11 | - touch ./_build/.nojekyll 12 | - if [ "$TRAVIS_BRANCH" == "master" ]; then make rere; else exit 0; fi 13 | 14 | deploy: 15 | - provider: pages 16 | skip_cleanup: true 17 | github_token: $GITHUB_TOKEN 18 | local_dir: _build 19 | target_branch: gh-pages 20 | "on": 21 | branch: master 22 | 23 | notifications: 24 | email: 25 | on_success: change 26 | on_failure: always 27 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. plugin.video.netflix documentation master file, created by 2 | sphinx-quickstart on Wed Apr 26 16:27:25 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | :github_url: https://github.com/asciidisco/plugin.video.netflix 7 | 8 | Code Docs 9 | ========= 10 | 11 | .. automodule:: resources.lib.utils 12 | :members: 13 | :private-members: 14 | 15 | Indices and tables 16 | ================== 17 | 18 | * :ref:`genindex` 19 | * :ref:`modindex` 20 | * :ref:`search` 21 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pycrypto==2.6.1 2 | pycryptodomex==3.4.5 3 | codeclimate-test-reporter==0.2.1 4 | nose==1.3.7 5 | pylint==1.6.5 6 | flake8==3.3.0 7 | mccabe==0.6.1 8 | pycodestyle==2.3.1 9 | pyflakes==1.5.0 10 | git+https://github.com/romanvm/Kodistubs.git#egg=Kodistubs 11 | httpretty==0.8.14 12 | mock==1.0.1 13 | requests==2.12.4 14 | pydes==2.0.1 15 | radon==2.1.1 16 | Sphinx==1.5.5 17 | sphinx_rtd_theme==0.2.4 18 | m2r==0.1.12 19 | git+https://github.com/asciidisco/kodi-release-helper.git#egg=kodi-release-helper 20 | dennis==0.9 21 | blessings==1.6 22 | demjson==2.2.4 23 | yamllint==1.8.1 24 | restructuredtext_lint==1.1.1 25 | -------------------------------------------------------------------------------- /resources/test/mocks/LoggerMocks.py: -------------------------------------------------------------------------------- 1 | from resources.lib.utils import log 2 | 3 | class TestLoggerWithNoArgs(object): 4 | def __init__(self, logger_1): 5 | self.log = logger_1 6 | @log 7 | def to_be_logged(self): 8 | return None 9 | 10 | class TestLoggerWithArgs(object): 11 | def __init__(self, logger_2): 12 | self.log = logger_2 13 | @log 14 | def to_be_logged(self, a): 15 | return None 16 | 17 | class TestLoggerWithCredentialArgs(object): 18 | def __init__(self,logger_3): 19 | self.log = logger_3 20 | @log 21 | def to_be_logged(self, credentials, account, a): 22 | return None 23 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This is the project's root directory 2 | root = true 3 | 4 | [*] 5 | # Use spaces for indentation 6 | indent_style = space 7 | # Each indent should contain 2 spaces 8 | indent_size = 2 9 | # Use Unix line endings 10 | end_of_line = lf 11 | # The files are utf-8 encoded 12 | charset = utf-8 13 | # No whitespace at the end of line 14 | trim_trailing_whitespace = true 15 | # A file must end with an empty line - this is good for version control systems 16 | insert_final_newline = true 17 | # A line should not have more than this amount of chars (not supported by all plugins) 18 | max_line_length = 100 19 | 20 | [*.{py,md,txt}] 21 | indent_size = 4 22 | 23 | [makefile] 24 | # Use tabs for indentation (Makefiles require tabs) 25 | indent_style = tab 26 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | pep8: 4 | enabled: true 5 | radon: 6 | enabled: true 7 | config: 8 | python_version: 2 9 | markdownlint: 10 | enabled: true 11 | ratings: 12 | paths: 13 | - "**.py" 14 | - "**.md" 15 | exclude_paths: 16 | - "docs" 17 | - "resources/test/" 18 | - "resources/lib/UniversalAnalytics/" 19 | - "resources/language/" 20 | - "resources/media/" 21 | - "resources/skins/" 22 | - "resources/fanart.jpg" 23 | - "resources/icon.png" 24 | - "resources/screenshot-01.jpg" 25 | - "resources/screenshot-02.jpg" 26 | - "resources/screenshot-03.jpg" 27 | - "resources/settings.xml" 28 | - "resources/__init__.py" 29 | - "resources/lib/__init__.py" 30 | - "__init__.py" 31 | - "addon.xml" 32 | - "LICENSE.txt" 33 | - "makefile" 34 | - "requirements.txt" 35 | - "ISSUE_TEMPLATE.md" 36 | - "PULL_REQUEST_TEMPLATE.md" 37 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Types of changes 2 | 3 | - [ ] Bug fix (non-breaking change which fixes an issue) 4 | - [ ] New feature (non-breaking change which adds functionality) 5 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 6 | - [ ] I have read the [**CONTRIBUTING**](Contributing.md) document. 7 | 8 | ### All Submissions: 9 | 10 | * [ ] Have you followed the guidelines in our Contributing document? 11 | * [ ] Have you checked to ensure there aren't other open [Pull Requests](../../pulls) for the same update/change? 12 | 13 | 14 | ### Changes to Core Features: 15 | 16 | * [ ] Have you added an explanation of what your changes do and why you'd like us to include them? 17 | * [ ] Have you successfully ran tests with your changes locally? 18 | 19 | ## Screenshots (if appropriate): 20 | 21 | [You can erase any parts of this template not applicable to your Pull Request.] 22 | -------------------------------------------------------------------------------- /resources/lib/ui/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Author: caphm 3 | # Package: kodi 4 | # Created on: 06.08.2018 5 | # License: MIT https://goo.gl/5bMj3H 6 | # pylint: disable=import-error 7 | 8 | """Kodi GUI stuff""" 9 | 10 | import xbmc 11 | 12 | CMD_AUTOCLOSE_DIALOG = 'AlarmClock(closedialog,Dialog.Close(all,true),' \ 13 | '{:02d}:{:02d},silent)' 14 | 15 | 16 | def show_modal_dialog(dlg_class, xml, path, **kwargs): 17 | """ 18 | Show a modal Dialog in the UI. 19 | Pass kwargs minutes and/or seconds tohave the dialog automatically 20 | close after the specified time. 21 | """ 22 | dlg = dlg_class(xml, path, "default", "1080i", **kwargs) 23 | minutes = kwargs.get('minutes', 0) 24 | seconds = kwargs.get('seconds', 0) 25 | if minutes > 0 or seconds > 0: 26 | xbmc.executebuiltin(CMD_AUTOCLOSE_DIALOG.format(minutes, seconds)) 27 | dlg.doModal() 28 | -------------------------------------------------------------------------------- /addon.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Author: asciidisco 3 | # Module: default 4 | # Created on: 13.01.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Kodi plugin for Netflix (https://netflix.com)""" 8 | 9 | 10 | import sys 11 | from resources.lib.NetflixCommon import NetflixCommon 12 | from resources.lib.Navigation import Navigation 13 | 14 | # Setup plugin 15 | PLUGIN_HANDLE = int(sys.argv[1]) 16 | BASE_URL = sys.argv[0] 17 | # We use string slicing to trim the leading ? from the plugin call paramstring 18 | REQUEST_PARAMS = sys.argv[2][1:] 19 | 20 | # init plugin libs 21 | NETFLIX_COMMON = NetflixCommon( 22 | plugin_handle=PLUGIN_HANDLE, 23 | base_url=BASE_URL 24 | ) 25 | 26 | NAVIGATION = Navigation( 27 | nx_common=NETFLIX_COMMON 28 | ) 29 | 30 | if __name__ == '__main__': 31 | # Call the router function and pass the plugin call parameters to it. 32 | NETFLIX_COMMON.log('Started (Version ' + NETFLIX_COMMON.version + ')') 33 | NAVIGATION.router(paramstring=REQUEST_PARAMS) 34 | -------------------------------------------------------------------------------- /resources/skins/default/1080i/plugin-video-netflix-Skip.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6012 4 | 0 5 | 6 | 7 | 30 8 | 60 9 | 80 10 | 180 11 | 12 | 13 | Skip intro 14 | 250 15 | 80 16 | font12 17 | 18 | FFededed 19 | FFededed 20 | FFededed 21 | 66000000 22 | 20 23 | center 24 | center 25 | smallbutton.png 26 | smallbutton.png 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sebastian Golasch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /resources/skins/default/1080i/plugin-video-netflix-SaveStreamSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6012 4 | 0 5 | 6 | 7 | 30 8 | 60 9 | 80 10 | 180 11 | 12 | 13 | Save stream settings 14 | 400 15 | 80 16 | font12 17 | 18 | FFededed 19 | FFededed 20 | FFededed 21 | 66000000 22 | 20 23 | center 24 | center 25 | smallbutton.png 26 | smallbutton.png 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *I'm submitting a ...* 2 | - [ ] bug report 3 | - [ ] feature request 4 | - [ ] support request 5 | 6 | ## General infomration 7 | 8 | ### Prerequisites 9 | 10 | * [ ] Are you running the latest version? 11 | 12 | ### Description 13 | 14 | [Description of the bug or feature] 15 | 16 | ### Steps to Reproduce 17 | 18 | 1. [First Step] 19 | 2. [Second Step] 20 | 3. [and so on...] 21 | 22 | **Expected behavior:** [What you expected to happen] 23 | 24 | **Actual behavior:** [What actually happened] 25 | 26 | ## Context (Environment) 27 | 28 | ### Installation 29 | 30 | * [ ] I installed the plugin via zip from the Releases page 31 | * [ ] I´m using the Netflix Repo 32 | * [ ] I´m using a different source (Please tell which) 33 | 34 | ### Operating System 35 | 36 | * [ ] Linux (x86/x64) 37 | * [ ] OSX (x86/x64) 38 | * [ ] Windows (x86/x64) 39 | * [ ] Linux (ARM) 40 | * [ ] Android 41 | 42 | #### Additional informatin on the environment 43 | 44 | [Descripe your environment a bit more detailed, are you using LibreElec f.e.] 45 | 46 | ## Debug log 47 | 48 | [Please include a link to your debug log (use something like [http://sprunge.us/](http://sprunge.us/)) or similar, please do not paste] 49 | 50 | ## Other information 51 | 52 | [e.g. detailed explanation, related issues, suggestions how to fix, links for us to have context, etc.] 53 | 54 | ### Screenshots 55 | 56 | [Please add a screenshot if that helps understanding your problem] 57 | 58 | [You can erase any parts of this template not applicable to your Issue.] 59 | -------------------------------------------------------------------------------- /resources/lib/UniversalAnalytics/LICENSE: -------------------------------------------------------------------------------- 1 | - BSD 3-Clause license - 2 | 3 | Copyright (c) 2013, Analytics Pros 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, this 13 | list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 16 | * Neither the name of Analytics Pros nor the names of its contributors may be 17 | used to endorse or promote products derived from this software without 18 | specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /resources/lib/ui/xmldialogs.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=invalid-name,missing-docstring 2 | # pylint: disable=attribute-defined-outside-init,import-error 3 | from platform import machine 4 | 5 | import xbmcgui 6 | 7 | ACTION_PLAYER_STOP = 13 8 | OS_MACHINE = machine() 9 | 10 | 11 | class Skip(xbmcgui.WindowXMLDialog): 12 | """ 13 | Dialog for skipping video parts (intro, recap, ...) 14 | """ 15 | def __init__(self, *args, **kwargs): 16 | self.skip_to = kwargs['skip_to'] 17 | self.label = kwargs['label'] 18 | if OS_MACHINE[0:5] == 'armv7': 19 | xbmcgui.WindowXMLDialog.__init__(self) 20 | else: 21 | xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs) 22 | 23 | def onInit(self): 24 | self.action_exitkeys_id = [10, 13] 25 | self.getControl(6012).setLabel(self.label) 26 | 27 | def onClick(self, controlID): 28 | if controlID == 6012: 29 | import xbmc 30 | xbmc.Player().seekTime(self.skip_to) 31 | self.close() 32 | 33 | 34 | 35 | class SaveStreamSettings(xbmcgui.WindowXMLDialog): 36 | """ 37 | Dialog for skipping video parts (intro, recap, ...) 38 | """ 39 | def __init__(self, *args, **kwargs): 40 | super(SaveStreamSettings, self).__init__(*args, **kwargs) 41 | self.new_show_settings = kwargs['new_show_settings'] 42 | self.tvshowid = kwargs['tvshowid'] 43 | self.storage = kwargs['storage'] 44 | 45 | def onInit(self): 46 | self.action_exitkeys_id = [10, 13] 47 | 48 | def onClick(self, controlID): 49 | if controlID == 6012: 50 | self.storage[self.tvshowid] = self.new_show_settings 51 | self.close() 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | parts/ 19 | sdist/ 20 | report/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | Changelog.md 27 | Authors.md 28 | node_modules/ 29 | package-lock.json 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | coverage/ 45 | coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | _build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # dotenv 86 | .env 87 | 88 | # virtualenv 89 | .venv 90 | venv/ 91 | ENV/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | .vscode 107 | -------------------------------------------------------------------------------- /resources/test/test_KodiHelper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Module: KodiHelper 3 | # Author: asciidisco 4 | # Created on: 11.10.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Tests for the `KodiHelper` module""" 8 | 9 | import unittest 10 | import mock 11 | from resources.lib.KodiHelper import KodiHelper 12 | 13 | class KodiHelperTestCase(unittest.TestCase): 14 | 15 | @mock.patch('xbmc.getInfoLabel') 16 | def test_encode(self, mock_getInfoLabel): 17 | """ADD ME""" 18 | mock_getInfoLabel.return_value = '00:80:41:ae:fd:7e' 19 | kodi_helper = KodiHelper() 20 | self.assertEqual( 21 | first=kodi_helper.decode(kodi_helper.encode('foo')), 22 | second='foo') 23 | 24 | @mock.patch('xbmc.getInfoLabel') 25 | def test_decode(self, mock_getInfoLabel): 26 | """ADD ME""" 27 | mock_getInfoLabel.return_value = '00:80:41:ae:fd:7e' 28 | kodi_helper = KodiHelper() 29 | self.assertEqual( 30 | first=kodi_helper.decode('UElth5ymr6hRVIderI80WpSTteTFDeWB3vr7JK/N9QqAuNvriQGZRznH+KCPyiCS'), 31 | second='foo') 32 | 33 | def test_refresh(self): 34 | """ADD ME""" 35 | kodi_helper = KodiHelper() 36 | self.assertEqual( 37 | first=kodi_helper.refresh(), 38 | second=None) 39 | 40 | def test_invalidate_memcache(self): 41 | """ADD ME""" 42 | cache = KodiHelper() 43 | self.assertEqual( 44 | first=cache.invalidate_memcache(), 45 | second=None) 46 | 47 | def test_set_main_menu_selection(self): 48 | """ADD ME""" 49 | kodi_helper = KodiHelper() 50 | self.assertEqual( 51 | first=kodi_helper.set_main_menu_selection('foo'), 52 | second=None) 53 | self.assertEqual( 54 | first=kodi_helper.get_main_menu_selection(), 55 | second='') 56 | 57 | def test_get_main_menu_selection(self): 58 | """ADD ME""" 59 | kodi_helper = KodiHelper() 60 | kodi_helper.set_main_menu_selection('foo') 61 | self.assertEqual( 62 | first=kodi_helper.get_main_menu_selection(), 63 | second='') 64 | 65 | def test_get_cached_item(self): 66 | """ADD ME""" 67 | kodi_helper = KodiHelper() 68 | self.assertEqual( 69 | first=kodi_helper.get_cached_item('foo'), 70 | second=None) 71 | 72 | def test_add_cached_item(self): 73 | """ADD ME""" 74 | kodi_helper = KodiHelper() 75 | self.assertEqual( 76 | first=kodi_helper.add_cached_item('foo', 'bar'), 77 | second=None) 78 | -------------------------------------------------------------------------------- /resources/lib/NetflixCredentials.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from Cryptodome import Random 3 | from Cryptodome.Cipher import AES 4 | from Cryptodome.Util import Padding 5 | from resources.lib.utils import uniq_id 6 | 7 | class NetflixCredentials(object): 8 | """ 9 | Stuff shared between / used from service and addon""" 10 | 11 | def __init__(self): 12 | self.bs = 32 13 | self.crypt_key = uniq_id() 14 | 15 | def encode_credentials(self, email, password): 16 | """Returns the users stored credentials 17 | 18 | Returns 19 | ------- 20 | :obj:`dict` of :obj:`str` 21 | The users stored account data 22 | """ 23 | # if everything is fine, we encode the values 24 | if '' != email or '' != password: 25 | return { 26 | 'email': self.encode(raw=email), 27 | 'password': self.encode(raw=password) 28 | } 29 | 30 | # if email is empty, we return an empty map 31 | return { 32 | 'email': '', 33 | 'password': '' 34 | } 35 | 36 | def decode_credentials(self, email, password): 37 | """Returns the users stored credentials 38 | 39 | Returns 40 | ------- 41 | :obj:`dict` of :obj:`str` 42 | The users stored account data 43 | """ 44 | # if everything is fine, we decode the values 45 | if (email and '' != email) or (password and '' != password): 46 | return { 47 | 'email': self.decode(enc=email), 48 | 'password': self.decode(enc=password) 49 | } 50 | 51 | # if email is empty, we return an empty map 52 | return { 53 | 'email': '', 54 | 'password': '' 55 | } 56 | 57 | def encode(self, raw): 58 | """ 59 | Encodes data 60 | 61 | :param data: Data to be encoded 62 | :type data: str 63 | :returns: string -- Encoded data 64 | """ 65 | raw = bytes(Padding.pad(data_to_pad=raw.encode('utf-8'), block_size=self.bs)) 66 | iv = Random.new().read(AES.block_size) 67 | cipher = AES.new(self.crypt_key, AES.MODE_CBC, iv) 68 | return base64.b64encode(iv + cipher.encrypt(raw)) 69 | 70 | def decode(self, enc): 71 | """ 72 | Decodes data 73 | 74 | :param data: Data to be decoded 75 | :type data: str 76 | :returns: string -- Decoded data 77 | """ 78 | enc = base64.b64decode(enc) 79 | iv = enc[:AES.block_size] 80 | cipher = AES.new(self.crypt_key, AES.MODE_CBC, iv) 81 | decoded = Padding.unpad( 82 | padded_data=cipher.decrypt(enc[AES.block_size:]), 83 | block_size=self.bs).decode('utf-8') 84 | return decoded 85 | -------------------------------------------------------------------------------- /resources/lib/playback/bookmarks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Author: caphm 3 | # Package: bookmarking 4 | # Created on: 02.08.2018 5 | # License: MIT https://goo.gl/5bMj3H 6 | # pylint: disable=import-error 7 | 8 | """Save bookmarks for library items and mark them as watched""" 9 | 10 | from resources.lib.playback import PlaybackActionManager, json_rpc 11 | 12 | OFFSET_WATCHED_TO_END = 'watchedToEndOffset' 13 | 14 | 15 | def update_library_item_details(dbtype, dbid, details): 16 | """ 17 | Update properties of an item in the Kodi library 18 | """ 19 | method = 'VideoLibrary.Set{}Details'.format(dbtype.capitalize()) 20 | params = {'{}id'.format(dbtype): dbid} 21 | params.update(details) 22 | return json_rpc(method, params) 23 | 24 | 25 | class BookmarkManager(PlaybackActionManager): 26 | """ 27 | Saves bookmarks on each playback tick if the played item exists in the 28 | Kodi library and marks it as watched after playback. 29 | """ 30 | def __init__(self, nx_common): 31 | super(BookmarkManager, self).__init__(nx_common) 32 | self.dbinfo = None 33 | self.markers = None 34 | self.progress = 0 35 | self.elapsed = 0 36 | 37 | def __str__(self): 38 | return ('enabled={}, dbinfo={}, markers={}' 39 | .format(self.enabled, self.dbinfo, self.markers)) 40 | 41 | def _initialize(self, data): 42 | self.dbinfo = data['dbinfo'] 43 | self.markers = data.get('timeline_markers', {}) 44 | self.progress = 0 45 | self.elapsed = 0 46 | 47 | def _on_playback_stopped(self): 48 | if self._watched_to_end(): 49 | self._mark_as_watched() 50 | 51 | def _on_tick(self, player_state): 52 | self.progress = player_state['percentage'] 53 | self.elapsed = player_state['elapsed_seconds'] 54 | if self.elapsed % 5 == 0: 55 | self._save_bookmark() 56 | 57 | def _save_bookmark(self): 58 | self.log('Saving bookmark for {} at {}s'.format(self.dbinfo, 59 | self.elapsed)) 60 | update_library_item_details( 61 | self.dbinfo['dbtype'], self.dbinfo['dbid'], 62 | {'resume': {'position': self.elapsed}}) 63 | 64 | def _watched_to_end(self): 65 | return ( 66 | (OFFSET_WATCHED_TO_END in self.markers and 67 | self.elapsed >= self.markers[OFFSET_WATCHED_TO_END]) or 68 | (OFFSET_WATCHED_TO_END not in self.markers and 69 | self.progress >= 90)) 70 | 71 | def _mark_as_watched(self): 72 | self.log('Marking {} as watched'.format(self.dbinfo)) 73 | update_library_item_details( 74 | self.dbinfo['dbtype'], self.dbinfo['dbid'], 75 | {'playcount': self.dbinfo.get('playcount', 0) + 1, 76 | 'resume': {'position': 0}}) 77 | -------------------------------------------------------------------------------- /resources/lib/storage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Author: caphm 3 | # Module: storage 4 | # Created on: 06.08.2018 5 | # License: MIT https://goo.gl/5bMj3H 6 | # pylint: disable=import-error 7 | 8 | """ 9 | Easily accessible persistent storage 10 | """ 11 | 12 | import os 13 | try: 14 | import cPickle as pickle 15 | except ImportError: 16 | import pickle 17 | 18 | import xbmcvfs 19 | 20 | from resources.lib.utils import LoggingComponent 21 | 22 | 23 | class PersistentStorage(LoggingComponent): 24 | """ 25 | Key-Value storage with a backing file on disk. 26 | Reads entire dict structure into memory on first access and updates 27 | the backing file with each changed entry. 28 | 29 | IMPORTANT: Changes to mutable objects inserted into the key-value-store 30 | are not automatically written to disk. You need to call commit() to 31 | persist these changes. 32 | """ 33 | def __init__(self, storage_id, nx_common): 34 | LoggingComponent.__init__(self, nx_common) 35 | self.storage_id = storage_id 36 | self.backing_file = os.path.join(nx_common.data_path, 37 | self.storage_id + '.ndb') 38 | self._contents = {} 39 | self._dirty = True 40 | self.log('Instantiated {}'.format(self.storage_id)) 41 | 42 | def __getitem__(self, key): 43 | self.log('Getting {}'.format(key)) 44 | return self.contents[key] 45 | 46 | def __setitem__(self, key, value): 47 | self.log('Setting {} to {}'.format(key, value)) 48 | self._contents[key] = value 49 | self.commit() 50 | self._dirty = True 51 | 52 | @property 53 | def contents(self): 54 | """ 55 | The contents of the storage file 56 | """ 57 | if self._dirty: 58 | self._load_from_disk() 59 | return self._contents 60 | 61 | def get(self, key, default=None): 62 | """ 63 | Return the value associated with key. If key does not exist, 64 | return default (defaults to None) 65 | """ 66 | return self.contents.get(key, default) 67 | 68 | def commit(self): 69 | """ 70 | Write current contents to disk 71 | """ 72 | f = xbmcvfs.File(self.backing_file, 'wb') 73 | f.write(bytearray(pickle.dumps(self._contents, protocol=2))) 74 | f.close() 75 | self.log('Committed changes to backing file') 76 | 77 | def clear(self): 78 | """ 79 | Clear contents and backing file 80 | """ 81 | self._contents = {} 82 | self.commit() 83 | 84 | def _load_from_disk(self): 85 | self.log('Trying to load contents from disk') 86 | if xbmcvfs.exists(self.backing_file): 87 | f = xbmcvfs.File(self.backing_file, 'rb') 88 | self._contents = pickle.loads(f.read()) 89 | self._dirty = False 90 | f.close() 91 | self.log('Loaded contents from backing file ({})'.format(self._contents)) 92 | else: 93 | self.log('Backing file does not exist') 94 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all test clean docs clean-pyc clean-report clean-docs clean-coverage rere 2 | .DEFAULT_GOAL := all 3 | 4 | SPHINXBUILD = sphinx-build 5 | SPHINXPROJ = pluginvideonetflix 6 | BUILDDIR = ./_build 7 | SOURCEDIR = ./resources/lib 8 | TEST_DIR = ./resources/test 9 | COVERAGE_FILE = ./.coverage 10 | COVERAGE_DIR = ./coverage 11 | REPORT_DIR = ./report 12 | DOCS_DIR = ./docs 13 | FLAKE_FILES = ./addon.py ./service.py ./setup.py ./resources/lib/utils.py ./resources/lib/MSLHttpRequestHandler.py ./resources/lib/NetflixHttpRequestHandler.py ./resources/lib/Navigation.py ./resources/lib/kodi/Dialogs.py 14 | RADON_FILES = resources/lib/*.py resources/lib/kodi/*.py ./addon.py ./service.py 15 | PYLINT_FILES = addon service setup resources.lib.kodi.Dialogs resources.lib.MSLHttpRequestHandler resources.lib.NetflixHttpRequestHandler resources.lib.utils 16 | LINT_REPORT_FILE = ./report/lint.html 17 | TEST_OPTIONS = -s --cover-package=resources.lib.utils --cover-package=resources.lib.NetflixSession --cover-package=resources.lib.Navigation --cover-package=resources.lib.MSL --cover-package=resources.lib.KodiHelper --cover-package=resources.lib.kodi.Dialogs --cover-package=resources.lib.Library --cover-package=resources.lib.KodiHelper --cover-package=resources.lib.Library --cover-package=resources.lib.NetflixHttpRequestHandler --cover-package=resources.lib.NetflixHttpSubRessourceHandler --cover-erase --with-coverage --cover-branches 18 | I18N_FILES = resources/language/**/*.po 19 | 20 | all: clean lint test docs 21 | 22 | clean: clean-pyc clean-report clean-docs clean-coverage 23 | 24 | clean-docs: 25 | rm -rf $(BUILDDIR) || exit 0 26 | 27 | clean-pyc: 28 | find . -name '*.pyc' -exec rm {} + 29 | find . -name '*.pyo' -exec rm {} + 30 | 31 | clean-report: 32 | rm -rf $(REPORT_DIR) || exit 0 33 | mkdir $(REPORT_DIR) 34 | 35 | clean-coverage: 36 | rm $(COVERAGE_FILE) || exit 0 37 | rm -rf $(COVERAGE_DIR) || exit 0 38 | mkdir $(COVERAGE_DIR) 39 | 40 | lint: 41 | flake8 --filename=$(FLAKE_FILES) 42 | pylint $(PYLINT_FILES) --ignore=test,UniversalAnalytics || exit 0 43 | pylint $(PYLINT_FILES) --ignore=test,UniversalAnalytics --output-format=html > $(LINT_REPORT_FILE) 44 | radon cc $(RADON_FILES) 45 | dennis-cmd lint $(I18N_FILES) 46 | rst-lint docs/index.rst --level=severe 47 | yamllint .travis.yml .codeclimate.yml 48 | 49 | docs: 50 | @$(SPHINXBUILD) $(DOCS_DIR) $(BUILDDIR) -T -c ./docs 51 | 52 | test: 53 | nosetests $(TEST_DIR) $(TEST_OPTIONS) --cover-html --cover-html-dir=$(COVERAGE_DIR) 54 | 55 | rere: 56 | codeclimate-test-reporter 57 | 58 | help: 59 | @echo " clean-pyc" 60 | @echo " Remove python artifacts." 61 | @echo " clean-report" 62 | @echo " Remove coverage/lint report artifacts." 63 | @echo " clean-docs" 64 | @echo " Remove sphinx artifacts." 65 | @echo " clean-coverage" 66 | @echo " Remove code coverage artifacts." 67 | @echo " clean" 68 | @echo " Calls all clean tasks." 69 | @echo " lint" 70 | @echo " Check style with flake8, pylint & radon" 71 | @echo " test" 72 | @echo " Run unit tests" 73 | @echo " docs" 74 | @echo " Generate sphinx docs" 75 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # plugin.video.netflix documentation build configuration file, created by 4 | # sphinx-quickstart on Wed Apr 26 16:27:25 2017. 5 | 6 | 7 | import os 8 | import re 9 | import sys 10 | from shutil import copyfile 11 | import sphinx_rtd_theme 12 | 13 | BASE_PATH = os.path.dirname(os.path.abspath(__file__)) + os.path.sep 14 | ROOT_PATH = os.path.dirname(os.path.dirname(BASE_PATH)) + os.path.sep 15 | 16 | sys.path.insert(0, BASE_PATH) 17 | sys.path.insert(0, ROOT_PATH) 18 | sys.path.insert(0, ROOT_PATH + 'resources' + os.path.sep) 19 | sys.path.insert(0, ROOT_PATH + 'resources' + os.path.sep + 'lib' + os.path.sep) 20 | 21 | from setup import get_addon_data 22 | 23 | ADDON_DATA = get_addon_data() 24 | 25 | # -- General configuration ------------------------------------------------ 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be 28 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 29 | # ones. 30 | extensions = [ 31 | 'sphinx.ext.autodoc', 32 | 'sphinx.ext.doctest', 33 | 'sphinx.ext.coverage', 34 | 'sphinx.ext.imgmath', 35 | 'sphinx.ext.viewcode', 36 | 'm2r'] 37 | 38 | # The suffix(es) of source filenames. 39 | source_suffix = ['.rst', '.md'] 40 | 41 | # The master toctree document. 42 | master_doc = 'index' 43 | 44 | # General information about the project. 45 | project = ADDON_DATA.get('id', '') 46 | copyright = u'2017, ' + ADDON_DATA.get('author', '') 47 | author = ADDON_DATA.get('author', '') 48 | 49 | # The version info for the project you're documenting, acts as replacement for 50 | # |version| and |release|, also used in various other places throughout the 51 | # built documents. 52 | # 53 | # The short X.Y version. 54 | version = ADDON_DATA.get('version', '') 55 | # The full version, including alpha/beta/rc tags. 56 | release = ADDON_DATA.get('version', '') 57 | 58 | # The language for content autogenerated by Sphinx. Refer to documentation 59 | # for a list of supported languages. 60 | # 61 | # This is also used if you do content translation via gettext catalogs. 62 | # Usually you set "language" from the command line for these cases. 63 | language = None 64 | 65 | # List of patterns, relative to source directory, that match files and 66 | # directories to ignore when looking for source files. 67 | # This patterns also effect to html_static_path and html_extra_path 68 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 69 | 70 | # The name of the Pygments (syntax highlighting) style to use. 71 | pygments_style = 'sphinx' 72 | 73 | # If true, `todo` and `todoList` produce output, else they produce nothing. 74 | todo_include_todos = False 75 | 76 | # -- Options for HTML output ---------------------------------------------- 77 | 78 | # The theme to use for HTML and HTML Help pages. 79 | html_theme = 'sphinx_rtd_theme' 80 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 81 | 82 | # Add any paths that contain custom static files (such as style sheets) here, 83 | # relative to this directory. They are copied after the builtin static files, 84 | # so a file named "default.css" will overwrite the builtin "default.css". 85 | html_static_path = ['../resources/icon.png'] 86 | html_logo = '_static/icon.png' 87 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Module: default 3 | # Author: asciidisco 4 | # Created on: 24.07.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Setup""" 8 | 9 | import os 10 | import re 11 | import sys 12 | from setuptools import find_packages, setup 13 | 14 | REQUIRED_PYTHON_VERSION = (2, 7) 15 | PACKAGES = find_packages() 16 | INSTALL_DEPENDENCIES = [] 17 | SETUP_DEPENDENCIES = [] 18 | TEST_DEPENDENCIES = [ 19 | 'nose', 20 | 'Kodistubs', 21 | 'httpretty', 22 | 'mock', 23 | ] 24 | EXTRA_DEPENDENCIES = { 25 | 'dev': [ 26 | 'nose', 27 | 'flake8', 28 | 'codeclimate-test-reporter', 29 | 'pylint', 30 | 'mccabe', 31 | 'pycodestyle', 32 | 'pyflakes', 33 | 'Kodistubs', 34 | 'httpretty', 35 | 'mock', 36 | 'requests', 37 | 'pyDes', 38 | 'radon', 39 | 'Sphinx', 40 | 'sphinx_rtd_theme', 41 | 'm2r', 42 | 'kodi-release-helper', 43 | 'dennis', 44 | 'blessings', 45 | 'demjson', 46 | 'restructuredtext_lint', 47 | 'yamllint', 48 | ] 49 | } 50 | 51 | 52 | def get_addon_data(): 53 | """Loads the Kodi plugin data from addon.xml""" 54 | root_dir = os.path.dirname(os.path.abspath(__file__)) 55 | pathname = os.path.join(root_dir, 'addon.xml') 56 | with open(pathname, 'rb') as addon_xml: 57 | addon_xml_contents = addon_xml.read() 58 | _id = re.search( 59 | r'(?(.+?)<', 72 | addon_xml_contents).group(1) 73 | email = re.search( 74 | r'(?(.+?)<', 75 | addon_xml_contents).group(1) 76 | source = re.search( 77 | r'(?(.+?)<', 78 | addon_xml_contents).group(1) 79 | return { 80 | 'id': _id, 81 | 'author': author, 82 | 'name': name, 83 | 'version': version, 84 | 'desc': desc, 85 | 'email': email, 86 | 'source': source, 87 | } 88 | 89 | 90 | if sys.version_info < REQUIRED_PYTHON_VERSION: 91 | sys.exit('Python >= 2.7 is required. Your version:\n' + sys.version) 92 | 93 | if __name__ == '__main__': 94 | ADDON_DATA = get_addon_data() 95 | setup( 96 | name=ADDON_DATA.get('name'), 97 | version=ADDON_DATA.get('version'), 98 | author=ADDON_DATA.get('author'), 99 | author_email=ADDON_DATA.get('email'), 100 | description=ADDON_DATA.get('desc'), 101 | packages=PACKAGES, 102 | include_package_data=True, 103 | install_requires=INSTALL_DEPENDENCIES, 104 | setup_requires=SETUP_DEPENDENCIES, 105 | tests_require=TEST_DEPENDENCIES, 106 | extras_require=EXTRA_DEPENDENCIES, 107 | test_suite='nose.collector', 108 | ) 109 | -------------------------------------------------------------------------------- /Code_of_Conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * Use of sexualized language/imagery and unwelcome sexual attention or advances 26 | * Trolling, insulting/derogatory comments, and personal or political attacks 27 | * Public or private harassment 28 | * Publishing others' private information without explicit permission 29 | * Other conduct which could reasonably be considered inappropriate in a 30 | professional setting 31 | 32 | ## Our Responsibilities 33 | 34 | Project maintainers are responsible for clarifying the standards of acceptable 35 | behavior and are expected to take appropriate and fair corrective action in 36 | response to any instances of unacceptable behavior. 37 | 38 | Project maintainers have the right and responsibility to remove, edit, or 39 | reject comments, commits, code, wiki edits, issues, and other contributions 40 | that are not aligned to this Code of Conduct, or to ban temporarily or 41 | permanently any contributor for other behaviors that they deem inappropriate, 42 | threatening, offensive, or harmful. 43 | 44 | ## Scope 45 | 46 | This Code of Conduct applies both within project spaces and in public spaces 47 | when an individual is representing the project or its community. Examples of 48 | representing a project or community include using an official project e-mail 49 | address, posting via an official social media account, or acting as an appointed 50 | representative at an online or offline event. Representation of a project may be 51 | further defined and clarified by project maintainers. 52 | 53 | ## Enforcement 54 | 55 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 56 | reported by contacting the project team at undefined. All 57 | complaints will be reviewed and investigated and will result in a response that 58 | is deemed necessary and appropriate to the circumstances. The project team is 59 | obligated to maintain confidentiality with regard to the reporter of an incident. 60 | Further details of specific enforcement policies may be posted separately. 61 | 62 | Project maintainers who do not follow or enforce the Code of Conduct in good 63 | faith may face temporary or permanent repercussions as determined by other 64 | members of the project's leadership. 65 | 66 | ## Attribution 67 | 68 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 69 | version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 70 | 71 | [homepage]: http://contributor-covenant.org 72 | [version]: http://contributor-covenant.org/version/1/4/ 73 | -------------------------------------------------------------------------------- /resources/lib/playback/section_skipping.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Author: caphm 3 | # Module: section_skipping 4 | # Created on: 31.07.2018 5 | # License: MIT https://goo.gl/5bMj3H 6 | # pylint: disable=import-error 7 | 8 | """Skipping of video sections (recap, intro)""" 9 | import xbmc 10 | import xbmcgui 11 | 12 | from resources.lib.ui import xmldialogs, show_modal_dialog 13 | from resources.lib.playback import PlaybackActionManager 14 | 15 | SKIPPABLE_SECTIONS = {'credit': 30076, 'recap': 30077} 16 | OFFSET_CREDITS = 'creditsOffset' 17 | 18 | 19 | class SectionSkipper(PlaybackActionManager): 20 | """ 21 | Checks if a skippable section has been reached and takes appropriate action 22 | """ 23 | def __init__(self, nx_common): 24 | super(SectionSkipper, self).__init__(nx_common) 25 | self.markers = {} 26 | self.auto_skip = False 27 | self.pause_on_skip = False 28 | 29 | def __str__(self): 30 | return ('enabled={}, markers={}, auto_skip={}, pause_on_skip={}' 31 | .format(self.enabled, self.markers, self.auto_skip, 32 | self.pause_on_skip)) 33 | 34 | def _initialize(self, data): 35 | self.markers = data['timeline_markers'] 36 | self.auto_skip = self.addon.getSetting('auto_skip_credits') == 'true' 37 | self.pause_on_skip = self.addon.getSetting('pause_on_skip') == 'true' 38 | 39 | def _on_tick(self, player_state): 40 | for section in SKIPPABLE_SECTIONS: 41 | self._check_section(section, player_state['elapsed_seconds']) 42 | 43 | def _check_section(self, section, elapsed): 44 | if (self.markers.get(section) and 45 | elapsed >= self.markers[section]['start'] and 46 | elapsed <= self.markers[section]['end']): 47 | self._skip_section(section) 48 | del self.markers[section] 49 | 50 | def _skip_section(self, section): 51 | self.log('Entered section {}'.format(section)) 52 | label = self.addon.getLocalizedString(SKIPPABLE_SECTIONS[section]) 53 | if self.auto_skip: 54 | self._auto_skip(section, label) 55 | else: 56 | self._ask_to_skip(section, label) 57 | 58 | def _auto_skip(self, section, label): 59 | self.log('Auto-skipping {}'.format(section)) 60 | player = xbmc.Player() 61 | xbmcgui.Dialog().notification( 62 | 'Netflix', '{}...'.format(label.encode('utf-8')), 63 | xbmcgui.NOTIFICATION_INFO, 5000) 64 | if self.pause_on_skip: 65 | player.pause() 66 | xbmc.sleep(1000) # give kodi the chance to execute 67 | player.seekTime(self.markers[section]['end']) 68 | xbmc.sleep(1000) # give kodi the chance to execute 69 | player.pause() # unpause playback at seek position 70 | else: 71 | player.seekTime(self.markers[section]['end']) 72 | 73 | def _ask_to_skip(self, section, label): 74 | self.log('Asking to skip {}'.format(section)) 75 | dialog_duration = (self.markers[section]['end'] - 76 | self.markers[section]['start']) 77 | seconds = dialog_duration % 60 78 | minutes = (dialog_duration - seconds) / 60 79 | show_modal_dialog(xmldialogs.Skip, 80 | "plugin-video-netflix-Skip.xml", 81 | self.addon.getAddonInfo('path'), 82 | minutes=minutes, 83 | seconds=seconds, 84 | skip_to=self.markers[section]['end'], 85 | label=label) 86 | -------------------------------------------------------------------------------- /resources/lib/NetflixHttpRequestHandler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Author: asciidisco 3 | # Module: NetflixHttpRequestHandler 4 | # Created on: 07.03.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Oppionionated internal proxy that dispatches requests to Netflix""" 8 | 9 | import json 10 | 11 | try: 12 | from http.server import BaseHTTPRequestHandler 13 | except ImportError: 14 | from BaseHTTPServer import BaseHTTPRequestHandler 15 | 16 | try: 17 | from urllib.parse import urlparse, parse_qs 18 | except ImportError: 19 | from urlparse import urlparse, parse_qs 20 | 21 | try: 22 | from socketserver import TCPServer 23 | except ImportError: 24 | from SocketServer import TCPServer 25 | 26 | from resources.lib.utils import get_class_methods 27 | from resources.lib.NetflixSession import NetflixSession 28 | from resources.lib.NetflixHttpSubRessourceHandler import \ 29 | NetflixHttpSubRessourceHandler 30 | 31 | 32 | # get list of methods & instance form the sub ressource handler 33 | METHODS = get_class_methods(class_item=NetflixHttpSubRessourceHandler) 34 | 35 | 36 | class NetflixHttpRequestHandler(BaseHTTPRequestHandler): 37 | """Oppionionated internal proxy that dispatches requests to Netflix""" 38 | 39 | # pylint: disable=invalid-name 40 | def do_GET(self): 41 | """ 42 | GET request handler 43 | (we only need this, as we only do GET requests internally) 44 | """ 45 | url = urlparse(self.path) 46 | params = parse_qs(url.query) 47 | method = params.get('method', [None])[0] 48 | 49 | # not method given 50 | if method is None: 51 | self.send_error(500, 'No method declared') 52 | return 53 | 54 | # no existing method given 55 | if method not in METHODS: 56 | error_msg = 'Method "' 57 | error_msg += str(method) 58 | error_msg += '" not found. Available methods: ' 59 | error_msg += str(METHODS) 60 | return self.send_error(404, error_msg) 61 | 62 | # call method & get the result 63 | result = getattr(self.server.res_handler, method)(params) 64 | self.send_response(200) 65 | self.send_header('Content-type', 'application/json') 66 | self.end_headers() 67 | return self.wfile.write(json.dumps({ 68 | 'method': method, 69 | 'result': result}).encode()) 70 | 71 | def log_message(self, *args): 72 | """Disable the BaseHTTPServer Log""" 73 | pass 74 | 75 | 76 | ################################## 77 | 78 | 79 | class NetflixTCPServer(TCPServer): 80 | """Override TCPServer to allow shared struct sharing""" 81 | 82 | def __init__(self, server_address, nx_common): 83 | """Initializes NetflixTCPServer""" 84 | nx_common.log(msg='Constructing netflixTCPServer') 85 | 86 | netflix_session = NetflixSession( 87 | cookie_path=nx_common.cookie_path, 88 | data_path=nx_common.data_path, 89 | verify_ssl=(nx_common.get_setting('ssl_verification') == 'true'), 90 | nx_common=nx_common) 91 | 92 | self.res_handler = NetflixHttpSubRessourceHandler( 93 | nx_common=nx_common, 94 | netflix_session=netflix_session) 95 | 96 | TCPServer.__init__(self, server_address, NetflixHttpRequestHandler) 97 | 98 | def esn_changed(self): 99 | """Return if the esn has changed on Session initialization""" 100 | return self.res_handler.nx_common.set_esn( 101 | self.res_handler.netflix_session.esn) 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecated 2 | Please use https://github.com/CastagnaIT/plugin.video.netflix/ 3 | 4 | # Netflix Plugin for Kodi 18 (plugin.video.netflix) 5 | 6 | [![Bitcoin donate button](https://img.shields.io/badge/bitcoin-donate-yellow.svg)](https://blockchain.info/address/1DHGftMkFXXsDY7UnqQuatWwxQzKVu88sF) 7 | [![Build Status](https://travis-ci.org/asciidisco/plugin.video.netflix.svg?branch=master)](https://travis-ci.org/asciidisco/plugin.video.netflix) 8 | [![Test Coverage](https://codeclimate.com/github/asciidisco/plugin.video.netflix/badges/coverage.svg)](https://codeclimate.com/github/asciidisco/plugin.video.netflix/coverage) 9 | [![Issue Count](https://codeclimate.com/github/asciidisco/plugin.video.netflix/badges/issue_count.svg)](https://codeclimate.com/github/asciidisco/plugin.video.netflix) 10 | [![Code Climate](https://codeclimate.com/github/asciidisco/plugin.video.netflix/badges/gpa.svg)](https://codeclimate.com/github/asciidisco/plugin.video.netflix) 11 | [![GitHub release](https://img.shields.io/github/release/asciidisco/plugin.video.netflix.svg)](https://github.com/asciidisco/plugin.video.netflix/releases) 12 | [![Docs](https://media.readthedocs.org/static/projects/badges/passing.svg)](https://asciidisco.github.io/plugin.video.netflix/) 13 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 14 | 15 | ## Disclaimer 16 | 17 | This plugin is not officially commisioned/supported by Netflix. 18 | The trademark "Netflix" is registered by "Netflix, Inc." 19 | 20 | ## Prerequisites 21 | 22 | - Kodi 18 [nightlybuild](http://mirrors.kodi.tv/nightlies/) 23 | - Inputstream.adaptive [>=v2.0.0](https://github.com/peak3d/inputstream.adaptive) 24 | (should be included in your Kodi 18 installation) 25 | - Libwidevine >=1.4.8.970 (for non Android devices) 26 | - Cryptdome python library (for Linux systems, install using `pip install --user pycryptodomex` as the user that will run Kodi) 27 | 28 | Note: The link to download the Widevine Libary for none ARM Systems can be 29 | found in the [Firefox Sources](https://hg.mozilla.org/mozilla-central/raw-file/31465a03c03d1eec31cd4dd5d6b803724dcb29cd/toolkit/content/gmp-sources/widevinecdm.json) 30 | & needs to be placed in the `cdm` folder in [special://home](http://kodi.wiki/view/Special_protocol). 31 | 32 | Please make sure to read the licence agreement that comes with it, 33 | so you know what you´re getting yourself into. 34 | 35 | ## Installation & Updates 36 | 37 | You can use 38 | [our repository](https://github.com/kodinerds/repo/raw/master/repository.netflix/repository.netflix-1.0.1.zip) 39 | to install plugin. 40 | Using this, you´ll immediately receive updates once a 41 | new release has been drafted. 42 | 43 | Further installations instructions can be found in the [Wiki](https://github.com/asciidisco/plugin.video.netflix/wiki) 44 | 45 | ## FAQ 46 | 47 | - [Does it work with Kodi 17](https://github.com/asciidisco/plugin.video.netflix/issues/25) 48 | - [Does it work on a RPI](https://github.com/asciidisco/plugin.video.netflix/issues/28) 49 | - [Which video resolutions are supported](https://github.com/asciidisco/plugin.video.netflix/issues/27) 50 | - [Can it play 4k Videos](https://github.com/asciidisco/plugin.video.netflix/issues/86) 51 | 52 | ## Functionality 53 | 54 | - Multiple profiles 55 | - Search Netflix (incl. suggestions) 56 | - Netflix categories, recommendations, "my list" & continue watching 57 | - Rate show/movie 58 | - Add & remove to/from "my list" 59 | - Export of complete shows & movies in local database 60 | 61 | ## Something doesn't work 62 | 63 | If something doesn't work for you, please: 64 | 65 | - Make sure all prerequisites are met 66 | - Enable verbose logging in the plugin settings 67 | - Enable the Debug log in you Kodi settings 68 | - Open an issue with a titles that summarises your problems 69 | 70 | ## Donate 71 | 72 | If you like this project feel free to buy us some cups of coffee. 73 | Our bitcoin address is: `1DHGftMkFXXsDY7UnqQuatWwxQzKVu88sF` 74 | 75 | ## Code of Conduct 76 | 77 | [Contributor Code of Conduct](Code_of_Conduct.md) 78 | By participating in this project you agree to abide by its terms. 79 | 80 | ## Licence 81 | 82 | Licenced under The MIT License. 83 | -------------------------------------------------------------------------------- /resources/lib/MSLHttpRequestHandler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Author: trummerjo 3 | # Module: MSLHttpRequestHandler 4 | # Created on: 26.01.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Handles & translates requests from Inputstream to Netflix""" 8 | 9 | import base64 10 | 11 | try: 12 | from http.server import BaseHTTPRequestHandler 13 | except ImportError: 14 | from BaseHTTPServer import BaseHTTPRequestHandler 15 | 16 | try: 17 | from urllib.parse import urlparse, parse_qs 18 | except ImportError: 19 | from urlparse import urlparse, parse_qs 20 | 21 | try: 22 | from socketserver import TCPServer 23 | except ImportError: 24 | from SocketServer import TCPServer 25 | 26 | from resources.lib.MSLv2 import MSL 27 | 28 | 29 | class MSLHttpRequestHandler(BaseHTTPRequestHandler): 30 | """Handles & translates requests from Inputstream to Netflix""" 31 | 32 | # pylint: disable=invalid-name 33 | def do_HEAD(self): 34 | """Answers head requests with a success code""" 35 | self.send_response(200) 36 | 37 | # pylint: disable=invalid-name 38 | def do_POST(self): 39 | """Loads the licence for the requested resource""" 40 | length = int(self.headers.get('content-length')) 41 | post = self.rfile.read(length) 42 | data = post.split(b'!') 43 | if len(data) is 2: 44 | challenge = data[0] 45 | sid = base64.standard_b64decode(data[1]) 46 | b64license = self.server.msl_handler.get_license(challenge, sid) 47 | if b64license is not '': 48 | self.send_response(200) 49 | self.end_headers() 50 | self.wfile.write(base64.standard_b64decode(b64license)) 51 | self.finish() 52 | else: 53 | self.server.nx_common.log(msg='Error getting License') 54 | self.send_response(400) 55 | else: 56 | self.server.nx_common.log(msg='Error in License Request') 57 | self.send_response(400) 58 | 59 | # pylint: disable=invalid-name 60 | def do_GET(self): 61 | """Loads the XML manifest for the requested resource""" 62 | url = urlparse(self.path) 63 | params = parse_qs(url.query) 64 | if 'id' not in params: 65 | self.send_response(400, 'No id') 66 | else: 67 | # Get the manifest with the given id 68 | dolby = (True if 'dolby' in params and 69 | params['dolby'][0].lower() == 'true' else False) 70 | hevc = (True if 'hevc' in params and 71 | params['hevc'][0].lower() == 'true' else False) 72 | hdr = (True if 'hdr' in params and 73 | params['hdr'][0].lower() == 'true' else False) 74 | dolbyvision = (True if 'dolbyvision' in params and 75 | params['dolbyvision'][0].lower() == 'true' else False) 76 | vp9 = (True if 'vp9' in params and 77 | params['vp9'][0].lower() == 'true' else False) 78 | 79 | data = self.server.msl_handler.load_manifest( 80 | int(params['id'][0]), 81 | dolby, hevc, hdr, dolbyvision, vp9) 82 | 83 | self.send_response(200) 84 | self.send_header('Content-type', 'application/xml') 85 | self.end_headers() 86 | self.wfile.write(data.encode('utf-8')) 87 | 88 | def log_message(self, *args): 89 | """Disable the BaseHTTPServer Log""" 90 | pass 91 | 92 | 93 | ################################## 94 | 95 | 96 | class MSLTCPServer(TCPServer): 97 | """Override TCPServer to allow usage of shared members""" 98 | 99 | def __init__(self, server_address, nx_common): 100 | """Initialization of MSLTCPServer""" 101 | nx_common.log(msg='Constructing MSLTCPServer') 102 | self.nx_common = nx_common 103 | self.msl_handler = MSL(nx_common) 104 | TCPServer.__init__(self, server_address, MSLHttpRequestHandler) 105 | 106 | def reset_msl_data(self): 107 | """Initialization of MSLTCPServerResets MSL data (perform handshake)""" 108 | self.msl_handler.perform_key_handshake() 109 | -------------------------------------------------------------------------------- /addon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | video 12 | 13 | 14 | 15 | true 16 | Netflix 17 | Addon für Netflix VOD Services 18 | Möglicherweise sind einge Teile dieses Addons in Ihrem Land illegal, Sie sollten dies unbedingt vor der Installation überprüfen. 19 | Netflix 20 | Netflix VOD Services Addon 21 | Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing. 22 | 23 | resources/icon.png 24 | resources/fanart.jpg 25 | resources/screenshot-01.jpg 26 | resources/screenshot-02.jpg 27 | resources/screenshot-03.jpg 28 | 29 | en de es he hr it nl pl pt sk sv 30 | all 31 | MIT 32 | https://www.netflix.com 33 | public at asciidisco dot com 34 | https://www.kodinerds.net/index.php/Thread/55612-PreRelease-Plugin-Netflix-Inputstream/ 35 | https://github.com/asciidisco/plugin.video.netflix 36 | 37 | v0.13.26 (2019-11-19) 38 | - DolbyVision codec added 39 | 40 | v0.13.25 (2019-10-29) 41 | - Python3 compatibility 42 | 43 | v0.13.24 (2019-10-09) 44 | - Change ESN generation for Android without buildgroup 45 | - Fix Content-Type in for shakti path (empty folders) 46 | 47 | v0.13.23 (2019-06-19) 48 | - Fix api_data change (credits @Jaker MX) 49 | 50 | v0.13.22 (2019-03-08) 51 | - Fix menu listing (credits @xLAva) 52 | 53 | v0.13.21 (2018-12-03) 54 | - More codec levels for FHD / UHD 55 | 56 | v0.13.20 (2018-11-29) 57 | - MSLv2 for manifest retrieval 58 | 59 | v0.13.19 (2018-11-24) 60 | - Fix VTT / VP9 issues 61 | 62 | v0.13.18 (2018-11-17) 63 | - Write correct codec into VP9 MPEG DAS profiles 64 | 65 | v0.13.17 (2018-11-16) 66 | - Add VP9 profiles to avoid PRK exception while downloading manifest 67 | - Android ESN generation enhanced 68 | 69 | v0.13.16 (2018-11-13) 70 | - WebVTT subtitles 71 | 72 | v0.13.13 (2018-09-13) 73 | - Fix disabling of intro skipping not working 74 | 75 | v0.13.12 (2018-08-21) 76 | - Fix issues with timeline markers 77 | 78 | v0.13.11 (2018-08-20) 79 | - Fix login issues 80 | - Fix fanarts for non-netflix originals 81 | - Add Korean translation 82 | - Update Italian translation 83 | - Add query parameter widget_display to suppress setting custom view modes when called from a widget 84 | - Skip intro and recap 85 | - Remember audio settings across all episodes of a show 86 | 87 | v0.13.9 (2018-06-14) 88 | - fix login issues after typo fix in netflix login page 89 | 90 | v0.13.8 (2018-06-07) 91 | - fix proxy communication 92 | - fix folder definition for image resources 93 | 94 | v0.13.7 (2018-05-28) 95 | - rework of login info parsing 96 | 97 | v0.13.0 (2018-04-26) 98 | - Android support WIDEVINE Cryptosession for MSL 99 | 100 | v0.12.9 (2018-04-16) 101 | - View for exported 102 | - Support for inputstreamhelper 103 | - Grab metadate for episodes on export 104 | - Auto export new episodes for exported shows 105 | - Auto update watched status inside kodi library 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /resources/language/resource.language.sv_sv/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2017-07-24 16:15+0000\n" 11 | "Last-Translator: Sebastian Golasch \n" 12 | "Language-Team: Swedish\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: sv\n" 17 | 18 | msgctxt "Addon Summary" 19 | msgid "Netflix" 20 | msgstr "" 21 | 22 | msgctxt "Addon Description" 23 | msgid "Netflix VOD Services Addon" 24 | msgstr "Netflix VOD-tjänstetillägg" 25 | 26 | msgctxt "Addon Disclaimer" 27 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 28 | msgstr "Vissa delar av detta tillägg kanske inte är fullt lagliga att använda i ditt land - kontrollera dina lokala lagar innan du installerar." 29 | 30 | msgctxt "#30001" 31 | msgid "Recommendations" 32 | msgstr "Rekommendationer" 33 | 34 | msgctxt "#30002" 35 | msgid "Adult Pin" 36 | msgstr "Barnskydd" 37 | 38 | msgctxt "#30003" 39 | msgid "Search term" 40 | msgstr "Sökord" 41 | 42 | msgctxt "#30004" 43 | msgid "Password" 44 | msgstr "Lösenord" 45 | 46 | msgctxt "#30005" 47 | msgid "E-mail" 48 | msgstr "E-post" 49 | 50 | msgctxt "#30006" 51 | msgid "Adult verification failed" 52 | msgstr "Verifiering av barnskyddet misslyckades" 53 | 54 | msgctxt "#30007" 55 | msgid "Please Check your adult pin" 56 | msgstr "Kontrollera koden till barnskyddet" 57 | 58 | msgctxt "#30008" 59 | msgid "Login failed" 60 | msgstr "Inloggningen misslyckades" 61 | 62 | msgctxt "#30009" 63 | msgid "Please Check your credentials" 64 | msgstr "Kontrollera dina inloggninguppgifter" 65 | 66 | msgctxt "#30010" 67 | msgid "Genres" 68 | msgstr "Genrer" 69 | 70 | msgctxt "#30011" 71 | msgid "Search" 72 | msgstr "Sök" 73 | 74 | msgctxt "#30012" 75 | msgid "No seasons available" 76 | msgstr "Inga säsonger tillgängliga" 77 | 78 | msgctxt "#30013" 79 | msgid "No matches found" 80 | msgstr "Inga träffar" 81 | 82 | msgctxt "#30014" 83 | msgid "Account" 84 | msgstr "Konto" 85 | 86 | msgctxt "#30017" 87 | msgid "Logout" 88 | msgstr "Logga ut" 89 | 90 | msgctxt "#30018" 91 | msgid "Export to library" 92 | msgstr "Exportera till biblioteket" 93 | 94 | msgctxt "#30019" 95 | msgid "Rate on Netflix" 96 | msgstr "Betygsätt på Netflix" 97 | 98 | msgctxt "#30020" 99 | msgid "Remove from 'My list'" 100 | msgstr "Ta bort från 'Min lista'" 101 | 102 | msgctxt "#30021" 103 | msgid "Add to 'My list'" 104 | msgstr "Lägg till 'Min lista'" 105 | 106 | msgctxt "#30022" 107 | msgid "(between 0 & 10)" 108 | msgstr "(mellan 0 & 10)" 109 | 110 | msgctxt "#30023" 111 | msgid "Expert" 112 | msgstr "" 113 | 114 | msgctxt "#30024" 115 | msgid "SSL verification" 116 | msgstr "SSL-kontroll" 117 | 118 | msgctxt "#30025" 119 | msgid "Library" 120 | msgstr "Bibliotek" 121 | 122 | msgctxt "#30026" 123 | msgid "Enable custom library folder" 124 | msgstr "Aktivera anpassad biblioteksmapp" 125 | 126 | msgctxt "#30027" 127 | msgid "Custom library path" 128 | msgstr "Anpassad bibliotekssökväg" 129 | 130 | msgctxt "#30028" 131 | msgid "Playback error" 132 | msgstr "Uppspelningsfel" 133 | 134 | msgctxt "#30029" 135 | msgid "Missing Inputstream addon" 136 | msgstr "Saknar tillägget InputStream" 137 | 138 | msgctxt "#30030" 139 | msgid "Remove from library" 140 | msgstr "Ta bort från biblioteket" 141 | 142 | msgctxt "#30031" 143 | msgid "Change library title" 144 | msgstr "Ändra bibliotekstiteln" 145 | 146 | msgctxt "#30032" 147 | msgid "Tracking" 148 | msgstr "Spårning" 149 | 150 | msgctxt "#30033" 151 | msgid "Use Dolby Sound" 152 | msgstr "Använd Dolby Digital-ljud" 153 | 154 | msgctxt "#30034" 155 | msgid "ESN (set automatically, can be changed manually)" 156 | msgstr "ESN (ändra automatiskt, kan ändras manuellt)" 157 | 158 | msgctxt "#30035" 159 | msgid "Inputstream Addon Settings..." 160 | msgstr "Inställningar för tillägget InputStream..." 161 | -------------------------------------------------------------------------------- /resources/language/resource.language.nl_nl/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2017-07-24 16:15+0000\n" 11 | "Last-Translator: Sebastian Golasch \n" 12 | "Language-Team: Dutch\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: nl\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "Addon voor de Netflix VOD Service" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "Mogelijk zijn enkele onderdelen van deze Addon in uw land niet legaal, raadpleegt u voor installatie de lokale wetten." 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "Aanbevelingen" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "Pincode" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "Zoekterm" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "Paswoord" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "Code kinderslot incorrect" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "Controleer de code van het kinderslot" 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "Inloggen mislukt" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "Controleer uw inloggegevens" 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "Zoeken" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "Geen seizoenen beschikbaar" 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "Geen overeenkomsten gevonden" 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "Uitloggen" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "Naar bibliotheek exporteren" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "Op Netflix beoordelen" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "Van 'Mijn lijst’ verwijderen" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "Aan 'Mijn lijst’ toevoegen" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "(tussen 0 & 10)" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "SSL verificatie" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "Bibliotheek" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "Eigen bibliotheek folder toestaan" 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "Eigen bibliotheek locatie" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "Afspeelfout" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "Inputstream niet gevonden” 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "Van de bibliotheek verwijderen” 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "Titel bibliotheek aanpassen" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "Gebruik Dolby geluid" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "ESN (automatisch, kan handmatig aangepast worden)" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "Inputstream Addon instellingen…" 162 | -------------------------------------------------------------------------------- /resources/lib/UniversalAnalytics/HTTPLog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ############################################################################### 3 | # Formatting filter for urllib2's HTTPHandler(debuglevel=1) output 4 | # Copyright (c) 2013, Analytics Pros 5 | # 6 | # This project is free software, distributed under the BSD license. 7 | # Analytics Pros offers consulting and integration services if your firm needs 8 | # assistance in strategy, implementation, or auditing existing work. 9 | ############################################################################### 10 | 11 | 12 | import sys, re, os 13 | from cStringIO import StringIO 14 | 15 | 16 | 17 | class BufferTranslator(object): 18 | """ Provides a buffer-compatible interface for filtering buffer content. 19 | """ 20 | parsers = [] 21 | 22 | def __init__(self, output): 23 | self.output = output 24 | self.encoding = getattr(output, 'encoding', None) 25 | 26 | def write(self, content): 27 | content = self.translate(content) 28 | self.output.write(content) 29 | 30 | 31 | @staticmethod 32 | def stripslashes(content): 33 | return content.decode('string_escape') 34 | 35 | @staticmethod 36 | def addslashes(content): 37 | return content.encode('string_escape') 38 | 39 | def translate(self, line): 40 | for pattern, method in self.parsers: 41 | match = pattern.match(line) 42 | if match: 43 | return method(match) 44 | 45 | return line 46 | 47 | 48 | 49 | class LineBufferTranslator(BufferTranslator): 50 | """ Line buffer implementation supports translation of line-format input 51 | even when input is not already line-buffered. Caches input until newlines 52 | occur, and then dispatches translated input to output buffer. 53 | """ 54 | def __init__(self, *a, **kw): 55 | self._linepending = [] 56 | super(LineBufferTranslator, self).__init__(*a, **kw) 57 | 58 | def write(self, _input): 59 | lines = _input.splitlines(True) 60 | for i in range(0, len(lines)): 61 | last = i 62 | if lines[i].endswith('\n'): 63 | prefix = len(self._linepending) and ''.join(self._linepending) or '' 64 | self.output.write(self.translate(prefix + lines[i])) 65 | del self._linepending[0:] 66 | last = -1 67 | 68 | if last >= 0: 69 | self._linepending.append(lines[ last ]) 70 | 71 | 72 | def __del__(self): 73 | if len(self._linepending): 74 | self.output.write(self.translate(''.join(self._linepending))) 75 | 76 | 77 | class HTTPTranslator(LineBufferTranslator): 78 | """ Translates output from |urllib2| HTTPHandler(debuglevel = 1) into 79 | HTTP-compatible, readible text structures for human analysis. 80 | """ 81 | 82 | RE_LINE_PARSER = re.compile(r'^(?:([a-z]+):)\s*(\'?)([^\r\n]*)\2(?:[\r\n]*)$') 83 | RE_LINE_BREAK = re.compile(r'(\r?\n|(?:\\r)?\\n)') 84 | RE_HTTP_METHOD = re.compile(r'^(POST|GET|HEAD|DELETE|PUT|TRACE|OPTIONS)') 85 | RE_PARAMETER_SPACER = re.compile(r'&([a-z0-9]+)=') 86 | 87 | @classmethod 88 | def spacer(cls, line): 89 | return cls.RE_PARAMETER_SPACER.sub(r' &\1= ', line) 90 | 91 | def translate(self, line): 92 | 93 | parsed = self.RE_LINE_PARSER.match(line) 94 | 95 | if parsed: 96 | value = parsed.group(3) 97 | stage = parsed.group(1) 98 | 99 | if stage == 'send': # query string is rendered here 100 | return '\n# HTTP Request:\n' + self.stripslashes(value) 101 | elif stage == 'reply': 102 | return '\n\n# HTTP Response:\n' + self.stripslashes(value) 103 | elif stage == 'header': 104 | return value + '\n' 105 | else: 106 | return value 107 | 108 | 109 | return line 110 | 111 | 112 | def consume(outbuffer = None): # Capture standard output 113 | sys.stdout = HTTPTranslator(outbuffer or sys.stdout) 114 | return sys.stdout 115 | 116 | 117 | if __name__ == '__main__': 118 | consume(sys.stdout).write(sys.stdin.read()) 119 | print '\n' 120 | 121 | # vim: set nowrap tabstop=4 shiftwidth=4 softtabstop=0 expandtab textwidth=0 filetype=python foldmethod=indent foldcolumn=4 122 | -------------------------------------------------------------------------------- /resources/lib/playback/stream_continuity.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Author: caphm 3 | # Module: stream_continuity 4 | # Created on: 02.08.2018 5 | # License: MIT https://goo.gl/5bMj3H 6 | # pylint: disable=import-error 7 | 8 | """ 9 | Remember and restore audio stream / subtitle settings between individual 10 | episodes of a tv show 11 | """ 12 | import xbmc 13 | 14 | from resources.lib.ui import xmldialogs, show_modal_dialog 15 | from resources.lib.playback import PlaybackActionManager 16 | 17 | STREAMS = { 18 | 'audio': { 19 | 'current': 'currentaudiostream', 20 | 'setter': xbmc.Player.setAudioStream, 21 | }, 22 | 'subtitle': { 23 | 'current': 'currentsubtitle', 24 | 'setter': xbmc.Player.setSubtitleStream, 25 | }, 26 | 'subtitleenabled': { 27 | 'current': 'subtitleenabled', 28 | 'setter': xbmc.Player.showSubtitles 29 | } 30 | } 31 | 32 | 33 | class StreamContinuityManager(PlaybackActionManager): 34 | """ 35 | Detects changes in audio / subtitle streams during playback, saves them 36 | for the currently playing show and restores them on subsequent episodes. 37 | """ 38 | def __init__(self, nx_common): 39 | super(StreamContinuityManager, self).__init__(nx_common) 40 | self.storage = nx_common.get_storage(__name__) 41 | self.current_show = None 42 | self.current_streams = {} 43 | self.player = xbmc.Player() 44 | self.did_restore = False 45 | 46 | @property 47 | def show_settings(self): 48 | """Stored stream settings for the current show""" 49 | return self.storage.get(self.current_show, {}) 50 | 51 | def _initialize(self, data): 52 | self.did_restore = False 53 | # let this throw a KeyError to disable this instance if the playback is 54 | # not a TV show 55 | self.current_show = data['tvshow_video_id'] 56 | 57 | def _on_playback_started(self, player_state): 58 | xbmc.sleep(1000) 59 | for stype in STREAMS: 60 | self._set_current_stream(stype, player_state) 61 | self._restore_stream(stype) 62 | self.did_restore = True 63 | 64 | def _on_tick(self, player_state): 65 | if not self.did_restore: 66 | self.log('Did not restore streams yet, ignoring tick') 67 | return 68 | 69 | for stype in STREAMS: 70 | current_stream = self.current_streams[stype] 71 | player_stream = player_state.get(STREAMS[stype]['current']) 72 | if player_stream != current_stream: 73 | self.log('{} has changed from {} to {}' 74 | .format(stype, current_stream, player_stream)) 75 | self._set_current_stream(stype, player_state) 76 | self._ask_to_save(stype, player_stream) 77 | 78 | def _set_current_stream(self, stype, player_state): 79 | self.current_streams.update({ 80 | stype: player_state.get(STREAMS[stype]['current']) 81 | }) 82 | 83 | def _restore_stream(self, stype): 84 | self.log('Trying to restore {}...'.format(stype)) 85 | set_stream = STREAMS[stype]['setter'] 86 | stored_stream = self.show_settings.get(stype) 87 | if (stored_stream is not None and 88 | self.current_streams[stype] != stored_stream): 89 | # subtitleenabled is boolean and not a dict 90 | set_stream(self.player, (stored_stream['index'] 91 | if isinstance(stored_stream, dict) 92 | else stored_stream)) 93 | self.current_streams[stype] = stored_stream 94 | self.log('Restored {} to {}'.format(stype, stored_stream)) 95 | 96 | def _ask_to_save(self, stype, stream): 97 | self.log('Asking to save {} for {}'.format(stream, stype)) 98 | new_show_settings = self.show_settings.copy() 99 | new_show_settings[stype] = stream 100 | show_modal_dialog( 101 | xmldialogs.SaveStreamSettings, 102 | "plugin-video-netflix-SaveStreamSettings.xml", 103 | self.addon.getAddonInfo('path'), 104 | minutes=0, 105 | seconds=5, 106 | new_show_settings=new_show_settings, 107 | tvshowid=self.current_show, 108 | storage=self.storage) 109 | 110 | 111 | def __str__(self): 112 | return ('enabled={}, current_show={}' 113 | .format(self.enabled, self.current_show)) 114 | -------------------------------------------------------------------------------- /resources/lib/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Module: utils 3 | # Created on: 13.01.2017 4 | 5 | """General utils""" 6 | 7 | import time 8 | import hashlib 9 | import platform 10 | from functools import wraps 11 | from types import FunctionType 12 | import xbmc 13 | 14 | from resources.lib.compat import itername 15 | 16 | class LoggingComponent(object): 17 | """ 18 | Prepends log statements with the calling class' name 19 | """ 20 | # pylint: disable=too-few-public-methods 21 | def __init__(self, nx_common): 22 | self._log = nx_common.log 23 | 24 | def log(self, msg, level=xbmc.LOGDEBUG): 25 | """ 26 | Log a message 27 | """ 28 | self._log('{}: {}'.format(self.__class__.__name__, msg), level) 29 | 30 | 31 | def noop(**kwargs): 32 | """Takes everything, does nothing, classic no operation function""" 33 | return kwargs 34 | 35 | 36 | def log(func): 37 | """ 38 | Log decarator that is used to annotate methods & output everything to 39 | the Kodi debug log 40 | 41 | :param delay: retry delay in sec 42 | :type delay: int 43 | :returns: string -- Devices MAC address 44 | """ 45 | name = func.__name__ 46 | 47 | @wraps(func) 48 | def wrapped(*args, **kwargs): 49 | """Wrapper function to maintain correct stack traces""" 50 | that = args[0] 51 | class_name = that.__class__.__name__ 52 | arguments = '' 53 | for key, value in getattr(kwargs, itername)(): 54 | if key != 'account' and key != 'credentials': 55 | arguments += ":%s = %s:" % (key, value) 56 | if arguments != '': 57 | that.log('"' + class_name + '::' + name + 58 | '" called with arguments ' + arguments) 59 | else: 60 | that.log('"' + class_name + '::' + name + '" called') 61 | result = func(*args, **kwargs) 62 | that.log('"' + class_name + '::' + name + '" returned: ' + str(result)) 63 | return result 64 | 65 | wrapped.__doc__ = func.__doc__ 66 | return wrapped 67 | 68 | 69 | def get_user_agent(): 70 | """ 71 | Determines the user agent string for the current platform. 72 | Needed to retrieve a valid ESN (except for Android, where the ESN can 73 | be generated locally) 74 | 75 | :returns: str -- User agent string 76 | """ 77 | chrome_version = 'Chrome/59.0.3071.115' 78 | base = 'Mozilla/5.0 ' 79 | base += '%PL% ' 80 | base += 'AppleWebKit/537.36 (KHTML, like Gecko) ' 81 | base += '%CH_VER% Safari/537.36'.replace('%CH_VER%', chrome_version) 82 | system = platform.system() 83 | # Mac OSX 84 | if system == 'Darwin': 85 | return base.replace('%PL%', '(Macintosh; Intel Mac OS X 10_10_1)') 86 | # Windows 87 | if system == 'Windows': 88 | return base.replace('%PL%', '(Windows NT 6.1; WOW64)') 89 | # ARM based Linux 90 | if platform.machine().startswith('arm'): 91 | return base.replace('%PL%', '(X11; CrOS armv7l 7647.78.0)') 92 | # x86 Linux 93 | return base.replace('%PL%', '(X11; Linux x86_64)') 94 | 95 | 96 | def uniq_id(delay=1): 97 | """ 98 | Returns a unique id based on the devices MAC address 99 | 100 | :param delay: Retry delay in sec 101 | :type delay: int 102 | :returns: string -- Unique secret 103 | """ 104 | mac_addr = __get_mac_address(delay=delay) 105 | if ':' in mac_addr and delay == 2: 106 | return hashlib.sha256(str(mac_addr).encode()).digest() 107 | else: 108 | return hashlib.sha256('UnsafeStaticSecret'.encode()).digest() 109 | 110 | 111 | def __get_mac_address(delay=1): 112 | """ 113 | Returns the users mac address 114 | 115 | :param delay: retry delay in sec 116 | :type delay: int 117 | :returns: string -- Devices MAC address 118 | """ 119 | mac_addr = xbmc.getInfoLabel('Network.MacAddress') 120 | # hack response busy 121 | i = 0 122 | while ':' not in mac_addr and i < 3: 123 | i += 1 124 | time.sleep(delay) 125 | mac_addr = xbmc.getInfoLabel('Network.MacAddress') 126 | return mac_addr 127 | 128 | 129 | def get_class_methods(class_item=None): 130 | """ 131 | Returns the class methods of agiven class object 132 | 133 | :param class_item: Class item to introspect 134 | :type class_item: object 135 | :returns: list -- Class methods 136 | """ 137 | _type = FunctionType 138 | return [x for x, y in class_item.__dict__.items() if isinstance(y, _type)] 139 | 140 | 141 | def find_episode(episode_id, seasons): 142 | """ 143 | Return metadata for a specific episode from within a nested 144 | metadata dict. 145 | Returns an empty dict if the episode could not be found. 146 | """ 147 | for season in seasons: 148 | for episode in season['episodes']: 149 | if str(episode['id']) == episode_id: 150 | return episode 151 | return {} 152 | -------------------------------------------------------------------------------- /resources/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /resources/lib/MSLCrypto.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | # -*- coding: utf-8 -*- 3 | # Author: trummerjo 4 | # Module: MSLHttpRequestHandler 5 | # Created on: 26.01.2017 6 | # License: MIT https://goo.gl/5bMj3H 7 | 8 | from Cryptodome.Random import get_random_bytes 9 | from Cryptodome.Hash import HMAC, SHA256 10 | from Cryptodome.Cipher import PKCS1_OAEP 11 | from Cryptodome.PublicKey import RSA 12 | from Cryptodome.Util import Padding 13 | from Cryptodome.Cipher import AES 14 | import json 15 | import base64 16 | 17 | 18 | class MSLCrypto(): 19 | 20 | def __init__(self, kodi_helper): 21 | self.kodi_helper = kodi_helper 22 | self.encryption_key = None 23 | self.sign_key = None 24 | 25 | def __init_generate_rsa_keys(self): 26 | self.kodi_helper.log(msg='Create new RSA Keys') 27 | # Create new Key Pair and save 28 | self.rsa_key = RSA.generate(2048) 29 | 30 | @staticmethod 31 | def __base64key_decode(payload): 32 | l = len(payload) % 4 33 | if l == 2: 34 | payload += '==' 35 | elif l == 3: 36 | payload += '=' 37 | elif l != 0: 38 | raise ValueError('Invalid base64 string') 39 | return base64.urlsafe_b64decode(payload.encode('utf-8')) 40 | 41 | def toDict(self): 42 | self.kodi_helper.log(msg='Provide RSA Keys to dict') 43 | # Get the DER Base64 of the keys 44 | encrypted_key = self.rsa_key.exportKey() 45 | 46 | data = { 47 | "encryption_key": base64.standard_b64encode(self.encryption_key).decode('ascii'), 48 | 'sign_key': base64.standard_b64encode(self.sign_key).decode('ascii'), 49 | 'rsa_key': base64.standard_b64encode(encrypted_key).decode('ascii') 50 | } 51 | return data 52 | 53 | def fromDict(self, msl_data): 54 | need_handshake = False 55 | rsa_key = None 56 | 57 | try: 58 | self.kodi_helper.log(msg='Parsing RSA Keys from Dict') 59 | self.encryption_key = base64.standard_b64decode(msl_data['encryption_key']) 60 | self.sign_key = base64.standard_b64decode(msl_data['sign_key']) 61 | rsa_key = base64.standard_b64decode(msl_data['rsa_key']) 62 | self.rsa_key = RSA.importKey(rsa_key) 63 | except: 64 | need_handshake = True 65 | 66 | if not rsa_key: 67 | need_handshake = True 68 | self.__init_generate_rsa_keys() 69 | 70 | if not (self.encryption_key and self.sign_key): 71 | need_handshake = True 72 | 73 | return need_handshake 74 | 75 | def get_key_request(self): 76 | raw_key = self.rsa_key.publickey().exportKey(format='DER') 77 | public_key = base64.standard_b64encode(raw_key).decode('ascii') 78 | 79 | key_request = [{ 80 | 'scheme': 'ASYMMETRIC_WRAPPED', 81 | 'keydata': { 82 | 'publickey': public_key, 83 | 'mechanism': 'JWK_RSA', 84 | 'keypairid': 'superKeyPair' 85 | } 86 | }] 87 | return key_request 88 | 89 | def parse_key_response(self, headerdata): 90 | # Init Decryption 91 | enc_key = headerdata['keyresponsedata']['keydata']['encryptionkey'] 92 | hmac_key = headerdata['keyresponsedata']['keydata']['hmackey'] 93 | encrypted_encryption_key = base64.standard_b64decode(enc_key) 94 | encrypted_sign_key = base64.standard_b64decode(hmac_key) 95 | cipher_rsa = PKCS1_OAEP.new(self.rsa_key) 96 | 97 | # Decrypt encryption key 98 | cipher_raw = cipher_rsa.decrypt(encrypted_encryption_key) 99 | encryption_key_data = json.JSONDecoder().decode(cipher_raw.decode()) 100 | self.encryption_key = self.__base64key_decode(encryption_key_data['k']) 101 | 102 | # Decrypt sign key 103 | sign_key_raw = cipher_rsa.decrypt(encrypted_sign_key) 104 | sign_key_data = json.JSONDecoder().decode(sign_key_raw.decode()) 105 | self.sign_key = self.__base64key_decode(sign_key_data['k']) 106 | 107 | def decrypt(self, iv, data): 108 | cipher = AES.new(self.encryption_key, AES.MODE_CBC, iv) 109 | return Padding.unpad(cipher.decrypt(data), 16) 110 | 111 | def encrypt(self, data, esn, sequence_number): 112 | """ 113 | Encrypt the given Plaintext with the encryption key 114 | :param plaintext: 115 | :return: Serialized JSON String of the encryption Envelope 116 | """ 117 | iv = get_random_bytes(16) 118 | encryption_envelope = { 119 | 'ciphertext': '', 120 | 'keyid': esn + '_' + str(sequence_number), 121 | 'sha256': 'AA==', 122 | 'iv': base64.standard_b64encode(iv).decode('ascii') 123 | } 124 | # Padd the plaintext 125 | plaintext = Padding.pad(data.encode('utf-8'), 16) 126 | # Encrypt the text 127 | cipher = AES.new(self.encryption_key, AES.MODE_CBC, iv) 128 | citext = cipher.encrypt(plaintext) 129 | encryption_envelope['ciphertext'] = base64.standard_b64encode(citext).decode('ascii') 130 | 131 | return encryption_envelope; 132 | 133 | def sign(self, message): 134 | return HMAC.new(self.sign_key, message, SHA256).digest() -------------------------------------------------------------------------------- /resources/test/test_Utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Module: Utils 3 | # Author: asciidisco 4 | # Created on: 11.10.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Tests for the `Utils` module""" 8 | 9 | import unittest 10 | import mock 11 | from resources.lib.utils import get_user_agent, noop, uniq_id, get_class_methods, __get_mac_address as gma 12 | from mocks.MinimalClassMocks import MockClass 13 | from mocks.LoggerMocks import TestLoggerWithArgs, TestLoggerWithCredentialArgs, TestLoggerWithNoArgs 14 | 15 | 16 | class UtilsTestCase(unittest.TestCase): 17 | """Tests for the `Utils` module""" 18 | 19 | def test_get_user_agent(self): 20 | """ADD ME""" 21 | self.assertIn( 22 | container=get_user_agent(), 23 | member='Chrome/59.0.3071.115') 24 | 25 | @mock.patch('platform.system') 26 | def test_get_user_agent_Linux(self, mock_system): 27 | """ADD ME""" 28 | mock_system.return_value = 'Linux' 29 | self.assertIn( 30 | container=get_user_agent(), 31 | member='Linux') 32 | 33 | @mock.patch('platform.system') 34 | def test_get_user_agent_Darwin(self, mock_system): 35 | """ADD ME""" 36 | mock_system.return_value = 'Darwin' 37 | self.assertIn( 38 | container=get_user_agent(), 39 | member='Mac') 40 | 41 | @mock.patch('platform.system') 42 | def test_get_user_agent_Windows(self, mock_system): 43 | """ADD ME""" 44 | mock_system.return_value = 'Windows' 45 | self.assertIn( 46 | container=get_user_agent(), 47 | member='Win') 48 | 49 | @mock.patch('platform.system') 50 | @mock.patch('platform.machine') 51 | def test_get_user_agent_Arm(self, mock_machine, mock_system): 52 | """ADD ME""" 53 | mock_system.return_value = 'Linux' 54 | mock_machine.return_value = 'arm' 55 | self.assertIn( 56 | container=get_user_agent(), 57 | member='armv') 58 | 59 | @mock.patch('xbmcaddon.Addon') 60 | def test_uniq_id(self, mock_xbmcaddon): 61 | """ADD ME""" 62 | self.assertEquals( 63 | first=uniq_id(delay=1), 64 | second='=\x05\xc1\xf7\x0b\xb5&\xd0\xa2\xd1]\xce\xf3\xee\x92\x8a\xb5\xc7\x985\x8a{\xf5A6TD\xf3/\x93\x84W') 65 | 66 | @mock.patch('xbmc.getInfoLabel') 67 | def test_get_mac_address_delay(self, mock_getInfoLabel): 68 | """ADD ME""" 69 | mock_getInfoLabel.return_value = '00:80:41:ae:fd:7e' 70 | self.assertEqual( 71 | first=uniq_id(delay=2), 72 | second='\xf7K\x87\xb3\xc4\xd2\xd4\xea7\x91\x99( C\xb2\x8e\xa7\x8d}L\xdbP\x93\xfaM&4\x8a\xb6\xda\xcfG') 73 | 74 | @mock.patch('xbmc.getInfoLabel') 75 | def test_get_mac_address(self, mock_getInfoLabel): 76 | """ADD ME""" 77 | mock_getInfoLabel.return_value = '00:80:41:ae:fd:7e' 78 | self.assertEqual( 79 | first=gma(), 80 | second='00:80:41:ae:fd:7e') 81 | 82 | @mock.patch('xbmc.getInfoLabel') 83 | def test_get_mac_address_malformed(self, mock_getInfoLabel): 84 | """ADD ME""" 85 | mock_getInfoLabel.return_value = '00-80-41-ae-fd-7e' 86 | self.assertEqual( 87 | first=gma(), 88 | second='00-80-41-ae-fd-7e') 89 | 90 | def test_noop(self): 91 | """ADD ME""" 92 | self.assertEqual( 93 | first=noop(a='a'), 94 | second={'a': 'a'}) 95 | 96 | def test_log_decorator(self): 97 | """Does log messages if a log function is applied to the parent class""" 98 | def logger_1(message): 99 | if 'returned' in message: 100 | self.assertEqual( 101 | first=message, 102 | second='"TestLoggerWithNoArgs::to_be_logged" returned: None') 103 | else: 104 | self.assertEqual( 105 | first=message, 106 | second='"TestLoggerWithNoArgs::to_be_logged" called') 107 | instTestLoggerWithNoArgs = TestLoggerWithNoArgs(logger_1=logger_1) 108 | instTestLoggerWithNoArgs.to_be_logged() 109 | 110 | def logger_2(message): 111 | if 'returned' in message: 112 | self.assertEqual( 113 | first=message, 114 | second='"TestLoggerWithArgs::to_be_logged" returned: None') 115 | else: 116 | self.assertEqual( 117 | first=message, 118 | second='"TestLoggerWithArgs::to_be_logged" called with arguments :a = b:') 119 | instTestLoggerWithArgs = TestLoggerWithArgs(logger_2=logger_2) 120 | instTestLoggerWithArgs.to_be_logged(a='b') 121 | 122 | def logger_3(message): 123 | if 'returned' in message: 124 | self.assertEqual( 125 | first=message, 126 | second='"TestLoggerWithCredentialArgs::to_be_logged" returned: None') 127 | else: 128 | self.assertEqual( 129 | first=message, 130 | second='"TestLoggerWithCredentialArgs::to_be_logged" called with arguments :a = b:') 131 | instTestLoggerWithCredentialArgs = TestLoggerWithCredentialArgs(logger_3=logger_3) 132 | instTestLoggerWithCredentialArgs.to_be_logged(credentials='foo', account='bar', a='b') 133 | 134 | def test_get_class_methods(self): 135 | self.assertEqual( 136 | first=get_class_methods(class_item=MockClass), 137 | second=['bar', 'foo', '__init__']) 138 | -------------------------------------------------------------------------------- /resources/lib/NetflixCommon.py: -------------------------------------------------------------------------------- 1 | import xbmc 2 | from xbmcaddon import Addon 3 | import xbmcvfs 4 | import json 5 | 6 | from resources.lib.storage import PersistentStorage 7 | from resources.lib.compat import compat_unicode 8 | 9 | 10 | class Signals(object): 11 | PLAYBACK_INITIATED = 'playback_initiated' 12 | 13 | 14 | class NetflixCommon(object): 15 | """ 16 | Stuff shared between / used from service and addon""" 17 | 18 | def __init__(self, plugin_handle, base_url): 19 | 20 | self.addon = Addon() 21 | self.data_path = xbmc.translatePath(self.addon.getAddonInfo('profile')) 22 | self.cookie_path = self.data_path + 'COOKIE' 23 | self.plugin = self.addon.getAddonInfo('name') 24 | self.verb_log = self.addon.getSetting('logging') == 'true' 25 | self.plugin_handle = plugin_handle 26 | self.base_url = base_url 27 | self.version = self.addon.getAddonInfo('version') 28 | 29 | xbmcvfs.mkdir(path=self.data_path) 30 | 31 | def get_addon(self): 32 | """Return the current addon instance""" 33 | return self.addon 34 | 35 | def get_addon_info(self, name): 36 | """Return the current addon instance""" 37 | return self.addon.getAddonInfo(name) 38 | 39 | def set_setting(self, key, value): 40 | return self.addon.setSetting(key, value) 41 | 42 | def get_setting(self, key): 43 | return self.addon.getSetting(key) 44 | 45 | def flush_settings(self): 46 | self.addon = Addon() 47 | 48 | def get_storage(self, storage_id): 49 | return PersistentStorage(storage_id, self) 50 | 51 | def get_esn(self): 52 | """ 53 | Returns the esn from settings 54 | """ 55 | return self.addon.getSetting('esn') 56 | 57 | def set_esn(self, esn): 58 | """ 59 | Returns True if MSL reset is required 60 | """ 61 | stored_esn = self.get_esn() 62 | if not stored_esn and esn: 63 | self.set_setting('esn', esn) 64 | return True 65 | return False 66 | 67 | def get_credentials(self): 68 | from resources.lib.NetflixCredentials import NetflixCredentials 69 | email = self.get_setting('email') 70 | password = self.get_setting('password') 71 | 72 | if '@' in email: 73 | self.set_credentials(email, password) 74 | return {'email' : email, 'password' : password } 75 | 76 | return NetflixCredentials().decode_credentials(email, password) 77 | 78 | def set_credentials(self, email, password): 79 | from resources.lib.NetflixCredentials import NetflixCredentials 80 | encoded = NetflixCredentials().encode_credentials(email, password) 81 | self.set_setting('email',encoded['email']) 82 | self.set_setting('password',encoded['password']) 83 | 84 | def log(self, msg, level=xbmc.LOGDEBUG): 85 | """Adds a log entry to the Kodi log 86 | 87 | Parameters 88 | ---------- 89 | msg : :obj:`str` 90 | Entry that should be turned into a list item 91 | 92 | level : :obj:`int` 93 | Kodi log level 94 | """ 95 | if isinstance(msg, str): 96 | msg = msg.encode('utf-8') 97 | xbmc.log('[%s] %s' % (self.plugin, msg.__str__()), level) 98 | 99 | @staticmethod 100 | def check_folder_path(path): 101 | """ 102 | Check if folderpath ends with path delimator 103 | If not correct it (makes sure xbmcvfs.exists is working correct) 104 | """ 105 | if isinstance(path, compat_unicode): 106 | check = path.encode('ascii', 'ignore') 107 | if b'/' in check and not str(check).endswith('/'): 108 | end = u'/' 109 | path = path + end 110 | return path 111 | if b'\\' in check and not str(check).endswith('\\'): 112 | end = u'\\' 113 | path = path + end 114 | return path 115 | if '/' in path and not compat_unicode(path).endswith('/'): 116 | path = path + '/' 117 | return path 118 | if '\\' in path and not compat_unicode(path).endswith('\\'): 119 | path = path + '\\' 120 | return path 121 | 122 | @staticmethod 123 | def file_exists(data_path, filename): 124 | """ 125 | Checks if a given file exists 126 | :param filename: The filename 127 | :return: True if so 128 | """ 129 | return xbmcvfs.exists(path=data_path + filename) 130 | 131 | @staticmethod 132 | def save_file(data_path, filename, content): 133 | """ 134 | Saves the given content under given filename 135 | :param filename: The filename 136 | :param content: The content of the file 137 | """ 138 | 139 | file_handle = xbmcvfs.File( 140 | filepath=data_path + filename, 141 | mode='w') 142 | file_content = file_handle.write(content) 143 | file_handle.close() 144 | 145 | @staticmethod 146 | def load_file(data_path, filename): 147 | """ 148 | Loads the content of a given filename 149 | :param filename: The file to load 150 | :return: The content of the file 151 | """ 152 | file_handle = xbmcvfs.File( 153 | filepath=data_path + filename) 154 | file_content = file_handle.read() 155 | file_handle.close() 156 | return file_content 157 | 158 | @staticmethod 159 | def list_dir(data_path): 160 | return xbmcvfs.listdir(data_path) 161 | 162 | @staticmethod 163 | def compare_versions(v1, v2): 164 | if len(v1) != len(v2): 165 | return len(v1) - len(v2) 166 | for i in range(0, len(v1)): 167 | if v1[i] > v2[i]: 168 | return 1 169 | elif v1[i] < v2[i]: 170 | return -1 171 | return 0 172 | -------------------------------------------------------------------------------- /resources/test/test_KodiHelper_Dialogs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Module: KodiHelper.Dialogs 3 | # Author: asciidisco 4 | # Created on: 11.10.2017 5 | # License: MIT https://goo.gl/5bMj3H 6 | 7 | """Tests for the `KodiHelper.Dialogs` module""" 8 | 9 | import unittest 10 | import mock 11 | from resources.lib.KodiHelper import KodiHelper 12 | from resources.lib.ui.Dialogs import Dialogs 13 | 14 | 15 | class KodiHelperDialogsTestCase(unittest.TestCase): 16 | """Tests for the `KodiHelper.Dialogs` module""" 17 | 18 | def test_show_rating_dialog(self): 19 | """Can call rating dialog""" 20 | kodi_helper = KodiHelper() 21 | self.assertEqual( 22 | first=kodi_helper.dialogs.show_rating_dialog(), 23 | second='') 24 | 25 | def test_show_adult_pin_dialog(self): 26 | """Can call adult pin dialog""" 27 | kodi_helper = KodiHelper() 28 | self.assertEqual( 29 | first=kodi_helper.dialogs.show_adult_pin_dialog(), 30 | second='') 31 | 32 | def test_show_search_term_dialog(self): 33 | """Can call input search term dialog (without value)""" 34 | kodi_helper = KodiHelper() 35 | self.assertEqual( 36 | first=kodi_helper.dialogs.show_search_term_dialog(), 37 | second=None) 38 | 39 | @mock.patch('xbmcgui.Dialog.input') 40 | def test_show_search_term_dialog_with_value(self, mock_dialog_input): 41 | """Can call input search term dialog (with value)""" 42 | mock_dialog_input.return_value = 'a' 43 | kodi_helper = KodiHelper() 44 | self.assertEqual( 45 | first=kodi_helper.dialogs.show_search_term_dialog(), 46 | second='a') 47 | 48 | def test_show_add_to_library_title_dialog(self): 49 | """Can call input library title dialog (without export)""" 50 | kodi_helper = KodiHelper() 51 | self.assertEqual( 52 | first=kodi_helper.dialogs.show_add_library_title_dialog( 53 | original_title='foo'), 54 | second='foo') 55 | 56 | def test_show_add_library_title_dialog_export_true(self): 57 | """Can call input library title dialog (with export)""" 58 | kodi_helper = KodiHelper() 59 | kodi_helper.dialogs.custom_export_name = 'true' 60 | self.assertEqual( 61 | first=kodi_helper.dialogs.show_add_library_title_dialog( 62 | original_title='foo'), 63 | second='foo') 64 | 65 | def test_show_password_dialog(self): 66 | """Can call input password dialog""" 67 | kodi_helper = KodiHelper() 68 | self.assertEqual( 69 | first=kodi_helper.dialogs.show_password_dialog(), 70 | second='') 71 | 72 | def test_show_email_dialog(self): 73 | """Can call input email dialog""" 74 | kodi_helper = KodiHelper() 75 | self.assertEqual( 76 | first=kodi_helper.dialogs.show_email_dialog(), 77 | second='') 78 | 79 | def test_show_login_failed_notify(self): 80 | """Can call login failed notification""" 81 | kodi_helper = KodiHelper() 82 | self.assertEqual( 83 | first=kodi_helper.dialogs.show_login_failed_notify(), 84 | second=None) 85 | 86 | def test_show_request_error_notify(self): 87 | """Can call request error notification""" 88 | kodi_helper = KodiHelper() 89 | self.assertEqual( 90 | first=kodi_helper.dialogs.show_request_error_notify(), 91 | second=None) 92 | 93 | def test_show_is_missing_notify(self): 94 | """Can call inputstream not installed notification""" 95 | kodi_helper = KodiHelper() 96 | self.assertEqual( 97 | first=kodi_helper.dialogs.show_is_missing_notify(), 98 | second=None) 99 | 100 | def test_show_no_search_results_notify(self): 101 | """Can call no search results notification""" 102 | kodi_helper = KodiHelper() 103 | self.assertEqual( 104 | first=kodi_helper.dialogs.show_no_search_results_notify(), 105 | second=None) 106 | 107 | def test_show_is_inactive_notify(self): 108 | """Can call inputstream inactive (disabled) notification""" 109 | kodi_helper = KodiHelper() 110 | self.assertEqual( 111 | first=kodi_helper.dialogs.show_is_inactive_notify(), 112 | second=None) 113 | 114 | def test_show_invalid_pin_notify(self): 115 | """Can call invalid adult pin notification""" 116 | kodi_helper = KodiHelper() 117 | self.assertEqual( 118 | first=kodi_helper.dialogs.show_invalid_pin_notify(), 119 | second=None) 120 | 121 | def test_show_no_seasons_notify(self): 122 | """Can call no seasons available notification""" 123 | kodi_helper = KodiHelper() 124 | self.assertEqual( 125 | first=kodi_helper.dialogs.show_no_seasons_notify(), 126 | second=None) 127 | 128 | def test_show_db_updated_notify(self): 129 | """Can call local db update notification""" 130 | kodi_helper = KodiHelper() 131 | self.assertEqual( 132 | first=kodi_helper.dialogs.show_db_updated_notify(), 133 | second=None) 134 | 135 | def test_show_no_metadata_notify(self): 136 | """Can call no metadata notification""" 137 | kodi_helper = KodiHelper() 138 | self.assertEqual( 139 | first=kodi_helper.dialogs.show_no_metadata_notify(), 140 | second=None) 141 | 142 | def test_show_autologin_enabled_notify(self): 143 | """Can call autologin enabled notification""" 144 | kodi_helper = KodiHelper() 145 | self.assertEqual( 146 | first=kodi_helper.dialogs.show_autologin_enabled_notify(), 147 | second=None) 148 | 149 | def test_show_finally_remove_modal(self): 150 | """Can call finally remove from exported db modal""" 151 | kodi_helper = KodiHelper() 152 | self.assertEqual( 153 | first=kodi_helper.dialogs.show_finally_remove_modal(title='foo', year='2015'), 154 | second=True) 155 | 156 | def test_show_finally_remove_modal_with_empty_year(self): 157 | """Can call finally remove from exported db modal with default year""" 158 | kodi_helper = KodiHelper() 159 | self.assertEqual( 160 | first=kodi_helper.dialogs.show_finally_remove_modal(title='foo'), 161 | second=True) 162 | -------------------------------------------------------------------------------- /resources/lib/MSLMediaDrm.py: -------------------------------------------------------------------------------- 1 | from os import urandom 2 | import json 3 | import base64 4 | import xbmcdrm 5 | 6 | class MSLMediaDrmCrypto: 7 | 8 | def __init__(self, kodi_helper): 9 | self.kodi_helper = kodi_helper 10 | 11 | self.keySetId = None 12 | self.keyId = None 13 | self.hmacKeyId = None 14 | 15 | try: 16 | self.cryptoSession = xbmcdrm.CryptoSession('edef8ba9-79d6-4ace-a3c8-27dcd51d21ed', 17 | 'AES/CBC/NoPadding', 'HmacSHA256') 18 | self.kodi_helper.log(msg='Widevine CryptoSession successful constructed') 19 | except: 20 | self.cryptoSession = None 21 | return 22 | 23 | self.systemId = self.cryptoSession.GetPropertyString('systemId') 24 | self.kodi_helper.log(msg='Widevine CryptoSession systemId:' + self.systemId) 25 | 26 | algorithms = self.cryptoSession.GetPropertyString('algorithms') 27 | self.kodi_helper.log(msg='Widevine CryptoSession algorithms:' + algorithms) 28 | 29 | def __del__(self): 30 | self.cryptoSession = None 31 | 32 | def __getKeyRequest(self, data): 33 | #No key update supported -> remove existing keys 34 | self.cryptoSession.RemoveKeys() 35 | keyRequest = self.cryptoSession.GetKeyRequest(data, 'application/xml', True, dict()) 36 | if keyRequest: 37 | self.kodi_helper.log(msg='Widevine CryptoSession getKeyRequest successful with size:' 38 | + str(len(keyRequest))) 39 | return keyRequest 40 | else: 41 | self.kodi_helper.log(msg='Widevine CryptoSession getKeyRequest failed!') 42 | 43 | def __provideKeyResponse(self, data): 44 | if len(data) == 0: 45 | return false 46 | 47 | self.keySetId = self.cryptoSession.ProvideKeyResponse(bytearray(data)) 48 | 49 | if self.keySetId: 50 | self.kodi_helper.log(msg='Widevine CryptoSession provideKeyResponse successful, keySetId:' 51 | + self.keySetId) 52 | else: 53 | self.kodi_helper.log(msg='Widevine CryptoSession provideKeyResponse failed!') 54 | 55 | self.keySetId = self.keySetId.encode() 56 | 57 | return self.keySetId != None 58 | 59 | def toDict(self): 60 | self.kodi_helper.log(msg='Provide Widevine keys to dict') 61 | data = { 62 | "key_set_id": base64.standard_b64encode(self.keySetId).decode('ascii'), 63 | 'key_id': base64.standard_b64encode(self.keyId).decode('ascii'), 64 | 'hmac_key_id': base64.standard_b64encode(self.hmacKeyId).decode('ascii') 65 | } 66 | return data 67 | 68 | def fromDict(self, msl_data): 69 | need_handshake = False 70 | 71 | if not self.cryptoSession: 72 | return False 73 | 74 | try: 75 | self.kodi_helper.log(msg='Parsing Widevine keys from Dict') 76 | self.keySetId = base64.standard_b64decode(msl_data['key_set_id']) 77 | self.keyId = base64.standard_b64decode(msl_data['key_id']) 78 | self.hmacKeyId = base64.standard_b64decode(msl_data['hmac_key_id']) 79 | 80 | self.cryptoSession.RestoreKeys(self.keySetId) 81 | 82 | except: 83 | need_handshake = True 84 | 85 | return need_handshake 86 | 87 | def get_key_request(self): 88 | drmKeyRequest = self.__getKeyRequest(bytearray([10, 122, 0, 108, 56, 43])) 89 | 90 | key_request = [{ 91 | 'scheme': 'WIDEVINE', 92 | 'keydata': { 93 | 'keyrequest': base64.standard_b64encode(drmKeyRequest).decode('ascii') 94 | } 95 | }] 96 | 97 | return key_request 98 | 99 | def parse_key_response(self, headerdata): 100 | # Init Decryption 101 | key_resonse = base64.standard_b64decode(headerdata['keyresponsedata']['keydata']['cdmkeyresponse']) 102 | 103 | if not self.__provideKeyResponse(key_resonse): 104 | return 105 | 106 | self.keyId = base64.standard_b64decode(headerdata['keyresponsedata']['keydata']['encryptionkeyid']) 107 | self.hmacKeyId = base64.standard_b64decode(headerdata['keyresponsedata']['keydata']['hmackeyid']) 108 | 109 | def decrypt(self, iv, data): 110 | decrypted = self.cryptoSession.Decrypt(bytearray(self.keyId), bytearray(data), bytearray(iv)) 111 | 112 | if decrypted: 113 | self.kodi_helper.log(msg='Widevine CryptoSession decrypt successful: ' 114 | + str(len(decrypted)) + ' bytes returned') 115 | 116 | # remove PKCS5Padding 117 | pad = decrypted[len(decrypted) - 1] 118 | 119 | return decrypted[:-pad].decode('utf-8') 120 | else: 121 | self.kodi_helper.log(msg='Widevine CryptoSession decrypt failed!') 122 | 123 | def encrypt(self, data, esn, sequence_number): 124 | 125 | iv = bytearray(urandom(16)) 126 | 127 | # Add PKCS5Padding 128 | pad = 16 - len(data) % 16 129 | newData = data + ''.join([chr(pad)] * pad) 130 | 131 | encrypted = self.cryptoSession.Encrypt(bytearray(self.keyId), newData, iv) 132 | 133 | if encrypted: 134 | self.kodi_helper.log(msg='Widevine CryptoSession encrypt successful: ' 135 | + str(len(encrypted)) + ' bytes returned') 136 | 137 | encryption_envelope = { 138 | 'version' : 1, 139 | 'ciphertext': base64.standard_b64encode(encrypted).decode('ascii'), 140 | 'sha256': 'AA==', 141 | 'keyid': base64.standard_b64encode(self.keyId).decode('ascii'), 142 | #'cipherspec' : 'AES/CBC/PKCS5Padding', 143 | 'iv': base64.standard_b64encode(iv).decode('ascii') 144 | } 145 | return encryption_envelope 146 | else: 147 | self.kodi_helper.log(msg='Widevine CryptoSession encrypt failed!') 148 | 149 | def sign(self, message): 150 | signature = self.cryptoSession.Sign(bytearray(self.hmacKeyId), bytearray(message)) 151 | if signature: 152 | self.kodi_helper.log(msg='Widevine CryptoSession sign success: length:' 153 | + str(len(signature))) 154 | return signature 155 | else: 156 | self.kodi_helper.log(msg='Widevine CryptoSession sign failed!') 157 | 158 | def verify(self, message, signature): 159 | return self.cryptoSession.Verify(bytearray(self.hmacKeyId), bytearray(message), bytearray(signature)) 160 | -------------------------------------------------------------------------------- /resources/language/resource.language.he_il/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2017-10-17 17:37+0300\n" 11 | "Last-Translator: A. Dambledore\n" 12 | "Language-Team: Eng2Heb\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: he_IL\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | 20 | msgctxt "Addon Summary" 21 | msgid "Netflix" 22 | msgstr "נטפליקס" 23 | 24 | msgctxt "Addon Description" 25 | msgid "Netflix VOD Services Addon" 26 | msgstr "תוסף שירותי וי או די של נטפליקס" 27 | 28 | msgctxt "Addon Disclaimer" 29 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 30 | msgstr "חלקים שונים בתוסף זה עשויים להיות לא חוקיים באיזור שהייתך - אנא בדוק את החוקים לפני ההתקנה." 31 | 32 | msgctxt "#30001" 33 | msgid "Recommendations" 34 | msgstr "המלצות" 35 | 36 | msgctxt "#30002" 37 | msgid "Adult Pin" 38 | msgstr "קוד מבוגרים" 39 | 40 | msgctxt "#30003" 41 | msgid "Search term" 42 | msgstr "חפש ביטוי" 43 | 44 | msgctxt "#30004" 45 | msgid "Password" 46 | msgstr "סיסמה" 47 | 48 | msgctxt "#30005" 49 | msgid "E-mail" 50 | msgstr "אי-מייל" 51 | 52 | msgctxt "#30006" 53 | msgid "Adult verification failed" 54 | msgstr "אימות קוד מבוגרים נכשל" 55 | 56 | msgctxt "#30007" 57 | msgid "Please Check your adult pin" 58 | msgstr "אנא בדוק את קוד המבוגרים" 59 | 60 | msgctxt "#30008" 61 | msgid "Login failed" 62 | msgstr "הכניסה נכשלה" 63 | 64 | msgctxt "#30009" 65 | msgid "Please Check your credentials" 66 | msgstr "אנא בדוק את הגדרות ההרשאה (שם משתמש וסיסמה)" 67 | 68 | msgctxt "#30010" 69 | msgid "Genres" 70 | msgstr "סגנונות" 71 | 72 | msgctxt "#30011" 73 | msgid "Search" 74 | msgstr "חיפוש" 75 | 76 | msgctxt "#30012" 77 | msgid "No seasons available" 78 | msgstr "לא נמצאו עונות" 79 | 80 | msgctxt "#30013" 81 | msgid "No matches found" 82 | msgstr "לא נמצאו התאמות" 83 | 84 | msgctxt "#30014" 85 | msgid "Account" 86 | msgstr "חשבון" 87 | 88 | msgctxt "#30017" 89 | msgid "Logout" 90 | msgstr "ניתוק חשבון" 91 | 92 | msgctxt "#30018" 93 | msgid "Export to library" 94 | msgstr "יצא לספרייה" 95 | 96 | msgctxt "#30019" 97 | msgid "Rate on Netflix" 98 | msgstr "תן ציון בנטפליקס" 99 | 100 | msgctxt "#30020" 101 | msgid "Remove from 'My list'" 102 | msgstr "הסר מתוך הרשימה שלי" 103 | 104 | msgctxt "#30021" 105 | msgid "Add to 'My list'" 106 | msgstr "הוסף לתוך הרשימה שלי" 107 | 108 | msgctxt "#30022" 109 | msgid "(between 0 & 10)" 110 | msgstr "(בין 0 ו- 10)" 111 | 112 | msgctxt "#30023" 113 | msgid "Expert" 114 | msgstr "מומחה" 115 | 116 | msgctxt "#30024" 117 | msgid "SSL verification" 118 | msgstr "אימות SSL" 119 | 120 | msgctxt "#30025" 121 | msgid "Library" 122 | msgstr "ספרייה" 123 | 124 | msgctxt "#30026" 125 | msgid "Enable custom library folder" 126 | msgstr "אפשר תיקייה מותאמת אישית לספרייה" 127 | 128 | msgctxt "#30027" 129 | msgid "Custom library path" 130 | msgstr "נתיב ספרייה מותאם אישית" 131 | 132 | msgctxt "#30028" 133 | msgid "Playback error" 134 | msgstr "תקלה בניגון" 135 | 136 | msgctxt "#30029" 137 | msgid "Missing Inputstream addon" 138 | msgstr "הרחבת Inputstream חסרה" 139 | 140 | msgctxt "#30030" 141 | msgid "Remove from library" 142 | msgstr "הסר מהספרייה" 143 | 144 | msgctxt "#30031" 145 | msgid "Change library title" 146 | msgstr "שנה כותרת ספרייה" 147 | 148 | msgctxt "#30032" 149 | msgid "Tracking" 150 | msgstr "מעקב" 151 | 152 | msgctxt "#30033" 153 | msgid "Use Dolby Sound" 154 | msgstr "תשתמש בפורמט דולבי" 155 | 156 | msgctxt "#30034" 157 | msgid "ESN (set automatically, can be changed manually)" 158 | msgstr "רשת ESN (ברירת מחדל אוטומטי, ניתן לשנות ידנית)" 159 | 160 | msgctxt "#30035" 161 | msgid "Inputstream Addon Settings..." 162 | msgstr "הגדרות הרחבת Inputstream..." 163 | 164 | msgctxt "#30036" 165 | msgid "Always use the original title on export" 166 | msgstr "תשתמש תמיד בכותרת המקורית לייצוא" 167 | 168 | msgctxt "#30037" 169 | msgid "Views" 170 | msgstr "תצוגות" 171 | 172 | msgctxt "#30038" 173 | msgid "Enable custom views" 174 | msgstr "אפשר תצוגות מותאמות אישית" 175 | 176 | msgctxt "#30039" 177 | msgid "View for folders" 178 | msgstr "תצוגה עבור תיקיות" 179 | 180 | msgctxt "#30040" 181 | msgid "View for movies" 182 | msgstr "תצוגה עבור סרטים" 183 | 184 | msgctxt "#30041" 185 | msgid "View for shows" 186 | msgstr "תצוגה עבור סדרות" 187 | 188 | msgctxt "#30042" 189 | msgid "View for seasons" 190 | msgstr "תצוגה עבור עונות" 191 | 192 | msgctxt "#30043" 193 | msgid "View for episodes" 194 | msgstr "תצוגה עבור פרקים" 195 | 196 | msgctxt "#30044" 197 | msgid "View for profiles" 198 | msgstr "תצוגה עבור פרופילים" 199 | 200 | msgctxt "#30045" 201 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 202 | msgstr "[COLOR cyan][B]-- לדף הבא --[/B][/COLOR]" 203 | 204 | msgctxt "#30046" 205 | msgid "Inputstream addon is not enabled" 206 | msgstr "הרחבת Inputstream לא מאופשרת" 207 | 208 | msgctxt "#30047" 209 | msgid "Finally remove?" 210 | msgstr "הוסר סוף סוף?" 211 | 212 | msgctxt "#30048" 213 | msgid "Exported" 214 | msgstr "מיוצא" 215 | 216 | msgctxt "#30049" 217 | msgid "Update DB" 218 | msgstr "עדכן בסיס נתונים" 219 | 220 | msgctxt "#30050" 221 | msgid "Update successful" 222 | msgstr "העדכון בוצע בהצלחה" 223 | 224 | msgctxt "#30051" 225 | msgid "Request Error" 226 | msgstr "בקשה שגויה" 227 | 228 | msgctxt "#30052" 229 | msgid "Unable to complete the request at this time" 230 | msgstr "אין אפשרות להשלים את הבקשה בזמן הזה" 231 | 232 | msgctxt "#30053" 233 | msgid "Auto Login" 234 | msgstr "כניסה אוטומטית" 235 | 236 | msgctxt "#30054" 237 | msgid "Enable Auto Login" 238 | msgstr "אפשר כניסה אוטומטית" 239 | 240 | msgctxt "#30055" 241 | msgid "Profile" 242 | msgstr "משתמש" 243 | 244 | msgctxt "#30056" 245 | msgid "ID" 246 | msgstr "מזהה" 247 | 248 | msgctxt "#30057" 249 | msgid "Select profile in profile listing -> context menu" 250 | msgstr "בחר שם משתמש ברשימת המשתמשים -> תפריט הקישור" 251 | 252 | msgctxt "#30058" 253 | msgid "Auto Login enabled!" 254 | msgstr "כניסה אוטומטית מופעלת!" 255 | 256 | msgctxt "#30059" 257 | msgid "Switch accounts" 258 | msgstr "החלפת חשבונות" 259 | 260 | msgctxt "#30060" 261 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 262 | msgstr "אפשר משתמשי HEVC ( 4k עבור אנדרואיד)" 263 | 264 | msgctxt "#30061" 265 | msgid "Update inside library" 266 | msgstr "עדכון בתוך הספרייה" 267 | 268 | msgctxt "#30062" 269 | msgid "Enable/disable adult pin. Active:" 270 | msgstr "אפשר או השבת קוד למבוגרים בלבד. פעיל:" 271 | -------------------------------------------------------------------------------- /resources/language/resource.language.pl_pl/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2017-07-24 16:15+0000\n" 11 | "Last-Translator: Marcin W.\n" 12 | "Language-Team: Polish\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: pl\n" 17 | "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "Wtyczka dla usług VOD serwisu Netflix" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "Niektóre części tego dodatku mogą być nielegalne w Twoim kraju - sprawdź przed instalacją zgodność z lokalnym prawem." 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "Polecane" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "Kod kontroli rodzicielskiej" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "Szukane wyrażenie" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "Hasło" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "Adres e-mail" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "Nieudana weryfikacja kontroli rodzicielskiej" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "Zweryfikuj poprawność kodu kontroli rodzicielskiej" 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "Nieudane logowanie" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "Zweryfikuj poprawność wprowadzonych danych" 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "Gatunki" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "Szukaj" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "Brak dostępnych sezonów" 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "Brak pasujących wyników" 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "Konto" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "Wyloguj" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "Eksportuj do biblioteki" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "Oceń w serwisie Netflix" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "Usuń z 'Moja lista'" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "Dodaj do 'Moja lista'" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "(między 0 a 10)" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "Eksperckie" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "Weryfikacja certyfikatu SSL" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "Biblioteka" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "Używaj niestandardowego folderu biblioteki" 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "Folder niestandardowy" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "Błąd odtwarzania" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "Brakuje wtyczki strumieniowej InputStream Adaptive" 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "Usuń z biblioteki" 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "Zmień tytuł" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "Śledzenie" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "Używaj ścieżek Dolby Sound" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "ESN (ustawiany automatycznie, z możliwością zmiany)" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "Ustawienia wtyczki strumieniowej..." 162 | 163 | msgctxt "#30036" 164 | msgid "Always use the original title on export" 165 | msgstr "Używaj oryginalnego tytułu podczas eksportu" 166 | 167 | msgctxt "#30037" 168 | msgid "Views" 169 | msgstr "Widoki" 170 | 171 | msgctxt "#30038" 172 | msgid "Enable custom views" 173 | msgstr "Aktywuj niestandardowe widoki" 174 | 175 | msgctxt "#30039" 176 | msgid "View for folders" 177 | msgstr "Widok folderów" 178 | 179 | msgctxt "#30040" 180 | msgid "View for movies" 181 | msgstr "Widok filmów" 182 | 183 | msgctxt "#30041" 184 | msgid "View for shows" 185 | msgstr "Widok seriali" 186 | 187 | msgctxt "#30042" 188 | msgid "View for seasons" 189 | msgstr "Widok sezonów" 190 | 191 | msgctxt "#30043" 192 | msgid "View for episodes" 193 | msgstr "Widok odcinków" 194 | 195 | msgctxt "#30044" 196 | msgid "View for profiles" 197 | msgstr "Widok profili" 198 | 199 | msgctxt "#30045" 200 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 201 | msgstr "[COLOR cyan][B]-- NASTĘPNA STRONA --[/B][/COLOR]" 202 | 203 | msgctxt "#30046" 204 | msgid "Inputstream addon is not enabled" 205 | msgstr "Wtyczka strumieniowa nie jest aktywna" 206 | 207 | msgctxt "#30047" 208 | msgid "Finally remove?" 209 | msgstr "Czy usunąć definitywnie?" 210 | 211 | msgctxt "#30048" 212 | msgid "Exported" 213 | msgstr "Wyeksportowane" 214 | 215 | msgctxt "#30049" 216 | msgid "Update DB" 217 | msgstr "Aktualizuj bazę" 218 | 219 | msgctxt "#30050" 220 | msgid "Update successful" 221 | msgstr "Aktualizacja zakończona powodzeniem" 222 | 223 | msgctxt "#30051" 224 | msgid "Request Error" 225 | msgstr "Błąd żądania" 226 | 227 | msgctxt "#30052" 228 | msgid "Unable to complete the request at this time" 229 | msgstr "Żądanie nie może teraz zostać wykonane" 230 | 231 | msgctxt "#30053" 232 | msgid "Auto Login" 233 | msgstr "Logowanie automatyczne" 234 | 235 | msgctxt "#30054" 236 | msgid "Enable Auto Login" 237 | msgstr "Aktywuj logowanie automatyczne" 238 | 239 | msgctxt "#30055" 240 | msgid "Profile" 241 | msgstr "Profil" 242 | 243 | msgctxt "#30056" 244 | msgid "ID" 245 | msgstr "Identyfikator" 246 | 247 | msgctxt "#30057" 248 | msgid "Select profile in profile listing -> context menu" 249 | msgstr "Wybierz profil z listy profili -> menu kontekstowe" 250 | 251 | msgctxt "#30058" 252 | msgid "Auto Login enabled!" 253 | msgstr "Logowanie automatyczne zostało aktywowane!" 254 | 255 | msgctxt "#30059" 256 | msgid "Switch accounts" 257 | msgstr "Zmień konto" 258 | 259 | msgctxt "#30060" 260 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 261 | msgstr "Aktywuj profile HEVC (funkcja 4K dla Androida)" 262 | -------------------------------------------------------------------------------- /resources/language/resource.language.hr_hr/strings.po: -------------------------------------------------------------------------------- 1 | # Croatian translation for Kodi Media Center Netfilx addon 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2017-07-24 16:15+0000\n" 11 | "Last-Translator: gogo\n" 12 | "Language-Team: Croatian\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: hr\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "Netflix dodatak za uslugu (VOD) videa na zahtjev" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "Neki dijelovi ovog dodatka možda nisu legalni u vašoj zemlji ili kraju - provjerite vaše lokalne zakone prije instalacije." 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "Preporuke" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "Pin sadržaja za odrasle" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "Izraz pretrage" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "Lozinka" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "E-pošta" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "Neuspjela ovjera sadržaja za odrasle" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "Provjerite svoj pin sadržaja za odrasle" 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "Neuspjela prijava" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "Provjereite svoje podatke ovjere" 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "Žanrovi" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "Pretraga" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "Nema dostupnih sezona" 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "Nema pronađenih podudaranja" 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "Račun" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "Odjava" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "Izvezi u videoteku" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "Ocjeni na Netflixu" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "Ukloni s 'Moj popis'" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "Dodaj na 'Moj popis'" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "(između 0 i 10)" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "Stručno" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "SSL ovjera" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "Videoteka" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "Omogući prilagođenu mapu videoteke" 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "Prilagođena putanja videoteke" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "Greška reprodukcije" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "Nedostaje dodatak ulaznog strujanja" 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "Ukloni iz videoteke" 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "Promijeni naslov videoteke" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "Praćenje" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "Koristi Dolby zvuk" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "ESN (postavi automatski, može se promijeniti ručno)" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "Postavke dodatka ulaznog strujanja..." 162 | 163 | msgctxt "#30036" 164 | msgid "Always use the original title on export" 165 | msgstr "Uvijek koristi izvorni naslov pri izvozu" 166 | 167 | msgctxt "#30037" 168 | msgid "Views" 169 | msgstr "Prikazi" 170 | 171 | msgctxt "#30038" 172 | msgid "Enable custom views" 173 | msgstr "Omogući prilagođene prikaze" 174 | 175 | msgctxt "#30039" 176 | msgid "View for folders" 177 | msgstr "Prikaz mapa" 178 | 179 | msgctxt "#30040" 180 | msgid "View for movies" 181 | msgstr "Prikaz filmova" 182 | 183 | msgctxt "#30041" 184 | msgid "View for shows" 185 | msgstr "Prikaz serija" 186 | 187 | msgctxt "#30042" 188 | msgid "View for seasons" 189 | msgstr "Prikaz sezona" 190 | 191 | msgctxt "#30043" 192 | msgid "View for episodes" 193 | msgstr "Prikaz epizoda" 194 | 195 | msgctxt "#30044" 196 | msgid "View for profiles" 197 | msgstr "Prikaz profila" 198 | 199 | msgctxt "#30045" 200 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 201 | msgstr "[COLOR cyan][B]-- SLJEDEĆA STRANICA --[/B][/COLOR]" 202 | 203 | msgctxt "#30046" 204 | msgid "Inputstream addon is not enabled" 205 | msgstr "Dodatak ulaznog strujanja nije omogućen" 206 | 207 | msgctxt "#30047" 208 | msgid "Finally remove?" 209 | msgstr "Konačno ukloni?" 210 | 211 | msgctxt "#30048" 212 | msgid "Exported" 213 | msgstr "Izvezeno" 214 | 215 | msgctxt "#30049" 216 | msgid "Update DB" 217 | msgstr "Nadopuni bazu podataka" 218 | 219 | msgctxt "#30050" 220 | msgid "Update successful" 221 | msgstr "Nadopuna uspješna" 222 | 223 | msgctxt "#30051" 224 | msgid "Request Error" 225 | msgstr "Greška zahtjeva" 226 | 227 | msgctxt "#30052" 228 | msgid "Unable to complete the request at this time" 229 | msgstr "Nemoguće je trenutno završavanje zahtjeva" 230 | 231 | msgctxt "#30053" 232 | msgid "Auto Login" 233 | msgstr "Automatska prijava" 234 | 235 | msgctxt "#30054" 236 | msgid "Enable Auto Login" 237 | msgstr "Omogući automatsku prijavu" 238 | 239 | msgctxt "#30055" 240 | msgid "Profile" 241 | msgstr "Profil" 242 | 243 | msgctxt "#30056" 244 | msgid "ID" 245 | msgstr "" 246 | 247 | msgctxt "#30057" 248 | msgid "Select profile in profile listing -> context menu" 249 | msgstr "Odaberi profil u popisu profila -> sadržajni izbornik" 250 | 251 | msgctxt "#30058" 252 | msgid "Auto Login enabled!" 253 | msgstr "Automatska prijava omogućena!" 254 | 255 | msgctxt "#30059" 256 | msgid "Switch accounts" 257 | msgstr "Zamijeni račune" 258 | 259 | msgctxt "#30060" 260 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 261 | msgstr "Omogući HEVC profile (npr. 4k za Android)" 262 | 263 | msgctxt "#30061" 264 | msgid "Update inside library" 265 | msgstr "Nadopuni unutar videoteke" 266 | 267 | msgctxt "#30062" 268 | msgid "Enable/disable adult pin. Active:" 269 | msgstr "Omogući/Onemogući pin sadržaja za odrasle. Aktivan:" 270 | -------------------------------------------------------------------------------- /resources/language/resource.language.sk_sk/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2018-02-18 19:19+0100\n" 11 | "Last-Translator: Matej Moško \n" 12 | "Language-Team: Slovak\n" 13 | "Language: sk\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "Netflix" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "Doplnok na prehrávanie obsahu služby Netflix" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "Niektoré súčasti tohto doplnku môžu byť v krajine vášho pobytu nelegálne. Pred inštaláciou prosím overte, či neporušujete miestne zákony." 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "Odporúčania" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "Rodičovský zámok" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "Vyhľadávací výraz" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "Heslo" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "E-mail" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "Zlyhalo odomknutie rodičovského zámku" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "Prosím skontrolujte heslo k rodičovskému zámku" 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "Prihlásenie sa nepodarilo" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "Prosím skontrolujte prihlasovacie údaje" 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "Žánre" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "Vyhľadávanie" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "Nenašli sa žiadne série" 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "Žiadna zhoda vo vyhľadávaní" 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "Účet" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "Odhlásiť sa" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "Exportovať do knižnice" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "Ohodnotiť na Netflixe" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "Odstrániť z 'Môjho zoznamu'" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "Pridať do 'Môjho zoznamu'" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "(od 0 do 10)" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "Rozšírené" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "SSL overovanie" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "Knižnica" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "Povoliť vlastný priečinok knižnice" 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "Cesta k vlastnej knižnici" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "Chyba prehrávania" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "Nepodarilo sa nájsť doplnok Inputstream" 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "Odstrániť z knižnice" 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "Zmeniť názov v knižnici" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "Sledovanie" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "Použiť Dolby Sound" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "ESN (nastavené automaticky, môžete to ručne zmeniť)" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "Nastavenia doplnku Inputstream..." 162 | 163 | msgctxt "#30036" 164 | msgid "Always use the original title on export" 165 | msgstr "Pri exporte vždy použiť orignálny názov" 166 | 167 | msgctxt "#30037" 168 | msgid "Views" 169 | msgstr "Zobrazenia" 170 | 171 | msgctxt "#30038" 172 | msgid "Enable custom views" 173 | msgstr "Povoliť vlastné zobrazenia" 174 | 175 | msgctxt "#30039" 176 | msgid "View for folders" 177 | msgstr "Zobrazenie pre priečinky" 178 | 179 | msgctxt "#30040" 180 | msgid "View for movies" 181 | msgstr "Zobrazenie pre filmy" 182 | 183 | msgctxt "#30041" 184 | msgid "View for shows" 185 | msgstr "Zobrazenie pre seriály" 186 | 187 | msgctxt "#30042" 188 | msgid "View for seasons" 189 | msgstr "Zobrazenie pre série" 190 | 191 | msgctxt "#30043" 192 | msgid "View for episodes" 193 | msgstr "Zobrazenie pre epizódy" 194 | 195 | msgctxt "#30044" 196 | msgid "View for profiles" 197 | msgstr "Zobrazenie pre profily" 198 | 199 | msgctxt "#30045" 200 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 201 | msgstr "[COLOR cyan][B]-- ĎALŠIA STRANA --[/B][/COLOR]" 202 | 203 | msgctxt "#30046" 204 | msgid "Inputstream addon is not enabled" 205 | msgstr "Inpustream je vypnutý" 206 | 207 | msgctxt "#30047" 208 | msgid "Finally remove?" 209 | msgstr "Naozaj odstrániť?" 210 | 211 | msgctxt "#30048" 212 | msgid "Exported" 213 | msgstr "Exportované" 214 | 215 | msgctxt "#30049" 216 | msgid "Update DB" 217 | msgstr "Aktualizovať DB" 218 | 219 | msgctxt "#30050" 220 | msgid "Update successful" 221 | msgstr "Aktualizácia úšpešná" 222 | 223 | msgctxt "#30051" 224 | msgid "Request Error" 225 | msgstr "Chyba požiadavky" 226 | 227 | msgctxt "#30052" 228 | msgid "Unable to complete the request at this time" 229 | msgstr "Tentokrát sa požiadavku nepodarilo dokončiť" 230 | 231 | msgctxt "#30053" 232 | msgid "Auto Login" 233 | msgstr "Automatické prihlasovanie" 234 | 235 | msgctxt "#30054" 236 | msgid "Enable Auto Login" 237 | msgstr "Povoliť automatické prihlasovanie" 238 | 239 | msgctxt "#30055" 240 | msgid "Profile" 241 | msgstr "Profil" 242 | 243 | msgctxt "#30056" 244 | msgid "ID" 245 | msgstr "ID" 246 | 247 | msgctxt "#30057" 248 | msgid "Select profile in profile listing -> context menu" 249 | msgstr "Profil si vyberiete v zozname profilov -> kontextové menu" 250 | 251 | msgctxt "#30058" 252 | msgid "Auto Login enabled!" 253 | msgstr "Automatické prihlasovanie zapnuté!" 254 | 255 | msgctxt "#30059" 256 | msgid "Switch accounts" 257 | msgstr "Prepnúť účty" 258 | 259 | msgctxt "#30060" 260 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 261 | msgstr "Povoliť HEVC profily (napr. 4k pre Android)" 262 | 263 | msgctxt "#30061" 264 | msgid "Update inside library" 265 | msgstr "Aktualizovať záznam v knižnici" 266 | 267 | msgctxt "#30062" 268 | msgid "Enable/disable adult pin. Active:" 269 | msgstr "Povoliť/zakázať rodičovský zámok. Aktívny:" 270 | -------------------------------------------------------------------------------- /resources/language/resource.language.ko_kr/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2017-07-24 16:15+0000\n" 11 | "Last-Translator: Allen Choi<37539914+Thunderbird2086@users.noreply.github.com>\n" 12 | "Language-Team: Korean\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: en\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "Netflix VOD 애드온" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "일부 국가에서는 이 애드온의 일부가 불법일 수도 있으니, 설치하기전 관련 법을 확인하기 바랍니다." 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "추천" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "성인인증코드" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "검색어" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "암호" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "성인인증 실패" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "성인인증코드를 확인하십시오." 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "로그인 실패" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "로그인 암호를 확인하십시오." 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "장르" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "검색" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "볼 수 있는 시즌이 없습니다." 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "일치하는 것이 없습니다." 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "계정" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "로그아웃" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "라이브러리로 보내기" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "평점주기" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "찜한 것들에서 삭제" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "찜한 것들에 추가" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "(0-10 사이)" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "고급설정" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "SSL인증" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "라이브러리" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "개인라이브러리를 사용합니다." 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "개인라이브러리 위치" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "재생 오류" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "Inputstream 애드온이 없군요." 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "라이브러리에서 지우기" 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "라이브러리 이름 바꾸기" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "추적" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "돌비사운드 사용" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "ESN(자동 설정, 수동변경 가능)" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "Inpustream 애드온 설정" 162 | 163 | msgctxt "#30036" 164 | msgid "Always use the original title on export" 165 | msgstr "내보낼 때 항상 원래 제목 사용하기" 166 | 167 | msgctxt "#30037" 168 | msgid "Views" 169 | msgstr "보기" 170 | 171 | msgctxt "#30038" 172 | msgid "Enable custom views" 173 | msgstr "사용자 설정" 174 | 175 | msgctxt "#30039" 176 | msgid "View for folders" 177 | msgstr "" 178 | 179 | msgctxt "#30040" 180 | msgid "View for movies" 181 | msgstr "" 182 | 183 | msgctxt "#30041" 184 | msgid "View for shows" 185 | msgstr "" 186 | 187 | msgctxt "#30042" 188 | msgid "View for seasons" 189 | msgstr "" 190 | 191 | msgctxt "#30043" 192 | msgid "View for episodes" 193 | msgstr "" 194 | 195 | msgctxt "#30044" 196 | msgid "View for profiles" 197 | msgstr "" 198 | 199 | msgctxt "#30045" 200 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 201 | msgstr "[COLOR cyan][B]-- 다음 --[/B][/COLOR]" 202 | 203 | msgctxt "#30046" 204 | msgid "Inputstream addon is not enabled" 205 | msgstr "Inpustream addon을 설치 혹은 켜십시오." 206 | 207 | msgctxt "#30047" 208 | msgid "Finally remove?" 209 | msgstr "정말로 지울까요?" 210 | 211 | msgctxt "#30048" 212 | msgid "Exported" 213 | msgstr "내보낸 것들" 214 | 215 | msgctxt "#30049" 216 | msgid "Update DB" 217 | msgstr "DB 새로고침" 218 | 219 | msgctxt "#30050" 220 | msgid "Update successful" 221 | msgstr "새로고침 성공" 222 | 223 | msgctxt "#30051" 224 | msgid "Request Error" 225 | msgstr "요청오류" 226 | 227 | msgctxt "#30052" 228 | msgid "Unable to complete the request at this time" 229 | msgstr "실패했습니다." 230 | 231 | msgctxt "#30053" 232 | msgid "Auto Login" 233 | msgstr "자동로그인" 234 | 235 | msgctxt "#30054" 236 | msgid "Enable Auto Login" 237 | msgstr "자동로그인 켬" 238 | 239 | msgctxt "#30055" 240 | msgid "Profile" 241 | msgstr "프로파일" 242 | 243 | msgctxt "#30056" 244 | msgid "ID" 245 | msgstr "" 246 | 247 | msgctxt "#30057" 248 | msgid "Select profile in profile listing -> context menu" 249 | msgstr "" 250 | 251 | msgctxt "#30058" 252 | msgid "Auto Login enabled!" 253 | msgstr "자동 로그인이 켜졌습니다!" 254 | 255 | msgctxt "#30059" 256 | msgid "Switch accounts" 257 | msgstr "다른 계정으로" 258 | 259 | msgctxt "#30060" 260 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 261 | msgstr "HEVC프로파일 활성화(안드로이드의 4k 등)" 262 | 263 | msgctxt "#30061" 264 | msgid "Update inside library" 265 | msgstr "라이브러리 새로 고침" 266 | 267 | msgctxt "#30062" 268 | msgid "Enable/disable adult pin. Active:" 269 | msgstr "성인인증코드 사용/사용안함" 270 | 271 | msgctxt "#30063" 272 | msgid "new episodes added to library" 273 | msgstr "새 에피소드가 라이브러리에 추가되었습니다." 274 | 275 | msgctxt "#30064" 276 | msgid "Export new episodes" 277 | msgstr "새 에피소드를 내보냅니다." 278 | 279 | msgctxt "#30065" 280 | msgid "Auto-update" 281 | msgstr "자동으로 새로 고칩니다." 282 | 283 | msgctxt "#30066" 284 | msgid "never" 285 | msgstr "안함" 286 | 287 | msgctxt "#30067" 288 | msgid "daily" 289 | msgstr "매일" 290 | 291 | msgctxt "#30068" 292 | msgid "every other day" 293 | msgstr "이틀에 한번" 294 | 295 | msgctxt "#30069" 296 | msgid "every 5 days" 297 | msgstr "닷새에 한번" 298 | 299 | msgctxt "#30070" 300 | msgid "weekly" 301 | msgstr "매주" 302 | 303 | msgctxt "#30071" 304 | msgid "Time of Day" 305 | msgstr "실행 시간" 306 | 307 | msgctxt "#30072" 308 | msgid "Only start after 5 minutes of idle" 309 | msgstr "5분이상 빈둥거리면 시작합니다." 310 | 311 | msgctxt "#30073" 312 | msgid "Check every X minutes if update scheduled" 313 | msgstr "새로 고칠 때 몇분 마다 확인할까요?" 314 | 315 | msgctxt "#30074" 316 | msgid "View for exported" 317 | msgstr "내보낸 것들 보기" 318 | -------------------------------------------------------------------------------- /resources/language/resource.language.en_gb/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2017-07-24 16:15+0000\n" 11 | "Last-Translator: Sebastian Golasch \n" 12 | "Language-Team: English\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: en\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "" 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "" 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "" 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "" 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "" 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "" 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "" 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "" 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "" 162 | 163 | msgctxt "#30036" 164 | msgid "Always use the original title on export" 165 | msgstr "" 166 | 167 | msgctxt "#30037" 168 | msgid "Views" 169 | msgstr "" 170 | 171 | msgctxt "#30038" 172 | msgid "Enable custom views" 173 | msgstr "" 174 | 175 | msgctxt "#30039" 176 | msgid "View for folders" 177 | msgstr "" 178 | 179 | msgctxt "#30040" 180 | msgid "View for movies" 181 | msgstr "" 182 | 183 | msgctxt "#30041" 184 | msgid "View for shows" 185 | msgstr "" 186 | 187 | msgctxt "#30042" 188 | msgid "View for seasons" 189 | msgstr "" 190 | 191 | msgctxt "#30043" 192 | msgid "View for episodes" 193 | msgstr "" 194 | 195 | msgctxt "#30044" 196 | msgid "View for profiles" 197 | msgstr "" 198 | 199 | msgctxt "#30045" 200 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 201 | msgstr "" 202 | 203 | msgctxt "#30046" 204 | msgid "Inputstream addon is not enabled" 205 | msgstr "" 206 | 207 | msgctxt "#30047" 208 | msgid "Finally remove?" 209 | msgstr "" 210 | 211 | msgctxt "#30048" 212 | msgid "Exported" 213 | msgstr "" 214 | 215 | msgctxt "#30049" 216 | msgid "Update DB" 217 | msgstr "" 218 | 219 | msgctxt "#30050" 220 | msgid "Update successful" 221 | msgstr "" 222 | 223 | msgctxt "#30051" 224 | msgid "Request Error" 225 | msgstr "" 226 | 227 | msgctxt "#30052" 228 | msgid "Unable to complete the request at this time" 229 | msgstr "" 230 | 231 | msgctxt "#30053" 232 | msgid "Auto Login" 233 | msgstr "" 234 | 235 | msgctxt "#30054" 236 | msgid "Enable Auto Login" 237 | msgstr "" 238 | 239 | msgctxt "#30055" 240 | msgid "Profile" 241 | msgstr "" 242 | 243 | msgctxt "#30056" 244 | msgid "ID" 245 | msgstr "" 246 | 247 | msgctxt "#30057" 248 | msgid "Select profile in profile listing -> context menu" 249 | msgstr "" 250 | 251 | msgctxt "#30058" 252 | msgid "Auto Login enabled!" 253 | msgstr "" 254 | 255 | msgctxt "#30059" 256 | msgid "Switch accounts" 257 | msgstr "" 258 | 259 | msgctxt "#30060" 260 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 261 | msgstr "" 262 | 263 | msgctxt "#30061" 264 | msgid "Update inside library" 265 | msgstr "" 266 | 267 | msgctxt "#30062" 268 | msgid "Enable/disable adult pin. Active:" 269 | msgstr "" 270 | 271 | msgctxt "#30063" 272 | msgid "new episodes added to library" 273 | msgstr "" 274 | 275 | msgctxt "#30064" 276 | msgid "Export new episodes" 277 | msgstr "" 278 | 279 | msgctxt "#30065" 280 | msgid "Auto-update" 281 | msgstr "" 282 | 283 | msgctxt "#30066" 284 | msgid "never" 285 | msgstr "" 286 | 287 | msgctxt "#30067" 288 | msgid "daily" 289 | msgstr "" 290 | 291 | msgctxt "#30068" 292 | msgid "every other day" 293 | msgstr "" 294 | 295 | msgctxt "#30069" 296 | msgid "every 5 days" 297 | msgstr "" 298 | 299 | msgctxt "#30070" 300 | msgid "weekly" 301 | msgstr "" 302 | 303 | msgctxt "#30071" 304 | msgid "Time of Day" 305 | msgstr "" 306 | 307 | msgctxt "#30072" 308 | msgid "Only start after 5 minutes of idle" 309 | msgstr "" 310 | 311 | msgctxt "#30073" 312 | msgid "Check every X minutes if update scheduled" 313 | msgstr "" 314 | 315 | msgctxt "#30074" 316 | msgid "View for exported" 317 | msgstr "" 318 | 319 | msgctxt "#30075" 320 | msgid "Ask to skip intro and recap" 321 | msgstr "" 322 | 323 | msgctxt "#30076" 324 | msgid "Skip intro" 325 | msgstr "" 326 | 327 | msgctxt "#30077" 328 | msgid "Skip recap" 329 | msgstr "" 330 | 331 | msgctxt "#30078" 332 | msgid "Playback" 333 | msgstr "" 334 | 335 | msgctxt "#30079" 336 | msgid "Skip automatically" 337 | msgstr "" 338 | 339 | msgctxt "#30080" 340 | msgid "Don't pause when skipping" 341 | msgstr "" 342 | 343 | msgctxt "#30081" 344 | msgid "Save audio/subtitle preferences for this show" 345 | msgstr "" 346 | 347 | msgctxt "#30082" 348 | msgid "Save audio / subtitle settings for TV shows" 349 | msgstr "" 350 | 351 | msgctxt "#30083" 352 | msgid "Save bookmarks for library items / mark as watched" 353 | msgstr "" 354 | 355 | msgctxt "#30084" 356 | msgid "Enable HDR profiles" 357 | msgstr "" 358 | 359 | msgctxt "#30085" 360 | msgid "Enable DolbyVision profiles" 361 | msgstr "" 362 | 363 | msgctxt "#30086" 364 | msgid "Enable VP9 profiles" 365 | msgstr "" 366 | -------------------------------------------------------------------------------- /resources/lib/playback/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Author: caphm 3 | # Package: playback 4 | # Created on: 08.02.2018 5 | # License: MIT https://goo.gl/5bMj3H 6 | # pylint: disable=import-error 7 | 8 | """Playback tracking and coordination of several actions during playback""" 9 | 10 | import json 11 | 12 | import xbmc 13 | import AddonSignals 14 | 15 | from resources.lib.NetflixCommon import Signals 16 | from resources.lib.utils import LoggingComponent 17 | 18 | def json_rpc(method, params=None): 19 | """ 20 | Executes a JSON-RPC in Kodi 21 | 22 | :param method: The JSON-RPC method to call 23 | :type method: string 24 | :param params: The parameters of the method call (optional) 25 | :type params: dict 26 | :returns: dict -- Method call result 27 | """ 28 | request_data = {'jsonrpc': '2.0', 'method': method, 'id': 1, 29 | 'params': params or {}} 30 | request = json.dumps(request_data) 31 | response = json.loads(xbmc.executeJSONRPC(request)) 32 | if 'error' in response: 33 | raise IOError('JSONRPC-Error {}: {}' 34 | .format(response['error']['code'], 35 | response['error']['message'])) 36 | return response['result'] 37 | 38 | 39 | class PlaybackController(xbmc.Monitor, LoggingComponent): 40 | """ 41 | Tracks status and progress of video playbacks initiated by the addon and 42 | saves bookmarks and watched state for the associated items into the Kodi 43 | library. 44 | """ 45 | def __init__(self, nx_common): 46 | xbmc.Monitor.__init__(self) 47 | LoggingComponent.__init__(self, nx_common) 48 | self.tracking = False 49 | self.active_player_id = None 50 | self.action_managers = [] 51 | 52 | AddonSignals.registerSlot( 53 | nx_common.addon.getAddonInfo('id'), Signals.PLAYBACK_INITIATED, 54 | self.initialize_playback) 55 | 56 | def initialize_playback(self, data): 57 | """ 58 | Callback for addon signal when this addon has initiated a playback 59 | """ 60 | self.tracking = True 61 | try: 62 | self._notify_all(PlaybackActionManager.initialize, data) 63 | except RuntimeError as exc: 64 | self.log('RuntimeError: {}'.format(exc), xbmc.LOGERROR) 65 | 66 | def onNotification(self, sender, method, data): 67 | # pylint: disable=unused-argument, invalid-name 68 | """ 69 | Callback for Kodi notifications that handles and dispatches playback 70 | started and playback stopped events. 71 | """ 72 | if self.tracking: 73 | try: 74 | if method == 'Player.OnAVStart': 75 | self._on_playback_started( 76 | json.loads(data)) 77 | elif method == 'Player.OnStop': 78 | self._on_playback_stopped() 79 | except RuntimeError as exc: 80 | self.log('RuntimeError: {}'.format(exc), xbmc.LOGERROR) 81 | 82 | def on_playback_tick(self): 83 | """ 84 | Notify action managers of playback tick 85 | """ 86 | if self.tracking: 87 | player_state = self._get_player_state() 88 | if player_state: 89 | self._notify_all(PlaybackActionManager.on_tick, 90 | player_state) 91 | 92 | def _on_playback_started(self, data): 93 | self.active_player_id = data['player']['playerid'] 94 | self._notify_all(PlaybackActionManager.on_playback_started, 95 | self._get_player_state()) 96 | 97 | def _on_playback_stopped(self): 98 | self.tracking = False 99 | self.active_player_id = None 100 | self._notify_all(PlaybackActionManager.on_playback_stopped) 101 | 102 | def _notify_all(self, notification, data=None): 103 | self.log('Notifying all managers of {} (data={})' 104 | .format(notification.__name__, data)) 105 | for manager in self.action_managers: 106 | notify_method = getattr(manager, notification.__name__) 107 | if data is not None: 108 | notify_method(data) 109 | else: 110 | notify_method() 111 | 112 | def _get_player_state(self): 113 | try: 114 | player_state = json_rpc('Player.GetProperties', { 115 | 'playerid': self.active_player_id, 116 | 'properties': [ 117 | 'audiostreams', 118 | 'currentaudiostream', 119 | 'subtitles', 120 | 'currentsubtitle', 121 | 'subtitleenabled', 122 | 'percentage', 123 | 'time'] 124 | }) 125 | except IOError: 126 | return {} 127 | 128 | # convert time dict to elapsed seconds 129 | player_state['elapsed_seconds'] = ( 130 | player_state['time']['hours'] * 3600 + 131 | player_state['time']['minutes'] * 60 + 132 | player_state['time']['seconds']) 133 | 134 | return player_state 135 | 136 | 137 | class PlaybackActionManager(LoggingComponent): 138 | """ 139 | Base class for managers that handle executing of specific actions 140 | during playback 141 | """ 142 | def __init__(self, nx_common): 143 | LoggingComponent.__init__(self, nx_common) 144 | self.addon = nx_common.get_addon() 145 | self._enabled = None 146 | 147 | @property 148 | def enabled(self): 149 | """ 150 | Indicates whether this instance is enabled or not. 151 | Loads the value from Kodi settings if it has not been set. 152 | """ 153 | if self._enabled is None: 154 | self.log('Loading enabled setting from store') 155 | self._enabled = self.addon.getSettingBool( 156 | '{}_enabled'.format(self.__class__.__name__)) 157 | 158 | return self._enabled 159 | 160 | @enabled.setter 161 | def enabled(self, enabled): 162 | self._enabled = enabled 163 | 164 | def initialize(self, data): 165 | """ 166 | Initialize the manager with data when the addon initiates a playback. 167 | """ 168 | # pylint: disable=bare-except 169 | try: 170 | self._call_if_enabled(self._initialize, data=data) 171 | except: 172 | self.enabled = False 173 | self.log('Initialiized ({})'.format(self)) 174 | 175 | def on_playback_started(self, player_state): 176 | """ 177 | Notify that the playback has actually started and supply initial 178 | player state 179 | """ 180 | self._call_if_enabled(self._on_playback_started, 181 | player_state=player_state) 182 | 183 | def on_tick(self, player_state): 184 | """ 185 | Notify that a playback tick has passed and supply current player state 186 | """ 187 | self._call_if_enabled(self._on_tick, player_state=player_state) 188 | 189 | def on_playback_stopped(self): 190 | """ 191 | Notify that a playback has stopped 192 | """ 193 | self._call_if_enabled(self._on_playback_stopped) 194 | self.enabled = None 195 | 196 | def _call_if_enabled(self, target_func, **kwargs): 197 | if self.enabled: 198 | target_func(**kwargs) 199 | 200 | def _initialize(self, data): 201 | """ 202 | Initialize the manager for a new playback. 203 | If preconditions are not met, this should raise an exception so the 204 | manager will be disabled throught the current playback. 205 | """ 206 | raise NotImplementedError 207 | 208 | def _on_playback_started(self, player_state): 209 | pass 210 | 211 | def _on_tick(self, player_state): 212 | raise NotImplementedError 213 | 214 | def _on_playback_stopped(self): 215 | pass 216 | -------------------------------------------------------------------------------- /resources/language/resource.language.it_it/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2017-07-24 16:15+0000\n" 11 | "Last-Translator: Sebastian Golasch \n" 12 | "Language-Team: Italian\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: it\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "Addon per i servizi VOD di Netflix" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "Alcune parti di questa addon potrebbero non essere legali nel tuo paese - verifica le leggi in vigore prima di installare." 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "Consigliati" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "Pin per il controllo parentale" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "Termine di ricerca" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "Controllo parentale fallito" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "Verifica che il pin di controllo parentale sia corretto" 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "Accesso fallito" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "Verifica le tue credenziali" 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "Generi" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "Ricerca" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "Nessuna stagione disponibile" 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "Nessuna corrispondenza trovata" 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "Esporta nella libreria" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "Vota su Netflix" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "Rimuovi da 'La mia lista'" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "Aggiungi a 'La mia lista'" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "(tra 0 e 10)" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "Esperto" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "Verifica SSL" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "Libreria" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "Abilita una cartella per la libreria personale" 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "Percorso della libreria personale" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "Errore di riproduzione" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "Addon Inputstream mancante" 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "Rimuovi dalla libreria" 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "Modifica il titolo della libreria" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "Utilizza l'audio Dolby" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "ESN" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "Configurazione dell'addon Inputstream..." 162 | 163 | msgctxt "#30036" 164 | msgid "Always use the original title on export" 165 | msgstr "Utilizza sempre il titolo originale" 166 | 167 | msgctxt "#30037" 168 | msgid "Views" 169 | msgstr "Viste" 170 | 171 | msgctxt "#30038" 172 | msgid "Enable custom views" 173 | msgstr "Abilita viste personalizzate" 174 | 175 | msgctxt "#30039" 176 | msgid "View for folders" 177 | msgstr "Vista per cartelle" 178 | 179 | msgctxt "#30040" 180 | msgid "View for movies" 181 | msgstr "Vista per film" 182 | 183 | msgctxt "#30041" 184 | msgid "View for shows" 185 | msgstr "Vista per serie" 186 | 187 | msgctxt "#30042" 188 | msgid "View for seasons" 189 | msgstr "Vista per stagioni" 190 | 191 | msgctxt "#30043" 192 | msgid "View for episodes" 193 | msgstr "Vista per episodi" 194 | 195 | msgctxt "#30044" 196 | msgid "View for profiles" 197 | msgstr "Vista per profili" 198 | 199 | msgctxt "#30045" 200 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 201 | msgstr "[COLOR cyan][B]-- PAGINA SUCCESSIVA --[/B][/COLOR]" 202 | 203 | msgctxt "#30046" 204 | msgid "Inputstream addon is not enabled" 205 | msgstr "L'addon Inputstream Adaptive non è abilitato" 206 | 207 | msgctxt "#30047" 208 | msgid "Finally remove?" 209 | msgstr "Rimuovere in modo permanente?" 210 | 211 | msgctxt "#30048" 212 | msgid "Exported" 213 | msgstr "Esportati" 214 | 215 | msgctxt "#30049" 216 | msgid "Update DB" 217 | msgstr "Aggiorna DB" 218 | 219 | msgctxt "#30050" 220 | msgid "Update successful" 221 | msgstr "Aggiornamento avvenuto con successo" 222 | 223 | msgctxt "#30051" 224 | msgid "Request Error" 225 | msgstr "Richiesta fallita" 226 | 227 | msgctxt "#30052" 228 | msgid "Unable to complete the request at this time" 229 | msgstr "Non è stato possibile completare la richiesta" 230 | 231 | msgctxt "#30053" 232 | msgid "Auto Login" 233 | msgstr "Login Automatico" 234 | 235 | msgctxt "#30054" 236 | msgid "Enable Auto Login" 237 | msgstr "Abilita l'accesso automatico" 238 | 239 | msgctxt "#30055" 240 | msgid "Profile" 241 | msgstr "Profilo" 242 | 243 | msgctxt "#30056" 244 | msgid "ID" 245 | msgstr "" 246 | 247 | msgctxt "#30057" 248 | msgid "Select profile in profile listing -> context menu" 249 | msgstr "Seleziona il profilo dalla lista profili -> menu contestuale" 250 | 251 | msgctxt "#30058" 252 | msgid "Auto Login enabled!" 253 | msgstr "Accesso automatico abilitato!" 254 | 255 | msgctxt "#30059" 256 | msgid "Switch accounts" 257 | msgstr "Cambia account" 258 | 259 | msgctxt "#30060" 260 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 261 | msgstr "Abilita HEVC (es: 4k per Android)" 262 | 263 | msgctxt "#30061" 264 | msgid "Update inside library" 265 | msgstr "Aggiorna nella libreria" 266 | 267 | msgctxt "#30062" 268 | msgid "Enable/disable adult pin. Active:" 269 | msgstr "Abilita/disabilita controllo parentale. Attivo:" 270 | 271 | msgctxt "#30063" 272 | msgid "new episodes added to library" 273 | msgstr "nuovi episodi aggiunti alla libreria" 274 | 275 | msgctxt "#30064" 276 | msgid "Export new episodes" 277 | msgstr "Esporta nuovi episodi" 278 | 279 | msgctxt "#30065" 280 | msgid "Auto-update" 281 | msgstr "Aggiornamento automatico" 282 | 283 | msgctxt "#30066" 284 | msgid "never" 285 | msgstr "mai" 286 | 287 | msgctxt "#30067" 288 | msgid "daily" 289 | msgstr "quotidianamente" 290 | 291 | msgctxt "#30068" 292 | msgid "every other day" 293 | msgstr "ogni altro giorno" 294 | 295 | msgctxt "#30069" 296 | msgid "every 5 days" 297 | msgstr "ogni 5 giorni" 298 | 299 | msgctxt "#30070" 300 | msgid "weekly" 301 | msgstr "settimanalmente" 302 | 303 | msgctxt "#30071" 304 | msgid "Time of Day" 305 | msgstr "Ora del Giorno" 306 | 307 | msgctxt "#30072" 308 | msgid "Only start after 5 minutes of idle" 309 | msgstr "Avvia solo dopo 5 minuti di inattività" 310 | 311 | msgctxt "#30073" 312 | msgid "Check every X minutes if update scheduled" 313 | msgstr "Controlla ogni X minuti se l'aggiornamento è schedulato" 314 | 315 | msgctxt "#30074" 316 | msgid "View for exported" 317 | msgstr "Vista per esportati" 318 | -------------------------------------------------------------------------------- /resources/language/resource.language.fi_fi/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2017-07-24 16:15+0000\n" 11 | "Last-Translator: Sebastian Golasch \n" 12 | "Language-Team: Finnish\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: fi\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "Netflix-suoratoistopalvelun lisäosa" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "Jotkin tämän lisäosan ominaisuuksista eivät välttämättä ole laillisia kotimaassasi - tarkista käytön laillisuus ennen asentamista." 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "Suositukset" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "Lapsilukon PIN-koodi" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "Hakusana" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "Salasana" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "Sähköpostiosoite" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "Lapsilukon poistaminen epäonnistui" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "Tarkista PIN-koodisi" 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "Kirjautuminen epäonnistui" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "Tarkista käyttäjätietosi" 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "Lajityypit" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "Haku" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "Ei katsottavissa olevia kausia" 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "Hakusi ei tuottanut tuloksia" 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "Käyttäjätili" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "Kirjaudu ulos" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "Vie kirjastoon" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "Anna arvosana" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "Poista 'Omalta listalta'" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "Lisää 'Omalle listalle'" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "(väliltä 0-10)" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "Asiantuntija" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "SSL-varmennus" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "Kirjasto" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "Käytä omaa kirjastokansiota" 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "Kirjastokansion polku" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "Toistovirhe" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "Inputstream-lisäosa puuttuu" 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "Poista kirjastosta" 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "Muokkaa kirjastonimeä" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "Seuranta" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "Käytä Dolby-ääniä" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "ESN (luotu automaattisesti, käyttäjän muokattavissa)" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "Inputstream-lisäosan asetukset..." 162 | 163 | msgctxt "#30036" 164 | msgid "Always use the original title on export" 165 | msgstr "Käytä aina alkuperäisiä nimiä" 166 | 167 | msgctxt "#30037" 168 | msgid "Views" 169 | msgstr "Näkymät" 170 | 171 | msgctxt "#30038" 172 | msgid "Enable custom views" 173 | msgstr "Käytä omia näkymiä" 174 | 175 | msgctxt "#30039" 176 | msgid "View for folders" 177 | msgstr "Kansioiden näkymä" 178 | 179 | msgctxt "#30040" 180 | msgid "View for movies" 181 | msgstr "Elokuvien näkymä" 182 | 183 | msgctxt "#30041" 184 | msgid "View for shows" 185 | msgstr "Tv-sarjojen näkymä" 186 | 187 | msgctxt "#30042" 188 | msgid "View for seasons" 189 | msgstr "Kausien näkymä" 190 | 191 | msgctxt "#30043" 192 | msgid "View for episodes" 193 | msgstr "Jaksojen näkymä" 194 | 195 | msgctxt "#30044" 196 | msgid "View for profiles" 197 | msgstr "Profiilien näkymä" 198 | 199 | msgctxt "#30045" 200 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 201 | msgstr "[COLOR cyan][B]-- SEURAAVA SIVU --[/B][/COLOR]" 202 | 203 | msgctxt "#30046" 204 | msgid "Inputstream addon is not enabled" 205 | msgstr "Inputstream-lisäosa ei ole käytössä" 206 | 207 | msgctxt "#30047" 208 | msgid "Finally remove?" 209 | msgstr "Poistetaanko kirjastosta?" 210 | 211 | msgctxt "#30048" 212 | msgid "Exported" 213 | msgstr "Kirjastoon viedyt" 214 | 215 | msgctxt "#30049" 216 | msgid "Update DB" 217 | msgstr "Päivitä tietokanta" 218 | 219 | msgctxt "#30050" 220 | msgid "Update successful" 221 | msgstr "Päivitys onnistui" 222 | 223 | msgctxt "#30051" 224 | msgid "Request Error" 225 | msgstr "Virhe pyynnössä" 226 | 227 | msgctxt "#30052" 228 | msgid "Unable to complete the request at this time" 229 | msgstr "Pyyntöä ei voida suorittaa tällä hetkellä" 230 | 231 | msgctxt "#30053" 232 | msgid "Auto Login" 233 | msgstr "Automaattinen kirjautuminen" 234 | 235 | msgctxt "#30054" 236 | msgid "Enable Auto Login" 237 | msgstr "Kirjaudu automaattisesti" 238 | 239 | msgctxt "#30055" 240 | msgid "Profile" 241 | msgstr "Käyttäjäprofiili" 242 | 243 | msgctxt "#30056" 244 | msgid "ID" 245 | msgstr "ID-numero" 246 | 247 | msgctxt "#30057" 248 | msgid "Select profile in profile listing -> context menu" 249 | msgstr "Valitse 'Automaattinen kirjautuminen' profiililistan sisältövalikosta" 250 | 251 | msgctxt "#30058" 252 | msgid "Auto Login enabled!" 253 | msgstr "Automaattinen kirjautuminen käytössä!" 254 | 255 | msgctxt "#30059" 256 | msgid "Switch accounts" 257 | msgstr "Vaihda käyttäjätiliä" 258 | 259 | msgctxt "#30060" 260 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 261 | msgstr "Käytä HEVC-pakkausta (mahdollistaa 4K-laadun Androidilla)" 262 | 263 | msgctxt "#30061" 264 | msgid "Update inside library" 265 | msgstr "Päivitä kirjastossa" 266 | 267 | msgctxt "#30062" 268 | msgid "Enable/disable adult pin. Active:" 269 | msgstr "Lapsilukon PIN-koodin määritys. Tila:" 270 | 271 | msgctxt "#30063" 272 | msgid "new episodes added to library" 273 | msgstr "uutta jaksoa lisätty kirjastoon" 274 | 275 | msgctxt "#30064" 276 | msgid "Export new episodes" 277 | msgstr "Vie uudet jaksot" 278 | 279 | msgctxt "#30065" 280 | msgid "Auto-update" 281 | msgstr "Automaattinen päivitys" 282 | 283 | msgctxt "#30066" 284 | msgid "never" 285 | msgstr "ei koskaan" 286 | 287 | msgctxt "#30067" 288 | msgid "daily" 289 | msgstr "päivittäin" 290 | 291 | msgctxt "#30068" 292 | msgid "every other day" 293 | msgstr "joka toinen päivä" 294 | 295 | msgctxt "#30069" 296 | msgid "every 5 days" 297 | msgstr "joka viides päivä" 298 | 299 | msgctxt "#30070" 300 | msgid "weekly" 301 | msgstr "viikoittain" 302 | 303 | msgctxt "#30071" 304 | msgid "Time of Day" 305 | msgstr "Kellonaika" 306 | 307 | msgctxt "#30072" 308 | msgid "Only start after 5 minutes of idle" 309 | msgstr "Aloita, kun laite on ollut 5 minuuttia käyttämättä" 310 | 311 | msgctxt "#30073" 312 | msgid "Check every X minutes if update scheduled" 313 | msgstr "Tarkista X minuutin välein, onko päivitystä ajastettu" 314 | 315 | msgctxt "#30074" 316 | msgid "View for exported" 317 | msgstr "Kirjastoon lisättyjen näkymä" 318 | -------------------------------------------------------------------------------- /resources/language/resource.language.fr_fr/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-11-23 16:15+0000\n" 10 | "PO-Revision-Date: 2017-11-23 16:15+0000\n" 11 | "Last-Translator: vo42 \n" 12 | "Language-Team: Français\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: fr\n" 17 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "Extension Netflix SVoD" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "Certaines parties de cette extension peuvent ne pas être légales dans votre pays de résidence - renseignez vous avant d'installer" 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "Recommandations" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "Code PIN adulte" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "Terme de recherche" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "Mot de passe" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "Mail" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "Échec de la vérification adulte" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "Vérifiez votre code PIN adulte" 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "Échec de connexion" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "Vérifiez vos identifiants" 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "Genres" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "Recherche" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "Pas de saison disponible" 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "Pas de résultat" 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "Compte" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "Déconnexion" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "Exporter vers la bibliothèque" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "Noter sur Netflix" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "Enlever de 'Ma Liste'" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "Ajouter à 'Ma Liste'" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "(entre 0 et 10)" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "Expert" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "Vérification SSL" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "Bibliothèque" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "Choisir un dossier pour la bibliothèque" 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "Chemin votre bibliothèque" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "Échec de lecture" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "Extension InputStream manquante" 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "Supprimer de la bibliothèque" 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "Changer le titre de la bibliothèque" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "Traceur" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "Activer le son Dolby" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "ESN (généré automatiquement, peut être changé manuellement)" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "Paramètres de l'extension InputStream" 162 | 163 | msgctxt "#30036" 164 | msgid "Always use the original title on export" 165 | msgstr "Toujours utiliser le titre original lors de l'export" 166 | 167 | msgctxt "#30037" 168 | msgid "Views" 169 | msgstr "Vues" 170 | 171 | msgctxt "#30038" 172 | msgid "Enable custom views" 173 | msgstr "Activer les vues personnalisées" 174 | 175 | msgctxt "#30039" 176 | msgid "View for folders" 177 | msgstr "Vue pour les dossiers" 178 | 179 | msgctxt "#30040" 180 | msgid "View for movies" 181 | msgstr "Vue pour les films" 182 | 183 | msgctxt "#30041" 184 | msgid "View for shows" 185 | msgstr "Vue pour les séries" 186 | 187 | msgctxt "#30042" 188 | msgid "View for seasons" 189 | msgstr "Vue pour les saisons" 190 | 191 | msgctxt "#30043" 192 | msgid "View for episodes" 193 | msgstr "Vue pour les épisodes" 194 | 195 | msgctxt "#30044" 196 | msgid "View for profiles" 197 | msgstr "Vue pour les profils" 198 | 199 | msgctxt "#30045" 200 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 201 | msgstr "[COLOR cyan][B]-- PAGE SUIVANTE --[/B][/COLOR]" 202 | 203 | msgctxt "#30046" 204 | msgid "Inputstream addon is not enabled" 205 | msgstr "L'extension InputStream n'est pas activée" 206 | 207 | msgctxt "#30047" 208 | msgid "Finally remove?" 209 | msgstr "Supprimer ?" 210 | 211 | msgctxt "#30048" 212 | msgid "Exported" 213 | msgstr "Exporter" 214 | 215 | msgctxt "#30049" 216 | msgid "Update DB" 217 | msgstr "Mettre à jour la base de donnée" 218 | 219 | msgctxt "#30050" 220 | msgid "Update successful" 221 | msgstr "Mis à jour !" 222 | 223 | msgctxt "#30051" 224 | msgid "Request Error" 225 | msgstr "La requête a échoué" 226 | 227 | msgctxt "#30052" 228 | msgid "Unable to complete the request at this time" 229 | msgstr "Impossible de finir la requête" 230 | 231 | msgctxt "#30053" 232 | msgid "Auto Login" 233 | msgstr "Connexion automatique" 234 | 235 | msgctxt "#30054" 236 | msgid "Enable Auto Login" 237 | msgstr "Activer la connexion automatique" 238 | 239 | msgctxt "#30055" 240 | msgid "Profile" 241 | msgstr "Profil" 242 | 243 | msgctxt "#30056" 244 | msgid "ID" 245 | msgstr "ID" 246 | 247 | msgctxt "#30057" 248 | msgid "Select profile in profile listing -> context menu" 249 | msgstr "Selectionnez le profile dans la liste des profils -> menu contextuel" 250 | 251 | msgctxt "#30058" 252 | msgid "Auto Login enabled!" 253 | msgstr "Connexion automatique activée !" 254 | 255 | msgctxt "#30059" 256 | msgid "Switch accounts" 257 | msgstr "Changer de compte" 258 | 259 | msgctxt "#30060" 260 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 261 | msgstr "Activer HEVC (par example pour la 4k sur Android)" 262 | 263 | msgctxt "#30061" 264 | msgid "Update inside library" 265 | msgstr "Mettre à jour la bibliothèque" 266 | 267 | msgctxt "#30062" 268 | msgid "Enable/disable adult pin. Active:" 269 | msgstr "Activer/Désactiver le code PIN adulte. Actif:" 270 | 271 | msgctxt "#30063" 272 | msgid "new episodes added to library" 273 | msgstr "Nouveaux épisodes ajoutés à la bibliothèque" 274 | 275 | msgctxt "#30064" 276 | msgid "Export new episodes" 277 | msgstr "Exporter les nouveaux épisodes" 278 | 279 | msgctxt "#30065" 280 | msgid "Auto-update" 281 | msgstr "Mise à jour automatique" 282 | 283 | msgctxt "#30066" 284 | msgid "never" 285 | msgstr "jamais" 286 | 287 | msgctxt "#30067" 288 | msgid "daily" 289 | msgstr "quotidien" 290 | 291 | msgctxt "#30068" 292 | msgid "every other day" 293 | msgstr "tous les autres jours" 294 | 295 | msgctxt "#30069" 296 | msgid "every 5 days" 297 | msgstr "tous les 5 jours" 298 | 299 | msgctxt "#30070" 300 | msgid "weekly" 301 | msgstr "hebdomadaire" 302 | 303 | msgctxt "#30071" 304 | msgid "Time of Day" 305 | msgstr "Heure du jour" 306 | 307 | msgctxt "#30072" 308 | msgid "Only start after 5 minutes of idle" 309 | msgstr "Démarrer après 5 minutes d'inactivité" 310 | 311 | msgctxt "#30073" 312 | msgid "Check every X minutes if update scheduled" 313 | msgstr "Vérifier toutes les X minutes si mise à jour programmée" 314 | 315 | msgctxt "#30074" 316 | msgid "View for exported" 317 | msgstr "Vue pour les exports" 318 | -------------------------------------------------------------------------------- /resources/language/resource.language.pt_pt/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2018-06-14 13:37+0000\n" 11 | "Last-Translator: Gustavo Silva \n" 12 | "Language-Team: Portuguese\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: pt\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "Addon do Netflix, um serviço de Video On Demand" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "Algumas partes deste addon poderão não ser legais no seu país de residência. Por favor, verifique as suas leis locais antes de instalar." 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "Recomendações" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "Pin de controlo parental" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "Termo de pesquisa" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "Senha" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "A validação de controlo parental falhou" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "Por favor, verifique se o pin de controlo parental está correcto" 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "Erro ao inicar sessão" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "Por favor, verifique as suas credenciais de acesso" 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "Géneros" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "Procurar" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "Sem temporadas disponíveis" 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "Não foram encontrados resultados" 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "Conta" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "Terminar sessão" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "Exportar para a biblioteca" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "Avaliar no Netflix" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "Remover da 'Minha Lista'" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "Adicionar à 'Minha Lista'" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "(Entre 0 e 10)" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "Verificação SSL" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "Bibilioteca" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "Activar pasta de biblioteca personalizada" 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "Caminho para a pasta de bibilioteca personalizada" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "Erro de reprodução" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "O addon Inputstream está em falta" 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "Remover da biblioteca" 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "Alterar título" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "Rastreio" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "Usar Dolby Sound" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "ESN (definido automaticamente, pode ser alterado manualmente)" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "Configurações do addon Inputstream" 162 | 163 | msgctxt "#30036" 164 | msgid "Always use the original title on export" 165 | msgstr "Utilizar sempre o título original para exportar" 166 | 167 | msgctxt "#30037" 168 | msgid "Views" 169 | msgstr "Reproduções" 170 | 171 | msgctxt "#30038" 172 | msgid "Enable custom views" 173 | msgstr "Activar vistas personalizadas" 174 | 175 | msgctxt "#30039" 176 | msgid "View for folders" 177 | msgstr "Vista para as pastas" 178 | 179 | msgctxt "#30040" 180 | msgid "View for movies" 181 | msgstr "Vista para os filmes" 182 | 183 | msgctxt "#30041" 184 | msgid "View for shows" 185 | msgstr "Vista para as séries" 186 | 187 | msgctxt "#30042" 188 | msgid "View for seasons" 189 | msgstr "Vista para as temporadas" 190 | 191 | msgctxt "#30043" 192 | msgid "View for episodes" 193 | msgstr "Vista para os episódios" 194 | 195 | msgctxt "#30044" 196 | msgid "View for profiles" 197 | msgstr "Vista para os perfis" 198 | 199 | msgctxt "#30045" 200 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 201 | msgstr "[COLOR cyan][B]-- PÁGINA SEGUINTE --[/B][/COLOR]" 202 | 203 | msgctxt "#30046" 204 | msgid "Inputstream addon is not enabled" 205 | msgstr "O addon Inputstream não está activado" 206 | 207 | msgctxt "#30047" 208 | msgid "Finally remove?" 209 | msgstr "Deseja eliminar?" 210 | 211 | msgctxt "#30048" 212 | msgid "Exported" 213 | msgstr "Exportado" 214 | 215 | msgctxt "#30049" 216 | msgid "Update DB" 217 | msgstr "Actualizar base de dados" 218 | 219 | msgctxt "#30050" 220 | msgid "Update successful" 221 | msgstr "Actualização concluída com sucesso" 222 | 223 | msgctxt "#30051" 224 | msgid "Request Error" 225 | msgstr "Erro no pedido" 226 | 227 | msgctxt "#30052" 228 | msgid "Unable to complete the request at this time" 229 | msgstr "De momento, não foi possível concluír o pedido" 230 | 231 | msgctxt "#30053" 232 | msgid "Auto Login" 233 | msgstr "Inicio de sessão automático" 234 | 235 | msgctxt "#30054" 236 | msgid "Enable Auto Login" 237 | msgstr "Activar o início de sessão automático" 238 | 239 | msgctxt "#30055" 240 | msgid "Profile" 241 | msgstr "Perfil" 242 | 243 | msgctxt "#30056" 244 | msgid "ID" 245 | msgstr "" 246 | 247 | msgctxt "#30057" 248 | msgid "Select profile in profile listing -> context menu" 249 | msgstr "Seleccionar perfil na lista de perfis -> menu de contexto" 250 | 251 | msgctxt "#30058" 252 | msgid "Auto Login enabled!" 253 | msgstr "Início de sessão automático activado!" 254 | 255 | msgctxt "#30059" 256 | msgid "Switch accounts" 257 | msgstr "Mudar de conta" 258 | 259 | msgctxt "#30060" 260 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 261 | msgstr "Activar perfis HEVC (por exemplo, 4k para Android)" 262 | 263 | msgctxt "#30061" 264 | msgid "Update inside library" 265 | msgstr "Actualizar dentro da biblioteca" 266 | 267 | msgctxt "#30062" 268 | msgid "Enable/disable adult pin. Active:" 269 | msgstr "Activar / desactivar o pin de controlo parental. Activo: " 270 | 271 | msgctxt "#30063" 272 | msgid "new episodes added to library" 273 | msgstr "novos episódios adicionados à biblioteca" 274 | 275 | msgctxt "#30064" 276 | msgid "Export new episodes" 277 | msgstr "Exportar novos episódios" 278 | 279 | msgctxt "#30065" 280 | msgid "Auto-update" 281 | msgstr "Actualizar automaticamente" 282 | 283 | msgctxt "#30066" 284 | msgid "never" 285 | msgstr "nunca" 286 | 287 | msgctxt "#30067" 288 | msgid "daily" 289 | msgstr "diáriamente" 290 | 291 | msgctxt "#30068" 292 | msgid "every other day" 293 | msgstr "dias alternados" 294 | 295 | msgctxt "#30069" 296 | msgid "every 5 days" 297 | msgstr "cada 5 dias" 298 | 299 | msgctxt "#30070" 300 | msgid "weekly" 301 | msgstr "semanalmente" 302 | 303 | msgctxt "#30071" 304 | msgid "Time of Day" 305 | msgstr "Hora do dia" 306 | 307 | msgctxt "#30072" 308 | msgid "Only start after 5 minutes of idle" 309 | msgstr "Começar apenas depois de 5 minutos de inactividade" 310 | 311 | msgctxt "#30073" 312 | msgid "Check every X minutes if update scheduled" 313 | msgstr "Verificar a cada X minutos se uma actualização está programada" 314 | 315 | msgctxt "#30074" 316 | msgid "View for exported" 317 | msgstr "Vista para exportados" 318 | -------------------------------------------------------------------------------- /resources/language/resource.language.es_es/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2018-06-02 19:13+0200\n" 11 | "Last-Translator: danielchc\n" 12 | "Language-Team: Spanish\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: es\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "Addon para Netflix" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "Algunas partes de este addon puede que sean ilegales en tu país de residencia, por favor, consulta con tus autoridades locales antes de instalarlo." 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "Recomendaciones" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "Pin del Control Parental" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "Término de búsqueda" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "Contraseña" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "La comprobación del control parental ha fallado" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "Por favor, comprueba que el pin del control parental es correcto" 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "Error al iniciar sesión" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "Por favor, comprueba que el e-mail y la contraseña son correctos" 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "Géneros" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "Buscar" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "No hay temporadas disponibles" 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "No se encontraron coincidencias" 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "Cuenta" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "Cerrar sesión" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "Exportar a biblioteca" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "Valorar en Netflix" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "Quitar de 'Mi Lista'" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "Añadir a 'Mi Lista'" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "(entre 0 y 10)" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "Experto" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "Verificación SSL" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "Biblioteca" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "Activar un directorio para la biblioteca personalizado" 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "Directorio personalizado de la biblioteca" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "Ocurrió un error en la reproducción" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "No se encuentra el addon Inputstream" 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "Eliminar de la biblioteca" 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "Cambiar nombre de la biblioteca" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "Usar Dolby Sound" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "ESN (establecido automáticamente, pero se puede cambiar de forma manual)" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "Configuración del addon Inputstream..." 162 | 163 | msgctxt "#30036" 164 | msgid "Always use the original title on export" 165 | msgstr "Utilizar siempre el título original para exportar" 166 | 167 | msgctxt "#30037" 168 | msgid "Views" 169 | msgstr "Reproduciones" 170 | 171 | msgctxt "#30038" 172 | msgid "Enable custom views" 173 | msgstr "" 174 | 175 | msgctxt "#30039" 176 | msgid "View for folders" 177 | msgstr "Vista para las carpetas" 178 | 179 | msgctxt "#30040" 180 | msgid "View for movies" 181 | msgstr "Vista para las películas" 182 | 183 | msgctxt "#30041" 184 | msgid "View for shows" 185 | msgstr "Vista para los shows" 186 | 187 | msgctxt "#30042" 188 | msgid "View for seasons" 189 | msgstr "Vista para la temporadas" 190 | 191 | msgctxt "#30043" 192 | msgid "View for episodes" 193 | msgstr "Vista para los episodios" 194 | 195 | msgctxt "#30044" 196 | msgid "View for profiles" 197 | msgstr "Vista para los perfiles" 198 | 199 | msgctxt "#30045" 200 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 201 | msgstr "[COLOR cyan][B]-- SIGUIENTE --[/B][/COLOR]" 202 | 203 | msgctxt "#30046" 204 | msgid "Inputstream addon is not enabled" 205 | msgstr "El addon Inputstream no está activado" 206 | 207 | msgctxt "#30047" 208 | msgid "Finally remove?" 209 | msgstr "¿Estás seguro de eliminar?" 210 | 211 | msgctxt "#30048" 212 | msgid "Exported" 213 | msgstr "Exportado" 214 | 215 | msgctxt "#30049" 216 | msgid "Update DB" 217 | msgstr "Actualizar base de datos" 218 | 219 | msgctxt "#30050" 220 | msgid "Update successful" 221 | msgstr "Actualización correcta" 222 | 223 | msgctxt "#30051" 224 | msgid "Request Error" 225 | msgstr "Error en la solicitud" 226 | 227 | msgctxt "#30052" 228 | msgid "Unable to complete the request at this time" 229 | msgstr "No se puede completar la solicitud en este momento" 230 | 231 | msgctxt "#30053" 232 | msgid "Auto Login" 233 | msgstr "Inicio de sesión automático" 234 | 235 | msgctxt "#30054" 236 | msgid "Enable Auto Login" 237 | msgstr "Activar inicio de sesión automático" 238 | 239 | msgctxt "#30055" 240 | msgid "Profile" 241 | msgstr "Perfil" 242 | 243 | msgctxt "#30056" 244 | msgid "ID" 245 | msgstr "" 246 | 247 | msgctxt "#30057" 248 | msgid "Select profile in profile listing -> context menu" 249 | msgstr "Seleccionar perfil en la lista de perfiles -> menú contextual" 250 | 251 | msgctxt "#30058" 252 | msgid "Auto Login enabled!" 253 | msgstr "¡Inicio de sesión automático activado!" 254 | 255 | msgctxt "#30059" 256 | msgid "Switch accounts" 257 | msgstr "Cambiar de cuenta" 258 | 259 | msgctxt "#30060" 260 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 261 | msgstr "Habilitar perfiles HEVC (por ejemplo, 4k para Android)" 262 | 263 | msgctxt "#30061" 264 | msgid "Update inside library" 265 | msgstr "Actualizar dentro de la biblioteca" 266 | 267 | msgctxt "#30062" 268 | msgid "Enable/disable adult pin. Active:" 269 | msgstr "Habilitar /deshabilitar el pin de Control Parental. Activo:" 270 | 271 | msgctxt "#30063" 272 | msgid "new episodes added to library" 273 | msgstr "nuevos episodios añadidos a la biblioteca" 274 | 275 | msgctxt "#30064" 276 | msgid "Export new episodes" 277 | msgstr "Exportar nuevos episodios" 278 | 279 | msgctxt "#30065" 280 | msgid "Auto-update" 281 | msgstr "Actualizar automáticamente" 282 | 283 | msgctxt "#30066" 284 | msgid "never" 285 | msgstr "nunca" 286 | 287 | msgctxt "#30067" 288 | msgid "daily" 289 | msgstr "diariamente" 290 | 291 | msgctxt "#30068" 292 | msgid "every other day" 293 | msgstr "cualquier otro día" 294 | 295 | msgctxt "#30069" 296 | msgid "every 5 days" 297 | msgstr "cada 5 días" 298 | 299 | msgctxt "#30070" 300 | msgid "weekly" 301 | msgstr "semanalmente" 302 | 303 | msgctxt "#30071" 304 | msgid "Time of Day" 305 | msgstr "Hora del día" 306 | 307 | msgctxt "#30072" 308 | msgid "Only start after 5 minutes of idle" 309 | msgstr "Solo empezar después de 5 minutos de inactividad" 310 | 311 | msgctxt "#30073" 312 | msgid "Check every X minutes if update scheduled" 313 | msgstr "Comprobar cada X minutos si la actualización está programada" 314 | 315 | msgctxt "#30074" 316 | msgid "View for exported" 317 | msgstr "Ver para exportar" 318 | -------------------------------------------------------------------------------- /resources/language/resource.language.de_de/strings.po: -------------------------------------------------------------------------------- 1 | # Kodi Media Center language file 2 | # Addon Name: Netflix 3 | # Addon id: plugin.video.netflix 4 | # Addon Provider: libdev + jojo + asciidisco 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: plugin.video.netflix\n" 8 | "Report-Msgid-Bugs-To: https://github.com/asciidisco/plugin.video.netflix\n" 9 | "POT-Creation-Date: 2017-07-24 16:15+0000\n" 10 | "PO-Revision-Date: 2017-07-24 16:15+0000\n" 11 | "Last-Translator: Sebastian Golasch \n" 12 | "Language-Team: German\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Language: de\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | msgctxt "Addon Summary" 20 | msgid "Netflix" 21 | msgstr "" 22 | 23 | msgctxt "Addon Description" 24 | msgid "Netflix VOD Services Addon" 25 | msgstr "Addon für Netflix VOD-Dienste" 26 | 27 | msgctxt "Addon Disclaimer" 28 | msgid "Some parts of this addon may not be legal in your country of residence - please check with your local laws before installing." 29 | msgstr "Möglicherweise sind einge Teile dieses Addons in Ihrem Land illegal, Sie sollten dies unbedingt vor der Installation überprüfen." 30 | 31 | msgctxt "#30001" 32 | msgid "Recommendations" 33 | msgstr "Vorschläge" 34 | 35 | msgctxt "#30002" 36 | msgid "Adult Pin" 37 | msgstr "Kindersicherungs-PIN" 38 | 39 | msgctxt "#30003" 40 | msgid "Search term" 41 | msgstr "Suchbegriff" 42 | 43 | msgctxt "#30004" 44 | msgid "Password" 45 | msgstr "Passwort" 46 | 47 | msgctxt "#30005" 48 | msgid "E-mail" 49 | msgstr "E-Mail" 50 | 51 | msgctxt "#30006" 52 | msgid "Adult verification failed" 53 | msgstr "Kindersicherungs-PIN nicht korrekt" 54 | 55 | msgctxt "#30007" 56 | msgid "Please Check your adult pin" 57 | msgstr "Bitter überprüfen Sie Ihre Kindersicherungs-PIN" 58 | 59 | msgctxt "#30008" 60 | msgid "Login failed" 61 | msgstr "Anmeldung nicht erfolgreich" 62 | 63 | msgctxt "#30009" 64 | msgid "Please Check your credentials" 65 | msgstr "Bitte die Anmeldedaten überprüfen" 66 | 67 | msgctxt "#30010" 68 | msgid "Genres" 69 | msgstr "" 70 | 71 | msgctxt "#30011" 72 | msgid "Search" 73 | msgstr "Suche" 74 | 75 | msgctxt "#30012" 76 | msgid "No seasons available" 77 | msgstr "Keine Staffeln verfügbar" 78 | 79 | msgctxt "#30013" 80 | msgid "No matches found" 81 | msgstr "Keine Titel gefunden" 82 | 83 | msgctxt "#30014" 84 | msgid "Account" 85 | msgstr "Konto" 86 | 87 | msgctxt "#30017" 88 | msgid "Logout" 89 | msgstr "Abmelden" 90 | 91 | msgctxt "#30018" 92 | msgid "Export to library" 93 | msgstr "In Bibliothek exportieren" 94 | 95 | msgctxt "#30019" 96 | msgid "Rate on Netflix" 97 | msgstr "Auf Netflix bewerten" 98 | 99 | msgctxt "#30020" 100 | msgid "Remove from 'My list'" 101 | msgstr "Von 'Meiner Liste' entfernen" 102 | 103 | msgctxt "#30021" 104 | msgid "Add to 'My list'" 105 | msgstr "Zu 'Meiner Liste' hinzufügen" 106 | 107 | msgctxt "#30022" 108 | msgid "(between 0 & 10)" 109 | msgstr "(zwischen 0 & 10)" 110 | 111 | msgctxt "#30023" 112 | msgid "Expert" 113 | msgstr "Experte" 114 | 115 | msgctxt "#30024" 116 | msgid "SSL verification" 117 | msgstr "SSL-Überprüfung" 118 | 119 | msgctxt "#30025" 120 | msgid "Library" 121 | msgstr "Bibliothek" 122 | 123 | msgctxt "#30026" 124 | msgid "Enable custom library folder" 125 | msgstr "Eigenen Bibliothekordner nutzen" 126 | 127 | msgctxt "#30027" 128 | msgid "Custom library path" 129 | msgstr "Pfad zur Bibliothek" 130 | 131 | msgctxt "#30028" 132 | msgid "Playback error" 133 | msgstr "Fehler beim Abspielen" 134 | 135 | msgctxt "#30029" 136 | msgid "Missing Inputstream addon" 137 | msgstr "Inputstream-Addon nicht gefunden" 138 | 139 | msgctxt "#30030" 140 | msgid "Remove from library" 141 | msgstr "Aus Bibliothek entfernen" 142 | 143 | msgctxt "#30031" 144 | msgid "Change library title" 145 | msgstr "Export Titel ändern" 146 | 147 | msgctxt "#30032" 148 | msgid "Tracking" 149 | msgstr "" 150 | 151 | msgctxt "#30033" 152 | msgid "Use Dolby Sound" 153 | msgstr "Benutze Dolby-Ton" 154 | 155 | msgctxt "#30034" 156 | msgid "ESN (set automatically, can be changed manually)" 157 | msgstr "ESN (änderbar, wird autom. gesetzt)" 158 | 159 | msgctxt "#30035" 160 | msgid "Inputstream Addon Settings..." 161 | msgstr "Inputstream Addon Einstellungen..." 162 | 163 | msgctxt "#30036" 164 | msgid "Always use the original title on export" 165 | msgstr "Immer den Originaltitel beim Export nutzen" 166 | 167 | msgctxt "#30037" 168 | msgid "Views" 169 | msgstr "Ansichten" 170 | 171 | msgctxt "#30038" 172 | msgid "Enable custom views" 173 | msgstr "Eigene Ansichten aktivieren" 174 | 175 | msgctxt "#30039" 176 | msgid "View for folders" 177 | msgstr "Ansicht für Ordner" 178 | 179 | msgctxt "#30040" 180 | msgid "View for movies" 181 | msgstr "Ansicht für Filme" 182 | 183 | msgctxt "#30041" 184 | msgid "View for shows" 185 | msgstr "Ansicht für Serien" 186 | 187 | msgctxt "#30042" 188 | msgid "View for seasons" 189 | msgstr "Ansicht für Staffeln" 190 | 191 | msgctxt "#30043" 192 | msgid "View for episodes" 193 | msgstr "Ansicht für Episoden" 194 | 195 | msgctxt "#30044" 196 | msgid "View for profiles" 197 | msgstr "Ansicht für Profile" 198 | 199 | msgctxt "#30045" 200 | msgid "[COLOR cyan][B]-- NEXT PAGE --[/B][/COLOR]" 201 | msgstr "[COLOR cyan][B]-- NÄCHSTE SEITE --[/B][/COLOR]" 202 | 203 | msgctxt "#30047" 204 | msgid "Finally remove?" 205 | msgstr "Endgültig entfernen?" 206 | 207 | msgctxt "#30048" 208 | msgid "Exported" 209 | msgstr "Exportiert" 210 | 211 | msgctxt "#30049" 212 | msgid "Update DB" 213 | msgstr "Aktualisiere DB" 214 | 215 | msgctxt "#30050" 216 | msgid "Update successful" 217 | msgstr "Aktualisierung erfolgreich" 218 | 219 | msgctxt "#30053" 220 | msgid "Auto Login" 221 | msgstr "Automatisch Anmelden" 222 | 223 | msgctxt "#30054" 224 | msgid "Enable Auto Login" 225 | msgstr "Aktiviere automatisches Anmelden" 226 | 227 | msgctxt "#30055" 228 | msgid "Profile" 229 | msgstr "Profil" 230 | 231 | msgctxt "#30056" 232 | msgid "ID" 233 | msgstr "" 234 | 235 | msgctxt "#30057" 236 | msgid "Select profile in profile listing -> context menu" 237 | msgstr "Wähle Profil in der Übersicht -> Kontextmenü" 238 | 239 | msgctxt "#30058" 240 | msgid "Auto Login enabled!" 241 | msgstr "Automatische Anmeldung aktiviert!" 242 | 243 | msgctxt "#30059" 244 | msgid "Switch accounts" 245 | msgstr "Accounts wechseln" 246 | 247 | msgctxt "#30060" 248 | msgid "Enable HEVC profiles (f.e. 4k for Android)" 249 | msgstr "Aktiviere HEVC profile (z.b. für 4k in Android)" 250 | 251 | msgctxt "#30061" 252 | msgid "Update inside library" 253 | msgstr "In Bibliothek aktualisieren" 254 | 255 | msgctxt "#30062" 256 | msgid "Enable/disable adult pin. Active:" 257 | msgstr "Kindersicherungs PIN Abfrage ein-/ausschalten. Aktiv:" 258 | 259 | msgctxt "#30063" 260 | msgid "new episodes added to library" 261 | msgstr "neue Folgen zur Bilbiothek hinzugefügt" 262 | 263 | msgctxt "#30064" 264 | msgid "Export new episodes" 265 | msgstr "Neue Folgen der Bibliothek hinzufügen" 266 | 267 | msgctxt "#30065" 268 | msgid "Auto-update" 269 | msgstr "Automatische Aktualisierung" 270 | 271 | msgctxt "#30066" 272 | msgid "never" 273 | msgstr "niemals" 274 | 275 | msgctxt "#30067" 276 | msgid "daily" 277 | msgstr "täglich" 278 | 279 | msgctxt "#30068" 280 | msgid "every other day" 281 | msgstr "alle zwei Tage" 282 | 283 | msgctxt "#30069" 284 | msgid "every 5 days" 285 | msgstr "alle 5 Tage" 286 | 287 | msgctxt "#30070" 288 | msgid "weekly" 289 | msgstr "wöchentlich" 290 | 291 | msgctxt "#30071" 292 | msgid "Time of Day" 293 | msgstr "Uhrzeit" 294 | 295 | msgctxt "#30072" 296 | msgid "Only start after 5 minutes of idle" 297 | msgstr "Nur nach 5 Minuten vorherigem Leerlauf starten" 298 | 299 | msgctxt "#30073" 300 | msgid "Check every X minutes if update scheduled" 301 | msgstr "Prüfe alle X Minuten ob Startzeitpunkt erreicht" 302 | 303 | msgctxt "#30074" 304 | msgid "View for exported" 305 | msgstr "Ansicht für Exportiert" 306 | 307 | msgctxt "#30075" 308 | msgid "Ask to skip intro and recap" 309 | msgstr "Aktiviere Intro und Recap überspringen Funktion" 310 | 311 | msgctxt "#30076" 312 | msgid "Skip intro" 313 | msgstr "Intro überspringen" 314 | 315 | msgctxt "#30077" 316 | msgid "Skip recap" 317 | msgstr "Recap überspringen" 318 | 319 | msgctxt "#30078" 320 | msgid "Playback" 321 | msgstr "Wiedergabe" 322 | 323 | msgctxt "#30079" 324 | msgid "Skip automatically" 325 | msgstr "Automatisch überspringen" 326 | 327 | msgctxt "#30080" 328 | msgid "Don't pause when skipping" 329 | msgstr "Beim Weiterspringen nicht pausieren" 330 | 331 | msgctxt "#30081" 332 | msgid "Save audio/subtitle preferences for this show" 333 | msgstr "Audioeinstellungen für diese Serie speichern" 334 | 335 | msgctxt "#30082" 336 | msgid "Save audio / subtitle settings for TV shows" 337 | msgstr "Sprache und Untertitel für gesamte Serie merken" 338 | 339 | msgctxt "#30083" 340 | msgid "Save bookmarks for library items / mark as watched" 341 | msgstr "Fortschritt für Bibliothekseinträge speichern und als gesehen markieren" 342 | --------------------------------------------------------------------------------