From 8b78f7d07fc198cb0d059e39141e88474ba654cd Mon Sep 17 00:00:00 2001 From: Naoto Yamaguchi Date: Mon, 2 Aug 2021 13:29:22 +0000 Subject: Initial commit for momiplayer momiplayer is a media player app based on agl media player app. Signed-off-by: Naoto Yamaguchi --- ImageButton.qml | 46 ++++ ToggleButton.qml | 31 +++ images/AGL_MediaPlayer_AlbumArtwork.svg | 71 +++++ images/AGL_MediaPlayer_BackArrow.svg | 56 ++++ images/AGL_MediaPlayer_Bluetooth_Active.svg | 84 ++++++ images/AGL_MediaPlayer_Bluetooth_Inactive.svg | 59 ++++ images/AGL_MediaPlayer_CD_Active.svg | 84 ++++++ images/AGL_MediaPlayer_CD_Inactive.svg | 59 ++++ images/AGL_MediaPlayer_DividingLine.svg | 60 +++++ images/AGL_MediaPlayer_ForwardArrow.svg | 56 ++++ images/AGL_MediaPlayer_Loop_Active.svg | 58 ++++ images/AGL_MediaPlayer_Loop_Inactive.svg | 58 ++++ images/AGL_MediaPlayer_Player_Pause.svg | 67 +++++ images/AGL_MediaPlayer_Player_Play.svg | 67 +++++ images/AGL_MediaPlayer_PlaylistToggle_Active.svg | 89 ++++++ images/AGL_MediaPlayer_PlaylistToggle_Inactive.svg | 89 ++++++ images/AGL_MediaPlayer_Playlist_Active.svg | 166 ++++++++++++ images/AGL_MediaPlayer_Playlist_Inactive.svg | 89 ++++++ images/AGL_MediaPlayer_Radio_Active.svg | 299 +++++++++++++++++++++ images/AGL_MediaPlayer_Radio_Inactive.svg | 131 +++++++++ images/AGL_MediaPlayer_Shuffle_Active.svg | 56 ++++ images/AGL_MediaPlayer_Shuffle_Inactive.svg | 56 ++++ images/Albums_Active.svg | 155 +++++++++++ images/Albums_Inactive.svg | 95 +++++++ images/DividingLine.svg | 58 ++++ images/GreenLine.svg | 59 ++++ images/Music_Active.svg | 63 +++++ images/Music_Inactive.svg | 63 +++++ images/Podcasts_Active.svg | 77 ++++++ images/Podcasts_Inactive.svg | 65 +++++ images/Popup_Highlight.svg | 67 +++++ images/Popup_PauseIcon.svg | 65 +++++ images/Popup_PlayIcon.svg | 65 +++++ images/Popup_VerticalLine.svg | 69 +++++ images/X.svg | 64 +++++ images/images.qrc | 37 +++ main.cpp | 53 ++++ mediaplay.pro | 34 +++ mediaplay.qml | 222 +++++++++++++++ playlistwithmetadata.cpp | 206 ++++++++++++++ playlistwithmetadata.h | 55 ++++ qml.qrc | 40 +++ 42 files changed, 3443 insertions(+) create mode 100644 ImageButton.qml create mode 100644 ToggleButton.qml create mode 100644 images/AGL_MediaPlayer_AlbumArtwork.svg create mode 100644 images/AGL_MediaPlayer_BackArrow.svg create mode 100644 images/AGL_MediaPlayer_Bluetooth_Active.svg create mode 100644 images/AGL_MediaPlayer_Bluetooth_Inactive.svg create mode 100644 images/AGL_MediaPlayer_CD_Active.svg create mode 100644 images/AGL_MediaPlayer_CD_Inactive.svg create mode 100644 images/AGL_MediaPlayer_DividingLine.svg create mode 100644 images/AGL_MediaPlayer_ForwardArrow.svg create mode 100644 images/AGL_MediaPlayer_Loop_Active.svg create mode 100644 images/AGL_MediaPlayer_Loop_Inactive.svg create mode 100644 images/AGL_MediaPlayer_Player_Pause.svg create mode 100644 images/AGL_MediaPlayer_Player_Play.svg create mode 100644 images/AGL_MediaPlayer_PlaylistToggle_Active.svg create mode 100644 images/AGL_MediaPlayer_PlaylistToggle_Inactive.svg create mode 100644 images/AGL_MediaPlayer_Playlist_Active.svg create mode 100644 images/AGL_MediaPlayer_Playlist_Inactive.svg create mode 100644 images/AGL_MediaPlayer_Radio_Active.svg create mode 100644 images/AGL_MediaPlayer_Radio_Inactive.svg create mode 100644 images/AGL_MediaPlayer_Shuffle_Active.svg create mode 100644 images/AGL_MediaPlayer_Shuffle_Inactive.svg create mode 100644 images/Albums_Active.svg create mode 100644 images/Albums_Inactive.svg create mode 100644 images/DividingLine.svg create mode 100644 images/GreenLine.svg create mode 100644 images/Music_Active.svg create mode 100644 images/Music_Inactive.svg create mode 100644 images/Podcasts_Active.svg create mode 100644 images/Podcasts_Inactive.svg create mode 100644 images/Popup_Highlight.svg create mode 100644 images/Popup_PauseIcon.svg create mode 100644 images/Popup_PlayIcon.svg create mode 100644 images/Popup_VerticalLine.svg create mode 100644 images/X.svg create mode 100644 images/images.qrc create mode 100644 main.cpp create mode 100644 mediaplay.pro create mode 100644 mediaplay.qml create mode 100644 playlistwithmetadata.cpp create mode 100644 playlistwithmetadata.h create mode 100644 qml.qrc diff --git a/ImageButton.qml b/ImageButton.qml new file mode 100644 index 0000000..c8d05b1 --- /dev/null +++ b/ImageButton.qml @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.6 +import QtQuick.Templates 2.0 as T + +T.Button { + id: control + implicitWidth: contentItem.implicitWidth + implicitHeight: contentItem.implicitHeight + + property url offImage + property url onImage: offImage + + contentItem: Image { + source: control.pressed ? control.onImage : control.offImage + transform: [ + Translate { + id: translate + } + ] + states: [ + State { + when: control.pressed + PropertyChanges { + target: translate + x: 5 + y: 5 + } + } + ] + } +} diff --git a/ToggleButton.qml b/ToggleButton.qml new file mode 100644 index 0000000..4a9fc72 --- /dev/null +++ b/ToggleButton.qml @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import QtQuick 2.6 +import QtQuick.Templates 2.0 as T + +T.CheckBox { + id: control + implicitWidth: contentItem.implicitWidth + implicitHeight: contentItem.implicitHeight + + property url onImage + property url offImage + + contentItem: Image { + source: control.checked ? control.onImage : control.offImage + } +} diff --git a/images/AGL_MediaPlayer_AlbumArtwork.svg b/images/AGL_MediaPlayer_AlbumArtwork.svg new file mode 100644 index 0000000..b2578c0 --- /dev/null +++ b/images/AGL_MediaPlayer_AlbumArtwork.svg @@ -0,0 +1,71 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_BackArrow.svg b/images/AGL_MediaPlayer_BackArrow.svg new file mode 100644 index 0000000..c49b519 --- /dev/null +++ b/images/AGL_MediaPlayer_BackArrow.svg @@ -0,0 +1,56 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Bluetooth_Active.svg b/images/AGL_MediaPlayer_Bluetooth_Active.svg new file mode 100644 index 0000000..7386246 --- /dev/null +++ b/images/AGL_MediaPlayer_Bluetooth_Active.svg @@ -0,0 +1,84 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Bluetooth_Inactive.svg b/images/AGL_MediaPlayer_Bluetooth_Inactive.svg new file mode 100644 index 0000000..24618b5 --- /dev/null +++ b/images/AGL_MediaPlayer_Bluetooth_Inactive.svg @@ -0,0 +1,59 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_CD_Active.svg b/images/AGL_MediaPlayer_CD_Active.svg new file mode 100644 index 0000000..a65f309 --- /dev/null +++ b/images/AGL_MediaPlayer_CD_Active.svg @@ -0,0 +1,84 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_CD_Inactive.svg b/images/AGL_MediaPlayer_CD_Inactive.svg new file mode 100644 index 0000000..adc9127 --- /dev/null +++ b/images/AGL_MediaPlayer_CD_Inactive.svg @@ -0,0 +1,59 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_DividingLine.svg b/images/AGL_MediaPlayer_DividingLine.svg new file mode 100644 index 0000000..51ed1ee --- /dev/null +++ b/images/AGL_MediaPlayer_DividingLine.svg @@ -0,0 +1,60 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_ForwardArrow.svg b/images/AGL_MediaPlayer_ForwardArrow.svg new file mode 100644 index 0000000..56576ac --- /dev/null +++ b/images/AGL_MediaPlayer_ForwardArrow.svg @@ -0,0 +1,56 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Loop_Active.svg b/images/AGL_MediaPlayer_Loop_Active.svg new file mode 100644 index 0000000..fed253d --- /dev/null +++ b/images/AGL_MediaPlayer_Loop_Active.svg @@ -0,0 +1,58 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Loop_Inactive.svg b/images/AGL_MediaPlayer_Loop_Inactive.svg new file mode 100644 index 0000000..27ae317 --- /dev/null +++ b/images/AGL_MediaPlayer_Loop_Inactive.svg @@ -0,0 +1,58 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Player_Pause.svg b/images/AGL_MediaPlayer_Player_Pause.svg new file mode 100644 index 0000000..ee55213 --- /dev/null +++ b/images/AGL_MediaPlayer_Player_Pause.svg @@ -0,0 +1,67 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Player_Play.svg b/images/AGL_MediaPlayer_Player_Play.svg new file mode 100644 index 0000000..c296f8a --- /dev/null +++ b/images/AGL_MediaPlayer_Player_Play.svg @@ -0,0 +1,67 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_PlaylistToggle_Active.svg b/images/AGL_MediaPlayer_PlaylistToggle_Active.svg new file mode 100644 index 0000000..6697335 --- /dev/null +++ b/images/AGL_MediaPlayer_PlaylistToggle_Active.svg @@ -0,0 +1,89 @@ + + + +image/svg+xmlPLAYLIST + \ No newline at end of file diff --git a/images/AGL_MediaPlayer_PlaylistToggle_Inactive.svg b/images/AGL_MediaPlayer_PlaylistToggle_Inactive.svg new file mode 100644 index 0000000..afbae1e --- /dev/null +++ b/images/AGL_MediaPlayer_PlaylistToggle_Inactive.svg @@ -0,0 +1,89 @@ + + + +image/svg+xmlPLAYLIST + \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Playlist_Active.svg b/images/AGL_MediaPlayer_Playlist_Active.svg new file mode 100644 index 0000000..d378e05 --- /dev/null +++ b/images/AGL_MediaPlayer_Playlist_Active.svg @@ -0,0 +1,166 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Playlist_Inactive.svg b/images/AGL_MediaPlayer_Playlist_Inactive.svg new file mode 100644 index 0000000..65ee832 --- /dev/null +++ b/images/AGL_MediaPlayer_Playlist_Inactive.svg @@ -0,0 +1,89 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Radio_Active.svg b/images/AGL_MediaPlayer_Radio_Active.svg new file mode 100644 index 0000000..77a9afa --- /dev/null +++ b/images/AGL_MediaPlayer_Radio_Active.svg @@ -0,0 +1,299 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Radio_Inactive.svg b/images/AGL_MediaPlayer_Radio_Inactive.svg new file mode 100644 index 0000000..fe00b77 --- /dev/null +++ b/images/AGL_MediaPlayer_Radio_Inactive.svg @@ -0,0 +1,131 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Shuffle_Active.svg b/images/AGL_MediaPlayer_Shuffle_Active.svg new file mode 100644 index 0000000..c2c04f8 --- /dev/null +++ b/images/AGL_MediaPlayer_Shuffle_Active.svg @@ -0,0 +1,56 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/AGL_MediaPlayer_Shuffle_Inactive.svg b/images/AGL_MediaPlayer_Shuffle_Inactive.svg new file mode 100644 index 0000000..068370a --- /dev/null +++ b/images/AGL_MediaPlayer_Shuffle_Inactive.svg @@ -0,0 +1,56 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/Albums_Active.svg b/images/Albums_Active.svg new file mode 100644 index 0000000..64086f3 --- /dev/null +++ b/images/Albums_Active.svg @@ -0,0 +1,155 @@ + + + +image/svg+xmlALBUMS + \ No newline at end of file diff --git a/images/Albums_Inactive.svg b/images/Albums_Inactive.svg new file mode 100644 index 0000000..7d6b1d8 --- /dev/null +++ b/images/Albums_Inactive.svg @@ -0,0 +1,95 @@ + + + +image/svg+xmlALBUMS + \ No newline at end of file diff --git a/images/DividingLine.svg b/images/DividingLine.svg new file mode 100644 index 0000000..40354c1 --- /dev/null +++ b/images/DividingLine.svg @@ -0,0 +1,58 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/GreenLine.svg b/images/GreenLine.svg new file mode 100644 index 0000000..8d3a431 --- /dev/null +++ b/images/GreenLine.svg @@ -0,0 +1,59 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/Music_Active.svg b/images/Music_Active.svg new file mode 100644 index 0000000..22d50d0 --- /dev/null +++ b/images/Music_Active.svg @@ -0,0 +1,63 @@ + + + +image/svg+xmlMUSIC + \ No newline at end of file diff --git a/images/Music_Inactive.svg b/images/Music_Inactive.svg new file mode 100644 index 0000000..c009efa --- /dev/null +++ b/images/Music_Inactive.svg @@ -0,0 +1,63 @@ + + + +image/svg+xmlMUSIC + \ No newline at end of file diff --git a/images/Podcasts_Active.svg b/images/Podcasts_Active.svg new file mode 100644 index 0000000..ab68439 --- /dev/null +++ b/images/Podcasts_Active.svg @@ -0,0 +1,77 @@ + + + +image/svg+xmlPODCASTS + \ No newline at end of file diff --git a/images/Podcasts_Inactive.svg b/images/Podcasts_Inactive.svg new file mode 100644 index 0000000..43d6958 --- /dev/null +++ b/images/Podcasts_Inactive.svg @@ -0,0 +1,65 @@ + + + +image/svg+xmlPODCASTS + \ No newline at end of file diff --git a/images/Popup_Highlight.svg b/images/Popup_Highlight.svg new file mode 100644 index 0000000..24795a4 --- /dev/null +++ b/images/Popup_Highlight.svg @@ -0,0 +1,67 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/Popup_PauseIcon.svg b/images/Popup_PauseIcon.svg new file mode 100644 index 0000000..f1df1a7 --- /dev/null +++ b/images/Popup_PauseIcon.svg @@ -0,0 +1,65 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/Popup_PlayIcon.svg b/images/Popup_PlayIcon.svg new file mode 100644 index 0000000..5053ebb --- /dev/null +++ b/images/Popup_PlayIcon.svg @@ -0,0 +1,65 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/Popup_VerticalLine.svg b/images/Popup_VerticalLine.svg new file mode 100644 index 0000000..b8457ec --- /dev/null +++ b/images/Popup_VerticalLine.svg @@ -0,0 +1,69 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/X.svg b/images/X.svg new file mode 100644 index 0000000..3afe6f6 --- /dev/null +++ b/images/X.svg @@ -0,0 +1,64 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/images/images.qrc b/images/images.qrc new file mode 100644 index 0000000..eb07c3b --- /dev/null +++ b/images/images.qrc @@ -0,0 +1,37 @@ + + + AGL_MediaPlayer_Bluetooth_Active.svg + AGL_MediaPlayer_AlbumArtwork.svg + AGL_MediaPlayer_BackArrow.svg + AGL_MediaPlayer_Bluetooth_Inactive.svg + AGL_MediaPlayer_CD_Active.svg + AGL_MediaPlayer_CD_Inactive.svg + AGL_MediaPlayer_DividingLine.svg + AGL_MediaPlayer_ForwardArrow.svg + AGL_MediaPlayer_Loop_Active.svg + AGL_MediaPlayer_Loop_Inactive.svg + AGL_MediaPlayer_Player_Pause.svg + AGL_MediaPlayer_Player_Play.svg + AGL_MediaPlayer_Playlist_Active.svg + AGL_MediaPlayer_Playlist_Inactive.svg + AGL_MediaPlayer_PlaylistToggle_Active.svg + AGL_MediaPlayer_PlaylistToggle_Inactive.svg + AGL_MediaPlayer_Radio_Active.svg + AGL_MediaPlayer_Radio_Inactive.svg + AGL_MediaPlayer_Shuffle_Active.svg + AGL_MediaPlayer_Shuffle_Inactive.svg + Albums_Active.svg + Albums_Inactive.svg + DividingLine.svg + GreenLine.svg + Music_Active.svg + Music_Inactive.svg + Podcasts_Active.svg + Podcasts_Inactive.svg + Popup_Highlight.svg + Popup_PauseIcon.svg + Popup_PlayIcon.svg + Popup_VerticalLine.svg + X.svg + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..6f9744d --- /dev/null +++ b/main.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "playlistwithmetadata.h" + +QVariantList readMusicFile(const QString &path) +{ + QVariantList ret; + QDir dir(path); + for (const auto &entry : dir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name)) { + QFileInfo fileInfo(dir.absoluteFilePath(entry)); + if (fileInfo.isDir()) { + ret.append(readMusicFile(fileInfo.absoluteFilePath())); + } else if (fileInfo.isFile()) { + ret.append(QUrl::fromLocalFile(fileInfo.absoluteFilePath())); + } + } + return ret; +} + + +int main(int argc, char *argv[]) +{ + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + + QGuiApplication app(argc, argv); + + qmlRegisterType("MediaPlayer", 1, 0, "PlaylistWithMetadata"); + + QVariantList mediaFiles; + for (const auto &music : QStandardPaths::standardLocations(QStandardPaths::MusicLocation)) { + mediaFiles.append(readMusicFile(music)); + } + QQmlApplicationEngine engine; + QQmlContext *context = engine.rootContext(); + context->setContextProperty("mediaFiles", mediaFiles); + const QUrl url(QStringLiteral("qrc:/mediaplay.qml")); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + &app, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + engine.load(url); + + return app.exec(); +} diff --git a/mediaplay.pro b/mediaplay.pro new file mode 100644 index 0000000..e70e45a --- /dev/null +++ b/mediaplay.pro @@ -0,0 +1,34 @@ +QT += quick quickcontrols2 multimedia + +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Refer to the documentation for the +# deprecated API to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp \ + playlistwithmetadata.cpp + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /usr/bin +!isEmpty(target.path): INSTALLS += target + +HEADERS += \ + playlistwithmetadata.h diff --git a/mediaplay.qml b/mediaplay.qml new file mode 100644 index 0000000..b197633 --- /dev/null +++ b/mediaplay.qml @@ -0,0 +1,222 @@ +import QtQuick 2.6 +import QtQuick.Window 2.12 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.0 +import QtMultimedia 5.6 +import MediaPlayer 1.0 + +ApplicationWindow { + id: root + visible: true + width: 1920 + height: 1080 + color: "#222222" + title: qsTr("momiplayer") + + MediaPlayer { + id: player + audioRole: MediaPlayer.MusicRole + autoLoad: true + playlist: playlist + function time2str(value) { + return Qt.formatTime(new Date(value), 'mm:ss') + } + onPositionChanged: slider.value = player.position + } + + Item { + x: 0 + y: 0 + width: 920 + height: 1080 + clip: true + Image { + x: 204 + y: 100 + width: 512 + height: 512 + fillMode: Image.PreserveAspectCrop + source: player.metaData.coverArtImage ? player.metaData.coverArtImage : '' + } + + Item { + x: 0 + y: 100+512 + height :300 + width : 920 + Rectangle { + anchors.fill: parent + color: '#444444' + //opacity: 0.75 + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: root.width * 0.02 + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Row { + spacing: 20 + ToggleButton { + id: random + offImage: './images/AGL_MediaPlayer_Shuffle_Inactive.svg' + onImage: './images/AGL_MediaPlayer_Shuffle_Active.svg' + } + ToggleButton { + id: loop + offImage: './images/AGL_MediaPlayer_Loop_Inactive.svg' + onImage: './images/AGL_MediaPlayer_Loop_Active.svg' + } + } + ColumnLayout { + anchors.fill: parent + Label { + id: title + Layout.alignment: Layout.Center + text: player.metaData.title ? player.metaData.title : '' + horizontalAlignment: Label.AlignHCenter + verticalAlignment: Label.AlignVCenter + } + Label { + id: artist + Layout.alignment: Layout.Center + text: player.metaData.author ? player.metaData.author : '' + horizontalAlignment: Label.AlignHCenter + verticalAlignment: Label.AlignVCenter + font.pixelSize: title.font.pixelSize * 0.6 + } + } + } + Slider { + id: slider + Layout.fillWidth: true + to: player.duration + Label { + id: position + anchors.left: parent.left + anchors.bottom: parent.top + font.pixelSize: 32 + text: player.time2str(player.position) + } + Label { + id: duration + anchors.right: parent.right + anchors.bottom: parent.top + font.pixelSize: 32 + text: player.time2str(player.duration) + } + onPressedChanged: player.seek(value) + } + RowLayout { + Layout.fillHeight: true + Item { Layout.fillWidth: true } + ImageButton { + offImage: './images/AGL_MediaPlayer_BackArrow.svg' + onClicked: playlist.previous() + } + ImageButton { + id: play + offImage: './images/AGL_MediaPlayer_Player_Play.svg' + onClicked: player.play() + states: [ + State { + when: player.playbackState === MediaPlayer.PlayingState + PropertyChanges { + target: play + offImage: './images/AGL_MediaPlayer_Player_Pause.svg' + onClicked: player.pause() + } + } + ] + } + ImageButton { + offImage: './images/AGL_MediaPlayer_ForwardArrow.svg' + onClicked: playlist.next() + } + + Item { Layout.fillWidth: true } + } + } + } + } + + Item { + x: 920 + y: 0 + width: 1000 + height: 1080 + ListView { + anchors.rightMargin: 0 + anchors.bottomMargin: 0 + anchors.leftMargin: 0 + anchors.topMargin: 0 + anchors.fill: parent + clip: true + header: Label { + x: 50 + text: 'PLAYLIST' + opacity: 0.5 + } + model: PlaylistWithMetadata { + source: playlist + } + currentIndex: playlist.currentIndex + + delegate: MouseArea { + id: delegate + width: ListView.view.width + height: ListView.view.height / 4 + RowLayout { + anchors.fill: parent + anchors.leftMargin: 10 + anchors.rightMargin: 50 + Image { + source: model.coverArt + fillMode: Image.PreserveAspectFit + Layout.preferredWidth: delegate.height + Layout.preferredHeight: delegate.height + } + ColumnLayout { + Layout.fillWidth: true + Label { + Layout.fillWidth: true + text: model.title + color: '#66FF99' + font.pixelSize: 48 + } + Label { + Layout.fillWidth: true + text: model.artist + color: '#66FF99' + font.pixelSize: 32 + } + } + Label { + text: player.time2str(model.duration) + color: '#66FF99' + font.pixelSize: 32 + } + } + onClicked: { + playlist.currentIndex = model.index + player.play() + } + } + + highlight: Rectangle { + color: 'white' + opacity: 0.25 + } + } + } + + Playlist { + id: playlist + playbackMode: random.checked ? Playlist.Random : loop.checked ? Playlist.Loop : Playlist.Sequential + + Component.onCompleted: { + playlist.addItems(mediaFiles) + } + } +} diff --git a/playlistwithmetadata.cpp b/playlistwithmetadata.cpp new file mode 100644 index 0000000..be6d9a1 --- /dev/null +++ b/playlistwithmetadata.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "playlistwithmetadata.h" + +#include +#include +#include +#include +#include +#include + +class PlaylistWithMetadata::Private +{ +public: + Private(PlaylistWithMetadata *parent); + + void disconnect(); + void connect(); + +private: + void loadMetadata(int row); + +private: + PlaylistWithMetadata *q; + +public: + QAbstractListModel *source; + QList connections; + QList urls; + QHash title; + QHash artist; + QHash coverArt; + QHash duration; + QHash players; +}; + +PlaylistWithMetadata::Private::Private(PlaylistWithMetadata *parent) + : q(parent) + , source(nullptr) +{ +} + +void PlaylistWithMetadata::Private::disconnect() +{ + if (source) { + for (const auto &connection : connections) + q->disconnect(connection); + connections.clear(); + } +} + +void PlaylistWithMetadata::Private::connect() +{ + if (source) { + connections.append(q->connect(source, &QAbstractListModel::rowsAboutToBeInserted, [&](const QModelIndex &parent, int first, int last) { + Q_UNUSED(parent) + q->beginInsertRows(QModelIndex(), first, last); + })); + connections.append(q->connect(source, &QAbstractListModel::rowsInserted, [&](const QModelIndex &parent, int first, int last) { + Q_UNUSED(parent) + for (int i = first; i <= last; i++) { + loadMetadata(i); + } + q->endInsertRows(); + })); + + int count = source->rowCount(); + if (count > 0) { + q->beginInsertRows(QModelIndex(), 0, count); + for (int i = 0; i < count; i++) { + loadMetadata(i); + } + q->endInsertRows(); + } + } +} + +void PlaylistWithMetadata::Private::loadMetadata(int row) +{ + QUrl url = source->data(source->index(row), Qt::UserRole + 1).toUrl(); + QMediaPlayer *player = new QMediaPlayer(q); + urls.append(url); + players.insert(url, player); + q->connect(player, &QMediaPlayer::mediaStatusChanged, [this, url](QMediaPlayer::MediaStatus mediaStatus) { + switch (mediaStatus) { + case QMediaPlayer::NoMedia: + case QMediaPlayer::LoadedMedia: { + QMediaPlayer *player = players.take(url); + title.insert(url, player->metaData(QMediaMetaData::Title).toString()); + artist.insert(url, player->metaData(QMediaMetaData::ContributingArtist).toString()); + QVariant coverArtImage = player->metaData(QMediaMetaData::CoverArtImage); + if (coverArtImage.type() == QVariant::Image) { + QImage image = coverArtImage.value(); + QByteArray data; + QBuffer buffer(&data); + buffer.open(QBuffer::WriteOnly); + QImageWriter png(&buffer, "png"); + if (png.write(image)) { + buffer.close(); + coverArt.insert(url, QUrl(QStringLiteral("data:image/png;base64,") + data.toBase64())); + } + } + duration.insert(url, player->duration()); + QModelIndex index = q->index(urls.indexOf(url)); + q->dataChanged(index, index, QVector() << TitleRole << ArtistRole << CoverArtRole << DurationRole); + player->deleteLater(); + break; } + default: + break; + } + + }); + player->setMedia(url); +} + +PlaylistWithMetadata::PlaylistWithMetadata(QObject *parent) + : QAbstractListModel(parent) + , d(new Private(this)) +{ +} + +PlaylistWithMetadata::~PlaylistWithMetadata() +{ + delete d; +} + +int PlaylistWithMetadata::rowCount(const QModelIndex &parent) const +{ + int ret = 0; + if (parent.isValid()) + return ret; + if (d->source) + ret = d->source->rowCount(QModelIndex()); + return ret; +} + +QVariant PlaylistWithMetadata::data(const QModelIndex &index, int role) const +{ + QVariant ret; + if (!index.isValid()) + return ret; + int row = index.row(); + if (row < 0 || rowCount() <= row) + return ret; + QUrl url = d->urls.at(row); + switch (role) { + case TitleRole: + ret = d->title.value(url); + break; + case ArtistRole: + ret = d->artist.value(url); + break; + case CoverArtRole: + ret = d->coverArt.value(url); + break; + case SourceRole: + ret = url; + break; + case DurationRole: + ret = d->duration.value(url); + break; + default: + qWarning() << role; + } + + return ret; +} + +QHash PlaylistWithMetadata::roleNames() const +{ + return { + {TitleRole, "title"}, + {ArtistRole, "artist"}, + {CoverArtRole, "coverArt"}, + {SourceRole, "source"}, + {DurationRole, "duration"} + }; +} + +QAbstractListModel *PlaylistWithMetadata::source() const +{ + return d->source; +} + +void PlaylistWithMetadata::setSource(QAbstractListModel *source) +{ + if (d->source == source) return; + d->disconnect(); + d->source = source; + d->connect(); + emit sourceChanged(source); +} diff --git a/playlistwithmetadata.h b/playlistwithmetadata.h new file mode 100644 index 0000000..74cf6f5 --- /dev/null +++ b/playlistwithmetadata.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 The Qt Company Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PLAYLISTWITHMETADATA_H +#define PLAYLISTWITHMETADATA_H + +#include + +class PlaylistWithMetadata : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(QAbstractListModel *source READ source WRITE setSource NOTIFY sourceChanged) +public: + PlaylistWithMetadata(QObject *parent = nullptr); + ~PlaylistWithMetadata(); + + enum { + TitleRole = Qt::DisplayRole + , ArtistRole = Qt::UserRole + 1 + , CoverArtRole + , SourceRole + , DurationRole + }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + + QAbstractListModel *source() const; + +public slots: + void setSource(QAbstractListModel *source); + +signals: + void sourceChanged(QAbstractListModel *source); + +private: + class Private; + Private *d; +}; + +#endif // PLAYLISTWITHMETADATA_H diff --git a/qml.qrc b/qml.qrc new file mode 100644 index 0000000..2d87efb --- /dev/null +++ b/qml.qrc @@ -0,0 +1,40 @@ + + + mediaplay.qml + images/Music_Active.svg + images/Music_Inactive.svg + images/Podcasts_Active.svg + images/Podcasts_Inactive.svg + images/Popup_Highlight.svg + images/Popup_PauseIcon.svg + images/Popup_PlayIcon.svg + images/Popup_VerticalLine.svg + images/X.svg + images/AGL_MediaPlayer_AlbumArtwork.svg + images/AGL_MediaPlayer_BackArrow.svg + images/AGL_MediaPlayer_Bluetooth_Active.svg + images/AGL_MediaPlayer_Bluetooth_Inactive.svg + images/AGL_MediaPlayer_CD_Active.svg + images/AGL_MediaPlayer_CD_Inactive.svg + images/AGL_MediaPlayer_DividingLine.svg + images/AGL_MediaPlayer_ForwardArrow.svg + images/AGL_MediaPlayer_Loop_Active.svg + images/AGL_MediaPlayer_Loop_Inactive.svg + images/AGL_MediaPlayer_Player_Pause.svg + images/AGL_MediaPlayer_Player_Play.svg + images/AGL_MediaPlayer_Playlist_Active.svg + images/AGL_MediaPlayer_Playlist_Inactive.svg + images/AGL_MediaPlayer_PlaylistToggle_Active.svg + images/AGL_MediaPlayer_PlaylistToggle_Inactive.svg + images/AGL_MediaPlayer_Radio_Active.svg + images/AGL_MediaPlayer_Radio_Inactive.svg + images/AGL_MediaPlayer_Shuffle_Active.svg + images/AGL_MediaPlayer_Shuffle_Inactive.svg + images/Albums_Active.svg + images/Albums_Inactive.svg + images/DividingLine.svg + images/GreenLine.svg + ImageButton.qml + ToggleButton.qml + + -- cgit