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 +1 -1
- TonieToolbox/dependency_manager.py +176 -71
- {tonietoolbox-0.1.7.dist-info → tonietoolbox-0.1.8.dist-info}/METADATA +1 -1
- {tonietoolbox-0.1.7.dist-info → tonietoolbox-0.1.8.dist-info}/RECORD +8 -8
- {tonietoolbox-0.1.7.dist-info → tonietoolbox-0.1.8.dist-info}/WHEEL +0 -0
- {tonietoolbox-0.1.7.dist-info → tonietoolbox-0.1.8.dist-info}/entry_points.txt +0 -0
- {tonietoolbox-0.1.7.dist-info → tonietoolbox-0.1.8.dist-info}/licenses/LICENSE.md +0 -0
- {tonietoolbox-0.1.7.dist-info → tonietoolbox-0.1.8.dist-info}/top_level.txt +0 -0
TonieToolbox/__init__.py
CHANGED
@@ -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
|
-
|
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(
|
137
|
-
|
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(
|
142
|
-
|
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(
|
147
|
-
|
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(
|
152
|
-
|
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
|
-
|
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
|
-
#
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
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(
|
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
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
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("
|
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
|
-
|
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(
|
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,
|
394
|
-
binary = find_binary_in_extracted_dir(
|
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,8 +1,8 @@
|
|
1
|
-
TonieToolbox/__init__.py,sha256=
|
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=
|
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.
|
16
|
-
tonietoolbox-0.1.
|
17
|
-
tonietoolbox-0.1.
|
18
|
-
tonietoolbox-0.1.
|
19
|
-
tonietoolbox-0.1.
|
20
|
-
tonietoolbox-0.1.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|