mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-07 05:31:15 -07:00
Bump plexapi from 4.15.4 to 4.15.10 (#2251)
* Bump plexapi from 4.15.4 to 4.15.10 Bumps [plexapi](https://github.com/pkkid/python-plexapi) from 4.15.4 to 4.15.10. - [Release notes](https://github.com/pkkid/python-plexapi/releases) - [Commits](https://github.com/pkkid/python-plexapi/compare/4.15.4...4.15.10) --- updated-dependencies: - dependency-name: plexapi dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Update plexapi==4.15.10 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com> [skip ci]
This commit is contained in:
parent
040972bcba
commit
b1c0972077
16 changed files with 413 additions and 189 deletions
|
@ -1,5 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from collections import deque
|
||||
from datetime import datetime
|
||||
from typing import Deque, Set, Tuple, Union
|
||||
from urllib.parse import parse_qsl, quote, quote_plus, unquote, urlencode, urlsplit
|
||||
|
||||
from plexapi import media, settings, utils
|
||||
|
@ -61,63 +63,96 @@ class AdvancedSettingsMixin:
|
|||
|
||||
|
||||
class SmartFilterMixin:
|
||||
""" Mixing for Plex objects that can have smart filters. """
|
||||
""" Mixin for Plex objects that can have smart filters. """
|
||||
|
||||
def _parseFilterGroups(self, feed: Deque[Tuple[str, str]], returnOn: Union[Set[str], None] = None) -> dict:
|
||||
""" Parse filter groups from input lines between push and pop. """
|
||||
currentFiltersStack: list[dict] = []
|
||||
operatorForStack = None
|
||||
if returnOn is None:
|
||||
returnOn = set("pop")
|
||||
else:
|
||||
returnOn.add("pop")
|
||||
allowedLogicalOperators = ["and", "or"] # first is the default
|
||||
|
||||
while feed:
|
||||
key, value = feed.popleft() # consume the first item
|
||||
if key == "push":
|
||||
# recurse and add the result to the current stack
|
||||
currentFiltersStack.append(
|
||||
self._parseFilterGroups(feed, returnOn)
|
||||
)
|
||||
elif key in returnOn:
|
||||
# stop iterating and return the current stack
|
||||
if not key == "pop":
|
||||
feed.appendleft((key, value)) # put the item back
|
||||
break
|
||||
|
||||
elif key in allowedLogicalOperators:
|
||||
# set the operator
|
||||
if operatorForStack and not operatorForStack == key:
|
||||
raise ValueError(
|
||||
"cannot have different logical operators for the same"
|
||||
" filter group"
|
||||
)
|
||||
operatorForStack = key
|
||||
|
||||
else:
|
||||
# add the key value pair to the current filter
|
||||
currentFiltersStack.append({key: value})
|
||||
|
||||
if not operatorForStack and len(currentFiltersStack) > 1:
|
||||
# consider 'and' as the default operator
|
||||
operatorForStack = allowedLogicalOperators[0]
|
||||
|
||||
if operatorForStack:
|
||||
return {operatorForStack: currentFiltersStack}
|
||||
return currentFiltersStack.pop()
|
||||
|
||||
def _parseQueryFeed(self, feed: "deque[Tuple[str, str]]") -> dict:
|
||||
""" Parse the query string into a dict. """
|
||||
filtersDict = {}
|
||||
special_keys = {"type", "sort"}
|
||||
integer_keys = {"includeGuids", "limit"}
|
||||
as_is_keys = {"group", "having"}
|
||||
reserved_keys = special_keys | integer_keys | as_is_keys
|
||||
while feed:
|
||||
key, value = feed.popleft()
|
||||
if key in integer_keys:
|
||||
filtersDict[key] = int(value)
|
||||
elif key in as_is_keys:
|
||||
filtersDict[key] = value
|
||||
elif key == "type":
|
||||
filtersDict["libtype"] = utils.reverseSearchType(value)
|
||||
elif key == "sort":
|
||||
filtersDict["sort"] = value.split(",")
|
||||
else:
|
||||
feed.appendleft((key, value)) # put the item back
|
||||
filter_group = self._parseFilterGroups(
|
||||
feed, returnOn=reserved_keys
|
||||
)
|
||||
if "filters" in filtersDict:
|
||||
filtersDict["filters"] = {
|
||||
"and": [filtersDict["filters"], filter_group]
|
||||
}
|
||||
else:
|
||||
filtersDict["filters"] = filter_group
|
||||
|
||||
return filtersDict
|
||||
|
||||
def _parseFilters(self, content):
|
||||
""" Parse the content string and returns the filter dict. """
|
||||
content = urlsplit(unquote(content))
|
||||
filters = {}
|
||||
filterOp = 'and'
|
||||
filterGroups = [[]]
|
||||
feed = deque()
|
||||
|
||||
for key, value in parse_qsl(content.query):
|
||||
# Move = sign to key when operator is ==
|
||||
if value.startswith('='):
|
||||
key += '='
|
||||
value = value[1:]
|
||||
if value.startswith("="):
|
||||
key, value = f"{key}=", value[1:]
|
||||
|
||||
if key == 'includeGuids':
|
||||
filters['includeGuids'] = int(value)
|
||||
elif key == 'type':
|
||||
filters['libtype'] = utils.reverseSearchType(value)
|
||||
elif key == 'sort':
|
||||
filters['sort'] = value.split(',')
|
||||
elif key == 'limit':
|
||||
filters['limit'] = int(value)
|
||||
elif key == 'push':
|
||||
filterGroups[-1].append([])
|
||||
filterGroups.append(filterGroups[-1][-1])
|
||||
elif key == 'and':
|
||||
filterOp = 'and'
|
||||
elif key == 'or':
|
||||
filterOp = 'or'
|
||||
elif key == 'pop':
|
||||
filterGroups[-1].insert(0, filterOp)
|
||||
filterGroups.pop()
|
||||
else:
|
||||
filterGroups[-1].append({key: value})
|
||||
feed.append((key, value))
|
||||
|
||||
if filterGroups:
|
||||
filters['filters'] = self._formatFilterGroups(filterGroups.pop())
|
||||
return filters
|
||||
|
||||
def _formatFilterGroups(self, groups):
|
||||
""" Formats the filter groups into the advanced search rules. """
|
||||
if len(groups) == 1 and isinstance(groups[0], list):
|
||||
groups = groups.pop()
|
||||
|
||||
filterOp = 'and'
|
||||
rules = []
|
||||
|
||||
for g in groups:
|
||||
if isinstance(g, list):
|
||||
rules.append(self._formatFilterGroups(g))
|
||||
elif isinstance(g, dict):
|
||||
rules.append(g)
|
||||
elif g in {'and', 'or'}:
|
||||
filterOp = g
|
||||
|
||||
return {filterOp: rules}
|
||||
return self._parseQueryFeed(feed)
|
||||
|
||||
|
||||
class SplitMergeMixin:
|
||||
|
@ -281,19 +316,16 @@ class PlayedUnplayedMixin:
|
|||
return self
|
||||
|
||||
@property
|
||||
@deprecated('use "isPlayed" instead', stacklevel=3)
|
||||
def isWatched(self):
|
||||
""" Returns True if the show is watched. """
|
||||
""" Alias to self.isPlayed. """
|
||||
return self.isPlayed
|
||||
|
||||
@deprecated('use "markPlayed" instead')
|
||||
def markWatched(self):
|
||||
""" Mark the video as played. """
|
||||
""" Alias to :func:`~plexapi.mixins.PlayedUnplayedMixin.markPlayed`. """
|
||||
self.markPlayed()
|
||||
|
||||
@deprecated('use "markUnplayed" instead')
|
||||
def markUnwatched(self):
|
||||
""" Mark the video as unplayed. """
|
||||
""" Alias to :func:`~plexapi.mixins.PlayedUnplayedMixin.markUnplayed`. """
|
||||
self.markUnplayed()
|
||||
|
||||
|
||||
|
@ -755,7 +787,8 @@ class EditTagsMixin:
|
|||
|
||||
if not remove:
|
||||
tags = getattr(self, self._tagPlural(tag), [])
|
||||
items = tags + items
|
||||
if isinstance(tags, list):
|
||||
items = tags + items
|
||||
|
||||
edits = self._tagHelper(self._tagSingular(tag), items, locked, remove)
|
||||
edits.update(kwargs)
|
||||
|
@ -1163,7 +1196,7 @@ class AlbumEditMixins(
|
|||
class TrackEditMixins(
|
||||
ArtLockMixin, PosterLockMixin, ThemeLockMixin,
|
||||
AddedAtMixin, TitleMixin, TrackArtistMixin, TrackNumberMixin, TrackDiscNumberMixin, UserRatingMixin,
|
||||
CollectionMixin, LabelMixin, MoodMixin
|
||||
CollectionMixin, GenreMixin, LabelMixin, MoodMixin
|
||||
):
|
||||
pass
|
||||
|
||||
|
@ -1189,3 +1222,10 @@ class CollectionEditMixins(
|
|||
LabelMixin
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class PlaylistEditMixins(
|
||||
ArtLockMixin, PosterLockMixin,
|
||||
SortTitleMixin, SummaryMixin, TitleMixin
|
||||
):
|
||||
pass
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue