Skip to content

Commit

Permalink
[script.plexmod] 0.7.5
Browse files Browse the repository at this point in the history
  • Loading branch information
pannal committed Feb 14, 2024
1 parent ab7f9e1 commit 4ea8421
Show file tree
Hide file tree
Showing 40 changed files with 2,019 additions and 952 deletions.
12 changes: 10 additions & 2 deletions script.plexmod/addon.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="script.plexmod"
name="PM4K for Plex"
version="0.7.4"
version="0.7.5"
provider-name="pannal">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
Expand All @@ -18,14 +18,22 @@
<extension point="xbmc.service" library="service.py" />
<extension point="xbmc.addon.metadata">
<summary lang="en_GB">PlexMod for Kodi</summary>
<summary lang="es_ES">PlexMod para Kodi</summary>
<summary lang="de_DE">PlexMod für Kodi</summary>
<description lang="en_GB">Unofficial Plex for Kodi add-on</description>
<description lang="es_ES">Complemento no oficial de Plex para Kodi</description>
<description lang="de_DE">Inoffizielles Plex für Kodi add-on</description>
<disclaimer lang="en">This add-on is not supported by Plex</disclaimer>
<disclaimer lang="es">Este add-on no está respaldado por Plex</disclaimer>
<disclaimer lang="de">Dieses Addon wird nicht von der Firma Plex Inc. unterstützt</disclaimer>

<license>GPL-2.0-only</license>
<forum>https://forums.plex.tv/t/plexmod-for-kodi-18-19-20-21/481208</forum>
<website>https://www.plex.tv</website>
<source>https://github.com/pannal/plex-for-kodi</source>
<platform>all</platform>
<news>
0.7.4
- Based on 0.7.5-rev2
</news>
<assets>
<icon>icon.png</icon>
Expand Down
61 changes: 61 additions & 0 deletions script.plexmod/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,64 @@
[- 0.7.5-rev2 -]
- Core: asyncio Kodi compat
- Fix: transcoding is broken due to deepcopy usage
- Add IMDB ID to video info
- Fix: Libraries: Missing items when filters applied and collections exist (thanks @bowlingbeeg )
- Fix: Libraries: Removed most filters from collections view as they don’t work (thanks @bowlingbeeg)
- Fix: Chapters not available in episodes during playback after manually changing watch status
- Fix: next episode receives resume state from previously resumed episode when pressing NEXT
- Fix: Items with non-existant files get removed from the home hubs when visited
- Fix: When using ACTION_PLAYER_PLAY to autoplay an item on Home that resumes, all title2 titles of the underlying screen disappear
- Fix: PPI: CPU core usage overlaps items when playing something with enabled subtitles
- Fix: SeekDialog/Handler/Player: Edge case where progress wasn't updated in certain situations
- Fix: Non-Home-Hubs: hub elements limited to 10 after modifying an underlying item
- Update Spanish translations (thanks @Deci8BelioS)
- TV Shows: Allow deleting TV shows if possible
- TV Shows: Allow deleting seasons if possible
- TV Shows: Add CONTEXT_MENU handler on seasons, allowing changing watched status and deletion
- TV Shows: More accurately show progress bar including in progress episodes
- Player: Don’t use old resume info when going to next video in playlist
- Player: Simplify and optimize stop/end/next logic
- Core: Fall back properly when Kodi version string couldn’t be parsed
- Core: Correctly reload addon settings on maximize from minimized state (so yes, you can minimize the addon, change addon settings, then maximize it again and the settings will be applied)
- Core: Edge-case: Ensure TV background music isn't recognized as audio
- Core: Logging: Clean Plex tokens from constructed item for playback dict
- Core: SeekHandler: fix usage of player.video.duration erroring
- Core: Home: Store last BG on minimize and on home select as well; don't store the same background URL if it hasn't changed
- Core: VideoPlayer: Make sure we're the only active player, try to stop all other active players for 5 seconds
- Core: Instead of using Action(back) before running addon, navigate to Home and try to guarantee that
- Home: Don’t round robin while loading the next hub pagination chunk
- Home: Sections: Increase section select timeout from 300 to 500ms
- Home: Fix autoplay from home on episodes (ACTION_PLAYER_PLAY)
- Home: Don't fail on empty hub
- InfoScreen: Add DV stream metadata info
- Episodes: Show episode options menu on long press select/OK (CONTEXT_MENU)
- Episodes/Seasons/Shows: Calculate and show season watched percentage including in-progress episodes
- Episodes/Seasons/Shows: Show progress even for shows/seasons with only an in progress episode, no fully watched ones
- Episodes: Adjust ratings/userratings positioning and label widths; add autoscroll to show and episode titles
- Episodes: Wait for full episode data to be loaded before enabling playback
- Episodes: Add directors/writers to episode view
- Episodes: BGM: Fix show() accessor failing
- Episodes: Don't play background music when current volume is zero
- Movies/TV/Episodes: Support ratings from non-legacy agents (also when primary provider is IMDB)
- Movies: Scroll long titles as well
- Movies/Preplay: Move userRating/stars in line with other ratings
- Movies/Preplay: Show writers besides directors as well
- PlayerSettings: Add Kodi Resolution Settings for Kodi >= 18 (only when you have a whitelist configured in Kodi/Video)
- PlayerSettings: Add Kodi Colour Management for Kodi >= 20 (when applicable)
- SeekDialog: Ignore immediate OK/SELECT on auto-skipping marker with countdown during the first second of the marker shown
- SeekDialog: Make sure we only send the correct timeline request on certain actions
- SeekDialog: Make sure we send a timeline request on certain actions
- SeekDialog: Remove now unnecessary negative offset on manual marker skip on final credits marker, as we’ve got much more robust PostPlay handling now
- SeekDialog: Avoid showing the final credits marker twice (after it’s been manually skipped already)
- SeekDialog: Hide once-manually-skipped markers and move them to the OSD as well
- SeekDialog: Don't react to SELECT/ENTER on counting down autoskip marker if marker is only visible in OSD
- SeekDialog: Bingemode: Don’t autoskip the credits of the last available episode of a TV show
- Player/SeekDialog: Simplify and harden PostPlay behaviour
- Libraries: Clean up code, improve performance (memory usage, CPU load) and optimize viewing experience by properly chunking the view’s requests based on the view position (thanks @bowlingbeeg)
- Addon Settings: Add setting for Library view chunk size
- Settings: Add separate playback setting to skip Post Play in TV shows (separate from binge mode)
- Settings: Clarify and reorder playback settings

