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.
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/python3
1
2
  """
2
3
  Functions for analyzing Tonie files
3
4
  """
@@ -10,13 +11,14 @@ from . import tonie_header_pb2
10
11
  from .ogg_page import OggPage
11
12
  from .logger import get_logger
12
13
 
13
- logger = get_logger('tonie_analysis')
14
- def format_time(ts):
14
+ logger = get_logger(__name__)
15
+
16
+ def format_time(ts: float) -> str:
15
17
  """
16
18
  Format a timestamp as a human-readable date and time string.
17
19
 
18
20
  Args:
19
- ts: Timestamp to format
21
+ ts (float): Timestamp to format
20
22
 
21
23
  Returns:
22
24
  str: Formatted date and time string
@@ -24,12 +26,12 @@ def format_time(ts):
24
26
  return datetime.datetime.fromtimestamp(ts, datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%S')
25
27
 
26
28
 
27
- def format_hex(data):
29
+ def format_hex(data: bytes) -> str:
28
30
  """
29
31
  Format binary data as a hex string.
30
32
 
31
33
  Args:
32
- data: Binary data to format
34
+ data (bytes): Binary data to format
33
35
 
34
36
  Returns:
35
37
  str: Formatted hex string
@@ -37,13 +39,13 @@ def format_hex(data):
37
39
  return "".join(format(x, "02X") for x in data)
38
40
 
39
41
 
40
- def granule_to_time_string(granule, sample_rate=1):
42
+ def granule_to_time_string(granule: int, sample_rate: int = 1) -> str:
41
43
  """
42
44
  Convert a granule position to a time string.
43
45
 
44
46
  Args:
45
- granule: Granule position
46
- sample_rate: Sample rate in Hz
47
+ granule (int): Granule position
48
+ sample_rate (int): Sample rate in Hz
47
49
 
48
50
  Returns:
49
51
  str: Formatted time string (HH:MM:SS.FF)
@@ -56,7 +58,7 @@ def granule_to_time_string(granule, sample_rate=1):
56
58
  return "{:02d}:{:02d}:{:02d}.{:02d}".format(hours, minutes, seconds, fraction)
57
59
 
58
60
 
59
- def get_header_info(in_file):
61
+ def get_header_info(in_file) -> tuple:
60
62
  """
61
63
  Get header information from a Tonie file.
62
64
 
@@ -164,15 +166,15 @@ def get_header_info(in_file):
164
166
  )
165
167
 
166
168
 
167
- def get_audio_info(in_file, sample_rate, tonie_header, header_size):
169
+ def get_audio_info(in_file, sample_rate: int, tonie_header, header_size: int) -> tuple:
168
170
  """
169
171
  Get audio information from a Tonie file.
170
172
 
171
173
  Args:
172
174
  in_file: Input file handle
173
- sample_rate: Sample rate in Hz
175
+ sample_rate (int): Sample rate in Hz
174
176
  tonie_header: Tonie header object
175
- header_size: Header size in bytes
177
+ header_size (int): Header size in bytes
176
178
 
177
179
  Returns:
178
180
  tuple: Page count, alignment OK flag, page size OK flag, total time, chapter times
@@ -228,12 +230,12 @@ def get_audio_info(in_file, sample_rate, tonie_header, header_size):
228
230
  return page_count, alignment_okay, page_size_okay, total_time, chapter_times
229
231
 
230
232
 
231
- def check_tonie_file(filename):
233
+ def check_tonie_file(filename: str) -> bool:
232
234
  """
233
235
  Check if a file is a valid Tonie file and display information about it.
234
236
 
235
237
  Args:
236
- filename: Path to the file to check
238
+ filename (str): Path to the file to check
237
239
 
238
240
  Returns:
239
241
  bool: True if the file is valid, False otherwise
@@ -315,13 +317,13 @@ def check_tonie_file(filename):
315
317
  return all_ok
316
318
 
317
319
 
318
- def split_to_opus_files(filename, output=None):
320
+ def split_to_opus_files(filename: str, output: str = None) -> None:
319
321
  """
320
322
  Split a Tonie file into individual Opus files.
321
323
 
322
324
  Args:
323
- filename: Path to the Tonie file
324
- output: Output directory path (optional)
325
+ filename (str): Path to the Tonie file
326
+ output (str | None): Output directory path (optional)
325
327
  """
326
328
  logger.info("Splitting Tonie file into individual Opus tracks: %s", filename)
327
329
 
@@ -412,14 +414,14 @@ def split_to_opus_files(filename, output=None):
412
414
  logger.info("Successfully split Tonie file into %d individual tracks", len(tonie_header.chapterPages))
413
415
 
414
416
 
415
- def compare_taf_files(file1, file2, detailed=False):
417
+ def compare_taf_files(file1: str, file2: str, detailed: bool = False) -> bool:
416
418
  """
417
419
  Compare two .taf files for debugging purposes.
418
420
 
419
421
  Args:
420
- file1: Path to the first .taf file
421
- file2: Path to the second .taf file
422
- detailed: Whether to show detailed comparison results
422
+ file1 (str): Path to the first .taf file
423
+ file2 (str): Path to the second .taf file
424
+ detailed (bool): Whether to show detailed comparison results
423
425
 
424
426
  Returns:
425
427
  bool: True if files are equivalent, False otherwise
@@ -572,7 +574,7 @@ def compare_taf_files(file1, file2, detailed=False):
572
574
  logger.info("Files comparison result: Equivalent")
573
575
  return True
574
576
 
575
- def get_header_info_cli(in_file):
577
+ def get_header_info_cli(in_file) -> tuple:
576
578
  """
577
579
  Get header information from a Tonie file.
578
580
 
@@ -687,12 +689,12 @@ def get_header_info_cli(in_file):
687
689
  return (0, tonie_header_pb2.TonieHeader(), 0, 0, None, False, 0, 0, 0, 0, {}, False)
688
690
 
689
691
 
690
- def check_tonie_file_cli(filename):
692
+ def check_tonie_file_cli(filename: str) -> bool:
691
693
  """
692
694
  Check if a file is a valid Tonie file
693
695
 
694
696
  Args:
695
- filename: Path to the file to check
697
+ filename (str): Path to the file to check
696
698
 
697
699
  Returns:
698
700
  bool: True if the file is valid, False otherwise
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/python3
1
2
  """
2
3
  Tonie file operations module
3
4
  """
@@ -15,18 +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
- # Setup logging
19
- logger = get_logger('tonie_file')
19
+ logger = get_logger(__name__)
20
20
 
21
21
 
22
- def toniefile_comment_add(buffer, length, comment_str):
22
+ def toniefile_comment_add(buffer: bytearray, length: int, comment_str: str) -> int:
23
23
  """
24
24
  Add a comment string to an Opus comment packet buffer.
25
25
 
26
26
  Args:
27
- buffer: Bytearray buffer to add comment to
28
- length: Current position in the buffer
29
- 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
30
30
 
31
31
  Returns:
32
32
  int: New position in the buffer after adding comment
@@ -38,7 +38,6 @@ def toniefile_comment_add(buffer, length, comment_str):
38
38
  buffer[length:length+4] = struct.pack("<I", str_length)
39
39
  length += 4
40
40
 
41
- # Add the actual string
42
41
  buffer[length:length+str_length] = comment_str.encode('utf-8')
43
42
  length += str_length
44
43
 
@@ -46,7 +45,7 @@ def toniefile_comment_add(buffer, length, comment_str):
46
45
  return length
47
46
 
48
47
 
49
- def check_identification_header(page):
48
+ def check_identification_header(page) -> None:
50
49
  """
51
50
  Check if a page contains a valid Opus identification header.
52
51
 
@@ -79,16 +78,16 @@ def check_identification_header(page):
79
78
  logger.debug("Opus identification header is valid")
80
79
 
81
80
 
82
- 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:
83
82
  """
84
83
  Prepare standard Opus tags for a Tonie file.
85
84
 
86
85
  Args:
87
86
  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
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
92
91
 
93
92
  Returns:
94
93
  OggPage: Modified page with Tonie-compatible Opus tags
@@ -115,49 +114,38 @@ def prepare_opus_tags(page, custom_tags=False, bitrate=64, vbr=True, opus_binary
115
114
  # Use custom tags for TonieToolbox
116
115
  # Create buffer for opus tags (similar to teddyCloud implementation)
117
116
  logger.debug("Creating custom Opus tags")
118
- comment_data = bytearray(0x1B4) # Same size as in teddyCloud
119
-
120
- # OpusTags signature
117
+ comment_data = bytearray(0x1B4)
121
118
  comment_data_pos = 0
122
119
  comment_data[comment_data_pos:comment_data_pos+8] = b"OpusTags"
123
- comment_data_pos += 8
124
-
120
+ comment_data_pos += 8
125
121
  # Vendor string
126
- comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, "TonieToolbox")
127
-
122
+ comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, "TonieToolbox")
128
123
  # Number of comments (3 comments: version, encoder info, and encoder options)
129
124
  comments_count = 3
130
125
  comment_data[comment_data_pos:comment_data_pos+4] = struct.pack("<I", comments_count)
131
- comment_data_pos += 4
132
-
126
+ comment_data_pos += 4
133
127
  # Add version information
134
128
  from . import __version__
135
129
  version_str = f"version={__version__}"
136
- comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, version_str)
137
-
130
+ comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, version_str)
138
131
  # Get actual opusenc version
139
132
  from .dependency_manager import get_opus_version
140
133
  encoder_info = get_opus_version(opus_binary)
141
- comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, f"encoder={encoder_info}")
142
-
134
+ comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, f"encoder={encoder_info}")
143
135
  # Create encoder options string with actual settings
144
136
  vbr_opt = "--vbr" if vbr else "--cbr"
145
137
  encoder_options = f"encoder_options=--bitrate {bitrate} {vbr_opt}"
146
- comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, encoder_options)
147
-
138
+ comment_data_pos = toniefile_comment_add(comment_data, comment_data_pos, encoder_options)
148
139
  # Add padding
149
140
  remain = len(comment_data) - comment_data_pos - 4
150
141
  comment_data[comment_data_pos:comment_data_pos+4] = struct.pack("<I", remain)
151
142
  comment_data_pos += 4
152
- comment_data[comment_data_pos:comment_data_pos+4] = b"pad="
153
-
143
+ comment_data[comment_data_pos:comment_data_pos+4] = b"pad="
154
144
  # 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
-
145
+ comment_data = comment_data[:comment_data_pos + remain] # Trim to actual used size
157
146
  # Split large data into smaller segments (each <= 255 bytes)
158
147
  remaining_data = comment_data
159
- first_segment = True
160
-
148
+ first_segment = True
161
149
  while remaining_data:
162
150
  chunk_size = min(255, len(remaining_data))
163
151
  segment = OpusPacket(None)
