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
|
||||
------------------------------------------------------------
|
||||
Qt 5.0.2 http://qt-project.org/
|
||||
Qt Multimedia 5.0.2 http://qt-project.org/
|
||||
Python 3.3.0 http://python.org/
|
||||
PyOtherSide 1.0.0 http://thp.io/2011/pyotherside/
|
||||
gPodder 4.0.0 http://gpodder.org/
|
||||
|
|
14
index.qml
14
index.qml
|
@ -21,9 +21,21 @@ import QtQuick 2.0
|
|||
import 'qml'
|
||||
|
||||
Rectangle {
|
||||
color: 'black'
|
||||
color: '#336688'
|
||||
|
||||
width: 480
|
||||
height: 800
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: 'qml/images/mask.png'
|
||||
}
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: 'qml/images/noise.png'
|
||||
fillMode: Image.Tile
|
||||
}
|
||||
|
||||
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
|
||||
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):
|
||||
episode = self._get_episode_by_id(episode_id)
|
||||
return {
|
||||
|
@ -218,6 +226,7 @@ pyotherside.send('hello', gpodder.__version__, gpodder.__copyright__)
|
|||
load_podcasts = gpotherside.load_podcasts
|
||||
load_episodes = gpotherside.load_episodes
|
||||
show_episode = gpotherside.show_episode
|
||||
play_episode = gpotherside.play_episode
|
||||
subscribe = gpotherside.subscribe
|
||||
unsubscribe = gpotherside.unsubscribe
|
||||
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
|
||||
}
|
||||
|
||||
ButtonArea {
|
||||
width: detailPage.width
|
||||
height: 100 * pgst.scalef
|
||||
onClicked: player.playbackEpisode(detailPage.episode_id)
|
||||
|
||||
PLabel {
|
||||
anchors.centerIn: parent
|
||||
text: 'Play'
|
||||
}
|
||||
}
|
||||
|
||||
PLabel {
|
||||
id: label
|
||||
width: parent.width * .8
|
||||
|
|
|
@ -44,6 +44,7 @@ SlidePage {
|
|||
PullMenuItem {
|
||||
source: 'images/play.png'
|
||||
onClicked: {
|
||||
pgst.loadPage('PlayerPage.qml');
|
||||
episodesPage.unPull();
|
||||
}
|
||||
}
|
||||
|
|
18
qml/Main.qml
18
qml/Main.qml
|
@ -21,25 +21,13 @@
|
|||
import QtQuick 2.0
|
||||
import io.thp.pyotherside 1.0
|
||||
|
||||
Rectangle {
|
||||
Item {
|
||||
id: pgst
|
||||
property bool ready: false
|
||||
|
||||
property real scalef: width / 480
|
||||
|
||||
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) {
|
||||
var index = -1;
|
||||
|
@ -97,6 +85,10 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
Player {
|
||||
id: player
|
||||
}
|
||||
|
||||
PBusyIndicator {
|
||||
anchors.centerIn: parent
|
||||
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 {
|
||||
PullMenuItem {
|
||||
source: 'images/play.png'
|
||||
onClicked: {
|
||||
pgst.loadPage('PlayerPage.qml');
|
||||
podcastsPage.unPull();
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
onClicked: pgst.loadPage('Settings.qml');
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue