TonieToolbox 0.1.6__tar.gz → 0.1.8__tar.gz
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-0.1.6/TonieToolbox.egg-info → tonietoolbox-0.1.8}/PKG-INFO +1 -1
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/__init__.py +1 -1
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/__main__.py +4 -1
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/dependency_manager.py +190 -47
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/version_handler.py +69 -6
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8/TonieToolbox.egg-info}/PKG-INFO +1 -1
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/LICENSE.md +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/MANIFEST.in +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/README.md +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/audio_conversion.py +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/constants.py +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/filename_generator.py +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/logger.py +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/ogg_page.py +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/opus_packet.py +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/tonie_analysis.py +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/tonie_file.py +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/tonie_header.proto +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox/tonie_header_pb2.py +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox.egg-info/SOURCES.txt +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox.egg-info/dependency_links.txt +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox.egg-info/entry_points.txt +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox.egg-info/requires.txt +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/TonieToolbox.egg-info/top_level.txt +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/pyproject.toml +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/setup.cfg +0 -0
- {tonietoolbox-0.1.6 → tonietoolbox-0.1.8}/setup.py +0 -0
@@ -89,10 +89,13 @@ def main():
|
|
89
89
|
# Check for updates
|
90
90
|
if not args.skip_update_check:
|
91
91
|
logger.debug("Checking for updates (force_refresh=%s)", args.force_refresh_cache)
|
92
|
-
check_for_updates(
|
92
|
+
is_latest, latest_version, message, update_confirmed = check_for_updates(
|
93
93
|
quiet=args.silent or args.quiet,
|
94
94
|
force_refresh=args.force_refresh_cache
|
95
95
|
)
|
96
|
+
|
97
|
+
if not is_latest and not update_confirmed and not (args.silent or args.quiet):
|
98
|
+
logger.info("Update available but user chose to continue without updating.")
|
96
99
|
|
97
100
|
ffmpeg_binary = args.ffmpeg
|
98
101
|
if ffmpeg_binary is None:
|
@@ -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,22 +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
|
-
#
|
306
|
-
|
307
|
-
|
308
|
-
if
|
309
|
-
|
310
|
-
|
390
|
+
# Set up paths to check for previously downloaded versions
|
391
|
+
user_data_dir = get_user_data_dir()
|
392
|
+
dependency_info = DEPENDENCIES[dependency_name].get(system, {})
|
393
|
+
binary_path = dependency_info.get('bin_path', dependency_name if dependency_name != 'opusenc' else 'opusenc')
|
394
|
+
|
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']
|
416
|
+
try:
|
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)
|
434
|
+
if result.returncode == 0:
|
435
|
+
logger.info("Using previously downloaded %s: %s", dependency_name, existing_binary)
|
436
|
+
return existing_binary
|
437
|
+
except:
|
438
|
+
pass
|
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)
|
458
|
+
except Exception as 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)
|
311
465
|
|
312
466
|
# If auto_download is not enabled, don't try to install or download
|
313
467
|
if not auto_download:
|
314
|
-
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)
|
315
469
|
return None
|
316
470
|
|
317
|
-
# If not in PATH, check if we should install via package manager
|
318
|
-
if 'package' in
|
319
|
-
package_name =
|
320
|
-
logger.info("%s not found. Attempting to install %s package...", dependency_name, package_name)
|
471
|
+
# If not in libs or PATH, check if we should install via package manager
|
472
|
+
if 'package' in dependency_info:
|
473
|
+
package_name = dependency_info['package']
|
474
|
+
logger.info("%s not found or forced download. Attempting to install %s package...", dependency_name, package_name)
|
321
475
|
if install_package(package_name):
|
322
476
|
path_binary = check_binary_in_path(bin_name)
|
323
477
|
if path_binary:
|
@@ -325,35 +479,24 @@ def ensure_dependency(dependency_name, auto_download=False):
|
|
325
479
|
return path_binary
|
326
480
|
|
327
481
|
# If not installable via package manager or installation failed, try downloading
|
328
|
-
if 'url' not in
|
482
|
+
if 'url' not in dependency_info:
|
329
483
|
logger.error("Cannot download %s for %s", dependency_name, system)
|
330
484
|
return None
|
331
485
|
|
332
|
-
# Set up paths
|
333
|
-
user_data_dir = get_user_data_dir()
|
334
|
-
dependency_info = DEPENDENCIES[dependency_name][system]
|
486
|
+
# Set up download paths
|
335
487
|
download_url = dependency_info['url']
|
336
|
-
extract_dir_name = dependency_info['extract_dir']
|
337
|
-
binary_path = dependency_info['bin_path']
|
338
|
-
|
339
|
-
extract_dir = os.path.join(user_data_dir, extract_dir_name)
|
340
|
-
logger.debug("Using extract directory: %s", extract_dir)
|
341
|
-
os.makedirs(extract_dir, exist_ok=True)
|
342
488
|
|
343
|
-
#
|
344
|
-
|
345
|
-
if existing_binary and os.path.exists(existing_binary):
|
346
|
-
logger.info("Using existing %s: %s", dependency_name, existing_binary)
|
347
|
-
return existing_binary
|
489
|
+
# Create dependency-specific directory
|
490
|
+
os.makedirs(dependency_dir, exist_ok=True)
|
348
491
|
|
349
492
|
# Download and extract
|
350
493
|
archive_ext = '.zip' if download_url.endswith('zip') else '.tar.xz'
|
351
|
-
archive_path = os.path.join(
|
494
|
+
archive_path = os.path.join(dependency_dir, f"{dependency_name}{archive_ext}")
|
352
495
|
logger.debug("Using archive path: %s", archive_path)
|
353
496
|
|
354
497
|
if download_file(download_url, archive_path):
|
355
|
-
if extract_archive(archive_path,
|
356
|
-
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)
|
357
500
|
if binary:
|
358
501
|
# Make sure it's executable on Unix-like systems
|
359
502
|
if system in ['linux', 'darwin']:
|
@@ -134,24 +134,27 @@ def check_for_updates(quiet=False, force_refresh=False):
|
|
134
134
|
Check if the current version of TonieToolbox is the latest.
|
135
135
|
|
136
136
|
Args:
|
137
|
-
quiet: If True, will not log any information messages
|
137
|
+
quiet: If True, will not log any information messages and skip user confirmation
|
138
138
|
force_refresh: If True, bypass cache and check PyPI directly
|
139
139
|
|
140
140
|
Returns:
|
141
|
-
tuple: (is_latest, latest_version, message)
|
141
|
+
tuple: (is_latest, latest_version, message, update_confirmed)
|
142
142
|
is_latest: boolean indicating if the current version is the latest
|
143
143
|
latest_version: string with the latest version
|
144
144
|
message: string message about the update status or error
|
145
|
+
update_confirmed: boolean indicating if the user confirmed the update
|
145
146
|
"""
|
146
147
|
logger = get_logger("version_handler")
|
147
148
|
current_version = __version__
|
149
|
+
update_confirmed = False
|
148
150
|
|
149
|
-
logger.debug("Starting update check (quiet=%s, force_refresh=%s)",
|
151
|
+
logger.debug("Starting update check (quiet=%s, force_refresh=%s)",
|
152
|
+
quiet, force_refresh)
|
150
153
|
latest_version, error = get_pypi_version(force_refresh)
|
151
154
|
|
152
155
|
if error:
|
153
156
|
logger.debug("Error occurred during update check: %s", error)
|
154
|
-
return True, current_version, error
|
157
|
+
return True, current_version, error, update_confirmed
|
155
158
|
|
156
159
|
compare_result = compare_versions(current_version, latest_version)
|
157
160
|
is_latest = compare_result >= 0 # current >= latest
|
@@ -166,9 +169,69 @@ def check_for_updates(quiet=False, force_refresh=False):
|
|
166
169
|
message = f"Update available! Current version: {current_version}, Latest version: {latest_version}"
|
167
170
|
if not quiet:
|
168
171
|
logger.info(message)
|
169
|
-
|
172
|
+
|
173
|
+
# Show confirmation prompt if not in quiet mode
|
174
|
+
try:
|
175
|
+
response = input(f"Do you want to upgrade to TonieToolbox {latest_version}? [y/N]: ").lower().strip()
|
176
|
+
update_confirmed = response == 'y' or response == 'yes'
|
177
|
+
|
178
|
+
if update_confirmed:
|
179
|
+
logger.info("Update confirmed. Attempting to install update...")
|
180
|
+
if install_update():
|
181
|
+
logger.info(f"Successfully updated to TonieToolbox {latest_version}")
|
182
|
+
import sys
|
183
|
+
logger.info("Exiting program. Please restart TonieToolbox to use the new version.")
|
184
|
+
sys.exit(0)
|
185
|
+
else:
|
186
|
+
logger.error("Failed to install update automatically")
|
187
|
+
logger.error("Please update manually using: pip install --upgrade TonieToolbox")
|
188
|
+
import sys
|
189
|
+
sys.exit(1)
|
190
|
+
else:
|
191
|
+
logger.info("Update skipped by user.")
|
192
|
+
except (EOFError, KeyboardInterrupt):
|
193
|
+
logger.debug("User input interrupted")
|
194
|
+
update_confirmed = False
|
195
|
+
|
196
|
+
return is_latest, latest_version, message, update_confirmed
|
197
|
+
|
198
|
+
|
199
|
+
def install_update():
|
200
|
+
"""
|
201
|
+
Try to install the update using pip, pip3, or pipx.
|
202
|
+
|
203
|
+
Returns:
|
204
|
+
bool: True if the update was successfully installed, False otherwise
|
205
|
+
"""
|
206
|
+
logger = get_logger("version_handler")
|
207
|
+
import subprocess
|
208
|
+
import sys
|
209
|
+
|
210
|
+
package_name = "TonieToolbox"
|
211
|
+
commands = [
|
212
|
+
[sys.executable, "-m", "pip", "install", "--upgrade", package_name],
|
213
|
+
["pip", "install", "--upgrade", package_name],
|
214
|
+
["pip3", "install", "--upgrade", package_name],
|
215
|
+
["pipx", "upgrade", package_name]
|
216
|
+
]
|
217
|
+
|
218
|
+
for cmd in commands:
|
219
|
+
try:
|
220
|
+
logger.info(f"Attempting to install update using: {' '.join(cmd)}")
|
221
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
222
|
+
|
223
|
+
if result.returncode == 0:
|
224
|
+
logger.debug("Update command succeeded")
|
225
|
+
logger.debug(f"Output: {result.stdout}")
|
226
|
+
return True
|
227
|
+
else:
|
228
|
+
logger.debug(f"Command failed with returncode {result.returncode}")
|
229
|
+
logger.debug(f"stdout: {result.stdout}")
|
230
|
+
logger.debug(f"stderr: {result.stderr}")
|
231
|
+
except Exception as e:
|
232
|
+
logger.debug(f"Exception while running {cmd[0]}: {str(e)}")
|
170
233
|
|
171
|
-
return
|
234
|
+
return False
|
172
235
|
|
173
236
|
|
174
237
|
def clear_version_cache():
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|