├── .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 | [](https://blockchain.info/address/1DHGftMkFXXsDY7UnqQuatWwxQzKVu88sF)
7 | [](https://travis-ci.org/asciidisco/plugin.video.netflix)
8 | [](https://codeclimate.com/github/asciidisco/plugin.video.netflix/coverage)
9 | [](https://codeclimate.com/github/asciidisco/plugin.video.netflix)
10 | [](https://codeclimate.com/github/asciidisco/plugin.video.netflix)
11 | [](https://github.com/asciidisco/plugin.video.netflix/releases)
12 | [](https://asciidisco.github.io/plugin.video.netflix/)
13 | [](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 |
--------------------------------------------------------------------------------