Skip to content

Commit

Permalink
Merge pull request #2041 from Spacetech/watchlist
Browse files Browse the repository at this point in the history
Add support for Plex Watchlist
  • Loading branch information
croneter authored Mar 26, 2024
2 parents 327266d + 8b0f302 commit 5e14075
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 42 deletions.
2 changes: 2 additions & 0 deletions default.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ def triage(mode, params, path, arguments, itemid):
entrypoint.show_section(params.get('section_index'))
elif mode == 'watchlater':
entrypoint.watchlater()
elif mode == 'watchlist':
entrypoint.watchlist(section_id=params.get('section_id'))
elif mode == 'channels':
entrypoint.browse_plex(key='/channels/all')
elif mode == 'search':
Expand Down
4 changes: 4 additions & 0 deletions resources/language/resource.language.en_gb/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,10 @@ msgctxt "#39211"
msgid "Watch later"
msgstr ""

msgctxt "#39212"
msgid "Watchlist"
msgstr ""

# Error message pop-up if {0} cannot be contacted. {0} will be replaced by e.g. the PMS' name
msgctxt "#39213"
msgid "{0} offline"
Expand Down
38 changes: 36 additions & 2 deletions resources/lib/entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,12 @@ def show_main_menu(content_type=None):
directory_item(utils.lang(136), path)
# Plex Search "Search"
directory_item(utils.lang(137), "plugin://%s?mode=search" % v.ADDON_ID)
# Plex Watch later
# Plex Watch later and Watchlist
if content_type not in ('image', 'audio'):
directory_item(utils.lang(39211),
"plugin://%s?mode=watchlater" % v.ADDON_ID)
directory_item(utils.lang(39212),
"plugin://%s?mode=watchlist" % v.ADDON_ID)
# Plex Channels
directory_item(utils.lang(30173), "plugin://%s?mode=channels" % v.ADDON_ID)
# Plex user switch
Expand Down Expand Up @@ -203,7 +205,7 @@ def show_listing(xml, plex_type=None, section_id=None, synched=True, key=None):
return
api = API(xml[0])
# Determine content type for Kodi's Container.content
if key == '/hubs/home/continueWatching':
if key == '/hubs/home/continueWatching' or key == 'watchlist':
# Mix of movies and episodes
plex_type = v.PLEX_TYPE_VIDEO
elif key == '/hubs/home/recentlyAdded?type=2':
Expand Down Expand Up @@ -250,6 +252,15 @@ def show_listing(xml, plex_type=None, section_id=None, synched=True, key=None):
widgets.KEY = key
# Process all items to show
all_items = mass_api(xml)

if key == "watchlist":
# filter out items that are not in the kodi db (items that will not be playable)
all_items = [item for item in all_items if item.kodi_id is not None]

# filter out items in the wrong section id when it's specified
if section_id is not None:
all_items = [item for item in all_items if item.section_id == utils.cast(int, section_id)]

all_items = [widgets.generate_item(api) for api in all_items]
all_items = [widgets.prepare_listitem(item) for item in all_items]
# fill that listing...
Expand Down Expand Up @@ -468,6 +479,29 @@ def watchlater():
show_listing(xml)


def watchlist(section_id=None):
"""
Listing for plex.tv Watchlist section (if signed in to plex.tv)
"""
_wait_for_auth()
if utils.window('plex_token') == '':
LOG.error('No watchlist - not signed in to plex.tv')
raise ListingException
if utils.window('plex_restricteduser') == 'true':
LOG.error('No watchlist - restricted user')
raise ListingException
app.init(entrypoint=True)
xml = DU().downloadUrl('https://metadata.provider.plex.tv/library/sections/watchlist/all',
authenticate=False,
headerOptions={'X-Plex-Token': utils.window('plex_token')})
try:
xml[0].attrib
except (TypeError, IndexError, AttributeError):
LOG.error('Could not download watch list list from plex.tv')
raise ListingException
show_listing(xml, None, section_id, False, "watchlist")


