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 }