[- 0.7.4 -]
- Add: Show video codec rendering type (SDR/HDR, ...) in "choose version" dialog
- Add: Add imperfect (but better-than-none) representation of DTS profiles and EAC3 JOC to preplay and stream screens
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def build(self, directPlay=None):

self.metadata = obj

util.LOG("Constructed audio item for playback: {0}".format(obj))
util.LOG("Constructed audio item for playback: {0}".format(util.cleanObjTokens(obj)))

return self.metadata

Expand Down
15 changes: 9 additions & 6 deletions script.plexmod/lib/_included_packages/plexnet/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,18 @@ def delete(self):
self.deleted = req.wasOK()
return self.deleted

def exists(self):
if self.deleted or self.deletedAt:
def exists(self, force_full_check=False):
if (self.deleted or self.deletedAt) and not force_full_check:
return False

data = self.server.query('/library/metadata/{0}'.format(self.ratingKey))
# force_full_check is imperfect, as it doesn't check for existence of its mediaparts
try:
data = self.server.query('/library/metadata/{0}'.format(self.ratingKey))
except exceptions.BadRequest:
# item does not exist anymore
util.DEBUG_LOG("Item {} doesn't exist.".format(self.ratingKey))
return False
return data is not None and data.attrib.get('size') != '0'
# req = plexrequest.PlexRequest(self.server, '/library/metadata/{0}'.format(self.ratingKey), method='HEAD')
# req.getToStringWithTimeout(10)
# return not req.wasNotFound()

def relatedHubs(self, data, _itemCls, hubIdentifiers=None, _filter=None):
hubs = data.find("Related")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,28 +131,28 @@ def __init__(self):
for timelineType in self.TIMELINE_TYPES:
self.timelines[timelineType] = TimelineData(timelineType)

def updatePlaybackState(self, timelineType, playerObject, state, time, playQueue=None, duration=0):
def updatePlaybackState(self, timelineType, playerObject, state, t, playQueue=None, duration=0, force=False):
timeline = self.timelines[timelineType]
timeline.state = state
timeline.item = playerObject.item
timeline.choice = playerObject.choice
timeline.playQueue = playQueue
timeline.attrs["time"] = str(time)
timeline.attrs["time"] = str(t)
timeline.duration = duration

# self.sendTimelineToAll()

self.sendTimelineToServer(timelineType, timeline, time)
self.sendTimelineToServer(timelineType, timeline, t, force=force)

