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 }