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/ogg_page.py CHANGED
@@ -18,12 +18,12 @@ from .logger import get_logger
18
18
  logger = get_logger('ogg_page')
19
19
 
20
20
 
21
- def create_crc_table():
21
+ def create_crc_table() -> list[int]:
22
22
  """
23
23
  Create a CRC lookup table for OGG page checksums.
24
24
 
25
25
  Returns:
26
- list: CRC32 lookup table for OGG pages
26
+ list[int]: CRC32 lookup table for OGG pages
27
27
  """
28
28
  logger.debug("Creating CRC table for OGG page checksums")
29
29
  table = []
@@ -39,12 +39,12 @@ def create_crc_table():
39
39
  CRC_TABLE = create_crc_table()
40
40
 
41
41
 
42
- def crc32(bytestream):
42
+ def crc32(bytestream: bytes) -> int:
43
43
  """
44
44
  Calculate a CRC32 checksum for the given bytestream.
45
45
 
46
46
  Args:
47
- bytestream: Bytes to calculate the CRC for
47
+ bytestream (bytes): Bytes to calculate the CRC for
48
48
 
49
49
  Returns:
50
50
  int: CRC32 checksum
@@ -64,7 +64,7 @@ class OggPage:
64
64
  with particular focus on features needed for Tonie compatibility.
65
65
  """
66
66
 
67
- def __init__(self, filehandle):
67
+ def __init__(self, filehandle) -> None:
68
68
  """
69
69
  Initialize a new OggPage.
70
70
 
@@ -88,7 +88,7 @@ class OggPage:
88
88
  self.parse_header(filehandle)
89
89
  self.parse_segments(filehandle)
90
90
 
91
- def parse_header(self, filehandle):
91
+ def parse_header(self, filehandle) -> None:
92
92
  """
93
93
  Parse the OGG page header.
94
94
 
@@ -108,7 +108,7 @@ class OggPage:
108
108
  logger.trace("Parsed OGG header - Page #%d, Type: %d, Granule: %d, Serial: %d, Segments: %d",
109
109
  self.page_no, self.page_type, self.granule_position, self.serial_no, self.segment_count)
110
110
 
111
- def parse_segments(self, filehandle):
111
+ def parse_segments(self, filehandle) -> None:
112
112
  """
113
113
  Parse the segments in this OGG page.
114
114
 
@@ -133,12 +133,12 @@ class OggPage:
133
133
  logger.error("Found an opus packet spanning OGG pages, which is not supported")
134
134
  raise RuntimeError("Found an opus packet spanning ogg pages. This is not supported yet.")
135
135
 
136
- def correct_values(self, last_granule):
136
+ def correct_values(self, last_granule: int) -> None:
137
137
  """
138
138
  Correct the granule position and checksum for this page.
139
139
 
140
140
  Args:
141
- last_granule: Last granule position
141
+ last_granule (int): Last granule position
142
142
 
143
143
  Raises:
144
144
  RuntimeError: If there are too many segments in the page
@@ -160,7 +160,7 @@ class OggPage:
160
160
  logger.trace("Corrected OGG page values: Page #%d, Segments: %d, Granule: %d",
161
161
  self.page_no, self.segment_count, self.granule_position)
162
162
 
163
- def calc_checksum(self):
163
+ def calc_checksum(self) -> int:
164
164
  """
165
165
  Calculate the checksum for this page.
166
166
 
@@ -178,7 +178,7 @@ class OggPage:
178
178
  logger.trace("Calculated checksum for page #%d: 0x%X", self.page_no, checksum)
179
179
  return checksum
180
180
 
181
- def get_page_size(self):
181
+ def get_page_size(self) -> int:
182
182
  """
183
183
  Get the total size of this page in bytes.
184
184
 
@@ -190,7 +190,7 @@ class OggPage:
190
190
  size = size + len(segment.data)
191
191
  return size
192
192
 
193
- def get_size_of_first_opus_packet(self):
193
+ def get_size_of_first_opus_packet(self) -> int:
194
194
  """
195
195
  Get the size of the first opus packet in bytes.
196
196
 
@@ -208,7 +208,7 @@ class OggPage:
208
208
  i = i + 1
209
209
  return size
210
210
 
211
- def get_segment_count_of_first_opus_packet(self):
211
+ def get_segment_count_of_first_opus_packet(self) -> int:
212
212
  """
213
213
  Get the number of segments in the first opus packet.
214
214
 
@@ -224,14 +224,14 @@ class OggPage:
224
224
  count = count + 1
225
225
  return count
226
226
 
227
- def insert_empty_segment(self, index_after, spanning_packet=False, first_packet=False):
227
+ def insert_empty_segment(self, index_after: int, spanning_packet: bool = False, first_packet: bool = False) -> None:
228
228
  """
229
229
  Insert an empty segment after the specified index.
230
230
 
231
231
  Args:
232
- index_after: Index to insert the segment after
233
- spanning_packet: Whether this segment belongs to a packet that spans pages
234
- first_packet: Whether this is the first segment of a packet
232
+ index_after (int): Index to insert the segment after
233
+ spanning_packet (bool): Whether this segment belongs to a packet that spans pages
234
+ first_packet (bool): Whether this is the first segment of a packet
235
235
  """
236
236
  logger.trace("Inserting empty segment after index %d (spanning: %s, first: %s)",
237
237
  index_after, spanning_packet, first_packet)
@@ -242,12 +242,12 @@ class OggPage:
242
242
  segment.data = bytes()
243
243
  self.segments.insert(index_after + 1, segment)
244
244
 
245
- def get_opus_packet_size(self, seg_start):
245
+ def get_opus_packet_size(self, seg_start: int) -> int:
246
246
  """
247
247
  Get the size of the opus packet starting at the specified segment index.
248
248
 
249
249
  Args:
250
- seg_start: Starting segment index
250
+ seg_start (int): Starting segment index
251
251
 
252
252
  Returns:
253
253
  int: Size of the opus packet in bytes
@@ -259,12 +259,12 @@ class OggPage:
259
259
  seg_start = seg_start + 1
260
260
  return size
261
261
 
262
- def get_segment_count_of_packet_at(self, seg_start):
262
+ def get_segment_count_of_packet_at(self, seg_start: int) -> int:
263
263
  """
264
264
  Get the number of segments in the packet starting at the specified segment index.
265
265
 
266
266
  Args:
267
- seg_start: Starting segment index
267
+ seg_start (int): Starting segment index
268
268
 
269
269
  Returns:
270
270
  int: Number of segments
@@ -274,13 +274,13 @@ class OggPage:
274
274
  seg_end = seg_end + 1
275
275
  return seg_end - seg_start
276
276
 
277
- def redistribute_packet_data_at(self, seg_start, pad_count):
277
+ def redistribute_packet_data_at(self, seg_start: int, pad_count: int) -> None:
278
278
  """
279
279
  Redistribute packet data starting at the specified segment index.
280
280
 
281
281
  Args:
282
- seg_start: Starting segment index
283
- pad_count: Number of padding bytes to add
282
+ seg_start (int): Starting segment index
283
+ pad_count (int): Number of padding bytes to add
284
284
  """
285
285
  logger.trace("Redistributing packet data at segment %d with %d padding bytes",
286
286
  seg_start, pad_count)
@@ -316,14 +316,14 @@ class OggPage:
316
316
  logger.trace("Redistribution complete, %d segments used", seg_count)
317
317
  assert len(full_data) == 0
318
318
 
319
- def convert_packet_to_framepacking_three_and_pad(self, seg_start, pad=False, count=0):
319
+ def convert_packet_to_framepacking_three_and_pad(self, seg_start: int, pad: bool = False, count: int = 0) -> None:
320
320
  """
321
321
  Convert the packet to framepacking three mode and add padding if required.
322
322
 
323
323
  Args:
324
- seg_start: Starting segment index
325
- pad: Whether to add padding
326
- count: Number of padding bytes to add
324
+ seg_start (int): Starting segment index
325
+ pad (bool): Whether to add padding
326
+ count (int): Number of padding bytes to add
327
327
 
328
328
  Raises:
329
329
  AssertionError: If the segment is not the first packet
@@ -336,13 +336,13 @@ class OggPage:
336
336
  self.segments[seg_start].set_pad_count(count)
337
337
  self.redistribute_packet_data_at(seg_start, count)
338
338
 
339
- def calc_actual_padding_value(self, seg_start, bytes_needed):
339
+ def calc_actual_padding_value(self, seg_start: int, bytes_needed: int) -> int:
340
340
  """
341
341
  Calculate the actual padding value needed for the packet.
342
342
 
343
343
  Args:
344
- seg_start: Starting segment index
345
- bytes_needed: Number of bytes needed for padding
344
+ seg_start (int): Starting segment index
345
+ bytes_needed (int): Number of bytes needed for padding
346
346
 
347
347
  Returns:
348
348
  int: Actual padding value or a special return code
@@ -426,13 +426,13 @@ class OggPage:
426
426
  logger.trace("Calculated actual padding value: %d", result)
427
427
  return result
428
428
 
429
- def pad(self, pad_to, idx_offset=-1):
429
+ def pad(self, pad_to: int, idx_offset: int = -1) -> None:
430
430
  """
431
431
  Pad the page to the specified size.
432
432
 
433
433
  Args:
434
- pad_to: Target size to pad to
435
- idx_offset: Index offset to start from, defaults to last segment
434
+ pad_to (int): Target size to pad to
435
+ idx_offset (int): Index offset to start from, defaults to last segment
436
436
 
437
437
  Raises:
438
438
  RuntimeError: If beginning of last packet cannot be found
@@ -490,7 +490,7 @@ class OggPage:
490
490
  final_size, pad_to)
491
491
  assert final_size == pad_to
492
492
 
493
- def pad_one_byte(self):
493
+ def pad_one_byte(self) -> None:
494
494
  """
495
495
  Add one byte of padding to the page.
496
496
 
@@ -515,7 +515,7 @@ class OggPage:
515
515
  logger.trace("Converting packet to framepacking 3")
516
516
  self.convert_packet_to_framepacking_three_and_pad(i)
517
517
 
518
- def write_page(self, filehandle, sha1=None):
518
+ def write_page(self, filehandle, sha1=None) -> None:
519
519
  """
520
520
  Write the page to a file handle.
521
521
 
@@ -537,12 +537,12 @@ class OggPage:
537
537
  segment.write(filehandle)
538
538
 
539
539
  @staticmethod
540
- def from_page(other_page):
540
+ def from_page(other_page: 'OggPage') -> 'OggPage':
541
541
  """
542
542
  Create a new OggPage based on another page.
543
543
 
544
544
  Args:
545
- other_page: Source page to copy from
545
+ other_page (OggPage): Source page to copy from
546
546
 
547
547
  Returns:
548
548
  OggPage: New page with copied properties
@@ -560,7 +560,7 @@ class OggPage:
560
560
  return new_page
561
561
 
562
562
  @staticmethod
563
- def seek_to_page_header(filehandle):
563
+ def seek_to_page_header(filehandle) -> bool:
564
564
  """
565
565
  Seek to the next OGG page header in a file.
566
566
 
@@ -18,15 +18,15 @@ class OpusPacket:
18
18
  with particular focus on features needed for Tonie compatibility.
19
19
  """
20
20
 
21
- def __init__(self, filehandle, size=-1, last_size=-1, dont_parse_info=False):
21
+ def __init__(self, filehandle, size: int = -1, last_size: int = -1, dont_parse_info: bool = False) -> None:
22
22
  """
23
23
  Initialize a new OpusPacket.
24
24
 
25
25
  Args:
26
26
  filehandle: File handle to read the packet data from, or None to create an empty packet
27
- size: Size of the packet in bytes
28
- last_size: Size of the previous packet in bytes
29
- dont_parse_info: If True, don't parse the packet information even if this is a first packet
27
+ size (int): Size of the packet in bytes
28
+ last_size (int): Size of the previous packet in bytes
29
+ dont_parse_info (bool): If True, don't parse the packet information even if this is a first packet
30
30
  """
31
31
  self.config_value = None
32
32
  self.stereo = None
@@ -51,7 +51,7 @@ class OpusPacket:
51
51
  if self.first_packet and not dont_parse_info:
52
52
  self.parse_segment_info()
53
53
 
54
- def get_frame_count(self):
54
+ def get_frame_count(self) -> int:
55
55
  """
56
56
  Get the number of frames in this packet based on its framepacking.
57
57
 
@@ -68,7 +68,7 @@ class OpusPacket:
68
68
  unpacked = struct.unpack("<B", self.data[1:2])
69
69
  return unpacked[0] & 63
70
70
 
71
- def get_padding(self):
71
+ def get_padding(self) -> int:
72
72
  """
73
73
  Get the padding count for this packet.
74
74
 
@@ -94,7 +94,7 @@ class OpusPacket:
94
94
  logger.trace("Packet has %d bytes of padding", total_padding)
95
95
  return total_padding
96
96
 
97
- def get_frame_size(self):
97
+ def get_frame_size(self) -> float:
98
98
  """
99
99
  Get the frame size in milliseconds based on the config value.
100
100
 
@@ -121,7 +121,7 @@ class OpusPacket:
121
121
  logger.error(error_msg)
122
122
  raise RuntimeError(error_msg)
123
123
 
124
- def calc_granule(self):
124
+ def calc_granule(self) -> float:
125
125
  """
126
126
  Calculate the granule position for this packet.
127
127
 
@@ -133,7 +133,7 @@ class OpusPacket:
133
133
  granule, self.frame_size, self.frame_count)
134
134
  return granule
135
135
 
136
- def parse_segment_info(self):
136
+ def parse_segment_info(self) -> None:
137
137
  """Parse the segment information from the packet data."""
138
138
  logger.trace("Parsing segment info from packet data")
139
139
  byte = struct.unpack("<B", self.data[0:1])[0]
@@ -148,7 +148,7 @@ class OpusPacket:
148
148
  logger.trace("Packet info: config=%d, stereo=%d, framepacking=%d, frame_count=%d, frame_size=%f",
149
149
  self.config_value, self.stereo, self.framepacking, self.frame_count, self.frame_size)
150
150
 
151
- def write(self, filehandle):
151
+ def write(self, filehandle) -> None:
152
152
  """
153
153
  Write the packet data to a file.
154
154
 
@@ -159,7 +159,7 @@ class OpusPacket:
159
159
  logger.trace("Writing %d bytes of packet data to file", len(self.data))
160
160
  filehandle.write(self.data)
161
161
 
162
- def convert_to_framepacking_three(self):
162
+ def convert_to_framepacking_three(self) -> None:
163
163
  """
164
164
  Convert the packet to use framepacking mode 3.
165
165
 
@@ -182,12 +182,12 @@ class OpusPacket:
182
182
  self.framepacking = 3
183
183
  logger.debug("Packet successfully converted to framepacking mode 3")
184
184
 
185
- def set_pad_count(self, count):
185
+ def set_pad_count(self, count: int) -> None:
186
186
  """
187
187
  Set the padding count for this packet.
188
188
 
189
189
  Args:
190
- count: Number of padding bytes to add
190
+ count (int): Number of padding bytes to add
191
191
 
192
192
  Raises:
193
193
  AssertionError: If the packet is not using framepacking mode 3 or is already padded
@@ -14,16 +14,16 @@ from .logger import get_logger
14
14
  logger = get_logger('recursive_processor')
15
15
 
16
16
 
17
- def find_audio_folders(root_path: str) -> List[Dict[str, any]]:
17
+ def find_audio_folders(root_path: str) -> list[dict[str, any]]:
18
18
  """
19
19
  Find and return all folders that contain audio files in a recursive manner,
20
20
  organized in a way that handles nested folder structures.
21
21
 
22
22
  Args:
23
- root_path: Root directory to start searching from
23
+ root_path (str): Root directory to start searching from
24
24
 
25
25
  Returns:
26
- List of dictionaries with folder information, including paths and relationships
26
+ list[dict[str, any]]: List of dictionaries with folder information, including paths and relationships
27
27
  """
28
28
  logger.info("Finding folders with audio files in: %s", root_path)
29
29
 
@@ -68,15 +68,15 @@ def find_audio_folders(root_path: str) -> List[Dict[str, any]]:
68
68
  return folder_list
69
69
 
70
70
 
71
- def determine_processing_folders(folders: List[Dict[str, any]]) -> List[Dict[str, any]]:
71
+ def determine_processing_folders(folders: list[dict[str, any]]) -> list[dict[str, any]]:
72
72
  """
73
73
  Determine which folders should be processed based on their position in the hierarchy.
74
74
 
75
75
  Args:
76
- folders: List of folder dictionaries with hierarchy information
76
+ folders (list[dict[str, any]]): List of folder dictionaries with hierarchy information
77
77
 
78
78
  Returns:
79
- List of folders that should be processed (filtered)
79
+ list[dict[str, any]]: List of folders that should be processed (filtered)
80
80
  """
81
81
  # We'll use a set to track which folders we've decided to process
82
82
  to_process = set()
@@ -120,15 +120,15 @@ def determine_processing_folders(folders: List[Dict[str, any]]) -> List[Dict[str
120
120
  return result
121
121
 
122
122
 
123
- def get_folder_audio_files(folder_path: str) -> List[str]:
123
+ def get_folder_audio_files(folder_path: str) -> list[str]:
124
124
  """
125
125
  Get all audio files in a specific folder.
126
126
 
127
127
  Args:
128
- folder_path: Path to folder
128
+ folder_path (str): Path to folder
129
129
 
130
130
  Returns:
131
- List of paths to audio files in natural sort order
131
+ list[str]: List of paths to audio files in natural sort order
132
132
  """
133
133
  audio_files = glob.glob(os.path.join(folder_path, "*"))
134
134
  filtered_files = filter_directories(audio_files)
@@ -140,15 +140,15 @@ def get_folder_audio_files(folder_path: str) -> List[str]:
140
140
  return sorted_files
141
141
 
142
142
 
143
- def natural_sort(file_list: List[str]) -> List[str]:
143
+ def natural_sort(file_list: list[str]) -> list[str]:
144
144
  """
145
145
  Sort a list of files in natural order (so that 2 comes before 10).
146
146
 
147
147
  Args:
148
- file_list: List of file paths
148
+ file_list (list[str]): List of file paths
149
149
 
150
150
  Returns:
151
- Naturally sorted list of file paths
151
+ list[str]: Naturally sorted list of file paths
152
152
  """
153
153
  def convert(text):
154
154
  return int(text) if text.isdigit() else text.lower()
@@ -159,16 +159,16 @@ def natural_sort(file_list: List[str]) -> List[str]:
159
159
  return sorted(file_list, key=alphanum_key)
160
160
 
161
161
 
162
- def extract_folder_meta(folder_path: str) -> Dict[str, str]:
162
+ def extract_folder_meta(folder_path: str) -> dict[str, str]:
163
163
  """
164
164
  Extract metadata from folder name.
165
165
  Common format might be: "YYYY - NNN - Title"
166
166
 
167
167
  Args:
168
- folder_path: Path to folder
168
+ folder_path (str): Path to folder
169
169
 
170
170
  Returns:
171
- Dictionary with extracted metadata (year, number, title)
171
+ dict[str, str]: Dictionary with extracted metadata (year, number, title)
172
172
  """
173
173
  folder_name = os.path.basename(folder_path)
174
174
  logger.debug("Extracting metadata from folder: %s", folder_name)
@@ -210,12 +210,12 @@ def get_folder_name_from_metadata(folder_path: str, use_media_tags: bool = False
210
210
  and optionally audio file metadata.
211
211
 
212
212
  Args:
213
- folder_path: Path to folder
214
- use_media_tags: Whether to use media tags from audio files if available
215
- template: Optional template for formatting output name using media tags
213
+ folder_path (str): Path to folder
214
+ use_media_tags (bool): Whether to use media tags from audio files if available
215
+ template (str | None): Optional template for formatting output name using media tags
216
216
 
217
217
  Returns:
218
- String with cleaned output name
218
+ str: String with cleaned output name
219
219
  """
220
220
  folder_meta = extract_folder_meta(folder_path)
221
221
  output_name = None
@@ -289,17 +289,17 @@ def get_folder_name_from_metadata(folder_path: str, use_media_tags: bool = False
289
289
  return output_name
290
290
 
291
291
 
292
- def process_recursive_folders(root_path, use_media_tags=False, name_template=None):
292
+ def process_recursive_folders(root_path: str, use_media_tags: bool = False, name_template: str = None) -> list[tuple[str, str, list[str]]]:
293
293
  """
294
294
  Process folders recursively for audio files to create Tonie files.
295
295
 
296
296
  Args:
297
297
  root_path (str): The root path to start processing from
298
298
  use_media_tags (bool): Whether to use media tags for naming
299
- name_template (str): Template for naming files using media tags
299
+ name_template (str | None): Template for naming files using media tags
300
300
 
301
301
  Returns:
302
- list: A list of tuples (output_name, folder_path, audio_files)
302
+ list[tuple[str, str, list[str]]]: A list of tuples (output_name, folder_path, audio_files)
303
303
  """
304
304
  logger = get_logger("recursive_processor")
305
305
  logger.info("Processing folders recursively: %s", root_path)
TonieToolbox/tags.py CHANGED
@@ -10,15 +10,14 @@ from typing import Optional, Union
10
10
 
11
11
  logger = get_logger('tags')
12
12
 
13
- def get_tags(client: TeddyCloudClient) -> bool:
13
+ def get_tags(client: 'TeddyCloudClient') -> bool:
14
14
  """
15
15
  Get and display tags from a TeddyCloud instance.
16
16
 
17
17
  Args:
18
- client: TeddyCloudClient instance to use for API communication
19
-
18
+ client (TeddyCloudClient): TeddyCloudClient instance to use for API communication
20
19
  Returns:
21
- True if tags were retrieved successfully, False otherwise
20
+ bool: True if tags were retrieved successfully, False otherwise
22
21
  """
23
22
  logger.info("Getting tags from TeddyCloud using provided client")
24
23