rind: Add seek support
I put a smaller progress bar into the header area that users can use for seeking or checking the current position. I also add two labels to show time played and time remaining in the current track. Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
5885228fd1
commit
a9f48534e5
48
emmental.ui
48
emmental.ui
|
@ -2,6 +2,11 @@
|
|||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<object class="GtkAdjustment" id="progress_adjustment">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">5</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkApplicationWindow" id="window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -12,6 +17,7 @@
|
|||
<object class="GtkHeaderBar" id="header">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">5</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="play_button">
|
||||
|
@ -140,6 +146,48 @@
|
|||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="duration">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="label" translatable="yes">0:00</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScale" id="progress_scale">
|
||||
<property name="width_request">125</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">progress_adjustment</property>
|
||||
<property name="show_fill_level">True</property>
|
||||
<property name="fill_level">100</property>
|
||||
<property name="digits">-1</property>
|
||||
<property name="draw_value">False</property>
|
||||
<property name="value_pos">left</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="position">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">0:00</property>
|
||||
<property name="xalign">1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
|
|
25
rind/gst.py
25
rind/gst.py
|
@ -1,15 +1,20 @@
|
|||
# Copyright 2019 (c) Anna Schumaker.
|
||||
from . import gtk
|
||||
import curds
|
||||
import datetime
|
||||
import gi
|
||||
import sys
|
||||
gi.require_version("Gst", "1.0")
|
||||
from gi.repository import Gst, GLib
|
||||
|
||||
Gst.init(sys.argv)
|
||||
Adjustment = gtk.Builder.get_object("progress_adjustment")
|
||||
Duration = gtk.Builder.get_object("duration")
|
||||
NextButton = gtk.Builder.get_object("next_button")
|
||||
PauseButton = gtk.Builder.get_object("pause_button")
|
||||
PlayButton = gtk.Builder.get_object("play_button")
|
||||
Position = gtk.Builder.get_object("position")
|
||||
ProgScale = gtk.Builder.get_object("progress_scale")
|
||||
Subtitle = gtk.Builder.get_object("subtitle")
|
||||
Title = gtk.Builder.get_object("title")
|
||||
|
||||
|
@ -24,6 +29,9 @@ class EmmentalAudio:
|
|||
PauseButton.connect("clicked", self.pause)
|
||||
PlayButton.connect( "clicked", self.play)
|
||||
NextButton.connect( "clicked", self.next)
|
||||
ProgScale.connect("change-value", self.seek)
|
||||
|
||||
self.timeout = GLib.timeout_add(100, self.update_progress)
|
||||
|
||||
def duration(self):
|
||||
(res, cur) = self.playbin.query_duration(Gst.Format.TIME)
|
||||
|
@ -60,11 +68,26 @@ class EmmentalAudio:
|
|||
|
||||
def position(self):
|
||||
(res, cur) = self.playbin.query_position(Gst.Format.TIME)
|
||||
return cur if res == True else 0
|
||||
return cur / Gst.SECOND if res == True else 0
|
||||
|
||||
def progress(self):
|
||||
pos = self.position()
|
||||
dur = self.duration()
|
||||
return (pos / dur) if dur > 0 else 0
|
||||
|
||||
def seek(self, range=None, scroll=None, value=0):
|
||||
(res, dur) = self.playbin.query_duration(Gst.Format.TIME)
|
||||
if res == True:
|
||||
pos = (dur * value) / 100.0
|
||||
self.playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, pos)
|
||||
|
||||
def update_progress(self):
|
||||
(p_m, p_s) = divmod(int(self.position()), 60)
|
||||
(d_m, d_s) = divmod(int(self.duration()) - int(self.position()), 60)
|
||||
|
||||
Position.set_text(datetime.time(0, p_m, p_s).strftime("%M:%S"))
|
||||
Duration.set_text(datetime.time(0, d_m, d_s).strftime("-%M:%S"))
|
||||
Adjustment.set_value(self.progress() * 100)
|
||||
return GLib.SOURCE_CONTINUE
|
||||
|
||||
Audio = EmmentalAudio()
|
||||
|
|
|
@ -3,6 +3,7 @@ from . import gtk
|
|||
from . import gst
|
||||
import curds
|
||||
import os
|
||||
import time
|
||||
import unittest
|
||||
from gi.repository import Gst, Gtk, GLib
|
||||
|
||||
|
@ -24,7 +25,7 @@ class TestGst(unittest.TestCase):
|
|||
curds.playlist.library.library_thread.stop()
|
||||
|
||||
def main_loop(self):
|
||||
events = GLib.main_context_default().iteration()
|
||||
events = GLib.main_context_default().iteration(may_block=True)
|
||||
while events == True:
|
||||
events = GLib.main_context_default().iteration(may_block=False)
|
||||
|
||||
|
@ -32,12 +33,20 @@ class TestGst(unittest.TestCase):
|
|||
audio = gst.EmmentalAudio()
|
||||
self.assertIsInstance(audio.playbin, Gst.Element)
|
||||
self.assertIsInstance(audio.bus, Gst.Bus)
|
||||
|
||||
self.assertIsInstance(gst.Adjustment, Gtk.Adjustment)
|
||||
self.assertIsInstance(gst.Duration, Gtk.Label)
|
||||
self.assertIsInstance(gst.NextButton, Gtk.Button)
|
||||
self.assertIsInstance(gst.PauseButton, Gtk.Button)
|
||||
self.assertIsInstance(gst.PlayButton, Gtk.Button)
|
||||
self.assertIsInstance(gst.Position, Gtk.Label)
|
||||
self.assertIsInstance(gst.ProgScale, Gtk.Scale)
|
||||
|
||||
self.assertFalse(gst.PauseButton.is_visible())
|
||||
self.assertTrue( gst.PlayButton.is_visible())
|
||||
self.assertEqual(gst.Adjustment.get_value(), 0.0)
|
||||
self.assertEqual(gst.Position.get_text(), "00:00")
|
||||
self.assertEqual(gst.Duration.get_text(), "-00:00")
|
||||
|
||||
audio.load(None)
|
||||
self.assertFalse(gst.PauseButton.is_visible())
|
||||
|
@ -51,6 +60,11 @@ class TestGst(unittest.TestCase):
|
|||
self.assertFalse(gst.PauseButton.is_visible())
|
||||
self.assertTrue( gst.PlayButton.is_visible())
|
||||
|
||||
audio.seek(value=20)
|
||||
self.assertEqual(gst.Adjustment.get_value(), 0.0)
|
||||
self.assertEqual(gst.Position.get_text(), "00:00")
|
||||
self.assertEqual(gst.Duration.get_text(), "-00:00")
|
||||
|
||||
self.assertEqual(audio.position(), 0)
|
||||
self.assertEqual(audio.duration(), 0)
|
||||
self.assertEqual(audio.progress(), 0)
|
||||
|
@ -73,15 +87,24 @@ class TestGst(unittest.TestCase):
|
|||
self.main_loop()
|
||||
|
||||
self.assertEqual(self.state, Gst.State.PAUSED)
|
||||
self.assertEqual(audio.position(), 0)
|
||||
self.assertEqual(audio.duration(), 10.0)
|
||||
self.assertEqual(gst.Title.get_text(), track['title'])
|
||||
self.assertEqual(gst.Subtitle.get_text(), "by " + track["artist"])
|
||||
|
||||
self.assertEqual(audio.progress(), 0)
|
||||
self.assertEqual(gtk.Builder.get_object("title").get_text(), track['title'])
|
||||
self.assertEqual(gtk.Builder.get_object("subtitle").get_text(), "by " + track["artist"])
|
||||
self.assertEqual(audio.position(), 0)
|
||||
self.assertEqual(gst.Position.get_text(), "00:00")
|
||||
|
||||
while self.state != Gst.State.PLAYING:
|
||||
time.sleep(0.1)
|
||||
self.main_loop()
|
||||
|
||||
self.assertEqual(audio.duration(), 10.0)
|
||||
self.assertEqual(gst.Duration.get_text(), "-00:10")
|
||||
|
||||
GLib.source_remove(audio.timeout)
|
||||
gst.Position.set_text("00:00")
|
||||
gst.Duration.set_text("-00:00")
|
||||
|
||||
def test_gst_controls(self):
|
||||
audio = gst.EmmentalAudio()
|
||||
gst.NextButton.clicked()
|
||||
|
@ -104,6 +127,18 @@ class TestGst(unittest.TestCase):
|
|||
self.assertFalse(gst.PauseButton.is_visible())
|
||||
self.assertTrue( gst.PlayButton.is_visible())
|
||||
|
||||
audio.seek(value=50.0)
|
||||
time.sleep(0.1)
|
||||
self.main_loop()
|
||||
self.assertEqual(audio.duration(), 3.0)
|
||||
self.assertEqual(audio.position(), 1.5)
|
||||
self.assertEqual(gst.Position.get_text(), "00:01")
|
||||
self.assertEqual(gst.Duration.get_text(), "-00:02")
|
||||
|
||||
audio.seek(value=0.0)
|
||||
time.sleep(0.1)
|
||||
self.main_loop()
|
||||
|
||||
gst.PlayButton.clicked()
|
||||
while self.state != Gst.State.PLAYING:
|
||||
self.main_loop()
|
||||
|
@ -113,3 +148,7 @@ class TestGst(unittest.TestCase):
|
|||
gst.PauseButton.clicked()
|
||||
while self.state != Gst.State.PAUSED:
|
||||
self.main_loop()
|
||||
|
||||
GLib.source_remove(audio.timeout)
|
||||
gst.Position.set_text("00:00")
|
||||
gst.Duration.set_text("-00:00")
|
||||
|
|
Loading…
Reference in New Issue