TonieToolbox 0.5.0a1__py3-none-any.whl → 0.6.0a1__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 +1 -1
- TonieToolbox/__main__.py +148 -127
- TonieToolbox/artwork.py +12 -7
- TonieToolbox/audio_conversion.py +104 -33
- TonieToolbox/config.py +1 -0
- TonieToolbox/constants.py +93 -9
- TonieToolbox/filename_generator.py +6 -8
- TonieToolbox/integration.py +20 -0
- TonieToolbox/integration_macos.py +428 -0
- TonieToolbox/integration_ubuntu.py +1 -0
- TonieToolbox/integration_windows.py +404 -0
- TonieToolbox/logger.py +8 -10
- TonieToolbox/media_tags.py +22 -101
- TonieToolbox/ogg_page.py +39 -39
- TonieToolbox/opus_packet.py +13 -13
- TonieToolbox/recursive_processor.py +32 -33
- TonieToolbox/tags.py +3 -4
- TonieToolbox/teddycloud.py +50 -50
- TonieToolbox/tonie_analysis.py +24 -23
- TonieToolbox/tonie_file.py +86 -71
- TonieToolbox/tonies_json.py +828 -36
- TonieToolbox/version_handler.py +12 -15
- {tonietoolbox-0.5.0a1.dist-info → tonietoolbox-0.6.0a1.dist-info}/METADATA +141 -98
- tonietoolbox-0.6.0a1.dist-info/RECORD +31 -0
- {tonietoolbox-0.5.0a1.dist-info → tonietoolbox-0.6.0a1.dist-info}/WHEEL +1 -1
- tonietoolbox-0.5.0a1.dist-info/RECORD +0 -26
- {tonietoolbox-0.5.0a1.dist-info → tonietoolbox-0.6.0a1.dist-info}/entry_points.txt +0 -0
- {tonietoolbox-0.5.0a1.dist-info → tonietoolbox-0.6.0a1.dist-info}/licenses/LICENSE.md +0 -0
- {tonietoolbox-0.5.0a1.dist-info → tonietoolbox-0.6.0a1.dist-info}/top_level.txt +0 -0
TonieToolbox/tonie_file.py
CHANGED
@@ -15,18 +15,17 @@ from .ogg_page import OggPage
|
|
15
15
|
from .constants import OPUS_TAGS, SAMPLE_RATE_KHZ, TIMESTAMP_DEDUCT
|
16
16
|
from .logger import get_logger
|
17
17
|
|
18
|
-
# Setup logging
|
19
18
|
logger = get_logger('tonie_file')
|
20
19
|
|
21
20
|
|
22
|
-
def toniefile_comment_add(buffer, length, comment_str):
|
21
|
+
def toniefile_comment_add(buffer: bytearray, length: int, comment_str: str) -> int:
|
23
22
|
"""
|
24
23
|
Add a comment string to an Opus comment packet buffer.
|
25
24
|
|
26
25
|
Args:
|
27
|
-
buffer: Bytearray buffer to add comment to
|
28
|
-
length: Current position in the buffer
|
29
|
-
comment_str: Comment string to add
|
26
|
+
buffer (bytearray): Bytearray buffer to add comment to
|
27
|
+
length (int): Current position in the buffer
|
28
|
+
comment_str (str): Comment string to add
|
30
29
|
|
31
30
|
Returns:
|
32
31
|
int: New position in the buffer after adding comment
|
@@ -38,7 +37,6 @@ def toniefile_comment_add(buffer, length, comment_str):
|
|
38
37
|
buffer[length:length+4] = struct.pack("<I", str_length)
|
39
38
|
length += 4
|
40
39
|
|
41
|
-
# Add the actual string
|
42
40
|
buffer[length:length+str_length] = comment_str.encode('utf-8')
|
43
41
|
length += str_length
|
44
42
|
|
@@ -46,7 +44,7 @@ def toniefile_comment_add(buffer, length, comment_str):
|
|
46
44
|
return length
|
47
45
|
|
48
46
|
|
49
|
-
def check_identification_header(page):
|
47
|
+
def check_identification_header(page) -> None:
|
50
48
|
"""
|
51
49
|
Check if a page contains a valid Opus identification header.
|
52
50
|
|
@@ -79,16 +77,16 @@ def check_identification_header(page):
|
|
79
77
|
logger.debug("Opus identification header is valid")
|
80
78
|
|
81
79
|
|
82
|
-
def prepare_opus_tags(page, custom_tags=False, bitrate=64, vbr=True, opus_binary=None):
|
80
|
+
def prepare_opus_tags(page, custom_tags: bool = False, bitrate: int = 64, vbr: bool = True, opus_binary: str = None) -> OggPage:
|
83
81
|
"""
|
84
82
|
Prepare standard Opus tags for a Tonie file.
|
85
83
|
|
86
84
|
Args:
|
87
85
|
page: OggPage to modify
|
88
|
-
custom_tags: Whether to use custom TonieToolbox tags instead of default ones
|
89
|
-
bitrate: Actual bitrate used for encoding
|
90
|
-
vbr: Whether variable bitrate was used
|
91
|
-
opus_binary: Path to opusenc binary for version detection
|
86
|
+
custom_tags (bool): Whether to use custom TonieToolbox tags instead of default ones
|
87
|
+
bitrate (int): Actual bitrate used for encoding
|
88
|
+
vbr (bool): Whether variable bitrate was used
|
89
|
+
opus_binary (str | None): Path to opusenc binary for version detection
|
92
90
|
|
93
91
|
Returns:
|
94
92
|
OggPage: Modified page with Tonie-compatible Opus tags
|
@@ -115,49 +113,38 @@ def prepare_opus_tags(page, custom_tags=False, bitrate=64, vbr=True, opus_binary
|
|
115
113
|
# Use custom tags for TonieToolbox
|
116
114
|
# Create buffer for opus tags (similar to teddyCloud implementation)
|
117
115
|
logger.debug("Creating custom Opus tags")
|
118
|
-
comment_data = bytearray(0x1B4)
|
119
|
-
|
120
|
-
# OpusTags signature
|
116
|
+
comment_data = bytearray(0x1B4)
|
121
117
|
comment_data_pos = 0
|
122
118
|
comment_data[comment_data_pos:comment_data_pos+8] = b"OpusTags"
|
123
|
-
comment_data_pos += 8
|
124
|
-
|
119
|
+
comment_data_pos += 8
|
125
120
|
# Vendor string
|
126
|
-
comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, "TonieToolbox")
|
127
|
-
|
121
|
+
comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, "TonieToolbox")
|
128
122
|
# Number of comments (3 comments: version, encoder info, and encoder options)
|
129
123
|
comments_count = 3
|
130
124
|
comment_data[comment_data_pos:comment_data_pos+4] = struct.pack("<I", comments_count)
|
131
|
-
comment_data_pos += 4
|
132
|
-
|
125
|
+
comment_data_pos += 4
|
133
126
|
# Add version information
|
134
127
|
from . import __version__
|
135
128
|
version_str = f"version={__version__}"
|
136
|
-
comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, version_str)
|
137
|
-
|
129
|
+
comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, version_str)
|
138
130
|
# Get actual opusenc version
|
139
131
|
from .dependency_manager import get_opus_version
|
140
132
|
encoder_info = get_opus_version(opus_binary)
|
141
|
-
comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, f"encoder={encoder_info}")
|
142
|
-
|
133
|
+
comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, f"encoder={encoder_info}")
|
143
134
|
# Create encoder options string with actual settings
|
144
135
|
vbr_opt = "--vbr" if vbr else "--cbr"
|
145
136
|
encoder_options = f"encoder_options=--bitrate {bitrate} {vbr_opt}"
|
146
|
-
comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, encoder_options)
|
147
|
-
|
137
|
+
comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, encoder_options)
|
148
138
|
# Add padding
|
149
139
|
remain = len(comment_data) - comment_data_pos - 4
|
150
140
|
comment_data[comment_data_pos:comment_data_pos+4] = struct.pack("<I", remain)
|
151
141
|
comment_data_pos += 4
|
152
|
-
comment_data[comment_data_pos:comment_data_pos+4] = b"pad="
|
153
|
-
|
142
|
+
comment_data[comment_data_pos:comment_data_pos+4] = b"pad="
|
154
143
|
# Create segments - handle data in chunks of 255 bytes maximum
|
155
|
-
comment_data = comment_data[:comment_data_pos + remain] # Trim to actual used size
|
156
|
-
|
144
|
+
comment_data = comment_data[:comment_data_pos + remain] # Trim to actual used size
|
157
145
|
# Split large data into smaller segments (each <= 255 bytes)
|
158
146
|
remaining_data = comment_data
|
159
|
-
first_segment = True
|
160
|
-
|
147
|
+
first_segment = True
|
161
148
|
while remaining_data:
|
162
149
|
chunk_size = min(255, len(remaining_data))
|
163
150
|
segment = OpusPacket(None)
|
@@ -175,19 +162,28 @@ def prepare_opus_tags(page, custom_tags=False, bitrate=64, vbr=True, opus_binary
|
|
175
162
|
return page
|
176
163
|
|
177
164
|
|
178
|
-
def copy_first_and_second_page(
|
165
|
+
def copy_first_and_second_page(
|
166
|
+
in_file,
|
167
|
+
out_file,
|
168
|
+
timestamp: int,
|
169
|
+
sha,
|
170
|
+
use_custom_tags: bool = True,
|
171
|
+
bitrate: int = 64,
|
172
|
+
vbr: bool = True,
|
173
|
+
opus_binary: str = None
|
174
|
+
) -> None:
|
179
175
|
"""
|
180
176
|
Copy and modify the first two pages of an Opus file for a Tonie file.
|
181
177
|
|
182
178
|
Args:
|
183
179
|
in_file: Input file handle
|
184
180
|
out_file: Output file handle
|
185
|
-
timestamp: Timestamp to use for the Tonie file
|
181
|
+
timestamp (int): Timestamp to use for the Tonie file
|
186
182
|
sha: SHA1 hash object to update with written data
|
187
|
-
use_custom_tags: Whether to use custom TonieToolbox tags
|
188
|
-
bitrate: Actual bitrate used for encoding
|
189
|
-
vbr: Whether VBR was used
|
190
|
-
opus_binary: Path to opusenc binary
|
183
|
+
use_custom_tags (bool): Whether to use custom TonieToolbox tags
|
184
|
+
bitrate (int): Actual bitrate used for encoding
|
185
|
+
vbr (bool): Whether VBR was used
|
186
|
+
opus_binary (str | None): Path to opusenc binary
|
191
187
|
"""
|
192
188
|
logger.debug("Copying first and second pages with timestamp %d", timestamp)
|
193
189
|
found = OggPage.seek_to_page_header(in_file)
|
@@ -215,7 +211,7 @@ def copy_first_and_second_page(in_file, out_file, timestamp, sha, use_custom_tag
|
|
215
211
|
logger.debug("Second page written successfully")
|
216
212
|
|
217
213
|
|
218
|
-
def skip_first_two_pages(in_file):
|
214
|
+
def skip_first_two_pages(in_file) -> None:
|
219
215
|
"""
|
220
216
|
Skip the first two pages of an Opus file.
|
221
217
|
|
@@ -248,7 +244,7 @@ def skip_first_two_pages(in_file):
|
|
248
244
|
logger.debug("First two pages skipped successfully")
|
249
245
|
|
250
246
|
|
251
|
-
def read_all_remaining_pages(in_file):
|
247
|
+
def read_all_remaining_pages(in_file) -> list:
|
252
248
|
"""
|
253
249
|
Read all remaining OGG pages from an input file.
|
254
250
|
|
@@ -273,19 +269,26 @@ def read_all_remaining_pages(in_file):
|
|
273
269
|
return remaining_pages
|
274
270
|
|
275
271
|
|
276
|
-
def resize_pages(
|
277
|
-
|
272
|
+
def resize_pages(
|
273
|
+
old_pages: list,
|
274
|
+
max_page_size: int,
|
275
|
+
first_page_size: int,
|
276
|
+
template_page,
|
277
|
+
last_granule: int = 0,
|
278
|
+
start_no: int = 2,
|
279
|
+
set_last_page_flag: bool = False
|
280
|
+
) -> list:
|
278
281
|
"""
|
279
282
|
Resize OGG pages to fit Tonie requirements.
|
280
283
|
|
281
284
|
Args:
|
282
|
-
old_pages: List of original OggPage objects
|
283
|
-
max_page_size: Maximum size for pages
|
284
|
-
first_page_size: Size for the first page
|
285
|
+
old_pages (list): List of original OggPage objects
|
286
|
+
max_page_size (int): Maximum size for pages
|
287
|
+
first_page_size (int): Size for the first page
|
285
288
|
template_page: Template OggPage to use for creating new pages
|
286
|
-
last_granule: Last granule position
|
287
|
-
start_no: Starting page number
|
288
|
-
set_last_page_flag: Whether to set the last page flag
|
289
|
+
last_granule (int): Last granule position
|
290
|
+
start_no (int): Starting page number
|
291
|
+
set_last_page_flag (bool): Whether to set the last page flag
|
289
292
|
|
290
293
|
Returns:
|
291
294
|
list: List of resized OggPage objects
|
@@ -339,14 +342,14 @@ def resize_pages(old_pages, max_page_size, first_page_size, template_page, last_
|
|
339
342
|
return new_pages
|
340
343
|
|
341
344
|
|
342
|
-
def fix_tonie_header(out_file, chapters, timestamp, sha):
|
345
|
+
def fix_tonie_header(out_file, chapters: list, timestamp: int, sha) -> None:
|
343
346
|
"""
|
344
347
|
Fix the Tonie header in a file.
|
345
348
|
|
346
349
|
Args:
|
347
350
|
out_file: Output file handle
|
348
|
-
chapters: List of chapter page numbers
|
349
|
-
timestamp: Timestamp for the Tonie file
|
351
|
+
chapters (list): List of chapter page numbers
|
352
|
+
timestamp (int): Timestamp for the Tonie file
|
350
353
|
sha: SHA1 hash object with file content
|
351
354
|
"""
|
352
355
|
logger.info("Writing Tonie header with %d chapters and timestamp %d", len(chapters), timestamp)
|
@@ -375,31 +378,43 @@ def fix_tonie_header(out_file, chapters, timestamp, sha):
|
|
375
378
|
logger.debug("Tonie header written successfully (size: %d bytes)", len(header))
|
376
379
|
|
377
380
|
|
378
|
-
def create_tonie_file(
|
379
|
-
|
380
|
-
|
381
|
+
def create_tonie_file(
|
382
|
+
output_file: str,
|
383
|
+
input_files: list[str],
|
384
|
+
no_tonie_header: bool = False,
|
385
|
+
user_timestamp: str = None,
|
386
|
+
bitrate: int = 96,
|
387
|
+
vbr: bool = True,
|
388
|
+
ffmpeg_binary: str = None,
|
389
|
+
opus_binary: str = None,
|
390
|
+
keep_temp: bool = False,
|
391
|
+
auto_download: bool = False,
|
392
|
+
use_custom_tags: bool = True,
|
393
|
+
no_mono_conversion: bool = False
|
394
|
+
) -> None:
|
381
395
|
"""
|
382
396
|
Create a Tonie file from input files.
|
383
397
|
|
384
398
|
Args:
|
385
|
-
output_file: Output file path
|
386
|
-
input_files: List of input file paths
|
387
|
-
no_tonie_header: Whether to omit the Tonie header
|
388
|
-
user_timestamp: Custom timestamp to use
|
389
|
-
bitrate: Bitrate for encoding in kbps
|
390
|
-
vbr: Whether to use variable bitrate encoding (True) or constant (False)
|
391
|
-
ffmpeg_binary: Path to ffmpeg binary
|
392
|
-
opus_binary: Path to opusenc binary
|
393
|
-
keep_temp: Whether to keep temporary opus files for testing
|
394
|
-
auto_download: Whether to automatically download dependencies if not found
|
395
|
-
use_custom_tags: Whether to use dynamic comment tags generated with toniefile_comment_add
|
399
|
+
output_file (str): Output file path
|
400
|
+
input_files (list[str]): List of input file paths
|
401
|
+
no_tonie_header (bool): Whether to omit the Tonie header
|
402
|
+
user_timestamp (str | None): Custom timestamp to use
|
403
|
+
bitrate (int): Bitrate for encoding in kbps
|
404
|
+
vbr (bool): Whether to use variable bitrate encoding (True) or constant (False)
|
405
|
+
ffmpeg_binary (str | None): Path to ffmpeg binary
|
406
|
+
opus_binary (str | None): Path to opusenc binary
|
407
|
+
keep_temp (bool): Whether to keep temporary opus files for testing
|
408
|
+
auto_download (bool): Whether to automatically download dependencies if not found
|
409
|
+
use_custom_tags (bool): Whether to use dynamic comment tags generated with toniefile_comment_add
|
410
|
+
no_mono_conversion (bool): Whether to skip mono conversion during audio processing
|
396
411
|
"""
|
397
412
|
from .audio_conversion import get_opus_tempfile
|
398
413
|
|
399
414
|
logger.trace("Entering create_tonie_file(output_file=%s, input_files=%s, no_tonie_header=%s, user_timestamp=%s, "
|
400
|
-
"bitrate=%d, vbr=%s, ffmpeg_binary=%s, opus_binary=%s, keep_temp=%s, auto_download=%s, use_custom_tags=%s)",
|
415
|
+
"bitrate=%d, vbr=%s, ffmpeg_binary=%s, opus_binary=%s, keep_temp=%s, auto_download=%s, use_custom_tags=%s, no_mono_conversion=%s)",
|
401
416
|
output_file, input_files, no_tonie_header, user_timestamp, bitrate, vbr, ffmpeg_binary,
|
402
|
-
opus_binary, keep_temp, auto_download, use_custom_tags)
|
417
|
+
opus_binary, keep_temp, auto_download, use_custom_tags, no_mono_conversion)
|
403
418
|
|
404
419
|
logger.info("Creating Tonie file from %d input files", len(input_files))
|
405
420
|
logger.debug("Output file: %s, Bitrate: %d kbps, VBR: %s, No header: %s",
|
@@ -465,9 +480,9 @@ def create_tonie_file(output_file, input_files, no_tonie_header=False, user_time
|
|
465
480
|
handle = open(fname, "rb")
|
466
481
|
temp_file_path = None
|
467
482
|
else:
|
468
|
-
logger.debug("Converting %s to Opus format (bitrate: %d kbps, VBR: %s)",
|
469
|
-
fname, bitrate, vbr)
|
470
|
-
handle, temp_file_path = get_opus_tempfile(ffmpeg_binary, opus_binary, fname, bitrate, vbr, keep_temp, auto_download)
|
483
|
+
logger.debug("Converting %s to Opus format (bitrate: %d kbps, VBR: %s, no_mono_conversion: %s)",
|
484
|
+
fname, bitrate, vbr, no_mono_conversion)
|
485
|
+
handle, temp_file_path = get_opus_tempfile(ffmpeg_binary, opus_binary, fname, bitrate, vbr, keep_temp, auto_download, no_mono_conversion=no_mono_conversion)
|
471
486
|
if temp_file_path:
|
472
487
|
temp_files.append(temp_file_path)
|
473
488
|
logger.debug("Temporary opus file saved to: %s", temp_file_path)
|