TonieToolbox 0.6.0a1__py3-none-any.whl → 0.6.0a3__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 +8 -2
- TonieToolbox/constants.py +35 -0
- TonieToolbox/dependency_manager.py +6 -1
- TonieToolbox/integration.py +53 -5
- TonieToolbox/integration_macos.py +273 -98
- TonieToolbox/integration_windows.py +47 -14
- {tonietoolbox-0.6.0a1.dist-info → tonietoolbox-0.6.0a3.dist-info}/METADATA +2 -2
- {tonietoolbox-0.6.0a1.dist-info → tonietoolbox-0.6.0a3.dist-info}/RECORD +13 -14
- TonieToolbox/config.py +0 -1
- {tonietoolbox-0.6.0a1.dist-info → tonietoolbox-0.6.0a3.dist-info}/WHEEL +0 -0
- {tonietoolbox-0.6.0a1.dist-info → tonietoolbox-0.6.0a3.dist-info}/entry_points.txt +0 -0
- {tonietoolbox-0.6.0a1.dist-info → tonietoolbox-0.6.0a3.dist-info}/licenses/LICENSE.md +0 -0
- {tonietoolbox-0.6.0a1.dist-info → tonietoolbox-0.6.0a3.dist-info}/top_level.txt +0 -0
TonieToolbox/__init__.py
CHANGED
TonieToolbox/__main__.py
CHANGED
@@ -21,7 +21,7 @@ from .teddycloud import TeddyCloudClient
|
|
21
21
|
from .tags import get_tags
|
22
22
|
from .tonies_json import fetch_and_update_tonies_json_v1, fetch_and_update_tonies_json_v2
|
23
23
|
from .artwork import upload_artwork
|
24
|
-
from .integration import handle_integration
|
24
|
+
from .integration import handle_integration, handle_config
|
25
25
|
|
26
26
|
def main():
|
27
27
|
"""Entry point for the TonieToolbox application."""
|
@@ -101,6 +101,8 @@ def main():
|
|
101
101
|
parser.add_argument('-D', '--detailed-compare', action='store_true',
|
102
102
|
help='Show detailed OGG page differences when comparing files')
|
103
103
|
# ------------- Parser - Context Menu Integration -------------
|
104
|
+
parser.add_argument('--config-integration', action='store_true',
|
105
|
+
help='Configure context menu integration')
|
104
106
|
parser.add_argument('--install-integration', action='store_true',
|
105
107
|
help='Integrate with the system (e.g., create context menu entries)')
|
106
108
|
parser.add_argument('--uninstall-integration', action='store_true',
|
@@ -133,7 +135,7 @@ def main():
|
|
133
135
|
args = parser.parse_args()
|
134
136
|
|
135
137
|
# ------------- Parser - Source Input -------------
|
136
|
-
if args.input_filename is None and not (args.get_tags or args.upload or args.install_integration or args.uninstall_integration):
|
138
|
+
if args.input_filename is None and not (args.get_tags or args.upload or args.install_integration or args.uninstall_integration or args.config_integration):
|
137
139
|
parser.error("the following arguments are required: SOURCE")
|
138
140
|
|
139
141
|
# ------------- Logging -------------
|
@@ -185,6 +187,10 @@ def main():
|
|
185
187
|
else:
|
186
188
|
logger.error("Failed to handle context menu integration")
|
187
189
|
sys.exit(0)
|
190
|
+
if args.config_integration:
|
191
|
+
logger.debug("Opening configuration file for editing")
|
192
|
+
handle_config()
|
193
|
+
sys.exit(0)
|
188
194
|
# ------------- Normalize Path Input -------------
|
189
195
|
if args.input_filename:
|
190
196
|
logger.debug("Original input path: %s", args.input_filename)
|
TonieToolbox/constants.py
CHANGED
@@ -95,6 +95,24 @@ SUPPORTED_EXTENSIONS = [
|
|
95
95
|
'.ape', '.wma', '.aiff', '.mp2', '.mp4', '.webm', '.mka'
|
96
96
|
]
|
97
97
|
|
98
|
+
UTI_MAPPINGS = {
|
99
|
+
'mp3': 'public.mp3',
|
100
|
+
'wav': 'public.wav',
|
101
|
+
'flac': 'org.xiph.flac',
|
102
|
+
'ogg': 'org.xiph.ogg',
|
103
|
+
'opus': 'public.opus',
|
104
|
+
'aac': 'public.aac-audio',
|
105
|
+
'm4a': 'public.m4a-audio',
|
106
|
+
'wma': 'com.microsoft.windows-media-wma',
|
107
|
+
'aiff': 'public.aiff-audio',
|
108
|
+
'mp2': 'public.mp2',
|
109
|
+
'mp4': 'public.mpeg-4-audio',
|
110
|
+
'mka': 'public.audio',
|
111
|
+
'webm': 'public.webm-audio',
|
112
|
+
'ape': 'public.audio',
|
113
|
+
'taf': 'public.audio'
|
114
|
+
}
|
115
|
+
|
98
116
|
ARTWORK_NAMES = [
|
99
117
|
'cover', 'folder', 'album', 'front', 'artwork', 'image',
|
100
118
|
'albumart', 'albumartwork', 'booklet'
|
@@ -171,4 +189,21 @@ TAG_MAPPINGS = {
|
|
171
189
|
'albuminterpret': 'albumartist', # German tag name
|
172
190
|
'interpret': 'artist', # German tag name
|
173
191
|
|
192
|
+
}
|
193
|
+
|
194
|
+
CONFIG_TEMPLATE = {
|
195
|
+
"metadata": {
|
196
|
+
"description": "TonieToolbox configuration",
|
197
|
+
"config_version": "1.0"
|
198
|
+
},
|
199
|
+
"log_level": "silent", # Options: trace, debug, info, warning, error, critical, silent
|
200
|
+
"log_to_file": False, # True if you want to log to a file ~\.tonietoolbox\logs
|
201
|
+
"upload": {
|
202
|
+
"url": [""], # https://teddycloud.example.com
|
203
|
+
"ignore_ssl_verify": False, # True if you want to ignore SSL certificate verification
|
204
|
+
"username": "", # Basic Auth username
|
205
|
+
"password": "", # Basic Auth password
|
206
|
+
"client_cert_path": "", # Path to client certificate file
|
207
|
+
"client_cert_key_path": "" # Path to client certificate key file
|
208
|
+
}
|
174
209
|
}
|
@@ -35,7 +35,7 @@ DEPENDENCIES = {
|
|
35
35
|
'extract_dir': 'ffmpeg'
|
36
36
|
},
|
37
37
|
'darwin': {
|
38
|
-
'url': 'https://evermeet.cx/ffmpeg/
|
38
|
+
'url': 'https://evermeet.cx/ffmpeg/get/zip',
|
39
39
|
'bin_path': 'ffmpeg',
|
40
40
|
'extract_dir': 'ffmpeg'
|
41
41
|
}
|
@@ -112,6 +112,11 @@ def download_file(url, destination):
|
|
112
112
|
return True
|
113
113
|
except Exception as e:
|
114
114
|
logger.error("Failed to download %s: %s", url, e)
|
115
|
+
# On macOS, provide more helpful error message for SSL certificate issues
|
116
|
+
if platform.system() == 'Darwin' and 'CERTIFICATE_VERIFY_FAILED' in str(e):
|
117
|
+
logger.error("SSL certificate verification failed on macOS. This is a known issue.")
|
118
|
+
logger.error("You can solve this by running: /Applications/Python 3.x/Install Certificates.command")
|
119
|
+
logger.error("Or by using the --auto-download flag which will bypass certificate verification.")
|
115
120
|
return False
|
116
121
|
|
117
122
|
def extract_archive(archive_path, extract_dir):
|
TonieToolbox/integration.py
CHANGED
@@ -1,20 +1,68 @@
|
|
1
1
|
import platform
|
2
|
+
from .logger import get_logger
|
3
|
+
|
4
|
+
logger = get_logger(__name__)
|
2
5
|
|
3
6
|
def handle_integration(args):
|
4
7
|
import platform
|
5
8
|
if platform.system() == 'Windows':
|
6
9
|
from .integration_windows import WindowsClassicContextMenuIntegration as ContextMenuIntegration
|
7
10
|
if args.install_integration:
|
8
|
-
ContextMenuIntegration.install()
|
11
|
+
success = ContextMenuIntegration.install()
|
12
|
+
if success:
|
13
|
+
logger.info("Integration installed successfully.")
|
14
|
+
return True
|
15
|
+
else:
|
16
|
+
logger.error("Integration installation failed.")
|
17
|
+
return False
|
9
18
|
elif args.uninstall_integration:
|
10
|
-
ContextMenuIntegration.uninstall()
|
19
|
+
success = ContextMenuIntegration.uninstall()
|
20
|
+
if success:
|
21
|
+
logger.info("Integration uninstalled successfully.")
|
22
|
+
return True
|
23
|
+
else:
|
24
|
+
logger.error("Integration uninstallation failed.")
|
25
|
+
return False
|
11
26
|
elif platform.system() == 'Darwin':
|
12
27
|
from .integration_macos import MacOSContextMenuIntegration as ContextMenuIntegration
|
13
28
|
if args.install_integration:
|
14
|
-
ContextMenuIntegration.install()
|
29
|
+
success = ContextMenuIntegration.install()
|
30
|
+
if success:
|
31
|
+
logger.info("Integration installed successfully.")
|
32
|
+
return True
|
33
|
+
else:
|
34
|
+
logger.error("Integration installation failed.")
|
35
|
+
return False
|
15
36
|
elif args.uninstall_integration:
|
16
|
-
ContextMenuIntegration.uninstall()
|
37
|
+
success = ContextMenuIntegration.uninstall()
|
38
|
+
if success:
|
39
|
+
logger.info("Integration uninstalled successfully.")
|
40
|
+
return True
|
41
|
+
else:
|
42
|
+
logger.error("Integration uninstallation failed.")
|
43
|
+
return False
|
17
44
|
elif platform.system() == 'Linux':
|
18
45
|
raise NotImplementedError("Context menu integration is not supported on Linux YET. But Soon™")
|
19
46
|
else:
|
20
|
-
raise NotImplementedError(f"Context menu integration is not supported on this OS: {platform.system()}")
|
47
|
+
raise NotImplementedError(f"Context menu integration is not supported on this OS: {platform.system()}")
|
48
|
+
|
49
|
+
def handle_config():
|
50
|
+
"""Opens the configuration file in the default text editor."""
|
51
|
+
import os
|
52
|
+
import platform
|
53
|
+
import subprocess
|
54
|
+
|
55
|
+
config_path = os.path.join(os.path.expanduser("~"), ".tonietoolbox", "config.json")
|
56
|
+
|
57
|
+
if not os.path.exists(config_path):
|
58
|
+
logger.info(f"Configuration file not found at {config_path}.")
|
59
|
+
logger.info("Creating a new configuration file. Using --install-integration will create a new config file.")
|
60
|
+
return
|
61
|
+
if platform.system() == "Windows":
|
62
|
+
os.startfile(config_path)
|
63
|
+
elif platform.system() == "Darwin":
|
64
|
+
subprocess.call(["open", config_path])
|
65
|
+
elif platform.system() == "Linux":
|
66
|
+
subprocess.call(["xdg-open", config_path])
|
67
|
+
else:
|
68
|
+
logger.error(f"Unsupported OS: {platform.system()}")
|
@@ -1,11 +1,10 @@
|
|
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
|
7
|
+
from .constants import SUPPORTED_EXTENSIONS, CONFIG_TEMPLATE,UTI_MAPPINGS
|
9
8
|
from .logger import get_logger
|
10
9
|
|
11
10
|
logger = get_logger('integration_macos')
|
@@ -22,15 +21,9 @@ class MacOSContextMenuIntegration:
|
|
22
21
|
self.services_dir = os.path.join(os.path.expanduser('~'), 'Library', 'Services')
|
23
22
|
self.icon_path = os.path.join(self.output_dir, 'icon.png')
|
24
23
|
os.makedirs(self.output_dir, exist_ok=True)
|
25
|
-
|
26
|
-
# Error handling and success messages for shell scripts
|
27
24
|
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
25
|
self.success_handling = 'echo "Command completed successfully"\nsleep 2'
|
29
|
-
|
30
|
-
# Load configuration
|
31
|
-
self.config = self._load_config()
|
32
|
-
|
33
|
-
# Ensure these attributes always exist
|
26
|
+
self.config = self._apply_config_template()
|
34
27
|
self.upload_url = ''
|
35
28
|
self.log_level = self.config.get('log_level', 'SILENT')
|
36
29
|
self.log_to_file = self.config.get('log_to_file', False)
|
@@ -40,14 +33,19 @@ class MacOSContextMenuIntegration:
|
|
40
33
|
|
41
34
|
logger.debug(f"Upload enabled: {self.upload_enabled}")
|
42
35
|
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
|
-
|
36
|
+
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'))}")
|
37
|
+
self._setup_commands()
|
38
|
+
|
47
39
|
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
40
|
"""Dynamically build command strings for quick actions."""
|
49
41
|
exe = self.exe_path
|
50
42
|
cmd = '#!/bin/bash\n\n'
|
43
|
+
# Debug output to see what's being passed to the script
|
44
|
+
cmd += 'echo "Arguments received: $@"\n'
|
45
|
+
cmd += 'echo "Number of arguments: $#"\n'
|
46
|
+
cmd += 'if [ $# -gt 0 ]; then\n'
|
47
|
+
cmd += ' echo "First argument: $1"\n'
|
48
|
+
cmd += 'fi\n\n'
|
51
49
|
|
52
50
|
# Add a description of what's being executed
|
53
51
|
cmd += 'echo "Running TonieToolbox'
|
@@ -70,12 +68,117 @@ class MacOSContextMenuIntegration:
|
|
70
68
|
else:
|
71
69
|
cmd += ' convert'
|
72
70
|
cmd += ' command..."\n\n'
|
71
|
+
# Properly handle paths from macOS Services
|
72
|
+
if is_folder or is_recursive:
|
73
|
+
# Handle multiple arguments and ensure we get a valid folder
|
74
|
+
cmd += '# Handle paths from macOS Services\n'
|
75
|
+
cmd += '# First, try to get paths from stdin (macOS passes paths this way)\n'
|
76
|
+
cmd += 'if [ -p /dev/stdin ]; then\n'
|
77
|
+
cmd += ' PATHS=$(cat /dev/stdin)\n'
|
78
|
+
cmd += ' echo "Found paths from stdin: $PATHS"\n'
|
79
|
+
cmd += 'fi\n\n'
|
80
|
+
cmd += '# If no paths from stdin, check command line arguments\n'
|
81
|
+
cmd += 'FOLDER_PATH=""\n'
|
82
|
+
cmd += 'if [ -z "$PATHS" ]; then\n'
|
83
|
+
cmd += ' for arg in "$@"; do\n'
|
84
|
+
cmd += ' if [ -d "$arg" ]; then\n'
|
85
|
+
cmd += ' FOLDER_PATH="$arg"\n'
|
86
|
+
cmd += ' echo "Processing folder from args: $FOLDER_PATH"\n'
|
87
|
+
cmd += ' break\n'
|
88
|
+
cmd += ' fi\n'
|
89
|
+
cmd += ' done\n'
|
90
|
+
cmd += 'else\n'
|
91
|
+
cmd += ' for path in $PATHS; do\n'
|
92
|
+
cmd += ' if [ -d "$path" ]; then\n'
|
93
|
+
cmd += ' FOLDER_PATH="$path"\n'
|
94
|
+
cmd += ' echo "Processing folder from stdin: $FOLDER_PATH"\n'
|
95
|
+
cmd += ' break\n'
|
96
|
+
cmd += ' fi\n'
|
97
|
+
cmd += ' done\n'
|
98
|
+
cmd += 'fi\n\n'
|
99
|
+
cmd += 'if [ -z "$FOLDER_PATH" ]; then\n'
|
100
|
+
cmd += ' echo "Error: No valid folder path found in arguments or stdin"\n'
|
101
|
+
cmd += ' read -p "Press any key to close this window..." key\n'
|
102
|
+
cmd += ' exit 1\n'
|
103
|
+
cmd += 'fi\n\n'
|
104
|
+
|
105
|
+
# Use the variable for the command
|
106
|
+
file_placeholder='$FOLDER_PATH'
|
107
|
+
elif use_compare:
|
108
|
+
# For compare operation, we need two file paths
|
109
|
+
cmd += '# Compare requires two files\n'
|
110
|
+
cmd += 'if [ $# -lt 2 ]; then\n'
|
111
|
+
cmd += ' echo "Error: Compare operation requires two files."\n'
|
112
|
+
cmd += ' read -p "Press any key to close this window..." key\n'
|
113
|
+
cmd += ' exit 1\n'
|
114
|
+
cmd += 'fi\n\n'
|
115
|
+
else:
|
116
|
+
# For regular file operations, handle paths correctly
|
117
|
+
cmd += '# Handle file paths correctly - try multiple methods for macOS\n'
|
118
|
+
cmd += 'FILE_PATH=""\n'
|
119
|
+
|
120
|
+
# First, try to get paths from stdin (macOS passes paths this way sometimes)
|
121
|
+
cmd += '# Method 1: Try to read from stdin if available\n'
|
122
|
+
cmd += 'if [ -p /dev/stdin ]; then\n'
|
123
|
+
cmd += ' STDIN_PATHS=$(cat)\n'
|
124
|
+
cmd += ' if [ -n "$STDIN_PATHS" ]; then\n'
|
125
|
+
cmd += ' for path in $STDIN_PATHS; do\n'
|
126
|
+
cmd += ' if [ -f "$path" ]; then\n'
|
127
|
+
cmd += ' FILE_PATH="$path"\n'
|
128
|
+
cmd += ' echo "Found file path from stdin: $FILE_PATH"\n'
|
129
|
+
cmd += ' break\n'
|
130
|
+
cmd += ' fi\n'
|
131
|
+
cmd += ' done\n'
|
132
|
+
cmd += ' fi\n'
|
133
|
+
cmd += 'fi\n\n'
|
134
|
+
|
135
|
+
# Method 2: Try command line arguments
|
136
|
+
cmd += '# Method 2: Check command line arguments\n'
|
137
|
+
cmd += 'if [ -z "$FILE_PATH" ]; then\n'
|
138
|
+
cmd += ' for arg in "$@"; do\n'
|
139
|
+
cmd += ' if [ -f "$arg" ]; then\n'
|
140
|
+
cmd += ' FILE_PATH="$arg"\n'
|
141
|
+
cmd += ' echo "Found file path from arguments: $FILE_PATH"\n'
|
142
|
+
cmd += ' break\n'
|
143
|
+
cmd += ' fi\n'
|
144
|
+
cmd += ' done\n'
|
145
|
+
cmd += 'fi\n\n'
|
146
|
+
|
147
|
+
# Method 3: Try to handle case where path might be in $1
|
148
|
+
cmd += '# Method 3: Try first argument directly\n'
|
149
|
+
cmd += 'if [ -z "$FILE_PATH" ] && [ -n "$1" ] && [ -f "$1" ]; then\n'
|
150
|
+
cmd += ' FILE_PATH="$1"\n'
|
151
|
+
cmd += ' echo "Using first argument directly as file path: $FILE_PATH"\n'
|
152
|
+
cmd += 'fi\n\n'
|
153
|
+
|
154
|
+
# Method 4: Parse automator's encoded path format
|
155
|
+
cmd += '# Method 4: Try to decode special format macOS might use\n'
|
156
|
+
cmd += 'if [ -z "$FILE_PATH" ] && [ -n "$1" ]; then\n'
|
157
|
+
cmd += ' # Sometimes macOS passes paths with "file://" prefix\n'
|
158
|
+
cmd += ' DECODED_PATH=$(echo "$1" | sed -e "s|^file://||" -e "s|%20| |g")\n'
|
159
|
+
cmd += ' if [ -f "$DECODED_PATH" ]; then\n'
|
160
|
+
cmd += ' FILE_PATH="$DECODED_PATH"\n'
|
161
|
+
cmd += ' echo "Using decoded path: $FILE_PATH"\n'
|
162
|
+
cmd += ' fi\n'
|
163
|
+
cmd += 'fi\n\n'
|
164
|
+
|
165
|
+
# Final check
|
166
|
+
cmd += 'if [ -z "$FILE_PATH" ]; then\n'
|
167
|
+
cmd += ' echo "Error: Could not find a valid file path. Tried:"\n'
|
168
|
+
cmd += ' echo "- Reading from stdin"\n'
|
169
|
+
cmd += ' echo "- Command arguments: $@"\n'
|
170
|
+
cmd += ' echo "- Decoding URL format"\n'
|
171
|
+
cmd += ' read -p "Press any key to close this window..." key\n'
|
172
|
+
cmd += ' exit 1\n'
|
173
|
+
cmd += 'fi\n\n'
|
174
|
+
|
175
|
+
# Use the variable for the command
|
176
|
+
file_placeholder='$FILE_PATH'
|
73
177
|
|
74
178
|
# Build the actual command
|
75
179
|
cmd_line = f'"{exe}" {base_args}'
|
76
180
|
if log_to_file:
|
77
|
-
cmd_line += ' --log-file'
|
78
|
-
if is_recursive:
|
181
|
+
cmd_line += ' --log-file' if is_recursive:
|
79
182
|
cmd_line += ' --recursive'
|
80
183
|
if output_to_source:
|
81
184
|
cmd_line += ' --output-to-source'
|
@@ -84,7 +187,28 @@ class MacOSContextMenuIntegration:
|
|
84
187
|
if is_split:
|
85
188
|
cmd_line += ' --split'
|
86
189
|
if use_compare:
|
87
|
-
|
190
|
+
# For compare, we need to handle two files
|
191
|
+
cmd += '# Find two TAF files for comparison\n'
|
192
|
+
cmd += 'FILE1=""\n'
|
193
|
+
cmd += 'FILE2=""\n'
|
194
|
+
cmd += 'for arg in "$@"; do\n'
|
195
|
+
cmd += ' if [ -f "$arg" ]; then\n'
|
196
|
+
cmd += ' if [ -z "$FILE1" ]; then\n'
|
197
|
+
cmd += ' FILE1="$arg"\n'
|
198
|
+
cmd += ' echo "First TAF file: $FILE1"\n'
|
199
|
+
cmd += ' elif [ -z "$FILE2" ]; then\n'
|
200
|
+
cmd += ' FILE2="$arg"\n'
|
201
|
+
cmd += ' echo "Second TAF file: $FILE2"\n'
|
202
|
+
cmd += ' break\n'
|
203
|
+
cmd += ' fi\n'
|
204
|
+
cmd += ' fi\n'
|
205
|
+
cmd += 'done\n\n'
|
206
|
+
cmd += 'if [ -z "$FILE1" ] || [ -z "$FILE2" ]; then\n'
|
207
|
+
cmd += ' echo "Error: Need two TAF files for comparison."\n'
|
208
|
+
cmd += ' read -p "Press any key to close this window..." key\n'
|
209
|
+
cmd += ' exit 1\n'
|
210
|
+
cmd += 'fi\n\n'
|
211
|
+
cmd_line += ' --compare "$FILE1" "$FILE2"'
|
88
212
|
else:
|
89
213
|
cmd_line += f' "{file_placeholder}"'
|
90
214
|
if use_upload:
|
@@ -101,6 +225,7 @@ class MacOSContextMenuIntegration:
|
|
101
225
|
cmd_line += ' --create-custom-json'
|
102
226
|
|
103
227
|
# Add the command to the script
|
228
|
+
cmd += f'echo "Executing: {cmd_line}"\n'
|
104
229
|
cmd += f'{cmd_line}\n\n'
|
105
230
|
|
106
231
|
# Add error and success handling
|
@@ -145,6 +270,18 @@ class MacOSContextMenuIntegration:
|
|
145
270
|
self.upload_folder_artwork_cmd = self._build_cmd(f'{log_level_arg}', is_recursive=True, is_folder=True, use_upload=True, use_artwork=True, log_to_file=self.log_to_file)
|
146
271
|
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
272
|
|
273
|
+
def _apply_config_template(self):
|
274
|
+
"""Apply the default configuration template if config.json is missing or invalid."""
|
275
|
+
config_path = os.path.join(self.output_dir, 'config.json')
|
276
|
+
if not os.path.exists(config_path):
|
277
|
+
with open(config_path, 'w') as f:
|
278
|
+
json.dump(CONFIG_TEMPLATE, f, indent=4)
|
279
|
+
logger.debug(f"Default configuration created at {config_path}")
|
280
|
+
return CONFIG_TEMPLATE
|
281
|
+
else:
|
282
|
+
logger.debug(f"Configuration file found at {config_path}")
|
283
|
+
return self._load_config()
|
284
|
+
|
148
285
|
def _load_config(self):
|
149
286
|
"""Load configuration settings from config.json"""
|
150
287
|
config_path = os.path.join(self.output_dir, 'config.json')
|
@@ -162,7 +299,6 @@ class MacOSContextMenuIntegration:
|
|
162
299
|
|
163
300
|
def _setup_upload(self):
|
164
301
|
"""Set up upload functionality based on config.json settings"""
|
165
|
-
# Always initialize authentication flags
|
166
302
|
self.basic_authentication = False
|
167
303
|
self.client_cert_authentication = False
|
168
304
|
self.none_authentication = False
|
@@ -198,22 +334,16 @@ class MacOSContextMenuIntegration:
|
|
198
334
|
return bool(self.upload_url)
|
199
335
|
except Exception as e:
|
200
336
|
logger.debug(f"Unexpected error while loading configuration: {e}")
|
201
|
-
return False
|
202
|
-
|
337
|
+
return False
|
203
338
|
def _create_quick_action(self, name, command, file_types=None, directory_based=False):
|
204
339
|
"""Create a macOS Quick Action (Service) with the given name and command."""
|
205
|
-
# Create Quick Action directory
|
206
340
|
action_dir = os.path.join(self.services_dir, f"{name}.workflow")
|
207
341
|
os.makedirs(action_dir, exist_ok=True)
|
208
|
-
|
209
|
-
# Create Contents directory
|
210
342
|
contents_dir = os.path.join(action_dir, "Contents")
|
211
343
|
os.makedirs(contents_dir, exist_ok=True)
|
212
|
-
|
213
|
-
# Create document.wflow file with plist content
|
214
344
|
document_path = os.path.join(contents_dir, "document.wflow")
|
215
345
|
|
216
|
-
#
|
346
|
+
# Set up the plist to ensure the service appears in context menus
|
217
347
|
info_plist = {
|
218
348
|
"NSServices": [
|
219
349
|
{
|
@@ -225,30 +355,27 @@ class MacOSContextMenuIntegration:
|
|
225
355
|
"NSApplicationIdentifier": "com.apple.finder"
|
226
356
|
},
|
227
357
|
"NSSendFileTypes": file_types if file_types else [],
|
228
|
-
"NSSendTypes": ["NSFilenamesPboardType"]
|
358
|
+
"NSSendTypes": ["NSFilenamesPboardType"], # Always include this to ensure paths are passed correctly
|
359
|
+
"NSUserData": name,
|
360
|
+
"NSExecutable": "script", # Ensure macOS knows which script to run
|
361
|
+
"NSReturnTypes": []
|
229
362
|
}
|
230
363
|
]
|
231
364
|
}
|
232
365
|
|
233
366
|
info_path = os.path.join(contents_dir, "Info.plist")
|
234
367
|
with open(info_path, "wb") as f:
|
235
|
-
plistlib.dump(info_plist, f)
|
236
|
-
|
237
|
-
# Create script file
|
368
|
+
plistlib.dump(info_plist, f)
|
238
369
|
script_dir = os.path.join(contents_dir, "MacOS")
|
239
370
|
os.makedirs(script_dir, exist_ok=True)
|
240
371
|
script_path = os.path.join(script_dir, "script")
|
241
372
|
|
242
373
|
with open(script_path, "w") as f:
|
243
|
-
f.write(command)
|
244
|
-
|
245
|
-
# Make the script executable
|
374
|
+
f.write(command)
|
246
375
|
os.chmod(script_path, 0o755)
|
247
|
-
|
248
|
-
# Create document.wflow file with a basic workflow definition
|
249
376
|
workflow = {
|
250
377
|
"AMApplication": "Automator",
|
251
|
-
"AMCanShowSelectedItemsWhenRun":
|
378
|
+
"AMCanShowSelectedItemsWhenRun": True,
|
252
379
|
"AMCanShowWhenRun": True,
|
253
380
|
"AMDockBadgeLabel": "",
|
254
381
|
"AMDockBadgeStyle": "badge",
|
@@ -260,7 +387,11 @@ class MacOSContextMenuIntegration:
|
|
260
387
|
"parameters": {
|
261
388
|
"shell": "/bin/bash",
|
262
389
|
"script": command,
|
263
|
-
"input": "as arguments"
|
390
|
+
"input": "as arguments",
|
391
|
+
"showStdout": True,
|
392
|
+
"showStderr": True,
|
393
|
+
"showOutput": True,
|
394
|
+
"runAsAdmin": False
|
264
395
|
}
|
265
396
|
}
|
266
397
|
],
|
@@ -271,158 +402,202 @@ class MacOSContextMenuIntegration:
|
|
271
402
|
},
|
272
403
|
"AMWorkflowSchemeVersion": 2.0,
|
273
404
|
}
|
274
|
-
|
275
405
|
with open(document_path, "wb") as f:
|
276
406
|
plistlib.dump(workflow, f)
|
277
407
|
|
278
408
|
return action_dir
|
279
|
-
|
409
|
+
|
410
|
+
def _extension_to_uti(self, extension):
|
411
|
+
"""Convert a file extension to macOS UTI (Uniform Type Identifier)."""
|
412
|
+
uti_map = UTI_MAPPINGS
|
413
|
+
ext = extension.lower().lstrip('.')
|
414
|
+
return uti_map.get(ext, f'public.{ext}')
|
415
|
+
|
280
416
|
def _generate_audio_extension_actions(self):
|
281
417
|
"""Generate Quick Actions for supported audio file extensions."""
|
282
418
|
extensions = [ext.lower().lstrip('.') for ext in SUPPORTED_EXTENSIONS]
|
283
|
-
|
284
|
-
|
419
|
+
# Convert extensions to UTIs (Uniform Type Identifiers)
|
420
|
+
utis = [self._extension_to_uti(ext) for ext in extensions]
|
285
421
|
self._create_quick_action(
|
286
422
|
"TonieToolbox - Convert to TAF",
|
287
423
|
self.convert_cmd,
|
288
|
-
file_types=
|
424
|
+
file_types=utis
|
289
425
|
)
|
290
426
|
|
291
427
|
if self.upload_enabled:
|
292
428
|
self._create_quick_action(
|
293
429
|
"TonieToolbox - Convert and Upload",
|
294
430
|
self.upload_cmd,
|
295
|
-
file_types=
|
431
|
+
file_types=utis
|
296
432
|
)
|
297
433
|
|
298
434
|
self._create_quick_action(
|
299
435
|
"TonieToolbox - Convert, Upload with Artwork",
|
300
436
|
self.upload_artwork_cmd,
|
301
|
-
file_types=
|
437
|
+
file_types=utis
|
302
438
|
)
|
303
439
|
|
304
440
|
self._create_quick_action(
|
305
441
|
"TonieToolbox - Convert, Upload with Artwork and JSON",
|
306
442
|
self.upload_artwork_json_cmd,
|
307
|
-
file_types=
|
443
|
+
file_types=utis
|
308
444
|
)
|
309
|
-
|
445
|
+
|
310
446
|
def _generate_taf_file_actions(self):
|
311
447
|
"""Generate Quick Actions for .taf files."""
|
448
|
+
taf_uti = self._extension_to_uti("taf") # Use UTI for TAF files
|
449
|
+
|
312
450
|
self._create_quick_action(
|
313
451
|
"TonieToolbox - Show Info",
|
314
452
|
self.show_info_cmd,
|
315
|
-
file_types=[
|
453
|
+
file_types=[taf_uti]
|
316
454
|
)
|
317
455
|
|
318
456
|
self._create_quick_action(
|
319
457
|
"TonieToolbox - Extract Opus Tracks",
|
320
458
|
self.extract_opus_cmd,
|
321
|
-
file_types=[
|
459
|
+
file_types=[taf_uti]
|
322
460
|
)
|
323
461
|
|
324
462
|
if self.upload_enabled:
|
325
463
|
self._create_quick_action(
|
326
464
|
"TonieToolbox - Upload",
|
327
465
|
self.upload_taf_cmd,
|
328
|
-
file_types=[
|
466
|
+
file_types=[taf_uti]
|
329
467
|
)
|
330
|
-
|
331
468
|
self._create_quick_action(
|
332
469
|
"TonieToolbox - Upload with Artwork",
|
333
470
|
self.upload_taf_artwork_cmd,
|
334
|
-
file_types=[
|
471
|
+
file_types=[taf_uti]
|
335
472
|
)
|
336
473
|
|
337
474
|
self._create_quick_action(
|
338
475
|
"TonieToolbox - Upload with Artwork and JSON",
|
339
476
|
self.upload_taf_artwork_json_cmd,
|
340
|
-
file_types=[
|
477
|
+
file_types=[taf_uti]
|
478
|
+
)
|
479
|
+
|
480
|
+
self._create_quick_action(
|
481
|
+
"TonieToolbox - Compare with another TAF file",
|
482
|
+
self.compare_taf_cmd,
|
483
|
+
file_types=[taf_uti]
|
341
484
|
)
|
342
485
|
|
343
|
-
self._create_quick_action(
|
344
|
-
"TonieToolbox - Compare with another TAF file",
|
345
|
-
self.compare_taf_cmd,
|
346
|
-
file_types=["taf"]
|
347
|
-
)
|
348
|
-
|
349
486
|
def _generate_folder_actions(self):
|
350
487
|
"""Generate Quick Actions for folders."""
|
351
488
|
self._create_quick_action(
|
352
|
-
"TonieToolbox - Convert Folder to TAF (recursive)",
|
489
|
+
"TonieToolbox - 1. Convert Folder to TAF (recursive)",
|
353
490
|
self.convert_folder_cmd,
|
354
491
|
directory_based=True
|
355
492
|
)
|
356
493
|
|
357
494
|
if self.upload_enabled:
|
358
495
|
self._create_quick_action(
|
359
|
-
"TonieToolbox - Convert Folder and Upload (recursive)",
|
496
|
+
"TonieToolbox - 2. Convert Folder and Upload (recursive)",
|
360
497
|
self.upload_folder_cmd,
|
361
498
|
directory_based=True
|
362
499
|
)
|
363
500
|
|
364
501
|
self._create_quick_action(
|
365
|
-
"TonieToolbox - Convert Folder, Upload with Artwork (recursive)",
|
502
|
+
"TonieToolbox - 3. Convert Folder, Upload with Artwork (recursive)",
|
366
503
|
self.upload_folder_artwork_cmd,
|
367
504
|
directory_based=True
|
368
505
|
)
|
369
506
|
|
370
507
|
self._create_quick_action(
|
371
|
-
"TonieToolbox - Convert Folder, Upload with Artwork and JSON (recursive)",
|
508
|
+
"TonieToolbox - 4. Convert Folder, Upload with Artwork and JSON (recursive)",
|
372
509
|
self.upload_folder_artwork_json_cmd,
|
373
510
|
directory_based=True
|
374
511
|
)
|
375
|
-
|
512
|
+
|
376
513
|
def install_quick_actions(self):
|
377
|
-
"""
|
378
|
-
|
379
|
-
os.makedirs(self.services_dir, exist_ok=True)
|
380
|
-
|
381
|
-
# Check if the icon exists, copy default if needed
|
382
|
-
if not os.path.exists(self.icon_path):
|
383
|
-
# Include code to extract icon from resources
|
384
|
-
logger.debug(f"Icon not found at {self.icon_path}, using default")
|
385
|
-
|
386
|
-
# Generate Quick Actions for different file types
|
387
|
-
self._generate_audio_extension_actions()
|
388
|
-
self._generate_taf_file_actions()
|
389
|
-
self._generate_folder_actions()
|
390
|
-
|
391
|
-
# Refresh the Services menu by restarting the Finder
|
392
|
-
subprocess.run(["killall", "-HUP", "Finder"], check=False)
|
514
|
+
"""
|
515
|
+
Install all Quick Actions.
|
393
516
|
|
394
|
-
|
395
|
-
|
396
|
-
|
517
|
+
Returns:
|
518
|
+
bool: True if all actions were installed successfully, False otherwise.
|
519
|
+
"""
|
520
|
+
try:
|
521
|
+
# Ensure Services directory exists
|
522
|
+
os.makedirs(self.services_dir, exist_ok=True)
|
523
|
+
|
524
|
+
# Check if the icon exists, copy default if needed
|
525
|
+
if not os.path.exists(self.icon_path):
|
526
|
+
# Include code to extract icon from resources
|
527
|
+
logger.debug(f"Icon not found at {self.icon_path}, using default")
|
528
|
+
|
529
|
+
# Generate Quick Actions for different file types
|
530
|
+
self._generate_audio_extension_actions()
|
531
|
+
self._generate_taf_file_actions()
|
532
|
+
self._generate_folder_actions()
|
533
|
+
|
534
|
+
# Refresh the Services menu by restarting the Finder
|
535
|
+
result = subprocess.run(["killall", "-HUP", "Finder"], check=False,
|
536
|
+
capture_output=True, text=True)
|
537
|
+
logger.info("TonieToolbox Quick Actions installed successfully.")
|
538
|
+
logger.info("You'll find them in the Services menu when right-clicking on audio files, TAF files, or folders.")
|
539
|
+
|
540
|
+
return True
|
541
|
+
except Exception as e:
|
542
|
+
logger.error(f"Failed to install Quick Actions: {e}")
|
543
|
+
return False
|
544
|
+
|
397
545
|
def uninstall_quick_actions(self):
|
398
|
-
"""
|
399
|
-
|
400
|
-
for item in os.listdir(self.services_dir):
|
401
|
-
if item.startswith("TonieToolbox - ") and item.endswith(".workflow"):
|
402
|
-
action_path = os.path.join(self.services_dir, item)
|
403
|
-
try:
|
404
|
-
subprocess.run(["rm", "-rf", action_path], check=True)
|
405
|
-
print(f"Removed: {item}")
|
406
|
-
except subprocess.CalledProcessError:
|
407
|
-
print(f"Failed to remove: {item}")
|
408
|
-
|
409
|
-
# Refresh the Services menu
|
410
|
-
subprocess.run(["killall", "-HUP", "Finder"], check=False)
|
546
|
+
"""
|
547
|
+
Uninstall all TonieToolbox Quick Actions.
|
411
548
|
|
412
|
-
|
413
|
-
|
549
|
+
Returns:
|
550
|
+
bool: True if all actions were uninstalled successfully, False otherwise.
|
551
|
+
"""
|
552
|
+
try:
|
553
|
+
any_failures = False
|
554
|
+
for item in os.listdir(self.services_dir):
|
555
|
+
if item.startswith("TonieToolbox - ") and item.endswith(".workflow"):
|
556
|
+
action_path = os.path.join(self.services_dir, item)
|
557
|
+
try:
|
558
|
+
subprocess.run(["rm", "-rf", action_path], check=True)
|
559
|
+
print(f"Removed: {item}")
|
560
|
+
except subprocess.CalledProcessError as e:
|
561
|
+
print(f"Failed to remove: {item}")
|
562
|
+
logger.error(f"Error removing {item}: {e}")
|
563
|
+
any_failures = True
|
564
|
+
subprocess.run(["killall", "-HUP", "Finder"], check=False)
|
565
|
+
|
566
|
+
print("TonieToolbox Quick Actions uninstalled successfully.")
|
567
|
+
|
568
|
+
return not any_failures
|
569
|
+
except Exception as e:
|
570
|
+
logger.error(f"Failed to uninstall Quick Actions: {e}")
|
571
|
+
return False
|
572
|
+
|
414
573
|
@classmethod
|
415
574
|
def install(cls):
|
416
575
|
"""
|
417
576
|
Generate Quick Actions and install them.
|
577
|
+
|
578
|
+
Returns:
|
579
|
+
bool: True if installation was successful, False otherwise.
|
418
580
|
"""
|
419
581
|
instance = cls()
|
420
|
-
instance.install_quick_actions()
|
582
|
+
if instance.install_quick_actions():
|
583
|
+
logger.info("macOS integration installed successfully.")
|
584
|
+
return True
|
585
|
+
else:
|
586
|
+
logger.error("macOS integration installation failed.")
|
587
|
+
return False
|
421
588
|
|
422
589
|
@classmethod
|
423
590
|
def uninstall(cls):
|
424
591
|
"""
|
425
592
|
Uninstall all TonieToolbox Quick Actions.
|
593
|
+
|
594
|
+
Returns:
|
595
|
+
bool: True if uninstallation was successful, False otherwise.
|
426
596
|
"""
|
427
597
|
instance = cls()
|
428
|
-
instance.uninstall_quick_actions()
|
598
|
+
if instance.uninstall_quick_actions():
|
599
|
+
logger.info("macOS integration uninstalled successfully.")
|
600
|
+
return True
|
601
|
+
else:
|
602
|
+
logger.error("macOS integration uninstallation failed.")
|
603
|
+
return False
|
@@ -2,7 +2,7 @@
|
|
2
2
|
import os
|
3
3
|
import sys
|
4
4
|
import json
|
5
|
-
from .constants import SUPPORTED_EXTENSIONS
|
5
|
+
from .constants import SUPPORTED_EXTENSIONS, CONFIG_TEMPLATE
|
6
6
|
from .logger import get_logger
|
7
7
|
|
8
8
|
logger = get_logger('integration_windows')
|
@@ -24,8 +24,7 @@ class WindowsClassicContextMenuIntegration:
|
|
24
24
|
self.separator_above = '"CommandFlags"=dword:00000020'
|
25
25
|
self.error_handling = r' && if %ERRORLEVEL% neq 0 (echo Error: Command failed with error code %ERRORLEVEL% && pause && exit /b %ERRORLEVEL%) else (echo Command completed successfully && ping -n 2 127.0.0.1 > nul)'
|
26
26
|
self.show_info_error_handling = r' && if %ERRORLEVEL% neq 0 (echo Error: Command failed with error code %ERRORLEVEL% && pause && exit /b %ERRORLEVEL%) else (echo. && echo Press any key to close this window... && pause > nul)'
|
27
|
-
self.config = self.
|
28
|
-
# Ensure these attributes always exist
|
27
|
+
self.config = self._apply_config_template()
|
29
28
|
self.upload_url = ''
|
30
29
|
self.log_level = self.config.get('log_level', 'SILENT')
|
31
30
|
self.log_to_file = self.config.get('log_to_file', False)
|
@@ -127,6 +126,18 @@ class WindowsClassicContextMenuIntegration:
|
|
127
126
|
self.upload_folder_artwork_cmd = self._build_cmd(f'{log_level_arg}', is_recursive=True, is_folder=True, use_upload=True, use_artwork=True, log_to_file=self.log_to_file)
|
128
127
|
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)
|
129
128
|
|
129
|
+
def _apply_config_template(self):
|
130
|
+
"""Apply the default configuration template if config.json is missing or invalid."""
|
131
|
+
config_path = os.path.join(self.output_dir, 'config.json')
|
132
|
+
if not os.path.exists(config_path):
|
133
|
+
with open(config_path, 'w') as f:
|
134
|
+
json.dump(CONFIG_TEMPLATE, f, indent=4)
|
135
|
+
logger.debug(f"Default configuration created at {config_path}")
|
136
|
+
return CONFIG_TEMPLATE
|
137
|
+
else:
|
138
|
+
logger.debug(f"Configuration file found at {config_path}")
|
139
|
+
return self._load_config()
|
140
|
+
|
130
141
|
def _load_config(self):
|
131
142
|
"""Load configuration settings from config.json"""
|
132
143
|
config_path = os.path.join(self.output_dir, 'config.json')
|
@@ -360,11 +371,14 @@ class WindowsClassicContextMenuIntegration:
|
|
360
371
|
f.write('\n'.join(unreg_lines))
|
361
372
|
|
362
373
|
return reg_path
|
363
|
-
|
374
|
+
|
364
375
|
def install_registry_files(self, uninstall=False):
|
365
376
|
"""
|
366
377
|
Import the generated .reg file into the Windows registry with UAC elevation.
|
367
378
|
If uninstall is True, imports the uninstaller .reg file.
|
379
|
+
|
380
|
+
Returns:
|
381
|
+
bool: True if registry import was successful, False otherwise.
|
368
382
|
"""
|
369
383
|
import subprocess
|
370
384
|
reg_file = os.path.join(
|
@@ -372,18 +386,27 @@ class WindowsClassicContextMenuIntegration:
|
|
372
386
|
'remove_tonietoolbox_context.reg' if uninstall else 'tonietoolbox_context.reg'
|
373
387
|
)
|
374
388
|
if not os.path.exists(reg_file):
|
375
|
-
|
389
|
+
logger.error(f"Registry file not found: {reg_file}")
|
390
|
+
return False
|
376
391
|
|
377
|
-
# Use PowerShell to run reg.exe import as administrator (fix argument passing)
|
378
392
|
ps_command = (
|
379
|
-
f"Start-Process reg.exe -ArgumentList @('import', '{reg_file}') -Verb RunAs"
|
393
|
+
f"Start-Process reg.exe -ArgumentList @('import', '{reg_file}') -Verb RunAs -Wait -PassThru"
|
380
394
|
)
|
381
395
|
try:
|
382
|
-
subprocess.run(["powershell.exe", "-Command", ps_command], check=
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
396
|
+
result = subprocess.run(["powershell.exe", "-Command", ps_command], check=False,
|
397
|
+
capture_output=True, text=True)
|
398
|
+
|
399
|
+
if result.returncode == 0:
|
400
|
+
logger.info(f"{'Uninstallation' if uninstall else 'Installation'} registry import completed.")
|
401
|
+
return True
|
402
|
+
else:
|
403
|
+
logger.error(f"Registry import command failed with return code {result.returncode}")
|
404
|
+
logger.error(f"STDERR: {result.stderr}")
|
405
|
+
return False
|
406
|
+
|
407
|
+
except subprocess.SubprocessError as e:
|
408
|
+
logger.error(f"Failed to import registry file: {e}")
|
409
|
+
return False
|
387
410
|
|
388
411
|
@classmethod
|
389
412
|
def install(cls):
|
@@ -392,7 +415,12 @@ class WindowsClassicContextMenuIntegration:
|
|
392
415
|
"""
|
393
416
|
instance = cls()
|
394
417
|
instance.generate_registry_files()
|
395
|
-
instance.install_registry_files(uninstall=False)
|
418
|
+
if instance.install_registry_files(uninstall=False):
|
419
|
+
logger.info("Integration installed successfully.")
|
420
|
+
return True
|
421
|
+
else:
|
422
|
+
logger.error("Integration installation failed.")
|
423
|
+
return False
|
396
424
|
|
397
425
|
@classmethod
|
398
426
|
def uninstall(cls):
|
@@ -401,4 +429,9 @@ class WindowsClassicContextMenuIntegration:
|
|
401
429
|
"""
|
402
430
|
instance = cls()
|
403
431
|
instance.generate_registry_files()
|
404
|
-
instance.install_registry_files(uninstall=True)
|
432
|
+
if instance.install_registry_files(uninstall=True):
|
433
|
+
logger.info("Integration uninstalled successfully.")
|
434
|
+
return True
|
435
|
+
else:
|
436
|
+
logger.error("Integration uninstallation failed.")
|
437
|
+
return False
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: TonieToolbox
|
3
|
-
Version: 0.6.
|
3
|
+
Version: 0.6.0a3
|
4
4
|
Summary: Create files for the Tonie box and interact with TeddyCloud servers
|
5
5
|
Home-page: https://github.com/Quentendo64/TonieToolbox
|
6
6
|
Author: Quentendo64
|
@@ -225,7 +225,7 @@ docker run --rm -v "$(pwd)/input:/tonietoolbox/input" -v "$(pwd)/output:/tonieto
|
|
225
225
|
docker run --rm -v "%cd%\input:/tonietoolbox/input" -v "%cd%\output:/tonietoolbox/output" quentendo64/tonietoolbox input/my-audio-file.mp3
|
226
226
|
```
|
227
227
|
|
228
|
-
**Or using docker-compose
|
228
|
+
**Or using docker-compose:**
|
229
229
|
|
230
230
|
```shell
|
231
231
|
docker-compose run --rm tonietoolbox input/my-audio-file.mp3
|
@@ -1,15 +1,14 @@
|
|
1
|
-
TonieToolbox/__init__.py,sha256=
|
2
|
-
TonieToolbox/__main__.py,sha256=
|
1
|
+
TonieToolbox/__init__.py,sha256=03DkelZsljyJIZJPxhaIrnGaLVecnU_C3sxGmHN3a_0,98
|
2
|
+
TonieToolbox/__main__.py,sha256=pD-goy-ke-M3Z8zkbUgW1IF94UQPGQSHql15yeBBDzk,34040
|
3
3
|
TonieToolbox/artwork.py,sha256=fOlYx1cffVq-2u1e_qiW5bd3LpPCGMmIfZAxIwC05tA,4451
|
4
4
|
TonieToolbox/audio_conversion.py,sha256=0GpC6mSRYikIjf_A1w26LAnYtCP2gpHLEKozOISapnM,17190
|
5
|
-
TonieToolbox/
|
6
|
-
TonieToolbox/
|
7
|
-
TonieToolbox/dependency_manager.py,sha256=EvVUO4T1CrhUXlrVk9HBgCRDER3b1BRNdgkZLSpCTho,27921
|
5
|
+
TonieToolbox/constants.py,sha256=4L-lOX0_UBt3Xo5_Y2tVKhlHCRysjTIbC-cdlE7lNl0,8104
|
6
|
+
TonieToolbox/dependency_manager.py,sha256=GB1RlXNkRHsEd4m7x45-uqly6kVbnyr9wlOmxHzAzgc,28392
|
8
7
|
TonieToolbox/filename_generator.py,sha256=ATCG4w8uN1vyAqvmdhOtpJLlb9QFKCnYIdBViYqpHjw,3464
|
9
|
-
TonieToolbox/integration.py,sha256=
|
10
|
-
TonieToolbox/integration_macos.py,sha256=
|
8
|
+
TonieToolbox/integration.py,sha256=NEQpKQSwLa6el7DoIqWbscBUGmRrYls_UiSmmvpSpxw,2785
|
9
|
+
TonieToolbox/integration_macos.py,sha256=bWAQ-vWrQK-KluL3NYdZexEUBwnkL_-tasFgkHL3yK4,26907
|
11
10
|
TonieToolbox/integration_ubuntu.py,sha256=MU6W0xRCdoHBxrIiOIHePqYTF5Wvn4JxBnDQUPf6fgg,33
|
12
|
-
TonieToolbox/integration_windows.py,sha256=
|
11
|
+
TonieToolbox/integration_windows.py,sha256=SSVg-9bf8wDQMuJgg-j_6cUczcx7H1-C1mbEIlNJlzs,23691
|
13
12
|
TonieToolbox/logger.py,sha256=Q_cXbCWfzNmt5q6fvVzeM8IugkD24CSZAVjuf16n6b4,3120
|
14
13
|
TonieToolbox/media_tags.py,sha256=oDlLe0AyvmIdQlqPzH74AUCqwbZZ-49AQKAJdrW26XE,20830
|
15
14
|
TonieToolbox/ogg_page.py,sha256=IHdP0er0TYjyLfON8zes11FdQtRab3QNxeK6sxnAX08,22340
|
@@ -23,9 +22,9 @@ TonieToolbox/tonie_header.proto,sha256=WaWfwO4VrwGtscK2ujfDRKtpeBpaVPoZhI8iMmR-C
|
|
23
22
|
TonieToolbox/tonie_header_pb2.py,sha256=s5bp4ULTEekgq6T61z9fDkRavyPM-3eREs20f_Pxxe8,3665
|
24
23
|
TonieToolbox/tonies_json.py,sha256=YGS2wtaDudxxSy7QuRLWaE5n4bf_AyoSvVLH1Vdh8SE,60754
|
25
24
|
TonieToolbox/version_handler.py,sha256=MLpJ9mSEHkcSoyePnACpfINHTSB1q1_4iEgcT1tGqNU,10028
|
26
|
-
tonietoolbox-0.6.
|
27
|
-
tonietoolbox-0.6.
|
28
|
-
tonietoolbox-0.6.
|
29
|
-
tonietoolbox-0.6.
|
30
|
-
tonietoolbox-0.6.
|
31
|
-
tonietoolbox-0.6.
|
25
|
+
tonietoolbox-0.6.0a3.dist-info/licenses/LICENSE.md,sha256=rGoga9ZAgNco9fBapVFpWf6ri7HOBp1KRnt1uIruXMk,35190
|
26
|
+
tonietoolbox-0.6.0a3.dist-info/METADATA,sha256=N6HxrJmWGLbiTvwXInNLMHsptNPJagYkg3TACTODc0M,26849
|
27
|
+
tonietoolbox-0.6.0a3.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
28
|
+
tonietoolbox-0.6.0a3.dist-info/entry_points.txt,sha256=oqpeyBxel7aScg35Xr4gZKnf486S5KW9okqeBwyJxxc,60
|
29
|
+
tonietoolbox-0.6.0a3.dist-info/top_level.txt,sha256=Wkkm-2p7I3ENfS7ZbYtYUB2g-xwHrXVlERHfonsOPuE,13
|
30
|
+
tonietoolbox-0.6.0a3.dist-info/RECORD,,
|
TonieToolbox/config.py
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
## TODO: Add config.py
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|