Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions src/pycaps/renderer/css_subtitle_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@

class CssSubtitleRenderer(SubtitleRenderer):

DEFAULT_DEVICE_SCALE_FACTOR: int = 2
BASE_DEVICE_SCALE_FACTOR: float = 2.0
REFERENCE_VIDEO_HEIGHT: int = 1280
MIN_SCALE_MODIFIER: float = 0.25
MAX_SCALE_MODIFIER: float = 5.0
DEFAULT_VIEWPORT_HEIGHT_RATIO: float = 0.25
DEFAULT_MIN_VIEWPORT_HEIGHT: int = 150

Expand All @@ -38,6 +41,12 @@ def __init__(self, browser: Optional['Browser'] = None):
self._current_line: Optional[Line] = None
self._current_line_state: Optional[ElementState] = None
self._renderer_page: RendererPage = RendererPage()
self._device_scale_factor: float = self.BASE_DEVICE_SCALE_FACTOR

def _calculate_scale_modifier(self, video_height: int) -> float:
"""Calculates a scale modifier based on video height relative to reference."""
modifier = video_height / self.REFERENCE_VIDEO_HEIGHT
return max(self.MIN_SCALE_MODIFIER, min(self.MAX_SCALE_MODIFIER, modifier))

def append_css(self, css: str):
self._custom_css += css
Expand All @@ -49,6 +58,8 @@ def open(self, video_width: int, video_height: int, resources_dir: Optional[Path
if self._page:
raise RuntimeError("Renderer is already open. Call close() first.")

scale_modifier = self._calculate_scale_modifier(video_height)
self._device_scale_factor = self.BASE_DEVICE_SCALE_FACTOR * scale_modifier
calculated_vp_height = max(self.DEFAULT_MIN_VIEWPORT_HEIGHT, int(video_height * self.DEFAULT_VIEWPORT_HEIGHT_RATIO))

self._cache_strategy = cache_strategy
Expand All @@ -66,7 +77,7 @@ def open(self, video_width: int, video_height: int, resources_dir: Optional[Path
" playwright install chromium\n\n"
f"Full error:\n{str(e)}"
) from e
context = self._browser.new_context(device_scale_factor=self.DEFAULT_DEVICE_SCALE_FACTOR, viewport={"width": video_width, "height": calculated_vp_height})
context = self._browser.new_context(device_scale_factor=self._device_scale_factor, viewport={"width": video_width, "height": calculated_vp_height})
self._page = context.new_page()
self._copy_resources_to_tempdir(resources_dir)
path = self._create_html_page()
Expand Down Expand Up @@ -223,7 +234,7 @@ def get_word_size(self, word: Word, line_state: ElementState, word_state: Elemen
cached_width = sum(s.width for s in cached_letters_size.values())
cached_height = max(s.height for s in cached_letters_size.values()) if cached_letters_size else 0
if len(not_cached_letters_size) == 0:
return int(cached_width * self.DEFAULT_DEVICE_SCALE_FACTOR), int(cached_height * self.DEFAULT_DEVICE_SCALE_FACTOR)
return int(cached_width * self._device_scale_factor), int(cached_height * self._device_scale_factor)

script = f"""
([letters, lineCssClasses, wordCssClasses]) => {{
Expand Down Expand Up @@ -256,7 +267,7 @@ def get_word_size(self, word: Word, line_state: ElementState, word_state: Elemen
height = max(cached_height, max(s.height for s in new_letters_size.values()))

# This is not precise, but it is enough to create the basic structure
return int(width * self.DEFAULT_DEVICE_SCALE_FACTOR), int(height * self.DEFAULT_DEVICE_SCALE_FACTOR)
return int(width * self._device_scale_factor), int(height * self._device_scale_factor)

def close(self):
"""Closes Playwright and cleans up resources."""
Expand Down
18 changes: 15 additions & 3 deletions src/pycaps/renderer/pictex_subtitle_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ class PictexSubtitleRenderer(SubtitleRenderer):

DEFAULT_CSS_CLASS_FOR_EACH_WORD: str = "word"
DEFAULT_CSS_CLASS_FOR_EACH_LINE: str = "line"

BASE_SCALE_FACTOR: float = 2.0
REFERENCE_VIDEO_HEIGHT: int = 1280
MIN_SCALE_MODIFIER: float = 0.25
MAX_SCALE_MODIFIER: float = 5.0

def __init__(self):
super().__init__()
self._custom_css: str = ""
Expand All @@ -22,11 +26,19 @@ def __init__(self):
self._original_cwd: Optional[Path] = None
self._cache_strategy = CacheStrategy.CSS_CLASSES_AWARE
self._image_cache: RenderedImageCache = None
self._scale_factor: float = self.BASE_SCALE_FACTOR

def _calculate_scale_modifier(self, video_height: int) -> float:
"""Calculates a scale modifier based on video height relative to reference."""
modifier = video_height / self.REFERENCE_VIDEO_HEIGHT
return max(self.MIN_SCALE_MODIFIER, min(self.MAX_SCALE_MODIFIER, modifier))

def append_css(self, css: str):
self._custom_css += css

def open(self, video_width: int, video_height: int, resources_dir: Optional[Path] = None, cache_strategy: CacheStrategy = CacheStrategy.CSS_CLASSES_AWARE):
scale_modifier = self._calculate_scale_modifier(video_height)
self._scale_factor = self.BASE_SCALE_FACTOR * scale_modifier
self._resources_dir = resources_dir
self._cache_strategy = cache_strategy
self._image_cache = RenderedImageCache(self._custom_css, self._cache_strategy)
Expand Down Expand Up @@ -56,7 +68,7 @@ def render_word(self, index: int, word: Word, state: ElementState, first_n_lette
renderer = Html2Pic(self.get_html(line_css_classes, word_css_classes, text), self._custom_css)
canvas, root_element = renderer.translator.translate(renderer.styled_tree, renderer.font_registry)
try:
image = canvas.render(root_element, crop_mode=CropMode.CONTENT_BOX, scale_factor=2)
image = canvas.render(root_element, crop_mode=CropMode.CONTENT_BOX, scale_factor=self._scale_factor)
pillow_image = image.to_pillow()
self._image_cache.set(index, word.text, all_css_classes, first_n_letters, pillow_image)
self._go_to_original_cwd()
Expand Down Expand Up @@ -87,7 +99,7 @@ def get_word_size(self, word: Word, line_state: ElementState, word_state: Elemen
renderer = Html2Pic(self.get_html(line_css_classes, word_css_classes, word.text), self._custom_css)
canvas, root_element = renderer.translator.translate(renderer.styled_tree, renderer.font_registry)
try:
image = canvas.render(root_element, crop_mode=CropMode.CONTENT_BOX, scale_factor=2)
image = canvas.render(root_element, crop_mode=CropMode.CONTENT_BOX, scale_factor=self._scale_factor)
self._image_cache.set(-1, word.text, all_css_classes, None, image.to_pillow())
self._go_to_original_cwd()
return (image.width, image.height)
Expand Down