TonieToolbox 0.6.0a2__py3-none-any.whl → 0.6.0a4__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/__main__.py +29 -11
- TonieToolbox/artwork.py +45 -1
- TonieToolbox/constants.py +22 -1
- TonieToolbox/dependency_manager.py +677 -183
- TonieToolbox/integration.py +5 -1
- TonieToolbox/integration_macos.py +197 -66
- TonieToolbox/integration_windows.py +6 -2
- {tonietoolbox-0.6.0a2.dist-info → tonietoolbox-0.6.0a4.dist-info}/METADATA +6 -2
- {tonietoolbox-0.6.0a2.dist-info → tonietoolbox-0.6.0a4.dist-info}/RECORD +14 -14
- {tonietoolbox-0.6.0a2.dist-info → tonietoolbox-0.6.0a4.dist-info}/WHEEL +0 -0
- {tonietoolbox-0.6.0a2.dist-info → tonietoolbox-0.6.0a4.dist-info}/entry_points.txt +0 -0
- {tonietoolbox-0.6.0a2.dist-info → tonietoolbox-0.6.0a4.dist-info}/licenses/LICENSE.md +0 -0
- {tonietoolbox-0.6.0a2.dist-info → tonietoolbox-0.6.0a4.dist-info}/top_level.txt +0 -0
TonieToolbox/integration.py
CHANGED
@@ -52,15 +52,19 @@ def handle_config():
|
|
52
52
|
import platform
|
53
53
|
import subprocess
|
54
54
|
|
55
|
+
|
55
56
|
config_path = os.path.join(os.path.expanduser("~"), ".tonietoolbox", "config.json")
|
56
|
-
|
57
57
|
if not os.path.exists(config_path):
|
58
58
|
logger.info(f"Configuration file not found at {config_path}.")
|
59
59
|
logger.info("Creating a new configuration file. Using --install-integration will create a new config file.")
|
60
60
|
return
|
61
61
|
if platform.system() == "Windows":
|
62
|
+
from .integration_windows import WindowsClassicContextMenuIntegration as ContextMenuIntegration
|
63
|
+
ContextMenuIntegration._apply_config_template()
|
62
64
|
os.startfile(config_path)
|
63
65
|
elif platform.system() == "Darwin":
|
66
|
+
from .integration_macos import MacOSContextMenuIntegration as ContextMenuIntegration
|
67
|
+
ContextMenuIntegration._apply_config_template()
|
64
68
|
subprocess.call(["open", config_path])
|
65
69
|
elif platform.system() == "Linux":
|
66
70
|
subprocess.call(["xdg-open", config_path])
|
@@ -1,11 +1,11 @@
|
|
1
|
-
# filepath: d:\Repository\TonieToolbox\TonieToolbox\integration_macos.py
|
2
1
|
import os
|
3
2
|
import sys
|
4
3
|
import json
|
5
4
|
import plistlib
|
6
5
|
import subprocess
|
7
6
|
from pathlib import Path
|
8
|
-
from .constants import SUPPORTED_EXTENSIONS, CONFIG_TEMPLATE
|
7
|
+
from .constants import SUPPORTED_EXTENSIONS, CONFIG_TEMPLATE,UTI_MAPPINGS,ICON_BASE64
|
8
|
+
from .artwork import base64_to_ico
|
9
9
|
from .logger import get_logger
|
10
10
|
|
11
11
|
logger = get_logger('integration_macos')
|
@@ -22,15 +22,9 @@ class MacOSContextMenuIntegration:
|
|
22
22
|
self.services_dir = os.path.join(os.path.expanduser('~'), 'Library', 'Services')
|
23
23
|
self.icon_path = os.path.join(self.output_dir, 'icon.png')
|
24
24
|
os.makedirs(self.output_dir, exist_ok=True)
|
25
|
-
|
26
|
-
# Error handling and success messages for shell scripts
|
27
25
|
self.error_handling = 'if [ $? -ne 0 ]; then\n echo "Error: Command failed with error code $?"\n read -p "Press any key to close this window..." key\n exit 1\nfi'
|
28
26
|
self.success_handling = 'echo "Command completed successfully"\nsleep 2'
|
29
|
-
|
30
|
-
# Load configuration
|
31
27
|
self.config = self._apply_config_template()
|
32
|
-
|
33
|
-
# Ensure these attributes always exist
|
34
28
|
self.upload_url = ''
|
35
29
|
self.log_level = self.config.get('log_level', 'SILENT')
|
36
30
|
self.log_to_file = self.config.get('log_to_file', False)
|
@@ -40,14 +34,19 @@ class MacOSContextMenuIntegration:
|
|
40
34
|
|
41
35
|
logger.debug(f"Upload enabled: {self.upload_enabled}")
|
42
36
|
logger.debug(f"Upload URL: {self.upload_url}")
|
43
|
-
logger.debug(f"Authentication: {'Basic Authentication' if self.basic_authentication else ('None' if self.none_authentication else ('Client Cert' if self.client_cert_authentication else 'Unknown'))}")
|
44
|
-
|
45
|
-
|
46
|
-
|
37
|
+
logger.debug(f"Authentication: {'Basic Authentication' if self.basic_authentication else ('None' if self.none_authentication else ('Client Cert' if self.client_cert_authentication else 'Unknown'))}")
|
38
|
+
self._setup_commands()
|
39
|
+
|
47
40
|
def _build_cmd(self, base_args, file_placeholder='$1', output_to_source=True, use_upload=False, use_artwork=False, use_json=False, use_compare=False, use_info=False, is_recursive=False, is_split=False, is_folder=False, keep_open=False, log_to_file=False):
|
48
41
|
"""Dynamically build command strings for quick actions."""
|
49
42
|
exe = self.exe_path
|
50
43
|
cmd = '#!/bin/bash\n\n'
|
44
|
+
# Debug output to see what's being passed to the script
|
45
|
+
cmd += 'echo "Arguments received: $@"\n'
|
46
|
+
cmd += 'echo "Number of arguments: $#"\n'
|
47
|
+
cmd += 'if [ $# -gt 0 ]; then\n'
|
48
|
+
cmd += ' echo "First argument: $1"\n'
|
49
|
+
cmd += 'fi\n\n'
|
51
50
|
|
52
51
|
# Add a description of what's being executed
|
53
52
|
cmd += 'echo "Running TonieToolbox'
|
@@ -70,11 +69,117 @@ class MacOSContextMenuIntegration:
|
|
70
69
|
else:
|
71
70
|
cmd += ' convert'
|
72
71
|
cmd += ' command..."\n\n'
|
72
|
+
# Properly handle paths from macOS Services
|
73
|
+
if is_folder or is_recursive:
|
74
|
+
# Handle multiple arguments and ensure we get a valid folder
|
75
|
+
cmd += '# Handle paths from macOS Services\n'
|
76
|
+
cmd += '# First, try to get paths from stdin (macOS passes paths this way)\n'
|
77
|
+
cmd += 'if [ -p /dev/stdin ]; then\n'
|
78
|
+
cmd += ' PATHS=$(cat /dev/stdin)\n'
|
79
|
+
cmd += ' echo "Found paths from stdin: $PATHS"\n'
|
80
|
+
cmd += 'fi\n\n'
|
81
|
+
cmd += '# If no paths from stdin, check command line arguments\n'
|
82
|
+
cmd += 'FOLDER_PATH=""\n'
|
83
|
+
cmd += 'if [ -z "$PATHS" ]; then\n'
|
84
|
+
cmd += ' for arg in "$@"; do\n'
|
85
|
+
cmd += ' if [ -d "$arg" ]; then\n'
|
86
|
+
cmd += ' FOLDER_PATH="$arg"\n'
|
87
|
+
cmd += ' echo "Processing folder from args: $FOLDER_PATH"\n'
|
88
|
+
cmd += ' break\n'
|
89
|
+
cmd += ' fi\n'
|
90
|
+
cmd += ' done\n'
|
91
|
+
cmd += 'else\n'
|
92
|
+
cmd += ' for path in $PATHS; do\n'
|
93
|
+
cmd += ' if [ -d "$path" ]; then\n'
|
94
|
+
cmd += ' FOLDER_PATH="$path"\n'
|
95
|
+
cmd += ' echo "Processing folder from stdin: $FOLDER_PATH"\n'
|
96
|
+
cmd += ' break\n'
|
97
|
+
cmd += ' fi\n'
|
98
|
+
cmd += ' done\n'
|
99
|
+
cmd += 'fi\n\n'
|
100
|
+
cmd += 'if [ -z "$FOLDER_PATH" ]; then\n'
|
101
|
+
cmd += ' echo "Error: No valid folder path found in arguments or stdin"\n'
|
102
|
+
cmd += ' read -p "Press any key to close this window..." key\n'
|
103
|
+
cmd += ' exit 1\n'
|
104
|
+
cmd += 'fi\n\n'
|
105
|
+
|
106
|
+
# Use the variable for the command
|
107
|
+
file_placeholder='$FOLDER_PATH'
|
108
|
+
elif use_compare:
|
109
|
+
# For compare operation, we need two file paths
|
110
|
+
cmd += '# Compare requires two files\n'
|
111
|
+
cmd += 'if [ $# -lt 2 ]; then\n'
|
112
|
+
cmd += ' echo "Error: Compare operation requires two files."\n'
|
113
|
+
cmd += ' read -p "Press any key to close this window..." key\n'
|
114
|
+
cmd += ' exit 1\n'
|
115
|
+
cmd += 'fi\n\n'
|
116
|
+
else:
|
117
|
+
# For regular file operations, handle paths correctly
|
118
|
+
cmd += '# Handle file paths correctly - try multiple methods for macOS\n'
|
119
|
+
cmd += 'FILE_PATH=""\n'
|
120
|
+
|
121
|
+
# First, try to get paths from stdin (macOS passes paths this way sometimes)
|
122
|
+
cmd += '# Method 1: Try to read from stdin if available\n'
|
123
|
+
cmd += 'if [ -p /dev/stdin ]; then\n'
|
124
|
+
cmd += ' STDIN_PATHS=$(cat)\n'
|
125
|
+
cmd += ' if [ -n "$STDIN_PATHS" ]; then\n'
|
126
|
+
cmd += ' for path in $STDIN_PATHS; do\n'
|
127
|
+
cmd += ' if [ -f "$path" ]; then\n'
|
128
|
+
cmd += ' FILE_PATH="$path"\n'
|
129
|
+
cmd += ' echo "Found file path from stdin: $FILE_PATH"\n'
|
130
|
+
cmd += ' break\n'
|
131
|
+
cmd += ' fi\n'
|
132
|
+
cmd += ' done\n'
|
133
|
+
cmd += ' fi\n'
|
134
|
+
cmd += 'fi\n\n'
|
135
|
+
|
136
|
+
# Method 2: Try command line arguments
|
137
|
+
cmd += '# Method 2: Check command line arguments\n'
|
138
|
+
cmd += 'if [ -z "$FILE_PATH" ]; then\n'
|
139
|
+
cmd += ' for arg in "$@"; do\n'
|
140
|
+
cmd += ' if [ -f "$arg" ]; then\n'
|
141
|
+
cmd += ' FILE_PATH="$arg"\n'
|
142
|
+
cmd += ' echo "Found file path from arguments: $FILE_PATH"\n'
|
143
|
+
cmd += ' break\n'
|
144
|
+
cmd += ' fi\n'
|
145
|
+
cmd += ' done\n'
|
146
|
+
cmd += 'fi\n\n'
|
147
|
+
|
148
|
+
# Method 3: Try to handle case where path might be in $1
|
149
|
+
cmd += '# Method 3: Try first argument directly\n'
|
150
|
+
cmd += 'if [ -z "$FILE_PATH" ] && [ -n "$1" ] && [ -f "$1" ]; then\n'
|
151
|
+
cmd += ' FILE_PATH="$1"\n'
|
152
|
+
cmd += ' echo "Using first argument directly as file path: $FILE_PATH"\n'
|
153
|
+
cmd += 'fi\n\n'
|
154
|
+
|
155
|
+
# Method 4: Parse automator's encoded path format
|
156
|
+
cmd += '# Method 4: Try to decode special format macOS might use\n'
|
157
|
+
cmd += 'if [ -z "$FILE_PATH" ] && [ -n "$1" ]; then\n'
|
158
|
+
cmd += ' # Sometimes macOS passes paths with "file://" prefix\n'
|
159
|
+
cmd += ' DECODED_PATH=$(echo "$1" | sed -e "s|^file://||" -e "s|%20| |g")\n'
|
160
|
+
cmd += ' if [ -f "$DECODED_PATH" ]; then\n'
|
161
|
+
cmd += ' FILE_PATH="$DECODED_PATH"\n'
|
162
|
+
cmd += ' echo "Using decoded path: $FILE_PATH"\n'
|
163
|
+
cmd += ' fi\n'
|
164
|
+
cmd += 'fi\n\n'
|
165
|
+
|
166
|
+
# Final check
|
167
|
+
cmd += 'if [ -z "$FILE_PATH" ]; then\n'
|
168
|
+
cmd += ' echo "Error: Could not find a valid file path. Tried:"\n'
|
169
|
+
cmd += ' echo "- Reading from stdin"\n'
|
170
|
+
cmd += ' echo "- Command arguments: $@"\n'
|
171
|
+
cmd += ' echo "- Decoding URL format"\n'
|
172
|
+
cmd += ' read -p "Press any key to close this window..." key\n'
|
173
|
+
cmd += ' exit 1\n'
|
174
|
+
cmd += 'fi\n\n'
|
175
|
+
|
176
|
+
# Use the variable for the command
|
177
|
+
file_placeholder='$FILE_PATH'
|
73
178
|
|
74
179
|
# Build the actual command
|
75
180
|
cmd_line = f'"{exe}" {base_args}'
|
76
181
|
if log_to_file:
|
77
|
-
cmd_line += ' --log-file'
|
182
|
+
cmd_line += ' --log-file'
|
78
183
|
if is_recursive:
|
79
184
|
cmd_line += ' --recursive'
|
80
185
|
if output_to_source:
|
@@ -84,7 +189,28 @@ class MacOSContextMenuIntegration:
|
|
84
189
|
if is_split:
|
85
190
|
cmd_line += ' --split'
|
86
191
|
if use_compare:
|
87
|
-
|
192
|
+
# For compare, we need to handle two files
|
193
|
+
cmd += '# Find two TAF files for comparison\n'
|
194
|
+
cmd += 'FILE1=""\n'
|
195
|
+
cmd += 'FILE2=""\n'
|
196
|
+
cmd += 'for arg in "$@"; do\n'
|
197
|
+
cmd += ' if [ -f "$arg" ]; then\n'
|
198
|
+
cmd += ' if [ -z "$FILE1" ]; then\n'
|
199
|
+
cmd += ' FILE1="$arg"\n'
|
200
|
+
cmd += ' echo "First TAF file: $FILE1"\n'
|
201
|
+
cmd += ' elif [ -z "$FILE2" ]; then\n'
|
202
|
+
cmd += ' FILE2="$arg"\n'
|
203
|
+
cmd += ' echo "Second TAF file: $FILE2"\n'
|
204
|
+
cmd += ' break\n'
|
205
|
+
cmd += ' fi\n'
|
206
|
+
cmd += ' fi\n'
|
207
|
+
cmd += 'done\n\n'
|
208
|
+
cmd += 'if [ -z "$FILE1" ] || [ -z "$FILE2" ]; then\n'
|
209
|
+
cmd += ' echo "Error: Need two TAF files for comparison."\n'
|
210
|
+
cmd += ' read -p "Press any key to close this window..." key\n'
|
211
|
+
cmd += ' exit 1\n'
|
212
|
+
cmd += 'fi\n\n'
|
213
|
+
cmd_line += ' --compare "$FILE1" "$FILE2"'
|
88
214
|
else:
|
89
215
|
cmd_line += f' "{file_placeholder}"'
|
90
216
|
if use_upload:
|
@@ -101,6 +227,7 @@ class MacOSContextMenuIntegration:
|
|
101
227
|
cmd_line += ' --create-custom-json'
|
102
228
|
|
103
229
|
# Add the command to the script
|
230
|
+
cmd += f'echo "Executing: {cmd_line}"\n'
|
104
231
|
cmd += f'{cmd_line}\n\n'
|
105
232
|
|
106
233
|
# Add error and success handling
|
@@ -146,8 +273,11 @@ class MacOSContextMenuIntegration:
|
|
146
273
|
self.upload_folder_artwork_json_cmd = self._build_cmd(f'{log_level_arg}', is_recursive=True, is_folder=True, use_upload=True, use_artwork=True, use_json=True, log_to_file=self.log_to_file)
|
147
274
|
|
148
275
|
def _apply_config_template(self):
|
149
|
-
"""Apply the default configuration template if config.json is missing or invalid."""
|
276
|
+
"""Apply the default configuration template if config.json is missing or invalid. Extracts the icon from base64 if not present."""
|
150
277
|
config_path = os.path.join(self.output_dir, 'config.json')
|
278
|
+
icon_path = os.path.join(self.output_dir, 'icon.ico')
|
279
|
+
if not os.path.exists(icon_path):
|
280
|
+
base64_to_ico(ICON_BASE64, icon_path)
|
151
281
|
if not os.path.exists(config_path):
|
152
282
|
with open(config_path, 'w') as f:
|
153
283
|
json.dump(CONFIG_TEMPLATE, f, indent=4)
|
@@ -174,7 +304,6 @@ class MacOSContextMenuIntegration:
|
|
174
304
|
|
175
305
|
def _setup_upload(self):
|
176
306
|
"""Set up upload functionality based on config.json settings"""
|
177
|
-
# Always initialize authentication flags
|
178
307
|
self.basic_authentication = False
|
179
308
|
self.client_cert_authentication = False
|
180
309
|
self.none_authentication = False
|
@@ -210,22 +339,16 @@ class MacOSContextMenuIntegration:
|
|
210
339
|
return bool(self.upload_url)
|
211
340
|
except Exception as e:
|
212
341
|
logger.debug(f"Unexpected error while loading configuration: {e}")
|
213
|
-
return False
|
214
|
-
|
342
|
+
return False
|
215
343
|
def _create_quick_action(self, name, command, file_types=None, directory_based=False):
|
216
344
|
"""Create a macOS Quick Action (Service) with the given name and command."""
|
217
|
-
# Create Quick Action directory
|
218
345
|
action_dir = os.path.join(self.services_dir, f"{name}.workflow")
|
219
346
|
os.makedirs(action_dir, exist_ok=True)
|
220
|
-
|
221
|
-
# Create Contents directory
|
222
347
|
contents_dir = os.path.join(action_dir, "Contents")
|
223
348
|
os.makedirs(contents_dir, exist_ok=True)
|
224
|
-
|
225
|
-
# Create document.wflow file with plist content
|
226
349
|
document_path = os.path.join(contents_dir, "document.wflow")
|
227
350
|
|
228
|
-
#
|
351
|
+
# Set up the plist to ensure the service appears in context menus
|
229
352
|
info_plist = {
|
230
353
|
"NSServices": [
|
231
354
|
{
|
@@ -237,30 +360,27 @@ class MacOSContextMenuIntegration:
|
|
237
360
|
"NSApplicationIdentifier": "com.apple.finder"
|
238
361
|
},
|
239
362
|
"NSSendFileTypes": file_types if file_types else [],
|
240
|
-
"NSSendTypes": ["NSFilenamesPboardType"]
|
363
|
+
"NSSendTypes": ["NSFilenamesPboardType"], # Always include this to ensure paths are passed correctly
|
364
|
+
"NSUserData": name,
|
365
|
+
"NSExecutable": "script", # Ensure macOS knows which script to run
|
366
|
+
"NSReturnTypes": []
|
241
367
|
}
|
242
368
|
]
|
243
369
|
}
|
244
370
|
|
245
371
|
info_path = os.path.join(contents_dir, "Info.plist")
|
246
372
|
with open(info_path, "wb") as f:
|
247
|
-
plistlib.dump(info_plist, f)
|
248
|
-
|
249
|
-
# Create script file
|
373
|
+
plistlib.dump(info_plist, f)
|
250
374
|
script_dir = os.path.join(contents_dir, "MacOS")
|
251
375
|
os.makedirs(script_dir, exist_ok=True)
|
252
376
|
script_path = os.path.join(script_dir, "script")
|
253
377
|
|
254
378
|
with open(script_path, "w") as f:
|
255
|
-
f.write(command)
|
256
|
-
|
257
|
-
# Make the script executable
|
379
|
+
f.write(command)
|
258
380
|
os.chmod(script_path, 0o755)
|
259
|
-
|
260
|
-
# Create document.wflow file with a basic workflow definition
|
261
381
|
workflow = {
|
262
382
|
"AMApplication": "Automator",
|
263
|
-
"AMCanShowSelectedItemsWhenRun":
|
383
|
+
"AMCanShowSelectedItemsWhenRun": True,
|
264
384
|
"AMCanShowWhenRun": True,
|
265
385
|
"AMDockBadgeLabel": "",
|
266
386
|
"AMDockBadgeStyle": "badge",
|
@@ -272,7 +392,11 @@ class MacOSContextMenuIntegration:
|
|
272
392
|
"parameters": {
|
273
393
|
"shell": "/bin/bash",
|
274
394
|
"script": command,
|
275
|
-
"input": "as arguments"
|
395
|
+
"input": "as arguments",
|
396
|
+
"showStdout": True,
|
397
|
+
"showStderr": True,
|
398
|
+
"showOutput": True,
|
399
|
+
"runAsAdmin": False
|
276
400
|
}
|
277
401
|
}
|
278
402
|
],
|
@@ -283,107 +407,113 @@ class MacOSContextMenuIntegration:
|
|
283
407
|
},
|
284
408
|
"AMWorkflowSchemeVersion": 2.0,
|
285
409
|
}
|
286
|
-
|
287
410
|
with open(document_path, "wb") as f:
|
288
411
|
plistlib.dump(workflow, f)
|
289
412
|
|
290
413
|
return action_dir
|
291
|
-
|
414
|
+
|
415
|
+
def _extension_to_uti(self, extension):
|
416
|
+
"""Convert a file extension to macOS UTI (Uniform Type Identifier)."""
|
417
|
+
uti_map = UTI_MAPPINGS
|
418
|
+
ext = extension.lower().lstrip('.')
|
419
|
+
return uti_map.get(ext, f'public.{ext}')
|
420
|
+
|
292
421
|
def _generate_audio_extension_actions(self):
|
293
422
|
"""Generate Quick Actions for supported audio file extensions."""
|
294
423
|
extensions = [ext.lower().lstrip('.') for ext in SUPPORTED_EXTENSIONS]
|
295
|
-
|
296
|
-
|
424
|
+
# Convert extensions to UTIs (Uniform Type Identifiers)
|
425
|
+
utis = [self._extension_to_uti(ext) for ext in extensions]
|
297
426
|
self._create_quick_action(
|
298
427
|
"TonieToolbox - Convert to TAF",
|
299
428
|
self.convert_cmd,
|
300
|
-
file_types=
|
429
|
+
file_types=utis
|
301
430
|
)
|
302
431
|
|
303
432
|
if self.upload_enabled:
|
304
433
|
self._create_quick_action(
|
305
434
|
"TonieToolbox - Convert and Upload",
|
306
435
|
self.upload_cmd,
|
307
|
-
file_types=
|
436
|
+
file_types=utis
|
308
437
|
)
|
309
438
|
|
310
439
|
self._create_quick_action(
|
311
440
|
"TonieToolbox - Convert, Upload with Artwork",
|
312
441
|
self.upload_artwork_cmd,
|
313
|
-
file_types=
|
442
|
+
file_types=utis
|
314
443
|
)
|
315
444
|
|
316
445
|
self._create_quick_action(
|
317
446
|
"TonieToolbox - Convert, Upload with Artwork and JSON",
|
318
447
|
self.upload_artwork_json_cmd,
|
319
|
-
file_types=
|
448
|
+
file_types=utis
|
320
449
|
)
|
321
|
-
|
450
|
+
|
322
451
|
def _generate_taf_file_actions(self):
|
323
452
|
"""Generate Quick Actions for .taf files."""
|
453
|
+
taf_uti = self._extension_to_uti("taf") # Use UTI for TAF files
|
454
|
+
|
324
455
|
self._create_quick_action(
|
325
456
|
"TonieToolbox - Show Info",
|
326
457
|
self.show_info_cmd,
|
327
|
-
file_types=[
|
458
|
+
file_types=[taf_uti]
|
328
459
|
)
|
329
460
|
|
330
461
|
self._create_quick_action(
|
331
462
|
"TonieToolbox - Extract Opus Tracks",
|
332
463
|
self.extract_opus_cmd,
|
333
|
-
file_types=[
|
464
|
+
file_types=[taf_uti]
|
334
465
|
)
|
335
466
|
|
336
467
|
if self.upload_enabled:
|
337
468
|
self._create_quick_action(
|
338
469
|
"TonieToolbox - Upload",
|
339
470
|
self.upload_taf_cmd,
|
340
|
-
file_types=[
|
471
|
+
file_types=[taf_uti]
|
341
472
|
)
|
342
|
-
|
343
473
|
self._create_quick_action(
|
344
474
|
"TonieToolbox - Upload with Artwork",
|
345
475
|
self.upload_taf_artwork_cmd,
|
346
|
-
file_types=[
|
476
|
+
file_types=[taf_uti]
|
347
477
|
)
|
348
478
|
|
349
479
|
self._create_quick_action(
|
350
480
|
"TonieToolbox - Upload with Artwork and JSON",
|
351
481
|
self.upload_taf_artwork_json_cmd,
|
352
|
-
file_types=[
|
482
|
+
file_types=[taf_uti]
|
483
|
+
)
|
484
|
+
|
485
|
+
self._create_quick_action(
|
486
|
+
"TonieToolbox - Compare with another TAF file",
|
487
|
+
self.compare_taf_cmd,
|
488
|
+
file_types=[taf_uti]
|
353
489
|
)
|
354
490
|
|
355
|
-
self._create_quick_action(
|
356
|
-
"TonieToolbox - Compare with another TAF file",
|
357
|
-
self.compare_taf_cmd,
|
358
|
-
file_types=["taf"]
|
359
|
-
)
|
360
|
-
|
361
491
|
def _generate_folder_actions(self):
|
362
492
|
"""Generate Quick Actions for folders."""
|
363
493
|
self._create_quick_action(
|
364
|
-
"TonieToolbox - Convert Folder to TAF (recursive)",
|
494
|
+
"TonieToolbox - 1. Convert Folder to TAF (recursive)",
|
365
495
|
self.convert_folder_cmd,
|
366
496
|
directory_based=True
|
367
497
|
)
|
368
498
|
|
369
499
|
if self.upload_enabled:
|
370
500
|
self._create_quick_action(
|
371
|
-
"TonieToolbox - Convert Folder and Upload (recursive)",
|
501
|
+
"TonieToolbox - 2. Convert Folder and Upload (recursive)",
|
372
502
|
self.upload_folder_cmd,
|
373
503
|
directory_based=True
|
374
504
|
)
|
375
505
|
|
376
506
|
self._create_quick_action(
|
377
|
-
"TonieToolbox - Convert Folder, Upload with Artwork (recursive)",
|
507
|
+
"TonieToolbox - 3. Convert Folder, Upload with Artwork (recursive)",
|
378
508
|
self.upload_folder_artwork_cmd,
|
379
509
|
directory_based=True
|
380
510
|
)
|
381
511
|
|
382
512
|
self._create_quick_action(
|
383
|
-
"TonieToolbox - Convert Folder, Upload with Artwork and JSON (recursive)",
|
513
|
+
"TonieToolbox - 4. Convert Folder, Upload with Artwork and JSON (recursive)",
|
384
514
|
self.upload_folder_artwork_json_cmd,
|
385
515
|
directory_based=True
|
386
|
-
)
|
516
|
+
)
|
387
517
|
|
388
518
|
def install_quick_actions(self):
|
389
519
|
"""
|
@@ -409,15 +539,14 @@ class MacOSContextMenuIntegration:
|
|
409
539
|
# Refresh the Services menu by restarting the Finder
|
410
540
|
result = subprocess.run(["killall", "-HUP", "Finder"], check=False,
|
411
541
|
capture_output=True, text=True)
|
412
|
-
|
413
|
-
|
414
|
-
print("You'll find them in the Services menu when right-clicking on audio files, TAF files, or folders.")
|
542
|
+
logger.info("TonieToolbox Quick Actions installed successfully.")
|
543
|
+
logger.info("You'll find them in the Services menu when right-clicking on audio files, TAF files, or folders.")
|
415
544
|
|
416
545
|
return True
|
417
546
|
except Exception as e:
|
418
547
|
logger.error(f"Failed to install Quick Actions: {e}")
|
419
548
|
return False
|
420
|
-
|
549
|
+
|
421
550
|
def uninstall_quick_actions(self):
|
422
551
|
"""
|
423
552
|
Uninstall all TonieToolbox Quick Actions.
|
@@ -444,7 +573,9 @@ class MacOSContextMenuIntegration:
|
|
444
573
|
return not any_failures
|
445
574
|
except Exception as e:
|
446
575
|
logger.error(f"Failed to uninstall Quick Actions: {e}")
|
447
|
-
return False
|
576
|
+
return False
|
577
|
+
|
578
|
+
@classmethod
|
448
579
|
def install(cls):
|
449
580
|
"""
|
450
581
|
Generate Quick Actions and install them.
|
@@ -2,7 +2,8 @@
|
|
2
2
|
import os
|
3
3
|
import sys
|
4
4
|
import json
|
5
|
-
from .constants import SUPPORTED_EXTENSIONS, CONFIG_TEMPLATE
|
5
|
+
from .constants import SUPPORTED_EXTENSIONS, CONFIG_TEMPLATE, ICON_BASE64
|
6
|
+
from .artwork import base64_to_ico
|
6
7
|
from .logger import get_logger
|
7
8
|
|
8
9
|
logger = get_logger('integration_windows')
|
@@ -127,8 +128,11 @@ class WindowsClassicContextMenuIntegration:
|
|
127
128
|
self.upload_folder_artwork_json_cmd = self._build_cmd(f'{log_level_arg}', is_recursive=True, is_folder=True, use_upload=True, use_artwork=True, use_json=True, log_to_file=self.log_to_file)
|
128
129
|
|
129
130
|
def _apply_config_template(self):
|
130
|
-
"""Apply the default configuration template if config.json is missing or invalid."""
|
131
|
+
"""Apply the default configuration template if config.json is missing or invalid. Extracts the icon from base64 if not present."""
|
131
132
|
config_path = os.path.join(self.output_dir, 'config.json')
|
133
|
+
icon_path = os.path.join(self.output_dir, 'icon.ico')
|
134
|
+
if not os.path.exists(icon_path):
|
135
|
+
base64_to_ico(ICON_BASE64, icon_path)
|
132
136
|
if not os.path.exists(config_path):
|
133
137
|
with open(config_path, 'w') as f:
|
134
138
|
json.dump(CONFIG_TEMPLATE, f, indent=4)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: TonieToolbox
|
3
|
-
Version: 0.6.
|
4
|
-
Summary:
|
3
|
+
Version: 0.6.0a4
|
4
|
+
Summary: Convert audio files to Toniebox compatible format (.TAF) and interact with TeddyCloud.
|
5
5
|
Home-page: https://github.com/Quentendo64/TonieToolbox
|
6
6
|
Author: Quentendo64
|
7
7
|
Author-email: Quentendo64 <quentin@wohlfeil.at>
|
@@ -23,6 +23,7 @@ Requires-Dist: protobuf<=3.19.0
|
|
23
23
|
Requires-Dist: requests>=2.32.3
|
24
24
|
Requires-Dist: mutagen>=1.47.0
|
25
25
|
Requires-Dist: packaging>=25.0
|
26
|
+
Requires-Dist: tqdm>=4.67.1
|
26
27
|
Provides-Extra: test
|
27
28
|
Requires-Dist: pytest>=8.3.5; extra == "test"
|
28
29
|
Requires-Dist: pytest-cov>=6.1.1; extra == "test"
|
@@ -806,3 +807,6 @@ If you need help, have questions, or want to report a bug, please use the follow
|
|
806
807
|
- [GitHub Issues](https://github.com/Quentendo64/TonieToolbox/issues) for bug reports and feature requests
|
807
808
|
- [GitHub Discussions](https://github.com/Quentendo64/TonieToolbox/discussions) for general questions and community support
|
808
809
|
- [HOWTO Guide](HOWTO.md) for common usage instructions
|
810
|
+
|
811
|
+
## Attribution
|
812
|
+
[Parrot Icon created by Freepik - Flaticon](https://www.flaticon.com/free-animated-icons/parrot)
|
@@ -1,14 +1,14 @@
|
|
1
|
-
TonieToolbox/__init__.py,sha256=
|
2
|
-
TonieToolbox/__main__.py,sha256=
|
3
|
-
TonieToolbox/artwork.py,sha256=
|
1
|
+
TonieToolbox/__init__.py,sha256=P-i-pmjFHddy0r76UYtLh4dFA2jppC0wc_BqhDAzfqw,98
|
2
|
+
TonieToolbox/__main__.py,sha256=F_FHZC7rLejBdEQ4plOg6dcyV4lgFKZDvJtQHpCthKs,34934
|
3
|
+
TonieToolbox/artwork.py,sha256=VRDa9Ydeq7AsXhRKYyEqpfFNH64AA1Xek7yo60bn8OI,5606
|
4
4
|
TonieToolbox/audio_conversion.py,sha256=0GpC6mSRYikIjf_A1w26LAnYtCP2gpHLEKozOISapnM,17190
|
5
|
-
TonieToolbox/constants.py,sha256=
|
6
|
-
TonieToolbox/dependency_manager.py,sha256=
|
5
|
+
TonieToolbox/constants.py,sha256=DabAgzKeG8trpV9I6tie1FA6mt3BhTRf_gF5UwtMpE4,33970
|
6
|
+
TonieToolbox/dependency_manager.py,sha256=7HCyUhVLZ6l0W1HPdYr3AgLc935S43qdTLpf1PipPkQ,49425
|
7
7
|
TonieToolbox/filename_generator.py,sha256=ATCG4w8uN1vyAqvmdhOtpJLlb9QFKCnYIdBViYqpHjw,3464
|
8
|
-
TonieToolbox/integration.py,sha256=
|
9
|
-
TonieToolbox/integration_macos.py,sha256=
|
8
|
+
TonieToolbox/integration.py,sha256=TXCkXxmeMGtw8G8M-jUVuKa7i_hI0JvVoVlIvYFvEnc,3097
|
9
|
+
TonieToolbox/integration_macos.py,sha256=HWHBcMU5BRe50ak2nqB9kIFJS83sJ8t2_B_0FMhat2U,27159
|
10
10
|
TonieToolbox/integration_ubuntu.py,sha256=MU6W0xRCdoHBxrIiOIHePqYTF5Wvn4JxBnDQUPf6fgg,33
|
11
|
-
TonieToolbox/integration_windows.py,sha256=
|
11
|
+
TonieToolbox/integration_windows.py,sha256=7F_8Dh7fWHuvCfcigM5TB-bF5CupzM6iQ-YqOvvNVFQ,23939
|
12
12
|
TonieToolbox/logger.py,sha256=Q_cXbCWfzNmt5q6fvVzeM8IugkD24CSZAVjuf16n6b4,3120
|
13
13
|
TonieToolbox/media_tags.py,sha256=oDlLe0AyvmIdQlqPzH74AUCqwbZZ-49AQKAJdrW26XE,20830
|
14
14
|
TonieToolbox/ogg_page.py,sha256=IHdP0er0TYjyLfON8zes11FdQtRab3QNxeK6sxnAX08,22340
|
@@ -22,9 +22,9 @@ TonieToolbox/tonie_header.proto,sha256=WaWfwO4VrwGtscK2ujfDRKtpeBpaVPoZhI8iMmR-C
|
|
22
22
|
TonieToolbox/tonie_header_pb2.py,sha256=s5bp4ULTEekgq6T61z9fDkRavyPM-3eREs20f_Pxxe8,3665
|
23
23
|
TonieToolbox/tonies_json.py,sha256=YGS2wtaDudxxSy7QuRLWaE5n4bf_AyoSvVLH1Vdh8SE,60754
|
24
24
|
TonieToolbox/version_handler.py,sha256=MLpJ9mSEHkcSoyePnACpfINHTSB1q1_4iEgcT1tGqNU,10028
|
25
|
-
tonietoolbox-0.6.
|
26
|
-
tonietoolbox-0.6.
|
27
|
-
tonietoolbox-0.6.
|
28
|
-
tonietoolbox-0.6.
|
29
|
-
tonietoolbox-0.6.
|
30
|
-
tonietoolbox-0.6.
|
25
|
+
tonietoolbox-0.6.0a4.dist-info/licenses/LICENSE.md,sha256=rGoga9ZAgNco9fBapVFpWf6ri7HOBp1KRnt1uIruXMk,35190
|
26
|
+
tonietoolbox-0.6.0a4.dist-info/METADATA,sha256=1ygRy4_HXQsn3C_TcLydyvkdpTppmYECp4OH7eQ2NTE,27009
|
27
|
+
tonietoolbox-0.6.0a4.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
28
|
+
tonietoolbox-0.6.0a4.dist-info/entry_points.txt,sha256=oqpeyBxel7aScg35Xr4gZKnf486S5KW9okqeBwyJxxc,60
|
29
|
+
tonietoolbox-0.6.0a4.dist-info/top_level.txt,sha256=Wkkm-2p7I3ENfS7ZbYtYUB2g-xwHrXVlERHfonsOPuE,13
|
30
|
+
tonietoolbox-0.6.0a4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|