TonieToolbox 0.1.5__tar.gz → 0.1.7__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.
Files changed (28) hide show
  1. {tonietoolbox-0.1.5/TonieToolbox.egg-info → tonietoolbox-0.1.7}/PKG-INFO +1 -1
  2. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/__init__.py +1 -1
  3. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/__main__.py +26 -4
  4. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/dependency_manager.py +55 -17
  5. tonietoolbox-0.1.7/TonieToolbox/version_handler.py +256 -0
  6. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7/TonieToolbox.egg-info}/PKG-INFO +1 -1
  7. tonietoolbox-0.1.5/TonieToolbox/version_handler.py +0 -125
  8. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/LICENSE.md +0 -0
  9. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/MANIFEST.in +0 -0
  10. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/README.md +0 -0
  11. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/audio_conversion.py +0 -0
  12. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/constants.py +0 -0
  13. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/filename_generator.py +0 -0
  14. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/logger.py +0 -0
  15. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/ogg_page.py +0 -0
  16. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/opus_packet.py +0 -0
  17. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/tonie_analysis.py +0 -0
  18. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/tonie_file.py +0 -0
  19. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/tonie_header.proto +0 -0
  20. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox/tonie_header_pb2.py +0 -0
  21. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox.egg-info/SOURCES.txt +0 -0
  22. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox.egg-info/dependency_links.txt +0 -0
  23. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox.egg-info/entry_points.txt +0 -0
  24. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox.egg-info/requires.txt +0 -0
  25. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/TonieToolbox.egg-info/top_level.txt +0 -0
  26. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/pyproject.toml +0 -0
  27. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/setup.cfg +0 -0
  28. {tonietoolbox-0.1.5 → tonietoolbox-0.1.7}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TonieToolbox
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: Convert audio files to Tonie box compatible format
5
5
  Home-page: https://github.com/Quentendo64/TonieToolbox
6
6
  Author: Quentendo64
@@ -2,4 +2,4 @@
2
2
  TonieToolbox - Convert audio files to Tonie box compatible format
