mirror of
https://github.com/pkkid/python-plexapi
synced 2024-11-13 23:47:06 +00:00
Cleanup download methods (#847)
* Add utils.cleanFilename * Refactor download methods * Add option to download episodes, tracks, photos into subfolders * Update download tests * Test download keep_original_filename
This commit is contained in:
parent
34a42185ac
commit
01131c95cf
9 changed files with 153 additions and 164 deletions
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
|
|
||||||
from plexapi import library, media, utils
|
from plexapi import library, media, utils
|
||||||
|
@ -205,23 +206,20 @@ class Artist(Audio, AdvancedSettingsMixin, ArtMixin, PosterMixin, RatingMixin, S
|
||||||
""" Alias of :func:`~plexapi.audio.Artist.track`. """
|
""" Alias of :func:`~plexapi.audio.Artist.track`. """
|
||||||
return self.track(title, album, track)
|
return self.track(title, album, track)
|
||||||
|
|
||||||
def download(self, savepath=None, keep_original_name=False, **kwargs):
|
def download(self, savepath=None, keep_original_name=False, subfolders=False, **kwargs):
|
||||||
""" Downloads all tracks for the artist to the specified location.
|
""" Download all tracks from the artist. See :func:`~plexapi.base.Playable.download` for details.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
savepath (str): Title of the track to return.
|
savepath (str): Defaults to current working dir.
|
||||||
keep_original_name (bool): Set True to keep the original filename as stored in
|
keep_original_name (bool): True to keep the original filename otherwise
|
||||||
the Plex server. False will create a new filename with the format
|
a friendlier filename is generated.
|
||||||
"<Atrist> - <Album> <Track>".
|
subfolders (bool): True to separate tracks in to album folders.
|
||||||
kwargs (dict): If specified, a :func:`~plexapi.audio.Track.getStreamURL` will
|
**kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL`.
|
||||||
be returned and the additional arguments passed in will be sent to that
|
|
||||||
function. If kwargs is not specified, the media items will be downloaded
|
|
||||||
and saved to disk.
|
|
||||||
"""
|
"""
|
||||||
filepaths = []
|
filepaths = []
|
||||||
for album in self.albums():
|
for track in self.tracks():
|
||||||
for track in album.tracks():
|
_savepath = os.path.join(savepath, track.parentTitle) if subfolders else savepath
|
||||||
filepaths += track.download(savepath, keep_original_name, **kwargs)
|
filepaths += track.download(_savepath, keep_original_name, **kwargs)
|
||||||
return filepaths
|
return filepaths
|
||||||
|
|
||||||
|
|
||||||
|
@ -314,17 +312,13 @@ class Album(Audio, ArtMixin, PosterMixin, RatingMixin, UnmatchMatchMixin,
|
||||||
return self.fetchItem(self.parentKey)
|
return self.fetchItem(self.parentKey)
|
||||||
|
|
||||||
def download(self, savepath=None, keep_original_name=False, **kwargs):
|
def download(self, savepath=None, keep_original_name=False, **kwargs):
|
||||||
""" Downloads all tracks for the artist to the specified location.
|
""" Download all tracks from the album. See :func:`~plexapi.base.Playable.download` for details.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
savepath (str): Title of the track to return.
|
savepath (str): Defaults to current working dir.
|
||||||
keep_original_name (bool): Set True to keep the original filename as stored in
|
keep_original_name (bool): True to keep the original filename otherwise
|
||||||
the Plex server. False will create a new filename with the format
|
a friendlier filename is generated.
|
||||||
"<Atrist> - <Album> <Track>".
|
**kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL`.
|
||||||
kwargs (dict): If specified, a :func:`~plexapi.audio.Track.getStreamURL` will
|
|
||||||
be returned and the additional arguments passed in will be sent to that
|
|
||||||
function. If kwargs is not specified, the media items will be downloaded
|
|
||||||
and saved to disk.
|
|
||||||
"""
|
"""
|
||||||
filepaths = []
|
filepaths = []
|
||||||
for track in self.tracks():
|
for track in self.tracks():
|
||||||
|
@ -398,7 +392,8 @@ class Track(Audio, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixin,
|
||||||
|
|
||||||
def _prettyfilename(self):
|
def _prettyfilename(self):
|
||||||
""" Returns a filename for use in download. """
|
""" Returns a filename for use in download. """
|
||||||
return '%s - %s %s' % (self.grandparentTitle, self.parentTitle, self.title)
|
return '%s - %s - %s - %s' % (
|
||||||
|
self.grandparentTitle, self.parentTitle, str(self.trackNumber).zfill(2), self.title)
|
||||||
|
|
||||||
def album(self):
|
def album(self):
|
||||||
""" Return the track's :class:`~plexapi.audio.Album`. """
|
""" Return the track's :class:`~plexapi.audio.Album`. """
|
||||||
|
|
|
@ -681,34 +681,50 @@ class Playable(object):
|
||||||
client.playMedia(self)
|
client.playMedia(self)
|
||||||
|
|
||||||
def download(self, savepath=None, keep_original_name=False, **kwargs):
|
def download(self, savepath=None, keep_original_name=False, **kwargs):
|
||||||
""" Downloads this items media to the specified location. Returns a list of
|
""" Downloads the media item to the specified location. Returns a list of
|
||||||
filepaths that have been saved to disk.
|
filepaths that have been saved to disk.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
savepath (str): Title of the track to return.
|
savepath (str): Defaults to current working dir.
|
||||||
keep_original_name (bool): Set True to keep the original filename as stored in
|
keep_original_name (bool): True to keep the original filename otherwise
|
||||||
the Plex server. False will create a new filename with the format
|
a friendlier filename is generated. See filenames below.
|
||||||
"<Artist> - <Album> <Track>".
|
**kwargs (dict): Additional options passed into :func:`~plexapi.audio.Track.getStreamURL`
|
||||||
kwargs (dict): If specified, a :func:`~plexapi.audio.Track.getStreamURL` will
|
to download a transcoded stream, otherwise the media item will be downloaded
|
||||||
be returned and the additional arguments passed in will be sent to that
|
as-is and saved to disk.
|
||||||
function. If kwargs is not specified, the media items will be downloaded
|
|
||||||
and saved to disk.
|
**Filenames**
|
||||||
|
|
||||||
|
* Movie: ``<title> (<year>)``
|
||||||
|
* Episode: ``<show title> - s00e00 - <episode title>``
|
||||||
|
* Track: ``<artist title> - <album title> - 00 - <track title>``
|
||||||
|
* Photo: ``<photoalbum title> - <photo/clip title>`` or ``<photo/clip title>``
|
||||||
"""
|
"""
|
||||||
filepaths = []
|
filepaths = []
|
||||||
locations = [i for i in self.iterParts() if i]
|
parts = [i for i in self.iterParts() if i]
|
||||||
for location in locations:
|
|
||||||
filename = location.file
|
for part in parts:
|
||||||
if keep_original_name is False:
|
if not keep_original_name:
|
||||||
filename = '%s.%s' % (self._prettyfilename(), location.container)
|
filename = utils.cleanFilename('%s.%s' % (self._prettyfilename(), part.container))
|
||||||
# So this seems to be a alot slower but allows transcode.
|
else:
|
||||||
|
filename = part.file
|
||||||
|
|
||||||
if kwargs:
|
if kwargs:
|
||||||
|
# So this seems to be a alot slower but allows transcode.
|
||||||
download_url = self.getStreamURL(**kwargs)
|
download_url = self.getStreamURL(**kwargs)
|
||||||
else:
|
else:
|
||||||
download_url = self._server.url('%s?download=1' % location.key)
|
download_url = self._server.url('%s?download=1' % part.key)
|
||||||
filepath = utils.download(download_url, self._server._token, filename=filename,
|
|
||||||
savepath=savepath, session=self._server._session)
|
filepath = utils.download(
|
||||||
|
download_url,
|
||||||
|
self._server._token,
|
||||||
|
filename=filename,
|
||||||
|
savepath=savepath,
|
||||||
|
session=self._server._session
|
||||||
|
)
|
||||||
|
|
||||||
if filepath:
|
if filepath:
|
||||||
filepaths.append(filepath)
|
filepaths.append(filepath)
|
||||||
|
|
||||||
return filepaths
|
return filepaths
|
||||||
|
|
||||||
def stop(self, reason=''):
|
def stop(self, reason=''):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
|
|
||||||
from plexapi import media, utils, video
|
from plexapi import media, utils, video
|
||||||
|
@ -107,34 +108,21 @@ class Photoalbum(PlexPartialObject, ArtMixin, PosterMixin, RatingMixin):
|
||||||
""" Alias to :func:`~plexapi.photo.Photoalbum.photo`. """
|
""" Alias to :func:`~plexapi.photo.Photoalbum.photo`. """
|
||||||
return self.episode(title)
|
return self.episode(title)
|
||||||
|
|
||||||
def iterParts(self):
|
def download(self, savepath=None, keep_original_name=False, subfolders=False):
|
||||||
""" Iterates over the parts of the media item. """
|
""" Download all photos and clips from the photo ablum. See :func:`~plexapi.base.Playable.download` for details.
|
||||||
for album in self.albums():
|
|
||||||
for photo in album.photos():
|
|
||||||
for part in photo.iterParts():
|
|
||||||
yield part
|
|
||||||
|
|
||||||
def download(self, savepath=None, keep_original_name=False, showstatus=False):
|
|
||||||
""" Download photo files to specified directory.
|
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
savepath (str): Defaults to current working dir.
|
savepath (str): Defaults to current working dir.
|
||||||
keep_original_name (bool): True to keep the original file name otherwise
|
keep_original_name (bool): True to keep the original filename otherwise
|
||||||
a friendlier is generated.
|
a friendlier filename is generated.
|
||||||
showstatus(bool): Display a progressbar.
|
subfolders (bool): True to separate photos/clips in to photo album folders.
|
||||||
"""
|
"""
|
||||||
filepaths = []
|
filepaths = []
|
||||||
locations = [i for i in self.iterParts() if i]
|
for album in self.albums():
|
||||||
for location in locations:
|
_savepath = os.path.join(savepath, album.title) if subfolders else savepath
|
||||||
name = location.file
|
filepaths += album.download(_savepath, keep_original_name)
|
||||||
if not keep_original_name:
|
for photo in self.photos() + self.clips():
|
||||||
title = self.title.replace(' ', '.')
|
filepaths += photo.download(savepath, keep_original_name)
|
||||||
name = '%s.%s' % (title, location.container)
|
|
||||||
url = self._server.url('%s?download=1' % location.key)
|
|
||||||
filepath = utils.download(url, self._server._token, filename=name, showstatus=showstatus,
|
|
||||||
savepath=savepath, session=self._server._session)
|
|
||||||
if filepath:
|
|
||||||
filepaths.append(filepath)
|
|
||||||
return filepaths
|
return filepaths
|
||||||
|
|
||||||
def _getWebURL(self, base=None):
|
def _getWebURL(self, base=None):
|
||||||
|
@ -218,6 +206,12 @@ class Photo(PlexPartialObject, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixi
|
||||||
self.userRating = utils.cast(float, data.attrib.get('userRating'))
|
self.userRating = utils.cast(float, data.attrib.get('userRating'))
|
||||||
self.year = utils.cast(int, data.attrib.get('year'))
|
self.year = utils.cast(int, data.attrib.get('year'))
|
||||||
|
|
||||||
|
def _prettyfilename(self):
|
||||||
|
""" Returns a filename for use in download. """
|
||||||
|
if self.parentTitle:
|
||||||
|
return '%s - %s' % (self.parentTitle, self.title)
|
||||||
|
return self.title
|
||||||
|
|
||||||
def photoalbum(self):
|
def photoalbum(self):
|
||||||
""" Return the photo's :class:`~plexapi.photo.Photoalbum`. """
|
""" Return the photo's :class:`~plexapi.photo.Photoalbum`. """
|
||||||
return self.fetchItem(self.parentKey)
|
return self.fetchItem(self.parentKey)
|
||||||
|
@ -241,12 +235,6 @@ class Photo(PlexPartialObject, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixi
|
||||||
"""
|
"""
|
||||||
return [part.file for item in self.media for part in item.parts if part]
|
return [part.file for item in self.media for part in item.parts if part]
|
||||||
|
|
||||||
def iterParts(self):
|
|
||||||
""" Iterates over the parts of the media item. """
|
|
||||||
for item in self.media:
|
|
||||||
for part in item.parts:
|
|
||||||
yield part
|
|
||||||
|
|
||||||
def sync(self, resolution, client=None, clientId=None, limit=None, title=None):
|
def sync(self, resolution, client=None, clientId=None, limit=None, title=None):
|
||||||
""" Add current photo as sync item for specified device.
|
""" Add current photo as sync item for specified device.
|
||||||
See :func:`~plexapi.myplex.MyPlexAccount.sync` for possible exceptions.
|
See :func:`~plexapi.myplex.MyPlexAccount.sync` for possible exceptions.
|
||||||
|
@ -283,29 +271,6 @@ class Photo(PlexPartialObject, Playable, ArtUrlMixin, PosterUrlMixin, RatingMixi
|
||||||
|
|
||||||
return myplex.sync(sync_item, client=client, clientId=clientId)
|
return myplex.sync(sync_item, client=client, clientId=clientId)
|
||||||
|
|
||||||
def download(self, savepath=None, keep_original_name=False, showstatus=False):
|
|
||||||
""" Download photo files to specified directory.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
savepath (str): Defaults to current working dir.
|
|
||||||
keep_original_name (bool): True to keep the original file name otherwise
|
|
||||||
a friendlier is generated.
|
|
||||||
showstatus(bool): Display a progressbar.
|
|
||||||
"""
|
|
||||||
filepaths = []
|
|
||||||
locations = [i for i in self.iterParts() if i]
|
|
||||||
for location in locations:
|
|
||||||
name = location.file
|
|
||||||
if not keep_original_name:
|
|
||||||
title = self.title.replace(' ', '.')
|
|
||||||
name = '%s.%s' % (title, location.container)
|
|
||||||
url = self._server.url('%s?download=1' % location.key)
|
|
||||||
filepath = utils.download(url, self._server._token, filename=name, showstatus=showstatus,
|
|
||||||
savepath=savepath, session=self._server._session)
|
|
||||||
if filepath:
|
|
||||||
filepaths.append(filepath)
|
|
||||||
return filepaths
|
|
||||||
|
|
||||||
def _getWebURL(self, base=None):
|
def _getWebURL(self, base=None):
|
||||||
""" Get the Plex Web URL with the correct parameters. """
|
""" Get the Plex Web URL with the correct parameters. """
|
||||||
return self._server._buildWebURL(base=base, endpoint='details', key=self.parentKey, legacy=1)
|
return self._server._buildWebURL(base=base, endpoint='details', key=self.parentKey, legacy=1)
|
||||||
|
|
|
@ -4,7 +4,9 @@ import functools
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import string
|
||||||
import time
|
import time
|
||||||
|
import unicodedata
|
||||||
import warnings
|
import warnings
|
||||||
import zipfile
|
import zipfile
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -251,6 +253,13 @@ def toList(value, itemcast=None, delim=','):
|
||||||
return [itemcast(item) for item in value.split(delim) if item != '']
|
return [itemcast(item) for item in value.split(delim) if item != '']
|
||||||
|
|
||||||
|
|
||||||
|
def cleanFilename(filename, replace='_'):
|
||||||
|
whitelist = "-_.()[] {}{}".format(string.ascii_letters, string.digits)
|
||||||
|
cleaned_filename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore').decode()
|
||||||
|
cleaned_filename = ''.join(c if c in whitelist else replace for c in cleaned_filename)
|
||||||
|
return cleaned_filename
|
||||||
|
|
||||||
|
|
||||||
def downloadSessionImages(server, filename=None, height=150, width=150,
|
def downloadSessionImages(server, filename=None, height=150, width=150,
|
||||||
opacity=100, saturation=100): # pragma: no cover
|
opacity=100, saturation=100): # pragma: no cover
|
||||||
""" Helper to download a bif image or thumb.url from plex.server.sessions.
|
""" Helper to download a bif image or thumb.url from plex.server.sessions.
|
||||||
|
|
|
@ -357,8 +357,8 @@ class Movie(Video, Playable, AdvancedSettingsMixin, ArtMixin, PosterMixin, Ratin
|
||||||
return any(part.hasPreviewThumbnails for media in self.media for part in media.parts)
|
return any(part.hasPreviewThumbnails for media in self.media for part in media.parts)
|
||||||
|
|
||||||
def _prettyfilename(self):
|
def _prettyfilename(self):
|
||||||
# This is just for compat.
|
""" Returns a filename for use in download. """
|
||||||
return self.title
|
return '%s (%s)' % (self.title, self.year)
|
||||||
|
|
||||||
def reviews(self):
|
def reviews(self):
|
||||||
""" Returns a list of :class:`~plexapi.media.Review` objects. """
|
""" Returns a list of :class:`~plexapi.media.Review` objects. """
|
||||||
|
@ -375,32 +375,6 @@ class Movie(Video, Playable, AdvancedSettingsMixin, ArtMixin, PosterMixin, Ratin
|
||||||
data = self._server.query(self._details_key)
|
data = self._server.query(self._details_key)
|
||||||
return self.findItems(data, library.Hub, rtag='Related')
|
return self.findItems(data, library.Hub, rtag='Related')
|
||||||
|
|
||||||
def download(self, savepath=None, keep_original_name=False, **kwargs):
|
|
||||||
""" Download video files to specified directory.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
savepath (str): Defaults to current working dir.
|
|
||||||
keep_original_name (bool): True to keep the original file name otherwise
|
|
||||||
a friendlier is generated.
|
|
||||||
**kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL`.
|
|
||||||
"""
|
|
||||||
filepaths = []
|
|
||||||
locations = [i for i in self.iterParts() if i]
|
|
||||||
for location in locations:
|
|
||||||
name = location.file
|
|
||||||
if not keep_original_name:
|
|
||||||
title = self.title.replace(' ', '.')
|
|
||||||
name = '%s.%s' % (title, location.container)
|
|
||||||
if kwargs is not None:
|
|
||||||
url = self.getStreamURL(**kwargs)
|
|
||||||
else:
|
|
||||||
self._server.url('%s?download=1' % location.key)
|
|
||||||
filepath = utils.download(url, self._server._token, filename=name,
|
|
||||||
savepath=savepath, session=self._server._session)
|
|
||||||
if filepath:
|
|
||||||
filepaths.append(filepath)
|
|
||||||
return filepaths
|
|
||||||
|
|
||||||
|
|
||||||
@utils.registerPlexObject
|
@utils.registerPlexObject
|
||||||
class Show(Video, AdvancedSettingsMixin, ArtMixin, BannerMixin, PosterMixin, RatingMixin, SplitMergeMixin, UnmatchMatchMixin,
|
class Show(Video, AdvancedSettingsMixin, ArtMixin, BannerMixin, PosterMixin, RatingMixin, SplitMergeMixin, UnmatchMatchMixin,
|
||||||
|
@ -582,18 +556,20 @@ class Show(Video, AdvancedSettingsMixin, ArtMixin, BannerMixin, PosterMixin, Rat
|
||||||
""" Returns list of unwatched :class:`~plexapi.video.Episode` objects. """
|
""" Returns list of unwatched :class:`~plexapi.video.Episode` objects. """
|
||||||
return self.episodes(viewCount=0)
|
return self.episodes(viewCount=0)
|
||||||
|
|
||||||
def download(self, savepath=None, keep_original_name=False, **kwargs):
|
def download(self, savepath=None, keep_original_name=False, subfolders=False, **kwargs):
|
||||||
""" Download video files to specified directory.
|
""" Download all episodes from the show. See :func:`~plexapi.base.Playable.download` for details.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
savepath (str): Defaults to current working dir.
|
savepath (str): Defaults to current working dir.
|
||||||
keep_original_name (bool): True to keep the original file name otherwise
|
keep_original_name (bool): True to keep the original filename otherwise
|
||||||
a friendlier is generated.
|
a friendlier filename is generated.
|
||||||
|
subfolders (bool): True to separate episodes in to season folders.
|
||||||
**kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL`.
|
**kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL`.
|
||||||
"""
|
"""
|
||||||
filepaths = []
|
filepaths = []
|
||||||
for episode in self.episodes():
|
for episode in self.episodes():
|
||||||
filepaths += episode.download(savepath, keep_original_name, **kwargs)
|
_savepath = os.path.join(savepath, 'Season %s' % str(episode.seasonNumber).zfill(2)) if subfolders else savepath
|
||||||
|
filepaths += episode.download(_savepath, keep_original_name, **kwargs)
|
||||||
return filepaths
|
return filepaths
|
||||||
|
|
||||||
|
|
||||||
|
@ -714,12 +690,12 @@ class Season(Video, ArtMixin, PosterMixin, RatingMixin, CollectionMixin):
|
||||||
return self.episodes(viewCount=0)
|
return self.episodes(viewCount=0)
|
||||||
|
|
||||||
def download(self, savepath=None, keep_original_name=False, **kwargs):
|
def download(self, savepath=None, keep_original_name=False, **kwargs):
|
||||||
""" Download video files to specified directory.
|
""" Download all episodes from the season. See :func:`~plexapi.base.Playable.download` for details.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
savepath (str): Defaults to current working dir.
|
savepath (str): Defaults to current working dir.
|
||||||
keep_original_name (bool): True to keep the original file name otherwise
|
keep_original_name (bool): True to keep the original filename otherwise
|
||||||
a friendlier is generated.
|
a friendlier filename is generated.
|
||||||
**kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL`.
|
**kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.getStreamURL`.
|
||||||
"""
|
"""
|
||||||
filepaths = []
|
filepaths = []
|
||||||
|
@ -839,8 +815,8 @@ class Episode(Video, Playable, ArtMixin, PosterMixin, RatingMixin,
|
||||||
] if p])
|
] if p])
|
||||||
|
|
||||||
def _prettyfilename(self):
|
def _prettyfilename(self):
|
||||||
""" Returns a human friendly filename. """
|
""" Returns a filename for use in download. """
|
||||||
return '%s.%s' % (self.grandparentTitle.replace(' ', '.'), self.seasonEpisode)
|
return '%s - %s - %s' % (self.grandparentTitle, self.seasonEpisode, self.title)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def actors(self):
|
def actors(self):
|
||||||
|
@ -953,6 +929,7 @@ class Clip(Video, Playable, ArtUrlMixin, PosterUrlMixin):
|
||||||
return [part.file for part in self.iterParts() if part]
|
return [part.file for part in self.iterParts() if part]
|
||||||
|
|
||||||
def _prettyfilename(self):
|
def _prettyfilename(self):
|
||||||
|
""" Returns a filename for use in download. """
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
|
||||||
|
@ -968,4 +945,5 @@ class Extra(Clip):
|
||||||
self.librarySectionTitle = parent.librarySectionTitle
|
self.librarySectionTitle = parent.librarySectionTitle
|
||||||
|
|
||||||
def _prettyfilename(self):
|
def _prettyfilename(self):
|
||||||
|
""" Returns a filename for use in download. """
|
||||||
return '%s (%s)' % (self.title, self.subtype)
|
return '%s (%s)' % (self.title, self.subtype)
|
||||||
|
|
|
@ -117,7 +117,7 @@ def test_audio_Artist_media_tags(artist):
|
||||||
test_media.tag_style(artist)
|
test_media.tag_style(artist)
|
||||||
|
|
||||||
|
|
||||||
def test_video_Artist_PlexWebURL(plex, artist):
|
def test_audio_Artist_PlexWebURL(plex, artist):
|
||||||
url = artist.getWebURL()
|
url = artist.getWebURL()
|
||||||
assert url.startswith('https://app.plex.tv/desktop')
|
assert url.startswith('https://app.plex.tv/desktop')
|
||||||
assert plex.machineIdentifier in url
|
assert plex.machineIdentifier in url
|
||||||
|
@ -224,7 +224,7 @@ def test_audio_Album_media_tags(album):
|
||||||
test_media.tag_style(album)
|
test_media.tag_style(album)
|
||||||
|
|
||||||
|
|
||||||
def test_video_Album_PlexWebURL(plex, album):
|
def test_audio_Album_PlexWebURL(plex, album):
|
||||||
url = album.getWebURL()
|
url = album.getWebURL()
|
||||||
assert url.startswith('https://app.plex.tv/desktop')
|
assert url.startswith('https://app.plex.tv/desktop')
|
||||||
assert plex.machineIdentifier in url
|
assert plex.machineIdentifier in url
|
||||||
|
@ -375,7 +375,7 @@ def test_audio_Track_media_tags(track):
|
||||||
test_media.tag_mood(track)
|
test_media.tag_mood(track)
|
||||||
|
|
||||||
|
|
||||||
def test_video_Track_PlexWebURL(plex, track):
|
def test_audio_Track_PlexWebURL(plex, track):
|
||||||
url = track.getWebURL()
|
url = track.getWebURL()
|
||||||
assert url.startswith('https://app.plex.tv/desktop')
|
assert url.startswith('https://app.plex.tv/desktop')
|
||||||
assert plex.machineIdentifier in url
|
assert plex.machineIdentifier in url
|
||||||
|
@ -390,16 +390,20 @@ def test_audio_Audio_section(artist, album, track):
|
||||||
assert track.section().key == album.section().key == artist.section().key
|
assert track.section().key == album.section().key == artist.section().key
|
||||||
|
|
||||||
|
|
||||||
|
def test_audio_Artist_download(monkeydownload, tmpdir, artist):
|
||||||
|
total = len(artist.tracks())
|
||||||
|
filepaths = artist.download(savepath=str(tmpdir))
|
||||||
|
assert len(filepaths) == total
|
||||||
|
subfolders = artist.download(savepath=str(tmpdir), subfolders=True)
|
||||||
|
assert len(subfolders) == total
|
||||||
|
|
||||||
|
|
||||||
|
def test_audio_Album_download(monkeydownload, tmpdir, album):
|
||||||
|
total = len(album.tracks())
|
||||||
|
filepaths = album.download(savepath=str(tmpdir))
|
||||||
|
assert len(filepaths) == total
|
||||||
|
|
||||||
|
|
||||||
def test_audio_Track_download(monkeydownload, tmpdir, track):
|
def test_audio_Track_download(monkeydownload, tmpdir, track):
|
||||||
f = track.download(savepath=str(tmpdir))
|
filepaths = track.download(savepath=str(tmpdir))
|
||||||
assert f
|
assert len(filepaths) == 1
|
||||||
|
|
||||||
|
|
||||||
def test_audio_album_download(monkeydownload, album, tmpdir):
|
|
||||||
f = album.download(savepath=str(tmpdir))
|
|
||||||
assert len(f) == 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_audio_Artist_download(monkeydownload, artist, tmpdir):
|
|
||||||
f = artist.download(savepath=str(tmpdir))
|
|
||||||
assert len(f) == 1
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ def test_photo_Photoalbum_mixins_rating(photoalbum):
|
||||||
test_mixins.edit_rating(photoalbum)
|
test_mixins.edit_rating(photoalbum)
|
||||||
|
|
||||||
|
|
||||||
def test_video_Photoalbum_PlexWebURL(plex, photoalbum):
|
def test_photo_Photoalbum_PlexWebURL(plex, photoalbum):
|
||||||
url = photoalbum.getWebURL()
|
url = photoalbum.getWebURL()
|
||||||
assert url.startswith('https://app.plex.tv/desktop')
|
assert url.startswith('https://app.plex.tv/desktop')
|
||||||
assert plex.machineIdentifier in url
|
assert plex.machineIdentifier in url
|
||||||
|
@ -48,10 +48,27 @@ def test_photo_Photo_media_tags(photo):
|
||||||
test_media.tag_tag(photo)
|
test_media.tag_tag(photo)
|
||||||
|
|
||||||
|
|
||||||
def test_video_Photo_PlexWebURL(plex, photo):
|
def test_photo_Photo_PlexWebURL(plex, photo):
|
||||||
url = photo.getWebURL()
|
url = photo.getWebURL()
|
||||||
assert url.startswith('https://app.plex.tv/desktop')
|
assert url.startswith('https://app.plex.tv/desktop')
|
||||||
assert plex.machineIdentifier in url
|
assert plex.machineIdentifier in url
|
||||||
assert 'details' in url
|
assert 'details' in url
|
||||||
assert quote_plus(photo.parentKey) in url
|
assert quote_plus(photo.parentKey) in url
|
||||||
assert 'legacy=1' in url
|
assert 'legacy=1' in url
|
||||||
|
|
||||||
|
|
||||||
|
def test_photo_Photoalbum_download(monkeydownload, tmpdir, photoalbum):
|
||||||
|
total = 0
|
||||||
|
for album in photoalbum.albums():
|
||||||
|
total += len(album.photos()) + len(album.clips())
|
||||||
|
total += len(photoalbum.photos())
|
||||||
|
total += len(photoalbum.clips())
|
||||||
|
filepaths = photoalbum.download(savepath=str(tmpdir))
|
||||||
|
assert len(filepaths) == total
|
||||||
|
subfolders = photoalbum.download(savepath=str(tmpdir), subfolders=True)
|
||||||
|
assert len(subfolders) == total
|
||||||
|
|
||||||
|
|
||||||
|
def test_photo_Photo_download(monkeydownload, tmpdir, photo):
|
||||||
|
filepaths = photo.download(savepath=str(tmpdir))
|
||||||
|
assert len(filepaths) == 1
|
||||||
|
|
|
@ -78,7 +78,8 @@ def test_utils_download(plex, episode):
|
||||||
url = episode.getStreamURL()
|
url = episode.getStreamURL()
|
||||||
locations = episode.locations[0]
|
locations = episode.locations[0]
|
||||||
session = episode._server._session
|
session = episode._server._session
|
||||||
assert utils.download(url, plex._token, filename=locations, mocked=True)
|
assert utils.download(
|
||||||
|
url, plex._token, filename=locations, mocked=True)
|
||||||
assert utils.download(
|
assert utils.download(
|
||||||
url, plex._token, filename=locations, session=session, mocked=True
|
url, plex._token, filename=locations, session=session, mocked=True
|
||||||
)
|
)
|
||||||
|
@ -87,7 +88,6 @@ def test_utils_download(plex, episode):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_millisecondToHumanstr():
|
def test_millisecondToHumanstr():
|
||||||
res = utils.millisecondToHumanstr(1000)
|
res = utils.millisecondToHumanstr(1000)
|
||||||
assert res == "00:00:01.0000"
|
assert res == "00:00:01.0000"
|
||||||
|
|
|
@ -143,10 +143,14 @@ def test_video_Movie_iterParts(movie):
|
||||||
|
|
||||||
|
|
||||||
def test_video_Movie_download(monkeydownload, tmpdir, movie):
|
def test_video_Movie_download(monkeydownload, tmpdir, movie):
|
||||||
filepaths1 = movie.download(savepath=str(tmpdir))
|
filepaths = movie.download(savepath=str(tmpdir))
|
||||||
assert len(filepaths1) >= 1
|
assert len(filepaths) == 1
|
||||||
filepaths2 = movie.download(savepath=str(tmpdir), videoResolution="500x300")
|
with_resolution = movie.download(
|
||||||
assert len(filepaths2) >= 1
|
savepath=str(tmpdir), keep_original_filename=True, videoResolution="500x300"
|
||||||
|
)
|
||||||
|
assert len(with_resolution) == 1
|
||||||
|
filename = os.path.basename(movie.media[0].parts[0].file)
|
||||||
|
assert filename in with_resolution[0]
|
||||||
|
|
||||||
|
|
||||||
def test_video_Movie_subtitlestreams(movie):
|
def test_video_Movie_subtitlestreams(movie):
|
||||||
|
@ -728,24 +732,25 @@ def test_video_Show_episodes(tvshows):
|
||||||
|
|
||||||
|
|
||||||
def test_video_Show_download(monkeydownload, tmpdir, show):
|
def test_video_Show_download(monkeydownload, tmpdir, show):
|
||||||
episodes = show.episodes()
|
total = len(show.episodes())
|
||||||
filepaths = show.download(savepath=str(tmpdir))
|
filepaths = show.download(savepath=str(tmpdir))
|
||||||
assert len(filepaths) == len(episodes)
|
assert len(filepaths) == total
|
||||||
|
subfolders = show.download(savepath=str(tmpdir), subfolders=True)
|
||||||
|
assert len(subfolders) == total
|
||||||
|
|
||||||
|
|
||||||
def test_video_Season_download(monkeydownload, tmpdir, show):
|
def test_video_Season_download(monkeydownload, tmpdir, show):
|
||||||
season = show.season("Season 1")
|
season = show.season(1)
|
||||||
|
total = len(season.episodes())
|
||||||
filepaths = season.download(savepath=str(tmpdir))
|
filepaths = season.download(savepath=str(tmpdir))
|
||||||
assert len(filepaths) >= 4
|
assert len(filepaths) == total
|
||||||
|
|
||||||
|
|
||||||
def test_video_Episode_download(monkeydownload, tmpdir, episode):
|
def test_video_Episode_download(monkeydownload, tmpdir, episode):
|
||||||
f = episode.download(savepath=str(tmpdir))
|
filepaths = episode.download(savepath=str(tmpdir))
|
||||||
assert len(f) == 1
|
assert len(filepaths) == 1
|
||||||
with_sceen_size = episode.download(
|
with_resolution = episode.download(savepath=str(tmpdir), videoResolution="500x300")
|
||||||
savepath=str(tmpdir), **{"videoResolution": "500x300"}
|
assert len(with_resolution) == 1
|
||||||
)
|
|
||||||
assert len(with_sceen_size) == 1
|
|
||||||
|
|
||||||
|
|
||||||
# Analyze seems to fail intermittently
|
# Analyze seems to fail intermittently
|
||||||
|
|
Loading…
Reference in a new issue