TonieToolbox 0.5.0a1__py3-none-any.whl → 0.6.0__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 +2 -1
- TonieToolbox/__main__.py +303 -141
- TonieToolbox/artwork.py +59 -10
- TonieToolbox/audio_conversion.py +106 -34
- TonieToolbox/constants.py +133 -10
- TonieToolbox/dependency_manager.py +679 -184
- TonieToolbox/filename_generator.py +57 -10
- TonieToolbox/integration.py +73 -0
- TonieToolbox/integration_macos.py +613 -0
- TonieToolbox/integration_ubuntu.py +2 -0
- TonieToolbox/integration_windows.py +445 -0
- TonieToolbox/logger.py +9 -10
- TonieToolbox/media_tags.py +24 -104
- TonieToolbox/ogg_page.py +41 -41
- TonieToolbox/opus_packet.py +15 -15
- TonieToolbox/recursive_processor.py +34 -34
- TonieToolbox/tags.py +4 -5
- TonieToolbox/teddycloud.py +164 -51
- TonieToolbox/tonie_analysis.py +26 -24
- TonieToolbox/tonie_file.py +88 -72
- TonieToolbox/tonies_json.py +830 -37
- TonieToolbox/version_handler.py +14 -20
- {tonietoolbox-0.5.0a1.dist-info → tonietoolbox-0.6.0.dist-info}/METADATA +257 -177
- tonietoolbox-0.6.0.dist-info/RECORD +30 -0
- {tonietoolbox-0.5.0a1.dist-info → tonietoolbox-0.6.0.dist-info}/WHEEL +1 -1
- tonietoolbox-0.5.0a1.dist-info/RECORD +0 -26
- {tonietoolbox-0.5.0a1.dist-info → tonietoolbox-0.6.0.dist-info}/entry_points.txt +0 -0
- {tonietoolbox-0.5.0a1.dist-info → tonietoolbox-0.6.0.dist-info}/licenses/LICENSE.md +0 -0
- {tonietoolbox-0.5.0a1.dist-info → tonietoolbox-0.6.0.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,4 @@
|
|
1
|
+
#!/usr/bin/python3
|
1
2
|
"""
|
2
3
|
Module for generating intelligent output filenames for TonieToolbox.
|
3
4
|
"""
|
@@ -8,17 +9,16 @@ from pathlib import Path
|
|
8
9
|
from typing import List, Optional
|
9
10
|
from .logger import get_logger
|
10
11
|
|
11
|
-
logger = get_logger(
|
12
|
+
logger = get_logger(__name__)
|
12
13
|
|
13
14
|
def sanitize_filename(filename: str) -> str:
|
14
15
|
"""
|
15
16
|
Sanitize a filename by removing invalid characters and trimming.
|
16
17
|
|
17
18
|
Args:
|
18
|
-
filename: The filename to sanitize
|
19
|
-
|
19
|
+
filename (str): The filename to sanitize
|
20
20
|
Returns:
|
21
|
-
A sanitized filename
|
21
|
+
str: A sanitized filename
|
22
22
|
"""
|
23
23
|
# Remove invalid characters for filenames
|
24
24
|
sanitized = re.sub(r'[<>:"/\\|?*]', '_', filename)
|
@@ -29,7 +29,7 @@ def sanitize_filename(filename: str) -> str:
|
|
29
29
|
return "tonie"
|
30
30
|
return sanitized
|
31
31
|
|
32
|
-
def guess_output_filename(input_filename: str, input_files:
|
32
|
+
def guess_output_filename(input_filename: str, input_files: list[str] = None) -> str:
|
33
33
|
"""
|
34
34
|
Generate a sensible output filename based on input file or directory.
|
35
35
|
|
@@ -40,11 +40,10 @@ def guess_output_filename(input_filename: str, input_files: List[str] = None) ->
|
|
40
40
|
4. For multiple files: Use the common parent directory name
|
41
41
|
|
42
42
|
Args:
|
43
|
-
input_filename: The input filename or pattern
|
44
|
-
input_files: List of resolved input files (optional)
|
45
|
-
|
43
|
+
input_filename (str): The input filename or pattern
|
44
|
+
input_files (list[str] | None): List of resolved input files (optional)
|
46
45
|
Returns:
|
47
|
-
Generated output filename without extension
|
46
|
+
str: Generated output filename without extension
|
48
47
|
"""
|
49
48
|
logger.debug("Guessing output filename from input: %s", input_filename)
|
50
49
|
|
@@ -91,4 +90,52 @@ def guess_output_filename(input_filename: str, input_files: List[str] = None) ->
|
|
91
90
|
except ValueError:
|
92
91
|
# Files might be on different drives
|
93
92
|
logger.debug("Could not determine common path, using generic name")
|
94
|
-
return "tonie_collection"
|
93
|
+
return "tonie_collection"
|
94
|
+
|
95
|
+
def apply_template_to_path(template, metadata):
|
96
|
+
"""
|
97
|
+
Apply metadata to a path template and ensure the path is valid.
|
98
|
+
|
99
|
+
Args:
|
100
|
+
template: String template with {tag} placeholders
|
101
|
+
metadata: Dictionary of tag values
|
102
|
+
|
103
|
+
Returns:
|
104
|
+
Formatted path with placeholders replaced by actual values
|
105
|
+
"""
|
106
|
+
if not template or not metadata:
|
107
|
+
return None
|
108
|
+
|
109
|
+
try:
|
110
|
+
# Replace any tags in the path with their values
|
111
|
+
formatted_path = template
|
112
|
+
for tag, value in metadata.items():
|
113
|
+
if value:
|
114
|
+
# Sanitize value for use in path
|
115
|
+
safe_value = re.sub(r'[<>:"|?*]', '_', str(value))
|
116
|
+
# Replace forward slashes with appropriate character, but NOT hyphens
|
117
|
+
safe_value = safe_value.replace('/', ' - ')
|
118
|
+
# Remove leading/trailing whitespace and dots
|
119
|
+
safe_value = safe_value.strip('. \t')
|
120
|
+
if not safe_value:
|
121
|
+
safe_value = "unknown"
|
122
|
+
|
123
|
+
placeholder = '{' + tag + '}'
|
124
|
+
formatted_path = formatted_path.replace(placeholder, safe_value)
|
125
|
+
|
126
|
+
# Check if there are any remaining placeholders
|
127
|
+
if re.search(r'{[^}]+}', formatted_path):
|
128
|
+
return None # Some placeholders couldn't be filled
|
129
|
+
|
130
|
+
# Normalize path separators for the OS
|
131
|
+
formatted_path = os.path.normpath(formatted_path)
|
132
|
+
return formatted_path
|
133
|
+
except Exception as e:
|
134
|
+
logger.error(f"Error applying template to path: {e}")
|
135
|
+
return None
|
136
|
+
|
137
|
+
def ensure_directory_exists(file_path):
|
138
|
+
"""Create the directory structure for a given file path if it doesn't exist."""
|
139
|
+
directory = os.path.dirname(file_path)
|
140
|
+
if directory:
|
141
|
+
os.makedirs(directory, exist_ok=True)
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/usr/bin/python3
|
2
|
+
import platform
|
3
|
+
import os
|
4
|
+
import subprocess
|
5
|
+
from .logger import get_logger
|
6
|
+
|
7
|
+
logger = get_logger(__name__)
|
8
|
+
|
9
|
+
def handle_integration(args):
|
10
|
+
if platform.system() == 'Windows':
|
11
|
+
from .integration_windows import WindowsClassicContextMenuIntegration as ContextMenuIntegration
|
12
|
+
if args.install_integration:
|
13
|
+
success = ContextMenuIntegration.install()
|
14
|
+
if success:
|
15
|
+
logger.info("Integration installed successfully.")
|
16
|
+
return True
|
17
|
+
else:
|
18
|
+
logger.error("Integration installation failed.")
|
19
|
+
return False
|
20
|
+
elif args.uninstall_integration:
|
21
|
+
success = ContextMenuIntegration.uninstall()
|
22
|
+
if success:
|
23
|
+
logger.info("Integration uninstalled successfully.")
|
24
|
+
return True
|
25
|
+
else:
|
26
|
+
logger.error("Integration uninstallation failed.")
|
27
|
+
return False
|
28
|
+
#elif platform.system() == 'Darwin':
|
29
|
+
# from .integration_macos import MacOSContextMenuIntegration as ContextMenuIntegration
|
30
|
+
# if args.install_integration:
|
31
|
+
# success = ContextMenuIntegration.install()
|
32
|
+
# if success:
|
33
|
+
# logger.info("Integration installed successfully.")
|
34
|
+
# return True
|
35
|
+
# else:
|
36
|
+
# logger.error("Integration installation failed.")
|
37
|
+
# return False
|
38
|
+
# elif args.uninstall_integration:
|
39
|
+
# success = ContextMenuIntegration.uninstall()
|
40
|
+
# if success:
|
41
|
+
# logger.info("Integration uninstalled successfully.")
|
42
|
+
# return True
|
43
|
+
# else:
|
44
|
+
# logger.error("Integration uninstallation failed.")
|
45
|
+
# return False
|
46
|
+
elif platform.system() == 'Darwin':
|
47
|
+
raise NotImplementedError("Context menu integration is not supported on MacOS YET. But Soon™")
|
48
|
+
elif platform.system() == 'Linux':
|
49
|
+
raise NotImplementedError("Context menu integration is not supported on Linux YET. But Soon™")
|
50
|
+
else:
|
51
|
+
raise NotImplementedError(f"Context menu integration is not supported on this OS: {platform.system()}")
|
52
|
+
|
53
|
+
def handle_config():
|
54
|
+
"""Opens the configuration file in the default text editor."""
|
55
|
+
config_path = os.path.join(os.path.expanduser("~"), ".tonietoolbox", "config.json")
|
56
|
+
if not os.path.exists(config_path):
|
57
|
+
logger.info(f"Configuration file not found at {config_path}.")
|
58
|
+
logger.info("Creating a new configuration file. Using --install-integration will create a new config file.")
|
59
|
+
return
|
60
|
+
if platform.system() == "Windows":
|
61
|
+
from .integration_windows import WindowsClassicContextMenuIntegration as ContextMenuIntegration
|
62
|
+
context_menu = ContextMenuIntegration()
|
63
|
+
context_menu._apply_config_template()
|
64
|
+
os.startfile(config_path)
|
65
|
+
elif platform.system() == "Darwin":
|
66
|
+
from .integration_macos import MacOSContextMenuIntegration as ContextMenuIntegration
|
67
|
+
context_menu = ContextMenuIntegration()
|
68
|
+
context_menu._apply_config_template()
|
69
|
+
subprocess.call(["open", config_path])
|
70
|
+
elif platform.system() == "Linux":
|
71
|
+
subprocess.call(["xdg-open", config_path])
|
72
|
+
else:
|
73
|
+
logger.error(f"Unsupported OS: {platform.system()}")
|