TonieToolbox 0.2.3__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tonietoolbox-0.2.3/TonieToolbox.egg-info → tonietoolbox-0.3.0}/PKG-INFO +99 -2
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/README.md +99 -2
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/__init__.py +1 -1
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/__main__.py +287 -5
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/dependency_manager.py +5 -5
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/media_tags.py +167 -1
- tonietoolbox-0.3.0/TonieToolbox/teddycloud.py +580 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0/TonieToolbox.egg-info}/PKG-INFO +99 -2
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox.egg-info/SOURCES.txt +1 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/LICENSE.md +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/MANIFEST.in +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/audio_conversion.py +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/constants.py +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/filename_generator.py +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/logger.py +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/ogg_page.py +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/opus_packet.py +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/recursive_processor.py +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/tonie_analysis.py +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/tonie_file.py +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/tonie_header.proto +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/tonie_header_pb2.py +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/version_handler.py +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox.egg-info/dependency_links.txt +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox.egg-info/entry_points.txt +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox.egg-info/requires.txt +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox.egg-info/top_level.txt +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/pyproject.toml +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/setup.cfg +0 -0
- {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: TonieToolbox
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.0
|
4
4
|
Summary: Convert audio files to Tonie box compatible format
|
5
5
|
Home-page: https://github.com/Quentendo64/TonieToolbox
|
6
6
|
Author: Quentendo64
|
@@ -47,12 +47,14 @@ A Python tool for converting audio files to Tonie box compatible format (TAF - T
|
|
47
47
|
- [Advanced Options](#advanced-options)
|
48
48
|
- [Common Usage Examples](#common-usage-examples)
|
49
49
|
- [Media Tags](#media-tags)
|
50
|
+
- [TeddyCloud Upload](#teddycloud-upload)
|
50
51
|
- [Technical Details](#technical-details)
|
51
52
|
- [TAF File Structure](#taf-tonie-audio-format-file-structure)
|
52
53
|
- [File Analysis](#file-analysis)
|
53
54
|
- [File Comparison](#file-comparison)
|
54
55
|
- [Related Projects](#related-projects)
|
55
56
|
- [Contributing](#contributing)
|
57
|
+
- [Legal Notice](#legal-notice)
|
56
58
|
|
57
59
|
## Overview
|
58
60
|
|
@@ -69,6 +71,8 @@ The tool provides several capabilities:
|
|
69
71
|
- Compare two TAF files for debugging differences
|
70
72
|
- Support various input formats through FFmpeg conversion
|
71
73
|
- Extract and use audio media tags (ID3, Vorbis Comments, etc.) for better file naming
|
74
|
+
- Upload Tonie files directly to a TeddyCloud server
|
75
|
+
- Automatically upload cover artwork alongside Tonie files
|
72
76
|
|
73
77
|
## Requirements
|
74
78
|
|
@@ -167,7 +171,11 @@ tonietoolbox -h
|
|
167
171
|
|
168
172
|
Output:
|
169
173
|
```
|
170
|
-
usage: TonieToolbox.py [-h] [-v] [
|
174
|
+
usage: TonieToolbox.py [-h] [-v] [--upload URL] [--include-artwork] [--get-tags URL]
|
175
|
+
[--ignore-ssl-verify] [--special-folder FOLDER] [--path PATH]
|
176
|
+
[--show-progress] [--connection-timeout SECONDS]
|
177
|
+
[--read-timeout SECONDS] [--max-retries RETRIES]
|
178
|
+
[--retry-delay SECONDS] [-t TIMESTAMP] [-f FFMPEG] [-o OPUSENC]
|
171
179
|
[-b BITRATE] [-c] [-a TAG] [-n] [-i] [-s] [-r] [-O]
|
172
180
|
[-A] [-k] [-C FILE2] [-D] [-m] [--name-template TEMPLATE]
|
173
181
|
[--show-tags] [-d] [-T] [-q] [-Q]
|
@@ -179,6 +187,24 @@ positional arguments:
|
|
179
187
|
SOURCE input file or directory or a file list (.lst)
|
180
188
|
TARGET the output file name (default: ---ID---)
|
181
189
|
|
190
|
+
TeddyCloud Options:
|
191
|
+
--upload URL Upload to TeddyCloud instance (e.g., https://teddycloud.example.com). Supports .taf, .jpg, .jpeg, .png files.
|
192
|
+
--include-artwork Upload cover artwork image alongside the Tonie file when using --upload
|
193
|
+
--get-tags URL Get available tags from TeddyCloud instance
|
194
|
+
--ignore-ssl-verify Ignore SSL certificate verification (for self-signed certificates)
|
195
|
+
--special-folder FOLDER
|
196
|
+
Special folder to upload to (currently only "library" is supported)
|
197
|
+
--path PATH Path where to write the file on TeddyCloud server
|
198
|
+
--show-progress Show progress bar during file upload (default: enabled)
|
199
|
+
--connection-timeout SECONDS
|
200
|
+
Connection timeout in seconds (default: 10)
|
201
|
+
--read-timeout SECONDS
|
202
|
+
Read timeout in seconds (default: 300)
|
203
|
+
--max-retries RETRIES
|
204
|
+
Maximum number of retry attempts (default: 3)
|
205
|
+
--retry-delay SECONDS
|
206
|
+
Delay between retry attempts in seconds (default: 5)
|
207
|
+
|
182
208
|
optional arguments:
|
183
209
|
-h, --help show this help message and exit
|
184
210
|
-v, --version show program version and exit
|
@@ -325,6 +351,65 @@ This will attempt to use the album information from the audio files for naming t
|
|
325
351
|
tonietoolbox --recursive --use-media-tags --name-template "{date} - {album} ({artist})" "Music/Collection/"
|
326
352
|
```
|
327
353
|
|
354
|
+
### TeddyCloud Upload
|
355
|
+
|
356
|
+
TonieToolbox can upload files directly to a TeddyCloud server, which is an alternative to the official Tonie cloud for managing custom Tonies.
|
357
|
+
|
358
|
+
#### Upload a Tonie file to TeddyCloud:
|
359
|
+
|
360
|
+
```
|
361
|
+
tonietoolbox --upload https://teddycloud.example.com my_tonie.taf
|
362
|
+
```
|
363
|
+
|
364
|
+
This will upload the specified Tonie file to the TeddyCloud server.
|
365
|
+
|
366
|
+
#### Upload a newly created Tonie file:
|
367
|
+
|
368
|
+
You can combine conversion and upload in a single command:
|
369
|
+
|
370
|
+
```
|
371
|
+
tonietoolbox input.mp3 --upload https://teddycloud.example.com
|
372
|
+
```
|
373
|
+
|
374
|
+
This will convert the input file to TAF format and then upload it to the TeddyCloud server.
|
375
|
+
|
376
|
+
#### Upload with custom path:
|
377
|
+
|
378
|
+
```
|
379
|
+
tonietoolbox my_tonie.taf --upload https://teddycloud.example.com --path "/custom_audio"
|
380
|
+
The path needs to be existing in the TeddyCloud Library.
|
381
|
+
```
|
382
|
+
|
383
|
+
#### Upload with artwork:
|
384
|
+
|
385
|
+
TonieToolbox can automatically find and upload cover artwork alongside your Tonie files:
|
386
|
+
|
387
|
+
```
|
388
|
+
tonietoolbox my_tonie.taf --upload https://teddycloud.example.com --include-artwork
|
389
|
+
```
|
390
|
+
|
391
|
+
This will:
|
392
|
+
1. Look for cover images (like "cover.jpg", "artwork.png", etc.) in the source directory
|
393
|
+
2. If no cover image is found, attempt to extract embedded artwork from the audio files
|
394
|
+
3. Upload the artwork to the "/custom_img" directory on the TeddyCloud server
|
395
|
+
4. The artwork will be uploaded with the same filename as the Tonie file for easier association
|
396
|
+
|
397
|
+
#### Recursive processing with uploads:
|
398
|
+
|
399
|
+
```
|
400
|
+
tonietoolbox --recursive "Music/Albums" --upload https://teddycloud.example.com --include-artwork
|
401
|
+
```
|
402
|
+
|
403
|
+
This will process all folders recursively, create TAF files, and upload both the TAF files and their cover artwork to the TeddyCloud server.
|
404
|
+
|
405
|
+
#### Upload with SSL certificate verification disabled:
|
406
|
+
|
407
|
+
```
|
408
|
+
tonietoolbox my_tonie.taf --upload https://teddycloud.example.com --ignore-ssl-verify
|
409
|
+
```
|
410
|
+
|
411
|
+
Use this option if the TeddyCloud server uses a self-signed certificate.
|
412
|
+
|
328
413
|
## Technical Details
|
329
414
|
|
330
415
|
### TAF (Tonie Audio Format) File Structure
|
@@ -404,3 +489,15 @@ This project is inspired by and builds upon the work of other Tonie-related open
|
|
404
489
|
## Contributing
|
405
490
|
|
406
491
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
492
|
+
|
493
|
+
## Legal Notice
|
494
|
+
|
495
|
+
This project is an independent, community-driven effort created for educational and personal use purposes.
|
496
|
+
|
497
|
+
- tonies®, toniebox®, Hörfigur® are registered trademarks of [tonies GmbH](https://tonies.com).
|
498
|
+
- This project is not affiliated with, endorsed by, or connected to tonies GmbH in any way.
|
499
|
+
- TonieToolbox is provided "as is" without warranty of any kind, either express or implied.
|
500
|
+
- Users are responsible for ensuring their usage complies with all applicable copyright and intellectual property laws.
|
501
|
+
- This tool is intended for personal use with legally owned content only.
|
502
|
+
|
503
|
+
By using TonieToolbox, you acknowledge that the authors of this software take no responsibility for any potential misuse or any damages that might result from the use of this software.
|
@@ -20,12 +20,14 @@ A Python tool for converting audio files to Tonie box compatible format (TAF - T
|
|
20
20
|
- [Advanced Options](#advanced-options)
|
21
21
|
- [Common Usage Examples](#common-usage-examples)
|
22
22
|
- [Media Tags](#media-tags)
|
23
|
+
- [TeddyCloud Upload](#teddycloud-upload)
|
23
24
|
- [Technical Details](#technical-details)
|
24
25
|
- [TAF File Structure](#taf-tonie-audio-format-file-structure)
|
25
26
|
- [File Analysis](#file-analysis)
|
26
27
|
- [File Comparison](#file-comparison)
|
27
28
|
- [Related Projects](#related-projects)
|
28
29
|
- [Contributing](#contributing)
|
30
|
+
- [Legal Notice](#legal-notice)
|
29
31
|
|
30
32
|
## Overview
|
31
33
|
|
@@ -42,6 +44,8 @@ The tool provides several capabilities:
|
|
42
44
|
- Compare two TAF files for debugging differences
|
43
45
|
- Support various input formats through FFmpeg conversion
|
44
46
|
- Extract and use audio media tags (ID3, Vorbis Comments, etc.) for better file naming
|
47
|
+
- Upload Tonie files directly to a TeddyCloud server
|
48
|
+
- Automatically upload cover artwork alongside Tonie files
|
45
49
|
|
46
50
|
## Requirements
|
47
51
|
|
@@ -140,7 +144,11 @@ tonietoolbox -h
|
|
140
144
|
|
141
145
|
Output:
|
142
146
|
```
|
143
|
-
usage: TonieToolbox.py [-h] [-v] [
|
147
|
+
usage: TonieToolbox.py [-h] [-v] [--upload URL] [--include-artwork] [--get-tags URL]
|
148
|
+
[--ignore-ssl-verify] [--special-folder FOLDER] [--path PATH]
|
149
|
+
[--show-progress] [--connection-timeout SECONDS]
|
150
|
+
[--read-timeout SECONDS] [--max-retries RETRIES]
|
151
|
+
[--retry-delay SECONDS] [-t TIMESTAMP] [-f FFMPEG] [-o OPUSENC]
|
144
152
|
[-b BITRATE] [-c] [-a TAG] [-n] [-i] [-s] [-r] [-O]
|
145
153
|
[-A] [-k] [-C FILE2] [-D] [-m] [--name-template TEMPLATE]
|
146
154
|
[--show-tags] [-d] [-T] [-q] [-Q]
|
@@ -152,6 +160,24 @@ positional arguments:
|
|
152
160
|
SOURCE input file or directory or a file list (.lst)
|
153
161
|
TARGET the output file name (default: ---ID---)
|
154
162
|
|
163
|
+
TeddyCloud Options:
|
164
|
+
--upload URL Upload to TeddyCloud instance (e.g., https://teddycloud.example.com). Supports .taf, .jpg, .jpeg, .png files.
|
165
|
+
--include-artwork Upload cover artwork image alongside the Tonie file when using --upload
|
166
|
+
--get-tags URL Get available tags from TeddyCloud instance
|
167
|
+
--ignore-ssl-verify Ignore SSL certificate verification (for self-signed certificates)
|
168
|
+
--special-folder FOLDER
|
169
|
+
Special folder to upload to (currently only "library" is supported)
|
170
|
+
--path PATH Path where to write the file on TeddyCloud server
|
171
|
+
--show-progress Show progress bar during file upload (default: enabled)
|
172
|
+
--connection-timeout SECONDS
|
173
|
+
Connection timeout in seconds (default: 10)
|
174
|
+
--read-timeout SECONDS
|
175
|
+
Read timeout in seconds (default: 300)
|
176
|
+
--max-retries RETRIES
|
177
|
+
Maximum number of retry attempts (default: 3)
|
178
|
+
--retry-delay SECONDS
|
179
|
+
Delay between retry attempts in seconds (default: 5)
|
180
|
+
|
155
181
|
optional arguments:
|
156
182
|
-h, --help show this help message and exit
|
157
183
|
-v, --version show program version and exit
|
@@ -298,6 +324,65 @@ This will attempt to use the album information from the audio files for naming t
|
|
298
324
|
tonietoolbox --recursive --use-media-tags --name-template "{date} - {album} ({artist})" "Music/Collection/"
|
299
325
|
```
|
300
326
|
|
327
|
+
### TeddyCloud Upload
|
328
|
+
|
329
|
+
TonieToolbox can upload files directly to a TeddyCloud server, which is an alternative to the official Tonie cloud for managing custom Tonies.
|
330
|
+
|
331
|
+
#### Upload a Tonie file to TeddyCloud:
|
332
|
+
|
333
|
+
```
|
334
|
+
tonietoolbox --upload https://teddycloud.example.com my_tonie.taf
|
335
|
+
```
|
336
|
+
|
337
|
+
This will upload the specified Tonie file to the TeddyCloud server.
|
338
|
+
|
339
|
+
#### Upload a newly created Tonie file:
|
340
|
+
|
341
|
+
You can combine conversion and upload in a single command:
|
342
|
+
|
343
|
+
```
|
344
|
+
tonietoolbox input.mp3 --upload https://teddycloud.example.com
|
345
|
+
```
|
346
|
+
|
347
|
+
This will convert the input file to TAF format and then upload it to the TeddyCloud server.
|
348
|
+
|
349
|
+
#### Upload with custom path:
|
350
|
+
|
351
|
+
```
|
352
|
+
tonietoolbox my_tonie.taf --upload https://teddycloud.example.com --path "/custom_audio"
|
353
|
+
The path needs to be existing in the TeddyCloud Library.
|
354
|
+
```
|
355
|
+
|
356
|
+
#### Upload with artwork:
|
357
|
+
|
358
|
+
TonieToolbox can automatically find and upload cover artwork alongside your Tonie files:
|
359
|
+
|
360
|
+
```
|
361
|
+
tonietoolbox my_tonie.taf --upload https://teddycloud.example.com --include-artwork
|
362
|
+
```
|
363
|
+
|
364
|
+
This will:
|
365
|
+
1. Look for cover images (like "cover.jpg", "artwork.png", etc.) in the source directory
|
366
|
+
2. If no cover image is found, attempt to extract embedded artwork from the audio files
|
367
|
+
3. Upload the artwork to the "/custom_img" directory on the TeddyCloud server
|
368
|
+
4. The artwork will be uploaded with the same filename as the Tonie file for easier association
|
369
|
+
|
370
|
+
#### Recursive processing with uploads:
|
371
|
+
|
372
|
+
```
|
373
|
+
tonietoolbox --recursive "Music/Albums" --upload https://teddycloud.example.com --include-artwork
|
374
|
+
```
|
375
|
+
|
376
|
+
This will process all folders recursively, create TAF files, and upload both the TAF files and their cover artwork to the TeddyCloud server.
|
377
|
+
|
378
|
+
#### Upload with SSL certificate verification disabled:
|
379
|
+
|
380
|
+
```
|
381
|
+
tonietoolbox my_tonie.taf --upload https://teddycloud.example.com --ignore-ssl-verify
|
382
|
+
```
|
383
|
+
|
384
|
+
Use this option if the TeddyCloud server uses a self-signed certificate.
|
385
|
+
|
301
386
|
## Technical Details
|
302
387
|
|
303
388
|
### TAF (Tonie Audio Format) File Structure
|
@@ -376,4 +461,16 @@ This project is inspired by and builds upon the work of other Tonie-related open
|
|
376
461
|
|
377
462
|
## Contributing
|
378
463
|
|
379
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
464
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
465
|
+
|
466
|
+
## Legal Notice
|
467
|
+
|
468
|
+
This project is an independent, community-driven effort created for educational and personal use purposes.
|
469
|
+
|
470
|
+
- tonies®, toniebox®, Hörfigur® are registered trademarks of [tonies GmbH](https://tonies.com).
|
471
|
+
- This project is not affiliated with, endorsed by, or connected to tonies GmbH in any way.
|
472
|
+
- TonieToolbox is provided "as is" without warranty of any kind, either express or implied.
|
473
|
+
- Users are responsible for ensuring their usage complies with all applicable copyright and intellectual property laws.
|
474
|
+
- This tool is intended for personal use with legally owned content only.
|
475
|
+
|
476
|
+
By using TonieToolbox, you acknowledge that the authors of this software take no responsibility for any potential misuse or any damages that might result from the use of this software.
|
@@ -18,13 +18,40 @@ from .filename_generator import guess_output_filename
|
|
18
18
|
from .version_handler import check_for_updates, clear_version_cache
|
19
19
|
from .recursive_processor import process_recursive_folders
|
20
20
|
from .media_tags import is_available as is_media_tags_available, ensure_mutagen
|
21
|
+
from .teddycloud import upload_to_teddycloud, get_tags_from_teddycloud, get_file_paths
|
21
22
|
|
22
23
|
def main():
|
23
24
|
"""Entry point for the TonieToolbox application."""
|
24
25
|
parser = argparse.ArgumentParser(description='Create Tonie compatible file from Ogg opus file(s).')
|
25
26
|
parser.add_argument('-v', '--version', action='version', version=f'TonieToolbox {__version__}',
|
26
27
|
help='show program version and exit')
|
27
|
-
|
28
|
+
|
29
|
+
# TeddyCloud options first to check for existence before requiring SOURCE
|
30
|
+
teddycloud_group = parser.add_argument_group('TeddyCloud Options')
|
31
|
+
teddycloud_group.add_argument('--upload', metavar='URL', action='store',
|
32
|
+
help='Upload to TeddyCloud instance (e.g., https://teddycloud.example.com). Supports .taf, .jpg, .jpeg, .png files.')
|
33
|
+
teddycloud_group.add_argument('--include-artwork', action='store_true',
|
34
|
+
help='Upload cover artwork image alongside the Tonie file when using --upload')
|
35
|
+
teddycloud_group.add_argument('--get-tags', action='store', metavar='URL',
|
36
|
+
help='Get available tags from TeddyCloud instance')
|
37
|
+
teddycloud_group.add_argument('--ignore-ssl-verify', action='store_true',
|
38
|
+
help='Ignore SSL certificate verification (for self-signed certificates)')
|
39
|
+
teddycloud_group.add_argument('--special-folder', action='store', metavar='FOLDER',
|
40
|
+
help='Special folder to upload to (currently only "library" is supported)', default='library')
|
41
|
+
teddycloud_group.add_argument('--path', action='store', metavar='PATH',
|
42
|
+
help='Path where to write the file on TeddyCloud server')
|
43
|
+
teddycloud_group.add_argument('--show-progress', action='store_true', default=True,
|
44
|
+
help='Show progress bar during file upload (default: enabled)')
|
45
|
+
teddycloud_group.add_argument('--connection-timeout', type=int, metavar='SECONDS', default=10,
|
46
|
+
help='Connection timeout in seconds (default: 10)')
|
47
|
+
teddycloud_group.add_argument('--read-timeout', type=int, metavar='SECONDS', default=300,
|
48
|
+
help='Read timeout in seconds (default: 300)')
|
49
|
+
teddycloud_group.add_argument('--max-retries', type=int, metavar='RETRIES', default=3,
|
50
|
+
help='Maximum number of retry attempts (default: 3)')
|
51
|
+
teddycloud_group.add_argument('--retry-delay', type=int, metavar='SECONDS', default=5,
|
52
|
+
help='Delay between retry attempts in seconds (default: 5)')
|
53
|
+
|
54
|
+
parser.add_argument('input_filename', metavar='SOURCE', type=str, nargs='?',
|
28
55
|
help='input file or directory or a file list (.lst)')
|
29
56
|
parser.add_argument('output_filename', metavar='TARGET', nargs='?', type=str,
|
30
57
|
help='the output file name (default: ---ID---)')
|
@@ -79,6 +106,11 @@ def main():
|
|
79
106
|
log_level_group.add_argument('-Q', '--silent', action='store_true', help='Show only errors')
|
80
107
|
|
81
108
|
args = parser.parse_args()
|
109
|
+
|
110
|
+
# Validate that input_filename is provided if not using --get-tags or --upload-existing
|
111
|
+
if args.input_filename is None and not (args.get_tags or args.upload):
|
112
|
+
parser.error("the following arguments are required: SOURCE")
|
113
|
+
|
82
114
|
if args.trace:
|
83
115
|
from .logger import TRACE
|
84
116
|
log_level = TRACE
|
@@ -90,11 +122,12 @@ def main():
|
|
90
122
|
log_level = logging.ERROR
|
91
123
|
else:
|
92
124
|
log_level = logging.INFO
|
93
|
-
|
125
|
+
|
94
126
|
setup_logging(log_level)
|
95
127
|
logger = get_logger('main')
|
96
128
|
logger.debug("Starting TonieToolbox v%s with log level: %s", __version__, logging.getLevelName(log_level))
|
97
129
|
|
130
|
+
|
98
131
|
if args.clear_version_cache:
|
99
132
|
if clear_version_cache():
|
100
133
|
logger.info("Version cache cleared successfully")
|
@@ -111,6 +144,59 @@ def main():
|
|
111
144
|
if not is_latest and not update_confirmed and not (args.silent or args.quiet):
|
112
145
|
logger.info("Update available but user chose to continue without updating.")
|
113
146
|
|
147
|
+
# Handle get-tags from TeddyCloud if requested
|
148
|
+
if args.get_tags:
|
149
|
+
teddycloud_url = args.get_tags
|
150
|
+
success = get_tags_from_teddycloud(teddycloud_url, args.ignore_ssl_verify)
|
151
|
+
sys.exit(0 if success else 1)
|
152
|
+
|
153
|
+
# Handle upload to TeddyCloud if requested
|
154
|
+
if args.upload:
|
155
|
+
teddycloud_url = args.upload
|
156
|
+
|
157
|
+
if not args.input_filename:
|
158
|
+
logger.error("Missing input file for --upload. Provide a file path as SOURCE argument.")
|
159
|
+
sys.exit(1)
|
160
|
+
|
161
|
+
# Check if the input file is already a .taf file or an image file
|
162
|
+
if os.path.exists(args.input_filename) and (args.input_filename.lower().endswith('.taf') or
|
163
|
+
args.input_filename.lower().endswith(('.jpg', '.jpeg', '.png'))):
|
164
|
+
# Direct upload of existing TAF or image file
|
165
|
+
# Use get_file_paths to handle Windows backslashes and resolve the paths correctly
|
166
|
+
file_paths = get_file_paths(args.input_filename)
|
167
|
+
|
168
|
+
if not file_paths:
|
169
|
+
logger.error("No files found for pattern %s", args.input_filename)
|
170
|
+
sys.exit(1)
|
171
|
+
|
172
|
+
logger.info("Found %d file(s) to upload to TeddyCloud %s", len(file_paths), teddycloud_url)
|
173
|
+
|
174
|
+
for file_path in file_paths:
|
175
|
+
# Only upload supported file types
|
176
|
+
if not file_path.lower().endswith(('.taf', '.jpg', '.jpeg', '.png')):
|
177
|
+
logger.warning("Skipping unsupported file type: %s", file_path)
|
178
|
+
continue
|
179
|
+
|
180
|
+
logger.info("Uploading %s to TeddyCloud %s", file_path, teddycloud_url)
|
181
|
+
upload_success = upload_to_teddycloud(
|
182
|
+
file_path, teddycloud_url, args.ignore_ssl_verify,
|
183
|
+
args.special_folder, args.path, args.show_progress,
|
184
|
+
args.connection_timeout, args.read_timeout,
|
185
|
+
args.max_retries, args.retry_delay
|
186
|
+
)
|
187
|
+
|
188
|
+
if not upload_success:
|
189
|
+
logger.error("Failed to upload %s to TeddyCloud", file_path)
|
190
|
+
sys.exit(1)
|
191
|
+
else:
|
192
|
+
logger.info("Successfully uploaded %s to TeddyCloud", file_path)
|
193
|
+
|
194
|
+
sys.exit(0)
|
195
|
+
|
196
|
+
# If we get here, it's not a TAF or image file, so continue with normal processing
|
197
|
+
# which will convert the input files and upload the result later
|
198
|
+
pass
|
199
|
+
|
114
200
|
ffmpeg_binary = args.ffmpeg
|
115
201
|
if ffmpeg_binary is None:
|
116
202
|
ffmpeg_binary = get_ffmpeg_binary(args.auto_download)
|
@@ -156,6 +242,7 @@ def main():
|
|
156
242
|
os.makedirs(output_dir, exist_ok=True)
|
157
243
|
logger.debug("Created output directory: %s", output_dir)
|
158
244
|
|
245
|
+
created_files = []
|
159
246
|
for task_index, (output_name, folder_path, audio_files) in enumerate(process_tasks):
|
160
247
|
if args.output_to_source:
|
161
248
|
task_out_filename = os.path.join(folder_path, f"{output_name}.taf")
|
@@ -169,8 +256,102 @@ def main():
|
|
169
256
|
args.bitrate, not args.cbr, ffmpeg_binary, opus_binary, args.keep_temp,
|
170
257
|
args.auto_download, not args.use_legacy_tags)
|
171
258
|
logger.info("Successfully created Tonie file: %s", task_out_filename)
|
259
|
+
created_files.append(task_out_filename)
|
172
260
|
|
173
261
|
logger.info("Recursive processing completed. Created %d Tonie files.", len(process_tasks))
|
262
|
+
|
263
|
+
# Handle upload to TeddyCloud if requested
|
264
|
+
if args.upload and created_files:
|
265
|
+
teddycloud_url = args.upload
|
266
|
+
|
267
|
+
for taf_file in created_files:
|
268
|
+
upload_success = upload_to_teddycloud(
|
269
|
+
taf_file, teddycloud_url, args.ignore_ssl_verify,
|
270
|
+
args.special_folder, args.path, args.show_progress,
|
271
|
+
args.connection_timeout, args.read_timeout,
|
272
|
+
args.max_retries, args.retry_delay
|
273
|
+
)
|
274
|
+
|
275
|
+
if not upload_success:
|
276
|
+
logger.error("Failed to upload %s to TeddyCloud", taf_file)
|
277
|
+
else:
|
278
|
+
logger.info("Successfully uploaded %s to TeddyCloud", taf_file)
|
279
|
+
|
280
|
+
# Handle artwork upload if requested
|
281
|
+
if args.include_artwork:
|
282
|
+
# Extract folder path from the current task
|
283
|
+
folder_path = os.path.dirname(taf_file)
|
284
|
+
taf_file_basename = os.path.basename(taf_file)
|
285
|
+
taf_name = os.path.splitext(taf_file_basename)[0] # Get name without extension
|
286
|
+
logger.info("Looking for artwork for %s", folder_path)
|
287
|
+
|
288
|
+
# Try to find cover image in the folder
|
289
|
+
from .media_tags import find_cover_image
|
290
|
+
artwork_path = find_cover_image(folder_path)
|
291
|
+
temp_artwork = None
|
292
|
+
|
293
|
+
# If no cover image found, try to extract it from one of the audio files
|
294
|
+
if not artwork_path:
|
295
|
+
# Get current task's audio files
|
296
|
+
for task_name, task_folder, task_files in process_tasks:
|
297
|
+
if task_folder == folder_path or os.path.normpath(task_folder) == os.path.normpath(folder_path):
|
298
|
+
if task_files and len(task_files) > 0:
|
299
|
+
# Try to extract from first file
|
300
|
+
from .media_tags import extract_artwork, ensure_mutagen
|
301
|
+
if ensure_mutagen(auto_install=args.auto_download):
|
302
|
+
temp_artwork = extract_artwork(task_files[0])
|
303
|
+
if temp_artwork:
|
304
|
+
artwork_path = temp_artwork
|
305
|
+
break
|
306
|
+
|
307
|
+
if artwork_path:
|
308
|
+
logger.info("Found artwork for %s: %s", folder_path, artwork_path)
|
309
|
+
artwork_upload_path = "/custom_img"
|
310
|
+
artwork_ext = os.path.splitext(artwork_path)[1]
|
311
|
+
|
312
|
+
# Create a temporary copy with the same name as the taf file
|
313
|
+
import shutil
|
314
|
+
renamed_artwork_path = None
|
315
|
+
try:
|
316
|
+
renamed_artwork_path = os.path.join(os.path.dirname(artwork_path),
|
317
|
+
f"{taf_name}{artwork_ext}")
|
318
|
+
|
319
|
+
if renamed_artwork_path != artwork_path:
|
320
|
+
shutil.copy2(artwork_path, renamed_artwork_path)
|
321
|
+
logger.debug("Created renamed artwork copy: %s", renamed_artwork_path)
|
322
|
+
|
323
|
+
logger.info("Uploading artwork to path: %s as %s%s",
|
324
|
+
artwork_upload_path, taf_name, artwork_ext)
|
325
|
+
|
326
|
+
artwork_upload_success = upload_to_teddycloud(
|
327
|
+
renamed_artwork_path, teddycloud_url, args.ignore_ssl_verify,
|
328
|
+
args.special_folder, artwork_upload_path, args.show_progress,
|
329
|
+
args.connection_timeout, args.read_timeout,
|
330
|
+
args.max_retries, args.retry_delay
|
331
|
+
)
|
332
|
+
|
333
|
+
if artwork_upload_success:
|
334
|
+
logger.info("Successfully uploaded artwork for %s", folder_path)
|
335
|
+
else:
|
336
|
+
logger.warning("Failed to upload artwork for %s", folder_path)
|
337
|
+
|
338
|
+
if renamed_artwork_path != artwork_path and os.path.exists(renamed_artwork_path):
|
339
|
+
try:
|
340
|
+
os.unlink(renamed_artwork_path)
|
341
|
+
logger.debug("Removed temporary renamed artwork file: %s", renamed_artwork_path)
|
342
|
+
except Exception as e:
|
343
|
+
logger.debug("Failed to remove temporary renamed artwork file: %s", e)
|
344
|
+
|
345
|
+
if temp_artwork and os.path.exists(temp_artwork) and temp_artwork != renamed_artwork_path:
|
346
|
+
try:
|
347
|
+
os.unlink(temp_artwork)
|
348
|
+
logger.debug("Removed temporary artwork file: %s", temp_artwork)
|
349
|
+
except Exception as e:
|
350
|
+
logger.debug("Failed to remove temporary artwork file: %s", e)
|
351
|
+
except Exception as e:
|
352
|
+
logger.error("Error during artwork renaming or upload: %s", e)
|
353
|
+
else:
|
354
|
+
logger.warning("No artwork found for %s", folder_path)
|
174
355
|
sys.exit(0)
|
175
356
|
|
176
357
|
# Handle directory or file input
|
@@ -214,7 +395,6 @@ def main():
|
|
214
395
|
print(f"{tag_name}: {tag_value}")
|
215
396
|
else:
|
216
397
|
print(f"\nFile {file_index + 1}: {os.path.basename(file_path)} - No tags found")
|
217
|
-
|
218
398
|
sys.exit(0)
|
219
399
|
|
220
400
|
# Use media tags for file naming if requested
|
@@ -324,13 +504,115 @@ def main():
|
|
324
504
|
|
325
505
|
if not out_filename.lower().endswith('.taf'):
|
326
506
|
out_filename += '.taf'
|
327
|
-
|
507
|
+
|
328
508
|
logger.info("Creating Tonie file: %s with %d input file(s)", out_filename, len(files))
|
329
509
|
create_tonie_file(out_filename, files, args.no_tonie_header, args.user_timestamp,
|
330
510
|
args.bitrate, not args.cbr, ffmpeg_binary, opus_binary, args.keep_temp,
|
331
511
|
args.auto_download, not args.use_legacy_tags)
|
332
512
|
logger.info("Successfully created Tonie file: %s", out_filename)
|
333
|
-
|
513
|
+
|
514
|
+
# Handle upload to TeddyCloud if requested
|
515
|
+
if args.upload:
|
516
|
+
teddycloud_url = args.upload
|
517
|
+
|
518
|
+
upload_success = upload_to_teddycloud(
|
519
|
+
out_filename, teddycloud_url, args.ignore_ssl_verify,
|
520
|
+
args.special_folder, args.path, args.show_progress,
|
521
|
+
args.connection_timeout, args.read_timeout,
|
522
|
+
args.max_retries, args.retry_delay
|
523
|
+
)
|
524
|
+
if not upload_success:
|
525
|
+
logger.error("Failed to upload %s to TeddyCloud", out_filename)
|
526
|
+
sys.exit(1)
|
527
|
+
else:
|
528
|
+
logger.info("Successfully uploaded %s to TeddyCloud", out_filename)
|
529
|
+
|
530
|
+
# Handle artwork upload if requested
|
531
|
+
if args.include_artwork:
|
532
|
+
logger.info("Looking for artwork to upload alongside the Tonie file")
|
533
|
+
artwork_path = None
|
534
|
+
|
535
|
+
# Try to find a cover image in the source directory first
|
536
|
+
source_dir = os.path.dirname(files[0]) if files else None
|
537
|
+
if source_dir:
|
538
|
+
from .media_tags import find_cover_image
|
539
|
+
artwork_path = find_cover_image(source_dir)
|
540
|
+
|
541
|
+
# If no cover in source directory, try to extract it from audio file
|
542
|
+
if not artwork_path and len(files) > 0:
|
543
|
+
from .media_tags import extract_artwork, ensure_mutagen
|
544
|
+
|
545
|
+
# Make sure mutagen is available for artwork extraction
|
546
|
+
if ensure_mutagen(auto_install=args.auto_download):
|
547
|
+
# Try to extract artwork from the first file
|
548
|
+
temp_artwork = extract_artwork(files[0])
|
549
|
+
if temp_artwork:
|
550
|
+
artwork_path = temp_artwork
|
551
|
+
# Note: this creates a temporary file that will be deleted after upload
|
552
|
+
|
553
|
+
# Upload the artwork if found
|
554
|
+
if artwork_path:
|
555
|
+
logger.info("Found artwork: %s", artwork_path)
|
556
|
+
|
557
|
+
# Create artwork upload path - keep same path but use "custom_img" folder
|
558
|
+
artwork_upload_path = args.path
|
559
|
+
if not artwork_upload_path:
|
560
|
+
artwork_upload_path = "/custom_img"
|
561
|
+
elif not artwork_upload_path.startswith("/custom_img"):
|
562
|
+
# Make sure we're using the custom_img folder
|
563
|
+
if artwork_upload_path.startswith("/"):
|
564
|
+
artwork_upload_path = "/custom_img" + artwork_upload_path
|
565
|
+
else:
|
566
|
+
artwork_upload_path = "/custom_img/" + artwork_upload_path
|
567
|
+
|
568
|
+
# Get the original artwork file extension
|
569
|
+
artwork_ext = os.path.splitext(artwork_path)[1]
|
570
|
+
|
571
|
+
# Create a temporary copy with the same name as the taf file
|
572
|
+
import shutil
|
573
|
+
renamed_artwork_path = None
|
574
|
+
try:
|
575
|
+
renamed_artwork_path = os.path.join(os.path.dirname(artwork_path),
|
576
|
+
f"{os.path.splitext(os.path.basename(out_filename))[0]}{artwork_ext}")
|
577
|
+
|
578
|
+
if renamed_artwork_path != artwork_path:
|
579
|
+
shutil.copy2(artwork_path, renamed_artwork_path)
|
580
|
+
logger.debug("Created renamed artwork copy: %s", renamed_artwork_path)
|
581
|
+
|
582
|
+
logger.info("Uploading artwork to path: %s as %s%s",
|
583
|
+
artwork_upload_path, os.path.splitext(os.path.basename(out_filename))[0], artwork_ext)
|
584
|
+
|
585
|
+
artwork_upload_success = upload_to_teddycloud(
|
586
|
+
renamed_artwork_path, teddycloud_url, args.ignore_ssl_verify,
|
587
|
+
args.special_folder, artwork_upload_path, args.show_progress,
|
588
|
+
args.connection_timeout, args.read_timeout,
|
589
|
+
args.max_retries, args.retry_delay
|
590
|
+
)
|
591
|
+
|
592
|
+
if artwork_upload_success:
|
593
|
+
logger.info("Successfully uploaded artwork")
|
594
|
+
else:
|
595
|
+
logger.warning("Failed to upload artwork")
|
596
|
+
|
597
|
+
# Clean up temporary renamed file
|
598
|
+
if renamed_artwork_path != artwork_path and os.path.exists(renamed_artwork_path):
|
599
|
+
try:
|
600
|
+
os.unlink(renamed_artwork_path)
|
601
|
+
logger.debug("Removed temporary renamed artwork file: %s", renamed_artwork_path)
|
602
|
+
except Exception as e:
|
603
|
+
logger.debug("Failed to remove temporary renamed artwork file: %s", e)
|
604
|
+
|
605
|
+
# Clean up temporary extracted artwork file if needed
|
606
|
+
if temp_artwork and os.path.exists(temp_artwork) and temp_artwork != renamed_artwork_path:
|
607
|
+
try:
|
608
|
+
os.unlink(temp_artwork)
|
609
|
+
logger.debug("Removed temporary artwork file: %s", temp_artwork)
|
610
|
+
except Exception as e:
|
611
|
+
logger.debug("Failed to remove temporary artwork file: %s", e)
|
612
|
+
except Exception as e:
|
613
|
+
logger.error("Error during artwork renaming or upload: %s", e)
|
614
|
+
else:
|
615
|
+
logger.warning("No artwork found to upload")
|
334
616
|
|
335
617
|
if __name__ == "__main__":
|
336
618
|
main()
|