"""
HTML Exporter for YouTube Summaries
Exports summaries to responsive HTML format with embedded styles
"""
import tempfile
from typing import Dict, Any, Optional
from ..export_service import BaseExporter
import html as html_module
class HTMLExporter(BaseExporter):
"""Export summaries to HTML format"""
async def export(
self,
summary_data: Dict[str, Any],
template: Optional[str] = None,
branding: Optional[Dict[str, Any]] = None
) -> str:
"""Export to HTML"""
data = self._prepare_summary_data(summary_data)
# Use custom template if provided, otherwise default
if template:
content = await self._render_custom_template(template, data)
else:
content = self._render_default_template(data, branding)
# Write to temporary file
with tempfile.NamedTemporaryFile(mode='w', suffix='.html', delete=False) as f:
f.write(content)
return f.name
async def _render_custom_template(self, template: str, data: Dict[str, Any]) -> str:
"""Render custom template with data"""
content = template
for key, value in data.items():
content = content.replace(f"{{{{{key}}}}}", str(value))
return content
def _render_default_template(self, data: Dict[str, Any], branding: Optional[Dict[str, Any]]) -> str:
"""Render default HTML template with responsive design"""
video_metadata = data.get("video_metadata", {})
processing_metadata = data.get("processing_metadata", {})
# Escape HTML in text content
def escape(text):
if text is None:
return 'N/A'
return html_module.escape(str(text))
# Generate HTML
html = f"""
"
paragraphs = text.split('\n\n')
formatted = []
for para in paragraphs:
if para.strip():
escaped_para = html_module.escape(para.strip())
formatted.append(f"
{escaped_para}
")
return '\n'.join(formatted)
def _format_list_items(self, items: list, ordered: bool = False) -> str:
"""Format list items as HTML"""
if not items:
return "
No items available
"
formatted = []
for item in items:
escaped_item = html_module.escape(str(item))
formatted.append(f"
{escaped_item}
")
return '\n'.join(formatted)
def _format_theme_tags(self, themes: list) -> str:
"""Format themes as tag elements"""
if not themes:
return 'No themes identified'
formatted = []
for theme in themes:
escaped_theme = html_module.escape(str(theme))
formatted.append(f'{escaped_theme}')
return '\n'.join(formatted)
def _format_chapters_section(self, chapters: list) -> str:
"""Format chapters section if available"""
if not chapters:
return ""
section = """
Chapter Breakdown
"""
for chapter in chapters:
timestamp = html_module.escape(str(chapter.get('timestamp', '')))
title = html_module.escape(str(chapter.get('title', '')))
summary = html_module.escape(str(chapter.get('summary', '')))
section += f"""
{timestamp}{title}
{f'
{summary}
' if summary else ''}
"""
section += """
"""
return section
def _format_duration(self, duration: Optional[Any]) -> str:
"""Format duration from seconds to human-readable format"""
if not duration:
return 'N/A'
# Handle string format (e.g., "10:30")
if isinstance(duration, str):
return duration
# Handle numeric format (seconds)
try:
seconds = int(duration)
except (ValueError, TypeError):
return 'N/A'
hours = seconds // 3600
minutes = (seconds % 3600) // 60
secs = seconds % 60
if hours > 0:
return f"{hours}h {minutes}m {secs}s"
elif minutes > 0:
return f"{minutes}m {secs}s"
else:
return f"{secs}s"
def _format_number(self, number: Optional[int]) -> str:
"""Format large numbers with commas"""
if number is None:
return 'N/A'
return f"{number:,}"
def _format_percentage(self, value: Optional[float]) -> str:
"""Format decimal as percentage"""
if value is None:
return 'N/A'
return f"{value * 100:.1f}%"
def get_file_extension(self) -> str:
return "html"