@@ 3,11 3,10 @@ gi.require_version("WebKit", "6.0")
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
gi.require_version('Gst', '1.0')
-from gi.repository import Gtk, Adw, WebKit, GLib, Gst
+from gi.repository import Gtk, Adw, WebKit, GLib, Gst, Gio
from unidecode import unidecode
from gettext import gettext as _
from datetime import datetime
-
from enum import Enum, auto
from melon.servers import Stream
@@ 15,6 14,19 @@ from melon.widgets.iconbutton import IconButton
from melon.widgets.preferencerow import PreferenceRow, PreferenceType, Preference
from melon.utils import pass_me
+def format_seconds(secs:float) -> str:
+ secs = int(secs)
+ seconds = secs % 60
+ mins_hours = secs // 60
+ minutes = mins_hours % 60
+ hours = mins_hours // 60
+ h = ""
+ if hours != 0:
+ h = f"{hours:02}:"
+ m = f"{minutes:02}"
+ s = f"{seconds:02}"
+ return f"{h}{m}:{s}"
+
def clamp(lower, value, upper):
return max(lower, min(value, upper))
@@ 211,17 223,16 @@ class VideoPlayerBase(Gtk.Overlay):
self.controls.append(self.duration_display)
self._update_playhead()
- # add quality selector
- # TODO: consider merging this into a 2d menu
- # which also controls playback speed
- self.quality_menu = Gtk.MenuButton()
- self.quality_menu.set_tooltip_text(_("Video quality"))
- self.quality_menu.set_icon_name("network-cellular-signal-good-symbolic")
- self.controls.append(self.quality_menu)
- self.quality_popover = Gtk.Popover()
- # show popover above toolbar
- self.quality_menu.set_direction(Gtk.ArrowType.UP)
- self.quality_menu.set_popover(self.quality_popover)
+ # options menu
+ # generated by _build_menu
+ # includes quality selector
+ self.options_menu = Gtk.MenuButton()
+ self.options_menu.set_tooltip_text(_("Stream options"))
+ self.options_menu.set_icon_name("applications-system-symbolic")
+ self.controls.append(self.options_menu)
+ self.options_menu.set_direction(Gtk.ArrowType.UP)
+ self.reg_action("quality", self._find_stream, "s")
+ self._build_menu()
# select_stream also call update_quality_list
# unreachable if len(streams) == 0
# which is why we don't need an additinal check
@@ 274,6 285,9 @@ class VideoPlayerBase(Gtk.Overlay):
self.change_brightness(self.brightness)
self.change_volume(self.volume)
+ def reg_action(self, name, func, variant=None):
+ self.install_action(name, variant, func)
+
def connect_update(self, callback=None):
self.update_callback = callback
def connect_ended(self, callback=None):
@@ 416,27 430,37 @@ class VideoPlayerBase(Gtk.Overlay):
def seek_backwards(self, delta=30):
self.seek(-delta)
- def _update_quality_list(self):
- # TODO consider making this scrollable
- ls = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
+ def _build_menu(self):
+ model = Gio.Menu()
+ quality_model = Gio.Menu()
# TODO: figure out if we can extract the qualities from m3u8 playlists
# maybe using playbin3?
for stream in self.streams:
- selected = stream == self.stream
- row = Adw.ActionRow()
- # TODO: consider giving streams a name
- # and use the name as title
- # and the quality as subtitle
- row.set_title(stream.quality)
- row.set_activatable(True)
- row.connect("activated", pass_me(lambda _, s: self.select_stream_wrapper(s), stream))
- # TODO: add suffix that indicates if this resolution is selected
- ls.append(row)
- self.quality_popover.set_child(ls)
-
- def _select_stream_wrapper(self, stream):
- self.quality_menu.popdown()
- self.select_stream(stream)
+ item = Gio.MenuItem.new(stream.quality, None)
+ item.set_action_and_target_value("quality", GLib.Variant("s", stream.quality))
+ quality_model.append_item(item)
+ model.append_submenu(_("Resolution"), quality_model)
+ self.options_menu.set_menu_model(model)
+
+ def select_stream(self, stream):
+ self.stream = stream
+ self.source.set_state(Gst.State.NULL)
+ self.source.set_property("uri", stream.url)
+ if not self.position is None:
+ self.target_position = self.position
+ self.play()
+ self._build_menu()
+
+ def _find_stream(self, widg, action:str, variant):
+ if action != "quality":
+ return
+ # get resolution id
+ resolution = variant[:]
+ # find stream associated with resolution
+ for stream in self.streams:
+ if stream.quality == resolution:
+ self.select_stream(stream)
+ break
def _start_loop(self):
if not self.stopped:
@@ 554,26 578,6 @@ class VideoPlayerBase(Gtk.Overlay):
else:
self.pause()
- def select_stream(self, stream):
- # TODO: confirm that this is acctually working
- self.stream = stream
- self.source.set_property("uri", stream.url)
- self.play()
- self._update_quality_list()
-
-def format_seconds(secs:float) -> str:
- secs = int(secs)
- seconds = secs % 60
- mins_hours = secs // 60
- minutes = mins_hours % 60
- hours = mins_hours // 60
- h = ""
- if hours != 0:
- h = f"{hours:02}:"
- m = f"{minutes:02}"
- s = f"{seconds:02}"
- return f"{h}{m}:{s}"
-
class VideoPlayer(Gtk.Stack):
def __init__(self, streams: list[Stream], *args, **kwargs):
super().__init__(*args, **kwargs)