layed out page boilerplate for bandcamp
This commit is contained in:
parent
e25f788b50
commit
8846eacf06
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -14,5 +14,8 @@
|
|||||||
"[python]": {
|
"[python]": {
|
||||||
"editor.defaultFormatter": "ms-python.autopep8"
|
"editor.defaultFormatter": "ms-python.autopep8"
|
||||||
},
|
},
|
||||||
"python.formatting.provider": "none"
|
"python.formatting.provider": "none",
|
||||||
|
"cSpell.words": [
|
||||||
|
"Bandcamp"
|
||||||
|
]
|
||||||
}
|
}
|
@ -29,8 +29,7 @@ if __name__ == "__main__":
|
|||||||
]
|
]
|
||||||
|
|
||||||
youtube_music_test = [
|
youtube_music_test = [
|
||||||
"s: #a Favorite #r Anarcho",
|
"s: #a Favorite #r Anarcho"
|
||||||
"0"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
music_kraken.cli.download(genre="test", command_list=youtube_music_test, process_metadata_anyway=True)
|
music_kraken.cli.download(genre="test", command_list=youtube_music_test, process_metadata_anyway=True)
|
||||||
|
@ -9,20 +9,14 @@ from ..utils.support_classes import Query, DownloadResult
|
|||||||
from ..utils.exception.download import UrlNotFoundException
|
from ..utils.exception.download import UrlNotFoundException
|
||||||
from ..utils.shared import DEBUG_PAGES
|
from ..utils.shared import DEBUG_PAGES
|
||||||
|
|
||||||
from ..pages import Page, EncyclopaediaMetallum, Musify, YouTube, YoutubeMusic, INDEPENDENT_DB_OBJECTS
|
from ..pages import Page, EncyclopaediaMetallum, Musify, YouTube, YoutubeMusic, Bandcamp, INDEPENDENT_DB_OBJECTS
|
||||||
|
|
||||||
if DEBUG_PAGES:
|
|
||||||
DEBUGGING_PAGE = YoutubeMusic
|
|
||||||
print(f"Only downloading from page {DEBUGGING_PAGE}.")
|
|
||||||
|
|
||||||
ALL_PAGES = {DEBUGGING_PAGE}
|
|
||||||
AUDIO_PAGES = ALL_PAGES.union(AUDIO_PAGES)
|
|
||||||
|
|
||||||
|
|
||||||
ALL_PAGES: Set[Type[Page]] = {
|
ALL_PAGES: Set[Type[Page]] = {
|
||||||
EncyclopaediaMetallum,
|
EncyclopaediaMetallum,
|
||||||
Musify,
|
Musify,
|
||||||
YoutubeMusic
|
YoutubeMusic,
|
||||||
|
Bandcamp
|
||||||
}
|
}
|
||||||
|
|
||||||
if youtube_settings["use_youtube_alongside_youtube_music"]:
|
if youtube_settings["use_youtube_alongside_youtube_music"]:
|
||||||
@ -31,13 +25,21 @@ if youtube_settings["use_youtube_alongside_youtube_music"]:
|
|||||||
AUDIO_PAGES: Set[Type[Page]] = {
|
AUDIO_PAGES: Set[Type[Page]] = {
|
||||||
Musify,
|
Musify,
|
||||||
YouTube,
|
YouTube,
|
||||||
YoutubeMusic
|
YoutubeMusic,
|
||||||
|
Bandcamp
|
||||||
}
|
}
|
||||||
|
|
||||||
SHADY_PAGES: Set[Type[Page]] = {
|
SHADY_PAGES: Set[Type[Page]] = {
|
||||||
Musify,
|
Musify,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if DEBUG_PAGES:
|
||||||
|
DEBUGGING_PAGE = Bandcamp
|
||||||
|
print(f"Only downloading from page {DEBUGGING_PAGE}.")
|
||||||
|
|
||||||
|
ALL_PAGES = {DEBUGGING_PAGE}
|
||||||
|
AUDIO_PAGES = ALL_PAGES.union(AUDIO_PAGES)
|
||||||
|
|
||||||
class Pages:
|
class Pages:
|
||||||
def __init__(self, exclude_pages: Set[Type[Page]] = None, exclude_shady: bool = False) -> None:
|
def __init__(self, exclude_pages: Set[Type[Page]] = None, exclude_shady: bool = False) -> None:
|
||||||
# initialize all page instances
|
# initialize all page instances
|
||||||
|
@ -2,5 +2,6 @@ from .encyclopaedia_metallum import EncyclopaediaMetallum
|
|||||||
from .musify import Musify
|
from .musify import Musify
|
||||||
from .youtube import YouTube
|
from .youtube import YouTube
|
||||||
from .youtube_music import YoutubeMusic
|
from .youtube_music import YoutubeMusic
|
||||||
|
from .bandcamp import Bandcamp
|
||||||
|
|
||||||
from .abstract import Page, INDEPENDENT_DB_OBJECTS
|
from .abstract import Page, INDEPENDENT_DB_OBJECTS
|
||||||
|
@ -220,7 +220,7 @@ class Page:
|
|||||||
|
|
||||||
if type(music_object) in search_functions:
|
if type(music_object) in search_functions:
|
||||||
r = search_functions[type(music_object)](music_object)
|
r = search_functions[type(music_object)](music_object)
|
||||||
if len(r) > 0:
|
if r is not None and len(r) > 0:
|
||||||
return r
|
return r
|
||||||
|
|
||||||
r = []
|
r = []
|
||||||
|
65
src/music_kraken/pages/bandcamp.py
Normal file
65
src/music_kraken/pages/bandcamp.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
from typing import List, Optional, Type
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
from ..objects import Source, DatabaseObject
|
||||||
|
from .abstract import Page
|
||||||
|
from ..objects import (
|
||||||
|
Artist,
|
||||||
|
Source,
|
||||||
|
SourcePages,
|
||||||
|
Song,
|
||||||
|
Album,
|
||||||
|
Label,
|
||||||
|
Target
|
||||||
|
)
|
||||||
|
from ..connection import Connection
|
||||||
|
from ..utils.support_classes import DownloadResult
|
||||||
|
from ..utils.config import main_settings, logging_settings
|
||||||
|
|
||||||
|
class Bandcamp(Page):
|
||||||
|
# CHANGE
|
||||||
|
SOURCE_TYPE = SourcePages.BANDCAMP
|
||||||
|
LOGGER = logging_settings["bandcamp_logger"]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.connection: Connection = Connection(
|
||||||
|
host="https://bandcamp.com/",
|
||||||
|
logger=self.LOGGER
|
||||||
|
)
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_source_type(self, source: Source) -> Optional[Type[DatabaseObject]]:
|
||||||
|
return super().get_source_type(source)
|
||||||
|
|
||||||
|
def general_search(self, search_query: str) -> List[DatabaseObject]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def label_search(self, label: Label) -> List[Label]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def artist_search(self, artist: Artist) -> List[Artist]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def album_search(self, album: Album) -> List[Album]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def song_search(self, song: Song) -> List[Song]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def fetch_song(self, source: Source, stop_at_level: int = 1) -> Song:
|
||||||
|
return Song()
|
||||||
|
|
||||||
|
def fetch_album(self, source: Source, stop_at_level: int = 1) -> Album:
|
||||||
|
return Album()
|
||||||
|
|
||||||
|
def fetch_artist(self, source: Source, stop_at_level: int = 1) -> Artist:
|
||||||
|
return Artist()
|
||||||
|
|
||||||
|
def fetch_label(self, source: Source, stop_at_level: int = 1) -> Label:
|
||||||
|
return Label()
|
||||||
|
|
||||||
|
def download_song_to_target(self, source: Source, target: Target, desc: str = None) -> DownloadResult:
|
||||||
|
return DownloadResult()
|
@ -79,6 +79,11 @@ Reference for the logging formats: https://docs.python.org/3/library/logging.htm
|
|||||||
description="The logger for the genius scraper",
|
description="The logger for the genius scraper",
|
||||||
default_value="genius"
|
default_value="genius"
|
||||||
),
|
),
|
||||||
|
LoggerAttribute(
|
||||||
|
name="bandcamp_logger",
|
||||||
|
description="The logger for the bandcamp scraper",
|
||||||
|
default_value="bandcamp"
|
||||||
|
)
|
||||||
|
|
||||||
], LOCATIONS.get_config_file("logging"))
|
], LOCATIONS.get_config_file("logging"))
|
||||||
|
|
||||||
@ -96,4 +101,5 @@ class SettingsStructure(TypedDict):
|
|||||||
youtube_logger: Logger
|
youtube_logger: Logger
|
||||||
youtube_music_logger: Logger
|
youtube_music_logger: Logger
|
||||||
metal_archives_logger: Logger
|
metal_archives_logger: Logger
|
||||||
genius_logger: Logger
|
genius_logger: Logger
|
||||||
|
bandcamp_logger: Logger
|
@ -1,154 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
from ..base_classes import (
|
|
||||||
SingleAttribute,
|
|
||||||
FloatAttribute,
|
|
||||||
StringAttribute,
|
|
||||||
Section,
|
|
||||||
Description,
|
|
||||||
EmptyLine,
|
|
||||||
BoolAttribute,
|
|
||||||
ListAttribute
|
|
||||||
)
|
|
||||||
from ...enums.album import AlbumType
|
|
||||||
from ...exception.config import SettingValueError
|
|
||||||
|
|
||||||
# Only the formats with id3 metadata can be used
|
|
||||||
# https://www.audioranger.com/audio-formats.php
|
|
||||||
# https://web.archive.org/web/20230322234434/https://www.audioranger.com/audio-formats.php
|
|
||||||
ID3_2_FILE_FORMATS = frozenset((
|
|
||||||
"mp3", "mp2", "mp1", # MPEG-1 ID3.2
|
|
||||||
"wav", "wave", "rmi", # RIFF (including WAV) ID3.2
|
|
||||||
"aiff", "aif", "aifc", # AIFF ID3.2
|
|
||||||
"aac", "aacp", # Raw AAC ID3.2
|
|
||||||
"tta", # True Audio ID3.2
|
|
||||||
))
|
|
||||||
_sorted_id3_2_formats = sorted(ID3_2_FILE_FORMATS)
|
|
||||||
|
|
||||||
ID3_1_FILE_FORMATS = frozenset((
|
|
||||||
"ape", # Monkey's Audio ID3.1
|
|
||||||
"mpc", "mpp", "mp+", # MusePack ID3.1
|
|
||||||
"wv", # WavPack ID3.1
|
|
||||||
"ofr", "ofs" # OptimFrog ID3.1
|
|
||||||
))
|
|
||||||
_sorted_id3_1_formats = sorted(ID3_1_FILE_FORMATS)
|
|
||||||
|
|
||||||
|
|
||||||
class AudioFormatAttribute(SingleAttribute):
|
|
||||||
def validate(self, value: str):
|
|
||||||
v = self.value.strip().lower()
|
|
||||||
if v not in ID3_1_FILE_FORMATS and v not in ID3_2_FILE_FORMATS:
|
|
||||||
raise SettingValueError(
|
|
||||||
setting_name=self.name,
|
|
||||||
setting_value=value,
|
|
||||||
rule="has to be a valid audio format, supporting id3 metadata"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def object_from_value(self) -> str:
|
|
||||||
v = self.value.strip().lower()
|
|
||||||
if v in ID3_2_FILE_FORMATS:
|
|
||||||
return v
|
|
||||||
if v in ID3_1_FILE_FORMATS:
|
|
||||||
logging.debug(f"setting audio format to a format that only supports ID3.1: {v}")
|
|
||||||
return v
|
|
||||||
|
|
||||||
raise ValueError(f"Invalid Audio Format: {v}")
|
|
||||||
|
|
||||||
|
|
||||||
class AlbumTypeListAttribute(ListAttribute):
|
|
||||||
def validate(self, value: str):
|
|
||||||
try:
|
|
||||||
AlbumType(value.strip())
|
|
||||||
except ValueError:
|
|
||||||
raise SettingValueError(
|
|
||||||
setting_name=self.name,
|
|
||||||
setting_value=value,
|
|
||||||
rule="has to be an existing album type"
|
|
||||||
)
|
|
||||||
|
|
||||||
def single_object_from_element(self, value: str) -> AlbumType:
|
|
||||||
return AlbumType(value)
|
|
||||||
|
|
||||||
|
|
||||||
class AudioSection(Section):
|
|
||||||
def __init__(self):
|
|
||||||
self.BITRATE = FloatAttribute(
|
|
||||||
name="bitrate",
|
|
||||||
description="Streams the audio with given bitrate [kB/s]. "
|
|
||||||
"Can't stream with a higher Bitrate, than the audio source provides.",
|
|
||||||
value="125"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.AUDIO_FORMAT = AudioFormatAttribute(name="audio_format", value="mp3", description=f"""
|
|
||||||
Music Kraken will stream the audio into this format.
|
|
||||||
You can use Audio formats which support ID3.2 and ID3.1,
|
|
||||||
but you will have cleaner Metadata using ID3.2.
|
|
||||||
ID3.2: {', '.join(_sorted_id3_2_formats)}
|
|
||||||
ID3.1: {', '.join(_sorted_id3_1_formats)}
|
|
||||||
""".strip())
|
|
||||||
|
|
||||||
self.SORT_BY_DATE = BoolAttribute(
|
|
||||||
name="sort_by_date",
|
|
||||||
description="If this is set to true, it will set the albumsort attribute such that,\n"
|
|
||||||
"the albums are sorted by date.",
|
|
||||||
value="true"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.SORT_BY_ALBUM_TYPE = BoolAttribute(
|
|
||||||
name="sort_album_by_type",
|
|
||||||
description="If this is set to true, it will set the albumsort attribute such that,\n"
|
|
||||||
"the albums are put into categories before being sorted.\n"
|
|
||||||
"This means for example, the Studio Albums and EP's are always in front of Singles, "
|
|
||||||
"and Compilations are in the back.",
|
|
||||||
value="true"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.DOWNLOAD_PATH = StringAttribute(
|
|
||||||
name="download_path",
|
|
||||||
value="{genre}/{artist}/{album}",
|
|
||||||
description="The folder music kraken should put the songs into."
|
|
||||||
)
|
|
||||||
|
|
||||||
self.DOWNLOAD_FILE = StringAttribute(
|
|
||||||
name="download_file",
|
|
||||||
value="{song}.{audio_format}",
|
|
||||||
description="The filename of the audio file."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
self.ALBUM_TYPE_BLACKLIST = AlbumTypeListAttribute(
|
|
||||||
name="album_type_blacklist",
|
|
||||||
description="Music Kraken ignores all albums of those types.\n"
|
|
||||||
"Following album types exist in the programm:\n"
|
|
||||||
f"{', '.join(album.value for album in AlbumType)}",
|
|
||||||
value=[
|
|
||||||
AlbumType.COMPILATION_ALBUM.value,
|
|
||||||
AlbumType.LIVE_ALBUM.value,
|
|
||||||
AlbumType.MIXTAPE.value
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.attribute_list = [
|
|
||||||
self.BITRATE,
|
|
||||||
self.AUDIO_FORMAT,
|
|
||||||
EmptyLine(),
|
|
||||||
self.SORT_BY_DATE,
|
|
||||||
self.SORT_BY_ALBUM_TYPE,
|
|
||||||
Description("""
|
|
||||||
There are multiple fields, you can use for the path and file name:
|
|
||||||
- genre
|
|
||||||
- label
|
|
||||||
- artist
|
|
||||||
- album
|
|
||||||
- song
|
|
||||||
- album_type
|
|
||||||
""".strip()),
|
|
||||||
self.DOWNLOAD_PATH,
|
|
||||||
self.DOWNLOAD_FILE,
|
|
||||||
self.ALBUM_TYPE_BLACKLIST,
|
|
||||||
]
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
|
|
||||||
AUDIO_SECTION = AudioSection()
|
|
@ -1,157 +0,0 @@
|
|||||||
from urllib.parse import urlparse, ParseResult
|
|
||||||
import re
|
|
||||||
|
|
||||||
from ..base_classes import Section, FloatAttribute, IntAttribute, BoolAttribute, ListAttribute, StringAttribute
|
|
||||||
from ...regex import URL_PATTERN
|
|
||||||
from ...exception.config import SettingValueError
|
|
||||||
|
|
||||||
|
|
||||||
class ProxAttribute(ListAttribute):
|
|
||||||
def single_object_from_element(self, value) -> dict:
|
|
||||||
return {
|
|
||||||
'http': value,
|
|
||||||
'https': value,
|
|
||||||
'ftp': value
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class UrlStringAttribute(StringAttribute):
|
|
||||||
def validate(self, value: str):
|
|
||||||
v = value.strip()
|
|
||||||
url = re.match(URL_PATTERN, v)
|
|
||||||
if url is None:
|
|
||||||
raise SettingValueError(
|
|
||||||
setting_name=self.name,
|
|
||||||
setting_value=v,
|
|
||||||
rule="has to be a valid url"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def object_from_value(self) -> ParseResult:
|
|
||||||
return urlparse(self.value)
|
|
||||||
|
|
||||||
|
|
||||||
class UrlListAttribute(ListAttribute):
|
|
||||||
def validate(self, value: str):
|
|
||||||
v = value.strip()
|
|
||||||
url = re.match(URL_PATTERN, v)
|
|
||||||
if url is None:
|
|
||||||
raise SettingValueError(
|
|
||||||
setting_name=self.name,
|
|
||||||
setting_value=v,
|
|
||||||
rule="has to be a valid url"
|
|
||||||
)
|
|
||||||
|
|
||||||
def single_object_from_element(self, value: str):
|
|
||||||
return urlparse(value)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ConnectionSection(Section):
|
|
||||||
def __init__(self):
|
|
||||||
self.PROXIES = ProxAttribute(
|
|
||||||
name="proxies",
|
|
||||||
description="Set your proxies.\n"
|
|
||||||
"Must be valid for http, as well as https.",
|
|
||||||
value=[]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.USE_TOR = BoolAttribute(
|
|
||||||
name="tor",
|
|
||||||
description="Route ALL traffic through Tor.\n"
|
|
||||||
"If you use Tor, make sure the Tor browser is installed, and running."
|
|
||||||
"I can't guarantee maximum security though!",
|
|
||||||
value="false"
|
|
||||||
)
|
|
||||||
self.TOR_PORT = IntAttribute(
|
|
||||||
name="tor_port",
|
|
||||||
description="The port, tor is listening. If tor is already working, don't change it.",
|
|
||||||
value="9150"
|
|
||||||
)
|
|
||||||
self.CHUNK_SIZE = IntAttribute(
|
|
||||||
name="chunk_size",
|
|
||||||
description="Size of the chunks that are streamed.",
|
|
||||||
value="1024"
|
|
||||||
)
|
|
||||||
self.SHOW_DOWNLOAD_ERRORS_THRESHOLD = FloatAttribute(
|
|
||||||
name="show_download_errors_threshold",
|
|
||||||
description="If the percentage of failed downloads goes over this threshold,\n"
|
|
||||||
"all the error messages are shown.",
|
|
||||||
value="0.3"
|
|
||||||
)
|
|
||||||
|
|
||||||
# INVIDIOUS INSTANCES LIST
|
|
||||||
self.INVIDIOUS_INSTANCE = UrlStringAttribute(
|
|
||||||
name="invidious_instance",
|
|
||||||
description="This is an attribute, where you can define the invidious instances,\n"
|
|
||||||
"the youtube downloader should use.\n"
|
|
||||||
"Here is a list of active ones: https://docs.invidious.io/instances/\n"
|
|
||||||
"Instances that use cloudflare or have source code changes could cause issues.\n"
|
|
||||||
"Hidden instances (.onion) will only work, when setting 'tor=true'.",
|
|
||||||
value="https://yt.artemislena.eu/"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.PIPED_INSTANCE = UrlStringAttribute(
|
|
||||||
name="piped_instance",
|
|
||||||
description="This is an attribute, where you can define the pioed instances,\n"
|
|
||||||
"the youtube downloader should use.\n"
|
|
||||||
"Here is a list of active ones: https://github.com/TeamPiped/Piped/wiki/Instances\n"
|
|
||||||
"Instances that use cloudflare or have source code changes could cause issues.\n"
|
|
||||||
"Hidden instances (.onion) will only work, when setting 'tor=true'.",
|
|
||||||
value="https://pipedapi.kavin.rocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.SLEEP_AFTER_YOUTUBE_403 = FloatAttribute(
|
|
||||||
name="sleep_after_youtube_403",
|
|
||||||
description="The time to wait, after youtube returned 403 (in seconds)",
|
|
||||||
value="20"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.YOUTUBE_MUSIC_API_KEY = StringAttribute(
|
|
||||||
name="youtube_music_api_key",
|
|
||||||
description="This is the API key used by YouTube-Music internally.\nDw. if it is empty, Rachel will fetch it automatically for you <333\n(she will also update outdated api keys/those that don't work)",
|
|
||||||
value="AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.YOUTUBE_MUSIC_CLEAN_DATA = BoolAttribute(
|
|
||||||
name="youtube_music_clean_data",
|
|
||||||
description="If set to true, it exclusively fetches artists/albums/songs, not things like user channels etc.",
|
|
||||||
value="true"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.ALL_YOUTUBE_URLS = UrlListAttribute(
|
|
||||||
name="youtube_url",
|
|
||||||
description="This is used to detect, if an url is from youtube, or any alternativ frontend.\n"
|
|
||||||
"If any instance seems to be missing, run music kraken with the -f flag.",
|
|
||||||
value=[
|
|
||||||
"https://www.youtube.com/",
|
|
||||||
"https://www.youtu.be/",
|
|
||||||
"https://redirect.invidious.io/",
|
|
||||||
"https://piped.kavin.rocks/"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.SPONSOR_BLOCK = BoolAttribute(
|
|
||||||
name="use_sponsor_block",
|
|
||||||
value="true",
|
|
||||||
description="Use sponsor block to remove adds or simmilar from the youtube videos."
|
|
||||||
)
|
|
||||||
|
|
||||||
self.attribute_list = [
|
|
||||||
self.USE_TOR,
|
|
||||||
self.TOR_PORT,
|
|
||||||
self.CHUNK_SIZE,
|
|
||||||
self.SHOW_DOWNLOAD_ERRORS_THRESHOLD,
|
|
||||||
self.INVIDIOUS_INSTANCE,
|
|
||||||
self.PIPED_INSTANCE,
|
|
||||||
self.SLEEP_AFTER_YOUTUBE_403,
|
|
||||||
self.YOUTUBE_MUSIC_API_KEY,
|
|
||||||
self.YOUTUBE_MUSIC_CLEAN_DATA,
|
|
||||||
self.ALL_YOUTUBE_URLS,
|
|
||||||
self.SPONSOR_BLOCK
|
|
||||||
]
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
|
|
||||||
CONNECTION_SECTION = ConnectionSection()
|
|
@ -1,130 +0,0 @@
|
|||||||
import logging
|
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
from ..base_classes import SingleAttribute, StringAttribute, Section, Description, EmptyLine
|
|
||||||
|
|
||||||
LOG_LEVELS = {
|
|
||||||
"CRITICAL": 50,
|
|
||||||
"ERROR": 40,
|
|
||||||
"WARNING": 30,
|
|
||||||
"INFO": 20,
|
|
||||||
"DEBUG": 10,
|
|
||||||
"NOTSET": 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class LoggerAttribute(SingleAttribute):
|
|
||||||
@property
|
|
||||||
def object_from_value(self) -> logging.Logger:
|
|
||||||
return logging.getLogger(self.value)
|
|
||||||
|
|
||||||
|
|
||||||
class LogLevelAttribute(SingleAttribute):
|
|
||||||
@property
|
|
||||||
def object_from_value(self) -> int:
|
|
||||||
"""
|
|
||||||
gets the numeric value of a log level
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
if self.value.isnumeric():
|
|
||||||
return int(self.value)
|
|
||||||
|
|
||||||
v = self.value.strip().upper()
|
|
||||||
|
|
||||||
if v not in LOG_LEVELS:
|
|
||||||
raise ValueError(
|
|
||||||
f"{self.name} can only been either one of the following levels, or an integer:\n"
|
|
||||||
f"{';'.join(key for key in LOG_LEVELS)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return LOG_LEVELS[v]
|
|
||||||
|
|
||||||
|
|
||||||
class LoggingSection(Section):
|
|
||||||
def __init__(self):
|
|
||||||
self.FORMAT = StringAttribute(
|
|
||||||
name="logging_format",
|
|
||||||
description="Reference for the logging formats: "
|
|
||||||
"https://docs.python.org/3/library/logging.html#logrecord-attributes",
|
|
||||||
value=logging.BASIC_FORMAT
|
|
||||||
)
|
|
||||||
self.LOG_LEVEL = LogLevelAttribute(
|
|
||||||
name="log_level",
|
|
||||||
description=f"can only been either one of the following levels, or an integer:\n"
|
|
||||||
f"{';'.join(key for key in LOG_LEVELS)}",
|
|
||||||
value=str(logging.INFO)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.DOWNLOAD_LOGGER = LoggerAttribute(
|
|
||||||
name="download_logger",
|
|
||||||
description="The logger for downloading.",
|
|
||||||
value="download"
|
|
||||||
)
|
|
||||||
self.TAGGING_LOGGER = LoggerAttribute(
|
|
||||||
name="tagging_logger",
|
|
||||||
description="The logger for tagging id3 containers.",
|
|
||||||
value="tagging"
|
|
||||||
)
|
|
||||||
self.CODEX_LOGGER = LoggerAttribute(
|
|
||||||
name="codex_logger",
|
|
||||||
description="The logger for streaming the audio into an uniform codex.",
|
|
||||||
value="codex"
|
|
||||||
)
|
|
||||||
self.OBJECT_LOGGER = LoggerAttribute(
|
|
||||||
name="object_logger",
|
|
||||||
description="The logger for creating Data-Objects.",
|
|
||||||
value="object"
|
|
||||||
)
|
|
||||||
self.DATABASE_LOGGER = LoggerAttribute(
|
|
||||||
name="database_logger",
|
|
||||||
description="The logger for Database operations.",
|
|
||||||
value="database"
|
|
||||||
)
|
|
||||||
self.MUSIFY_LOGGER = LoggerAttribute(
|
|
||||||
name="musify_logger",
|
|
||||||
description="The logger for the musify scraper.",
|
|
||||||
value="musify"
|
|
||||||
)
|
|
||||||
self.YOUTUBE_LOGGER = LoggerAttribute(
|
|
||||||
name="youtube_logger",
|
|
||||||
description="The logger for the youtube scraper.",
|
|
||||||
value="youtube"
|
|
||||||
)
|
|
||||||
self.YOUTUBE_MUSIC_LOGGER = LoggerAttribute(
|
|
||||||
name="youtube_music_logger",
|
|
||||||
description="The logger for the youtube music scraper.\n(The scraper is seperate to the youtube scraper)",
|
|
||||||
value="youtube_music"
|
|
||||||
)
|
|
||||||
self.ENCYCLOPAEDIA_METALLUM_LOGGER = LoggerAttribute(
|
|
||||||
name="metal_archives_logger",
|
|
||||||
description="The logger for the metal archives scraper.",
|
|
||||||
value="metal_archives"
|
|
||||||
)
|
|
||||||
self.GENIUS_LOGGER = LoggerAttribute(
|
|
||||||
name="genius_logger",
|
|
||||||
description="The logger for the genius scraper",
|
|
||||||
value="genius"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.attribute_list = [
|
|
||||||
Description("Logging settings for the actual logging:"),
|
|
||||||
self.FORMAT,
|
|
||||||
self.LOG_LEVEL,
|
|
||||||
EmptyLine(),
|
|
||||||
Description("Just the names for different logger, for different parts of the programm:"),
|
|
||||||
self.DOWNLOAD_LOGGER,
|
|
||||||
self.TAGGING_LOGGER,
|
|
||||||
self.CODEX_LOGGER,
|
|
||||||
self.OBJECT_LOGGER,
|
|
||||||
self.DATABASE_LOGGER,
|
|
||||||
self.MUSIFY_LOGGER,
|
|
||||||
self.YOUTUBE_LOGGER,
|
|
||||||
self.YOUTUBE_MUSIC_LOGGER,
|
|
||||||
self.ENCYCLOPAEDIA_METALLUM_LOGGER,
|
|
||||||
self.GENIUS_LOGGER
|
|
||||||
]
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
|
|
||||||
LOGGING_SECTION = LoggingSection()
|
|
@ -1,72 +0,0 @@
|
|||||||
from ..base_classes import Section, IntAttribute, ListAttribute, BoolAttribute
|
|
||||||
|
|
||||||
|
|
||||||
class MiscSection(Section):
|
|
||||||
def __init__(self):
|
|
||||||
self.HASNT_YET_STARTED = BoolAttribute(
|
|
||||||
name="hasnt_yet_started",
|
|
||||||
description="If you did already run, and configured everything, this is false.",
|
|
||||||
value="true"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.ENABLE_RESULT_HISTORY = BoolAttribute(
|
|
||||||
name="result_history",
|
|
||||||
description="If enabled, you can go back to the previous results.\n"
|
|
||||||
"The consequence is a higher meory consumption, because every result is saved.",
|
|
||||||
value="false"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.HISTORY_LENGTH = IntAttribute(
|
|
||||||
name="history_length",
|
|
||||||
description="You can choose how far back you can go in the result history.\n"
|
|
||||||
"The further you choose to be able to go back, the higher the memory usage.\n"
|
|
||||||
"'-1' removes the Limit entirely.",
|
|
||||||
value="8"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.HAPPY_MESSAGES = ListAttribute(
|
|
||||||
name="happy_messages",
|
|
||||||
description="Just some nice and wholesome messages.\n"
|
|
||||||
"If your mindset has traits of a [file corruption], you might not agree.\n"
|
|
||||||
"But anyways... Freedom of thought, so go ahead and change the messages.",
|
|
||||||
value=[
|
|
||||||
"Support the artist.",
|
|
||||||
"Star Me: https://github.com/HeIIow2/music-downloader",
|
|
||||||
"🏳️⚧️🏳️⚧️ Trans rights are human rights. 🏳️⚧️🏳️⚧️",
|
|
||||||
"🏳️⚧️🏳️⚧️ Trans women are women, trans men are men, and enbies are enbies. 🏳️⚧️🏳️⚧️",
|
|
||||||
"🏴☠️🏴☠️ Unite under one flag, fck borders. 🏴☠️🏴☠️",
|
|
||||||
"Join my Matrix Space: https://matrix.to/#/#music-kraken:matrix.org",
|
|
||||||
"Gotta love the BPJM ;-;",
|
|
||||||
"🏳️⚧️🏳️⚧️ Protect trans youth. 🏳️⚧️🏳️⚧️",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.MODIFY_GC = BoolAttribute(
|
|
||||||
name="modify_gc",
|
|
||||||
description="If set to true, it will modify the gc for the sake of performance.\n"
|
|
||||||
"This should not drive up ram usage, but if it is, then turn it of.\n"
|
|
||||||
"Here a blog post about that matter:\n"
|
|
||||||
"https://mkennedy.codes/posts/python-gc-settings-change-this-and-make-your-app-go-20pc-faster/\n"
|
|
||||||
"https://web.archive.org/web/20221124122222/https://mkennedy.codes/posts/python-gc-settings-change-this-and-make-your-app-go-20pc-faster/",
|
|
||||||
value="true"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.ID_BITS = IntAttribute(
|
|
||||||
name="id_bits",
|
|
||||||
description="I really dunno why I even made this a setting.. Modifying this is a REALLY dumb idea.",
|
|
||||||
value="64"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.attribute_list = [
|
|
||||||
self.HASNT_YET_STARTED,
|
|
||||||
self.ENABLE_RESULT_HISTORY,
|
|
||||||
self.HISTORY_LENGTH,
|
|
||||||
self.HAPPY_MESSAGES,
|
|
||||||
self.MODIFY_GC,
|
|
||||||
self.ID_BITS
|
|
||||||
]
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
|
|
||||||
MISC_SECTION = MiscSection()
|
|
@ -1,59 +0,0 @@
|
|||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from ...path_manager import LOCATIONS
|
|
||||||
from ..base_classes import Section, StringAttribute, ListAttribute
|
|
||||||
|
|
||||||
|
|
||||||
class PathAttribute(StringAttribute):
|
|
||||||
@property
|
|
||||||
def object_from_value(self) -> Path:
|
|
||||||
return Path(self.value.strip())
|
|
||||||
|
|
||||||
|
|
||||||
class PathsSection(Section):
|
|
||||||
def __init__(self):
|
|
||||||
self.MUSIC_DIRECTORY = PathAttribute(
|
|
||||||
name="music_directory",
|
|
||||||
description="The directory, all the music will be downloaded to.",
|
|
||||||
value=str(LOCATIONS.MUSIC_DIRECTORY)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.TEMP_DIRECTORY = PathAttribute(
|
|
||||||
name="temp_directory",
|
|
||||||
description="All temporary stuff is gonna be dumped in this directory.",
|
|
||||||
value=str(LOCATIONS.TEMP_DIRECTORY)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.LOG_PATH = PathAttribute(
|
|
||||||
name="log_file",
|
|
||||||
description="The path to the logging file",
|
|
||||||
value=str(LOCATIONS.get_log_file("download_logs.log"))
|
|
||||||
)
|
|
||||||
|
|
||||||
self.NOT_A_GENRE_REGEX = ListAttribute(
|
|
||||||
name="not_a_genre_regex",
|
|
||||||
description="These regular expressions tell music-kraken, which sub-folders of the music-directory\n"
|
|
||||||
"it should ignore, and not count to genres",
|
|
||||||
value=[
|
|
||||||
r'^\.' # is hidden/starts with a "."
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.FFMPEG_BINARY = PathAttribute(
|
|
||||||
name="ffmpeg_binary",
|
|
||||||
description="Set the path to the ffmpeg binary.",
|
|
||||||
value=str(LOCATIONS.FFMPEG_BIN)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.attribute_list = [
|
|
||||||
self.MUSIC_DIRECTORY,
|
|
||||||
self.TEMP_DIRECTORY,
|
|
||||||
self.LOG_PATH,
|
|
||||||
self.NOT_A_GENRE_REGEX,
|
|
||||||
self.FFMPEG_BINARY
|
|
||||||
]
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
|
|
||||||
PATHS_SECTION = PathsSection()
|
|
@ -2,9 +2,9 @@ import random
|
|||||||
|
|
||||||
from .config import main_settings
|
from .config import main_settings
|
||||||
|
|
||||||
DEBUG = False
|
DEBUG = True
|
||||||
DEBUG_YOUTUBE_INITIALIZING = DEBUG and False
|
DEBUG_YOUTUBE_INITIALIZING = DEBUG and False
|
||||||
DEBUG_PAGES = DEBUG and False
|
DEBUG_PAGES = DEBUG and True
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print("DEBUG ACTIVE")
|
print("DEBUG ACTIVE")
|
||||||
|
Loading…
Reference in New Issue
Block a user