diff --git a/requirements.txt b/requirements.txt index 0d644fb..4462589 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ pycountry~=22.3.5 python-dateutil~=2.8.2 pandoc~=2.3 SQLAlchemy~=2.0.7 -setuptools~=60.2.0 +setuptools~=68.2.0 tqdm~=4.65.0 ffmpeg-python~=0.2.0 platformdirs~=3.2.0 @@ -18,3 +18,6 @@ pyffmpeg~=2.4.2.18 ffmpeg-progress-yield~=0.7.8 pathvalidate~=2.5.2 guppy3~=3.1.3 + +toml~=0.10.2 +typing_extensions~=4.7.1 \ No newline at end of file diff --git a/src/music_kraken/pages/youtube_music/youtube_music.py b/src/music_kraken/pages/youtube_music/youtube_music.py index a4c765a..249a3db 100644 --- a/src/music_kraken/pages/youtube_music/youtube_music.py +++ b/src/music_kraken/pages/youtube_music/youtube_music.py @@ -6,6 +6,9 @@ import json from dataclasses import dataclass import re +from youtube_dl.jsinterp import JSInterpreter +from youtube_dl.extractor.youtube import YoutubeIE + from ...utils.exception.config import SettingValueError from ...utils.config import main_settings, youtube_settings, logging_settings from ...utils.shared import DEBUG, DEBUG_YOUTUBE_INITIALIZING diff --git a/src/music_kraken/utils/cache.py b/src/music_kraken/utils/cache.py new file mode 100644 index 0000000..e69de29 diff --git a/src/music_kraken/utils/config/config_files/main_config.py b/src/music_kraken/utils/config/config_files/main_config.py index ba6ef91..6712a4c 100644 --- a/src/music_kraken/utils/config/config_files/main_config.py +++ b/src/music_kraken/utils/config/config_files/main_config.py @@ -12,7 +12,7 @@ from ..attributes.special_attributes import ( AudioFormatAttribute, ) -config = Config([ +config = Config(( Attribute(name="hasnt_yet_started", default_value=False, description="This will be set automatically, to look if it needs to run the scripts that run on start."), Attribute(name="bitrate", default_value=125, description="Streams the audio with given bitrate [kB/s]. Can't stream with a higher Bitrate, than the audio source provides."), AudioFormatAttribute(name="audio_format", default_value="mp3", description="""Music Kraken will stream the audio into this format. @@ -64,6 +64,8 @@ all the error messages are shown."""), PathAttribute(name="temp_directory", default_value=LOCATIONS.TEMP_DIRECTORY.resolve(), description="All temporary stuff is gonna be dumped in this directory."), PathAttribute(name="log_file", default_value=LOCATIONS.get_log_file("download_logs.log").resolve()), PathAttribute(name="ffmpeg_binary", default_value=LOCATIONS.FFMPEG_BIN.resolve(), description="Set the path to the ffmpeg binary."), + PathAttribute(name="cache_directory", default_value=LOCATIONS.CACHE_DIRECTORY.resolve(), + description="Set the path of the cache directory."), Attribute( name="not_a_genre_regex", description="These regular expressions tell music-kraken, which sub-folders of the music-directory\n" @@ -93,7 +95,7 @@ But anyways... Freedom of thought, so go ahead and change the messages."""), Attribute(name="id_bits", default_value=64, description="I really dunno why I even made this a setting.. Modifying this is a REALLY dumb idea."), Description("🏳️‍⚧️🏳️‍⚧️ Protect trans youth. 🏳️‍⚧️🏳️‍⚧️\n"), -], LOCATIONS.get_config_file("main")) +), LOCATIONS.get_config_file("main")) class SettingsStructure(TypedDict): @@ -126,4 +128,4 @@ class SettingsStructure(TypedDict): log_file: Path not_a_genre_regex: List[str] ffmpeg_binary: Path - + cache_directory: Path diff --git a/src/music_kraken/utils/hooks.py b/src/music_kraken/utils/hooks.py deleted file mode 100644 index e3cd954..0000000 --- a/src/music_kraken/utils/hooks.py +++ /dev/null @@ -1,29 +0,0 @@ -from typing import List, Iterable, Dict, TypeVar, Generic, Iterator, Any, Type -from enum import Enum -from dataclasses import dataclass -from collections import defaultdict - - -class HookEventTypes(Enum): - pass - - -@dataclass -class Event: - target: Any - - -class Hooks: - def __init__(self, target) -> None: - self.target = target - - self._callbacks: Dict[HookEventTypes, List[callable]] = defaultdict(list) - - def add_event_listener(self, event_type: HookEventTypes, callback: callable): - self._callbacks[event_type].append(callback) - - def trigger_event(self, event_type: HookEventTypes, *args, **kwargs): - event: Event = Event(target=self.target) - - for callback in self._callbacks[event_type]: - callback(event, *args, **kwargs) diff --git a/src/music_kraken/utils/path_manager/locations.py b/src/music_kraken/utils/path_manager/locations.py index 66953d1..a3917bf 100644 --- a/src/music_kraken/utils/path_manager/locations.py +++ b/src/music_kraken/utils/path_manager/locations.py @@ -1,14 +1,63 @@ +import configparser from pathlib import Path import os +from os.path import expandvars +import logging +from sys import platform import tempfile +from typing import Optional + from pyffmpeg import FFmpeg + from .music_directory import get_music_directory from .config_directory import get_config_directory class Locations: + @staticmethod + def _get_env(key: str, default: Path, default_for_windows: bool = True) -> Optional[Path]: + res = os.environ.get(key.upper()) + if res is not None: + return res + + xdg_user_dirs_file = os.environ.get("XDG_CONFIG_HOME") or Path(Path.home(), ".config", "user-dirs.dirs") + xdg_user_dirs_default_file = Path("/etc/xdg/user-dirs.defaults") + + def get_dir_from_xdg_file(xdg_file_path: os.PathLike) -> Optional[Path]: + nonlocal key + + try: + with open(xdg_file_path, 'r') as f: + data = "[XDG_USER_DIRS]\n" + f.read() + config = configparser.ConfigParser(allow_no_value=True) + config.read_string(data) + xdg_config = config['XDG_USER_DIRS'] + + return Path(expandvars(xdg_config[key.lower()].strip('"'))) + + except (FileNotFoundError, KeyError) as e: + logging.warning( + f"Missing file or No entry found for \"{key}\" in: \"{xdg_file_path}\".\n" + ) + logging.debug(str(e)) + + res = get_dir_from_xdg_file(xdg_user_dirs_file) + if res is not None: + return res + + res = get_dir_from_xdg_file(xdg_user_dirs_default_file) + if res is not None: + return res + + logging.warning(f"couldn't find a {key}, falling back to: {default}") + + if not default_for_windows and platform == "linux": + return + + return default + def __init__(self, application_name: os.PathLike = "music-kraken"): self.FILE_ENCODING: str = "utf-8" @@ -21,6 +70,14 @@ class Locations: self.CONFIG_DIRECTORY.mkdir(exist_ok=True, parents=True) self.CONFIG_FILE = Path(self.CONFIG_DIRECTORY, f"{application_name}.conf") self.LEGACY_CONFIG_FILE = Path(self.CONFIG_DIRECTORY, f"{application_name}.conf") + + self.CACHE_DIRECTORY = self._get_env("XDG_CACHE_HOME", Path(Path.home(), ".cache")) + if self.CACHE_DIRECTORY is None: + logging.warning(f"Could not find a cache dir. Falling back to the temp dir: {self.TEMP_DIRECTORY}") + self.CACHE_DIRECTORY = self.TEMP_DIRECTORY + else: + self.CACHE_DIRECTORY = Path(self.CACHE_DIRECTORY, application_name) + self.CACHE_DIRECTORY.mkdir(parents=True, exist_ok=True) self.FFMPEG_BIN = Path(FFmpeg(enable_log=False).get_ffmpeg_bin())