def sendTimelineToServer(self, timelineType, timeline, time):
def sendTimelineToServer(self, timelineType, timeline, t, force=False):
if not hasattr(timeline.item, 'getServer') or not timeline.item.getServer():
return

serverTimeline = self.getServerTimeline(timelineType)

# Only send timeline if it's the first, item changes, playstate changes or timer pops
itemsEqual = timeline.item and serverTimeline.item and timeline.item.ratingKey == serverTimeline.item.ratingKey
if itemsEqual and timeline.state == serverTimeline.state and not serverTimeline.isExpired():
if itemsEqual and timeline.state == serverTimeline.state and not serverTimeline.isExpired() and not force:
return

serverTimeline.reset()
Expand All @@ -168,11 +168,11 @@ def sendTimelineToServer(self, timelineType, timeline, time):
# It's possible with timers and in player seeking for the time to be greater than the
# duration, which causes a 400, so in that case we'll set the time to the duration.
duration = timeline.item.duration.asInt() or timeline.duration
if time > duration:
time = duration
if t > duration:
t = duration

params = util.AttributeDict()
params["time"] = time
params["time"] = t
params["duration"] = duration
params["state"] = timeline.state
params["guid"] = timeline.item.guid
Expand Down
27 changes: 17 additions & 10 deletions script.plexmod/lib/_included_packages/plexnet/plexlibrary.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,16 @@ def folder(self, start=None, size=None, subDir=False):

def items(self, path, start, size, filter_, sort, unwatched, type_, tag_fallback):

args = {"includeCollections" : "1"}
args = {}

if size is not None:
args['X-Plex-Container-Start'] = start
args['X-Plex-Container-Size'] = size

if filter_:
args[filter_[0]] = filter_[1]
else:
args['includeCollections'] = 1

if sort:
args['sort'] = '{0}:{1}'.format(*sort)
Expand All @@ -183,10 +185,12 @@ def jumpList(self, filter_=None, sort=None, unwatched=False, type_=None):
else:
path = '/library/sections/{0}/firstCharacter'.format(self.key)

args = {"includeCollections" : "1"}
args = {}

if filter_:
args[filter_[0]] = filter_[1]
else:
args['includeCollections'] = 1

if sort:
args['sort'] = '{0}:{1}'.format(*sort)
Expand Down Expand Up @@ -444,7 +448,7 @@ def __repr__(self):
title = self.title.replace(' ', '.')[0:20]
return '<{0}:{1}:{2}>'.format(self.__class__.__name__, self.key, title)

def exists(self):
def exists(self, *args, **kwargs):
try:
self.server.query('/playlists/{0}'.format(self.ratingKey))
return True
Expand Down Expand Up @@ -506,6 +510,10 @@ def buildComposite(self, **kwargs):


class BaseHub(plexobjects.PlexObject):
def __init__(self, *args, **kwargs):
super(BaseHub, self).__init__(*args, **kwargs)
self._identifier = None

def reset(self):
self.set('offset', 0)
self.set('size', len(self.items))
Expand All @@ -516,6 +524,11 @@ def reset(self):
(self.items[0].container.offset.asInt() + self.items[0].container.size.asInt() < totalSize) and '1' or ''
)

def getCleanHubIdentifier(self):
if not self._identifier:
self._identifier = re.sub(r'\.\d+$', '', re.sub(r'\.\d+$', '', self.hubIdentifier))
return self._identifier


class Hub(BaseHub):
TYPE = "Hub"
Expand All @@ -541,13 +554,10 @@ def init(self, data):
def __repr__(self):
return '<{0}:{1}>'.format(self.__class__.__name__, self.hubIdentifier)

def getCleanHubIdentifier(self):
return re.sub(r'\.\d+$', '', re.sub(r'\.\d+$', '', self.hubIdentifier))

def reload(self, **kwargs):
""" Reload the data for this object from PlexServer XML. """
try:
data = self.server.query(self.key, params=kwargs)
data = self.server.query(self.key, **kwargs)
except Exception as e:
import traceback
traceback.print_exc()
Expand Down Expand Up @@ -594,9 +604,6 @@ def init(self, data):
util.DEBUG_LOG('AudioPlaylistHub: Bad request: {0}'.format(self))
self.items = []

def getCleanHubIdentifier(self):
return re.sub(r'\.\d+$', '', re.sub(r'\.\d+$', '', self.hubIdentifier))

def extend(self, start=None, size=None):
path = '/playlists/all?playlistType={0}'.format(self.type)

