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.
Files changed (30) hide show
  1. {tonietoolbox-0.2.3/TonieToolbox.egg-info → tonietoolbox-0.3.0}/PKG-INFO +99 -2
  2. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/README.md +99 -2
  3. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/__init__.py +1 -1
  4. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/__main__.py +287 -5
  5. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/dependency_manager.py +5 -5
  6. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/media_tags.py +167 -1
  7. tonietoolbox-0.3.0/TonieToolbox/teddycloud.py +580 -0
  8. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0/TonieToolbox.egg-info}/PKG-INFO +99 -2
  9. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox.egg-info/SOURCES.txt +1 -0
  10. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/LICENSE.md +0 -0
  11. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/MANIFEST.in +0 -0
  12. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/audio_conversion.py +0 -0
  13. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/constants.py +0 -0
  14. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/filename_generator.py +0 -0
  15. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/logger.py +0 -0
  16. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/ogg_page.py +0 -0
  17. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/opus_packet.py +0 -0
  18. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/recursive_processor.py +0 -0
  19. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/tonie_analysis.py +0 -0
  20. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/tonie_file.py +0 -0
  21. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/tonie_header.proto +0 -0
  22. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/tonie_header_pb2.py +0 -0
  23. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox/version_handler.py +0 -0
  24. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox.egg-info/dependency_links.txt +0 -0
  25. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox.egg-info/entry_points.txt +0 -0
  26. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox.egg-info/requires.txt +0 -0
  27. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/TonieToolbox.egg-info/top_level.txt +0 -0
  28. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/pyproject.toml +0 -0
  29. {tonietoolbox-0.2.3 → tonietoolbox-0.3.0}/setup.cfg +0 -0
  30. {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.2.3
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] [-t TIMESTAMP] [-f FFMPEG] [-o OPUSENC]
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] [-t TIMESTAMP] [-f FFMPEG] [-o OPUSENC]
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.
@@ -2,4 +2,4 @@
2
2
  TonieToolbox - Convert audio files to Tonie box compatible format
3
3
  """
4
4
 
5
- __version__ = '0.2.3'
5
+ __version__ = '0.3.0'
@@ -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
- parser.add_argument('input_filename', metavar='SOURCE', type=str,
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()