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:
JonnyWong16 2021-11-20 14:16:58 -08:00 committed by GitHub
parent 34a42185ac
commit 01131c95cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 153 additions and 164 deletions

View file

@ -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`. """

View file

@ -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=''):

View file

@ -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)

View file

@ -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.

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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