Warning, /multimedia/audiotube/src/contents/ui/PlayerFooter.qml is written in an unsupported language. File is not indexed.
0001 // SPDX-FileCopyrightText: 2021 Jonah BrĂ¼chert <jbb@kaidan.im>
0002 // SPDX-FileCopyrightText: 2021 Bart De Vries <bart@mogwai.be>
0003 // SPDX-FileCopyrightText: 2020-2022 Devin Lin <devin@kde.org>
0004 //
0005 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006
0007 import QtQuick 2.7
0008 import QtQuick.Layouts 1.3
0009 import QtQuick.Effects
0010 import QtQuick.Controls 2.12 as Controls
0011
0012 import QtMultimedia
0013
0014 import org.kde.kirigami 2.12 as Kirigami
0015 import org.kde.ytmusic 1.0
0016
0017 Flickable {
0018 id: footerItem
0019
0020 property int footerSpacing: 0
0021 property bool maximized: false
0022
0023 readonly property int progressBarHeight: Kirigami.Units.gridUnit / 4
0024 readonly property int minimizedPlayerContentHeight: Math.round(Kirigami.Units.gridUnit * 3.5)
0025 readonly property int minimizedPlayerHeight: minimizedPlayerContentHeight + progressBarHeight
0026
0027 readonly property string thumbnail: thumbnailSource.cachedPath
0028
0029 boundsBehavior: Flickable.StopAtBounds
0030
0031 function close() {
0032 toClose.restart();
0033 }
0034
0035 function resetToBoundsOnFlick() {
0036 if (!atYBeginning || !atYEnd) {
0037 if (footerItem.verticalVelocity > 0) {
0038 toOpen.restart();
0039 } else if (footerItem.verticalVelocity < 0) {
0040 toClose.restart();
0041 } else { // i.e. when verticalVelocity === 0
0042 if (contentY > contentHeight / 4) {
0043 toOpen.restart();
0044 } else {
0045 toClose.restart();
0046 }
0047 }
0048 }
0049 }
0050
0051 function resetToBoundsOnResize() {
0052 if (contentY > contentHeight / 4) {
0053 contentY = contentHeight / 2;
0054 } else {
0055 contentY = 0;
0056 }
0057 }
0058
0059 NumberAnimation on contentY {
0060 id: toOpen
0061 from: contentY
0062 to: contentHeight / 2
0063 duration: Kirigami.Units.longDuration * 2
0064 easing.type: Easing.OutCubic
0065 running: false
0066 }
0067
0068 NumberAnimation on contentY {
0069 id: toClose
0070 from: contentY
0071 to: 0
0072 duration: Kirigami.Units.longDuration * 2
0073 easing.type: Easing.OutCubic
0074 running: false
0075 }
0076
0077 // snap to end
0078 MouseArea {
0079 anchors.fill: footerLayout
0080 propagateComposedEvents: true
0081 onPressed: {
0082 toOpen.stop();
0083 toClose.stop();
0084 propagateComposedEvents = true;
0085 }
0086 onReleased: footerItem.resetToBoundsOnFlick()
0087 }
0088
0089 onMovementStarted: {
0090 toOpen.stop();
0091 toClose.stop();
0092 }
0093 onFlickStarted: resetToBoundsOnFlick()
0094 onMovementEnded: resetToBoundsOnFlick()
0095 onHeightChanged: resetToBoundsOnResize()
0096
0097 property var videoInfoExtractor: VideoInfoExtractor {
0098 id: info
0099
0100 videoId: UserPlaylistModel.currentVideoId
0101 onTitleChanged: {
0102 let index = UserPlaylistModel.index(UserPlaylistModel.currentIndex, 0)
0103 let videoId = UserPlaylistModel.data(index, UserPlaylistModel.VideoId)
0104 let title = UserPlaylistModel.data(index, UserPlaylistModel.Title)
0105 let artist = UserPlaylistModel.data(index, UserPlaylistModel.Artists)
0106 let album = UserPlaylistModel.data(index, UserPlaylistModel.Album)
0107 Library.addPlaybackHistoryItem(videoId, title, artist, album)
0108 }
0109 }
0110
0111 ThumbnailSource {
0112 id: thumbnailSource
0113 videoId: UserPlaylistModel.currentVideoId
0114 }
0115
0116 property MediaPlayer audioPlayer: audioLoader.item
0117 property AudioOutput audioOutput: audioLoader.item.audioOutput
0118
0119 Loader {
0120 id: audioLoader
0121 active: true
0122 asynchronous: true
0123 sourceComponent: MediaPlayer {
0124 id: audio
0125
0126 source: info.audioUrl
0127 onSourceChanged: play()
0128 onMediaStatusChanged: {
0129 if (mediaStatus === MediaPlayer.EndOfMedia) {
0130 console.log("Song ended");
0131 UserPlaylistModel.next();
0132 }
0133 }
0134
0135 audioOutput: AudioOutput {
0136 id: audioOutput
0137 }
0138 }
0139 }
0140
0141 contentHeight: height * 2
0142
0143 ColumnLayout {
0144 id: footerLayout
0145 y:-footerSpacing
0146 anchors.left: parent.left
0147 anchors.right: parent.right
0148 spacing: 0
0149
0150 // margin from top
0151 Item {
0152 Layout.minimumHeight: footerItem.height - footerItem.minimizedPlayerHeight
0153 }
0154
0155 Controls.Control {
0156 Layout.fillWidth: true
0157 padding: 0
0158 implicitHeight: footerItem.minimizedPlayerHeight+footerSpacing
0159
0160 // minimized player background
0161 background: Item {
0162 Rectangle {
0163 anchors.fill: parent
0164 color: Qt.rgba(25, 25, 30, 1)
0165 }
0166
0167 Image {
0168 opacity: 0.2
0169 source: thumbnailSource.cachedPath
0170 asynchronous: true
0171
0172 anchors.fill: parent
0173 fillMode: Image.PreserveAspectCrop
0174 }
0175
0176 layer.enabled: true
0177 layer.effect: MultiEffect {
0178 brightness: -0.7
0179 saturation: 1
0180
0181 blur: 1.0
0182 blurMultiplier: 3.0
0183 blurEnabled: true
0184 autoPaddingEnabled: false
0185 }
0186 }
0187
0188 MinimizedPlayerControls {
0189 progressBarHeight: footerItem.progressBarHeight
0190 minimizedPlayerContentHeight: footerItem.minimizedPlayerContentHeight
0191 height: minimizedPlayerHeight
0192
0193 focus: true
0194 anchors.left: parent.left
0195 anchors.right: parent.right
0196 anchors.top: parent.top
0197
0198 thumbnail: thumbnailSource.cachedPath
0199 info: footerItem.videoInfoExtractor
0200 audio: footerItem.audioPlayer
0201
0202 onRequestOpen: toOpen.start();
0203 }
0204 }
0205
0206 MaximizedPlayerPage {
0207 id: maximizedPlayerPage
0208 Layout.fillWidth: true
0209 Layout.fillHeight: true
0210 implicitHeight: footerItem.height
0211
0212 thumbnail: footerItem.videoInfoExtractor.thumbnail
0213 info: footerItem.videoInfoExtractor
0214 audio: footerItem.audioPlayer
0215
0216 onRequestClose: toClose.start();
0217 }
0218 }
0219 }