From 90014362565dd737b84cb58439f2f79e5e511bc4 Mon Sep 17 00:00:00 2001 From: Thomas Perl Date: Sun, 2 Feb 2014 15:44:18 +0100 Subject: [PATCH] Playback progress and position handling --- common/GPodderCore.qml | 2 ++ common/GPodderPlayback.qml | 73 ++++++++++++++++++++++++++++++++++++-- main.py | 16 +++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/common/GPodderCore.qml b/common/GPodderCore.qml index 7fcb7e5..1511ea9 100644 --- a/common/GPodderCore.qml +++ b/common/GPodderCore.qml @@ -28,6 +28,7 @@ Python { property bool ready: false signal downloading(int episode_id) signal downloadProgress(int episode_id, real progress) + signal playbackProgress(int episode_id, real progress) signal downloaded(int episode_id) signal deleted(int episode_id) signal isNewChanged(int episode_id, bool is_new) @@ -41,6 +42,7 @@ Python { setHandler('downloading', py.downloading); setHandler('download-progress', py.downloadProgress); + setHandler('playback-progress', py.playbackProgress); setHandler('downloaded', py.downloaded); setHandler('deleted', py.deleted); setHandler('is-new-changed', py.isNewChanged); diff --git a/common/GPodderPlayback.qml b/common/GPodderPlayback.qml index f082c6a..f39305c 100644 --- a/common/GPodderPlayback.qml +++ b/common/GPodderPlayback.qml @@ -24,15 +24,84 @@ import QtMultimedia 5.0 MediaPlayer { id: player - property int episode + property int episode: 0 property var queue: ([]) property bool isPlaying: playbackState == MediaPlayer.PlayingState + property bool inhibitPositionEvents: false + property bool seekAfterPlay: false + property int seekTargetSeconds: 0 + property int lastPosition: 0 + property int lastDuration: 0 + property int playedFrom: 0 + function playbackEpisode(episode_id) { - player.episode = episode_id; + if (episode == episode_id) { + // If the episode is already loaded, just start playing + play(); + return; + } + + // First, make sure we stop any seeking / position update events + sendPositionToCore(lastPosition); + player.inhibitPositionEvents = true; + player.stop(); + py.call('main.play_episode', [episode_id], function (episode) { + // Load media / prepare and start playback + player.episode = episode_id; player.source = episode.source; + player.seekTargetSeconds = episode.position; + seekAfterPlay = true; + player.play(); }); } + + function seekAndSync(target_position) { + sendPositionToCore(lastPosition); + seek(target_position); + playedFrom = target_position; + } + + onPlaybackStateChanged: { + if (playbackState == MediaPlayer.PlayingState) { + if (seekAfterPlay) { + // A seek was scheduled, execute now that we're playing + player.inhibitPositionEvents = false; + player.seek(seekTargetSeconds * 1000); + player.playedFrom = seekTargetSeconds * 1000; + seekAfterPlay = false; + } else { + player.playedFrom = position; + } + } else { + sendPositionToCore(lastPosition); + } + } + + function sendPositionToCore(positionToSend) { + if (episode != 0 && !inhibitPositionEvents) { + var begin = playedFrom / 1000; + var end = positionToSend / 1000; + var duration = ((lastDuration > 0) ? lastDuration : 0) / 1000; + var diff = end - begin; + + // Only send playback events if they are 2 seconds or longer + // (all other events might just be seeking events or wrong ones) + if (diff >= 2) { + py.call('main.report_playback_event', [episode, begin, end, duration]); + } + } + } + + onPositionChanged: { + if (isPlaying && !inhibitPositionEvents) { + lastPosition = position; + lastDuration = duration; + + // Directly update the playback progress in the episode list + py.playbackProgress(episode, position / duration); + } + } } diff --git a/main.py b/main.py index cb2ea8e..fb7126d 100644 --- a/main.py +++ b/main.py @@ -93,6 +93,12 @@ class gPotherSide: return '' return 'file://' + filename + def _get_playback_progress(self, episode): + if episode.total_time > 0 and episode.current_position > 0: + return float(episode.current_position) / float(episode.total_time) + + return 0 + def convert_podcast(self, podcast): total, deleted, new, downloaded, unplayed = podcast.get_statistics() @@ -118,6 +124,7 @@ class gPotherSide: 'progress': episode.download_progress(), 'downloadState': episode.state, 'isNew': episode.is_new, + 'playbackProgress': self._get_playback_progress(episode), } def load_episodes(self, id): @@ -254,8 +261,16 @@ class gPotherSide: 'source': episode.local_filename(False) if episode.state == gpodder.STATE_DOWNLOADED else episode.url, + 'position': episode.current_position, + 'total': episode.total_time, } + def report_playback_event(self, episode_id, position_from, position_to, duration): + episode = self._get_episode_by_id(episode_id) + print('Played', episode.title, 'from', position_from, 'to', position_to, 'of', duration) + episode.report_playback_event(position_from, position_to, duration) + pyotherside.send('playback-progress', episode_id, self._get_playback_progress(episode)) + def show_episode(self, episode_id): episode = self._get_episode_by_id(episode_id) if episode is None: @@ -287,3 +302,4 @@ delete_episode = gpotherside.delete_episode toggle_new = gpotherside.toggle_new rename_podcast = gpotherside.rename_podcast change_section = gpotherside.change_section +report_playback_event = gpotherside.report_playback_event