TonieToolbox 0.5.0a1__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
TonieToolbox/artwork.py CHANGED
@@ -4,6 +4,7 @@ Artwork handling functionality for TonieToolbox.
4
4
  """
5
5
 
6
6
  import os
7
+ import base64
7
8
  import tempfile
8
9
  import shutil
9
10
  from typing import List, Optional, Tuple
@@ -12,20 +13,25 @@ from .logger import get_logger
12
13
  from .teddycloud import TeddyCloudClient
13
14
  from .media_tags import extract_artwork, find_cover_image
14
15
 
16
+ logger = get_logger(__name__)
15
17
 
16
- def upload_artwork(client: TeddyCloudClient, taf_filename, source_path, audio_files) -> Tuple[bool, Optional[str]]:
18
+ def upload_artwork(
19
+ client: TeddyCloudClient,
20
+ taf_filename: str,
21
+ source_path: str,
22
+ audio_files: list[str],
23
+ ) -> tuple[bool, Optional[str]]:
17
24
  """
18
25
  Find and upload artwork for a Tonie file.
19
-
26
+
20
27
  Args:
21
- client: TeddyCloudClient instance to use for API communication
22
- taf_filename: The filename of the Tonie file (.taf)
23
- source_path: Source directory to look for artwork
24
- audio_files: List of audio files to extract artwork from if needed
28
+ client (TeddyCloudClient): TeddyCloudClient instance to use for API communication
29
+ taf_filename (str): The filename of the Tonie file (.taf)
30
+ source_path (str): Source directory to look for artwork
31
+ audio_files (list[str]): List of audio files to extract artwork from if needed
25
32
  Returns:
26
- tuple: (success, artwork_url) where success is a boolean and artwork_url is the URL of the uploaded artwork
27
- """
28
- logger = get_logger('artwork')
33
+ tuple[bool, Optional[str]]: (success, artwork_url) where success is a boolean and artwork_url is the URL of the uploaded artwork
34
+ """
29
35
  logger.info("Looking for artwork for Tonie file: %s", taf_filename)
30
36
  taf_basename = os.path.basename(taf_filename)
31
37
  taf_name = os.path.splitext(taf_basename)[0]
