style: lint all python files (#1228)

This commit is contained in:
ReenigneArcher 2023-08-28 23:29:39 -04:00 committed by GitHub
parent dba3369f55
commit a6b6ebfbff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 131 additions and 105 deletions

10
.flake8
View file

@ -5,12 +5,14 @@
# E701: multiple statements on one line (colon)
# E702: multiple statements on one line (semicolon)
# E731: do not assign a lambda expression, use a def
# W293: blank line contains whitespace
# W503: line break before binary operator
# W605: invalid escape sequence
[flake8]
ignore=E128,E701,E702,E731,W293,W503,W605
exclude=compat.py
ignore=E128,E701,E702,E731,W503,W605
exclude=compat.py,venv
per-file-ignores =
tests/payloads.py:E501
max-complexity = -1
max-line-length = 125
# The GitHub editor is 127 chars wide
max-line-length = 127
show-source = True

View file

@ -54,9 +54,13 @@ jobs:
run: |
. venv/bin/activate
# stop the build if there are Python syntax errors or undefined names
flake8 plexapi --count --select=E9,F63,F7,F82 --show-source --statistics
echo "::group::flake8 pass 1"
flake8 --count --select=E9,F63,F7,F82 --show-source --statistics
echo "::endgroup::"
# The GitHub editor is 127 chars wide
flake8 plexapi --count --max-complexity=12 --max-line-length=127 --statistics
echo "::group::flake8 pass 2"
flake8 --count --max-complexity=12 --max-line-length=127 --statistics
echo "::endgroup::"
pytest:

View file

@ -12,12 +12,13 @@
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
import copy, sys
import copy
from os.path import abspath, dirname, join
import sys
path = dirname(dirname(abspath(__file__)))
sys.path.append(path)
sys.path.append(join(path, 'plexapi'))
import plexapi
import plexapi # noqa: E402
extensions = [
'sphinx.ext.autodoc',
@ -27,13 +28,17 @@ extensions = [
]
# -- Monkey-patch docstring to not auto-link :ivars ------------------------
from sphinx.domains.python import PythonDomain
from sphinx.domains.python import PythonDomain # noqa: E402
print('Monkey-patching PythonDomain.resolve_xref()')
old_resolve_xref = copy.deepcopy(PythonDomain.resolve_xref)
def new_resolve_xref(*args):
if '.' not in args[5]: # target
return None
return old_resolve_xref(*args)
PythonDomain.resolve_xref = new_resolve_xref
# -- Napoleon Settings -----------------------------------------------------
@ -79,7 +84,7 @@ author = 'M.Shepanski'
# The short X.Y version.
version = plexapi.VERSION
# The full version, including alpha/beta/rc tags.
#release = '2.0.2'
# release = '2.0.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@ -137,7 +142,7 @@ html_context = {'css_files': ['_static/custom.css']}
html_theme_options = {
'collapse_navigation': False,
'display_version': False,
#'navigation_depth': 3,
# 'navigation_depth': 3,
}
@ -238,17 +243,17 @@ htmlhelp_basename = 'PythonPlexAPIdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper',
# The paper size ('letterpaper' or 'a4paper').
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt',
# The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
# 'preamble': '',
# Additional stuff for the LaTeX preamble.
# 'preamble': '',
# Latex figure (float) alignment
# 'figure_align': 'htbp',
# Latex figure (float) alignment
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples

View file

@ -919,7 +919,7 @@ class PlexSession(object):
def stop(self, reason=''):
""" Stop playback for the session.
Parameters:
reason (str): Message displayed to the user for stopping playback.
"""

View file

@ -400,7 +400,7 @@ class Collection(
@deprecated('use editTitle, editSortTitle, editContentRating, and editSummary instead')
def edit(self, title=None, titleSort=None, contentRating=None, summary=None, **kwargs):
""" Edit the collection.
Parameters:
title (str, optional): The title of the collection.
titleSort (str, optional): The sort title of the collection.

View file

@ -542,7 +542,7 @@ class LibrarySection(PlexObject):
def addLocations(self, location):
""" Add a location to a library.
Parameters:
location (str or list): A single folder path, list of paths.
@ -565,7 +565,7 @@ class LibrarySection(PlexObject):
def removeLocations(self, location):
""" Remove a location from a library.
Parameters:
location (str or list): A single folder path, list of paths.
@ -744,7 +744,7 @@ class LibrarySection(PlexObject):
def lockAllField(self, field, libtype=None):
""" Lock a field for all items in the library.
Parameters:
field (str): The field to lock (e.g. thumb, rating, collection).
libtype (str, optional): The library type to lock (movie, show, season, episode,
@ -754,7 +754,7 @@ class LibrarySection(PlexObject):
def unlockAllField(self, field, libtype=None):
""" Unlock a field for all items in the library.
Parameters:
field (str): The field to unlock (e.g. thumb, rating, collection).
libtype (str, optional): The library type to lock (movie, show, season, episode,
@ -847,7 +847,7 @@ class LibrarySection(PlexObject):
"""
_key = ('/library/sections/{key}/{filter}?includeMeta=1&includeAdvanced=1'
'&X-Plex-Container-Start=0&X-Plex-Container-Size=0')
key = _key.format(key=self.key, filter='all')
data = self._server.query(key)
self._filterTypes = self.findItems(data, FilteringType, rtag='Meta')
@ -894,7 +894,7 @@ class LibrarySection(PlexObject):
def getFieldType(self, fieldType):
""" Returns a :class:`~plexapi.library.FilteringFieldType` for a specified fieldType.
Parameters:
fieldType (str): The data type for the field (tag, integer, string, boolean, date,
subtitleLanguage, audioLanguage, resolution).
@ -927,7 +927,7 @@ class LibrarySection(PlexObject):
"""
return self.getFilterType(libtype).filters
def listSorts(self, libtype=None):
""" Returns a list of available :class:`~plexapi.library.FilteringSort` for a specified libtype.
This is the list of options in the sorting dropdown menu
@ -970,7 +970,7 @@ class LibrarySection(PlexObject):
""" Returns a list of available :class:`~plexapi.library.FilteringOperator` for a specified fieldType.
This is the list of options in the custom filter operator dropdown menu
(`screenshot <../_static/images/LibrarySection.search.png>`__).
Parameters:
fieldType (str): The data type for the field (tag, integer, string, boolean, date,
subtitleLanguage, audioLanguage, resolution).
@ -992,7 +992,7 @@ class LibrarySection(PlexObject):
:class:`~plexapi.library.FilteringFilter` or filter field.
This is the list of available values for a custom filter
(`screenshot <../_static/images/LibrarySection.search.png>`__).
Parameters:
field (str): :class:`~plexapi.library.FilteringFilter` object,
or the name of the field (genre, year, contentRating, etc.).
@ -1024,7 +1024,7 @@ class LibrarySection(PlexObject):
availableFilters = [f.filter for f in self.listFilters(libtype)]
raise NotFound(f'Unknown filter field "{field}" for libtype "{libtype}". '
f'Available filters: {availableFilters}') from None
data = self._server.query(field.key)
return self.findItems(data, FilterChoice)
@ -1111,7 +1111,7 @@ class LibrarySection(PlexObject):
except (ValueError, AttributeError):
raise BadRequest(f'Invalid value "{value}" for filter field "{filterField.key}", '
f'value should be type {fieldType.type}') from None
return results
def _validateFieldValueDate(self, value):
@ -1345,7 +1345,7 @@ class LibrarySection(PlexObject):
Tag type filter values can be a :class:`~plexapi.library.FilterChoice` object,
:class:`~plexapi.media.MediaTag` object, the exact name :attr:`MediaTag.tag` (*str*),
or the exact id :attr:`MediaTag.id` (*int*).
Date type filter values can be a ``datetime`` object, a relative date using a one of the
available date suffixes (e.g. ``30d``) (*str*), or a date in ``YYYY-MM-DD`` (*str*) format.
@ -1358,7 +1358,7 @@ class LibrarySection(PlexObject):
* ``w``: ``weeks``
* ``mon``: ``months``
* ``y``: ``years``
Multiple values can be ``OR`` together by providing a list of values.
Examples:
@ -1686,10 +1686,10 @@ class LibrarySection(PlexObject):
""" Validates the specified items are from this library and of the same type. """
if items is None or items == []:
raise BadRequest('No items specified.')
if not isinstance(items, list):
items = [items]
itemType = items[0].type
for item in items:
if item.librarySectionID != self.key:
@ -3102,6 +3102,7 @@ class FirstCharacter(PlexObject):
size (str): Total amount of library items starting with this character.
title (str): Character (#, !, A, B, C, ...).
"""
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self._data = data

View file

@ -1008,6 +1008,7 @@ class BaseResource(PlexObject):
selected (bool): True if the resource is currently selected.
thumb (str): The URL to retrieve the resource thumbnail.
"""
def _loadData(self, data):
self._data = data
self.key = data.attrib.get('key')

View file

@ -39,7 +39,7 @@ class AdvancedSettingsMixin:
pref = preferences[settingID]
except KeyError:
raise NotFound(f'{value} not found in {list(preferences.keys())}')
enumValues = pref.enumValues
if enumValues.get(value, enumValues.get(str(value))):
data[settingID] = value
@ -69,7 +69,7 @@ class SmartFilterMixin:
filters = {}
filterOp = 'and'
filterGroups = [[]]
for key, value in parse_qsl(content.query):
# Move = sign to key when operator is ==
if value.startswith('='):
@ -96,11 +96,11 @@ class SmartFilterMixin:
filterGroups.pop()
else:
filterGroups[-1].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):
@ -131,7 +131,7 @@ class SplitMergeMixin:
def merge(self, ratingKeys):
""" Merge other Plex objects into the current object.
Parameters:
ratingKeys (list): A list of rating keys to merge.
"""
@ -320,7 +320,7 @@ class RatingMixin:
class ArtUrlMixin:
""" Mixin for Plex objects that can have a background artwork url. """
@property
def artUrl(self):
""" Return the art url for the Plex object. """
@ -349,7 +349,7 @@ class ArtMixin(ArtUrlMixin, ArtLockMixin):
def uploadArt(self, url=None, filepath=None):
""" Upload a background artwork from a url or filepath.
Parameters:
url (str): The full URL to the image to upload.
filepath (str): The full file path the the image to upload or file-like object.
@ -365,7 +365,7 @@ class ArtMixin(ArtUrlMixin, ArtLockMixin):
def setArt(self, art):
""" Set the background artwork for a Plex object.
Parameters:
art (:class:`~plexapi.media.Art`): The art object to select.
"""
@ -425,7 +425,7 @@ class PosterMixin(PosterUrlMixin, PosterLockMixin):
def setPoster(self, poster):
""" Set the poster for a Plex object.
Parameters:
poster (:class:`~plexapi.media.Poster`): The poster object to select.
"""
@ -491,11 +491,11 @@ class ThemeMixin(ThemeUrlMixin, ThemeLockMixin):
class EditFieldMixin:
""" Mixin for editing Plex object fields. """
def editField(self, field, value, locked=True, **kwargs):
""" Edit the field of a Plex object. All field editing methods can be chained together.
Also see :func:`~plexapi.base.PlexPartialObject.batchEdits` for batch editing fields.
Parameters:
field (str): The name of the field to edit.
value (str): The value to edit the field to.

View file

@ -674,7 +674,7 @@ class MyPlexAccount(PlexObject):
if (invite.username and invite.email and invite.id and username.lower() in
(invite.username.lower(), invite.email.lower(), str(invite.id))):
return invite
raise NotFound(f'Unable to find invite {username}')
def pendingInvites(self, includeSent=True, includeReceived=True):
@ -952,7 +952,7 @@ class MyPlexAccount(PlexObject):
"""
if not isinstance(items, list):
items = [items]
for item in items:
if self.onWatchlist(item):
raise BadRequest(f'"{item.title}" is already on the watchlist')
@ -973,7 +973,7 @@ class MyPlexAccount(PlexObject):
"""
if not isinstance(items, list):
items = [items]
for item in items:
if not self.onWatchlist(item):
raise BadRequest(f'"{item.title}" is not on the watchlist')
@ -1944,7 +1944,7 @@ class AccountOptOut(PlexObject):
def optOutManaged(self):
""" Sets the Online Media Source to "Disabled for Managed Users".
Raises:
:exc:`~plexapi.exceptions.BadRequest`: When trying to opt out music.
"""

View file

@ -256,7 +256,7 @@ class Photo(
List<str> of file paths where the photo is found on disk.
"""
return [part.file for item in self.media for part in item.parts if part]
def sync(self, resolution, client=None, clientId=None, limit=None, title=None):
""" Add current photo as sync item for specified device.
See :func:`~plexapi.myplex.MyPlexAccount.sync` for possible exceptions.

View file

@ -155,7 +155,7 @@ class Playlist(
sectionKey = int(match.group(1))
self._section = self._server.library.sectionByID(sectionKey)
return self._section
# Try to get the library section from the first item in the playlist
if self.items():
self._section = self.items()[0].section()
@ -314,7 +314,7 @@ class Playlist(
def edit(self, title=None, summary=None):
""" Edit the playlist.
Parameters:
title (str, optional): The title of the playlist.
summary (str, optional): The summary of the playlist.
@ -432,7 +432,7 @@ class Playlist(
def copyToUser(self, user):
""" Copy playlist to another user account.
Parameters:
user (:class:`~plexapi.myplex.MyPlexUser` or str): `MyPlexUser` object, username,
email, or user id of the user to copy the playlist to.

View file

@ -202,7 +202,7 @@ class PlexServer(PlexObject):
def claim(self, account):
""" Claim the Plex server using a :class:`~plexapi.myplex.MyPlexAccount`.
This will only work with an unclaimed server on localhost or the same subnet.
Parameters:
account (:class:`~plexapi.myplex.MyPlexAccount`): The account used to
claim the server.
@ -245,7 +245,7 @@ class PlexServer(PlexObject):
def switchUser(self, user, session=None, timeout=None):
""" Returns a new :class:`~plexapi.server.PlexServer` object logged in as the given username.
Note: Only the admin account can switch to other users.
Parameters:
user (:class:`~plexapi.myplex.MyPlexUser` or str): `MyPlexUser` object, username,
email, or user id of the user to log in to the server.
@ -590,7 +590,7 @@ class PlexServer(PlexObject):
def runButlerTask(self, task):
""" Manually run a butler task immediately instead of waiting for the scheduled task to run.
Note: The butler task is run asynchronously. Check Plex Web to monitor activity.
Parameters:
task (str): The name of the task to run. (e.g. 'BackupDatabase')
@ -666,7 +666,7 @@ class PlexServer(PlexObject):
args['librarySectionID'] = librarySectionID
if mindate:
args['viewedAt>'] = int(mindate.timestamp())
key = f'/status/sessions/history/all{utils.joinArgs(args)}'
return self.fetchItems(key, maxresults=maxresults)
@ -1258,7 +1258,7 @@ class StatisticsResources(PlexObject):
@utils.registerPlexObject
class ButlerTask(PlexObject):
""" Represents a single scheduled butler task.
Attributes:
TAG (str): 'ButlerTask'
description (str): The description of the task.
@ -1291,7 +1291,7 @@ class Identity(PlexObject):
def __repr__(self):
return f"<{self.__class__.__name__}:{self.machineIdentifier}>"
def _loadData(self, data):
self._data = data
self.claimed = utils.cast(bool, data.attrib.get('claimed'))

View file

@ -23,7 +23,6 @@ you can set items to be synced to your app) you need to init some variables.
You have to fake platform/device/model because transcoding profiles are hardcoded in Plex, and you obviously have
to explicitly specify that your app supports `sync-target`.
"""
import requests
import plexapi

View file

@ -88,7 +88,7 @@ def test_history_PlexHistory(plex, movie):
movie.markPlayed()
history = plex.history()
assert len(history)
hist = history[0]
assert hist.source() == movie
assert hist.accountID
@ -106,14 +106,20 @@ def test_history_User(account, shared_username):
user = account.user(shared_username)
history = user.history()
assert isinstance(history, list)
def test_history_UserServer(account, shared_username, plex):
userSharedServer = account.user(shared_username).server(plex.friendlyName)
history = userSharedServer.history()
assert isinstance(history, list)
def test_history_UserSection(account, shared_username, plex):
userSharedServerSection = (
account.user(shared_username).server(plex.friendlyName).section("Movies")
)
history = userSharedServerSection.history()
assert isinstance(history, list)

View file

@ -611,7 +611,7 @@ def test_library_MusicSection_search(music, artist):
album.removeMood("test_search", locked=False)
album.removeCollection("test_search", locked=False)
album.removeLabel("test_search", locked=False)
track = album.track(track=1)
track.addMood("test_search")
_test_library_search(music, track)
@ -777,7 +777,7 @@ def test_library_search_exceptions(movies):
movies.search(sort="titleSort:bad")
def _test_library_search(library, obj):
def _test_library_search(library, obj): # noqa: C901
# Create & operator
AndOperator = namedtuple("AndOperator", ["key", "title"])
andOp = AndOperator("&=", "and")

View file

@ -317,7 +317,7 @@ def _test_mixins_edit_theme(obj):
obj.unlockTheme()
obj.reload()
assert "theme" not in _fields()
# Lock the theme
obj.lockTheme()
obj.reload()

View file

@ -28,6 +28,7 @@ def test_navigate_around_artist(account, plex):
print(f"Album: {album}")
print(f"Tracks: {tracks}...")
print(f"Track: {track}")
assert isinstance(albums, list), "Unable to list artist albums."
assert artist.track("As Colourful as Ever") == track, "Unable to get artist track."
assert album.track("As Colourful as Ever") == track, "Unable to get album track."
assert album.artist() == artist, "album.artist() doesn't match expected artist."

View file

@ -436,7 +436,7 @@ def test_server_system_devices(plex):
assert len(device.name) or device.name == ""
assert len(device.platform) or device.platform == ""
assert plex.systemDevice(device.id) == device
@pytest.mark.authenticated
def test_server_dashboard_bandwidth(account_plexpass, plex):

View file

@ -383,7 +383,7 @@ def test_video_Movie_download(monkeydownload, tmpdir, movie):
def test_video_Movie_videoStreams(movie):
assert movie.videoStreams()
def test_video_Movie_audioStreams(movie):
assert movie.audioStreams()
@ -418,7 +418,7 @@ def test_video_Movie_upload_select_remove_subtitle(movie, subtitle):
try:
os.remove(filepath)
except:
except OSError:
pass
@ -632,7 +632,7 @@ def test_video_Movie_batchEdits(movie):
assert movie.tagline == tagline
assert movie.studio == studio
assert not movie.fields
with pytest.raises(BadRequest):
movie.saveEdits()

View file

@ -17,7 +17,7 @@ import os
from datetime import datetime
from plexapi.server import PlexServer
TAGS = {'keep5':5, 'keep10':10, 'keep15':15, 'keepSeason':'season'}
TAGS = {'keep5': 5, 'keep10': 10, 'keep15': 15, 'keepSeason': 'season'}
datestr = lambda: datetime.now().strftime('%Y-%m-%d %H:%M:%S')
@ -40,7 +40,7 @@ def keep_episodes(show, keep):
""" Delete all but last count episodes in show. """
deleted = 0
print('%s Cleaning %s to %s episodes.' % (datestr(), show.title, keep))
sort = lambda x:x.originallyAvailableAt or x.addedAt
sort = lambda x: x.originallyAvailableAt or x.addedAt
items = sorted(show.episodes(), key=sort, reverse=True)
for episode in items[keep:]:
delete_episode(episode)

View file

@ -3,8 +3,9 @@
"""
Backup and restore the watched status of Plex libraries to a json file.
"""
import argparse, json
import argparse
from collections import defaultdict
import json
from plexapi import utils
SECTIONS = ('movie', 'show')
@ -32,7 +33,7 @@ def _item_key(item):
def _iter_sections(plex, opts):
libraries = opts.libraries.split(',') if opts.libraries else []
libraries = [l.strip().lower() for l in libraries]
libraries = [lib.strip().lower() for lib in libraries]
for section in plex.library.sections():
title = section.title.lower()
if section.type in SECTIONS and (not libraries or title in libraries):
@ -76,11 +77,11 @@ def restore_watched(plex, opts):
skey = section.title.lower()
for item in _iter_items(section):
ikey = _item_key(item)
sval = source.get(skey,{}).get(ikey)
sval = source.get(skey, {}).get(ikey)
if sval is None:
raise SystemExit('%s not found' % ikey)
if (sval is not None and item.isWatched != sval) and (not opts.watchedonly or sval):
differences[skey][ikey] = {'isWatched':sval, 'item':item}
differences[skey][ikey] = {'isWatched': sval, 'item': item}
print('Applying %s differences to destination' % len(differences))
import pprint; pprint.pprint(differences)
@ -93,7 +94,8 @@ if __name__ == '__main__':
parser.add_argument('-u', '--username', default=CONFIG.get('auth.myplex_username'), help='Plex username')
parser.add_argument('-p', '--password', default=CONFIG.get('auth.myplex_password'), help='Plex password')
parser.add_argument('-s', '--servername', help='Plex server name')
parser.add_argument('-w', '--watchedonly', default=False, action='store_true', help='Only backup or restore watched items.')
parser.add_argument('-w', '--watchedonly', default=False, action='store_true',
help='Only backup or restore watched items.')
parser.add_argument('-l', '--libraries', help='Only backup or restore the specified libraries (comma separated).')
opts = parser.parse_args()
account = utils.getMyPlexAccount(opts)

View file

@ -121,15 +121,15 @@ def setup_music(music_path, docker=False):
"Broke for free": {
"Layers": [
"1 - As Colorful As Ever.mp3",
#"02 - Knock Knock.mp3",
#"03 - Only Knows.mp3",
#"04 - If.mp3",
#"05 - Note Drop.mp3",
#"06 - Murmur.mp3",
#"07 - Spellbound.mp3",
#"08 - The Collector.mp3",
#"09 - Quit Bitching.mp3",
#"10 - A Year.mp3",
# "02 - Knock Knock.mp3",
# "03 - Only Knows.mp3",
# "04 - If.mp3",
# "05 - Note Drop.mp3",
# "06 - Murmur.mp3",
# "07 - Spellbound.mp3",
# "08 - The Collector.mp3",
# "09 - Quit Bitching.mp3",
# "10 - A Year.mp3",
]
},
@ -279,7 +279,7 @@ def add_library_section(server, section):
raise SystemExit("Timeout adding section to Plex instance.")
def create_section(server, section, opts):
def create_section(server, section, opts): # noqa: C901
processed_media = 0
expected_media_count = section.pop("expected_media_count", 0)
expected_media_type = (section["type"],)
@ -337,7 +337,7 @@ def create_section(server, section, opts):
notifier.stop()
if __name__ == "__main__":
if __name__ == "__main__": # noqa: C901
default_ip = get_default_ip()
parser = argparse.ArgumentParser(description=__doc__)
# Authentication arguments

View file

@ -23,7 +23,7 @@ def search_for_item(url=None):
servers = [s for s in account.resources() if 'server' in s.provides]
server = utils.choose('Choose a Server', servers, 'name').connect()
query = input('What are you looking for?: ')
item = []
item = []
items = [i for i in server.search(query) if i.__class__ in VALID_TYPES]
items = utils.choose('Choose result', items, lambda x: '(%s) %s' % (x.type.title(), x.title[0:60]))
@ -63,10 +63,10 @@ def get_item_from_url(url):
server = servers[0].connect()
return server.fetchItem(key)
if __name__ == '__main__':
# Command line parser
from plexapi import CONFIG
from tqdm import tqdm
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('-u', '--username', help='Your Plex username',
default=CONFIG.get('auth.myplex_username'))
@ -84,4 +84,3 @@ if __name__ == '__main__':
url = item._server.url('%s?download=1' % part.key)
filepath = utils.download(url, token=item._server._token, filename=filename, savepath=os.getcwd(),
session=item._server._session, showstatus=True)
#print(' %s' % filepath)

View file

@ -6,7 +6,14 @@ items to build a collection of attributes on each media type. The resulting list
can be compared with the current object implementation in python-plexapi to track
new attributes and deprecate old ones.
"""
import argparse, copy, pickle, plexapi, os, re, sys, time
import argparse
import copy
import pickle
import plexapi
import os
import re
import sys
import time
from os.path import abspath, dirname, join
from collections import defaultdict
from datetime import datetime
@ -296,19 +303,19 @@ class PlexAttributes():
def _safe_connect(self, elem):
try:
return elem.connect()
except:
except Exception:
return None
def _safe_reload(self, elem):
try:
elem.reload()
except:
except Exception:
pass
def _(text, color):
FMTSTR = '\033[%dm%s\033[0m'
COLORS = {'blue':34, 'cyan':36, 'green':32, 'grey':30, 'purple':35, 'red':31, 'white':37, 'yellow':33}
COLORS = {'blue': 34, 'cyan': 36, 'green': 32, 'grey': 30, 'purple': 35, 'red': 31, 'white': 37, 'yellow': 33}
return FMTSTR % (COLORS[color], text)

View file

@ -10,7 +10,7 @@ from os.path import abspath, dirname, join
from plexapi import utils
from plexapi.server import PlexServer
GROUPNAMES = {'butler':'Scheduled Task', 'dlna':'DLNA'}
GROUPNAMES = {'butler': 'Scheduled Task', 'dlna': 'DLNA'}
OUTPUT = join(dirname(dirname(abspath(__file__))), 'docs/settingslist.rst')
@ -20,7 +20,7 @@ def _setting_group(setting):
return setting.group
def _write_settings(handle, groups, group):
def _write_settings(handle, groups, group): # noqa: C901
title = GROUPNAMES.get(group, group.title())
print('\n%s Settings\n%s' % (title, '-' * (len(title) + 9)))
handle.write('%s Settings\n%s\n' % (title, '-' * (len(title) + 9)))

View file

@ -47,7 +47,7 @@ def _list_devices(account, servers):
def _test_servers(servers):
items, seen = [], set()
print('Finding Plex clients..')
listargs = [[PlexServer, s, t, None, 5] for s,t in servers.items()]
listargs = [[PlexServer, s, t, None, 5] for s, t in servers.items()]
results = utils.threaded(_connect, listargs)
for url, token, plex, runtime in results:
clients = plex.clients() if plex else []

View file

@ -41,17 +41,17 @@ def _iter_items(search):
if __name__ == '__main__':
datestr = lambda: datetime.now().strftime('%Y-%m-%d %H:%M:%S') # noqa
print('{datestr} Starting plex-markwatched script..'.format(datestr=datestr()))
print(f'{datestr()} Starting plex-markwatched script..')
plex = PlexServer()
for section in plex.library.sections():
print('{datestr} Checking {section.title} for unwatched items..'.format(datestr=datestr()))
print(f'{datestr()} Checking {section.title} for unwatched items..')
for item in _iter_items(section.search(collection='markwatched')):
if not item.isWatched:
print('{datestr} Marking {_get_title(item)} watched.'.format(datestr=datestr()))
print(f'{datestr()} Marking {_get_title(item)} watched.')
item.markWatched()
# Check all OnDeck items
print('{datestr} Checking OnDeck for unwatched items..'.format(datestr=datestr()))
print(f'{datestr()} Checking OnDeck for unwatched items.')
for item in plex.library.onDeck():
if not item.isWatched and _has_markwatched_tag(item):
print('{datestr} Marking {_get_title(item)} watched.'.format(datestr=datestr()))
print(f'{datestr()} Marking {_get_title(item)} watched.')
item.markWatched()

View file

@ -90,10 +90,9 @@ def main():
if arguments.tag:
subprocess.run(["git", "tag", str(bumped), "-m", f"Release {bumped}"])
def test_bump_version():
"""Make sure it all works."""
import pytest
assert bump_version(Version("4.7.0"), "patch") == Version("4.7.1")
assert bump_version(Version("4.7.0"), "minor") == Version("4.8.0")
assert bump_version(Version("4.7.3"), "minor") == Version("4.8.0")