Compare commits

..

2 Commits

Author SHA1 Message Date
cef87460a7 draft
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-05-24 14:46:38 +02:00
c0fbd16929 feat: basic select layout 2024-05-24 13:21:07 +02:00
6 changed files with 131 additions and 30 deletions

6
.vscode/launch.json vendored
View File

@ -17,6 +17,12 @@
"request": "launch",
"program": "development/actual_donwload.py",
"console": "integratedTerminal"
},
{
"name": "Python Debugger: Music Kraken",
"type": "debugpy",
"request": "launch", // run the module
"module": "music_kraken",
}
]
}

View File

@ -4,10 +4,9 @@ from pathlib import Path
from typing import Dict, List, Set, Type
from .. import console
from ..download import Downloader
from ..download import Downloader, Page
from ..download.results import GoToResults, Option, PageResults, Results
from ..objects import Album, Artist, DatabaseObject, Song
from ..pages import Page
from ..utils import BColors, output
from ..utils.config import main_settings, write_config
from ..utils.enums.colors import BColors

View File

@ -8,8 +8,8 @@ from copy import copy
from dataclasses import dataclass, field
from pathlib import Path
from string import Formatter
from typing import (TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type,
TypedDict, Union)
from typing import (TYPE_CHECKING, Any, Callable, Dict, Generator, List,
Optional, Set, Tuple, Type, TypedDict, Union)
import requests
from bs4 import BeautifulSoup
@ -23,7 +23,7 @@ from ..utils import BColors, output, trace
from ..utils.config import main_settings, youtube_settings
from ..utils.enums import ALL_SOURCE_TYPES, SourceType
from ..utils.enums.album import AlbumType
from ..utils.exception import MKMissingNameException
from ..utils.exception import MKComposeException, MKMissingNameException
from ..utils.exception.download import UrlNotFoundException
from ..utils.path_manager import LOCATIONS
from ..utils.shared import DEBUG_PAGES
@ -75,7 +75,7 @@ class Downloader:
self.scan_for_pages(**kwargs)
def register_page(self, page_type: Type[Page], **kwargs):
if page_type in _registered_pages:
if page_type in self._registered_pages:
return
self._registered_pages[page_type].add(page_type(
@ -343,13 +343,15 @@ class Page:
return super().__new__(cls)
def __init__(self, download_options: DownloadOptions = None, fetch_options: FetchOptions = None, **kwargs):
self.SOURCE_TYPE.register_page(self)
if self.SOURCE_TYPE is not None:
self.SOURCE_TYPE.register_page(self)
self.download_options: DownloadOptions = download_options or DownloadOptions()
self.fetch_options: FetchOptions = fetch_options or FetchOptions()
def __del__(self):
self.SOURCE_TYPE.deregister_page()
if self.SOURCE_TYPE is not None:
self.SOURCE_TYPE.deregister_page()
def _search_regex(self, pattern, string, default=None, fatal=True, flags=0, group=None):
"""
@ -451,10 +453,106 @@ class Option:
This could represent a data object, a string or a page.
"""
def __init__(self, value: Any, text: Optional[str] = None, keys: Set[str] = None):
def __init__(
self,
value: Any,
text: Optional[str] = None,
keys: List[Any] = None,
hidden: bool = False,
parse_key: Callable[[Any], Any] = lambda x: x,
):
self._parse_key: Callable[[Any], Any] = parse_key
self.value = value
self.text = text or str(value)
self.hidden = hidden
self.keys = keys or set()
self.keys.add(self.text)
self._raw_keys = set(keys or [])
self._raw_keys.add(self.text)
self.keys = set(self.parse_key(key) for key in self._raw_keys)
def register_key(self, key: Any):
self._raw_keys.add(key)
self.keys.add(self._parse_key(key))
@property
def parse_key(self) -> Callable[[Any], Any]:
return self._parse_key
@parse_key.setter
def parse_key(self, value: Callable[[Any], Any]):
self._parse_key = value
self.keys = set(self._parse_key(key) for key in self._raw_keys)
def __str__(self):
return self.text
class Select:
def __init__(
self,
options: List[Option] = None,
option_factory: Callable[[Any], Option] = None,
raw_options: List[Any] = None,
parse_option_key: Callable[[Any], Any] = lambda x: x,
ask_for_creating_option: Callable[[Option], bool] = lambda x: True,
**kwargs
):
self._parse_option_key: Callable[[Any], Any] = parse_option_key
self._ask_for_creating_option: Callable[[Option], bool] = ask_for_creating_option
self._key_to_option: Dict[Any, Option] = dict()
self._options: List[Option] = []
options = options or []
self.option_factory: Optional[Callable[[Any], Option]] = option_factory
if self.can_create_options:
for raw_option in raw_options or []:
self.append(self.option_factory(raw_option))
elif raw_options is not None:
raise MKComposeException("Cannot create options without a factory.")
self.extend(options)
@property
def can_create_options(self) -> bool:
return self.option_factory is not None
def append(self, option: Option):
option.parse_key = self._parse_option_key
self._options.append(option)
for key in option.keys:
self._key_to_option[key] = option
def extend(self, options: List[Option]):
for option in options:
self.append(option)
def __iter__(self) -> Generator[Option, None, None]:
for option in self._options:
if option.hidden:
continue
yield option
def __contains__(self, key: Any) -> bool:
return key in self._key_to_option
def __getitem__(self, key: Any) -> Option:
return self._key_to_option[key]
def create_option(self, key: Any, **kwargs) -> Option:
if not self.can_create_options:
raise MKComposeException("Cannot create options without a factory.")
option = self.option_factory(key, **kwargs)
self.append(option)
return option
def choose(self, key: Any) -> Optional[Option]:
if key not in self:
if self.can_create_options and self._ask_for_creating_option(key):
return self.create_option(key)
return None
return self[key]

View File

@ -1,8 +1,12 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Dict, Generator, List, Tuple, Type, Union
from typing import TYPE_CHECKING, Dict, Generator, List, Tuple, Type, Union
from ..objects import DatabaseObject
from . import Page
if TYPE_CHECKING:
from . import Page
@dataclass

View File

@ -1,26 +1,17 @@
from typing import List, Optional, Type, Tuple
from urllib.parse import urlparse, urlunparse, parse_qs
from enum import Enum
import requests
from typing import List, Optional, Tuple, Type
from urllib.parse import parse_qs, urlparse, urlunparse
import python_sponsorblock
import requests
from ...objects import Source, DatabaseObject, Song, Target
from .._abstract import Page
from ...objects import (
Artist,
Source,
Song,
Album,
Label,
Target,
FormattedText,
ID3Timestamp
)
from ...connection import Connection
from ...download import Page
from ...objects import (Album, Artist, DatabaseObject, FormattedText,
ID3Timestamp, Label, Song, Source, Target)
from ...utils.config import logging_settings, main_settings, youtube_settings
from ...utils.enums import ALL_SOURCE_TYPES, SourceType
from ...utils.support_classes.download_result import DownloadResult
from ...utils.config import youtube_settings, logging_settings, main_settings
from ...utils.enums import SourceType, ALL_SOURCE_TYPES
def get_invidious_url(path: str = "", params: str = "", query: str = "", fragment: str = "") -> str:

View File

@ -3,6 +3,9 @@ class MKBaseException(Exception):
self.message = message
super().__init__(message, **kwargs)
# Compose exceptions. Those usually mean a bug on my side.
class MKComposeException(MKBaseException):
pass
# Downloading
class MKDownloadException(MKBaseException):