Fix position saving and other minor issues

This commit is contained in:
Jan Hamal Dvořák 2025-03-09 17:41:42 +01:00
parent 0b0b491195
commit fa24847dce
3 changed files with 47 additions and 30 deletions

View file

@ -70,24 +70,24 @@ class MainWindow(Gtk.ApplicationWindow):
self.video_widget.set_vexpand(True) self.video_widget.set_vexpand(True)
self.video_widget.set_hexpand(True) self.video_widget.set_hexpand(True)
overlay_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) video_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
overlay_box.set_name("black-overlay") video_box.set_name("black-overlay")
overlay_box.set_vexpand(True) video_box.set_vexpand(True)
overlay_box.set_hexpand(True) video_box.set_hexpand(True)
# Create an overlay container # Create an overlay container
overlay = Gtk.Overlay() overlay = Gtk.Overlay()
overlay.set_child(self.video_widget) overlay.set_child(self.video_widget)
overlay.add_overlay(self.overlay_label) overlay.add_overlay(self.overlay_label)
overlay_box.append(overlay) video_box.append(overlay)
# Setup video player # Setup video player
self.video_player = VideoPlayer(self.video_widget) self.video_player = VideoPlayer(self.video_widget)
# Add both main menu and overlay to stack # Add both main menu and overlay to stack
self.stack.add_named(main_box, "menu") self.stack.add_named(main_box, "menu")
self.stack.add_named(overlay_box, "overlay") self.stack.add_named(video_box, "player")
self.stack.set_visible_child_name("menu") self.stack.set_visible_child_name("menu")
# Create a grid to handle the 1:2 ratio # Create a grid to handle the 1:2 ratio
@ -139,8 +139,9 @@ class MainWindow(Gtk.ApplicationWindow):
# Start playing the video # Start playing the video
track = file_item.load_attribute("subtitle_track", -2) track = file_item.load_attribute("subtitle_track", -2)
self.video_player.play(file_item.full_path, position, track) self.video_player.play(file_item.full_path, position, track)
self.last_position_save = self.now
self.stack.set_visible_child_name("overlay") self.stack.set_visible_child_name("player")
self.show_overlay_text(f"Playing: {file_item.name}") self.show_overlay_text(f"Playing: {file_item.name}")
self.list_view.connect("activate", on_activate) self.list_view.connect("activate", on_activate)
@ -271,7 +272,7 @@ class MainWindow(Gtk.ApplicationWindow):
"""Toggle between play and pause states""" """Toggle between play and pause states"""
self.video_player.toggle_play_pause() self.video_player.toggle_play_pause()
def _on_video_key_pressed( def _on_player_key_pressed(
self, self,
keyval: int, keyval: int,
keycode: int, keycode: int,
@ -313,8 +314,16 @@ class MainWindow(Gtk.ApplicationWindow):
return True return True
elif keyval == Gdk.keyval_from_name("j"): elif keyval == Gdk.keyval_from_name("j"):
msg = self.video_player.cycle_subtitles() has_subs, index, lang = self.video_player.cycle_subtitles()
self.show_overlay_text(msg)
if has_subs:
if index:
self.show_overlay_text(f"Subtitles #{index} ({lang})")
else:
self.show_overlay_text("Subtitles turned off")
else:
self.show_overlay_text("No subtitles available")
return True return True
return False return False
@ -368,8 +377,8 @@ class MainWindow(Gtk.ApplicationWindow):
state: Gdk.ModifierType, state: Gdk.ModifierType,
) -> bool: ) -> bool:
# Handle keys differently based on which view is active # Handle keys differently based on which view is active
if self.stack.get_visible_child_name() == "overlay": if self.stack.get_visible_child_name() == "player":
return self._on_video_key_pressed(keyval, keycode, state) return self._on_player_key_pressed(keyval, keycode, state)
else: else:
return self._on_menu_key_pressed(keyval, keycode, state) return self._on_menu_key_pressed(keyval, keycode, state)

View file

