forked from openlp/openlp
Merge trunk updates
This commit is contained in:
commit
703eb0079b
@ -111,6 +111,8 @@ class OpenLPJSONDecoder(JSONDecoder):
|
||||
:param dict obj: A decoded JSON object
|
||||
:return: The custom object from the serialized data if the custom object is registered, else obj
|
||||
"""
|
||||
if '__Path__' in obj:
|
||||
return PathSerializer.encode_json(obj, **self.kwargs)
|
||||
try:
|
||||
key = obj['json_meta']['class']
|
||||
except KeyError:
|
||||
@ -150,8 +152,8 @@ class OpenLPJSONEncoder(JSONEncoder):
|
||||
if isinstance(obj, JSONMixin):
|
||||
return obj.json_object()
|
||||
elif obj.__class__.__name__ in _registered_classes:
|
||||
return _registered_classes[obj.__class__.__name__].json_object(obj)
|
||||
return super().default(obj)
|
||||
return _registered_classes[obj.__class__.__name__].json_object(obj, **self.kwargs)
|
||||
return super().default(obj, **self.kwargs)
|
||||
|
||||
|
||||
def is_serializable(obj):
|
||||
@ -174,17 +176,22 @@ class PathSerializer(JSONMixin, register_names=('Path', 'PosixPath', 'WindowsPat
|
||||
:param kwargs: Contains any extra parameters. Not used!
|
||||
:return Path: The deserialized Path object
|
||||
"""
|
||||
path = Path(*obj['parts'])
|
||||
if '__Path__' in obj:
|
||||
parts = obj['__Path__']
|
||||
else:
|
||||
parts = obj['parts']
|
||||
path = Path(*parts)
|
||||
if base_path and not path.is_absolute():
|
||||
return base_path / path
|
||||
return path
|
||||
|
||||
@classmethod
|
||||
def json_object(cls, obj, base_path=None, **kwargs):
|
||||
def json_object(cls, obj, base_path=None, is_js=False, **kwargs):
|
||||
"""
|
||||
Create a dictionary that can be JSON decoded.
|
||||
|
||||
:param Path base_path: If specified, an absolute path to make a relative path from.
|
||||
:param bool is_js: Encode the path as a uri. For example for use in the js rendering code.
|
||||
:param kwargs: Contains any extra parameters. Not used!
|
||||
:return: The dictionary representation of this Path object.
|
||||
:rtype: dict[tuple]
|
||||
@ -193,6 +200,8 @@ class PathSerializer(JSONMixin, register_names=('Path', 'PosixPath', 'WindowsPat
|
||||
if base_path:
|
||||
with suppress(ValueError):
|
||||
path = path.relative_to(base_path)
|
||||
if is_js is True:
|
||||
return path.as_uri()
|
||||
json_dict = {'parts': path.parts}
|
||||
cls.attach_meta(json_dict)
|
||||
return json_dict
|
||||
|
@ -210,6 +210,8 @@ class Settings(QtCore.QSettings):
|
||||
'media/media auto start': QtCore.Qt.Unchecked,
|
||||
'media/stream command': '',
|
||||
'media/vlc arguments': '',
|
||||
'media/video': '',
|
||||
'media/audio': '',
|
||||
'remotes/download version': '0.0',
|
||||
'players/background color': '#000000',
|
||||
'servicemanager/last directory': None,
|
||||
@ -610,7 +612,7 @@ class Settings(QtCore.QSettings):
|
||||
elif isinstance(default_value, dict):
|
||||
return {}
|
||||
elif isinstance(setting, str):
|
||||
if 'json_meta' in setting or setting.startswith('{'):
|
||||
if 'json_meta' in setting or '__Path__' in setting or setting.startswith('{'):
|
||||
return json.loads(setting, cls=OpenLPJSONDecoder)
|
||||
# Convert the setting to the correct type.
|
||||
if isinstance(default_value, bool):
|
||||
|
@ -117,20 +117,6 @@ function _prepareText(text) {
|
||||
return "<p>" + _nl2br(text) + "</p>";
|
||||
}
|
||||
|
||||
/**
|
||||
* The paths we get are JSON versions of Python Path objects, so let's just fix that.
|
||||
* @private
|
||||
* @param {object} path - The Path object
|
||||
* @returns {string} The actual file path
|
||||
*/
|
||||
function _pathToString(path) {
|
||||
var filename = path.__Path__.join("/").replace("//", "/");
|
||||
if (!filename.startsWith("/")) {
|
||||
filename = "/" + filename;
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* An audio player with a play list
|
||||
*/
|
||||
@ -676,13 +662,13 @@ var Display = {
|
||||
}
|
||||
break;
|
||||
case BackgroundType.Image:
|
||||
background_filename = _pathToString(theme.background_filename);
|
||||
backgroundStyle["background-image"] = "url('file://" + background_filename + "')";
|
||||
backgroundStyle["background-image"] = "url('" + theme.background_filename + "')";
|
||||
console.warn(backgroundStyle["background-image"]);
|
||||
break;
|
||||
case BackgroundType.Video:
|
||||
background_filename = _pathToString(theme.background_filename);
|
||||
backgroundStyle["background-color"] = theme.background_border_color;
|
||||
backgroundHtml = "<video loop autoplay muted><source src='file://" + background_filename + "'></video>";
|
||||
backgroundHtml = "<video loop autoplay muted><source src='" + theme.background_filename + "'></video>";
|
||||
console.warn(backgroundHtml);
|
||||
break;
|
||||
default:
|
||||
backgroundStyle["background"] = "#000";
|
||||
|
@ -332,9 +332,9 @@ class DisplayWindow(QtWidgets.QWidget):
|
||||
theme_copy = copy.deepcopy(theme)
|
||||
theme_copy.background_type = 'image'
|
||||
theme_copy.background_filename = self.checkerboard_path
|
||||
exported_theme = theme_copy.export_theme()
|
||||
exported_theme = theme_copy.export_theme(is_js=True)
|
||||
else:
|
||||
exported_theme = theme.export_theme()
|
||||
exported_theme = theme.export_theme(is_js=True)
|
||||
self.run_javascript('Display.setTheme({theme});'.format(theme=exported_theme))
|
||||
|
||||
def get_video_types(self):
|
||||
|
@ -173,6 +173,7 @@ class ItemCapabilities(object):
|
||||
HasNotes = 20
|
||||
HasThumbnails = 21
|
||||
HasMetaData = 22
|
||||
CanStream = 23
|
||||
|
||||
|
||||
def get_text_file_string(text_file_path):
|
||||
|
@ -225,17 +225,18 @@ class Theme(object):
|
||||
jsn = json.loads(theme, cls=OpenLPJSONDecoder)
|
||||
self.expand_json(jsn)
|
||||
|
||||
def export_theme(self, theme_path=None):
|
||||
def export_theme(self, theme_path=None, is_js=False):
|
||||
"""
|
||||
Loop through the fields and build a dictionary of them
|
||||
|
||||
:param pathlib.Path | None theme_path:
|
||||
:param bool is_js: For internal use, for example with the theme js code.
|
||||
:return str: The json encoded theme object
|
||||
"""
|
||||
theme_data = {}
|
||||
for attr, value in self.__dict__.items():
|
||||
theme_data["{attr}".format(attr=attr)] = value
|
||||
if theme_path:
|
||||
return json.dumps(theme_data, cls=OpenLPJSONEncoder, base_path=theme_path)
|
||||
return json.dumps(theme_data, cls=OpenLPJSONEncoder)
|
||||
return json.dumps(theme_data, cls=OpenLPJSONEncoder, base_path=theme_path, is_js=is_js)
|
||||
|
||||
def parse(self, xml):
|
||||
"""
|
||||
|
@ -26,6 +26,19 @@ import logging
|
||||
|
||||
log = logging.getLogger(__name__ + '.__init__')
|
||||
|
||||
# Audio and video extensions copied from 'include/vlc_interface.h' from vlc 2.2.0 source
|
||||
AUDIO_EXT = ['*.3ga', '*.669', '*.a52', '*.aac', '*.ac3', '*.adt', '*.adts', '*.aif', '*.aifc', '*.aiff', '*.amr',
|
||||
'*.aob', '*.ape', '*.awb', '*.caf', '*.dts', '*.flac', '*.it', '*.kar', '*.m4a', '*.m4b', '*.m4p', '*.m5p',
|
||||
'*.mid', '*.mka', '*.mlp', '*.mod', '*.mpa', '*.mp1', '*.mp2', '*.mp3', '*.mpc', '*.mpga', '*.mus',
|
||||
'*.oga', '*.ogg', '*.oma', '*.opus', '*.qcp', '*.ra', '*.rmi', '*.s3m', '*.sid', '*.spx', '*.thd', '*.tta',
|
||||
'*.voc', '*.vqf', '*.w64', '*.wav', '*.wma', '*.wv', '*.xa', '*.xm']
|
||||
VIDEO_EXT = ['*.3g2', '*.3gp', '*.3gp2', '*.3gpp', '*.amv', '*.asf', '*.avi', '*.bik', '*.divx', '*.drc', '*.dv',
|
||||
'*.f4v', '*.flv', '*.gvi', '*.gxf', '*.iso', '*.m1v', '*.m2v', '*.m2t', '*.m2ts', '*.m4v', '*.mkv',
|
||||
'*.mov', '*.mp2', '*.mp2v', '*.mp4', '*.mp4v', '*.mpe', '*.mpeg', '*.mpeg1', '*.mpeg2', '*.mpeg4', '*.mpg',
|
||||
'*.mpv2', '*.mts', '*.mtv', '*.mxf', '*.mxg', '*.nsv', '*.nuv', '*.ogg', '*.ogm', '*.ogv', '*.ogx', '*.ps',
|
||||
'*.rec', '*.rm', '*.rmvb', '*.rpl', '*.thp', '*.tod', '*.ts', '*.tts', '*.txd', '*.vob', '*.vro', '*.webm',
|
||||
'*.wm', '*.wmv', '*.wtv', '*.xesc', '*.nut', '*.rv', '*.xvid']
|
||||
|
||||
|
||||
class MediaState(object):
|
||||
"""
|
||||
|
@ -42,9 +42,9 @@ from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib.serviceitem import ItemCapabilities
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui import DisplayControllerType
|
||||
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path
|
||||
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path, VIDEO_EXT, AUDIO_EXT
|
||||
from openlp.core.ui.media.endpoint import media_endpoint
|
||||
from openlp.core.ui.media.vlcplayer import AUDIO_EXT, VIDEO_EXT, VlcPlayer, get_vlc
|
||||
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -184,7 +184,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||
display.has_audio = False
|
||||
self.vlc_player.setup(display, preview)
|
||||
|
||||
def set_controls_visible(self, controller, value):
|
||||
@staticmethod
|
||||
def set_controls_visible(controller, value):
|
||||
"""
|
||||
After a new display is configured, all media related widget will be created too
|
||||
|
||||
@ -229,7 +230,10 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||
display = self._define_display(controller)
|
||||
if controller.is_live:
|
||||
# if this is an optical device use special handling
|
||||
if service_item.is_capable(ItemCapabilities.IsOptical):
|
||||
if service_item.is_capable(ItemCapabilities.CanStream):
|
||||
is_valid = self._check_file_type(controller, display, True)
|
||||
controller.media_info.media_type = MediaType.Stream
|
||||
elif service_item.is_capable(ItemCapabilities.IsOptical):
|
||||
log.debug('video is optical and live')
|
||||
path = service_item.get_frame_path()
|
||||
(name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path)
|
||||
@ -249,7 +253,10 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||
controller.media_info.start_time = service_item.start_time
|
||||
controller.media_info.end_time = service_item.end_time
|
||||
elif controller.preview_display:
|
||||
if service_item.is_capable(ItemCapabilities.IsOptical):
|
||||
if service_item.is_capable(ItemCapabilities.CanStream):
|
||||
controller.media_info.media_type = MediaType.Stream
|
||||
is_valid = self._check_file_type(controller, display, True)
|
||||
elif service_item.is_capable(ItemCapabilities.IsOptical):
|
||||
log.debug('video is optical and preview')
|
||||
path = service_item.get_frame_path()
|
||||
(name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path)
|
||||
@ -270,6 +277,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||
# display.frame.runJavaScript('show_video("setBackBoard", null, null,"visible");')
|
||||
# now start playing - Preview is autoplay!
|
||||
autoplay = False
|
||||
if service_item.is_capable(ItemCapabilities.CanStream):
|
||||
autoplay = True
|
||||
# Preview requested
|
||||
if not controller.is_live:
|
||||
autoplay = True
|
||||
@ -346,13 +355,21 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
||||
controller.media_info.media_type = MediaType.DVD
|
||||
return True
|
||||
|
||||
def _check_file_type(self, controller, display):
|
||||
def _check_file_type(self, controller, display, stream=False):
|
||||
"""
|
||||
Select the correct media Player type from the prioritized Player list
|
||||
|
||||
:param controller: First element is the controller which should be used
|
||||
:param display: Which display to use
|
||||
:param stream: Are we streaming or not
|
||||
"""
|
||||
if stream:
|
||||
self.resize(display, self.vlc_player)
|
||||
display.media_info.media_type = MediaType.Stream
|
||||
if self.vlc_player.load(display, None):
|
||||
self.current_media_players[controller.controller_type] = self.vlc_player
|
||||
return True
|
||||
return True
|
||||
for file in controller.media_info.file_info:
|
||||
if file.is_file:
|
||||
suffix = '*%s' % file.suffix.lower()
|
||||
|
@ -33,9 +33,9 @@ from openlp.core.common.settings import Settings
|
||||
from openlp.core.lib.settingstab import SettingsTab
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
|
||||
LINUX_STREAM = 'v4l2://{video} :v4l2-standard= :input-slave={audio} :live-caching=300'
|
||||
LINUX_STREAM = 'v4l2://{video}:v4l2-standard= :input-slave=alsa://{audio} :live-caching=300'
|
||||
WIN_STREAM = 'dshow://:dshow-vdev={video} :dshow-adev={audio} :live-caching=300'
|
||||
OSX_STREAM = 'avcapture://{video} :qtsound://{audio} :live-caching=300'
|
||||
OSX_STREAM = 'avcapture://{video}:qtsound://{audio} :live-caching=300'
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -68,11 +68,15 @@ class MediaTab(SettingsTab):
|
||||
self.left_layout.addWidget(self.live_media_group_box)
|
||||
self.stream_media_group_box = QtWidgets.QGroupBox(self.left_column)
|
||||
self.stream_media_group_box.setObjectName('stream_media_group_box')
|
||||
self.stream_media_layout = QtWidgets.QHBoxLayout(self.stream_media_group_box)
|
||||
self.stream_media_layout = QtWidgets.QFormLayout(self.stream_media_group_box)
|
||||
self.stream_media_layout.setObjectName('stream_media_layout')
|
||||
self.stream_media_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.stream_edit = QtWidgets.QLabel(self)
|
||||
self.stream_media_layout.addWidget(self.stream_edit)
|
||||
self.video_edit = QtWidgets.QLineEdit(self)
|
||||
self.stream_media_layout.addRow(translate('MediaPlugin.MediaTab', 'Video:'), self.video_edit)
|
||||
self.audio_edit = QtWidgets.QLineEdit(self)
|
||||
self.stream_media_layout.addRow(translate('MediaPlugin.MediaTab', 'Audio:'), self.audio_edit)
|
||||
self.stream_cmd = QtWidgets.QLabel(self)
|
||||
self.stream_media_layout.addWidget(self.stream_cmd)
|
||||
self.left_layout.addWidget(self.stream_media_group_box)
|
||||
self.vlc_arguments_group_box = QtWidgets.QGroupBox(self.left_column)
|
||||
self.vlc_arguments_group_box.setObjectName('vlc_arguments_group_box')
|
||||
@ -84,7 +88,6 @@ class MediaTab(SettingsTab):
|
||||
self.left_layout.addWidget(self.vlc_arguments_group_box)
|
||||
self.left_layout.addStretch()
|
||||
self.right_layout.addStretch()
|
||||
# # Signals and slots
|
||||
|
||||
def retranslate_ui(self):
|
||||
"""
|
||||
@ -100,22 +103,28 @@ class MediaTab(SettingsTab):
|
||||
Load the settings
|
||||
"""
|
||||
self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start'))
|
||||
self.stream_edit.setText(Settings().value(self.settings_section + '/stream command'))
|
||||
if not self.stream_edit.text():
|
||||
if is_linux:
|
||||
self.stream_edit.setText(LINUX_STREAM)
|
||||
elif is_win:
|
||||
self.stream_edit.setText(WIN_STREAM)
|
||||
else:
|
||||
self.stream_edit.setText(OSX_STREAM)
|
||||
self.stream_cmd.setText(Settings().value(self.settings_section + '/stream command'))
|
||||
self.audio_edit.setText(Settings().value(self.settings_section + '/audio'))
|
||||
self.video_edit.setText(Settings().value(self.settings_section + '/video'))
|
||||
if not self.stream_cmd.text():
|
||||
self.set_base_stream()
|
||||
self.vlc_arguments_edit.setPlainText(Settings().value(self.settings_section + '/vlc arguments'))
|
||||
if Settings().value('advanced/experimental'):
|
||||
# vlc.MediaPlayer().audio_output_device_enum()
|
||||
for cam in QCameraInfo.availableCameras():
|
||||
log.debug(cam.deviceName())
|
||||
log.debug(cam.description())
|
||||
for au in QAudioDeviceInfo.availableDevices(QAudio.AudioInput):
|
||||
log.debug(au.deviceName())
|
||||
|
||||
def set_base_stream(self):
|
||||
if is_linux:
|
||||
self.stream_cmd.setText(LINUX_STREAM)
|
||||
elif is_win:
|
||||
self.stream_cmd.setText(WIN_STREAM)
|
||||
else:
|
||||
self.stream_cmd.setText(OSX_STREAM)
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Save the settings
|
||||
@ -123,8 +132,12 @@ class MediaTab(SettingsTab):
|
||||
setting_key = self.settings_section + '/media auto start'
|
||||
if Settings().value(setting_key) != self.auto_start_check_box.checkState():
|
||||
Settings().setValue(setting_key, self.auto_start_check_box.checkState())
|
||||
Settings().setValue(self.settings_section + '/stream command', self.stream_edit.text())
|
||||
Settings().setValue(self.settings_section + '/stream command', self.stream_cmd.text())
|
||||
Settings().setValue(self.settings_section + '/vlc arguments', self.vlc_arguments_edit.toPlainText())
|
||||
Settings().setValue(self.settings_section + '/video', self.video_edit.text())
|
||||
Settings().setValue(self.settings_section + '/audio', self.audio_edit.text())
|
||||
self.stream_cmd.setText(self.stream_cmd.text().format(video=self.video_edit.text(),
|
||||
audio=self.audio_edit.text()))
|
||||
|
||||
def post_set_up(self, post_update=False):
|
||||
"""
|
||||
|
@ -40,18 +40,6 @@ from openlp.core.ui.media.mediaplayer import MediaPlayer
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Audio and video extensions copied from 'include/vlc_interface.h' from vlc 2.2.0 source
|
||||
AUDIO_EXT = ('3ga', '669', 'a52', 'aac', 'ac3', 'adt', 'adts', 'aif', 'aifc', 'aiff', 'amr', 'aob', 'ape', 'awb', 'caf',
|
||||
'dts', 'flac', 'it', 'kar', 'm4a', 'm4b', 'm4p', 'm5p', 'mid', 'mka', 'mlp', 'mod', 'mpa', 'mp1', 'mp2',
|
||||
'mp3', 'mpc', 'mpga', 'mus', 'oga', 'ogg', 'oma', 'opus', 'qcp', 'ra', 'rmi', 's3m', 'sid', 'spx', 'thd',
|
||||
'tta', 'voc', 'vqf', 'w64', 'wav', 'wma', 'wv', 'xa', 'xm')
|
||||
|
||||
VIDEO_EXT = ('3g2', '3gp', '3gp2', '3gpp', 'amv', 'asf', 'avi', 'bik', 'divx', 'drc', 'dv', 'f4v', 'flv', 'gvi', 'gxf',
|
||||
'iso', 'm1v', 'm2v', 'm2t', 'm2ts', 'm4v', 'mkv', 'mov', 'mp2', 'mp2v', 'mp4', 'mp4v', 'mpe', 'mpeg',
|
||||
'mpeg1', 'mpeg2', 'mpeg4', 'mpg', 'mpv2', 'mts', 'mtv', 'mxf', 'mxg', 'nsv', 'nuv', 'ogg', 'ogm', 'ogv',
|
||||
'ogx', 'ps', 'rec', 'rm', 'rmvb', 'rpl', 'thp', 'tod', 'ts', 'tts', 'txd', 'vob', 'vro', 'webm', 'wm',
|
||||
'wmv', 'wtv', 'xesc',
|
||||
# These extensions was not in the official list, added manually.
|
||||
'nut', 'rv', 'xvid')
|
||||
|
||||
|
||||
def get_vlc():
|
||||
@ -159,16 +147,15 @@ class VlcPlayer(MediaPlayer):
|
||||
Load a video into VLC
|
||||
|
||||
:param output_display: The display where the media is
|
||||
:param file: file to be played
|
||||
:param file: file to be played or None for live streaming
|
||||
:return:
|
||||
"""
|
||||
vlc = get_vlc()
|
||||
log.debug('load vid in Vlc Controller')
|
||||
controller = output_display
|
||||
volume = controller.media_info.volume
|
||||
path = os.path.normcase(file)
|
||||
if file:
|
||||
path = os.path.normcase(file)
|
||||
# create the media
|
||||
if controller.media_info.media_type == MediaType.CD:
|
||||
if output_display.media_info.media_type == MediaType.CD:
|
||||
if is_win():
|
||||
path = '/' + path
|
||||
output_display.vlc_media = output_display.vlc_instance.media_new_location('cdda://' + path)
|
||||
@ -180,8 +167,8 @@ class VlcPlayer(MediaPlayer):
|
||||
audio_cd_tracks = output_display.vlc_media.subitems()
|
||||
if not audio_cd_tracks or audio_cd_tracks.count() < 1:
|
||||
return False
|
||||
output_display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track)
|
||||
elif controller.media_info.media_type == MediaType.Stream:
|
||||
output_display.vlc_media = audio_cd_tracks.item_at_index(output_display.media_info.title_track)
|
||||
elif output_display.media_info.media_type == MediaType.Stream:
|
||||
stream_cmd = Settings().value('media/stream command')
|
||||
output_display.vlc_media = output_display.vlc_instance.media_new_location(stream_cmd)
|
||||
else:
|
||||
@ -190,7 +177,7 @@ class VlcPlayer(MediaPlayer):
|
||||
output_display.vlc_media_player.set_media(output_display.vlc_media)
|
||||
# parse the metadata of the file
|
||||
output_display.vlc_media.parse()
|
||||
self.volume(output_display, volume)
|
||||
self.volume(output_display, output_display.media_info.volume)
|
||||
return True
|
||||
|
||||
def media_state_wait(self, output_display, media_state):
|
||||
|
@ -48,7 +48,7 @@ from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.lib.serviceitem import ItemCapabilities, ServiceItem
|
||||
from openlp.core.lib.ui import create_widget_action, critical_error_message_box, find_and_set_in_combo_box
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.core.ui.media.vlcplayer import AUDIO_EXT, VIDEO_EXT
|
||||
from openlp.core.ui.media import AUDIO_EXT, VIDEO_EXT
|
||||
from openlp.core.ui.serviceitemeditform import ServiceItemEditForm
|
||||
from openlp.core.ui.servicenoteform import ServiceNoteForm
|
||||
from openlp.core.ui.starttimeform import StartTimeForm
|
||||
|
@ -32,7 +32,7 @@ from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.theme import BackgroundGradientType, BackgroundType
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.media.vlcplayer import VIDEO_EXT
|
||||
from openlp.core.ui.media import VIDEO_EXT
|
||||
from openlp.core.ui.themelayoutform import ThemeLayoutForm
|
||||
from openlp.core.ui.themewizard import Ui_ThemeWizard
|
||||
|
||||
|
@ -103,8 +103,8 @@ class ProxyWidget(QtWidgets.QGroupBox):
|
||||
:param QtWidgets.QRadioButton button: The button that has toggled
|
||||
:param bool checked: The buttons new state
|
||||
"""
|
||||
id = self.radio_group.id(button) # The work around (see above comment)
|
||||
enable_manual_edits = id == ProxyMode.MANUAL_PROXY and checked
|
||||
group_id = self.radio_group.id(button) # The work around (see above comment)
|
||||
enable_manual_edits = group_id == ProxyMode.MANUAL_PROXY and checked
|
||||
self.http_edit.setEnabled(enable_manual_edits)
|
||||
self.https_edit.setEnabled(enable_manual_edits)
|
||||
self.username_edit.setEnabled(enable_manual_edits)
|
||||
|
@ -37,8 +37,8 @@ from openlp.core.lib.mediamanageritem import MediaManagerItem
|
||||
from openlp.core.lib.serviceitem import ItemCapabilities
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.core.ui.media import parse_optical_path, format_milliseconds
|
||||
from openlp.core.ui.media.vlcplayer import AUDIO_EXT, VIDEO_EXT, get_vlc
|
||||
from openlp.core.ui.media import parse_optical_path, format_milliseconds, AUDIO_EXT, VIDEO_EXT
|
||||
from openlp.core.ui.media.vlcplayer import get_vlc
|
||||
|
||||
|
||||
if get_vlc() is not None:
|
||||
@ -175,7 +175,11 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
||||
return False
|
||||
filename = str(item.data(QtCore.Qt.UserRole))
|
||||
# Special handling if the filename is a optical clip
|
||||
if filename.startswith('optical:'):
|
||||
if filename == 'live':
|
||||
service_item.processor = 'vlc'
|
||||
service_item.title = filename
|
||||
service_item.add_capability(ItemCapabilities.CanStream)
|
||||
elif filename.startswith('optical:'):
|
||||
(name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(filename)
|
||||
if not os.path.exists(name):
|
||||
if not remote:
|
||||
@ -232,9 +236,9 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
||||
"""
|
||||
# self.populate_display_types()
|
||||
self.on_new_file_masks = translate('MediaPlugin.MediaItem',
|
||||
'Videos (*.{video});;Audio (*.{audio});;{files} '
|
||||
'(*)').format(video=' *.'.join(VIDEO_EXT),
|
||||
audio=' *.'.join(AUDIO_EXT),
|
||||
'Videos ({video});;Audio ({audio});;{files} '
|
||||
'(*)').format(video=' '.join(VIDEO_EXT),
|
||||
audio=' '.join(AUDIO_EXT),
|
||||
files=UiStrings().AllFiles)
|
||||
|
||||
def on_delete_click(self):
|
||||
@ -258,6 +262,12 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
||||
"""
|
||||
# TODO needs to be fixed as no idea why this fails
|
||||
# media.sort(key=lambda file_path: get_natural_key(file_path.name))
|
||||
file_name = translate('MediaPlugin.MediaItem', 'Live Stream')
|
||||
item_name = QtWidgets.QListWidgetItem(file_name)
|
||||
item_name.setIcon(UiIcons().video)
|
||||
item_name.setData(QtCore.Qt.UserRole, 'live')
|
||||
item_name.setToolTip(translate('MediaPlugin.MediaItem', 'Show Live Stream'))
|
||||
self.list_view.addItem(item_name)
|
||||
for track in media:
|
||||
track_str = str(track)
|
||||
track_info = QtCore.QFileInfo(track_str)
|
||||
|
@ -129,7 +129,7 @@ class PresentationDocument(object):
|
||||
thumbnail_folder_path = self.get_thumbnail_folder()
|
||||
temp_folder_path = self.get_temp_folder()
|
||||
if thumbnail_folder_path.exists():
|
||||
thumbnail_folder_path.rmtree()
|
||||
shutil.rmtree(thumbnail_folder_path)
|
||||
if temp_folder_path.exists():
|
||||
shutil.rmtree(temp_folder_path)
|
||||
except OSError:
|
||||
|
@ -88,52 +88,72 @@ class SongReviewWidget(QtWidgets.QWidget):
|
||||
self.song_alternate_title_content.setText(self.song.alternate_title)
|
||||
self.song_alternate_title_content.setWordWrap(True)
|
||||
self.song_info_form_layout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.song_alternate_title_content)
|
||||
# Add last modified date.
|
||||
self.song_last_modified_label = QtWidgets.QLabel(self)
|
||||
self.song_last_modified_label.setObjectName('last_modified_label')
|
||||
self.song_last_modified_label.setText('Last Modified:')
|
||||
self.song_info_form_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.song_last_modified_label)
|
||||
self.song_last_modified_content = QtWidgets.QLabel(self)
|
||||
self.song_last_modified_content.setObjectName('last_modified_content')
|
||||
self.song_last_modified_content.setText(self.song.last_modified.strftime("%Y-%m-%d %H:%M:%S"))
|
||||
self.song_last_modified_content.setWordWrap(True)
|
||||
self.song_info_form_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.song_last_modified_content)
|
||||
# Add Theme widget.
|
||||
self.song_theme_label = QtWidgets.QLabel(self)
|
||||
self.song_theme_label.setObjectName('song_theme_label')
|
||||
self.song_theme_label.setText('Theme:')
|
||||
self.song_info_form_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.song_theme_label)
|
||||
self.song_theme_content = QtWidgets.QLabel(self)
|
||||
self.song_theme_content.setObjectName('song_theme_content')
|
||||
self.song_theme_content.setText(self.song.theme_name)
|
||||
self.song_theme_content.setWordWrap(True)
|
||||
self.song_info_form_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.song_theme_content)
|
||||
# Add CCLI number widget.
|
||||
self.song_ccli_number_label = QtWidgets.QLabel(self)
|
||||
self.song_ccli_number_label.setObjectName('song_ccli_number_label')
|
||||
self.song_info_form_layout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.song_ccli_number_label)
|
||||
self.song_info_form_layout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.song_ccli_number_label)
|
||||
self.song_ccli_number_content = QtWidgets.QLabel(self)
|
||||
self.song_ccli_number_content.setObjectName('song_ccli_number_content')
|
||||
self.song_ccli_number_content.setText(self.song.ccli_number)
|
||||
self.song_ccli_number_content.setWordWrap(True)
|
||||
self.song_info_form_layout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.song_ccli_number_content)
|
||||
self.song_info_form_layout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.song_ccli_number_content)
|
||||
# Add copyright widget.
|
||||
self.song_copyright_label = QtWidgets.QLabel(self)
|
||||
self.song_copyright_label.setObjectName('song_copyright_label')
|
||||
self.song_info_form_layout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.song_copyright_label)
|
||||
self.song_info_form_layout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.song_copyright_label)
|
||||
self.song_copyright_content = QtWidgets.QLabel(self)
|
||||
self.song_copyright_content.setObjectName('song_copyright_content')
|
||||
self.song_copyright_content.setWordWrap(True)
|
||||
self.song_copyright_content.setText(self.song.copyright)
|
||||
self.song_info_form_layout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.song_copyright_content)
|
||||
self.song_info_form_layout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.song_copyright_content)
|
||||
# Add comments widget.
|
||||
self.song_comments_label = QtWidgets.QLabel(self)
|
||||
self.song_comments_label.setObjectName('song_comments_label')
|
||||
self.song_info_form_layout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.song_comments_label)
|
||||
self.song_info_form_layout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.song_comments_label)
|
||||
self.song_comments_content = QtWidgets.QLabel(self)
|
||||
self.song_comments_content.setObjectName('song_comments_content')
|
||||
self.song_comments_content.setText(self.song.comments)
|
||||
self.song_comments_content.setWordWrap(True)
|
||||
self.song_info_form_layout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.song_comments_content)
|
||||
self.song_info_form_layout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.song_comments_content)
|
||||
# Add authors widget.
|
||||
self.song_authors_label = QtWidgets.QLabel(self)
|
||||
self.song_authors_label.setObjectName('song_authors_label')
|
||||
self.song_info_form_layout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.song_authors_label)
|
||||
self.song_info_form_layout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.song_authors_label)
|
||||
self.song_authors_content = QtWidgets.QLabel(self)
|
||||
self.song_authors_content.setObjectName('song_authors_content')
|
||||
self.song_authors_content.setWordWrap(True)
|
||||
authors_text = ', '.join([author.display_name for author in self.song.authors])
|
||||
self.song_authors_content.setText(authors_text)
|
||||
self.song_info_form_layout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.song_authors_content)
|
||||
self.song_info_form_layout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.song_authors_content)
|
||||
# Add verse order widget.
|
||||
self.song_verse_order_label = QtWidgets.QLabel(self)
|
||||
self.song_verse_order_label.setObjectName('song_verse_order_label')
|
||||
self.song_info_form_layout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.song_verse_order_label)
|
||||
self.song_info_form_layout.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.song_verse_order_label)
|
||||
self.song_verse_order_content = QtWidgets.QLabel(self)
|
||||
self.song_verse_order_content.setObjectName('song_verse_order_content')
|
||||
self.song_verse_order_content.setText(self.song.verse_order)
|
||||
self.song_verse_order_content.setWordWrap(True)
|
||||
self.song_info_form_layout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.song_verse_order_content)
|
||||
self.song_info_form_layout.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.song_verse_order_content)
|
||||
self.song_group_box_layout.addLayout(self.song_info_form_layout)
|
||||
# Add verses widget.
|
||||
self.song_info_verse_list_widget = QtWidgets.QTableWidget(self.song_group_box)
|
||||
|
@ -423,7 +423,7 @@ class SongsPlugin(Plugin):
|
||||
"""
|
||||
Remove temporary songs from the database
|
||||
"""
|
||||
songs = self.manager.get_all_objects(Song, Song.temporary is True)
|
||||
songs = self.manager.get_all_objects(Song, Song.temporary == True) # noqa: E712
|
||||
for song in songs:
|
||||
self.manager.delete_object(Song, song.id)
|
||||
|
||||
|
@ -27,6 +27,7 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.ui.media.mediacontroller import MediaController
|
||||
from openlp.core.ui.media import ItemMediaInfo
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
from tests.utils.constants import RESOURCE_PATH
|
||||
@ -57,7 +58,7 @@ class TestMediaController(TestCase, TestMixin):
|
||||
# THEN: The player's resize method should be called correctly
|
||||
mocked_player.resize.assert_called_with(mocked_display)
|
||||
|
||||
def test_check_file_type(self):
|
||||
def test_check_file_type_null(self):
|
||||
"""
|
||||
Test that we don't try to play media when no players available
|
||||
"""
|
||||
@ -71,7 +72,47 @@ class TestMediaController(TestCase, TestMixin):
|
||||
ret = media_controller._check_file_type(mocked_controller, mocked_display)
|
||||
|
||||
# THEN: it should return False
|
||||
assert ret is False, '_check_file_type should return False when no mediaplayers are available.'
|
||||
assert ret is False, '_check_file_type should return False when no media file matches.'
|
||||
|
||||
def test_check_file_video(self):
|
||||
"""
|
||||
Test that we process a file that is valid
|
||||
"""
|
||||
# GIVEN: A mocked UiStrings, get_used_players, controller, display and service_item
|
||||
media_controller = MediaController()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_display = MagicMock()
|
||||
media_controller.media_players = MagicMock()
|
||||
mocked_controller.media_info = ItemMediaInfo()
|
||||
mocked_controller.media_info.file_info = [TEST_PATH / 'mp3_file.mp3']
|
||||
media_controller.current_media_players = {}
|
||||
media_controller.vlc_player = MagicMock()
|
||||
|
||||
# WHEN: calling _check_file_type when no players exists
|
||||
ret = media_controller._check_file_type(mocked_controller, mocked_display)
|
||||
|
||||
# THEN: it should return False
|
||||
assert ret is True, '_check_file_type should return True when audio file is present and matches.'
|
||||
|
||||
def test_check_file_audio(self):
|
||||
"""
|
||||
Test that we process a file that is valid
|
||||
"""
|
||||
# GIVEN: A mocked UiStrings, get_used_players, controller, display and service_item
|
||||
media_controller = MediaController()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_display = MagicMock()
|
||||
media_controller.media_players = MagicMock()
|
||||
mocked_controller.media_info = ItemMediaInfo()
|
||||
mocked_controller.media_info.file_info = [TEST_PATH / 'mp4_file.mp4']
|
||||
media_controller.current_media_players = {}
|
||||
media_controller.vlc_player = MagicMock()
|
||||
|
||||
# WHEN: calling _check_file_type when no players exists
|
||||
ret = media_controller._check_file_type(mocked_controller, mocked_display)
|
||||
|
||||
# THEN: it should return False
|
||||
assert ret is True, '_check_file_type should return True when media file is present and matches.'
|
||||
|
||||
def test_media_play_msg(self):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user