2018-10-20 23:49:04 +11:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from __future__ import absolute_import, division, unicode_literals
|
|
|
|
from logging import getLogger
|
|
|
|
import copy
|
|
|
|
|
|
|
|
from . import common, videonodes
|
|
|
|
from ..utils import cast
|
2018-10-25 02:17:02 +11:00
|
|
|
from ..plex_db import PlexDB
|
|
|
|
from .. import kodidb_functions as kodidb
|
2018-10-20 23:49:04 +11:00
|
|
|
from .. import itemtypes
|
2018-10-25 02:55:56 +11:00
|
|
|
from .. import plex_functions as PF, music, utils, state, variables as v
|
2018-10-20 23:49:04 +11:00
|
|
|
|
|
|
|
LOG = getLogger('PLEX.library_sync.sections')
|
|
|
|
|
|
|
|
VNODES = videonodes.VideoNodes()
|
|
|
|
PLAYLISTS = {}
|
|
|
|
NODES = {}
|
|
|
|
SECTIONS = []
|
|
|
|
|
|
|
|
|
|
|
|
def sync_from_pms():
|
|
|
|
"""
|
|
|
|
Sync the Plex library sections
|
|
|
|
"""
|
|
|
|
sections = PF.get_plex_sections()
|
|
|
|
try:
|
|
|
|
sections.attrib
|
|
|
|
except AttributeError:
|
|
|
|
LOG.error("Error download PMS sections, abort")
|
|
|
|
return False
|
|
|
|
if state.DIRECT_PATHS is True and state.ENABLE_MUSIC is True:
|
|
|
|
# Will reboot Kodi is new library detected
|
|
|
|
music.excludefromscan_music_folders(xml=sections)
|
|
|
|
|
|
|
|
global PLAYLISTS, NODES, SECTIONS
|
|
|
|
SECTIONS = []
|
|
|
|
NODES = {
|
|
|
|
v.PLEX_TYPE_MOVIE: [],
|
|
|
|
v.PLEX_TYPE_SHOW: [],
|
|
|
|
v.PLEX_TYPE_ARTIST: [],
|
|
|
|
v.PLEX_TYPE_PHOTO: []
|
|
|
|
}
|
|
|
|
PLAYLISTS = copy.deepcopy(NODES)
|
|
|
|
sorted_sections = []
|
|
|
|
|
|
|
|
for section in sections:
|
|
|
|
if (section.attrib['type'] in
|
|
|
|
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW, v.PLEX_TYPE_PHOTO,
|
|
|
|
v.PLEX_TYPE_ARTIST)):
|
|
|
|
sorted_sections.append(cast(unicode,
|
|
|
|
section.attrib['title']))
|
|
|
|
LOG.debug('Sorted sections: %s', sorted_sections)
|
|
|
|
totalnodes = len(sorted_sections)
|
|
|
|
|
|
|
|
VNODES.clearProperties()
|
|
|
|
|
2018-10-25 02:17:02 +11:00
|
|
|
with PlexDB() as plexdb:
|
2018-10-20 23:49:04 +11:00
|
|
|
# Backup old sections to delete them later, if needed (at the end
|
|
|
|
# of this method, only unused sections will be left in old_sections)
|
2018-10-25 22:19:46 +11:00
|
|
|
old_sections = list(plexdb.section_ids())
|
2018-10-20 23:49:04 +11:00
|
|
|
with kodidb.GetKodiDB('video') as kodi_db:
|
|
|
|
for section in sections:
|
|
|
|
_process_section(section,
|
|
|
|
kodi_db,
|
2018-10-25 02:17:02 +11:00
|
|
|
plexdb,
|
2018-10-20 23:49:04 +11:00
|
|
|
sorted_sections,
|
|
|
|
old_sections,
|
|
|
|
totalnodes)
|
|
|
|
if old_sections:
|
|
|
|
# Section has been deleted on the PMS
|
|
|
|
delete_sections(old_sections)
|
|
|
|
# update sections for all:
|
2018-10-25 22:20:46 +11:00
|
|
|
with PlexDB() as plexdb:
|
2018-10-25 22:19:46 +11:00
|
|
|
SECTIONS = list(plexdb.section_infos())
|
2018-10-20 23:49:04 +11:00
|
|
|
utils.window('Plex.nodes.total', str(totalnodes))
|
|
|
|
LOG.info("Finished processing library sections: %s", SECTIONS)
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
2018-10-25 02:17:02 +11:00
|
|
|
def _process_section(section_xml, kodi_db, plexdb, sorted_sections,
|
2018-10-20 23:49:04 +11:00
|
|
|
old_sections, totalnodes):
|
|
|
|
folder = section_xml.attrib
|
|
|
|
plex_type = cast(unicode, folder['type'])
|
|
|
|
# Only process supported formats
|
|
|
|
if plex_type not in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW,
|
|
|
|
v.PLEX_TYPE_ARTIST, v.PLEX_TYPE_PHOTO):
|
|
|
|
LOG.error('Unsupported Plex section type: %s', folder)
|
|
|
|
return totalnodes
|
|
|
|
section_id = cast(int, folder['key'])
|
|
|
|
section_name = cast(unicode, folder['title'])
|
|
|
|
global PLAYLISTS, NODES
|
|
|
|
# Prevent duplicate for nodes of the same type
|
|
|
|
nodes = NODES[plex_type]
|
|
|
|
# Prevent duplicate for playlists of the same type
|
|
|
|
playlists = PLAYLISTS[plex_type]
|
|
|
|
# Get current media folders from plex database
|
2018-10-25 02:17:02 +11:00
|
|
|
section = plexdb.section(section_id)
|
2018-10-20 23:49:04 +11:00
|
|
|
try:
|
|
|
|
current_sectionname = section[1]
|
|
|
|
current_sectiontype = section[2]
|
|
|
|
current_tagid = section[3]
|
|
|
|
except TypeError:
|
|
|
|
LOG.info('Creating section id: %s in Plex database.', section_id)
|
|
|
|
tagid = kodi_db.create_tag(section_name)
|
|
|
|
# Create playlist for the video library
|
|
|
|
if (section_name not in playlists and
|
|
|
|
plex_type in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
|
|
|
utils.playlist_xsp(plex_type, section_name, section_id)
|
|
|
|
playlists.append(section_name)
|
|
|
|
# Create the video node
|
|
|
|
if section_name not in nodes:
|
|
|
|
VNODES.viewNode(sorted_sections.index(section_name),
|
|
|
|
section_name,
|
|
|
|
plex_type,
|
|
|
|
None,
|
|
|
|
section_id)
|
|
|
|
nodes.append(section_name)
|
|
|
|
totalnodes += 1
|
|
|
|
# Add view to plex database
|
2018-10-25 02:17:02 +11:00
|
|
|
plexdb.add_section(section_id, section_name, plex_type, tagid)
|
2018-10-20 23:49:04 +11:00
|
|
|
else:
|
|
|
|
LOG.info('Found library section id %s, name %s, type %s, tagid %s',
|
|
|
|
section_id, current_sectionname, current_sectiontype,
|
|
|
|
current_tagid)
|
|
|
|
# Remove views that are still valid to delete rest later
|
|
|
|
try:
|
|
|
|
old_sections.remove(section_id)
|
|
|
|
except ValueError:
|
|
|
|
# View was just created, nothing to remove
|
|
|
|
pass
|
|
|
|
|
|
|
|
# View was modified, update with latest info
|
|
|
|
if current_sectionname != section_name:
|
|
|
|
LOG.info('section id: %s new sectionname: %s',
|
|
|
|
section_id, section_name)
|
|
|
|
tagid = kodi_db.create_tag(section_name)
|
|
|
|
|
|
|
|
# Update view with new info
|
2018-10-25 02:17:02 +11:00
|
|
|
plexdb.add_section(section_id,
|
2018-10-23 22:54:09 +11:00
|
|
|
section_name,
|
|
|
|
plex_type,
|
|
|
|
tagid)
|
2018-10-20 23:49:04 +11:00
|
|
|
|
2018-10-25 02:17:02 +11:00
|
|
|
if plexdb.section_id_by_name(current_sectionname) is None:
|
2018-10-20 23:49:04 +11:00
|
|
|
# The tag could be a combined view. Ensure there's
|
|
|
|
# no other tags with the same name before deleting
|
|
|
|
# playlist.
|
|
|
|
utils.playlist_xsp(plex_type,
|
|
|
|
current_sectionname,
|
|
|
|
section_id,
|
|
|
|
current_sectiontype,
|
|
|
|
True)
|
|
|
|
# Delete video node
|
|
|
|
if plex_type != "musicvideos":
|
|
|
|
VNODES.viewNode(
|
|
|
|
indexnumber=sorted_sections.index(section_name),
|
|
|
|
tagname=current_sectionname,
|
|
|
|
mediatype=plex_type,
|
|
|
|
viewtype=None,
|
|
|
|
viewid=section_id,
|
|
|
|
delete=True)
|
|
|
|
# Added new playlist
|
|
|
|
if section_name not in playlists and plex_type in v.KODI_VIDEOTYPES:
|
|
|
|
utils.playlist_xsp(plex_type,
|
|
|
|
section_name,
|
|
|
|
section_id)
|
|
|
|
playlists.append(section_name)
|
|
|
|
# Add new video node
|
|
|
|
if section_name not in nodes and plex_type != "musicvideos":
|
|
|
|
VNODES.viewNode(sorted_sections.index(section_name),
|
|
|
|
section_name,
|
|
|
|
plex_type,
|
|
|
|
None,
|
|
|
|
section_id)
|
|
|
|
nodes.append(section_name)
|
|
|
|
totalnodes += 1
|
|
|
|
# Update items with new tag
|
2018-10-25 02:17:02 +11:00
|
|
|
for item in plexdb.kodi_id_by_section(section_id):
|
2018-10-20 23:49:04 +11:00
|
|
|
# Remove the "s" from viewtype for tags
|
|
|
|
kodi_db.update_tag(
|
|
|
|
current_tagid, tagid, item[0], current_sectiontype[:-1])
|
|
|
|
else:
|
|
|
|
# Validate the playlist exists or recreate it
|
|
|
|
if (section_name not in playlists and plex_type in
|
|
|
|
(v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)):
|
|
|
|
utils.playlist_xsp(plex_type,
|
|
|
|
section_name,
|
|
|
|
section_id)
|
|
|
|
playlists.append(section_name)
|
|
|
|
# Create the video node if not already exists
|
|
|
|
if section_name not in nodes and plex_type != "musicvideos":
|
|
|
|
VNODES.viewNode(sorted_sections.index(section_name),
|
|
|
|
section_name,
|
|
|
|
plex_type,
|
|
|
|
None,
|
|
|
|
section_id)
|
|
|
|
nodes.append(section_name)
|
|
|
|
totalnodes += 1
|
|
|
|
return totalnodes
|
|
|
|
|
|
|
|
|
|
|
|
def delete_sections(old_sections):
|
|
|
|
"""
|
|
|
|
Deletes all elements for a Plex section that has been deleted. (e.g. all
|
|
|
|
TV shows, Seasons and Episodes of a Show section)
|
|
|
|
"""
|
|
|
|
utils.dialog('notification',
|
|
|
|
heading='{plex}',
|
|
|
|
message=utils.lang(30052),
|
|
|
|
icon='{plex}',
|
|
|
|
sound=False)
|
|
|
|
video_library_update = False
|
|
|
|
music_library_update = False
|
2018-10-25 02:17:02 +11:00
|
|
|
with PlexDB() as plexdb:
|
|
|
|
old_sections = [plexdb.section(x) for x in old_sections]
|
2018-10-20 23:49:04 +11:00
|
|
|
LOG.info("Removing entire Plex library sections: %s", old_sections)
|
|
|
|
with kodidb.GetKodiDB() as kodi_db:
|
|
|
|
for section in old_sections:
|
|
|
|
if section[2] == v.KODI_TYPE_MOVIE:
|
|
|
|
video_library_update = True
|
2018-10-25 02:17:02 +11:00
|
|
|
context = itemtypes.Movie(plexdb=plexdb,
|
2018-10-20 23:49:04 +11:00
|
|
|
kodi_db=kodi_db)
|
|
|
|
elif section[2] == v.KODI_TYPE_SHOW:
|
|
|
|
video_library_update = True
|
2018-10-25 02:17:02 +11:00
|
|
|
context = itemtypes.Show(plexdb=plexdb,
|
2018-10-20 23:49:04 +11:00
|
|
|
kodi_db=kodi_db)
|
|
|
|
elif section[2] == v.KODI_TYPE_ARTIST:
|
|
|
|
music_library_update = True
|
2018-10-25 02:17:02 +11:00
|
|
|
context = itemtypes.Artist(plexdb=plexdb,
|
2018-10-20 23:49:04 +11:00
|
|
|
kodi_db=kodi_db)
|
|
|
|
elif section[2] == v.KODI_TYPE_PHOTO:
|
|
|
|
# not synced
|
2018-10-25 02:17:02 +11:00
|
|
|
plexdb.remove_section(section[0])
|
2018-10-20 23:49:04 +11:00
|
|
|
continue
|
2018-10-25 02:17:02 +11:00
|
|
|
for plex_id in plexdb.plexid_by_section(section[0]):
|
2018-10-20 23:49:04 +11:00
|
|
|
context.remove(plex_id)
|
|
|
|
# Only remove Plex entry if we've removed all items first
|
2018-10-25 02:17:02 +11:00
|
|
|
plexdb.remove_section(section[0])
|
2018-10-20 23:49:04 +11:00
|
|
|
common.update_kodi_library(video=video_library_update,
|
|
|
|
music=music_library_update)
|