/
/
1"""Constants for the Filesystem Local provider."""
2
3from __future__ import annotations
4
5from dataclasses import replace
6from typing import Final
7
8from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption
9from music_assistant_models.enums import ConfigEntryType
10
11CONF_MISSING_ALBUM_ARTIST_ACTION = "missing_album_artist_action"
12CONF_CONTENT_TYPE = "content_type"
13
14CONF_ENTRY_MISSING_ALBUM_ARTIST = ConfigEntry(
15 key=CONF_MISSING_ALBUM_ARTIST_ACTION,
16 type=ConfigEntryType.STRING,
17 default_value="various_artists",
18 help_link="https://music-assistant.io/music-providers/local-files/#tagging-files",
19 required=False,
20 options=[
21 ConfigValueOption("track_artist"),
22 ConfigValueOption("various_artists"),
23 ConfigValueOption("folder_name"),
24 ],
25 depends_on=CONF_CONTENT_TYPE,
26 depends_on_value="music",
27)
28
29
30CONF_ENTRY_PATH = ConfigEntry(
31 key="path",
32 type=ConfigEntryType.STRING,
33 default_value="/media",
34)
35
36CONF_ENTRY_CONTENT_TYPE = ConfigEntry(
37 key=CONF_CONTENT_TYPE,
38 type=ConfigEntryType.STRING,
39 default_value="music",
40 required=False,
41 options=[
42 ConfigValueOption("music"),
43 ConfigValueOption("audiobooks"),
44 ConfigValueOption("podcasts"),
45 ConfigValueOption("sound_effects"),
46 ],
47)
48
49
50def content_type_config_entry(content_type: str) -> ConfigEntry:
51 """
52 Return the read-only mirror of the (setup flow owned) content type for the options page.
53
54 :param content_type: The content type resolved from the provider's setup data.
55 """
56 # mirrored as the entry default so the other entries resolve their depends_on chain
57 # against it without it ever being persisted back into the stored values
58 return replace(CONF_ENTRY_CONTENT_TYPE, read_only=True, default_value=content_type)
59
60
61CONF_ENTRY_LIBRARY_SYNC_TRACKS = ConfigEntry(
62 key="library_sync_tracks",
63 type=ConfigEntryType.BOOLEAN,
64 default_value=True,
65 category="sync_options",
66 depends_on=CONF_CONTENT_TYPE,
67 depends_on_value="music",
68)
69CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS = ConfigEntry(
70 key="library_sync_playlists",
71 type=ConfigEntryType.BOOLEAN,
72 default_value=True,
73 category="sync_options",
74 depends_on=CONF_CONTENT_TYPE,
75 depends_on_value="music",
76)
77CONF_ENTRY_LIBRARY_SYNC_PODCASTS = ConfigEntry(
78 key="library_sync_podcasts",
79 type=ConfigEntryType.BOOLEAN,
80 default_value=True,
81 category="sync_options",
82 depends_on=CONF_CONTENT_TYPE,
83 depends_on_value="podcasts",
84)
85CONF_ENTRY_LIBRARY_SYNC_AUDIOBOOKS = ConfigEntry(
86 key="library_sync_audiobooks",
87 type=ConfigEntryType.BOOLEAN,
88 default_value=True,
89 category="sync_options",
90 depends_on=CONF_CONTENT_TYPE,
91 depends_on_value="audiobooks",
92)
93
94CONF_ENTRY_PROPAGATE_GENRES = ConfigEntry(
95 key="propagate_track_genres",
96 type=ConfigEntryType.BOOLEAN,
97 default_value=False,
98 required=False,
99 category="sync_options",
100 depends_on=CONF_CONTENT_TYPE,
101 depends_on_value="music",
102)
103
104CONF_ENTRY_IGNORE_ALBUM_PLAYLISTS = ConfigEntry(
105 key="ignore_album_playlists",
106 type=ConfigEntryType.BOOLEAN,
107 default_value=True,
108 required=False,
109 depends_on=CONF_CONTENT_TYPE,
110 depends_on_value="music",
111)
112
113TRACK_EXTENSIONS = {
114 "aac",
115 "mp3",
116 "m4a",
117 "mp4",
118 "flac",
119 "wav",
120 "ogg",
121 "aiff",
122 "wma",
123 "dsf",
124 "opus",
125 "wv",
126 "amr",
127 "awb",
128 "spx",
129 "tak",
130 "ape",
131 "mpc",
132 "mp2",
133 "m2a",
134 "mp1",
135 "dra",
136 "mpeg",
137 "mpg",
138 "ac3",
139 "ec3",
140 "aif",
141 "oga",
142 "dff",
143 "ts",
144 "m2ts",
145 "mp+",
146}
147PLAYLIST_EXTENSIONS = {"m3u", "pls", "m3u8"}
148CUE_EXTENSIONS = {"cue"}
149IMAGE_EXTENSIONS = {"jpg", "jpeg", "png", "gif"}
150AUDIOBOOK_EXTENSIONS = {"aa", "aax", "m4b", "m4a", "mp3", "mp4", "flac", "ogg", "opus"}
151PODCAST_EPISODE_EXTENSIONS = {"aa", "aax", "m4b", "m4a", "mp3", "mp4", "flac", "ogg", "opus"}
152SOUND_EFFECT_EXTENSIONS = TRACK_EXTENSIONS
153SUPPORTED_EXTENSIONS = {
154 *TRACK_EXTENSIONS,
155 *AUDIOBOOK_EXTENSIONS,
156 *PODCAST_EPISODE_EXTENSIONS,
157 *PLAYLIST_EXTENSIONS,
158 *CUE_EXTENSIONS,
159}
160
161
162class IsChapterFile(Exception):
163 """Exception to indicate that a file is part of a multi-part media (e.g. audiobook chapter)."""
164
165
166CACHE_CATEGORY_ARTIST_INFO: Final[int] = 1
167CACHE_CATEGORY_ALBUM_INFO: Final[int] = 2
168CACHE_CATEGORY_FOLDER_IMAGES: Final[int] = 3
169CACHE_CATEGORY_AUDIOBOOK_CHAPTERS: Final[int] = 4
170CACHE_CATEGORY_PODCAST_METADATA: Final[int] = 5
171CACHE_CATEGORY_CUE_SHEETS: Final[int] = 6
172CACHE_CATEGORY_SOUND_EFFECTS: Final[int] = 7
173CACHE_CATEGORY_PODCAST_EPISODES: Final[int] = 8
174
175# how long a podcast episode listing that lost a file to a parse failure is cached for:
176# the missing episode cannot reappear any sooner than this
177PARTIAL_LISTING_CACHE_EXPIRATION: Final[int] = 300
178
179DEFAULT_AUDIOBOOK_PODCAST_GENRE: Final[str] = "Spoken Word"
180
181# how often storage that went away during a scan is re-checked, so the provider comes
182# back within minutes instead of waiting for the next scheduled sync
183AVAILABILITY_PROBE_INTERVAL: Final[int] = 300
184