3
3
  """
4
4
 
5
- __version__ = '0.1.5'
5
+ __version__ = '0.1.7'
@@ -15,7 +15,7 @@ from .tonie_analysis import check_tonie_file, split_to_opus_files
15
15
  from .dependency_manager import get_ffmpeg_binary, get_opus_binary
16
16
  from .logger import setup_logging, get_logger
17
17
  from .filename_generator import guess_output_filename
18
- from .version_handler import check_for_updates
18
+ from .version_handler import check_for_updates, clear_version_cache
19
19
 
20
20
  def main():
21
21
  """Entry point for the TonieToolbox application."""
@@ -45,8 +45,15 @@ def main():
45
45
  help='Compare input file with another .taf file for debugging')
46
46
  parser.add_argument('--detailed-compare', action='store_true',
47
47
  help='Show detailed OGG page differences when comparing files')
48
- parser.add_argument('--skip-update-check', action='store_true',
48
+
49
+ # Version check options
50
+ version_group = parser.add_argument_group('Version Check Options')
51
+ version_group.add_argument('--skip-update-check', action='store_true',
49
52
  help='Skip checking for updates')
53
+ version_group.add_argument('--force-refresh-cache', action='store_true',
54
+ help='Force refresh of update information from PyPI')
55
+ version_group.add_argument('--clear-version-cache', action='store_true',
56
+ help='Clear cached version information')
50
57
 
51
58
  log_group = parser.add_argument_group('Logging Options')
52
59
  log_level_group = log_group.add_mutually_exclusive_group()
@@ -70,10 +77,25 @@ def main():
70
77
 
71
78
  setup_logging(log_level)
72
79
  logger = get_logger('main')
73
- logger.debug("Starting TonieToolbox with log level: %s", logging.getLevelName(log_level))
80
+ logger.debug("Starting TonieToolbox v%s with log level: %s", __version__, logging.getLevelName(log_level))
81
+
82
+ # Handle version cache operations
83
+ if args.clear_version_cache:
84
+ if clear_version_cache():
85
+ logger.info("Version cache cleared successfully")
86
+ else:
87
+ logger.info("No version cache to clear or error clearing cache")
74
88
 
89
+ # Check for updates
75
90
  if not args.skip_update_check:
76
- check_for_updates(quiet=args.silent or args.quiet)
91
+ logger.debug("Checking for updates (force_refresh=%s)", args.force_refresh_cache)
92
+ is_latest, latest_version, message, update_confirmed = check_for_updates(
93
+ quiet=args.silent or args.quiet,
94
+ force_refresh=args.force_refresh_cache
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.")
77
99
 
78
100
  ffmpeg_binary = args.ffmpeg
79
101
  if ffmpeg_binary is None:
@@ -309,14 +309,65 @@ def ensure_dependency(dependency_name, auto_download=False):
309
309
  logger.info("Found %s in PATH: %s", dependency_name, path_binary)
310
310
  return path_binary
311
311
 
312
+ # Set up paths to check for previously downloaded versions
313
+ user_data_dir = get_user_data_dir()
314
+ 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)
318
+
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
342
+ try:
343
+ result = subprocess.run([existing_binary], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5)
344
+ if result.returncode == 0:
345
+ logger.info("Using previously downloaded %s: %s", dependency_name, existing_binary)
346
+ return existing_binary
347
+ except:
348
+ 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)
360
+ except Exception as e:
361
+ logger.warning("Error verifying downloaded binary: %s", e)
362
+
312
363
  # If auto_download is not enabled, don't try to install or download
313
364
  if not auto_download:
314
365
  logger.warning("%s not found in PATH and auto-download is disabled. Use --auto-download to enable automatic installation.", dependency_name)
315
366
  return None
316
367
 
317
368
  # If not in PATH, check if we should install via package manager
318
- if 'package' in DEPENDENCIES[dependency_name].get(system, {}):
319
- package_name = DEPENDENCIES[dependency_name][system]['package']
369
+ if 'package' in dependency_info:
370
+ package_name = dependency_info['package']
320
371
  logger.info("%s not found. Attempting to install %s package...", dependency_name, package_name)
321
372
  if install_package(package_name):
322
373
  path_binary = check_binary_in_path(bin_name)
@@ -325,27 +376,14 @@ def ensure_dependency(dependency_name, auto_download=False):
325
376
  return path_binary
326
377
 
327
378
  # If not installable via package manager or installation failed, try downloading
328
- if 'url' not in DEPENDENCIES[dependency_name].get(system, {}):
379
+ if 'url' not in dependency_info:
329
380
  logger.error("Cannot download %s for %s", dependency_name, system)
330
381
  return None
331
382
 
332
- # Set up paths
333
- user_data_dir = get_user_data_dir()
334
- dependency_info = DEPENDENCIES[dependency_name][system]
383
+ # Set up download paths
335
384
  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
385
  os.makedirs(extract_dir, exist_ok=True)
342
386
 
343
- # Check if we already downloaded and extracted it
344
- existing_binary = find_binary_in_extracted_dir(extract_dir, binary_path)
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
348
-
349
387
  # Download and extract
350
388
  archive_ext = '.zip' if download_url.endswith('zip') else '.tar.xz'
351
389
  archive_path = os.path.join(user_data_dir, f"{dependency_name}{archive_ext}")
@@ -0,0 +1,256 @@
1
+ """
2
+ Version handler to check if the latest version of TonieToolbox is being used.
3
+ """
4
+
5
+ import json
6
+ import logging
7
+ import os
8
+ import time
9
+ from urllib import request
10
+ from urllib.error import URLError
11
+
12
+ from . import __version__
13
+ from .logger import get_logger
14
+
15
+ # Cache filename for version information
16
+ CACHE_DIR = os.path.join(os.path.expanduser("~"), ".tonietoolbox")
17
+ CACHE_FILE = os.path.join(CACHE_DIR, "version_cache.json")
18
+ CACHE_EXPIRY = 86400 # 24 hours in seconds
19
+
20
+
21
+ def get_pypi_version(force_refresh=False):
22
+ """
23
+ Get the latest version of TonieToolbox from PyPI.
24
+
25
+ Args:
26
+ force_refresh: If True, ignore the cache and fetch directly from PyPI
27
+
28
+ Returns:
29
+ tuple: (latest_version, None) on success, (current_version, error_message) on failure
30
+ """
31
+ logger = get_logger("version_handler")
32
+ logger.debug("Checking for latest version (force_refresh=%s)", force_refresh)
33
+ logger.debug("Current version: %s", __version__)
34
+
35
+ try:
36
+ # Check if we have a recent cache and should use it
37
+ if not force_refresh and os.path.exists(CACHE_FILE):
38
+ try:
39
+ with open(CACHE_FILE, "r") as f:
40
+ cache_data = json.load(f)
41
+
42
+ cached_version = cache_data.get("version")
43
+ cache_timestamp = cache_data.get("timestamp", 0)
44
+ cache_age = time.time() - cache_timestamp
45
+
46
+ logger.debug("Cache info: version=%s, age=%d seconds (expires after %d)",
47
+ cached_version, cache_age, CACHE_EXPIRY)
48
+
49
+ if cache_age < CACHE_EXPIRY:
50
+ logger.debug("Using cached version info: %s", cached_version)
51
+ return cached_version, None
52
+ else:
53
+ logger.debug("Cache expired (%d seconds old), refreshing from PyPI", cache_age)
54
+ except (json.JSONDecodeError, KeyError) as e:
55
+ logger.debug("Cache file corrupt, will fetch from PyPI: %s", e)
56
+ else:
57
+ if force_refresh:
58
+ logger.debug("Forced refresh requested, bypassing cache")
59
+ else:
60
+ logger.debug("No cache found, fetching from PyPI")
61
+
62
+ # Fetch from PyPI
63
+ logger.debug("Fetching latest version from PyPI")
64
+ with request.urlopen("https://pypi.org/pypi/TonieToolbox/json", timeout=2) as response:
65
+ pypi_data = json.loads(response.read().decode("utf-8"))
66
+ latest_version = pypi_data["info"]["version"]
67
+
68
+ # Update cache
69
+ if not os.path.exists(CACHE_DIR):
70
+ os.makedirs(CACHE_DIR, exist_ok=True)
71
+
72
+ with open(CACHE_FILE, "w") as f:
73
+ cache_data = {
74
+ "version": latest_version,
75
+ "timestamp": time.time()
76
+ }
77
+ json.dump(cache_data, f)
78
+ logger.debug("Updated cache: %s", cache_data)
79
+
80
+ logger.debug("Latest version from PyPI: %s", latest_version)
81
+ return latest_version, None
82
+
83
+ except (URLError, json.JSONDecodeError) as e:
84
+ logger.debug("Failed to fetch version from PyPI: %s", e)
85
+ return __version__, f"Failed to check for updates: {str(e)}"
86
+ except Exception as e:
87
+ logger.debug("Unexpected error checking for updates: %s", e)
88
+ return __version__, f"Unexpected error checking for updates: {str(e)}"
89
+
90
+
91
+ def compare_versions(v1, v2):
92
+ """
93
+ Compare two version strings.
94
+
95
+ Args:
96
+ v1: First version string
97
+ v2: Second version string
98
+
99
+ Returns:
100
+ int: -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
101
+ """
102
+ logger = get_logger("version_handler")
103
+ logger.debug("Comparing versions: '%s' vs '%s'", v1, v2)
104
+
105
+ try:
106
+ v1_parts = [int(x) for x in v1.split('.')]
107
+ v2_parts = [int(x) for x in v2.split('.')]
108
+
109
+ logger.debug("Version parts: %s vs %s", v1_parts, v2_parts)
110
+
111
+ for i in range(max(len(v1_parts), len(v2_parts))):
112
+ v1_part = v1_parts[i] if i < len(v1_parts) else 0
113
+ v2_part = v2_parts[i] if i < len(v2_parts) else 0
114
+
115
+ logger.debug("Comparing part %d: %d vs %d", i, v1_part, v2_part)
116
+
117
+ if v1_part < v2_part:
118
+ logger.debug("Result: '%s' is OLDER than '%s'", v1, v2)
119
+ return -1
120
+ elif v1_part > v2_part:
121
+ logger.debug("Result: '%s' is NEWER than '%s'", v1, v2)
122
+ return 1
123
+
124
+ logger.debug("Result: versions are EQUAL")
125
+ return 0
126
+ except Exception as e:
127
+ logger.debug("Error comparing versions '%s' and '%s': %s", v1, v2, e)
128
+ # On error, assume versions are equal
129
+ return 0
130
+
131
+
132
+ def check_for_updates(quiet=False, force_refresh=False):
133
+ """
134
+ Check if the current version of TonieToolbox is the latest.
135
+
136
+ Args:
137
+ quiet: If True, will not log any information messages and skip user confirmation
138
+ force_refresh: If True, bypass cache and check PyPI directly
139
+
140
+ Returns:
141
+ tuple: (is_latest, latest_version, message, update_confirmed)
142
+ is_latest: boolean indicating if the current version is the latest
143
+ latest_version: string with the latest version
144
+ message: string message about the update status or error
145
+ update_confirmed: boolean indicating if the user confirmed the update
146
+ """
147
+ logger = get_logger("version_handler")
148
+ current_version = __version__
149
+ update_confirmed = False
150
+
151
+ logger.debug("Starting update check (quiet=%s, force_refresh=%s)",
152
+ quiet, force_refresh)
153
+ latest_version, error = get_pypi_version(force_refresh)
154
+
155
+ if error:
156
+ logger.debug("Error occurred during update check: %s", error)
157
+ return True, current_version, error, update_confirmed
158
+
159
+ compare_result = compare_versions(current_version, latest_version)
160
+ is_latest = compare_result >= 0 # current >= latest
161
+
162
+ logger.debug("Version comparison result: %d (is_latest=%s)", compare_result, is_latest)
163
+
164
+ if is_latest:
165
+ message = f"You are using the latest version of TonieToolbox ({current_version})"
166
+ if not quiet:
167
+ logger.debug(message)
168
+ else:
169
+ message = f"Update available! Current version: {current_version}, Latest version: {latest_version}"
170
+ if not quiet:
171
+ logger.info(message)
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)}")
233
+
234
+ return False
235
+
236
+
237
+ def clear_version_cache():
238
+ """
239
+ Clear the version cache file to force a refresh on next check.
240
+
241
+ Returns:
242
+ bool: True if cache was cleared, False otherwise
243
+ """
244
+ logger = get_logger("version_handler")
245
+
246
+ try:
247
+ if os.path.exists(CACHE_FILE):
248
+ logger.debug("Removing version cache file: %s", CACHE_FILE)
249
+ os.remove(CACHE_FILE)
250
+ return True
251
+ else:
252
+ logger.debug("No cache file to remove")
253
+ return False
254
+ except Exception as e:
255
+ logger.debug("Error clearing cache: %s", e)
256
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TonieToolbox
3
- Version: 0.1.5
3
+ Version: 0.1.7
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,125 +0,0 @@
1
- """
2
- Version handler to check if the latest version of TonieToolbox is being used.
3
- """
4
-
5
- import json
6
- import logging
7
- import os
8
- import time
9
- from urllib import request
10
- from urllib.error import URLError
11
-
12
- from . import __version__
13
- from .logger import get_logger
14
-
15
- CACHE_DIR = os.path.join(os.path.expanduser("~"), ".tonietoolbox")
16
- CACHE_FILE = os.path.join(CACHE_DIR, "version_cache.json")
17
- CACHE_EXPIRY = 86400 # 24 hours in seconds
18
-
19
-
20
- def get_pypi_version():
21
- """
22
- Get the latest version of TonieToolbox from PyPI.
23
-
24
- Returns:
25
- tuple: (latest_version, None) on success, (current_version, error_message) on failure
26
- """
27
- logger = get_logger("version_handler")
28
-
29
- try:
30
- if os.path.exists(CACHE_FILE):
31
- try:
32
- with open(CACHE_FILE, "r") as f:
33
- cache_data = json.load(f)
34
-
35
- if time.time() - cache_data.get("timestamp", 0) < CACHE_EXPIRY:
36
- logger.debug("Using cached version info: %s", cache_data["version"])
37
- return cache_data["version"], None
38
- except (json.JSONDecodeError, KeyError) as e:
39
- logger.debug("Cache file corrupt, will fetch from PyPI: %s", e)
40
-
41
- logger.debug("Fetching latest version from PyPI")
42
- with request.urlopen("https://pypi.org/pypi/TonieToolbox/json", timeout=2) as response:
43
- pypi_data = json.loads(response.read().decode("utf-8"))
44
- latest_version = pypi_data["info"]["version"]
45
-
46
- if not os.path.exists(CACHE_DIR):
47
- os.makedirs(CACHE_DIR, exist_ok=True)
48
-
49
- with open(CACHE_FILE, "w") as f:
50
- json.dump({
51
- "version": latest_version,
52
- "timestamp": time.time()
53
- }, f)
54
-
55
- logger.debug("Latest version from PyPI: %s", latest_version)
56
- return latest_version, None
57
-
58
- except (URLError, json.JSONDecodeError) as e:
59
- logger.debug("Failed to fetch version from PyPI: %s", e)
60
- return __version__, f"Failed to check for updates: {str(e)}"
61
- except Exception as e:
62
- logger.debug("Unexpected error checking for updates: %s", e)
63
- return __version__, f"Unexpected error checking for updates: {str(e)}"
64
-
65
-
66
- def compare_versions(v1, v2):
67
- """
68
- Compare two version strings.
69
-
70
- Args:
71
- v1: First version string
72
- v2: Second version string
73
-
74
- Returns:
75
- int: -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
76
- """
77
- v1_parts = [int(x) for x in v1.split('.')]
78
- v2_parts = [int(x) for x in v2.split('.')]
79
-
80
- for i in range(max(len(v1_parts), len(v2_parts))):
81
- v1_part = v1_parts[i] if i < len(v1_parts) else 0
82
- v2_part = v2_parts[i] if i < len(v2_parts) else 0
83
-
84
- if v1_part < v2_part:
85
- return -1
86
- elif v1_part > v2_part:
87
- return 1
88
-
89
- return 0
90
-
91
-
92
- def check_for_updates(quiet=False):
93
- """
94
- Check if the current version of TonieToolbox is the latest.
95
-
96
- Args:
97
- quiet: If True, will not log any information messages
98
-
99
- Returns:
100
- tuple: (is_latest, latest_version, message)
101
- is_latest: boolean indicating if the current version is the latest
102
- latest_version: string with the latest version
103
- message: string message about the update status or error
104
- """
105
- logger = get_logger("version_handler")
106
- current_version = __version__
107
-
108
- latest_version, error = get_pypi_version()
109
-
110
- if error:
111
- return True, current_version, error
112
-
113
- is_latest = compare_versions(current_version, latest_version) >= 0
114
-
115
- if is_latest:
116
- message = f"You are using the latest version of TonieToolbox ({current_version})"
117
- if not quiet:
118
- logger.debug(message)
119
- else:
120
- message = f"Update available! Current version: {current_version}, Latest version: {latest_version}"
121
- if not quiet:
122
- logger.info(message)
123
- logger.info("Consider upgrading with: pip install --upgrade TonieToolbox")
124
-
125
- return is_latest, latest_version, message
File without changes
File without changes
File without changes
File without changes
File without changes