From 5a009b7ea05644c25d3c238659b26a90e8936dbf Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 9 Sep 2021 13:55:20 +0200 Subject: [PATCH 01/17] Implement Kodi's "Reset resume position" --- resources/lib/kodi_db/video.py | 16 ++++++++++++++++ resources/lib/kodimonitor.py | 31 +++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/resources/lib/kodi_db/video.py b/resources/lib/kodi_db/video.py index 6a349150..7e0916b9 100644 --- a/resources/lib/kodi_db/video.py +++ b/resources/lib/kodi_db/video.py @@ -575,6 +575,22 @@ class KodiVideoDB(common.KodiDBBase): return return movie_id, typus + def file_id_from_id(self, kodi_id, kodi_type): + """ + Returns the Kodi file_id for the item with kodi_id and kodi_type or + None + """ + if kodi_type == v.KODI_TYPE_MOVIE: + identifier = 'idMovie' + elif kodi_type == v.KODI_TYPE_EPISODE: + identifier = 'idEpisode' + self.cursor.execute('SELECT idFile FROM %s WHERE %s = ? LIMIT 1' + % (kodi_type, identifier), (kodi_id, )) + try: + return self.cursor.fetchone()[0] + except TypeError: + pass + def get_resume(self, file_id): """ Returns the first resume point in seconds (int) if found, else None for diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index 2701c9b0..4e0582fc 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -14,6 +14,7 @@ import xbmc from .plex_api import API from .plex_db import PlexDB +from .kodi_db import KodiVideoDB from . import kodi_db from .downloadutils import DownloadUtils as DU from . import utils, timing, plex_functions as PF @@ -86,7 +87,8 @@ class KodiMonitor(xbmc.Monitor): with app.APP.lock_playqueues: self._playlist_onclear(data) elif method == "VideoLibrary.OnUpdate": - _videolibrary_onupdate(data) + with app.APP.lock_playqueues: + _videolibrary_onupdate(data) elif method == "VideoLibrary.OnRemove": pass elif method == "System.OnSleep": @@ -652,17 +654,34 @@ def _videolibrary_onupdate(data): A specific Kodi library item has been updated. This seems to happen if the user marks an item as watched/unwatched or if playback of the item just stopped + + 2 kinds of messages possible, e.g. + Method: VideoLibrary.OnUpdate Data: ("Reset resume position" and also + fired just after stopping playback - BEFORE OnStop fires) + {'id': 1, 'type': 'movie'} + Method: VideoLibrary.OnUpdate Data: ("Mark as watched") + {'item': {'id': 1, 'type': 'movie'}, 'playcount': 1} """ - playcount = data.get('playcount') - item = data.get('item') - if playcount is None or item is None: - return + item = data.get('item') if 'item' in data else data try: kodi_id = item['id'] kodi_type = item['type'] except (KeyError, TypeError): - LOG.info("Item is invalid for playstate update.") + LOG.debug("Item is invalid for a Plex playstate update") return + playcount = data.get('playcount') + if playcount is None: + # "Reset resume position" + # Kodi might set as watched or unwatched! + with KodiVideoDB(lock=False) as kodidb: + file_id = kodidb.file_id_from_id(kodi_id, kodi_type) + if file_id is None: + return + if kodidb.get_resume(file_id): + # We do have an existing bookmark entry - not toggling to + # either watched or unwatched on the Plex side + return + playcount = kodidb.get_playcount(file_id) or 0 if app.PLAYSTATE.item and kodi_id == app.PLAYSTATE.item.kodi_id and \ kodi_type == app.PLAYSTATE.item.kodi_type: # Kodi updates an item immediately after playback. Hence we do NOT From 2432ce5ee637ce02b551503f5fde5fe5e5f82f1f Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 9 Sep 2021 14:59:02 +0200 Subject: [PATCH 02/17] Beta version bump 2.14.3 --- addon.xml | 7 +++++-- changelog.txt | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/addon.xml b/addon.xml index e1e90ad6..1bf33ad8 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + @@ -88,7 +88,10 @@ Plex를 Kodi에 기본 통합 Kodi를 Plex Media Server에 연결합니다. 이 플러그인은 Plex로 모든 비디오를 관리하고 Kodi로는 관리하지 않는다고 가정합니다. Kodi 비디오 및 음악 데이터베이스에 이미 저장된 데이터가 손실 될 수 있습니다 (이 플러그인이 직접 변경하므로). 자신의 책임하에 사용하십시오! 자신의 책임하에 사용 - version 2.14.2: + version 2.14.3 (beta only): +- Implement "Reset resume position" from the Kodi context menu + +version 2.14.2: - version 2.14.1 for everyone version 2.14.1 (beta only): diff --git a/changelog.txt b/changelog.txt index c57e1ac8..57527792 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,6 @@ +version 2.14.3 (beta only): +- Implement "Reset resume position" from the Kodi context menu + version 2.14.2: - version 2.14.1 for everyone From 057921b05e768d759cbdaf9de0ed15905378a67c Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 24 Sep 2021 16:59:41 +0200 Subject: [PATCH 03/17] Fix download not always returning entire requests.response object --- resources/lib/downloadutils.py | 9 +++++---- resources/lib/plex_api/media.py | 15 +++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py index 678876c4..33ee72df 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -224,7 +224,11 @@ class DownloadUtils(): if r.status_code != 401: self.count_unauthorized = 0 - if r.status_code == 204: + if return_response is True: + # return the entire response object + return r + + elif r.status_code == 204: # No body in the response # But read (empty) content to release connection back to pool # (see requests: keep-alive documentation) @@ -258,9 +262,6 @@ class DownloadUtils(): elif r.status_code in (200, 201): # 200: OK # 201: Created - if return_response is True: - # return the entire response object - return r try: # xml response r = utils.defused_etree.fromstring(r.content) diff --git a/resources/lib/plex_api/media.py b/resources/lib/plex_api/media.py index 52e10262..4246f03a 100644 --- a/resources/lib/plex_api/media.py +++ b/resources/lib/plex_api/media.py @@ -320,16 +320,15 @@ class Media(object): filename, extension) response = DU().downloadUrl(url, return_response=True) - try: - response.status_code - except AttributeError: + if not response.ok: LOG.error('Could not temporarily download subtitle %s', url) + LOG.error('HTTP status: %s, message: %s', + response.status_code, response.text) return - else: - LOG.debug('Writing temp subtitle to %s', path) - with open(path_ops.encode_path(path), 'wb') as f: - f.write(response.content) - return path + LOG.debug('Writing temp subtitle to %s', path) + with open(path_ops.encode_path(path), 'wb') as f: + f.write(response.content) + return path def validate_playurl(self, path, typus, force_check=False, folder=False, omit_check=False): From e96df700c12e8340ada45f48b7d10c352a64ac70 Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 24 Sep 2021 16:24:57 +0200 Subject: [PATCH 04/17] Fix logging if fanart.tv lookup fails --- resources/lib/plex_api/artwork.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/resources/lib/plex_api/artwork.py b/resources/lib/plex_api/artwork.py index 632d4355..08841a35 100644 --- a/resources/lib/plex_api/artwork.py +++ b/resources/lib/plex_api/artwork.py @@ -222,12 +222,14 @@ class Artwork(object): else: # Not supported artwork return artworks - data = DU().downloadUrl(url, authenticate=False, timeout=15) - try: - data.get('test') - except AttributeError: - LOG.error('Could not download data from FanartTV') + data = DU().downloadUrl(url, + authenticate=False, + timeout=15, + return_response=True) + if not data.ok: + LOG.debug('Could not download data from FanartTV') return artworks + data = data.json() fanart_tv_types = list(v.FANART_TV_TO_KODI_TYPE) From 11d06d909e83252e1d85b1936a2540e533077a40 Mon Sep 17 00:00:00 2001 From: croneter Date: Mon, 13 Sep 2021 08:52:45 +0200 Subject: [PATCH 05/17] Transcoding: Fix Plex burning-in subtitles when it should not --- resources/lib/playback_decision.py | 15 +++++---------- resources/lib/plex_functions.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/resources/lib/playback_decision.py b/resources/lib/playback_decision.py index aa8f3ba5..8b2d3640 100644 --- a/resources/lib/playback_decision.py +++ b/resources/lib/playback_decision.py @@ -426,7 +426,8 @@ def setup_transcoding_audio_subtitle_prefs(mediastreams, part_id): action_type='PUT', parameters=args) - select_subs_index = '' + # Zero telling the PMS to deactivate subs altogether + select_subs_index = 0 if sub_num == 1: # Note: we DO need to tell the PMS that we DONT want any sub # Otherwise, the PMS might pick-up the last one @@ -445,15 +446,9 @@ def setup_transcoding_audio_subtitle_prefs(mediastreams, part_id): LOG.info('User chose to not burn-in any subtitles') else: LOG.info('User chose to burn-in subtitle %s: %s', - select_subs_index, - subtitle_streams[resp].decode('utf-8')) + select_subs_index, + subtitle_streams[resp].decode('utf-8')) select_subs_index = subtitle_streams_list[resp - 1] # Now prep the PMS for our choice - args = { - 'subtitleStreamID': select_subs_index, - 'allParts': 1 - } - DU().downloadUrl('{server}/library/parts/%s' % part_id, - action_type='PUT', - parameters=args) + PF.change_subtitle(select_subs_index, part_id) return True diff --git a/resources/lib/plex_functions.py b/resources/lib/plex_functions.py index 3d421122..dc26a164 100644 --- a/resources/lib/plex_functions.py +++ b/resources/lib/plex_functions.py @@ -1117,3 +1117,19 @@ def playback_decision(path, media, part, playmethod, video=True, args=None): return DU().downloadUrl(utils.extend_url(url, arguments), headerOptions=v.STREAMING_HEADERS, reraise=True) + + +def change_subtitle(plex_stream_id, part_id): + """ + Tell the PMS to display/burn-in the subtitle stream with id plex_stream_id + for the Part (PMS XML etree tag "Part") with unique id part_id. + - plex_stream_id = 0 will deactivate the subtitle + - We always do this for ALL parts of a video + """ + arguments = { + 'subtitleStreamID': plex_stream_id, + 'allParts': 1 + } + url = '{server}/library/parts/%s' % part_id + return DU().downloadUrl(utils.extend_url(url, arguments), + action_type='PUT') From 9da61a059f3f1fb435700101d643f1570f48f896 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 12 Sep 2021 16:33:18 +0200 Subject: [PATCH 06/17] Disentangle and optimize some code Rename method Simplify some code Clarify some code --- resources/lib/kodimonitor.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index 4e0582fc..e387675b 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -30,7 +30,7 @@ class KodiMonitor(xbmc.Monitor): """ def __init__(self): self._already_slept = False - self._switch_to_plex_streams = None + self._switched_to_plex_streams = True xbmc.Monitor.__init__(self) for playerid in app.PLAYSTATE.player_states: app.PLAYSTATE.player_states[playerid] = copy.deepcopy(app.PLAYSTATE.template) @@ -68,7 +68,7 @@ class KodiMonitor(xbmc.Monitor): self.PlayBackStart(data) elif method == 'Player.OnAVChange': with app.APP.lock_playqueues: - self.on_av_change() + self._on_av_change(data) elif method == "Player.OnStop": with app.APP.lock_playqueues: _playback_cleanup(ended=data.get('end')) @@ -359,32 +359,36 @@ class KodiMonitor(xbmc.Monitor): status['plex_type'] = plex_type status['playmethod'] = item.playmethod status['playcount'] = item.playcount - try: - status['external_player'] = app.APP.player.isExternalPlayer() == 1 - except AttributeError: - # Kodi version < 17 - pass + status['external_player'] = app.APP.player.isExternalPlayer() == 1 LOG.debug('Set the player state: %s', status) # Workaround for the Kodi add-on Up Next if not app.SYNC.direct_paths: _notify_upnext(item) - self._switch_to_plex_streams = item + self._switched_to_plex_streams = False - def on_av_change(self): + def _on_av_change(self, data): """ Will be called when Kodi has a video, audio or subtitle stream. Also happens when the stream changes. + + Example data as returned by Kodi: + {'item': {'id': 5, 'type': 'movie'}, + 'player': {'playerid': 1, 'speed': 1}} """ - if self._switch_to_plex_streams is not None: - self.switch_to_plex_streams(self._switch_to_plex_streams) - self._switch_to_plex_streams = None + if not self._switched_to_plex_streams: + self.switch_to_plex_streams() + self._switched_to_plex_streams = True @staticmethod - def switch_to_plex_streams(item): + def switch_to_plex_streams(): """ Override Kodi audio and subtitle streams with Plex PMS' selection """ + item = app.PLAYSTATE.item + if item is None: + # Player might've quit + return for typus in ('audio', 'subtitle'): try: plex_index, language_tag = item.active_plex_stream_index(typus) From 176fa07e8034cbbdfbc952745e356abefc432b0a Mon Sep 17 00:00:00 2001 From: croneter Date: Mon, 13 Sep 2021 11:24:06 +0200 Subject: [PATCH 07/17] Refactoring: move all exceptions in a single module --- resources/lib/db.py | 8 +------- resources/lib/exceptions.py | 32 +++++++++++++++++++++++++++++ resources/lib/kodimonitor.py | 5 +++-- resources/lib/playback.py | 5 +++-- resources/lib/playlist_func.py | 8 +------- resources/lib/playlists/__init__.py | 4 ++-- resources/lib/playlists/common.py | 9 ++------ resources/lib/playlists/db.py | 5 +++-- resources/lib/playlists/kodi_pl.py | 4 +++- resources/lib/playlists/plex_pl.py | 3 ++- resources/lib/playlists/pms.py | 4 ++-- resources/lib/playqueue.py | 9 ++++---- resources/lib/plex_companion.py | 5 +++-- resources/lib/subtitles.py | 5 +---- 14 files changed, 63 insertions(+), 43 deletions(-) create mode 100644 resources/lib/exceptions.py diff --git a/resources/lib/db.py b/resources/lib/db.py index d6b1bfaf..140493a3 100644 --- a/resources/lib/db.py +++ b/resources/lib/db.py @@ -4,19 +4,13 @@ import sqlite3 from functools import wraps from . import variables as v, app +from .exceptions import LockedDatabase DB_WRITE_ATTEMPTS = 100 DB_WRITE_ATTEMPTS_TIMEOUT = 1 # in seconds DB_CONNECTION_TIMEOUT = 10 -class LockedDatabase(Exception): - """ - Dedicated class to make sure we're not silently catching locked DBs. - """ - pass - - def catch_operationalerrors(method): """ sqlite.OperationalError is raised immediately if another DB connection diff --git a/resources/lib/exceptions.py b/resources/lib/exceptions.py new file mode 100644 index 00000000..396277a0 --- /dev/null +++ b/resources/lib/exceptions.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, unicode_literals + + +class PlaylistError(Exception): + """ + Exception for our playlist constructs + """ + pass + + +class LockedDatabase(Exception): + """ + Dedicated class to make sure we're not silently catching locked DBs. + """ + pass + + +class SubtitleError(Exception): + """ + Exceptions relating to subtitles + """ + pass + + +class ProcessingNotDone(Exception): + """ + Exception to detect whether we've completed our sync and did not have to + abort or suspend. + """ + pass diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index e387675b..d9e5d8b0 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -20,6 +20,7 @@ from .downloadutils import DownloadUtils as DU from . import utils, timing, plex_functions as PF from . import json_rpc as js, playqueue as PQ, playlist_func as PL from . import backgroundthread, app, variables as v +from . import exceptions LOG = getLogger('PLEX.kodimonitor') @@ -181,7 +182,7 @@ class KodiMonitor(xbmc.Monitor): try: for i, item in enumerate(items): PL.add_item_to_plex_playqueue(playqueue, i + 1, kodi_item=item) - except PL.PlaylistError: + except exceptions.PlaylistError: LOG.info('Could not build Plex playlist for: %s', items) def _json_item(self, playerid): @@ -318,7 +319,7 @@ class KodiMonitor(xbmc.Monitor): return try: item = PL.init_plex_playqueue(playqueue, plex_id=plex_id) - except PL.PlaylistError: + except exceptions.PlaylistError: LOG.info('Could not initialize the Plex playlist') return item.file = path diff --git a/resources/lib/playback.py b/resources/lib/playback.py index 310f8b0f..1ce775a7 100644 --- a/resources/lib/playback.py +++ b/resources/lib/playback.py @@ -16,6 +16,7 @@ from .kodi_db import KodiVideoDB from . import plex_functions as PF, playlist_func as PL, playqueue as PQ from . import json_rpc as js, variables as v, utils, transfer from . import playback_decision, app +from . import exceptions ############################################################################### LOG = getLogger('PLEX.playback') @@ -192,7 +193,7 @@ def _playback_init(plex_id, plex_type, playqueue, pos, resume): # Special case - we already got a filled Kodi playqueue try: _init_existing_kodi_playlist(playqueue, pos) - except PL.PlaylistError: + except exceptions.PlaylistError: LOG.error('Playback_init for existing Kodi playlist failed') _ensure_resolve(abort=True) return @@ -312,7 +313,7 @@ def _init_existing_kodi_playlist(playqueue, pos): kodi_items = js.playlist_get_items(playqueue.playlistid) if not kodi_items: LOG.error('No Kodi items returned') - raise PL.PlaylistError('No Kodi items returned') + raise exceptions.PlaylistError('No Kodi items returned') item = PL.init_plex_playqueue(playqueue, kodi_item=kodi_items[pos]) item.force_transcode = app.PLAYSTATE.force_transcode # playqueue.py will add the rest - this will likely put the PMS under diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index 80bd387a..2aad50df 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -15,19 +15,13 @@ from . import utils from . import json_rpc as js from . import variables as v from . import app +from .exceptions import PlaylistError from .subtitles import accessible_plex_subtitles LOG = getLogger('PLEX.playlist_func') -class PlaylistError(Exception): - """ - Exception for our playlist constructs - """ - pass - - class Playqueue_Object(object): """ PKC object to represent PMS playQueues and Kodi playlist for queueing diff --git a/resources/lib/playlists/__init__.py b/resources/lib/playlists/__init__.py index 5e612a4c..2ca77fea 100644 --- a/resources/lib/playlists/__init__.py +++ b/resources/lib/playlists/__init__.py @@ -15,13 +15,13 @@ from __future__ import absolute_import, division, unicode_literals from logging import getLogger from sqlite3 import OperationalError -from .common import Playlist, PlaylistError, PlaylistObserver, \ - kodi_playlist_hash +from .common import Playlist, PlaylistObserver, kodi_playlist_hash from . import pms, db, kodi_pl, plex_pl from ..watchdog import events from ..plex_api import API from .. import utils, path_ops, variables as v, app +from ..exceptions import PlaylistError ############################################################################### LOG = getLogger('PLEX.playlists') diff --git a/resources/lib/playlists/common.py b/resources/lib/playlists/common.py index 96ca151b..7dae1e9a 100644 --- a/resources/lib/playlists/common.py +++ b/resources/lib/playlists/common.py @@ -12,6 +12,8 @@ from ..watchdog.observers import Observer from ..watchdog.utils.bricks import OrderedSetQueue from .. import path_ops, variables as v, app +from ..exceptions import PlaylistError + ############################################################################### LOG = getLogger('PLEX.playlists.common') @@ -20,13 +22,6 @@ SIMILAR_EVENTS = (events.EVENT_TYPE_CREATED, events.EVENT_TYPE_MODIFIED) ############################################################################### -class PlaylistError(Exception): - """ - The one main exception thrown if anything goes awry - """ - pass - - class Playlist(object): """ Class representing a synced Playlist with info for both Kodi and Plex. diff --git a/resources/lib/playlists/db.py b/resources/lib/playlists/db.py index 673fc02a..66e4d888 100644 --- a/resources/lib/playlists/db.py +++ b/resources/lib/playlists/db.py @@ -7,10 +7,11 @@ module from __future__ import absolute_import, division, unicode_literals from logging import getLogger -from .common import Playlist, PlaylistError +from .common import Playlist from ..plex_db import PlexDB from ..kodi_db import kodiid_from_filename from .. import path_ops, utils, variables as v +from ..exceptions import PlaylistError ############################################################################### LOG = getLogger('PLEX.playlists.db') @@ -121,7 +122,7 @@ def m3u_to_plex_ids(playlist): def playlist_file_to_plex_ids(playlist): """ Takes the playlist file located at path [unicode] and parses it. - Returns a list of plex_ids (str) or raises PL.PlaylistError if a single + Returns a list of plex_ids (str) or raises PlaylistError if a single item cannot be parsed from Kodi to Plex. """ if playlist.kodi_extension == 'm3u': diff --git a/resources/lib/playlists/kodi_pl.py b/resources/lib/playlists/kodi_pl.py index 39cfa5de..86629d2f 100644 --- a/resources/lib/playlists/kodi_pl.py +++ b/resources/lib/playlists/kodi_pl.py @@ -7,11 +7,13 @@ from __future__ import absolute_import, division, unicode_literals from logging import getLogger import re -from .common import Playlist, PlaylistError, kodi_playlist_hash +from .common import Playlist, kodi_playlist_hash from . import db, pms from ..plex_api import API from .. import utils, path_ops, variables as v +from ..exceptions import PlaylistError + ############################################################################### LOG = getLogger('PLEX.playlists.kodi_pl') REGEX_FILE_NUMBERING = re.compile(r'''_(\d\d)\.\w+$''') diff --git a/resources/lib/playlists/plex_pl.py b/resources/lib/playlists/plex_pl.py index 2b10af60..2bca0779 100644 --- a/resources/lib/playlists/plex_pl.py +++ b/resources/lib/playlists/plex_pl.py @@ -6,8 +6,9 @@ Create and delete playlists on the Plex side of things from __future__ import absolute_import, division, unicode_literals from logging import getLogger -from .common import PlaylistError from . import pms, db +from ..exceptions import PlaylistError + ############################################################################### LOG = getLogger('PLEX.playlists.plex_pl') # Used for updating Plex playlists due to Kodi changes - Plex playlist diff --git a/resources/lib/playlists/pms.py b/resources/lib/playlists/pms.py index b4de8e4a..e13540aa 100644 --- a/resources/lib/playlists/pms.py +++ b/resources/lib/playlists/pms.py @@ -7,11 +7,11 @@ manipulate playlists from __future__ import absolute_import, division, unicode_literals from logging import getLogger -from .common import PlaylistError - from ..plex_api import API from ..downloadutils import DownloadUtils as DU from .. import utils, app, variables as v +from ..exceptions import PlaylistError + ############################################################################### LOG = getLogger('PLEX.playlists.pms') diff --git a/resources/lib/playqueue.py b/resources/lib/playqueue.py index 45fb1d50..46edb0bc 100644 --- a/resources/lib/playqueue.py +++ b/resources/lib/playqueue.py @@ -12,6 +12,7 @@ import xbmc from .plex_api import API from . import playlist_func as PL, plex_functions as PF from . import backgroundthread, utils, json_rpc as js, app, variables as v +from . import exceptions ############################################################################### LOG = getLogger('PLEX.playqueue') @@ -88,7 +89,7 @@ def init_playqueue_from_plex_children(plex_id, transient_token=None): api = API(child) try: PL.add_item_to_playlist(playqueue, i, plex_id=api.plex_id) - except PL.PlaylistError: + except exceptions.PlaylistError: LOG.error('Could not add Plex item to our playlist: %s, %s', child.tag, child.attrib) playqueue.plex_transient_token = transient_token @@ -151,7 +152,7 @@ class PlayqueueMonitor(backgroundthread.KillableThread): i + j, i) try: PL.move_playlist_item(playqueue, i + j, i) - except PL.PlaylistError: + except exceptions.PlaylistError: LOG.error('Could not modify playqueue positions') LOG.error('This is likely caused by mixing audio and ' 'video tracks in the Kodi playqueue') @@ -167,7 +168,7 @@ class PlayqueueMonitor(backgroundthread.KillableThread): PL.add_item_to_plex_playqueue(playqueue, i, kodi_item=new_item) - except PL.PlaylistError: + except exceptions.PlaylistError: # Could not add the element pass except KeyError: @@ -196,7 +197,7 @@ class PlayqueueMonitor(backgroundthread.KillableThread): LOG.debug('Detected deletion of playqueue element at pos %s', i) try: PL.delete_playlist_item_from_PMS(playqueue, i) - except PL.PlaylistError: + except exceptions.PlaylistError: LOG.error('Could not delete PMS element from position %s', i) LOG.error('This is likely caused by mixing audio and ' 'video tracks in the Kodi playqueue') diff --git a/resources/lib/plex_companion.py b/resources/lib/plex_companion.py index e0cbb807..1f135a78 100644 --- a/resources/lib/plex_companion.py +++ b/resources/lib/plex_companion.py @@ -21,6 +21,7 @@ from . import playqueue as PQ from . import variables as v from . import backgroundthread from . import app +from . import exceptions ############################################################################### @@ -51,7 +52,7 @@ def update_playqueue_from_PMS(playqueue, with app.APP.lock_playqueues: try: xml = PL.get_PMS_playlist(playqueue, playqueue_id) - except PL.PlaylistError: + except exceptions.PlaylistError: LOG.error('Could now download playqueue %s', playqueue_id) return if playqueue.id == playqueue_id: @@ -64,7 +65,7 @@ def update_playqueue_from_PMS(playqueue, # Get new metadata for the playqueue first try: PL.get_playlist_details_from_xml(playqueue, xml) - except PL.PlaylistError: + except exceptions.PlaylistError: LOG.error('Could not get playqueue ID %s', playqueue_id) return playqueue.repeat = 0 if not repeat else int(repeat) diff --git a/resources/lib/subtitles.py b/resources/lib/subtitles.py index 439e6373..3948d678 100644 --- a/resources/lib/subtitles.py +++ b/resources/lib/subtitles.py @@ -9,6 +9,7 @@ import xml.etree.ElementTree as etree from . import app from . import path_ops from . import variables as v +from .exceptions import SubtitleError LOG = getLogger('PLEX.subtitles') @@ -467,7 +468,3 @@ def external_subs_from_filesystem(dirname, filename): class DummySub(etree.Element): def __init__(self): super(DummySub, self).__init__('Stream-subtitle-dummy') - - -class SubtitleError(Exception): - pass From 2bd692e1737515bfb4656c223e5adaa6d3aab46b Mon Sep 17 00:00:00 2001 From: croneter Date: Mon, 13 Sep 2021 13:26:04 +0200 Subject: [PATCH 08/17] Refactoring: playlist and playqueue items to use API instead of xml --- resources/lib/kodimonitor.py | 10 ++++----- resources/lib/playback.py | 15 +++++++------- resources/lib/playlist_func.py | 37 ++++++++++++++++++---------------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index d9e5d8b0..5dfa7526 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -344,8 +344,7 @@ class KodiMonitor(xbmc.Monitor): container_key = '/library/metadata/%s' % plex_id # Mechanik for Plex skip intro feature if utils.settings('enableSkipIntro') == 'true': - api = API(item.xml) - status['intro_markers'] = api.intro_markers() + status['intro_markers'] = item.api.intro_markers() # Remember the currently playing item app.PLAYSTATE.item = item # Remember that this player has been active @@ -593,11 +592,10 @@ def _next_episode(current_api): current_api.grandparent_title()) return try: - next_api = API(xml[counter + 1]) + return API(xml[counter + 1]) except IndexError: # Was the last episode - return - return next_api + pass def _complete_artwork_keys(info): @@ -623,7 +621,7 @@ def _notify_upnext(item): """ if not item.plex_type == v.PLEX_TYPE_EPISODE: return - this_api = API(item.xml) + this_api = item.api next_api = _next_episode(this_api) if next_api is None: return diff --git a/resources/lib/playback.py b/resources/lib/playback.py index 1ce775a7..c754436a 100644 --- a/resources/lib/playback.py +++ b/resources/lib/playback.py @@ -445,27 +445,26 @@ def _conclude_playback(playqueue, pos): """ LOG.debug('Concluding playback for playqueue position %s', pos) item = playqueue.items[pos] - api = API(item.xml) - if api.mediastream_number() is None: + if item.api.mediastream_number() is None: # E.g. user could choose between several media streams and cancelled LOG.debug('Did not get a mediastream_number') _ensure_resolve() return - api.part = item.part or 0 - playback_decision.set_pkc_playmethod(api, item) - if not playback_decision.audio_subtitle_prefs(api, item): + item.api.part = item.part or 0 + playback_decision.set_pkc_playmethod(item.api, item) + if not playback_decision.audio_subtitle_prefs(item.api, item): LOG.info('Did not set audio subtitle prefs, aborting silently') _ensure_resolve() return - playback_decision.set_playurl(api, item) + playback_decision.set_playurl(item.api, item) if not item.file: LOG.info('Did not get a playurl, aborting playback silently') _ensure_resolve() return - listitem = api.listitem(listitem=transfer.PKCListItem, resume=False) + listitem = item.api.listitem(listitem=transfer.PKCListItem, resume=False) listitem.setPath(item.file.encode('utf-8')) if item.playmethod != v.PLAYBACK_METHOD_DIRECT_PATH: - listitem.setSubtitles(api.cache_external_subs()) + listitem.setSubtitles(item.api.cache_external_subs()) transfer.send(listitem) LOG.debug('Done concluding playback') diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index 2aad50df..26bf2c1b 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -149,13 +149,14 @@ class PlaylistItem(object): file = None [str] Path to the item's file. STRING!! uri = None [str] PMS path to item; will be auto-set with plex_id guid = None [str] Weird Plex guid - xml = None [etree] XML from PMS, 1 lvl below + api = None [API] API of xml 1 lvl below playmethod = None [str] either 'DirectPath', 'DirectStream', 'Transcode' playcount = None [int] how many times the item has already been played offset = None [int] the item's view offset UPON START in Plex time part = 0 [int] part number if Plex video consists of mult. parts force_transcode [bool] defaults to False """ + def __init__(self): self.id = None self._plex_id = None @@ -165,7 +166,7 @@ class PlaylistItem(object): self.file = None self._uri = None self.guid = None - self.xml = None + self.api = None self.playmethod = None self.playcount = None self.offset = None @@ -226,10 +227,10 @@ class PlaylistItem(object): count = 0 if kodi_stream_index == -1: # Kodi telling us "it's the last one" - iterator = list(reversed(self.xml[0][self.part])) + iterator = list(reversed(self.api.plex_media_streams())) kodi_stream_index = 0 else: - iterator = self.xml[0][self.part] + iterator = self.api.plex_media_streams() # Kodi indexes differently than Plex for stream in iterator: if (stream.get('streamType') == stream_type and @@ -270,7 +271,7 @@ class PlaylistItem(object): Returns None if no stream has been selected """ stream_type = v.PLEX_STREAM_TYPE_FROM_STREAM_TYPE[stream_type] - for stream in self.xml[0][self.part]: + for stream in self.api.plex_media_streams(): if stream.get('streamType') == stream_type \ and stream.get('selected') == '1': return (utils.cast(int, stream.get('id')), @@ -287,9 +288,9 @@ class PlaylistItem(object): if stream_type == '3': streams = accessible_plex_subtitles(self.playmethod, self.file, - self.xml[0][self.part]) + self.api.plex_media_streams()) else: - streams = [x for x in self.xml[0][self.part] + streams = [x for x in self.api.plex_media_streams() if x.get('streamType') == stream_type] return streams @@ -415,14 +416,15 @@ def playlist_item_from_xml(xml_video_element, kodi_id=None, kodi_type=None): item.guid = api.guid_html_escaped() item.playcount = api.viewcount() item.offset = api.resume_point() - item.xml = xml_video_element + item.api = api LOG.debug('Created new playlist item from xml: %s', item) return item -def _get_playListVersion_from_xml(playlist, xml): +def _update_playlist_version(playlist, xml): """ - Takes a PMS xml as input to overwrite the playlist version (e.g. Plex + Takes a PMS xml (one level above the xml-depth where we're usually applying + API()) as input to overwrite the playlist version (e.g. Plex playQueueVersion). Raises PlaylistError if unsuccessful @@ -605,7 +607,7 @@ def add_item_to_plex_playqueue(playlist, pos, plex_id=None, kodi_item=None): raise PlaylistError('Could not add item %s to playlist %s' % (kodi_item, playlist)) api = API(xml[-1]) - item.xml = xml[-1] + item.api = api item.id = api.item_id() item.guid = api.guid_html_escaped() item.offset = api.resume_point() @@ -613,7 +615,7 @@ def add_item_to_plex_playqueue(playlist, pos, plex_id=None, kodi_item=None): playlist.items.append(item) if pos == len(playlist.items) - 1: # Item was added at the end - _get_playListVersion_from_xml(playlist, xml) + _update_playlist_version(playlist, xml) else: # Move the new item to the correct position move_playlist_item(playlist, @@ -657,7 +659,7 @@ def add_item_to_kodi_playlist(playlist, pos, kodi_id=None, kodi_type=None, {'id': kodi_id, 'type': kodi_type, 'file': file}) if item.plex_id is not None: xml = PF.GetPlexMetadata(item.plex_id) - item.xml = xml[-1] + item.api = API(xml[-1]) playlist.items.insert(pos, item) return item @@ -681,9 +683,10 @@ def move_playlist_item(playlist, before_pos, after_pos): playlist.id, playlist.items[before_pos].id, playlist.items[after_pos - 1].id) - # We need to increment the playlistVersion - _get_playListVersion_from_xml( - playlist, DU().downloadUrl(url, action_type="PUT")) + # Tell the PMS that we're moving items around + xml = DU().downloadUrl(url, action_type="PUT") + # We need to increment the playlist version for communicating with the PMS + _update_playlist_version(playlist, xml) # Move our item's position in our internal playlist playlist.items.insert(after_pos, playlist.items.pop(before_pos)) LOG.debug('Done moving for %s', playlist) @@ -731,7 +734,7 @@ def delete_playlist_item_from_PMS(playlist, pos): playlist.repeat), action_type="DELETE") del playlist.items[pos] - _get_playListVersion_from_xml(playlist, xml) + _update_playlist_version(playlist, xml) # Functions operating on the Kodi playlist objects ########## From 887f659b2fc1df5d4924fb9e8fcc31416b5a5130 Mon Sep 17 00:00:00 2001 From: croneter Date: Mon, 13 Sep 2021 14:35:58 +0200 Subject: [PATCH 09/17] Refactor usage of a media part's id --- resources/lib/playback_decision.py | 13 +++---------- resources/lib/plex_api/base.py | 6 ++++++ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/resources/lib/playback_decision.py b/resources/lib/playback_decision.py index aa8f3ba5..c7164faf 100644 --- a/resources/lib/playback_decision.py +++ b/resources/lib/playback_decision.py @@ -329,19 +329,12 @@ def audio_subtitle_prefs(api, item): Returns None if user cancelled or we need to abort, True otherwise """ # Set media and part where we're at - if (api.mediastream is None and - api.mediastream_number() is None): + if api.mediastream is None and api.mediastream_number() is None: return - try: - mediastreams = api.plex_media_streams() - except (TypeError, IndexError): - LOG.error('Could not get media %s, part %s', - api.mediastream, api.part) - return - part_id = mediastreams.attrib['id'] if item.playmethod != v.PLAYBACK_METHOD_TRANSCODE: return True - return setup_transcoding_audio_subtitle_prefs(mediastreams, part_id) + return setup_transcoding_audio_subtitle_prefs(api.plex_media_streams(), + api.part_id()) def setup_transcoding_audio_subtitle_prefs(mediastreams, part_id): diff --git a/resources/lib/plex_api/base.py b/resources/lib/plex_api/base.py index b1385fed..183166a9 100644 --- a/resources/lib/plex_api/base.py +++ b/resources/lib/plex_api/base.py @@ -256,6 +256,12 @@ class Base(object): """ return self.xml[self.mediastream][self.part] + def part_id(self): + """ + Returns the unique id of the currently active part [int] + """ + return int(self.xml[self.mediastream][self.part].attrib['id']) + def plot(self): """ Returns the plot or None. From d54307ffd5903b151022211a1ab5f024311738a7 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 19 Sep 2021 13:38:25 +0200 Subject: [PATCH 10/17] Tell the PMS if a video's audio stream or potentially subtitle stream has changed. For subtitles, this functionality is broken due to a Kodi bug --- resources/lib/json_rpc.py | 35 +++++++ resources/lib/kodimonitor.py | 68 +++++++++---- resources/lib/playlist_func.py | 169 +++++++++++++++++++------------- resources/lib/plex_functions.py | 16 +++ resources/lib/variables.py | 4 + 5 files changed, 206 insertions(+), 86 deletions(-) diff --git a/resources/lib/json_rpc.py b/resources/lib/json_rpc.py index 165b0a37..d72bfc2c 100644 --- a/resources/lib/json_rpc.py +++ b/resources/lib/json_rpc.py @@ -421,6 +421,41 @@ def get_item(playerid): 'properties': ['title', 'file']})['result']['item'] +def get_current_audio_stream_index(playerid): + """ + Returns the currently active audio stream index [int] + """ + return JsonRPC('Player.GetProperties').execute({ + 'playerid': playerid, + 'properties': ['currentaudiostream']})['result']['currentaudiostream']['index'] + + +def get_current_subtitle_stream_index(playerid): + """ + Returns the currently active subtitle stream index [int] or None if there + are no subs + PICKING UP CHANGES ON SUBTITLES IS CURRENTLY BROKEN ON THE KODI SIDE! The + JSON reply won't change even though subtitles are changed :-( + """ + try: + return JsonRPC('Player.GetProperties').execute({ + 'playerid': playerid, + 'properties': ['currentsubtitle', ]})['result']['currentsubtitle']['index'] + except KeyError: + pass + + +def get_subtitle_enabled(playerid): + """ + Returns True if a subtitle is currently enabled, False otherwise. + PICKING UP CHANGES ON SUBTITLES IS CURRENTLY BROKEN ON THE KODI SIDE! The + JSON reply won't change even though subtitles are changed :-( + """ + return JsonRPC('Player.GetProperties').execute({ + 'playerid': playerid, + 'properties': ['subtitleenabled', ]})['result']['subtitleenabled'] + + def get_player_props(playerid): """ Returns a dict for the active Kodi player with the following values: diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index 5dfa7526..466a6280 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -375,10 +375,39 @@ class KodiMonitor(xbmc.Monitor): Example data as returned by Kodi: {'item': {'id': 5, 'type': 'movie'}, 'player': {'playerid': 1, 'speed': 1}} + + PICKING UP CHANGES ON SUBTITLES IS CURRENTLY BROKEN ON THE KODI SIDE! + Kodi subs will never change. Also see json_rpc.py """ + playerid = data['player']['playerid'] + if not playerid == v.KODI_VIDEO_PLAYER_ID: + # We're just messing with Kodi's videoplayer + return if not self._switched_to_plex_streams: + # We need to switch to the Plex streams ONCE upon playback start + # after onavchange has been fired self.switch_to_plex_streams() self._switched_to_plex_streams = True + else: + item = app.PLAYSTATE.item + if item is None: + # Player might've quit + return + kodi_audio_stream = js.get_current_audio_stream_index(playerid) + sub_enabled = js.get_subtitle_enabled(playerid) + kodi_sub_stream = js.get_current_subtitle_stream_index(playerid) + # Audio + if kodi_audio_stream != item.current_kodi_audio_stream: + item.on_kodi_audio_stream_change(kodi_audio_stream) + # Subtitles - CURRENTLY BROKEN ON THE KODI SIDE! + # current_kodi_sub_stream may also be zero + subs_off = (None, False) + if ((sub_enabled and item.current_kodi_sub_stream in subs_off) + or (not sub_enabled and item.current_kodi_sub_stream not in subs_off) + or (kodi_sub_stream is not None + and kodi_sub_stream != item.current_kodi_sub_stream)): + item.on_kodi_subtitle_stream_change(kodi_sub_stream, + sub_enabled) @staticmethod def switch_to_plex_streams(): @@ -393,30 +422,33 @@ class KodiMonitor(xbmc.Monitor): try: plex_index, language_tag = item.active_plex_stream_index(typus) except TypeError: - if typus == 'subtitle': - LOG.info('Deactivating Kodi subtitles because the PMS ' - 'told us to not show any subtitles') - app.APP.player.showSubtitles(False) + LOG.debug('Deactivating Kodi subtitles because the PMS ' + 'told us to not show any subtitles') + app.APP.player.showSubtitles(False) + item.current_kodi_sub_stream = False continue - LOG.info('The PMS wants to display %s stream with Plex id %s and ' - 'languageTag %s', - typus, plex_index, language_tag) - kodi_index = item.kodi_stream_index(plex_index, - typus) + LOG.debug('The PMS wants to display %s stream with Plex id %s and ' + 'languageTag %s', + typus, plex_index, language_tag) + kodi_index = item.kodi_stream_index(plex_index, typus) if kodi_index is None: - LOG.info('Leaving Kodi %s stream settings untouched since we ' - 'could not parse Plex %s stream with id %s to a Kodi ' - 'index', typus, typus, plex_index) + LOG.debug('Leaving Kodi %s stream settings untouched since we ' + 'could not parse Plex %s stream with id %s to a Kodi' + ' index', typus, typus, plex_index) else: - LOG.info('Switching to Kodi %s stream number %s because the ' - 'PMS told us to show stream with Plex id %s', - typus, kodi_index, plex_index) + LOG.debug('Switching to Kodi %s stream number %s because the ' + 'PMS told us to show stream with Plex id %s', + typus, kodi_index, plex_index) # If we're choosing an "illegal" index, this function does # need seem to fail nor log any errors - if typus == 'subtitle': - app.APP.player.setSubtitleStream(kodi_index) - else: + if typus == 'audio': app.APP.player.setAudioStream(kodi_index) + else: + app.APP.player.setSubtitleStream(kodi_index) + if typus == 'audio': + item.current_kodi_audio_stream = kodi_index + else: + item.current_kodi_sub_stream = kodi_index def _playback_cleanup(ended=False): diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index 26bf2c1b..e5c278eb 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -12,6 +12,7 @@ from . import plex_functions as PF from .kodi_db import kodiid_from_filename from .downloadutils import DownloadUtils as DU from . import utils +from .utils import cast from . import json_rpc as js from . import variables as v from . import app @@ -180,6 +181,16 @@ class PlaylistItem(object): # False: do NOT resume, don't ask user # True: do resume, don't ask user self.resume = None + # Get the Plex audio and subtitle streams in the same order as Kodi + # uses them (Kodi uses indexes to activate them, not ids like Plex) + self._streams_have_been_processed = False + self._audio_streams = None + self._subtitle_streams = None + # Which Kodi streams are active? + self.current_kodi_audio_stream = None + # False means "deactivated", None means "we do not have a Kodi + # equivalent for this Plex subtitle" + self.current_kodi_sub_stream = None @property def plex_id(self): @@ -195,6 +206,18 @@ class PlaylistItem(object): def uri(self): return self._uri + @property + def audio_streams(self): + if not self._streams_have_been_processed: + self._process_streams() + return self._audio_streams + + @property + def subtitle_streams(self): + if not self._streams_have_been_processed: + self._process_streams() + return self._subtitle_streams + def __unicode__(self): return ("{{" "'id': {self.id}, " @@ -214,85 +237,100 @@ class PlaylistItem(object): def __repr__(self): return self.__unicode__().encode('utf-8') + def _process_streams(self): + """ + Builds audio and subtitle streams and enables matching between Plex + and Kodi using self.audio_streams and self.subtitle_streams + """ + # The playqueue response from the PMS does not contain a stream filename + # thanks Plex + self._subtitle_streams = accessible_plex_subtitles( + self.playmethod, + self.file, + self.api.plex_media_streams()) + # Audio streams are much easier - they're always available and sorted + # the same in Kodi and Plex + self._audio_streams = [x for x in self.api.plex_media_streams() + if x.get('streamType') == '2'] + self._streams_have_been_processed = True + + def _get_iterator(self, stream_type): + if stream_type == 'audio': + return self.audio_streams + elif stream_type == 'subtitle': + return self.subtitle_streams + def plex_stream_index(self, kodi_stream_index, stream_type): """ Pass in the kodi_stream_index [int] in order to receive the Plex stream - index. - + index [int]. stream_type: 'video', 'audio', 'subtitle' - Returns None if unsuccessful """ - stream_type = v.PLEX_STREAM_TYPE_FROM_STREAM_TYPE[stream_type] - count = 0 - if kodi_stream_index == -1: - # Kodi telling us "it's the last one" - iterator = list(reversed(self.api.plex_media_streams())) - kodi_stream_index = 0 - else: - iterator = self.api.plex_media_streams() - # Kodi indexes differently than Plex - for stream in iterator: - if (stream.get('streamType') == stream_type and - 'key' in stream.attrib): - if count == kodi_stream_index: - return stream.get('id') - count += 1 - for stream in iterator: - if (stream.get('streamType') == stream_type and - 'key' not in stream.attrib): - if count == kodi_stream_index: - return stream.get('id') - count += 1 + if stream_type == 'audio': + return int(self.audio_streams[kodi_stream_index].get('id')) + elif stream_type == 'subtitle': + try: + return int(self.subtitle_streams[kodi_stream_index].get('id')) + except (IndexError, TypeError): + pass def kodi_stream_index(self, plex_stream_index, stream_type): """ - Pass in the kodi_stream_index [int] in order to receive the Plex stream - index. - + Pass in the plex_stream_index [int] in order to receive the Kodi stream + index [int]. stream_type: 'video', 'audio', 'subtitle' - Returns None if unsuccessful """ if plex_stream_index is None: return - stream_type = v.PLEX_STREAM_TYPE_FROM_STREAM_TYPE[stream_type] - count = 0 - streams = self.sorted_accessible_plex_subtitles(stream_type) - for stream in streams: - if utils.cast(int, stream.get('id')) == plex_stream_index: - return count - count += 1 + for i, stream in enumerate(self._get_iterator(stream_type)): + if cast(int, stream.get('id')) == plex_stream_index: + return i def active_plex_stream_index(self, stream_type): """ Returns the following tuple for the active stream on the Plex side: - (id [int], languageTag [str]) + (Plex stream id [int], languageTag [str] or None) Returns None if no stream has been selected """ - stream_type = v.PLEX_STREAM_TYPE_FROM_STREAM_TYPE[stream_type] - for stream in self.api.plex_media_streams(): - if stream.get('streamType') == stream_type \ - and stream.get('selected') == '1': - return (utils.cast(int, stream.get('id')), - stream.get('languageTag')) + for i, stream in enumerate(self._get_iterator(stream_type)): + if stream.get('selected') == '1': + return (int(stream.get('id')), stream.get('languageTag')) - def sorted_accessible_plex_subtitles(self, stream_type): + def on_kodi_subtitle_stream_change(self, kodi_stream_index, subs_enabled): """ - Returns only the subtitles that Kodi can access when PKC Direct Paths - are used; i.e. Kodi has access to a video's directory. - NOT supported: additional subtitles downloaded using the Plex interface + Call this method if Kodi changed its subtitle and you want Plex to + know. """ - # The playqueue response from the PMS does not contain a stream filename - # thanks Plex - if stream_type == '3': - streams = accessible_plex_subtitles(self.playmethod, - self.file, - self.api.plex_media_streams()) + if subs_enabled: + try: + plex_stream_index = int(self.subtitle_streams[kodi_stream_index].get('id')) + except (IndexError, TypeError): + LOG.debug('Kodi subtitle change detected to a sub %s that is ' + 'NOT available on the Plex side', kodi_stream_index) + self.current_kodi_sub_stream = None + return + LOG.debug('Kodi subtitle change detected: telling Plex about ' + 'switch to index %s, Plex stream id %s', + kodi_stream_index, plex_stream_index) + self.current_kodi_sub_stream = kodi_stream_index else: - streams = [x for x in self.api.plex_media_streams() - if x.get('streamType') == stream_type] - return streams + plex_stream_index = 0 + LOG.debug('Kodi subtitle has been deactivated, telling Plex') + self.current_kodi_sub_stream = False + PF.change_subtitle(plex_stream_index, self.api.part_id()) + + def on_kodi_audio_stream_change(self, kodi_stream_index): + """ + Call this method if Kodi changed its audio stream and you want Plex to + know. kodi_stream_index [int] + """ + plex_stream_index = int(self.audio_streams[kodi_stream_index].get('id')) + LOG.debug('Changing Plex audio stream to %s, Kodi index %s', + plex_stream_index, kodi_stream_index) + PF.change_audio_stream(plex_stream_index, self.api.part_id()) + self.current_kodi_audio_stream = kodi_stream_index def playlist_item_from_kodi(kodi_item): @@ -318,7 +356,7 @@ def playlist_item_from_kodi(kodi_item): except IndexError: query = '' query = dict(utils.parse_qsl(query)) - item.plex_id = utils.cast(int, query.get('plex_id')) + item.plex_id = cast(int, query.get('plex_id')) item.plex_type = query.get('itemType') LOG.debug('Made playlist item from Kodi: %s', item) return item @@ -445,18 +483,13 @@ def get_playlist_details_from_xml(playlist, xml): """ if xml is None: raise PlaylistError('No playlist received for playlist %s' % playlist) - playlist.id = utils.cast(int, - xml.get('%sID' % playlist.kind)) - playlist.version = utils.cast(int, - xml.get('%sVersion' % playlist.kind)) - playlist.shuffled = utils.cast(int, - xml.get('%sShuffled' % playlist.kind)) - playlist.selectedItemID = utils.cast(int, - xml.get('%sSelectedItemID' - % playlist.kind)) - playlist.selectedItemOffset = utils.cast(int, - xml.get('%sSelectedItemOffset' - % playlist.kind)) + playlist.id = cast(int, xml.get('%sID' % playlist.kind)) + playlist.version = cast(int, xml.get('%sVersion' % playlist.kind)) + playlist.shuffled = cast(int, xml.get('%sShuffled' % playlist.kind)) + playlist.selectedItemID = cast(int, xml.get('%sSelectedItemID' + % playlist.kind)) + playlist.selectedItemOffset = cast(int, xml.get('%sSelectedItemOffset' + % playlist.kind)) LOG.debug('Updated playlist from xml: %s', playlist) diff --git a/resources/lib/plex_functions.py b/resources/lib/plex_functions.py index dc26a164..08f5a2a3 100644 --- a/resources/lib/plex_functions.py +++ b/resources/lib/plex_functions.py @@ -1133,3 +1133,19 @@ def change_subtitle(plex_stream_id, part_id): url = '{server}/library/parts/%s' % part_id return DU().downloadUrl(utils.extend_url(url, arguments), action_type='PUT') + + +def change_audio_stream(plex_stream_id, part_id): + """ + Tell the PMS to display/burn-in the subtitle stream with id plex_stream_id + for the Part (PMS XML etree tag "Part") with unique id part_id. + - plex_stream_id = 0 will deactivate the subtitle + - We always do this for ALL parts of a video + """ + arguments = { + 'audioStreamID': plex_stream_id, + 'allParts': 1 + } + url = '{server}/library/parts/%s' % part_id + return DU().downloadUrl(utils.extend_url(url, arguments), + action_type='PUT') diff --git a/resources/lib/variables.py b/resources/lib/variables.py index b50a0490..1a31759d 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -45,6 +45,10 @@ ADDON_PATH = try_decode(_ADDON.getAddonInfo('path')) ADDON_FOLDER = try_decode(xbmc.translatePath('special://home')) ADDON_PROFILE = try_decode(xbmc.translatePath(_ADDON.getAddonInfo('profile'))) +# Used e.g. for json_rpc +KODI_VIDEO_PLAYER_ID = 1 +KODI_AUDIO_PLAYER_ID = 0 + KODILANGUAGE = xbmc.getLanguage(xbmc.ISO_639_1) KODIVERSION = int(xbmc.getInfoLabel("System.BuildVersion")[:2]) KODILONGVERSION = xbmc.getInfoLabel('System.BuildVersion') From d80d3525b346cfafca65674c9c4eda55f233d356 Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 24 Sep 2021 17:39:56 +0200 Subject: [PATCH 11/17] Beta version bump 2.14.4 --- addon.xml | 11 +++++++++-- changelog.txt | 7 +++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/addon.xml b/addon.xml index 1bf33ad8..e9d792a3 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + @@ -88,7 +88,14 @@ Plex를 Kodi에 기본 통합 Kodi를 Plex Media Server에 연결합니다. 이 플러그인은 Plex로 모든 비디오를 관리하고 Kodi로는 관리하지 않는다고 가정합니다. Kodi 비디오 및 음악 데이터베이스에 이미 저장된 데이터가 손실 될 수 있습니다 (이 플러그인이 직접 변경하므로). 자신의 책임하에 사용하십시오! 자신의 책임하에 사용 - version 2.14.3 (beta only): + version 2.14.4 (beta only): +- Tell the PMS if a video's audio stream or potentially subtitle stream has changed. For subtitles, this functionality is broken due to a Kodi bug +- Transcoding: Fix Plex burning-in subtitles when it should not +- Fix logging if fanart.tv lookup fails: be less verbose +- Large refactoring of playlist and playqueue code +- Refactor usage of a media part's id + +version 2.14.3 (beta only): - Implement "Reset resume position" from the Kodi context menu version 2.14.2: diff --git a/changelog.txt b/changelog.txt index 57527792..4f2de4d0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +version 2.14.4 (beta only): +- Tell the PMS if a video's audio stream or potentially subtitle stream has changed. For subtitles, this functionality is broken due to a Kodi bug +- Transcoding: Fix Plex burning-in subtitles when it should not +- Fix logging if fanart.tv lookup fails: be less verbose +- Large refactoring of playlist and playqueue code +- Refactor usage of a media part's id + version 2.14.3 (beta only): - Implement "Reset resume position" from the Kodi context menu From 436d2e4391a39f574e385ffec9a23201af42f97f Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 30 Sep 2021 14:17:52 +0200 Subject: [PATCH 12/17] Direct Paths: Fix TypeError: element indices must be integers for subtitles --- resources/lib/plex_api/base.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/lib/plex_api/base.py b/resources/lib/plex_api/base.py index 183166a9..f73a1db5 100644 --- a/resources/lib/plex_api/base.py +++ b/resources/lib/plex_api/base.py @@ -254,13 +254,21 @@ class Base(object): Returns the media streams directly from the PMS xml. Mind to set self.mediastream and self.part before calling this method! """ - return self.xml[self.mediastream][self.part] + try: + return self.xml[self.mediastream][self.part] + except TypeError: + # Direct Paths when we don't set mediastream and part + return self.xml[0][0] def part_id(self): """ Returns the unique id of the currently active part [int] """ - return int(self.xml[self.mediastream][self.part].attrib['id']) + try: + return int(self.xml[self.mediastream][self.part].attrib['id']) + except TypeError: + # Direct Paths when we don't set mediastream and part + return int(self.xml[0][0].attrib['id']) def plot(self): """ From bdc98d0352720bcddad7f0095d5a1147e3036735 Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 30 Sep 2021 13:31:23 +0200 Subject: [PATCH 13/17] Fix whitespace --- resources/lib/plex_api/base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/lib/plex_api/base.py b/resources/lib/plex_api/base.py index 183166a9..7cdc8d7e 100644 --- a/resources/lib/plex_api/base.py +++ b/resources/lib/plex_api/base.py @@ -17,12 +17,15 @@ METADATA_PROVIDERS = (('imdb', utils.REGEX_IMDB), ('tvdb', utils.REGEX_TVDB), ('tmdb', utils.REGEX_TMDB), ('anidb', utils.REGEX_ANIDB)) + + class Base(object): """ Processes a Plex media server's XML response xml: xml.etree.ElementTree element """ + def __init__(self, xml): self.xml = xml # which media part in the XML response shall we look at if several From 61114e0d2ef8f147a3ff9bf883033599d22ce883 Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 30 Sep 2021 12:13:29 +0200 Subject: [PATCH 14/17] Refactor and fix Kodi not activating subtitle when it should --- resources/lib/kodimonitor.py | 69 ++++------------------------------ resources/lib/playlist_func.py | 52 +++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 62 deletions(-) diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index 466a6280..62e7e031 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -383,72 +383,17 @@ class KodiMonitor(xbmc.Monitor): if not playerid == v.KODI_VIDEO_PLAYER_ID: # We're just messing with Kodi's videoplayer return - if not self._switched_to_plex_streams: - # We need to switch to the Plex streams ONCE upon playback start - # after onavchange has been fired - self.switch_to_plex_streams() - self._switched_to_plex_streams = True - else: - item = app.PLAYSTATE.item - if item is None: - # Player might've quit - return - kodi_audio_stream = js.get_current_audio_stream_index(playerid) - sub_enabled = js.get_subtitle_enabled(playerid) - kodi_sub_stream = js.get_current_subtitle_stream_index(playerid) - # Audio - if kodi_audio_stream != item.current_kodi_audio_stream: - item.on_kodi_audio_stream_change(kodi_audio_stream) - # Subtitles - CURRENTLY BROKEN ON THE KODI SIDE! - # current_kodi_sub_stream may also be zero - subs_off = (None, False) - if ((sub_enabled and item.current_kodi_sub_stream in subs_off) - or (not sub_enabled and item.current_kodi_sub_stream not in subs_off) - or (kodi_sub_stream is not None - and kodi_sub_stream != item.current_kodi_sub_stream)): - item.on_kodi_subtitle_stream_change(kodi_sub_stream, - sub_enabled) - - @staticmethod - def switch_to_plex_streams(): - """ - Override Kodi audio and subtitle streams with Plex PMS' selection - """ item = app.PLAYSTATE.item if item is None: # Player might've quit return - for typus in ('audio', 'subtitle'): - try: - plex_index, language_tag = item.active_plex_stream_index(typus) - except TypeError: - LOG.debug('Deactivating Kodi subtitles because the PMS ' - 'told us to not show any subtitles') - app.APP.player.showSubtitles(False) - item.current_kodi_sub_stream = False - continue - LOG.debug('The PMS wants to display %s stream with Plex id %s and ' - 'languageTag %s', - typus, plex_index, language_tag) - kodi_index = item.kodi_stream_index(plex_index, typus) - if kodi_index is None: - LOG.debug('Leaving Kodi %s stream settings untouched since we ' - 'could not parse Plex %s stream with id %s to a Kodi' - ' index', typus, typus, plex_index) - else: - LOG.debug('Switching to Kodi %s stream number %s because the ' - 'PMS told us to show stream with Plex id %s', - typus, kodi_index, plex_index) - # If we're choosing an "illegal" index, this function does - # need seem to fail nor log any errors - if typus == 'audio': - app.APP.player.setAudioStream(kodi_index) - else: - app.APP.player.setSubtitleStream(kodi_index) - if typus == 'audio': - item.current_kodi_audio_stream = kodi_index - else: - item.current_kodi_sub_stream = kodi_index + if not self._switched_to_plex_streams: + # We need to switch to the Plex streams ONCE upon playback start + # after onavchange has been fired + item.switch_to_plex_streams() + self._switched_to_plex_streams = True + else: + item.on_av_change(playerid) def _playback_cleanup(ended=False): diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index e5c278eb..f3370a96 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -332,6 +332,58 @@ class PlaylistItem(object): PF.change_audio_stream(plex_stream_index, self.api.part_id()) self.current_kodi_audio_stream = kodi_stream_index + def switch_to_plex_streams(self): + self.switch_to_plex_stream('audio') + self.switch_to_plex_stream('subtitle') + + def switch_to_plex_stream(self, typus): + try: + plex_index, language_tag = self.active_plex_stream_index(typus) + except TypeError: + LOG.debug('Deactivating Kodi subtitles because the PMS ' + 'told us to not show any subtitles') + app.APP.player.showSubtitles(False) + self.current_kodi_sub_stream = False + return + LOG.debug('The PMS wants to display %s stream with Plex id %s and ' + 'languageTag %s', typus, plex_index, language_tag) + kodi_index = self.kodi_stream_index(plex_index, typus) + if kodi_index is None: + LOG.debug('Leaving Kodi %s stream settings untouched since we ' + 'could not parse Plex %s stream with id %s to a Kodi' + ' index', typus, typus, plex_index) + else: + LOG.debug('Switching to Kodi %s stream number %s because the ' + 'PMS told us to show stream with Plex id %s', + typus, kodi_index, plex_index) + # If we're choosing an "illegal" index, this function does + # need seem to fail nor log any errors + if typus == 'audio': + app.APP.player.setAudioStream(kodi_index) + else: + app.APP.player.setSubtitleStream(kodi_index) + app.APP.player.showSubtitles(True) + if typus == 'audio': + self.current_kodi_audio_stream = kodi_index + else: + self.current_kodi_sub_stream = kodi_index + + def on_av_change(self, playerid): + kodi_audio_stream = js.get_current_audio_stream_index(playerid) + sub_enabled = js.get_subtitle_enabled(playerid) + kodi_sub_stream = js.get_current_subtitle_stream_index(playerid) + # Audio + if kodi_audio_stream != self.current_kodi_audio_stream: + self.on_kodi_audio_stream_change(kodi_audio_stream) + # Subtitles - CURRENTLY BROKEN ON THE KODI SIDE! + # current_kodi_sub_stream may also be zero + subs_off = (None, False) + if ((sub_enabled and self.current_kodi_sub_stream in subs_off) + or (not sub_enabled and self.current_kodi_sub_stream not in subs_off) + or (kodi_sub_stream is not None + and kodi_sub_stream != self.current_kodi_sub_stream)): + self.on_kodi_subtitle_stream_change(kodi_sub_stream, sub_enabled) + def playlist_item_from_kodi(kodi_item): """ From f96c246244f6e72a7270e211b80fbf7f5f4a1ff6 Mon Sep 17 00:00:00 2001 From: croneter Date: Sat, 9 Oct 2021 17:23:47 +0200 Subject: [PATCH 15/17] Add playback settings to let the user choose whether Plex or Kodi provides the default audio and subtitle streams on playback start --- resources/language/resource.language.en_gb/strings.po | 10 ++++++++++ resources/lib/kodimonitor.py | 6 +++++- resources/settings.xml | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po index c83b1fe0..7493327f 100644 --- a/resources/language/resource.language.en_gb/strings.po +++ b/resources/language/resource.language.en_gb/strings.po @@ -665,6 +665,16 @@ msgctxt "#30546" msgid "Pick the first video if several versions are present" msgstr "" +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index 62e7e031..36617ff9 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -29,6 +29,7 @@ class KodiMonitor(xbmc.Monitor): """ PKC implementation of the Kodi Monitor class. Invoke only once. """ + def __init__(self): self._already_slept = False self._switched_to_plex_streams = True @@ -390,7 +391,10 @@ class KodiMonitor(xbmc.Monitor): if not self._switched_to_plex_streams: # We need to switch to the Plex streams ONCE upon playback start # after onavchange has been fired - item.switch_to_plex_streams() + if utils.settings('audioStreamPick') == '0': + item.switch_to_plex_stream('audio') + if utils.settings('subtitleStreamPick') == '0': + item.switch_to_plex_stream('subtitle') self._switched_to_plex_streams = True else: item.on_av_change(playerid) diff --git a/resources/settings.xml b/resources/settings.xml index bb6924db..400c3dfe 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -114,6 +114,8 @@ + + From e5fa5de6703c1b4cd99cd4e1328d8bc6229db260 Mon Sep 17 00:00:00 2001 From: croneter Date: Sat, 9 Oct 2021 18:09:12 +0200 Subject: [PATCH 16/17] Update translations from Transifex --- .../resource.language.cs_CZ/strings.po | 15 +++++++++++++++ .../resource.language.da_DK/strings.po | 15 +++++++++++++++ .../resource.language.de_DE/strings.po | 15 +++++++++++++++ .../resource.language.el_GR/strings.po | 15 +++++++++++++++ .../resource.language.es_AR/strings.po | 15 +++++++++++++++ .../resource.language.es_ES/strings.po | 15 +++++++++++++++ .../resource.language.es_MX/strings.po | 15 +++++++++++++++ .../resource.language.fr_CA/strings.po | 15 +++++++++++++++ .../resource.language.fr_FR/strings.po | 15 +++++++++++++++ .../resource.language.hu_HU/strings.po | 15 +++++++++++++++ .../resource.language.it_IT/strings.po | 15 +++++++++++++++ .../resource.language.ko_KR/strings.po | 15 +++++++++++++++ .../resource.language.lt_LT/strings.po | 15 +++++++++++++++ .../resource.language.lv_LV/strings.po | 15 +++++++++++++++ .../resource.language.nl_NL/strings.po | 15 +++++++++++++++ .../resource.language.no_NO/strings.po | 15 +++++++++++++++ .../resource.language.pl_PL/strings.po | 19 +++++++++++++++++-- .../resource.language.pt_BR/strings.po | 15 +++++++++++++++ .../resource.language.pt_PT/strings.po | 15 +++++++++++++++ .../resource.language.ru_RU/strings.po | 15 +++++++++++++++ .../resource.language.sv_SE/strings.po | 15 +++++++++++++++ .../resource.language.uk_UA/strings.po | 15 +++++++++++++++ .../resource.language.zh_CN/strings.po | 15 +++++++++++++++ .../resource.language.zh_TW/strings.po | 15 +++++++++++++++ 24 files changed, 362 insertions(+), 2 deletions(-) diff --git a/resources/language/resource.language.cs_CZ/strings.po b/resources/language/resource.language.cs_CZ/strings.po index 1d4fcef7..13f7d9c6 100644 --- a/resources/language/resource.language.cs_CZ/strings.po +++ b/resources/language/resource.language.cs_CZ/strings.po @@ -702,6 +702,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Vynutit překódování obrázků" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.da_DK/strings.po b/resources/language/resource.language.da_DK/strings.po index 6470e370..c6162e93 100644 --- a/resources/language/resource.language.da_DK/strings.po +++ b/resources/language/resource.language.da_DK/strings.po @@ -702,6 +702,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Transcode billeder" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.de_DE/strings.po b/resources/language/resource.language.de_DE/strings.po index a0232cc1..7f18ec32 100644 --- a/resources/language/resource.language.de_DE/strings.po +++ b/resources/language/resource.language.de_DE/strings.po @@ -715,6 +715,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Bilder immer transkodieren" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "Ersten Videostream wählen, wenn mehrere Versionen vorhanden sind" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "Wer wählt den Audiotrack beim Start der Wiedergabe?" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "Wer wählt Untertitel beim Start der Wiedergabe?" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.el_GR/strings.po b/resources/language/resource.language.el_GR/strings.po index b016d88c..ac4eb2a2 100644 --- a/resources/language/resource.language.el_GR/strings.po +++ b/resources/language/resource.language.el_GR/strings.po @@ -690,6 +690,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.es_AR/strings.po b/resources/language/resource.language.es_AR/strings.po index ad88103e..299dc9e8 100644 --- a/resources/language/resource.language.es_AR/strings.po +++ b/resources/language/resource.language.es_AR/strings.po @@ -710,6 +710,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Obligar transcodificar fotografías" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.es_ES/strings.po b/resources/language/resource.language.es_ES/strings.po index ad6e4d14..ba8f2c17 100644 --- a/resources/language/resource.language.es_ES/strings.po +++ b/resources/language/resource.language.es_ES/strings.po @@ -712,6 +712,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Obligar transcodificar fotografías" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.es_MX/strings.po b/resources/language/resource.language.es_MX/strings.po index e57fbbb4..29c14f97 100644 --- a/resources/language/resource.language.es_MX/strings.po +++ b/resources/language/resource.language.es_MX/strings.po @@ -710,6 +710,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Obligar transcodificar fotografías" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.fr_CA/strings.po b/resources/language/resource.language.fr_CA/strings.po index eeb2598f..9714c428 100644 --- a/resources/language/resource.language.fr_CA/strings.po +++ b/resources/language/resource.language.fr_CA/strings.po @@ -715,6 +715,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Forcer le transcodage des images" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.fr_FR/strings.po b/resources/language/resource.language.fr_FR/strings.po index 7e0ab03d..dd1ddb09 100644 --- a/resources/language/resource.language.fr_FR/strings.po +++ b/resources/language/resource.language.fr_FR/strings.po @@ -719,6 +719,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Forcer le transcodage des images" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.hu_HU/strings.po b/resources/language/resource.language.hu_HU/strings.po index 9b9b765e..04cd89f3 100644 --- a/resources/language/resource.language.hu_HU/strings.po +++ b/resources/language/resource.language.hu_HU/strings.po @@ -718,6 +718,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Képek transzkódolásának kényszerítése" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.it_IT/strings.po b/resources/language/resource.language.it_IT/strings.po index 4d84902e..a08dee26 100644 --- a/resources/language/resource.language.it_IT/strings.po +++ b/resources/language/resource.language.it_IT/strings.po @@ -710,6 +710,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Forza transcodifica immagini" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.ko_KR/strings.po b/resources/language/resource.language.ko_KR/strings.po index 7483d39b..7752a1f8 100644 --- a/resources/language/resource.language.ko_KR/strings.po +++ b/resources/language/resource.language.ko_KR/strings.po @@ -697,6 +697,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "사진 트랜스 코딩 강제" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.lt_LT/strings.po b/resources/language/resource.language.lt_LT/strings.po index aca329b1..c618ae10 100644 --- a/resources/language/resource.language.lt_LT/strings.po +++ b/resources/language/resource.language.lt_LT/strings.po @@ -706,6 +706,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Priverstinai perkoduoti nuotraukas" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.lv_LV/strings.po b/resources/language/resource.language.lv_LV/strings.po index 7c3e1a42..fd869e8b 100644 --- a/resources/language/resource.language.lv_LV/strings.po +++ b/resources/language/resource.language.lv_LV/strings.po @@ -699,6 +699,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Uzspiest attēlu pārkodēšanu" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.nl_NL/strings.po b/resources/language/resource.language.nl_NL/strings.po index abe4c3c3..0aacdd81 100644 --- a/resources/language/resource.language.nl_NL/strings.po +++ b/resources/language/resource.language.nl_NL/strings.po @@ -707,6 +707,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Forceer transcoden van foto's" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.no_NO/strings.po b/resources/language/resource.language.no_NO/strings.po index 06af7807..c0e44000 100644 --- a/resources/language/resource.language.no_NO/strings.po +++ b/resources/language/resource.language.no_NO/strings.po @@ -704,6 +704,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Tving transkoding av bilde" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.pl_PL/strings.po b/resources/language/resource.language.pl_PL/strings.po index e1305f91..a0660851 100644 --- a/resources/language/resource.language.pl_PL/strings.po +++ b/resources/language/resource.language.pl_PL/strings.po @@ -3,7 +3,7 @@ # Croneter None , 2017 # Wiktor Dackiewicz , 2017 # Kacpolz , 2019 -# Top Secret , 2020 +# Ziuta , 2020 # msgid "" msgstr "" @@ -11,7 +11,7 @@ msgstr "" "Report-Msgid-Bugs-To: croneter@gmail.com\n" "POT-Creation-Date: 2017-04-15 13:13+0000\n" "PO-Revision-Date: 2017-04-30 08:30+0000\n" -"Last-Translator: Top Secret , 2020\n" +"Last-Translator: Ziuta , 2020\n" "Language-Team: Polish (Poland) (https://www.transifex.com/croneter/teams/73837/pl_PL/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -695,6 +695,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.pt_BR/strings.po b/resources/language/resource.language.pt_BR/strings.po index b64dba90..71396a79 100644 --- a/resources/language/resource.language.pt_BR/strings.po +++ b/resources/language/resource.language.pt_BR/strings.po @@ -694,6 +694,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Forçar transcodificação de imagens" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.pt_PT/strings.po b/resources/language/resource.language.pt_PT/strings.po index 7198699e..41484f77 100644 --- a/resources/language/resource.language.pt_PT/strings.po +++ b/resources/language/resource.language.pt_PT/strings.po @@ -697,6 +697,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Forçar transcodificação de imagens" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.ru_RU/strings.po b/resources/language/resource.language.ru_RU/strings.po index 3e80b3cb..047e8fab 100644 --- a/resources/language/resource.language.ru_RU/strings.po +++ b/resources/language/resource.language.ru_RU/strings.po @@ -711,6 +711,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Принудительно транскодировать изображения" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.sv_SE/strings.po b/resources/language/resource.language.sv_SE/strings.po index 8af56049..8d87d11c 100644 --- a/resources/language/resource.language.sv_SE/strings.po +++ b/resources/language/resource.language.sv_SE/strings.po @@ -706,6 +706,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Tvinga omkodning av bilder" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.uk_UA/strings.po b/resources/language/resource.language.uk_UA/strings.po index 3cdc4313..95be95d7 100644 --- a/resources/language/resource.language.uk_UA/strings.po +++ b/resources/language/resource.language.uk_UA/strings.po @@ -706,6 +706,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "Примусове перекодування зображень" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.zh_CN/strings.po b/resources/language/resource.language.zh_CN/strings.po index 955eaa48..d9eda772 100644 --- a/resources/language/resource.language.zh_CN/strings.po +++ b/resources/language/resource.language.zh_CN/strings.po @@ -689,6 +689,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "强制图片转码" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" diff --git a/resources/language/resource.language.zh_TW/strings.po b/resources/language/resource.language.zh_TW/strings.po index 1df787d9..dfdcb423 100644 --- a/resources/language/resource.language.zh_TW/strings.po +++ b/resources/language/resource.language.zh_TW/strings.po @@ -687,6 +687,21 @@ msgctxt "#30545" msgid "Force transcode pictures" msgstr "強制圖片轉碼" +# PKC Settings - Playback +msgctxt "#30546" +msgid "Pick the first video if several versions are present" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30547" +msgid "Who picks the audio stream on playback start?" +msgstr "" + +# PKC Settings - Playback +msgctxt "#30548" +msgid "Who picks subtitles on playback start?" +msgstr "" + # Welcome to Plex notification msgctxt "#33000" msgid "Welcome" From 4cfcc4c1f818f6d58daec01cc6a69d9ab31e7363 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 17 Oct 2021 12:07:18 +0200 Subject: [PATCH 17/17] Beta and stable version bump 2.15.0 --- addon.xml | 11 +++++++++-- changelog.txt | 7 +++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/addon.xml b/addon.xml index e9d792a3..ffbdd078 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + @@ -88,7 +88,14 @@ Plex를 Kodi에 기본 통합 Kodi를 Plex Media Server에 연결합니다. 이 플러그인은 Plex로 모든 비디오를 관리하고 Kodi로는 관리하지 않는다고 가정합니다. Kodi 비디오 및 음악 데이터베이스에 이미 저장된 데이터가 손실 될 수 있습니다 (이 플러그인이 직접 변경하므로). 자신의 책임하에 사용하십시오! 자신의 책임하에 사용 - version 2.14.4 (beta only): + version 2.15.0: +- versions 2.14.3-2.14.4 for everyone +- Direct Paths: Fix TypeError: "element indices must be integers" on playback startup [backport] +- Refactor stream code and fix Kodi not activating subtitle when it should [backport] +- Add playback settings to let the user choose whether Plex or Kodi provides the default audio or subtitle stream on playback start [backport] +- Update translations from Transifex [backport] + +version 2.14.4 (beta only): - Tell the PMS if a video's audio stream or potentially subtitle stream has changed. For subtitles, this functionality is broken due to a Kodi bug - Transcoding: Fix Plex burning-in subtitles when it should not - Fix logging if fanart.tv lookup fails: be less verbose diff --git a/changelog.txt b/changelog.txt index 4f2de4d0..d23bb77d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +version 2.15.0: +- versions 2.14.3-2.14.4 for everyone +- Direct Paths: Fix TypeError: "element indices must be integers" on playback startup [backport] +- Refactor stream code and fix Kodi not activating subtitle when it should [backport] +- Add playback settings to let the user choose whether Plex or Kodi provides the default audio or subtitle stream on playback start [backport] +- Update translations from Transifex [backport] + version 2.14.4 (beta only): - Tell the PMS if a video's audio stream or potentially subtitle stream has changed. For subtitles, this functionality is broken due to a Kodi bug - Transcoding: Fix Plex burning-in subtitles when it should not