""" Summarization Module Uses OpenRouter API to analyze transcript and identify highlights """ from openai import OpenAI import json import re def parse_timestamps_from_response(response_text): """ Parse timestamps from OpenRouter API response Expected format: JSON with segments containing start/end times Args: response_text: Response text from OpenRouter API Returns: List of dictionaries with 'start', 'end', and 'text' keys """ try: # Try to extract JSON from response # Look for JSON blocks in the response json_match = re.search(r'\{.*\}', response_text, re.DOTALL) if json_match: data = json.loads(json_match.group()) else: # Try parsing the entire response as JSON data = json.loads(response_text) segments = [] # Handle different possible response formats if isinstance(data, list): segments = data elif isinstance(data, dict): if 'segments' in data: segments = data['segments'] elif 'highlights' in data: segments = data['highlights'] elif 'clips' in data: segments = data['clips'] else: # Assume the dict itself contains segment info segments = [data] # Normalize segment format normalized_segments = [] for seg in segments: if isinstance(seg, dict): start = seg.get('start', seg.get('start_time', seg.get('startTime', 0))) end = seg.get('end', seg.get('end_time', seg.get('endTime', start + 30))) text = seg.get('text', seg.get('description', seg.get('summary', ''))) normalized_segments.append({ 'start': float(start), 'end': float(end), 'text': text }) return normalized_segments except json.JSONDecodeError: # If JSON parsing fails, try to extract timestamps from text print("⚠️ Could not parse JSON, attempting to extract timestamps from text...") return extract_timestamps_from_text(response_text) except Exception as e: print(f"❌ Error parsing timestamps: {str(e)}") print(f"Response text: {response_text[:500]}") return [] def extract_timestamps_from_text(text): """ Fallback: Extract timestamps from plain text response Looks for patterns like "00:01:23 - 00:01:45" or "1:23 - 1:45" """ segments = [] # Pattern for HH:MM:SS - HH:MM:SS pattern = r'(\d{1,2}):(\d{2}):(\d{2})\s*-\s*(\d{1,2}):(\d{2}):(\d{2})' matches = re.finditer(pattern, text) for match in matches: h1, m1, s1, h2, m2, s2 = map(int, match.groups()) start = h1 * 3600 + m1 * 60 + s1 end = h2 * 3600 + m2 * 60 + s2 # Try to extract text after the timestamp text_start = match.end() text_end = text.find('\n', text_start) if text_end == -1: text_end = min(text_start + 100, len(text)) segment_text = text[text_start:text_end].strip() segments.append({ 'start': start, 'end': end, 'text': segment_text }) return segments def get_highlights_from_openrouter(transcript, num_clips=3, api_key=None, model="kwaipilot/kat-coder-pro:free"): """ Use OpenRouter API to identify highlights in the transcript Args: transcript: Full transcript text or SRT content num_clips: Number of highlight clips to generate api_key: OpenRouter API key model: Model to use from OpenRouter Returns: List of segments with start/end times and descriptions """ if api_key is None: raise ValueError("OpenRouter API key is required") print(f"🤖 Analyzing transcript with OpenRouter ({model})...") # Prepare prompt # Note: Use double curly braces {{ }} to escape them in f-strings prompt = f""" You are an intelligent assistant for video analysis. Analyze the following transcript from an Arabic video and identify {num_clips} meaningful moments that each form a coherent segment between **30 and 60 seconds** in duration. These segments should not be arbitrary excerpts — each one must reflect a **complete idea** and help convey the **main concept of the video**, providing meaningful context. Return the results in **JSON format only**, containing a list of segments. Each segment should include: - "start": starting time in seconds (float) - "end": ending time in seconds (float) - "text": a concise description of the content in that segment that captures the key idea The output should be in JSON format only, with no additional commentary. ExampleOutput: {{ "segments": [ {{ "start": 10.5, "end": 42.0, "text": "Explanation of the core concept and its importance" }}, {{ "start": 75.0, "end": 120.0, "text": "A meaningful real-world example illustrating the main idea" }} ] }} Transcript: {transcript} Return **JSON only**, with no additional commentary.""" try: client = OpenAI( base_url="https://openrouter.ai/api/v1", api_key=api_key, ) completion = client.chat.completions.create( extra_headers={ "HTTP-Referer": "https://github.com/ahmedomahmoud/Video-Summarizer", "X-Title": "Video Summarizer", }, model=model, messages=[ { "role": "user", "content": prompt } ], temperature=0.7, max_tokens=2000 ) response_text = completion.choices[0].message.content print(f"✅ Received response from OpenRouter") # Parse the response segments = parse_timestamps_from_response(response_text) if not segments: print("⚠️ No segments found in response, using fallback method") # Fallback: divide video into equal segments return create_fallback_segments(transcript, num_clips) # Limit to requested number of clips segments = segments[:num_clips] print(f"✅ Identified {len(segments)} highlight segments") return segments except Exception as e: print(f"❌ Error calling OpenRouter API: {str(e)}") print("⚠️ Using fallback method to create segments") return create_fallback_segments(transcript, num_clips) def create_fallback_segments(transcript, num_clips): """ Fallback method: Create segments by dividing transcript into equal parts """ # This is a simple fallback - in practice, you'd want better logic # For now, we'll return empty segments and let the UI handle it print("⚠️ Using fallback segment creation") return [] def read_srt_file(srt_path): """Read SRT file and return transcript text""" try: with open(srt_path, 'r', encoding='utf-8') as f: content = f.read() return content except Exception as e: print(f"❌ Error reading SRT file: {str(e)}") return ""