mirror of
https://github.com/beetbox/beets.git
synced 2025-12-25 01:53:31 +01:00
position and seeking
--HG-- extra : convert_revision : svn%3A41726ec3-264d-0410-9c23-a9f1637257cc/trunk%40173
This commit is contained in:
parent
c752f36a4b
commit
efddffb094
2 changed files with 76 additions and 13 deletions
|
|
@ -306,8 +306,6 @@ class BaseServer(object):
|
|||
|
||||
if self.error:
|
||||
yield 'error: ' + self.error
|
||||
|
||||
#fixme Still missing: time, bitrate, audio, updating_db
|
||||
|
||||
def cmd_clearerror(self, conn):
|
||||
"""Removes the persistent error state of the server. This
|
||||
|
|
@ -341,6 +339,7 @@ class BaseServer(object):
|
|||
"""Clear the playlist."""
|
||||
self.playlist = []
|
||||
self.playlist_version += 1
|
||||
self.cmd_stop(conn)
|
||||
|
||||
def cmd_delete(self, conn, index):
|
||||
"""Remove the song at index from the playlist."""
|
||||
|
|
@ -407,7 +406,7 @@ class BaseServer(object):
|
|||
except IndexError:
|
||||
raise ArgumentIndexError()
|
||||
yield self._item_info(track)
|
||||
def cmd_playlistid(self, track_id=-1):
|
||||
def cmd_playlistid(self, conn, track_id=-1):
|
||||
for l in self.cmd_playlistinfo(conn, self._id_to_index(track_id)):
|
||||
yield l
|
||||
|
||||
|
|
@ -464,12 +463,17 @@ class BaseServer(object):
|
|||
def cmd_play(self, conn, index=-1):
|
||||
"""Begin playback, possibly at a specified playlist index."""
|
||||
index = cast_arg(int, index)
|
||||
|
||||
if index < -1 or index > len(self.playlist):
|
||||
raise ArgumentIndexError()
|
||||
|
||||
if index == -1: # No index specified: start where we are.
|
||||
if not self.playlist: # Empty playlist: stop immediately.
|
||||
self.cmd_stop(conn)
|
||||
if self.current_index == -1: # No current song.
|
||||
self.current_index = 0 # Start at the beginning.
|
||||
# If we have a current song, just stay there.
|
||||
|
||||
else: # Start with the specified index.
|
||||
self.current_index = index
|
||||
|
||||
|
|
@ -488,16 +492,15 @@ class BaseServer(object):
|
|||
self.current_index = -1
|
||||
self.paused = False
|
||||
|
||||
def cmd_seek(self, conn, index, time):
|
||||
def cmd_seek(self, conn, index, pos):
|
||||
"""Seek to a specified point in a specified song."""
|
||||
index = cast_arg(int, index)
|
||||
if index < 0 or index >= len(self.playlist):
|
||||
raise ArgumentIndexError()
|
||||
self.current_index = index
|
||||
self.cmd_play(conn)
|
||||
def cmd_seekid(self, track_id, time):
|
||||
def cmd_seekid(self, track_id, pos):
|
||||
index = self._id_to_index(track_id)
|
||||
for l in self.cmd_seek(conn, index, time): yield l
|
||||
for l in self.cmd_seek(conn, index, pos): yield l
|
||||
|
||||
class Connection(object):
|
||||
"""A connection between a client and the server. Handles input and
|
||||
|
|
@ -839,8 +842,15 @@ class Server(BaseServer):
|
|||
for l in super(Server, self).cmd_status(conn): yield l
|
||||
if self.current_index > -1:
|
||||
item = self.playlist[self.current_index]
|
||||
|
||||
yield 'bitrate: ' + str(item.bitrate/1000)
|
||||
yield 'time: 0:' + str(int(item.length)) #fixme
|
||||
#fixme: missing 'audio'
|
||||
|
||||
(pos, total) = self.player.time()
|
||||
yield 'time: ' + str(pos) + ':' + str(total)
|
||||
|
||||
#fixme: also missing 'updating_db'
|
||||
|
||||
|
||||
def cmd_stats(self, conn):
|
||||
# The first three items need to be done more efficiently. The
|
||||
|
|
@ -918,14 +928,21 @@ class Server(BaseServer):
|
|||
|
||||
|
||||
|
||||
# The functions below hook into the half-implementations provided
|
||||
# by the base class. Together, they're enough to implement all
|
||||
# normal playback functionality.
|
||||
# Playback control. The functions below hook into the
|
||||
# half-implementations provided by the base class. Together, they're
|
||||
# enough to implement all normal playback functionality.
|
||||
|
||||
def cmd_play(self, conn, index=-1):
|
||||
new_index = index != -1 and index != self.current_index
|
||||
was_paused = self.paused
|
||||
super(Server, self).cmd_play(conn, index)
|
||||
|
||||
if self.current_index > -1: # Not stopped.
|
||||
self.player.play_file(self.playlist[self.current_index].path)
|
||||
if was_paused and not new_index:
|
||||
# Just unpause.
|
||||
self.player.play()
|
||||
else:
|
||||
self.player.play_file(self.playlist[self.current_index].path)
|
||||
|
||||
def cmd_pause(self, conn, state=None):
|
||||
super(Server, self).cmd_pause(conn, state)
|
||||
|
|
@ -935,8 +952,15 @@ class Server(BaseServer):
|
|||
self.player.play()
|
||||
|
||||
def cmd_stop(self, conn):
|
||||
super(Server, self).cmd_stop()
|
||||
super(Server, self).cmd_stop(conn)
|
||||
self.player.stop()
|
||||
|
||||
def cmd_seek(self, conn, index, pos):
|
||||
"""Seeks to the specified position in the specified song."""
|
||||
index = cast_arg(int, index)
|
||||
pos = cast_arg(int, pos)
|
||||
super(Server, self).cmd_seek(conn, index, pos)
|
||||
self.player.seek(pos)
|
||||
|
||||
|
||||
# When run as a script, just start the server.
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ class GstPlayer(object):
|
|||
# Set up our own stuff.
|
||||
self.playing = False
|
||||
self.finished_callback = finished_callback
|
||||
self.cached_time = None
|
||||
|
||||
def _get_state(self):
|
||||
"""Returns the current state flag of the playbin."""
|
||||
|
|
@ -59,6 +60,7 @@ class GstPlayer(object):
|
|||
if message.type == gst.MESSAGE_EOS:
|
||||
# file finished playing
|
||||
self.playing = False
|
||||
self.cached_time = None
|
||||
if self.finished_callback:
|
||||
self.finished_callback()
|
||||
|
||||
|
|
@ -91,6 +93,8 @@ class GstPlayer(object):
|
|||
def stop(self):
|
||||
"""Halt playback."""
|
||||
self.player.set_state(gst.STATE_NULL)
|
||||
self.playing = False
|
||||
self.cached_time = None
|
||||
|
||||
def run(self):
|
||||
"""Start a new thread for the player.
|
||||
|
|
@ -105,6 +109,41 @@ class GstPlayer(object):
|
|||
loop.run()
|
||||
thread.start_new_thread(start, ())
|
||||
|
||||
def time(self):
|
||||
"""Returns a tuple containing (position, length) where both
|
||||
values are integers in seconds. If no stream is available,
|
||||
returns (0, 0).
|
||||
"""
|
||||
fmt = gst.Format(gst.FORMAT_TIME)
|
||||
try:
|
||||
pos = self.player.query_position(fmt, None)[0]/(10**9)
|
||||
length = self.player.query_duration(fmt, None)[0]/(10**9)
|
||||
self.cached_time = (pos, length)
|
||||
return (pos, length)
|
||||
|
||||
except gst.QueryError:
|
||||
# Stream not ready. For small gaps of time, for instance
|
||||
# after seeking, the time values are unavailable. For this
|
||||
# reason, we cache recent.
|
||||
if self.playing and self.cached_time:
|
||||
return self.cached_time
|
||||
else:
|
||||
return (0, 0)
|
||||
|
||||
def seek(self, position):
|
||||
"""Seeks to position (in seconds)."""
|
||||
cur_pos, cur_len = self.time()
|
||||
if position > cur_len:
|
||||
self.stop()
|
||||
return
|
||||
|
||||
fmt = gst.Format(gst.FORMAT_TIME)
|
||||
ns = position * 10**9 # convert to nanoseconds
|
||||
self.player.seek_simple(fmt, gst.SEEK_FLAG_FLUSH, ns)
|
||||
|
||||
# save new cached time
|
||||
self.cached_time = (position, cur_len)
|
||||
|
||||
def block(self):
|
||||
"""Block until playing finishes."""
|
||||
while self.playing:
|
||||
|
|
|
|||
Loading…
Reference in a new issue