diff --git a/src/music_kraken/cli/options/invidious/shell.py b/src/music_kraken/cli/options/invidious/shell.py index 1d83d9c..131a9d8 100644 --- a/src/music_kraken/cli/options/invidious/shell.py +++ b/src/music_kraken/cli/options/invidious/shell.py @@ -4,7 +4,6 @@ from dataclasses import dataclass from collections import defaultdict from ....utils import config, write -from ....utils import exception INSTANCES_ENDPOINT = "https://api.invidious.io/instances.json" diff --git a/src/music_kraken/pages/youtube.py b/src/music_kraken/pages/youtube.py index b23b1f8..1a84eef 100644 --- a/src/music_kraken/pages/youtube.py +++ b/src/music_kraken/pages/youtube.py @@ -1,6 +1,8 @@ from typing import List, Optional, Type -from urllib.parse import urlparse +from urllib.parse import urlparse, urlunparse, parse_qs import logging +from dataclasses import dataclass +from enum import Enum from ..objects import Source, DatabaseObject from .abstract import Page @@ -15,16 +17,111 @@ from ..objects import ( ) from ..connection import Connection from ..utils.support_classes import DownloadResult -from ..utils.shared import YOUTUBE_LOGGER +from ..utils.shared import YOUTUBE_LOGGER, INVIDIOUS_INSTANCE """ -- https://y.com.sb/api/v1/search?q=Zombiez+-+Topic&page=1&date=none&type=channel&duration=none&sort=relevance -- https://y.com.sb/api/v1/channels/playlists/UCV0Ntl3lVR7xDXKoCU6uUXA -- https://y.com.sb/api/v1/playlists/OLAK5uy_kcUBiDv5ATbl-R20OjNaZ5G28XFanQOmM +- https://yt.artemislena.eu/api/v1/search?q=Zombiez+-+Topic&page=1&date=none&type=channel&duration=none&sort=relevance +- https://yt.artemislena.eu/api/v1/channels/playlists/UCV0Ntl3lVR7xDXKoCU6uUXA +- https://yt.artemislena.eu/api/v1/playlists/OLAK5uy_kcUBiDv5ATbl-R20OjNaZ5G28XFanQOmM +- https://yt.artemislena.eu/api/v1/videos/SULFl39UjgY """ +def get_invidious_url(path: str = "", query: str = "", fragment: str = "") -> str: + return urlunparse((INVIDIOUS_INSTANCE.scheme, INVIDIOUS_INSTANCE.netloc, path, query, fragment)) + + +class YouTubeUrlType(Enum): + CHANNEL = "channel" + PLAYLIST = "playlist" + VIDEO = "watch" + NONE = "" + + +class YouTubeUrl: + """ + Artist + https://yt.artemislena.eu/channel/UCV0Ntl3lVR7xDXKoCU6uUXA + https://www.youtube.com/channel/UCV0Ntl3lVR7xDXKoCU6uUXA + + Release + https://yt.artemislena.eu/playlist?list=OLAK5uy_nEg5joAyFjHBPwnS_ADHYtgSqAjFMQKLw + https://www.youtube.com/playlist?list=OLAK5uy_nEg5joAyFjHBPwnS_ADHYtgSqAjFMQKLw + + Track + https://yt.artemislena.eu/watch?v=SULFl39UjgY&list=OLAK5uy_nEg5joAyFjHBPwnS_ADHYtgSqAjFMQKLw&index=1 + https://www.youtube.com/watch?v=SULFl39UjgY + """ + + def __init__(self, url: str) -> None: + """ + Raises Index exception for wrong url, and value error for not found enum type + """ + self.id = "" + parsed = urlparse(url=url) + + self.url_type: YouTubeUrlType + + type_frag_list = parsed.path.split("/") + if len(type_frag_list) < 2: + self.url_type = YouTubeUrlType.NONE + else: + try: + self.url_type = YouTubeUrlType(type_frag_list[1].strip()) + except ValueError: + self.url_type = YouTubeUrlType.NONE + + if self.url_type == YouTubeUrlType.CHANNEL: + if len(type_frag_list) < 3: + self.couldnt_find_id(url) + else: + self.id = type_frag_list[2] + + elif self.url_type == YouTubeUrlType.PLAYLIST: + query_stuff = parse_qs(parsed.query) + if "list" not in query_stuff: + self.couldnt_find_id(url) + else: + self.id = query_stuff["list"] + + elif self.url_type == YouTubeUrlType.VIDEO: + query_stuff = parse_qs(parsed.query) + if "v" not in query_stuff: + self.couldnt_find_id(url) + else: + self.id = query_stuff["v"] + + + def couldnt_find_id(self, url: str): + YOUTUBE_LOGGER.warning(f"The id is missing: {url}") + self.url_type = YouTubeUrlType.NONE + + @property + def api(self) -> str: + if self.url_type == YouTubeUrlType.CHANNEL: + return get_invidious_url(path=f"/api/v1/channels/playlists/{self.id}") + + if self.url_type == YouTubeUrlType.PLAYLIST: + return get_invidious_url(path=f"/api/v1/playlists/{id}") + + if self.url_type == YouTubeUrlType.VIDEO: + return get_invidious_url(path=f"/api/v1/videos/{self.id}") + + return get_invidious_url() + + @property + def normal(self) -> str: + if self.url_type.CHANNEL: + return get_invidious_url(path=f"/channel/{self.id}") + + if self.url_type.PLAYLIST: + return get_invidious_url(path="/playlist", query=f"list={self.id}") + + if self.url_type.VIDEO: + return get_invidious_url(path="/watch", query=f"v={self.id}") + + class YouTube(Page): # CHANGE SOURCE_TYPE = SourcePages.YOUTUBE @@ -32,14 +129,22 @@ class YouTube(Page): def __init__(self, *args, **kwargs): self.connection: Connection = Connection( - host="https://www.preset.cum/", + host=urlunparse(INVIDIOUS_INSTANCE), logger=self.LOGGER ) super().__init__(*args, **kwargs) def get_source_type(self, source: Source) -> Optional[Type[DatabaseObject]]: - return super().get_source_type(source) + _url_type = { + YouTubeUrlType.CHANNEL: Artist, + YouTubeUrlType.PLAYLIST: Album, + YouTubeUrlType.VIDEO: Song, + } + + parsed = YouTubeUrl(source.url) + if parsed.url_type in _url_type: + return _url_type[parsed.url_type] def general_search(self, search_query: str) -> List[DatabaseObject]: return [Artist(name="works")] diff --git a/src/music_kraken/utils/shared.py b/src/music_kraken/utils/shared.py index b17d3cd..97c2009 100644 --- a/src/music_kraken/utils/shared.py +++ b/src/music_kraken/utils/shared.py @@ -2,6 +2,7 @@ import logging import random from pathlib import Path from typing import List, Tuple, Set, Dict +from urllib.parse import ParseResult from .path_manager import LOCATIONS from .config import LOGGING_SECTION, AUDIO_SECTION, CONNECTION_SECTION, MISC_SECTION, PATHS_SECTION @@ -90,6 +91,7 @@ if TOR: 'http': f'socks5h://127.0.0.1:{CONNECTION_SECTION.TOR_PORT.object_from_value}', 'https': f'socks5h://127.0.0.1:{CONNECTION_SECTION.TOR_PORT.object_from_value}' } +INVIDIOUS_INSTANCE: ParseResult = CONNECTION_SECTION.INVIDIOUS_INSTANCE.value # size of the chunks that are streamed CHUNK_SIZE = CONNECTION_SECTION.CHUNK_SIZE.object_from_value