my-private-iptv-m3u/scripts/generate_playlist.py

290 lines
11 KiB
Python
Raw Normal View History

2025-06-27 23:26:06 +02:00
#!/usr/bin/env python3
"""
2025-06-29 03:33:41 +02:00
IPTV Enhanced Country Detection - Simplified No-Hang Version
Uses your existing 3-point analysis but removes network calls that cause hanging
2025-06-27 23:26:06 +02:00
"""
2025-06-27 16:34:52 +02:00
import os
2025-06-29 02:02:34 +02:00
import shutil
2025-06-29 03:33:41 +02:00
import re
2025-06-27 17:36:03 +02:00
from datetime import datetime
2025-06-29 02:06:07 +02:00
from pathlib import Path
2025-06-29 02:47:11 +02:00
# Ensure correct directory
2025-06-29 02:06:07 +02:00
script_dir = Path(__file__).parent
root_dir = script_dir.parent
os.chdir(root_dir)
2025-06-27 16:34:52 +02:00
2025-06-29 03:33:41 +02:00
def is_streaming_platform(all_text):
"""Check if channel is from streaming platform."""
platforms = [
"plex.tv", "pluto.tv", "jmp2.uk/plu-", "sam-", "samsung",
"tubi", "xumo", "roku", "youtube", "daddylive", "drew247tv",
"moveonjoy", "247tv", "livetv"
2025-06-29 03:22:13 +02:00
]
2025-06-29 03:33:41 +02:00
return any(platform in all_text for platform in platforms)
def detect_traditional_broadcaster_country(channel_name, epg_id="", logo_url="", stream_url=""):
"""Enhanced 3-point country detection for traditional broadcasters."""
all_text = f"{channel_name.lower()} {epg_id.lower()} {logo_url.lower()} {stream_url.lower()}"
channel_lower = channel_name.lower()
2025-06-29 03:22:13 +02:00
2025-06-29 03:33:41 +02:00
# Skip streaming platforms entirely
if is_streaming_platform(all_text):
return "Uncategorized"
# PRIORITY 1: EPG ID detection (most reliable)
epg_countries = {
".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",
".mx": "🇲🇽 Mexico", ".ar": "🇦🇷 Argentina"
2025-06-29 02:47:11 +02:00
}
2025-06-29 02:35:11 +02:00
2025-06-29 03:33:41 +02:00
for domain, country in epg_countries.items():
2025-06-29 02:47:11 +02:00
if domain in epg_id.lower():
return country
2025-06-29 02:02:34 +02:00
2025-06-29 03:33:41 +02:00
# PRIORITY 2: Specific channel fixes from your original code
2025-06-29 03:22:13 +02:00
# 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"
2025-06-29 03:33:41 +02:00
# PRIORITY 3: Logo URL detection (your 3-point analysis)
2025-06-29 03:22:13 +02:00
logo_patterns = {
2025-06-29 03:33:41 +02:00
"/canada/": "🇨🇦 Canada", "/ca/": "🇨🇦 Canada", "canada": "🇨🇦 Canada",
"/usa/": "🇺🇸 United States", "/us/": "🇺🇸 United States", "america": "🇺🇸 United States",
"/uk/": "🇬🇧 United Kingdom", "/britain/": "🇬🇧 United Kingdom", "british": "🇬🇧 United Kingdom",
"/germany/": "🇩🇪 Germany", "/de/": "🇩🇪 Germany",
"/france/": "🇫🇷 France", "/fr/": "🇫🇷 France",
"/spain/": "🇪🇸 Spain", "/es/": "🇪🇸 Spain",
"/italy/": "🇮🇹 Italy", "/it/": "🇮🇹 Italy"
2025-06-29 02:02:34 +02:00
}
2025-06-29 03:33:41 +02:00
for pattern, country in logo_patterns.items():
if pattern in logo_url.lower():
return country
2025-06-29 03:22:13 +02:00
2025-06-29 03:33:41 +02:00
# PRIORITY 4: Essential broadcaster patterns
broadcasters = {
"🇨🇦 Canada": ["tsn", "cbc", "ctv", "global", "sportsnet", "w network", "city tv", "aptn"],
"🇺🇸 United States": ["cbs", "nbc", "abc", "fox", "cnn", "espn", "discovery", "hgtv", "mtv", "c-span", "msnbc", "comedy central"],
"🇬🇧 United Kingdom": ["bbc", "itv", "sky", "channel 4", "channel 5", "dave", "e4", "film4"],
"🇵🇭 Philippines": ["abs-cbn", "gma", "anc", "tv5", "pbo", "net 25"],
"🇦🇺 Australia": ["abc australia", "nine network", "seven network", "ten network", "sbs"],
"🇯🇵 Japan": ["nhk", "fuji tv", "animax", "tbs", "tv asahi"],
"🇲🇾 Malaysia": ["tv1", "tv2", "astro", "rtm", "8tv"],
"🇩🇪 Germany": ["ard", "zdf", "rtl", "sat.1", "pro7", "vox"],
"🇫🇷 France": ["tf1", "france 2", "canal+", "m6", "arte"],
"🇪🇸 Spain": ["antena 3", "telecinco", "tve", "la sexta"],
"🇮🇹 Italy": ["rai", "mediaset", "canale 5", "la7"],
"🇧🇷 Brazil": ["globo", "sbt", "record", "band"],
"🇲🇽 Mexico": ["televisa", "tv azteca", "canal 5"],
"🇷🇺 Russia": ["первый", "россия", "нтв", "тнт"]
2025-06-29 03:22:13 +02:00
}
2025-06-29 03:33:41 +02:00
for country, keywords in broadcasters.items():
if any(keyword in all_text for keyword in keywords):
return country
2025-06-29 02:02:34 +02:00
2025-06-29 02:47:11 +02:00
return "Uncategorized"
2025-06-29 02:06:07 +02:00
2025-06-29 02:02:34 +02:00
def load_channels():
2025-06-29 02:47:11 +02:00
"""Load channels from channels.txt."""
2025-06-29 02:02:34 +02:00
if not os.path.exists('channels.txt'):
2025-06-29 02:47:11 +02:00
print("❌ No channels.txt found")
return []
2025-06-28 23:41:12 +02:00
try:
2025-06-29 02:02:34 +02:00
with open('channels.txt', 'r', encoding='utf-8') as f:
2025-06-28 23:41:12 +02:00
content = f.read()
2025-06-29 02:47:11 +02:00
channels = []
for block in content.split('\n\n'):
2025-06-28 23:41:12 +02:00
if not block.strip():
continue
2025-06-29 02:47:11 +02:00
channel_data = {}
for line in block.strip().split('\n'):
2025-06-28 23:41:12 +02:00
if '=' in line:
key, value = line.split('=', 1)
channel_data[key.strip()] = value.strip()
2025-06-29 02:47:11 +02:00
if channel_data.get('Stream name'):
2025-06-28 23:41:12 +02:00
channels.append(channel_data)
2025-06-29 02:47:11 +02:00
print(f"✅ Loaded {len(channels)} channels")
return channels
2025-06-28 23:41:12 +02:00
except Exception as e:
2025-06-29 02:02:34 +02:00
print(f"❌ Error loading channels: {e}")
2025-06-29 02:47:11 +02:00
return []
2025-06-28 23:41:12 +02:00
2025-06-29 02:47:11 +02:00
def reorganize_channels(channels):
2025-06-29 03:33:41 +02:00
"""Reorganize channels by country (traditional only)."""
print("📺 Organizing traditional broadcasters by country...")
2025-06-29 02:02:34 +02:00
changes = 0
2025-06-29 03:33:41 +02:00
stats = {}
2025-06-29 02:02:34 +02:00
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', '')
2025-06-29 02:35:11 +02:00
stream_url = channel.get('Stream URL', '')
2025-06-29 02:02:34 +02:00
2025-06-29 03:33:41 +02:00
new_group = detect_traditional_broadcaster_country(stream_name, epg_id, logo, stream_url)
2025-06-29 02:02:34 +02:00
2025-06-29 03:33:41 +02:00
if old_group != new_group:
if new_group == "Uncategorized":
2025-06-29 02:47:11 +02:00
print(f"📱 Platform: '{stream_name}' → Uncategorized")
2025-06-29 03:33:41 +02:00
else:
print(f"📺 Fixed: '{stream_name}' {old_group}{new_group}")
channel['Group'] = new_group
changes += 1
2025-06-29 02:35:11 +02:00
2025-06-29 03:33:41 +02:00
stats[new_group] = stats.get(new_group, 0) + 1
2025-06-29 03:22:13 +02:00
2025-06-29 03:33:41 +02:00
print(f"\n✅ Changes made: {changes}")
print(f"📺 Traditional broadcasters: {sum(v for k, v in stats.items() if k != 'Uncategorized')}")
print(f"📱 Streaming/Unknown: {stats.get('Uncategorized', 0)}")
2025-06-29 02:02:34 +02:00
return channels
def save_channels(channels):
2025-06-29 02:47:11 +02:00
"""Save channels to file."""
# Backup
2025-06-29 02:02:34 +02:00
if os.path.exists('channels.txt'):
2025-06-29 02:47:11 +02:00
backup = f"channels_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
shutil.copy2('channels.txt', backup)
print(f"📋 Backup: {backup}")
2025-06-29 02:02:34 +02:00
2025-06-28 23:41:12 +02:00
try:
2025-06-29 02:02:34 +02:00
with open('channels.txt', 'w', encoding='utf-8') as f:
2025-06-28 23:41:12 +02:00
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")
2025-06-29 02:47:11 +02:00
print(f"✅ Saved {len(channels)} channels")
2025-06-28 23:41:12 +02:00
return True
except Exception as e:
2025-06-29 02:47:11 +02:00
print(f"❌ Save error: {e}")
2025-06-28 23:41:12 +02:00
return False
2025-06-29 02:02:34 +02:00
def generate_m3u(channels):
2025-06-29 02:47:11 +02:00
"""Generate M3U playlist."""
2025-06-28 23:41:12 +02:00
try:
2025-06-29 02:02:34 +02:00
with open('playlist.m3u', 'w', encoding='utf-8') as f:
2025-06-28 23:41:12 +02:00
f.write('#EXTM3U\n')
for channel in channels:
2025-06-29 02:47:11 +02:00
name = channel.get('Stream name', '')
2025-06-28 23:41:12 +02:00
group = channel.get('Group', 'Uncategorized')
logo = channel.get('Logo', '')
epg_id = channel.get('EPG id', '')
url = channel.get('Stream URL', '')
2025-06-29 02:47:11 +02:00
if name and url:
2025-06-28 23:41:12 +02:00
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}"')
2025-06-29 02:47:11 +02:00
f.write(f',{name}\n{url}\n')
2025-06-27 18:36:13 +02:00
2025-06-29 02:47:11 +02:00
print("✅ Generated playlist.m3u")
2025-06-29 02:02:34 +02:00
return True
except Exception as e:
2025-06-29 02:47:11 +02:00
print(f"❌ M3U error: {e}")
2025-06-29 02:02:34 +02:00
return False
2025-06-29 03:33:41 +02:00
def create_logs():
"""Create logs directory and simple log."""
try:
os.makedirs('logs', exist_ok=True)
with open('logs/playlist_update.log', 'w', encoding='utf-8') as f:
f.write(f"Enhanced IPTV Playlist Generator Log\n")
f.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"Status: Completed successfully\n")
print("✅ Created logs")
except Exception as e:
print(f"⚠️ Log creation failed: {e}")
2025-06-29 03:22:13 +02:00
2025-06-29 02:02:34 +02:00
def main():
2025-06-29 02:47:11 +02:00
"""Main function."""
2025-06-29 03:33:41 +02:00
print("📺 Enhanced Country Detection - No Network Calls")
print("=" * 60)
# Create logs directory
create_logs()
2025-06-29 02:02:34 +02:00
channels = load_channels()
if not channels:
2025-06-29 03:33:41 +02:00
print(" No channels to process")
return True
2025-06-29 02:02:34 +02:00
2025-06-29 03:33:41 +02:00
# Reorganize
2025-06-29 02:47:11 +02:00
channels = reorganize_channels(channels)
2025-06-29 02:02:34 +02:00
2025-06-29 03:33:41 +02:00
# Sort: Countries first, then Uncategorized last
2025-06-29 02:47:11 +02:00
channels.sort(key=lambda x: (
"zzz" if x.get('Group') == "Uncategorized" else x.get('Group', ''),
x.get('Stream name', '')
2025-06-29 02:35:11 +02:00
))
2025-06-29 02:02:34 +02:00
2025-06-29 02:47:11 +02:00
# Save and generate
if not save_channels(channels):
2025-06-29 02:02:34 +02:00
return False
2025-06-29 02:47:11 +02:00
if not generate_m3u(channels):
2025-06-29 02:02:34 +02:00
return False
2025-06-29 02:47:11 +02:00
# Clear import
2025-06-29 02:02:34 +02:00
try:
with open('bulk_import.m3u', 'w', encoding='utf-8') as f:
2025-06-29 02:47:11 +02:00
f.write('#EXTM3U\n')
2025-06-29 02:02:34 +02:00
print("🧹 Cleared import file")
except:
pass
2025-06-29 03:33:41 +02:00
print("\n🎉 COMPLETED!")
print("✅ Traditional broadcasters organized by country")
print("✅ All streaming platforms → Uncategorized")
print("✅ No network calls - fast execution")
2025-06-29 02:02:34 +02:00
return True
2025-06-27 16:34:52 +02:00
if __name__ == "__main__":
2025-06-29 02:02:34 +02:00
success = main()
2025-06-27 23:26:06 +02:00
exit(0 if success else 1)