Expand Down
8 changes: 7 additions & 1 deletion script.plexmod/lib/_included_packages/plexnet/plexobjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ def __new__(cls, value, parent=None):
def __call__(self, default):
return not self.NA and self or PlexValue(default, self.parent)

def __copy__(self):
return self.__deepcopy__()

def __deepcopy__(self, memodict=None):
return self.__class__(self)

def asBool(self):
return self == '1'

Expand Down Expand Up @@ -187,7 +193,7 @@ def __getattr__(self, attr):

return a

def exists(self):
def exists(self, *args, **kwargs):
# Used for media items - for others we just return True
return True

Expand Down
6 changes: 3 additions & 3 deletions script.plexmod/lib/_included_packages/plexnet/plexplayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def _build(self, directPlay=None, directStream=True, currentPartIndex=None):

self.metadata = obj

util.LOG("Constructed video item for playback: {0}".format(dict(obj)))
util.LOG("Constructed video item for playback: {0}".format(util.cleanObjTokens(dict(obj))))

return self.metadata

Expand Down Expand Up @@ -853,7 +853,7 @@ def build(self, directPlay=None):

self.metadata = obj

util.LOG("Constructed audio item for playback: {0}".format(dict(obj)))
util.LOG("Constructed audio item for playback: {0}".format(util.cleanObjTokens(dict(obj))))

return self.metadata

Expand Down Expand Up @@ -927,7 +927,7 @@ def build(self, item=None):
obj.url = server.buildUrl(path, True)
obj.enableBlur = server.supportsPhotoTranscoding

util.DEBUG_LOG("Constructed photo item for playback: {0}".format(dict(obj)))
util.DEBUG_LOG("Constructed photo item for playback: {0}".format(util.cleanObjTokens(dict(obj))))

self.metadata = obj

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from . import util
from . import exceptions
from . import compat
from . import verlib

from xml.etree import ElementTree
from . import signalsmixin
Expand Down
12 changes: 10 additions & 2 deletions script.plexmod/lib/_included_packages/plexnet/plexstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,17 @@ def videoCodecRendering(self):
render = "sdr"

if self.DOVIProfile == "8" and self.DOVIBLCompatID == "1":
render = "dv/hdr10"
render = "dv p8.1/hdr"
elif self.DOVIProfile == "8" and self.DOVIBLCompatID == "2":
render = "dv p8.2/sdr"
elif self.DOVIProfile == "8" and self.DOVIBLCompatID == "4":
render = "dv p8.4/hlg"
elif self.DOVIProfile == "7":
render = "dv p7"
elif self.DOVIProfile == "5":
render = "dv p5"
elif self.DOVIProfile:
render = "dv"
render = "dv p{}".format(self.DOVIProfile)
elif self.colorTrc == "smpte2084":
render = "hdr"
elif self.colorTrc == "arib-std-b67":
Expand Down
23 changes: 23 additions & 0 deletions script.plexmod/lib/_included_packages/plexnet/util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# coding=utf-8

from __future__ import absolute_import

from copy import copy

from . import simpleobjects
import re
import sys
Expand Down Expand Up @@ -156,6 +159,26 @@ def cleanToken(url):
return re.sub(r'X-Plex-Token=[^&]+', 'X-Plex-Token=****', url)


def cleanObjTokens(dorig, flistkeys=("streamUrls",), fstrkeys=("url", "token")):
d = {}
dcopy = copy(dorig)

# filter lists
for k in flistkeys:
if k not in d:
continue
d[k] = list(map(lambda x: cleanToken(x), d[k][:]))

# filter strings
for k in fstrkeys:
if k not in d:
continue
d[k] = "****" if k == "token" else cleanToken(d[k])

dcopy.update(d)
return dcopy


def now(local=False):
if local:
return time.time()
Expand Down
2 changes: 2 additions & 0 deletions script.plexmod/lib/_included_packages/plexnet/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ def _setData(self, data):
self.roles = plexobjects.PlexItemList(data, media.Role, media.Role.TYPE, server=self.server, container=self.container)
#self.related = plexobjects.PlexItemList(data.find('Related'), plexlibrary.Hub, plexlibrary.Hub.TYPE, server=self.server, container=self)
self.extras = PlexVideoItemList(data.find('Extras'), initpath=self.initpath, server=self.server, container=self)
self.onDeck = PlexVideoItemList(data.find('OnDeck'), initpath=self.initpath, server=self.server,
container=self)

@property
def unViewedLeafCount(self):
Expand Down
Loading

0 comments on commit 4ea8421

Please sign in to comment.