emmental/audio/player.py

132 lines
4.4 KiB
Python

# Copyright 2021 (c) Anna Schumaker.
from . import artwork
from . import controls
from . import nowplaying
from . import seeker
from lib import publisher
from lib import settings
from gi.repository import Gst, GLib
import tagdb
class Player:
def __init__(self):
self.video = Gst.ElementFactory.make("fakesink")
self.playbin = Gst.ElementFactory.make("playbin")
self.playbin.set_property("video-sink", self.video)
self.bus = self.playbin.get_bus()
self.bus.add_signal_watch()
self.bus.connect("message::eos", self.next)
self.bus.connect("message::state-changed", self.on_state_changed)
self.bus.connect("message::tag", self.on_tag)
self.Controls = controls.Controls()
self.Controls.connect("previous", self.previous)
self.Controls.connect("play", self.play)
self.Controls.connect("pause", self.pause)
self.Controls.connect("next", self.next)
self.Controls.connect("volume-changed", self.volume_changed)
self.NowPlaying = nowplaying.NowPlaying()
self.Artwork = artwork.Artwork()
self.Seeker = seeker.Seeker()
self.Seeker.connect(self.seeked)
self.Controls.sizegroup.add_widget(self.Seeker.scale)
GLib.timeout_add(250, self.update_progress)
self.TrackChanged = publisher.Publisher()
self.track = tagdb.Tracks[settings.get_int("audio.trackid")]
self.load_set_state(self.track, Gst.State.PAUSED)
if self.track:
self.track.add_to_playlist("Previous")
self.volume_changed()
def duration(self):
(res, dur) = self.playbin.query_duration(Gst.Format.TIME)
return dur if res == True else 0
def get_state(self):
(ret, state, pending) = self.playbin.get_state(Gst.CLOCK_TIME_NONE)
if ret == Gst.StateChangeReturn.SUCCESS:
return state
return ret
def load_track(self, track):
prev = self.track
self.track = track
if track is not None:
settings.set("audio.trackid", track.trackid)
uri = track.filepath().absolute().as_uri()
self.playbin.set_property("uri", uri)
self.TrackChanged.publish(prev, self.track)
def load_set_state(self, track, state):
self.playbin.set_state(Gst.State.READY)
self.load_track(track)
if track is not None:
self.playbin.set_state(state)
def next(self, *args):
duration = self.duration()
if duration > 0 and (self.runtime() / duration) > (2 / 3):
self.track.played()
(track, cont) = tagdb.Stack.next()
state = Gst.State.PLAYING if cont else Gst.State.PAUSED
self.load_set_state(track, state)
def on_state_changed(self, bus, message):
(old, new, pending) = message.parse_state_changed()
self.Controls.set_state(new)
def on_tag(self, bus, message):
taglist = message.parse_tag()
(res, title) = taglist.get_string("title")
if res == True:
self.NowPlaying.set_title(title)
(res, artist) = taglist.get_string("artist")
if res == True:
self.NowPlaying.set_artist(artist)
(res, sample) = taglist.get_sample("image")
if res == True:
self.Artwork.set_from_sample(sample)
else:
self.Artwork.reset()
def pause(self, *args):
self.playbin.set_state(Gst.State.PAUSED)
def play(self, *args):
self.playbin.set_state(Gst.State.PLAYING)
def play_track(self, track):
if track == self.track:
return False
self.load_set_state(track, Gst.State.PLAYING)
return True
def position(self):
(res, pos) = self.playbin.query_position(Gst.Format.TIME)
return pos if res == True else 0
def previous(self, *args):
self.play_track(tagdb.Stack.previous())
def runtime(self):
if self.playbin.clock == None:
return 0
return self.playbin.clock.get_time() - self.playbin.base_time
def seeked(self, scale, scroll, value):
self.playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH,
value * Gst.SECOND)
def update_progress(self):
self.Seeker.configure(self.position() / Gst.SECOND,
self.duration() / Gst.SECOND)
return GLib.SOURCE_CONTINUE
def volume_changed(self, *args):
self.playbin.set_property("volume", self.Controls.menu.volume.get_value())