Commit 840de210 authored by Victor Thompson's avatar Victor Thompson Committed by Tarmac
Browse files

* Add support for media-hub background playlists

* Bump framework to 15.04.3-qml
* Bump QtMultimedia import to 5.6. Fixes: https://bugs.launchpad.net/bugs/1251624, https://bugs.launchpad.net/bugs/1480280, https://bugs.launchpad.net/bugs/1500457, https://bugs.launchpad.net/bugs/1534172, https://bugs.launchpad.net/bugs/1534840, https://bugs.launchpad.net/bugs/1535530.

Approved by Jenkins Bot, Victor Thompson.
parents 8548c6ba 587ed728
/*
* Copyright (C) 2013, 2014, 2015
* Copyright (C) 2013, 2014, 2015, 2016
* Andrew Hayzen <ahayzen@gmail.com>
* Daniel Holm <d.holmen@gmail.com>
* Victor Thompson <victor.thompson@gmail.com>
......@@ -25,7 +25,7 @@ import QtGraphicalEffects 1.0
Item {
width: parent.width
property string art // : player.currentMetaFile === "" ? Qt.resolvedUrl("../graphics/music-app-cover@30.png") : player.currentMetaArt
property string art
// dark layer
Rectangle {
......
/*
* Copyright (C) 2013, 2014, 2015
* Copyright (C) 2013, 2014, 2015, 2016
* Andrew Hayzen <ahayzen@gmail.com>
* Daniel Holm <d.holmen@gmail.com>
* Victor Thompson <victor.thompson@gmail.com>
......@@ -25,6 +25,10 @@ MusicListView {
// so we need to expose if in multiselect mode for the header states
state: ViewItems.selectMode ? "multiselectable" : "normal"
// Describes if model.move() should be called when a list item drag is completed
// this is not required on the Queue as onReorder performs playlist.moveItem()
property bool autoModelMove: true
signal clearSelection()
signal closeSelection()
signal reorder(int from, int to)
......@@ -63,7 +67,10 @@ MusicListView {
if (event.status == ListItemDrag.Moving) {
event.accept = false
} else if (event.status == ListItemDrag.Dropped) {
model.move(event.from, event.to, 1);
if (autoModelMove) { // check the model should auto move
model.move(event.from, event.to, 1);
}
reorder(event.from, event.to)
}
}
......
/*
* Copyright (C) 2015
* Copyright (C) 2015, 2016
* Andrew Hayzen <ahayzen@gmail.com>
* Victor Thompson <victor.thompson@gmail.com>
*
......@@ -59,14 +59,14 @@ PageHeadState {
visible: addToQueue
onTriggered: {
var items = []
var items = [];
var indicies = listview.getSelectedIndices();
for (var i=0; i < indicies.length; i++) {
items.push(listview.model.get(indicies[i], listview.model.RoleModelData));
items.push(Qt.resolvedUrl(listview.model.get(indicies[i], listview.model.RoleModelData).filename));
}
trackQueue.appendList(items)
player.mediaPlayer.playlist.addItems(items);
listview.closeSelection()
}
......
/*
* Copyright (C) 2014, 2015
* Copyright (C) 2014, 2015, 2016
* Andrew Hayzen <ahayzen@gmail.com>
* Daniel Holm <d.holmen@gmail.com>
* Victor Thompson <victor.thompson@gmail.com>
......@@ -154,9 +154,6 @@ Item {
contentHubWaitForFile.searchPaths = contentHub.searchPaths;
contentHubWaitForFile.processId = processId;
contentHubWaitForFile.start();
// Stop queue loading in bg
queueLoaderWorker.canLoad = false
} else {
contentHubWaitForFile.searchPaths.push.apply(contentHubWaitForFile.searchPaths, contentHub.searchPaths);
contentHubWaitForFile.count = 0;
......@@ -220,15 +217,21 @@ Item {
else {
stopTimer();
trackQueue.clear();
player.mediaPlayer.playlist.clearWrapper();
for (i=0; i < searchPaths.length; i++) {
model = musicStore.lookup(decodeFileURI(searchPaths[i]))
var items = [];
trackQueue.append(makeDict(model));
for (i=0; i < searchPaths.length; i++) {
// Don't need to check if in ms2 as that is done above
items.push(Qt.resolvedUrl(decodeURIComponent(searchPaths[i])))
}
player.mediaPlayer.playlist.addItems(items);
trackQueueClick(0);
// Show the Now playing page and make sure the track is visible
tabs.pushNowPlaying();
}
}
}
......
/*
* Copyright (C) 2013, 2014, 2015
* Copyright (C) 2013, 2014, 2015, 2016
* Andrew Hayzen <ahayzen@gmail.com>
* Daniel Holm <d.holmen@gmail.com>
* Victor Thompson <victor.thompson@gmail.com>
......@@ -38,14 +38,6 @@ Item {
}
function processAlbum(uri) {
// Stop queue loading in the background
queueLoaderWorker.canLoad = false
if (queueLoaderWorker.processing > 0) {
waitForWorker.workerStop(queueLoaderWorker, processAlbum, [uri])
return;
}
selectedAlbum = true;
var split = uri.split("/");
......@@ -60,34 +52,27 @@ Item {
}
function processFile(uri, play) {
// Stop queue loading in the background
queueLoaderWorker.canLoad = false
if (queueLoaderWorker.processing > 0) {
waitForWorker.workerStop(queueLoaderWorker, processFile, [uri, play])
return;
}
// Lookup track in songs model
var track = musicStore.lookup(decodeFileURI(uri));
if (!track) {
console.debug("Unknown file " + uri + ", skipping")
return;
}
if (play) {
// clear play queue
trackQueue.clear()
}
} else {
if (play) {
// clear play queue
player.mediaPlayer.playlist.clearWrapper()
}
// enqueue
trackQueue.append(makeDict(track));
// enqueue
player.mediaPlayer.playlist.addItem(Qt.resolvedUrl(track.filename));
// play first URI
if (play) {
trackQueueClick(trackQueue.model.count - 1);
// play first URI
if (play) {
trackQueueClick(player.mediaPlayer.playlist.itemCount - 1);
tabs.pushNowPlaying(); // ensure now playing is shown for first
}
}
}
function process(uri, play) {
......
/*
* Copyright (C) 2013, 2014, 2015
* Copyright (C) 2013, 2014, 2015, 2016
* Andrew Hayzen <ahayzen@gmail.com>
* Daniel Holm <d.holmen@gmail.com>
* Victor Thompson <victor.thompson@gmail.com>
......@@ -36,21 +36,24 @@ Item {
// Connections for usermetrics
Connections {
id: userMetricPlayerConnection
target: player
property bool songCounted: false
target: player.mediaPlayer
onSourceChanged: {
songCounted = false
}
property bool songCounted: false
onPositionChanged: {
// Increment song count on Welcome screen if song has been
// playing for over 10 seconds.
if (player.position > 10000 && !songCounted) {
if (player.mediaPlayer.position > 10000 && !songCounted) {
songCounted = true
songsMetric.increment()
console.debug("Increment UserMetrics")
}
}
}
Connections {
target: player.mediaPlayer.playlist
onCurrentIndexChanged: userMetricPlayerConnection.songCounted = false
onCurrentItemSourceChanged: userMetricPlayerConnection.songCounted = false
}
}
/*
* Copyright (C) 2014, 2015
* Copyright (C) 2014, 2015, 2016
* Andrew Hayzen <ahayzen@gmail.com>
* Daniel Holm <d.holmen@gmail.com>
* Victor Thompson <victor.thompson@gmail.com>
......@@ -25,10 +25,14 @@ Action {
objectName: "addToPlaylistAction"
text: i18n.tr("Add to playlist")
// Used when model can't be given to add to playlist
// for example in the Queue it is called metaModel not model
property var modelOverride: null
onTriggered: {
console.debug("Debug: Add track to playlist");
mainPageStack.push(Qt.resolvedUrl("../../ui/AddToPlaylist.qml"),
{"chosenElements": [makeDict(model)]})
{"chosenElements": [modelOverride || makeDict(model)]})
}
}
/*
* Copyright (C) 2014, 2015
* Copyright (C) 2014, 2015, 2016
* Andrew Hayzen <ahayzen@gmail.com>
* Daniel Holm <d.holmen@gmail.com>
* Victor Thompson <victor.thompson@gmail.com>
......@@ -29,6 +29,6 @@ Action {
onTriggered: {
console.debug("Debug: Add track to queue: " + model)
trackQueue.append(model)
player.mediaPlayer.playlist.addItem(Qt.resolvedUrl(model.filename))
}
}
/*
* Copyright (C) 2013, 2014, 2015
* Copyright (C) 2013, 2014, 2015, 2016
* Andrew Hayzen <ahayzen@gmail.com>
* Daniel Holm <d.holmen@gmail.com>
* Victor Thompson <victor.thompson@gmail.com>
......@@ -18,7 +18,7 @@
*/
import QtQuick 2.4
import QtMultimedia 5.0
import QtMultimedia 5.6
import Ubuntu.Components 1.3
Rectangle {
......@@ -42,7 +42,7 @@ Rectangle {
anchors {
fill: parent
}
state: trackQueue.model.count === 0 ? "disabled" : "enabled"
state: player.mediaPlayer.playlist.empty ? "disabled" : "enabled"
states: [
State {
name: "disabled"
......@@ -104,7 +104,7 @@ Rectangle {
}
color: "#FFF"
height: units.gu(4)
name: player.playbackState === MediaPlayer.PlayingState ?
name: player.mediaPlayer.playbackState === MediaPlayer.PlayingState ?
"media-playback-pause" : "media-playback-start"
objectName: "disabledSmallPlayShape"
width: height
......@@ -116,11 +116,10 @@ Rectangle {
fill: parent
}
onClicked: {
if (trackQueue.model.count === 0) {
if (player.mediaPlayer.playlist.empty) {
playRandomSong();
}
else {
player.toggle();
} else {
player.mediaPlayer.toggle();
}
}
}
......@@ -144,7 +143,7 @@ Rectangle {
left: parent.left
top: parent.top
}
covers: [{art: player.currentMetaArt, author: player.currentMetaArtist, album: player.currentMetaArt}]
covers: [player.currentMeta]
size: parent.height
}
......@@ -170,8 +169,9 @@ Rectangle {
elide: Text.ElideRight
fontSize: "small"
font.weight: Font.DemiBold
text: player.currentMetaTitle === ""
? player.source : player.currentMetaTitle
text: player.currentMeta.title === ""
? player.mediaPlayer.playlist.currentItemSource
: player.currentMeta.title
}
/* Artist of track */
......@@ -185,7 +185,7 @@ Rectangle {
elide: Text.ElideRight
fontSize: "small"
opacity: 0.4
text: player.currentMetaArtist
text: player.currentMeta.author
}
}
......@@ -197,9 +197,9 @@ Rectangle {
rightMargin: units.gu(3)
verticalCenter: parent.verticalCenter
}
color: "#FFF"
color: playerControlsPlayButtonMouseArea.pressed ? UbuntuColors.blue : "white"
height: units.gu(4)
name: player.playbackState === MediaPlayer.PlayingState ?
name: player.mediaPlayer.playbackState === MediaPlayer.PlayingState ?
"media-playback-pause" : "media-playback-start"
objectName: "playShape"
width: height
......@@ -217,12 +217,13 @@ Rectangle {
/* Mouse area for the play button (ontop of the jump to now playing) */
MouseArea {
id: playerControlsPlayButtonMouseArea
anchors {
bottom: parent.bottom
horizontalCenter: playerControlsPlayButton.horizontalCenter
top: parent.top
}
onClicked: player.toggle()
onClicked: player.mediaPlayer.toggle()
width: units.gu(8)
Rectangle {
......@@ -260,16 +261,29 @@ Rectangle {
}
color: UbuntuColors.blue
height: parent.height
width: player.duration > 0 ? (player.position / player.duration) * playerControlsProgressBar.width : 0
width: player.mediaPlayer.progress * playerControlsProgressBar.width
// FIXME: Workaround for pad.lv/1494031 by querying gst as it does not
// emit until it changes to the PLAYING state. But by asking for a
// value we get gst to perform a query and return a result
// However this has to be done once the source is set, hence the delay
//
// NOTE: This does not solve when the currentIndex is removed though
Timer {
id: refreshProgressTimer
interval: 48
repeat: false
// Use binding so the width updates and value isn't saved
onTriggered: playerControlsProgressBarHint.width = Qt.binding(function() { return player.mediaPlayer.progress * playerControlsProgressBar.width; })
}
Connections {
target: player
onPositionChanged: {
playerControlsProgressBarHint.width = (player.position / player.duration) * playerControlsProgressBar.width
}
onStopped: {
playerControlsProgressBarHint.width = 0;
}
target: player.mediaPlayer.playlist
// Call timer when source or index changes
// so we call even if there are duplicate sources or source removal
onCurrentItemSourceChanged: refreshProgressTimer.start()
onCurrentIndexChanged: refreshProgressTimer.start()
}
}
}
......
/*
* Copyright (C) 2013, 2014, 2015
* Copyright (C) 2013, 2014, 2015, 2016
* Andrew Hayzen <ahayzen@gmail.com>
* Daniel Holm <d.holmen@gmail.com>
* Victor Thompson <victor.thompson@gmail.com>
......@@ -51,7 +51,7 @@ Item {
CoverGrid {
id: albumImage
anchors.centerIn: parent
covers: [{art: player.currentMetaArt, author: player.currentMetaArtist, album: player.currentMetaAlbum}]
covers: [player.currentMeta]
size: parent.height
}
}
......@@ -92,7 +92,15 @@ Item {
fontSize: "x-large"
maximumLineCount: 2
objectName: "playercontroltitle"
text: trackQueue.model.count === 0 ? "" : player.currentMetaTitle === "" ? player.currentMetaFile : player.currentMetaTitle
text: {
if (player.mediaPlayer.playlist.empty) {
""
} else if (player.currentMeta.title === "") {
player.mediaPlayer.playlist.currentSource
} else {
player.currentMeta.title
}
}
wrapMode: Text.WordWrap
}
......@@ -108,7 +116,7 @@ Item {
color: styleMusic.nowPlaying.labelSecondaryColor
elide: Text.ElideRight
fontSize: "small"
text: trackQueue.model.count === 0 ? "" : player.currentMetaArtist
text: player.mediaPlayer.playlist.empty ? "" : player.currentMeta.author
}
}
......@@ -122,12 +130,13 @@ Item {
onReleased: {
var diff = mouse.x - lastX
if (Math.abs(diff) < units.gu(4)) {
return;
} else if (diff < 0) {
player.nextSong()
player.mediaPlayer.playlist.nextWrapper()
} else if (diff > 0) {
player.previousSong()
player.mediaPlayer.playlist.previousWrapper()
}
}
}
......@@ -167,7 +176,7 @@ Item {
fontSize: "small"
height: parent.height
horizontalAlignment: Text.AlignHCenter
text: durationToString(player.position)
text: durationToString(player.mediaPlayer.position)
verticalAlignment: Text.AlignVCenter
width: units.gu(3)
}
......@@ -176,10 +185,10 @@ Item {
id: progressSliderMusic
anchors.left: parent.left
anchors.right: parent.right
maximumValue: player.duration // load value at startup
maximumValue: player.mediaPlayer.duration || 1 // fallback to 1 when 0 so that the progress bar works
objectName: "progressSliderShape"
style: UbuntuBlueSliderStyle {}
value: player.position // load value at startup
value: player.mediaPlayer.position // load value at startup
function formatValue(v) {
if (seeking) { // update position label while dragging
......@@ -194,7 +203,7 @@ Item {
onSeekingChanged: {
if (seeking === false) {
musicToolbarFullPositionLabel.text = durationToString(player.position)
musicToolbarFullPositionLabel.text = durationToString(player.mediaPlayer.position)
}
}
......@@ -203,22 +212,23 @@ Item {
if (!pressed) {
seeked = true
player.seek(value)
player.mediaPlayer.seek(value)
musicToolbarFullPositionLabel.text = durationToString(value)
}
}
Connections {
target: player
target: player.mediaPlayer
onPositionChanged: {
// seeked is a workaround for bug 1310706 as the first position after a seek is sometimes invalid (0)
if (progressSliderMusic.seeking === false && !progressSliderMusic.seeked) {
musicToolbarFullPositionLabel.text = durationToString(player.position)
musicToolbarFullDurationLabel.text = durationToString(player.duration)
musicToolbarFullPositionLabel.text = durationToString(player.mediaPlayer.position)
musicToolbarFullDurationLabel.text = durationToString(player.mediaPlayer.duration)
progressSliderMusic.value = player.position
progressSliderMusic.maximumValue = player.duration
progressSliderMusic.value = player.mediaPlayer.position
// fallback to 1 when 0 so that the progress bar works
progressSliderMusic.maximumValue = player.mediaPlayer.duration || 1
}
progressSliderMusic.seeked = false;
......@@ -240,9 +250,39 @@ Item {
fontSize: "small"
height: parent.height
horizontalAlignment: Text.AlignHCenter
text: durationToString(player.duration)
text: durationToString(player.mediaPlayer.duration || 1)
verticalAlignment: Text.AlignVCenter
width: units.gu(3)
}
// FIXME: Workaround for pad.lv/1494031 by querying gst as it does not
// emit until it changes to the PLAYING state. But by asking for a
// value we get gst to perform a query and return a result
// However this has to be done once the source is set, hence the delay
//
// NOTE: This does not solve when the currentIndex is removed though
Timer {
id: refreshProgressTimer
interval: 48
repeat: false
onTriggered: {
if (!progressSliderMusic.seeking) {
musicToolbarFullPositionLabel.text = durationToString(player.mediaPlayer.position);
musicToolbarFullDurationLabel.text = durationToString(player.mediaPlayer.duration || 1);
progressSliderMusic.value = player.mediaPlayer.position
// fallback to 1 when 0 so that the progress bar works
progressSliderMusic.maximumValue = player.mediaPlayer.duration || 1
}
}
}
Connections {
target: player.mediaPlayer.playlist
// Call timer when source or index changes
// so we call even if there are duplicate sources or source removal
onCurrentItemSourceChanged: refreshProgressTimer.start()
onCurrentIndexChanged: refreshProgressTimer.start()
}
}
}
/*
* Copyright (C) 2013, 2014, 2015
* Copyright (C) 2013, 2014, 2015, 2016
* Andrew Hayzen <ahayzen@gmail.com>
* Daniel Holm <d.holmen@gmail.com>
* Victor Thompson <victor.thompson@gmail.com>
......@@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtMultimedia 5.0
import QtMultimedia 5.6
import QtQuick 2.4
import Ubuntu.Components 1.3
......@@ -37,7 +37,6 @@ Rectangle {
anchors.rightMargin: units.gu(1)
anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
height: units.gu(6)
opacity: player.repeat ? 1 : .4
width: height
onClicked: player.repeat = !player.repeat
......@@ -47,23 +46,23 @@ Rectangle {
width: height
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
color: "white"
color: parent.pressed ? UbuntuColors.blue : "white"
name: "media-playlist-repeat"
objectName: "repeatShape"
opacity: player.repeat ? 1 : .4
opacity: player.repeat ? 1 : .2
}
}
/* Previous button */
MouseArea {
id: nowPlayingPreviousButton
enabled: player.mediaPlayer.playlist.canGoPrevious
anchors.right: nowPlayingPlayButton.left
anchors.rightMargin: units.gu(1)
anchors.verticalCenter: nowPlayingPlayButton.verticalCenter
height: units.gu(6)
opacity: trackQueue.model.count === 0 ? .4 : 1
width: height
onClicked: player.previousSong()