├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ └── publish-addon.yml
├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE
├── Pipfile
├── README.md
├── pyproject.toml
└── script.service.hyperion-control
├── addon.xml
├── resources
├── __init__.py
├── fanart.png
├── icon.png
├── language
│ ├── resource.language.de_de
│ │ └── strings.po
│ ├── resource.language.en_gb
│ │ └── strings.po
│ ├── resource.language.es_es
│ │ └── strings.po
│ ├── resource.language.fr_fr
│ │ └── strings.po
│ ├── resource.language.hu_hu
│ │ └── strings.po
│ └── resource.language.pl_pl
│ │ └── strings.po
├── lib
│ ├── __init__.py
│ ├── api_client.py
│ ├── gui.py
│ ├── hyperion.py
│ ├── interfaces.py
│ ├── logger.py
│ ├── monitor.py
│ ├── player.py
│ ├── settings_manager.py
│ ├── ssdp.py
│ └── utils.py
├── screenshot-01.png
└── settings.xml
└── service.py
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 |
4 | - package-ecosystem: "github-actions"
5 | directory: "/"
6 | schedule:
7 | interval: "monthly"
8 |
--------------------------------------------------------------------------------
/.github/workflows/publish-addon.yml:
--------------------------------------------------------------------------------
1 | name: Check and publish addon
2 |
3 | on: [push]
4 |
5 | env:
6 | ADDON_NAME: script.service.hyperion-control
7 | TARGET_KODI_VER: nexus
8 |
9 | jobs:
10 | check:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v4
14 | - name: Set up Python 3.11
15 | uses: actions/setup-python@v5
16 | with:
17 | python-version: "3.11"
18 | - name: Install addon checker
19 | run: |
20 | pip install -q kodi-addon-checker
21 | - name: Check with addon-checker
22 | run: |
23 | kodi-addon-checker --branch $TARGET_KODI_VER --allow-folder-id-mismatch $ADDON_NAME
24 |
25 | github_release:
26 | runs-on: ubuntu-latest
27 | permissions:
28 | contents: write
29 |
30 | needs: check
31 | if: github.ref_type == 'tag'
32 | steps:
33 | - uses: actions/checkout@v4
34 | - name: Set up Python 3.11
35 | uses: actions/setup-python@v5
36 | with:
37 | python-version: "3.11"
38 | - name: Install addon submitter
39 | run: |
40 | pip install -q git+https://github.com/xbmc/kodi-addon-submitter.git
41 | - name: Package addon
42 | run: |
43 | submit-addon -s -z $ADDON_NAME
44 | - name: Publish release
45 | uses: ncipollo/release-action@v1
46 | with:
47 | artifacts: "*.zip"
48 |
49 | kodi_publish:
50 | runs-on: ubuntu-latest
51 |
52 | needs: check
53 | if: github.ref_type == 'tag'
54 | steps:
55 | - uses: actions/checkout@v4
56 | - name: Set up Python 3.11
57 | uses: actions/setup-python@v5
58 | with:
59 | python-version: "3.11"
60 | - name: Install addon submitter
61 | run: |
62 | pip install -q git+https://github.com/xbmc/kodi-addon-submitter.git
63 | - name: Submit addon
64 | run: |
65 | submit-addon -r repo-scripts -b $TARGET_KODI_VER -s --pull-request $ADDON_NAME
66 | env:
67 | GH_USERNAME: ${{ github.actor }}
68 | GH_TOKEN: ${{ secrets.GH_TOKEN }}
69 | EMAIL: "${{ github.actor }}@users.noreply.github.com"
70 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # python
9 | *.pyo
10 | __pycache__
11 | .idea
12 | Pipfile.lock
13 | .mypy_cache
14 | .ruff_cache
15 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | default_language_version:
2 | python: python3
3 | repos:
4 | - repo: https://github.com/pre-commit/pre-commit-hooks
5 | rev: v4.3.0
6 | hooks:
7 | - id: check-toml
8 | - id: check-yaml
9 | - id: end-of-file-fixer
10 | - id: trailing-whitespace
11 | - id: check-added-large-files
12 | - repo: https://github.com/executablebooks/mdformat
13 | rev: 0.7.16
14 | hooks:
15 | - id: mdformat
16 | exclude: CHANGELOG.md
17 | additional_dependencies:
18 | - mdformat-black
19 | - mdformat-frontmatter
20 | - mdformat-admon
21 | - repo: https://github.com/psf/black
22 | rev: 22.10.0
23 | hooks:
24 | - id: black
25 | - repo: https://github.com/charliermarsh/ruff-pre-commit
26 | rev: 'v0.0.252'
27 | hooks:
28 | - id: ruff
29 | args: [--fix, --exit-non-zero-on-fix]
30 | - repo: https://github.com/pre-commit/mirrors-mypy
31 | rev: 'v1.4.1'
32 | hooks:
33 | - id: mypy
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 hyperion-project
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 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.org/simple"
3 | verify_ssl = true
4 | name = "pypi"
5 |
6 | [packages]
7 | pre-commit = "*"
8 | cfgv = "*"
9 |
10 | [dev-packages]
11 | kodistubs = "*"
12 | kodi-addon-checker = "*"
13 |
14 | [requires]
15 | python_version = "3.11"
16 |
17 | [scripts]
18 | pre-commit = "pre-commit install"
19 | lint = "pre-commit run --all"
20 | check = "kodi-addon-checker --branch nexus --allow-folder-id-mismatch script.service.hyperion-control"
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Hyperion Control for Kodi 20 (Nexus)
2 |
3 | [](https://github.com/hyperion-project/hyperion.control/releases)
4 | [](https://poeditor.com/join/project?hash=WauIsJeEx4)
5 | [](https://www.hyperion-project.org)
6 |
7 | Hyperion Control is an addon for Kodi Mediacenter that observes Kodi events (playing state, screensaver, ...). Based on these events you can control Hyperion components to enable or disable accordingly.
8 |
9 | For example playing a video should enable screen capture, if you pause the video or you are at the Kodi menu the screen capture should be disabled to show a background effect as mood light
10 |
11 | ### Features
12 |
13 | - Supports various Hyperion components across multiple instances
14 | - Setup wizard with Hyperion Server detection (zero configuration)
15 | - Token Authorization (optional)
16 | - Languages: English, German, Spanish, French, Polish, Hungarian
17 | - Execute from settings dialog: Hyperion Server detection
18 |
19 | ### Installation
20 |
21 | - Download .zip from release page and use "Install from zip file" dialog at the Kodi addons section.
22 |
23 | ### Credits
24 |
25 | - Dan Krause for the SSDP client
26 | - Paulchen-Panther for service development
27 | - brindosch for service development
28 | - derBernhard for Kodi 19 migration
29 | - sanzoghenzo for Kodi 20 (Nexus) support and major refactoring
30 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.mypy]
2 | check_untyped_defs = true
3 | disallow_any_generics = true
4 | disallow_incomplete_defs = true
5 | disallow_untyped_calls = false
6 | disallow_untyped_defs = true
7 | no_implicit_optional = true
8 | show_error_codes = true
9 | strict_equality = true
10 | strict_optional = true
11 | warn_redundant_casts = true
12 | warn_unreachable = true
13 | warn_unused_configs = true
14 | warn_unused_ignores = true
15 |
16 | [[tool.mypy.overrides]]
17 | module = ["requests.*"]
18 | ignore_missing_imports = true
19 |
20 | [tool.ruff]
21 | select = ["B", "C", "D", "E", "F", "I", "N", "S", "U", "W"]
22 | ignore = ["E501", "D212", "D103", "D107"]
23 | target-version = "py310"
24 | src = ["resources"]
25 |
26 | [tool.ruff.per-file-ignores]
27 | "script.service.hyperion-control/resources/lib/player.py" = ["N802"]
28 | "script.service.hyperion-control/resources/lib/monitor.py" = ["N802"]
29 |
30 | [tool.ruff.pydocstyle]
31 | convention = "google"
32 |
33 | [tool.ruff.isort]
34 | force-single-line = true
35 |
36 | [tool.ruff.pyupgrade]
37 | keep-runtime-typing = true
38 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/addon.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Control Hyperion Ambilight
11 | Hyperion Ambilight Steuerung
12 | Enable and disable components (like capture) of Hyperion automatically based on playing/screensaver state of Kodi[CR]-Supports auto detection of Hyperion Servers[CR]-Token authentication
13 | Aktiviere und deaktiviere automatisch Hyperion Komponenten basierend auf dem aktuellen Kodi Status[CR]-Unterstützt Hyperion Server suche[CR]-Token Autorisierung
14 | all
15 | MIT
16 | https://hyperion-project.org/forum
17 | https://hyperion-project.org/forum
18 | https://github.com/hyperion-project/hyperion.control
19 |
20 | 20.0.1
21 | - Removed redundant debug setting
22 |
23 | 20.0.0
24 | - Kodi 20 (Nexus) support
25 | - Multi instance handling
26 | - New language: Hungarian
27 |
28 | 19.0.2
29 | - New languages: Español, Français, Polski
30 |
31 | 19.0.1
32 | - Kodi 19 support
33 | - minor cleanups and corrections
34 |
35 | 1.0.1
36 | - Fixed a crash with 3d mode @b-jesch
37 |
38 | 1.0.0
39 | - Added support for server search
40 | - Added support token authentication
41 | - Fixed issue where kodi api does not properly announce video playing states
42 |
43 |
44 | resources/icon.png
45 | resources/fanart.png
46 | resources/screenshot-01.png
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/__init__.py:
--------------------------------------------------------------------------------
1 | """Hyperion control addon resources."""
2 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/fanart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperion-project/hyperion.control/931034fbbb7b35a2aa1eeb4aec3c547fe3cd1fae/script.service.hyperion-control/resources/fanart.png
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperion-project/hyperion.control/931034fbbb7b35a2aa1eeb4aec3c547fe3cd1fae/script.service.hyperion-control/resources/icon.png
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/language/resource.language.de_de/strings.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "MIME-Version: 1.0\n"
4 | "Content-Type: text/plain; charset=UTF-8\n"
5 | "Content-Transfer-Encoding: 8bit\n"
6 | "X-Generator: POEditor.com\n"
7 | "Project-Id-Version: Hyperion Control\n"
8 | "Language: de\n"
9 |
10 | #:
11 | msgctxt "#32000"
12 | msgid "General"
13 | msgstr "Allgemein"
14 |
15 | #:
16 | msgctxt "#32001"
17 | msgid "Hyperion IP"
18 | msgstr "Hyperion IP"
19 |
20 | #:
21 | msgctxt "#32002"
22 | msgid "Hyperion Webserver Port"
23 | msgstr "Hyperion Webserver Port"
24 |
25 | #:
26 | msgctxt "#32003"
27 | msgid "Switch automatically between 2D/3D"
28 | msgstr "Automatisch auf 2D/3D umstellen"
29 |
30 | #:
31 | msgctxt "#32004"
32 | msgid "Enable Hyperion on startup"
33 | msgstr "Aktiviere Hyperion beim Starten"
34 |
35 | #:
36 | msgctxt "#32005"
37 | msgid "Disable Hyperion on shutdown"
38 | msgstr "Deaktiviere Hyperion beim Beenden"
39 |
40 | #:
41 | msgctxt "#32007"
42 | msgid "Show changelog after update"
43 | msgstr "Zeige Änderungen nach Update"
44 |
45 | #:
46 | msgctxt "#32008"
47 | msgid "Authorization Token"
48 | msgstr "Autorisierungs-Token"
49 |
50 | #:
51 | msgctxt "#32025"
52 | msgid "Component"
53 | msgstr "Komponente"
54 |
55 | #:
56 | msgctxt "#32026"
57 | msgid "Enable the chosen component in these situations else disable it."
58 | msgstr "Aktiviere die Komponente in folgenden Situationen, sonst wird sie deaktiviert."
59 |
60 | #:
61 | msgctxt "#32027"
62 | msgid "Hyperion component"
63 | msgstr "Hyperion Komponente"
64 |
65 | #:
66 | msgctxt "#32028"
67 | msgid "Enable during video playback"
68 | msgstr "Aktiviere bei Videos"
69 |
70 | #:
71 | msgctxt "#32029"
72 | msgid "Enable during music playback"
73 | msgstr "Aktiviere bei Musik"
74 |
75 | #:
76 | msgctxt "#32030"
77 | msgid "Enable during player pause"
78 | msgstr "Aktiviere bei pausiertem Player"
79 |
80 | #:
81 | msgctxt "#32031"
82 | msgid "Enable in Kodi menu"
83 | msgstr "Aktiviere im Kodi Menü"
84 |
85 | #:
86 | msgctxt "#32032"
87 | msgid "Enable during active screensaver"
88 | msgstr "Aktiviere bei Bildschirmschoner"
89 |
90 | #:
91 | msgctxt "#32041"
92 | msgid "USB Capture"
93 | msgstr "USB Aufnahme"
94 |
95 | #:
96 | msgctxt "#32042"
97 | msgid "LED Hardware"
98 | msgstr "LED Hardware"
99 |
100 | #:
101 | msgctxt "#32043"
102 | msgid "Smoothing"
103 | msgstr "Glättung"
104 |
105 | #:
106 | msgctxt "#32045"
107 | msgid "Forwarder"
108 | msgstr "Weiterleitung"
109 |
110 | #:
111 | msgctxt "#32101"
112 | msgid "Would you like to search for a Hyperion Server and adjust settings?"
113 | msgstr "Hyperion Server suchen und Einstellungen anpassen?"
114 |
115 | #:
116 | msgctxt "#32102"
117 | msgid "Select a Hyperion Server, we found more than one"
118 | msgstr "Wähle einen Hyperion Server, es wurden mehrere gefunden"
119 |
120 | #:
121 | msgctxt "#32104"
122 | msgid "We are sorry, no Hyperion Server has been found. You need to configure IP address and port by hand"
123 | msgstr "Leider wurde kein Hyperion Server gefunden, du musst IP-Adresse und Port selbst eintragen"
124 |
125 | #:
126 | msgctxt "#32105"
127 | msgid "The Authorization Token isn't valid"
128 | msgstr "Das Autorisierungs-Token ist nicht gültig"
129 |
130 | #:
131 | msgctxt "#32150"
132 | msgid "Execute"
133 | msgstr "Ausführen"
134 |
135 | #:
136 | msgctxt "#32151"
137 | msgid "Execute a specific task you might need again.[CR]Select a task from the tasklist and press OK to close the settings dialog."
138 | msgstr "Führe eine spezifische Aufgabe nochmals aus.[CR]Wähle dazu aus der Liste eine Aufgabe und drücke den OK Button."
139 |
140 | #:
141 | msgctxt "#32152"
142 | msgid "The addon will handle your request immediately.[CR]Be aware that the addon should be already enabled"
143 | msgstr "Die Einstellungen schließen sich.[CR]Das Addon wird sofort mit der Ausführung beginnen, sofern es aktiviert ist"
144 |
145 | #:
146 | msgctxt "#32153"
147 | msgid "Tasklist"
148 | msgstr "Aufgabenliste"
149 |
150 | #:
151 | msgctxt "#32154"
152 | msgid "No Task"
153 | msgstr "keine Aufgabe"
154 |
155 | #:
156 | msgctxt "#32155"
157 | msgid "Search: Hyperion Server"
158 | msgstr "Suchen: Hyperion Server"
159 |
160 | #:
161 | msgctxt "#32044"
162 | msgid "Blackbar detector"
163 | msgstr "Schwarze Balken Erkennung"
164 |
165 | #:
166 | msgctxt "#32040"
167 | msgid "Screen Capture"
168 | msgstr "Bildschirm Aufnahme"
169 |
170 | #:
171 | msgctxt "#32100"
172 | msgid "Welcome to Hyperion Control!"
173 | msgstr "Willkommen zu Hyperion Control!"
174 |
175 | #:
176 | msgctxt "#32103"
177 | msgid "We found the following Hyperion Server for usage:"
178 | msgstr "Es wurde folgender Hyperion Server gefunden:"
179 |
180 | #:
181 | msgctxt "#32046"
182 | msgid "Boblight Server"
183 | msgstr "Boblight Server"
184 |
185 | #:
186 | msgctxt "#32047"
187 | msgid "Hyperion"
188 | msgstr "Hyperion"
189 |
190 | #:
191 | msgctxt "#32048"
192 | msgid "Audio Capture"
193 | msgstr "Audio Aufnahme"
194 |
195 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/language/resource.language.en_gb/strings.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "MIME-Version: 1.0\n"
4 | "Content-Type: text/plain; charset=UTF-8\n"
5 | "Content-Transfer-Encoding: 8bit\n"
6 | "X-Generator: POEditor.com\n"
7 | "Project-Id-Version: Hyperion Control\n"
8 | "Language: en\n"
9 |
10 | #:
11 | msgctxt "#32000"
12 | msgid "General"
13 | msgstr "General"
14 |
15 | #:
16 | msgctxt "#32001"
17 | msgid "Hyperion IP"
18 | msgstr "Hyperion IP"
19 |
20 | #:
21 | msgctxt "#32002"
22 | msgid "Hyperion Webserver Port"
23 | msgstr "Hyperion Webserver Port"
24 |
25 | #:
26 | msgctxt "#32003"
27 | msgid "Switch automatically between 2D/3D"
28 | msgstr "Switch automatically between 2D/3D"
29 |
30 | #:
31 | msgctxt "#32004"
32 | msgid "Enable Hyperion on startup"
33 | msgstr "Enable Hyperion on startup"
34 |
35 | #:
36 | msgctxt "#32005"
37 | msgid "Disable Hyperion on shutdown"
38 | msgstr "Disable Hyperion on shutdown"
39 |
40 | #:
41 | msgctxt "#32007"
42 | msgid "Show changelog after update"
43 | msgstr "Show changelog after update"
44 |
45 | #:
46 | msgctxt "#32008"
47 | msgid "Authorization Token"
48 | msgstr "Authorization Token"
49 |
50 | #:
51 | msgctxt "#32025"
52 | msgid "Component"
53 | msgstr "Component"
54 |
55 | #:
56 | msgctxt "#32026"
57 | msgid "Enable the chosen component in these situations else disable it."
58 | msgstr "Enable the chosen component in these situations else disable it."
59 |
60 | #:
61 | msgctxt "#32027"
62 | msgid "Hyperion component"
63 | msgstr "Hyperion component"
64 |
65 | #:
66 | msgctxt "#32028"
67 | msgid "Enable during video playback"
68 | msgstr "Enable during video playback"
69 |
70 | #:
71 | msgctxt "#32029"
72 | msgid "Enable during music playback"
73 | msgstr "Enable during music playback"
74 |
75 | #:
76 | msgctxt "#32030"
77 | msgid "Enable during player pause"
78 | msgstr "Enable during player pause"
79 |
80 | #:
81 | msgctxt "#32031"
82 | msgid "Enable in Kodi menu"
83 | msgstr "Enable in Kodi menu"
84 |
85 | #:
86 | msgctxt "#32032"
87 | msgid "Enable during active screensaver"
88 | msgstr "Enable during active screensaver"
89 |
90 | #:
91 | msgctxt "#32041"
92 | msgid "USB Capture"
93 | msgstr "USB Capture"
94 |
95 | #:
96 | msgctxt "#32042"
97 | msgid "LED Hardware"
98 | msgstr "LED Hardware"
99 |
100 | #:
101 | msgctxt "#32043"
102 | msgid "Smoothing"
103 | msgstr "Smoothing"
104 |
105 | #:
106 | msgctxt "#32045"
107 | msgid "Forwarder"
108 | msgstr "Forwarder"
109 |
110 | #:
111 | msgctxt "#32101"
112 | msgid "Would you like to search for a Hyperion Server and adjust settings?"
113 | msgstr "Would you like to search for a Hyperion Server and adjust settings?"
114 |
115 | #:
116 | msgctxt "#32102"
117 | msgid "Select a Hyperion Server, we found more than one"
118 | msgstr "Select a Hyperion Server, we found more than one"
119 |
120 | #:
121 | msgctxt "#32104"
122 | msgid "We are sorry, no Hyperion Server has been found. You need to configure IP address and port by hand"
123 | msgstr "We are sorry, no Hyperion Server has been found. You need to configure IP address and port by hand"
124 |
125 | #:
126 | msgctxt "#32105"
127 | msgid "The Authorization Token isn't valid"
128 | msgstr "The Authorization Token isn't valid"
129 |
130 | #:
131 | msgctxt "#32150"
132 | msgid "Execute"
133 | msgstr "Execute"
134 |
135 | #:
136 | msgctxt "#32151"
137 | msgid "Execute a specific task you might need again.[CR]Select a task from the tasklist and press OK to close the settings dialog."
138 | msgstr "Execute a specific task you might need again.[CR]Select a task from the tasklist and press OK to close the settings dialog."
139 |
140 | #:
141 | msgctxt "#32152"
142 | msgid "The addon will handle your request immediately.[CR]Be aware that the addon should be already enabled"
143 | msgstr "The addon will handle your request immediately.[CR]Be aware that the addon should be already enabled"
144 |
145 | #:
146 | msgctxt "#32153"
147 | msgid "Tasklist"
148 | msgstr "Tasklist"
149 |
150 | #:
151 | msgctxt "#32154"
152 | msgid "No Task"
153 | msgstr "No Task"
154 |
155 | #:
156 | msgctxt "#32155"
157 | msgid "Search: Hyperion Server"
158 | msgstr "Search: Hyperion Server"
159 |
160 | #:
161 | msgctxt "#32044"
162 | msgid "Blackbar detector"
163 | msgstr "Blackbar detector"
164 |
165 | #:
166 | msgctxt "#32040"
167 | msgid "Screen Capture"
168 | msgstr "Screen Capture"
169 |
170 | #:
171 | msgctxt "#32100"
172 | msgid "Welcome to Hyperion Control!"
173 | msgstr "Welcome to Hyperion Control!"
174 |
175 | #:
176 | msgctxt "#32103"
177 | msgid "We found the following Hyperion Server for usage:"
178 | msgstr "We found the following Hyperion Server for usage:"
179 |
180 | #:
181 | msgctxt "#32046"
182 | msgid "Boblight Server"
183 | msgstr "Boblight Server"
184 |
185 | #:
186 | msgctxt "#32047"
187 | msgid "Hyperion"
188 | msgstr "Hyperion"
189 |
190 | #:
191 | msgctxt "#32048"
192 | msgid "Audio Capture"
193 | msgstr "Audio Capture"
194 |
195 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/language/resource.language.es_es/strings.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "MIME-Version: 1.0\n"
4 | "Content-Type: text/plain; charset=UTF-8\n"
5 | "Content-Transfer-Encoding: 8bit\n"
6 | "X-Generator: POEditor.com\n"
7 | "Project-Id-Version: Hyperion Control\n"
8 | "Language: es\n"
9 |
10 | #:
11 | msgctxt "#32000"
12 | msgid "General"
13 | msgstr "General"
14 |
15 | #:
16 | msgctxt "#32001"
17 | msgid "Hyperion IP"
18 | msgstr "IP de Hyperion"
19 |
20 | #:
21 | msgctxt "#32002"
22 | msgid "Hyperion Webserver Port"
23 | msgstr "Puerto del servidor web de Hyperion"
24 |
25 | #:
26 | msgctxt "#32003"
27 | msgid "Switch automatically between 2D/3D"
28 | msgstr "Cambio automático entre 2D/3D"
29 |
30 | #:
31 | msgctxt "#32004"
32 | msgid "Enable Hyperion on startup"
33 | msgstr "Habilitar Hyperion al inicio"
34 |
35 | #:
36 | msgctxt "#32005"
37 | msgid "Disable Hyperion on shutdown"
38 | msgstr "Deshabilitar Hyperion al apagar"
39 |
40 | #:
41 | msgctxt "#32007"
42 | msgid "Show changelog after update"
43 | msgstr "Mostrar el registro de cambios tras la actualización"
44 |
45 | #:
46 | msgctxt "#32008"
47 | msgid "Authorization Token"
48 | msgstr "Token de autorización"
49 |
50 | #:
51 | msgctxt "#32025"
52 | msgid "Component"
53 | msgstr "Componente"
54 |
55 | #:
56 | msgctxt "#32026"
57 | msgid "Enable the chosen component in these situations else disable it."
58 | msgstr "Habilitar el componente elegido en estas situaciones o deshabilitarlo."
59 |
60 | #:
61 | msgctxt "#32027"
62 | msgid "Hyperion component"
63 | msgstr "Componente Hyperion"
64 |
65 | #:
66 | msgctxt "#32028"
67 | msgid "Enable during video playback"
68 | msgstr "Activar durante la reproducción de vídeo"
69 |
70 | #:
71 | msgctxt "#32029"
72 | msgid "Enable during music playback"
73 | msgstr "Activar durante la reproducción de música"
74 |
75 | #:
76 | msgctxt "#32030"
77 | msgid "Enable during player pause"
78 | msgstr "Habilitar durante la pausa del reproductor"
79 |
80 | #:
81 | msgctxt "#32031"
82 | msgid "Enable in Kodi menu"
83 | msgstr "Habilitar en el menú de Kodi"
84 |
85 | #:
86 | msgctxt "#32032"
87 | msgid "Enable during active screensaver"
88 | msgstr "Habilitar durante el salvapantallas activo"
89 |
90 | #:
91 | msgctxt "#32041"
92 | msgid "USB Capture"
93 | msgstr "Captura USB"
94 |
95 | #:
96 | msgctxt "#32042"
97 | msgid "LED Hardware"
98 | msgstr "Hardware LED"
99 |
100 | #:
101 | msgctxt "#32043"
102 | msgid "Smoothing"
103 | msgstr "Suavizado"
104 |
105 | #:
106 | msgctxt "#32045"
107 | msgid "Forwarder"
108 | msgstr "Transmisor"
109 |
110 | #:
111 | msgctxt "#32101"
112 | msgid "Would you like to search for a Hyperion Server and adjust settings?"
113 | msgstr "¿Desea buscar un Servidor Hyperion y ajustar la configuración?"
114 |
115 | #:
116 | msgctxt "#32102"
117 | msgid "Select a Hyperion Server, we found more than one"
118 | msgstr "Seleccione un servidor Hyperion, encontramos más de uno"
119 |
120 | #:
121 | msgctxt "#32104"
122 | msgid "We are sorry, no Hyperion Server has been found. You need to configure IP address and port by hand"
123 | msgstr "Lo sentimos, no se ha encontrado ningún Hyperion Server. Debe configurar la dirección IP y el puerto a mano"
124 |
125 | #:
126 | msgctxt "#32105"
127 | msgid "The Authorization Token isn't valid"
128 | msgstr "Lo sentimos, no se ha encontrado ningún Servidor Hyperion. Es necesario configurar la dirección IP y el puerto a mano"
129 |
130 | #:
131 | msgctxt "#32150"
132 | msgid "Execute"
133 | msgstr "Ejecutar"
134 |
135 | #:
136 | msgctxt "#32151"
137 | msgid "Execute a specific task you might need again.[CR]Select a task from the tasklist and press OK to close the settings dialog."
138 | msgstr "Ejecutar una tarea específica que se pueda necesitar de nuevo.[CR]Seleccionar una tarea de la lista de tareas y pulsar OK para cerrar el diálogo de configuración."
139 |
140 | #:
141 | msgctxt "#32152"
142 | msgid "The addon will handle your request immediately.[CR]Be aware that the addon should be already enabled"
143 | msgstr "El complemento se encargará de la solicitud inmediatamente.[CR]Ten en cuenta que el complemento debe estar ya activado"
144 |
145 | #:
146 | msgctxt "#32153"
147 | msgid "Tasklist"
148 | msgstr "Lista de tareas"
149 |
150 | #:
151 | msgctxt "#32154"
152 | msgid "No Task"
153 | msgstr "Sin Tareas"
154 |
155 | #:
156 | msgctxt "#32155"
157 | msgid "Search: Hyperion Server"
158 | msgstr "Buscar: Servidor Hyperion"
159 |
160 | #:
161 | msgctxt "#32044"
162 | msgid "Blackbar detector"
163 | msgstr "Detección de bordes negros"
164 |
165 | #:
166 | msgctxt "#32040"
167 | msgid "Screen Capture"
168 | msgstr "Captura de Plataforma"
169 |
170 | #:
171 | msgctxt "#32100"
172 | msgid "Welcome to Hyperion Control!"
173 | msgstr "Bienvenidol Control de Hyperion!"
174 |
175 | #:
176 | msgctxt "#32103"
177 | msgid "We found the following Hyperion Server for usage:"
178 | msgstr "Encontramos los siguientes servidores Hyperion para su uso:"
179 |
180 | #:
181 | msgctxt "#32046"
182 | msgid "Boblight Server"
183 | msgstr "Servidor Boblight"
184 |
185 | #:
186 | msgctxt "#32047"
187 | msgid "Hyperion"
188 | msgstr "Hyperion"
189 |
190 | #:
191 | msgctxt "#32048"
192 | msgid "Audio Capture"
193 | msgstr "Captura del Audio"
194 |
195 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/language/resource.language.fr_fr/strings.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "MIME-Version: 1.0\n"
4 | "Content-Type: text/plain; charset=UTF-8\n"
5 | "Content-Transfer-Encoding: 8bit\n"
6 | "X-Generator: POEditor.com\n"
7 | "Project-Id-Version: Hyperion Control\n"
8 | "Language: fr\n"
9 |
10 | #:
11 | msgctxt "#32000"
12 | msgid "General"
13 | msgstr "Général"
14 |
15 | #:
16 | msgctxt "#32001"
17 | msgid "Hyperion IP"
18 | msgstr "Adresse IP Hyperion"
19 |
20 | #:
21 | msgctxt "#32002"
22 | msgid "Hyperion Webserver Port"
23 | msgstr "Port Webserver Hyperion"
24 |
25 | #:
26 | msgctxt "#32003"
27 | msgid "Switch automatically between 2D/3D"
28 | msgstr "Basculer automatiquement entre 2D/3D"
29 |
30 | #:
31 | msgctxt "#32004"
32 | msgid "Enable Hyperion on startup"
33 | msgstr "Activer Hyperion au démarrage"
34 |
35 | #:
36 | msgctxt "#32005"
37 | msgid "Disable Hyperion on shutdown"
38 | msgstr "Désactiver Hyperion à l'extinction"
39 |
40 | #:
41 | msgctxt "#32007"
42 | msgid "Show changelog after update"
43 | msgstr "Afficher le changelog après la mise à jour"
44 |
45 | #:
46 | msgctxt "#32008"
47 | msgid "Authorization Token"
48 | msgstr "Token d'autorisation"
49 |
50 | #:
51 | msgctxt "#32025"
52 | msgid "Component"
53 | msgstr "Composant"
54 |
55 | #:
56 | msgctxt "#32026"
57 | msgid "Enable the chosen component in these situations else disable it."
58 | msgstr "Activer le composant pour ces situations sinon le désactiver."
59 |
60 | #:
61 | msgctxt "#32027"
62 | msgid "Hyperion component"
63 | msgstr "Composant Hyperion"
64 |
65 | #:
66 | msgctxt "#32028"
67 | msgid "Enable during video playback"
68 | msgstr "Activer durant la lecture vidéo"
69 |
70 | #:
71 | msgctxt "#32029"
72 | msgid "Enable during music playback"
73 | msgstr "Activer durant la lecture audio"
74 |
75 | #:
76 | msgctxt "#32030"
77 | msgid "Enable during player pause"
78 | msgstr "Activer pendant la pause du lecteur"
79 |
80 | #:
81 | msgctxt "#32031"
82 | msgid "Enable in Kodi menu"
83 | msgstr "Activer dans le menu de Kodi"
84 |
85 | #:
86 | msgctxt "#32032"
87 | msgid "Enable during active screensaver"
88 | msgstr "Activer quand l'économiseur d'écran est actif"
89 |
90 | #:
91 | msgctxt "#32041"
92 | msgid "USB Capture"
93 | msgstr "Capture USB"
94 |
95 | #:
96 | msgctxt "#32042"
97 | msgid "LED Hardware"
98 | msgstr "LEDs"
99 |
100 | #:
101 | msgctxt "#32043"
102 | msgid "Smoothing"
103 | msgstr "Lissage"
104 |
105 | #:
106 | msgctxt "#32045"
107 | msgid "Forwarder"
108 | msgstr "Forwarder"
109 |
110 | #:
111 | msgctxt "#32101"
112 | msgid "Would you like to search for a Hyperion Server and adjust settings?"
113 | msgstr "Voulez vous chercher un serveur Hyperion et mettre à jour les paramètres ?"
114 |
115 | #:
116 | msgctxt "#32102"
117 | msgid "Select a Hyperion Server, we found more than one"
118 | msgstr "Plusieurs serveurs Hyperion ont été trouvés, veuillez en sélectionner un"
119 |
120 | #:
121 | msgctxt "#32104"
122 | msgid "We are sorry, no Hyperion Server has been found. You need to configure IP address and port by hand"
123 | msgstr "Désolé, aucun serveur Hyperion trouvé. Configurez l'adresse IP et le port manuellement."
124 |
125 | #:
126 | msgctxt "#32105"
127 | msgid "The Authorization Token isn't valid"
128 | msgstr "Le token d'autorisation n'est pas valide"
129 |
130 | #:
131 | msgctxt "#32150"
132 | msgid "Execute"
133 | msgstr "Exécuter"
134 |
135 | #:
136 | msgctxt "#32151"
137 | msgid "Execute a specific task you might need again.[CR]Select a task from the tasklist and press OK to close the settings dialog."
138 | msgstr "Exécuter une tâche spécifique dont vous pourriez avoir besoin à nouveau.\n"
139 | "Choisir une tâche dans la liste et appuyer sur OK pour fermer la boite de dialogue."
140 |
141 | #:
142 | msgctxt "#32152"
143 | msgid "The addon will handle your request immediately.[CR]Be aware that the addon should be already enabled"
144 | msgstr "L'addon va exécuter votre requête immédiatement.\n"
145 | "Attention : l'addon doit déjà être actif"
146 |
147 | #:
148 | msgctxt "#32153"
149 | msgid "Tasklist"
150 | msgstr "Liste de tâches"
151 |
152 | #:
153 | msgctxt "#32154"
154 | msgid "No Task"
155 | msgstr "Pas de tâche"
156 |
157 | #:
158 | msgctxt "#32155"
159 | msgid "Search: Hyperion Server"
160 | msgstr "Recherche : serveur Hyperion"
161 |
162 | #:
163 | msgctxt "#32044"
164 | msgid "Blackbar detector"
165 | msgstr "Détecteur de bandes noires"
166 |
167 | #:
168 | msgctxt "#32040"
169 | msgid "Screen Capture"
170 | msgstr "Capture interne"
171 |
172 | #:
173 | msgctxt "#32100"
174 | msgid "Welcome to Hyperion Control!"
175 | msgstr "Bienvenue dans l'addon Hyperion!"
176 |
177 | #:
178 | msgctxt "#32103"
179 | msgid "We found the following Hyperion Server for usage:"
180 | msgstr "Le serveur Hyperion suivant a été trouvé pour utilisation:"
181 |
182 | #:
183 | msgctxt "#32046"
184 | msgid "Boblight Server"
185 | msgstr "Serveur Boblight"
186 |
187 | #:
188 | msgctxt "#32047"
189 | msgid "Hyperion"
190 | msgstr "Hyperion"
191 |
192 | #:
193 | msgctxt "#32048"
194 | msgid "Audio Capture"
195 | msgstr "Capture Audio"
196 |
197 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/language/resource.language.hu_hu/strings.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "MIME-Version: 1.0\n"
4 | "Content-Type: text/plain; charset=UTF-8\n"
5 | "Content-Transfer-Encoding: 8bit\n"
6 | "X-Generator: POEditor.com\n"
7 | "Project-Id-Version: Hyperion Control\n"
8 | "Language: hu\n"
9 |
10 | #:
11 | msgctxt "#32000"
12 | msgid "General"
13 | msgstr "Általános"
14 |
15 | #:
16 | msgctxt "#32001"
17 | msgid "Hyperion IP"
18 | msgstr "Hyperion IP"
19 |
20 | #:
21 | msgctxt "#32002"
22 | msgid "Hyperion Webserver Port"
23 | msgstr "Hyperion Webserver Port"
24 |
25 | #:
26 | msgctxt "#32003"
27 | msgid "Switch automatically between 2D/3D"
28 | msgstr "Automatikus átkapcsolás 2D/3D"
29 |
30 | #:
31 | msgctxt "#32004"
32 | msgid "Enable Hyperion on startup"
33 | msgstr "Hyperion automatikus indítás"
34 |
35 | #:
36 | msgctxt "#32005"
37 | msgid "Disable Hyperion on shutdown"
38 | msgstr "Hyperion kikapcsolása leállításkor"
39 |
40 | #:
41 | msgctxt "#32007"
42 | msgid "Show changelog after update"
43 | msgstr "Változásnapló megjelenítése frissítés után"
44 |
45 | #:
46 | msgctxt "#32008"
47 | msgid "Authorization Token"
48 | msgstr "Hozzáférési Token"
49 |
50 | #:
51 | msgctxt "#32025"
52 | msgid "Component"
53 | msgstr "Komponens"
54 |
55 | #:
56 | msgctxt "#32026"
57 | msgid "Enable the chosen component in these situations else disable it."
58 | msgstr "Engedélyezze a kiválasztott összetevőt, ellenkező esetben tiltsa le."
59 |
60 | #:
61 | msgctxt "#32027"
62 | msgid "Hyperion component"
63 | msgstr "Hyperion komponens"
64 |
65 | #:
66 | msgctxt "#32028"
67 | msgid "Enable during video playback"
68 | msgstr "Engedélyezés videólejátszás közben"
69 |
70 | #:
71 | msgctxt "#32029"
72 | msgid "Enable during music playback"
73 | msgstr "Engedélyezés zenelejátszás közben"
74 |
75 | #:
76 | msgctxt "#32030"
77 | msgid "Enable during player pause"
78 | msgstr "Engedélyezés lejátszási szünetben"
79 |
80 | #:
81 | msgctxt "#32031"
82 | msgid "Enable in Kodi menu"
83 | msgstr "Engedélyezés a Kodi menüben"
84 |
85 | #:
86 | msgctxt "#32032"
87 | msgid "Enable during active screensaver"
88 | msgstr "Engedélyezés aktív képernyővédő alatt"
89 |
90 | #:
91 | msgctxt "#32041"
92 | msgid "USB Capture"
93 | msgstr "USB Capture"
94 |
95 | #:
96 | msgctxt "#32042"
97 | msgid "LED Hardware"
98 | msgstr "LED Hardver"
99 |
100 | #:
101 | msgctxt "#32043"
102 | msgid "Smoothing"
103 | msgstr "Símítás"
104 |
105 | #:
106 | msgctxt "#32045"
107 | msgid "Forwarder"
108 | msgstr "Továbbító"
109 |
110 | #:
111 | msgctxt "#32101"
112 | msgid "Would you like to search for a Hyperion Server and adjust settings?"
113 | msgstr "Szeretne keresni egy Hyperion szervert és módosítani a beállításokat?"
114 |
115 | #:
116 | msgctxt "#32102"
117 | msgid "Select a Hyperion Server, we found more than one"
118 | msgstr "Válasszon egy Hyperion kiszolgálót, többet is találtunk"
119 |
120 | #:
121 | msgctxt "#32104"
122 | msgid "We are sorry, no Hyperion Server has been found. You need to configure IP address and port by hand"
123 | msgstr "Sajnáljuk, nem található Hyperion Server. Az IP-címet és a portot kézzel kell konfigurálnia!"
124 |
125 | #:
126 | msgctxt "#32105"
127 | msgid "The Authorization Token isn't valid"
128 | msgstr "Az engedélyezési token nem érvényes"
129 |
130 | #:
131 | msgctxt "#32150"
132 | msgid "Execute"
133 | msgstr "Végrehajtás"
134 |
135 | #:
136 | msgctxt "#32151"
137 | msgid "Execute a specific task you might need again.[CR]Select a task from the tasklist and press OK to close the settings dialog."
138 | msgstr "Hajtsa végre újra az adott feladatot, amelyre szüksége lehet.[CR]Válasszon ki egy feladatot a feladatlistából, és nyomja meg az OK gombot a beállítások párbeszédpanel bezárásához."
139 |
140 | #:
141 | msgctxt "#32152"
142 | msgid "The addon will handle your request immediately.[CR]Be aware that the addon should be already enabled"
143 | msgstr "A bővítmény azonnal kezeli a kérést.[CR]Ne feledje, hogy a kiegészítőnek már engedélyezve kell lennie"
144 |
145 | #:
146 | msgctxt "#32153"
147 | msgid "Tasklist"
148 | msgstr "Feladat lista"
149 |
150 | #:
151 | msgctxt "#32154"
152 | msgid "No Task"
153 | msgstr "Nincs feladat"
154 |
155 | #:
156 | msgctxt "#32155"
157 | msgid "Search: Hyperion Server"
158 | msgstr "Keresés: Hyperion Server"
159 |
160 | #:
161 | msgctxt "#32044"
162 | msgid "Blackbar detector"
163 | msgstr "Feketesáv detector"
164 |
165 | #:
166 | msgctxt "#32040"
167 | msgid "Screen Capture"
168 | msgstr "Screen Capture"
169 |
170 | #:
171 | msgctxt "#32100"
172 | msgid "Welcome to Hyperion Control!"
173 | msgstr "Üdvözli a Hyperion Control!"
174 |
175 | #:
176 | msgctxt "#32103"
177 | msgid "We found the following Hyperion Server for usage:"
178 | msgstr "A következő Hyperion szervert találtuk használatra:"
179 |
180 | #:
181 | msgctxt "#32046"
182 | msgid "Boblight Server"
183 | msgstr "Boblight Server"
184 |
185 | #:
186 | msgctxt "#32047"
187 | msgid "Hyperion"
188 | msgstr "Hyperion"
189 |
190 | #:
191 | msgctxt "#32048"
192 | msgid "Audio Capture"
193 | msgstr "Hangrögzítés"
194 |
195 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/language/resource.language.pl_pl/strings.po:
--------------------------------------------------------------------------------
1 | msgid ""
2 | msgstr ""
3 | "MIME-Version: 1.0\n"
4 | "Content-Type: text/plain; charset=UTF-8\n"
5 | "Content-Transfer-Encoding: 8bit\n"
6 | "X-Generator: POEditor.com\n"
7 | "Project-Id-Version: Hyperion Control\n"
8 | "Language: pl\n"
9 |
10 | #:
11 | msgctxt "#32000"
12 | msgid "General"
13 | msgstr "Ogólne"
14 |
15 | #:
16 | msgctxt "#32001"
17 | msgid "Hyperion IP"
18 | msgstr "Hyperion IP"
19 |
20 | #:
21 | msgctxt "#32002"
22 | msgid "Hyperion Webserver Port"
23 | msgstr "Port Hyperion Webserver"
24 |
25 | #:
26 | msgctxt "#32003"
27 | msgid "Switch automatically between 2D/3D"
28 | msgstr "Zmieniaj automatycznie 2D/3D"
29 |
30 | #:
31 | msgctxt "#32004"
32 | msgid "Enable Hyperion on startup"
33 | msgstr "Uruchom Hyperion wraz ze startem systemu"
34 |
35 | #:
36 | msgctxt "#32005"
37 | msgid "Disable Hyperion on shutdown"
38 | msgstr "Wyłącz Hyperion przy zamknięciu systemu"
39 |
40 | #:
41 | msgctxt "#32007"
42 | msgid "Show changelog after update"
43 | msgstr "Pokaż dziennik zmian po aktualizacji"
44 |
45 | #:
46 | msgctxt "#32008"
47 | msgid "Authorization Token"
48 | msgstr "Token autoryzacyjny"
49 |
50 | #:
51 | msgctxt "#32025"
52 | msgid "Component"
53 | msgstr "Komponent"
54 |
55 | #:
56 | msgctxt "#32026"
57 | msgid "Enable the chosen component in these situations else disable it."
58 | msgstr "Włącz wybrany komponent w wybranych sytuacjach - w przeciwnym wypadku wyłącz komponent."
59 |
60 | #:
61 | msgctxt "#32027"
62 | msgid "Hyperion component"
63 | msgstr "Komponent Hyperion"
64 |
65 | #:
66 | msgctxt "#32028"
67 | msgid "Enable during video playback"
68 | msgstr "Włącz podczas odtwarzania wideo"
69 |
70 | #:
71 | msgctxt "#32029"
72 | msgid "Enable during music playback"
73 | msgstr "Włącz podczas odtwarzania muzyki"
74 |
75 | #:
76 | msgctxt "#32030"
77 | msgid "Enable during player pause"
78 | msgstr "Włącz podczas zapauzowania odtwarzacza"
79 |
80 | #:
81 | msgctxt "#32031"
82 | msgid "Enable in Kodi menu"
83 | msgstr "Włącz w menu Kodi"
84 |
85 | #:
86 | msgctxt "#32032"
87 | msgid "Enable during active screensaver"
88 | msgstr "Włącz podczas aktywnego wygaszacza ekranu"
89 |
90 | #:
91 | msgctxt "#32041"
92 | msgid "USB Capture"
93 | msgstr "Przechwytywanie USB"
94 |
95 | #:
96 | msgctxt "#32042"
97 | msgid "LED Hardware"
98 | msgstr "Sprzęt LED"
99 |
100 | #:
101 | msgctxt "#32043"
102 | msgid "Smoothing"
103 | msgstr "Wygładzanie"
104 |
105 | #:
106 | msgctxt "#32045"
107 | msgid "Forwarder"
108 | msgstr "Przekierowywacz"
109 |
110 | #:
111 | msgctxt "#32101"
112 | msgid "Would you like to search for a Hyperion Server and adjust settings?"
113 | msgstr "Czy chcesz wyszukać serwer Hyperion i dostosować ustawienia?"
114 |
115 | #:
116 | msgctxt "#32102"
117 | msgid "Select a Hyperion Server, we found more than one"
118 | msgstr "Wybierz serwer Hyperion, znaleziono więcej niż jeden"
119 |
120 | #:
121 | msgctxt "#32104"
122 | msgid "We are sorry, no Hyperion Server has been found. You need to configure IP address and port by hand"
123 | msgstr "Niestety, nie znaleziono serwera Hyperion. Skonfiguruj adres IP oraz port manualnie"
124 |
125 | #:
126 | msgctxt "#32105"
127 | msgid "The Authorization Token isn't valid"
128 | msgstr "Nieprawidłowy token autoryzacyjny"
129 |
130 | #:
131 | msgctxt "#32150"
132 | msgid "Execute"
133 | msgstr "Wykonaj"
134 |
135 | #:
136 | msgctxt "#32151"
137 | msgid "Execute a specific task you might need again.[CR]Select a task from the tasklist and press OK to close the settings dialog."
138 | msgstr "Wykonaj zadanie ponownie. Wybierz zadanie z listy zadań i wciśnij OK aby zamknąć okno ustawień."
139 |
140 | #:
141 | msgctxt "#32152"
142 | msgid "The addon will handle your request immediately.[CR]Be aware that the addon should be already enabled"
143 | msgstr "Twoje zadanie zostanie wykonane natychmiastowo. Miej na uwadze, że addon powinien być wcześniej włączony"
144 |
145 | #:
146 | msgctxt "#32153"
147 | msgid "Tasklist"
148 | msgstr "Lista zadań"
149 |
150 | #:
151 | msgctxt "#32154"
152 | msgid "No Task"
153 | msgstr "Brak zadania"
154 |
155 | #:
156 | msgctxt "#32155"
157 | msgid "Search: Hyperion Server"
158 | msgstr "Wyszukaj: Serwer Hyperion"
159 |
160 | #:
161 | msgctxt "#32044"
162 | msgid "Blackbar detector"
163 | msgstr "Detekcja czarnych ramek"
164 |
165 | #:
166 | msgctxt "#32040"
167 | msgid "Screen Capture"
168 | msgstr "Przechwytywanie platformy"
169 |
170 | #:
171 | msgctxt "#32100"
172 | msgid "Welcome to Hyperion Control!"
173 | msgstr "Witaj w Hyperion Control!"
174 |
175 | #:
176 | msgctxt "#32103"
177 | msgid "We found the following Hyperion Server for usage:"
178 | msgstr "Znaleziono następujący serwer Hyperion:"
179 |
180 | #:
181 | msgctxt "#32046"
182 | msgid "Boblight Server"
183 | msgstr "Server Boblight"
184 |
185 | #:
186 | msgctxt "#32047"
187 | msgid "Hyperion"
188 | msgstr "Hyperion"
189 |
190 | #:
191 | msgctxt "#32048"
192 | msgid "Audio Capture"
193 | msgstr "Przechwytywanie Dźwięku"
194 |
195 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/lib/__init__.py:
--------------------------------------------------------------------------------
1 | """Hyperion control addon library."""
2 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/lib/api_client.py:
--------------------------------------------------------------------------------
1 | """Hyperion JSON RPC/HTTP(S) API client."""
2 | from __future__ import annotations
3 |
4 | import contextlib
5 | import random
6 | import string
7 | from json import JSONDecodeError
8 | from typing import Any
9 |
10 | import requests
11 | from requests.exceptions import ConnectTimeout
12 |
13 | from resources.lib.interfaces import GuiHandler
14 | from resources.lib.interfaces import Logger
15 | from resources.lib.interfaces import SettingsManager
16 |
17 |
18 | class ApiClient:
19 | """Manages the request to the hyperion server."""
20 |
21 | def __init__(
22 | self, logger: Logger, gui: GuiHandler, settings: SettingsManager
23 | ) -> None:
24 | self._settings = settings
25 | self._logger = logger
26 | self._gui = gui
27 | self._session = requests.Session()
28 |
29 | @property
30 | def headers(self) -> dict[str, str]:
31 | """Request headers."""
32 | headers = {"Content-type": "application/json"}
33 | if self._settings.auth_token:
34 | headers["Authorization"] = f"token {self._settings.auth_token}"
35 | return headers
36 |
37 | def _send(
38 | self, body: dict[str, Any], timeout: float = 0.5
39 | ) -> dict[str, Any] | None:
40 | url = self._settings.base_url
41 | logger = self._logger
42 | logger.log(f"Send to: {url} payload: {body}")
43 | with contextlib.suppress(ConnectTimeout, JSONDecodeError):
44 | response = self._session.post(
45 | url, json=body, headers=self.headers, timeout=timeout
46 | )
47 | json_content = response.json()
48 | if json_content.get("success"):
49 | return json_content.get("info")
50 | if json_content["error"] == "No Authorization":
51 | self._gui.notify_text("Error: No Authorization, API Token required")
52 | logger.error(json_content["error"])
53 | return None
54 |
55 | def needs_auth(self) -> bool:
56 | """Whether the hyperion server needs API authentication."""
57 | if res := self._send({"command": "authorize", "subcommand": "tokenRequired"}):
58 | return res["required"]
59 | return False
60 |
61 | def get_token(self) -> str:
62 | """Requests the authentication token."""
63 | pool = string.ascii_uppercase + string.ascii_lowercase + string.digits
64 | control_code = "".join(random.choice(pool) for _ in range(16))
65 | message = {
66 | "command": "authorize",
67 | "subcommand": "requestToken",
68 | "comment": "Kodi Hyperion Control",
69 | "id": control_code,
70 | }
71 | return res["token"] if (res := self._send(message, timeout=180)) else ""
72 |
73 | def send_component_state(self, component: str, state: bool) -> None:
74 | """Sends the component state."""
75 | body = {
76 | "command": "componentstate",
77 | "componentstate": {"component": component, "state": state},
78 | }
79 | if component == "FORWARDER":
80 | self.switch_to_instance(0)
81 | self._send(body)
82 | else:
83 | self.send_to_all_instances(body)
84 |
85 | def send_video_mode(self, mode: str) -> None:
86 | """Sends the current video mode."""
87 | self._send({"command": "videoMode", "videoMode": mode})
88 |
89 | def get_server_info(self) -> dict[str, Any] | None:
90 | """Gets the server info."""
91 | return self._send({"command": "serverinfo"})
92 |
93 | def get_instances(self) -> list[dict[str, Any]]:
94 | """Gets the server info."""
95 | server_info = self.get_server_info()
96 | return server_info["instance"] if server_info else []
97 |
98 | def switch_to_instance(self, instance_num: int) -> None:
99 | """Switches to the specified instance."""
100 | self._send(
101 | {"command": "instance", "subcommand": "switchTo", "instance": instance_num}
102 | )
103 |
104 | def send_to_all_instances(self, body: dict[str, Any]) -> None:
105 | """Sends a command to all instances."""
106 | for instance in self.get_instances():
107 | self.switch_to_instance(instance["instance"])
108 | self._send(body)
109 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/lib/gui.py:
--------------------------------------------------------------------------------
1 | """Kodi GUI handler."""
2 | from __future__ import annotations
3 |
4 | import xbmcaddon
5 | import xbmcgui
6 |
7 | from resources.lib import ssdp
8 | from resources.lib.interfaces import SettingsManager
9 |
10 |
11 | class GuiHandler:
12 | """Kodi GUI handler."""
13 |
14 | def __init__(
15 | self, addon: xbmcaddon.Addon, settings_manager: SettingsManager
16 | ) -> None:
17 | self._addon = addon
18 | self._settings = settings_manager
19 | self._dialog = xbmcgui.Dialog() # TODO: DI with embedded getlocalizedstring
20 | self._addon_name = addon.getAddonInfo("name")
21 | self._addon_icon = addon.getAddonInfo("icon")
22 |
23 | def _get_localized_string(self, label_id: int) -> str:
24 | """Returns the localized string of a label ID."""
25 | return self._addon.getLocalizedString(label_id)
26 |
27 | def notify_label(self, label_id: int) -> None:
28 | """Displays a notification with the localized message."""
29 | message = self._get_localized_string(label_id)
30 | self.notify_text(message, time=1000, icon=self._addon_icon)
31 |
32 | def notify_text(
33 | self, message: str, time: int = 3000, icon: str = xbmcgui.NOTIFICATION_INFO
34 | ) -> None:
35 | """Displays a notification."""
36 | self._dialog.notification(self._addon_name, message, icon, time)
37 |
38 | def do_ssdp_discovery(self) -> None:
39 | """Perform the SSDP discovery and lets the user choose the service."""
40 | servers = ssdp.discover()
41 |
42 | if not servers:
43 | self._dialog.ok("Hyperion Control", self._get_localized_string(32104))
44 | return
45 | # if there is more than one entry the user should select one
46 | if len(servers) > 1:
47 | selection_idx = self._dialog.select(
48 | self._get_localized_string(32102), build_select_list(servers)
49 | )
50 | selected_server = servers[selection_idx] if selection_idx > -1 else None
51 | else:
52 | selected_server = servers[0]
53 | self._dialog.ok(
54 | "Hyperion Control",
55 | f'{self._get_localized_string(32103)}[CR]{selected_server["ip"]}:{selected_server["port"]}',
56 | )
57 |
58 | if selected_server:
59 | self._settings.address = selected_server["ip"]
60 | self._settings.port = selected_server["port"]
61 |
62 | def do_initial_wizard(self) -> None:
63 | """Displays the initial wizard."""
64 | if self._dialog.yesno(
65 | "Hyperion Control",
66 | f"{self._get_localized_string(32100)}[CR]{self._get_localized_string(32101)}",
67 | ):
68 | self.do_ssdp_discovery()
69 | self._addon.openSettings()
70 |
71 | def do_changelog_display(self) -> None:
72 | """Displays the changelog."""
73 | if self._settings.current_version != self._addon.getAddonInfo("version"):
74 | self._dialog.textviewer(
75 | "Hyperion Control - Changelog", self._addon.getAddonInfo("changelog")
76 | )
77 |
78 |
79 | def build_select_list(data: list[dict[str, str | int | None]]) -> list[str]:
80 | return [f"{item['ip']}:{item['port']}" for item in data]
81 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/lib/hyperion.py:
--------------------------------------------------------------------------------
1 | """Hyperion Controller."""
2 | from __future__ import annotations
3 |
4 | from collections.abc import Callable
5 |
6 | from resources.lib.interfaces import ApiClient
7 | from resources.lib.interfaces import GuiHandler
8 | from resources.lib.interfaces import Logger
9 | from resources.lib.interfaces import SettingsManager
10 |
11 |
12 | class Hyperion:
13 | """Main instance class."""
14 |
15 | def __init__(
16 | self,
17 | settings_manager: SettingsManager,
18 | logger: Logger,
19 | gui_handler: GuiHandler,
20 | api_client: ApiClient,
21 | get_video_mode_function: Callable[[], str],
22 | addon_version: str,
23 | ) -> None:
24 | self._prev_video_mode = "2D"
25 |
26 | self._addon_version = addon_version
27 | self._client = api_client
28 | self._settings = settings_manager
29 | self._logger = logger
30 | self._gui = gui_handler
31 | self._video_mode_fn = get_video_mode_function
32 |
33 | self._kodi_state: str = "menu"
34 | self._prev_comp_state: bool | None = None
35 | self._initialize()
36 |
37 | def _initialize(self) -> None:
38 | # check for changelog display, but not on first run
39 | settings = self._settings
40 | if settings.should_display_changelog:
41 | self._gui.do_changelog_display()
42 |
43 | # check for setup wizard
44 | if settings.first_run:
45 | # be sure to fill in the current version
46 | settings.set_addon_version(self._addon_version)
47 | self._gui.do_initial_wizard()
48 |
49 | if settings.enable_hyperion:
50 | self._client.send_component_state("ALL", True)
51 | settings.set_first_run_done()
52 |
53 | def notify(self, command: str) -> None:
54 | """Process the commands sent by the observables."""
55 | self._logger.log(f"received command: {command}")
56 | if command == "updateSettings":
57 | self.update_settings()
58 | else:
59 | self._kodi_state = command
60 | self._update_state()
61 |
62 | def update_settings(self) -> None:
63 | """Update the settings."""
64 | settings = self._settings
65 | settings.read_settings()
66 |
67 | auth_token = settings.auth_token
68 | if auth_token and len(auth_token) != 36:
69 | self._gui.notify_label(32105)
70 |
71 | # Checkout Tasklist for pending tasks
72 | if settings.tasks == 1:
73 | settings.set_tasks(0)
74 | self._gui.do_ssdp_discovery()
75 |
76 | def _update_state(self) -> None:
77 | comp_state = self._get_comp_state()
78 | if self._prev_comp_state != comp_state:
79 | self._client.send_component_state(self._settings.target_comp, comp_state)
80 | self._prev_comp_state = comp_state
81 |
82 | # update stereoscopic mode always, better apis for detection available?
83 | # Bug: race condition, return of jsonapi has wrong gui state
84 | # after onPlayBackStopped after a 3D movie
85 | if self._settings.video_mode_enabled:
86 | new_mode = self._video_mode_fn()
87 | if self._prev_video_mode != new_mode:
88 | self._client.send_video_mode(new_mode)
89 | self._prev_video_mode = new_mode
90 |
91 | def _get_comp_state(self) -> bool:
92 | """Get the desired state of the target component based on kodi state."""
93 | settings = self._settings
94 | state = self._kodi_state
95 | if state == "screensaver":
96 | return settings.screensaver_enabled
97 | if state == "pause":
98 | return settings.pause_enabled
99 | if state == "playAudio":
100 | return settings.audio_enabled
101 | if state == "playVideo":
102 | return settings.video_enabled
103 | return settings.menu_enabled
104 |
105 | def stop(self) -> None:
106 | """Stops the hyperion control."""
107 | if self._settings.disable_hyperion:
108 | self._client.send_component_state("ALL", False)
109 | self._logger.log("Hyperion-control stopped")
110 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/lib/interfaces.py:
--------------------------------------------------------------------------------
1 | """Observer interface."""
2 | from typing import Protocol
3 |
4 |
5 | class Observer(Protocol):
6 | """Observer interface."""
7 |
8 | def notify(self, command: str) -> None:
9 | """Process the received command."""
10 | pass
11 |
12 |
13 | class Logger(Protocol):
14 | """Logger interface."""
15 |
16 | def log(self, message: str, level: int = 0) -> None:
17 | """Logs a message."""
18 |
19 | def debug(self, message: str) -> None:
20 | """Writes a debug message to the log."""
21 |
22 | def info(self, message: str) -> None:
23 | """Writes an info message to the log."""
24 |
25 | def error(self, message: str) -> None:
26 | """Writes an error message to the log."""
27 |
28 |
29 | class SettingsManager(Protocol):
30 | """Settings manager interface."""
31 |
32 | auth_token: str
33 | current_version: str
34 | address: str
35 | port: int
36 | video_mode_enabled: bool
37 | enable_hyperion: bool
38 | disable_hyperion: bool
39 | target_comp: str
40 | screensaver_enabled: bool
41 | video_enabled: bool
42 | audio_enabled: bool
43 | pause_enabled: bool
44 | menu_enabled: bool
45 | show_changelog_on_update: bool
46 | tasks: int
47 | first_run: bool
48 |
49 | @property
50 | def should_display_changelog(self) -> bool:
51 | """Whether the changelog should be displayed."""
52 | pass
53 |
54 | @property
55 | def base_url(self) -> str:
56 | """Hyperion server JSON RPC base url."""
57 | pass
58 |
59 | def set_tasks(self, value: int) -> None:
60 | """Sets the tasks to run."""
61 |
62 | def set_addon_version(self, value: str) -> None:
63 | """Sets the current addon version for changelog checks."""
64 |
65 | def set_first_run_done(self) -> None:
66 | """Sets the first run settings to false."""
67 |
68 | def read_settings(self) -> None:
69 | """Read all settings."""
70 |
71 |
72 | class GuiHandler(Protocol):
73 | """GUI handler interface."""
74 |
75 | def notify_label(self, label_id: int) -> None:
76 | """Displays a notification with the localized message."""
77 |
78 | def notify_text(self, message: str, time: int = 3000, icon: str = "info") -> None:
79 | """Displays a notification."""
80 |
81 | def do_ssdp_discovery(self) -> None:
82 | """Perform the SSDP discovery and lets the user choose the service."""
83 |
84 | def do_initial_wizard(self) -> None:
85 | """Displays the initial wizard."""
86 |
87 | def do_changelog_display(self) -> None:
88 | """Displays the changelog."""
89 |
90 |
91 | class ApiClient(Protocol):
92 | """API client interface."""
93 |
94 | def send_component_state(self, component: str, state: bool) -> None:
95 | """Sends the component state."""
96 |
97 | def send_video_mode(self, mode: str) -> None:
98 | """Sends the current video mode."""
99 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/lib/logger.py:
--------------------------------------------------------------------------------
1 | """Logging facility."""
2 | import xbmc
3 |
4 |
5 | class Logger:
6 | """Logging facility for Kodi add-ons."""
7 |
8 | def __init__(self, addon_name: str) -> None:
9 | self._addon_name = addon_name
10 |
11 | def log(self, message: str, level: int = xbmc.LOGDEBUG) -> None:
12 | """Writes the message to the logger with the addon name as prefix."""
13 | xbmc.log(f"[{self._addon_name}] - {message}", level=level)
14 |
15 | def debug(self, message: str) -> None:
16 | """Writes a debug message to the log."""
17 | self.log(message)
18 |
19 | def info(self, message: str) -> None:
20 | """Writes an info message to the log."""
21 | self.log(message, level=xbmc.LOGINFO)
22 |
23 | def error(self, message: str) -> None:
24 | """Writes an error message to the log."""
25 | self.log(message, level=xbmc.LOGERROR)
26 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/lib/monitor.py:
--------------------------------------------------------------------------------
1 | """Observable monitor."""
2 | from __future__ import annotations
3 |
4 | import xbmc
5 |
6 | from resources.lib.interfaces import Observer
7 |
8 |
9 | class XBMCMonitor(xbmc.Monitor):
10 | """xbmc monitor class."""
11 |
12 | def __init__(self) -> None:
13 | super().__init__()
14 | self._observers: list[Observer] = []
15 |
16 | def register_observer(self, observer: Observer) -> None:
17 | """Register an observer to the events."""
18 | self._observers.append(observer)
19 |
20 | def notify_observers(self, command: str) -> None:
21 | """Sends the command to the observers."""
22 | for observer in self._observers:
23 | observer.notify(command)
24 |
25 | def onSettingsChanged(self) -> None:
26 | """Settings changed event."""
27 | self.notify_observers("updateSettings")
28 |
29 | def onScreensaverActivated(self) -> None:
30 | """Screensaver activated event."""
31 | self.notify_observers("screensaver")
32 |
33 | def onScreensaverDeactivated(self) -> None:
34 | """Screensaver deactivated event."""
35 | self.notify_observers("menu")
36 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/lib/player.py:
--------------------------------------------------------------------------------
1 | """Observable player."""
2 | from __future__ import annotations
3 |
4 | import xbmc
5 |
6 | from resources.lib.interfaces import Observer
7 |
8 |
9 | class Player(xbmc.Player):
10 | """xbmc player class."""
11 |
12 | def __init__(self) -> None:
13 | super().__init__()
14 | self._observers: list[Observer] = []
15 |
16 | def register_observer(self, observer: Observer) -> None:
17 | """Register an observer to the events."""
18 | self._observers.append(observer)
19 |
20 | def notify_observers(self, command: str) -> None:
21 | """Sends the command to the observers."""
22 | for observer in self._observers:
23 | observer.notify(command)
24 |
25 | def onPlayBackPaused(self) -> None:
26 | """Playback paused event."""
27 | self.notify_observers("pause")
28 |
29 | def onPlayBackResumed(self) -> None:
30 | """Playback resumed event."""
31 | self._play_handler()
32 |
33 | def onAVStarted(self) -> None:
34 | """Audio or Video started event."""
35 | self._play_handler()
36 |
37 | def onPlayBackStopped(self) -> None:
38 | """Playback stopped event."""
39 | self.notify_observers("menu")
40 |
41 | def onPlayBackEnded(self) -> None:
42 | """Playback end event."""
43 | self.notify_observers("menu")
44 |
45 | def _play_handler(self) -> None:
46 | command = "playAudio" if self.isPlayingAudio() else "playVideo"
47 | self.notify_observers(command)
48 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/lib/settings_manager.py:
--------------------------------------------------------------------------------
1 | """Settings manager."""
2 | from __future__ import annotations
3 |
4 | from typing import TYPE_CHECKING
5 | from typing import Any
6 |
7 | from resources.lib.interfaces import Logger
8 |
9 | if TYPE_CHECKING:
10 | import xbmcaddon
11 |
12 |
13 | INT_TO_COMP_STRING = {
14 | 0: "GRABBER",
15 | 1: "V4L",
16 | 2: "AUDIO",
17 | 3: "LEDDEVICE",
18 | 4: "SMOOTHING",
19 | 5: "BLACKBORDER",
20 | 6: "FORWARDER",
21 | 7: "BOBLIGHTSERVER",
22 | 8: "ALL",
23 | }
24 |
25 |
26 | class SettingsManager:
27 | """Class which contains all addon settings."""
28 |
29 | def __init__(
30 | self, settings: xbmcaddon.Settings, logger: Logger, addon: xbmcaddon.Addon
31 | ) -> None:
32 | self._logger = logger
33 | self.rev = 0
34 | self._settings = settings
35 | self._addon = addon
36 | self.current_version: str = ""
37 | self._address: str = "localhost"
38 | self._port: int = 8090
39 | self._base_url = "http://localhost:8090/json-rpc"
40 | self.video_mode_enabled: bool
41 | self.enable_hyperion: bool
42 | self.disable_hyperion: bool
43 | self.auth_token: str
44 | self.target_comp: str
45 | self.screensaver_enabled: bool
46 | self.video_enabled: bool
47 | self.audio_enabled: bool
48 | self.pause_enabled: bool
49 | self.menu_enabled: bool
50 | self.show_changelog_on_update: bool
51 | self.tasks: int
52 | self.first_run: bool
53 | self.read_settings()
54 |
55 | @property
56 | def address(self) -> str:
57 | """Hyperion server address."""
58 | return self._address
59 |
60 | @address.setter
61 | def address(self, value: str) -> None:
62 | """Hyperion server address."""
63 | self._address = value
64 | self._update_url()
65 | self._set_string("ip", value)
66 |
67 | @property
68 | def port(self) -> int:
69 | """Hyperion server port."""
70 | return self._port
71 |
72 | @port.setter
73 | def port(self, value: int) -> None:
74 | """Hyperion server port."""
75 | self._port = value
76 | self._update_url()
77 | self._set_int("port", value)
78 |
79 | def _update_url(self) -> None:
80 | self._base_url = f"http://{self._address}:{self._port}/json-rpc"
81 |
82 | @property
83 | def base_url(self) -> str:
84 | """Hyperion server JSON RPC base url."""
85 | return self._base_url
86 |
87 | @property
88 | def should_display_changelog(self) -> bool:
89 | """Whether the changelog should be displayed."""
90 | return self.show_changelog_on_update and not self.first_run
91 |
92 | def _set_string(self, name: str, value: str) -> None:
93 | self._addon.setSettingString(name, value)
94 | outcome = value == self._settings.getString(name)
95 | self._log_set_outcome(name, value, outcome)
96 |
97 | def _set_int(self, name: str, value: int) -> None:
98 | self._addon.setSettingInt(name, value)
99 | outcome = value == self._settings.getInt(name)
100 | self._log_set_outcome(name, value, outcome)
101 |
102 | def _set_bool(self, name: str, value: bool) -> None:
103 | self._addon.setSettingBool(name, value)
104 | outcome = value == self._settings.getBool(name)
105 | self._log_set_outcome(name, value, outcome)
106 |
107 | def _log_set_outcome(self, name: str, value: Any, outcome: bool) -> None:
108 | if not outcome:
109 | self._logger.error(f"Error setting {name} to {value} (outcome={outcome})")
110 | else:
111 | self._logger.log(f"Set {name} to {value}")
112 |
113 | def set_tasks(self, value: int) -> None:
114 | """Sets the tasks to run."""
115 | self._set_int("tasks", value)
116 | self.tasks = value
117 |
118 | def set_addon_version(self, value: str) -> None:
119 | """Sets the current addon version for changelog checks."""
120 | self._set_string("currAddonVersion", value)
121 | self.current_version = value
122 |
123 | def set_first_run_done(self) -> None:
124 | """Sets the first run settings to false."""
125 | self._set_bool("firstRun", False)
126 | self.first_run = False
127 |
128 | def read_settings(self) -> None:
129 | """Read all settings."""
130 | settings = self._settings
131 | get_bool = settings.getBool
132 | get_string = settings.getString
133 | get_int = settings.getInt
134 | self._address = get_string("ip")
135 | self._port = get_int("port")
136 | self._update_url()
137 | self.auth_token = get_string("authToken")
138 | self.video_mode_enabled = get_bool("videoModeEnabled")
139 | self.enable_hyperion = get_bool("enableHyperion")
140 | self.disable_hyperion = get_bool("disableHyperion")
141 | self.show_changelog_on_update = get_bool("showChangelogOnUpdate")
142 | self.first_run = get_bool("firstRun")
143 | self.current_version = get_string("currAddonVersion")
144 |
145 | self.target_comp = INT_TO_COMP_STRING.get(
146 | get_int("targetComponent"), "NOT_FOUND"
147 | )
148 | self.video_enabled = get_bool("videoEnabled")
149 | self.audio_enabled = get_bool("audioEnabled")
150 | self.pause_enabled = get_bool("pauseEnabled")
151 | self.menu_enabled = get_bool("menuEnabled")
152 | self.screensaver_enabled = get_bool("screensaverEnabled")
153 | self.tasks = get_int("tasks")
154 | self.rev += 1
155 |
156 | self._log_settings()
157 |
158 | def _log_settings(self) -> None:
159 | log = self._logger.log
160 | log("Settings updated!")
161 | log(f"Hyperion ip: {self.address}")
162 | log(f"Hyperion port: {self.port}")
163 | log(f"Enable H on start: {self.enable_hyperion}")
164 | log(f"Disable H on stop: {self.disable_hyperion}")
165 | log(f"VideoMode enabled: {self.video_mode_enabled}")
166 | log(f"Hyperion target comp: {self.target_comp}")
167 | log(f"Screensaver enabled: {self.screensaver_enabled}")
168 | log(f"Video enabled: {self.video_enabled}")
169 | log(f"Audio enabled: {self.audio_enabled}")
170 | log(f"Pause enabled: {self.pause_enabled}")
171 | log(f"Menu enabled: {self.menu_enabled}")
172 | log(f"ChangelogOnUpdate: {self.show_changelog_on_update}")
173 | log(f"tasks: {self.tasks}")
174 | log(f"first run: {self.first_run}")
175 | log(f"current version: {self.current_version}")
176 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/lib/ssdp.py:
--------------------------------------------------------------------------------
1 | """
2 | Hyperion UPnP / SSDP service discovery.
3 |
4 | Copyright 2014 Dan Krause
5 | Copyright 2023 Andrea Ghensi
6 |
7 | Licensed under the Apache License, Version 2.0 (the "License");
8 | you may not use this file except in compliance with the License.
9 | You may obtain a copy of the License at
10 |
11 | http://www.apache.org/licenses/LICENSE-2.0
12 |
13 | Unless required by applicable law or agreed to in writing, software
14 | distributed under the License is distributed on an "AS IS" BASIS,
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | See the License for the specific language governing permissions and
17 | limitations under the License.
18 |
19 | All credits to Dan Krause at Github: https://gist.github.com/dankrause/6000248
20 | """
21 | from __future__ import annotations
22 |
23 | import http.client
24 | import socket
25 | from io import BytesIO
26 | from typing import Any
27 |
28 |
29 | class _FakeSocket(BytesIO):
30 | """Fake socket to make ssdp response compatible with HTTPResponse."""
31 |
32 | def makefile(self, *_args: Any, **_kw: Any) -> BytesIO:
33 | """Duck-types the call to make the socket available."""
34 | return self
35 |
36 |
37 | def _parse_location(location: str | None) -> tuple[str, int]:
38 | if location is None:
39 | return "", 0
40 | start = location.find("//") + 2
41 | end = location.rfind("/")
42 | hostname, port = location[start:end].split(":")
43 | return hostname, int(port)
44 |
45 |
46 | class SSDPResponse:
47 | """
48 | Response from SSDP discovery.
49 |
50 | Typical Hyperion response:
51 |
52 | CACHE-CONTROL: max-age = 1800
53 | DATE: Sun, 23 Jul 2023 19:58:01 G7T19444
54 | EXT:
55 | LOCATION: http://192.168.2.180:8090/description.xml
56 | SERVER: LibreELEC (official): 11.0.1/11.0 UPnP/1.0 Hyperion/2.0.16-beta.1+PR1617
57 | ST: urn:hyperion-project.org:device:basic:1
58 | USN: uuid:04928741-2192-5c24-93e6-638c9a184443
59 | HYPERION-FBS-PORT: 19400
60 | HYPERION-JSS-PORT: 19444
61 | HYPERION-NAME: My Hyperion Config
62 | """
63 |
64 | def __init__(self, response: bytes) -> None:
65 | r = http.client.HTTPResponse(_FakeSocket(response)) # type: ignore
66 | r.begin()
67 | hostname, port = _parse_location(r.getheader("location"))
68 | self.hostname = hostname
69 | self.port = port
70 | self.usn = r.getheader("usn")
71 | self.st = r.getheader("st")
72 | cache = r.getheader("cache-control")
73 | self.cache = cache.split("=")[1] if cache else ""
74 |
75 |
76 | def discover(timeout: int = 3, retries: int = 1, mx: int = 2) -> list[dict[str, Any]]:
77 | service = "urn:hyperion-project.org:device:basic:1"
78 | group = ("239.255.255.250", 1900)
79 | lines = [
80 | "M-SEARCH * HTTP/1.1",
81 | f"HOST: {group[0]}:{group[1]}",
82 | 'MAN: "ssdp:discover"',
83 | f"ST: {service}",
84 | f"MX: {mx}",
85 | "",
86 | "",
87 | ]
88 | message = "\r\n".join(lines).encode("utf-8")
89 | socket.setdefaulttimeout(timeout)
90 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
91 | sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
92 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
93 | responses = []
94 | for _ in range(retries):
95 | sock.sendto(message, group)
96 | while True:
97 | try:
98 | res_data = SSDPResponse(sock.recv(1024))
99 | if res_data.st != service:
100 | continue
101 | responses.append(
102 | {
103 | "ip": res_data.hostname,
104 | "port": res_data.port,
105 | "usn": res_data.usn,
106 | }
107 | )
108 | except socket.timeout:
109 | break
110 | sock.close()
111 | return responses
112 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/lib/utils.py:
--------------------------------------------------------------------------------
1 | """Stereoscopic mode detection."""
2 | import json
3 |
4 | import xbmc
5 |
6 | from resources.lib.interfaces import Logger
7 |
8 |
9 | def get_stereoscopic_mode(logger: Logger) -> str:
10 | """Returns the currently active stereoscopic mode."""
11 | msg = {
12 | "jsonrpc": "2.0",
13 | "method": "GUI.GetProperties",
14 | "params": {"properties": ["stereoscopicmode"]},
15 | "id": 669,
16 | }
17 | try:
18 | response = json.loads(xbmc.executeJSONRPC(json.dumps(msg)))
19 | mode = response["result"]["stereoscopicmode"]["mode"]
20 | except Exception:
21 | logger.error("Error executing JSONRPC call")
22 | return "2D"
23 | if mode == "split_vertical":
24 | return "3DSBS"
25 | return "3DTAB" if mode == "split_horizontal" else "2D"
26 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/resources/screenshot-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperion-project/hyperion.control/931034fbbb7b35a2aa1eeb4aec3c547fe3cd1fae/script.service.hyperion-control/resources/screenshot-01.png
--------------------------------------------------------------------------------
/script.service.hyperion-control/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 |
--------------------------------------------------------------------------------
/script.service.hyperion-control/service.py:
--------------------------------------------------------------------------------
1 | """Hyperion control addon entrypoint."""
2 | import xbmcaddon
3 | from resources.lib.api_client import ApiClient
4 | from resources.lib.gui import GuiHandler
5 | from resources.lib.hyperion import Hyperion
6 | from resources.lib.logger import Logger
7 | from resources.lib.monitor import XBMCMonitor
8 | from resources.lib.player import Player
9 | from resources.lib.settings_manager import SettingsManager
10 | from resources.lib.utils import get_stereoscopic_mode
11 |
12 | ADDON_NAME = "script.service.hyperion-control"
13 |
14 |
15 | def main() -> None:
16 | addon = xbmcaddon.Addon(ADDON_NAME)
17 | logger = Logger(addon.getAddonInfo("name"))
18 | settings_manager = SettingsManager(addon.getSettings(), logger, addon)
19 | gui_handler = GuiHandler(addon, settings_manager)
20 | api_client = ApiClient(logger, gui_handler, settings_manager)
21 |
22 | def get_video_mode_fn() -> str:
23 | return get_stereoscopic_mode(logger)
24 |
25 | hyperion = Hyperion(
26 | settings_manager,
27 | logger,
28 | gui_handler,
29 | api_client,
30 | get_video_mode_fn,
31 | addon.getAddonInfo("version"),
32 | )
33 | player = Player()
34 | player.register_observer(hyperion)
35 | monitor = XBMCMonitor()
36 | monitor.register_observer(hyperion)
37 |
38 | while not monitor.abortRequested():
39 | if monitor.waitForAbort(10):
40 | hyperion.stop()
41 | break
42 |
43 |
44 | if __name__ == "__main__":
45 | main()
46 |
--------------------------------------------------------------------------------