TonieToolbox 0.4.2__py3-none-any.whl → 0.5.0a1__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.
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/python3
2
+ """
3
+ Artwork handling functionality for TonieToolbox.
4
+ """
5
+
6
+ import os
7
+ import tempfile
8
+ import shutil
9
+ from typing import List, Optional, Tuple
10
+
11
+ from .logger import get_logger
12
+ from .teddycloud import TeddyCloudClient
13
+ from .media_tags import extract_artwork, find_cover_image
14
+
15
+
16
+ def upload_artwork(client: TeddyCloudClient, taf_filename, source_path, audio_files) -> Tuple[bool, Optional[str]]:
17
+ """
18
+ Find and upload artwork for a Tonie file.
19
+
20
+ Args:
21
+ client: TeddyCloudClient instance to use for API communication
22
+ taf_filename: The filename of the Tonie file (.taf)
23
+ source_path: Source directory to look for artwork
24
+ audio_files: List of audio files to extract artwork from if needed
25
+ Returns:
26
+ tuple: (success, artwork_url) where success is a boolean and artwork_url is the URL of the uploaded artwork
27
+ """
28
+ logger = get_logger('artwork')
29
+ logger.info("Looking for artwork for Tonie file: %s", taf_filename)
30
+ taf_basename = os.path.basename(taf_filename)
31
+ taf_name = os.path.splitext(taf_basename)[0]
32
+ artwork_path = None
33
+ temp_artwork = None
34
+ artwork_path = find_cover_image(source_path)
35
+ if not artwork_path and audio_files and len(audio_files) > 0:
36
+ logger.info("No cover image found, trying to extract from audio files")
37
+ temp_artwork = extract_artwork(audio_files[0])
38
+ if temp_artwork:
39
+ artwork_path = temp_artwork
40
+ logger.info("Extracted artwork from audio file: %s", temp_artwork)
41
+
42
+ if not artwork_path:
43
+ logger.warning("No artwork found for %s", source_path)
44
+ return False, None
45
+
46
+ logger.info("Found artwork: %s", artwork_path)
47
+ artwork_upload_path = "/custom_img"
48
+ artwork_ext = os.path.splitext(artwork_path)[1]
49
+ renamed_artwork_path = None
50
+ upload_success = False
51
+ artwork_url = None
52
+
53
+ try:
54
+ renamed_artwork_path = os.path.join(os.path.dirname(artwork_path),
55
+ f"{taf_name}{artwork_ext}")
56
+
57
+ if renamed_artwork_path != artwork_path:
58
+ shutil.copy(artwork_path, renamed_artwork_path)
59
+ logger.debug("Created renamed artwork copy: %s", renamed_artwork_path)
60
+
61
+ logger.info("Uploading artwork to path: %s as %s%s",
62
+ artwork_upload_path, taf_name, artwork_ext)
63
+ try:
64
+ response = client.upload_file(
65
+ file_path=renamed_artwork_path,
66
+ destination_path=artwork_upload_path,
67
+ special="library"
68
+ )
69
+ upload_success = response.get('success', False)
70
+
71
+ if not upload_success:
72
+ logger.error("Failed to upload %s to TeddyCloud", renamed_artwork_path)
73
+ else:
74
+ logger.info("Successfully uploaded %s to TeddyCloud", renamed_artwork_path)
75
+ logger.debug("Upload response: %s", response)
76
+ except Exception as e:
77
+ logger.error("Error uploading artwork: %s", e)
78
+ upload_success = False
79
+
80
+ if upload_success:
81
+ if not artwork_upload_path.endswith('/'):
82
+ artwork_upload_path += '/'
83
+ artwork_url = f"{artwork_upload_path}{taf_name}{artwork_ext}"
84
+ logger.debug("Artwork URL: %s", artwork_url)
85
+
86
+ except Exception as e:
87
+ logger.error("Error during artwork handling: %s", e)
88
+ upload_success = False
89
+
90
+ finally:
91
+ if renamed_artwork_path != artwork_path and renamed_artwork_path and os.path.exists(renamed_artwork_path):
92
+ try:
93
+ os.unlink(renamed_artwork_path)
94
+ logger.debug("Removed temporary renamed artwork file: %s", renamed_artwork_path)
95
+ except Exception as e:
96
+ logger.debug("Failed to remove temporary renamed artwork file: %s", e)
97
+ if temp_artwork and os.path.exists(temp_artwork):
98
+ try:
99
+ if temp_artwork.startswith(tempfile.gettempdir()):
100
+ os.unlink(temp_artwork)
101
+ logger.debug("Removed temporary extracted artwork file: %s", temp_artwork)
102
+ except Exception as e:
103
+ logger.debug("Failed to remove temporary artwork file: %s", e)
104
+
105
+ return upload_success, artwork_url
@@ -290,20 +290,26 @@ def get_folder_name_from_metadata(folder_path: str, use_media_tags: bool = False
290
290
  return output_name
291
291
 
292
292
 
293
- def process_recursive_folders(root_path: str, use_media_tags: bool = False,
294
- name_template: str = None) -> List[Tuple[str, str, List[str]]]:
293
+ def process_recursive_folders(root_path, use_media_tags=False, name_template=None):
295
294
  """
296
- Process folders recursively and prepare data for conversion.
295
+ Process folders recursively for audio files to create Tonie files.
297
296
 
298
297
  Args:
299
- root_path: Root directory to start processing from
300
- use_media_tags: Whether to use media tags from audio files for naming
301
- name_template: Optional template for formatting output names using media tags
302
-
298
+ root_path (str): The root path to start processing from
299
+ use_media_tags (bool): Whether to use media tags for naming
300
+ name_template (str): Template for naming files using media tags
301
+
303
302
  Returns:
304
- List of tuples: (output_filename, folder_path, list_of_audio_files)
303
+ list: A list of tuples (output_name, folder_path, audio_files)
305
304
  """
305
+ logger = get_logger("recursive_processor")
306
306
  logger.info("Processing folders recursively: %s", root_path)
307
+ # Make sure the path exists
308
+ if not os.path.exists(root_path):
309
+ logger.error("Path does not exist: %s", root_path)
310
+ return []
311
+
312
+ logger.info("Finding folders with audio files in: %s", root_path)
307
313
 
308
314
  # Get folder info with hierarchy details
309
315
  all_folders = find_audio_folders(root_path)
TonieToolbox/tags.py ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/python3
2
+ """
3
+ TonieToolbox - Tags handling functionality.
4
+ This module provides functionality to retrieve and display tags from a TeddyCloud instance.
5
+ """
6
+ from .logger import get_logger
7
+ from .teddycloud import TeddyCloudClient
8
+ import json
9
+ from typing import Optional, Union
10
+
11
+ logger = get_logger('tags')
12
+
13
+ def get_tags(client: TeddyCloudClient) -> bool:
14
+ """
15
+ Get and display tags from a TeddyCloud instance.
16
+
17
+ Args:
18
+ client: TeddyCloudClient instance to use for API communication
19
+
20
+ Returns:
21
+ True if tags were retrieved successfully, False otherwise
22
+ """
23
+ logger.info("Getting tags from TeddyCloud using provided client")
24
+
25
+ response = client.get_tag_index()
26
+
27
+ if not response:
28
+ logger.error("Failed to retrieve tags from TeddyCloud")
29
+ return False
30
+ if isinstance(response, dict) and 'tags' in response:
31
+ tags = response['tags']
32
+ logger.info("Successfully retrieved %d tags from TeddyCloud", len(tags))
33
+
34
+ print("\nAvailable Tags from TeddyCloud:")
35
+ print("-" * 60)
36
+
37
+ sorted_tags = sorted(tags, key=lambda x: (x.get('type', ''), x.get('uid', '')))
38
+
39
+ for tag in sorted_tags:
40
+ uid = tag.get('uid', 'Unknown UID')
41
+ tag_type = tag.get('type', 'Unknown')
42
+ valid = "✓" if tag.get('valid', False) else "✗"
43
+ series = tag.get('tonieInfo', {}).get('series', '')
44
+ episode = tag.get('tonieInfo', {}).get('episode', '')
45
+ source = tag.get('source', '')
46
+ print(f"UID: {uid} ({tag_type}) - Valid: {valid}")
47
+ if series:
48
+ print(f"Series: {series}")
49
+ if episode:
50
+ print(f"Episode: {episode}")
51
+ if source:
52
+ print(f"Source: {source}")
53
+ tracks = tag.get('tonieInfo', {}).get('tracks', [])
54
+ if tracks:
55
+ print("Tracks:")
56
+ for i, track in enumerate(tracks, 1):
57
+ print(f" {i}. {track}")
58
+ track_seconds = tag.get('trackSeconds', [])
59
+ if track_seconds and len(track_seconds) > 1:
60
+ total_seconds = track_seconds[-1]
61
+ minutes = total_seconds // 60
62
+ seconds = total_seconds % 60
63
+ print(f"Duration: {minutes}:{seconds:02d} ({len(track_seconds)-1} tracks)")
64
+
65
+ print("-" * 60)
66
+ else:
67
+ logger.info("Successfully retrieved tag data from TeddyCloud")
68
+ print("\nTag data from TeddyCloud:")
69
+ print("-" * 60)
70
+ print(json.dumps(response, indent=2))
71
+
72
+ print("-" * 60)
73
+
74
+ return True