Initial player integration using Qt Multimedia
This commit is contained in:
parent
a333def9c1
commit
844c133017
13 changed files with 306 additions and 14 deletions
1
README
1
README
|
@ -10,6 +10,7 @@ Dependencies:
|
||||||
Package Min.Version URL
|
Package Min.Version URL
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
Qt 5.0.2 http://qt-project.org/
|
Qt 5.0.2 http://qt-project.org/
|
||||||
|
Qt Multimedia 5.0.2 http://qt-project.org/
|
||||||
Python 3.3.0 http://python.org/
|
Python 3.3.0 http://python.org/
|
||||||
PyOtherSide 1.0.0 http://thp.io/2011/pyotherside/
|
PyOtherSide 1.0.0 http://thp.io/2011/pyotherside/
|
||||||
gPodder 4.0.0 http://gpodder.org/
|
gPodder 4.0.0 http://gpodder.org/
|
||||||
|
|
14
index.qml
14
index.qml
|
@ -21,9 +21,21 @@ import QtQuick 2.0
|
||||||
import 'qml'
|
import 'qml'
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
color: 'black'
|
color: '#336688'
|
||||||
|
|
||||||
width: 480
|
width: 480
|
||||||
height: 800
|
height: 800
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: 'qml/images/mask.png'
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: 'qml/images/noise.png'
|
||||||
|
fillMode: Image.Tile
|
||||||
|
}
|
||||||
|
|
||||||
Main {}
|
Main {}
|
||||||
}
|
}
|
||||||
|
|
28
index_sailfish.qml
Normal file
28
index_sailfish.qml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gPodder QML UI Reference Implementation
|
||||||
|
* Copyright (c) 2013, Thomas Perl <m@thp.io>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
import 'qml'
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
initialPage: Page {
|
||||||
|
Main {}
|
||||||
|
}
|
||||||
|
}
|
9
main.py
9
main.py
|
@ -202,6 +202,14 @@ class gPotherSide:
|
||||||
self._checking_for_new_episodes = False
|
self._checking_for_new_episodes = False
|
||||||
pyotherside.send('refreshing', False)
|
pyotherside.send('refreshing', False)
|
||||||
|
|
||||||
|
def play_episode(self, episode_id):
|
||||||
|
episode = self._get_episode_by_id(episode_id)
|
||||||
|
return {
|
||||||
|
'source': episode.local_filename(False)
|
||||||
|
if episode.was_downloaded(and_exists=True)
|
||||||
|
else episode.url,
|
||||||
|
}
|
||||||
|
|
||||||
def show_episode(self, episode_id):
|
def show_episode(self, episode_id):
|
||||||
episode = self._get_episode_by_id(episode_id)
|
episode = self._get_episode_by_id(episode_id)
|
||||||
return {
|
return {
|
||||||
|
@ -218,6 +226,7 @@ pyotherside.send('hello', gpodder.__version__, gpodder.__copyright__)
|
||||||
load_podcasts = gpotherside.load_podcasts
|
load_podcasts = gpotherside.load_podcasts
|
||||||
load_episodes = gpotherside.load_episodes
|
load_episodes = gpotherside.load_episodes
|
||||||
show_episode = gpotherside.show_episode
|
show_episode = gpotherside.show_episode
|
||||||
|
play_episode = gpotherside.play_episode
|
||||||
subscribe = gpotherside.subscribe
|
subscribe = gpotherside.subscribe
|
||||||
unsubscribe = gpotherside.unsubscribe
|
unsubscribe = gpotherside.unsubscribe
|
||||||
check_for_episodes = gpotherside.check_for_episodes
|
check_for_episodes = gpotherside.check_for_episodes
|
||||||
|
|
45
qml/ButtonRow.qml
Normal file
45
qml/ButtonRow.qml
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gPodder QML UI Reference Implementation
|
||||||
|
* Copyright (c) 2013, Thomas Perl <m@thp.io>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: buttonRow
|
||||||
|
property var model
|
||||||
|
|
||||||
|
height: 100 * pgst.scalef
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: repeater
|
||||||
|
model: buttonRow.model
|
||||||
|
|
||||||
|
delegate: ButtonArea {
|
||||||
|
height: buttonRow.height
|
||||||
|
width: buttonRow.width / repeater.count
|
||||||
|
onClicked: buttonRow.model[index].clicked()
|
||||||
|
|
||||||
|
PLabel {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: modelData.label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -52,6 +52,17 @@ SlidePage {
|
||||||
title: detailPage.title
|
title: detailPage.title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ButtonArea {
|
||||||
|
width: detailPage.width
|
||||||
|
height: 100 * pgst.scalef
|
||||||
|
onClicked: player.playbackEpisode(detailPage.episode_id)
|
||||||
|
|
||||||
|
PLabel {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: 'Play'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PLabel {
|
PLabel {
|
||||||
id: label
|
id: label
|
||||||
width: parent.width * .8
|
width: parent.width * .8
|
||||||
|
|
|
@ -44,6 +44,7 @@ SlidePage {
|
||||||
PullMenuItem {
|
PullMenuItem {
|
||||||
source: 'images/play.png'
|
source: 'images/play.png'
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
pgst.loadPage('PlayerPage.qml');
|
||||||
episodesPage.unPull();
|
episodesPage.unPull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
qml/Main.qml
18
qml/Main.qml
|
@ -21,25 +21,13 @@
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import io.thp.pyotherside 1.0
|
import io.thp.pyotherside 1.0
|
||||||
|
|
||||||
Rectangle {
|
Item {
|
||||||
id: pgst
|
id: pgst
|
||||||
property bool ready: false
|
property bool ready: false
|
||||||
|
|
||||||
property real scalef: width / 480
|
property real scalef: width / 480
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: '#336688'
|
|
||||||
|
|
||||||
Image {
|
|
||||||
anchors.fill: parent
|
|
||||||
source: 'images/mask.png'
|
|
||||||
}
|
|
||||||
|
|
||||||
Image {
|
|
||||||
anchors.fill: parent
|
|
||||||
source: 'images/noise.png'
|
|
||||||
fillMode: Image.Tile
|
|
||||||
}
|
|
||||||
|
|
||||||
function update(page, x) {
|
function update(page, x) {
|
||||||
var index = -1;
|
var index = -1;
|
||||||
|
@ -97,6 +85,10 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Player {
|
||||||
|
id: player
|
||||||
|
}
|
||||||
|
|
||||||
PBusyIndicator {
|
PBusyIndicator {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
visible: !pgst.ready
|
visible: !pgst.ready
|
||||||
|
|
53
qml/PSlider.qml
Normal file
53
qml/PSlider.qml
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gPodder QML UI Reference Implementation
|
||||||
|
* Copyright (c) 2013, Thomas Perl <m@thp.io>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: slider
|
||||||
|
|
||||||
|
property real value
|
||||||
|
property real min: 0.0
|
||||||
|
property real max: 1.0
|
||||||
|
|
||||||
|
signal valueChangeRequested(real newValue)
|
||||||
|
|
||||||
|
color: '#aa000000'
|
||||||
|
|
||||||
|
height: 50 * pgst.scalef
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: slider.valueChangeRequested(min + (max - min) * (mouse.x / width))
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: parent.height * 0.9
|
||||||
|
width: height
|
||||||
|
|
||||||
|
color: '#aaffffff'
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: parent.width * (parent.value - parent.min) / (parent.max - parent.min)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
qml/Player.qml
Normal file
37
qml/Player.qml
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gPodder QML UI Reference Implementation
|
||||||
|
* Copyright (c) 2013, Thomas Perl <m@thp.io>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
import QtMultimedia 5.0
|
||||||
|
|
||||||
|
MediaPlayer {
|
||||||
|
id: player
|
||||||
|
|
||||||
|
property int episode
|
||||||
|
property var queue: ([])
|
||||||
|
|
||||||
|
function playbackEpisode(episode_id) {
|
||||||
|
player.episode = episode_id;
|
||||||
|
py.call('main.play_episode', [episode_id], function (episode) {
|
||||||
|
player.source = episode.source;
|
||||||
|
player.play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
83
qml/PlayerPage.qml
Normal file
83
qml/PlayerPage.qml
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gPodder QML UI Reference Implementation
|
||||||
|
* Copyright (c) 2013, Thomas Perl <m@thp.io>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
import 'constants.js' as Constants
|
||||||
|
|
||||||
|
SlidePage {
|
||||||
|
id: playerPage
|
||||||
|
|
||||||
|
property string episodeTitle
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
py.call('main.show_episode', [player.episode], function (episode) {
|
||||||
|
playerPage.episodeTitle = episode.title;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: flickable
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
contentWidth: column.width
|
||||||
|
contentHeight: column.height + column.spacing
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: column
|
||||||
|
|
||||||
|
width: playerPage.width
|
||||||
|
spacing: 10 * pgst.scalef
|
||||||
|
|
||||||
|
SlidePageHeader {
|
||||||
|
title: 'Now playing'
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonRow {
|
||||||
|
width: playerPage.width
|
||||||
|
model: [
|
||||||
|
{ label: 'Play', clicked: function() {
|
||||||
|
player.play();
|
||||||
|
}},
|
||||||
|
{ label: 'Pause', clicked: function() {
|
||||||
|
player.pause();
|
||||||
|
}},
|
||||||
|
{ label: 'Details', clicked: function() {
|
||||||
|
pgst.loadPage('EpisodeDetail.qml', {
|
||||||
|
episode_id: player.episode,
|
||||||
|
title: playerPage.episodeTitle
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
PSlider {
|
||||||
|
width: playerPage.width
|
||||||
|
value: player.playbackRate
|
||||||
|
min: 0.5
|
||||||
|
max: 3.0
|
||||||
|
onValueChangeRequested: {
|
||||||
|
player.playbackRate = newValue
|
||||||
|
value = player.playbackRate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,6 +68,10 @@ SlidePage {
|
||||||
PullMenu {
|
PullMenu {
|
||||||
PullMenuItem {
|
PullMenuItem {
|
||||||
source: 'images/play.png'
|
source: 'images/play.png'
|
||||||
|
onClicked: {
|
||||||
|
pgst.loadPage('PlayerPage.qml');
|
||||||
|
podcastsPage.unPull();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PullMenuItem {
|
PullMenuItem {
|
||||||
|
|
|
@ -181,6 +181,22 @@ SlidePage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ButtonArea {
|
||||||
|
onClicked: pgst.loadPage('PlayerPage.qml');
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
left: recommendationsPane.left
|
||||||
|
right: recommendationsPane.right
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 100 * pgst.scalef
|
||||||
|
|
||||||
|
PLabel {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: 'Now playing'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ButtonArea {
|
ButtonArea {
|
||||||
onClicked: pgst.loadPage('Settings.qml');
|
onClicked: pgst.loadPage('Settings.qml');
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue