TonieToolbox 0.5.1__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 +85 -84
- TonieToolbox/artwork.py +12 -7
- TonieToolbox/audio_conversion.py +31 -28
- 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 +17 -97
- TonieToolbox/ogg_page.py +39 -39
- TonieToolbox/opus_packet.py +13 -13
- TonieToolbox/recursive_processor.py +22 -22
- TonieToolbox/tags.py +3 -4
- TonieToolbox/teddycloud.py +50 -50
- TonieToolbox/tonie_analysis.py +24 -23
- TonieToolbox/tonie_file.py +71 -44
- TonieToolbox/tonies_json.py +69 -66
- TonieToolbox/version_handler.py +12 -15
- {tonietoolbox-0.5.1.dist-info → tonietoolbox-0.6.0a1.dist-info}/METADATA +2 -2
- tonietoolbox-0.6.0a1.dist-info/RECORD +31 -0
- tonietoolbox-0.5.1.dist-info/RECORD +0 -26
- {tonietoolbox-0.5.1.dist-info → tonietoolbox-0.6.0a1.dist-info}/WHEEL +0 -0
- {tonietoolbox-0.5.1.dist-info → tonietoolbox-0.6.0a1.dist-info}/entry_points.txt +0 -0
- {tonietoolbox-0.5.1.dist-info → tonietoolbox-0.6.0a1.dist-info}/licenses/LICENSE.md +0 -0
- {tonietoolbox-0.5.1.dist-info → tonietoolbox-0.6.0a1.dist-info}/top_level.txt +0 -0
TonieToolbox/teddycloud.py
CHANGED
@@ -19,27 +19,33 @@ DEFAULT_RETRY_DELAY = 5 # seconds
|
|
19
19
|
class TeddyCloudClient:
|
20
20
|
"""Client for interacting with TeddyCloud API."""
|
21
21
|
|
22
|
-
def __init__(
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
base_url: str,
|
25
|
+
ignore_ssl_verify: bool = False,
|
26
|
+
connection_timeout: int = DEFAULT_CONNECTION_TIMEOUT,
|
27
|
+
read_timeout: int = DEFAULT_READ_TIMEOUT,
|
28
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
29
|
+
retry_delay: int = DEFAULT_RETRY_DELAY,
|
30
|
+
username: str = None,
|
31
|
+
password: str = None,
|
32
|
+
cert_file: str = None,
|
33
|
+
key_file: str = None
|
34
|
+
) -> None:
|
29
35
|
"""
|
30
36
|
Initialize the TeddyCloud client.
|
31
37
|
|
32
38
|
Args:
|
33
|
-
base_url: Base URL of the TeddyCloud instance (e.g., https://teddycloud.example.com)
|
34
|
-
ignore_ssl_verify: If True, SSL certificate verification will be disabled (useful for self-signed certificates)
|
35
|
-
connection_timeout: Timeout for establishing a connection
|
36
|
-
read_timeout: Timeout for reading data from the server
|
37
|
-
max_retries: Maximum number of retries for failed requests
|
38
|
-
retry_delay: Delay between retries
|
39
|
-
username: Username for basic authentication (optional)
|
40
|
-
password: Password for basic authentication (optional)
|
41
|
-
cert_file: Path to client certificate file for certificate-based authentication (optional)
|
42
|
-
key_file: Path to client private key file for certificate-based authentication (optional)
|
39
|
+
base_url (str): Base URL of the TeddyCloud instance (e.g., https://teddycloud.example.com)
|
40
|
+
ignore_ssl_verify (bool): If True, SSL certificate verification will be disabled (useful for self-signed certificates)
|
41
|
+
connection_timeout (int): Timeout for establishing a connection
|
42
|
+
read_timeout (int): Timeout for reading data from the server
|
43
|
+
max_retries (int): Maximum number of retries for failed requests
|
44
|
+
retry_delay (int): Delay between retries
|
45
|
+
username (str | None): Username for basic authentication (optional)
|
46
|
+
password (str | None): Password for basic authentication (optional)
|
47
|
+
cert_file (str | None): Path to client certificate file for certificate-based authentication (optional)
|
48
|
+
key_file (str | None): Path to client private key file for certificate-based authentication (optional)
|
43
49
|
"""
|
44
50
|
self.base_url = base_url.rstrip('/')
|
45
51
|
self.ignore_ssl_verify = ignore_ssl_verify
|
@@ -81,7 +87,7 @@ class TeddyCloudClient:
|
|
81
87
|
except ssl.SSLError as e:
|
82
88
|
raise ValueError(f"Failed to load client certificate: {e}")
|
83
89
|
|
84
|
-
def _create_request_kwargs(self):
|
90
|
+
def _create_request_kwargs(self) -> dict:
|
85
91
|
"""
|
86
92
|
Create common request keyword arguments for all API calls.
|
87
93
|
|
@@ -98,18 +104,16 @@ class TeddyCloudClient:
|
|
98
104
|
kwargs['cert'] = self.cert
|
99
105
|
return kwargs
|
100
106
|
|
101
|
-
def _make_request(self, method, endpoint, **kwargs):
|
107
|
+
def _make_request(self, method: str, endpoint: str, **kwargs) -> 'requests.Response':
|
102
108
|
"""
|
103
109
|
Make an HTTP request to the TeddyCloud API with retry logic.
|
104
110
|
|
105
111
|
Args:
|
106
|
-
method: HTTP method (GET, POST, etc.)
|
107
|
-
endpoint: API endpoint (without base URL)
|
112
|
+
method (str): HTTP method (GET, POST, etc.)
|
113
|
+
endpoint (str): API endpoint (without base URL)
|
108
114
|
**kwargs: Additional arguments to pass to requests
|
109
|
-
|
110
115
|
Returns:
|
111
116
|
requests.Response: Response object
|
112
|
-
|
113
117
|
Raises:
|
114
118
|
requests.exceptions.RequestException: If request fails after all retries
|
115
119
|
"""
|
@@ -171,7 +175,7 @@ class TeddyCloudClient:
|
|
171
175
|
|
172
176
|
# ------------- GET API Methods -------------
|
173
177
|
|
174
|
-
def get_tonies_custom_json(self):
|
178
|
+
def get_tonies_custom_json(self) -> dict:
|
175
179
|
"""
|
176
180
|
Get custom Tonies JSON data from the TeddyCloud server.
|
177
181
|
|
@@ -181,7 +185,7 @@ class TeddyCloudClient:
|
|
181
185
|
response = self._make_request('GET', '/api/toniesCustomJson')
|
182
186
|
return response.json()
|
183
187
|
|
184
|
-
def get_tonies_json(self):
|
188
|
+
def get_tonies_json(self) -> dict:
|
185
189
|
"""
|
186
190
|
Get Tonies JSON data from the TeddyCloud server.
|
187
191
|
|
@@ -191,7 +195,7 @@ class TeddyCloudClient:
|
|
191
195
|
response = self._make_request('GET', '/api/toniesJson')
|
192
196
|
return response.json()
|
193
197
|
|
194
|
-
def get_tag_index(self):
|
198
|
+
def get_tag_index(self) -> dict:
|
195
199
|
"""
|
196
200
|
Get tag index data from the TeddyCloud server.
|
197
201
|
|
@@ -201,7 +205,7 @@ class TeddyCloudClient:
|
|
201
205
|
response = self._make_request('GET', '/api/getTagIndex')
|
202
206
|
return response.json()
|
203
207
|
|
204
|
-
def get_file_index(self):
|
208
|
+
def get_file_index(self) -> dict:
|
205
209
|
"""
|
206
210
|
Get file index data from the TeddyCloud server.
|
207
211
|
|
@@ -211,7 +215,7 @@ class TeddyCloudClient:
|
|
211
215
|
response = self._make_request('GET', '/api/fileIndex')
|
212
216
|
return response.json()
|
213
217
|
|
214
|
-
def get_file_index_v2(self):
|
218
|
+
def get_file_index_v2(self) -> dict:
|
215
219
|
"""
|
216
220
|
Get version 2 file index data from the TeddyCloud server.
|
217
221
|
|
@@ -221,7 +225,7 @@ class TeddyCloudClient:
|
|
221
225
|
response = self._make_request('GET', '/api/fileIndexV2')
|
222
226
|
return response.json()
|
223
227
|
|
224
|
-
def get_tonieboxes_json(self):
|
228
|
+
def get_tonieboxes_json(self) -> dict:
|
225
229
|
"""
|
226
230
|
Get Tonieboxes JSON data from the TeddyCloud server.
|
227
231
|
|
@@ -233,15 +237,14 @@ class TeddyCloudClient:
|
|
233
237
|
|
234
238
|
# ------------- POST API Methods -------------
|
235
239
|
|
236
|
-
def create_directory(self, path, overlay=None, special=None):
|
240
|
+
def create_directory(self, path: str, overlay: str = None, special: str = None) -> str:
|
237
241
|
"""
|
238
242
|
Create a directory on the TeddyCloud server.
|
239
243
|
|
240
244
|
Args:
|
241
|
-
path: Directory path to create
|
242
|
-
overlay: Settings overlay ID (optional)
|
243
|
-
special: Special folder source, only 'library' supported yet (optional)
|
244
|
-
|
245
|
+
path (str): Directory path to create
|
246
|
+
overlay (str | None): Settings overlay ID (optional)
|
247
|
+
special (str | None): Special folder source, only 'library' supported yet (optional)
|
245
248
|
Returns:
|
246
249
|
str: Response message from server (usually "OK")
|
247
250
|
"""
|
@@ -254,15 +257,14 @@ class TeddyCloudClient:
|
|
254
257
|
response = self._make_request('POST', '/api/dirCreate', params=params, data=path)
|
255
258
|
return response.text
|
256
259
|
|
257
|
-
def delete_directory(self, path, overlay=None, special=None):
|
260
|
+
def delete_directory(self, path: str, overlay: str = None, special: str = None) -> str:
|
258
261
|
"""
|
259
262
|
Delete a directory from the TeddyCloud server.
|
260
263
|
|
261
264
|
Args:
|
262
|
-
path: Directory path to delete
|
263
|
-
overlay: Settings overlay ID (optional)
|
264
|
-
special: Special folder source, only 'library' supported yet (optional)
|
265
|
-
|
265
|
+
path (str): Directory path to delete
|
266
|
+
overlay (str | None): Settings overlay ID (optional)
|
267
|
+
special (str | None): Special folder source, only 'library' supported yet (optional)
|
266
268
|
Returns:
|
267
269
|
str: Response message from server (usually "OK")
|
268
270
|
"""
|
@@ -275,15 +277,14 @@ class TeddyCloudClient:
|
|
275
277
|
response = self._make_request('POST', '/api/dirDelete', params=params, data=path)
|
276
278
|
return response.text
|
277
279
|
|
278
|
-
def delete_file(self, path, overlay=None, special=None):
|
280
|
+
def delete_file(self, path: str, overlay: str = None, special: str = None) -> str:
|
279
281
|
"""
|
280
282
|
Delete a file from the TeddyCloud server.
|
281
283
|
|
282
284
|
Args:
|
283
|
-
path: File path to delete
|
284
|
-
overlay: Settings overlay ID (optional)
|
285
|
-
special: Special folder source, only 'library' supported yet (optional)
|
286
|
-
|
285
|
+
path (str): File path to delete
|
286
|
+
overlay (str | None): Settings overlay ID (optional)
|
287
|
+
special (str | None): Special folder source, only 'library' supported yet (optional)
|
287
288
|
Returns:
|
288
289
|
str: Response message from server (usually "OK")
|
289
290
|
"""
|
@@ -296,16 +297,15 @@ class TeddyCloudClient:
|
|
296
297
|
response = self._make_request('POST', '/api/fileDelete', params=params, data=path)
|
297
298
|
return response.text
|
298
299
|
|
299
|
-
def upload_file(self, file_path, destination_path=None, overlay=None, special=None):
|
300
|
+
def upload_file(self, file_path: str, destination_path: str = None, overlay: str = None, special: str = None) -> dict:
|
300
301
|
"""
|
301
302
|
Upload a file to the TeddyCloud server.
|
302
303
|
|
303
304
|
Args:
|
304
|
-
file_path: Local path to the file to upload
|
305
|
-
destination_path: Server path where to write the file to (optional)
|
306
|
-
overlay: Settings overlay ID (optional)
|
307
|
-
special: Special folder source, only 'library' supported yet (optional)
|
308
|
-
|
305
|
+
file_path (str): Local path to the file to upload
|
306
|
+
destination_path (str | None): Server path where to write the file to (optional)
|
307
|
+
overlay (str | None): Settings overlay ID (optional)
|
308
|
+
special (str | None): Special folder source, only 'library' supported yet (optional)
|
309
309
|
Returns:
|
310
310
|
dict: JSON response from server
|
311
311
|
"""
|
TonieToolbox/tonie_analysis.py
CHANGED
@@ -11,12 +11,13 @@ from .ogg_page import OggPage
|
|
11
11
|
from .logger import get_logger
|
12
12
|
|
13
13
|
logger = get_logger('tonie_analysis')
|
14
|
-
|
14
|
+
|
15
|
+
def format_time(ts: float) -> str:
|
15
16
|
"""
|
16
17
|
Format a timestamp as a human-readable date and time string.
|
17
18
|
|
18
19
|
Args:
|
19
|
-
ts: Timestamp to format
|
20
|
+
ts (float): Timestamp to format
|
20
21
|
|
21
22
|
Returns:
|
22
23
|
str: Formatted date and time string
|
@@ -24,12 +25,12 @@ def format_time(ts):
|
|
24
25
|
return datetime.datetime.fromtimestamp(ts, datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%S')
|
25
26
|
|
26
27
|
|
27
|
-
def format_hex(data):
|
28
|
+
def format_hex(data: bytes) -> str:
|
28
29
|
"""
|
29
30
|
Format binary data as a hex string.
|
30
31
|
|
31
32
|
Args:
|
32
|
-
data: Binary data to format
|
33
|
+
data (bytes): Binary data to format
|
33
34
|
|
34
35
|
Returns:
|
35
36
|
str: Formatted hex string
|
@@ -37,13 +38,13 @@ def format_hex(data):
|
|
37
38
|
return "".join(format(x, "02X") for x in data)
|
38
39
|
|
39
40
|
|
40
|
-
def granule_to_time_string(granule, sample_rate=1):
|
41
|
+
def granule_to_time_string(granule: int, sample_rate: int = 1) -> str:
|
41
42
|
"""
|
42
43
|
Convert a granule position to a time string.
|
43
44
|
|
44
45
|
Args:
|
45
|
-
granule: Granule position
|
46
|
-
sample_rate: Sample rate in Hz
|
46
|
+
granule (int): Granule position
|
47
|
+
sample_rate (int): Sample rate in Hz
|
47
48
|
|
48
49
|
Returns:
|
49
50
|
str: Formatted time string (HH:MM:SS.FF)
|
@@ -56,7 +57,7 @@ def granule_to_time_string(granule, sample_rate=1):
|
|
56
57
|
return "{:02d}:{:02d}:{:02d}.{:02d}".format(hours, minutes, seconds, fraction)
|
57
58
|
|
58
59
|
|
59
|
-
def get_header_info(in_file):
|
60
|
+
def get_header_info(in_file) -> tuple:
|
60
61
|
"""
|
61
62
|
Get header information from a Tonie file.
|
62
63
|
|
@@ -164,15 +165,15 @@ def get_header_info(in_file):
|
|
164
165
|
)
|
165
166
|
|
166
167
|
|
167
|
-
def get_audio_info(in_file, sample_rate, tonie_header, header_size):
|
168
|
+
def get_audio_info(in_file, sample_rate: int, tonie_header, header_size: int) -> tuple:
|
168
169
|
"""
|
169
170
|
Get audio information from a Tonie file.
|
170
171
|
|
171
172
|
Args:
|
172
173
|
in_file: Input file handle
|
173
|
-
sample_rate: Sample rate in Hz
|
174
|
+
sample_rate (int): Sample rate in Hz
|
174
175
|
tonie_header: Tonie header object
|
175
|
-
header_size: Header size in bytes
|
176
|
+
header_size (int): Header size in bytes
|
176
177
|
|
177
178
|
Returns:
|
178
179
|
tuple: Page count, alignment OK flag, page size OK flag, total time, chapter times
|
@@ -228,12 +229,12 @@ def get_audio_info(in_file, sample_rate, tonie_header, header_size):
|
|
228
229
|
return page_count, alignment_okay, page_size_okay, total_time, chapter_times
|
229
230
|
|
230
231
|
|
231
|
-
def check_tonie_file(filename):
|
232
|
+
def check_tonie_file(filename: str) -> bool:
|
232
233
|
"""
|
233
234
|
Check if a file is a valid Tonie file and display information about it.
|
234
235
|
|
235
236
|
Args:
|
236
|
-
filename: Path to the file to check
|
237
|
+
filename (str): Path to the file to check
|
237
238
|
|
238
239
|
Returns:
|
239
240
|
bool: True if the file is valid, False otherwise
|
@@ -315,13 +316,13 @@ def check_tonie_file(filename):
|
|
315
316
|
return all_ok
|
316
317
|
|
317
318
|
|
318
|
-
def split_to_opus_files(filename, output=None):
|
319
|
+
def split_to_opus_files(filename: str, output: str = None) -> None:
|
319
320
|
"""
|
320
321
|
Split a Tonie file into individual Opus files.
|
321
322
|
|
322
323
|
Args:
|
323
|
-
filename: Path to the Tonie file
|
324
|
-
output: Output directory path (optional)
|
324
|
+
filename (str): Path to the Tonie file
|
325
|
+
output (str | None): Output directory path (optional)
|
325
326
|
"""
|
326
327
|
logger.info("Splitting Tonie file into individual Opus tracks: %s", filename)
|
327
328
|
|
@@ -412,14 +413,14 @@ def split_to_opus_files(filename, output=None):
|
|
412
413
|
logger.info("Successfully split Tonie file into %d individual tracks", len(tonie_header.chapterPages))
|
413
414
|
|
414
415
|
|
415
|
-
def compare_taf_files(file1, file2, detailed=False):
|
416
|
+
def compare_taf_files(file1: str, file2: str, detailed: bool = False) -> bool:
|
416
417
|
"""
|
417
418
|
Compare two .taf files for debugging purposes.
|
418
419
|
|
419
420
|
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
|
421
|
+
file1 (str): Path to the first .taf file
|
422
|
+
file2 (str): Path to the second .taf file
|
423
|
+
detailed (bool): Whether to show detailed comparison results
|
423
424
|
|
424
425
|
Returns:
|
425
426
|
bool: True if files are equivalent, False otherwise
|
@@ -572,7 +573,7 @@ def compare_taf_files(file1, file2, detailed=False):
|
|
572
573
|
logger.info("Files comparison result: Equivalent")
|
573
574
|
return True
|
574
575
|
|
575
|
-
def get_header_info_cli(in_file):
|
576
|
+
def get_header_info_cli(in_file) -> tuple:
|
576
577
|
"""
|
577
578
|
Get header information from a Tonie file.
|
578
579
|
|
@@ -687,12 +688,12 @@ def get_header_info_cli(in_file):
|
|
687
688
|
return (0, tonie_header_pb2.TonieHeader(), 0, 0, None, False, 0, 0, 0, 0, {}, False)
|
688
689
|
|
689
690
|
|
690
|
-
def check_tonie_file_cli(filename):
|
691
|
+
def check_tonie_file_cli(filename: str) -> bool:
|
691
692
|
"""
|
692
693
|
Check if a file is a valid Tonie file
|
693
694
|
|
694
695
|
Args:
|
695
|
-
filename: Path to the file to check
|
696
|
+
filename (str): Path to the file to check
|
696
697
|
|
697
698
|
Returns:
|
698
699
|
bool: True if the file is valid, False otherwise
|
TonieToolbox/tonie_file.py
CHANGED
@@ -18,14 +18,14 @@ from .logger import get_logger
|
|
18
18
|
logger = get_logger('tonie_file')
|
19
19
|
|
20
20
|
|
21
|
-
def toniefile_comment_add(buffer, length, comment_str):
|
21
|
+
def toniefile_comment_add(buffer: bytearray, length: int, comment_str: str) -> int:
|
22
22
|
"""
|
23
23
|
Add a comment string to an Opus comment packet buffer.
|
24
24
|
|
25
25
|
Args:
|
26
|
-
buffer: Bytearray buffer to add comment to
|
27
|
-
length: Current position in the buffer
|
28
|
-
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
|
29
29
|
|
30
30
|
Returns:
|
31
31
|
int: New position in the buffer after adding comment
|
@@ -44,7 +44,7 @@ def toniefile_comment_add(buffer, length, comment_str):
|
|
44
44
|
return length
|
45
45
|
|
46
46
|
|
47
|
-
def check_identification_header(page):
|
47
|
+
def check_identification_header(page) -> None:
|
48
48
|
"""
|
49
49
|
Check if a page contains a valid Opus identification header.
|
50
50
|
|
@@ -77,16 +77,16 @@ def check_identification_header(page):
|
|
77
77
|
logger.debug("Opus identification header is valid")
|
78
78
|
|
79
79
|
|
80
|
-
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:
|
81
81
|
"""
|
82
82
|
Prepare standard Opus tags for a Tonie file.
|
83
83
|
|
84
84
|
Args:
|
85
85
|
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
|
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
|
90
90
|
|
91
91
|
Returns:
|
92
92
|
OggPage: Modified page with Tonie-compatible Opus tags
|
@@ -162,19 +162,28 @@ def prepare_opus_tags(page, custom_tags=False, bitrate=64, vbr=True, opus_binary
|
|
162
162
|
return page
|
163
163
|
|
164
164
|
|
165
|
-
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:
|
166
175
|
"""
|
167
176
|
Copy and modify the first two pages of an Opus file for a Tonie file.
|
168
177
|
|
169
178
|
Args:
|
170
179
|
in_file: Input file handle
|
171
180
|
out_file: Output file handle
|
172
|
-
timestamp: Timestamp to use for the Tonie file
|
181
|
+
timestamp (int): Timestamp to use for the Tonie file
|
173
182
|
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
|
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
|
178
187
|
"""
|
179
188
|
logger.debug("Copying first and second pages with timestamp %d", timestamp)
|
180
189
|
found = OggPage.seek_to_page_header(in_file)
|
@@ -202,7 +211,7 @@ def copy_first_and_second_page(in_file, out_file, timestamp, sha, use_custom_tag
|
|
202
211
|
logger.debug("Second page written successfully")
|
203
212
|
|
204
213
|
|
205
|
-
def skip_first_two_pages(in_file):
|
214
|
+
def skip_first_two_pages(in_file) -> None:
|
206
215
|
"""
|
207
216
|
Skip the first two pages of an Opus file.
|
208
217
|
|
@@ -235,7 +244,7 @@ def skip_first_two_pages(in_file):
|
|
235
244
|
logger.debug("First two pages skipped successfully")
|
236
245
|
|
237
246
|
|
238
|
-
def read_all_remaining_pages(in_file):
|
247
|
+
def read_all_remaining_pages(in_file) -> list:
|
239
248
|
"""
|
240
249
|
Read all remaining OGG pages from an input file.
|
241
250
|
|
@@ -260,19 +269,26 @@ def read_all_remaining_pages(in_file):
|
|
260
269
|
return remaining_pages
|
261
270
|
|
262
271
|
|
263
|
-
def resize_pages(
|
264
|
-
|
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:
|
265
281
|
"""
|
266
282
|
Resize OGG pages to fit Tonie requirements.
|
267
283
|
|
268
284
|
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
|
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
|
272
288
|
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
|
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
|
276
292
|
|
277
293
|
Returns:
|
278
294
|
list: List of resized OggPage objects
|
@@ -326,14 +342,14 @@ def resize_pages(old_pages, max_page_size, first_page_size, template_page, last_
|
|
326
342
|
return new_pages
|
327
343
|
|
328
344
|
|
329
|
-
def fix_tonie_header(out_file, chapters, timestamp, sha):
|
345
|
+
def fix_tonie_header(out_file, chapters: list, timestamp: int, sha) -> None:
|
330
346
|
"""
|
331
347
|
Fix the Tonie header in a file.
|
332
348
|
|
333
349
|
Args:
|
334
350
|
out_file: Output file handle
|
335
|
-
chapters: List of chapter page numbers
|
336
|
-
timestamp: Timestamp for the Tonie file
|
351
|
+
chapters (list): List of chapter page numbers
|
352
|
+
timestamp (int): Timestamp for the Tonie file
|
337
353
|
sha: SHA1 hash object with file content
|
338
354
|
"""
|
339
355
|
logger.info("Writing Tonie header with %d chapters and timestamp %d", len(chapters), timestamp)
|
@@ -362,25 +378,36 @@ def fix_tonie_header(out_file, chapters, timestamp, sha):
|
|
362
378
|
logger.debug("Tonie header written successfully (size: %d bytes)", len(header))
|
363
379
|
|
364
380
|
|
365
|
-
def create_tonie_file(
|
366
|
-
|
367
|
-
|
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:
|
368
395
|
"""
|
369
396
|
Create a Tonie file from input files.
|
370
397
|
|
371
398
|
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
|
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
|
384
411
|
"""
|
385
412
|
from .audio_conversion import get_opus_tempfile
|
386
413
|
|