1
1
mirror of https://github.com/OpenVoiceOS/OpenVoiceOS synced 2025-01-20 05:00:26 +01:00
OpenVoiceOS/buildroot-external/package/python-ovos-ocp-audio-plugin/0001-Add-QTAV-support.patch

1613 lines
58 KiB
Diff
Raw Normal View History

From 8fe077091bfa3ebf5caeef8e8a8cfb03a77fc515 Mon Sep 17 00:00:00 2001
From: Aditya Mehra <aix.m@outlook.com>
Date: Sun, 15 Jan 2023 10:10:22 +1030
Subject: [PATCH 1/4] Add optional support for QtAV video player via
configuration
---
ovos_plugin_common_play/ocp/gui.py | 7 +-
ovos_plugin_common_play/ocp/player.py | 4 +-
.../ui/+mediacenter/OVOSSeekControlQtAv.qml | 407 ++++++++++++++++++
.../ui/+mediacenter/OVOSVideoPlayerQtAv.qml | 241 +++++++++++
.../ocp/res/ui/OVOSSeekControlQtAv.qml | 407 ++++++++++++++++++
.../ocp/res/ui/OVOSVideoPlayerQtAv.qml | 241 +++++++++++
6 files changed, 1305 insertions(+), 2 deletions(-)
create mode 100644 ovos_plugin_common_play/ocp/res/ui/+mediacenter/OVOSSeekControlQtAv.qml
create mode 100644 ovos_plugin_common_play/ocp/res/ui/+mediacenter/OVOSVideoPlayerQtAv.qml
create mode 100644 ovos_plugin_common_play/ocp/res/ui/OVOSSeekControlQtAv.qml
create mode 100644 ovos_plugin_common_play/ocp/res/ui/OVOSVideoPlayerQtAv.qml
diff --git a/ovos_plugin_common_play/ocp/gui.py b/ovos_plugin_common_play/ocp/gui.py
index eb0efbf..87840e1 100644
--- a/ovos_plugin_common_play/ocp/gui.py
+++ b/ovos_plugin_common_play/ocp/gui.py
@@ -23,6 +23,7 @@ def __init__(self):
self.search_mode_is_app = False
self.persist_home_display = False
self.event_scheduler_interface = None
+ self.video_player_interface = None
def bind(self, player):
self.player = player
@@ -36,6 +37,7 @@ def bind(self, player):
self.player.add_event('ovos.common_play.skill.play',
self.handle_play_skill_featured_media)
self.event_scheduler_interface = EventSchedulerInterface(name="ovos.common_play", bus=self.bus)
+ self.video_player_interface = self.player.video_player_interface
@property
def home_screen_page(self):
@@ -55,7 +57,10 @@ def audio_service_page(self):
@property
def video_player_page(self):
- return join(self.player.res_dir, "ui", "OVOSVideoPlayer.qml")
+ if self.video_player_interface = "qtav":
+ return join(self.player.res_dir, "ui", "OVOSVideoPlayerQtAv.qml")
+ else:
+ return join(self.player.res_dir, "ui", "OVOSVideoPlayer.qml")
@property
def web_player_page(self):
diff --git a/ovos_plugin_common_play/ocp/player.py b/ovos_plugin_common_play/ocp/player.py
index af2db6e..f246654 100644
--- a/ovos_plugin_common_play/ocp/player.py
+++ b/ovos_plugin_common_play/ocp/player.py
@@ -25,6 +25,8 @@ def __init__(self, bus=None, settings=None, lang=None, gui=None,
gui = gui or OCPMediaPlayerGUI()
# mpris settings
manage_players = settings.get("manage_external_players", False)
+ # can be "default (QtMultimedia via Mycroft GUI)" or "qtav (QtAv via QML interface)"
+ video_player_interface = settings.get("video_player_interface", "default")
if settings.disable_mpris:
LOG.info("MPRIS integration is disabled")
self.mpris = None
@@ -730,4 +732,4 @@ def handle_set_app_timeout_mode(self, message):
self.settings["app_view_timeout_mode"] = message.data.get("mode", "all")
self.settings.store()
self.gui["app_view_timeout_mode"] = self.settings.get("app_view_timeout_mode", "all")
- self.gui.cancel_app_view_timeout()
\ No newline at end of file
+ self.gui.cancel_app_view_timeout()
diff --git a/ovos_plugin_common_play/ocp/res/ui/+mediacenter/OVOSSeekControlQtAv.qml b/ovos_plugin_common_play/ocp/res/ui/+mediacenter/OVOSSeekControlQtAv.qml
new file mode 100644
index 0000000..a9437dd
--- /dev/null
+++ b/ovos_plugin_common_play/ocp/res/ui/+mediacenter/OVOSSeekControlQtAv.qml
@@ -0,0 +1,407 @@
+/*
+ * Copyright 2019 by Aditya Mehra <aix.m@outlook.com>
+ * Copyright 2019 by Marco Martin <mart@kde.org>
+ *
+ * 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.Layouts 1.4
+import QtQuick 2.12
+import QtQuick.Controls 2.12 as Controls
+import org.kde.kirigami 2.10 as Kirigami
+import QtQuick.Templates 2.12 as Templates
+import QtGraphicalEffects 1.0
+import Mycroft 1.0 as Mycroft
+import QtAV 1.7
+
+Item {
+ id: seekControl
+ property bool opened: false
+ property int duration: 0
+ property int playPosition: 0
+ property int seekPosition: 0
+ property bool enabled: true
+ property bool seeking: false
+ property var videoControl
+ property string title
+ property var currentState: videoService.playbackState
+
+ readonly property var videoService: Mycroft.MediaService
+
+ clip: true
+ implicitWidth: parent.width
+ implicitHeight: mainLayout.implicitHeight + Kirigami.Units.largeSpacing * 2
+ opacity: opened
+
+ onOpenedChanged: {
+ if (opened) {
+ hideTimer.restart();
+ }
+ }
+
+ onFocusChanged: {
+ if(focus) {
+ backButton.forceActiveFocus()
+ }
+ }
+
+ Timer {
+ id: hideTimer
+ interval: 5000
+ onTriggered: {
+ seekControl.opened = false;
+ videoRoot.forceActiveFocus();
+ }
+ }
+
+ Rectangle {
+ width: parent.width
+ height: parent.height
+ property color tempColor: Qt.darker(Kirigami.Theme.backgroundColor, 2)
+ color: Qt.rgba(tempColor.r, tempColor.g, tempColor.b, 0.8)
+ y: opened ? 0 : parent.height
+
+ Rectangle {
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: Mycroft.Units.gridUnit / 4
+ color: Kirigami.Theme.highlightColor
+ }
+
+ ColumnLayout {
+ id: mainLayout
+
+ anchors {
+ fill: parent
+ margins: Kirigami.Units.largeSpacing
+ }
+
+ RowLayout {
+ id: mainLayout2
+ Layout.fillHeight: true
+
+ Controls.Button {
+ id: button
+ Layout.preferredWidth: parent.width > 600 ? Kirigami.Units.iconSizes.large : Kirigami.Units.iconSizes.medium
+ Layout.preferredHeight: Layout.preferredWidth
+ highlighted: focus ? 1 : 0
+ z: 1000
+
+ background: Rectangle {
+ color: Kirigami.Theme.backgroundColor
+ radius: 5
+ border.width: 1.25
+ border.color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.25)
+ }
+
+ contentItem: Kirigami.Icon {
+ anchors.fill: parent
+ anchors.margins: Mycroft.Units.gridUnit
+
+ source: avVideo.playbackState == MediaPlayer.PlayingState ? Qt.resolvedUrl("images/media-playback-pause.svg") : Qt.resolvedUrl("images/media-playback-start.svg")
+
+ ColorOverlay {
+ source: parent
+ anchors.fill: parent
+ color: Kirigami.Theme.textColor
+ }
+ }
+
+ onClicked: {
+ avVideo.playbackState == MediaPlayer.PlayingState ? videoControl.pause() : videoControl.currentState == MediaPlayer.PausedState ? videoControl.resume() : videoControl.play()
+ hideTimer.restart();
+ }
+ KeyNavigation.up: video
+ KeyNavigation.left: backButton
+ KeyNavigation.right: slider
+ Keys.onReturnPressed: {
+ clicked()
+ }
+ onFocusChanged: {
+ hideTimer.restart();
+ }
+ }
+
+ Controls.Button {
+ id: prevbutton
+ Layout.preferredWidth: parent.width > 600 ? Kirigami.Units.iconSizes.large : Kirigami.Units.iconSizes.medium
+ Layout.preferredHeight: Layout.preferredWidth
+ highlighted: focus ? 1 : 0
+ z: 1000
+
+ background: Rectangle {
+ color: Kirigami.Theme.backgroundColor
+ radius: 5
+ border.width: 1.25
+ border.color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.25)
+ }
+
+ contentItem: Kirigami.Icon {
+ anchors.fill: parent
+ anchors.margins: Mycroft.Units.gridUnit
+
+ source: Qt.resolvedUrl("images/media-skip-backward.svg")
+
+ ColorOverlay {
+ source: parent
+ anchors.fill: parent
+ color: Kirigami.Theme.textColor
+ }
+ }
+
+ onClicked: {
+ videoControl.previous()
+ hideTimer.restart();
+ }
+
+ KeyNavigation.up: video
+ KeyNavigation.left: backButton
+ KeyNavigation.right: slider
+ Keys.onReturnPressed: {
+ clicked()
+ }
+ onFocusChanged: {
+ hideTimer.restart();
+ }
+ }
+
+ Controls.Button {
+ id: nextbutton
+ Layout.preferredWidth: parent.width > 600 ? Kirigami.Units.iconSizes.large : Kirigami.Units.iconSizes.medium
+ Layout.preferredHeight: Layout.preferredWidth
+ highlighted: focus ? 1 : 0
+ z: 1000
+
+ background: Rectangle {
+ color: Kirigami.Theme.backgroundColor
+ radius: 5
+ border.width: 1.25
+ border.color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.25)
+ }
+
+ contentItem: Kirigami.Icon {
+ anchors.fill: parent
+ anchors.margins: Mycroft.Units.gridUnit
+
+ source: Qt.resolvedUrl("images/media-skip-forward.svg")
+
+
+ ColorOverlay {
+ source: parent
+ anchors.fill: parent
+ color: Kirigami.Theme.textColor
+ }
+ }
+
+ onClicked: {
+ videoControl.next()
+ hideTimer.restart();
+ }
+ KeyNavigation.up: video
+ KeyNavigation.left: backButton
+ KeyNavigation.right: slider
+ Keys.onReturnPressed: {
+ clicked()
+ }
+ onFocusChanged: {
+ hideTimer.restart();
+ }
+ }
+
+ Templates.Slider {
+ id: slider
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter
+ implicitHeight: Kirigami.Units.gridUnit
+ value: seekControl.playPosition
+ from: 0
+ to: seekControl.duration
+ z: 1000
+ property bool navSliderItem
+ property int minimumValue: 0
+ property int maximumValue: 20
+ onMoved: {
+ seekControl.seekPosition = value;
+ hideTimer.restart();
+ }
+
+ onNavSliderItemChanged: {
+ if(slider.navSliderItem){
+ recthandler.color = "red"
+ } else if (slider.focus) {
+ recthandler.color = Kirigami.Theme.linkColor
+ }
+ }
+
+ onFocusChanged: {
+ if(!slider.focus){
+ recthandler.color = Kirigami.Theme.textColor
+ } else {
+ recthandler.color = Kirigami.Theme.linkColor
+ }
+ }
+
+ handle: Item {
+ x: slider.visualPosition * (parent.width - (Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing))
+ anchors.verticalCenter: parent.verticalCenter
+ height: parent.height + Mycroft.Units.gridUnit
+
+ Rectangle {
+ id: hand
+ anchors.verticalCenter: parent.verticalCenter
+ implicitWidth: Kirigami.Units.iconSizes.small + Kirigami.Units.smallSpacing
+ implicitHeight: parent.height / 2
+ color: Kirigami.Theme.backgroundColor
+ border.color: Kirigami.Theme.highlightColor
+ }
+ }
+
+ background: Item {
+ Rectangle {
+ id: groove
+ anchors {
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ right: parent.right
+ }
+ height: Math.round(Kirigami.Units.gridUnit/3)
+ color: Qt.lighter(Kirigami.Theme.highlightColor, 1.5)
+
+ Rectangle {
+ anchors {
+ left: parent.left
+ top: parent.top
+ bottom: parent.bottom
+ }
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop { position: 0.0; color: Kirigami.Theme.highlightColor }
+ GradientStop { position: 1.0; color: Qt.darker(Kirigami.Theme.highlightColor, 1.5) }
+ }
+ width: slider.position * (parent.width - slider.handle.width/2) + slider.handle.width/2
+ }
+ }
+
+ Controls.Label {
+ anchors {
+ left: parent.left
+ top: groove.bottom
+ topMargin: Kirigami.Units.smallSpacing
+ }
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ text: formatedPosition(playPosition)
+ color: Kirigami.Theme.textColor
+ }
+
+ Controls.Label {
+ anchors {
+ right: parent.right
+ top: groove.bottom
+ topMargin: Kirigami.Units.smallSpacing
+ }
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ text: formatedDuration(duration)
+ color: Kirigami.Theme.textColor
+ }
+ }
+ KeyNavigation.up: video
+ KeyNavigation.left: button
+ Keys.onReturnPressed: {
+ hideTimer.restart();
+ if(!navSliderItem){
+ navSliderItem = true
+ } else {
+ navSliderItem = false
+ }
+ }
+
+ Keys.onLeftPressed: {
+ console.log("leftPressedonSlider")
+ hideTimer.restart();
+ if(navSliderItem) {
+ videoControl.seek(video.position - 5000)
+ } else {
+ button.forceActiveFocus()
+ }
+ }
+
+ Keys.onRightPressed: {
+ hideTimer.restart();
+ if(navSliderItem) {
+ videoControl.seek(video.position + 5000)
+ }
+ }
+ }
+
+ Controls.Button {
+ id: backButton
+ Layout.preferredWidth: parent.width > 600 ? Kirigami.Units.iconSizes.large : Kirigami.Units.iconSizes.medium
+ Layout.preferredHeight: Layout.preferredWidth
+ highlighted: focus ? 1 : 0
+ z: 1000
+
+ background: Rectangle {
+ color: Kirigami.Theme.backgroundColor
+ radius: 5
+ border.width: 1.25
+ border.color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.25)
+ }
+
+ contentItem: Kirigami.Icon {
+ anchors.fill: parent
+ anchors.margins: Mycroft.Units.gridUnit
+
+ source: Qt.resolvedUrl("images/back.svg")
+
+ ColorOverlay {
+ source: parent
+ anchors.fill: parent
+ color: Kirigami.Theme.textColor
+ }
+ }
+
+ onClicked: {
+ triggerGuiEvent("video.media.playback.ended", {})
+ video.stop();
+ }
+ KeyNavigation.up: video
+ KeyNavigation.right: button
+ Keys.onReturnPressed: {
+ hideTimer.restart();
+ triggerGuiEvent("video.media.playback.ended", {})
+ video.stop();
+ }
+ onFocusChanged: {
+ hideTimer.restart();
+ }
+ }
+ }
+ }
+ }
+
+ function formatedDuration(millis){
+ var minutes = Math.floor(millis / 60000);
+ var seconds = ((millis % 60000) / 1000).toFixed(0);
+ return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
+ }
+
+ function formatedPosition(millis){
+ var minutes = Math.floor(millis / 60000);
+ var seconds = ((millis % 60000) / 1000).toFixed(0);
+ return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
+ }
+}
diff --git a/ovos_plugin_common_play/ocp/res/ui/+mediacenter/OVOSVideoPlayerQtAv.qml b/ovos_plugin_common_play/ocp/res/ui/+mediacenter/OVOSVideoPlayerQtAv.qml
new file mode 100644
index 0000000..395e1d9
--- /dev/null
+++ b/ovos_plugin_common_play/ocp/res/ui/+mediacenter/OVOSVideoPlayerQtAv.qml
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2019 by Aditya Mehra <aix.m@outlook.com>
+ * Copyright 2019 by Marco Martin <mart@kde.org>
+ *
+ * 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 QtMultimedia 5.12
+import QtQuick.Layouts 1.4
+import QtQuick 2.12
+import QtQuick.Controls 2.12 as Controls
+import org.kde.kirigami 2.10 as Kirigami
+import QtQuick.Window 2.3
+import QtGraphicalEffects 1.0
+import Mycroft 1.0 as Mycroft
+import "." as Local
+import QtAV 1.7
+
+Rectangle {
+ id: root
+ readonly property var videoService: Mycroft.MediaService
+ property Component controlBar
+ color: "black"
+
+ readonly property Item controlBarItem: {
+ if (controlBar) {
+ return controlBar.createObject(root, {"z": 9999});
+ } else {
+ return null;
+ }
+ }
+
+ property var videoSource
+ property var videoStatus
+ property var videoRepeat
+ property var videoThumb
+ property var videoTitle: sessionData.title
+ property var videoAuthor: sessionData.artist
+ property var playerMeta
+ property var cpsMeta
+ property bool busyIndicate: false
+
+ //Player Button Control Actions
+ property var currentState: avVideo.playbackState
+
+ //Mediaplayer Related Properties To Be Set By Probe MediaPlayer
+ property var playerDuration
+ property var playerPosition
+
+ Keys.onDownPressed: {
+ controlBarItem.opened = true
+ controlBarItem.forceActiveFocus()
+ }
+
+ onVideoSourceChanged: {
+ root.play()
+ delay(6000, function() {
+ infomationBar.visible = false;
+ })
+ }
+
+ function play(){
+ avVideo.source = videoSource
+ }
+
+ function pause(){
+ avVideo.pause()
+ }
+
+ function stop(){
+ avVideo.stop()
+ }
+
+ function resume(){
+ avVideo.play()
+ }
+
+ function seek(val){
+ avVideo.seek(val)
+ }
+
+ function next(){
+ videoService.playerNext()
+ }
+
+ function previous(){
+ videoService.playerPrevious()
+ }
+
+ Connections {
+ target: Window.window
+ onVisibleChanged: {
+ if(video.playbackState == MediaPlayer.PlayingState) {
+ stop()
+ }
+ }
+ }
+
+ Connections {
+ target: Mycroft.MediaService
+
+ onPlayRequested: {
+ videoSource = videoService.getTrack()
+ }
+
+ onStopRequested: {
+ videoSource = ""
+ }
+
+ onMediaStatusChanged: {
+ triggerGuiEvent("media.state", {"state": status})
+ if (status == MediaPlayer.EndOfMedia) {
+ pause()
+ }
+ }
+
+ onMetaUpdated: {
+ root.playerMeta = videoService.getPlayerMeta()
+
+ if(root.playerMeta.hasOwnProperty("Title")) {
+ root.videoTitle = root.playerMeta.Title ? root.playerMeta.Title : ""
+ }
+
+ if(root.playerMeta.hasOwnProperty("Artist")) {
+ root.videoAuthor = root.playerMeta.Artist
+ } else if(root.playerMeta.hasOwnProperty("ContributingArtist")) {
+ root.videoAuthor = root.playerMeta.ContributingArtist
+ }
+ }
+
+ onMetaReceived: {
+ root.cpsMeta = videoService.getCPSMeta()
+ root.videoThumb = root.cpsMeta.thumbnail
+ root.videoAuthor = root.cpsMeta.artist
+ root.videoTitle = root.cpsMeta.title
+ }
+ }
+
+
+ Timer {
+ id: delaytimer
+ }
+
+ function delay(delayTime, cb) {
+ delaytimer.interval = delayTime;
+ delaytimer.repeat = false;
+ delaytimer.triggered.connect(cb);
+ delaytimer.start();
+ }
+
+ controlBar: Local.OVOSSeekControlQtAv {
+ id: seekControl
+
+ anchors {
+ bottom: parent.bottom
+ }
+ title: videoTitle
+ videoControl: root
+ duration: avVideo.duration
+ playPosition: avVideo.position
+ onSeekPositionChanged: seek(seekPosition);
+ z: 1000
+ }
+
+ Item {
+ id: videoRoot
+ anchors.fill: parent
+
+ Rectangle {
+ id: infomationBar
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ visible: false
+ color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.6)
+ implicitHeight: vidTitle.implicitHeight + Kirigami.Units.largeSpacing * 2
+ z: 1001
+
+ onVisibleChanged: {
+ delay(15000, function() {
+ infomationBar.visible = false;
+ })
+ }
+
+ Controls.Label {
+ id: vidTitle
+ visible: true
+ maximumLineCount: 2
+ wrapMode: Text.Wrap
+ anchors.left: parent.left
+ anchors.leftMargin: Kirigami.Units.largeSpacing
+ anchors.verticalCenter: parent.verticalCenter
+ text: videoTitle
+ z: 100
+ }
+ }
+
+ Video {
+ id: avVideo
+ anchors.fill: parent
+ autoLoad: true
+ autoPlay: true
+
+ Keys.onReturnPressed: {
+ avVideo.playbackState == MediaPlayer.PlayingState ? avVideo.pause() : avVideo.play()
+ }
+
+ Keys.onDownPressed: {
+ controlBarItem.opened = true
+ controlBarItem.forceActiveFocus()
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ controlBarItem.opened = !controlBarItem.opened
+ }
+ }
+
+ onStatusChanged: {
+ triggerGuiEvent("media.state", {"state": status})
+ if (status == MediaPlayer.EndOfMedia) {
+ pause()
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/ovos_plugin_common_play/ocp/res/ui/OVOSSeekControlQtAv.qml b/ovos_plugin_common_play/ocp/res/ui/OVOSSeekControlQtAv.qml
new file mode 100644
index 0000000..a9437dd
--- /dev/null
+++ b/ovos_plugin_common_play/ocp/res/ui/OVOSSeekControlQtAv.qml
@@ -0,0 +1,407 @@
+/*
+ * Copyright 2019 by Aditya Mehra <aix.m@outlook.com>
+ * Copyright 2019 by Marco Martin <mart@kde.org>
+ *
+ * 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.Layouts 1.4
+import QtQuick 2.12
+import QtQuick.Controls 2.12 as Controls
+import org.kde.kirigami 2.10 as Kirigami
+import QtQuick.Templates 2.12 as Templates
+import QtGraphicalEffects 1.0
+import Mycroft 1.0 as Mycroft
+import QtAV 1.7
+
+Item {
+ id: seekControl
+ property bool opened: false
+ property int duration: 0
+ property int playPosition: 0
+ property int seekPosition: 0
+ property bool enabled: true
+ property bool seeking: false
+ property var videoControl
+ property string title
+ property var currentState: videoService.playbackState
+
+ readonly property var videoService: Mycroft.MediaService
+
+ clip: true
+ implicitWidth: parent.width
+ implicitHeight: mainLayout.implicitHeight + Kirigami.Units.largeSpacing * 2
+ opacity: opened
+
+ onOpenedChanged: {
+ if (opened) {
+ hideTimer.restart();
+ }
+ }
+
+ onFocusChanged: {
+ if(focus) {
+ backButton.forceActiveFocus()
+ }
+ }
+
+ Timer {
+ id: hideTimer
+ interval: 5000
+ onTriggered: {
+ seekControl.opened = false;
+ videoRoot.forceActiveFocus();
+ }
+ }
+
+ Rectangle {
+ width: parent.width
+ height: parent.height
+ property color tempColor: Qt.darker(Kirigami.Theme.backgroundColor, 2)
+ color: Qt.rgba(tempColor.r, tempColor.g, tempColor.b, 0.8)
+ y: opened ? 0 : parent.height
+
+ Rectangle {
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ height: Mycroft.Units.gridUnit / 4
+ color: Kirigami.Theme.highlightColor
+ }
+
+ ColumnLayout {
+ id: mainLayout
+
+ anchors {
+ fill: parent
+ margins: Kirigami.Units.largeSpacing
+ }
+
+ RowLayout {
+ id: mainLayout2
+ Layout.fillHeight: true
+
+ Controls.Button {
+ id: button
+ Layout.preferredWidth: parent.width > 600 ? Kirigami.Units.iconSizes.large : Kirigami.Units.iconSizes.medium
+ Layout.preferredHeight: Layout.preferredWidth
+ highlighted: focus ? 1 : 0
+ z: 1000
+
+ background: Rectangle {
+ color: Kirigami.Theme.backgroundColor
+ radius: 5
+ border.width: 1.25
+ border.color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.25)
+ }
+
+ contentItem: Kirigami.Icon {
+ anchors.fill: parent
+ anchors.margins: Mycroft.Units.gridUnit
+
+ source: avVideo.playbackState == MediaPlayer.PlayingState ? Qt.resolvedUrl("images/media-playback-pause.svg") : Qt.resolvedUrl("images/media-playback-start.svg")
+
+ ColorOverlay {
+ source: parent
+ anchors.fill: parent
+ color: Kirigami.Theme.textColor
+ }
+ }
+
+ onClicked: {
+ avVideo.playbackState == MediaPlayer.PlayingState ? videoControl.pause() : videoControl.currentState == MediaPlayer.PausedState ? videoControl.resume() : videoControl.play()
+ hideTimer.restart();
+ }
+ KeyNavigation.up: video
+ KeyNavigation.left: backButton
+ KeyNavigation.right: slider
+ Keys.onReturnPressed: {
+ clicked()
+ }
+ onFocusChanged: {
+ hideTimer.restart();
+ }
+ }
+
+ Controls.Button {
+ id: prevbutton
+ Layout.preferredWidth: parent.width > 600 ? Kirigami.Units.iconSizes.large : Kirigami.Units.iconSizes.medium
+ Layout.preferredHeight: Layout.preferredWidth
+ highlighted: focus ? 1 : 0
+ z: 1000
+
+ background: Rectangle {
+ color: Kirigami.Theme.backgroundColor
+ radius: 5
+ border.width: 1.25
+ border.color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.25)
+ }
+
+ contentItem: Kirigami.Icon {
+ anchors.fill: parent
+ anchors.margins: Mycroft.Units.gridUnit
+
+ source: Qt.resolvedUrl("images/media-skip-backward.svg")
+
+ ColorOverlay {
+ source: parent
+ anchors.fill: parent
+ color: Kirigami.Theme.textColor
+ }
+ }
+
+ onClicked: {
+ videoControl.previous()
+ hideTimer.restart();
+ }
+
+ KeyNavigation.up: video
+ KeyNavigation.left: backButton
+ KeyNavigation.right: slider
+ Keys.onReturnPressed: {
+ clicked()
+ }
+ onFocusChanged: {
+ hideTimer.restart();
+ }
+ }
+
+ Controls.Button {
+ id: nextbutton
+ Layout.preferredWidth: parent.width > 600 ? Kirigami.Units.iconSizes.large : Kirigami.Units.iconSizes.medium
+ Layout.preferredHeight: Layout.preferredWidth
+ highlighted: focus ? 1 : 0
+ z: 1000
+
+ background: Rectangle {
+ color: Kirigami.Theme.backgroundColor
+ radius: 5
+ border.width: 1.25
+ border.color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.25)
+ }
+
+ contentItem: Kirigami.Icon {
+ anchors.fill: parent
+ anchors.margins: Mycroft.Units.gridUnit
+
+ source: Qt.resolvedUrl("images/media-skip-forward.svg")
+
+
+ ColorOverlay {
+ source: parent
+ anchors.fill: parent
+ color: Kirigami.Theme.textColor
+ }
+ }
+
+ onClicked: {
+ videoControl.next()
+ hideTimer.restart();
+ }
+ KeyNavigation.up: video
+ KeyNavigation.left: backButton
+ KeyNavigation.right: slider
+ Keys.onReturnPressed: {
+ clicked()
+ }
+ onFocusChanged: {
+ hideTimer.restart();
+ }
+ }
+
+ Templates.Slider {
+ id: slider
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter
+ implicitHeight: Kirigami.Units.gridUnit
+ value: seekControl.playPosition
+ from: 0
+ to: seekControl.duration
+ z: 1000
+ property bool navSliderItem
+ property int minimumValue: 0
+ property int maximumValue: 20
+ onMoved: {
+ seekControl.seekPosition = value;
+ hideTimer.restart();
+ }
+
+ onNavSliderItemChanged: {
+ if(slider.navSliderItem){
+ recthandler.color = "red"
+ } else if (slider.focus) {
+ recthandler.color = Kirigami.Theme.linkColor
+ }
+ }
+
+ onFocusChanged: {
+ if(!slider.focus){
+ recthandler.color = Kirigami.Theme.textColor
+ } else {
+ recthandler.color = Kirigami.Theme.linkColor
+ }
+ }
+
+ handle: Item {
+ x: slider.visualPosition * (parent.width - (Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing))
+ anchors.verticalCenter: parent.verticalCenter
+ height: parent.height + Mycroft.Units.gridUnit
+
+ Rectangle {
+ id: hand
+ anchors.verticalCenter: parent.verticalCenter
+ implicitWidth: Kirigami.Units.iconSizes.small + Kirigami.Units.smallSpacing
+ implicitHeight: parent.height / 2
+ color: Kirigami.Theme.backgroundColor
+ border.color: Kirigami.Theme.highlightColor
+ }
+ }
+
+ background: Item {
+ Rectangle {
+ id: groove
+ anchors {
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ right: parent.right
+ }
+ height: Math.round(Kirigami.Units.gridUnit/3)
+ color: Qt.lighter(Kirigami.Theme.highlightColor, 1.5)
+
+ Rectangle {
+ anchors {
+ left: parent.left
+ top: parent.top
+ bottom: parent.bottom
+ }
+ gradient: Gradient {
+ orientation: Gradient.Horizontal
+ GradientStop { position: 0.0; color: Kirigami.Theme.highlightColor }
+ GradientStop { position: 1.0; color: Qt.darker(Kirigami.Theme.highlightColor, 1.5) }
+ }
+ width: slider.position * (parent.width - slider.handle.width/2) + slider.handle.width/2
+ }
+ }
+
+ Controls.Label {
+ anchors {
+ left: parent.left
+ top: groove.bottom
+ topMargin: Kirigami.Units.smallSpacing
+ }
+ horizontalAlignment: Text.AlignLeft
+ verticalAlignment: Text.AlignVCenter
+ text: formatedPosition(playPosition)
+ color: Kirigami.Theme.textColor
+ }
+
+ Controls.Label {
+ anchors {
+ right: parent.right
+ top: groove.bottom
+ topMargin: Kirigami.Units.smallSpacing
+ }
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ text: formatedDuration(duration)
+ color: Kirigami.Theme.textColor
+ }
+ }
+ KeyNavigation.up: video
+ KeyNavigation.left: button
+ Keys.onReturnPressed: {
+ hideTimer.restart();
+ if(!navSliderItem){
+ navSliderItem = true
+ } else {
+ navSliderItem = false
+ }
+ }
+
+ Keys.onLeftPressed: {
+ console.log("leftPressedonSlider")
+ hideTimer.restart();
+ if(navSliderItem) {
+ videoControl.seek(video.position - 5000)
+ } else {
+ button.forceActiveFocus()
+ }
+ }
+
+ Keys.onRightPressed: {
+ hideTimer.restart();
+ if(navSliderItem) {
+ videoControl.seek(video.position + 5000)
+ }
+ }
+ }
+
+ Controls.Button {
+ id: backButton
+ Layout.preferredWidth: parent.width > 600 ? Kirigami.Units.iconSizes.large : Kirigami.Units.iconSizes.medium
+ Layout.preferredHeight: Layout.preferredWidth
+ highlighted: focus ? 1 : 0
+ z: 1000
+
+ background: Rectangle {
+ color: Kirigami.Theme.backgroundColor
+ radius: 5
+ border.width: 1.25
+ border.color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.25)
+ }
+
+ contentItem: Kirigami.Icon {
+ anchors.fill: parent
+ anchors.margins: Mycroft.Units.gridUnit
+
+ source: Qt.resolvedUrl("images/back.svg")
+
+ ColorOverlay {
+ source: parent
+ anchors.fill: parent
+ color: Kirigami.Theme.textColor
+ }
+ }
+
+ onClicked: {
+ triggerGuiEvent("video.media.playback.ended", {})
+ video.stop();
+ }
+ KeyNavigation.up: video
+ KeyNavigation.right: button
+ Keys.onReturnPressed: {
+ hideTimer.restart();
+ triggerGuiEvent("video.media.playback.ended", {})
+ video.stop();
+ }
+ onFocusChanged: {
+ hideTimer.restart();
+ }
+ }
+ }
+ }
+ }
+
+ function formatedDuration(millis){
+ var minutes = Math.floor(millis / 60000);
+ var seconds = ((millis % 60000) / 1000).toFixed(0);
+ return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
+ }
+
+ function formatedPosition(millis){
+ var minutes = Math.floor(millis / 60000);
+ var seconds = ((millis % 60000) / 1000).toFixed(0);
+ return minutes + ":" + (seconds < 10 ? '0' : '') + seconds;
+ }
+}
diff --git a/ovos_plugin_common_play/ocp/res/ui/OVOSVideoPlayerQtAv.qml b/ovos_plugin_common_play/ocp/res/ui/OVOSVideoPlayerQtAv.qml
new file mode 100644
index 0000000..395e1d9
--- /dev/null
+++ b/ovos_plugin_common_play/ocp/res/ui/OVOSVideoPlayerQtAv.qml
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2019 by Aditya Mehra <aix.m@outlook.com>
+ * Copyright 2019 by Marco Martin <mart@kde.org>
+ *
+ * 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 QtMultimedia 5.12
+import QtQuick.Layouts 1.4
+import QtQuick 2.12
+import QtQuick.Controls 2.12 as Controls
+import org.kde.kirigami 2.10 as Kirigami
+import QtQuick.Window 2.3
+import QtGraphicalEffects 1.0
+import Mycroft 1.0 as Mycroft
+import "." as Local
+import QtAV 1.7
+
+Rectangle {
+ id: root
+ readonly property var videoService: Mycroft.MediaService
+ property Component controlBar
+ color: "black"
+
+ readonly property Item controlBarItem: {
+ if (controlBar) {
+ return controlBar.createObject(root, {"z": 9999});
+ } else {
+ return null;
+ }
+ }
+
+ property var videoSource
+ property var videoStatus
+ property var videoRepeat
+ property var videoThumb
+ property var videoTitle: sessionData.title
+ property var videoAuthor: sessionData.artist
+ property var playerMeta
+ property var cpsMeta
+ property bool busyIndicate: false
+
+ //Player Button Control Actions
+ property var currentState: avVideo.playbackState
+
+ //Mediaplayer Related Properties To Be Set By Probe MediaPlayer
+ property var playerDuration
+ property var playerPosition
+
+ Keys.onDownPressed: {
+ controlBarItem.opened = true
+ controlBarItem.forceActiveFocus()
+ }
+
+ onVideoSourceChanged: {
+ root.play()
+ delay(6000, function() {
+ infomationBar.visible = false;
+ })
+ }
+
+ function play(){
+ avVideo.source = videoSource
+ }
+
+ function pause(){
+ avVideo.pause()
+ }
+
+ function stop(){
+ avVideo.stop()
+ }
+
+ function resume(){
+ avVideo.play()
+ }
+
+ function seek(val){
+ avVideo.seek(val)
+ }
+
+ function next(){
+ videoService.playerNext()
+ }
+
+ function previous(){
+ videoService.playerPrevious()
+ }
+
+ Connections {
+ target: Window.window
+ onVisibleChanged: {
+ if(video.playbackState == MediaPlayer.PlayingState) {
+ stop()
+ }
+ }
+ }
+
+ Connections {
+ target: Mycroft.MediaService
+
+ onPlayRequested: {
+ videoSource = videoService.getTrack()
+ }
+
+ onStopRequested: {
+ videoSource = ""
+ }
+
+ onMediaStatusChanged: {
+ triggerGuiEvent("media.state", {"state": status})
+ if (status == MediaPlayer.EndOfMedia) {
+ pause()
+ }
+ }
+
+ onMetaUpdated: {
+ root.playerMeta = videoService.getPlayerMeta()
+
+ if(root.playerMeta.hasOwnProperty("Title")) {
+ root.videoTitle = root.playerMeta.Title ? root.playerMeta.Title : ""
+ }
+
+ if(root.playerMeta.hasOwnProperty("Artist")) {
+ root.videoAuthor = root.playerMeta.Artist
+ } else if(root.playerMeta.hasOwnProperty("ContributingArtist")) {
+ root.videoAuthor = root.playerMeta.ContributingArtist
+ }
+ }
+
+ onMetaReceived: {
+ root.cpsMeta = videoService.getCPSMeta()
+ root.videoThumb = root.cpsMeta.thumbnail
+ root.videoAuthor = root.cpsMeta.artist
+ root.videoTitle = root.cpsMeta.title
+ }
+ }
+
+
+ Timer {
+ id: delaytimer
+ }
+
+ function delay(delayTime, cb) {
+ delaytimer.interval = delayTime;
+ delaytimer.repeat = false;
+ delaytimer.triggered.connect(cb);
+ delaytimer.start();
+ }
+
+ controlBar: Local.OVOSSeekControlQtAv {
+ id: seekControl
+
+ anchors {
+ bottom: parent.bottom
+ }
+ title: videoTitle
+ videoControl: root
+ duration: avVideo.duration
+ playPosition: avVideo.position
+ onSeekPositionChanged: seek(seekPosition);
+ z: 1000
+ }
+
+ Item {
+ id: videoRoot
+ anchors.fill: parent
+
+ Rectangle {
+ id: infomationBar
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ visible: false
+ color: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.6)
+ implicitHeight: vidTitle.implicitHeight + Kirigami.Units.largeSpacing * 2
+ z: 1001
+
+ onVisibleChanged: {
+ delay(15000, function() {
+ infomationBar.visible = false;
+ })
+ }
+
+ Controls.Label {
+ id: vidTitle
+ visible: true
+ maximumLineCount: 2
+ wrapMode: Text.Wrap
+ anchors.left: parent.left
+ anchors.leftMargin: Kirigami.Units.largeSpacing
+ anchors.verticalCenter: parent.verticalCenter
+ text: videoTitle
+ z: 100
+ }
+ }
+
+ Video {
+ id: avVideo
+ anchors.fill: parent
+ autoLoad: true
+ autoPlay: true
+
+ Keys.onReturnPressed: {
+ avVideo.playbackState == MediaPlayer.PlayingState ? avVideo.pause() : avVideo.play()
+ }
+
+ Keys.onDownPressed: {
+ controlBarItem.opened = true
+ controlBarItem.forceActiveFocus()
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ controlBarItem.opened = !controlBarItem.opened
+ }
+ }
+
+ onStatusChanged: {
+ triggerGuiEvent("media.state", {"state": status})
+ if (status == MediaPlayer.EndOfMedia) {
+ pause()
+ }
+ }
+ }
+ }
+}
+
+
From 8aaebc157d14052b991a2e0dc045480e39bdd522 Mon Sep 17 00:00:00 2001
From: jarbasai <jarbasai@mailfence.com>
Date: Mon, 16 Jan 2023 18:11:43 +0000
Subject: [PATCH 2/4] use enum
---
ovos_plugin_common_play/__init__.py | 6 ++++++
ovos_plugin_common_play/ocp/gui.py | 28 ++++++++++++++++++++-------
ovos_plugin_common_play/ocp/player.py | 4 +---
3 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/ovos_plugin_common_play/__init__.py b/ovos_plugin_common_play/__init__.py
index 0dc036c..81a816a 100644
--- a/ovos_plugin_common_play/__init__.py
+++ b/ovos_plugin_common_play/__init__.py
@@ -209,6 +209,12 @@ def load_service(base_config, bus):
## this list is checked in order until a available backend is found
"preferred_audio_services": ["vlc", "mplayer", "simple"],
+ # to handle video playback different backends are supported
+ # - valid options - "native", "qtav", "auto"
+ # "qtav" does not support widget integration
+ # "native" sometimes has problems playing stream urls
+ "video_player_backend": "auto",
+
## when media playback ends "click next"
"autoplay": True,
## if True behaves as if the search results are part of the playlist
diff --git a/ovos_plugin_common_play/ocp/gui.py b/ovos_plugin_common_play/ocp/gui.py
index 87840e1..708ff33 100644
--- a/ovos_plugin_common_play/ocp/gui.py
+++ b/ovos_plugin_common_play/ocp/gui.py
@@ -1,13 +1,21 @@
+import enum
from os.path import join, dirname
-from time import sleep, time
+from threading import Timer
+from time import sleep
+
from mycroft_bus_client.message import Message
-from ovos_utils.gui import GUIInterface
+from ovos_config import Configuration
from ovos_utils.events import EventSchedulerInterface
+from ovos_utils.gui import GUIInterface
from ovos_utils.log import LOG
-from ovos_config import Configuration
from ovos_plugin_common_play.ocp.status import *
-from threading import Timer
+
+
+class VideoPlayerBackend(str, enum.Enum):
+ AUTO = "auto"
+ QTAV = "qtav"
+ NATIVE = "native"
class OCPMediaPlayerGUI(GUIInterface):
@@ -23,7 +31,6 @@ def __init__(self):
self.search_mode_is_app = False
self.persist_home_display = False
self.event_scheduler_interface = None
- self.video_player_interface = None
def bind(self, player):
self.player = player
@@ -37,7 +44,11 @@ def bind(self, player):
self.player.add_event('ovos.common_play.skill.play',
self.handle_play_skill_featured_media)
self.event_scheduler_interface = EventSchedulerInterface(name="ovos.common_play", bus=self.bus)
- self.video_player_interface = self.player.video_player_interface
+
+ @property
+ def video_backend(self):
+ return self.player.settings.get("video_player_backend") or \
+ VideoPlayerBackend.AUTO
@property
def home_screen_page(self):
@@ -57,7 +68,10 @@ def audio_service_page(self):
@property
def video_player_page(self):
- if self.video_player_interface = "qtav":
+ if self.video_backend == VideoPlayerBackend.AUTO:
+ # TODO - detect if qtav is available, if yes use it
+ pass
+ if self.video_backend == VideoPlayerBackend.QTAV:
return join(self.player.res_dir, "ui", "OVOSVideoPlayerQtAv.qml")
else:
return join(self.player.res_dir, "ui", "OVOSVideoPlayer.qml")
diff --git a/ovos_plugin_common_play/ocp/player.py b/ovos_plugin_common_play/ocp/player.py
index f246654..af2db6e 100644
--- a/ovos_plugin_common_play/ocp/player.py
+++ b/ovos_plugin_common_play/ocp/player.py
@@ -25,8 +25,6 @@ def __init__(self, bus=None, settings=None, lang=None, gui=None,
gui = gui or OCPMediaPlayerGUI()
# mpris settings
manage_players = settings.get("manage_external_players", False)
- # can be "default (QtMultimedia via Mycroft GUI)" or "qtav (QtAv via QML interface)"
- video_player_interface = settings.get("video_player_interface", "default")
if settings.disable_mpris:
LOG.info("MPRIS integration is disabled")
self.mpris = None
@@ -732,4 +730,4 @@ def handle_set_app_timeout_mode(self, message):
self.settings["app_view_timeout_mode"] = message.data.get("mode", "all")
self.settings.store()
self.gui["app_view_timeout_mode"] = self.settings.get("app_view_timeout_mode", "all")
- self.gui.cancel_app_view_timeout()
+ self.gui.cancel_app_view_timeout()
\ No newline at end of file
From 65fcf3053e92f332dd625c3bd1f3ea5b85ce5e9b Mon Sep 17 00:00:00 2001
From: jarbasai <jarbasai@mailfence.com>
Date: Mon, 16 Jan 2023 18:24:50 +0000
Subject: [PATCH 3/4] autodetect qtav
---
ovos_plugin_common_play/ocp/gui.py | 16 ++++++++++------
ovos_plugin_common_play/ocp/utils.py | 8 +++++++-
2 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/ovos_plugin_common_play/ocp/gui.py b/ovos_plugin_common_play/ocp/gui.py
index 708ff33..edab35b 100644
--- a/ovos_plugin_common_play/ocp/gui.py
+++ b/ovos_plugin_common_play/ocp/gui.py
@@ -10,6 +10,7 @@
from ovos_utils.log import LOG
from ovos_plugin_common_play.ocp.status import *
+from ovos_plugin_common_play.ocp.utils import is_qtav_available
class VideoPlayerBackend(str, enum.Enum):
@@ -68,13 +69,16 @@ def audio_service_page(self):
@property
def video_player_page(self):
+ qtav = join(self.player.res_dir, "ui", "OVOSVideoPlayerQtAv.qml")
+ native = join(self.player.res_dir, "ui", "OVOSVideoPlayer.qml")
if self.video_backend == VideoPlayerBackend.AUTO:
- # TODO - detect if qtav is available, if yes use it
- pass
- if self.video_backend == VideoPlayerBackend.QTAV:
- return join(self.player.res_dir, "ui", "OVOSVideoPlayerQtAv.qml")
- else:
- return join(self.player.res_dir, "ui", "OVOSVideoPlayer.qml")
+ # detect if qtav is available, if yes use it
+ if is_qtav_available():
+ return qtav
+ elif self.video_backend == VideoPlayerBackend.QTAV:
+ return qtav
+
+ return native
@property
def web_player_page(self):
diff --git a/ovos_plugin_common_play/ocp/utils.py b/ovos_plugin_common_play/ocp/utils.py
index 71983ab..e1ef17f 100644
--- a/ovos_plugin_common_play/ocp/utils.py
+++ b/ovos_plugin_common_play/ocp/utils.py
@@ -2,7 +2,7 @@
import shutil
import tempfile
from os import makedirs
-from os.path import basename, expanduser, isfile, join, dirname
+from os.path import basename, expanduser, isfile, join, dirname, exists
from ovos_plugin_common_play.ocp.status import TrackState, PlaybackType
from ovos_plugin_manager.ocp import StreamHandler
@@ -10,6 +10,12 @@
ocp_plugins = StreamHandler()
+def is_qtav_available():
+ return exists("/usr/include/qt/QtAV") or \
+ exists("/usr/lib/qt/qml/QtAV") or \
+ exists("/usr/lib/libQtAV.so")
+
+
def find_mime(uri):
""" Determine mime type. """
mime = mimetypes.guess_type(uri)
From 19223329133f660e1e02463d9eb3689583bb81ae Mon Sep 17 00:00:00 2001
From: jarbasai <jarbasai@mailfence.com>
Date: Mon, 16 Jan 2023 19:23:15 +0000
Subject: [PATCH 4/4] add debug logs for qtav
---
ovos_plugin_common_play/ocp/gui.py | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/ovos_plugin_common_play/ocp/gui.py b/ovos_plugin_common_play/ocp/gui.py
index edab35b..2f61ce0 100644
--- a/ovos_plugin_common_play/ocp/gui.py
+++ b/ovos_plugin_common_play/ocp/gui.py
@@ -71,12 +71,21 @@ def audio_service_page(self):
def video_player_page(self):
qtav = join(self.player.res_dir, "ui", "OVOSVideoPlayerQtAv.qml")
native = join(self.player.res_dir, "ui", "OVOSVideoPlayer.qml")
+ has_qtav = is_qtav_available()
+ if has_qtav:
+ LOG.info("QtAV detected")
+
if self.video_backend == VideoPlayerBackend.AUTO:
# detect if qtav is available, if yes use it
- if is_qtav_available():
+ if has_qtav:
+ LOG.debug("defaulting to OVOSVideoPlayerQtAv")
return qtav
+ LOG.debug("defaulting to native OVOSVideoPlayer")
elif self.video_backend == VideoPlayerBackend.QTAV:
+ LOG.debug("OVOSVideoPlayerQtAv explicitly configured")
return qtav
+ elif self.video_backend == VideoPlayerBackend.NATIVE:
+ LOG.debug("native OVOSVideoPlayer explicitly configured")
return native