#!/usr/bin/env python3 """ IPTV Country + Platform Organizer Groups channels by country first, then platform within country Example: ๐Ÿ‡บ๐Ÿ‡ธ USA, ๐Ÿ‡บ๐Ÿ‡ธ USA - Plex, ๐Ÿ‡บ๐Ÿ‡ธ USA - Pluto, ๐Ÿ‡จ๐Ÿ‡ฆ Canada, ๐Ÿ‡จ๐Ÿ‡ฆ Canada - Plex """ import os import sys import shutil from datetime import datetime from pathlib import Path # FIXED: Ensure we're in the right directory script_dir = Path(__file__).parent root_dir = script_dir.parent os.chdir(root_dir) def detect_country_and_platform(channel_name, epg_id="", logo_url="", stream_url=""): """Enhanced country + platform detection.""" all_text = f"{channel_name.lower().strip()} {epg_id.lower().strip()} {logo_url.lower().strip()} {stream_url.lower().strip()}" channel_lower = channel_name.lower() # STEP 1: Detect the country first country = detect_base_country(channel_name, epg_id, logo_url, stream_url) # STEP 2: Detect platform platform = detect_platform(all_text) # STEP 3: Combine country + platform if platform: return f"{country} - {platform}" else: return country def detect_base_country(channel_name, epg_id="", logo_url="", stream_url=""): """Detect the base country of the channel.""" all_text = f"{channel_name.lower().strip()} {epg_id.lower().strip()} {logo_url.lower().strip()} {stream_url.lower().strip()}" channel_lower = channel_name.lower() # PRIORITY 1: EPG ID suffix detection (most reliable) if ".ca" in epg_id.lower(): return "๐Ÿ‡จ๐Ÿ‡ฆ Canada" elif ".us" in epg_id.lower(): return "๐Ÿ‡บ๐Ÿ‡ธ United States" elif ".uk" in epg_id.lower(): return "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom" elif ".ph" in epg_id.lower(): return "๐Ÿ‡ต๐Ÿ‡ญ Philippines" elif ".au" in epg_id.lower(): return "๐Ÿ‡ฆ๐Ÿ‡บ Australia" elif ".jp" in epg_id.lower(): return "๐Ÿ‡ฏ๐Ÿ‡ต Japan" elif ".my" in epg_id.lower(): return "๐Ÿ‡ฒ๐Ÿ‡พ Malaysia" elif ".de" in epg_id.lower(): return "๐Ÿ‡ฉ๐Ÿ‡ช Germany" elif ".fr" in epg_id.lower(): return "๐Ÿ‡ซ๐Ÿ‡ท France" # PRIORITY 2: Specific channel fixes for misclassified channels # Canadian channels if any(x in channel_lower for x in ["tsn 1", "tsn 2", "tsn 3", "tsn 4", "tsn 5", "tsn1", "tsn2", "tsn3", "tsn4", "tsn5"]): return "๐Ÿ‡จ๐Ÿ‡ฆ Canada" if any(x in channel_lower for x in ["cbc news", "cbc news toronto", "cbc news british columbia"]): return "๐Ÿ‡จ๐Ÿ‡ฆ Canada" if "w network" in channel_lower and "canada" in all_text: return "๐Ÿ‡จ๐Ÿ‡ฆ Canada" # US channels that were misclassified if any(x in channel_lower for x in ["tv land", "tvland", "we tv", "wetv", "all weddings we tv", "cheaters", "cheers", "christmas 365"]): return "๐Ÿ‡บ๐Ÿ‡ธ United States" if any(x in channel_lower for x in ["cbs", "nbc", "abc", "fox news", "cnn", "espn", "discovery channel", "cartoon network"]): return "๐Ÿ‡บ๐Ÿ‡ธ United States" # UK channels if "come dine with me" in channel_lower: return "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom" if any(x in channel_lower for x in ["bbc", "itv", "sky news", "channel 4", "channel 5"]): return "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom" # Philippines channels if any(x in channel_lower for x in ["anc global", "anc ph"]): return "๐Ÿ‡ต๐Ÿ‡ญ Philippines" # Malaysia channels if "malaysia" in channel_lower or "bein sports 1 malaysia" in channel_lower: return "๐Ÿ‡ฒ๐Ÿ‡พ Malaysia" # Japan channels if "animax" in channel_lower: return "๐Ÿ‡ฏ๐Ÿ‡ต Japan" # PRIORITY 3: Special platform handling with country detection # Pluto TV regional detection if "pluto.tv" in all_text or "images.pluto.tv" in all_text or "jmp2.uk/plu-" in all_text: pluto_countries = { "cbc news toronto": "๐Ÿ‡จ๐Ÿ‡ฆ Canada", "cbc news british columbia": "๐Ÿ‡จ๐Ÿ‡ฆ Canada", "come dine with me": "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom" } for channel_pattern, country in pluto_countries.items(): if channel_pattern in channel_lower: return country # Default Pluto TV to US return "๐Ÿ‡บ๐Ÿ‡ธ United States" # Samsung TV Plus regional detection if "samsung" in all_text or "sam-" in stream_url: if "cabc" in epg_id.lower(): # Canadian Samsung channels return "๐Ÿ‡จ๐Ÿ‡ฆ Canada" return "๐Ÿ‡บ๐Ÿ‡ธ United States" # Default Samsung to US # Plex TV (mostly US unless specifically regional) if "plex.tv" in all_text or "provider-static.plex.tv" in all_text: return "๐Ÿ‡บ๐Ÿ‡ธ United States" # PRIORITY 4: Pattern matching by keywords country_patterns = { "๐Ÿ‡บ๐Ÿ‡ธ United States": ["usa", "america", "united states", "c-span", "newsmax", "newsnation"], "๐Ÿ‡จ๐Ÿ‡ฆ Canada": ["canada", "canadian", "ctv", "global", "sportsnet"], "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom": ["uk", "british", "britain", "england"], "๐Ÿ‡ต๐Ÿ‡ญ Philippines": ["philippines", "filipino"], "๐Ÿ‡ฆ๐Ÿ‡บ Australia": ["australia", "australian"], "๐Ÿ‡ฏ๐Ÿ‡ต Japan": ["japan", "japanese", "nhk"], "๐Ÿ‡ฒ๐Ÿ‡พ Malaysia": ["malaysia", "malaysian"], "๐Ÿ‡ฉ๐Ÿ‡ช Germany": ["germany", "german", "deutschland"], "๐Ÿ‡ซ๐Ÿ‡ท France": ["france", "french"], "๐Ÿ‡ช๐Ÿ‡ธ Spain": ["spain", "spanish"], "๐Ÿ‡ฎ๐Ÿ‡น Italy": ["italy", "italian"], "๐Ÿ‡ง๐Ÿ‡ท Brazil": ["brazil", "brazilian"], "๐Ÿ‡ฒ๐Ÿ‡ฝ Mexico": ["mexico", "mexican"], "๐Ÿ‡ท๐Ÿ‡บ Russia": ["russia", "russian"] } for country, keywords in country_patterns.items(): if any(keyword in all_text for keyword in keywords): return country return "๐ŸŒ International" def detect_platform(all_text): """Detect streaming platform.""" # Platform detection patterns if "pluto.tv" in all_text or "images.pluto.tv" in all_text or "jmp2.uk/plu-" in all_text: return "Pluto TV" elif "plex.tv" in all_text or "provider-static.plex.tv" in all_text: return "Plex TV" elif "samsung" in all_text or "sam-" in all_text: return "Samsung TV+" elif "tubi" in all_text: return "Tubi" elif "xumo" in all_text: return "Xumo" elif "crackle" in all_text: return "Crackle" elif "roku" in all_text: return "Roku Channel" elif "youtube" in all_text: return "YouTube" elif "peacock" in all_text: return "Peacock" elif "paramount+" in all_text or "paramount plus" in all_text: return "Paramount+" return None # No platform detected = traditional broadcaster def load_channels(): """Load existing channels from channels.txt.""" channels = [] if not os.path.exists('channels.txt'): print("โŒ No existing channels.txt found") return channels try: with open('channels.txt', 'r', encoding='utf-8') as f: content = f.read() print(f"๐Ÿ“„ channels.txt size: {len(content)} characters") blocks = content.split('\n\n') for block in blocks: if not block.strip(): continue lines = block.strip().split('\n') channel_data = {} for line in lines: if '=' in line: key, value = line.split('=', 1) channel_data[key.strip()] = value.strip() if channel_data and channel_data.get('Stream name'): channels.append(channel_data) print(f"โœ… Loaded {len(channels)} existing channels") except Exception as e: print(f"โŒ Error loading channels: {e}") return channels def reorganize_channels_by_country_platform(channels): """Reorganize channels by country, then platform within country.""" print("๐ŸŒ Reorganizing channels by country + platform...") changes = 0 group_stats = {} for channel in channels: old_group = channel.get('Group', 'Uncategorized') stream_name = channel.get('Stream name', '') epg_id = channel.get('EPG id', '') logo = channel.get('Logo', '') stream_url = channel.get('Stream URL', '') # Apply country + platform detection new_group = detect_country_and_platform(stream_name, epg_id, logo, stream_url) if old_group != new_group: print(f"๐Ÿ”„ Reorg: '{stream_name}' {old_group} โ†’ {new_group}") channel['Group'] = new_group changes += 1 # Count groups group_stats[new_group] = group_stats.get(new_group, 0) + 1 print(f"โœ… Reorganized {changes} channel classifications") # Show organization results print(f"\n๐Ÿ—‚๏ธ NEW ORGANIZATION:") sorted_groups = sorted(group_stats.items(), key=lambda x: (x[0].split(' - ')[0], x[0])) for group, count in sorted_groups: print(f" {group}: {count} channels") return channels def save_channels(channels): """Save channels to channels.txt.""" if os.path.exists('channels.txt'): backup_name = f"channels_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt" shutil.copy2('channels.txt', backup_name) print(f"๐Ÿ“‹ Created backup: {backup_name}") try: with open('channels.txt', 'w', encoding='utf-8') as f: for i, channel in enumerate(channels): if i > 0: f.write("\n\n") f.write(f"Group = {channel.get('Group', 'Uncategorized')}\n") f.write(f"Stream name = {channel.get('Stream name', 'Unknown')}\n") f.write(f"Logo = {channel.get('Logo', '')}\n") f.write(f"EPG id = {channel.get('EPG id', '')}\n") f.write(f"Stream URL = {channel.get('Stream URL', '')}\n") print(f"โœ… Saved {len(channels)} channels to channels.txt") return True except Exception as e: print(f"โŒ Error saving channels: {e}") return False def generate_m3u(channels): """Generate M3U playlist with country + platform organization.""" try: with open('playlist.m3u', 'w', encoding='utf-8') as f: f.write('#EXTM3U\n') valid_channels = 0 group_stats = {} for channel in channels: stream_name = channel.get('Stream name', '') group = channel.get('Group', 'Uncategorized') logo = channel.get('Logo', '') epg_id = channel.get('EPG id', '') url = channel.get('Stream URL', '') if stream_name and url: f.write(f'#EXTINF:-1 group-title="{group}"') if logo: f.write(f' tvg-logo="{logo}"') if epg_id: f.write(f' tvg-id="{epg_id}"') f.write(f',{stream_name}\n') f.write(f'{url}\n') valid_channels += 1 group_stats[group] = group_stats.get(group, 0) + 1 print(f"๐Ÿ“บ Generated playlist.m3u with {valid_channels} channels") # Show organized groups print("๐ŸŒ Organized Groups:") sorted_groups = sorted(group_stats.items(), key=lambda x: (x[0].split(' - ')[0], x[0])) for group, count in sorted_groups[:15]: # Show top 15 print(f" {group}: {count} channels") return True except Exception as e: print(f"โŒ Error generating playlist: {e}") return False def main(): """Main execution function.""" print("๐ŸŒ IPTV Country + Platform Organizer") print("=" * 60) print("Organizing channels: Country first, then platform within country") print("Example: ๐Ÿ‡บ๐Ÿ‡ธ USA, ๐Ÿ‡บ๐Ÿ‡ธ USA - Plex, ๐Ÿ‡จ๐Ÿ‡ฆ Canada, ๐Ÿ‡จ๐Ÿ‡ฆ Canada - Plex") print("=" * 60) # Load existing channels channels = load_channels() if not channels: print("โŒ No channels found to process") return False # Reorganize by country + platform reorganized_channels = reorganize_channels_by_country_platform(channels) # Sort channels: Country first, then platform within country, then channel name print("๐Ÿ“ Sorting channels by country + platform...") reorganized_channels.sort(key=lambda x: ( x.get('Group', '').split(' - ')[0], # Country first x.get('Group', ''), # Then platform within country x.get('Stream name', '') # Then channel name )) # Save reorganized channels if not save_channels(reorganized_channels): return False # Generate playlist if not generate_m3u(reorganized_channels): return False # Clear import file try: with open('bulk_import.m3u', 'w', encoding='utf-8') as f: f.write('#EXTM3U\n# Import processed\n') print("๐Ÿงน Cleared import file") except: pass print("\n๐ŸŽ‰ COUNTRY + PLATFORM ORGANIZATION COMPLETED!") print("โœ… Channels organized by country first, then platform") print("โœ… TSN channels โ†’ Canada") print("โœ… CBC News โ†’ Canada") print("โœ… TV Land โ†’ USA") print("โœ… Plex/Pluto channels organized within their countries") print("โœ… Clean country-based organization achieved!") return True if __name__ == "__main__": success = main() exit(0 if success else 1)