diff --git a/mycroft/enclosure/gui.py b/mycroft/enclosure/gui.py
index 33130bca7961..df0492e47fd9 100644
--- a/mycroft/enclosure/gui.py
+++ b/mycroft/enclosure/gui.py
@@ -14,12 +14,19 @@
#
""" Interface for interacting with the Mycroft gui qml viewer. """
from os.path import join
-
+from enum import IntEnum
from mycroft.configuration import Configuration
from mycroft.messagebus.message import Message
from mycroft.util import resolve_resource_file
+class GUIPlaybackStatus(IntEnum):
+ STOPPED = 0
+ PLAYING = 1
+ PAUSED = 2
+ UNDEFINED = 3
+
+
class SkillGUI:
"""SkillGUI - Interface to the Graphical User Interface
@@ -38,6 +45,7 @@ def __init__(self, skill):
self.skill = skill
self.on_gui_changed_callback = None
self.config = Configuration.get()
+ self.video_info = None
@property
def connected(self):
@@ -63,6 +71,42 @@ def setup_default_handlers(self):
msg_type = self.build_message_type('set')
self.skill.add_event(msg_type, self.gui_set)
+ # TODO can we rename this namespace to mycroft.playback.XXX ?
+ self.skill.add_event('mycroft.audio.service.pause',
+ self.__handle_gui_pause)
+ self.skill.add_event('mycroft.audio.service.resume',
+ self.__handle_gui_resume)
+ self.skill.add_event('mycroft.audio.service.stop',
+ self.__handle_gui_stop)
+ self.skill.add_event('mycroft.audio.service.track_info',
+ self.__handle_gui_track_info)
+ self.skill.add_event('mycroft.audio.queue_end',
+ self.__handle_gui_stop)
+ self.skill.gui.register_handler('video.media.playback.ended',
+ self.__handle_gui_stop)
+
+ # Audio Service bus messages
+ def __handle_gui_resume(self, message):
+ """Resume video playback in gui"""
+ self.resume_video()
+
+ def __handle_gui_stop(self, message):
+ """Stop video playback in gui"""
+ self.stop_video()
+
+ def __handle_gui_pause(self, message):
+ """Pause video playback in gui"""
+ self.pause_video()
+
+ def __handle_gui_track_info(self, message):
+ """Answer request information of current playing track.
+ Needed for handling stop """
+ if self.video_info:
+ self.skill.bus.emit(
+ message.reply('mycroft.audio.service.track_info_reply',
+ self.video_info))
+ return self.video_info
+
def register_handler(self, event, handler):
"""Register a handler for GUI events.
@@ -367,6 +411,72 @@ def release(self):
self.skill.bus.emit(Message("mycroft.gui.screen.close",
{"skill_id": self.skill.skill_id}))
+ 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)
+
+ @property
+ def is_video_displayed(self):
+ """Returns whether the gui is in a video playback state.
+ Eg if the video is paused, it would still be displayed on screen
+ but the video itself is not "playing" so to speak"""
+ return self.video_info is not None
+
+ @property
+ def playback_status(self):
+ """Returns gui playback status,
+ indicates if gui is playing, paused or stopped"""
+ if self.__session_data.get("playStatus", -1) == "play":
+ return GUIPlaybackStatus.PLAYING
+ if self.__session_data.get("playStatus", -1) == "pause":
+ return GUIPlaybackStatus.PAUSED
+ if self.__session_data.get("playStatus", -1) == "stop":
+ return GUIPlaybackStatus.STOPPED
+ return GUIPlaybackStatus.UNDEFINED
+
+ def pause_video(self):
+ """Pause video playback."""
+ if self.is_video_displayed:
+ self["playStatus"] = "pause"
+
+ def stop_video(self):
+ """Stop video playback."""
+ # TODO detect end of media playback from gui and call this
+ if self.is_video_displayed:
+ self["playStatus"] = "stop"
+ self.skill.bus.emit(Message("mycroft.gui.screen.close",
+ {"skill_id": self.skill.skill_id}))
+ self.video_info = None
+
+ def resume_video(self):
+ """Resume paused video playback."""
+ if self.__session_data.get("playStatus", "stop") == "pause":
+ self["playStatus"] = "play"
+
def shutdown(self):
"""Shutdown gui interface.
diff --git a/mycroft/res/ui/SYSTEM_VideoPlayer.qml b/mycroft/res/ui/SYSTEM_VideoPlayer.qml
new file mode 100644
index 000000000000..b4cdb6dfe6f4
--- /dev/null
+++ b/mycroft/res/ui/SYSTEM_VideoPlayer.qml
@@ -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
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/mycroft/res/ui/SeekControl.qml b/mycroft/res/ui/SeekControl.qml
new file mode 100644
index 000000000000..30c48bc2492c
--- /dev/null
+++ b/mycroft/res/ui/SeekControl.qml
@@ -0,0 +1,271 @@
+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.Templates 2.2 as Templates
+import QtGraphicalEffects 1.0
+
+import Mycroft 1.0 as Mycroft
+
+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
+
+ 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
+ color: Qt.rgba(0, 0, 0, 0.8)
+ y: opened ? 0 : parent.height
+
+ ColumnLayout {
+ id: mainLayout
+
+ anchors {
+ fill: parent
+ margins: Kirigami.Units.largeSpacing
+ }
+
+ RowLayout {
+ id: mainLayout2
+ Layout.fillHeight: true
+ Controls.RoundButton {
+ 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 {
+ radius: 200
+ color: "#1a1a1a"
+ border.width: 1.25
+ border.color: "white"
+ }
+
+ contentItem: Item {
+ Image {
+ width: parent.width - Kirigami.Units.largeSpacing
+ height: width
+ anchors.centerIn: parent
+ source: "images/back.svg"
+ }
+ }
+
+ 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();
+ }
+ }
+ Controls.RoundButton {
+ 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 {
+ radius: 200
+ color: "#1a1a1a"
+ border.width: 1.25
+ border.color: "white"
+ }
+
+ contentItem: Item {
+ Image {
+ width: parent.width - Kirigami.Units.largeSpacing
+ height: width
+ anchors.centerIn: parent
+ source: videoControl.playbackState === MediaPlayer.PlayingState ? "images/media-pause.svg" : "images/media-play.svg"
+ }
+ }
+
+ onClicked: {
+ video.playbackState === MediaPlayer.PlayingState ? video.pause() : video.play();
+ hideTimer.restart();
+ }
+ KeyNavigation.up: video
+ KeyNavigation.left: backButton
+ KeyNavigation.right: slider
+ Keys.onReturnPressed: {
+ video.playbackState === MediaPlayer.PlayingState ? video.pause() : video.play();
+ hideTimer.restart();
+ }
+ 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: Rectangle {
+ id: recthandler
+ x: slider.position * (parent.width - width)
+ implicitWidth: Kirigami.Units.gridUnit
+ implicitHeight: implicitWidth
+ radius: width
+ color: Kirigami.Theme.textColor
+ }
+ background: Item {
+ Rectangle {
+ id: groove
+ anchors {
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ right: parent.right
+ }
+ radius: height
+ height: Math.round(Kirigami.Units.gridUnit/3)
+ color: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.3)
+ Rectangle {
+ anchors {
+ left: parent.left
+ top: parent.top
+ bottom: parent.bottom
+ }
+ radius: height
+ color: Kirigami.Theme.highlightColor
+ 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: formatTime(playPosition)
+ color: "white"
+ }
+
+ Controls.Label {
+ anchors {
+ right: parent.right
+ top: groove.bottom
+ topMargin: Kirigami.Units.smallSpacing
+ }
+ horizontalAlignment: Text.AlignRight
+ verticalAlignment: Text.AlignVCenter
+ text: formatTime(duration)
+ }
+ }
+ 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) {
+ video.seek(video.position - 5000)
+ } else {
+ button.forceActiveFocus()
+ }
+ }
+
+ Keys.onRightPressed: {
+ hideTimer.restart();
+ if(navSliderItem) {
+ video.seek(video.position + 5000)
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+
+ function formatTime(timeInMs) {
+ if (!timeInMs || timeInMs <= 0) return "0:00"
+ var seconds = timeInMs / 1000;
+ var minutes = Math.floor(seconds / 60)
+ seconds = Math.floor(seconds % 60)
+ if (seconds < 10) seconds = "0" + seconds;
+ return minutes + ":" + seconds
+ }
+}
diff --git a/mycroft/res/ui/images/back.svg b/mycroft/res/ui/images/back.svg
new file mode 100644
index 000000000000..eaf775bac2fc
--- /dev/null
+++ b/mycroft/res/ui/images/back.svg
@@ -0,0 +1,79 @@
+
+
+
+
diff --git a/mycroft/res/ui/images/media-fullscreen.svg b/mycroft/res/ui/images/media-fullscreen.svg
new file mode 100644
index 000000000000..0ce22138e575
--- /dev/null
+++ b/mycroft/res/ui/images/media-fullscreen.svg
@@ -0,0 +1,135 @@
+
+
+
+
diff --git a/mycroft/res/ui/images/media-mute.svg b/mycroft/res/ui/images/media-mute.svg
new file mode 100644
index 000000000000..fac83baa966c
--- /dev/null
+++ b/mycroft/res/ui/images/media-mute.svg
@@ -0,0 +1,130 @@
+
+
+
+
diff --git a/mycroft/res/ui/images/media-next.svg b/mycroft/res/ui/images/media-next.svg
new file mode 100644
index 000000000000..1c5a5262f8d4
--- /dev/null
+++ b/mycroft/res/ui/images/media-next.svg
@@ -0,0 +1,96 @@
+
+
+
+
diff --git a/mycroft/res/ui/images/media-pause.svg b/mycroft/res/ui/images/media-pause.svg
new file mode 100644
index 000000000000..06d0241fa527
--- /dev/null
+++ b/mycroft/res/ui/images/media-pause.svg
@@ -0,0 +1,95 @@
+
+
+
+
diff --git a/mycroft/res/ui/images/media-play.svg b/mycroft/res/ui/images/media-play.svg
new file mode 100644
index 000000000000..ab08c7cc6253
--- /dev/null
+++ b/mycroft/res/ui/images/media-play.svg
@@ -0,0 +1,86 @@
+
+
+
+
diff --git a/mycroft/res/ui/images/media-playback-pause.svg b/mycroft/res/ui/images/media-playback-pause.svg
new file mode 100644
index 000000000000..972dfa2f09d8
--- /dev/null
+++ b/mycroft/res/ui/images/media-playback-pause.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/mycroft/res/ui/images/media-playback-start.svg b/mycroft/res/ui/images/media-playback-start.svg
new file mode 100644
index 000000000000..4627f98824b3
--- /dev/null
+++ b/mycroft/res/ui/images/media-playback-start.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/mycroft/res/ui/images/media-previous.svg b/mycroft/res/ui/images/media-previous.svg
new file mode 100644
index 000000000000..1dc4d094e5ab
--- /dev/null
+++ b/mycroft/res/ui/images/media-previous.svg
@@ -0,0 +1,96 @@
+
+
+
+
diff --git a/mycroft/res/ui/images/media-repeat.svg b/mycroft/res/ui/images/media-repeat.svg
new file mode 100644
index 000000000000..ea57dd894ae3
--- /dev/null
+++ b/mycroft/res/ui/images/media-repeat.svg
@@ -0,0 +1,154 @@
+
+
+
+
diff --git a/mycroft/res/ui/images/media-stop.svg b/mycroft/res/ui/images/media-stop.svg
new file mode 100644
index 000000000000..ae86a1951f6f
--- /dev/null
+++ b/mycroft/res/ui/images/media-stop.svg
@@ -0,0 +1,87 @@
+
+
+
+
diff --git a/mycroft/res/ui/images/media-unmute.svg b/mycroft/res/ui/images/media-unmute.svg
new file mode 100644
index 000000000000..6988c01636f9
--- /dev/null
+++ b/mycroft/res/ui/images/media-unmute.svg
@@ -0,0 +1,137 @@
+
+
+
+
diff --git a/test/unittests/enclosure/test_gui.py b/test/unittests/enclosure/test_gui.py
index 9af11e4f4285..c46dc32a85f2 100644
--- a/test/unittests/enclosure/test_gui.py
+++ b/test/unittests/enclosure/test_gui.py
@@ -16,7 +16,7 @@
from unittest import TestCase, mock
-from mycroft.enclosure.gui import SkillGUI
+from mycroft.enclosure.gui import SkillGUI, GUIPlaybackStatus
from mycroft.messagebus import Message
from mycroft.util.file_utils import resolve_resource_file
@@ -106,6 +106,29 @@ def test_show_html(self):
self.assertEqual(sent_message.data['page'], [page_url])
self.assertEqual(self.gui['html'], html)
+ def test_video_playback(self):
+ self.assertEqual(self.gui.playback_status, GUIPlaybackStatus.UNDEFINED)
+ self.assertEqual(self.gui.is_video_displayed, False)
+ video_url = "https://downloads.mycroft.ai/assets/tests/test.mp4"
+ video_title = "Logo animation"
+ self.gui.play_video(video_url, video_title)
+ sent_message = self.mock_skill.bus.emit.call_args_list[-1][0][0]
+ page_path = resolve_resource_file('ui/SYSTEM_VideoPlayer.qml')
+ page_url = 'file://{}'.format(page_path)
+ self.assertEqual(sent_message.data['page'], [page_url])
+ self.assertEqual(self.gui['video'], video_url)
+ self.assertEqual(self.gui['title'], video_title)
+ self.assertEqual(self.gui.get('repeat'), None)
+ self.assertEqual(self.gui.is_video_displayed, True)
+ self.assertEqual(self.gui.playback_status, GUIPlaybackStatus.PLAYING)
+ self.gui.pause_video()
+ self.assertEqual(self.gui.playback_status, GUIPlaybackStatus.PAUSED)
+ self.gui.resume_video()
+ self.assertEqual(self.gui.playback_status, GUIPlaybackStatus.PLAYING)
+ self.gui.stop_video()
+ self.assertEqual(self.gui.playback_status, GUIPlaybackStatus.STOPPED)
+ self.assertEqual(self.gui.is_video_displayed, False)
+
def test_send_event(self):
"""Check that send_event sends message using the correct format."""
params = 'Not again'