~comcloudway/melon

3b748d82b0641fc22e8517b5b0a0813c28343c73 — Jakob Meier 6 months ago c9e1e0d dev-gstreamer
add resolution picker
1 files changed, 55 insertions(+), 51 deletions(-)

M melon/widgets/player.py
M melon/widgets/player.py => melon/widgets/player.py +55 -51
@@ 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)