openlp/openlp/core/ui/media/webkitplayer.py

396 lines
14 KiB
Python
Raw Normal View History

2011-04-18 19:50:12 +00:00
# -*- coding: utf-8 -*-
2012-12-11 19:55:47 +00:00
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
2011-04-18 19:50:12 +00:00
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
2016-12-31 11:05:48 +00:00
# Copyright (c) 2008-2017 OpenLP Developers #
2011-04-18 19:50:12 +00:00
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`~openlp.core.ui.media.webkit` module contains our WebKit video player
"""
2015-11-07 00:49:40 +00:00
from PyQt5 import QtGui, QtWebKitWidgets
2012-09-29 18:36:30 +00:00
2011-05-21 08:18:52 +00:00
import logging
from openlp.core.common import Settings
2013-10-13 20:36:42 +00:00
from openlp.core.lib import translate
2011-11-11 16:45:25 +00:00
from openlp.core.ui.media import MediaState
2012-09-09 06:06:44 +00:00
from openlp.core.ui.media.mediaplayer import MediaPlayer
2011-04-18 19:50:12 +00:00
2011-05-21 08:18:52 +00:00
log = logging.getLogger(__name__)
2013-08-31 18:17:38 +00:00
VIDEO_CSS = """
2011-11-02 20:27:53 +00:00
#videobackboard {
2011-08-30 16:54:45 +00:00
z-index:3;
background-color: %(bgcolor)s;
2011-11-02 20:27:53 +00:00
}
#video {
background-color: %(bgcolor)s;
2011-11-02 20:27:53 +00:00
z-index:4;
2011-08-30 16:54:45 +00:00
}
"""
2013-08-31 18:17:38 +00:00
VIDEO_JS = """
function show_video(state, path, volume, loop, variable_value){
// Sometimes video.currentTime stops slightly short of video.duration and video.ended is intermittent!
var video = document.getElementById('video');
2011-08-30 16:54:45 +00:00
if(volume != null){
video.volume = volume;
2011-08-30 16:54:45 +00:00
}
switch(state){
case 'load':
video.src = 'file:///' + path;
if(loop == true) {
video.loop = true;
}
video.load();
2011-08-30 16:54:45 +00:00
break;
case 'play':
video.play();
2011-08-30 16:54:45 +00:00
break;
case 'pause':
video.pause();
2011-08-30 16:54:45 +00:00
break;
case 'stop':
show_video('pause');
video.currentTime = 0;
2011-08-30 16:54:45 +00:00
break;
case 'close':
show_video('stop');
video.src = '';
2011-08-30 16:54:45 +00:00
break;
case 'length':
return video.duration;
2013-03-09 14:46:57 +00:00
case 'current_time':
return video.currentTime;
2011-08-30 16:54:45 +00:00
case 'seek':
video.currentTime = variable_value;
2011-08-30 16:54:45 +00:00
break;
case 'isEnded':
return video.ended;
2011-08-30 16:54:45 +00:00
case 'setVisible':
video.style.visibility = variable_value;
2011-08-30 16:54:45 +00:00
break;
2011-11-02 20:27:53 +00:00
case 'setBackBoard':
var back = document.getElementById('videobackboard');
back.style.visibility = variable_value;
2011-11-02 20:27:53 +00:00
break;
2011-08-30 16:54:45 +00:00
}
}
"""
2013-08-31 18:17:38 +00:00
VIDEO_HTML = """
2011-11-02 20:27:53 +00:00
<div id="videobackboard" class="size" style="visibility:hidden"></div>
<video id="video" class="size" style="visibility:hidden" autobuffer preload></video>
2011-08-30 16:54:45 +00:00
"""
2013-08-31 18:17:38 +00:00
FLASH_CSS = """
2011-08-30 16:54:45 +00:00
#flash {
2011-11-02 20:27:53 +00:00
z-index:5;
2011-08-30 16:54:45 +00:00
}
"""
2013-08-31 18:17:38 +00:00
FLASH_JS = """
2011-08-30 16:54:45 +00:00
function getFlashMovieObject(movieName)
{
if (window.document[movieName]){
2011-08-30 16:54:45 +00:00
return window.document[movieName];
}
if (document.embeds && document.embeds[movieName]){
2011-08-30 16:54:45 +00:00
return document.embeds[movieName];
}
2011-08-30 16:54:45 +00:00
}
function show_flash(state, path, volume, variable_value){
2011-08-30 16:54:45 +00:00
var text = document.getElementById('flash');
var flashMovie = getFlashMovieObject("OpenLPFlashMovie");
var src = "src = 'file:///" + path + "'";
var view_parm = " wmode='opaque'" + " width='100%%'" + " height='100%%'";
var swf_parm = " name='OpenLPFlashMovie'" + " autostart='true' loop='false' play='true'" +
" hidden='false' swliveconnect='true' allowscriptaccess='always'" + " volume='" + volume + "'";
2011-08-30 16:54:45 +00:00
switch(state){
case 'load':
text.innerHTML = "<embed " + src + view_parm + swf_parm + "/>";
flashMovie = getFlashMovieObject("OpenLPFlashMovie");
flashMovie.Play();
break;
case 'play':
flashMovie.Play();
break;
case 'pause':
flashMovie.StopPlay();
break;
case 'stop':
flashMovie.StopPlay();
tempHtml = text.innerHTML;
text.innerHTML = '';
text.innerHTML = tempHtml;
break;
case 'close':
flashMovie.StopPlay();
text.innerHTML = '';
break;
case 'length':
return flashMovie.TotalFrames();
2013-03-09 14:46:57 +00:00
case 'current_time':
2011-08-30 16:54:45 +00:00
return flashMovie.CurrentFrame();
case 'seek':
// flashMovie.GotoFrame(variable_value);
2011-08-30 16:54:45 +00:00
break;
case 'isEnded':
//TODO check flash end
return false;
2011-08-30 16:54:45 +00:00
case 'setVisible':
text.style.visibility = variable_value;
2011-08-30 16:54:45 +00:00
break;
}
}
"""
2013-08-31 18:17:38 +00:00
FLASH_HTML = """
2011-08-30 16:54:45 +00:00
<div id="flash" class="size" style="visibility:hidden"></div>
"""
2014-04-12 20:19:22 +00:00
VIDEO_EXT = ['*.3gp', '*.3gpp', '*.3g2', '*.3gpp2', '*.aac', '*.flv', '*.f4a', '*.f4b', '*.f4p', '*.f4v', '*.mov',
'*.m4a', '*.m4b', '*.m4p', '*.m4v', '*.mkv', '*.mp4', '*.ogv', '*.webm', '*.mpg', '*.wmv', '*.mpeg',
'*.avi', '*.swf']
AUDIO_EXT = ['*.mp3', '*.ogg']
2011-11-02 20:27:53 +00:00
2011-11-11 16:45:25 +00:00
class WebkitPlayer(MediaPlayer):
2011-11-02 20:27:53 +00:00
"""
A specialised version of the MediaPlayer class, which provides a QtWebKit
2011-11-02 20:27:53 +00:00
display.
"""
def __init__(self, parent):
"""
Constructor
"""
2013-08-31 18:17:38 +00:00
super(WebkitPlayer, self).__init__(parent, 'webkit')
self.original_name = 'WebKit'
self.display_name = '&WebKit'
2011-11-02 20:27:53 +00:00
self.parent = parent
2013-03-06 22:54:16 +00:00
self.can_background = True
2011-11-02 20:27:53 +00:00
self.audio_extensions_list = AUDIO_EXT
self.video_extensions_list = VIDEO_EXT
2011-06-05 21:10:49 +00:00
2011-08-29 19:55:58 +00:00
def get_media_display_css(self):
2011-06-08 13:18:05 +00:00
"""
Add css style sheets to htmlbuilder
"""
2013-08-31 18:17:38 +00:00
background = QtGui.QColor(Settings().value('players/background color')).name()
css = VIDEO_CSS % {'bgcolor': background}
2012-09-29 18:36:30 +00:00
return css + FLASH_CSS
2011-06-08 13:18:05 +00:00
2011-08-29 19:55:58 +00:00
def get_media_display_javascript(self):
2011-06-08 13:18:05 +00:00
"""
Add javascript functions to htmlbuilder
"""
2011-10-24 20:04:29 +00:00
return VIDEO_JS + FLASH_JS
2011-06-08 13:18:05 +00:00
2011-08-29 19:55:58 +00:00
def get_media_display_html(self):
2011-06-08 13:18:05 +00:00
"""
Add html code to htmlbuilder
"""
2011-10-24 20:04:29 +00:00
return VIDEO_HTML + FLASH_HTML
2011-06-08 13:18:05 +00:00
2011-06-05 21:10:49 +00:00
def setup(self, display):
"""
Set up the player
"""
2013-02-20 19:31:51 +00:00
display.web_view.resize(display.size())
display.web_view.raise_()
2013-03-06 22:54:16 +00:00
self.has_own_widget = False
2011-05-17 19:50:00 +00:00
2011-06-08 13:18:05 +00:00
def check_available(self):
"""
2015-01-26 20:42:19 +00:00
Check the availability of the media player.
:return: boolean. True if available
"""
2015-11-07 00:49:40 +00:00
web = QtWebKitWidgets.QWebPage()
2015-01-26 20:42:19 +00:00
# This script should return '[object HTMLVideoElement]' if the html5 video is available in webkit. Otherwise it
# should return '[object HTMLUnknownElement]'
return web.mainFrame().evaluateJavaScript(
"Object.prototype.toString.call(document.createElement('video'));") == '[object HTMLVideoElement]'
2011-05-17 19:50:00 +00:00
2011-06-05 21:10:49 +00:00
def load(self, display):
"""
Load a video
"""
2013-08-31 18:17:38 +00:00
log.debug('load vid in Webkit Controller')
2011-06-06 19:12:14 +00:00
controller = display.controller
2013-03-06 22:54:16 +00:00
if display.has_audio and not controller.media_info.is_background:
2011-08-23 20:19:45 +00:00
volume = controller.media_info.volume
vol = float(volume) / float(100)
else:
vol = 0
2011-06-05 21:10:49 +00:00
path = controller.media_info.file_info.absoluteFilePath()
if controller.media_info.is_background:
2013-08-31 18:17:38 +00:00
loop = 'true'
2011-05-24 20:03:57 +00:00
else:
2013-08-31 18:17:38 +00:00
loop = 'false'
2013-02-20 19:31:51 +00:00
display.web_view.setVisible(True)
2013-08-31 18:17:38 +00:00
if controller.media_info.file_info.suffix() == 'swf':
2011-08-29 19:55:58 +00:00
controller.media_info.is_flash = True
2013-08-31 18:17:38 +00:00
js = 'show_flash("load","%s");' % (path.replace('\\', '\\\\'))
2011-04-18 19:50:12 +00:00
else:
2013-08-31 18:17:38 +00:00
js = 'show_video("load", "%s", %s, %s);' % (path.replace('\\', '\\\\'), str(vol), loop)
2011-04-18 19:50:12 +00:00
display.frame.evaluateJavaScript(js)
2011-05-21 08:18:52 +00:00
return True
2011-05-10 20:39:17 +00:00
2011-06-05 21:10:49 +00:00
def resize(self, display):
"""
Resize the player
"""
2013-02-20 19:31:51 +00:00
display.web_view.resize(display.size())
2011-04-18 19:50:12 +00:00
def play(self, display):
"""
Play a video
"""
2011-06-06 19:12:14 +00:00
controller = display.controller
2013-02-20 19:31:51 +00:00
display.web_loaded = True
2011-07-25 20:56:39 +00:00
length = 0
2011-12-01 18:07:15 +00:00
start_time = 0
2012-12-11 19:55:47 +00:00
if self.state != MediaState.Paused and controller.media_info.start_time > 0:
2011-12-01 18:07:15 +00:00
start_time = controller.media_info.start_time
2011-05-17 19:50:00 +00:00
self.set_visible(display, True)
2011-08-29 19:55:58 +00:00
if controller.media_info.is_flash:
2013-08-31 18:17:38 +00:00
display.frame.evaluateJavaScript('show_flash("play");')
2011-04-18 19:50:12 +00:00
else:
2013-08-31 18:17:38 +00:00
display.frame.evaluateJavaScript('show_video("play");')
2011-12-01 18:07:15 +00:00
if start_time > 0:
2012-05-19 15:30:09 +00:00
self.seek(display, controller.media_info.start_time * 1000)
2011-08-29 19:55:58 +00:00
# TODO add playing check and get the correct media length
2011-07-25 20:56:39 +00:00
controller.media_info.length = length
2011-05-10 20:39:17 +00:00
self.state = MediaState.Playing
2013-02-20 19:31:51 +00:00
display.web_view.raise_()
2011-08-29 19:55:58 +00:00
return True
2011-04-18 19:50:12 +00:00
def pause(self, display):
"""
Pause a video
"""
2011-06-06 19:12:14 +00:00
controller = display.controller
2011-08-29 19:55:58 +00:00
if controller.media_info.is_flash:
2013-08-31 18:17:38 +00:00
display.frame.evaluateJavaScript('show_flash("pause");')
2011-04-18 19:50:12 +00:00
else:
2013-08-31 18:17:38 +00:00
display.frame.evaluateJavaScript('show_video("pause");')
2011-05-10 20:39:17 +00:00
self.state = MediaState.Paused
2011-04-18 19:50:12 +00:00
def stop(self, display):
"""
Stop a video
"""
2011-06-06 19:12:14 +00:00
controller = display.controller
2011-08-29 19:55:58 +00:00
if controller.media_info.is_flash:
2013-08-31 18:17:38 +00:00
display.frame.evaluateJavaScript('show_flash("stop");')
2011-04-18 19:50:12 +00:00
else:
2013-08-31 18:17:38 +00:00
display.frame.evaluateJavaScript('show_video("stop");')
2011-05-10 20:39:17 +00:00
self.state = MediaState.Stopped
2013-03-23 07:07:06 +00:00
def volume(self, display, volume):
"""
Set the volume
"""
2011-06-06 19:12:14 +00:00
controller = display.controller
2011-06-05 21:10:49 +00:00
# 1.0 is the highest value
2013-03-06 22:54:16 +00:00
if display.has_audio:
2013-03-23 07:07:06 +00:00
vol = float(volume) / float(100)
2011-08-29 19:55:58 +00:00
if not controller.media_info.is_flash:
2013-08-31 18:17:38 +00:00
display.frame.evaluateJavaScript('show_video(null, null, %s);' % str(vol))
2011-04-18 19:50:12 +00:00
2013-03-23 07:07:06 +00:00
def seek(self, display, seek_value):
"""
Go to a position in the video
"""
2011-06-06 19:12:14 +00:00
controller = display.controller
2011-08-29 19:55:58 +00:00
if controller.media_info.is_flash:
2013-03-23 07:07:06 +00:00
seek = seek_value
2016-01-09 16:26:14 +00:00
display.frame.evaluateJavaScript('show_flash("seek", null, null, "%s");' % seek)
2011-06-05 21:10:49 +00:00
else:
2013-03-23 07:07:06 +00:00
seek = float(seek_value) / 1000
2016-01-09 16:26:14 +00:00
display.frame.evaluateJavaScript('show_video("seek", null, null, null, "%f");' % seek)
2011-04-18 19:50:12 +00:00
def reset(self, display):
"""
Reset the player
"""
2011-06-06 19:12:14 +00:00
controller = display.controller
2011-08-29 19:55:58 +00:00
if controller.media_info.is_flash:
2013-08-31 18:17:38 +00:00
display.frame.evaluateJavaScript('show_flash("close");')
2011-05-10 20:39:17 +00:00
else:
2013-08-31 18:17:38 +00:00
display.frame.evaluateJavaScript('show_video("close");')
2011-05-17 19:50:00 +00:00
self.state = MediaState.Off
def set_visible(self, display, status):
"""
Set the visibility
"""
2011-08-29 19:55:58 +00:00
controller = display.controller
if status:
is_visible = "visible"
else:
is_visible = "hidden"
if controller.media_info.is_flash:
2013-12-31 20:29:03 +00:00
display.frame.evaluateJavaScript('show_flash("setVisible", null, null, "%s");' % is_visible)
2011-08-29 19:55:58 +00:00
else:
2013-12-31 20:29:03 +00:00
display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % is_visible)
2011-04-19 12:03:36 +00:00
2011-06-05 21:10:49 +00:00
def update_ui(self, display):
"""
Update the UI
"""
2011-06-06 19:12:14 +00:00
controller = display.controller
2011-08-29 19:55:58 +00:00
if controller.media_info.is_flash:
2013-08-31 18:17:38 +00:00
current_time = display.frame.evaluateJavaScript('show_flash("current_time");')
length = display.frame.evaluateJavaScript('show_flash("length");')
2011-06-05 21:10:49 +00:00
else:
2013-08-31 18:17:38 +00:00
if display.frame.evaluateJavaScript('show_video("isEnded");'):
self.stop(display)
2013-08-31 18:17:38 +00:00
current_time = display.frame.evaluateJavaScript('show_video("current_time");')
2011-06-06 19:12:14 +00:00
# check if conversion was ok and value is not 'NaN'
2013-03-09 14:46:57 +00:00
if current_time and current_time != float('inf'):
current_time = int(current_time * 1000)
2013-08-31 18:17:38 +00:00
length = display.frame.evaluateJavaScript('show_video("length");')
2011-06-06 19:12:14 +00:00
# check if conversion was ok and value is not 'NaN'
2013-01-21 06:46:38 +00:00
if length and length != float('inf'):
2012-05-19 15:25:39 +00:00
length = int(length * 1000)
if current_time and length:
2011-07-25 20:56:39 +00:00
controller.media_info.length = length
2013-03-23 07:28:24 +00:00
controller.seek_slider.setMaximum(length)
if not controller.seek_slider.isSliderDown():
controller.seek_slider.blockSignals(True)
2013-05-18 14:11:43 +00:00
controller.seek_slider.setSliderPosition(current_time)
2013-03-23 07:28:24 +00:00
controller.seek_slider.blockSignals(False)
2012-10-15 17:35:14 +00:00
def get_info(self):
"""
Return some information about this player
"""
2014-04-12 20:19:22 +00:00
part1 = translate('Media.player', 'Webkit is a media player which runs inside a web browser. This player '
'allows text over video to be rendered.')
part2 = translate('Media.player', 'Audio')
part3 = translate('Media.player', 'Video')
return part1 + '<br/> <strong>' + part2 + '</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' + part3 + \
'</strong><br/>' + str(VIDEO_EXT) + '<br/>'