TonieToolbox 0.1.7__py3-none-any.whl → 0.1.8__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 CHANGED
@@ -2,4 +2,4 @@
2
2
  TonieToolbox - Convert audio files to Tonie box compatible format
3
3
  """
4
4
 
5
- __version__ = '0.1.7'
5
+ __version__ = '0.1.8'
@@ -13,11 +13,15 @@ import shutil
13
13
  import zipfile
14
14
  import tarfile
15
15
  import urllib.request
16
+ import time
16
17
  from pathlib import Path
17
18
 
18
19
  from .logger import get_logger
19
20
  logger = get_logger('dependency_manager')
20
21
 
22
+ CACHE_DIR = os.path.join(os.path.expanduser("~"), ".tonietoolbox")
23
+ LIBS_DIR = os.path.join(CACHE_DIR, "libs")
24
+
21
25
  DEPENDENCIES = {
22
26
  'ffmpeg': {
23
27
  'windows': {
@@ -59,16 +63,7 @@ def get_system():
59
63
 
60
64
  def get_user_data_dir():
61
65
  """Get the user data directory for storing downloaded dependencies."""
62
- system = get_system()
63
-
64
- if system == 'windows':
65
- base_dir = os.environ.get('APPDATA', os.path.expanduser('~'))
66
- elif system == 'darwin':
67
- base_dir = os.path.expanduser('~/Library/Application Support')
68
- else: # linux or other unix-like
69
- base_dir = os.environ.get('XDG_DATA_HOME', os.path.expanduser('~/.local/share'))
70
-
71
- app_dir = os.path.join(base_dir, 'TonieToolbox')
66
+ app_dir = LIBS_DIR
72
67
  logger.debug("Using application data directory: %s", app_dir)
73
68
 
74
69
  os.makedirs(app_dir, exist_ok=True)
@@ -130,31 +125,121 @@ def extract_archive(archive_path, extract_dir):
130
125
  logger.info("Extracting %s to %s", archive_path, extract_dir)
131
126
  os.makedirs(extract_dir, exist_ok=True)
132
127
 
128
+ # Extract to a temporary subdirectory first
129
+ temp_extract_dir = os.path.join(extract_dir, "_temp_extract")
130
+ os.makedirs(temp_extract_dir, exist_ok=True)
131
+
133
132
  if archive_path.endswith('.zip'):
134
133
  logger.debug("Extracting ZIP archive")
135
134
  with zipfile.ZipFile(archive_path, 'r') as zip_ref:
136
- zip_ref.extractall(extract_dir)
137
- logger.trace("Extracted files: %s", zip_ref.namelist())
135
+ zip_ref.extractall(temp_extract_dir)
136
+ files_extracted = zip_ref.namelist()
137
+ logger.trace("Extracted files: %s", files_extracted)
138
138
  elif archive_path.endswith(('.tar.gz', '.tgz')):
139
139
  logger.debug("Extracting TAR.GZ archive")
140
140
  with tarfile.open(archive_path, 'r:gz') as tar_ref:
141
- tar_ref.extractall(extract_dir)
142
- logger.trace("Extracted files: %s", tar_ref.getnames())
141
+ tar_ref.extractall(temp_extract_dir)
142
+ files_extracted = tar_ref.getnames()
143
+ logger.trace("Extracted files: %s", files_extracted)
143
144
  elif archive_path.endswith(('.tar.xz', '.txz')):
144
145
  logger.debug("Extracting TAR.XZ archive")
145
146
  with tarfile.open(archive_path, 'r:xz') as tar_ref:
146
- tar_ref.extractall(extract_dir)
147
- logger.trace("Extracted files: %s", tar_ref.getnames())
147
+ tar_ref.extractall(temp_extract_dir)
148
+ files_extracted = tar_ref.getnames()
149
+ logger.trace("Extracted files: %s", files_extracted)
148
150
  elif archive_path.endswith('.tar'):
149
151
  logger.debug("Extracting TAR archive")
150
152
  with tarfile.open(archive_path, 'r') as tar_ref:
151
- tar_ref.extractall(extract_dir)
152
- logger.trace("Extracted files: %s", tar_ref.getnames())
153
+ tar_ref.extractall(temp_extract_dir)
154
+ files_extracted = tar_ref.getnames()
155
+ logger.trace("Extracted files: %s", files_extracted)
153
156
  else:
154
157
  logger.error("Unsupported archive format: %s", archive_path)
155
158
  return False
156
159
 
157
160
  logger.info("Archive extracted successfully")
161
+
162
+ # Fix FFmpeg nested directory issue by moving binary files to the correct location
163
+ dependency_name = os.path.basename(extract_dir)
164
+ if dependency_name == 'ffmpeg':
165
+ # Check for common nested directory structures for FFmpeg
166
+ if os.path.exists(os.path.join(temp_extract_dir, "ffmpeg-master-latest-win64-gpl", "bin")):
167
+ # Windows FFmpeg path
168
+ bin_dir = os.path.join(temp_extract_dir, "ffmpeg-master-latest-win64-gpl", "bin")
169
+ logger.debug("Found nested FFmpeg bin directory: %s", bin_dir)
170
+
171
+ # Move all files from bin directory to the main dependency directory
172
+ for file in os.listdir(bin_dir):
173
+ src = os.path.join(bin_dir, file)
174
+ dst = os.path.join(extract_dir, file)
175
+ logger.debug("Moving %s to %s", src, dst)
176
+ shutil.move(src, dst)
177
+
178
+ elif os.path.exists(os.path.join(temp_extract_dir, "ffmpeg-master-latest-linux64-gpl", "bin")):
179
+ # Linux FFmpeg path
180
+ bin_dir = os.path.join(temp_extract_dir, "ffmpeg-master-latest-linux64-gpl", "bin")
181
+ logger.debug("Found nested FFmpeg bin directory: %s", bin_dir)
182
+
183
+ # Move all files from bin directory to the main dependency directory
184
+ for file in os.listdir(bin_dir):
185
+ src = os.path.join(bin_dir, file)
186
+ dst = os.path.join(extract_dir, file)
187
+ logger.debug("Moving %s to %s", src, dst)
188
+ shutil.move(src, dst)
189
+ else:
190
+ # Check for any directory with a 'bin' subdirectory
191
+ for root, dirs, _ in os.walk(temp_extract_dir):
192
+ if "bin" in dirs:
193
+ bin_dir = os.path.join(root, "bin")
194
+ logger.debug("Found nested bin directory: %s", bin_dir)
195
+
196
+ # Move all files from bin directory to the main dependency directory
197
+ for file in os.listdir(bin_dir):
198
+ src = os.path.join(bin_dir, file)
199
+ dst = os.path.join(extract_dir, file)
200
+ logger.debug("Moving %s to %s", src, dst)
201
+ shutil.move(src, dst)
202
+ break
203
+ else:
204
+ # If no bin directory was found, just move everything from the temp directory
205
+ logger.debug("No bin directory found, moving all files from temp directory")
206
+ for item in os.listdir(temp_extract_dir):
207
+ src = os.path.join(temp_extract_dir, item)
208
+ dst = os.path.join(extract_dir, item)
209
+ if os.path.isfile(src):
210
+ logger.debug("Moving file %s to %s", src, dst)
211
+ shutil.move(src, dst)
212
+ else:
213
+ # For non-FFmpeg dependencies, just move all files from temp directory
214
+ for item in os.listdir(temp_extract_dir):
215
+ src = os.path.join(temp_extract_dir, item)
216
+ dst = os.path.join(extract_dir, item)
217
+ if os.path.isfile(src):
218
+ logger.debug("Moving file %s to %s", src, dst)
219
+ shutil.move(src, dst)
220
+ else:
221
+ logger.debug("Moving directory %s to %s", src, dst)
222
+ # If destination already exists, remove it first
223
+ if os.path.exists(dst):
224
+ shutil.rmtree(dst)
225
+ shutil.move(src, dst)
226
+
227
+ # Clean up the temporary extraction directory
228
+ try:
229
+ shutil.rmtree(temp_extract_dir)
230
+ logger.debug("Removed temporary extraction directory")
231
+ except Exception as e:
232
+ logger.warning("Failed to remove temporary extraction directory: %s", e)
233
+
234
+ # Remove the archive file after successful extraction
235
+ try:
236
+ logger.debug("Removing archive file: %s", archive_path)
237
+ os.remove(archive_path)
238
+ logger.debug("Archive file removed successfully")
239
+ except Exception as e:
240
+ logger.warning("Failed to remove archive file: %s (error: %s)", archive_path, e)
241
+ # Continue even if we couldn't remove the file
242
+
158
243
  return True
159
244
  except Exception as e:
160
245
  logger.error("Failed to extract %s: %s", archive_path, e)
@@ -302,73 +387,91 @@ def ensure_dependency(dependency_name, auto_download=False):
302
387
  logger.error("Unknown dependency: %s", dependency_name)
303
388
  return None
304
389
 
305
- # First check if it's already in PATH
306
- bin_name = dependency_name if dependency_name != 'opusenc' else 'opusenc'
307
- path_binary = check_binary_in_path(bin_name)
308
- if path_binary:
309
- logger.info("Found %s in PATH: %s", dependency_name, path_binary)
310
- return path_binary
311
-
312
390
  # Set up paths to check for previously downloaded versions
313
391
  user_data_dir = get_user_data_dir()
314
392
  dependency_info = DEPENDENCIES[dependency_name].get(system, {})
315
- extract_dir_name = dependency_info.get('extract_dir', dependency_name)
316
- binary_path = dependency_info.get('bin_path', bin_name)
317
- extract_dir = os.path.join(user_data_dir, extract_dir_name)
393
+ binary_path = dependency_info.get('bin_path', dependency_name if dependency_name != 'opusenc' else 'opusenc')
318
394
 
319
- # Check if we already downloaded and extracted it previously
320
- logger.debug("Checking for previously downloaded %s in %s", dependency_name, extract_dir)
321
- if os.path.exists(extract_dir):
322
- existing_binary = find_binary_in_extracted_dir(extract_dir, binary_path)
323
- if existing_binary and os.path.exists(existing_binary):
324
- # Verify that the binary works
325
- logger.info("Found previously downloaded %s: %s", dependency_name, existing_binary)
326
- try:
327
- if os.access(existing_binary, os.X_OK) or system == 'windows':
328
- if system in ['linux', 'darwin']:
329
- logger.debug("Ensuring executable permissions on %s", existing_binary)
330
- os.chmod(existing_binary, 0o755)
331
-
332
- # Quick check to verify binary works
333
- if dependency_name == 'opusenc':
334
- cmd = [existing_binary, '--version']
335
- try:
336
- result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5)
337
- if result.returncode == 0:
338
- logger.info("Using previously downloaded %s: %s", dependency_name, existing_binary)
339
- return existing_binary
340
- except:
341
- # If --version fails, try without arguments
395
+ # Create a specific folder for this dependency
396
+ dependency_dir = os.path.join(user_data_dir, dependency_name)
397
+
398
+ # First priority: Check if we already downloaded and extracted it previously
399
+ # When auto_download is True, we'll skip this check and download fresh versions
400
+ if not auto_download:
401
+ logger.debug("Checking for previously downloaded %s in %s", dependency_name, dependency_dir)
402
+ if os.path.exists(dependency_dir):
403
+ existing_binary = find_binary_in_extracted_dir(dependency_dir, binary_path)
404
+ if existing_binary and os.path.exists(existing_binary):
405
+ # Verify that the binary works
406
+ logger.info("Found previously downloaded %s: %s", dependency_name, existing_binary)
407
+ try:
408
+ if os.access(existing_binary, os.X_OK) or system == 'windows':
409
+ if system in ['linux', 'darwin']:
410
+ logger.debug("Ensuring executable permissions on %s", existing_binary)
411
+ os.chmod(existing_binary, 0o755)
412
+
413
+ # Quick check to verify binary works
414
+ if dependency_name == 'opusenc':
415
+ cmd = [existing_binary, '--version']
342
416
  try:
343
- result = subprocess.run([existing_binary], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5)
417
+ result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5)
418
+ if result.returncode == 0:
419
+ logger.info("Using previously downloaded %s: %s", dependency_name, existing_binary)
420
+ return existing_binary
421
+ except:
422
+ # If --version fails, try without arguments
423
+ try:
424
+ result = subprocess.run([existing_binary], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5)
425
+ if result.returncode == 0:
426
+ logger.info("Using previously downloaded %s: %s", dependency_name, existing_binary)
427
+ return existing_binary
428
+ except:
429
+ pass
430
+ else:
431
+ cmd = [existing_binary, '-version']
432
+ try:
433
+ result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5)
344
434
  if result.returncode == 0:
345
435
  logger.info("Using previously downloaded %s: %s", dependency_name, existing_binary)
346
436
  return existing_binary
347
437
  except:
348
438
  pass
349
- else:
350
- cmd = [existing_binary, '-version']
351
- try:
352
- result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5)
353
- if result.returncode == 0:
354
- logger.info("Using previously downloaded %s: %s", dependency_name, existing_binary)
355
- return existing_binary
356
- except:
357
- pass
358
-
359
- logger.warning("Previously downloaded %s exists but failed verification", dependency_name)
439
+
440
+ logger.warning("Previously downloaded %s exists but failed verification", dependency_name)
441
+ except Exception as e:
442
+ logger.warning("Error verifying downloaded binary: %s", e)
443
+
444
+ # Second priority: Check if it's in PATH (only if auto_download is False)
445
+ bin_name = dependency_name if dependency_name != 'opusenc' else 'opusenc'
446
+ path_binary = check_binary_in_path(bin_name)
447
+ if path_binary:
448
+ logger.info("Found %s in PATH: %s", dependency_name, path_binary)
449
+ return path_binary
450
+ else:
451
+ logger.info("Auto-download enabled, forcing download/installation of %s", dependency_name)
452
+ # If there's an existing download directory, rename or remove it
453
+ if os.path.exists(dependency_dir):
454
+ try:
455
+ backup_dir = f"{dependency_dir}_backup_{int(time.time())}"
456
+ logger.debug("Moving existing dependency directory to: %s", backup_dir)
457
+ os.rename(dependency_dir, backup_dir)
360
458
  except Exception as e:
361
- logger.warning("Error verifying downloaded binary: %s", e)
459
+ logger.warning("Failed to rename existing dependency directory: %s", e)
460
+ try:
461
+ logger.debug("Trying to remove existing dependency directory")
462
+ shutil.rmtree(dependency_dir, ignore_errors=True)
463
+ except Exception as e:
464
+ logger.warning("Failed to remove existing dependency directory: %s", e)
362
465
 
363
466
  # If auto_download is not enabled, don't try to install or download
364
467
  if not auto_download:
365
- logger.warning("%s not found in PATH and auto-download is disabled. Use --auto-download to enable automatic installation.", dependency_name)
468
+ logger.warning("%s not found in libs directory or PATH and auto-download is disabled. Use --auto-download to enable automatic installation.", dependency_name)
366
469
  return None
367
470
 
368
- # If not in PATH, check if we should install via package manager
471
+ # If not in libs or PATH, check if we should install via package manager
369
472
  if 'package' in dependency_info:
370
473
  package_name = dependency_info['package']
371
- logger.info("%s not found. Attempting to install %s package...", dependency_name, package_name)
474
+ logger.info("%s not found or forced download. Attempting to install %s package...", dependency_name, package_name)
372
475
  if install_package(package_name):
373
476
  path_binary = check_binary_in_path(bin_name)
374
477
  if path_binary:
@@ -382,16 +485,18 @@ def ensure_dependency(dependency_name, auto_download=False):
382
485
 
383
486
  # Set up download paths
384
487
  download_url = dependency_info['url']
385
- os.makedirs(extract_dir, exist_ok=True)
488
+
489
+ # Create dependency-specific directory
490
+ os.makedirs(dependency_dir, exist_ok=True)
386
491
 
387
492
  # Download and extract
388
493
  archive_ext = '.zip' if download_url.endswith('zip') else '.tar.xz'
389
- archive_path = os.path.join(user_data_dir, f"{dependency_name}{archive_ext}")
494
+ archive_path = os.path.join(dependency_dir, f"{dependency_name}{archive_ext}")
390
495
  logger.debug("Using archive path: %s", archive_path)
391
496
 
392
497
  if download_file(download_url, archive_path):
393
- if extract_archive(archive_path, extract_dir):
394
- binary = find_binary_in_extracted_dir(extract_dir, binary_path)
498
+ if extract_archive(archive_path, dependency_dir):
499
+ binary = find_binary_in_extracted_dir(dependency_dir, binary_path)
395
500
  if binary:
396
501
  # Make sure it's executable on Unix-like systems
397
502
  if system in ['linux', 'darwin']:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TonieToolbox
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary: Convert audio files to Tonie box compatible format
5
5
  Home-page: https://github.com/Quentendo64/TonieToolbox
6
6
  Author: Quentendo64
@@ -1,8 +1,8 @@
1
- TonieToolbox/__init__.py,sha256=H7iZa88oEbyIf0RTuwe8HaIbX7hDamX8WW6fQubcijs,96
1
+ TonieToolbox/__init__.py,sha256=hVjFi2m8AmDYdgUfUTtXSWyyN2C7gLHLN2UgFdmsjpo,96
2
2
  TonieToolbox/__main__.py,sha256=eEsfnwLsgI6ynwR4uxo4CKptuOJDpGp6lYWSRwJSY3M,8520
3
3
  TonieToolbox/audio_conversion.py,sha256=10PayO1VQDGee2bcO6JD8zRSFoJRJhO2pBeba5k15vk,7998
4
4
  TonieToolbox/constants.py,sha256=QQWQpnCI65GByLlXLOkt2n8nALLu4m6BWp0zuhI3M04,2021
5
- TonieToolbox/dependency_manager.py,sha256=LHH8PlKp12_TkFFk2dyg6CRURh3-oCOiDff1AIEUbKU,17397
5
+ TonieToolbox/dependency_manager.py,sha256=Ag4_xuB6shqTAuoIVs5hfwzXA3nrOJbxW_08vC_76Ig,23386
6
6
  TonieToolbox/filename_generator.py,sha256=RqQHyGTKakuWR01yMSnFVMU_HfLw3rqFxKhXNIHdTlg,3441
7
7
  TonieToolbox/logger.py,sha256=Up9fBVkOZwkY61_645bX4tienCpyVSkap-FeTV0v730,1441
8
8
  TonieToolbox/ogg_page.py,sha256=-ViaIRBgh5ayfwmyplL8QmmRr5P36X8W0DdHkSFUYUU,21948
@@ -12,9 +12,9 @@ TonieToolbox/tonie_file.py,sha256=nIS4qhpBKIyPvTU39yYljRidpY6cz78halXlz3HJy9w,15
12
12
  TonieToolbox/tonie_header.proto,sha256=WaWfwO4VrwGtscK2ujfDRKtpeBpaVPoZhI8iMmR-C0U,202
13
13
  TonieToolbox/tonie_header_pb2.py,sha256=s5bp4ULTEekgq6T61z9fDkRavyPM-3eREs20f_Pxxe8,3665
14
14
  TonieToolbox/version_handler.py,sha256=7Zx-pgzAUhz6jMplvNal1wHyxidodVxaNcAV0EMph5k,9778
15
- tonietoolbox-0.1.7.dist-info/licenses/LICENSE.md,sha256=rGoga9ZAgNco9fBapVFpWf6ri7HOBp1KRnt1uIruXMk,35190
16
- tonietoolbox-0.1.7.dist-info/METADATA,sha256=7kF7ZRqq_GWhRUrw-z6XlxdfG6kY_nTOtlIUfVFk0DU,8971
17
- tonietoolbox-0.1.7.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
18
- tonietoolbox-0.1.7.dist-info/entry_points.txt,sha256=oqpeyBxel7aScg35Xr4gZKnf486S5KW9okqeBwyJxxc,60
19
- tonietoolbox-0.1.7.dist-info/top_level.txt,sha256=Wkkm-2p7I3ENfS7ZbYtYUB2g-xwHrXVlERHfonsOPuE,13
20
- tonietoolbox-0.1.7.dist-info/RECORD,,
15
+ tonietoolbox-0.1.8.dist-info/licenses/LICENSE.md,sha256=rGoga9ZAgNco9fBapVFpWf6ri7HOBp1KRnt1uIruXMk,35190
16
+ tonietoolbox-0.1.8.dist-info/METADATA,sha256=4TWL1cpSAVPDreuEIhjT2I3EZkVr7we-UFHrSNJ4Vlw,8971
17
+ tonietoolbox-0.1.8.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
18
+ tonietoolbox-0.1.8.dist-info/entry_points.txt,sha256=oqpeyBxel7aScg35Xr4gZKnf486S5KW9okqeBwyJxxc,60
19
+ tonietoolbox-0.1.8.dist-info/top_level.txt,sha256=Wkkm-2p7I3ENfS7ZbYtYUB2g-xwHrXVlERHfonsOPuE,13
20
+ tonietoolbox-0.1.8.dist-info/RECORD,,