Compare commits

..

No commits in common. "cf529cae28d9879a7411ba65b030d2780725af11" and "18c1e6c4a7d5b5fc3fe612fc942f080354dddcce" have entirely different histories.

View file

@ -3,7 +3,7 @@ from __future__ import annotations
import os
import sys
from pathlib import Path
from typing import Any, cast, overload
from typing import Any, TypeVar, cast, overload
import gi
@ -15,6 +15,8 @@ gi.require_version("Gst", "1.0")
gi.require_version("Pango", "1.0")
from gi.repository import Gdk, Gio, Gst, Gtk, Pango # NOQA: E402
_T = TypeVar("_T")
class MainWindow(Gtk.ApplicationWindow):
file_info_label: Gtk.Label
@ -24,7 +26,6 @@ class MainWindow(Gtk.ApplicationWindow):
selection_model: Gtk.SingleSelection
video_widget: Gtk.Picture
pipeline: Gst.Pipeline
playbin: Gst.Element
overlay_tick_callback_id: int
overlay_label: Gtk.Label
overlay_hide_time: float
@ -93,14 +94,12 @@ class MainWindow(Gtk.ApplicationWindow):
if not playbin:
raise RuntimeError("Failed to create playbin element")
self.playbin = playbin
video_sink = Gst.ElementFactory.make("gtk4paintablesink", "gtk4paintablesink")
if not video_sink:
raise RuntimeError("Failed to create gtk4paintablesink element")
self.playbin.set_property("video-sink", video_sink)
self.pipeline.add(self.playbin)
playbin.set_property("video-sink", video_sink)
self.pipeline.add(playbin)
# Link video widget to sink
paintable = video_sink.get_property("paintable")
@ -155,20 +154,24 @@ class MainWindow(Gtk.ApplicationWindow):
position = self._load_attribute("position", 0)
# Start playing the video
playbin = self.pipeline.get_by_name("playbin")
if not playbin:
return
full_path = os.path.abspath(file_item.full_path)
self.playbin.set_property("uri", f"file://{full_path}")
playbin.set_property("uri", f"file://{full_path}")
track = self._load_attribute("subtitle_track", -2)
if track >= 0:
flags = self.playbin.get_property("flags")
flags = playbin.get_property("flags")
flags |= 0x00000004 # TEXT flag
self.playbin.set_property("flags", flags)
self.playbin.set_property("current-text", track)
playbin.set_property("flags", flags)
playbin.set_property("current-text", track)
elif track == -1:
flags = self.playbin.get_property("flags")
flags = playbin.get_property("flags")
flags &= ~0x00000004 # TEXT flag
self.playbin.set_property("flags", flags)
playbin.set_property("flags", flags)
if position:
# Pause and wait for it to complete.
@ -302,20 +305,6 @@ class MainWindow(Gtk.ApplicationWindow):
self._seek_relative(-60)
return True
elif keyval == Gdk.keyval_from_name("Home"):
self.pipeline.seek_simple(
Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0
)
return True
elif keyval == Gdk.keyval_from_name("End"):
success, duration = self.pipeline.query_duration(Gst.Format.TIME)
if success:
self.pipeline.seek_simple(
Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, duration - 1
)
return True
elif keyval == Gdk.keyval_from_name("j"):
self._cycle_subtitles()
return True
@ -337,6 +326,10 @@ class MainWindow(Gtk.ApplicationWindow):
def _seek_relative(self, offset: int) -> None:
"""Seek relative to current position by offset seconds"""
playbin = self.pipeline.get_by_name("playbin")
if not playbin:
return
# Query current position
success, current = self.pipeline.query_position(Gst.Format.TIME)
if not success:
@ -357,8 +350,12 @@ class MainWindow(Gtk.ApplicationWindow):
def _get_subtitle_info(self, track_index: int) -> str:
"""Get subtitle track info including language if available"""
playbin = self.pipeline.get_by_name("playbin")
if not playbin:
return str(track_index)
# Query the subtitle track's tags
caps: Gst.TagList | None = self.playbin.emit("get-text-tags", track_index)
caps: Gst.TagList | None = playbin.emit("get-text-tags", track_index)
if not caps:
return str(track_index)
@ -368,6 +365,10 @@ class MainWindow(Gtk.ApplicationWindow):
def _save_position(self) -> None:
"""Save current playback position as xattr"""
playbin = self.pipeline.get_by_name("playbin")
if not playbin:
return
success, position = self.pipeline.query_position(Gst.Format.TIME)
success2, duration = self.pipeline.query_duration(Gst.Format.TIME)
@ -411,10 +412,14 @@ class MainWindow(Gtk.ApplicationWindow):
def _cycle_subtitles(self) -> None:
"""Cycle through available subtitle tracks, including off state"""
playbin = self.pipeline.get_by_name("playbin")
if not playbin:
return
# Get current flags and subtitle track
flags = self.playbin.get_property("flags")
current = self.playbin.get_property("current-text")
n_text = self.playbin.get_property("n-text")
flags = playbin.get_property("flags")
current = playbin.get_property("current-text")
n_text = playbin.get_property("n-text")
if n_text == 0:
self.show_overlay_text("No subtitles available")
@ -423,8 +428,8 @@ class MainWindow(Gtk.ApplicationWindow):
# If subtitles are disabled, enable them and set to first track
if not (flags & 0x00000004): # TEXT flag
flags |= 0x00000004
self.playbin.set_property("flags", flags)
self.playbin.set_property("current-text", 0)
playbin.set_property("flags", flags)
playbin.set_property("current-text", 0)
track_info = self._get_subtitle_info(0)
self.show_overlay_text(f"Subtitle track: {track_info}")
self._save_attribute("subtitle_track", 0)
@ -433,14 +438,14 @@ class MainWindow(Gtk.ApplicationWindow):
# If we're on the last track, disable subtitles
if current >= n_text - 1:
flags &= ~0x00000004 # TEXT flag
self.playbin.set_property("flags", flags)
playbin.set_property("flags", flags)
self.show_overlay_text("Subtitles: Off")
self._save_attribute("subtitle_track", -1)
return
# Otherwise cycle to next track
next_track = current + 1
self.playbin.set_property("current-text", next_track)
playbin.set_property("current-text", next_track)
track_info = self._get_subtitle_info(next_track)
self.show_overlay_text(f"Subtitle track: {track_info}")
self._save_attribute("subtitle_track", next_track)