#!/usr/bin/env python3 import os import sys import threading import traceback from datetime import datetime import time import json from bottle import Bottle, run, request, response, redirect, HTTPResponse from urllib.parse import urlencode, parse_qsl # Add lib path for imports script_dir = os.path.dirname(os.path.abspath(__file__)) LIB_PATH = os.path.join(script_dir, 'lib') if os.path.exists(LIB_PATH): sys.path.insert(0, LIB_PATH) try: from streaming_providers import get_configured_manager from streaming_providers.base.models import StreamingChannel from streaming_providers.base.utils import logger, MPDRewriter, MPDCacheManager from streaming_providers.base.utils.environment import get_environment_manager, get_vfs_instance from streaming_providers.base.utils.environment import is_kodi_environment from streaming_providers.base.settings.provider_enable_manager import ProviderEnableManager # Add EPG Manager import from streaming_providers.base.epg.epg_manager import EPGManager except ImportError as import_err: print(f"Ultimate Backend: Critical import failed - {str(import_err)}", file=sys.stderr) raise class UltimateService: def __init__(self, config_dir: str = None): self.app = Bottle() # Get environment manager self.env_manager = get_environment_manager() # Override config directory if provided if config_dir: self.env_manager.set_config('profile_path', config_dir) # Get settings self.server_port = self.env_manager.get_config('server_port', 7777) self.default_country = self.env_manager.get_config('default_country', 'DE') # Initialize manager try: self.manager = get_configured_manager() logger.info("Manager initialized successfully") except Exception as init_err: logger.error(f"Failed to initialize manager - {str(init_err)}") raise # Initialize VFS for M3U caching self.vfs = get_vfs_instance(subdir="m3u_cache") logger.info(f"VFS initialized for M3U caching: {self.vfs.base_path}") self.mpd_cache = MPDCacheManager() logger.info(f"MPD cache initialized: {self.mpd_cache.vfs.base_path}") # 1. Determine EPG URL FIRST (with proper precedence) self.epg_url = self._determine_epg_url() logger.info(f"UltimateService: Final EPG URL determined: {self.epg_url}") # 2. Initialize EPG Manager WITH the URL try: self.epg_manager = EPGManager(self.epg_url) # Pass the URL here logger.info(f"EPG Manager initialized with URL: {self.epg_url}") except ImportError as e: logger.warning(f"Could not import EPG Manager: {e}") self.epg_manager = None except Exception as e: logger.warning(f"Could not initialize EPG Manager: {e}") self.epg_manager = None self.setup_routes() self.config_html = self._load_config_html() def _determine_epg_url(self) -> str: """ Determine EPG URL with proper precedence. Must match the precedence logic in EPGManager. """ import os # 1. Environment variable (highest priority for Docker) env_url = os.environ.get('ULTIMATE_EPG_URL') if env_url and env_url.strip() and env_url != "https://example.com/epg.xml.gz": logger.info(f"UltimateService: Using EPG URL from environment variable: {env_url}") return env_url.strip() # 2. Try config.json via environment manager try: config_url = self.env_manager.get_config('epg_url') if config_url and config_url.strip() and config_url != "https://example.com/epg.xml.gz": logger.info(f"UltimateService: Using EPG URL from config.json: {config_url}") return config_url.strip() except Exception as e: logger.debug(f"UltimateService: Could not get EPG URL from environment manager: {e}") # 3. Try Kodi addon setting try: if is_kodi_environment(): import xbmcaddon addon = xbmcaddon.Addon() kodi_url = addon.getSetting('epg_xml_url') if kodi_url and kodi_url.strip() and kodi_url != "https://example.com/epg.xml.gz": logger.info(f"UltimateService: Using EPG URL from Kodi settings: {kodi_url}") return kodi_url.strip() except Exception as e: logger.debug(f"UltimateService: Could not get EPG URL from Kodi settings: {e}") # 4. Default fallback default_url = "https://example.com/epg.xml.gz" logger.warning(f"UltimateService: No valid EPG URL found, using default: {default_url}") logger.warning("Please set ULTIMATE_EPG_URL environment variable!") return default_url def _load_config_html(self): """Load the web interface HTML template with embedded CSS and JS""" base_dir = os.path.dirname(os.path.abspath(__file__)) web_dir = os.path.join(base_dir, 'resources', 'web') # Define file paths html_path = os.path.join(web_dir, 'config.html') css_path = os.path.join(web_dir, 'config.css') js_path = os.path.join(web_dir, 'config.js') # Proxy files proxy_css_path = os.path.join(web_dir, 'proxy.css') proxy_js_path = os.path.join(web_dir, 'proxy.js') # EPG mapping files epg_css_path = os.path.join(web_dir, 'epg_mapping.css') epg_js_path = os.path.join(web_dir, 'epg_mapping.js') fuzzyset_path = os.path.join(web_dir, 'lib', 'fuzzyset.js') debounce_path = os.path.join(web_dir, 'lib', 'debounce.js') # Enable/disable files enable_css_path = os.path.join(web_dir, 'provider_enable.css') enable_js_path = os.path.join(web_dir, 'provider_enable.js') try: # Load HTML with open(html_path, 'r', encoding='utf-8') as f: html = f.read() # Load CSS files with open(css_path, 'r', encoding='utf-8') as f: css = f.read() with open(proxy_css_path, 'r', encoding='utf-8') as f: proxy_css = f.read() with open(epg_css_path, 'r', encoding='utf-8') as f: epg_css = f.read() with open(enable_css_path, 'r', encoding='utf-8') as f: enable_css = f.read() # Load JS files with open(js_path, 'r', encoding='utf-8') as f: js = f.read() with open(proxy_js_path, 'r', encoding='utf-8') as f: proxy_js = f.read() with open(epg_js_path, 'r', encoding='utf-8') as f: epg_js = f.read() with open(fuzzyset_path, 'r', encoding='utf-8') as f: fuzzyset_js = f.read() with open(debounce_path, 'r', encoding='utf-8') as f: debounce_js = f.read() with open(enable_js_path, 'r', encoding='utf-8') as f: enable_js = f.read() # Combine all CSS (correct order: base -> proxy -> epg -> enable) combined_css = f"{css}\n\n/* Proxy CSS */\n{proxy_css}\n\n/* EPG Mapping CSS */\n{epg_css}\n\n/* Provider Enable/Disable CSS */\n{enable_css}" # Combine all JS (with proper order) combined_js = f""" /* Debounce Utility */ {debounce_js} /* FuzzySet Library */ {fuzzyset_js} /* Main Config JS */ {js} /* Proxy Management JS */ {proxy_js} /* EPG Mapping JS */ {epg_js} /* Provider Enable/Disable JS */ {enable_js} """ # Replace CSS in HTML html = html.replace('', f'') # Inject JavaScript before
Warning: Full interface not loaded. Using basic mode.
tag script_tag = f'' if '' in html: html = html.replace('', script_tag) else: html = html.replace('', f'{script_tag}\n') return html except Exception as e: logger.error(f"Failed to load config files: {e}") return self._get_fallback_html() @staticmethod def _get_fallback_html(): """Generate a minimal fallback HTML if file is not found""" return """