#!/usr/bin/env python3 """ IPTV Enhanced Country Detection - Updated Version Uses 3-point analysis: Channel Name + EPG ID + Logo URL Then filters to keep only legitimate countries """ import os import shutil from datetime import datetime from pathlib import Path # Ensure correct directory script_dir = Path(__file__).parent root_dir = script_dir.parent os.chdir(root_dir) def detect_country_from_channel_content(channel_name, epg_id="", logo_url="", stream_url=""): """ Enhanced country detection using 3-point analysis Priority: EPG ID > Logo URL > Channel Name > Stream URL """ # Combine all text for analysis all_text = f"{channel_name.lower()} {epg_id.lower()} {logo_url.lower()} {stream_url.lower()}" # STEP 1: Check for streaming services first (these go to Uncategorized) streaming_services = [ "plex", "pluto", "tubi", "samsung", "xumo", "stirr", "crackle", "imdb tv", "daddylive", "drew247", "aixmedia", "moveonjoy", "drewlive24", "udptv", "a1xs.vip", "zekonew", "forcedtoplay", "cdn1host", "tvpass.org", "jmp2.uk/plu-", "provider-static.plex.tv", "images.pluto.tv" ] for service in streaming_services: if service in all_text: return "Uncategorized" # STEP 2: EPG ID detection (most reliable) epg_patterns = { ".ca": "๐Ÿ‡จ๐Ÿ‡ฆ Canada", ".us": "๐Ÿ‡บ๐Ÿ‡ธ United States", ".uk": "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom", ".ph": "๐Ÿ‡ต๐Ÿ‡ญ Philippines", ".au": "๐Ÿ‡ฆ๐Ÿ‡บ Australia", ".jp": "๐Ÿ‡ฏ๐Ÿ‡ต Japan", ".my": "๐Ÿ‡ฒ๐Ÿ‡พ Malaysia", ".de": "๐Ÿ‡ฉ๐Ÿ‡ช Germany", ".fr": "๐Ÿ‡ซ๐Ÿ‡ท France", ".es": "๐Ÿ‡ช๐Ÿ‡ธ Spain", ".it": "๐Ÿ‡ฎ๐Ÿ‡น Italy", ".br": "๐Ÿ‡ง๐Ÿ‡ท Brazil", ".nl": "๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands" } for domain, country in epg_patterns.items(): if domain in epg_id.lower(): return country # STEP 3: Specific channel fixes (from your channel_processor.txt) channel_lower = channel_name.lower() # Canadian sports channels (TSN series) 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" # CBC News Toronto (Canadian) if "cbc news toronto" in channel_lower: 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" # UK shows/channels if "come dine with me" in channel_lower: return "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom" # Philippines news channels if any(x in channel_lower for x in ["anc global", "anc ph"]): return "๐Ÿ‡ต๐Ÿ‡ญ Philippines" # Japan anime channels if "animax" in channel_lower: return "๐Ÿ‡ฏ๐Ÿ‡ต Japan" # STEP 4: Logo URL analysis logo_patterns = { "๐Ÿ‡จ๐Ÿ‡ฆ Canada": ["/canada/", "/ca/", "canada.", "canadian"], "๐Ÿ‡บ๐Ÿ‡ธ United States": ["/usa/", "/us/", "united-states", "american"], "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom": ["/uk/", "/united-kingdom/", "british", "england"], "๐Ÿ‡ฉ๐Ÿ‡ช Germany": ["/germany/", "/de/", "german", "deutschland"], "๐Ÿ‡ซ๐Ÿ‡ท France": ["/france/", "/fr/", "french", "franรงais"], "๐Ÿ‡ฎ๐Ÿ‡น Italy": ["/italy/", "/it/", "italian", "italiano"], "๐Ÿ‡ช๐Ÿ‡ธ Spain": ["/spain/", "/es/", "spanish", "espaรฑol"], "๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands": ["/netherlands/", "/nl/", "dutch", "nederland"], "๐Ÿ‡ฆ๐Ÿ‡บ Australia": ["/australia/", "/au/", "australian", "aussie"], "๐Ÿ‡ฏ๐Ÿ‡ต Japan": ["/japan/", "/jp/", "japanese", "ๆ—ฅๆœฌ"], "๐Ÿ‡ฐ๐Ÿ‡ท South Korea": ["/korea/", "/kr/", "korean", "ํ•œ๊ตญ"], "๐Ÿ‡ฎ๐Ÿ‡ณ India": ["/india/", "/in/", "indian", "เคญเคพเคฐเคค"], "๐Ÿ‡ง๐Ÿ‡ท Brazil": ["/brazil/", "/br/", "brazilian", "brasil"], "๐Ÿ‡ฒ๐Ÿ‡ฝ Mexico": ["/mexico/", "/mx/", "mexican", "mรฉxico"], "๐Ÿ‡ฆ๐Ÿ‡ท Argentina": ["/argentina/", "/ar/", "argentinian", "argentina"], "๐Ÿ‡ต๐Ÿ‡ญ Philippines": ["/philippines/", "/ph/", "filipino", "pilipinas"] } for country, patterns in logo_patterns.items(): for pattern in patterns: if pattern in logo_url.lower(): return country # STEP 5: Enhanced broadcaster patterns broadcaster_patterns = { "๐Ÿ‡จ๐Ÿ‡ฆ Canada": [ "cbc", "tsn", "ctv", "global", "sportsnet", "citytv", "aptn", "teletoon", "ytv", "discovery canada", "history canada", "slice", "w network", "oln", "hgtv canada", "food network canada", "showcase", "crave", "super channel", "hollywood suite" ], "๐Ÿ‡บ๐Ÿ‡ธ United States": [ "cbs", "nbc", "abc", "fox", "cnn", "espn", "amc", "mtv", "comedy central", "discovery usa", "history usa", "tlc usa", "hgtv usa", "food network usa", "paramount", "nickelodeon usa", "cartoon network usa", "disney usa", "lifetime", "e!", "bravo usa" ], "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom": [ "bbc", "itv", "channel 4", "channel 5", "sky", "dave", "really", "yesterday", "discovery uk", "history uk", "tlc uk", "living", "alibi", "gold", "drama" ], "๐Ÿ‡ฉ๐Ÿ‡ช Germany": [ "ard", "zdf", "rtl", "pro7", "sat.1", "vox", "kabel eins", "super rtl", "rtl2", "discovery germany", "history germany", "tlc germany", "dmax", "sixx", "tele 5" ], "๐Ÿ‡ซ๐Ÿ‡ท France": [ "tf1", "france 2", "france 3", "france 5", "m6", "canal+", "arte", "w9", "tmc", "discovery france", "history france", "tlc france", "planete+", "ushuaia tv" ], "๐Ÿ‡ฎ๐Ÿ‡น Italy": [ "rai", "canale 5", "italia 1", "rete 4", "la7", "tv8", "nove", "20 mediaset", "discovery italia", "history italia", "dmax italia", "real time", "giallo" ], "๐Ÿ‡ช๐Ÿ‡ธ Spain": [ "tve", "la 1", "la 2", "antena 3", "cuatro", "telecinco", "la sexta", "nova", "discovery spain", "history spain", "dmax spain", "mega", "neox", "clan" ], "๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands": [ "npo", "rtl 4", "rtl 5", "rtl 7", "sbs6", "veronica", "net5", "rtl z", "discovery netherlands", "history netherlands", "tlc netherlands" ], "๐Ÿ‡ฆ๐Ÿ‡บ Australia": [ "abc australia", "nine network", "seven network", "ten", "foxtel", "discovery australia", "history australia", "lifestyle" ], "๐Ÿ‡ฏ๐Ÿ‡ต Japan": [ "nhk", "fuji tv", "tbs", "tv asahi", "tv tokyo", "nippon tv", "animax" ], "๐Ÿ‡ฐ๐Ÿ‡ท South Korea": [ "kbs", "mbc", "sbs", "jtbc", "tvn", "ocn" ], "๐Ÿ‡ฎ๐Ÿ‡ณ India": [ "zee", "star plus", "colors", "sony tv", "& tv", "discovery india" ], "๐Ÿ‡ง๐Ÿ‡ท Brazil": [ "globo", "sbt", "record", "band", "discovery brasil" ], "๐Ÿ‡ฒ๐Ÿ‡ฝ Mexico": [ "televisa", "tv azteca", "once tv", "discovery mexico" ], "๐Ÿ‡ฆ๐Ÿ‡ท Argentina": [ "telefe", "canal 13", "america tv", "discovery argentina" ], "๐Ÿ‡ต๐Ÿ‡ญ Philippines": [ "abs-cbn", "gma", "anc", "tv5", "pba rush" ] } for country, keywords in broadcaster_patterns.items(): for keyword in keywords: if keyword in all_text: return country return "Uncategorized" def is_valid_country_group(group_name): """Check if group name is a valid country (not a streaming service)""" valid_countries = [ "๐Ÿ‡บ๐Ÿ‡ธ United States", "๐Ÿ‡จ๐Ÿ‡ฆ Canada", "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom", "๐Ÿ‡ฉ๐Ÿ‡ช Germany", "๐Ÿ‡ซ๐Ÿ‡ท France", "๐Ÿ‡ฎ๐Ÿ‡น Italy", "๐Ÿ‡ช๐Ÿ‡ธ Spain", "๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands", "๐Ÿ‡ง๐Ÿ‡ช Belgium", "๐Ÿ‡ฆ๐Ÿ‡น Austria", "๐Ÿ‡จ๐Ÿ‡ญ Switzerland", "๐Ÿ‡ธ๐Ÿ‡ช Sweden", "๐Ÿ‡ณ๐Ÿ‡ด Norway", "๐Ÿ‡ฉ๐Ÿ‡ฐ Denmark", "๐Ÿ‡ซ๐Ÿ‡ฎ Finland", "๐Ÿ‡ต๐Ÿ‡ฑ Poland", "๐Ÿ‡จ๐Ÿ‡ฟ Czech Republic", "๐Ÿ‡ญ๐Ÿ‡บ Hungary", "๐Ÿ‡ต๐Ÿ‡น Portugal", "๐Ÿ‡ฌ๐Ÿ‡ท Greece", "๐Ÿ‡ท๐Ÿ‡ด Romania", "๐Ÿ‡ง๐Ÿ‡ฌ Bulgaria", "๐Ÿ‡ญ๐Ÿ‡ท Croatia", "๐Ÿ‡ท๐Ÿ‡ธ Serbia", "๐Ÿ‡ฆ๐Ÿ‡บ Australia", "๐Ÿ‡ฏ๐Ÿ‡ต Japan", "๐Ÿ‡ฐ๐Ÿ‡ท South Korea", "๐Ÿ‡ฎ๐Ÿ‡ณ India", "๐Ÿ‡จ๐Ÿ‡ณ China", "๐Ÿ‡ง๐Ÿ‡ท Brazil", "๐Ÿ‡ฒ๐Ÿ‡ฝ Mexico", "๐Ÿ‡ฆ๐Ÿ‡ท Argentina", "๐Ÿ‡จ๐Ÿ‡ฑ Chile", "๐Ÿ‡จ๐Ÿ‡ด Colombia", "๐Ÿ‡ท๐Ÿ‡บ Russia", "๐Ÿ‡น๐Ÿ‡ท Turkey", "๐Ÿ‡ธ๐Ÿ‡ฆ Saudi Arabia", "๐Ÿ‡ฆ๐Ÿ‡ช UAE", "๐Ÿ‡ช๐Ÿ‡ฌ Egypt", "๐Ÿ‡ฟ๐Ÿ‡ฆ South Africa", "๐Ÿ‡ณ๐Ÿ‡ฌ Nigeria", "๐Ÿ‡ฐ๐Ÿ‡ช Kenya", "๐Ÿ‡ฎ๐Ÿ‡ฑ Israel", "๐Ÿ‡น๐Ÿ‡ญ Thailand", "๐Ÿ‡ป๐Ÿ‡ณ Vietnam", "๐Ÿ‡ต๐Ÿ‡ญ Philippines", "๐Ÿ‡ฎ๐Ÿ‡ฉ Indonesia", "๐Ÿ‡ฒ๐Ÿ‡พ Malaysia", "๐Ÿ‡ธ๐Ÿ‡ฌ Singapore" ] return group_name in valid_countries def load_channels(): """Load channels from channels.txt.""" if not os.path.exists('channels.txt'): print("โŒ No channels.txt found") return [] try: with open('channels.txt', 'r', encoding='utf-8') as f: content = f.read() channels = [] for block in content.split('\n\n'): if not block.strip(): continue channel_data = {} for line in block.strip().split('\n'): if '=' in line: key, value = line.split('=', 1) channel_data[key.strip()] = value.strip() if channel_data.get('Stream name'): channels.append(channel_data) print(f"โœ… Loaded {len(channels)} channels") return channels except Exception as e: print(f"โŒ Error loading channels: {e}") return [] def reorganize_channels(channels): """Enhanced reorganization with 3-point analysis.""" print("๐Ÿ” Enhanced Country Detection with 3-Point Analysis") print("๐Ÿ“Š Analyzing: Channel Name + EPG ID + Logo URL") print("-" * 60) changes = 0 stats = { 'country_detected': 0, 'sent_to_uncategorized': 0, 'kept_existing_country': 0 } country_counts = {} 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', '') # Detect country using enhanced 3-point analysis detected_country = detect_country_from_channel_content(stream_name, epg_id, logo, stream_url) # Decide final group if is_valid_country_group(old_group) and detected_country != "Uncategorized": # Keep existing valid country final_group = old_group stats['kept_existing_country'] += 1 elif detected_country != "Uncategorized": # Use detected country final_group = detected_country stats['country_detected'] += 1 if old_group != detected_country: print(f"๐Ÿ” Fixed: '{stream_name}' {old_group} โ†’ {detected_country}") changes += 1 else: # Send to Uncategorized final_group = "Uncategorized" stats['sent_to_uncategorized'] += 1 if old_group != "Uncategorized": print(f"๐Ÿ“ฑ Platform: '{stream_name}' โ†’ Uncategorized") changes += 1 channel['Group'] = final_group country_counts[final_group] = country_counts.get(final_group, 0) + 1 print(f"\n๐Ÿ“Š PROCESSING RESULTS:") print(f"โœ… Changes made: {changes}") print(f"๐Ÿ” Country detected: {stats['country_detected']}") print(f"โœ… Kept existing countries: {stats['kept_existing_country']}") print(f"๐Ÿ“ฑ Sent to Uncategorized: {stats['sent_to_uncategorized']}") print(f"\n๐ŸŒ FINAL GROUP DISTRIBUTION:") sorted_countries = sorted(country_counts.items(), key=lambda x: (x[0] == "Uncategorized", -x[1])) for country, count in sorted_countries: print(f" {country}: {count} channels") return channels def save_channels(channels): """Save channels to file.""" # Backup if os.path.exists('channels.txt'): backup = f"channels_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt" shutil.copy2('channels.txt', backup) print(f"๐Ÿ“‹ Backup: {backup}") 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") return True except Exception as e: print(f"โŒ Save error: {e}") return False def generate_m3u(channels): """Generate M3U playlist.""" try: with open('playlist.m3u', 'w', encoding='utf-8') as f: f.write('#EXTM3U\n') for channel in channels: 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 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',{name}\n{url}\n') print("โœ… Generated playlist.m3u") return True except Exception as e: print(f"โŒ M3U error: {e}") return False def main(): """Main function.""" print("๐ŸŽฏ Enhanced IPTV Country Detection - 3-Point Analysis") print("=" * 70) print("๐Ÿ” Analyzes: Channel Name + EPG ID + Logo URL") print("๐ŸŽฏ Filters: Only countries remain, streaming services โ†’ Uncategorized") print("=" * 70) channels = load_channels() if not channels: return False # Enhanced reorganization channels = reorganize_channels(channels) # Sort: Countries first (alphabetically), then Uncategorized last channels.sort(key=lambda x: ( "zzz" if x.get('Group') == "Uncategorized" else x.get('Group', ''), x.get('Stream name', '') )) # Save and generate if not save_channels(channels): return False if not generate_m3u(channels): return False # Clear import try: with open('bulk_import.m3u', 'w', encoding='utf-8') as f: f.write('#EXTM3U\n') print("๐Ÿงน Cleared import file") except: pass print("\n๐ŸŽ‰ ENHANCED PROCESSING COMPLETE!") print("โœ… 3-point analysis applied to all channels") print("โœ… Countries detected from EPG ID, Logo URL, and Channel Names") print("โœ… Streaming services filtered to Uncategorized") print("โœ… Clean country-organized playlist generated") return True if __name__ == "__main__": success = main() exit(0 if success else 1)