TonieToolbox 0.5.1__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/__init__.py +2 -1
- TonieToolbox/__main__.py +240 -98
- TonieToolbox/artwork.py +59 -10
- TonieToolbox/audio_conversion.py +33 -29
- TonieToolbox/constants.py +133 -10
- TonieToolbox/dependency_manager.py +679 -184
- TonieToolbox/filename_generator.py +57 -10
- TonieToolbox/integration.py +73 -0
- TonieToolbox/integration_macos.py +613 -0
- TonieToolbox/integration_ubuntu.py +2 -0
- TonieToolbox/integration_windows.py +445 -0
- TonieToolbox/logger.py +9 -10
- TonieToolbox/media_tags.py +19 -100
- TonieToolbox/ogg_page.py +41 -41
- TonieToolbox/opus_packet.py +15 -15
- TonieToolbox/recursive_processor.py +24 -23
- TonieToolbox/tags.py +4 -5
- TonieToolbox/teddycloud.py +164 -51
- TonieToolbox/tonie_analysis.py +26 -24
- TonieToolbox/tonie_file.py +73 -45
- TonieToolbox/tonies_json.py +71 -67
- TonieToolbox/version_handler.py +14 -20
- {tonietoolbox-0.5.1.dist-info → tonietoolbox-0.6.0.dist-info}/METADATA +129 -92
- tonietoolbox-0.6.0.dist-info/RECORD +30 -0
- {tonietoolbox-0.5.1.dist-info → tonietoolbox-0.6.0.dist-info}/WHEEL +1 -1
- tonietoolbox-0.5.1.dist-info/RECORD +0 -26
- {tonietoolbox-0.5.1.dist-info → tonietoolbox-0.6.0.dist-info}/entry_points.txt +0 -0
- {tonietoolbox-0.5.1.dist-info → tonietoolbox-0.6.0.dist-info}/licenses/LICENSE.md +0 -0
- {tonietoolbox-0.5.1.dist-info → tonietoolbox-0.6.0.dist-info}/top_level.txt +0 -0
TonieToolbox/tonie_file.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#!/usr/bin/python3
|
1
2
|
"""
|
2
3
|
Tonie file operations module
|
3
4
|
"""
|
@@ -15,17 +16,17 @@ from .ogg_page import OggPage
|
|
15
16
|
from .constants import OPUS_TAGS, SAMPLE_RATE_KHZ, TIMESTAMP_DEDUCT
|
16
17
|
from .logger import get_logger
|
17
18
|
|
18
|
-
logger = get_logger(
|
19
|
+
logger = get_logger(__name__)
|
19
20
|
|
20
21
|
|
21
|
-
def toniefile_comment_add(buffer, length, comment_str):
|
22
|
+
def toniefile_comment_add(buffer: bytearray, length: int, comment_str: str) -> int:
|
22
23
|
"""
|
23
24
|
Add a comment string to an Opus comment packet buffer.
|
24
25
|
|
25
26
|
Args:
|
26
|
-
buffer: Bytearray buffer to add comment to
|
27
|
-
length: Current position in the buffer
|
28
|
-
comment_str: Comment string to add
|
27
|
+
buffer (bytearray): Bytearray buffer to add comment to
|
28
|
+
length (int): Current position in the buffer
|
29
|
+
comment_str (str): Comment string to add
|
29
30
|
|
30
31
|
Returns:
|
31
32
|
int: New position in the buffer after adding comment
|
@@ -44,7 +45,7 @@ def toniefile_comment_add(buffer, length, comment_str):
|
|
44
45
|
return length
|
45
46
|
|
46
47
|
|
47
|
-
def check_identification_header(page):
|
48
|
+
def check_identification_header(page) -> None:
|
48
49
|
"""
|
49
50
|
Check if a page contains a valid Opus identification header.
|
50
51
|
|
@@ -77,16 +78,16 @@ def check_identification_header(page):
|
|
77
78
|
logger.debug("Opus identification header is valid")
|
78
79
|
|
79
80
|
|
80
|
-
def prepare_opus_tags(page, custom_tags=False, bitrate=64, vbr=True, opus_binary=None):
|
81
|
+
def prepare_opus_tags(page, custom_tags: bool = False, bitrate: int = 64, vbr: bool = True, opus_binary: str = None) -> OggPage:
|
81
82
|
"""
|
82
83
|
Prepare standard Opus tags for a Tonie file.
|
83
84
|
|
84
85
|
Args:
|
85
86
|
page: OggPage to modify
|
86
|
-
custom_tags: Whether to use custom TonieToolbox tags instead of default ones
|
87
|
-
bitrate: Actual bitrate used for encoding
|
88
|
-
vbr: Whether variable bitrate was used
|
89
|
-
opus_binary: Path to opusenc binary for version detection
|
87
|
+
custom_tags (bool): Whether to use custom TonieToolbox tags instead of default ones
|
88
|
+
bitrate (int): Actual bitrate used for encoding
|
89
|
+
vbr (bool): Whether variable bitrate was used
|
90
|
+
opus_binary (str | None): Path to opusenc binary for version detection
|
90
91
|
|
91
92
|
Returns:
|
92
93
|
OggPage: Modified page with Tonie-compatible Opus tags
|
@@ -162,19 +163,28 @@ def prepare_opus_tags(page, custom_tags=False, bitrate=64, vbr=True, opus_binary
|
|
162
163
|
return page
|
163
164
|
|
164
165
|
|
165
|
-
def copy_first_and_second_page(
|
166
|
+
def copy_first_and_second_page(
|
167
|
+
in_file,
|
168
|
+
out_file,
|
169
|
+
timestamp: int,
|
170
|
+
sha,
|
171
|
+
use_custom_tags: bool = True,
|
172
|
+
bitrate: int = 64,
|
173
|
+
vbr: bool = True,
|
174
|
+
opus_binary: str = None
|
175
|
+
) -> None:
|
166
176
|
"""
|
167
177
|
Copy and modify the first two pages of an Opus file for a Tonie file.
|
168
178
|
|
169
179
|
Args:
|
170
180
|
in_file: Input file handle
|
171
181
|
out_file: Output file handle
|
172
|
-
timestamp: Timestamp to use for the Tonie file
|
182
|
+
timestamp (int): Timestamp to use for the Tonie file
|
173
183
|
sha: SHA1 hash object to update with written data
|
174
|
-
use_custom_tags: Whether to use custom TonieToolbox tags
|
175
|
-
bitrate: Actual bitrate used for encoding
|
176
|
-
vbr: Whether VBR was used
|
177
|
-
opus_binary: Path to opusenc binary
|
184
|
+
use_custom_tags (bool): Whether to use custom TonieToolbox tags
|
185
|
+
bitrate (int): Actual bitrate used for encoding
|
186
|
+
vbr (bool): Whether VBR was used
|
187
|
+
opus_binary (str | None): Path to opusenc binary
|
178
188
|
"""
|
179
189
|
logger.debug("Copying first and second pages with timestamp %d", timestamp)
|
180
190
|
found = OggPage.seek_to_page_header(in_file)
|
@@ -202,7 +212,7 @@ def copy_first_and_second_page(in_file, out_file, timestamp, sha, use_custom_tag
|
|
202
212
|
logger.debug("Second page written successfully")
|
203
213
|
|
204
214
|
|
205
|
-
def skip_first_two_pages(in_file):
|
215
|
+
def skip_first_two_pages(in_file) -> None:
|
206
216
|
"""
|
207
217
|
Skip the first two pages of an Opus file.
|
208
218
|
|
@@ -235,7 +245,7 @@ def skip_first_two_pages(in_file):
|
|
235
245
|
logger.debug("First two pages skipped successfully")
|
236
246
|
|
237
247
|
|
238
|
-
def read_all_remaining_pages(in_file):
|
248
|
+
def read_all_remaining_pages(in_file) -> list:
|
239
249
|
"""
|
240
250
|
Read all remaining OGG pages from an input file.
|
241
251
|
|
@@ -260,19 +270,26 @@ def read_all_remaining_pages(in_file):
|
|
260
270
|
return remaining_pages
|
261
271
|
|
262
272
|
|
263
|
-
def resize_pages(
|
264
|
-
|
273
|
+
def resize_pages(
|
274
|
+
old_pages: list,
|
275
|
+
max_page_size: int,
|
276
|
+
first_page_size: int,
|
277
|
+
template_page,
|
278
|
+
last_granule: int = 0,
|
279
|
+
start_no: int = 2,
|
280
|
+
set_last_page_flag: bool = False
|
281
|
+
) -> list:
|
265
282
|
"""
|
266
283
|
Resize OGG pages to fit Tonie requirements.
|
267
284
|
|
268
285
|
Args:
|
269
|
-
old_pages: List of original OggPage objects
|
270
|
-
max_page_size: Maximum size for pages
|
271
|
-
first_page_size: Size for the first page
|
286
|
+
old_pages (list): List of original OggPage objects
|
287
|
+
max_page_size (int): Maximum size for pages
|
288
|
+
first_page_size (int): Size for the first page
|
272
289
|
template_page: Template OggPage to use for creating new pages
|
273
|
-
last_granule: Last granule position
|
274
|
-
start_no: Starting page number
|
275
|
-
set_last_page_flag: Whether to set the last page flag
|
290
|
+
last_granule (int): Last granule position
|
291
|
+
start_no (int): Starting page number
|
292
|
+
set_last_page_flag (bool): Whether to set the last page flag
|
276
293
|
|
277
294
|
Returns:
|
278
295
|
list: List of resized OggPage objects
|
@@ -326,14 +343,14 @@ def resize_pages(old_pages, max_page_size, first_page_size, template_page, last_
|
|
326
343
|
return new_pages
|
327
344
|
|
328
345
|
|
329
|
-
def fix_tonie_header(out_file, chapters, timestamp, sha):
|
346
|
+
def fix_tonie_header(out_file, chapters: list, timestamp: int, sha) -> None:
|
330
347
|
"""
|
331
348
|
Fix the Tonie header in a file.
|
332
349
|
|
333
350
|
Args:
|
334
351
|
out_file: Output file handle
|
335
|
-
chapters: List of chapter page numbers
|
336
|
-
timestamp: Timestamp for the Tonie file
|
352
|
+
chapters (list): List of chapter page numbers
|
353
|
+
timestamp (int): Timestamp for the Tonie file
|
337
354
|
sha: SHA1 hash object with file content
|
338
355
|
"""
|
339
356
|
logger.info("Writing Tonie header with %d chapters and timestamp %d", len(chapters), timestamp)
|
@@ -362,25 +379,36 @@ def fix_tonie_header(out_file, chapters, timestamp, sha):
|
|
362
379
|
logger.debug("Tonie header written successfully (size: %d bytes)", len(header))
|
363
380
|
|
364
381
|
|
365
|
-
def create_tonie_file(
|
366
|
-
|
367
|
-
|
382
|
+
def create_tonie_file(
|
383
|
+
output_file: str,
|
384
|
+
input_files: list[str],
|
385
|
+
no_tonie_header: bool = False,
|
386
|
+
user_timestamp: str = None,
|
387
|
+
bitrate: int = 96,
|
388
|
+
vbr: bool = True,
|
389
|
+
ffmpeg_binary: str = None,
|
390
|
+
opus_binary: str = None,
|
391
|
+
keep_temp: bool = False,
|
392
|
+
auto_download: bool = False,
|
393
|
+
use_custom_tags: bool = True,
|
394
|
+
no_mono_conversion: bool = False
|
395
|
+
) -> None:
|
368
396
|
"""
|
369
397
|
Create a Tonie file from input files.
|
370
398
|
|
371
399
|
Args:
|
372
|
-
output_file: Output file path
|
373
|
-
input_files: List of input file paths
|
374
|
-
no_tonie_header: Whether to omit the Tonie header
|
375
|
-
user_timestamp: Custom timestamp to use
|
376
|
-
bitrate: Bitrate for encoding in kbps
|
377
|
-
vbr: Whether to use variable bitrate encoding (True) or constant (False)
|
378
|
-
ffmpeg_binary: Path to ffmpeg binary
|
379
|
-
opus_binary: Path to opusenc binary
|
380
|
-
keep_temp: Whether to keep temporary opus files for testing
|
381
|
-
auto_download: Whether to automatically download dependencies if not found
|
382
|
-
use_custom_tags: Whether to use dynamic comment tags generated with toniefile_comment_add
|
383
|
-
no_mono_conversion: Whether to skip mono conversion during audio processing
|
400
|
+
output_file (str): Output file path
|
401
|
+
input_files (list[str]): List of input file paths
|
402
|
+
no_tonie_header (bool): Whether to omit the Tonie header
|
403
|
+
user_timestamp (str | None): Custom timestamp to use
|
404
|
+
bitrate (int): Bitrate for encoding in kbps
|
405
|
+
vbr (bool): Whether to use variable bitrate encoding (True) or constant (False)
|
406
|
+
ffmpeg_binary (str | None): Path to ffmpeg binary
|
407
|
+
opus_binary (str | None): Path to opusenc binary
|
408
|
+
keep_temp (bool): Whether to keep temporary opus files for testing
|
409
|
+
auto_download (bool): Whether to automatically download dependencies if not found
|
410
|
+
use_custom_tags (bool): Whether to use dynamic comment tags generated with toniefile_comment_add
|
411
|
+
no_mono_conversion (bool): Whether to skip mono conversion during audio processing
|
384
412
|
"""
|
385
413
|
from .audio_conversion import get_opus_tempfile
|
386
414
|
|
TonieToolbox/tonies_json.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#!/usr/bin/python3
|
1
2
|
"""
|
2
3
|
TonieToolbox module for handling the tonies.custom.json operations.
|
3
4
|
|
@@ -19,7 +20,7 @@ from .media_tags import get_file_tags, extract_album_info
|
|
19
20
|
from .constants import LANGUAGE_MAPPING, GENRE_MAPPING
|
20
21
|
from .teddycloud import TeddyCloudClient
|
21
22
|
|
22
|
-
logger = get_logger(
|
23
|
+
logger = get_logger(__name__)
|
23
24
|
|
24
25
|
class ToniesJsonHandlerv1:
|
25
26
|
"""Handler for tonies.custom.json operations using v1 format."""
|
@@ -29,7 +30,7 @@ class ToniesJsonHandlerv1:
|
|
29
30
|
Initialize the handler.
|
30
31
|
|
31
32
|
Args:
|
32
|
-
client: TeddyCloudClient instance to use for API communication
|
33
|
+
client (TeddyCloudClient | None): TeddyCloudClient instance to use for API communication
|
33
34
|
"""
|
34
35
|
self.client = client
|
35
36
|
self.custom_json = []
|
@@ -40,7 +41,7 @@ class ToniesJsonHandlerv1:
|
|
40
41
|
Load tonies.custom.json from the TeddyCloud server.
|
41
42
|
|
42
43
|
Returns:
|
43
|
-
True if successful, False otherwise
|
44
|
+
bool: True if successful, False otherwise
|
44
45
|
"""
|
45
46
|
if self.client is None:
|
46
47
|
logger.error("Cannot load from server: no client provided")
|
@@ -71,10 +72,10 @@ class ToniesJsonHandlerv1:
|
|
71
72
|
Load tonies.custom.json from a local file.
|
72
73
|
|
73
74
|
Args:
|
74
|
-
file_path: Path to the tonies.custom.json file
|
75
|
+
file_path (str): Path to the tonies.custom.json file
|
75
76
|
|
76
77
|
Returns:
|
77
|
-
True if successful, False otherwise
|
78
|
+
bool: True if successful, False otherwise
|
78
79
|
"""
|
79
80
|
try:
|
80
81
|
if os.path.exists(file_path):
|
@@ -109,10 +110,10 @@ class ToniesJsonHandlerv1:
|
|
109
110
|
Save tonies.custom.json to a local file.
|
110
111
|
|
111
112
|
Args:
|
112
|
-
file_path: Path where to save the tonies.custom.json file
|
113
|
+
file_path (str): Path where to save the tonies.custom.json file
|
113
114
|
|
114
115
|
Returns:
|
115
|
-
True if successful, False otherwise
|
116
|
+
bool: True if successful, False otherwise
|
116
117
|
"""
|
117
118
|
if not self.is_loaded:
|
118
119
|
logger.error("Cannot save tonies.custom.json: data not loaded")
|
@@ -131,10 +132,13 @@ class ToniesJsonHandlerv1:
|
|
131
132
|
logger.error("Error saving tonies.custom.json to file: %s", e)
|
132
133
|
return False
|
133
134
|
|
134
|
-
def renumber_series_entries(self, series: str):
|
135
|
+
def renumber_series_entries(self, series: str) -> None:
|
135
136
|
"""
|
136
137
|
Re-sort and re-number all entries for a series by year (chronological),
|
137
138
|
with entries without a year coming last.
|
139
|
+
|
140
|
+
Args:
|
141
|
+
series (str): Series name to renumber
|
138
142
|
"""
|
139
143
|
# Collect all entries for the series
|
140
144
|
series_entries = [entry for entry in self.custom_json if entry.get('series') == series]
|
@@ -167,12 +171,12 @@ class ToniesJsonHandlerv1:
|
|
167
171
|
If an entry with the same series+episodes exists, the new hash will be added to it.
|
168
172
|
|
169
173
|
Args:
|
170
|
-
taf_file: Path to the TAF file
|
171
|
-
input_files: List of input audio files used to create the TAF
|
172
|
-
artwork_url: URL of the uploaded artwork (if any)
|
174
|
+
taf_file (str): Path to the TAF file
|
175
|
+
input_files (list[str]): List of input audio files used to create the TAF
|
176
|
+
artwork_url (str | None): URL of the uploaded artwork (if any)
|
173
177
|
|
174
178
|
Returns:
|
175
|
-
True if successful, False otherwise
|
179
|
+
bool: True if successful, False otherwise
|
176
180
|
"""
|
177
181
|
logger.trace("Entering add_entry_from_taf() with taf_file=%s, input_files=%s, artwork_url=%s",
|
178
182
|
taf_file, input_files, artwork_url)
|
@@ -329,12 +333,12 @@ class ToniesJsonHandlerv1:
|
|
329
333
|
2. For entries without years: assign the next available number after those with years
|
330
334
|
|
331
335
|
Args:
|
332
|
-
series: Series name
|
333
|
-
episodes: Episodes name
|
334
|
-
year: Release year from metadata, if available
|
336
|
+
series (str): Series name
|
337
|
+
episodes (str): Episodes name
|
338
|
+
year (int | None): Release year from metadata, if available
|
335
339
|
|
336
340
|
Returns:
|
337
|
-
Generated entry number as string
|
341
|
+
str: Generated entry number as string
|
338
342
|
"""
|
339
343
|
logger.trace("Entering _generate_entry_no() with series='%s', episodes='%s', year=%s",
|
340
344
|
series, episodes, year)
|
@@ -443,10 +447,10 @@ class ToniesJsonHandlerv1:
|
|
443
447
|
Extract a year (1900-2099) from text.
|
444
448
|
|
445
449
|
Args:
|
446
|
-
text: The text to extract the year from
|
450
|
+
text (str): The text to extract the year from
|
447
451
|
|
448
452
|
Returns:
|
449
|
-
The extracted year as int, or None if no valid year found
|
453
|
+
int | None: The extracted year as int, or None if no valid year found
|
450
454
|
"""
|
451
455
|
import re
|
452
456
|
year_pattern = re.compile(r'(19\d{2}|20\d{2})')
|
@@ -467,11 +471,11 @@ class ToniesJsonHandlerv1:
|
|
467
471
|
Format a number to match the existing entry number format (e.g., with leading zeros).
|
468
472
|
|
469
473
|
Args:
|
470
|
-
number: The number to format
|
471
|
-
existing_entries: List of existing entries with their numbers
|
474
|
+
number (int): The number to format
|
475
|
+
existing_entries (list[dict]): List of existing entries with their numbers
|
472
476
|
|
473
477
|
Returns:
|
474
|
-
Formatted number as string
|
478
|
+
str: Formatted number as string
|
475
479
|
"""
|
476
480
|
max_digits = 1
|
477
481
|
for entry in existing_entries:
|
@@ -492,7 +496,7 @@ class ToniesJsonHandlerv1:
|
|
492
496
|
Generate a unique model number for a new entry.
|
493
497
|
|
494
498
|
Returns:
|
495
|
-
Unique model number in the format "model-" followed by sequential number with zero padding
|
499
|
+
str: Unique model number in the format "model-" followed by sequential number with zero padding
|
496
500
|
"""
|
497
501
|
logger.trace("Entering _generate_model_number()")
|
498
502
|
highest_num = -1
|
@@ -525,10 +529,10 @@ class ToniesJsonHandlerv1:
|
|
525
529
|
Determine the category in v1 format.
|
526
530
|
|
527
531
|
Args:
|
528
|
-
metadata: Dictionary containing file metadata
|
532
|
+
metadata (dict): Dictionary containing file metadata
|
529
533
|
|
530
534
|
Returns:
|
531
|
-
Category string in v1 format
|
535
|
+
str: Category string in v1 format
|
532
536
|
"""
|
533
537
|
if 'genre' in metadata:
|
534
538
|
genre_value = metadata['genre'].lower().strip()
|
@@ -551,10 +555,10 @@ class ToniesJsonHandlerv1:
|
|
551
555
|
Find an entry in the custom JSON by TAF hash.
|
552
556
|
|
553
557
|
Args:
|
554
|
-
taf_hash: SHA1 hash of the TAF file to find
|
558
|
+
taf_hash (str): SHA1 hash of the TAF file to find
|
555
559
|
|
556
560
|
Returns:
|
557
|
-
Tuple of (entry, entry_index) if found, or (None, None) if not found
|
561
|
+
tuple[dict | None, int | None]: Tuple of (entry, entry_index) if found, or (None, None) if not found
|
558
562
|
"""
|
559
563
|
logger.trace("Searching for entry with hash %s", taf_hash)
|
560
564
|
|
@@ -575,11 +579,11 @@ class ToniesJsonHandlerv1:
|
|
575
579
|
Find an entry in the custom JSON by series and episodes.
|
576
580
|
|
577
581
|
Args:
|
578
|
-
series: Series name to find
|
579
|
-
episodes: Episodes name to find
|
582
|
+
series (str): Series name to find
|
583
|
+
episodes (str): Episodes name to find
|
580
584
|
|
581
585
|
Returns:
|
582
|
-
Tuple of (entry, entry_index) if found, or (None, None) if not found
|
586
|
+
tuple[dict | None, int | None]: Tuple of (entry, entry_index) if found, or (None, None) if not found
|
583
587
|
"""
|
584
588
|
logger.trace("Searching for entry with series='%s', episodes='%s'", series, episodes)
|
585
589
|
|
@@ -596,10 +600,10 @@ class ToniesJsonHandlerv1:
|
|
596
600
|
Extract metadata from audio files to use in the custom JSON entry.
|
597
601
|
|
598
602
|
Args:
|
599
|
-
input_files: List of paths to audio files
|
603
|
+
input_files (list[str]): List of paths to audio files
|
600
604
|
|
601
605
|
Returns:
|
602
|
-
Dictionary containing metadata extracted from files
|
606
|
+
dict: Dictionary containing metadata extracted from files
|
603
607
|
"""
|
604
608
|
metadata = {}
|
605
609
|
track_descriptions = []
|
@@ -638,10 +642,10 @@ class ToniesJsonHandlerv1:
|
|
638
642
|
Convert data from v2 format to v1 format.
|
639
643
|
|
640
644
|
Args:
|
641
|
-
v2_data: Data in v2 format
|
645
|
+
v2_data (list[dict]): Data in v2 format
|
642
646
|
|
643
647
|
Returns:
|
644
|
-
Converted data in v1 format
|
648
|
+
list[dict]: Converted data in v1 format
|
645
649
|
"""
|
646
650
|
v1_data = []
|
647
651
|
|
@@ -687,10 +691,10 @@ class ToniesJsonHandlerv1:
|
|
687
691
|
Convert category from v2 format to v1 format.
|
688
692
|
|
689
693
|
Args:
|
690
|
-
v2_category: Category in v2 format
|
694
|
+
v2_category (str): Category in v2 format
|
691
695
|
|
692
696
|
Returns:
|
693
|
-
Category in v1 format
|
697
|
+
str: Category in v1 format
|
694
698
|
"""
|
695
699
|
v2_to_v1_mapping = {
|
696
700
|
"music": "music",
|
@@ -710,7 +714,7 @@ class ToniesJsonHandlerv2:
|
|
710
714
|
Initialize the handler.
|
711
715
|
|
712
716
|
Args:
|
713
|
-
client: TeddyCloudClient instance to use for API communication
|
717
|
+
client (TeddyCloudClient | None): TeddyCloudClient instance to use for API communication
|
714
718
|
"""
|
715
719
|
self.client = client
|
716
720
|
self.custom_json = []
|
@@ -721,7 +725,7 @@ class ToniesJsonHandlerv2:
|
|
721
725
|
Load tonies.custom.json from the TeddyCloud server.
|
722
726
|
|
723
727
|
Returns:
|
724
|
-
True if successful, False otherwise
|
728
|
+
bool: True if successful, False otherwise
|
725
729
|
"""
|
726
730
|
if self.client is None:
|
727
731
|
logger.error("Cannot load from server: no client provided")
|
@@ -747,10 +751,10 @@ class ToniesJsonHandlerv2:
|
|
747
751
|
Load tonies.custom.json from a local file.
|
748
752
|
|
749
753
|
Args:
|
750
|
-
file_path: Path to the tonies.custom.json file
|
754
|
+
file_path (str): Path to the tonies.custom.json file
|
751
755
|
|
752
756
|
Returns:
|
753
|
-
True if successful, False otherwise
|
757
|
+
bool: True if successful, False otherwise
|
754
758
|
"""
|
755
759
|
try:
|
756
760
|
if os.path.exists(file_path):
|
@@ -780,10 +784,10 @@ class ToniesJsonHandlerv2:
|
|
780
784
|
Save tonies.custom.json to a local file.
|
781
785
|
|
782
786
|
Args:
|
783
|
-
file_path: Path where to save the tonies.custom.json file
|
787
|
+
file_path (str): Path where to save the tonies.custom.json file
|
784
788
|
|
785
789
|
Returns:
|
786
|
-
True if successful, False otherwise
|
790
|
+
bool: True if successful, False otherwise
|
787
791
|
"""
|
788
792
|
if not self.is_loaded:
|
789
793
|
logger.error("Cannot save tonies.custom.json: data not loaded")
|
@@ -809,12 +813,12 @@ class ToniesJsonHandlerv2:
|
|
809
813
|
If an entry with the same series+episode exists, the new hash will be added to it.
|
810
814
|
|
811
815
|
Args:
|
812
|
-
taf_file: Path to the TAF file
|
813
|
-
input_files: List of input audio files used to create the TAF
|
814
|
-
artwork_url: URL of the uploaded artwork (if any)
|
816
|
+
taf_file (str): Path to the TAF file
|
817
|
+
input_files (list[str]): List of input audio files used to create the TAF
|
818
|
+
artwork_url (str | None): URL of the uploaded artwork (if any)
|
815
819
|
|
816
820
|
Returns:
|
817
|
-
True if successful, False otherwise
|
821
|
+
bool: True if successful, False otherwise
|
818
822
|
"""
|
819
823
|
logger.trace("Entering add_entry_from_taf() with taf_file=%s, input_files=%s, artwork_url=%s",
|
820
824
|
taf_file, input_files, artwork_url)
|
@@ -915,7 +919,7 @@ class ToniesJsonHandlerv2:
|
|
915
919
|
Generate a unique article ID for a new entry.
|
916
920
|
|
917
921
|
Returns:
|
918
|
-
Unique article ID in the format "tt-42" followed by sequential number starting from 0
|
922
|
+
str: Unique article ID in the format "tt-42" followed by sequential number starting from 0
|
919
923
|
"""
|
920
924
|
logger.trace("Entering _generate_article_id()")
|
921
925
|
highest_num = -1
|
@@ -948,10 +952,10 @@ class ToniesJsonHandlerv2:
|
|
948
952
|
Extract metadata from audio files to use in the custom JSON entry.
|
949
953
|
|
950
954
|
Args:
|
951
|
-
input_files: List of paths to audio files
|
955
|
+
input_files (list[str]): List of paths to audio files
|
952
956
|
|
953
957
|
Returns:
|
954
|
-
Dictionary containing metadata extracted from files
|
958
|
+
dict: Dictionary containing metadata extracted from files
|
955
959
|
"""
|
956
960
|
metadata = {}
|
957
961
|
track_descriptions = []
|
@@ -1043,10 +1047,10 @@ class ToniesJsonHandlerv2:
|
|
1043
1047
|
Find an entry in the custom JSON by TAF hash.
|
1044
1048
|
|
1045
1049
|
Args:
|
1046
|
-
taf_hash: SHA1 hash of the TAF file to find
|
1050
|
+
taf_hash (str): SHA1 hash of the TAF file to find
|
1047
1051
|
|
1048
1052
|
Returns:
|
1049
|
-
Tuple of (entry, entry_index, data_index) if found, or (None, None, None) if not found
|
1053
|
+
tuple[dict | None, int | None, int | None]: Tuple of (entry, entry_index, data_index) if found, or (None, None, None) if not found
|
1050
1054
|
"""
|
1051
1055
|
logger.trace("Searching for entry with hash %s", taf_hash)
|
1052
1056
|
|
@@ -1071,11 +1075,11 @@ class ToniesJsonHandlerv2:
|
|
1071
1075
|
Find an entry in the custom JSON by series and episode.
|
1072
1076
|
|
1073
1077
|
Args:
|
1074
|
-
series: Series name to find
|
1075
|
-
episode: Episode name to find
|
1078
|
+
series (str): Series name to find
|
1079
|
+
episode (str): Episode name to find
|
1076
1080
|
|
1077
1081
|
Returns:
|
1078
|
-
Tuple of (entry, entry_index, data_index) if found, or (None, None, None) if not found
|
1082
|
+
tuple[dict | None, int | None, int | None]: Tuple of (entry, entry_index, data_index) if found, or (None, None, None) if not found
|
1079
1083
|
"""
|
1080
1084
|
logger.trace("Searching for entry with series='%s', episode='%s'", series, episode)
|
1081
1085
|
|
@@ -1096,10 +1100,10 @@ class ToniesJsonHandlerv2:
|
|
1096
1100
|
Calculate the total runtime in minutes from a list of audio files.
|
1097
1101
|
|
1098
1102
|
Args:
|
1099
|
-
input_files: List of paths to audio files
|
1103
|
+
input_files (list[str]): List of paths to audio files
|
1100
1104
|
|
1101
1105
|
Returns:
|
1102
|
-
Total runtime in minutes (rounded to the nearest minute)
|
1106
|
+
int: Total runtime in minutes (rounded to the nearest minute)
|
1103
1107
|
"""
|
1104
1108
|
logger.trace("Entering _calculate_runtime() with %d input files", len(input_files))
|
1105
1109
|
total_runtime_seconds = 0
|
@@ -1166,14 +1170,14 @@ def fetch_and_update_tonies_json_v1(client: TeddyCloudClient, taf_file: Optional
|
|
1166
1170
|
Fetch tonies.custom.json from server and merge with local file if it exists, then update with new entry in v1 format.
|
1167
1171
|
|
1168
1172
|
Args:
|
1169
|
-
client: TeddyCloudClient instance to use for API communication
|
1170
|
-
taf_file: Path to the TAF file to add
|
1171
|
-
input_files: List of input audio files used to create the TAF
|
1172
|
-
artwork_url: URL of the uploaded artwork (if any)
|
1173
|
-
output_dir: Directory where to save the tonies.custom.json file (defaults to './output')
|
1173
|
+
client (TeddyCloudClient): TeddyCloudClient instance to use for API communication
|
1174
|
+
taf_file (str | None): Path to the TAF file to add
|
1175
|
+
input_files (list[str] | None): List of input audio files used to create the TAF
|
1176
|
+
artwork_url (str | None): URL of the uploaded artwork (if any)
|
1177
|
+
output_dir (str | None): Directory where to save the tonies.custom.json file (defaults to './output')
|
1174
1178
|
|
1175
1179
|
Returns:
|
1176
|
-
True if successful, False otherwise
|
1180
|
+
bool: True if successful, False otherwise
|
1177
1181
|
"""
|
1178
1182
|
logger.trace("Entering fetch_and_update_tonies_json_v1 with client=%s, taf_file=%s, input_files=%s, artwork_url=%s, output_dir=%s",
|
1179
1183
|
client, taf_file, input_files, artwork_url, output_dir)
|
@@ -1275,14 +1279,14 @@ def fetch_and_update_tonies_json_v2(client: TeddyCloudClient, taf_file: Optional
|
|
1275
1279
|
Fetch tonies.custom.json from server and merge with local file if it exists, then update with new entry.
|
1276
1280
|
|
1277
1281
|
Args:
|
1278
|
-
client: TeddyCloudClient instance to use for API communication
|
1279
|
-
taf_file: Path to the TAF file to add
|
1280
|
-
input_files: List of input audio files used to create the TAF
|
1281
|
-
artwork_url: URL of the uploaded artwork (if any)
|
1282
|
-
output_dir: Directory where to save the tonies.custom.json file (defaults to './output')
|
1282
|
+
client (TeddyCloudClient): TeddyCloudClient instance to use for API communication
|
1283
|
+
taf_file (str | None): Path to the TAF file to add
|
1284
|
+
input_files (list[str] | None): List of input audio files used to create the TAF
|
1285
|
+
artwork_url (str | None): URL of the uploaded artwork (if any)
|
1286
|
+
output_dir (str | None): Directory where to save the tonies.custom.json file (defaults to './output')
|
1283
1287
|
|
1284
1288
|
Returns:
|
1285
|
-
True if successful, False otherwise
|
1289
|
+
bool: True if successful, False otherwise
|
1286
1290
|
"""
|
1287
1291
|
logger.trace("Entering fetch_and_update_tonies_json with client=%s, taf_file=%s, input_files=%s, artwork_url=%s, output_dir=%s",
|
1288
1292
|
client, taf_file, input_files, artwork_url, output_dir)
|