Warning, /graphics/koko/src/qml/imagedelegate/VideoPlayer.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  * SPDX-FileCopyrightText: (C) 2021 Mikel Johnson <mikel5764@gmail.com>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 import QtQuick 2.15
0008 import QtQuick.Layouts 1.15
0009 import QtQuick.Controls 2.15 as Controls
0010 import org.kde.kirigami 2.15 as Kirigami
0011 import QtMultimedia 5.15
0012 import org.kde.kcoreaddons 1.0 as KCA
0013 
0014 Item {
0015     id: videoPlayerRoot
0016 
0017     // `required` here breaks stuff
0018     property string source
0019     readonly property alias player: videoPlayer
0020     readonly property bool playing: videoPlayer.playbackState === MediaPlayer.PlayingState
0021     readonly property alias status: videoPlayer.status
0022 
0023     // signals when playback starts and finishes
0024     signal playbackStarted()
0025     signal playbackFinished()
0026 
0027     // convenience function
0028     function play() {
0029         if (videoPlayer.status != MediaPlayer.Loaded) {
0030             videoPlayer.autoPlay = true
0031         } else {
0032             videoPlayer.play();
0033         }
0034     }
0035 
0036     function stop() {
0037         videoPlayer.stop();
0038     }
0039 
0040     implicitWidth: videoPlayer.implicitWidth
0041     implicitHeight: videoPlayer.implicitHeight
0042 
0043     Timer {
0044         id: doubleClickTimer
0045         interval: 150
0046         onTriggered: {
0047             applicationWindow().controlsVisible = !applicationWindow().controlsVisible;
0048         }
0049     }
0050 
0051     MouseArea {
0052         anchors.fill: parent
0053         onClicked: {
0054             if (applicationWindow().contextDrawer) {
0055                 applicationWindow().contextDrawer.drawerOpen = false;
0056             }
0057             doubleClickTimer.restart();
0058         }
0059         onDoubleClicked: {
0060             doubleClickTimer.running = false;
0061             if (videoPlayer.playbackState === MediaPlayer.PlayingState) {
0062                 videoPlayer.pause();
0063             } else{
0064                 videoPlayer.play();
0065             }
0066         }
0067     }
0068 
0069     Video {
0070         id: videoPlayer
0071         implicitWidth: videoPlayer.metaData.resolution ? videoPlayer.metaData.resolution.width : 0
0072         implicitHeight: videoPlayer.metaData.resolution ? videoPlayer.metaData.resolution.height : 0
0073         anchors.fill: parent
0074         loops: videoPlayer.duration >= 5000 ? 0 : MediaPlayer.Infinite // loop short videos
0075         // See https://doc.qt.io/qt-5/qml-qtmultimedia-qtmultimedia.html#convertVolume-method
0076         volume: QtMultimedia.convertVolume(volumeSlider.value,
0077                             QtMultimedia.LogarithmicVolumeScale,
0078                             QtMultimedia.LinearVolumeScale)
0079         source: videoPlayerRoot.source
0080         onPlaying: videoPlayerRoot.playbackStarted()
0081         onStopped: videoPlayerRoot.playbackFinished()
0082 
0083         function seekForward() {
0084             if (videoPlayer.position + 5000 < videoPlayer.duration) {
0085                 videoPlayer.seek(videoPlayer.position + 5000);
0086             } else {
0087                 videoPlayer.seek(0);
0088                 videoPlayer.stop();
0089             }
0090         }
0091 
0092         function seekBackward() {
0093             videoPlayer.seek(videoPlayer.position - 5000);
0094         }
0095     }
0096 
0097     Controls.ToolButton {
0098         anchors.centerIn: parent
0099 
0100         icon.name: "media-playback-start"
0101         icon.color: "white"
0102 
0103         icon.width: Kirigami.Units.gridUnit * 3
0104         icon.height: Kirigami.Units.gridUnit * 3
0105 
0106         visible: videoPlayer.playbackState === MediaPlayer.StoppedState
0107 
0108         onClicked: {
0109             videoPlayer.play();
0110         }
0111     }
0112 
0113     Item {
0114         id: playerToolbar
0115 
0116         anchors.left: parent.left
0117         anchors.right: parent.right
0118         anchors.bottom: parent.bottom
0119         anchors.bottomMargin: Kirigami.Units.smallSpacing
0120 
0121         height: Kirigami.Units.gridUnit * 2
0122         opacity: applicationWindow().controlsVisible ? 1 : 0
0123         visible: opacity !== 0
0124 
0125         Kirigami.Theme.inherit: false
0126         Kirigami.Theme.textColor: "white"
0127 
0128         Behavior on opacity {
0129             OpacityAnimator {
0130                 duration: Kirigami.Units.longDuration
0131                 easing.type: Easing.InOutQuad
0132             }
0133         }
0134 
0135         // Pretty gradient ftw
0136         Rectangle {
0137             anchors.left: parent.left
0138             anchors.right: parent.right
0139             anchors.bottom: parent.bottom
0140             height: Kirigami.Units.gridUnit * 4
0141             opacity: 0.6
0142             gradient: Gradient {
0143                 GradientStop { position: 0.0; color: "transparent" }
0144                 GradientStop { position: 1.0; color: "black" }
0145             }
0146         }
0147 
0148         Rectangle {
0149             anchors.left:parent.left
0150             anchors.right:parent.right
0151             anchors.top: parent.bottom
0152             height: parent.anchors.bottomMargin
0153             opacity: 0.6
0154             color: "black"
0155         }
0156 
0157         Controls.Slider {
0158             id: timeSlider
0159 
0160             // NOTE: Screen reader reports raw numbers, not sure if there's any way around it
0161             Accessible.name: i18n("Seek slider")
0162 
0163             anchors.left: parent.left
0164             anchors.right: parent.right
0165             anchors.bottom: parent.top
0166             anchors.leftMargin: Kirigami.Units.smallSpacing
0167             anchors.rightMargin: Kirigami.Units.smallSpacing
0168 
0169             value: pressed ? 0 : videoPlayer.position // don't change value while we drag
0170             to: videoPlayer.duration
0171 
0172             Controls.ToolTip {
0173                 parent: timeSlider.handle
0174                 visible: timeSlider.pressed
0175                 text: KCA.Format.formatDuration(timeSlider.value, KCA.FormatTypes.FoldHours)
0176             }
0177 
0178             Keys.onLeftPressed: {
0179                 videoPlayer.seekBackward();
0180                 event.accepted = true;
0181             }
0182             Keys.onRightPressed: {
0183                 videoPlayer.seekForward();
0184                 event.accepted = true;
0185             }
0186             // update on release
0187             onPressedChanged: {
0188                 if (!pressed) {
0189                     videoPlayer.seek(value);
0190                 }
0191             }
0192         }
0193 
0194         RowLayout {
0195             anchors.left: parent.left
0196             anchors.right: parent.right
0197             anchors.leftMargin: Kirigami.Units.smallSpacing
0198             anchors.rightMargin: Kirigami.Units.smallSpacing
0199             anchors.verticalCenter: parent.verticalCenter
0200             spacing: Kirigami.Units.smallSpacing
0201 
0202             Controls.ToolButton {
0203                 Accessible.name: i18np("Skip backward 1 second", "Skip backward %1 seconds", 5)
0204                 visible: videoPlayer.duration >= 5000 && !Kirigami.Settings.isMobile
0205                 icon.name: "media-skip-backward"
0206                 enabled: videoPlayer.playbackState != MediaPlayer.StoppedState
0207                 onClicked: {
0208                     videoPlayer.seekBackward();
0209                 }
0210             }
0211 
0212             Controls.ToolButton {
0213                 Accessible.name: videoPlayer.playbackState == MediaPlayer.PlayingState ? i18n("Pause playback") : i18n("Continue playback")
0214                 icon.name: videoPlayer.playbackState == MediaPlayer.PlayingState ? "media-playback-pause" : "media-playback-start"
0215                 onClicked: {
0216                     if (videoPlayer.playbackState === MediaPlayer.PlayingState) {
0217                         videoPlayer.pause();
0218                     } else {
0219                         videoPlayer.play();
0220                     }
0221                 }
0222             }
0223 
0224             Controls.ToolButton {
0225                 Accessible.name: i18np("Skip backward 1 second", "Skip forward %1 seconds", 5)
0226                 visible: videoPlayer.duration >= 5000 && !Kirigami.Settings.isMobile
0227                 icon.name: "media-skip-forward"
0228                 enabled: videoPlayer.playbackState != MediaPlayer.StoppedState
0229                 onClicked: {
0230                     videoPlayer.seekForward();
0231                 }
0232             }
0233 
0234             Controls.ToolButton {
0235                 Accessible.name: videoPlayer.muted ? i18n("Unmute audio") : i18n("Mute audio")
0236                 visible: videoPlayer.hasAudio
0237                 icon.name: videoPlayer.muted ? "audio-volume-muted" :
0238                            volumeSlider.value == 0 ? "audio-volume-low" :
0239                            volumeSlider.value >= 0.5 ? "audio-volume-high" : "audio-volume-medium"
0240                 onClicked: {
0241                     videoPlayer.muted = !videoPlayer.muted;
0242                 }
0243             }
0244 
0245             Controls.Slider {
0246                 id: volumeSlider
0247                 Accessible.name: i18n("Volume slider")
0248                 value: 1
0249                 visible: videoPlayer.hasAudio
0250                 Layout.preferredWidth: Kirigami.Units.gridUnit * 5
0251                 onPressedChanged: videoPlayer.muted = false
0252             }
0253 
0254             Item {
0255                 Layout.fillWidth: true
0256                 height: 1
0257             }
0258 
0259             Controls.Label {
0260                 text: KCA.Format.formatDuration(videoPlayer.position, KCA.FormatTypes.FoldHours) + " / " +
0261                       KCA.Format.formatDuration(videoPlayer.duration, KCA.FormatTypes.FoldHours)
0262             }
0263 
0264             // this local and independed from slideshow to avoid confusion
0265             Controls.ToolButton {
0266                 // Follows Elisa's convention
0267                 Accessible.name: videoPlayer.loops == MediaPlayer.Infinite ? i18n("Repeat current video") : i18n("Don't repeat current video")
0268                 Controls.ToolTip.text: Accessible.name
0269                 Controls.ToolTip.visible: hovered
0270                 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay
0271                 icon.name: videoPlayer.loops == MediaPlayer.Infinite ? "media-repeat-single" : "media-repeat-none"
0272                 onClicked: {
0273                     if (videoPlayer.loops == MediaPlayer.Infinite) {
0274                         videoPlayer.loops = 0;
0275                     } else {
0276                         videoPlayer.loops = MediaPlayer.Infinite;
0277                     }
0278                 }
0279             }
0280         }
0281     }
0282 }