Rewire llibrary sync, part 6
This commit is contained in:
parent
2f96749fc7
commit
f520cebf66
11 changed files with 928 additions and 244 deletions
|
@ -3,6 +3,18 @@
|
||||||
from __future__ import absolute_import, division, unicode_literals
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
from .movies import Movie
|
from .movies import Movie
|
||||||
from .tvshows import Show, Season, Episode
|
from .tvshows import Show, Season, Episode
|
||||||
|
from .music import Artist, Album, Song
|
||||||
|
from .. import variables as v
|
||||||
|
|
||||||
# Note: always use same order of URL arguments, NOT urlencode:
|
# Note: always use same order of URL arguments, NOT urlencode:
|
||||||
# plex_id=<plex_id>&plex_type=<plex_type>&mode=play
|
# plex_id=<plex_id>&plex_type=<plex_type>&mode=play
|
||||||
|
|
||||||
|
ITEMTYPE_FROM_PLEXTYPE = {
|
||||||
|
v.PLEX_TYPE_MOVIE: Movie,
|
||||||
|
v.PLEX_TYPE_SHOW: Show,
|
||||||
|
v.PLEX_TYPE_SEASON: Season,
|
||||||
|
v.PLEX_TYPE_EPISODE: Episode,
|
||||||
|
v.PLEX_TYPE_ARTIST: Artist,
|
||||||
|
v.PLEX_TYPE_ALBUM: Album,
|
||||||
|
v.PLEX_TYPE_SONG: Song
|
||||||
|
}
|
||||||
|
|
544
resources/lib/itemtypes/music.py
Normal file
544
resources/lib/itemtypes/music.py
Normal file
|
@ -0,0 +1,544 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
|
from logging import getLogger
|
||||||
|
|
||||||
|
from .common import ItemBase, process_path
|
||||||
|
from ..plex_api import API
|
||||||
|
from .. import state, variables as v
|
||||||
|
|
||||||
|
LOG = getLogger('PLEX.music')
|
||||||
|
|
||||||
|
|
||||||
|
class MusicMixin(object):
|
||||||
|
def remove(self, plex_id, plex_type=None):
|
||||||
|
"""
|
||||||
|
Remove the entire TV shows object (show, season or episode) including
|
||||||
|
all associated entries from the Kodi DB.
|
||||||
|
"""
|
||||||
|
if plex_type is None:
|
||||||
|
entry = self.plex_db.episode(plex_id)
|
||||||
|
kodi_type = v.KODI_TYPE_EPISODE
|
||||||
|
if not entry:
|
||||||
|
entry = self.plex_db.season(plex_id)
|
||||||
|
kodi_type = v.KODI_TYPE_SEASON
|
||||||
|
if not entry:
|
||||||
|
entry = self.plex_db.show(plex_id)
|
||||||
|
kodi_type = v.KODI_TYPE_SHOW
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
if not entry:
|
||||||
|
LOG.debug('Cannot delete plex_id %s - not found in DB', plex_id)
|
||||||
|
return
|
||||||
|
kodi_id = entry[0]
|
||||||
|
file_id = entry[1]
|
||||||
|
parent_id = entry[3]
|
||||||
|
kodi_type = entry[4]
|
||||||
|
LOG.debug("Removing %s with kodi_id: %s file_id: %s parent_id: %s",
|
||||||
|
kodi_type, kodi_id, file_id, parent_id)
|
||||||
|
|
||||||
|
# Remove the plex reference
|
||||||
|
self.plex_db.removeItem(plex_id)
|
||||||
|
|
||||||
|
# EPISODE #####
|
||||||
|
if kodi_type == v.KODI_TYPE_EPISODE:
|
||||||
|
# Delete episode, verify season and tvshow
|
||||||
|
self.remove_episode(kodi_id, file_id)
|
||||||
|
# Season verification
|
||||||
|
season = self.plex_db.getItem_byKodiId(parent_id,
|
||||||
|
v.KODI_TYPE_SEASON)
|
||||||
|
if season is not None:
|
||||||
|
if not self.plex_db.getItem_byParentId(parent_id,
|
||||||
|
v.KODI_TYPE_EPISODE):
|
||||||
|
# No episode left for season - so delete the season
|
||||||
|
self.remove_season(parent_id)
|
||||||
|
self.plex_db.removeItem(season[0])
|
||||||
|
show = self.plex_db.getItem_byKodiId(season[1],
|
||||||
|
v.KODI_TYPE_SHOW)
|
||||||
|
if show is not None:
|
||||||
|
if not self.plex_db.getItem_byParentId(season[1],
|
||||||
|
v.KODI_TYPE_SEASON):
|
||||||
|
# No seasons for show left - so delete entire show
|
||||||
|
self.remove_show(season[1])
|
||||||
|
self.plex_db.removeItem(show[0])
|
||||||
|
else:
|
||||||
|
LOG.error('No show found in Plex DB for season %s', season)
|
||||||
|
else:
|
||||||
|
LOG.error('No season found in Plex DB!')
|
||||||
|
# SEASON #####
|
||||||
|
elif kodi_type == v.KODI_TYPE_SEASON:
|
||||||
|
# Remove episodes, season, verify tvshow
|
||||||
|
for episode in self.plex_db.getItem_byParentId(
|
||||||
|
kodi_id, v.KODI_TYPE_EPISODE):
|
||||||
|
self.remove_episode(episode[1], episode[2])
|
||||||
|
self.plex_db.removeItem(episode[0])
|
||||||
|
# Remove season
|
||||||
|
self.remove_season(kodi_id)
|
||||||
|
# Show verification
|
||||||
|
if not self.plex_db.getItem_byParentId(parent_id,
|
||||||
|
v.KODI_TYPE_SEASON):
|
||||||
|
# There's no other season left, delete the show
|
||||||
|
self.remove_show(parent_id)
|
||||||
|
self.plex_db.removeItem_byKodiId(parent_id, v.KODI_TYPE_SHOW)
|
||||||
|
# TVSHOW #####
|
||||||
|
elif kodi_type == v.KODI_TYPE_SHOW:
|
||||||
|
# Remove episodes, seasons and the tvshow itself
|
||||||
|
for season in self.plex_db.getItem_byParentId(kodi_id,
|
||||||
|
v.KODI_TYPE_SEASON):
|
||||||
|
for episode in self.plex_db.getItem_byParentId(
|
||||||
|
season[1], v.KODI_TYPE_EPISODE):
|
||||||
|
self.remove_episode(episode[1], episode[2])
|
||||||
|
self.plex_db.removeItem(episode[0])
|
||||||
|
self.remove_season(season[1])
|
||||||
|
self.plex_db.removeItem(season[0])
|
||||||
|
self.remove_show(kodi_id)
|
||||||
|
|
||||||
|
LOG.debug("Deleted %s %s from Kodi database", kodi_type, plex_id)
|
||||||
|
|
||||||
|
def remove_show(self, kodi_id):
|
||||||
|
"""
|
||||||
|
Remove a TV show, and only the show, no seasons or episodes
|
||||||
|
"""
|
||||||
|
self.kodi_db.modify_genres(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
|
self.kodi_db.modify_studios(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
|
self.kodi_db.modify_tags(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
|
self.artwork.delete_artwork(kodi_id,
|
||||||
|
v.KODI_TYPE_SHOW,
|
||||||
|
self.kodicursor)
|
||||||
|
self.kodicursor.execute("DELETE FROM tvshow WHERE idShow = ?",
|
||||||
|
(kodi_id,))
|
||||||
|
if v.KODIVERSION >= 17:
|
||||||
|
self.kodi_db.remove_uniqueid(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
|
self.kodi_db.remove_ratings(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
|
LOG.debug("Removed tvshow: %s", kodi_id)
|
||||||
|
|
||||||
|
def remove_season(self, kodi_id):
|
||||||
|
"""
|
||||||
|
Remove a season, and only a season, not the show or episodes
|
||||||
|
"""
|
||||||
|
self.artwork.delete_artwork(kodi_id,
|
||||||
|
v.KODI_TYPE_SEASON,
|
||||||
|
self.kodicursor)
|
||||||
|
self.kodicursor.execute("DELETE FROM seasons WHERE idSeason = ?",
|
||||||
|
(kodi_id,))
|
||||||
|
LOG.debug("Removed season: %s", kodi_id)
|
||||||
|
|
||||||
|
def remove_episode(self, kodi_id, file_id):
|
||||||
|
"""
|
||||||
|
Remove an episode, and episode only from the Kodi DB (not Plex DB)
|
||||||
|
"""
|
||||||
|
self.kodi_db.modify_people(kodi_id, v.KODI_TYPE_EPISODE)
|
||||||
|
self.kodi_db.remove_file(file_id, plex_type=v.PLEX_TYPE_EPISODE)
|
||||||
|
self.artwork.delete_artwork(kodi_id,
|
||||||
|
v.KODI_TYPE_EPISODE,
|
||||||
|
self.kodicursor)
|
||||||
|
self.kodicursor.execute("DELETE FROM episode WHERE idEpisode = ?",
|
||||||
|
(kodi_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)
|
||||||
|
LOG.debug("Removed episode: %s", kodi_id)
|
||||||
|
|
||||||
|
|
||||||
|
class Artist(ItemBase, MusicMixin):
|
||||||
|
"""
|
||||||
|
For Plex library-type artists
|
||||||
|
"""
|
||||||
|
def add_update(self, xml, section_name=None, section_id=None,
|
||||||
|
children=None):
|
||||||
|
"""
|
||||||
|
Process a single artist
|
||||||
|
"""
|
||||||
|
api = API(xml)
|
||||||
|
update_item = True
|
||||||
|
plex_id = api.plex_id()
|
||||||
|
LOG.debug('Adding show with plex_id %s', plex_id)
|
||||||
|
if not plex_id:
|
||||||
|
LOG.error("Cannot parse XML data for TV show: %s", xml.attrib)
|
||||||
|
return
|
||||||
|
show = self.plex_db.show(plex_id)
|
||||||
|
try:
|
||||||
|
kodi_id = show[3]
|
||||||
|
kodi_pathid = show[4]
|
||||||
|
except TypeError:
|
||||||
|
update_item = False
|
||||||
|
query = 'SELECT COALESCE(MAX(idShow), 0) FROM tvshow'
|
||||||
|
self.kodicursor.execute(query)
|
||||||
|
kodi_id = self.kodicursor.fetchone()[0] + 1
|
||||||
|
else:
|
||||||
|
# Verification the item is still in Kodi
|
||||||
|
self.kodicursor.execute('SELECT * FROM tvshow WHERE idShow = ?',
|
||||||
|
(kodi_id,))
|
||||||
|
try:
|
||||||
|
self.kodicursor.fetchone()[0]
|
||||||
|
except TypeError:
|
||||||
|
# item is not found, let's recreate it.
|
||||||
|
update_item = False
|
||||||
|
LOG.info("idShow: %s missing from Kodi, repairing the entry.",
|
||||||
|
kodi_id)
|
||||||
|
|
||||||
|
genres = api.genre_list()
|
||||||
|
genre = api.list_to_string(genres)
|
||||||
|
studios = api.music_studio_list()
|
||||||
|
studio = api.list_to_string(studios)
|
||||||
|
|
||||||
|
# GET THE FILE AND PATH #####
|
||||||
|
if state.DIRECT_PATHS:
|
||||||
|
# Direct paths is set the Kodi way
|
||||||
|
playurl = api.validate_playurl(api.tv_show_path(),
|
||||||
|
api.plex_type(),
|
||||||
|
folder=True)
|
||||||
|
if playurl is None:
|
||||||
|
return
|
||||||
|
path, toplevelpath = process_path(playurl)
|
||||||
|
toppathid = self.kodi_db.add_video_path(
|
||||||
|
toplevelpath,
|
||||||
|
content='tvshows',
|
||||||
|
scraper='metadata.local')
|
||||||
|
else:
|
||||||
|
# Set plugin path
|
||||||
|
toplevelpath = "plugin://%s.tvshows/" % v.ADDON_ID
|
||||||
|
path = "%s%s/" % (toplevelpath, plex_id)
|
||||||
|
# Do NOT set a parent id because addon-path cannot be "stacked"
|
||||||
|
toppathid = None
|
||||||
|
|
||||||
|
kodi_pathid = self.kodi_db.add_video_path(path,
|
||||||
|
date_added=api.date_created(),
|
||||||
|
id_parent_path=toppathid)
|
||||||
|
# UPDATE THE TVSHOW #####
|
||||||
|
if update_item:
|
||||||
|
LOG.info("UPDATE tvshow plex_id: %s - Title: %s",
|
||||||
|
plex_id, api.title())
|
||||||
|
# update new ratings Kodi 17
|
||||||
|
rating_id = self.kodi_db.get_ratingid(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
|
self.kodi_db.update_ratings(kodi_id,
|
||||||
|
v.KODI_TYPE_SHOW,
|
||||||
|
"default",
|
||||||
|
api.audience_rating(),
|
||||||
|
api.votecount(),
|
||||||
|
rating_id)
|
||||||
|
# update new uniqueid Kodi 17
|
||||||
|
if api.provider('tvdb') is not None:
|
||||||
|
uniqueid = self.kodi_db.get_uniqueid(kodi_id,
|
||||||
|
v.KODI_TYPE_SHOW)
|
||||||
|
self.kodi_db.update_uniqueid(kodi_id,
|
||||||
|
v.KODI_TYPE_SHOW,
|
||||||
|
api.provider('tvdb'),
|
||||||
|
"unknown",
|
||||||
|
uniqueid)
|
||||||
|
else:
|
||||||
|
self.kodi_db.remove_uniqueid(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
|
uniqueid = -1
|
||||||
|
# Update the tvshow entry
|
||||||
|
query = '''
|
||||||
|
UPDATE tvshow
|
||||||
|
SET c00 = ?, c01 = ?, c04 = ?, c05 = ?, c08 = ?, c09 = ?,
|
||||||
|
c12 = ?, c13 = ?, c14 = ?, c15 = ?
|
||||||
|
WHERE idShow = ?
|
||||||
|
'''
|
||||||
|
self.kodicursor.execute(
|
||||||
|
query, (api.title(), api.plot(), rating_id,
|
||||||
|
api.premiere_date(), genre, api.title(), uniqueid,
|
||||||
|
api.content_rating(), studio, api.sorttitle(),
|
||||||
|
kodi_id))
|
||||||
|
# OR ADD THE TVSHOW #####
|
||||||
|
else:
|
||||||
|
LOG.info("ADD tvshow plex_id: %s - Title: %s",
|
||||||
|
plex_id, api.title())
|
||||||
|
# Link the path
|
||||||
|
query = "INSERT INTO tvshowlinkpath(idShow, idPath) values (?, ?)"
|
||||||
|
self.kodicursor.execute(query, (kodi_id, kodi_pathid))
|
||||||
|
rating_id = self.kodi_db.get_ratingid(kodi_id, v.KODI_TYPE_SHOW)
|
||||||
|
self.kodi_db.add_ratings(rating_id,
|
||||||
|
kodi_id,
|
||||||
|
v.KODI_TYPE_SHOW,
|
||||||
|
"default",
|
||||||
|
api.audience_rating(),
|
||||||
|
api.votecount())
|
||||||
|
if api.provider('tvdb') is not None:
|
||||||
|
uniqueid = self.kodi_db.get_uniqueid(kodi_id,
|
||||||
|
v.KODI_TYPE_SHOW)
|
||||||
|
self.kodi_db.add_uniqueid(uniqueid,
|
||||||
|
kodi_id,
|
||||||
|
v.KODI_TYPE_SHOW,
|
||||||
|
api.provider('tvdb'),
|
||||||
|
"unknown")
|
||||||
|
else:
|
||||||
|
uniqueid = -1
|
||||||
|
# Create the tvshow entry
|
||||||
|
query = '''
|
||||||
|
INSERT INTO tvshow(
|
||||||
|
idShow, c00, c01, c04, c05, c08, c09, c12, c13, c14,
|
||||||
|
c15)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
'''
|
||||||
|
self.kodicursor.execute(
|
||||||
|
query, (kodi_id, api.title(), api.plot(), rating_id,
|
||||||
|
api.premiere_date(), genre, api.title(), uniqueid,
|
||||||
|
api.content_rating(), studio, api.sorttitle()))
|
||||||
|
|
||||||
|
self.kodi_db.modify_people(kodi_id,
|
||||||
|
v.KODI_TYPE_SHOW,
|
||||||
|
api.people_list())
|
||||||
|
self.kodi_db.modify_genres(kodi_id, v.KODI_TYPE_SHOW, genres)
|
||||||
|
self.artwork.modify_artwork(api.artwork(),
|
||||||
|
kodi_id,
|
||||||
|
v.KODI_TYPE_SHOW,
|
||||||
|
self.kodicursor)
|
||||||
|
# Process studios
|
||||||
|
self.kodi_db.modify_studios(kodi_id, v.KODI_TYPE_SHOW, studios)
|
||||||
|
# Process tags: view, PMS collection tags
|
||||||
|
tags = [section_name]
|
||||||
|
tags.extend([i for _, i in api.collection_list()])
|
||||||
|
self.kodi_db.modify_tags(kodi_id, v.KODI_TYPE_SHOW, tags)
|
||||||
|
self.plex_db.add_show(plex_id=plex_id,
|
||||||
|
checksum=api.checksum(),
|
||||||
|
section_id=section_id,
|
||||||
|
kodi_id=kodi_id,
|
||||||
|
kodi_pathid=kodi_pathid,
|
||||||
|
last_sync=self.last_sync)
|
||||||
|
|
||||||
|
|
||||||
|
class Album(ItemBase, MusicMixin):
|
||||||
|
def add_update(self, xml, section_name=None, section_id=None,
|
||||||
|
children=None):
|
||||||
|
"""
|
||||||
|
Process a single album
|
||||||
|
"""
|
||||||
|
api = API(xml)
|
||||||
|
plex_id = api.plex_id()
|
||||||
|
LOG.debug('Adding season with plex_id %s', plex_id)
|
||||||
|
if not plex_id:
|
||||||
|
LOG.error('Error getting plex_id for season, skipping: %s',
|
||||||
|
xml.attrib)
|
||||||
|
return
|
||||||
|
show_id = api.parent_plex_id()
|
||||||
|
show = self.plex_db.show(show_id)
|
||||||
|
try:
|
||||||
|
parent_id = show[3]
|
||||||
|
except TypeError:
|
||||||
|
LOG.error('Could not find parent tv show for season %s. '
|
||||||
|
'Skipping season for now.', plex_id)
|
||||||
|
return
|
||||||
|
kodi_id = self.kodi_db.add_season(parent_id, api.season_number())
|
||||||
|
self.artwork.modify_artwork(api.artwork(),
|
||||||
|
kodi_id,
|
||||||
|
v.KODI_TYPE_SEASON,
|
||||||
|
self.kodicursor)
|
||||||
|
self.plex_db.add_season(plex_id=plex_id,
|
||||||
|
checksum=api.checksum(),
|
||||||
|
section_id=section_id,
|
||||||
|
show_id=show_id,
|
||||||
|
parent_id=parent_id,
|
||||||
|
kodi_id=kodi_id,
|
||||||
|
last_sync=self.last_sync)
|
||||||
|
|
||||||
|
|
||||||
|
class Song(ItemBase, MusicMixin):
|
||||||
|
def add_update(self, xml, section_name=None, section_id=None,
|
||||||
|
children=None):
|
||||||
|
"""
|
||||||
|
Process single song/track
|
||||||
|
"""
|
||||||
|
api = API(xml)
|
||||||
|
update_item = True
|
||||||
|
plex_id = api.plex_id()
|
||||||
|
LOG.debug('Adding episode with plex_id %s', plex_id)
|
||||||
|
if not plex_id:
|
||||||
|
LOG.error('Error getting plex_id for episode, skipping: %s',
|
||||||
|
xml.attrib)
|
||||||
|
return
|
||||||
|
entry = self.plex_db.item_by_id(plex_id)
|
||||||
|
try:
|
||||||
|
kodi_id = entry[0]
|
||||||
|
old_kodi_fileid = entry[1]
|
||||||
|
kodi_pathid = entry[2]
|
||||||
|
except TypeError:
|
||||||
|
update_item = False
|
||||||
|
query = 'SELECT COALESCE(MAX(idEpisode), 0) FROM episode'
|
||||||
|
self.kodicursor.execute(query)
|
||||||
|
kodi_id = self.kodicursor.fetchone()[0] + 1
|
||||||
|
else:
|
||||||
|
# Verification the item is still in Kodi
|
||||||
|
query = 'SELECT * FROM episode WHERE idEpisode = ? LIMIT 1'
|
||||||
|
self.kodicursor.execute(query, (kodi_id, ))
|
||||||
|
try:
|
||||||
|
self.kodicursor.fetchone()[0]
|
||||||
|
except TypeError:
|
||||||
|
# item is not found, let's recreate it.
|
||||||
|
update_item = False
|
||||||
|
LOG.info('idEpisode %s missing from Kodi, repairing entry.',
|
||||||
|
kodi_id)
|
||||||
|
|
||||||
|
peoples = api.people()
|
||||||
|
director = api.list_to_string(peoples['Director'])
|
||||||
|
writer = api.list_to_string(peoples['Writer'])
|
||||||
|
userdata = api.userdata()
|
||||||
|
show_id, season_id, _, season_no, episode_no = api.episode_data()
|
||||||
|
|
||||||
|
if season_no is None:
|
||||||
|
season_no = -1
|
||||||
|
if episode_no is None:
|
||||||
|
episode_no = -1
|
||||||
|
airs_before_season = "-1"
|
||||||
|
airs_before_episode = "-1"
|
||||||
|
|
||||||
|
show = self.plex_db.show(show_id)
|
||||||
|
try:
|
||||||
|
grandparent_id = show[3]
|
||||||
|
except TypeError:
|
||||||
|
LOG.error("Parent tvshow now found, skip item")
|
||||||
|
return False
|
||||||
|
parent_id = self.kodi_db.add_season(grandparent_id, season_no)
|
||||||
|
|
||||||
|
# GET THE FILE AND PATH #####
|
||||||
|
do_indirect = not state.DIRECT_PATHS
|
||||||
|
if state.DIRECT_PATHS:
|
||||||
|
playurl = api.file_path(force_first_media=True)
|
||||||
|
if playurl is None:
|
||||||
|
do_indirect = True
|
||||||
|
else:
|
||||||
|
playurl = api.validate_playurl(playurl, v.PLEX_TYPE_EPISODE)
|
||||||
|
if "\\" in playurl:
|
||||||
|
# Local path
|
||||||
|
filename = playurl.rsplit("\\", 1)[1]
|
||||||
|
else:
|
||||||
|
# Network share
|
||||||
|
filename = playurl.rsplit("/", 1)[1]
|
||||||
|
path = playurl.replace(filename, "")
|
||||||
|
parent_path_id = self.kodi_db.parent_path_id(path)
|
||||||
|
kodi_pathid = self.kodi_db.add_video_path(
|
||||||
|
path, id_parent_path=parent_path_id)
|
||||||
|
if do_indirect:
|
||||||
|
# Set plugin path - do NOT use "intermediate" paths for the show
|
||||||
|
# as with direct paths!
|
||||||
|
filename = api.file_name(force_first_media=True)
|
||||||
|
path = 'plugin://%s.tvshows/%s/' % (v.ADDON_ID, show_id)
|
||||||
|
filename = ('%s?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
||||||
|
% (path, plex_id, v.PLEX_TYPE_EPISODE, filename))
|
||||||
|
playurl = filename
|
||||||
|
# Root path tvshows/ already saved in Kodi DB
|
||||||
|
kodi_pathid = self.kodi_db.add_video_path(path)
|
||||||
|
|
||||||
|
# add/retrieve kodi_pathid and fileid
|
||||||
|
# if the path or file already exists, the calls return current value
|
||||||
|
kodi_fileid = self.kodi_db.add_file(filename,
|
||||||
|
kodi_pathid,
|
||||||
|
api.date_created())
|
||||||
|
|
||||||
|
# UPDATE THE EPISODE #####
|
||||||
|
if update_item:
|
||||||
|
LOG.info("UPDATE episode plex_id: %s, Title: %s",
|
||||||
|
plex_id, api.title())
|
||||||
|
if kodi_fileid != old_kodi_fileid:
|
||||||
|
self.kodi_db.remove_file(old_kodi_fileid)
|
||||||
|
ratingid = self.kodi_db.get_ratingid(kodi_id,
|
||||||
|
v.KODI_TYPE_EPISODE)
|
||||||
|
self.kodi_db.update_ratings(kodi_id,
|
||||||
|
v.KODI_TYPE_EPISODE,
|
||||||
|
"default",
|
||||||
|
userdata['Rating'],
|
||||||
|
api.votecount(),
|
||||||
|
ratingid)
|
||||||
|
# update new uniqueid Kodi 17
|
||||||
|
uniqueid = self.kodi_db.get_uniqueid(kodi_id,
|
||||||
|
v.KODI_TYPE_EPISODE)
|
||||||
|
self.kodi_db.update_uniqueid(kodi_id,
|
||||||
|
v.KODI_TYPE_EPISODE,
|
||||||
|
api.provider('tvdb'),
|
||||||
|
"tvdb",
|
||||||
|
uniqueid)
|
||||||
|
query = '''
|
||||||
|
UPDATE episode
|
||||||
|
SET c00 = ?, c01 = ?, c03 = ?, c04 = ?, c05 = ?, c09 = ?,
|
||||||
|
c10 = ?, c12 = ?, c13 = ?, c14 = ?, c15 = ?, c16 = ?,
|
||||||
|
c18 = ?, c19 = ?, idFile=?, idSeason = ?,
|
||||||
|
userrating = ?
|
||||||
|
WHERE idEpisode = ?
|
||||||
|
'''
|
||||||
|
self.kodicursor.execute(
|
||||||
|
query, (api.title(), api.plot(), ratingid, writer,
|
||||||
|
api.premiere_date(), api.runtime(), director, season_no,
|
||||||
|
episode_no, api.title(), airs_before_season,
|
||||||
|
airs_before_episode, playurl, kodi_pathid, kodi_fileid,
|
||||||
|
parent_id, userdata['UserRating'], kodi_id))
|
||||||
|
|
||||||
|
# OR ADD THE EPISODE #####
|
||||||
|
else:
|
||||||
|
LOG.info("ADD episode plex_id: %s - Title: %s",
|
||||||
|
plex_id, api.title())
|
||||||
|
# Create the episode entry
|
||||||
|
rating_id = self.kodi_db.get_ratingid(kodi_id,
|
||||||
|
v.KODI_TYPE_EPISODE)
|
||||||
|
self.kodi_db.add_ratings(rating_id,
|
||||||
|
kodi_id,
|
||||||
|
v.KODI_TYPE_EPISODE,
|
||||||
|
"default",
|
||||||
|
userdata['Rating'],
|
||||||
|
api.votecount())
|
||||||
|
# add new uniqueid Kodi 17
|
||||||
|
uniqueid = self.kodi_db.get_uniqueid(kodi_id,
|
||||||
|
v.KODI_TYPE_EPISODE)
|
||||||
|
self.kodi_db.add_uniqueid(uniqueid,
|
||||||
|
kodi_id,
|
||||||
|
v.KODI_TYPE_EPISODE,
|
||||||
|
api.provider('tvdb'),
|
||||||
|
"tvdb")
|
||||||
|
query = '''
|
||||||
|
INSERT INTO episode( idEpisode, idFile, c00, c01, c03, c04,
|
||||||
|
c05, c09, c10, c12, c13, c14, idShow, c15, c16, c18,
|
||||||
|
c19, idSeason, userrating)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||||
|
?, ?)
|
||||||
|
'''
|
||||||
|
self.kodicursor.execute(
|
||||||
|
query, (kodi_id, kodi_fileid, api.title(), api.plot(), rating_id,
|
||||||
|
writer, api.premiere_date(), api.runtime(), director,
|
||||||
|
season_no, episode_no, api.title(), grandparent_id,
|
||||||
|
airs_before_season, airs_before_episode, playurl,
|
||||||
|
kodi_pathid, parent_id, userdata['UserRating']))
|
||||||
|
|
||||||
|
self.kodi_db.modify_people(kodi_id,
|
||||||
|
v.KODI_TYPE_EPISODE,
|
||||||
|
api.people_list())
|
||||||
|
self.artwork.modify_artwork(api.artwork(),
|
||||||
|
kodi_id,
|
||||||
|
v.KODI_TYPE_EPISODE,
|
||||||
|
self.kodicursor)
|
||||||
|
streams = api.mediastreams()
|
||||||
|
self.kodi_db.modify_streams(kodi_fileid, streams, api.runtime())
|
||||||
|
self.kodi_db.set_resume(kodi_fileid,
|
||||||
|
api.resume_point(),
|
||||||
|
api.runtime(),
|
||||||
|
userdata['PlayCount'],
|
||||||
|
userdata['LastPlayedDate'],
|
||||||
|
None) # Do send None, we check here
|
||||||
|
if not state.DIRECT_PATHS:
|
||||||
|
# need to set a SECOND file entry for a path without plex show id
|
||||||
|
filename = api.file_name(force_first_media=True)
|
||||||
|
path = 'plugin://%s.tvshows/' % v.ADDON_ID
|
||||||
|
# Filename is exactly the same, WITH plex show id!
|
||||||
|
filename = ('%s%s/?plex_id=%s&plex_type=%s&mode=play&filename=%s'
|
||||||
|
% (path, show_id, plex_id, v.PLEX_TYPE_EPISODE,
|
||||||
|
filename))
|
||||||
|
kodi_pathid = self.kodi_db.add_video_path(path)
|
||||||
|
kodi_fileid = self.kodi_db.add_file(filename,
|
||||||
|
kodi_pathid,
|
||||||
|
api.date_created())
|
||||||
|
self.kodi_db.set_resume(kodi_fileid,
|
||||||
|
api.resume_point(),
|
||||||
|
api.runtime(),
|
||||||
|
userdata['PlayCount'],
|
||||||
|
userdata['LastPlayedDate'],
|
||||||
|
None) # Do send None - 2nd entry
|
||||||
|
self.plex_db.add_episode(plex_id=plex_id,
|
||||||
|
checksum=api.checksum(),
|
||||||
|
section_id=section_id,
|
||||||
|
show_id=show_id,
|
||||||
|
grandparent_id=grandparent_id,
|
||||||
|
season_id=season_id,
|
||||||
|
parent_id=parent_id,
|
||||||
|
kodi_id=kodi_id,
|
||||||
|
kodi_fileid=kodi_fileid,
|
||||||
|
kodi_pathid=kodi_pathid,
|
||||||
|
last_sync=self.last_sync)
|
|
@ -16,84 +16,59 @@ class TvShowMixin(object):
|
||||||
Remove the entire TV shows object (show, season or episode) including
|
Remove the entire TV shows object (show, season or episode) including
|
||||||
all associated entries from the Kodi DB.
|
all associated entries from the Kodi DB.
|
||||||
"""
|
"""
|
||||||
if plex_type is None:
|
db_item = self.plex_db.item_by_id(plex_id, plex_type)
|
||||||
entry = self.plex_db.episode(plex_id)
|
if not db_item:
|
||||||
kodi_type = v.KODI_TYPE_EPISODE
|
|
||||||
if not entry:
|
|
||||||
entry = self.plex_db.season(plex_id)
|
|
||||||
kodi_type = v.KODI_TYPE_SEASON
|
|
||||||
if not entry:
|
|
||||||
entry = self.plex_db.show(plex_id)
|
|
||||||
kodi_type = v.KODI_TYPE_SHOW
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
if not entry:
|
|
||||||
LOG.debug('Cannot delete plex_id %s - not found in DB', plex_id)
|
LOG.debug('Cannot delete plex_id %s - not found in DB', plex_id)
|
||||||
return
|
return
|
||||||
kodi_id = entry[0]
|
LOG.debug('Removing %s %s with kodi_id: %s',
|
||||||
file_id = entry[1]
|
db_item['plex_type'], plex_id, db_item['kodi_id'])
|
||||||
parent_id = entry[3]
|
|
||||||
kodi_type = entry[4]
|
|
||||||
LOG.debug("Removing %s with kodi_id: %s file_id: %s parent_id: %s",
|
|
||||||
kodi_type, kodi_id, file_id, parent_id)
|
|
||||||
|
|
||||||
# Remove the plex reference
|
# Remove the plex reference
|
||||||
self.plex_db.removeItem(plex_id)
|
self.plex_db.remove(plex_id, db_item['plex_type'])
|
||||||
|
|
||||||
# EPISODE #####
|
# EPISODE #####
|
||||||
if kodi_type == v.KODI_TYPE_EPISODE:
|
if db_item['plex_type'] == v.PLEX_TYPE_EPISODE:
|
||||||
# Delete episode, verify season and tvshow
|
# Delete episode, verify season and tvshow
|
||||||
self.remove_episode(kodi_id, file_id)
|
self.remove_episode(db_item['kodi_id'], db_item['kodi_fileid'])
|
||||||
# Season verification
|
# Season verification
|
||||||
season = self.plex_db.getItem_byKodiId(parent_id,
|
if not self.plex_db.season_has_episodes(db_item['season_id']):
|
||||||
v.KODI_TYPE_SEASON)
|
# No episode left for this season - so delete the season
|
||||||
if season is not None:
|
self.remove_season(db_item['parent_id'])
|
||||||
if not self.plex_db.getItem_byParentId(parent_id,
|
self.plex_db.remove(db_item['season_id'], v.PLEX_TYPE_SEASON)
|
||||||
v.KODI_TYPE_EPISODE):
|
|
||||||
# No episode left for season - so delete the season
|
|
||||||
self.remove_season(parent_id)
|
|
||||||
self.plex_db.removeItem(season[0])
|
|
||||||
show = self.plex_db.getItem_byKodiId(season[1],
|
|
||||||
v.KODI_TYPE_SHOW)
|
|
||||||
if show is not None:
|
|
||||||
if not self.plex_db.getItem_byParentId(season[1],
|
|
||||||
v.KODI_TYPE_SEASON):
|
|
||||||
# No seasons for show left - so delete entire show
|
|
||||||
self.remove_show(season[1])
|
|
||||||
self.plex_db.removeItem(show[0])
|
|
||||||
else:
|
|
||||||
LOG.error('No show found in Plex DB for season %s', season)
|
|
||||||
else:
|
|
||||||
LOG.error('No season found in Plex DB!')
|
|
||||||
# SEASON #####
|
|
||||||
elif kodi_type == v.KODI_TYPE_SEASON:
|
|
||||||
# Remove episodes, season, verify tvshow
|
|
||||||
for episode in self.plex_db.getItem_byParentId(
|
|
||||||
kodi_id, v.KODI_TYPE_EPISODE):
|
|
||||||
self.remove_episode(episode[1], episode[2])
|
|
||||||
self.plex_db.removeItem(episode[0])
|
|
||||||
# Remove season
|
|
||||||
self.remove_season(kodi_id)
|
|
||||||
# Show verification
|
# Show verification
|
||||||
if not self.plex_db.getItem_byParentId(parent_id,
|
if (not self.plex_db.show_has_seasons(db_item['show_id']) and
|
||||||
v.KODI_TYPE_SEASON):
|
not self.plex_db.show_has_episodes(db_item['show_id'])):
|
||||||
# There's no other season left, delete the show
|
# No seasons for show left - so delete entire show
|
||||||
self.remove_show(parent_id)
|
self.remove_show(db_item['grandparent_id'])
|
||||||
self.plex_db.removeItem_byKodiId(parent_id, v.KODI_TYPE_SHOW)
|
self.plex_db.remove(db_item['show_id'], v.PLEX_TYPE_SHOW)
|
||||||
|
# SEASON #####
|
||||||
|
elif db_item['plex_type'] == v.PLEX_TYPE_SEASON:
|
||||||
|
# Remove episodes, season, verify tvshow
|
||||||
|
for episode in self.plex_db.episode_by_season(db_item['plex_id']):
|
||||||
|
self.remove_episode(episode['kodi_id'], episode['kodi_fileid'])
|
||||||
|
self.plex_db.remove(episode['plex_id'], v.PLEX_TYPE_EPISODE)
|
||||||
|
# Remove season
|
||||||
|
self.remove_season(db_item['kodi_id'])
|
||||||
|
# Show verification
|
||||||
|
if (not self.plex_db.show_has_seasons(db_item['show_id']) and
|
||||||
|
not self.plex_db.show_has_episodes(db_item['show_id'])):
|
||||||
|
# There's no other season or episode left, delete the show
|
||||||
|
self.remove_show(db_item['parent_id'])
|
||||||
|
self.plex_db.remove(db_item['show_id'], v.KODI_TYPE_SHOW)
|
||||||
# TVSHOW #####
|
# TVSHOW #####
|
||||||
elif kodi_type == v.KODI_TYPE_SHOW:
|
elif db_item['plex_type'] == v.PLEX_TYPE_SHOW:
|
||||||
# Remove episodes, seasons and the tvshow itself
|
# Remove episodes, seasons and the tvshow itself
|
||||||
for season in self.plex_db.getItem_byParentId(kodi_id,
|
for episode in self.plex_db.episode_by_show(db_item['plex_id']):
|
||||||
v.KODI_TYPE_SEASON):
|
self.remove_episode(episode['kodi_id'],
|
||||||
for episode in self.plex_db.getItem_byParentId(
|
episode['kodi_fileid'])
|
||||||
season[1], v.KODI_TYPE_EPISODE):
|
self.plex_db.remove(episode['plex_id'], v.PLEX_TYPE_EPISODE)
|
||||||
self.remove_episode(episode[1], episode[2])
|
for season in self.plex_db.season_by_show(db_item['plex_id']):
|
||||||
self.plex_db.removeItem(episode[0])
|
self.remove_season(season['kodi_id'])
|
||||||
self.remove_season(season[1])
|
self.plex_db.remove(season['plex_id'], v.PLEX_TYPE_SEASON)
|
||||||
self.plex_db.removeItem(season[0])
|
self.remove_show(db_item['kodi_id'])
|
||||||
self.remove_show(kodi_id)
|
|
||||||
|
|
||||||
LOG.debug("Deleted %s %s from Kodi database", kodi_type, plex_id)
|
LOG.debug('Deleted %s %s from all databases',
|
||||||
|
db_item['plex_type'], db_item['plex_id'])
|
||||||
|
|
||||||
def remove_show(self, kodi_id):
|
def remove_show(self, kodi_id):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -3,3 +3,4 @@ from __future__ import absolute_import, division, unicode_literals
|
||||||
|
|
||||||
from .full_sync import start, PLAYLIST_SYNC_ENABLED
|
from .full_sync import start, PLAYLIST_SYNC_ENABLED
|
||||||
from .time import sync_pms_time
|
from .time import sync_pms_time
|
||||||
|
from .common import update_kodi_library
|
||||||
|
|
|
@ -1,26 +1,23 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import, division, unicode_literals
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from threading import Thread
|
|
||||||
from Queue import Empty
|
|
||||||
import xbmc
|
import xbmc
|
||||||
|
|
||||||
from ..plex_api import API
|
from ..plex_api import API
|
||||||
from .. import utils, plexdb_functions as plexdb, kodidb_functions as kodidb
|
from ..plex_db import PlexDB
|
||||||
|
from .. import backgroundthread
|
||||||
|
from ..backgroundthread.Queue import Empty
|
||||||
|
from .. import utils, kodidb_functions as kodidb
|
||||||
from .. import itemtypes, artwork, plex_functions as PF, variables as v, state
|
from .. import itemtypes, artwork, plex_functions as PF, variables as v, state
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
LOG = getLogger("PLEX." + __name__)
|
LOG = getLogger('PLEX.library_sync.fanart')
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
@utils.thread_methods(add_suspends=['SUSPEND_LIBRARY_THREAD',
|
class ThreadedProcessFanart(backgroundthread.KillableThread):
|
||||||
'DB_SCAN',
|
|
||||||
'STOP_SYNC',
|
|
||||||
'SUSPEND_SYNC'])
|
|
||||||
class ThreadedProcessFanart(Thread):
|
|
||||||
"""
|
"""
|
||||||
Threaded download of additional fanart in the background
|
Threaded download of additional fanart in the background
|
||||||
|
|
||||||
|
@ -36,27 +33,57 @@ class ThreadedProcessFanart(Thread):
|
||||||
"""
|
"""
|
||||||
def __init__(self, queue):
|
def __init__(self, queue):
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
Thread.__init__(self)
|
super(ThreadedProcessFanart, self).__init__()
|
||||||
|
|
||||||
|
def isCanceled(self):
|
||||||
|
return xbmc.abortRequested or state.STOP_PKC
|
||||||
|
|
||||||
|
def isSuspended(self):
|
||||||
|
return (state.SUSPEND_LIBRARY_THREAD or
|
||||||
|
state.DB_SCAN or
|
||||||
|
state.STOP_SYNC or
|
||||||
|
state.SUSPEND_SYNC)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
LOG.info('---===### Starting FanartSync ###===---')
|
||||||
|
try:
|
||||||
|
self._run()
|
||||||
|
except:
|
||||||
|
utils.ERROR(txt='FanartSync crashed', notify=True)
|
||||||
|
raise
|
||||||
|
LOG.info('---===### Stopping FanartSync ###===---')
|
||||||
|
|
||||||
|
def _run(self):
|
||||||
"""
|
"""
|
||||||
Do the work
|
Do the work
|
||||||
"""
|
"""
|
||||||
LOG.debug("---===### Starting FanartSync ###===---")
|
# First run through our already synced items in the Plex DB
|
||||||
while not self.stopped():
|
for plex_type in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW):
|
||||||
|
with PlexDB() as plexdb:
|
||||||
|
for plex_id in plexdb.fanart(plex_type):
|
||||||
|
if self.isCanceled():
|
||||||
|
break
|
||||||
|
while self.isSuspended():
|
||||||
|
if self.isCanceled():
|
||||||
|
break
|
||||||
|
xbmc.sleep(1000)
|
||||||
|
process_item(plexdb, {'plex_id': plex_id,
|
||||||
|
'plex_type': plex_type,
|
||||||
|
'refresh': False})
|
||||||
|
|
||||||
|
# Then keep checking the queue for new items
|
||||||
|
while not self.isCanceled():
|
||||||
# In the event the server goes offline
|
# In the event the server goes offline
|
||||||
while self.suspended():
|
while self.isSuspended():
|
||||||
# Set in service.py
|
# Set in service.py
|
||||||
if self.stopped():
|
if self.isCanceled():
|
||||||
# Abort was requested while waiting. We should exit
|
|
||||||
LOG.debug("---===### Stopped FanartSync ###===---")
|
|
||||||
return
|
return
|
||||||
xbmc.sleep(1000)
|
xbmc.sleep(1000)
|
||||||
# grabs Plex item from queue
|
# grabs Plex item from queue
|
||||||
try:
|
try:
|
||||||
item = self.queue.get(block=False)
|
item = self.queue.get(block=False)
|
||||||
except Empty:
|
except Empty:
|
||||||
xbmc.sleep(200)
|
xbmc.sleep(1000)
|
||||||
continue
|
continue
|
||||||
self.queue.task_done()
|
self.queue.task_done()
|
||||||
if isinstance(item, artwork.ArtworkSyncMessage):
|
if isinstance(item, artwork.ArtworkSyncMessage):
|
||||||
|
@ -68,26 +95,23 @@ class ThreadedProcessFanart(Thread):
|
||||||
sound=False)
|
sound=False)
|
||||||
continue
|
continue
|
||||||
LOG.debug('Get additional fanart for Plex id %s', item['plex_id'])
|
LOG.debug('Get additional fanart for Plex id %s', item['plex_id'])
|
||||||
_process(item)
|
with PlexDB() as plexdb:
|
||||||
LOG.debug("---===### Stopped FanartSync ###===---")
|
process_item(plexdb, item)
|
||||||
|
|
||||||
|
|
||||||
def _process(item):
|
def process_item(plexdb, item):
|
||||||
done = False
|
done = False
|
||||||
try:
|
try:
|
||||||
artworks = None
|
artworks = None
|
||||||
with plexdb.Get_Plex_DB() as plex_db:
|
db_item = plexdb.item_by_id(item['plex_id'], item['plex_type'])
|
||||||
db_item = plex_db.getItem_byId(item['plex_id'])
|
if not db_item:
|
||||||
try:
|
|
||||||
kodi_id = db_item[0]
|
|
||||||
kodi_type = db_item[4]
|
|
||||||
except TypeError:
|
|
||||||
LOG.error('Could not get Kodi id for plex id %s, abort getfanart',
|
LOG.error('Could not get Kodi id for plex id %s, abort getfanart',
|
||||||
item['plex_id'])
|
item['plex_id'])
|
||||||
return
|
return
|
||||||
if item['refresh'] is False:
|
if item['refresh'] is False:
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with kodidb.GetKodiDB('video') as kodi_db:
|
||||||
artworks = kodi_db.get_art(kodi_id, kodi_type)
|
artworks = kodi_db.get_art(db_item['kodi_id'],
|
||||||
|
db_item['kodi_type'])
|
||||||
# Check if we even need to get additional art
|
# Check if we even need to get additional art
|
||||||
for key in v.ALL_KODI_ARTWORK:
|
for key in v.ALL_KODI_ARTWORK:
|
||||||
if key not in artworks:
|
if key not in artworks:
|
||||||
|
@ -111,9 +135,10 @@ def _process(item):
|
||||||
artworks = api.artwork()
|
artworks = api.artwork()
|
||||||
# Get additional missing artwork from fanart artwork sites
|
# Get additional missing artwork from fanart artwork sites
|
||||||
artworks = api.fanart_artwork(artworks)
|
artworks = api.fanart_artwork(artworks)
|
||||||
with getattr(itemtypes,
|
with itemtypes.ITEMTYPE_FROM_PLEXTYPE[item['plex_type']] as context:
|
||||||
v.ITEMTYPE_FROM_PLEXTYPE[item['plex_type']])() as itm:
|
context.set_fanart(artworks,
|
||||||
itm.set_fanart(artworks, kodi_id, kodi_type)
|
db_item['kodi_id'],
|
||||||
|
db_item['kodi_type'])
|
||||||
# Additional fanart for sets/collections
|
# Additional fanart for sets/collections
|
||||||
if api.plex_type() == v.PLEX_TYPE_MOVIE:
|
if api.plex_type() == v.PLEX_TYPE_MOVIE:
|
||||||
for _, setname in api.collection_list():
|
for _, setname in api.collection_list():
|
||||||
|
@ -128,14 +153,13 @@ def _process(item):
|
||||||
for art in kodi_artwork:
|
for art in kodi_artwork:
|
||||||
if art in external_set_artwork:
|
if art in external_set_artwork:
|
||||||
del external_set_artwork[art]
|
del external_set_artwork[art]
|
||||||
with itemtypes.Movies() as movie_db:
|
with itemtypes.Movie() as movie:
|
||||||
movie_db.artwork.modify_artwork(external_set_artwork,
|
movie.artwork.modify_artwork(external_set_artwork,
|
||||||
setid,
|
setid,
|
||||||
v.KODI_TYPE_SET,
|
v.KODI_TYPE_SET,
|
||||||
movie_db.kodicursor)
|
movie.kodicursor)
|
||||||
done = True
|
done = True
|
||||||
finally:
|
finally:
|
||||||
if done is True:
|
if done is True:
|
||||||
LOG.debug('Done getting fanart for Plex id %s', item['plex_id'])
|
LOG.debug('Done getting fanart for Plex id %s', item['plex_id'])
|
||||||
with plexdb.Get_Plex_DB() as plex_db:
|
plexdb.set_fanart_synced(item['plex_id'], item['plex_type'])
|
||||||
plex_db.set_fanart_synched(item['plex_id'])
|
|
||||||
|
|
|
@ -38,6 +38,38 @@ class FullSync(backgroundthread.KillableThread, common.libsync_mixin):
|
||||||
self.processing_thread = None
|
self.processing_thread = None
|
||||||
super(FullSync, self).__init__()
|
super(FullSync, self).__init__()
|
||||||
|
|
||||||
|
def plex_update_watched(self, viewId, item_class, lastViewedAt=None,
|
||||||
|
updatedAt=None):
|
||||||
|
"""
|
||||||
|
YET to implement
|
||||||
|
|
||||||
|
Updates plex elements' view status ('watched' or 'unwatched') and
|
||||||
|
also updates resume times.
|
||||||
|
This is done by downloading one XML for ALL elements with viewId
|
||||||
|
"""
|
||||||
|
if self.new_items_only is False:
|
||||||
|
# Only do this once for fullsync: the first run where new items are
|
||||||
|
# added to Kodi
|
||||||
|
return
|
||||||
|
xml = PF.GetAllPlexLeaves(viewId,
|
||||||
|
lastViewedAt=lastViewedAt,
|
||||||
|
updatedAt=updatedAt)
|
||||||
|
# Return if there are no items in PMS reply - it's faster
|
||||||
|
try:
|
||||||
|
xml[0].attrib
|
||||||
|
except (TypeError, AttributeError, IndexError):
|
||||||
|
LOG.error('Error updating watch status. Could not get viewId: '
|
||||||
|
'%s of item_class %s with lastViewedAt: %s, updatedAt: '
|
||||||
|
'%s', viewId, item_class, lastViewedAt, updatedAt)
|
||||||
|
return
|
||||||
|
|
||||||
|
if item_class in ('Movies', 'TVShows'):
|
||||||
|
self.update_kodi_video_library = True
|
||||||
|
elif item_class == 'Music':
|
||||||
|
self.update_kodi_music_library = True
|
||||||
|
with getattr(itemtypes, item_class)() as itemtype:
|
||||||
|
itemtype.updateUserdata(xml)
|
||||||
|
|
||||||
def process_item(self, xml_item):
|
def process_item(self, xml_item):
|
||||||
"""
|
"""
|
||||||
Processes a single library item
|
Processes a single library item
|
||||||
|
@ -94,7 +126,8 @@ class FullSync(backgroundthread.KillableThread, common.libsync_mixin):
|
||||||
self.context,
|
self.context,
|
||||||
utils.cast(int, iterator.get('totalSize', 0)),
|
utils.cast(int, iterator.get('totalSize', 0)),
|
||||||
utils.cast(unicode, iterator.get('librarySectionTitle')),
|
utils.cast(unicode, iterator.get('librarySectionTitle')),
|
||||||
section['id'])
|
section['id'],
|
||||||
|
utils.cast(unicode, section['viewGroup']))
|
||||||
self.queue.put(queue_info)
|
self.queue.put(queue_info)
|
||||||
for xml_item in iterator:
|
for xml_item in iterator:
|
||||||
if self.isCanceled():
|
if self.isCanceled():
|
||||||
|
|
|
@ -4,7 +4,7 @@ from logging import getLogger
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
|
||||||
from . import common
|
from . import common
|
||||||
from .. import utils, backgroundthread
|
from .. import backgroundthread, utils, variables as v
|
||||||
|
|
||||||
LOG = getLogger('PLEX.library_sync.process_metadata')
|
LOG = getLogger('PLEX.library_sync.process_metadata')
|
||||||
|
|
||||||
|
@ -17,11 +17,12 @@ class InitNewSection(object):
|
||||||
context: itemtypes.Movie, itemtypes.Episode, etc.
|
context: itemtypes.Movie, itemtypes.Episode, etc.
|
||||||
"""
|
"""
|
||||||
def __init__(self, context, total_number_of_items, section_name,
|
def __init__(self, context, total_number_of_items, section_name,
|
||||||
section_id):
|
section_id, plex_type):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.total = total_number_of_items
|
self.total = total_number_of_items
|
||||||
self.name = section_name
|
self.name = section_name
|
||||||
self.id = section_id
|
self.id = section_id
|
||||||
|
self.plex_type = plex_type
|
||||||
|
|
||||||
|
|
||||||
class ProcessMetadata(backgroundthread.KillableThread, common.libsync_mixin):
|
class ProcessMetadata(backgroundthread.KillableThread, common.libsync_mixin):
|
||||||
|
@ -59,6 +60,8 @@ class ProcessMetadata(backgroundthread.KillableThread, common.libsync_mixin):
|
||||||
self.section_name,
|
self.section_name,
|
||||||
'%s/%s: %s'
|
'%s/%s: %s'
|
||||||
% (self.current, self.total, self.title))
|
% (self.current, self.total, self.title))
|
||||||
|
common.update_kodi_library(video=self.is_video,
|
||||||
|
music=not self.is_video)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
"""
|
||||||
|
@ -80,6 +83,7 @@ class ProcessMetadata(backgroundthread.KillableThread, common.libsync_mixin):
|
||||||
self.current = 0
|
self.current = 0
|
||||||
self.total = section.total
|
self.total = section.total
|
||||||
self.section_name = section.name
|
self.section_name = section.name
|
||||||
|
self.is_video = section.plex_type in v.PLEX_VIDEO_TYPES
|
||||||
with section.context(self.last_sync) as context:
|
with section.context(self.last_sync) as context:
|
||||||
while self.isCanceled() is False:
|
while self.isCanceled() is False:
|
||||||
# grabs item from queue. This will block!
|
# grabs item from queue. This will block!
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import absolute_import, division, unicode_literals
|
|
||||||
from logging import getLogger
|
|
||||||
from threading import Thread, Lock
|
|
||||||
from xbmc import sleep
|
|
||||||
from xbmcgui import DialogProgressBG
|
|
||||||
|
|
||||||
from .. import utils
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
LOG = getLogger("PLEX." + __name__)
|
|
||||||
|
|
||||||
GET_METADATA_COUNT = 0
|
|
||||||
PROCESS_METADATA_COUNT = 0
|
|
||||||
PROCESSING_VIEW_NAME = ''
|
|
||||||
LOCK = Lock()
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
|
|
||||||
@utils.thread_methods(add_stops=['SUSPEND_LIBRARY_THREAD',
|
|
||||||
'STOP_SYNC',
|
|
||||||
'SUSPEND_SYNC'])
|
|
||||||
class ThreadedShowSyncInfo(Thread):
|
|
||||||
"""
|
|
||||||
Threaded class to show the Kodi statusbar of the metadata download.
|
|
||||||
|
|
||||||
Input:
|
|
||||||
total: Total number of items to get
|
|
||||||
item_type:
|
|
||||||
"""
|
|
||||||
def __init__(self, total, item_type):
|
|
||||||
self.total = total
|
|
||||||
self.item_type = item_type
|
|
||||||
Thread.__init__(self)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""
|
|
||||||
Do the work
|
|
||||||
"""
|
|
||||||
LOG.debug('Show sync info thread started')
|
|
||||||
# cache local variables because it's faster
|
|
||||||
total = self.total
|
|
||||||
dialog = DialogProgressBG('dialoglogProgressBG')
|
|
||||||
dialog.create("%s %s: %s %s"
|
|
||||||
% (utils.lang(39714),
|
|
||||||
self.item_type,
|
|
||||||
unicode(total),
|
|
||||||
utils.lang(39715)))
|
|
||||||
|
|
||||||
total = 2 * total
|
|
||||||
total_progress = 0
|
|
||||||
while not self.stopped():
|
|
||||||
with LOCK:
|
|
||||||
get_progress = GET_METADATA_COUNT
|
|
||||||
process_progress = PROCESS_METADATA_COUNT
|
|
||||||
view_name = PROCESSING_VIEW_NAME
|
|
||||||
total_progress = get_progress + process_progress
|
|
||||||
try:
|
|
||||||
percentage = int(float(total_progress) / float(total) * 100.0)
|
|
||||||
except ZeroDivisionError:
|
|
||||||
percentage = 0
|
|
||||||
dialog.update(percentage,
|
|
||||||
message="%s %s. %s %s: %s"
|
|
||||||
% (get_progress,
|
|
||||||
utils.lang(39712),
|
|
||||||
process_progress,
|
|
||||||
utils.lang(39713),
|
|
||||||
view_name))
|
|
||||||
# Sleep for x milliseconds
|
|
||||||
sleep(200)
|
|
||||||
dialog.close()
|
|
||||||
LOG.debug('Show sync info thread terminated')
|
|
|
@ -3,6 +3,7 @@
|
||||||
from __future__ import absolute_import, division, unicode_literals
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from random import shuffle
|
from random import shuffle
|
||||||
|
import Queue
|
||||||
import xbmc
|
import xbmc
|
||||||
|
|
||||||
from . import library_sync
|
from . import library_sync
|
||||||
|
@ -39,8 +40,12 @@ class LibrarySync(backgroundthread.KillableThread):
|
||||||
self.session_keys = {}
|
self.session_keys = {}
|
||||||
self.sync_successful = False
|
self.sync_successful = False
|
||||||
self.last_full_sync = 0
|
self.last_full_sync = 0
|
||||||
self.fanartqueue = Queue.Queue()
|
if utils.settings('FanartTV') == 'true':
|
||||||
self.fanartthread = fanart.ThreadedProcessFanart(self.fanartqueue)
|
self.fanartqueue = Queue.Queue()
|
||||||
|
self.fanartthread = library_sync.fanart.ThreadedProcessFanart(self.fanartqueue)
|
||||||
|
else:
|
||||||
|
self.fanartqueue = None
|
||||||
|
self.fanartthread = None
|
||||||
# How long should we wait at least to process new/changed PMS items?
|
# How long should we wait at least to process new/changed PMS items?
|
||||||
# Show sync dialog even if user deactivated?
|
# Show sync dialog even if user deactivated?
|
||||||
self.force_dialog = False
|
self.force_dialog = False
|
||||||
|
@ -92,36 +97,6 @@ class LibrarySync(backgroundthread.KillableThread):
|
||||||
message=message,
|
message=message,
|
||||||
icon='{error}')
|
icon='{error}')
|
||||||
|
|
||||||
def plex_update_watched(self, viewId, item_class, lastViewedAt=None,
|
|
||||||
updatedAt=None):
|
|
||||||
"""
|
|
||||||
Updates plex elements' view status ('watched' or 'unwatched') and
|
|
||||||
also updates resume times.
|
|
||||||
This is done by downloading one XML for ALL elements with viewId
|
|
||||||
"""
|
|
||||||
if self.new_items_only is False:
|
|
||||||
# Only do this once for fullsync: the first run where new items are
|
|
||||||
# added to Kodi
|
|
||||||
return
|
|
||||||
xml = PF.GetAllPlexLeaves(viewId,
|
|
||||||
lastViewedAt=lastViewedAt,
|
|
||||||
updatedAt=updatedAt)
|
|
||||||
# Return if there are no items in PMS reply - it's faster
|
|
||||||
try:
|
|
||||||
xml[0].attrib
|
|
||||||
except (TypeError, AttributeError, IndexError):
|
|
||||||
LOG.error('Error updating watch status. Could not get viewId: '
|
|
||||||
'%s of item_class %s with lastViewedAt: %s, updatedAt: '
|
|
||||||
'%s', viewId, item_class, lastViewedAt, updatedAt)
|
|
||||||
return
|
|
||||||
|
|
||||||
if item_class in ('Movies', 'TVShows'):
|
|
||||||
self.update_kodi_video_library = True
|
|
||||||
elif item_class == 'Music':
|
|
||||||
self.update_kodi_music_library = True
|
|
||||||
with getattr(itemtypes, item_class)() as itemtype:
|
|
||||||
itemtype.updateUserdata(xml)
|
|
||||||
|
|
||||||
def process_message(self, message):
|
def process_message(self, message):
|
||||||
"""
|
"""
|
||||||
processes json.loads() messages from websocket. Triage what we need to
|
processes json.loads() messages from websocket. Triage what we need to
|
||||||
|
@ -207,8 +182,8 @@ class LibrarySync(backgroundthread.KillableThread):
|
||||||
delete_list)
|
delete_list)
|
||||||
# Let Kodi know of the change
|
# Let Kodi know of the change
|
||||||
if self.update_kodi_video_library or self.update_kodi_music_library:
|
if self.update_kodi_video_library or self.update_kodi_music_library:
|
||||||
update_library(video=self.update_kodi_video_library,
|
library_sync.update_library(video=self.update_kodi_video_library,
|
||||||
music=self.update_kodi_music_library)
|
music=self.update_kodi_music_library)
|
||||||
self.update_kodi_video_library = False
|
self.update_kodi_video_library = False
|
||||||
self.update_kodi_music_library = False
|
self.update_kodi_music_library = False
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import, division, unicode_literals
|
from __future__ import absolute_import, division, unicode_literals
|
||||||
|
|
||||||
from . import utils
|
from . import utils, variables as v
|
||||||
|
|
||||||
|
|
||||||
class PlexDBBase(object):
|
class PlexDBBase(object):
|
||||||
"""
|
"""
|
||||||
Methods used for all types of items
|
Plex database methods used for all types of items
|
||||||
"""
|
"""
|
||||||
def __init__(self, cursor=None):
|
def __init__(self, cursor=None):
|
||||||
# Allows us to use this class with a cursor instead of context mgr
|
# Allows us to use this class with a cursor instead of context mgr
|
||||||
|
@ -22,6 +22,111 @@ class PlexDBBase(object):
|
||||||
self.plexconn.commit()
|
self.plexconn.commit()
|
||||||
self.plexconn.close()
|
self.plexconn.close()
|
||||||
|
|
||||||
|
def item_by_id(self, plex_id, plex_type=None):
|
||||||
|
"""
|
||||||
|
Returns the following dict or None if not found
|
||||||
|
{
|
||||||
|
plex_id
|
||||||
|
plex_type
|
||||||
|
kodi_id
|
||||||
|
kodi_type
|
||||||
|
}
|
||||||
|
for plex_id. Supply with the correct plex_type to speed up lookup
|
||||||
|
"""
|
||||||
|
answ = None
|
||||||
|
if plex_type == v.PLEX_TYPE_MOVIE:
|
||||||
|
entry = self.movie(plex_id)
|
||||||
|
if entry:
|
||||||
|
answ = self.entry_to_movie(entry)
|
||||||
|
elif plex_type == v.PLEX_TYPE_EPISODE:
|
||||||
|
entry = self.episode(plex_id)
|
||||||
|
if entry:
|
||||||
|
answ = self.entry_to_episode(entry)
|
||||||
|
elif plex_type == v.PLEX_TYPE_SHOW:
|
||||||
|
entry = self.show(plex_id)
|
||||||
|
if entry:
|
||||||
|
answ = self.entry_to_show(entry)
|
||||||
|
elif plex_type == v.PLEX_TYPE_SEASON:
|
||||||
|
entry = self.season(plex_id)
|
||||||
|
if entry:
|
||||||
|
answ = self.entry_to_season(entry)
|
||||||
|
else:
|
||||||
|
# SLOW - lookup plex_id in all our tables
|
||||||
|
for kind in (v.PLEX_TYPE_MOVIE,
|
||||||
|
v.PLEX_TYPE_SHOW,
|
||||||
|
v.PLEX_TYPE_EPISODE,
|
||||||
|
v.PLEX_TYPE_SEASON):
|
||||||
|
method = getattr(self, kind)
|
||||||
|
entry = method(plex_id)
|
||||||
|
if entry:
|
||||||
|
method = getattr(self, 'entry_to_%s' % kind)
|
||||||
|
answ = method(entry)
|
||||||
|
break
|
||||||
|
return answ
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def entry_to_movie(entry):
|
||||||
|
return {
|
||||||
|
'plex_type': v.PLEX_TYPE_MOVIE,
|
||||||
|
'kodi_type': v.KODI_TYPE_MOVIE,
|
||||||
|
'plex_id': entry[0],
|
||||||
|
'checksum': entry[1],
|
||||||
|
'section_id': entry[2],
|
||||||
|
'kodi_id': entry[3],
|
||||||
|
'kodi_fileid': entry[4],
|
||||||
|
'kodi_pathid': entry[5],
|
||||||
|
'fanart_synced': entry[6],
|
||||||
|
'last_sync': entry[7]
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def entry_to_episode(entry):
|
||||||
|
return {
|
||||||
|
'plex_type': v.PLEX_TYPE_EPISODE,
|
||||||
|
'kodi_type': v.KODI_TYPE_EPISODE,
|
||||||
|
'plex_id': entry[0],
|
||||||
|
'checksum': entry[1],
|
||||||
|
'section_id': entry[2],
|
||||||
|
'show_id': entry[3],
|
||||||
|
'grandparent_id': entry[4],
|
||||||
|
'season_id': entry[5],
|
||||||
|
'parent_id': entry[6],
|
||||||
|
'kodi_id': entry[7],
|
||||||
|
'kodi_fileid': entry[8],
|
||||||
|
'kodi_pathid': entry[9],
|
||||||
|
'fanart_synced': entry[10],
|
||||||
|
'last_sync': entry[11]
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def entry_to_show(entry):
|
||||||
|
return {
|
||||||
|
'plex_type': v.PLEX_TYPE_SHOW,
|
||||||
|
'kodi_type': v.KODI_TYPE_SHOW,
|
||||||
|
'plex_id': entry[0],
|
||||||
|
'checksum': entry[1],
|
||||||
|
'section_id': entry[2],
|
||||||
|
'kodi_id': entry[3],
|
||||||
|
'kodi_pathid': entry[4],
|
||||||
|
'fanart_synced': entry[5],
|
||||||
|
'last_sync': entry[6]
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def entry_to_season(entry):
|
||||||
|
return {
|
||||||
|
'plex_type': v.PLEX_TYPE_SEASON,
|
||||||
|
'kodi_type': v.KODI_TYPE_SEASON,
|
||||||
|
'plex_id': entry[0],
|
||||||
|
'checksum': entry[1],
|
||||||
|
'section_id': entry[2],
|
||||||
|
'show_id': entry[3],
|
||||||
|
'parent_id': entry[4],
|
||||||
|
'kodi_id': entry[5],
|
||||||
|
'fanart_synced': entry[6],
|
||||||
|
'last_sync': entry[7]
|
||||||
|
}
|
||||||
|
|
||||||
def section_ids(self):
|
def section_ids(self):
|
||||||
"""
|
"""
|
||||||
Returns an iterator for section Plex ids for all sections
|
Returns an iterator for section Plex ids for all sections
|
||||||
|
@ -118,14 +223,30 @@ class PlexDBBase(object):
|
||||||
query = 'DELETE FROM ? WHERE plex_id = ?' % plex_type
|
query = 'DELETE FROM ? WHERE plex_id = ?' % plex_type
|
||||||
self.cursor.execute(query, (plex_id, ))
|
self.cursor.execute(query, (plex_id, ))
|
||||||
|
|
||||||
|
def fanart(self, plex_type):
|
||||||
|
"""
|
||||||
|
Returns an iterator for plex_type for all plex_id, where fanart_synced
|
||||||
|
has not yet been set to 1
|
||||||
|
"""
|
||||||
|
query = 'SELECT plex_id from %s WHERE fanart_synced = 0' % plex_type
|
||||||
|
self.cursor.execute(query)
|
||||||
|
return (x[0] for x in self.cursor)
|
||||||
|
|
||||||
|
def set_fanart_synced(self, plex_id, plex_type):
|
||||||
|
"""
|
||||||
|
Toggles fanart_synced to 1 for plex_id
|
||||||
|
"""
|
||||||
|
query = 'UPDATE %s SET fanart_synced = 1 WHERE plex_id = ?' % plex_type
|
||||||
|
self.cursor.execute(query, (plex_id, ))
|
||||||
|
|
||||||
|
|
||||||
def initialize():
|
def initialize():
|
||||||
"""
|
"""
|
||||||
Run once during startup to verify that plex db exists.
|
Run once upon PKC startup to verify that plex db exists.
|
||||||
"""
|
"""
|
||||||
with PlexDBBase() as plex_db:
|
with PlexDBBase() as plexdb:
|
||||||
# Create the tables for the plex database
|
# Create the tables for the plex database
|
||||||
plex_db.cursor.execute('''
|
plexdb.cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS sections(
|
CREATE TABLE IF NOT EXISTS sections(
|
||||||
section_id INTEGER PRIMARY KEY,
|
section_id INTEGER PRIMARY KEY,
|
||||||
section_name TEXT,
|
section_name TEXT,
|
||||||
|
@ -133,9 +254,9 @@ def initialize():
|
||||||
kodi_tagid INTEGER,
|
kodi_tagid INTEGER,
|
||||||
sync_to_kodi INTEGER)
|
sync_to_kodi INTEGER)
|
||||||
''')
|
''')
|
||||||
plex_db.cursor.execute('''
|
plexdb.cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS movie(
|
CREATE TABLE IF NOT EXISTS movie(
|
||||||
plex_id INTEGER PRIMARY KEY ASC,
|
plex_id INTEGER PRIMARY KEY,
|
||||||
checksum INTEGER UNIQUE,
|
checksum INTEGER UNIQUE,
|
||||||
section_id INTEGER,
|
section_id INTEGER,
|
||||||
kodi_id INTEGER,
|
kodi_id INTEGER,
|
||||||
|
@ -144,9 +265,9 @@ def initialize():
|
||||||
fanart_synced INTEGER,
|
fanart_synced INTEGER,
|
||||||
last_sync INTEGER)
|
last_sync INTEGER)
|
||||||
''')
|
''')
|
||||||
plex_db.cursor.execute('''
|
plexdb.cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS show(
|
CREATE TABLE IF NOT EXISTS show(
|
||||||
plex_id INTEGER PRIMARY KEY ASC,
|
plex_id INTEGER PRIMARY KEY,
|
||||||
checksum INTEGER UNIQUE,
|
checksum INTEGER UNIQUE,
|
||||||
section_id INTEGER,
|
section_id INTEGER,
|
||||||
kodi_id INTEGER,
|
kodi_id INTEGER,
|
||||||
|
@ -154,7 +275,7 @@ def initialize():
|
||||||
fanart_synced INTEGER,
|
fanart_synced INTEGER,
|
||||||
last_sync INTEGER)
|
last_sync INTEGER)
|
||||||
''')
|
''')
|
||||||
plex_db.cursor.execute('''
|
plexdb.cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS season(
|
CREATE TABLE IF NOT EXISTS season(
|
||||||
plex_id INTEGER PRIMARY KEY,
|
plex_id INTEGER PRIMARY KEY,
|
||||||
checksum INTEGER UNIQUE,
|
checksum INTEGER UNIQUE,
|
||||||
|
@ -165,7 +286,7 @@ def initialize():
|
||||||
fanart_synced INTEGER,
|
fanart_synced INTEGER,
|
||||||
last_sync INTEGER)
|
last_sync INTEGER)
|
||||||
''')
|
''')
|
||||||
plex_db.cursor.execute('''
|
plexdb.cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS episode(
|
CREATE TABLE IF NOT EXISTS episode(
|
||||||
plex_id INTEGER PRIMARY KEY,
|
plex_id INTEGER PRIMARY KEY,
|
||||||
checksum INTEGER UNIQUE,
|
checksum INTEGER UNIQUE,
|
||||||
|
@ -180,16 +301,16 @@ def initialize():
|
||||||
fanart_synced INTEGER,
|
fanart_synced INTEGER,
|
||||||
last_sync INTEGER)
|
last_sync INTEGER)
|
||||||
''')
|
''')
|
||||||
plex_db.cursor.execute('''
|
plexdb.cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS artist(
|
CREATE TABLE IF NOT EXISTS artist(
|
||||||
plex_id INTEGER PRIMARY KEY ASC,
|
plex_id INTEGER PRIMARY KEY,
|
||||||
checksum INTEGER UNIQUE,
|
checksum INTEGER UNIQUE,
|
||||||
section_id INTEGER,
|
section_id INTEGER,
|
||||||
kodi_id INTEGER,
|
kodi_id INTEGER,
|
||||||
fanart_synced INTEGER,
|
fanart_synced INTEGER,
|
||||||
last_sync INTEGER)
|
last_sync INTEGER)
|
||||||
''')
|
''')
|
||||||
plex_db.cursor.execute('''
|
plexdb.cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS album(
|
CREATE TABLE IF NOT EXISTS album(
|
||||||
plex_id INTEGER PRIMARY KEY,
|
plex_id INTEGER PRIMARY KEY,
|
||||||
checksum INTEGER UNIQUE,
|
checksum INTEGER UNIQUE,
|
||||||
|
@ -200,7 +321,7 @@ def initialize():
|
||||||
fanart_synced INTEGER,
|
fanart_synced INTEGER,
|
||||||
last_sync INTEGER)
|
last_sync INTEGER)
|
||||||
''')
|
''')
|
||||||
plex_db.cursor.execute('''
|
plexdb.cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS track(
|
CREATE TABLE IF NOT EXISTS track(
|
||||||
plex_id INTEGER PRIMARY KEY,
|
plex_id INTEGER PRIMARY KEY,
|
||||||
checksum INTEGER UNIQUE,
|
checksum INTEGER UNIQUE,
|
||||||
|
@ -215,17 +336,15 @@ def initialize():
|
||||||
fanart_synced INTEGER,
|
fanart_synced INTEGER,
|
||||||
last_sync INTEGER)
|
last_sync INTEGER)
|
||||||
''')
|
''')
|
||||||
plex_db.cursor.execute('''
|
plexdb.cursor.execute('''
|
||||||
CREATE TABLE IF NOT EXISTS playlists(
|
CREATE TABLE IF NOT EXISTS playlists(
|
||||||
plex_id INTEGER PRIMARY KEY ASC,
|
plex_id INTEGER PRIMARY KEY,
|
||||||
plex_name TEXT,
|
plex_name TEXT,
|
||||||
plex_updatedat INTEGER,
|
plex_updatedat INTEGER,
|
||||||
kodi_path TEXT,
|
kodi_path TEXT,
|
||||||
kodi_type TEXT,
|
kodi_type TEXT,
|
||||||
kodi_hash TEXT)
|
kodi_hash TEXT)
|
||||||
''')
|
''')
|
||||||
# Create an index for actors to speed up sync
|
|
||||||
utils.create_actor_db_index()
|
|
||||||
|
|
||||||
|
|
||||||
def wipe():
|
def wipe():
|
||||||
|
@ -233,10 +352,10 @@ def wipe():
|
||||||
Completely resets the Plex database
|
Completely resets the Plex database
|
||||||
"""
|
"""
|
||||||
query = "SELECT name FROM sqlite_master WHERE type = 'table'"
|
query = "SELECT name FROM sqlite_master WHERE type = 'table'"
|
||||||
with PlexDBBase() as plex_db:
|
with PlexDBBase() as plexdb:
|
||||||
plex_db.cursor.execute(query)
|
plexdb.cursor.execute(query)
|
||||||
tables = plex_db.cursor.fetchall()
|
tables = plexdb.cursor.fetchall()
|
||||||
tables = [i[0] for i in tables]
|
tables = [i[0] for i in tables]
|
||||||
for table in tables:
|
for table in tables:
|
||||||
delete_query = 'DELETE FROM %s' % table
|
delete_query = 'DELETE FROM %s' % table
|
||||||
plex_db.cursor.execute(delete_query)
|
plexdb.cursor.execute(delete_query)
|
||||||
|
|
|
@ -11,8 +11,13 @@ class TVShows(object):
|
||||||
"""
|
"""
|
||||||
query = '''
|
query = '''
|
||||||
INSERT OR REPLACE INTO show(
|
INSERT OR REPLACE INTO show(
|
||||||
plex_id, checksum, section_id, kodi_id, kodi_pathid,
|
plex_id
|
||||||
fanart_synced, last_sync)
|
checksum
|
||||||
|
section_id
|
||||||
|
kodi_id
|
||||||
|
kodi_pathid
|
||||||
|
fanart_synced
|
||||||
|
last_sync)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
'''
|
'''
|
||||||
self.plexcursor.execute(
|
self.plexcursor.execute(
|
||||||
|
@ -27,8 +32,14 @@ class TVShows(object):
|
||||||
"""
|
"""
|
||||||
query = '''
|
query = '''
|
||||||
INSERT OR REPLACE INTO season(
|
INSERT OR REPLACE INTO season(
|
||||||
plex_id, checksum, section_id, show_id, parent_id,
|
plex_id
|
||||||
kodi_id, fanart_synced, last_sync)
|
checksum
|
||||||
|
section_id
|
||||||
|
show_id
|
||||||
|
parent_id
|
||||||
|
kodi_id
|
||||||
|
fanart_synced
|
||||||
|
last_sync)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
'''
|
'''
|
||||||
self.plexcursor.execute(
|
self.plexcursor.execute(
|
||||||
|
@ -45,9 +56,18 @@ class TVShows(object):
|
||||||
"""
|
"""
|
||||||
query = '''
|
query = '''
|
||||||
INSERT OR REPLACE INTO episode(
|
INSERT OR REPLACE INTO episode(
|
||||||
plex_id, checksum, section_id, show_id, grandparent_id,
|
plex_id
|
||||||
season_id, parent_id, kodi_id, kodi_fileid, kodi_pathid,
|
checksum
|
||||||
fanart_synced, last_sync)
|
section_id
|
||||||
|
show_id
|
||||||
|
grandparent_id
|
||||||
|
season_id
|
||||||
|
parent_id
|
||||||
|
kodi_id
|
||||||
|
kodi_fileid
|
||||||
|
kodi_pathid
|
||||||
|
fanart_synced
|
||||||
|
last_sync)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
'''
|
'''
|
||||||
self.plexcursor.execute(
|
self.plexcursor.execute(
|
||||||
|
@ -106,3 +126,54 @@ class TVShows(object):
|
||||||
self.cursor.execute('SELECT * FROM episode WHERE plex_id = ? LIMIT 1',
|
self.cursor.execute('SELECT * FROM episode WHERE plex_id = ? LIMIT 1',
|
||||||
(plex_id, ))
|
(plex_id, ))
|
||||||
return self.cursor.fetchone()
|
return self.cursor.fetchone()
|
||||||
|
|
||||||
|
def season_has_episodes(self, plex_id):
|
||||||
|
"""
|
||||||
|
Returns True if there are episodes left for the season with plex_id
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT plex_id FROM episode WHERE season_id = ? LIMIT 1',
|
||||||
|
(plex_id, ))
|
||||||
|
return self.cursor.fetchone() is not None
|
||||||
|
|
||||||
|
def show_has_seasons(self, plex_id):
|
||||||
|
"""
|
||||||
|
Returns True if there are seasons left for the show with plex_id
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT plex_id FROM season WHERE show_id = ? LIMIT 1',
|
||||||
|
(plex_id, ))
|
||||||
|
return self.cursor.fetchone() is not None
|
||||||
|
|
||||||
|
def show_has_episodes(self, plex_id):
|
||||||
|
"""
|
||||||
|
Returns True if there are episodes left for the show with plex_id
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT plex_id FROM episode WHERE show_id = ? LIMIT 1',
|
||||||
|
(plex_id, ))
|
||||||
|
return self.cursor.fetchone() is not None
|
||||||
|
|
||||||
|
def episode_by_season(self, plex_id):
|
||||||
|
"""
|
||||||
|
Returns an iterator for all episodes that have a parent season_id with
|
||||||
|
a value of plex_id
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT * FROM episode WHERE season_id = ?',
|
||||||
|
(plex_id, ))
|
||||||
|
return (self.entry_to_episode(x) for x in self.cursor)
|
||||||
|
|
||||||
|
def episode_by_show(self, plex_id):
|
||||||
|
"""
|
||||||
|
Returns an iterator for all episodes that have a grandparent show_id
|
||||||
|
with a value of plex_id
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT * FROM episode WHERE show_id = ?',
|
||||||
|
(plex_id, ))
|
||||||
|
return (self.entry_to_episode(x) for x in self.cursor)
|
||||||
|
|
||||||
|
def season_by_show(self, plex_id):
|
||||||
|
"""
|
||||||
|
Returns an iterator for all seasons that have a parent show_id
|
||||||
|
with a value of plex_id
|
||||||
|
"""
|
||||||
|
self.cursor.execute('SELECT * FROM season WHERE show_id = ?',
|
||||||
|
(plex_id, ))
|
||||||
|
return (self.entry_to_season(x) for x in self.cursor)
|
||||||
|
|
Loading…
Reference in a new issue