new start

This commit is contained in:
Tim Bentley 2016-02-13 13:13:00 +00:00
commit ea40173f9b
14 changed files with 319 additions and 38 deletions

View File

@ -542,6 +542,7 @@ class ServiceItem(RegistryProperties):
:param length: The length of the media item
"""
print("set_media_length " + str(length) + " " + self.processor)
self.media_length = length
if length > 0:
self.add_capability(ItemCapabilities.HasVariableStartTime)
@ -610,7 +611,8 @@ class ServiceItem(RegistryProperties):
str(datetime.timedelta(seconds=self.start_time))
if self.media_length != 0:
end = translate('OpenLP.ServiceItem', '<strong>Length</strong>: %s') % \
str(datetime.timedelta(seconds=self.media_length))
str(datetime.timedelta(seconds=self.media_length // 1000))
print("get_media_time " + str(self.media_length))
if not start and not end:
return ''
elif start and not end:

View File

@ -66,6 +66,8 @@ class MediaInfo(object):
start_time = 0
end_time = 0
title_track = 0
playing = False
timer = 1000
audio_track = 0
subtitle_track = 0
media_type = MediaType()

View File

@ -33,12 +33,15 @@ from openlp.core.lib import OpenLPToolbar, ItemCapabilities
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players,\
parse_optical_path
from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
from openlp.core.ui.media.mediaplayer import MediaPlayer
from openlp.core.common import AppLocation
from openlp.core.ui import DisplayControllerType
log = logging.getLogger(__name__)
TICK_TIME = 200
class MediaSlider(QtWidgets.QSlider):
"""
@ -97,7 +100,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
self.current_media_players = {}
# Timer for video state
self.timer = QtCore.QTimer()
self.timer.setInterval(200)
self.timer.setInterval(TICK_TIME)
# Signals
self.timer.timeout.connect(self.media_state)
Registry().register_function('playbackPlay', self.media_play_msg)
@ -202,6 +205,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
self.current_media_players[source].update_ui(display)
if self.current_media_players[source].state == MediaState.Playing:
any_active = True
self.tick(self.display_controllers[source])
# There are still any active players - no need to stop timer.
if any_active:
return
@ -274,6 +278,11 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
icon=':/slides/media_playback_stop.png',
tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
triggers=controller.send_to_plugins)
controller.position_label = QtWidgets.QLabel()
controller.position_label.setText(' 00:00 / 00:00')
controller.position_label.setToolTip(translate('OpenLP.SlideController', 'Video timer.'))
controller.position_label.setObjectName('position_label')
controller.mediabar.add_toolbar_widget(controller.position_label)
# Build the seek_slider.
controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
controller.seek_slider.setMaximum(1000)
@ -353,9 +362,10 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param hidden: The player which is doing the playing
:param video_behind_text: Is the video to be played behind text.
"""
log.debug('video')
print("### video", source)
is_valid = False
controller = self.display_controllers[source]
print(controller)
# stop running videos
self.media_reset(controller)
controller.media_info = MediaInfo()
@ -373,6 +383,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller)
else:
log.debug('video is not optical and live')
controller.media_info.length = service_item.media_length
is_valid = self._check_file_type(controller, display, service_item)
display.override['theme'] = ''
display.override['video'] = True
@ -392,6 +403,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller)
else:
log.debug('video is not optical and preview')
controller.media_info.length = service_item.media_length
is_valid = self._check_file_type(controller, display, service_item)
if not is_valid:
# Media could not be loaded correctly
@ -428,8 +440,9 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param service_item: The ServiceItem containing the details to be played.
"""
print('### media_length')
controller = self.display_controllers[DisplayControllerType.Plugin]
log.debug('media_length')
print(controller)
# stop running videos
self.media_reset(controller)
controller.media_info = MediaInfo()
@ -441,12 +454,10 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
translate('MediaPlugin.MediaItem', 'Unsupported File'))
return False
if not self.media_play(controller):
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
translate('MediaPlugin.MediaItem', 'Unsupported File'))
return False
service_item.set_media_length(controller.media_info.length)
self.media_stop(controller)
media_data = MediaInfoWrapper.parse(service_item.get_frame_path())
print(media_data.to_data())
# duration returns in milli seconds
service_item.set_media_length(media_data.tracks[0].duration)
log.debug('use %s controller' % self.current_media_players[controller.controller_type])
return True
@ -464,7 +475,6 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param controller: The media contraoller.
:return: True if setup succeded else False.
"""
log.debug('media_setup_optical')
if controller is None:
controller = self.display_controllers[DisplayControllerType.Plugin]
# stop running videos
@ -573,7 +583,6 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param msg: First element is the controller which should be used
:param status:
"""
log.debug('media_play_msg')
self.media_play(msg[0], status)
def media_play(self, controller, status=True):
@ -583,7 +592,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param controller: The controller to be played
:param status:
"""
log.debug('media_play')
print('### media_play')
controller.seek_slider.blockSignals(True)
controller.volume_slider.blockSignals(True)
display = self._define_display(controller)
@ -615,6 +624,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
self.timer.start()
controller.seek_slider.blockSignals(False)
controller.volume_slider.blockSignals(False)
controller.media_info.playing = True
return True
def media_pause_msg(self, msg):
@ -623,21 +633,43 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param msg: First element is the controller which should be used
"""
log.debug('media_pause_msg')
self.media_pause(msg[0])
def tick(self, controller):
"""
Add a tick while the media is playing but only count if not paused
:param controller: The Controller to be processed
"""
if controller.media_info.playing and controller.media_info.length > 0:
print("tick", controller.media_info.timer, controller.media_info.length)
if controller.media_info.timer > controller.media_info.length:
controller.media_info.timer = controller.media_info.length
controller.media_info.timer = controller.media_info.length
self.media_stop(controller)
controller.media_info.timer += TICK_TIME
seconds = controller.media_info.timer // 1000
minutes = seconds // 60
seconds %= 60
total_seconds = controller.media_info.length
total_minutes = total_seconds // 60
total_seconds %= 60
controller.position_label.setText(' %02d:%02d / %02d:%02d' %
(minutes, seconds, total_minutes, total_seconds))
def media_pause(self, controller):
"""
Responds to the request to pause a loaded video
:param controller: The Controller to be paused
"""
log.debug('media_pause')
print('### media_pause')
display = self._define_display(controller)
self.current_media_players[controller.controller_type].pause(display)
controller.mediabar.actions['playbackPlay'].setVisible(True)
controller.mediabar.actions['playbackStop'].setDisabled(False)
controller.mediabar.actions['playbackPause'].setVisible(False)
controller.media_info.playing = False
def media_stop_msg(self, msg):
"""
@ -645,7 +677,6 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param msg: First element is the controller which should be used
"""
log.debug('media_stop_msg')
self.media_stop(msg[0])
def media_stop(self, controller):
@ -654,7 +685,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param controller: The controller that needs to be stopped
"""
log.debug('media_stop')
print('### media_stop')
display = self._define_display(controller)
if controller.controller_type in self.current_media_players:
display.frame.evaluateJavaScript('show_blank("black");')
@ -664,6 +695,9 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller.mediabar.actions['playbackPlay'].setVisible(True)
controller.mediabar.actions['playbackStop'].setDisabled(True)
controller.mediabar.actions['playbackPause'].setVisible(False)
controller.media_info.playing = False
controller.media_info.timer = 1000
controller.media_timer = 0
def media_volume_msg(self, msg):
"""
@ -694,7 +728,6 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param msg: First element is the controller which should be used
Second element is a list with the seek value as first element
"""
log.debug('media_seek')
controller = msg[0]
seek_value = msg[1][0]
self.media_seek(controller, seek_value)
@ -706,15 +739,15 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
:param controller: The controller to use.
:param seek_value: The value to set.
"""
log.debug('media_seek')
display = self._define_display(controller)
self.current_media_players[controller.controller_type].seek(display, seek_value)
controller.media_info.timer = seek_value
def media_reset(self, controller):
"""
Responds to the request to reset a loaded video
:param controller: The controller to use.
"""
log.debug('media_reset')
self.set_controls_visible(controller, False)
display = self._define_display(controller)
if controller.controller_type in self.current_media_players:

