diff --git a/scripts/config_manager.py b/scripts/config_manager.py index 781390a..52afb3b 100644 --- a/scripts/config_manager.py +++ b/scripts/config_manager.py @@ -1,5 +1,6 @@ """ Configuration Manager - Handles all configuration loading and management +Pure country-based detection with enhanced patterns """ import json @@ -8,7 +9,7 @@ import logging from pathlib import Path class ConfigManager: - """Centralized configuration management.""" + """Centralized configuration management for pure country-based organization.""" def __init__(self, config_dir="config"): self.config_dir = Path(config_dir) @@ -20,17 +21,18 @@ class ConfigManager: self.import_file = "bulk_import.m3u" self.log_file = "playlist_update.log" - # Config files + # Config files (removed group_overrides) self.settings_file = self.config_dir / "settings.json" - self.patterns_file = self.config_dir / "patterns.json" - self.group_overrides_file = self.config_dir / "group_overrides.json" + self.patterns_file = self.config_dir / "patterns.json" # Load configurations self.settings = self._load_settings() self.patterns = self._load_patterns() - self.group_overrides = self._load_group_overrides() - logging.info("Configuration manager initialized") + # No group overrides - pure country detection + self.group_overrides = {} + + logging.info("Configuration manager initialized (Pure Country Mode)") def _load_settings(self): """Load settings with comprehensive defaults.""" @@ -50,7 +52,12 @@ class ConfigManager: "max_backups": 5, "log_level": "INFO", "clear_import_after_processing": True, - "delete_import_file": False + "delete_import_file": False, + # Enhanced country detection settings + "pure_country_mode": True, + "prefer_country_over_category": True, + "enable_prefix_detection": True, + "enable_quality_tags": True } if self.settings_file.exists(): @@ -71,132 +78,161 @@ class ConfigManager: return defaults def _load_patterns(self): - """Load country detection patterns from external config.""" - default_patterns = { + """Load enhanced country detection patterns from external config.""" + # This will be the fallback if patterns.json doesn't exist + minimal_patterns = { "country_patterns": { - "๐Ÿ‡บ๐Ÿ‡ธ United States": ["cbs", "nbc", "abc", "fox", "espn", "cnn", "hbo", " usa", " us ", ".us", "america", "nfl"], - "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom": ["bbc", "itv", "sky", "channel 4", "e4", " uk", ".uk", "british", "premier league"], - "๐Ÿ‡จ๐Ÿ‡ฆ Canada": ["cbc", "ctv", "global", "canada", "canadian", " ca ", ".ca"], - "๐Ÿ‡ฉ๐Ÿ‡ช Germany": ["ard", "zdf", "rtl", "sat.1", "pro7", "germany", "german", " de ", ".de"], - "๐Ÿ‡ซ๐Ÿ‡ท France": ["tf1", "france 2", "m6", "canal+", "france", "french", " fr ", ".fr"], - "๐Ÿ‡ช๐Ÿ‡ธ Spain": ["tve", "antena 3", "telecinco", "spain", "spanish", " es ", ".es"], - "๐Ÿ‡ฎ๐Ÿ‡น Italy": ["rai", "mediaset", "canale 5", "italy", "italian", " it ", ".it"], - "๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands": ["npo", "rtl nl", "netherlands", "dutch", "holland", " nl ", ".nl"], - "๐Ÿ‡ง๐Ÿ‡ช Belgium": ["vtm", "รฉรฉn", "canvas", "belgium", "belgian", " be ", ".be"], - "๐Ÿ‡จ๐Ÿ‡ญ Switzerland": ["srf", "rts", "switzerland", "swiss", " ch ", ".ch"], - "๐Ÿ‡ฆ๐Ÿ‡น Austria": ["orf", "austria", "austrian", " at ", ".at"], - "๐Ÿ‡ต๐Ÿ‡น Portugal": ["rtp", "sic", "tvi", "portugal", "portuguese", " pt ", ".pt"], - "๐Ÿ‡ฎ๐Ÿ‡ช Ireland": ["rte", "tg4", "ireland", "irish", " ie ", ".ie"], - "๐Ÿ‡ธ๐Ÿ‡ช Sweden": ["svt", "tv4", "sweden", "swedish", " se ", ".se"], - "๐Ÿ‡ณ๐Ÿ‡ด Norway": ["nrk", "tv 2 no", "norway", "norwegian", " no ", ".no"], - "๐Ÿ‡ฉ๐Ÿ‡ฐ Denmark": ["dr", "tv2 dk", "denmark", "danish", " dk ", ".dk"], - "๐Ÿ‡ซ๐Ÿ‡ฎ Finland": ["yle", "mtv3", "finland", "finnish", " fi ", ".fi"], - "๐Ÿ‡ฎ๐Ÿ‡ธ Iceland": ["ruv", "iceland", "icelandic", " is ", ".is"], - "๐Ÿ‡ท๐Ÿ‡บ Russia": ["channel one", "rossiya", "ntv", "russia", "russian", " ru ", ".ru"], - "๐Ÿ‡ต๐Ÿ‡ฑ Poland": ["tvp", "polsat", "tvn", "poland", "polish", " pl ", ".pl"], - "๐Ÿ‡จ๐Ÿ‡ฟ Czech Republic": ["ct", "nova", "prima", "czech", " cz ", ".cz"], - "๐Ÿ‡ธ๐Ÿ‡ฐ Slovakia": ["rtvs", "markiza", "slovakia", "slovak", " sk ", ".sk"], - "๐Ÿ‡ญ๐Ÿ‡บ Hungary": ["mtv hu", "rtl klub", "hungary", "hungarian", " hu ", ".hu"], - "๐Ÿ‡บ๐Ÿ‡ฆ Ukraine": ["1+1", "inter", "ictv", "ukraine", "ukrainian", " ua ", ".ua"], - "๐Ÿ‡ท๐Ÿ‡ด Romania": ["tvr", "pro tv", "romania", "romanian", " ro ", ".ro"], - "๐Ÿ‡ง๐Ÿ‡ฌ Bulgaria": ["btv", "nova bg", "bulgaria", "bulgarian", " bg ", ".bg"], - "๐Ÿ‡ญ๐Ÿ‡ท Croatia": ["hrt", "nova tv hr", "croatia", "croatian", " hr ", ".hr"], - "๐Ÿ‡ท๐Ÿ‡ธ Serbia": ["rts", "pink", "serbia", "serbian", " rs ", ".rs"], - "๐Ÿ‡ฌ๐Ÿ‡ท Greece": ["ert", "mega gr", "greece", "greek", " gr ", ".gr"], - "๐Ÿ‡ง๐Ÿ‡ท Brazil": ["globo", "band", "sbt", "brazil", "brasil", " br ", ".br"], - "๐Ÿ‡ฆ๐Ÿ‡ท Argentina": ["telefe", "canal 13", "argentina", " ar ", ".ar"], - "๐Ÿ‡ฒ๐Ÿ‡ฝ Mexico": ["televisa", "tv azteca", "mexico", "mรฉxico", " mx ", ".mx"], - "๐Ÿ‡จ๐Ÿ‡ฑ Chile": ["tvn", "mega", "chile", "chilean", " cl ", ".cl"], - "๐Ÿ‡จ๐Ÿ‡ด Colombia": ["caracol", "rcn", "colombia", "colombian", " co ", ".co"], - "๐Ÿ‡ต๐Ÿ‡ช Peru": ["america tv pe", "peru", "peruvian", " pe ", ".pe"], - "๐Ÿ‡ป๐Ÿ‡ช Venezuela": ["venevision", "venezuela", "venezuelan", " ve ", ".ve"], - "๐Ÿ‡จ๐Ÿ‡ณ China": ["cctv", "phoenix", "china", "chinese", " cn ", ".cn"], - "๐Ÿ‡ฏ๐Ÿ‡ต Japan": ["nhk", "fuji", "tv asahi", "japan", "japanese", " jp ", ".jp"], - "๐Ÿ‡ฐ๐Ÿ‡ท South Korea": ["kbs", "sbs kr", "mbc kr", "korea", "korean", " kr ", ".kr"], - "๐Ÿ‡ฐ๐Ÿ‡ต North Korea": ["kctv", "north korea", "dprk"], - "๐Ÿ‡น๐Ÿ‡ผ Taiwan": ["cts", "ctv", "tvbs", "taiwan", "taiwanese", " tw ", ".tw"], - "๐Ÿ‡ญ๐Ÿ‡ฐ Hong Kong": ["tvb", "atv", "hong kong", "hongkong", " hk ", ".hk"], - "๐Ÿ‡น๐Ÿ‡ญ Thailand": ["ch3", "ch7", "thai pbs", "thailand", "thai", " th ", ".th"], - "๐Ÿ‡ป๐Ÿ‡ณ Vietnam": ["vtv", "htv", "vietnam", "vietnamese", " vn ", ".vn"], - "๐Ÿ‡ฎ๐Ÿ‡ฉ Indonesia": ["tvri", "sctv", "rcti", "indonesia", "indonesian", " id ", ".id"], - "๐Ÿ‡ฒ๐Ÿ‡พ Malaysia": ["tv1", "tv3", "astro", "malaysia", "malaysian", " my ", ".my", "my:"], - "๐Ÿ‡ธ๐Ÿ‡ฌ Singapore": ["channel 5", "channel 8", "singapore", " sg ", ".sg"], - "๐Ÿ‡ต๐Ÿ‡ญ Philippines": ["abs-cbn", "gma", "philippines", "filipino", " ph ", ".ph"], - "๐Ÿ‡ฎ๐Ÿ‡ณ India": ["star plus", "zee tv", "colors", "sony tv", "india", "indian", "hindi", " in ", ".in"], - "๐Ÿ‡ต๐Ÿ‡ฐ Pakistan": ["ptv", "geo tv", "ary", "pakistan", "pakistani", " pk ", ".pk"], - "๐Ÿ‡ง๐Ÿ‡ฉ Bangladesh": ["btv", "channel i", "bangladesh", "bangladeshi", " bd ", ".bd"], - "๐Ÿ‡ฑ๐Ÿ‡ฐ Sri Lanka": ["rupavahini", "sirasa", "sri lanka", " lk ", ".lk"], - "๐Ÿ‡ณ๐Ÿ‡ต Nepal": ["nepal tv", "kantipur", "nepal", "nepali", " np ", ".np"], - "๐Ÿ‡ฆ๐Ÿ‡ซ Afghanistan": ["rta", "tolo tv", "afghanistan", "afghan", " af ", ".af"], - "๐Ÿ‡ฆ๐Ÿ‡บ Australia": ["abc au", "seven", "nine", "ten", "australia", "australian", "aussie", " au ", ".au"], - "๐Ÿ‡ณ๐Ÿ‡ฟ New Zealand": ["tvnz", "tvnz 1", "tvnz 2", "three nz", "tvnz duke", "new zealand", "kiwi", " nz ", ".nz"], - "๐Ÿ‡ธ๐Ÿ‡ฆ Arabic": ["al jazeera", "mbc", "lbc", "dubai tv", "arabic", "arab", "qatar", "dubai", "saudi"], - "๐Ÿ‡ฎ๐Ÿ‡ฑ Israel": ["kan", "keshet 12", "israel", "israeli", "hebrew", " il ", ".il"], - "๐Ÿ‡น๐Ÿ‡ท Turkey": ["trt", "atv", "kanal d", "turkey", "turkish", " tr ", ".tr", "tr |"], - "๐Ÿ‡ฎ๐Ÿ‡ท Iran": ["irib", "press tv", "iran", "iranian", "persian", " ir ", ".ir"], - "๐Ÿ‡ช๐Ÿ‡ฌ Egypt": ["nile tv", "cbc egypt", "egypt", "egyptian", " eg ", ".eg"], - "๐Ÿ‡ฟ๐Ÿ‡ฆ South Africa": ["sabc", "etv", "mnet", "south africa", " za ", ".za"], - "๐Ÿ‡ณ๐Ÿ‡ฌ Nigeria": ["nta", "channels tv", "nigeria", "nigerian", " ng ", ".ng"] + "๐Ÿ‡บ๐Ÿ‡ธ United States": ["cbs", "nbc", "abc", "fox", "espn", "cnn", "hbo", "usa", "america", "nfl"], + "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom": ["bbc", "itv", "sky", "channel 4", "e4", "uk", "british", "premier league"], + "๐Ÿ‡จ๐Ÿ‡ฆ Canada": ["cbc", "ctv", "global", "canada", "canadian"], + "๐Ÿ‡ฉ๐Ÿ‡ช Germany": ["ard", "zdf", "rtl", "sat.1", "pro7", "germany", "german"], + "๐Ÿ‡ซ๐Ÿ‡ท France": ["tf1", "france 2", "m6", "canal+", "france", "french"], + "๐Ÿ‡ช๐Ÿ‡ธ Spain": ["tve", "antena 3", "telecinco", "spain", "spanish"], + "๐Ÿ‡ฎ๐Ÿ‡น Italy": ["rai", "mediaset", "canale 5", "italy", "italian"], + "๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands": ["npo", "rtl nl", "netherlands", "dutch", "holland"], + "๐Ÿ‡ง๐Ÿ‡ท Brazil": ["globo", "band", "sbt", "brazil", "brasil"], + "๐Ÿ‡ฆ๐Ÿ‡ท Argentina": ["telefe", "canal 13", "argentina"], + "๐Ÿ‡ฒ๐Ÿ‡ฝ Mexico": ["televisa", "tv azteca", "mexico", "mรฉxico"], + "๐Ÿ‡ฆ๐Ÿ‡บ Australia": ["abc au", "seven", "nine", "ten", "australia", "australian"], + "๐Ÿ‡ธ๐Ÿ‡ฆ Arabic": ["al jazeera", "mbc", "lbc", "dubai tv", "arabic", "arab"] }, "country_prefixes": { - "๐Ÿ‡บ๐Ÿ‡ฆ Ukraine": ["ua |"], - "๐Ÿ‡ต๐Ÿ‡ฑ Poland": ["pl |"], - "๐Ÿ‡น๐Ÿ‡ท Turkey": ["tr |"], - "๐Ÿ‡ฒ๐Ÿ‡พ Malaysia": ["my:", "my |"], - "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom": ["uk:", "uk |"], - "๐Ÿ‡บ๐Ÿ‡ธ United States": ["us:", "us |"] + "๐Ÿ‡บ๐Ÿ‡ธ United States": ["us:", "us |", "usa:"], + "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom": ["uk:", "uk |", "gb:"], + "๐Ÿ‡ฉ๐Ÿ‡ช Germany": ["de:", "de |", "ger:"], + "๐Ÿ‡ซ๐Ÿ‡ท France": ["fr:", "fr |", "france:"], + "๐Ÿ‡ช๐Ÿ‡ธ Spain": ["es:", "es |", "spain:"], + "๐Ÿ‡ฎ๐Ÿ‡น Italy": ["it:", "it |", "italy:"], + "๐Ÿ‡บ๐Ÿ‡ฆ Ukraine": ["ua:", "ua |", "ukraine:"], + "๐Ÿ‡ต๐Ÿ‡ฑ Poland": ["pl:", "pl |", "poland:"], + "๐Ÿ‡น๐Ÿ‡ท Turkey": ["tr:", "tr |", "turkey:"], + "๐Ÿ‡ฒ๐Ÿ‡พ Malaysia": ["my:", "my |", "malaysia:"], + "๐Ÿ‡ฆ๐Ÿ‡บ Australia": ["au:", "au |", "australia:"], + "๐Ÿ‡จ๐Ÿ‡ฆ Canada": ["ca:", "ca |", "canada:"] }, "quality_patterns": { - "4K": ["4k", "uhd", "2160p"], - "FHD": ["fhd", "1080p", "1080"], - "HD": ["hd", "720p", "720"], - "SD": ["sd", "480p", "360p"] + "4K": ["4k", "uhd", "2160p", "ultra hd"], + "FHD": ["fhd", "1080p", "1080", "full hd"], + "HD": ["hd", "720p", "720", "high definition"], + "SD": ["sd", "480p", "360p", "standard"] }, - "adult_keywords": ["xxx", "adult", "porn", "sex", "erotic", "playboy", "18+"] + "adult_keywords": [ + "xxx", "adult", "porn", "sex", "erotic", "playboy", "18+", + "nude", "naked", "sexy", "babes" + ] } if self.patterns_file.exists(): try: with open(self.patterns_file, 'r', encoding='utf-8') as f: user_patterns = json.load(f) - # Deep merge with defaults - for category, patterns in user_patterns.items(): - if category in default_patterns: - if isinstance(default_patterns[category], dict): - default_patterns[category].update(patterns) - else: - default_patterns[category] = patterns - else: - default_patterns[category] = patterns - logging.info(f"Loaded user patterns from {self.patterns_file}") + logging.info(f"Loaded enhanced patterns from {self.patterns_file}") + + # Validate that required sections exist + required_sections = ["country_patterns", "country_prefixes", "quality_patterns", "adult_keywords"] + for section in required_sections: + if section not in user_patterns: + logging.warning(f"Missing section '{section}' in patterns file, using minimal fallback") + user_patterns[section] = minimal_patterns.get(section, {}) + + return user_patterns except (json.JSONDecodeError, IOError) as e: - logging.warning(f"Could not load patterns, using defaults: {e}") + logging.warning(f"Could not load patterns, using minimal patterns: {e}") else: - # Create default patterns file for user customization + # Create minimal patterns file (user should update with enhanced version) with open(self.patterns_file, 'w', encoding='utf-8') as f: - json.dump(default_patterns, f, indent=2, ensure_ascii=False) - logging.info(f"Created default patterns file: {self.patterns_file}") + json.dump(minimal_patterns, f, indent=2, ensure_ascii=False) + logging.info(f"Created minimal patterns file: {self.patterns_file}") + logging.info("Update patterns.json with enhanced version for better detection") - return default_patterns + return minimal_patterns - def _load_group_overrides(self): - """Load group overrides.""" - if self.group_overrides_file.exists(): - try: - with open(self.group_overrides_file, 'r', encoding='utf-8') as f: - overrides = json.load(f) - logging.info(f"Loaded {len(overrides)} group overrides") - return overrides - except (json.JSONDecodeError, IOError) as e: - logging.warning(f"Could not load group overrides: {e}") - else: - # Create empty overrides file as example - example_overrides = { - "example_keyword": "๐Ÿ‡บ๐Ÿ‡ธ United States", - "another_keyword": "๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom" - } - with open(self.group_overrides_file, 'w', encoding='utf-8') as f: - json.dump(example_overrides, f, indent=2, ensure_ascii=False) - logging.info(f"Created example group overrides file: {self.group_overrides_file}") + def get_country_detection_stats(self): + """Get statistics about country detection capabilities.""" + stats = { + 'total_countries': len(self.patterns.get('country_patterns', {})), + 'total_prefixes': len(self.patterns.get('country_prefixes', {})), + 'total_patterns': 0, + 'quality_levels': len(self.patterns.get('quality_patterns', {})), + 'adult_keywords': len(self.patterns.get('adult_keywords', [])), + 'pure_country_mode': True + } - return {} \ No newline at end of file + # Count total detection patterns + for country, patterns in self.patterns.get('country_patterns', {}).items(): + stats['total_patterns'] += len(patterns) + + return stats + + def validate_patterns(self): + """Validate pattern configuration and return any issues.""" + issues = [] + + # Check for required sections + required_sections = ['country_patterns', 'country_prefixes', 'quality_patterns', 'adult_keywords'] + for section in required_sections: + if section not in self.patterns: + issues.append(f"Missing required section: {section}") + + # Check for empty country patterns + country_patterns = self.patterns.get('country_patterns', {}) + if not country_patterns: + issues.append("No country patterns defined") + else: + for country, patterns in country_patterns.items(): + if not patterns or not isinstance(patterns, list): + issues.append(f"Invalid or empty patterns for country: {country}") + + # Check for duplicate patterns across countries + all_patterns = [] + for country, patterns in country_patterns.items(): + for pattern in patterns: + if pattern in all_patterns: + issues.append(f"Duplicate pattern '{pattern}' found in multiple countries") + all_patterns.append(pattern) + + return issues + + def get_countries_list(self): + """Get a list of all supported countries.""" + return list(self.patterns.get('country_patterns', {}).keys()) + + def save_settings(self): + """Save current settings to file.""" + try: + with open(self.settings_file, 'w', encoding='utf-8') as f: + json.dump(self.settings, f, indent=2) + logging.info("Settings saved successfully") + return True + except Exception as e: + logging.error(f"Could not save settings: {e}") + return False + + def update_setting(self, key, value): + """Update a specific setting.""" + self.settings[key] = value + logging.info(f"Updated setting: {key} = {value}") + + def is_pure_country_mode(self): + """Check if pure country mode is enabled.""" + return self.settings.get('pure_country_mode', True) + + def get_detection_summary(self): + """Get a summary of detection capabilities for logging.""" + stats = self.get_country_detection_stats() + return (f"Country Detection: {stats['total_countries']} countries, " + f"{stats['total_patterns']} patterns, " + f"{stats['total_prefixes']} prefixes, " + f"Mode: Pure Country") + + def cleanup_old_config_files(self): + """Remove old group overrides file if it exists.""" + old_overrides_file = self.config_dir / "group_overrides.json" + if old_overrides_file.exists(): + try: + old_overrides_file.unlink() + logging.info("Removed obsolete group_overrides.json file") + return True + except Exception as e: + logging.warning(f"Could not remove old group_overrides.json: {e}") + return False \ No newline at end of file