commit
e5585aec44
11 changed files with 79 additions and 67 deletions
14
addon.xml
14
addon.xml
|
@ -1,5 +1,5 @@
|
||||||
<?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.12.10" provider-name="croneter">
|
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.12.12" 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" />
|
||||||
|
@ -83,7 +83,17 @@
|
||||||
<summary lang="lt_LT">Natūralioji „Plex“ integracija į „Kodi“</summary>
|
<summary lang="lt_LT">Natūralioji „Plex“ integracija į „Kodi“</summary>
|
||||||
<description lang="lt_LT">Prijunkite „Kodi“ prie „Plex Medija Serverio“. Šiame papildinyje daroma prielaida, kad valdote visus savo vaizdo įrašus naudodami „Plex“ (ir nė vieno su „Kodi“). Galite prarasti jau saugomus „Kodi“ vaizdo įrašų ir muzikos duomenų bazių duomenis (kadangi šis papildinys juos tiesiogiai pakeičia). Naudokite savo pačių rizika!</description>
|
<description lang="lt_LT">Prijunkite „Kodi“ prie „Plex Medija Serverio“. Šiame papildinyje daroma prielaida, kad valdote visus savo vaizdo įrašus naudodami „Plex“ (ir nė vieno su „Kodi“). Galite prarasti jau saugomus „Kodi“ vaizdo įrašų ir muzikos duomenų bazių duomenis (kadangi šis papildinys juos tiesiogiai pakeičia). Naudokite savo pačių rizika!</description>
|
||||||
<disclaimer lang="lt_LT">Naudokite savo pačių rizika</disclaimer>
|
<disclaimer lang="lt_LT">Naudokite savo pačių rizika</disclaimer>
|
||||||
<news>version 2.12.10:
|
<news>version 2.12.12:
|
||||||
|
- Hopefully fix rare case when sync would get stuck indefinitely
|
||||||
|
- Fix ValueError: invalid literal for int() for invalid dates sent by Plex
|
||||||
|
- version 2.12.11 for everyone
|
||||||
|
|
||||||
|
version 2.12.11 (beta only):
|
||||||
|
- Fix PKC not auto-picking audio/subtitle stream when transcoding
|
||||||
|
- Fix ValueError when deleting a music album
|
||||||
|
- Fix OSError: Invalid argument when Plex returns an invalid timestamp
|
||||||
|
|
||||||
|
version 2.12.10:
|
||||||
- Fix pictures from Plex picture libraries not working/displaying
|
- Fix pictures from Plex picture libraries not working/displaying
|
||||||
|
|
||||||
version 2.12.9:
|
version 2.12.9:
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
version 2.12.12:
|
||||||
|
- Hopefully fix rare case when sync would get stuck indefinitely
|
||||||
|
- Fix ValueError: invalid literal for int() for invalid dates sent by Plex
|
||||||
|
- version 2.12.11 for everyone
|
||||||
|
|
||||||
|
version 2.12.11 (beta only):
|
||||||
|
- Fix PKC not auto-picking audio/subtitle stream when transcoding
|
||||||
|
- Fix ValueError when deleting a music album
|
||||||
|
- Fix OSError: Invalid argument when Plex returns an invalid timestamp
|
||||||
|
|
||||||
version 2.12.10:
|
version 2.12.10:
|
||||||
- Fix pictures from Plex picture libraries not working/displaying
|
- Fix pictures from Plex picture libraries not working/displaying
|
||||||
|
|
||||||
|
|
|
@ -630,7 +630,7 @@ msgstr ""
|
||||||
|
|
||||||
# PKC Settings - Playback
|
# PKC Settings - Playback
|
||||||
msgctxt "#30541"
|
msgctxt "#30541"
|
||||||
msgid "Don't ask to pick a certain stream/quality"
|
msgid "Transcoding: Auto-pick audio and subtitle stream using Plex defaults"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
# PKC Settings - Playback
|
# PKC Settings - Playback
|
||||||
|
|
|
@ -136,6 +136,11 @@ class ProcessingQueue(Queue.Queue, object):
|
||||||
return self._current_queue._qsize() if self._current_queue else 0
|
return self._current_queue._qsize() if self._current_queue else 0
|
||||||
|
|
||||||
def _total_qsize(self):
|
def _total_qsize(self):
|
||||||
|
"""
|
||||||
|
This method is BROKEN as it can lead to a deadlock when a single item
|
||||||
|
from the current section takes longer to download then any new items
|
||||||
|
coming in
|
||||||
|
"""
|
||||||
return sum(q._qsize() for q in self._queues) if self._queues else 0
|
return sum(q._qsize() for q in self._queues) if self._queues else 0
|
||||||
|
|
||||||
def put(self, item, block=True, timeout=None):
|
def put(self, item, block=True, timeout=None):
|
||||||
|
@ -147,16 +152,16 @@ class ProcessingQueue(Queue.Queue, object):
|
||||||
try:
|
try:
|
||||||
if self.maxsize > 0:
|
if self.maxsize > 0:
|
||||||
if not block:
|
if not block:
|
||||||
if self._total_qsize() == self.maxsize:
|
if self._qsize() == self.maxsize:
|
||||||
raise Queue.Full
|
raise Queue.Full
|
||||||
elif timeout is None:
|
elif timeout is None:
|
||||||
while self._total_qsize() == self.maxsize:
|
while self._qsize() == self.maxsize:
|
||||||
self.not_full.wait()
|
self.not_full.wait()
|
||||||
elif timeout < 0:
|
elif timeout < 0:
|
||||||
raise ValueError("'timeout' must be a non-negative number")
|
raise ValueError("'timeout' must be a non-negative number")
|
||||||
else:
|
else:
|
||||||
endtime = _time() + timeout
|
endtime = _time() + timeout
|
||||||
while self._total_qsize() == self.maxsize:
|
while self._qsize() == self.maxsize:
|
||||||
remaining = endtime - _time()
|
remaining = endtime - _time()
|
||||||
if remaining <= 0.0:
|
if remaining <= 0.0:
|
||||||
raise Queue.Full
|
raise Queue.Full
|
||||||
|
|
|
@ -135,7 +135,6 @@ class MusicMixin(object):
|
||||||
'''
|
'''
|
||||||
Remove an album
|
Remove an album
|
||||||
'''
|
'''
|
||||||
self.kodidb.delete_album_from_discography(kodi_id)
|
|
||||||
if v.KODIVERSION < 18:
|
if v.KODIVERSION < 18:
|
||||||
self.kodidb.delete_album_from_album_genre(kodi_id)
|
self.kodidb.delete_album_from_album_genre(kodi_id)
|
||||||
self.kodidb.remove_album(kodi_id)
|
self.kodidb.remove_album(kodi_id)
|
||||||
|
@ -353,11 +352,6 @@ class Album(MusicMixin, ItemBase):
|
||||||
timing.unix_date_to_kodi(self.last_sync),
|
timing.unix_date_to_kodi(self.last_sync),
|
||||||
'album')
|
'album')
|
||||||
self.kodidb.add_albumartist(artist_id, kodi_id, api.artist_name())
|
self.kodidb.add_albumartist(artist_id, kodi_id, api.artist_name())
|
||||||
if v.KODIVERSION < 18:
|
|
||||||
self.kodidb.add_discography(artist_id, name, api.year())
|
|
||||||
self.kodidb.add_music_genres(kodi_id,
|
|
||||||
api.genres(),
|
|
||||||
v.KODI_TYPE_ALBUM)
|
|
||||||
if app.SYNC.artwork:
|
if app.SYNC.artwork:
|
||||||
self.kodidb.modify_artwork(artworks,
|
self.kodidb.modify_artwork(artworks,
|
||||||
kodi_id,
|
kodi_id,
|
||||||
|
|
|
@ -106,26 +106,6 @@ class KodiMusicDB(common.KodiDBBase):
|
||||||
self.cursor.execute('DELETE FROM song_artist WHERE idSong = ?',
|
self.cursor.execute('DELETE FROM song_artist WHERE idSong = ?',
|
||||||
(song_id, ))
|
(song_id, ))
|
||||||
|
|
||||||
@db.catch_operationalerrors
|
|
||||||
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!
|
|
||||||
self.cursor.execute('SELECT strAlbum, iYear FROM album WHERE idAlbum = ? LIMIT 1',
|
|
||||||
(album_id, ))
|
|
||||||
try:
|
|
||||||
name, year = self.cursor.fetchone()
|
|
||||||
except TypeError:
|
|
||||||
return
|
|
||||||
self.cursor.execute('SELECT idArtist FROM album_artist WHERE idAlbum = ? LIMIT 1',
|
|
||||||
(album_id, ))
|
|
||||||
artist = self.cursor.fetchone()
|
|
||||||
if not artist:
|
|
||||||
return
|
|
||||||
self.cursor.execute('DELETE FROM discography WHERE idArtist = ? AND strAlbum = ? AND strYear = ?',
|
|
||||||
(artist[0], name, year))
|
|
||||||
|
|
||||||
@db.catch_operationalerrors
|
@db.catch_operationalerrors
|
||||||
def delete_song_from_song_genre(self, song_id):
|
def delete_song_from_song_genre(self, song_id):
|
||||||
"""
|
"""
|
||||||
|
@ -352,16 +332,6 @@ class KodiMusicDB(common.KodiDBBase):
|
||||||
VALUES (?, ?, ?)
|
VALUES (?, ?, ?)
|
||||||
''', (artist_id, kodi_id, artistname))
|
''', (artist_id, kodi_id, artistname))
|
||||||
|
|
||||||
@db.catch_operationalerrors
|
|
||||||
def add_discography(self, artist_id, albumname, year):
|
|
||||||
self.cursor.execute('''
|
|
||||||
INSERT OR REPLACE INTO discography(
|
|
||||||
idArtist,
|
|
||||||
strAlbum,
|
|
||||||
strYear)
|
|
||||||
VALUES (?, ?, ?)
|
|
||||||
''', (artist_id, albumname, year))
|
|
||||||
|
|
||||||
@db.catch_operationalerrors
|
@db.catch_operationalerrors
|
||||||
def add_music_genres(self, kodiid, genres, mediatype):
|
def add_music_genres(self, kodiid, genres, mediatype):
|
||||||
"""
|
"""
|
||||||
|
@ -656,5 +626,3 @@ class KodiMusicDB(common.KodiDBBase):
|
||||||
(kodi_id, ))
|
(kodi_id, ))
|
||||||
self.cursor.execute('DELETE FROM song_artist WHERE idArtist = ?',
|
self.cursor.execute('DELETE FROM song_artist WHERE idArtist = ?',
|
||||||
(kodi_id, ))
|
(kodi_id, ))
|
||||||
self.cursor.execute('DELETE FROM discography WHERE idArtist = ?',
|
|
||||||
(kodi_id, ))
|
|
||||||
|
|
|
@ -41,8 +41,8 @@ class FillMetadataQueue(common.LibrarySyncMixin,
|
||||||
plex_id = int(xml.get('ratingKey'))
|
plex_id = int(xml.get('ratingKey'))
|
||||||
checksum = int('{}{}'.format(
|
checksum = int('{}{}'.format(
|
||||||
plex_id,
|
plex_id,
|
||||||
xml.get('updatedAt',
|
abs(int(xml.get('updatedAt',
|
||||||
xml.get('addedAt', '1541572987')).replace('-', '')))
|
xml.get('addedAt', '1541572987'))))))
|
||||||
if (not self.repair and
|
if (not self.repair and
|
||||||
plexdb.checksum(plex_id, section.plex_type) == checksum):
|
plexdb.checksum(plex_id, section.plex_type) == checksum):
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -349,8 +349,14 @@ def audio_subtitle_prefs(api, item):
|
||||||
action_type='PUT',
|
action_type='PUT',
|
||||||
parameters=args)
|
parameters=args)
|
||||||
return True
|
return True
|
||||||
|
return setup_transcoding_audio_subtitle_prefs(mediastreams, part_id)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_transcoding_audio_subtitle_prefs(mediastreams, part_id):
|
||||||
audio_streams_list = []
|
audio_streams_list = []
|
||||||
audio_streams = []
|
audio_streams = []
|
||||||
|
audio_default = None
|
||||||
|
subtitle_default = None
|
||||||
subtitle_streams_list = []
|
subtitle_streams_list = []
|
||||||
# "Don't burn-in any subtitle"
|
# "Don't burn-in any subtitle"
|
||||||
subtitle_streams = ['1 %s' % utils.lang(39706)]
|
subtitle_streams = ['1 %s' % utils.lang(39706)]
|
||||||
|
@ -379,6 +385,8 @@ def audio_subtitle_prefs(api, item):
|
||||||
utils.lang(39707), # unknown
|
utils.lang(39707), # unknown
|
||||||
codec,
|
codec,
|
||||||
channellayout)
|
channellayout)
|
||||||
|
if stream.get('default'):
|
||||||
|
audio_default = audio_numb
|
||||||
audio_streams_list.append(index)
|
audio_streams_list.append(index)
|
||||||
audio_streams.append(track.encode('utf-8'))
|
audio_streams.append(track.encode('utf-8'))
|
||||||
audio_numb += 1
|
audio_numb += 1
|
||||||
|
@ -391,7 +399,6 @@ def audio_subtitle_prefs(api, item):
|
||||||
continue
|
continue
|
||||||
# Subtitle is available within the video file
|
# Subtitle is available within the video file
|
||||||
# Burn in the subtitle, if user chooses to do so
|
# Burn in the subtitle, if user chooses to do so
|
||||||
default = stream.get('default')
|
|
||||||
forced = stream.get('forced')
|
forced = stream.get('forced')
|
||||||
try:
|
try:
|
||||||
track = '{} {}'.format(sub_num + 1,
|
track = '{} {}'.format(sub_num + 1,
|
||||||
|
@ -400,7 +407,8 @@ def audio_subtitle_prefs(api, item):
|
||||||
track = '{} {} ({})'.format(sub_num + 1,
|
track = '{} {} ({})'.format(sub_num + 1,
|
||||||
utils.lang(39707), # unknown
|
utils.lang(39707), # unknown
|
||||||
stream.get('codec'))
|
stream.get('codec'))
|
||||||
if default:
|
if stream.get('default'):
|
||||||
|
subtitle_default = sub_num
|
||||||
track = "%s - %s" % (track, utils.lang(39708)) # Default
|
track = "%s - %s" % (track, utils.lang(39708)) # Default
|
||||||
if forced:
|
if forced:
|
||||||
track = "%s - %s" % (track, utils.lang(39709)) # Forced
|
track = "%s - %s" % (track, utils.lang(39709)) # Forced
|
||||||
|
@ -410,10 +418,14 @@ def audio_subtitle_prefs(api, item):
|
||||||
sub_num += 1
|
sub_num += 1
|
||||||
|
|
||||||
if audio_numb > 1:
|
if audio_numb > 1:
|
||||||
resp = utils.dialog('select', utils.lang(33013), audio_streams)
|
# "Transcoding: Auto-pick audio and subtitle stream using Plex defaults"
|
||||||
if resp == -1:
|
if utils.settings('bestQuality') == 'true' and audio_default is not None:
|
||||||
LOG.info('User aborted dialog to select audio stream')
|
resp = audio_default
|
||||||
return
|
else:
|
||||||
|
resp = utils.dialog('select', utils.lang(33013), audio_streams)
|
||||||
|
if resp == -1:
|
||||||
|
LOG.info('User aborted dialog to select audio stream')
|
||||||
|
return
|
||||||
args = {
|
args = {
|
||||||
'audioStreamID': audio_streams_list[resp],
|
'audioStreamID': audio_streams_list[resp],
|
||||||
'allParts': 1
|
'allParts': 1
|
||||||
|
@ -428,18 +440,22 @@ def audio_subtitle_prefs(api, item):
|
||||||
# Otherwise, the PMS might pick-up the last one
|
# Otherwise, the PMS might pick-up the last one
|
||||||
LOG.info('No subtitles to burn-in')
|
LOG.info('No subtitles to burn-in')
|
||||||
else:
|
else:
|
||||||
resp = utils.dialog('select', utils.lang(33014), subtitle_streams)
|
# "Transcoding: Auto-pick audio and subtitle stream using Plex defaults"
|
||||||
if resp == -1:
|
if utils.settings('bestQuality') == 'true' and subtitle_default is not None:
|
||||||
LOG.info('User aborted dialog to select subtitle stream')
|
resp = subtitle_default
|
||||||
return
|
|
||||||
elif resp == 0:
|
|
||||||
# User did not select a subtitle or backed out of the dialog
|
|
||||||
LOG.info('User chose to not burn-in any subtitles')
|
|
||||||
else:
|
else:
|
||||||
LOG.info('User chose to burn-in subtitle %s: %s',
|
resp = utils.dialog('select', utils.lang(33014), subtitle_streams)
|
||||||
select_subs_index,
|
if resp == -1:
|
||||||
subtitle_streams[resp].decode('utf-8'))
|
LOG.info('User aborted dialog to select subtitle stream')
|
||||||
select_subs_index = subtitle_streams_list[resp - 1]
|
return
|
||||||
|
elif resp == 0:
|
||||||
|
# User did not select a subtitle or backed out of the dialog
|
||||||
|
LOG.info('User chose to not burn-in any subtitles')
|
||||||
|
else:
|
||||||
|
LOG.info('User chose to burn-in subtitle %s: %s',
|
||||||
|
select_subs_index,
|
||||||
|
subtitle_streams[resp].decode('utf-8'))
|
||||||
|
select_subs_index = subtitle_streams_list[resp - 1]
|
||||||
# Now prep the PMS for our choice
|
# Now prep the PMS for our choice
|
||||||
args = {
|
args = {
|
||||||
'subtitleStreamID': select_subs_index,
|
'subtitleStreamID': select_subs_index,
|
||||||
|
|
|
@ -231,8 +231,8 @@ class Base(object):
|
||||||
addedAt is used.
|
addedAt is used.
|
||||||
"""
|
"""
|
||||||
return int('%s%s' % (self.xml.get('ratingKey'),
|
return int('%s%s' % (self.xml.get('ratingKey'),
|
||||||
self.xml.get('updatedAt') or
|
abs(int(self.xml.get('updatedAt') or
|
||||||
self.xml.get('addedAt', '1541572987')))
|
self.xml.get('addedAt', '1541572987')))))
|
||||||
|
|
||||||
def title(self):
|
def title(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- 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 datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from time import localtime, strftime
|
from time import localtime, strftime
|
||||||
|
|
||||||
|
LOG = getLogger('PLEX.timing')
|
||||||
|
|
||||||
EPOCH = datetime.utcfromtimestamp(0)
|
EPOCH = datetime.utcfromtimestamp(0)
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +32,13 @@ def unix_date_to_kodi(unix_kodi_time):
|
||||||
|
|
||||||
Output: Y-m-d h:m:s = 2009-04-05 23:16:04
|
Output: Y-m-d h:m:s = 2009-04-05 23:16:04
|
||||||
"""
|
"""
|
||||||
return strftime('%Y-%m-%d %H:%M:%S', localtime(float(unix_kodi_time)))
|
try:
|
||||||
|
return strftime('%Y-%m-%d %H:%M:%S', localtime(float(unix_kodi_time)))
|
||||||
|
except Exception:
|
||||||
|
LOG.exception('Received an illegal timestamp from Plex: %s. '
|
||||||
|
'Using 1970-01-01 12:00:00',
|
||||||
|
unix_kodi_time)
|
||||||
|
return '1970-01-01 12:00:00'
|
||||||
|
|
||||||
|
|
||||||
def plex_date_to_kodi(plex_timestamp):
|
def plex_date_to_kodi(plex_timestamp):
|
||||||
|
|
|
@ -120,7 +120,7 @@
|
||||||
<setting id="audioBoost" type="slider" label="39001" default="0" range="0,10,100" option="int"/>
|
<setting id="audioBoost" type="slider" label="39001" default="0" range="0,10,100" option="int"/>
|
||||||
<setting id="subtitleSize" label="39002" type="slider" option="int" range="0,30,300" default="100" />
|
<setting id="subtitleSize" label="39002" type="slider" option="int" range="0,30,300" default="100" />
|
||||||
<setting id="force_transcode_pix" type="bool" label="30545" default="false" />
|
<setting id="force_transcode_pix" type="bool" label="30545" default="false" />
|
||||||
<setting id="bestQuality" type="bool" label="30541" default="false" />
|
<setting id="bestQuality" type="bool" label="30541" default="false" /><!-- Transcoding: Auto-pick audio and subtitle stream using Plex defaults -->
|
||||||
<setting id="bestTrailer" type="bool" label="30542" default="true" />
|
<setting id="bestTrailer" type="bool" label="30542" default="true" />
|
||||||
<setting type="sep" />
|
<setting type="sep" />
|
||||||
<setting id="offerDelete" type="bool" label="30114" default="false" visible="false"/>
|
<setting id="offerDelete" type="bool" label="30114" default="false" visible="false"/>
|
||||||
|
|
Loading…
Reference in a new issue