mirror of
https://github.com/nathom/streamrip.git
synced 2025-05-24 20:14:42 -04:00
Start Media ABC implementation
Signed-off-by: nathom <nathanthomas707@gmail.com>
This commit is contained in:
parent
4142e1c831
commit
489402165c
1 changed files with 80 additions and 31 deletions
|
@ -55,6 +55,8 @@ TYPE_REGEXES = {
|
||||||
|
|
||||||
|
|
||||||
class Media(abc.ABC):
|
class Media(abc.ABC):
|
||||||
|
__metaclass__ = abc.ABCMeta
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def download(self, **kwargs):
|
def download(self, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
@ -67,11 +69,6 @@ class Media(abc.ABC):
|
||||||
def tag(self, **kwargs):
|
def tag(self, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@property
|
|
||||||
@abc.abstractmethod
|
|
||||||
def type(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def convert(self, **kwargs):
|
def convert(self, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
@ -84,6 +81,29 @@ class Media(abc.ABC):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def type(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# @property
|
||||||
|
# @abc.abstractmethod
|
||||||
|
# def id(self):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# @id.setter
|
||||||
|
# def id(self, other):
|
||||||
|
# pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abc.abstractmethod
|
||||||
|
def downloaded_ids(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@downloaded_ids.setter
|
||||||
|
def downloaded_ids(self, other):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Track(Media):
|
class Track(Media):
|
||||||
"""Represents a downloadable track.
|
"""Represents a downloadable track.
|
||||||
|
@ -102,6 +122,19 @@ class Track(Media):
|
||||||
>>> t.tag()
|
>>> t.tag()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
id = None
|
||||||
|
downloaded_ids: set = set()
|
||||||
|
downloaded: bool = False
|
||||||
|
tagged: bool = False
|
||||||
|
converted: bool = False
|
||||||
|
|
||||||
|
quality: int
|
||||||
|
folder: str
|
||||||
|
meta: TrackMetadata
|
||||||
|
|
||||||
|
final_path: str
|
||||||
|
container: str
|
||||||
|
|
||||||
def __init__(self, client: Client, **kwargs):
|
def __init__(self, client: Client, **kwargs):
|
||||||
"""Create a track object.
|
"""Create a track object.
|
||||||
|
|
||||||
|
@ -117,22 +150,13 @@ class Track(Media):
|
||||||
:type meta: Optional[TrackMetadata]
|
:type meta: Optional[TrackMetadata]
|
||||||
:param kwargs: id, filepath_format, meta, quality, folder
|
:param kwargs: id, filepath_format, meta, quality, folder
|
||||||
"""
|
"""
|
||||||
|
logger.debug(kwargs)
|
||||||
|
|
||||||
self.client = client
|
self.client = client
|
||||||
self.id = None
|
|
||||||
self.__dict__.update(kwargs)
|
self.__dict__.update(kwargs)
|
||||||
|
|
||||||
self.downloaded = False
|
|
||||||
self.tagged = False
|
|
||||||
self.converted = False
|
|
||||||
self.part_of_tracklist = kwargs.get("part_of_tracklist", False)
|
self.part_of_tracklist = kwargs.get("part_of_tracklist", False)
|
||||||
|
|
||||||
self.final_path: str
|
|
||||||
self.container: str
|
|
||||||
|
|
||||||
# TODO: find better solution
|
|
||||||
for attr in ("quality", "folder", "meta"):
|
|
||||||
setattr(self, attr, None)
|
|
||||||
|
|
||||||
if isinstance(kwargs.get("meta"), TrackMetadata):
|
if isinstance(kwargs.get("meta"), TrackMetadata):
|
||||||
self.meta = kwargs["meta"]
|
self.meta = kwargs["meta"]
|
||||||
|
|
||||||
|
@ -373,7 +397,7 @@ class Track(Media):
|
||||||
|
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
return click.style(f"Track {int(self.meta.tracknumber):02}", fg="blue")
|
return click.style(f"Track {self.meta.tracknumber:02}", fg="blue")
|
||||||
|
|
||||||
def download_cover(self, width=999999, height=999999):
|
def download_cover(self, width=999999, height=999999):
|
||||||
"""Download the cover art, if cover_url is given."""
|
"""Download the cover art, if cover_url is given."""
|
||||||
|
@ -645,7 +669,7 @@ class Track(Media):
|
||||||
:param keys:
|
:param keys:
|
||||||
:param default:
|
:param default:
|
||||||
"""
|
"""
|
||||||
return safe_get(self.meta, *keys, default=default)
|
return safe_get(self.meta, *keys, default=default) # type: ignore
|
||||||
|
|
||||||
def set(self, key, val):
|
def set(self, key, val):
|
||||||
"""Set attribute `key` to `val`.
|
"""Set attribute `key` to `val`.
|
||||||
|
@ -821,7 +845,7 @@ class YoutubeVideo(Media):
|
||||||
:param url: URL to the youtube video.
|
:param url: URL to the youtube video.
|
||||||
:type url: str
|
:type url: str
|
||||||
"""
|
"""
|
||||||
self.url = url
|
self.id = url
|
||||||
self.client = self.DummyClient()
|
self.client = self.DummyClient()
|
||||||
|
|
||||||
def download(
|
def download(
|
||||||
|
@ -842,7 +866,7 @@ class YoutubeVideo(Media):
|
||||||
:type youtube_video_downloads_folder: str
|
:type youtube_video_downloads_folder: str
|
||||||
:param kwargs:
|
:param kwargs:
|
||||||
"""
|
"""
|
||||||
click.secho(f"Downloading url {self.url}", fg="blue")
|
click.secho(f"Downloading url {self.id}", fg="blue")
|
||||||
filename_formatter = "%(track_number)s.%(track)s.%(container)s"
|
filename_formatter = "%(track_number)s.%(track)s.%(container)s"
|
||||||
filename = os.path.join(parent_folder, filename_formatter)
|
filename = os.path.join(parent_folder, filename_formatter)
|
||||||
|
|
||||||
|
@ -857,7 +881,7 @@ class YoutubeVideo(Media):
|
||||||
"--embed-thumbnail",
|
"--embed-thumbnail",
|
||||||
"-o",
|
"-o",
|
||||||
filename,
|
filename,
|
||||||
self.url,
|
self.id,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -872,7 +896,7 @@ class YoutubeVideo(Media):
|
||||||
youtube_video_downloads_folder,
|
youtube_video_downloads_folder,
|
||||||
"%(title)s.%(container)s",
|
"%(title)s.%(container)s",
|
||||||
),
|
),
|
||||||
self.url,
|
self.id,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
pv.wait()
|
pv.wait()
|
||||||
|
@ -1180,6 +1204,10 @@ class Tracklist(list):
|
||||||
def type(self) -> str:
|
def type(self) -> str:
|
||||||
return self.__class__.__name__.lower()
|
return self.__class__.__name__.lower()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def downloaded_ids(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
"""Get an item if key is int, otherwise get an attr.
|
"""Get an item if key is int, otherwise get an attr.
|
||||||
|
|
||||||
|
@ -1207,7 +1235,7 @@ class Tracklist(list):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class Album(Tracklist):
|
class Album(Tracklist, Media):
|
||||||
"""Represents a downloadable album.
|
"""Represents a downloadable album.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
@ -1218,6 +1246,9 @@ class Album(Tracklist):
|
||||||
>>> album.download()
|
>>> album.download()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
downloaded_ids: set = set()
|
||||||
|
id: str
|
||||||
|
|
||||||
def __init__(self, client: Client, **kwargs):
|
def __init__(self, client: Client, **kwargs):
|
||||||
"""Create a new Album object.
|
"""Create a new Album object.
|
||||||
|
|
||||||
|
@ -1302,11 +1333,12 @@ class Album(Tracklist):
|
||||||
), f"Invalid cover size. Must be in {self.cover_urls.keys()}"
|
), f"Invalid cover size. Must be in {self.cover_urls.keys()}"
|
||||||
|
|
||||||
embed_cover_url = self.cover_urls[embed_cover_size]
|
embed_cover_url = self.cover_urls[embed_cover_size]
|
||||||
if embed_cover_url is not None:
|
if not os.path.exists(cover_path):
|
||||||
tqdm_download(embed_cover_url, cover_path)
|
if embed_cover_url is not None:
|
||||||
else: # sometimes happens with Deezer
|
tqdm_download(embed_cover_url, cover_path)
|
||||||
cover_url = [u for u in self.cover_urls.values() if u][0]
|
else: # sometimes happens with Deezer
|
||||||
tqdm_download(cover_url, cover_path)
|
cover_url = [u for u in self.cover_urls.values() if u][0]
|
||||||
|
tqdm_download(cover_url, cover_path)
|
||||||
|
|
||||||
hires_cov_path = os.path.join(self.folder, "cover.jpg")
|
hires_cov_path = os.path.join(self.folder, "cover.jpg")
|
||||||
if kwargs.get("keep_hires_cover", True) and not os.path.exists(hires_cov_path):
|
if kwargs.get("keep_hires_cover", True) and not os.path.exists(hires_cov_path):
|
||||||
|
@ -1375,6 +1407,8 @@ class Album(Tracklist):
|
||||||
embed_cover=kwargs.get("embed_cover", True),
|
embed_cover=kwargs.get("embed_cover", True),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.downloaded_ids.add(item.id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_get_resp(resp: dict, client: Client) -> TrackMetadata:
|
def _parse_get_resp(resp: dict, client: Client) -> TrackMetadata:
|
||||||
"""Parse information from a client.get(query, 'album') call.
|
"""Parse information from a client.get(query, 'album') call.
|
||||||
|
@ -1499,7 +1533,7 @@ class Album(Tracklist):
|
||||||
return hash(self.id)
|
return hash(self.id)
|
||||||
|
|
||||||
|
|
||||||
class Playlist(Tracklist):
|
class Playlist(Tracklist, Media):
|
||||||
"""Represents a downloadable playlist.
|
"""Represents a downloadable playlist.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
@ -1509,6 +1543,9 @@ class Playlist(Tracklist):
|
||||||
>>> pl.download()
|
>>> pl.download()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
id = None
|
||||||
|
downloaded_ids: set = set()
|
||||||
|
|
||||||
def __init__(self, client: Client, **kwargs):
|
def __init__(self, client: Client, **kwargs):
|
||||||
"""Create a new Playlist object.
|
"""Create a new Playlist object.
|
||||||
|
|
||||||
|
@ -1519,6 +1556,7 @@ class Playlist(Tracklist):
|
||||||
"""
|
"""
|
||||||
self.name: str
|
self.name: str
|
||||||
self.client = client
|
self.client = client
|
||||||
|
logger.debug(kwargs)
|
||||||
|
|
||||||
for k, v in kwargs.items():
|
for k, v in kwargs.items():
|
||||||
setattr(self, k, v)
|
setattr(self, k, v)
|
||||||
|
@ -1667,6 +1705,8 @@ class Playlist(Tracklist):
|
||||||
audio["TRACKNUMBER"] = f"{item['tracknumber']:02}"
|
audio["TRACKNUMBER"] = f"{item['tracknumber']:02}"
|
||||||
audio.save()
|
audio.save()
|
||||||
|
|
||||||
|
self.downloaded_ids.add(item.id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _parse_get_resp(item: dict, client: Client) -> dict:
|
def _parse_get_resp(item: dict, client: Client) -> dict:
|
||||||
"""Parse information from a search result returned by a client.search call.
|
"""Parse information from a search result returned by a client.search call.
|
||||||
|
@ -1717,6 +1757,9 @@ class Playlist(Tracklist):
|
||||||
"""
|
"""
|
||||||
return f"<Playlist: {self.name}>"
|
return f"<Playlist: {self.name}>"
|
||||||
|
|
||||||
|
def tag(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
"""Return a readable string representation of this track.
|
"""Return a readable string representation of this track.
|
||||||
|
|
||||||
|
@ -1725,7 +1768,7 @@ class Playlist(Tracklist):
|
||||||
return f"{self.name} ({len(self)} tracks)"
|
return f"{self.name} ({len(self)} tracks)"
|
||||||
|
|
||||||
|
|
||||||
class Artist(Tracklist):
|
class Artist(Tracklist, Media):
|
||||||
"""Represents a downloadable artist.
|
"""Represents a downloadable artist.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
@ -1744,6 +1787,7 @@ class Artist(Tracklist):
|
||||||
:param kwargs:
|
:param kwargs:
|
||||||
"""
|
"""
|
||||||
self.client = client
|
self.client = client
|
||||||
|
self.downloaded_ids: set = set()
|
||||||
|
|
||||||
for k, v in kwargs.items():
|
for k, v in kwargs.items():
|
||||||
setattr(self, k, v)
|
setattr(self, k, v)
|
||||||
|
@ -1842,7 +1886,11 @@ class Artist(Tracklist):
|
||||||
:param kwargs:
|
:param kwargs:
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
item.load_meta()
|
try:
|
||||||
|
item.load_meta()
|
||||||
|
except NonStreamable as e:
|
||||||
|
e.print(item)
|
||||||
|
return
|
||||||
|
|
||||||
kwargs.pop("parent_folder")
|
kwargs.pop("parent_folder")
|
||||||
# always an Album
|
# always an Album
|
||||||
|
@ -1850,6 +1898,7 @@ class Artist(Tracklist):
|
||||||
parent_folder=self.folder,
|
parent_folder=self.folder,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
self.downloaded_ids.update(item.downloaded_ids)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self) -> str:
|
def title(self) -> str:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue