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.
@@ -13,8 +13,8 @@ import base64
13
13
  from mutagen.flac import Picture
14
14
  from .logger import get_logger
15
15
  from .dependency_manager import is_mutagen_available, ensure_mutagen
16
+ from .constants import ARTWORK_NAMES, ARTWORK_EXTENSIONS, TAG_VALUE_REPLACEMENTS, TAG_MAPPINGS
16
17
 
17
- # Global variables to track dependency state and store module references
18
18
  MUTAGEN_AVAILABLE = False
19
19
  mutagen = None
20
20
  ID3 = None
@@ -53,84 +53,11 @@ def _import_mutagen():
53
53
  MUTAGEN_AVAILABLE = False
54
54
  return False
55
55
 
56
- # Try to import mutagen if it's available
57
56
  if is_mutagen_available():
58
57
  _import_mutagen()
59
58
 
60
59
  logger = get_logger('media_tags')
61
60
 
62
- # Define tag mapping for different formats to standardized names
63
- # This helps normalize tags across different audio formats
64
- TAG_MAPPING = {
65
- # ID3 (MP3) tags
66
- 'TIT2': 'title',
67
- 'TALB': 'album',
68
- 'TPE1': 'artist',
69
- 'TPE2': 'albumartist',
70
- 'TCOM': 'composer',
71
- 'TRCK': 'tracknumber',
72
- 'TPOS': 'discnumber',
73
- 'TDRC': 'date',
74
- 'TCON': 'genre',
75
- 'TPUB': 'publisher',
76
- 'TCOP': 'copyright',
77
- 'COMM': 'comment',
78
-
79
- # Vorbis tags (FLAC, OGG)
80
- 'title': 'title',
81
- 'album': 'album',
82
- 'artist': 'artist',
83
- 'albumartist': 'albumartist',
84
- 'composer': 'composer',
85
- 'tracknumber': 'tracknumber',
86
- 'discnumber': 'discnumber',
87
- 'date': 'date',
88
- 'genre': 'genre',
89
- 'publisher': 'publisher',
90
- 'copyright': 'copyright',
91
- 'comment': 'comment',
92
-
93
- # MP4 (M4A, AAC) tags
94
- '©nam': 'title',
95
- '©alb': 'album',
96
- '©ART': 'artist',
97
- 'aART': 'albumartist',
98
- '©wrt': 'composer',
99
- 'trkn': 'tracknumber',
100
- 'disk': 'discnumber',
101
- '©day': 'date',
102
- '©gen': 'genre',
103
- '©pub': 'publisher',
104
- 'cprt': 'copyright',
105
- '©cmt': 'comment',
106
-
107
- # Additional tags some files might have
108
- 'album_artist': 'albumartist',
109
- 'track': 'tracknumber',
110
- 'track_number': 'tracknumber',
111
- 'disc': 'discnumber',
112
- 'disc_number': 'discnumber',
113
- 'year': 'date',
114
- 'albuminterpret': 'albumartist', # German tag name
115
- 'interpret': 'artist', # German tag name
116
- }
117
-
118
- # Define replacements for special tag values
119
- TAG_VALUE_REPLACEMENTS = {
120
- "Die drei ???": "Die drei Fragezeichen",
121
- "Die Drei ???": "Die drei Fragezeichen",
122
- "DIE DREI ???": "Die drei Fragezeichen",
123
- "Die drei !!!": "Die drei Ausrufezeichen",
124
- "Die Drei !!!": "Die drei Ausrufezeichen",
125
- "DIE DREI !!!": "Die drei Ausrufezeichen",
126
- "TKKG™": "TKKG",
127
- "Die drei ??? Kids": "Die drei Fragezeichen Kids",
128
- "Die Drei ??? Kids": "Die drei Fragezeichen Kids",
129
- "Bibi & Tina": "Bibi und Tina",
130
- "Benjamin Blümchen™": "Benjamin Blümchen",
131
- "???": "Fragezeichen",
132
- "!!!": "Ausrufezeichen",
133
- }
134
61
 
135
62
  def normalize_tag_value(value: str) -> str:
136
63
  """
@@ -146,7 +73,6 @@ def normalize_tag_value(value: str) -> str:
146
73
  if not value:
147
74
  return value
148
75
 
149
- # Check for direct replacements first
150
76
  if value in TAG_VALUE_REPLACEMENTS:
151
77
  logger.debug("Direct tag replacement: '%s' -> '%s'", value, TAG_VALUE_REPLACEMENTS[value])
152
78
  return TAG_VALUE_REPLACEMENTS[value]
@@ -158,10 +84,7 @@ def normalize_tag_value(value: str) -> str:
158
84
  original = result
159
85
  result = result.replace(pattern, replacement)
160
86
  logger.debug("Partial tag replacement: '%s' -> '%s'", original, result)
161
-
162
- # Special case for "Die drei ???" type patterns that might have been missed
163
- result = result.replace("???", "Fragezeichen")
164
-
87
+
165
88
  return result
166
89
 
167
90
  def is_available() -> bool:
@@ -213,60 +136,61 @@ def get_file_tags(file_path: str) -> Dict[str, Any]:
213
136
  id3 = audio if isinstance(audio, ID3) else audio.ID3
214
137
  for tag_key, tag_value in id3.items():
215
138
  tag_name = tag_key.split(':')[0] # Handle ID3 tags with colons
216
- if tag_name in TAG_MAPPING:
139
+ if tag_name in TAG_MAPPINGS:
217
140
  tag_value_str = str(tag_value)
218
- tags[TAG_MAPPING[tag_name]] = normalize_tag_value(tag_value_str)
141
+ tags[TAG_MAPPINGS[tag_name]] = normalize_tag_value(tag_value_str)
219
142
  except (AttributeError, TypeError) as e:
220
143
  logger.debug("Error accessing ID3 tags: %s", e)
221
144
  # Try alternative approach for ID3 tags
222
145
  try:
223
146
  if hasattr(audio, 'tags') and audio.tags:
224
147
  for tag_key in audio.tags.keys():
225
- if tag_key in TAG_MAPPING:
148
+ if tag_key in TAG_MAPPINGS:
226
149
  tag_value = audio.tags[tag_key]
227
150
  if hasattr(tag_value, 'text'):
228
151
  tag_value_str = str(tag_value.text[0]) if tag_value.text else ''
229
152
  else:
230
153
  tag_value_str = str(tag_value)
231
- tags[TAG_MAPPING[tag_key]] = normalize_tag_value(tag_value_str)
154
+ tags[TAG_MAPPINGS[tag_key]] = normalize_tag_value(tag_value_str)
232
155
  except Exception as e:
233
156
  logger.debug("Alternative ID3 tag reading failed: %s", e)
234
157
  elif isinstance(audio, (FLAC, OggOpus, OggVorbis)):
235
158
  # FLAC and OGG files
236
159
  for tag_key, tag_values in audio.items():
237
160
  tag_key_lower = tag_key.lower()
238
- if tag_key_lower in TAG_MAPPING:
161
+ if tag_key_lower in TAG_MAPPINGS:
239
162
  # Some tags might have multiple values, we'll take the first one
240
163
  tag_value = tag_values[0] if tag_values else ''
241
- tags[TAG_MAPPING[tag_key_lower]] = normalize_tag_value(tag_value)
164
+ tags[TAG_MAPPINGS[tag_key_lower]] = normalize_tag_value(tag_value)
242
165
  elif isinstance(audio, MP4):
243
166
  # MP4 files
244
167
  for tag_key, tag_value in audio.items():
245
- if tag_key in TAG_MAPPING:
168
+ if tag_key in TAG_MAPPINGS:
246
169
  if isinstance(tag_value, list):
247
170
  if tag_key in ('trkn', 'disk'):
248
171
  # Handle track and disc number tuples
249
172
  if tag_value and isinstance(tag_value[0], tuple) and len(tag_value[0]) >= 1:
250
- tags[TAG_MAPPING[tag_key]] = str(tag_value[0][0])
173
+ tags[TAG_MAPPINGS[tag_key]] = str(tag_value[0][0])
251
174
  else:
252
175
  tag_value_str = str(tag_value[0]) if tag_value else ''
253
- tags[TAG_MAPPING[tag_key]] = normalize_tag_value(tag_value_str)
176
+ tags[TAG_MAPPINGS[tag_key]] = normalize_tag_value(tag_value_str)
254
177
  else:
255
178
  tag_value_str = str(tag_value)
256
- tags[TAG_MAPPING[tag_key]] = normalize_tag_value(tag_value_str)
179
+ tags[TAG_MAPPINGS[tag_key]] = normalize_tag_value(tag_value_str)
257
180
  else:
258
181
  # Generic audio file - try to read any available tags
259
182
  for tag_key, tag_value in audio.items():
260
183
  tag_key_lower = tag_key.lower()
261
- if tag_key_lower in TAG_MAPPING:
184
+ if tag_key_lower in TAG_MAPPINGS:
262
185
  if isinstance(tag_value, list):
263
186
  tag_value_str = str(tag_value[0]) if tag_value else ''
264
- tags[TAG_MAPPING[tag_key_lower]] = normalize_tag_value(tag_value_str)
187
+ tags[TAG_MAPPINGS[tag_key_lower]] = normalize_tag_value(tag_value_str)
265
188
  else:
266
189
  tag_value_str = str(tag_value)
267
- tags[TAG_MAPPING[tag_key_lower]] = normalize_tag_value(tag_value_str)
190
+ tags[TAG_MAPPINGS[tag_key_lower]] = normalize_tag_value(tag_value_str)
268
191
 
269
192
  logger.debug("Successfully read %d tags from file", len(tags))
193
+ logger.debug("Tags: %s", str(tags))
270
194
  return tags
271
195
  except Exception as e:
272
196
  logger.error("Error reading tags from file %s: %s", file_path, str(e))
@@ -330,12 +254,12 @@ def extract_album_info(folder_path: str) -> Dict[str, str]:
330
254
  if not all_tags:
331
255
  logger.debug("Could not read tags from any files in folder")
332
256
  return {}
333
-
334
- # Try to find consistent album information
335
257
  result = {}
336
- key_tags = ['album', 'albumartist', 'artist', 'date', 'genre']
258
+ all_tag_names = set()
259
+ for tags in all_tags:
260
+ all_tag_names.update(tags.keys())
337
261
 
338
- for tag_name in key_tags:
262
+ for tag_name in all_tag_names:
339
263
  # Count occurrences of each value
340
264
  value_counts = {}
341
265
  for tags in all_tags:
@@ -614,13 +538,10 @@ def find_cover_image(source_dir):
614
538
  return None
615
539
 
616
540
  # Common cover image file names
617
- cover_names = [
618
- 'cover', 'folder', 'album', 'front', 'artwork', 'image',
619
- 'albumart', 'albumartwork', 'booklet'
620
- ]
541
+ cover_names = ARTWORK_NAMES
621
542
 
622
543
  # Common image extensions
623
- image_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']
544
+ image_extensions = ARTWORK_EXTENSIONS
624
545
 
625
546
  # Try different variations
626
547
  for name in cover_names:
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