@@ -102,4 +108,47 @@ def upload_artwork(client: TeddyCloudClient, taf_filename, source_path, audio_fi
102
108
  except Exception as e:
103
109
  logger.debug("Failed to remove temporary artwork file: %s", e)
104
110
 
105
- return upload_success, artwork_url
111
+ return upload_success, artwork_url
112
+
113
+ def ico_to_base64(ico_path):
114
+ """
115
+ Convert an ICO file to a base64 string
116
+
117
+ Args:
118
+ ico_path: Path to the ICO file
119
+
120
+ Returns:
121
+ Base64 encoded string of the ICO file
122
+ """
123
+ if not os.path.exists(ico_path):
124
+ raise FileNotFoundError(f"ICO file not found: {ico_path}")
125
+
126
+ with open(ico_path, "rb") as ico_file:
127
+ ico_bytes = ico_file.read()
128
+
129
+ base64_string = base64.b64encode(ico_bytes).decode('utf-8')
130
+ return base64_string
131
+
132
+
133
+ def base64_to_ico(base64_string, output_path):
134
+ """
135
+ Convert a base64 string back to an ICO file
136
+
137
+ Args:
138
+ base64_string: Base64 encoded string of the ICO file
139
+ output_path: Path where to save the ICO file
140
+
141
+ Returns:
142
+ Path to the saved ICO file
143
+ """
144
+ ico_bytes = base64.b64decode(base64_string)
145
+
146
+ # Create directory if it doesn't exist
147
+ output_dir = os.path.dirname(output_path)
148
+ if output_dir and not os.path.exists(output_dir):
149
+ os.makedirs(output_dir)
150
+
151
+ with open(output_path, "wb") as ico_file:
152
+ ico_file.write(ico_bytes)
153
+
154
+ return output_path
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/python3
1
2
  """
2
3
  Audio conversion functionality for the TonieToolbox package
3
4
  """
@@ -7,29 +8,39 @@ import glob
7
8
  import subprocess
8
9
  import tempfile
9
10
  from .dependency_manager import get_ffmpeg_binary, get_opus_binary
11
+ from .constants import SUPPORTED_EXTENSIONS
10
12
  from .logger import get_logger
11
13
 
12
- logger = get_logger('audio_conversion')
14
+ logger = get_logger(__name__)
13
15
 
14
16
 
15
- def get_opus_tempfile(ffmpeg_binary=None, opus_binary=None, filename=None, bitrate=48, vbr=True, keep_temp=False, auto_download=False):
17
+ def get_opus_tempfile(
18
+ ffmpeg_binary: str = None,
19
+ opus_binary: str = None,
20
+ filename: str = None,
21
+ bitrate: int = 48,
22
+ vbr: bool = True,
23
+ keep_temp: bool = False,
24
+ auto_download: bool = False,
25
+ no_mono_conversion: bool = False
26
+ ) -> tuple[tempfile.SpooledTemporaryFile | None, str | None]:
16
27
  """
17
28
  Convert an audio file to Opus format and return a temporary file handle.
18
29
 
19
30
  Args:
20
- ffmpeg_binary: Path to the ffmpeg binary. If None, will be auto-detected or downloaded.
21
- opus_binary: Path to the opusenc binary. If None, will be auto-detected or downloaded.
22
- filename: Path to the input audio file
23
- bitrate: Bitrate for the Opus encoding in kbps
24
- vbr: Whether to use variable bitrate encoding
25
- keep_temp: Whether to keep the temporary files for testing
26
- auto_download: Whether to automatically download dependencies if not found
27
-
31
+ ffmpeg_binary (str | None): Path to the ffmpeg binary. If None, will be auto-detected or downloaded.
32
+ opus_binary (str | None): Path to the opusenc binary. If None, will be auto-detected or downloaded.
33
+ filename (str | None): Path to the input audio file
34
+ bitrate (int): Bitrate for the Opus encoding in kbps
35
+ vbr (bool): Whether to use variable bitrate encoding
36
+ keep_temp (bool): Whether to keep the temporary files for testing
37
+ auto_download (bool): Whether to automatically download dependencies if not found
38
+ no_mono_conversion (bool): Whether to skip mono to stereo conversion
28
39
  Returns:
29
- tuple: (file handle, temp_file_path) or (file handle, None) if keep_temp is False
40
+ tuple[tempfile.SpooledTemporaryFile | None, str | None]: (file handle, temp_file_path) or (file handle, None) if keep_temp is False
30
41
  """
31
- logger.trace("Entering get_opus_tempfile(ffmpeg_binary=%s, opus_binary=%s, filename=%s, bitrate=%d, vbr=%s, keep_temp=%s, auto_download=%s)",
32
- ffmpeg_binary, opus_binary, filename, bitrate, vbr, keep_temp, auto_download)
42
+ logger.trace("Entering get_opus_tempfile(ffmpeg_binary=%s, opus_binary=%s, filename=%s, bitrate=%d, vbr=%s, keep_temp=%s, auto_download=%s, no_mono_conversion=%s)",
43
+ ffmpeg_binary, opus_binary, filename, bitrate, vbr, keep_temp, auto_download, no_mono_conversion)
33
44
 
34
45
  logger.debug("Converting %s to Opus format (bitrate: %d kbps, vbr: %s)", filename, bitrate, vbr)
35
46
 
@@ -52,6 +63,38 @@ def get_opus_tempfile(ffmpeg_binary=None, opus_binary=None, filename=None, bitra
52
63
  vbr_parameter = "--vbr" if vbr else "--hard-cbr"
53
64
  logger.debug("Using encoding parameter: %s", vbr_parameter)
54
65
 
66
+ is_mono = False
67
+ ffprobe_path = None
68
+ ffmpeg_dir, ffmpeg_file = os.path.split(ffmpeg_binary)
69
+ ffprobe_candidates = [
70
+ os.path.join(ffmpeg_dir, 'ffprobe'),
71
+ os.path.join(ffmpeg_dir, 'ffprobe.exe'),
72
+ 'ffprobe',
73
+ 'ffprobe.exe',
74
+ ]
75
+ for candidate in ffprobe_candidates:
76
+ if os.path.exists(candidate):
77
+ ffprobe_path = candidate
78
+ break
79
+ if ffprobe_path:
80
+ try:
81
+ probe_cmd = [ffprobe_path, '-v', 'error', '-select_streams', 'a:0', '-show_entries', 'stream=channels', '-of', 'default=noprint_wrappers=1:nokey=1', filename]
82
+ logger.debug(f"Probing audio channels with: {' '.join(probe_cmd)}")
83
+ result = subprocess.run(probe_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
84
+ if result.returncode == 0:
85
+ channels = result.stdout.strip()
86
+ logger.debug(f"Detected channels: {channels}")
87
+ if channels == '1':
88
+ is_mono = True
89
+ else:
90
+ logger.warning(f"ffprobe failed to detect channels: {result.stderr}")
91
+ except Exception as e:
92
+ logger.warning(f"Mono detection failed: {e}")
93
+ else:
94
+ logger.warning("ffprobe not found, will always force stereo conversion for non-Opus input.")
95
+ is_mono = True # Always force stereo if we can't check
96
+ logger.info(f"Mono detected: {is_mono}, no_mono_conversion: {no_mono_conversion}")
97
+
55
98
  temp_path = None
56
99
  if keep_temp:
57
100
  temp_dir = os.path.join(tempfile.gettempdir(), "tonie_toolbox_temp")
@@ -62,7 +105,12 @@ def get_opus_tempfile(ffmpeg_binary=None, opus_binary=None, filename=None, bitra
62
105
 
63
106
  logger.debug("Starting FFmpeg process")
64
107
  try:
65
- ffmpeg_cmd = [ffmpeg_binary, "-hide_banner", "-loglevel", "warning", "-i", filename, "-f", "wav", "-ar", "48000", "-"]
108
+ if is_mono and not no_mono_conversion:
109
+ ffmpeg_cmd = [ffmpeg_binary, "-hide_banner", "-loglevel", "warning", "-i", filename, "-f", "wav", "-ar", "48000", "-ac", "2", "-"]
110
+ logger.info(f"Forcing stereo conversion for mono input: {' '.join(ffmpeg_cmd)}")
111
+ else:
112
+ ffmpeg_cmd = [ffmpeg_binary, "-hide_banner", "-loglevel", "warning", "-i", filename, "-f", "wav", "-ar", "48000", "-"]
113
+ logger.info(f"FFmpeg command: {' '.join(ffmpeg_cmd)}")
66
114
  logger.trace("FFmpeg command: %s", ffmpeg_cmd)
67
115
  ffmpeg_process = subprocess.Popen(ffmpeg_cmd, stdout=subprocess.PIPE)
68
116
  except FileNotFoundError:
@@ -106,7 +154,12 @@ def get_opus_tempfile(ffmpeg_binary=None, opus_binary=None, filename=None, bitra
106
154
 
107
155
  logger.debug("Starting FFmpeg process")
108
156
  try:
109
- ffmpeg_cmd = [ffmpeg_binary, "-hide_banner", "-loglevel", "warning", "-i", filename, "-f", "wav", "-ar", "48000", "-"]
157
+ if is_mono and not no_mono_conversion:
158
+ ffmpeg_cmd = [ffmpeg_binary, "-hide_banner", "-loglevel", "warning", "-i", filename, "-f", "wav", "-ar", "48000", "-ac", "2", "-"]
159
+ logger.info(f"Forcing stereo conversion for mono input: {' '.join(ffmpeg_cmd)}")
160
+ else:
161
+ ffmpeg_cmd = [ffmpeg_binary, "-hide_banner", "-loglevel", "warning", "-i", filename, "-f", "wav", "-ar", "48000", "-"]
162
+ logger.info(f"FFmpeg command: {' '.join(ffmpeg_cmd)}")
110
163
  logger.trace("FFmpeg command: %s", ffmpeg_cmd)
111
164
  ffmpeg_process = subprocess.Popen(ffmpeg_cmd, stdout=subprocess.PIPE)
112
165
  except FileNotFoundError:
@@ -155,24 +208,20 @@ def get_opus_tempfile(ffmpeg_binary=None, opus_binary=None, filename=None, bitra
155
208
  return tmp_file, None
156
209
 
157
210
 
158
- def filter_directories(glob_list):
211
+ def filter_directories(glob_list: list[str]) -> list[str]:
159
212
  """
160
213
  Filter a list of glob results to include only audio files that can be handled by ffmpeg.
161
214
 
162
215
  Args:
163
- glob_list: List of path names from glob.glob()
164
-
216
+ glob_list (list[str]): List of path names from glob.glob()
165
217
  Returns:
166
- list: Filtered list containing only supported audio files
218
+ list[str]: Filtered list containing only supported audio files
167
219
  """
168
220
  logger.trace("Entering filter_directories() with %d items", len(glob_list))
169
221
  logger.debug("Filtering %d glob results for supported audio files", len(glob_list))
170
222
 
171
- # Common audio file extensions supported by ffmpeg
172
- supported_extensions = [
173
- '.wav', '.mp3', '.aac', '.m4a', '.flac', '.ogg', '.opus',
174
- '.ape', '.wma', '.aiff', '.mp2', '.mp4', '.webm', '.mka'
175
- ]
223
+ supported_extensions = SUPPORTED_EXTENSIONS
224
+ logger.debug("Supported audio file extensions: %s", supported_extensions)
176
225
 
177
226
  filtered = []
178
227
  for name in glob_list:
@@ -189,17 +238,16 @@ def filter_directories(glob_list):
189
238
  return filtered
190
239
 
191
240
 
192
- def get_input_files(input_filename):
241
+ def get_input_files(input_filename: str) -> list[str]:
193
242
  """
194
243
  Get a list of input files to process.
195
244
 
196
245
  Supports direct file paths, directory paths, glob patterns, and .lst files.
197
246
 
198
247
  Args:
199
- input_filename: Input file pattern or list file path
200
-
248
+ input_filename (str): Input file pattern or list file path
201
249
  Returns:
202
- list: List of input file paths
250
+ list[str]: List of input file paths
203
251
  """
204
252
  logger.trace("Entering get_input_files(input_filename=%s)", input_filename)
205
253
  logger.debug("Getting input files for pattern: %s", input_filename)
@@ -244,22 +292,46 @@ def get_input_files(input_filename):
244
292
 
245
293
  logger.debug("Found %d files in list file", len(input_files))
246
294
  else:
247
- logger.debug("Processing glob pattern: %s", input_filename)
295
+ logger.debug("Processing input path: %s", input_filename)
296
+
297
+ # Try the exact pattern first
248
298
  input_files = sorted(filter_directories(glob.glob(input_filename)))
249
- logger.debug("Found %d files matching pattern", len(input_files))
299
+ if input_files:
300
+ logger.debug("Found %d files matching exact pattern", len(input_files))
301
+ else:
302
+ # If no extension is provided, try appending a wildcard for extension
303
+ _, ext = os.path.splitext(input_filename)
304
+ if not ext:
305
+ wildcard_pattern = input_filename + ".*"
306
+ logger.debug("No extension in pattern, trying with wildcard: %s", wildcard_pattern)
307
+ input_files = sorted(filter_directories(glob.glob(wildcard_pattern)))
308
+
309
+ # If still no files found, try treating it as a directory
310
+ if not input_files and os.path.exists(os.path.dirname(input_filename)):
311
+ potential_dir = input_filename
312
+ if os.path.isdir(potential_dir):
313
+ logger.debug("Treating input as directory: %s", potential_dir)
314
+ dir_glob = os.path.join(potential_dir, "*")
315
+ input_files = sorted(filter_directories(glob.glob(dir_glob)))
316
+ if input_files:
317
+ logger.debug("Found %d audio files in directory", len(input_files))
318
+
319
+ if input_files:
320
+ logger.debug("Found %d files after trying alternatives", len(input_files))
321
+ else:
322
+ logger.warning("No files found for pattern %s even after trying alternatives", input_filename)
250
323
 
251
324
  logger.trace("Exiting get_input_files() with %d files", len(input_files))
252
325
  return input_files
253
326
 
254
327
 
255
- def append_to_filename(output_filename, tag):
328
+ def append_to_filename(output_filename: str, tag: str) -> str:
256
329
  """
257
330
  Append a tag to a filename, preserving the extension.
258
331
 
259
332
  Args:
260
- output_filename: Original filename
261
- tag: Tag to append (typically an 8-character hex value)
262
-
333
+ output_filename (str): Original filename
334
+ tag (str): Tag to append (typically an 8-character hex value)
263
335
  Returns:
264
336
  str: Modified filename with tag
265
337
  """
TonieToolbox/constants.py CHANGED
@@ -1,13 +1,14 @@
1
+ #!/usr/bin/python3
1
2
  """
2
3
  Constants used throughout the TonieToolbox package
3
4
  """
4
- SAMPLE_RATE_KHZ = 48
5
- ONLY_CONVERT_FRAMEPACKING = -1
6
- OTHER_PACKET_NEEDED = -2
7
- DO_NOTHING = -3
8
- TOO_MANY_SEGMENTS = -4
9
- TIMESTAMP_DEDUCT = 0x50000000
10
- OPUS_TAGS = [
5
+ SAMPLE_RATE_KHZ: int = 48
6
+ ONLY_CONVERT_FRAMEPACKING: int = -1
7
+ OTHER_PACKET_NEEDED: int = -2
8
+ DO_NOTHING: int = -3
9
+ TOO_MANY_SEGMENTS: int = -4
10
+ TIMESTAMP_DEDUCT: int = 0x50000000
11
+ OPUS_TAGS: list[bytearray] = [
11
12
  bytearray(
12
13
  b"\x4F\x70\x75\x73\x54\x61\x67\x73\x0D\x00\x00\x00\x4C\x61\x76\x66\x35\x38\x2E\x32\x30\x2E\x31\x30\x30\x03\x00\x00\x00\x26\x00\x00\x00\x65\x6E\x63\x6F\x64\x65\x72\x3D\x6F\x70\x75\x73\x65\x6E\x63\x20\x66\x72\x6F\x6D\x20\x6F\x70\x75\x73\x2D\x74\x6F\x6F\x6C\x73\x20\x30\x2E\x31\x2E\x31\x30\x2A\x00\x00\x00\x65\x6E\x63\x6F\x64\x65\x72\x5F\x6F\x70\x74\x69\x6F\x6E\x73\x3D\x2D\x2D\x71\x75\x69\x65\x74\x20\x2D\x2D\x62\x69\x74\x72\x61\x74\x65\x20\x39\x36\x20\x2D\x2D\x76\x62\x72\x3B\x01\x00\x00\x70\x61\x64\x3D\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30"),
13
14
  bytearray(
@@ -15,7 +16,7 @@ OPUS_TAGS = [
15
16
  ]
16
17
 
17
18
  # Mapping of language tags to ISO codes
18
- LANGUAGE_MAPPING = {
19
+ LANGUAGE_MAPPING: dict[str, str] = {
19
20
  # Common language names to ISO codes
20
21
  'deutsch': 'de-de',
21
22
  'german': 'de-de',
@@ -39,7 +40,7 @@ LANGUAGE_MAPPING = {
39
40
  }
40
41
 
41
42
  # Mapping of genre tags to tonie categories
42
- GENRE_MAPPING = {
43
+ GENRE_MAPPING: dict[str, str] = {
43
44
  # Standard Tonie category names from tonies.json
44
45
  'hörspiel': 'Hörspiele & Hörbücher',
45
46
  'hörbuch': 'Hörspiele & Hörbücher',
@@ -87,4 +88,126 @@ GENRE_MAPPING = {
87
88
 
88
89
  # Default to standard format for custom
89
90
  'custom': 'Hörspiele & Hörbücher',
90
- }
91
+ }
92
+
93
+ # Supported file extensions for audio files
94
+ SUPPORTED_EXTENSIONS = [
95
+ '.wav', '.mp3', '.aac', '.m4a', '.flac', '.ogg', '.opus',
96
+ '.ape', '.wma', '.aiff', '.mp2', '.mp4', '.webm', '.mka'
97
+ ]
98
+
99
+ UTI_MAPPINGS = {
100
+ 'mp3': 'public.mp3',
101
+ 'wav': 'public.wav',
102
+ 'flac': 'org.xiph.flac',
103
+ 'ogg': 'org.xiph.ogg',
104
+ 'opus': 'public.opus',
105
+ 'aac': 'public.aac-audio',
106
+ 'm4a': 'public.m4a-audio',
107
+ 'wma': 'com.microsoft.windows-media-wma',
108
+ 'aiff': 'public.aiff-audio',
109
+ 'mp2': 'public.mp2',
110
+ 'mp4': 'public.mpeg-4-audio',
111
+ 'mka': 'public.audio',
112
+ 'webm': 'public.webm-audio',
113
+ 'ape': 'public.audio',
114
+ 'taf': 'public.audio'
115
+ }
116
+
117
+ ARTWORK_NAMES = [
118
+ 'cover', 'folder', 'album', 'front', 'artwork', 'image',
119
+ 'albumart', 'albumartwork', 'booklet'
120
+ ]
121
+ ARTWORK_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']
122
+
123
+
124
+ TAG_VALUE_REPLACEMENTS = {
125
+ "Die drei ???": "Die drei Fragezeichen",
126
+ "Die Drei ???": "Die drei Fragezeichen",
127
+ "DIE DREI ???": "Die drei Fragezeichen",
128
+ "Die drei !!!": "Die drei Ausrufezeichen",
129
+ "Die Drei !!!": "Die drei Ausrufezeichen",
130
+ "DIE DREI !!!": "Die drei Ausrufezeichen",
131
+ "TKKG™": "TKKG",
132
+ "Die drei ??? Kids": "Die drei Fragezeichen Kids",
133
+ "Die Drei ??? Kids": "Die drei Fragezeichen Kids",
134
+ "Bibi & Tina": "Bibi und Tina",
135
+ "Benjamin Blümchen™": "Benjamin Blümchen",
136
+ "???": "Fragezeichen",
137
+ "!!!": "Ausrufezeichen",
138
+ }
139
+
140
+ TAG_MAPPINGS = {
141
+ # ID3 (MP3) tags
142
+ 'TIT2': 'title',
143
+ 'TALB': 'album',
144
+ 'TPE1': 'artist',
145
+ 'TPE2': 'albumartist',
146
+ 'TCOM': 'composer',
147
+ 'TRCK': 'tracknumber',
148
+ 'TPOS': 'discnumber',
149
+ 'TDRC': 'date',
150
+ 'TCON': 'genre',
151
+ 'TPUB': 'publisher',
152
+ 'TCOP': 'copyright',
153
+ 'COMM': 'comment',
154
+
155
+ # Vorbis tags (FLAC, OGG)
156
+ 'title': 'title',
157
+ 'album': 'album',
158
+ 'artist': 'artist',
159
+ 'albumartist': 'albumartist',
160
+ 'composer': 'composer',
161
+ 'tracknumber': 'tracknumber',
162
+ 'discnumber': 'discnumber',
163
+ 'date': 'date',
164
+ 'genre': 'genre',
165
+ 'publisher': 'publisher',
166
+ 'copyright': 'copyright',
167
+ 'comment': 'comment',
168
+
169
+ # MP4 (M4A, AAC) tags
170
+ '©nam': 'title',
171
+ '©alb': 'album',
172
+ '©ART': 'artist',
173
+ 'aART': 'albumartist',
174
+ '©wrt': 'composer',
175
+ 'trkn': 'tracknumber',
176
+ 'disk': 'discnumber',
177
+ '©day': 'date',
178
+ '©gen': 'genre',
179
+ '©pub': 'publisher',
180
+ 'cprt': 'copyright',
181
+ '©cmt': 'comment',
182
+
183
+ # Additional tags some files might have
184
+ 'album_artist': 'albumartist',
185
+ 'track': 'tracknumber',
186
+ 'track_number': 'tracknumber',
187
+ 'disc': 'discnumber',
188
+ 'disc_number': 'discnumber',
189
+ 'year': 'date',
190
+ 'albuminterpret': 'albumartist', # German tag name
191
+ 'interpret': 'artist', # German tag name
192
+
193
+ }
194
+
195
+ CONFIG_TEMPLATE = {
196
+ "metadata": {
197
+ "description": "TonieToolbox configuration",
198
+ "config_version": "1.0"
199
+ },
200
+ "log_level": "silent", # Options: trace, debug, info, warning, error, critical, silent
201
+ "log_to_file": False, # True if you want to log to a file ~\.tonietoolbox\logs
202
+ "upload": {
203
+ "url": [""], # https://teddycloud.example.com
204
+ "ignore_ssl_verify": False, # True if you want to ignore SSL certificate verification
205
+ "username": "", # Basic Auth username
206
+ "password": "", # Basic Auth password
207
+ "client_cert_path": "", # Path to client certificate file
208
+ "client_cert_key_path": "" # Path to client certificate key file
209
+ }
210
+ }
211
+
212
+ ICON_BASE64="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"
213
+