Files
FastAnime/fastanime/core/config/model.py

371 lines
12 KiB
Python

from pathlib import Path
from typing import Literal
from pydantic import BaseModel, Field, PrivateAttr, computed_field
from ...libs.media_api.types import MediaSort, UserMediaListSort
from ...libs.provider.anime.types import ProviderName, ProviderServer
from ..constants import APP_ASCII_ART
from . import defaults
from . import descriptions as desc
class GeneralConfig(BaseModel):
"""Configuration for general application behavior and integrations."""
preferred_tracker: Literal["local", "remote"] = Field(
default=defaults.GENERAL_PREFERRED_TRACKER,
description=desc.GENERAL_PREFERRED_TRACKER,
)
pygment_style: str = Field(
default=defaults.GENERAL_PYGMENT_STYLE, description=desc.GENERAL_PYGMENT_STYLE
)
media_api: Literal["anilist", "jikan"] = Field(
default=defaults.GENERAL_API_CLIENT,
description=desc.GENERAL_API_CLIENT,
)
provider: ProviderName = Field(
default=ProviderName.ALLANIME,
description=desc.GENERAL_PROVIDER,
)
selector: Literal["default", "fzf", "rofi"] = Field(
default_factory=defaults.GENERAL_SELECTOR,
description=desc.GENERAL_SELECTOR,
)
auto_select_anime_result: bool = Field(
default=defaults.GENERAL_AUTO_SELECT_ANIME_RESULT,
description=desc.GENERAL_AUTO_SELECT_ANIME_RESULT,
)
icons: bool = Field(default=defaults.GENERAL_ICONS, description=desc.GENERAL_ICONS)
preview: Literal["full", "text", "image", "none"] = Field(
default_factory=defaults.GENERAL_PREVIEW,
description=desc.GENERAL_PREVIEW,
)
image_renderer: Literal["icat", "chafa", "imgcat"] = Field(
default_factory=defaults.GENERAL_IMAGE_RENDERER,
description=desc.GENERAL_IMAGE_RENDERER,
)
manga_viewer: Literal["feh", "icat"] = Field(
default=defaults.GENERAL_MANGA_VIEWER,
description=desc.GENERAL_MANGA_VIEWER,
)
check_for_updates: bool = Field(
default=defaults.GENERAL_CHECK_FOR_UPDATES,
description=desc.GENERAL_CHECK_FOR_UPDATES,
)
cache_requests: bool = Field(
default=defaults.GENERAL_CACHE_REQUESTS,
description=desc.GENERAL_CACHE_REQUESTS,
)
max_cache_lifetime: str = Field(
default=defaults.GENERAL_MAX_CACHE_LIFETIME,
description=desc.GENERAL_MAX_CACHE_LIFETIME,
)
normalize_titles: bool = Field(
default=defaults.GENERAL_NORMALIZE_TITLES,
description=desc.GENERAL_NORMALIZE_TITLES,
)
discord: bool = Field(
default=defaults.GENERAL_DISCORD,
description=desc.GENERAL_DISCORD,
)
recent: int = Field(
default=defaults.GENERAL_RECENT,
ge=0,
description=desc.GENERAL_RECENT,
)
class StreamConfig(BaseModel):
"""Configuration specific to video streaming and playback."""
player: Literal["mpv", "vlc"] = Field(
default=defaults.STREAM_PLAYER,
description=desc.STREAM_PLAYER,
)
quality: Literal["360", "480", "720", "1080"] = Field(
default=defaults.STREAM_QUALITY,
description=desc.STREAM_QUALITY,
)
translation_type: Literal["sub", "dub"] = Field(
default=defaults.STREAM_TRANSLATION_TYPE,
description=desc.STREAM_TRANSLATION_TYPE,
)
server: ProviderServer = Field(
default=ProviderServer.TOP,
description=desc.STREAM_SERVER,
)
auto_next: bool = Field(
default=defaults.STREAM_AUTO_NEXT,
description=desc.STREAM_AUTO_NEXT,
)
continue_from_watch_history: bool = Field(
default=defaults.STREAM_CONTINUE_FROM_WATCH_HISTORY,
description=desc.STREAM_CONTINUE_FROM_WATCH_HISTORY,
)
preferred_watch_history: Literal["local", "remote"] = Field(
default=defaults.STREAM_PREFERRED_WATCH_HISTORY,
description=desc.STREAM_PREFERRED_WATCH_HISTORY,
)
auto_skip: bool = Field(
default=defaults.STREAM_AUTO_SKIP,
description=desc.STREAM_AUTO_SKIP,
)
episode_complete_at: int = Field(
default=defaults.STREAM_EPISODE_COMPLETE_AT,
ge=0,
le=100,
description=desc.STREAM_EPISODE_COMPLETE_AT,
)
ytdlp_format: str = Field(
default=defaults.STREAM_YTDLP_FORMAT,
description=desc.STREAM_YTDLP_FORMAT,
)
force_forward_tracking: bool = Field(
default=defaults.STREAM_FORCE_FORWARD_TRACKING,
description=desc.STREAM_FORCE_FORWARD_TRACKING,
)
default_media_list_tracking: Literal["track", "disabled", "prompt"] = Field(
default=defaults.STREAM_DEFAULT_MEDIA_LIST_TRACKING,
description=desc.STREAM_DEFAULT_MEDIA_LIST_TRACKING,
)
sub_lang: str = Field(
default=defaults.STREAM_SUB_LANG,
description=desc.STREAM_SUB_LANG,
)
use_ipc: bool = Field(
default_factory=defaults.STREAM_USE_IPC,
description=desc.STREAM_USE_IPC,
)
class OtherConfig(BaseModel):
pass
class SessionsConfig(OtherConfig):
dir: Path = Field(
default_factory=lambda: defaults.SESSIONS_DIR,
description=desc.SESSIONS_DIR,
)
class FzfConfig(OtherConfig):
"""Configuration specific to the FZF selector."""
_opts: str = PrivateAttr(
default_factory=lambda: defaults.FZF_OPTS.read_text(encoding="utf-8")
)
header_color: str = Field(
default=defaults.FZF_HEADER_COLOR, description=desc.FZF_HEADER_COLOR
)
_header_ascii_art: str = PrivateAttr(
default_factory=lambda: APP_ASCII_ART.read_text(encoding="utf-8")
)
preview_header_color: str = Field(
default=defaults.FZF_PREVIEW_HEADER_COLOR,
description=desc.FZF_PREVIEW_HEADER_COLOR,
)
preview_separator_color: str = Field(
default=defaults.FZF_PREVIEW_SEPARATOR_COLOR,
description=desc.FZF_PREVIEW_SEPARATOR_COLOR,
)
def __init__(self, **kwargs):
opts = kwargs.pop("opts", None)
header_ascii_art = kwargs.pop("header_ascii_art", None)
super().__init__(**kwargs)
if opts:
self._opts = opts
if header_ascii_art:
self._header_ascii_art = header_ascii_art
@computed_field(description=desc.FZF_OPTS)
@property
def opts(self) -> str:
return "\n" + "\n".join([f"\t{line}" for line in self._opts.split()])
@computed_field(description=desc.FZF_HEADER_ASCII_ART)
@property
def header_ascii_art(self) -> str:
return "\n" + "\n".join(
[f"\t{line}" for line in self._header_ascii_art.split()]
)
class RofiConfig(OtherConfig):
"""Configuration specific to the Rofi selector."""
theme_main: Path = Field(
default_factory=lambda: Path(str(defaults.ROFI_THEME_MAIN)),
description=desc.ROFI_THEME_MAIN,
)
theme_preview: Path = Field(
default_factory=lambda: Path(str(defaults.ROFI_THEME_PREVIEW)),
description=desc.ROFI_THEME_PREVIEW,
)
theme_confirm: Path = Field(
default_factory=lambda: Path(str(defaults.ROFI_THEME_CONFIRM)),
description=desc.ROFI_THEME_CONFIRM,
)
theme_input: Path = Field(
default_factory=lambda: Path(str(defaults.ROFI_THEME_INPUT)),
description=desc.ROFI_THEME_INPUT,
)
class MpvConfig(OtherConfig):
"""Configuration specific to the MPV player integration."""
args: str = Field(default=defaults.MPV_ARGS, description=desc.MPV_ARGS)
pre_args: str = Field(
default=defaults.MPV_PRE_ARGS,
description=desc.MPV_PRE_ARGS,
)
class VlcConfig(OtherConfig):
"""Configuration specific to the vlc player integration."""
args: str = Field(default=defaults.VLC_ARGS, description=desc.VLC_ARGS)
class AnilistConfig(OtherConfig):
"""Configuration for interacting with the AniList API."""
per_page: int = Field(
default=defaults.ANILIST_PER_PAGE,
gt=0,
le=50,
description=desc.ANILIST_PER_PAGE,
)
sort_by: MediaSort = Field(
default=MediaSort.SEARCH_MATCH,
description=desc.ANILIST_SORT_BY,
)
media_list_sort_by: UserMediaListSort = Field(
default=UserMediaListSort.MEDIA_POPULARITY_DESC,
description=desc.ANILIST_MEDIA_LIST_SORT_BY,
)
preferred_language: Literal["english", "romaji"] = Field(
default=defaults.ANILIST_PREFERRED_LANGUAGE,
description=desc.ANILIST_PREFERRED_LANGUAGE,
)
class JikanConfig(OtherConfig):
"""Configuration for the Jikan API (currently none)."""
per_page: int = Field(
default=defaults.ANILIST_PER_PAGE,
gt=0,
le=50,
description=desc.ANILIST_PER_PAGE,
)
sort_by: MediaSort = Field(
default=MediaSort.SEARCH_MATCH,
description=desc.ANILIST_SORT_BY,
)
media_list_sort_by: UserMediaListSort = Field(
default=UserMediaListSort.MEDIA_POPULARITY_DESC,
description=desc.ANILIST_MEDIA_LIST_SORT_BY,
)
preferred_language: Literal["english", "romaji"] = Field(
default=defaults.ANILIST_PREFERRED_LANGUAGE,
description=desc.ANILIST_PREFERRED_LANGUAGE,
)
class DownloadsConfig(OtherConfig):
"""Configuration for download related options"""
downloader: Literal["auto", "default", "yt-dlp"] = Field(
default=defaults.DOWNLOADS_DOWNLOADER, description=desc.DOWNLOADS_DOWNLOADER
)
downloads_dir: Path = Field(
default_factory=lambda: defaults.DOWNLOADS_DOWNLOADS_DIR,
description=desc.DOWNLOADS_DOWNLOADS_DIR,
)
enable_tracking: bool = Field(
default=defaults.DOWNLOADS_ENABLE_TRACKING,
description=desc.DOWNLOADS_ENABLE_TRACKING,
)
max_concurrent_downloads: int = Field(
default=defaults.DOWNLOADS_MAX_CONCURRENT,
ge=1,
description=desc.DOWNLOADS_MAX_CONCURRENT,
)
retry_attempts: int = Field(
default=defaults.DOWNLOADS_RETRY_ATTEMPTS,
ge=0,
description=desc.DOWNLOADS_RETRY_ATTEMPTS,
)
retry_delay: int = Field(
default=defaults.DOWNLOADS_RETRY_DELAY,
ge=0,
description=desc.DOWNLOADS_RETRY_DELAY,
)
merge_subtitles: bool = Field(
default=defaults.DOWNLOADS_MERGE_SUBTITLES,
description=desc.DOWNLOADS_MERGE_SUBTITLES,
)
cleanup_after_merge: bool = Field(
default=defaults.DOWNLOADS_CLEANUP_AFTER_MERGE,
description=desc.DOWNLOADS_CLEANUP_AFTER_MERGE,
)
class MediaRegistryConfig(OtherConfig):
"""Configuration for registry related options"""
media_dir: Path = Field(
default=defaults.MEDIA_REGISTRY_DIR,
description=desc.MEDIA_REGISTRY_DIR,
)
index_dir: Path = Field(
default=defaults.MEDIA_REGISTRY_INDEX_DIR,
description=desc.MEDIA_REGISTRY_INDEX_DIR,
)
class AppConfig(BaseModel):
"""The root configuration model for the FastAnime application."""
general: GeneralConfig = Field(
default_factory=GeneralConfig,
description=desc.APP_GENERAL,
)
stream: StreamConfig = Field(
default_factory=StreamConfig,
description=desc.APP_STREAM,
)
downloads: DownloadsConfig = Field(
default_factory=DownloadsConfig, description=desc.APP_DOWNLOADS
)
anilist: AnilistConfig = Field(
default_factory=AnilistConfig,
description=desc.APP_ANILIST,
)
jikan: JikanConfig = Field(
default_factory=JikanConfig,
description=desc.APP_JIKAN,
)
fzf: FzfConfig = Field(
default_factory=FzfConfig,
description=desc.APP_FZF,
)
rofi: RofiConfig = Field(
default_factory=RofiConfig,
description=desc.APP_ROFI,
)
mpv: MpvConfig = Field(default_factory=MpvConfig, description=desc.APP_MPV)
media_registry: MediaRegistryConfig = Field(
default_factory=MediaRegistryConfig, description=desc.APP_MEDIA_REGISTRY
)
sessions: SessionsConfig = Field(
default_factory=SessionsConfig, description=desc.APP_SESSIONS
)