Spaces:
Sleeping
Sleeping
| """ | |
| 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 "" | |