Major music overhaul: Direct Paths should now work!
- Remember to always use Direct Paths with Music ;-) - Fixes #84
This commit is contained in:
parent
ec6a526f09
commit
cbb44e4ccf
5 changed files with 154 additions and 19 deletions
|
@ -1894,3 +1894,8 @@ msgstr ""
|
||||||
msgctxt "#39710"
|
msgctxt "#39710"
|
||||||
msgid "burn-in"
|
msgid "burn-in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
# Dialog text if PKC detected a new Music library and Kodi needs to be restarted
|
||||||
|
msgctxt "#39711"
|
||||||
|
msgid "New Plex music library detected. Sorry, but we need to restart Kodi now due to the changes made."
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -7,7 +7,7 @@ import xbmc
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
|
||||||
from utils import settings, window, language as lang, tryEncode, \
|
from utils import settings, window, language as lang, tryEncode, \
|
||||||
advancessettings_xml
|
advancedsettings_xml
|
||||||
import downloadutils
|
import downloadutils
|
||||||
from userclient import UserClient
|
from userclient import UserClient
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ class InitialSetup():
|
||||||
dialog = self.dialog
|
dialog = self.dialog
|
||||||
|
|
||||||
# Get current Kodi video cache setting
|
# Get current Kodi video cache setting
|
||||||
cache = advancessettings_xml(['cache', 'memorysize'])
|
cache, _ = advancedsettings_xml(['cache', 'memorysize'])
|
||||||
if cache is not None:
|
if cache is not None:
|
||||||
cache = str(cache.text)
|
cache = str(cache.text)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -29,6 +29,7 @@ from library_sync.get_metadata import Threaded_Get_Metadata
|
||||||
from library_sync.process_metadata import Threaded_Process_Metadata
|
from library_sync.process_metadata import Threaded_Process_Metadata
|
||||||
import library_sync.sync_info as sync_info
|
import library_sync.sync_info as sync_info
|
||||||
from library_sync.fanart import Process_Fanart_Thread
|
from library_sync.fanart import Process_Fanart_Thread
|
||||||
|
import music
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
@ -71,6 +72,7 @@ class LibrarySync(Thread):
|
||||||
self.enableMusic = settings('enableMusic') == "true"
|
self.enableMusic = settings('enableMusic') == "true"
|
||||||
self.enableBackgroundSync = settings(
|
self.enableBackgroundSync = settings(
|
||||||
'enableBackgroundSync') == "true"
|
'enableBackgroundSync') == "true"
|
||||||
|
self.direct_paths = settings('useDirectPaths') == '1'
|
||||||
|
|
||||||
# Init for replacing paths
|
# Init for replacing paths
|
||||||
window('remapSMB', value=settings('remapSMB'))
|
window('remapSMB', value=settings('remapSMB'))
|
||||||
|
@ -295,6 +297,15 @@ class LibrarySync(Thread):
|
||||||
}
|
}
|
||||||
if self.enableMusic:
|
if self.enableMusic:
|
||||||
process['music'] = self.PlexMusic
|
process['music'] = self.PlexMusic
|
||||||
|
if self.direct_paths is True:
|
||||||
|
if music.set_excludefromscan_music_folders() is True:
|
||||||
|
log.info('Detected new Music library - restarting now')
|
||||||
|
# 'New Plex music library detected. Sorry, but we need to
|
||||||
|
# restart Kodi now due to the changes made.'
|
||||||
|
dialog('ok', lang(29999), lang(39711))
|
||||||
|
from xbmc import executebuiltin
|
||||||
|
executebuiltin('RestartApp')
|
||||||
|
return False
|
||||||
|
|
||||||
# Do the processing
|
# Do the processing
|
||||||
for itemtype in process:
|
for itemtype in process:
|
||||||
|
|
119
resources/lib/music.py
Normal file
119
resources/lib/music.py
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from logging import getLogger
|
||||||
|
from re import compile as re_compile
|
||||||
|
import xml.etree.ElementTree as etree
|
||||||
|
|
||||||
|
from utils import advancedsettings_xml, indent, tryEncode
|
||||||
|
from PlexFunctions import get_plex_sections
|
||||||
|
from PlexAPI import API
|
||||||
|
import variables as v
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
log = getLogger("PLEX."+__name__)
|
||||||
|
|
||||||
|
REGEX_MUSICPATH = re_compile(r'''^\^(.+)\$$''')
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_music_folders():
|
||||||
|
"""
|
||||||
|
Returns a list of encoded strings as paths to the currently "blacklisted"
|
||||||
|
excludefromscan music folders in the advancedsettings.xml
|
||||||
|
"""
|
||||||
|
paths = []
|
||||||
|
root, _ = advancedsettings_xml(['audio', 'excludefromscan'])
|
||||||
|
if root is None:
|
||||||
|
return paths
|
||||||
|
|
||||||
|
for element in root:
|
||||||
|
try:
|
||||||
|
path = REGEX_MUSICPATH.findall(element.text)[0]
|
||||||
|
except IndexError:
|
||||||
|
log.error('Could not parse %s of xml element %s'
|
||||||
|
% (element.text, element.tag))
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
paths.append(path)
|
||||||
|
return paths
|
||||||
|
|
||||||
|
|
||||||
|
def set_excludefromscan_music_folders():
|
||||||
|
"""
|
||||||
|
Gets a complete list of paths for music libraries from the PMS. Sets them
|
||||||
|
to be excluded in the advancedsettings.xml from being scanned by Kodi.
|
||||||
|
Existing keys will be replaced
|
||||||
|
|
||||||
|
Returns False if no new Plex libraries needed to be exluded, True otherwise
|
||||||
|
"""
|
||||||
|
changed = False
|
||||||
|
write_xml = False
|
||||||
|
xml = get_plex_sections()
|
||||||
|
try:
|
||||||
|
xml[0].attrib
|
||||||
|
except (TypeError, IndexError, AttributeError):
|
||||||
|
log.error('Could not get Plex sections')
|
||||||
|
return
|
||||||
|
# Build paths
|
||||||
|
paths = []
|
||||||
|
api = API(item=None)
|
||||||
|
for library in xml:
|
||||||
|
if library.attrib['type'] != v.PLEX_TYPE_ARTIST:
|
||||||
|
# Only look at music libraries
|
||||||
|
continue
|
||||||
|
for location in library:
|
||||||
|
if location.tag == 'Location':
|
||||||
|
path = api.validatePlayurl(location.attrib['path'],
|
||||||
|
typus=v.PLEX_TYPE_ARTIST,
|
||||||
|
forceCheck=True)
|
||||||
|
path = tryEncode(path)
|
||||||
|
paths.append(__turn_to_regex(path))
|
||||||
|
# Get existing advancedsettings
|
||||||
|
root, tree = advancedsettings_xml(['audio', 'excludefromscan'],
|
||||||
|
force_create=True)
|
||||||
|
|
||||||
|
for path in paths:
|
||||||
|
for element in root:
|
||||||
|
if element.text == path:
|
||||||
|
# Path already excluded
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
changed = True
|
||||||
|
write_xml = True
|
||||||
|
log.info('New Plex music library detected: %s' % path)
|
||||||
|
element = etree.Element(tag='regexp')
|
||||||
|
element.text = path
|
||||||
|
root.append(element)
|
||||||
|
|
||||||
|
# Delete obsolete entries (unlike above, we don't change 'changed' to not
|
||||||
|
# enforce a restart)
|
||||||
|
for element in root:
|
||||||
|
for path in paths:
|
||||||
|
if element.text == path:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
log.info('Deleting Plex music library from advancedsettings: %s'
|
||||||
|
% element.text)
|
||||||
|
root.remove(element)
|
||||||
|
write_xml = True
|
||||||
|
|
||||||
|
if write_xml is True:
|
||||||
|
indent(tree.getroot())
|
||||||
|
tree.write('%sadvancedsettings.xml' % v.KODI_PROFILE)
|
||||||
|
return changed
|
||||||
|
|
||||||
|
|
||||||
|
def __turn_to_regex(path):
|
||||||
|
"""
|
||||||
|
Turns a path into regex expression to be fed to Kodi's advancedsettings.xml
|
||||||
|
"""
|
||||||
|
# Make sure we have a slash or backslash at the end of the path
|
||||||
|
if '/' in path:
|
||||||
|
if not path.endswith('/'):
|
||||||
|
path = '%s/' % path
|
||||||
|
else:
|
||||||
|
if not path.endswith('\\'):
|
||||||
|
path = '%s\\' % path
|
||||||
|
# Need to escape backslashes
|
||||||
|
path = path.replace('\\', '\\\\')
|
||||||
|
# Beginning of path only needs to be similar
|
||||||
|
return '^%s' % path
|
|
@ -23,7 +23,7 @@ import xbmcaddon
|
||||||
import xbmcgui
|
import xbmcgui
|
||||||
|
|
||||||
from variables import DB_VIDEO_PATH, DB_MUSIC_PATH, DB_TEXTURE_PATH, \
|
from variables import DB_VIDEO_PATH, DB_MUSIC_PATH, DB_TEXTURE_PATH, \
|
||||||
DB_PLEX_PATH
|
DB_PLEX_PATH, KODI_PROFILE
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
@ -522,9 +522,11 @@ def __setSubElement(element, subelement):
|
||||||
return answ
|
return answ
|
||||||
|
|
||||||
|
|
||||||
def advancessettings_xml(node_list, new_value=None, attrib=None):
|
def advancedsettings_xml(node_list, new_value=None, attrib=None,
|
||||||
|
force_create=False):
|
||||||
"""
|
"""
|
||||||
Returns the etree element for nodelist (if it exists) and None if not set
|
Returns the etree element for nodelist (if it exists) and the tree. None if
|
||||||
|
not set
|
||||||
|
|
||||||
node_list is a list of node names starting from the outside, ignoring the
|
node_list is a list of node names starting from the outside, ignoring the
|
||||||
outter advancedsettings. Example nodelist=['video', 'busydialogdelayms']
|
outter advancedsettings. Example nodelist=['video', 'busydialogdelayms']
|
||||||
|
@ -547,28 +549,29 @@ def advancessettings_xml(node_list, new_value=None, attrib=None):
|
||||||
|
|
||||||
If the dict attrib is set, the Element's attributs will be appended
|
If the dict attrib is set, the Element's attributs will be appended
|
||||||
accordingly
|
accordingly
|
||||||
|
|
||||||
|
force_create=True will forcibly create the key even if no value is provided
|
||||||
"""
|
"""
|
||||||
path = '%sadvancedsettings.xml' % xbmc.translatePath("special://profile/")
|
path = '%sadvancedsettings.xml' % KODI_PROFILE
|
||||||
try:
|
try:
|
||||||
xml = etree.parse(path)
|
tree = etree.parse(path)
|
||||||
except IOError:
|
except IOError:
|
||||||
# Document is blank or missing
|
# Document is blank or missing
|
||||||
if new_value is None and attrib is None:
|
if new_value is None and attrib is None and force_create is False:
|
||||||
log.debug('Could not parse advancedsettings.xml, returning None')
|
log.debug('Could not parse advancedsettings.xml, returning None')
|
||||||
return
|
return
|
||||||
# Create topmost xml entry
|
# Create topmost xml entry
|
||||||
root = etree.Element('advancedsettings')
|
tree = etree.ElementTree(element=etree.Element('advancedsettings'))
|
||||||
else:
|
root = tree.getroot()
|
||||||
root = xml.getroot()
|
|
||||||
element = root
|
element = root
|
||||||
|
|
||||||
# Reading values
|
# Reading values
|
||||||
if new_value is None and attrib is None:
|
if new_value is None and attrib is None and force_create is False:
|
||||||
for node in node_list:
|
for node in node_list:
|
||||||
element = element.find(node)
|
element = element.find(node)
|
||||||
if element is None:
|
if element is None:
|
||||||
break
|
break
|
||||||
return element
|
return element, tree
|
||||||
|
|
||||||
# Setting new values. Get correct element first
|
# Setting new values. Get correct element first
|
||||||
for node in node_list:
|
for node in node_list:
|
||||||
|
@ -581,11 +584,8 @@ def advancessettings_xml(node_list, new_value=None, attrib=None):
|
||||||
# Indent and make readable
|
# Indent and make readable
|
||||||
indent(root)
|
indent(root)
|
||||||
# Safe the changed xml
|
# Safe the changed xml
|
||||||
try:
|
tree.write(path)
|
||||||
xml.write(path)
|
return element, tree
|
||||||
except NameError:
|
|
||||||
etree.ElementTree(root).write(path)
|
|
||||||
return element
|
|
||||||
|
|
||||||
|
|
||||||
def advancedsettings_tweaks():
|
def advancedsettings_tweaks():
|
||||||
|
@ -595,7 +595,7 @@ def advancedsettings_tweaks():
|
||||||
Changes advancedsettings.xml, musiclibrary:
|
Changes advancedsettings.xml, musiclibrary:
|
||||||
backgroundupdate set to "true"
|
backgroundupdate set to "true"
|
||||||
"""
|
"""
|
||||||
advancessettings_xml(['musiclibrary', 'backgroundupdate'],
|
advancedsettings_xml(['musiclibrary', 'backgroundupdate'],
|
||||||
new_value='true')
|
new_value='true')
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue