#!/usr/bin/env python3 import os import sys import threading 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: 1. Explicit config_dir override (if provided) 2. Environment variable ULTIMATE_EPG_URL 3. config.json via environment manager 4. Kodi addon setting (if in Kodi) 5. Default fallback Returns: EPG URL as string """ import os # 1. Check environment variable FIRST (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"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"Using EPG URL from config.json: {config_url}") return config_url.strip() except Exception as e: logger.debug(f"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(): logger.info(f"Using EPG URL from Kodi settings: {kodi_url}") return kodi_url.strip() except Exception as e: logger.debug(f"Could not get EPG URL from Kodi settings: {e}") # 4. Default fallback default_url = "https://example.com/epg.xml.gz" logger.warning(f"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') # 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') 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() # Load EPG CSS with open(epg_css_path, 'r', encoding='utf-8') as f: epg_css = f.read() # Load JS files with open(js_path, 'r', encoding='utf-8') as f: js = f.read() # Load EPG JS and libraries 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() # Combine all CSS combined_css = f"{css}\n\n/* EPG Mapping CSS */\n{epg_css}" # Combine all JS (with proper order) combined_js = f""" /* Debounce Utility */ {debounce_js} /* FuzzySet Library */ {fuzzyset_js} /* Main Config JS */ {js} /* EPG Mapping JS */ {epg_js} """ # Replace CSS in HTML html = html.replace('', f'') # Inject JavaScript before
Warning: Full interface not loaded. Using basic mode.
tag since ' # Check if script tag exists in HTML (for backward compatibility) if '' in html: html = html.replace('', script_tag) else: # Script tag was removed, inject before 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 """