containerize & enhance filtering
All checks were successful
Build & Push Docker Image / build-and-publish (push) Successful in 14s
All checks were successful
Build & Push Docker Image / build-and-publish (push) Successful in 14s
This commit is contained in:
@@ -45,7 +45,8 @@ class EmbyClient:
|
||||
raise Exception(f"HTTP {e.code}: {error_body}")
|
||||
|
||||
def get_live_tv_channels(self) -> List[Dict]:
|
||||
response = self._make_request('/LiveTv/Channels?Type=TV&EnableImages=true')
|
||||
# Request channels with Fields parameter to ensure tags are included
|
||||
response = self._make_request('/LiveTv/Channels?Type=TV&EnableImages=true&Fields=Tags,TagItems')
|
||||
return response.get('Items', [])
|
||||
|
||||
def get_channel_by_id(self, channel_id: str) -> Dict:
|
||||
@@ -92,6 +93,49 @@ class EmbyClient:
|
||||
raise Exception(f"Failed to delete {image_type}: HTTP {e.code} - {error_body}")
|
||||
|
||||
|
||||
def filter_channels_by_tags(channels: List[Dict], tags: Optional[List[str]]) -> List[Dict]:
|
||||
"""Filter channels by tags. Returns all channels if tags is None or empty."""
|
||||
if not tags:
|
||||
return channels
|
||||
|
||||
filtered = []
|
||||
for channel in channels:
|
||||
# Try multiple possible tag field names
|
||||
channel_tags = channel.get('Tags', []) or channel.get('TagItems', [])
|
||||
|
||||
# Extract tag names from the channel
|
||||
tag_names = []
|
||||
if isinstance(channel_tags, list) and channel_tags:
|
||||
if isinstance(channel_tags[0], dict):
|
||||
# TagItems format: list of dicts with 'Name' field
|
||||
tag_names = [tag.get('Name', '') for tag in channel_tags if tag.get('Name')]
|
||||
else:
|
||||
# Tags format: simple list of strings
|
||||
tag_names = channel_tags
|
||||
|
||||
# Check if any requested tag matches this channel's tags
|
||||
if any(tag in tag_names for tag in tags):
|
||||
filtered.append(channel)
|
||||
|
||||
return filtered
|
||||
|
||||
|
||||
def get_all_unique_tags(channels: List[Dict]) -> List[str]:
|
||||
"""Get all unique tags from channels."""
|
||||
tags = set()
|
||||
for channel in channels:
|
||||
# Try multiple possible tag field names
|
||||
channel_tags = channel.get('Tags', []) or channel.get('TagItems', [])
|
||||
if isinstance(channel_tags, list):
|
||||
# If it's a list of dicts with 'Name' field (TagItems format)
|
||||
if channel_tags and isinstance(channel_tags[0], dict):
|
||||
tags.update(tag.get('Name', '') for tag in channel_tags if tag.get('Name'))
|
||||
else:
|
||||
# If it's a simple list of strings (Tags format)
|
||||
tags.update(channel_tags)
|
||||
return sorted(list(tags))
|
||||
|
||||
|
||||
def copy_primary_to_light_logos(client: EmbyClient, channel_id: str, channel_name: str, dry_run: bool = True, force: bool = False) -> bool:
|
||||
try:
|
||||
channel_data = client.get_channel_by_id(channel_id)
|
||||
@@ -172,8 +216,26 @@ def clear_channel_logos(client: EmbyClient, channel_id: str, channel_name: str,
|
||||
return False
|
||||
|
||||
|
||||
def update_channel_logos(client: EmbyClient, dry_run: bool = True, first_only: bool = False, force: bool = False) -> None:
|
||||
def update_channel_logos(client: EmbyClient, dry_run: bool = True, first_only: bool = False, force: bool = False, tags: Optional[List[str]] = None) -> None:
|
||||
channels = client.get_live_tv_channels()
|
||||
|
||||
# Filter by tags if specified
|
||||
if tags:
|
||||
print(f"Filtering channels by tags: {', '.join(tags)}")
|
||||
channels = filter_channels_by_tags(channels, tags)
|
||||
if not channels:
|
||||
print(f"\n⚠️ No channels found with the specified tags")
|
||||
all_tags = get_all_unique_tags(client.get_live_tv_channels())
|
||||
if all_tags:
|
||||
print(f"\nAvailable tags in your channels ({len(all_tags)}):")
|
||||
for tag in all_tags[:20]: # Show first 20 tags
|
||||
print(f" - {tag}")
|
||||
if len(all_tags) > 20:
|
||||
print(f" ... and {len(all_tags) - 20} more")
|
||||
else:
|
||||
print("No tags found in any channels")
|
||||
return
|
||||
|
||||
channels_to_process = [channels[0]] if first_only else channels
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
@@ -208,8 +270,26 @@ def update_channel_logos(client: EmbyClient, dry_run: bool = True, first_only: b
|
||||
print("\n💡 Processed first channel only. Remove --first-only to process all.")
|
||||
|
||||
|
||||
def clear_logos(client: EmbyClient, dry_run: bool = True, first_only: bool = False) -> None:
|
||||
def clear_logos(client: EmbyClient, dry_run: bool = True, first_only: bool = False, tags: Optional[List[str]] = None) -> None:
|
||||
channels = client.get_live_tv_channels()
|
||||
|
||||
# Filter by tags if specified
|
||||
if tags:
|
||||
print(f"Filtering channels by tags: {', '.join(tags)}")
|
||||
channels = filter_channels_by_tags(channels, tags)
|
||||
if not channels:
|
||||
print(f"\n⚠️ No channels found with the specified tags")
|
||||
all_tags = get_all_unique_tags(client.get_live_tv_channels())
|
||||
if all_tags:
|
||||
print(f"\nAvailable tags in your channels ({len(all_tags)}):")
|
||||
for tag in all_tags[:20]: # Show first 20 tags
|
||||
print(f" - {tag}")
|
||||
if len(all_tags) > 20:
|
||||
print(f" ... and {len(all_tags) - 20} more")
|
||||
else:
|
||||
print("No tags found in any channels")
|
||||
return
|
||||
|
||||
channels_to_process = [channels[0]] if first_only else channels
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
@@ -250,10 +330,22 @@ def main():
|
||||
parser.add_argument('--first-only', action='store_true', help='Only process first channel')
|
||||
parser.add_argument('--clear', action='store_true', help='Clear all logos (Primary, LogoLight, LogoLightColor)')
|
||||
parser.add_argument('--force', action='store_true', help='Overwrite existing logos (only applies to copy mode)')
|
||||
parser.add_argument('--non-interactive', action='store_true', help='Skip confirmation prompts (for automated execution)')
|
||||
parser.add_argument('--tags', help='Comma-separated list of tags to filter channels (e.g., "sports,news")')
|
||||
parser.add_argument('--list-tags', action='store_true', help='List all available tags and exit')
|
||||
parser.add_argument('--debug-tags', action='store_true', help='Show tag fields from first channel for debugging')
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
# Parse tags if provided
|
||||
tags = None
|
||||
if args.tags:
|
||||
tags = [tag.strip() for tag in args.tags.split(',') if tag.strip()]
|
||||
if not tags:
|
||||
print("Warning: --tags specified but no valid tags found", file=sys.stderr)
|
||||
tags = None
|
||||
|
||||
client = EmbyClient(args.server, args.api_key)
|
||||
|
||||
|
||||
try:
|
||||
print(f"Connecting to {args.server}...")
|
||||
channels = client.get_live_tv_channels()
|
||||
@@ -261,26 +353,54 @@ def main():
|
||||
except Exception as e:
|
||||
print(f"✗ Connection failed: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Handle --list-tags
|
||||
if args.list_tags:
|
||||
all_tags = get_all_unique_tags(channels)
|
||||
if all_tags:
|
||||
print(f"\nFound {len(all_tags)} unique tags:")
|
||||
for tag in all_tags:
|
||||
print(f" - {tag}")
|
||||
else:
|
||||
print("\nNo tags found in any channels")
|
||||
sys.exit(0)
|
||||
|
||||
# Handle --debug-tags
|
||||
if args.debug_tags:
|
||||
if channels:
|
||||
print("\nFirst channel debug info:")
|
||||
print(f"Channel name: {channels[0].get('Name', 'Unknown')}")
|
||||
print(f"Tags field: {channels[0].get('Tags', 'NOT PRESENT')}")
|
||||
print(f"TagItems field: {channels[0].get('TagItems', 'NOT PRESENT')}")
|
||||
print("\nAll available fields:")
|
||||
for key in sorted(channels[0].keys()):
|
||||
if 'tag' in key.lower():
|
||||
print(f" {key}: {channels[0][key]}")
|
||||
else:
|
||||
print("\nNo channels available for debugging")
|
||||
sys.exit(0)
|
||||
|
||||
if args.clear:
|
||||
if args.execute:
|
||||
target = "first channel" if args.first_only else "all channels"
|
||||
if input(f"\nCLEAR ALL LOGOS from {target}? (yes/no): ").lower() == 'yes':
|
||||
clear_logos(client, dry_run=False, first_only=args.first_only)
|
||||
tag_info = f" with tags [{', '.join(tags)}]" if tags else ""
|
||||
if args.non_interactive or input(f"\nCLEAR ALL LOGOS from {target}{tag_info}? (yes/no): ").lower() == 'yes':
|
||||
clear_logos(client, dry_run=False, first_only=args.first_only, tags=tags)
|
||||
else:
|
||||
print("Cancelled.")
|
||||
else:
|
||||
clear_logos(client, dry_run=True, first_only=args.first_only)
|
||||
clear_logos(client, dry_run=True, first_only=args.first_only, tags=tags)
|
||||
else:
|
||||
if args.execute:
|
||||
target = "first channel" if args.first_only else "all channels"
|
||||
action = "overwrite logos in" if args.force else "modify"
|
||||
if input(f"\n{action.capitalize()} {target}? (yes/no): ").lower() == 'yes':
|
||||
update_channel_logos(client, dry_run=False, first_only=args.first_only, force=args.force)
|
||||
tag_info = f" with tags [{', '.join(tags)}]" if tags else ""
|
||||
if args.non_interactive or input(f"\n{action.capitalize()} {target}{tag_info}? (yes/no): ").lower() == 'yes':
|
||||
update_channel_logos(client, dry_run=False, first_only=args.first_only, force=args.force, tags=tags)
|
||||
else:
|
||||
print("Cancelled.")
|
||||
else:
|
||||
update_channel_logos(client, dry_run=True, first_only=args.first_only, force=args.force)
|
||||
update_channel_logos(client, dry_run=True, first_only=args.first_only, force=args.force, tags=tags)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user