Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ad76560ed | ||
|
|
d6f449930c | ||
|
|
ebdf501115 | ||
|
|
34861fb9b9 | ||
|
|
2f3fe8ae6b | ||
|
|
4ee91654ea | ||
|
|
3190658e0c | ||
|
|
c330523b9e | ||
|
|
5a5c865135 | ||
|
|
f5ae301c97 | ||
|
|
d910441470 | ||
|
|
b667bf4117 | ||
|
|
4d8b4c9d43 | ||
|
|
02c44eef82 | ||
|
|
c21d10d7f9 | ||
|
|
d2d14e4c19 | ||
|
|
1da8bca9d1 | ||
|
|
dffbfd8860 | ||
|
|
7247c51b10 | ||
|
|
2517e30f55 | ||
|
|
caa0e47399 | ||
|
|
8ce6ec49e1 | ||
|
|
276b18650c | ||
|
|
a69a8a269f | ||
|
|
921628b2a1 | ||
|
|
53225b427c | ||
|
|
6aad1eb92d | ||
|
|
d4bccb9b05 | ||
|
|
02e9886e2e | ||
|
|
a05363d202 | ||
|
|
78fd1e206b | ||
|
|
74951b9dec | ||
|
|
5ac21ea240 | ||
|
|
086e92da4b | ||
|
|
e54c4c1bec | ||
|
|
5bb20e482f | ||
|
|
569462f755 | ||
|
|
658050548c | ||
|
|
4d635f6eb4 | ||
|
|
a0b1e9177b | ||
|
|
7a1a7843e6 | ||
|
|
66d4e02024 | ||
|
|
04a46f0cd9 | ||
|
|
4e0ac5330f | ||
|
|
32288aba97 | ||
|
|
801f119a5c | ||
|
|
567a02872b | ||
|
|
cfc19cafc5 | ||
|
|
44823f5043 | ||
|
|
bad5f8e561 | ||
|
|
075e1e8974 | ||
|
|
06f78ce620 | ||
|
|
cce9acb182 | ||
|
|
048b8f0385 |
2
.github/tools/reformat_changelog.py
vendored
2
.github/tools/reformat_changelog.py
vendored
@@ -23,7 +23,7 @@ class SectionType(TypedDict):
|
||||
|
||||
def reformat(item_format: str, output_emoji: bool) -> None:
|
||||
data = [
|
||||
emojize(x.strip(), use_aliases=True, variant="emoji_type")
|
||||
emojize(x.strip(), variant="emoji_type")
|
||||
for x in sys.stdin.readlines()
|
||||
if x.strip()
|
||||
]
|
||||
|
||||
2
.github/workflows/build.yaml
vendored
2
.github/workflows/build.yaml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
|
||||
2
.github/workflows/codeql.yaml
vendored
2
.github/workflows/codeql.yaml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.version }}
|
||||
|
||||
|
||||
2
.github/workflows/publish.yaml
vendored
2
.github/workflows/publish.yaml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
|
||||
2
.github/workflows/test.yaml
vendored
2
.github/workflows/test.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python ${{ matrix.py_version }}
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.py_version }}
|
||||
|
||||
|
||||
43
README.md
43
README.md
@@ -1,6 +1,47 @@
|
||||
# JellyCon
|
||||
|
||||
JellyCon is a lightweight Kodi addon that lets you browse and play media files from your Jellyfin server directly within the Kodi interface.
|
||||
JellyCon is a lightweight Kodi add-on that lets you browse and play media files directly from your Jellyfin server within the Kodi interface. It can be thought of as a thin frontend for a Jellyfin server.
|
||||
|
||||
JellyCon can be used with Movie, TV Show, Music Video, and Music libraries, in addition to viewing LiveTV from the server. It can easily switch between multiple user accounts at will. It's easy to integrate with any customizable Kodi skin with a large collection of custom menus. Media items are populated from the server dynamically, and menu speed will vary based on local device speed.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
#### 1. Adding the Jellyfin repository
|
||||
|
||||
https://jellyfin.org/docs/general/clients/kodi.html#install-add-on-repository
|
||||
|
||||
#### 2. Install JellyCon Add-on
|
||||
|
||||
- From within Kodi, navigate to "Add-on Browser"
|
||||
- Select "Install from Repository"
|
||||
- Choose "Kodi Jellyfin Add-ons", followed by "Video Add-ons"
|
||||
- Select the JellyCon add-on and choose install
|
||||
|
||||
#### 3. Login
|
||||
|
||||
- Within a few seconds after the installation you should be prompted for your server details.
|
||||
- If a Jellyfin server is detected on your local network, it will displayed in a dialog. Otherwise, you will be prompted to enter the URL of your Jellyfin server
|
||||
- If Quick Connect is enabled in the server, a code will be displayed that you can use to log in via Quick Connect in the web UI or a mobile app.
|
||||
- If Quick Connect is not enabled, or if you select the "Manual Login" button, you will be able to select a user from the list, or manually login using your username and password.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
#### Configuring Home
|
||||
|
||||
Many Kodi skins allow for customizing of the home menu with custom nodes and widgets. However, all of these use slightly different layouts and terminology. Rather than a step by step guide, this section serves as an barebones introduction to customizing a skin.
|
||||
Examples
|
||||
|
||||
If you would like a link on the home screen to open a library in your Jellyfin server called "Kid's Movies", you would point the menu item to the path: Add-On -> Video Add-On -> JellyCon -> Jellyfin Libraries -> Kid's Movies -> Create menu item to here.
|
||||
|
||||
Beyond just modifying where the home menu headers go, many skins also allow you to use widgets. Widgets help populate the home screen with data, often the posters of media in the selected image. If you would like to display the most recent movies across all of your Jellyfin libraries on the home screen, the path would be: Add-On -> Video Add-On -> JellyCon -> Global Lists -> Movies -> Movies - Recently Added (20) -> Use as widget
|
||||
|
||||
Another common use case of widgets would be to display the next available episodes of shows that you may be watching. As above, this can be done both with individual libraries or with all libraries combined:
|
||||
|
||||
Add-On -> Video Add-On -> JellyCon -> Jellyfin Libraries -> Anime -> Anime - Next Up (20) -> Use as widget
|
||||
Add-On -> Video Add-On -> JellyCon -> Global Lists -> TV Shows -> TV Shows - Next Up (20) -> Use as widget
|
||||
|
||||
|
||||
## License
|
||||
|
||||
|
||||
56
release.yaml
56
release.yaml
@@ -1,60 +1,8 @@
|
||||
version: '0.5.0'
|
||||
version: '0.5.5'
|
||||
changelog: |-
|
||||
New features and improvements
|
||||
-----------------------------
|
||||
+ Make sure manual login shows when connecting to a 10.7 server (#167) @mcarlton00
|
||||
+ Add genres and alpha picker to music (#161) @mcarlton00
|
||||
+ Add button to the bitrate selector (#160) @mcarlton00
|
||||
+ Add quick connect authentication (#159) @mcarlton00
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
+ Fix playing all files (#162) @mcarlton00
|
||||
+ Ensure server is present in API requests (#157) @mcarlton00
|
||||
+ Fix errors when stopping transcoded playback (#156) @mcarlton00
|
||||
+ Fix content update checks (#155) @mcarlton00
|
||||
+ Fix stale data after storage migration (#153) @mcarlton00
|
||||
+ Fix legacy skin shortcuts (#150) @mcarlton00
|
||||
+ Use timezone when calculating last activity date (#147) @mcarlton00
|
||||
+ Fix live tv playback (#144) @mcarlton00
|
||||
+ Fix browsing the Live TV programs menu (#145) @mcarlton00
|
||||
+ Force login if no saved credentials (#139) @mcarlton00
|
||||
+ Fix movie recommendations (#132) @mcarlton00
|
||||
+ Fix remote control (#134) @mcarlton00
|
||||
+ Don't throw errors when non-JellyCon content is playing (#135) @mcarlton00
|
||||
+ Fallback to empty lists instead of failing (#136) @mcarlton00
|
||||
+ Make widgets respect episode name format setting (#137) @mcarlton00
|
||||
+ Make requests respect the verify certificate setting (#131) @mcarlton00
|
||||
+ Fix manual user login (#123) @mcarlton00
|
||||
+ Rework user data storage (#119) @mcarlton00
|
||||
|
||||
Code or Repo Maintenance
|
||||
------------------------
|
||||
+ Use offscreen option when generating all listitems (#168) @mcarlton00
|
||||
+ Remove safe delete code (#166) @mcarlton00
|
||||
+ Address flake8 warnings (#165) @mcarlton00
|
||||
+ Simplify url param dict definitions (#164) @mcarlton00
|
||||
+ Optimize loops while building menus (#163) @mcarlton00
|
||||
+ Verify certificates by default (#154) @mcarlton00
|
||||
+ Rework live tv playback (#148) @mcarlton00
|
||||
+ Remove trakt integration (#133) @mcarlton00
|
||||
+ Copy translate_path from Jf4Kodi, fix LazyLogger (#130) @oddstr13
|
||||
+ Rework the network stack (#124) @mcarlton00
|
||||
+ Rework user data storage (#119) @mcarlton00
|
||||
+ Code reorganizing (#111) @mcarlton00
|
||||
+ Remove unused and commented out code (#109) @mcarlton00
|
||||
|
||||
CI & build changes
|
||||
------------------
|
||||
+ Bump release-drafter/release-drafter from 5.19.0 to 5.20.0 (#152) @dependabot
|
||||
+ Bump github/codeql-action from 1 to 2 (#143) @dependabot
|
||||
+ Bump actions/upload-artifact from 2 to 3 (#141) @dependabot
|
||||
+ Bump release-drafter/release-drafter from 5.18.1 to 5.19.0 (#138) @dependabot
|
||||
+ Bump actions/checkout from 2 to 3 (#127) @dependabot
|
||||
+ Bump actions/setup-python from 2 to 3 (#126) @dependabot
|
||||
+ Bump release-drafter/release-drafter from 5.17.5 to 5.18.1 (#122) @dependabot
|
||||
+ Bump release-drafter/release-drafter from 5.15.0 to 5.17.5 (#118) @dependabot
|
||||
+ Bump burnett01/rsync-deployments from 5.1 to 5.2 (#113) @dependabot
|
||||
+ Ensure a handle is valid before trying to use it to start playback (#194) @mcarlton00
|
||||
dependencies:
|
||||
py2:
|
||||
- addon: 'xbmc.python'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2022-05-17 04:13+0000\n"
|
||||
"Last-Translator: José Albano <josealbano@disroot.org>\n"
|
||||
"PO-Revision-Date: 2022-07-06 13:22+0000\n"
|
||||
"Last-Translator: Joaquín Díaz <joac.emi.a.n@gmail.com>\n"
|
||||
"Language-Team: Spanish <https://translate.jellyfin.org/projects/jellycon/"
|
||||
"jellycon/es/>\n"
|
||||
"Language: es\n"
|
||||
@@ -1089,3 +1089,15 @@ msgstr "Detectada solicitud adicional a reanudar"
|
||||
msgctxt "#30260"
|
||||
msgid "BoxSets"
|
||||
msgstr "Colecciones"
|
||||
|
||||
msgctxt "#30444"
|
||||
msgid "Login using Quick Connect"
|
||||
msgstr "Inicia sesión usando Conexión Rápida"
|
||||
|
||||
msgctxt "#30443"
|
||||
msgid "Quick Connect"
|
||||
msgstr "Conexión rápida"
|
||||
|
||||
msgctxt "#30323"
|
||||
msgid "Artists"
|
||||
msgstr "Artistas"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2022-05-20 04:22+0000\n"
|
||||
"Last-Translator: MoFanFr <hennebelle.benoit@gmail.com>\n"
|
||||
"PO-Revision-Date: 2022-07-06 13:22+0000\n"
|
||||
"Last-Translator: Thomas Schwery <thomas@inf3.ch>\n"
|
||||
"Language-Team: French <https://translate.jellyfin.org/projects/jellycon/"
|
||||
"jellycon/fr/>\n"
|
||||
"Language: fr\n"
|
||||
@@ -169,10 +169,9 @@ msgctxt "#30210"
|
||||
msgid "HTTP direct stream"
|
||||
msgstr "Stream direct HTTP"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30208"
|
||||
msgid "Max stream bitrate (Kbits)"
|
||||
msgstr "Bitrate maximum du flux (Kbps)"
|
||||
msgstr "Débit maximum du flux (Kbps)"
|
||||
|
||||
msgctxt "#30207"
|
||||
msgid "Playback"
|
||||
@@ -734,17 +733,14 @@ msgctxt "#30349"
|
||||
msgid " - Recently Played"
|
||||
msgstr "- Lus récemment"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30347"
|
||||
msgid "Getting Existing Images"
|
||||
msgstr "Récupération des images existantes"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30346"
|
||||
msgid "Deleteing Cached Images"
|
||||
msgstr "Suppression des images en cache"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30345"
|
||||
msgid "Cache Jellyfin server data requests"
|
||||
msgstr "Garder les requêtes de données serveur de Jellyfin en cache"
|
||||
@@ -761,7 +757,6 @@ msgctxt "#30322"
|
||||
msgid "Auto resume"
|
||||
msgstr "Reprise automatique"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30314"
|
||||
msgid "Play"
|
||||
msgstr "Lecture"
|
||||
@@ -899,31 +894,58 @@ msgctxt "#30126"
|
||||
msgid "Processing Item : "
|
||||
msgstr "Traitement de l'élément :"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30356"
|
||||
msgid "Loading existing image list"
|
||||
msgstr "Chargement de la liste des images existantes"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30354"
|
||||
msgid "Go To Series"
|
||||
msgstr "Voir les Séries"
|
||||
msgstr "Voir les Séries"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30319"
|
||||
msgid "Music - All Album Artists"
|
||||
msgstr "Musique - Tous les Albums par Artistes"
|
||||
msgstr "Musique - Tous les Artistes d'album"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30343"
|
||||
msgid "Changes Require Kodi Restart"
|
||||
msgstr "Les modifications nécessite le redémarrage de Kodi"
|
||||
msgstr "Les modifications nécessitent le redémarrage de Kodi"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30341"
|
||||
msgid "Background image update interval (0 = disabled)"
|
||||
msgstr "Intervalle de mise à jours des imades en arrière plan (0=désactivé)"
|
||||
msgstr "Intervalle de mise à jour des images en arrière plan (0=désactivé)"
|
||||
|
||||
msgctxt "#30333"
|
||||
msgid "Cache artwork in the background"
|
||||
msgstr "Mise en cache des illustrations en arrière plan"
|
||||
|
||||
msgctxt "#30366"
|
||||
msgid "Manually enter user details"
|
||||
msgstr "Renseigner manuellement les détails utilisateurs"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30359"
|
||||
msgid "Building full image list"
|
||||
msgstr "Construction de la liste complète des images"
|
||||
|
||||
msgctxt "#30358"
|
||||
msgid "Retreiving remote image list"
|
||||
msgstr "Récupération de la liste des images distantes"
|
||||
|
||||
msgctxt "#30357"
|
||||
msgid "Processing existing image list"
|
||||
msgstr "Traitement de la liste des images existantes"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30355"
|
||||
msgid "Kodi Settings->Services->Allow remote control via HTTP"
|
||||
msgstr "Paramètres de Kodi->Services->Autoriser le contrôle à distance par HTTP"
|
||||
|
||||
#, fuzzy
|
||||
msgctxt "#30348"
|
||||
msgid "Add user ratings"
|
||||
msgstr "Ajouter une note"
|
||||
|
||||
msgctxt "#30323"
|
||||
msgid "Artists"
|
||||
msgstr "Artistes"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2021-12-02 18:05+0000\n"
|
||||
"Last-Translator: Alfonso Scarpino <alfonso.scarpino@gmail.com>\n"
|
||||
"PO-Revision-Date: 2022-06-28 13:22+0000\n"
|
||||
"Last-Translator: Michele Fattoruso <michele.fattoruso@gmail.com>\n"
|
||||
"Language-Team: Italian <https://translate.jellyfin.org/projects/jellycon/"
|
||||
"jellycon/it/>\n"
|
||||
"Language: it\n"
|
||||
@@ -9,7 +9,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.5.2\n"
|
||||
"X-Generator: Weblate 4.10.1\n"
|
||||
|
||||
msgctxt "#30120"
|
||||
msgid "Show load progress"
|
||||
@@ -97,7 +97,7 @@ msgstr "Avanzate"
|
||||
|
||||
msgctxt "#30021"
|
||||
msgid "Show all episodes item"
|
||||
msgstr "Mostra elemento tutti gli episodi"
|
||||
msgstr "Mostra tutti gli episodi"
|
||||
|
||||
msgctxt "#30020"
|
||||
msgid "Flatten single season"
|
||||
@@ -426,3 +426,18 @@ msgstr "Percorso diretto del file"
|
||||
msgctxt "#30208"
|
||||
msgid "Max stream bitrate (Kbits)"
|
||||
msgstr "Bitrate massimo del flusso (Kbits)"
|
||||
|
||||
msgctxt "#30277"
|
||||
msgid "JellyCon needs to prompt for resume on partily played items, Kodi can also prompt, this can cause a double prompt. Do you want to remove the double prompt?"
|
||||
msgstr ""
|
||||
"JellyCon richiede una finestra per continuare la riproduzione di elementi, "
|
||||
"anche Kodi può richiedere una finestra, causando lapertura di una doppia "
|
||||
"finestra. Vuoi rimuovere la doppia finestra?"
|
||||
|
||||
msgctxt "#30213"
|
||||
msgid "Video force 8 bit"
|
||||
msgstr "Forza video 8 bit"
|
||||
|
||||
msgctxt "#30114"
|
||||
msgid "Jump back seconds"
|
||||
msgstr "Riavvolgi secondi"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2021-12-13 17:05+0000\n"
|
||||
"PO-Revision-Date: 2022-06-28 13:22+0000\n"
|
||||
"Last-Translator: Marcin Woliński <cierdek@gmail.com>\n"
|
||||
"Language-Team: Polish <https://translate.jellyfin.org/projects/jellycon/"
|
||||
"jellycon/pl/>\n"
|
||||
@@ -10,7 +10,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
|
||||
"|| n%100>=20) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 4.5.2\n"
|
||||
"X-Generator: Weblate 4.10.1\n"
|
||||
|
||||
msgctxt "#30313"
|
||||
msgid "Menu"
|
||||
@@ -1089,3 +1089,15 @@ msgstr "Odtwórz kinowe intro"
|
||||
msgctxt "#30437"
|
||||
msgid "Playback options"
|
||||
msgstr "Opcje odtwarzania"
|
||||
|
||||
msgctxt "#30444"
|
||||
msgid "Login using Quick Connect"
|
||||
msgstr "Zaloguj używając Szybkiego Połączenia"
|
||||
|
||||
msgctxt "#30443"
|
||||
msgid "Quick Connect"
|
||||
msgstr "Szybkie połączenie"
|
||||
|
||||
msgctxt "#30323"
|
||||
msgid "Artists"
|
||||
msgstr "Artyści"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"PO-Revision-Date: 2022-03-04 12:13+0000\n"
|
||||
"Last-Translator: 小造xu_zh <ngc7331@outlook.com>\n"
|
||||
"PO-Revision-Date: 2022-06-20 16:22+0000\n"
|
||||
"Last-Translator: wolong gl <wolong98@gmail.com>\n"
|
||||
"Language-Team: Chinese (Simplified) <https://translate.jellyfin.org/projects/"
|
||||
"jellycon/jellycon/zh_Hans/>\n"
|
||||
"Language: zh_Hans\n"
|
||||
@@ -1082,3 +1082,15 @@ msgstr "端口"
|
||||
msgctxt "#30000"
|
||||
msgid "Host"
|
||||
msgstr "主机"
|
||||
|
||||
msgctxt "#30444"
|
||||
msgid "Login using Quick Connect"
|
||||
msgstr "使用 Quick Connect 登录"
|
||||
|
||||
msgctxt "#30443"
|
||||
msgid "Quick Connect"
|
||||
msgstr "Quick Connect"
|
||||
|
||||
msgctxt "#30323"
|
||||
msgid "Artists"
|
||||
msgstr "艺术家"
|
||||
|
||||
@@ -153,14 +153,14 @@ def main_entry_point():
|
||||
|
||||
file_time_stamp = time.strftime("%Y%m%d-%H%M%S")
|
||||
tab_file_name = __addondir__ + "profile(" + file_time_stamp + ").txt"
|
||||
s = StringIO.StringIO()
|
||||
s = StringIO()
|
||||
ps = pstats.Stats(pr, stream=s)
|
||||
ps = ps.sort_stats('cumulative')
|
||||
ps.print_stats()
|
||||
ps.strip_dirs()
|
||||
ps = ps.sort_stats('tottime')
|
||||
ps.print_stats()
|
||||
with open(tab_file_name, 'wb') as f:
|
||||
with open(tab_file_name, 'w') as f:
|
||||
f.write(s.getvalue())
|
||||
|
||||
log.debug("===== JellyCon FINISHED =====")
|
||||
@@ -348,7 +348,7 @@ def show_menu(params):
|
||||
li.setProperty('menu_id', 'play')
|
||||
action_items.append(li)
|
||||
|
||||
if result["Type"] in ["Season", "MusicAlbum", "Playlist"]:
|
||||
if result["Type"] in ["Season", "MusicArtist", "MusicAlbum", "Playlist"]:
|
||||
li = xbmcgui.ListItem(translate_string(30317), offscreen=True)
|
||||
li.setProperty('menu_id', 'play_all')
|
||||
action_items.append(li)
|
||||
@@ -358,7 +358,7 @@ def show_menu(params):
|
||||
li.setProperty('menu_id', 'transcode')
|
||||
action_items.append(li)
|
||||
|
||||
if result["Type"] in ["Episode", "Movie", "Music", "Video", "Audio"]:
|
||||
if result["Type"] in ["Episode", "Movie", "Music", "Video", "Audio", "MusicArtist", "MusicAlbum"]:
|
||||
li = xbmcgui.ListItem(translate_string(30402), offscreen=True)
|
||||
li.setProperty('menu_id', 'add_to_playlist')
|
||||
action_items.append(li)
|
||||
|
||||
@@ -4,10 +4,11 @@ import sys
|
||||
from six.moves.urllib.parse import quote
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil import tz
|
||||
|
||||
import xbmcgui
|
||||
|
||||
from .utils import datetime_from_string, get_art_url, image_url, kodi_version
|
||||
from .utils import datetime_from_string, get_art_url, image_url, get_current_datetime
|
||||
from .lazylogger import LazyLogger
|
||||
from six import ensure_text
|
||||
|
||||
@@ -402,12 +403,15 @@ def add_gui_item(url, item_details, display_options, folder=True, default_sort=F
|
||||
end_time = datetime_from_string(item_details.program_end_date)
|
||||
|
||||
duration = (end_time - start_time).total_seconds()
|
||||
time_done = (datetime.now().astimezone() - start_time).total_seconds()
|
||||
now = get_current_datetime()
|
||||
time_done = (now - start_time).total_seconds()
|
||||
percentage_done = (float(time_done) / float(duration)) * 100.0
|
||||
capped_percentage = int(percentage_done)
|
||||
|
||||
start_time_string = start_time.strftime("%H:%M")
|
||||
end_time_string = end_time.strftime("%H:%M")
|
||||
# Convert dates to local timezone for display
|
||||
local = tz.tzlocal()
|
||||
start_time_string = start_time.astimezone(local).strftime("%H:%M")
|
||||
end_time_string = end_time.astimezone(local).strftime("%H:%M")
|
||||
|
||||
item_details.duration = int(duration)
|
||||
item_details.resume_time = int(time_done)
|
||||
|
||||
@@ -4,6 +4,7 @@ import xbmcaddon
|
||||
from kodi_six.utils import py2_decode
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
from .utils import get_device_id, get_version, load_user_details
|
||||
from .lazylogger import LazyLogger
|
||||
@@ -36,7 +37,17 @@ class API:
|
||||
|
||||
r = requests.get(url, headers=self.headers, verify=self.verify_cert)
|
||||
try:
|
||||
response_data = r.json()
|
||||
try:
|
||||
'''
|
||||
The requests library defaults to using simplejson to handle
|
||||
json decoding. On low power devices and using Py3, this is
|
||||
significantly slower than the builtin json library. Skip that
|
||||
and just parse the json ourselves. Fall back to using
|
||||
requests/simplejson if there's a parsing error.
|
||||
'''
|
||||
response_data = json.loads(r.text)
|
||||
except ValueError:
|
||||
response_data = r.json()
|
||||
except:
|
||||
response_data = {}
|
||||
return response_data
|
||||
@@ -49,7 +60,11 @@ class API:
|
||||
|
||||
r = requests.post(url, json=payload, headers=self.headers, verify=self.verify_cert)
|
||||
try:
|
||||
response_data = r.json()
|
||||
try:
|
||||
# Much faster on low power devices, see above comment
|
||||
response_data = json.loads(r.text)
|
||||
except ValueError:
|
||||
response_data = r.json()
|
||||
except:
|
||||
response_data = {}
|
||||
return response_data
|
||||
|
||||
@@ -5,17 +5,19 @@ import xbmc
|
||||
import xbmcgui
|
||||
import xbmcaddon
|
||||
import xbmcvfs
|
||||
import xbmcplugin
|
||||
|
||||
from datetime import timedelta
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from six.moves.urllib.parse import urlencode
|
||||
import sys
|
||||
|
||||
from .jellyfin import api
|
||||
from .lazylogger import LazyLogger
|
||||
from .dialogs import ResumeDialog
|
||||
from .utils import send_event_notification, convert_size, get_device_id, translate_string, load_user_details, translate_path
|
||||
from .utils import send_event_notification, convert_size, get_device_id, translate_string, load_user_details, translate_path, get_jellyfin_url, download_external_sub
|
||||
from .kodi_utils import HomeWindow
|
||||
from .datamanager import clear_old_cache_data
|
||||
from .item_functions import extract_item_info, add_gui_item, get_art
|
||||
@@ -254,13 +256,18 @@ def play_file(play_info):
|
||||
return
|
||||
|
||||
# if this is a season, playlist or album then play all items in that parent
|
||||
if result.get("Type") in ["Season", "MusicAlbum", "Playlist"]:
|
||||
if result.get("Type") in ["Season", "MusicArtist", "MusicAlbum", "Playlist"]:
|
||||
log.debug("PlayAllFiles for parent item id: {0}".format(item_id))
|
||||
url = ('/Users/{}/items'.format(api.user_id) +
|
||||
'?ParentId=%s' +
|
||||
'&Fields=MediaSources' +
|
||||
'&format=json')
|
||||
url = url % (item_id,)
|
||||
url_root = '/Users/{}/Items'.format(api.user_id)
|
||||
# Look specifically for episodes or audio files
|
||||
url_params = {
|
||||
'ParentId': item_id,
|
||||
'Fields': 'MediaSources',
|
||||
'IncludeItemTypes': 'Episode,Audio',
|
||||
'Recursive': True
|
||||
}
|
||||
|
||||
url = get_jellyfin_url(url_root, url_params)
|
||||
result = api.get(url)
|
||||
log.debug("PlayAllFiles items: {0}".format(result))
|
||||
|
||||
@@ -409,7 +416,7 @@ def play_file(play_info):
|
||||
display_options["addSubtitleAvailable"] = False
|
||||
display_options["addUserRatings"] = False
|
||||
|
||||
gui_item = add_gui_item("", item_details, display_options, False)
|
||||
gui_item = add_gui_item(item_id, item_details, display_options, False)
|
||||
list_item = gui_item[1]
|
||||
|
||||
if playback_type == "2": # if transcoding then prompt for audio and subtitle
|
||||
@@ -449,12 +456,20 @@ def play_file(play_info):
|
||||
if len(intro_items) > 0:
|
||||
playlist = play_all_files(intro_items, play_items=False)
|
||||
playlist.add(playurl, list_item)
|
||||
player.play(playlist)
|
||||
else:
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
playlist.clear()
|
||||
playlist.add(playurl, list_item)
|
||||
|
||||
player.play(playlist)
|
||||
if len(sys.argv) > 1 and int(sys.argv[1]) > 0:
|
||||
# Play from info menu
|
||||
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, list_item)
|
||||
else:
|
||||
'''
|
||||
Play from remote control or addon menus. Doesn't have a handle,
|
||||
so need to call player directly
|
||||
'''
|
||||
playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
|
||||
playlist.clear()
|
||||
playlist.add(playurl, list_item)
|
||||
player.play(playlist)
|
||||
|
||||
if seek_time != 0:
|
||||
player.pause()
|
||||
@@ -836,26 +851,32 @@ def external_subs(media_source, list_item, item_id):
|
||||
source_id = media_source['Id']
|
||||
|
||||
language = stream.get('Language', '')
|
||||
if language and stream['IsDefault']:
|
||||
language = '{}.default'.format(language)
|
||||
if language and stream['IsForced']:
|
||||
language = '{}.forced'.format(language)
|
||||
is_sdh = stream.get('Title') and stream['Title'] in ('sdh', 'cc')
|
||||
if language and is_sdh:
|
||||
language = '{}.{}'.format(language, stream['Title'])
|
||||
codec = stream.get('Codec', '')
|
||||
|
||||
url_root = '{}/Videos/{}/{}/Subtitles/{}'.format(server, item_id, source_id, index)
|
||||
url = '{}{}'.format(server, stream.get('DeliveryUrl'))
|
||||
if language:
|
||||
url = '{}/0/Stream.{}.{}?api_key={}'.format(
|
||||
url_root, language, codec, token)
|
||||
'''
|
||||
Starting in 10.8, the server no longer provides language
|
||||
specific download points. We have to download the file
|
||||
and name it with the language code ourselves so Kodi
|
||||
will parse it correctly
|
||||
'''
|
||||
subtitle_file = download_external_sub(language, codec, url)
|
||||
else:
|
||||
url = '{}/0/Stream.{}?api_key={}'.format(url_root, codec, token)
|
||||
# If there is no language defined, we can go directly to the server
|
||||
subtitle_file = url
|
||||
|
||||
default = ""
|
||||
if stream['IsDefault']:
|
||||
default = "default"
|
||||
forced = ""
|
||||
if stream['IsForced']:
|
||||
forced = "forced"
|
||||
|
||||
sub_name = '{} ( {} ) {} {}'.format(language, codec, default, forced)
|
||||
sub_name = '{} ( {} )'.format(language, codec)
|
||||
|
||||
sub_names.append(sub_name)
|
||||
externalsubs.append(url)
|
||||
externalsubs.append(subtitle_file)
|
||||
|
||||
if len(externalsubs) == 0:
|
||||
return
|
||||
|
||||
@@ -13,7 +13,7 @@ import xbmc
|
||||
from .kodi_utils import HomeWindow
|
||||
from .jellyfin import API
|
||||
from .lazylogger import LazyLogger
|
||||
from .utils import datetime_from_string, translate_string, save_user_details, load_user_details
|
||||
from .utils import datetime_from_string, translate_string, save_user_details, load_user_details, get_current_datetime
|
||||
from .dialogs import QuickConnectDialog
|
||||
|
||||
log = LazyLogger(__name__)
|
||||
@@ -368,6 +368,7 @@ def create_user_listitem(server, user):
|
||||
Create a user listitem for the user selection screen
|
||||
'''
|
||||
config = user.get("Configuration")
|
||||
now = get_current_datetime()
|
||||
if config is not None:
|
||||
name = user.get("Name")
|
||||
time_ago = ""
|
||||
@@ -375,7 +376,7 @@ def create_user_listitem(server, user):
|
||||
# Calculate how long it's been since the user was last active
|
||||
if last_active:
|
||||
last_active_date = datetime_from_string(last_active)
|
||||
ago = datetime.now().astimezone() - last_active_date
|
||||
ago = now - last_active_date
|
||||
# Check days
|
||||
if ago.days > 0:
|
||||
time_ago += ' {}d'.format(ago.days)
|
||||
|
||||
@@ -15,6 +15,7 @@ import time
|
||||
import math
|
||||
import os
|
||||
import hashlib
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from dateutil import tz
|
||||
import re
|
||||
@@ -104,14 +105,20 @@ def datetime_from_string(time_string):
|
||||
# https://bugs.python.org/issue27400
|
||||
dt = datetime(*(time.strptime(time_string, "%Y-%m-%dT%H:%M:%S.%f %Z")[0:6]))
|
||||
|
||||
# Convert server dates from UTC to local time
|
||||
# Dates received from the server are in UTC, but parsing them results in naive objects
|
||||
utc = tz.tzutc()
|
||||
local = tz.tzlocal()
|
||||
|
||||
utc_dt = dt.replace(tzinfo=utc)
|
||||
local_dt = utc_dt.astimezone(local)
|
||||
|
||||
return local_dt
|
||||
return utc_dt
|
||||
|
||||
|
||||
def get_current_datetime():
|
||||
# Get current time in UTC
|
||||
now = datetime.utcnow()
|
||||
utc = tz.tzutc()
|
||||
now_dt = now.replace(tzinfo=utc)
|
||||
|
||||
return now_dt
|
||||
|
||||
|
||||
def convert_size(size_bytes):
|
||||
@@ -357,3 +364,18 @@ def translate_path(path):
|
||||
return xbmcvfs.translatePath(path)
|
||||
else:
|
||||
return xbmc.translatePath(path)
|
||||
|
||||
|
||||
def download_external_sub(language, codec, url):
|
||||
|
||||
# Download the subtitle file
|
||||
r = requests.get(url)
|
||||
r.raise_for_status()
|
||||
|
||||
# Write the subtitle file to the local filesystem
|
||||
file_name = 'Stream.{}.{}'.format(language, codec)
|
||||
file_path = py2_decode(translate_path('special://temp/{}'.format(file_name)))
|
||||
with open(file_path, 'wb') as f:
|
||||
f.write(r.content)
|
||||
|
||||
return file_path
|
||||
|
||||
Reference in New Issue
Block a user