Merge branch 'master' into translations
This commit is contained in:
commit
056285f7ae
20 changed files with 913 additions and 791 deletions
|
@ -1,5 +1,5 @@
|
||||||
[![stable version](https://img.shields.io/badge/stable_version-1.8.18-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip)
|
[![stable version](https://img.shields.io/badge/stable_version-1.8.18-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip)
|
||||||
[![beta version](https://img.shields.io/badge/beta_version-2.0.18-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip)
|
[![beta version](https://img.shields.io/badge/beta_version-2.0.19-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip)
|
||||||
|
|
||||||
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation)
|
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation)
|
||||||
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)
|
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)
|
||||||
|
|
16
addon.xml
16
addon.xml
|
@ -1,10 +1,10 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.0.18" provider-name="croneter">
|
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.0.19" provider-name="croneter">
|
||||||
<requires>
|
<requires>
|
||||||
<import addon="xbmc.python" version="2.1.0"/>
|
<import addon="xbmc.python" version="2.1.0"/>
|
||||||
<import addon="script.module.requests" version="2.9.1" />
|
<import addon="script.module.requests" version="2.9.1" />
|
||||||
<import addon="plugin.video.plexkodiconnect.movies" version="2.0.1" />
|
<import addon="plugin.video.plexkodiconnect.movies" version="2.0.2" />
|
||||||
<import addon="plugin.video.plexkodiconnect.tvshows" version="2.0.2" />
|
<import addon="plugin.video.plexkodiconnect.tvshows" version="2.0.3" />
|
||||||
</requires>
|
</requires>
|
||||||
<extension point="xbmc.python.pluginsource" library="default.py">
|
<extension point="xbmc.python.pluginsource" library="default.py">
|
||||||
<provides>video audio image</provides>
|
<provides>video audio image</provides>
|
||||||
|
@ -67,7 +67,15 @@
|
||||||
<summary lang="ru_RU">Нативная интеграция сервера Plex в Kodi</summary>
|
<summary lang="ru_RU">Нативная интеграция сервера Plex в Kodi</summary>
|
||||||
<description lang="ru_RU">Подключите Kodi к своему серверу Plex. Плагин предполагает что вы управляете своими видео с помощью Plex (а не в Kodi). Вы можете потерять текущие базы данных музыки и видео в Kodi (так как плагин напрямую их изменяет). Используйте на свой страх и риск</description>
|
<description lang="ru_RU">Подключите Kodi к своему серверу Plex. Плагин предполагает что вы управляете своими видео с помощью Plex (а не в Kodi). Вы можете потерять текущие базы данных музыки и видео в Kodi (так как плагин напрямую их изменяет). Используйте на свой страх и риск</description>
|
||||||
<disclaimer lang="ru_RU">Используйте на свой страх и риск</disclaimer>
|
<disclaimer lang="ru_RU">Используйте на свой страх и риск</disclaimer>
|
||||||
<news>version 2.0.18 (beta only):
|
<news>version 2.0.19 (beta only):
|
||||||
|
- Fix PKC playback startup getting caught in infinity loop
|
||||||
|
- Rewire library sync, suspend sync during playback
|
||||||
|
- Fix playback failing in certain cases
|
||||||
|
- Fix PKC not working anymore after using context menu on songs
|
||||||
|
- Fix deletion of Plex music items
|
||||||
|
- Code cleanup
|
||||||
|
|
||||||
|
version 2.0.18 (beta only):
|
||||||
- Fix some playqueue inconsistencies using Plex Companion
|
- Fix some playqueue inconsistencies using Plex Companion
|
||||||
- Direct paths: fix replaying item where playback was started via PMS
|
- Direct paths: fix replaying item where playback was started via PMS
|
||||||
- Fix Plex trailers screwing up playqueue
|
- Fix Plex trailers screwing up playqueue
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
version 2.0.19 (beta only):
|
||||||
|
- Fix PKC playback startup getting caught in infinity loop
|
||||||
|
- Rewire library sync, suspend sync during playback
|
||||||
|
- Fix playback failing in certain cases
|
||||||
|
- Fix PKC not working anymore after using context menu on songs
|
||||||
|
- Fix deletion of Plex music items
|
||||||
|
- Code cleanup
|
||||||
|
|
||||||
version 2.0.18 (beta only):
|
version 2.0.18 (beta only):
|
||||||
- Fix some playqueue inconsistencies using Plex Companion
|
- Fix some playqueue inconsistencies using Plex Companion
|
||||||
- Direct paths: fix replaying item where playback was started via PMS
|
- Direct paths: fix replaying item where playback was started via PMS
|
||||||
|
|
|
@ -171,8 +171,12 @@ class Main():
|
||||||
"""
|
"""
|
||||||
Start up playback_starter in main Python thread
|
Start up playback_starter in main Python thread
|
||||||
"""
|
"""
|
||||||
|
request = '%s&handle=%s' % (argv[2], HANDLE)
|
||||||
# Put the request into the 'queue'
|
# Put the request into the 'queue'
|
||||||
plex_command('PLAY', argv[2])
|
plex_command('PLAY', request)
|
||||||
|
if HANDLE == -1:
|
||||||
|
# Handle -1 received, not waiting for main thread
|
||||||
|
return
|
||||||
# Wait for the result
|
# Wait for the result
|
||||||
while not pickl_window('plex_result'):
|
while not pickl_window('plex_result'):
|
||||||
sleep(50)
|
sleep(50)
|
||||||
|
|
|
@ -94,8 +94,7 @@ class PlexCompanion(Thread):
|
||||||
params = {
|
params = {
|
||||||
'mode': 'plex_node',
|
'mode': 'plex_node',
|
||||||
'key': '{server}%s' % data.get('key'),
|
'key': '{server}%s' % data.get('key'),
|
||||||
'offset': data.get('offset'),
|
'offset': data.get('offset')
|
||||||
'play_directly': 'true'
|
|
||||||
}
|
}
|
||||||
executebuiltin('RunPlugin(plugin://%s?%s)'
|
executebuiltin('RunPlugin(plugin://%s?%s)'
|
||||||
% (v.ADDON_ID, urlencode(params)))
|
% (v.ADDON_ID, urlencode(params)))
|
||||||
|
|
|
@ -29,7 +29,7 @@ LOG = getLogger("PLEX." + __name__)
|
||||||
|
|
||||||
WINDOW_PROPERTIES = (
|
WINDOW_PROPERTIES = (
|
||||||
"plex_online", "plex_serverStatus", "plex_shouldStop", "plex_dbScan",
|
"plex_online", "plex_serverStatus", "plex_shouldStop", "plex_dbScan",
|
||||||
"plex_initialScan", "plex_customplayqueue", "plex_playbackProps",
|
"plex_customplayqueue", "plex_playbackProps",
|
||||||
"pms_token", "plex_token", "pms_server", "plex_machineIdentifier",
|
"pms_token", "plex_token", "pms_server", "plex_machineIdentifier",
|
||||||
"plex_servername", "plex_authenticated", "PlexUserImage", "useDirectPaths",
|
"plex_servername", "plex_authenticated", "PlexUserImage", "useDirectPaths",
|
||||||
"countError", "countUnauthorized", "plex_restricteduser",
|
"countError", "countUnauthorized", "plex_restricteduser",
|
||||||
|
|
|
@ -1719,6 +1719,7 @@ class Music(Items):
|
||||||
# Update album artwork
|
# Update album artwork
|
||||||
artwork.modify_artwork(artworks, albumid, v.KODI_TYPE_ALBUM, kodicursor)
|
artwork.modify_artwork(artworks, albumid, v.KODI_TYPE_ALBUM, kodicursor)
|
||||||
|
|
||||||
|
@catch_exceptions(warnuser=True)
|
||||||
def remove(self, plex_id):
|
def remove(self, plex_id):
|
||||||
"""
|
"""
|
||||||
Completely remove the item with plex_id from the Kodi and Plex DBs.
|
Completely remove the item with plex_id from the Kodi and Plex DBs.
|
||||||
|
@ -1728,92 +1729,124 @@ class Music(Items):
|
||||||
try:
|
try:
|
||||||
kodi_id = plex_dbitem[0]
|
kodi_id = plex_dbitem[0]
|
||||||
file_id = plex_dbitem[1]
|
file_id = plex_dbitem[1]
|
||||||
|
path_id = plex_dbitem[2]
|
||||||
parent_id = plex_dbitem[3]
|
parent_id = plex_dbitem[3]
|
||||||
kodi_type = plex_dbitem[4]
|
kodi_type = plex_dbitem[4]
|
||||||
LOG.info("Removing %s with kodi_id: %s, parent_id: %s, file_id: %s",
|
LOG.debug('Removing plex_id %s with kodi_type %s, kodi_id %s, '
|
||||||
kodi_type, kodi_id, parent_id, file_id)
|
'parent_id %s, file_id %s, pathid %s',
|
||||||
|
plex_id, kodi_type, kodi_id, parent_id, file_id, path_id)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
LOG.debug('Cannot delete item with plex id %s from Kodi', plex_id)
|
LOG.debug('Cannot delete item with plex id %s from Kodi', plex_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Remove the plex reference
|
# Remove the plex reference
|
||||||
self.plex_db.removeItem(plex_id)
|
self.plex_db.removeItem(plex_id)
|
||||||
##### SONG #####
|
##### SONG #####
|
||||||
if kodi_type == v.KODI_TYPE_SONG:
|
if kodi_type == v.KODI_TYPE_SONG:
|
||||||
# Delete song
|
# Delete song and orphaned artists and albums
|
||||||
self.remove_song(kodi_id)
|
self._remove_song(kodi_id, path_id=path_id)
|
||||||
# Album verification
|
# Album verification
|
||||||
|
album = self.plex_db.getItem_byKodiId(parent_id,
|
||||||
for item in self.plex_db.getItem_byWildId(plex_id):
|
v.KODI_TYPE_ALBUM)
|
||||||
|
if not self.plex_db.getItem_byParentId(parent_id,
|
||||||
item_kid = item[0]
|
v.KODI_TYPE_SONG):
|
||||||
item_kodi_type = item[1]
|
# No song left for album - so delete the album
|
||||||
|
self.plex_db.removeItem(album[0])
|
||||||
if item_kodi_type == v.KODI_TYPE_ALBUM:
|
self._remove_album(parent_id)
|
||||||
childs = self.plex_db.getItem_byParentId(item_kid,
|
|
||||||
v.KODI_TYPE_SONG)
|
|
||||||
if not childs:
|
|
||||||
# Delete album
|
|
||||||
self.remove_album(item_kid)
|
|
||||||
|
|
||||||
##### ALBUM #####
|
##### ALBUM #####
|
||||||
elif kodi_type == v.KODI_TYPE_ALBUM:
|
elif kodi_type == v.KODI_TYPE_ALBUM:
|
||||||
# Delete songs, album
|
# Delete songs, album
|
||||||
album_songs = self.plex_db.getItem_byParentId(kodi_id,
|
songs = self.plex_db.getItem_byParentId(kodi_id,
|
||||||
v.KODI_TYPE_SONG)
|
v.KODI_TYPE_SONG)
|
||||||
for song in album_songs:
|
for song in songs:
|
||||||
self.remove_song(song[1])
|
self._remove_song(song[1], path_id=song[2])
|
||||||
# Remove plex songs
|
# Remove songs from Plex table
|
||||||
self.plex_db.removeItems_byParentId(kodi_id,
|
self.plex_db.removeItems_byParentId(kodi_id,
|
||||||
v.KODI_TYPE_SONG)
|
v.KODI_TYPE_SONG)
|
||||||
# Remove the album
|
# Remove the album and associated orphaned entries
|
||||||
self.remove_album(kodi_id)
|
self._remove_album(kodi_id)
|
||||||
|
|
||||||
##### IF ARTIST #####
|
##### IF ARTIST #####
|
||||||
elif kodi_type == v.KODI_TYPE_ARTIST:
|
elif kodi_type == v.KODI_TYPE_ARTIST:
|
||||||
# Delete songs, album, artist
|
# Delete songs, album, artist
|
||||||
albums = self.plex_db.getItem_byParentId(kodi_id, v.KODI_TYPE_ALBUM)
|
albums = self.plex_db.getItem_byParentId(kodi_id, v.KODI_TYPE_ALBUM)
|
||||||
for album in albums:
|
for album in albums:
|
||||||
albumid = album[1]
|
songs = self.plex_db.getItem_byParentId(album[1],
|
||||||
album_songs = self.plex_db.getItem_byParentId(albumid,
|
|
||||||
v.KODI_TYPE_SONG)
|
v.KODI_TYPE_SONG)
|
||||||
for song in album_songs:
|
for song in songs:
|
||||||
self.remove_song(song[1])
|
self._remove_song(song[1], path_id=song[2])
|
||||||
# Remove plex song
|
# Remove entries for the songs in the Plex db
|
||||||
self.plex_db.removeItems_byParentId(albumid, v.KODI_TYPE_SONG)
|
self.plex_db.removeItems_byParentId(album[1], v.KODI_TYPE_SONG)
|
||||||
# Remove plex artist
|
|
||||||
self.plex_db.removeItems_byParentId(albumid, v.KODI_TYPE_ARTIST)
|
|
||||||
# Remove kodi album
|
# Remove kodi album
|
||||||
self.remove_album(albumid)
|
self._remove_album(album[1])
|
||||||
# Remove plex albums
|
# Remove album entries in the Plex db
|
||||||
self.plex_db.removeItems_byParentId(kodi_id, v.KODI_TYPE_ALBUM)
|
self.plex_db.removeItems_byParentId(kodi_id, v.KODI_TYPE_ALBUM)
|
||||||
# Remove artist
|
# Remove artist
|
||||||
self.remove_artist(kodi_id)
|
self._remove_artist(kodi_id)
|
||||||
|
|
||||||
LOG.debug("Deleted plex_id %s from kodi database", plex_id)
|
LOG.debug("Deleted plex_id %s from kodi database", plex_id)
|
||||||
|
|
||||||
def remove_song(self, kodi_id):
|
def _remove_song(self, kodi_id, path_id=None):
|
||||||
"""
|
"""
|
||||||
Remove song, and only the song
|
Remove song, orphaned artists and orphaned paths
|
||||||
"""
|
"""
|
||||||
|
if not path_id:
|
||||||
|
query = 'SELECT idPath FROM song WHERE idSong = ? LIMIT 1'
|
||||||
|
self.kodicursor.execute(query, (kodi_id, ))
|
||||||
|
try:
|
||||||
|
path_id = self.kodicursor.fetchone()[0]
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
artist_to_delete = self.kodi_db.delete_song_from_song_artist(kodi_id)
|
||||||
|
if artist_to_delete:
|
||||||
|
# Delete the artist reference in the Plex table
|
||||||
|
artist = self.plex_db.getItem_byKodiId(artist_to_delete,
|
||||||
|
v.KODI_TYPE_ARTIST)
|
||||||
|
try:
|
||||||
|
plex_id = artist[0]
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.plex_db.removeItem(plex_id)
|
||||||
|
self._remove_artist(artist_to_delete)
|
||||||
|
self.kodicursor.execute('DELETE FROM song WHERE idSong = ?',
|
||||||
|
(kodi_id, ))
|
||||||
|
# Check whether we have orphaned path entries
|
||||||
|
query = 'SELECT idPath FROM song WHERE idPath = ? LIMIT 1'
|
||||||
|
self.kodicursor.execute(query, (path_id, ))
|
||||||
|
if not self.kodicursor.fetchone():
|
||||||
|
self.kodicursor.execute('DELETE FROM path WHERE idPath = ?',
|
||||||
|
(path_id, ))
|
||||||
|
if v.KODIVERSION < 18:
|
||||||
|
self.kodi_db.delete_song_from_song_genre(kodi_id)
|
||||||
|
query = 'DELETE FROM albuminfosong WHERE idAlbumInfoSong = ?'
|
||||||
|
self.kodicursor.execute(query, (kodi_id, ))
|
||||||
self.artwork.delete_artwork(kodi_id, v.KODI_TYPE_SONG, self.kodicursor)
|
self.artwork.delete_artwork(kodi_id, v.KODI_TYPE_SONG, self.kodicursor)
|
||||||
self.kodicursor.execute("DELETE FROM song WHERE idSong = ?",
|
|
||||||
(kodi_id,))
|
|
||||||
|
|
||||||
def remove_album(self, kodi_id):
|
def _remove_album(self, kodi_id):
|
||||||
"""
|
'''
|
||||||
Remove an album, and only the album
|
Remove an album
|
||||||
"""
|
'''
|
||||||
|
self.kodi_db.delete_album_from_discography(kodi_id)
|
||||||
|
if v.KODIVERSION < 18:
|
||||||
|
self.kodi_db.delete_album_from_album_genre(kodi_id)
|
||||||
|
query = 'DELETE FROM albuminfosong WHERE idAlbumInfo = ?'
|
||||||
|
self.kodicursor.execute(query, (kodi_id, ))
|
||||||
|
self.kodicursor.execute('DELETE FROM album_artist WHERE idAlbum = ?',
|
||||||
|
(kodi_id, ))
|
||||||
|
self.kodicursor.execute('DELETE FROM album WHERE idAlbum = ?',
|
||||||
|
(kodi_id, ))
|
||||||
self.artwork.delete_artwork(kodi_id, v.KODI_TYPE_ALBUM, self.kodicursor)
|
self.artwork.delete_artwork(kodi_id, v.KODI_TYPE_ALBUM, self.kodicursor)
|
||||||
self.kodicursor.execute("DELETE FROM album WHERE idAlbum = ?",
|
|
||||||
(kodi_id,))
|
|
||||||
|
|
||||||
def remove_artist(self, kodi_id):
|
def _remove_artist(self, kodi_id):
|
||||||
"""
|
'''
|
||||||
Remove an artist, and only the artist
|
Remove an artist and associated songs and albums
|
||||||
"""
|
'''
|
||||||
|
self.kodicursor.execute('DELETE FROM album_artist WHERE idArtist = ?',
|
||||||
|
(kodi_id, ))
|
||||||
|
self.kodicursor.execute('DELETE FROM artist WHERE idArtist = ?',
|
||||||
|
(kodi_id, ))
|
||||||
|
self.kodicursor.execute('DELETE FROM song_artist WHERE idArtist = ?',
|
||||||
|
(kodi_id, ))
|
||||||
|
self.kodicursor.execute('DELETE FROM discography WHERE idArtist = ?',
|
||||||
|
(kodi_id, ))
|
||||||
self.artwork.delete_artwork(kodi_id,
|
self.artwork.delete_artwork(kodi_id,
|
||||||
v.KODI_TYPE_ARTIST,
|
v.KODI_TYPE_ARTIST,
|
||||||
self.kodicursor)
|
self.kodicursor)
|
||||||
self.kodicursor.execute("DELETE FROM artist WHERE idArtist = ?",
|
|
||||||
(kodi_id,))
|
|
||||||
|
|
|
@ -907,6 +907,102 @@ class KodiDBMethods(object):
|
||||||
self.cursor.execute(query, (name, artistid,))
|
self.cursor.execute(query, (name, artistid,))
|
||||||
return artistid
|
return artistid
|
||||||
|
|
||||||
|
def delete_song_from_song_artist(self, song_id):
|
||||||
|
"""
|
||||||
|
Deletes son from song_artist table and possibly orphaned roles
|
||||||
|
Will returned an orphaned idArtist or None if not orphaned
|
||||||
|
"""
|
||||||
|
query = '''
|
||||||
|
SELECT idArtist, idRole FROM song_artist WHERE idSong = ? LIMIT 1
|
||||||
|
'''
|
||||||
|
self.cursor.execute(query, (song_id, ))
|
||||||
|
artist = self.cursor.fetchone()
|
||||||
|
if artist is None:
|
||||||
|
# No entry to begin with
|
||||||
|
return
|
||||||
|
# Delete the entry
|
||||||
|
self.cursor.execute('DELETE FROM song_artist WHERE idSong = ?',
|
||||||
|
(song_id, ))
|
||||||
|
# Check whether we need to delete orphaned roles
|
||||||
|
query = 'SELECT idRole FROM song_artist WHERE idRole = ? LIMIT 1'
|
||||||
|
self.cursor.execute(query, (artist[1], ))
|
||||||
|
if not self.cursor.fetchone():
|
||||||
|
# Delete orphaned role
|
||||||
|
self.cursor.execute('DELETE FROM role WHERE idRole = ?',
|
||||||
|
(artist[1], ))
|
||||||
|
# Check whether we need to delete orphaned artists
|
||||||
|
query = 'SELECT idArtist FROM song_artist WHERE idArtist = ? LIMIT 1'
|
||||||
|
self.cursor.execute(query, (artist[0], ))
|
||||||
|
if self.cursor.fetchone():
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return artist[0]
|
||||||
|
|
||||||
|
def delete_album_from_discography(self, album_id):
|
||||||
|
"""
|
||||||
|
Removes the album with id album_id from the table discography
|
||||||
|
"""
|
||||||
|
# Need to get the album name as a string first!
|
||||||
|
query = 'SELECT strAlbum, iYear FROM album WHERE idAlbum = ? LIMIT 1'
|
||||||
|
self.cursor.execute(query, (album_id, ))
|
||||||
|
try:
|
||||||
|
name, year = self.cursor.fetchone()
|
||||||
|
except TypeError:
|
||||||
|
return
|
||||||
|
query = 'SELECT idArtist FROM album_artist WHERE idAlbum = ? LIMIT 1'
|
||||||
|
self.cursor.execute(query, (album_id, ))
|
||||||
|
artist = self.cursor.fetchone()
|
||||||
|
if not artist:
|
||||||
|
return
|
||||||
|
query = '''
|
||||||
|
DELETE FROM discography
|
||||||
|
WHERE idArtist = ? AND strAlbum = ? AND strYear = ?
|
||||||
|
'''
|
||||||
|
self.cursor.execute(query, (artist[0], name, year))
|
||||||
|
|
||||||
|
def delete_song_from_song_genre(self, song_id):
|
||||||
|
"""
|
||||||
|
Deletes the one entry with id song_id from the song_genre table.
|
||||||
|
Will also delete orphaned genres from genre table
|
||||||
|
"""
|
||||||
|
query = 'SELECT idGenre FROM song_genre WHERE idSong = ?'
|
||||||
|
self.cursor.execute(query, (song_id, ))
|
||||||
|
genres = self.cursor.fetchall()
|
||||||
|
self.cursor.execute('DELETE FROM song_genre WHERE idSong = ?',
|
||||||
|
(song_id, ))
|
||||||
|
# Check for orphaned genres in both song_genre and album_genre tables
|
||||||
|
query = 'SELECT idGenre FROM song_genre WHERE idGenre = ? LIMIT 1'
|
||||||
|
query2 = 'SELECT idGenre FROM album_genre WHERE idGenre = ? LIMIT 1'
|
||||||
|
for genre in genres:
|
||||||
|
self.cursor.execute(query, (genre[0], ))
|
||||||
|
if not self.cursor.fetchone():
|
||||||
|
self.cursor.execute(query2, (genre[0], ))
|
||||||
|
if not self.cursor.fetchone():
|
||||||
|
self.cursor.execute('DELETE FROM genre WHERE idGenre = ?',
|
||||||
|
(genre[0], ))
|
||||||
|
|
||||||
|
def delete_album_from_album_genre(self, album_id):
|
||||||
|
"""
|
||||||
|
Deletes the one entry with id album_id from the album_genre table.
|
||||||
|
Will also delete orphaned genres from genre table
|
||||||
|
"""
|
||||||
|
query = 'SELECT idGenre FROM album_genre WHERE idAlbum = ?'
|
||||||
|
self.cursor.execute(query, (album_id, ))
|
||||||
|
genres = self.cursor.fetchall()
|
||||||
|
self.cursor.execute('DELETE FROM album_genre WHERE idAlbum = ?',
|
||||||
|
(album_id, ))
|
||||||
|
# Check for orphaned genres in both album_genre and song_genre tables
|
||||||
|
query = 'SELECT idGenre FROM album_genre WHERE idGenre = ? LIMIT 1'
|
||||||
|
query2 = 'SELECT idGenre FROM song_genre WHERE idGenre = ? LIMIT 1'
|
||||||
|
for genre in genres:
|
||||||
|
self.cursor.execute(query, (genre[0], ))
|
||||||
|
if not self.cursor.fetchone():
|
||||||
|
self.cursor.execute(query2, (genre[0], ))
|
||||||
|
if not self.cursor.fetchone():
|
||||||
|
self.cursor.execute('DELETE FROM genre WHERE idGenre = ?',
|
||||||
|
(genre[0], ))
|
||||||
|
|
||||||
|
|
||||||
def addAlbum(self, name, musicbrainz):
|
def addAlbum(self, name, musicbrainz):
|
||||||
query = 'SELECT idAlbum FROM album WHERE strMusicBrainzAlbumID = ?'
|
query = 'SELECT idAlbum FROM album WHERE strMusicBrainzAlbumID = ?'
|
||||||
self.cursor.execute(query, (musicbrainz,))
|
self.cursor.execute(query, (musicbrainz,))
|
||||||
|
|
|
@ -136,6 +136,7 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
LOG.debug("Method: %s Data: %s", method, data)
|
LOG.debug("Method: %s Data: %s", method, data)
|
||||||
|
|
||||||
if method == "Player.OnPlay":
|
if method == "Player.OnPlay":
|
||||||
|
state.SUSPEND_SYNC = True
|
||||||
self.PlayBackStart(data)
|
self.PlayBackStart(data)
|
||||||
elif method == "Player.OnStop":
|
elif method == "Player.OnStop":
|
||||||
# Should refresh our video nodes, e.g. on deck
|
# Should refresh our video nodes, e.g. on deck
|
||||||
|
@ -143,12 +144,13 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
if data.get('end'):
|
if data.get('end'):
|
||||||
if state.PKC_CAUSED_STOP is True:
|
if state.PKC_CAUSED_STOP is True:
|
||||||
state.PKC_CAUSED_STOP = False
|
state.PKC_CAUSED_STOP = False
|
||||||
state.PKC_CAUSED_STOP_DONE = True
|
|
||||||
LOG.debug('PKC caused this playback stop - ignoring')
|
LOG.debug('PKC caused this playback stop - ignoring')
|
||||||
else:
|
else:
|
||||||
_playback_cleanup(ended=True)
|
_playback_cleanup(ended=True)
|
||||||
else:
|
else:
|
||||||
_playback_cleanup()
|
_playback_cleanup()
|
||||||
|
state.PKC_CAUSED_STOP_DONE = True
|
||||||
|
state.SUSPEND_SYNC = False
|
||||||
elif method == 'Playlist.OnAdd':
|
elif method == 'Playlist.OnAdd':
|
||||||
self._playlist_onadd(data)
|
self._playlist_onadd(data)
|
||||||
elif method == 'Playlist.OnRemove':
|
elif method == 'Playlist.OnRemove':
|
||||||
|
@ -253,8 +255,8 @@ class KodiMonitor(xbmc.Monitor):
|
||||||
"""
|
"""
|
||||||
playqueue = PQ.PLAYQUEUES[data['playlistid']]
|
playqueue = PQ.PLAYQUEUES[data['playlistid']]
|
||||||
if not playqueue.is_pkc_clear():
|
if not playqueue.is_pkc_clear():
|
||||||
playqueue.clear(kodi=False)
|
|
||||||
playqueue.pkc_edit = True
|
playqueue.pkc_edit = True
|
||||||
|
playqueue.clear(kodi=False)
|
||||||
else:
|
else:
|
||||||
LOG.debug('Detected PKC clear - ignoring')
|
LOG.debug('Detected PKC clear - ignoring')
|
||||||
|
|
||||||
|
|
|
@ -12,15 +12,16 @@ import variables as v
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
log = getLogger("PLEX."+__name__)
|
LOG = getLogger("PLEX." + __name__)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
@thread_methods(add_suspends=['SUSPEND_LIBRARY_THREAD',
|
@thread_methods(add_suspends=['SUSPEND_LIBRARY_THREAD',
|
||||||
'DB_SCAN',
|
'DB_SCAN',
|
||||||
'STOP_SYNC'])
|
'STOP_SYNC',
|
||||||
class Process_Fanart_Thread(Thread):
|
'SUSPEND_SYNC'])
|
||||||
|
class ThreadedProcessFanart(Thread):
|
||||||
"""
|
"""
|
||||||
Threaded download of additional fanart in the background
|
Threaded download of additional fanart in the background
|
||||||
|
|
||||||
|
@ -39,21 +40,10 @@ class Process_Fanart_Thread(Thread):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
|
||||||
Catch all exceptions and log them
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
self.__run()
|
|
||||||
except Exception as e:
|
|
||||||
log.error('Exception %s' % e)
|
|
||||||
import traceback
|
|
||||||
log.error("Traceback:\n%s" % traceback.format_exc())
|
|
||||||
|
|
||||||
def __run(self):
|
|
||||||
"""
|
"""
|
||||||
Do the work
|
Do the work
|
||||||
"""
|
"""
|
||||||
log.debug("---===### Starting FanartSync ###===---")
|
LOG.debug("---===### Starting FanartSync ###===---")
|
||||||
stopped = self.stopped
|
stopped = self.stopped
|
||||||
suspended = self.suspended
|
suspended = self.suspended
|
||||||
queue = self.queue
|
queue = self.queue
|
||||||
|
@ -63,7 +53,7 @@ class Process_Fanart_Thread(Thread):
|
||||||
# Set in service.py
|
# Set in service.py
|
||||||
if stopped():
|
if stopped():
|
||||||
# Abort was requested while waiting. We should exit
|
# Abort was requested while waiting. We should exit
|
||||||
log.info("---===### Stopped FanartSync ###===---")
|
LOG.info("---===### Stopped FanartSync ###===---")
|
||||||
return
|
return
|
||||||
sleep(1000)
|
sleep(1000)
|
||||||
# grabs Plex item from queue
|
# grabs Plex item from queue
|
||||||
|
@ -73,15 +63,14 @@ class Process_Fanart_Thread(Thread):
|
||||||
sleep(200)
|
sleep(200)
|
||||||
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'])
|
||||||
with getattr(itemtypes,
|
with getattr(itemtypes,
|
||||||
v.ITEMTYPE_FROM_PLEXTYPE[item['plex_type']])() as cls:
|
v.ITEMTYPE_FROM_PLEXTYPE[item['plex_type']])() as item_type:
|
||||||
result = cls.getfanart(item['plex_id'],
|
result = item_type.getfanart(item['plex_id'],
|
||||||
refresh=item['refresh'])
|
refresh=item['refresh'])
|
||||||
if result is True:
|
if result is True:
|
||||||
log.debug('Done getting fanart for Plex id %s'
|
LOG.debug('Done getting fanart for Plex id %s', item['plex_id'])
|
||||||
% item['plex_id'])
|
|
||||||
with plexdb.Get_Plex_DB() as plex_db:
|
with plexdb.Get_Plex_DB() as plex_db:
|
||||||
plex_db.set_fanart_synched(item['plex_id'])
|
plex_db.set_fanart_synched(item['plex_id'])
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
log.debug("---===### Stopped FanartSync ###===---")
|
LOG.debug("---===### Stopped FanartSync ###===---")
|
||||||
|
|
|
@ -11,20 +11,22 @@ import sync_info
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
log = getLogger("PLEX."+__name__)
|
LOG = getLogger("PLEX." + __name__)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
@thread_methods(add_stops=['SUSPEND_LIBRARY_THREAD', 'STOP_SYNC'])
|
@thread_methods(add_stops=['SUSPEND_LIBRARY_THREAD',
|
||||||
class Threaded_Get_Metadata(Thread):
|
'STOP_SYNC',
|
||||||
|
'SUSPEND_SYNC'])
|
||||||
|
class ThreadedGetMetadata(Thread):
|
||||||
"""
|
"""
|
||||||
Threaded download of Plex XML metadata for a certain library item.
|
Threaded download of Plex XML metadata for a certain library item.
|
||||||
Fills the out_queue with the downloaded etree XML objects
|
Fills the out_queue with the downloaded etree XML objects
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
queue Queue.Queue() object that you'll need to fill up
|
queue Queue.Queue() object that you'll need to fill up
|
||||||
with Plex itemIds
|
with plex_ids
|
||||||
out_queue Queue() object where this thread will store
|
out_queue Queue() object where this thread will store
|
||||||
the downloaded metadata XMLs as etree objects
|
the downloaded metadata XMLs as etree objects
|
||||||
"""
|
"""
|
||||||
|
@ -60,21 +62,10 @@ class Threaded_Get_Metadata(Thread):
|
||||||
self.out_queue.task_done()
|
self.out_queue.task_done()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
|
||||||
Catch all exceptions and log them
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
self.__run()
|
|
||||||
except Exception as e:
|
|
||||||
log.error('Exception %s' % e)
|
|
||||||
import traceback
|
|
||||||
log.error("Traceback:\n%s" % traceback.format_exc())
|
|
||||||
|
|
||||||
def __run(self):
|
|
||||||
"""
|
"""
|
||||||
Do the work
|
Do the work
|
||||||
"""
|
"""
|
||||||
log.debug('Starting get metadata thread')
|
LOG.debug('Starting get metadata thread')
|
||||||
# cache local variables because it's faster
|
# cache local variables because it's faster
|
||||||
queue = self.queue
|
queue = self.queue
|
||||||
out_queue = self.out_queue
|
out_queue = self.out_queue
|
||||||
|
@ -88,11 +79,11 @@ class Threaded_Get_Metadata(Thread):
|
||||||
sleep(20)
|
sleep(20)
|
||||||
continue
|
continue
|
||||||
# Download Metadata
|
# Download Metadata
|
||||||
xml = GetPlexMetadata(item['itemId'])
|
xml = GetPlexMetadata(item['plex_id'])
|
||||||
if xml is None:
|
if xml is None:
|
||||||
# Did not receive a valid XML - skip that item for now
|
# Did not receive a valid XML - skip that item for now
|
||||||
log.error("Could not get metadata for %s. Skipping that item "
|
LOG.error("Could not get metadata for %s. Skipping that item "
|
||||||
"for now" % item['itemId'])
|
"for now", item['plex_id'])
|
||||||
# Increase BOTH counters - since metadata won't be processed
|
# Increase BOTH counters - since metadata won't be processed
|
||||||
with sync_info.LOCK:
|
with sync_info.LOCK:
|
||||||
sync_info.GET_METADATA_COUNT += 1
|
sync_info.GET_METADATA_COUNT += 1
|
||||||
|
@ -100,21 +91,21 @@ class Threaded_Get_Metadata(Thread):
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
continue
|
continue
|
||||||
elif xml == 401:
|
elif xml == 401:
|
||||||
log.error('HTTP 401 returned by PMS. Too much strain? '
|
LOG.error('HTTP 401 returned by PMS. Too much strain? '
|
||||||
'Cancelling sync for now')
|
'Cancelling sync for now')
|
||||||
window('plex_scancrashed', value='401')
|
window('plex_scancrashed', value='401')
|
||||||
# Kill remaining items in queue (for main thread to cont.)
|
# Kill remaining items in queue (for main thread to cont.)
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
break
|
break
|
||||||
|
|
||||||
item['XML'] = xml
|
item['xml'] = xml
|
||||||
if item.get('get_children') is True:
|
if item.get('get_children') is True:
|
||||||
children_xml = GetAllPlexChildren(item['itemId'])
|
children_xml = GetAllPlexChildren(item['plex_id'])
|
||||||
try:
|
try:
|
||||||
children_xml[0].attrib
|
children_xml[0].attrib
|
||||||
except (TypeError, IndexError, AttributeError):
|
except (TypeError, IndexError, AttributeError):
|
||||||
log.error('Could not get children for Plex id %s'
|
LOG.error('Could not get children for Plex id %s',
|
||||||
% item['itemId'])
|
item['plex_id'])
|
||||||
item['children'] = []
|
item['children'] = []
|
||||||
else:
|
else:
|
||||||
item['children'] = children_xml
|
item['children'] = children_xml
|
||||||
|
@ -128,4 +119,4 @@ class Threaded_Get_Metadata(Thread):
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
# Empty queue in case PKC was shut down (main thread hangs otherwise)
|
# Empty queue in case PKC was shut down (main thread hangs otherwise)
|
||||||
self.terminate_now()
|
self.terminate_now()
|
||||||
log.debug('Get metadata thread terminated')
|
LOG.debug('Get metadata thread terminated')
|
||||||
|
|
|
@ -10,13 +10,15 @@ import itemtypes
|
||||||
import sync_info
|
import sync_info
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
log = getLogger("PLEX."+__name__)
|
LOG = getLogger("PLEX." + __name__)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
@thread_methods(add_stops=['SUSPEND_LIBRARY_THREAD', 'STOP_SYNC'])
|
@thread_methods(add_stops=['SUSPEND_LIBRARY_THREAD',
|
||||||
class Threaded_Process_Metadata(Thread):
|
'STOP_SYNC',
|
||||||
|
'SUSPEND_SYNC'])
|
||||||
|
class ThreadedProcessMetadata(Thread):
|
||||||
"""
|
"""
|
||||||
Not yet implemented for more than 1 thread - if ever. Only to be called by
|
Not yet implemented for more than 1 thread - if ever. Only to be called by
|
||||||
ONE thread!
|
ONE thread!
|
||||||
|
@ -25,12 +27,12 @@ class Threaded_Process_Metadata(Thread):
|
||||||
Input:
|
Input:
|
||||||
queue: Queue.Queue() object that you'll need to fill up with
|
queue: Queue.Queue() object that you'll need to fill up with
|
||||||
the downloaded XML eTree objects
|
the downloaded XML eTree objects
|
||||||
item_type: as used to call functions in itemtypes.py e.g. 'Movies' =>
|
item_class: as used to call functions in itemtypes.py e.g. 'Movies' =>
|
||||||
itemtypes.Movies()
|
itemtypes.Movies()
|
||||||
"""
|
"""
|
||||||
def __init__(self, queue, item_type):
|
def __init__(self, queue, item_class):
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.item_type = item_type
|
self.item_class = item_class
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
|
|
||||||
def terminate_now(self):
|
def terminate_now(self):
|
||||||
|
@ -49,23 +51,12 @@ class Threaded_Process_Metadata(Thread):
|
||||||
self.queue.task_done()
|
self.queue.task_done()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
|
||||||
Catch all exceptions and log them
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
self.__run()
|
|
||||||
except Exception as e:
|
|
||||||
log.error('Exception %s' % e)
|
|
||||||
import traceback
|
|
||||||
log.error("Traceback:\n%s" % traceback.format_exc())
|
|
||||||
|
|
||||||
def __run(self):
|
|
||||||
"""
|
"""
|
||||||
Do the work
|
Do the work
|
||||||
"""
|
"""
|
||||||
log.debug('Processing thread started')
|
LOG.debug('Processing thread started')
|
||||||
# Constructs the method name, e.g. itemtypes.Movies
|
# Constructs the method name, e.g. itemtypes.Movies
|
||||||
item_fct = getattr(itemtypes, self.item_type)
|
item_fct = getattr(itemtypes, self.item_class)
|
||||||
# cache local variables because it's faster
|
# cache local variables because it's faster
|
||||||
queue = self.queue
|
queue = self.queue
|
||||||
stopped = self.stopped
|
stopped = self.stopped
|
||||||
|
@ -79,24 +70,19 @@ class Threaded_Process_Metadata(Thread):
|
||||||
continue
|
continue
|
||||||
# Do the work
|
# Do the work
|
||||||
item_method = getattr(item_class, item['method'])
|
item_method = getattr(item_class, item['method'])
|
||||||
if item.get('children') is not None:
|
if item.get('children'):
|
||||||
item_method(item['XML'][0],
|
item_method(item['xml'][0],
|
||||||
viewtag=item['viewName'],
|
viewtag=item['view_name'],
|
||||||
viewid=item['viewId'],
|
viewid=item['view_id'],
|
||||||
children=item['children'])
|
children=item['children'])
|
||||||
else:
|
else:
|
||||||
item_method(item['XML'][0],
|
item_method(item['xml'][0],
|
||||||
viewtag=item['viewName'],
|
viewtag=item['view_name'],
|
||||||
viewid=item['viewId'])
|
viewid=item['view_id'])
|
||||||
# Keep track of where we are at
|
# Keep track of where we are at
|
||||||
try:
|
|
||||||
log.debug('found child: %s'
|
|
||||||
% item['children'].attrib)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
with sync_info.LOCK:
|
with sync_info.LOCK:
|
||||||
sync_info.PROCESS_METADATA_COUNT += 1
|
sync_info.PROCESS_METADATA_COUNT += 1
|
||||||
sync_info.PROCESSING_VIEW_NAME = item['title']
|
sync_info.PROCESSING_VIEW_NAME = item['title']
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
self.terminate_now()
|
self.terminate_now()
|
||||||
log.debug('Processing thread terminated')
|
LOG.debug('Processing thread terminated')
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from threading import Thread, Lock
|
from threading import Thread, Lock
|
||||||
|
|
||||||
from xbmc import sleep, Player
|
from xbmc import sleep
|
||||||
from xbmcgui import DialogProgressBG
|
from xbmcgui import DialogProgressBG
|
||||||
|
|
||||||
from utils import thread_methods, language as lang
|
from utils import thread_methods, language as lang
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
log = getLogger("PLEX."+__name__)
|
LOG = getLogger("PLEX." + __name__)
|
||||||
|
|
||||||
GET_METADATA_COUNT = 0
|
GET_METADATA_COUNT = 0
|
||||||
PROCESS_METADATA_COUNT = 0
|
PROCESS_METADATA_COUNT = 0
|
||||||
|
@ -19,8 +19,10 @@ LOCK = Lock()
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
@thread_methods(add_stops=['SUSPEND_LIBRARY_THREAD', 'STOP_SYNC'])
|
@thread_methods(add_stops=['SUSPEND_LIBRARY_THREAD',
|
||||||
class Threaded_Show_Sync_Info(Thread):
|
'STOP_SYNC',
|
||||||
|
'SUSPEND_SYNC'])
|
||||||
|
class ThreadedShowSyncInfo(Thread):
|
||||||
"""
|
"""
|
||||||
Threaded class to show the Kodi statusbar of the metadata download.
|
Threaded class to show the Kodi statusbar of the metadata download.
|
||||||
|
|
||||||
|
@ -34,38 +36,26 @@ class Threaded_Show_Sync_Info(Thread):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
|
||||||
Catch all exceptions and log them
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
self.__run()
|
|
||||||
except Exception as e:
|
|
||||||
log.error('Exception %s' % e)
|
|
||||||
import traceback
|
|
||||||
log.error("Traceback:\n%s" % traceback.format_exc())
|
|
||||||
|
|
||||||
def __run(self):
|
|
||||||
"""
|
"""
|
||||||
Do the work
|
Do the work
|
||||||
"""
|
"""
|
||||||
log.debug('Show sync info thread started')
|
LOG.debug('Show sync info thread started')
|
||||||
# cache local variables because it's faster
|
# cache local variables because it's faster
|
||||||
total = self.total
|
total = self.total
|
||||||
dialog = DialogProgressBG('dialoglogProgressBG')
|
dialog = DialogProgressBG('dialoglogProgressBG')
|
||||||
dialog.create("%s %s: %s %s"
|
dialog.create("%s %s: %s %s"
|
||||||
% (lang(39714), self.item_type, str(total), lang(39715)))
|
% (lang(39714), self.item_type, str(total), lang(39715)))
|
||||||
player = Player()
|
|
||||||
|
|
||||||
total = 2 * total
|
total = 2 * total
|
||||||
totalProgress = 0
|
total_progress = 0
|
||||||
while self.stopped() is False and not player.isPlaying():
|
while not self.stopped():
|
||||||
with LOCK:
|
with LOCK:
|
||||||
get_progress = GET_METADATA_COUNT
|
get_progress = GET_METADATA_COUNT
|
||||||
process_progress = PROCESS_METADATA_COUNT
|
process_progress = PROCESS_METADATA_COUNT
|
||||||
viewName = PROCESSING_VIEW_NAME
|
view_name = PROCESSING_VIEW_NAME
|
||||||
totalProgress = get_progress + process_progress
|
total_progress = get_progress + process_progress
|
||||||
try:
|
try:
|
||||||
percentage = int(float(totalProgress) / float(total)*100.0)
|
percentage = int(float(total_progress) / float(total)*100.0)
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
percentage = 0
|
percentage = 0
|
||||||
dialog.update(percentage,
|
dialog.update(percentage,
|
||||||
|
@ -74,8 +64,8 @@ class Threaded_Show_Sync_Info(Thread):
|
||||||
lang(39712),
|
lang(39712),
|
||||||
process_progress,
|
process_progress,
|
||||||
lang(39713),
|
lang(39713),
|
||||||
viewName))
|
view_name))
|
||||||
# Sleep for x milliseconds
|
# Sleep for x milliseconds
|
||||||
sleep(200)
|
sleep(200)
|
||||||
dialog.close()
|
dialog.close()
|
||||||
log.debug('Show sync info thread terminated')
|
LOG.debug('Show sync info thread terminated')
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -124,8 +124,12 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
|
||||||
LOG.debug('Playing trailers: %s', trailers)
|
LOG.debug('Playing trailers: %s', trailers)
|
||||||
if RESOLVE:
|
if RESOLVE:
|
||||||
# Sleep a bit to let setResolvedUrl do its thing - bit ugly
|
# Sleep a bit to let setResolvedUrl do its thing - bit ugly
|
||||||
|
sleep_timer = 0
|
||||||
while not state.PKC_CAUSED_STOP_DONE:
|
while not state.PKC_CAUSED_STOP_DONE:
|
||||||
sleep(50)
|
sleep(50)
|
||||||
|
sleep_timer += 1
|
||||||
|
if sleep_timer > 100:
|
||||||
|
break
|
||||||
playqueue.clear()
|
playqueue.clear()
|
||||||
if plex_type != v.PLEX_TYPE_CLIP:
|
if plex_type != v.PLEX_TYPE_CLIP:
|
||||||
# Post to the PMS to create a playqueue - in any case due to Companion
|
# Post to the PMS to create a playqueue - in any case due to Companion
|
||||||
|
|
|
@ -18,46 +18,50 @@ LOG = getLogger("PLEX." + __name__)
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
class Playback_Starter(Thread):
|
class PlaybackStarter(Thread):
|
||||||
"""
|
"""
|
||||||
Processes new plays
|
Processes new plays
|
||||||
"""
|
"""
|
||||||
def triage(self, item):
|
@staticmethod
|
||||||
try:
|
def _triage(item):
|
||||||
_, params = item.split('?', 1)
|
_, params = item.split('?', 1)
|
||||||
except ValueError:
|
params = dict(parse_qsl(params))
|
||||||
|
mode = params.get('mode')
|
||||||
|
resolve = False if params.get('handle') == '-1' else True
|
||||||
|
LOG.debug('Received mode: %s, params: %s', mode, params)
|
||||||
|
if mode == 'play':
|
||||||
|
playback.playback_triage(plex_id=params.get('plex_id'),
|
||||||
|
plex_type=params.get('plex_type'),
|
||||||
|
path=params.get('path'),
|
||||||
|
resolve=resolve)
|
||||||
|
elif mode == 'plex_node':
|
||||||
|
playback.process_indirect(params['key'],
|
||||||
|
params['offset'],
|
||||||
|
resolve=resolve)
|
||||||
|
elif mode == 'navigation':
|
||||||
# e.g. when plugin://...tvshows is called for entire season
|
# e.g. when plugin://...tvshows is called for entire season
|
||||||
with kodidb.GetKodiDB('video') as kodi_db:
|
with kodidb.GetKodiDB('video') as kodi_db:
|
||||||
show_id = kodi_db.show_id_from_path(item)
|
show_id = kodi_db.show_id_from_path(params.get('path'))
|
||||||
if show_id:
|
if show_id:
|
||||||
js.activate_window('videos',
|
js.activate_window('videos',
|
||||||
'videodb://tvshows/titles/%s' % show_id)
|
'videodb://tvshows/titles/%s' % show_id)
|
||||||
else:
|
else:
|
||||||
LOG.error('Could not find tv show id for %s', item)
|
LOG.error('Could not find tv show id for %s', item)
|
||||||
|
if resolve:
|
||||||
pickle_me(Playback_Successful())
|
pickle_me(Playback_Successful())
|
||||||
return
|
|
||||||
params = dict(parse_qsl(params))
|
|
||||||
mode = params.get('mode')
|
|
||||||
LOG.debug('Received mode: %s, params: %s', mode, params)
|
|
||||||
if mode == 'play':
|
|
||||||
playback.playback_triage(plex_id=params.get('plex_id'),
|
|
||||||
plex_type=params.get('plex_type'),
|
|
||||||
path=params.get('path'))
|
|
||||||
elif mode == 'plex_node':
|
|
||||||
playback.process_indirect(params['key'], params['offset'])
|
|
||||||
elif mode == 'context_menu':
|
elif mode == 'context_menu':
|
||||||
ContextMenu(kodi_id=params['kodi_id'],
|
ContextMenu(kodi_id=params.get('kodi_id'),
|
||||||
kodi_type=params['kodi_type'])
|
kodi_type=params.get('kodi_type'))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
queue = state.COMMAND_PIPELINE_QUEUE
|
queue = state.COMMAND_PIPELINE_QUEUE
|
||||||
LOG.info("----===## Starting Playback_Starter ##===----")
|
LOG.info("----===## Starting PlaybackStarter ##===----")
|
||||||
while True:
|
while True:
|
||||||
item = queue.get()
|
item = queue.get()
|
||||||
if item is None:
|
if item is None:
|
||||||
# Need to shutdown - initiated by command_pipeline
|
# Need to shutdown - initiated by command_pipeline
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self.triage(item)
|
self._triage(item)
|
||||||
queue.task_done()
|
queue.task_done()
|
||||||
LOG.info("----===## Playback_Starter stopped ##===----")
|
LOG.info("----===## PlaybackStarter stopped ##===----")
|
||||||
|
|
|
@ -237,6 +237,7 @@ class Plex_DB_Functions():
|
||||||
SELECT plex_id, parent_id, plex_type
|
SELECT plex_id, parent_id, plex_type
|
||||||
FROM plex
|
FROM plex
|
||||||
WHERE kodi_id = ? AND kodi_type = ?
|
WHERE kodi_id = ? AND kodi_type = ?
|
||||||
|
LIMIT 1
|
||||||
'''
|
'''
|
||||||
self.plexcursor.execute(query, (kodi_id, kodi_type,))
|
self.plexcursor.execute(query, (kodi_id, kodi_type,))
|
||||||
return self.plexcursor.fetchone()
|
return self.plexcursor.fetchone()
|
||||||
|
|
|
@ -10,6 +10,9 @@ STOP_PKC = False
|
||||||
SUSPEND_LIBRARY_THREAD = False
|
SUSPEND_LIBRARY_THREAD = False
|
||||||
# Set if user decided to cancel sync
|
# Set if user decided to cancel sync
|
||||||
STOP_SYNC = False
|
STOP_SYNC = False
|
||||||
|
# Set e.g. during media playback if PKC should not do any syncs. Will NOT
|
||||||
|
# suspend synching of playstate progress
|
||||||
|
SUSPEND_SYNC = False
|
||||||
# Could we access the paths?
|
# Could we access the paths?
|
||||||
PATH_VERIFIED = False
|
PATH_VERIFIED = False
|
||||||
# Set if a Plex-Kodi DB sync is being done - along with
|
# Set if a Plex-Kodi DB sync is being done - along with
|
||||||
|
@ -36,8 +39,6 @@ FORCE_RELOAD_SKIN = True
|
||||||
# Stemming from the PKC settings.xml
|
# Stemming from the PKC settings.xml
|
||||||
# Shall we show Kodi dialogs when synching?
|
# Shall we show Kodi dialogs when synching?
|
||||||
SYNC_DIALOG = True
|
SYNC_DIALOG = True
|
||||||
# Have we already checked the Kodi DB on consistency?
|
|
||||||
KODI_DB_CHECKED = False
|
|
||||||
# Is synching of Plex music enabled?
|
# Is synching of Plex music enabled?
|
||||||
ENABLE_MUSIC = True
|
ENABLE_MUSIC = True
|
||||||
# How often shall we sync?
|
# How often shall we sync?
|
||||||
|
|
|
@ -1050,10 +1050,10 @@ def thread_methods(cls=None, add_stops=None, add_suspends=None):
|
||||||
suspends
|
suspends
|
||||||
|
|
||||||
invoke with either
|
invoke with either
|
||||||
@Newthread_methods
|
@thread_methods
|
||||||
class MyClass():
|
class MyClass():
|
||||||
or
|
or
|
||||||
@Newthread_methods(add_stops=['SUSPEND_LIBRARY_TRHEAD'],
|
@thread_methods(add_stops=['SUSPEND_LIBRARY_TRHEAD'],
|
||||||
add_suspends=['DB_SCAN', 'WHATEVER'])
|
add_suspends=['DB_SCAN', 'WHATEVER'])
|
||||||
class MyClass():
|
class MyClass():
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -38,7 +38,7 @@ from websocket_client import PMS_Websocket, Alexa_Websocket
|
||||||
from PlexFunctions import check_connection
|
from PlexFunctions import check_connection
|
||||||
from PlexCompanion import PlexCompanion
|
from PlexCompanion import PlexCompanion
|
||||||
from command_pipeline import Monitor_Window
|
from command_pipeline import Monitor_Window
|
||||||
from playback_starter import Playback_Starter
|
from playback_starter import PlaybackStarter
|
||||||
from playqueue import PlayqueueMonitor
|
from playqueue import PlayqueueMonitor
|
||||||
from artwork import Image_Cache_Thread
|
from artwork import Image_Cache_Thread
|
||||||
import variables as v
|
import variables as v
|
||||||
|
@ -111,7 +111,7 @@ class Service():
|
||||||
self.library = LibrarySync()
|
self.library = LibrarySync()
|
||||||
self.plexCompanion = PlexCompanion()
|
self.plexCompanion = PlexCompanion()
|
||||||
self.specialMonitor = SpecialMonitor()
|
self.specialMonitor = SpecialMonitor()
|
||||||
self.playback_starter = Playback_Starter()
|
self.playback_starter = PlaybackStarter()
|
||||||
self.playqueue = PlayqueueMonitor()
|
self.playqueue = PlayqueueMonitor()
|
||||||
if settings('enableTextureCache') == "true":
|
if settings('enableTextureCache') == "true":
|
||||||
self.image_cache_thread = Image_Cache_Thread()
|
self.image_cache_thread = Image_Cache_Thread()
|
||||||
|
|
Loading…
Reference in a new issue