def browse_plex(key=None, plex_type=None, section_id=None, synched=True,
args=None, prompt=None, query=None):
"""
Expand Down
2 changes: 2 additions & 0 deletions resources/lib/itemtypes/movies.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def add_update(self, xml, section_name=None, section_id=None,
section_id or api.library_section_id())
return
plex_id = api.plex_id
plex_guid = api.plex_guid
movie = self.plexdb.movie(plex_id)
if movie:
update_item = True
Expand Down Expand Up @@ -168,6 +169,7 @@ def add_update(self, xml, section_name=None, section_id=None,
api.viewcount(),
api.lastplayed())
self.plexdb.add_movie(plex_id=plex_id,
plex_guid=plex_guid,
checksum=api.checksum(),
section_id=section_id,
kodi_id=kodi_id,
Expand Down
7 changes: 7 additions & 0 deletions resources/lib/itemtypes/tvshows.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def add_update(self, xml, section_name=None, section_id=None,
section_id or api.library_section_id())
return
plex_id = api.plex_id
plex_guid = api.plex_guid
show = self.plexdb.show(plex_id)
if not show:
update_item = False
Expand Down Expand Up @@ -253,6 +254,7 @@ def add_update(self, xml, section_name=None, section_id=None,
tags.extend([i for _, i in api.collections()])
self.kodidb.modify_tags(kodi_id, v.KODI_TYPE_SHOW, tags)
self.plexdb.add_show(plex_id=plex_id,
plex_guid=plex_guid,
checksum=api.checksum(),
section_id=section_id,
kodi_id=kodi_id,
Expand Down Expand Up @@ -283,6 +285,7 @@ def add_update(self, xml, section_name=None, section_id=None,
section_id or api.library_section_id())
return
plex_id = api.plex_id
plex_guid = api.plex_guid
season = self.plexdb.season(plex_id)
if not season:
update_item = False
Expand Down Expand Up @@ -340,6 +343,7 @@ def add_update(self, xml, section_name=None, section_id=None,
kodi_id,
v.KODI_TYPE_SEASON)
self.plexdb.add_season(plex_id=plex_id,
plex_guid=plex_guid,
checksum=api.checksum(),
section_id=section_id,
show_id=show_id,
Expand All @@ -361,6 +365,7 @@ def add_update(self, xml, section_name=None, section_id=None,
section_id or api.library_section_id())
return
plex_id = api.plex_id
plex_guid = api.plex_guid
episode = self.plexdb.episode(plex_id)
if not episode:
update_item = False
Expand Down Expand Up @@ -496,6 +501,7 @@ def add_update(self, xml, section_name=None, section_id=None,
api.viewcount(),
api.lastplayed())
self.plexdb.add_episode(plex_id=plex_id,
plex_guid=plex_guid,
checksum=api.checksum(),
section_id=section_id,
show_id=api.show_id(),
Expand Down Expand Up @@ -566,6 +572,7 @@ def add_update(self, xml, section_name=None, section_id=None,
api.viewcount(),
api.lastplayed())
self.plexdb.add_episode(plex_id=plex_id,
plex_guid=plex_guid,
checksum=api.checksum(),
section_id=section_id,
show_id=api.show_id(),
Expand Down
21 changes: 21 additions & 0 deletions resources/lib/library_sync/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_MOVIE),
('watchlist',
utils.lang(39212), # "Watchlist"
{
'mode': 'watchlist',
'key': '/library/sections/{self.section_id}/watchlist',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_MOVIE),
('browse',
utils.lang(39702), # "Browse by folder"
{
Expand Down Expand Up @@ -175,6 +183,14 @@
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_EPISODE),
('watchlist',
utils.lang(39212), # "Watchlist"
{
'mode': 'watchlist',
'key': '/library/sections/{self.section_id}/watchlist',
'section_id': '{self.section_id}'
},
v.CONTENT_TYPE_SHOW),
('browse',
utils.lang(39702), # "Browse by folder"
{
Expand Down Expand Up @@ -391,3 +407,8 @@ def node_more(section, node_name, args):

def node_plex_sets(section, node_name, args):
return _folder_template(section, node_name, args)


def node_watchlist(section, node_name, args):
return _folder_template(section, node_name, args)

18 changes: 18 additions & 0 deletions resources/lib/plex_api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ def plex_id(self):
"""
return cast(int, self.xml.get('ratingKey'))

@property
def plex_guid(self):
"""
Returns the Plex guid as unicode or None
"""
return self.xml.get('guid')

@property
def fast_key(self):
"""
Expand Down Expand Up @@ -163,11 +170,18 @@ def check_db(self, plexdb=None):
if self.plex_type == v.PLEX_TYPE_CLIP:
# Clips won't ever be synched to Kodi
return
remap_keys = False
if plexdb:
db_item = plexdb.item_by_id(self.plex_id, self.plex_type)
if not db_item and self.plex_guid is not None:
db_item = plexdb.item_by_guid(self.plex_guid, self.plex_type)
remap_keys = True
else:
with PlexDB(lock=False) as plexdb:
db_item = plexdb.item_by_id(self.plex_id, self.plex_type)
if not db_item and self.plex_guid is not None:
db_item = plexdb.item_by_guid(self.plex_guid, self.plex_type)
remap_keys = True
if not db_item:
return
self._section_id = db_item['section_id']
Expand All @@ -180,6 +194,10 @@ def check_db(self, plexdb=None):
self._kodi_pathid = db_item['kodi_pathid']
if 'fanart_synced' in db_item:
self._fanart_synced = db_item['fanart_synced']
if remap_keys:
# update keys to correctly point to the local plex server
self.xml.set('key', '/library/metadata/%s' % db_item["plex_id"])
self.xml.set('ratingKey', db_item["plex_id"])

def path_and_plex_id(self):
"""
Expand Down
36 changes: 36 additions & 0 deletions resources/lib/plex_db/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,34 @@ def item_by_id(self, plex_id, plex_type=None):
break
return answ

def item_by_guid(self, plex_guid, plex_type=None):
"""
Returns the item for plex_guid or None.
Supply with the correct plex_type to speed up lookup
"""
answ = None
if plex_type == v.PLEX_TYPE_MOVIE:
answ = self.movie_by_guid(plex_guid)
elif plex_type == v.PLEX_TYPE_EPISODE:
answ = self.episode_by_guid(plex_guid)
elif plex_type == v.PLEX_TYPE_SHOW:
answ = self.show_by_guid(plex_guid)
elif plex_type == v.PLEX_TYPE_SEASON:
answ = self.season_by_guid(plex_guid)
elif plex_type is None:
# SLOW - lookup plex_id in all our tables
for kind in (v.PLEX_TYPE_MOVIE,
v.PLEX_TYPE_EPISODE,
v.PLEX_TYPE_SHOW,
v.PLEX_TYPE_SEASON):
method = getattr(self, kind + "_by_guid")
answ = method(plex_guid)
if answ:
break
else:
pass
return answ

def item_by_kodi_id(self, kodi_id, kodi_type):
"""
"""
Expand Down Expand Up @@ -223,6 +251,7 @@ def initialize():
plexdb.cursor.execute('''
CREATE TABLE IF NOT EXISTS movie(
plex_id INTEGER PRIMARY KEY,
plex_guid TEXT,
checksum INTEGER UNIQUE,
section_id INTEGER,
kodi_id INTEGER,
Expand All @@ -235,6 +264,7 @@ def initialize():
plexdb.cursor.execute('''
CREATE TABLE IF NOT EXISTS show(
plex_id INTEGER PRIMARY KEY,
plex_guid TEXT,
checksum INTEGER UNIQUE,
section_id INTEGER,
kodi_id INTEGER,
Expand All @@ -245,6 +275,7 @@ def initialize():
plexdb.cursor.execute('''
CREATE TABLE IF NOT EXISTS season(
plex_id INTEGER PRIMARY KEY,
plex_guid TEXT,
checksum INTEGER UNIQUE,
section_id INTEGER,
show_id INTEGER,
Expand All @@ -256,6 +287,7 @@ def initialize():
plexdb.cursor.execute('''
CREATE TABLE IF NOT EXISTS episode(
plex_id INTEGER PRIMARY KEY,
plex_guid TEXT,
checksum INTEGER UNIQUE,
section_id INTEGER,
show_id INTEGER,
Expand Down Expand Up @@ -313,12 +345,16 @@ def initialize():
commands = (
'CREATE INDEX IF NOT EXISTS ix_movie_1 ON movie (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_movie_2 ON movie (kodi_id)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_movie_3 ON movie (plex_guid)',
'CREATE INDEX IF NOT EXISTS ix_show_1 ON show (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_show_2 ON show (kodi_id)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_show_3 ON show (plex_guid)',
'CREATE INDEX IF NOT EXISTS ix_season_1 ON season (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_season_2 ON season (kodi_id)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_season_3 ON season (plex_guid)',
'CREATE INDEX IF NOT EXISTS ix_episode_1 ON episode (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_episode_2 ON episode (kodi_id)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_episode_3 ON season (plex_guid)',
'CREATE INDEX IF NOT EXISTS ix_artist_1 ON artist (last_sync)',
'CREATE UNIQUE INDEX IF NOT EXISTS ix_artist_2 ON artist (kodi_id)',
'CREATE INDEX IF NOT EXISTS ix_album_1 ON album (last_sync)',
Expand Down
Loading

0 comments on commit 5e14075

Please sign in to comment.