View File

@ -4,14 +4,7 @@
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# 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 #
@ -216,7 +209,8 @@ class SystemPlayer(MediaPlayer):
@staticmethod
def set_duration(controller, duration):
controller.media_info.length = int(duration / 1000)
print("system set duration", controller.media_info.length, duration)
#controller.media_info.length = int(duration / 1000)
controller.seek_slider.setMaximum(controller.media_info.length * 1000)
def update_ui(self, display):

View File

@ -0,0 +1,140 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# 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.mediainfo` module contains code to run mediainfo on a media file and obtain
information related to the rwquested media.
"""
import json
import os
from subprocess import Popen
from tempfile import mkstemp
import six
from bs4 import BeautifulSoup, NavigableString
ENV_DICT = os.environ
class Track(object):
def __getattribute__(self, name):
try:
return object.__getattribute__(self, name)
except:
pass
return None
def __init__(self, xml_dom_fragment):
self.xml_dom_fragment = xml_dom_fragment
self.track_type = xml_dom_fragment.attrs['type']
for el in self.xml_dom_fragment.children:
if not isinstance(el, NavigableString):
node_name = el.name.lower().strip().strip('_')
if node_name == 'id':
node_name = 'track_id'
node_value = el.string
other_node_name = "other_%s" % node_name
if getattr(self, node_name) is None:
setattr(self, node_name, node_value)
else:
if getattr(self, other_node_name) is None:
setattr(self, other_node_name, [node_value, ])
else:
getattr(self, other_node_name).append(node_value)
for o in [d for d in self.__dict__.keys() if d.startswith('other_')]:
try:
primary = o.replace('other_', '')
setattr(self, primary, int(getattr(self, primary)))
except:
for v in getattr(self, o):
try:
current = getattr(self, primary)
setattr(self, primary, int(v))
getattr(self, o).append(current)
break
except:
pass
def __repr__(self):
return "<Track track_id='{0}', track_type='{1}'>".format(self.track_id, self.track_type)
def to_data(self):
data = {}
for k, v in six.iteritems(self.__dict__):
if k != 'xml_dom_fragment':
data[k] = v
return data
class MediaInfoWrapper(object):
def __init__(self, xml):
self.xml_dom = xml
xml_types = (str,) # no unicode type in python3
if isinstance(xml, xml_types):
self.xml_dom = MediaInfoWrapper.parse_xml_data_into_dom(xml)
@staticmethod
def parse_xml_data_into_dom(xml_data):
return BeautifulSoup(xml_data, "xml")
@staticmethod
def parse(filename, environment=ENV_DICT):
command = ["mediainfo", "-f", "--Output=XML", filename]
fileno_out, fname_out = mkstemp(suffix=".xml", prefix="media-")
fileno_err, fname_err = mkstemp(suffix=".err", prefix="media-")
fp_out = os.fdopen(fileno_out, 'r+b')
fp_err = os.fdopen(fileno_err, 'r+b')
p = Popen(command, stdout=fp_out, stderr=fp_err, env=environment)
p.wait()
fp_out.seek(0)
xml_dom = MediaInfoWrapper.parse_xml_data_into_dom(fp_out.read())
fp_out.close()
fp_err.close()
os.unlink(fname_out)
os.unlink(fname_err)
return MediaInfoWrapper(xml_dom)
def _populate_tracks(self):
if self.xml_dom is None:
return
for xml_track in self.xml_dom.Mediainfo.File.find_all("track"):
self._tracks.append(Track(xml_track))
@property
def tracks(self):
if not hasattr(self, "_tracks"):
self._tracks = []
if len(self._tracks) == 0:
self._populate_tracks()
return self._tracks
def to_data(self):
data = {'tracks': []}
for track in self.tracks:
data['tracks'].append(track.to_data())
return data
def to_json(self):
return json.dumps(self.to_data())

View File

@ -144,6 +144,9 @@ class VlcPlayer(MediaPlayer):
def setup(self, display):
"""
Set up the media player
:param display: The display where the media is
:return:
"""
vlc = get_vlc()
display.vlc_widget = QtWidgets.QFrame(display)
@ -186,6 +189,9 @@ class VlcPlayer(MediaPlayer):
def load(self, display):
"""
Load a video into VLC
:param display: The display where the media is
:return:
"""
vlc = get_vlc()
log.debug('load vid in Vlc Controller')
@ -219,13 +225,17 @@ class VlcPlayer(MediaPlayer):
# and once to just get media length.
#
# Media plugin depends on knowing media length before playback.
controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
#controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
return True
def media_state_wait(self, display, media_state):
"""
Wait for the video to change its state
Wait no longer than 60 seconds. (loading an iso file needs a long time)
:param media_state: The state of the playing media
:param display: The display where the media is
:return:
"""
vlc = get_vlc()
start = datetime.now()
@ -240,12 +250,18 @@ class VlcPlayer(MediaPlayer):
def resize(self, display):
"""
Resize the player
:param display: The display where the media is
:return:
"""
display.vlc_widget.resize(display.size())
def play(self, display):
"""
Play the current item
:param display: The display where the media is
:return:
"""
vlc = get_vlc()
controller = display.controller
@ -280,11 +296,13 @@ class VlcPlayer(MediaPlayer):
start_time = controller.media_info.start_time
controller.media_info.length = controller.media_info.end_time - controller.media_info.start_time
else:
controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
print("vlc len", controller.media_info.length)
#controller.media_info.length = int(display.vlc_media_player.get_media().get_duration())
self.volume(display, controller.media_info.volume)
if start_time > 0 and display.vlc_media_player.is_seekable():
display.vlc_media_player.set_time(int(start_time * 1000))
controller.seek_slider.setMaximum(controller.media_info.length * 1000)
display.vlc_media_player.set_time(int(start_time))
controller.seek_slider.setMaximum(controller.media_info.length)
print("VLC play " + str(controller.media_info.length))
self.state = MediaState.Playing
display.vlc_widget.raise_()
return True
@ -292,6 +310,9 @@ class VlcPlayer(MediaPlayer):
def pause(self, display):
"""
Pause the current item
:param display: The display where the media is
:return:
"""
vlc = get_vlc()
if display.vlc_media.get_state() != vlc.State.Playing:
@ -303,6 +324,9 @@ class VlcPlayer(MediaPlayer):
def stop(self, display):
"""
Stop the current item
:param display: The display where the media is
:return:
"""
threading.Thread(target=display.vlc_media_player.stop).start()
self.state = MediaState.Stopped
@ -310,6 +334,10 @@ class VlcPlayer(MediaPlayer):
def volume(self, display, vol):
"""
Set the volume
:param vol: The volume to be sets
:param display: The display where the media is
:return:
"""
if display.has_audio:
display.vlc_media_player.audio_set_volume(vol)
@ -317,6 +345,9 @@ class VlcPlayer(MediaPlayer):
def seek(self, display, seek_value):
"""
Go to a particular position
:param seek_value: The position of where a seek goes to
:param display: The display where the media is
"""
if display.controller.media_info.media_type == MediaType.CD \
or display.controller.media_info.media_type == MediaType.DVD:
@ -327,6 +358,8 @@ class VlcPlayer(MediaPlayer):
def reset(self, display):
"""
Reset the player
:param display: The display where the media is
"""
display.vlc_media_player.stop()
display.vlc_widget.setVisible(False)
@ -335,6 +368,9 @@ class VlcPlayer(MediaPlayer):
def set_visible(self, display, status):
"""
Set the visibility
:param display: The display where the media is
:param status: The visibility status
"""
if self.has_own_widget:
display.vlc_widget.setVisible(status)
@ -342,6 +378,8 @@ class VlcPlayer(MediaPlayer):
def update_ui(self, display):
"""
Update the UI
:param display: The display where the media is
"""
vlc = get_vlc()
# Stop video if playback is finished.

View File

@ -281,7 +281,7 @@ class WebkitPlayer(MediaPlayer):
if start_time > 0:
self.seek(display, controller.media_info.start_time * 1000)
# TODO add playing check and get the correct media length
controller.media_info.length = length
print("Webkit play " + str(length))
self.state = MediaState.Playing
display.web_view.raise_()
return True
@ -376,6 +376,7 @@ class WebkitPlayer(MediaPlayer):
if length and length != float('inf'):
length = int(length * 1000)
if current_time and length:
print("webkit update_ui", controller.media_info.length)
controller.media_info.length = length
controller.seek_slider.setMaximum(length)
if not controller.seek_slider.isSliderDown():

View File

@ -272,11 +272,10 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
service_item.title = name
service_item.processor = self.display_type_combo_box.currentText()
service_item.add_from_command(path, name, CLAPPERBOARD)
print("building service item")
# Only get start and end times if going to a service
if context == ServiceItemContext.Service:
# Start media and obtain the length
if not self.media_controller.media_length(service_item):
return False
if not self.media_controller.media_length(service_item):
return False
service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
service_item.add_capability(ItemCapabilities.CanEditTitle)
service_item.add_capability(ItemCapabilities.RequiresMedia)

View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################

View File

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 OpenLP Developers #
# --------------------------------------------------------------------------- #
# 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 #
###############################################################################
"""
Package to test the openlp.core.ui.media package.
"""
import os
from unittest import TestCase
from openlp.core.ui.media.vendor.mediainfoWrapper import MediaInfoWrapper
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources', 'media'))
TEST_MEDIA = [['avi_file.avi', 61495], ['mp3_file.mp3', 134426], ['mpg_file.mpg', 9404], ['mp4_file.mp4', 188336]]
class TestMediainfoWrapper(TestCase):
def media_length_test(self):
"""
Test the Media Info basic functionality
"""
for test_data in TEST_MEDIA:
# GIVEN: a media file
full_path = os.path.normpath(os.path.join(TEST_PATH, test_data[0]))
# WHEN the media data is retrieved
results = MediaInfoWrapper.parse(full_path)
# THEN you can determine the run time
self.assertEqual(results.tracks[0].duration, test_data[1], 'The correct duration is returned for ' +
test_data[0])

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.