Source code for upsies.utils.disc.playlist

"""
Normalized representation of a sequence of :mod:`~.disc.bluray` or :mod:`~.disc.dvd` video files
"""

import functools

from ... import utils


[docs] class Playlist: """Representation of multiple videos in a sequence""" def __init__(self, items, filepath, *, discpath=None, duration=None, is_main=False): self._items = tuple(items) self._filepath = str(filepath) self._discpath = str(discpath) if discpath else None self._duration = duration self.is_main = is_main @functools.cached_property def type(self): """ Underlying disc format "bluray" if :attr:`filename` has the file extension "mpls". "dvd" if :attr:`filename` has the file extension "ifo". Empty string otherwise. Matching is done case-insensitively. """ file_extension = utils.fs.file_extension(self._filepath).lower() if file_extension == 'mpls': return 'bluray' elif file_extension == 'ifo': return 'dvd' else: return '' @functools.cached_property def label(self): """ Human-readable string that serves as a unique playlist ID Combination of :attr:`discpath` (if provided) and :attr:`filename`. """ if self.discname: return f'{self.discname}[{utils.fs.strip_extension(self.filename)}]' else: return utils.fs.strip_extension(self.filename) @property def items(self): """Sequence of file paths in the playlist""" return self._items @functools.cached_property def largest_item(self): """Largest file in :attr:`items`""" items_sorted = sorted( self.items, key=lambda filepath: (utils.fs.file_size(filepath) or 0), reverse=True, ) return items_sorted[0] @property def filepath(self): """Path of the playlist file (e.g. MPLS or IFO file)""" return self._filepath @property def filename(self): """Basename of :attr:`filepath`""" return utils.fs.basename(self._filepath) @property def discpath(self): """ Path of the directory that contains :attr:`filepath` Usually, this ia directory path that contains a "BDMV" or "VIDEO_TS" directory. """ return self._discpath @property def discname(self): """Basename of :attr:`discpath`""" if self.discpath: return utils.fs.basename(self.discpath) @functools.cached_property def size(self): """Combined file size of all :attr:`items` as :attr:`utils.types.Bytes`""" return utils.types.Bytes(sum( # file_size() returns `None` by default. (utils.fs.file_size(item) or 0) for item in self.items )) @functools.cached_property def duration(self): """Combined runtime of :attr:`items` in seconds""" if self._duration is None: self._duration = sum( utils.mediainfo.get_duration(item) for item in self.items ) return utils.types.Timestamp(self._duration) @property def is_main(self): """ Simple boolean mark to track main playlists This property is mutable. """ return self._is_main @is_main.setter def is_main(self, is_main): self._is_main = bool(is_main) # Attributes that make an instance unique. _id_attributes = ('discpath', 'filepath', 'items', 'duration') @property def id(self): """ Sequence of values that make this instance unique Instances with the same :attr:`id` are equal. """ return tuple( getattr(self, attr) for attr in self._id_attributes ) def __eq__(self, other): if isinstance(other, type(self)): return self.id == other.id else: return NotImplemented def __hash__(self): return hash(self.id) def __repr__(self): return f'<{type(self).__name__} {self.label}>'
[docs] def mark_main_playlists(playlists): """Find the main playlist(s) and set their :attr:`~.Playlist.is_main` property to ``True``""" if not playlists: return # Find the largest playlist first. We go by size instead of runtime because the runtime is often # wrong while size always goes up as video complexity/quality increases, and the main playlist # is most likely of the highest complexity/quality. playlists_sorted = sorted(playlists, key=lambda playlist: playlist.size) largest_playlist = playlists_sorted[-1] # Main playlists have a similar duration as the largest playlist. duration_min = largest_playlist.duration * 0.7 duration_max = largest_playlist.duration * 1.3 for playlist in playlists: playlist.is_main = bool(duration_min <= playlist.duration <= duration_max)