@@ -175,19 +163,28 @@ def prepare_opus_tags(page, custom_tags=False, bitrate=64, vbr=True, opus_binary
175
163
  return page
176
164
 
177
165
 
178
- def copy_first_and_second_page(in_file, out_file, timestamp, sha, use_custom_tags=True, bitrate=64, vbr=True, opus_binary=None):
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:
179
176
  """
180
177
  Copy and modify the first two pages of an Opus file for a Tonie file.
181
178
 
182
179
  Args:
183
180
  in_file: Input file handle
184
181
  out_file: Output file handle
185
- timestamp: Timestamp to use for the Tonie file
182
+ timestamp (int): Timestamp to use for the Tonie file
186
183
  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
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
191
188
  """
192
189
  logger.debug("Copying first and second pages with timestamp %d", timestamp)
193
190
  found = OggPage.seek_to_page_header(in_file)
@@ -215,7 +212,7 @@ def copy_first_and_second_page(in_file, out_file, timestamp, sha, use_custom_tag
215
212
  logger.debug("Second page written successfully")
216
213
 
217
214
 
218
- def skip_first_two_pages(in_file):
215
+ def skip_first_two_pages(in_file) -> None:
219
216
  """
220
217
  Skip the first two pages of an Opus file.
221
218
 
@@ -248,7 +245,7 @@ def skip_first_two_pages(in_file):
248
245
  logger.debug("First two pages skipped successfully")
249
246
 
250
247
 
251
- def read_all_remaining_pages(in_file):
248
+ def read_all_remaining_pages(in_file) -> list:
252
249
  """
253
250
  Read all remaining OGG pages from an input file.
254
251
 
@@ -273,19 +270,26 @@ def read_all_remaining_pages(in_file):
273
270
  return remaining_pages
274
271
 
275
272
 
276
- def resize_pages(old_pages, max_page_size, first_page_size, template_page, last_granule=0, start_no=2,
277
- set_last_page_flag=False):
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:
278
282
  """
279
283
  Resize OGG pages to fit Tonie requirements.
280
284
 
281
285
  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
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
285
289
  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
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
289
293
 
290
294
  Returns:
291
295
  list: List of resized OggPage objects
@@ -339,14 +343,14 @@ def resize_pages(old_pages, max_page_size, first_page_size, template_page, last_
339
343
  return new_pages
340
344
 
341
345
 
342
- def fix_tonie_header(out_file, chapters, timestamp, sha):
346
+ def fix_tonie_header(out_file, chapters: list, timestamp: int, sha) -> None:
343
347
  """
344
348
  Fix the Tonie header in a file.
345
349
 
346
350
  Args:
347
351
  out_file: Output file handle
348
- chapters: List of chapter page numbers
349
- timestamp: Timestamp for the Tonie file
352
+ chapters (list): List of chapter page numbers
353
+ timestamp (int): Timestamp for the Tonie file
350
354
  sha: SHA1 hash object with file content
351
355
  """
352
356
  logger.info("Writing Tonie header with %d chapters and timestamp %d", len(chapters), timestamp)
@@ -375,31 +379,43 @@ def fix_tonie_header(out_file, chapters, timestamp, sha):
375
379
  logger.debug("Tonie header written successfully (size: %d bytes)", len(header))
376
380
 
377
381
 
378
- def create_tonie_file(output_file, input_files, no_tonie_header=False, user_timestamp=None,
379
- bitrate=96, vbr=True, ffmpeg_binary=None, opus_binary=None, keep_temp=False, auto_download=False,
380
- use_custom_tags=True):
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:
381
396
  """
382
397
  Create a Tonie file from input files.
383
398
 
384
399
  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
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
396
412
  """
397
413
  from .audio_conversion import get_opus_tempfile
398
414
 
399
415
  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)",
416
+ "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
417
  output_file, input_files, no_tonie_header, user_timestamp, bitrate, vbr, ffmpeg_binary,
402
- opus_binary, keep_temp, auto_download, use_custom_tags)
418
+ opus_binary, keep_temp, auto_download, use_custom_tags, no_mono_conversion)
403
419
 
404
420
  logger.info("Creating Tonie file from %d input files", len(input_files))
405
421
  logger.debug("Output file: %s, Bitrate: %d kbps, VBR: %s, No header: %s",
@@ -465,9 +481,9 @@ def create_tonie_file(output_file, input_files, no_tonie_header=False, user_time
465
481
  handle = open(fname, "rb")
466
482
  temp_file_path = None
467
483
  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)
484
+ logger.debug("Converting %s to Opus format (bitrate: %d kbps, VBR: %s, no_mono_conversion: %s)",
485
+ fname, bitrate, vbr, no_mono_conversion)
486
+ 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
487
  if temp_file_path:
472
488
  temp_files.append(temp_file_path)
473
489
  logger.debug("Temporary opus file saved to: %s", temp_file_path)