1
0

containerize & enhance filtering
All checks were successful
Build & Push Docker Image / build-and-publish (push) Successful in 14s

This commit is contained in:
2026-02-08 11:58:22 -05:00
parent 25ef2adf71
commit 312c54c5bd
8 changed files with 608 additions and 19 deletions

View File

@@ -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__':