@ -28,7 +28,7 @@ listview > row {
color: white; color: white;
font-size: 24px; font-size: 24px;
font-family: monospace; font-family: monospace;
background-color: rgba(64, 64, 64, 0.25); background-color: rgba(32, 32, 32, 0.5);
padding: 24px; padding: 24px;
border-radius: 8px; border-radius: 8px;
margin: 32px; margin: 32px;

View file

@ -7,16 +7,21 @@ import gi
gi.require_version("Gtk", "4.0") gi.require_version("Gtk", "4.0")
gi.require_version("Gst", "1.0") gi.require_version("Gst", "1.0")
from gi.repository import Gst, Gtk # NOQA: E402 gi.require_version("GObject", "2.0")
from gi.repository import GObject, Gst, Gtk # NOQA: E402 # NOQA: E402
class VideoPlayer: class VideoPlayer(GObject.Object):
pipeline: Gst.Pipeline pipeline: Gst.Pipeline
playbin: Gst.Element playbin: Gst.Element
is_playing: bool
__gtype_name__ = "VideoPlayer"
is_playing = GObject.Property(type=bool, default=False)
is_paused = GObject.Property(type=bool, default=True)
def __init__(self, picture: Gtk.Picture): def __init__(self, picture: Gtk.Picture):
self.is_playing = False super().__init__()
self.pipeline = Gst.Pipeline.new("video-player") self.pipeline = Gst.Pipeline.new("video-player")
@ -55,11 +60,11 @@ class VideoPlayer:
flags &= ~0x00000004 # TEXT flag flags &= ~0x00000004 # TEXT flag
self.playbin.set_property("flags", flags) self.playbin.set_property("flags", flags)
if position: # Pause and wait for it to complete
# Pause and wait for it to complete self.pipeline.set_state(Gst.State.PAUSED)
self.pipeline.set_state(Gst.State.PAUSED) self.pipeline.get_state(Gst.CLOCK_TIME_NONE)
self.pipeline.get_state(Gst.CLOCK_TIME_NONE)
if position:
# Seek to saved position # Seek to saved position
self.pipeline.seek_simple( self.pipeline.seek_simple(
Gst.Format.TIME, Gst.Format.TIME,
@ -69,20 +74,24 @@ class VideoPlayer:
# Start playing # Start playing
self.pipeline.set_state(Gst.State.PLAYING) self.pipeline.set_state(Gst.State.PLAYING)
self.is_playing = True self.set_property("is-playing", True)
self.set_property("is-paused", False)
def stop(self) -> None: def stop(self) -> None:
"""Stop playback and release resources""" """Stop playback and release resources"""
self.pipeline.set_state(Gst.State.NULL) self.pipeline.set_state(Gst.State.NULL)
self.is_playing = False self.set_property("is-paused", True)
self.set_property("is-playing", False)
def toggle_play_pause(self) -> None: def toggle_play_pause(self) -> None:
"""Toggle between play and pause states""" """Toggle between play and pause states"""
_, state, _ = self.pipeline.get_state(0) _, state, _ = self.pipeline.get_state(0)
if state == Gst.State.PLAYING: if state == Gst.State.PLAYING:
self.pipeline.set_state(Gst.State.PAUSED) self.pipeline.set_state(Gst.State.PAUSED)
self.set_property("is-paused", True)
else: else:
self.pipeline.set_state(Gst.State.PLAYING) self.pipeline.set_state(Gst.State.PLAYING)
self.set_property("is-paused", False)
def seek_relative(self, offset: float) -> None: def seek_relative(self, offset: float) -> None:
"""Seek relative to current position by offset seconds""" """Seek relative to current position by offset seconds"""
@ -128,7 +137,7 @@ class VideoPlayer:
success, duration = self.pipeline.query_duration(Gst.Format.TIME) success, duration = self.pipeline.query_duration(Gst.Format.TIME)
return duration if success else None return duration if success else None
def cycle_subtitles(self) -> str: def cycle_subtitles(self) -> tuple[bool, int, str]:
"""Cycle through available subtitle tracks, including off state""" """Cycle through available subtitle tracks, including off state"""
flags = self.playbin.get_property("flags") flags = self.playbin.get_property("flags")
@ -136,25 +145,24 @@ class VideoPlayer:
n_text = self.playbin.get_property("n-text") n_text = self.playbin.get_property("n-text")
if n_text == 0: if n_text == 0:
return "No subtitles available" return False, 0, ""
if not (flags & 0x00000004): # TEXT flag if not (flags & 0x00000004): # TEXT flag
flags |= 0x00000004 flags |= 0x00000004
self.playbin.set_property("flags", flags) self.playbin.set_property("flags", flags)
self.playbin.set_property("current-text", 0) self.playbin.set_property("current-text", 0)
return f"Subtitle track: {self._get_subtitle_info(0)}" return True, 1, self._get_subtitle_lang(0)
if current >= n_text - 1: if current >= n_text - 1:
flags &= ~0x00000004 # TEXT flag flags &= ~0x00000004 # TEXT flag
self.playbin.set_property("flags", flags) self.playbin.set_property("flags", flags)
return "Subtitles: Off" return True, 0, ""
next_track = current + 1 next_track = current + 1
self.playbin.set_property("current-text", next_track) self.playbin.set_property("current-text", next_track)
return f"Subtitle track: {self._get_subtitle_info(next_track)}" return True, next_track + 1, self._get_subtitle_lang(next_track)
def _get_subtitle_info(self, track_index: int) -> str: def _get_subtitle_lang(self, track_index: int) -> str:
"""Get subtitle track info including language if available"""
caps: Gst.TagList | None = self.playbin.emit("get-text-tags", track_index) caps: Gst.TagList | None = self.playbin.emit("get-text-tags", track_index)
if not caps: if not caps:
return str(track_index) return str(track_index)