Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple Play Video Implementation for Mycroft GUI #2913

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions mycroft/enclosure/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ def setup_default_handlers(self):
"""Sets the handlers for the default messages."""
msg_type = self.build_message_type('set')
self.skill.add_event(msg_type, self.gui_set)
self.skill.gui.register_handler('video.media.playback.ended',
self.stop_video)

def register_handler(self, event, handler):
"""Register a handler for GUI events.
Expand Down Expand Up @@ -358,6 +360,40 @@ def show_url(self, url, override_idle=None,
self.show_page("SYSTEM_UrlFrame.qml", override_idle,
override_animations)

def play_video(self, url, title="", repeat=None, override_idle=True,
override_animations=None):
""" Play video stream

Arguments:
url (str): URL of video source
title (str): Title of media to be displayed
repeat (boolean, int):
True: Infinitly loops the current video track
(int): Loops the video track for specified number of
times.
override_idle (boolean, int):
True: Takes over the resting page indefinitely
(int): Delays resting page for the specified number of
seconds.
override_animations (boolean):
True: Disables showing all platform skill animations.
False: 'Default' always show animations.
"""
self["playStatus"] = "play"
self["video"] = url
self["title"] = title
self["playerRepeat"] = repeat
self.video_info = {"title": title, "url": url}
self.show_page("SYSTEM_VideoPlayer.qml",
override_idle=override_idle,
override_animations=override_animations)

def stop_video(self):
"""Stop video playback."""
self["playStatus"] = "stop"
self.skill.bus.emit(Message("mycroft.gui.screen.close",
{"skill_id": self.skill.skill_id}))

def release(self):
"""Signal that this skill is no longer using the GUI,
allow different platforms to properly handle this event.
Expand Down
225 changes: 225 additions & 0 deletions mycroft/res/ui/SYSTEM_VideoPlayer.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import QtMultimedia 5.12
import QtQuick.Layouts 1.4
import QtQuick 2.9
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

Mycroft.Delegate {
id: root

property var videoSource: sessionData.video
property var videoStatus: sessionData.playStatus
property var videoRepeat: sessionData.playerRepeat
property var videoThumb: sessionData.videoThumb
property var videoTitle: sessionData.title
property bool busyIndicate: false

fillWidth: true
background: Rectangle {
color: "black"
}
leftPadding: 0
topPadding: 0
rightPadding: 0
bottomPadding: 0

onEnabledChanged: syncStatusTimer.restart()
onVideoSourceChanged: syncStatusTimer.restart()

Component.onCompleted: {
syncStatusTimer.restart()
}

Keys.onDownPressed: {
controlBarItem.opened = true
controlBarItem.forceActiveFocus()
}

onFocusChanged: {
video.forceActiveFocus();
}

onVideoRepeatChanged: {
if(typeof videoRepeat !== "undefined" && typeof videoRepeat == "boolean"){
if(videoRepeat){
video.loops = MediaPlayer.Infinite
video.flushMode = VideoOutput.LastFrame
}
} else if(typeof videoRepeat !== "undefined" && typeof videoRepeat == "number"){
if(videoRepeat > 1){
video.loops = videoRepeat
video.flushMode = VideoOutput.LastFrame
}
} else {
video.loops = 1
video.flushMode = VideoOutput.EmptyFrame
}
}

onVideoStatusChanged: {
switch(videoStatus){
case "stop":
video.stop();
break;
case "pause":
video.pause()
break;
case "play":
video.play()
delay(6000, function() {
infomationBar.visible = false;
})
break;
}
}

Connections {
target: Window.window
onVisibleChanged: {
if(video.playbackState == MediaPlayer.PlayingState) {
video.stop()
}
}
}

Timer {
id: syncStatusTimer
interval: 0
onTriggered: {
if (enabled && videoStatus == "play") {
video.play();
} else if (videoStatus == "stop") {
video.stop();
} else {
video.pause();
}
}
}

Timer {
id: delaytimer
}

function delay(delayTime, cb) {
delaytimer.interval = delayTime;
delaytimer.repeat = false;
delaytimer.triggered.connect(cb);
delaytimer.start();
}

controlBar: Local.SeekControl {
id: seekControl
anchors {
bottom: parent.bottom
}
title: videoTitle
videoControl: video
duration: video.duration
playPosition: video.position
onSeekPositionChanged: video.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: video
anchors.fill: parent
focus: true
autoLoad: true
autoPlay: false
loops: 1
source: videoSource

Keys.onReturnPressed: {
video.playbackState == MediaPlayer.PlayingState ? video.pause() : video.play()
}

Keys.onDownPressed: {
controlBarItem.opened = true
controlBarItem.forceActiveFocus()
}

MouseArea {
anchors.fill: parent
onClicked: {
controlBarItem.opened = !controlBarItem.opened
}
}

onStatusChanged: {
console.log(status)
if(status == MediaPlayer.EndOfMedia) {
triggerGuiEvent("video.media.playback.ended", {})
busyIndicatorPop.enabled = false
}
if(status == MediaPlayer.Loading) {
busyIndicatorPop.visible = true
busyIndicatorPop.enabled = true
}
if(status == MediaPlayer.Loaded || status == MediaPlayer.Buffered){
busyIndicatorPop.visible = false
busyIndicatorPop.enabled = false
}
}

Rectangle {
id: busyIndicatorPop
width: parent.width
height: parent.height
color: Qt.rgba(0, 0, 0, 0.2)
visible: false
enabled: false

Controls.BusyIndicator {
id: busyIndicate
running: busyIndicate
anchors.centerIn: parent
}

onEnabledChanged: {
if(busyIndicatorPop.enabled){
busyIndicate.running = true
} else {
busyIndicate.running = false
}
}
}
}
}
}
Loading