Merge branch 'hotfixes' of https://github.com/croneter/PlexKodiConnect into hotfixes
This commit is contained in:
commit
6c851bd3a6
12 changed files with 672 additions and 802 deletions
|
@ -1,5 +1,5 @@
|
|||
[![stable version](https://img.shields.io/badge/stable_version-1.8.18-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip)
|
||||
[![beta version](https://img.shields.io/badge/beta_version-2.0.3-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip)
|
||||
[![beta version](https://img.shields.io/badge/beta_version-2.0.4-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip)
|
||||
|
||||
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation)
|
||||
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)
|
||||
|
|
19
addon.xml
19
addon.xml
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.0.3" provider-name="croneter">
|
||||
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.0.4" provider-name="croneter">
|
||||
<requires>
|
||||
<import addon="xbmc.python" version="2.1.0"/>
|
||||
<import addon="script.module.requests" version="2.9.1" />
|
||||
|
@ -61,7 +61,22 @@
|
|||
<summary lang="da_DK">Indbygget Integration af Plex i Kodi</summary>
|
||||
<description lang="da_DK">Tilslut Kodi til din Plex Media Server. Dette plugin forudsætter, at du administrere alle dine videoer med Plex (og ikke med Kodi). Du kan miste data som allerede er gemt i Kodi video og musik-databaser (dette plugin ændrer direkte i dem). Brug på eget ansvar!</description>
|
||||
<disclaimer lang="da_DK">Brug på eget ansvar</disclaimer>
|
||||
<news>version 2.0.3 (beta only):
|
||||
<news>version 2.0.4 (beta only):
|
||||
- WARNING: You will need to reset the Kodi database!
|
||||
- Many improvements to the Kodi database handling which should get rid of some weird bugs
|
||||
- Many improvements to playback startup
|
||||
- Fix info screen and actors not working
|
||||
- Fix Companion displaying and selecting wrong subtitle
|
||||
- Don't cache subtitles if direct playing
|
||||
- Wipe all existing resume point, e.g. on user switch
|
||||
- Don't mess with Kodi's screensaver settings
|
||||
- Inhibit idle shutdown only during initial sync
|
||||
- Fix KeyError for server discovery
|
||||
- Increase Python requests dependency to version 2.9.1
|
||||
- Re-introduce PlexKodiConnect dependency add-ons for movies and tv shows
|
||||
- And a lot of other stuff
|
||||
|
||||
version 2.0.3 (beta only):
|
||||
- Fix Alexa playback
|
||||
- Fix Kodi boot loop
|
||||
- Fix playback being reported to the wrong Plex user
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
version 2.0.4 (beta only):
|
||||
- WARNING: You will need to reset the Kodi database!
|
||||
- Many improvements to the Kodi database handling which should get rid of some weird bugs
|
||||
- Many improvements to playback startup
|
||||
- Fix info screen and actors not working
|
||||
- Fix Companion displaying and selecting wrong subtitle
|
||||
- Don't cache subtitles if direct playing
|
||||
- Wipe all existing resume point, e.g. on user switch
|
||||
- Don't mess with Kodi's screensaver settings
|
||||
- Inhibit idle shutdown only during initial sync
|
||||
- Fix KeyError for server discovery
|
||||
- Increase Python requests dependency to version 2.9.1
|
||||
- Re-introduce PlexKodiConnect dependency add-ons for movies and tv shows
|
||||
- And a lot of other stuff
|
||||
|
||||
version 2.0.3 (beta only):
|
||||
- Fix Alexa playback
|
||||
- Fix Kodi boot loop
|
||||
|
|
|
@ -1670,10 +1670,6 @@ msgctxt "#39213"
|
|||
msgid "is offline"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#39214"
|
||||
msgid "Even though we signed in to plex.tv, we could not authorize for PMS"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "#39215"
|
||||
msgid "Enter your Plex Media Server's IP or URL, Examples are:"
|
||||
msgstr ""
|
||||
|
|
|
@ -41,7 +41,7 @@ from xbmcvfs import exists
|
|||
import clientinfo as client
|
||||
from downloadutils import DownloadUtils as DU
|
||||
from utils import window, settings, language as lang, try_decode, try_encode, \
|
||||
unix_date_to_kodi, exists_dir, slugify, dialog
|
||||
unix_date_to_kodi, exists_dir, slugify, dialog, escape_html
|
||||
import PlexFunctions as PF
|
||||
import plexdb_functions as plexdb
|
||||
import variables as v
|
||||
|
@ -337,22 +337,14 @@ class API(object):
|
|||
"""
|
||||
people = []
|
||||
for child in self.item:
|
||||
if child.tag in PEOPLE_OF_INTEREST.keys():
|
||||
name = child.attrib['tag']
|
||||
name_id = child.attrib['id']
|
||||
typus = PEOPLE_OF_INTEREST[child.tag]
|
||||
url = child.get('thumb')
|
||||
role = child.get('role')
|
||||
if child.tag in PEOPLE_OF_INTEREST:
|
||||
people.append({
|
||||
'Name': name,
|
||||
'Type': typus,
|
||||
'Id': name_id,
|
||||
'imageurl': url
|
||||
'Name': child.attrib['tag'],
|
||||
'Type': PEOPLE_OF_INTEREST[child.tag],
|
||||
'Id': child.attrib['id'],
|
||||
'imageurl': child.get('thumb'),
|
||||
'Role': child.get('role')
|
||||
})
|
||||
if url:
|
||||
people[-1].update({'imageurl': url})
|
||||
if role:
|
||||
people[-1].update({'Role': role})
|
||||
return people
|
||||
|
||||
def genre_list(self):
|
||||
|
@ -365,6 +357,17 @@ class API(object):
|
|||
genre.append(child.attrib['tag'])
|
||||
return genre
|
||||
|
||||
def guid_html_escaped(self):
|
||||
"""
|
||||
Returns the 'guid' attribute, e.g.
|
||||
'com.plexapp.agents.thetvdb://76648/2/4?lang=en'
|
||||
as an HTML-escaped string or None
|
||||
"""
|
||||
answ = self.item.get('guid')
|
||||
if answ is not None:
|
||||
answ = escape_html(answ)
|
||||
return answ
|
||||
|
||||
def provider(self, providername=None):
|
||||
"""
|
||||
providername: e.g. 'imdb', 'tvdb'
|
||||
|
@ -642,14 +645,15 @@ class API(object):
|
|||
"""
|
||||
Returns the ratingKey (plex_id) of the trailer or None
|
||||
"""
|
||||
for extra in self.item.iterfind('Extras'):
|
||||
try:
|
||||
typus = int(extra.attrib['extraType'])
|
||||
except (KeyError, TypeError):
|
||||
typus = None
|
||||
if typus != 1:
|
||||
continue
|
||||
return extra.get('ratingKey')
|
||||
for extras in self.item.iterfind('Extras'):
|
||||
for extra in extras:
|
||||
try:
|
||||
typus = int(extra.attrib['extraType'])
|
||||
except (KeyError, TypeError):
|
||||
typus = None
|
||||
if typus != 1:
|
||||
continue
|
||||
return extra.get('ratingKey')
|
||||
|
||||
def mediastreams(self):
|
||||
"""
|
||||
|
|
|
@ -310,15 +310,9 @@ class Artwork():
|
|||
self.cacheTexture(imageUrl)
|
||||
|
||||
def deleteArtwork(self, kodiId, mediaType, cursor):
|
||||
query = ' '.join((
|
||||
"SELECT url",
|
||||
"FROM art",
|
||||
"WHERE media_id = ?",
|
||||
"AND media_type = ?"
|
||||
))
|
||||
query = 'SELECT url FROM art WHERE media_id = ? AND media_type = ?'
|
||||
cursor.execute(query, (kodiId, mediaType,))
|
||||
rows = cursor.fetchall()
|
||||
for row in rows:
|
||||
for row in cursor.fetchall():
|
||||
self.deleteCachedArtwork(row[0])
|
||||
|
||||
def deleteCachedArtwork(self, url):
|
||||
|
@ -330,7 +324,7 @@ class Artwork():
|
|||
(url,))
|
||||
cachedurl = cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
LOG.info("Could not find cached url.")
|
||||
LOG.debug("Could not find cached url.")
|
||||
else:
|
||||
# Delete thumbnail as well as the entry
|
||||
path = translatePath("special://thumbnails/%s" % cachedurl)
|
||||
|
|
|
@ -317,7 +317,6 @@ class InitialSetup(object):
|
|||
Returns server or None if unsuccessful
|
||||
"""
|
||||
https_updated = False
|
||||
checked_plex_tv = False
|
||||
server = None
|
||||
while True:
|
||||
if https_updated is False:
|
||||
|
@ -340,25 +339,6 @@ class InitialSetup(object):
|
|||
server['scheme'] = 'https'
|
||||
https_updated = True
|
||||
continue
|
||||
if chk == 401:
|
||||
LOG.warn('Not yet authorized for Plex server %s',
|
||||
server['name'])
|
||||
if self.check_plex_tv_sign_in() is True:
|
||||
if checked_plex_tv is False:
|
||||
# Try again
|
||||
checked_plex_tv = True
|
||||
https_updated = False
|
||||
continue
|
||||
else:
|
||||
LOG.warn('Not authorized even though we are signed '
|
||||
' in to plex.tv correctly')
|
||||
dialog('ok',
|
||||
lang(29999),
|
||||
'%s %s' % (lang(39214),
|
||||
try_encode(server['name'])))
|
||||
return
|
||||
else:
|
||||
return
|
||||
# Problems connecting
|
||||
elif chk >= 400 or chk is False:
|
||||
LOG.warn('Problems connecting to server %s. chk is %s',
|
||||
|
|
|
@ -48,7 +48,7 @@ class Items(object):
|
|||
self.kodiconn = kodi_sql('video')
|
||||
self.kodicursor = self.kodiconn.cursor()
|
||||
self.plex_db = plexdb.Plex_DB_Functions(self.plexcursor)
|
||||
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
|
||||
self.kodi_db = kodidb.KodiDBMethods(self.kodicursor)
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
|
@ -326,6 +326,7 @@ class Movies(Items):
|
|||
"imdb",
|
||||
uniqueid)
|
||||
else:
|
||||
self.kodi_db.remove_uniqueid(movieid, v.KODI_TYPE_MOVIE)
|
||||
uniqueid = -1
|
||||
query = '''
|
||||
UPDATE movie
|
||||
|
@ -360,7 +361,8 @@ class Movies(Items):
|
|||
LOG.info("ADD movie itemid: %s - Title: %s", itemid, title)
|
||||
if v.KODIVERSION >= 17:
|
||||
# add new ratings Kodi 17
|
||||
rating_id = self.kodi_db.create_entry_rating()
|
||||
rating_id = self.kodi_db.get_ratingid(movieid,
|
||||
v.KODI_TYPE_MOVIE)
|
||||
self.kodi_db.add_ratings(rating_id,
|
||||
movieid,
|
||||
v.KODI_TYPE_MOVIE,
|
||||
|
@ -369,7 +371,8 @@ class Movies(Items):
|
|||
votecount)
|
||||
# add new uniqueid Kodi 17
|
||||
if imdb is not None:
|
||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||
uniqueid = self.kodi_db.get_uniqueid(movieid,
|
||||
v.KODI_TYPE_MOVIE)
|
||||
self.kodi_db.add_uniqueid(uniqueid,
|
||||
movieid,
|
||||
v.KODI_TYPE_MOVIE,
|
||||
|
@ -434,23 +437,23 @@ class Movies(Items):
|
|||
kodicursor.execute(query, (pathid, filename, dateadded, fileid))
|
||||
|
||||
# Process countries
|
||||
self.kodi_db.addCountries(movieid, countries, "movie")
|
||||
self.kodi_db.modify_countries(movieid, v.KODI_TYPE_MOVIE, countries)
|
||||
# Process cast
|
||||
self.kodi_db.addPeople(movieid, api.people_list(), "movie")
|
||||
# Process genres
|
||||
self.kodi_db.addGenres(movieid, genres, "movie")
|
||||
self.kodi_db.modify_genres(movieid, v.KODI_TYPE_MOVIE, genres)
|
||||
# Process artwork
|
||||
artwork.addArtwork(api.artwork(), movieid, "movie", kodicursor)
|
||||
# Process stream details
|
||||
self.kodi_db.addStreams(fileid, api.mediastreams(), runtime)
|
||||
self.kodi_db.modify_streams(fileid, api.mediastreams(), runtime)
|
||||
# Process studios
|
||||
self.kodi_db.addStudios(movieid, studios, "movie")
|
||||
self.kodi_db.modify_studios(movieid, v.KODI_TYPE_MOVIE, studios)
|
||||
# Process tags: view, Plex collection tags
|
||||
tags = [viewtag]
|
||||
tags.extend(collections)
|
||||
if userdata['Favorite']:
|
||||
tags.append("Favorite movies")
|
||||
self.kodi_db.addTags(movieid, tags, "movie")
|
||||
self.kodi_db.modify_tags(movieid, v.KODI_TYPE_MOVIE, tags)
|
||||
# Add any sets from Plex collection tags
|
||||
self.kodi_db.addSets(movieid, collections, kodicursor)
|
||||
# Process playstates
|
||||
|
@ -469,7 +472,7 @@ class Movies(Items):
|
|||
kodi_id = plex_dbitem[0]
|
||||
file_id = plex_dbitem[1]
|
||||
kodi_type = plex_dbitem[4]
|
||||
LOG.info("Removing %sid: %s file_id: %s",
|
||||
LOG.debug("Removing %sid: %s file_id: %s",
|
||||
kodi_type, kodi_id, file_id)
|
||||
except TypeError:
|
||||
return
|
||||
|
@ -480,11 +483,21 @@ class Movies(Items):
|
|||
artwork.deleteArtwork(kodi_id, kodi_type, kodicursor)
|
||||
|
||||
if kodi_type == v.KODI_TYPE_MOVIE:
|
||||
set_id = self.kodi_db.get_set_id(kodi_id)
|
||||
self.kodi_db.delete_countries(kodi_id, kodi_type)
|
||||
self.kodi_db.delete_people(kodi_id, kodi_type)
|
||||
self.kodi_db.delete_genre(kodi_id, kodi_type)
|
||||
self.kodi_db.delete_studios(kodi_id, kodi_type)
|
||||
self.kodi_db.delete_tags(kodi_id, kodi_type)
|
||||
self.kodi_db.modify_streams(file_id)
|
||||
self.kodi_db.delete_playstate(file_id)
|
||||
# Delete kodi movie and file
|
||||
kodicursor.execute("DELETE FROM movie WHERE idMovie = ?",
|
||||
(kodi_id,))
|
||||
kodicursor.execute("DELETE FROM files WHERE idFile = ?",
|
||||
(file_id,))
|
||||
if set_id:
|
||||
self.kodi_db.delete_possibly_empty_set(set_id)
|
||||
if v.KODIVERSION >= 17:
|
||||
self.kodi_db.remove_uniqueid(kodi_id, kodi_type)
|
||||
self.kodi_db.remove_ratings(kodi_id, kodi_type)
|
||||
|
@ -499,7 +512,7 @@ class Movies(Items):
|
|||
# Update plex reference
|
||||
plex_db.updateParentId(plexid, None)
|
||||
kodicursor.execute("DELETE FROM sets WHERE idSet = ?", (kodi_id,))
|
||||
LOG.info("Deleted %s %s from kodi database", kodi_type, itemid)
|
||||
LOG.debug("Deleted %s %s from kodi database", kodi_type, itemid)
|
||||
|
||||
|
||||
class TVShows(Items):
|
||||
|
@ -627,6 +640,7 @@ class TVShows(Items):
|
|||
"unknown",
|
||||
uniqueid)
|
||||
else:
|
||||
self.kodi_db.remove_uniqueid(showid, v.KODI_TYPE_SHOW)
|
||||
uniqueid = -1
|
||||
# Update the tvshow entry
|
||||
query = '''
|
||||
|
@ -677,7 +691,7 @@ class TVShows(Items):
|
|||
view_id=viewid)
|
||||
if v.KODIVERSION >= 17:
|
||||
# add new ratings Kodi 17
|
||||
rating_id = self.kodi_db.create_entry_rating()
|
||||
rating_id = self.kodi_db.get_ratingid(showid, v.KODI_TYPE_SHOW)
|
||||
self.kodi_db.add_ratings(rating_id,
|
||||
showid,
|
||||
v.KODI_TYPE_SHOW,
|
||||
|
@ -686,7 +700,8 @@ class TVShows(Items):
|
|||
votecount)
|
||||
# add new uniqueid Kodi 17
|
||||
if tvdb is not None:
|
||||
uniqueid = self.kodi_db.create_entry_uniqueid()
|
||||
uniqueid = self.kodi_db.get_uniqueid(showid,
|
||||
v.KODI_TYPE_SHOW)
|
||||
self.kodi_db.add_uniqueid(uniqueid,
|
||||
showid,
|
||||
v.KODI_TYPE_SHOW,
|
||||
|
@ -985,7 +1000,8 @@ class TVShows(Items):
|
|||
# Create the episode entry
|
||||
if v.KODIVERSION >= 17:
|
||||
# add new ratings Kodi 17
|
||||
rating_id = self.kodi_db.create_entry_rating()
|
||||
rating_id = self.kodi_db.get_ratingid(episodeid,
|
||||
v.KODI_TYPE_EPISODE)
|
||||
self.kodi_db.add_ratings(rating_id,
|
||||
episodeid,
|
||||
v.KODI_TYPE_EPISODE,
|
||||
|
@ -993,7 +1009,9 @@ class TVShows(Items):
|
|||
rating,
|
||||
votecount)
|
||||
# add new uniqueid Kodi 17
|
||||
self.kodi_db.add_uniqueid(self.kodi_db.create_entry_uniqueid(),
|
||||
uniqueid = self.kodi_db.get_uniqueid(episodeid,
|
||||
v.KODI_TYPE_EPISODE)
|
||||
self.kodi_db.add_uniqueid(uniqueid,
|
||||
episodeid,
|
||||
v.KODI_TYPE_EPISODE,
|
||||
tvdb,
|
||||
|
@ -1083,7 +1101,7 @@ class TVShows(Items):
|
|||
|
||||
# Process stream details
|
||||
streams = api.mediastreams()
|
||||
self.kodi_db.addStreams(fileid, streams, runtime)
|
||||
self.kodi_db.modify_streams(fileid, streams, runtime)
|
||||
# Process playstates
|
||||
self.kodi_db.addPlaystate(fileid,
|
||||
resume,
|
||||
|
@ -1203,6 +1221,9 @@ class TVShows(Items):
|
|||
Remove a TV show, and only the show, no seasons or episodes
|
||||
"""
|
||||
kodicursor = self.kodicursor
|
||||
self.kodi_db.delete_genre(kodi_id, v.KODI_TYPE_SHOW)
|
||||
self.kodi_db.delete_studios(kodi_id, v.KODI_TYPE_SHOW)
|
||||
self.kodi_db.delete_tags(kodi_id, v.KODI_TYPE_SHOW)
|
||||
self.artwork.deleteArtwork(kodi_id, v.KODI_TYPE_SHOW, kodicursor)
|
||||
kodicursor.execute("DELETE FROM tvshow WHERE idShow = ?", (kodi_id,))
|
||||
if v.KODIVERSION >= 17:
|
||||
|
@ -1220,15 +1241,18 @@ class TVShows(Items):
|
|||
(kodi_id,))
|
||||
LOG.info("Removed season: %s.", kodi_id)
|
||||
|
||||
def removeEpisode(self, kodi_id, fileid):
|
||||
def removeEpisode(self, kodi_id, file_id):
|
||||
"""
|
||||
Remove an episode, and episode only
|
||||
"""
|
||||
kodicursor = self.kodicursor
|
||||
self.kodi_db.delete_people(kodi_id, v.KODI_TYPE_EPISODE)
|
||||
self.kodi_db.modify_streams(file_id)
|
||||
self.kodi_db.delete_playstate(file_id)
|
||||
self.artwork.deleteArtwork(kodi_id, "episode", kodicursor)
|
||||
kodicursor.execute("DELETE FROM episode WHERE idEpisode = ?",
|
||||
(kodi_id,))
|
||||
kodicursor.execute("DELETE FROM files WHERE idFile = ?", (fileid,))
|
||||
kodicursor.execute("DELETE FROM files WHERE idFile = ?", (file_id,))
|
||||
if v.KODIVERSION >= 17:
|
||||
self.kodi_db.remove_uniqueid(kodi_id, v.KODI_TYPE_EPISODE)
|
||||
self.kodi_db.remove_ratings(kodi_id, v.KODI_TYPE_EPISODE)
|
||||
|
@ -1250,7 +1274,7 @@ class Music(Items):
|
|||
self.kodiconn = kodi_sql('music')
|
||||
self.kodicursor = self.kodiconn.cursor()
|
||||
self.plex_db = plexdb.Plex_DB_Functions(self.plexcursor)
|
||||
self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor)
|
||||
self.kodi_db = kodidb.KodiDBMethods(self.kodicursor)
|
||||
return self
|
||||
|
||||
@catch_exceptions(warnuser=True)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -24,9 +24,9 @@ import variables as v
|
|||
import state
|
||||
|
||||
###############################################################################
|
||||
|
||||
LOG = getLogger("PLEX." + __name__)
|
||||
|
||||
# Do we need to return ultimately with a setResolvedUrl?
|
||||
RESOLVE = True
|
||||
###############################################################################
|
||||
|
||||
|
||||
|
@ -49,13 +49,13 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True):
|
|||
"""
|
||||
LOG.info('playback_triage called with plex_id %s, plex_type %s, path %s',
|
||||
plex_id, plex_type, path)
|
||||
global RESOLVE
|
||||
RESOLVE = resolve
|
||||
if not state.AUTHENTICATED:
|
||||
LOG.error('Not yet authenticated for PMS, abort starting playback')
|
||||
if resolve is True:
|
||||
# Release default.py
|
||||
pickle_me(Playback_Successful())
|
||||
# "Unauthorized for PMS"
|
||||
dialog('notification', lang(29999), lang(30017))
|
||||
_ensure_resolve()
|
||||
return
|
||||
playqueue = PQ.get_playqueue_from_type(
|
||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[plex_type])
|
||||
|
@ -67,19 +67,13 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True):
|
|||
try:
|
||||
playqueue.items[pos]
|
||||
except IndexError:
|
||||
# Release our default.py before starting our own Kodi player instance
|
||||
if resolve is True:
|
||||
state.PKC_CAUSED_STOP = True
|
||||
result = Playback_Successful()
|
||||
result.listitem = PKC_ListItem(path='PKC_Dummy_Path_Which_Fails')
|
||||
pickle_me(result)
|
||||
playback_init(plex_id, plex_type, playqueue)
|
||||
_playback_init(plex_id, plex_type, playqueue, pos)
|
||||
else:
|
||||
# kick off playback on second pass
|
||||
conclude_playback(playqueue, pos)
|
||||
_conclude_playback(playqueue, pos)
|
||||
|
||||
|
||||
def playback_init(plex_id, plex_type, playqueue):
|
||||
def _playback_init(plex_id, plex_type, playqueue, pos):
|
||||
"""
|
||||
Playback setup if Kodi starts playing an item for the first time.
|
||||
"""
|
||||
|
@ -91,9 +85,25 @@ def playback_init(plex_id, plex_type, playqueue):
|
|||
LOG.error('Could not get a PMS xml for plex id %s', plex_id)
|
||||
# "Play error"
|
||||
dialog('notification', lang(29999), lang(30128), icon='{error}')
|
||||
_ensure_resolve()
|
||||
return
|
||||
trailers = False
|
||||
if playqueue.kodi_pl.size() > 1:
|
||||
# Special case - we already got a filled Kodi playqueue
|
||||
try:
|
||||
_init_existing_kodi_playlist(playqueue)
|
||||
except PL.PlaylistError:
|
||||
LOG.error('Aborting playback_init for longer Kodi playlist')
|
||||
_ensure_resolve()
|
||||
return
|
||||
# Now we need to use setResolvedUrl for the item at position pos
|
||||
_conclude_playback(playqueue, pos)
|
||||
return
|
||||
# "Usual" case - consider trailers and parts and build both Kodi and Plex
|
||||
# playqueues
|
||||
# Fail the item we're trying to play now so we can restart the player
|
||||
_ensure_resolve()
|
||||
api = API(xml[0])
|
||||
trailers = False
|
||||
if (plex_type == v.PLEX_TYPE_MOVIE and not api.resume_point() and
|
||||
settings('enableCinema') == "true"):
|
||||
if settings('askCinema') == "true":
|
||||
|
@ -115,6 +125,7 @@ def playback_init(plex_id, plex_type, playqueue):
|
|||
plex_id, xml.attrib.get('librarySectionUUID'))
|
||||
# "Play error"
|
||||
dialog('notification', lang(29999), lang(30128), icon='{error}')
|
||||
_ensure_resolve()
|
||||
return
|
||||
# Should already be empty, but just in case
|
||||
PL.get_playlist_details_from_xml(playqueue, xml)
|
||||
|
@ -139,6 +150,39 @@ def playback_init(plex_id, plex_type, playqueue):
|
|||
thread.start()
|
||||
|
||||
|
||||
def _ensure_resolve():
|
||||
"""
|
||||
Will check whether RESOLVE=True and if so, fail Kodi playback startup
|
||||
with the path 'PKC_Dummy_Path_Which_Fails' using setResolvedUrl (and some
|
||||
pickling)
|
||||
|
||||
This way we're making sure that other Python instances (calling default.py)
|
||||
will be destroyed.
|
||||
"""
|
||||
if RESOLVE is True:
|
||||
state.PKC_CAUSED_STOP = True
|
||||
result = Playback_Successful()
|
||||
result.listitem = PKC_ListItem(path='PKC_Dummy_Path_Which_Fails')
|
||||
pickle_me(result)
|
||||
|
||||
|
||||
def _init_existing_kodi_playlist(playqueue):
|
||||
"""
|
||||
Will take the playqueue's kodi_pl with MORE than 1 element and initiate
|
||||
playback (without adding trailers)
|
||||
"""
|
||||
LOG.debug('Kodi playlist size: %s', playqueue.kodi_pl.size())
|
||||
for i, kodi_item in enumerate(js.playlist_get_items(playqueue.playlistid)):
|
||||
if i == 0:
|
||||
item = PL.init_Plex_playlist(playqueue, kodi_item=kodi_item)
|
||||
else:
|
||||
item = PL.add_item_to_PMS_playlist(playqueue,
|
||||
i,
|
||||
kodi_item=kodi_item)
|
||||
item.force_transcode = state.FORCE_TRANSCODE
|
||||
LOG.debug('Done building Plex playlist from Kodi playlist')
|
||||
|
||||
|
||||
def _prep_playlist_stack(xml):
|
||||
stack = []
|
||||
for item in xml:
|
||||
|
@ -152,7 +196,9 @@ def _prep_playlist_stack(xml):
|
|||
kodi_id = plex_dbitem[0] if plex_dbitem else None
|
||||
kodi_type = plex_dbitem[4] if plex_dbitem else None
|
||||
else:
|
||||
# We will never store clips (trailers) in the Kodi DB
|
||||
# We will never store clips (trailers) in the Kodi DB.
|
||||
# Also set kodi_id to None for playback via PMS, so that we're
|
||||
# using add-on paths.
|
||||
kodi_id = None
|
||||
kodi_type = None
|
||||
for part, _ in enumerate(item[0]):
|
||||
|
@ -165,8 +211,7 @@ def _prep_playlist_stack(xml):
|
|||
'plex_type': api.plex_type()
|
||||
}
|
||||
path = ('plugin://%s/?%s'
|
||||
% (v.ADDON_TYPE[api.plex_type()],
|
||||
urlencode(params)))
|
||||
% (v.ADDON_TYPE[api.plex_type()], urlencode(params)))
|
||||
listitem = api.create_listitem()
|
||||
listitem.setPath(try_encode(path))
|
||||
else:
|
||||
|
@ -217,7 +262,7 @@ def _process_stack(playqueue, stack):
|
|||
pos += 1
|
||||
|
||||
|
||||
def conclude_playback(playqueue, pos):
|
||||
def _conclude_playback(playqueue, pos):
|
||||
"""
|
||||
ONLY if actually being played (e.g. at 5th position of a playqueue).
|
||||
|
||||
|
@ -246,9 +291,9 @@ def conclude_playback(playqueue, pos):
|
|||
else:
|
||||
playurl = item.file
|
||||
listitem.setPath(try_encode(playurl))
|
||||
if item.playmethod in ('DirectStream', 'DirectPlay'):
|
||||
if item.playmethod == 'DirectStream':
|
||||
listitem.setSubtitles(api.cache_external_subs())
|
||||
else:
|
||||
elif item.playmethod == 'Transcode':
|
||||
playutils.audio_subtitle_prefs(listitem)
|
||||
if state.RESUME_PLAYBACK is True:
|
||||
state.RESUME_PLAYBACK = False
|
||||
|
@ -281,6 +326,8 @@ def process_indirect(key, offset, resolve=True):
|
|||
setResolvedUrl
|
||||
"""
|
||||
LOG.info('process_indirect called with key: %s, offset: %s', key, offset)
|
||||
global RESOLVE
|
||||
RESOLVE = resolve
|
||||
result = Playback_Successful()
|
||||
if key.startswith('http') or key.startswith('{server}'):
|
||||
xml = DU().downloadUrl(key)
|
||||
|
@ -292,9 +339,7 @@ def process_indirect(key, offset, resolve=True):
|
|||
xml[0].attrib
|
||||
except (TypeError, IndexError, AttributeError):
|
||||
LOG.error('Could not download PMS metadata')
|
||||
if resolve is True:
|
||||
# Release default.py
|
||||
pickle_me(result)
|
||||
_ensure_resolve()
|
||||
return
|
||||
if offset != '0':
|
||||
offset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(offset))
|
||||
|
@ -317,9 +362,7 @@ def process_indirect(key, offset, resolve=True):
|
|||
xml[0].attrib
|
||||
except (TypeError, IndexError, AttributeError):
|
||||
LOG.error('Could not download last xml for playurl')
|
||||
if resolve is True:
|
||||
# Release default.py
|
||||
pickle_me(result)
|
||||
_ensure_resolve()
|
||||
return
|
||||
playurl = xml[0].attrib['key']
|
||||
item.file = playurl
|
||||
|
|
|
@ -8,7 +8,7 @@ from re import compile as re_compile
|
|||
|
||||
import plexdb_functions as plexdb
|
||||
from downloadutils import DownloadUtils as DU
|
||||
from utils import try_encode, escape_html
|
||||
from utils import try_encode
|
||||
from PlexAPI import API
|
||||
from PlexFunctions import GetPlexMetadata
|
||||
from kodidb_functions import kodiid_from_filename
|
||||
|
@ -190,8 +190,16 @@ class Playlist_Item(object):
|
|||
"""
|
||||
stream_type = v.PLEX_STREAM_TYPE_FROM_STREAM_TYPE[stream_type]
|
||||
count = 0
|
||||
# Kodi indexes differently than Plex
|
||||
for stream in self.xml[0][self.part]:
|
||||
if stream.attrib['streamType'] == stream_type:
|
||||
if (stream.attrib['streamType'] == stream_type and
|
||||
'key' in stream.attrib):
|
||||
if count == kodi_stream_index:
|
||||
return stream.attrib['id']
|
||||
count += 1
|
||||
for stream in self.xml[0][self.part]:
|
||||
if (stream.attrib['streamType'] == stream_type and
|
||||
'key' not in stream.attrib):
|
||||
if count == kodi_stream_index:
|
||||
return stream.attrib['id']
|
||||
count += 1
|
||||
|
@ -208,7 +216,14 @@ class Playlist_Item(object):
|
|||
stream_type = v.PLEX_STREAM_TYPE_FROM_STREAM_TYPE[stream_type]
|
||||
count = 0
|
||||
for stream in self.xml[0][self.part]:
|
||||
if stream.attrib['streamType'] == stream_type:
|
||||
if (stream.attrib['streamType'] == stream_type and
|
||||
'key' in stream.attrib):
|
||||
if stream.attrib['id'] == plex_stream_index:
|
||||
return count
|
||||
count += 1
|
||||
for stream in self.xml[0][self.part]:
|
||||
if (stream.attrib['streamType'] == stream_type and
|
||||
'key' not in stream.attrib):
|
||||
if stream.attrib['id'] == plex_stream_index:
|
||||
return count
|
||||
count += 1
|
||||
|
@ -308,8 +323,7 @@ def playlist_item_from_plex(plex_id):
|
|||
return item
|
||||
|
||||
|
||||
def playlist_item_from_xml(playlist, xml_video_element, kodi_id=None,
|
||||
kodi_type=None):
|
||||
def playlist_item_from_xml(xml_video_element, kodi_id=None, kodi_type=None):
|
||||
"""
|
||||
Returns a playlist element for the playqueue using the Plex xml
|
||||
|
||||
|
@ -319,13 +333,9 @@ def playlist_item_from_xml(playlist, xml_video_element, kodi_id=None,
|
|||
api = API(xml_video_element)
|
||||
item.plex_id = api.plex_id()
|
||||
item.plex_type = api.plex_type()
|
||||
try:
|
||||
item.id = xml_video_element.attrib['%sItemID' % playlist.kind]
|
||||
except KeyError:
|
||||
pass
|
||||
item.guid = xml_video_element.attrib.get('guid')
|
||||
if item.guid is not None:
|
||||
item.guid = escape_html(item.guid)
|
||||
# item.id will only be set if you passed in an xml_video_element from e.g.
|
||||
# a playQueue
|
||||
item.id = api.item_id()
|
||||
if kodi_id is not None:
|
||||
item.kodi_id = kodi_id
|
||||
item.kodi_type = kodi_type
|
||||
|
@ -333,9 +343,12 @@ def playlist_item_from_xml(playlist, xml_video_element, kodi_id=None,
|
|||
with plexdb.Get_Plex_DB() as plex_db:
|
||||
db_element = plex_db.getItem_byId(item.plex_id)
|
||||
try:
|
||||
item.kodi_id, item.kodi_type = int(db_element[0]), db_element[4]
|
||||
item.kodi_id, item.kodi_type = db_element[0], db_element[4]
|
||||
except TypeError:
|
||||
pass
|
||||
item.guid = api.guid_html_escaped()
|
||||
item.playcount = api.viewcount()
|
||||
item.offset = api.resume_point()
|
||||
item.xml = xml_video_element
|
||||
LOG.debug('Created new playlist item from xml: %s', item)
|
||||
return item
|
||||
|
@ -420,7 +433,7 @@ def init_Plex_playlist(playlist, plex_id=None, kodi_item=None):
|
|||
parameters=params)
|
||||
get_playlist_details_from_xml(playlist, xml)
|
||||
# Need to get the details for the playlist item
|
||||
item = playlist_item_from_xml(playlist, xml[0])
|
||||
item = playlist_item_from_xml(xml[0])
|
||||
except (KeyError, IndexError, TypeError):
|
||||
raise PlaylistError('Could not init Plex playlist with plex_id %s and '
|
||||
'kodi_item %s' % (plex_id, kodi_item))
|
||||
|
@ -484,8 +497,8 @@ def add_item_to_playlist(playlist, pos, kodi_id=None, kodi_type=None,
|
|||
params['item'] = {'file': item.file}
|
||||
reply = js.playlist_insert(params)
|
||||
if reply.get('error') is not None:
|
||||
raise PlaylistError('Could not add item to playlist. Kodi reply. %s',
|
||||
reply)
|
||||
raise PlaylistError('Could not add item to playlist. Kodi reply. %s'
|
||||
% reply)
|
||||
return item
|
||||
|
||||
|
||||
|
@ -506,17 +519,16 @@ def add_item_to_PMS_playlist(playlist, pos, plex_id=None, kodi_item=None):
|
|||
# Will always put the new item at the end of the Plex playlist
|
||||
xml = DU().downloadUrl(url, action_type="PUT")
|
||||
try:
|
||||
item.xml = xml[-1]
|
||||
item.id = xml[-1].attrib['%sItemID' % playlist.kind]
|
||||
except IndexError:
|
||||
LOG.info('Could not get playlist children. Adding a dummy')
|
||||
except (TypeError, AttributeError, KeyError):
|
||||
raise PlaylistError('Could not add item %s to playlist %s',
|
||||
kodi_item, playlist)
|
||||
# Get the guid for this item
|
||||
for plex_item in xml:
|
||||
if plex_item.attrib['%sItemID' % playlist.kind] == item.id:
|
||||
item.guid = escape_html(plex_item.attrib['guid'])
|
||||
xml[-1].attrib
|
||||
except (TypeError, AttributeError, KeyError, IndexError):
|
||||
raise PlaylistError('Could not add item %s to playlist %s'
|
||||
% (kodi_item, playlist))
|
||||
api = API(xml[-1])
|
||||
item.xml = xml[-1]
|
||||
item.id = api.item_id()
|
||||
item.guid = api.guid_html_escaped()
|
||||
item.offset = api.resume_point()
|
||||
item.playcount = api.viewcount()
|
||||
playlist.items.append(item)
|
||||
if pos == len(playlist.items) - 1:
|
||||
# Item was added at the end
|
||||
|
@ -555,7 +567,7 @@ def add_item_to_kodi_playlist(playlist, pos, kodi_id=None, kodi_type=None,
|
|||
raise PlaylistError('Could not add item to playlist. Kodi reply. %s',
|
||||
reply)
|
||||
if xml_video_element is not None:
|
||||
item = playlist_item_from_xml(playlist, xml_video_element)
|
||||
item = playlist_item_from_xml(xml_video_element)
|
||||
item.kodi_id = kodi_id
|
||||
item.kodi_type = kodi_type
|
||||
item.file = file
|
||||
|
@ -646,7 +658,7 @@ def add_to_Kodi_playlist(playlist, xml_video_element):
|
|||
|
||||
Returns a Playlist_Item or raises PlaylistError
|
||||
"""
|
||||
item = playlist_item_from_xml(playlist, xml_video_element)
|
||||
item = playlist_item_from_xml(xml_video_element)
|
||||
if item.kodi_id:
|
||||
json_item = {'%sid' % item.kodi_type: item.kodi_id}
|
||||
else:
|
||||
|
@ -673,7 +685,7 @@ def add_listitem_to_Kodi_playlist(playlist, pos, listitem, file,
|
|||
playlist.kodi_pl.add(url=file, listitem=listitem, index=pos)
|
||||
# We need to add this to our internal queue as well
|
||||
if xml_video_element is not None:
|
||||
item = playlist_item_from_xml(playlist, xml_video_element)
|
||||
item = playlist_item_from_xml(xml_video_element)
|
||||
else:
|
||||
item = playlist_item_from_kodi(kodi_item)
|
||||
if file is not None:
|
||||
|
|
|
@ -75,7 +75,7 @@ COMPANION_PORT = int(_ADDON.getSetting('companionPort'))
|
|||
PKC_MACHINE_IDENTIFIER = None
|
||||
|
||||
# Minimal PKC version needed for the Kodi database - otherwise need to recreate
|
||||
MIN_DB_VERSION = '2.0.0'
|
||||
MIN_DB_VERSION = '2.0.4'
|
||||
|
||||
# Database paths
|
||||
_DB_VIDEO_VERSION = {
|
||||
|
|
Loading…
Reference in a new issue