Add support for network streams.

This commit is contained in:
Tomas Groth 2020-03-08 22:05:09 +00:00 committed by Tim Bentley
parent b51829d4d6
commit 127ee05ac3
9 changed files with 329 additions and 124 deletions

View File

@ -37,6 +37,7 @@ from openlp.core.ui.media.vlcplayer import get_vlc
if get_vlc() is not None:
from openlp.plugins.media.forms.streamselectorform import StreamSelectorForm
from openlp.plugins.media.forms.networkstreamselectorform import NetworkStreamSelectorForm
class BackgroundPage(GridLayoutPage):
@ -130,11 +131,16 @@ class BackgroundPage(GridLayoutPage):
self.stream_lineedit.setObjectName('stream_lineedit')
self.stream_lineedit.setReadOnly(True)
self.stream_layout.addWidget(self.stream_lineedit)
# button to open select stream forms
self.stream_select_button = QtWidgets.QToolButton(self)
self.stream_select_button.setObjectName('stream_select_button')
self.stream_select_button.setIcon(UiIcons().device_stream)
self.stream_layout.addWidget(self.stream_select_button)
# button to open select device stream form
self.device_stream_select_button = QtWidgets.QToolButton(self)
self.device_stream_select_button.setObjectName('device_stream_select_button')
self.device_stream_select_button.setIcon(UiIcons().device_stream)
self.stream_layout.addWidget(self.device_stream_select_button)
# button to open select network stream form
self.network_stream_select_button = QtWidgets.QToolButton(self)
self.network_stream_select_button.setObjectName('network_stream_select_button')
self.network_stream_select_button.setIcon(UiIcons().network_stream)
self.stream_layout.addWidget(self.network_stream_select_button)
self.layout.addLayout(self.stream_layout, 6, 1, 1, 3)
self.stream_color_label = FormLabel(self)
self.stream_color_label.setObjectName('stream_color_label')
@ -143,14 +149,15 @@ class BackgroundPage(GridLayoutPage):
self.stream_color_button.color = '#000000'
self.stream_color_button.setObjectName('stream_color_button')
self.layout.addWidget(self.stream_color_button, 7, 1)
self.stream_widgets = [self.stream_label, self.stream_lineedit, self.stream_select_button,
self.stream_widgets = [self.stream_label, self.stream_lineedit, self.device_stream_select_button,
self.stream_color_label, self.stream_color_button]
# Force everything up
self.layout_spacer = QtWidgets.QSpacerItem(1, 1)
self.layout.addItem(self.layout_spacer, 8, 0, 1, 4)
# Connect slots
self.background_combo_box.currentIndexChanged.connect(self._on_background_type_index_changed)
self.stream_select_button.clicked.connect(self._on_stream_select_button_triggered)
self.device_stream_select_button.clicked.connect(self._on_device_stream_select_button_triggered)
self.network_stream_select_button.clicked.connect(self._on_network_stream_select_button_triggered)
# Force the first set of widgets to show
self._on_background_type_index_changed(0)
@ -208,7 +215,7 @@ class BackgroundPage(GridLayoutPage):
for widget in widget_sets[index]:
widget.show()
def _on_stream_select_button_triggered(self):
def _on_device_stream_select_button_triggered(self):
"""
Open the Stream selection form.
"""
@ -222,6 +229,20 @@ class BackgroundPage(GridLayoutPage):
critical_error_message_box(translate('MediaPlugin.MediaItem', 'VLC is not available'),
translate('MediaPlugin.MediaItem', 'Device streaming support requires VLC.'))
def _on_network_stream_select_button_triggered(self):
"""
Open the Stream selection form.
"""
if get_vlc():
stream_selector_form = NetworkStreamSelectorForm(self, self.set_stream, True)
if self.stream_lineedit.text():
stream_selector_form.set_mrl(self.stream_lineedit.text())
stream_selector_form.exec()
del stream_selector_form
else:
critical_error_message_box(translate('MediaPlugin.MediaItem', 'VLC is not available'),
translate('MediaPlugin.MediaItem', 'Network streaming support requires VLC.'))
def set_stream(self, stream_str):
"""
callback method used to get the stream mrl and options

View File

@ -99,6 +99,7 @@ class UiIcons(metaclass=Singleton):
'media': {'icon': 'fa.fax'},
'minus': {'icon': 'fa.minus'},
'music': {'icon': 'fa.music'},
'network_stream': {'icon': 'fa.link'},
'new': {'icon': 'fa.file'},
'new_group': {'icon': 'fa.folder'},
'notes': {'icon': 'fa.sticky-note'},

View File

@ -104,16 +104,18 @@ def parse_optical_path(input_string):
return filename, title, audio_track, subtitle_track, start, end, clip_name
def parse_devicestream_path(input_string):
def parse_stream_path(input_string):
"""
Split the device stream path info.
:param input_string: The string to parse
:return: The elements extracted from the string: streamname, MRL, VLC-options
"""
log.debug('parse_devicestream_path, about to parse: "{text}"'.format(text=input_string))
# skip the header: 'devicestream:', split at '&&'
stream_info = input_string[len('devicestream:'):].split('&&')
log.debug('parse_stream_path, about to parse: "{text}"'.format(text=input_string))
# skip the header: 'devicestream:' or 'networkstream:'
header, data = input_string.split(':', 1)
# split at '&&'
stream_info = data.split('&&')
name = stream_info[0]
mrl = stream_info[1]
options = stream_info[2]

View File

@ -41,7 +41,7 @@ from openlp.core.common.registry import Registry, RegistryBase
from openlp.core.lib.serviceitem import ItemCapabilities
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui import DisplayControllerType, HideMode
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path, parse_devicestream_path, \
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path, parse_stream_path, \
VIDEO_EXT, AUDIO_EXT
from openlp.core.ui.media.remote import register_views
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
@ -231,7 +231,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.media_info.file_info = service_item.background_audio
else:
if service_item.is_capable(ItemCapabilities.HasBackgroundStream):
(name, mrl, options) = parse_devicestream_path(service_item.stream_mrl)
(name, mrl, options) = parse_stream_path(service_item.stream_mrl)
controller.media_info.file_info = (mrl, options)
controller.media_info.is_background = True
controller.media_info.media_type = MediaType.Stream
@ -255,7 +255,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
log.debug('video is stream and live')
path = service_item.get_frames()[0]['path']
controller.media_info.media_type = MediaType.Stream
(name, mrl, options) = parse_devicestream_path(path)
(name, mrl, options) = parse_stream_path(path)
controller.media_info.file_info = (mrl, options)
is_valid = self._check_file_type(controller, display)
else:
@ -274,7 +274,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
elif service_item.is_capable(ItemCapabilities.CanStream):
path = service_item.get_frames()[0]['path']
controller.media_info.media_type = MediaType.Stream
(name, mrl, options) = parse_devicestream_path(path)
(name, mrl, options) = parse_stream_path(path)
controller.media_info.file_info = (mrl, options)
is_valid = self._check_file_type(controller, display)
else:

View File

@ -18,3 +18,149 @@
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
import re
from PyQt5 import QtCore, QtWidgets
from openlp.core.common.i18n import translate
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui.media import parse_stream_path
class StreamSelectorFormBase(QtWidgets.QDialog):
"""
Class to manage the clip selection
"""
def __init__(self, parent, callback, theme_stream=False):
"""
Constructor
"""
super(StreamSelectorFormBase, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint |
QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
self.callback = callback
self.theme_stream = theme_stream
self.setup_base_ui()
self.type = ''
def setup_base_ui(self):
self.setObjectName('stream_selector')
self.combobox_size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.Fixed)
self.setSizePolicy(
QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding))
self.main_layout = QtWidgets.QVBoxLayout(self)
self.main_layout.setObjectName('main_layout')
self.top_widget = QtWidgets.QWidget(self)
self.top_widget.setObjectName('top_widget')
self.top_layout = QtWidgets.QFormLayout(self.top_widget)
self.top_layout.setObjectName('top_layout')
# Stream name
if not self.theme_stream:
self.stream_name_label = QtWidgets.QLabel(self.top_widget)
self.stream_name_label.setObjectName('stream_name_label')
self.stream_name_edit = QtWidgets.QLineEdit(self.top_widget)
self.stream_name_edit.setObjectName('stream_name_edit')
self.top_layout.addRow(self.stream_name_label, self.stream_name_edit)
def exec(self):
"""
Start dialog
"""
return QtWidgets.QDialog.exec(self)
def accept(self):
"""
Saves the current stream as a clip to the mediamanager
"""
if not self.theme_stream:
# Verify that a stream name exists
if not self.stream_name_edit.text().strip():
critical_error_message_box(message=translate('MediaPlugin.StreamSelector', 'A Stream name is needed.'))
return
stream_name = self.stream_name_edit.text().strip()
else:
stream_name = ' '
# Verify that a MRL exists
if not self.more_options_group.mrl_lineedit.text().strip():
critical_error_message_box(message=translate('MediaPlugin.StreamSelector', 'A MRL is needed.'), parent=self)
return
stream_string = '{type}:{name}&&{mrl}&&{options}'.format(
type=self.type, name=stream_name, mrl=self.more_options_group.mrl_lineedit.text().strip(),
options=self.more_options_group.vlc_options_lineedit.text().strip())
self.callback(stream_string)
return QtWidgets.QDialog.accept(self)
def update_mrl_options(self, mrl, options):
"""
Callback method used to fill the MRL and Options text fields
"""
options += ' :live-caching={cache}'.format(cache=self.more_options_group.caching.value())
self.more_options_group.mrl_lineedit.setText(mrl)
self.more_options_group.vlc_options_lineedit.setText(options)
def set_mrl(self, stream_str):
"""
Setup the stream widgets based on the saved stream string. This is best effort as the string is
editable for the user.
"""
(name, mrl, options) = parse_stream_path(stream_str)
cache = re.search(r'live-caching=(\d+)', options)
if cache:
self.more_options_group.caching.setValue(int(cache.group(1)))
self.more_options_group.mrl_lineedit.setText(mrl)
self.more_options_group.vlc_options_lineedit.setText(options)
class VLCOptionsWidget(QtWidgets.QGroupBox):
"""
Groupbox widget for VLC options: caching, mrl and VLC options
"""
def __init__(self, parent=None):
"""
Initialise the widget.
:param QtWidgets.QWidget | None parent: The widgets parent
"""
super().__init__(parent)
self.setup_ui()
self.retranslate_ui()
def setup_ui(self):
"""
Create the widget layout and sub widgets
"""
# Groupbox for VLC options
self.vlc_options_group_layout = QtWidgets.QFormLayout(self)
self.vlc_options_group_layout.setObjectName('more_options_group_layout')
# Caching spinbox
self.caching_label = QtWidgets.QLabel(self)
self.caching_label.setObjectName('caching_label')
self.caching = QtWidgets.QSpinBox(self)
self.caching.setAlignment(QtCore.Qt.AlignRight)
self.caching.setSuffix(' ms')
self.caching.setSingleStep(100)
self.caching.setMaximum(65535)
self.caching.setValue(300)
self.vlc_options_group_layout.addRow(self.caching_label, self.caching)
# MRL
self.mrl_label = QtWidgets.QLabel(self)
self.mrl_label.setObjectName('mrl_label')
self.mrl_lineedit = QtWidgets.QLineEdit(self)
self.mrl_lineedit.setObjectName('mrl_lineedit')
self.vlc_options_group_layout.addRow(self.mrl_label, self.mrl_lineedit)
# VLC options
self.vlc_options_label = QtWidgets.QLabel(self)
self.vlc_options_label.setObjectName('vlc_options_label')
self.vlc_options_lineedit = QtWidgets.QLineEdit(self)
self.vlc_options_lineedit.setObjectName('vlc_options_lineedit')
self.vlc_options_group_layout.addRow(self.vlc_options_label, self.vlc_options_lineedit)
def retranslate_ui(self):
self.setTitle(translate('MediaPlugin.StreamSelector', 'More options'))
self.caching_label.setText(translate('MediaPlugin.StreamSelector', 'Caching'))
self.mrl_label.setText(translate('MediaPlugin.StreamSelector', 'MRL'))
self.vlc_options_label.setText(translate('MediaPlugin.StreamSelector', 'VLC options'))

View File

@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2020 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, either version 3 of the License, or #
# (at your option) any later version. #
# #
# 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, see <https://www.gnu.org/licenses/>. #
##########################################################################
import logging
from PyQt5 import QtWidgets
from openlp.core.common.i18n import translate
from openlp.plugins.media.forms import StreamSelectorFormBase, VLCOptionsWidget
log = logging.getLogger(__name__)
class NetworkStreamSelectorForm(StreamSelectorFormBase):
"""
Class to manage the network stream selection
"""
log.info('{name} NetworkStreamSelectorForm loaded'.format(name=__name__))
def __init__(self, parent, callback, theme_stream=False):
"""
Constructor
"""
super(NetworkStreamSelectorForm, self).__init__(parent, callback, theme_stream)
self.type = 'networkstream'
self.setup_ui()
def setup_ui(self):
self.net_mrl_label = QtWidgets.QLabel(self)
self.net_mrl_label.setObjectName('net_mrl_label')
self.net_mrl_lineedit = QtWidgets.QLineEdit(self)
self.net_mrl_lineedit.setObjectName('net_mrl_lineedit')
self.top_layout.addRow(self.net_mrl_label, self.net_mrl_lineedit)
self.main_layout.addWidget(self.top_widget)
# Add groupbox for VLC options
self.more_options_group = VLCOptionsWidget(self)
# Add groupbox for more options to main layout
self.main_layout.addWidget(self.more_options_group)
# Save and close buttons
self.button_box = QtWidgets.QDialogButtonBox(self)
self.button_box.addButton(QtWidgets.QDialogButtonBox.Save)
self.button_box.addButton(QtWidgets.QDialogButtonBox.Close)
self.close_button = self.button_box.button(QtWidgets.QDialogButtonBox.Close)
self.save_button = self.button_box.button(QtWidgets.QDialogButtonBox.Save)
self.main_layout.addWidget(self.button_box)
# translate
self.retranslate_ui()
# connect
self.net_mrl_lineedit.editingFinished.connect(self.on_updates)
self.more_options_group.caching.valueChanged.connect(self.on_updates)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
def retranslate_ui(self):
self.setWindowTitle(translate('MediaPlugin.StreamSelector', 'Insert Input Stream'))
if not self.theme_stream:
self.stream_name_label.setText(translate('MediaPlugin.StreamSelector', 'Stream name'))
self.net_mrl_label.setText(translate('MediaPlugin.StreamSelector', 'Network URL'))
def on_updates(self):
self.update_mrl_options(self.net_mrl_lineedit.text(), '')

View File

@ -34,6 +34,7 @@ from PyQt5.QtMultimedia import QCameraInfo, QAudioDeviceInfo, QAudio
from openlp.core.common import is_linux, is_macosx, is_win
from openlp.core.common.i18n import translate
from openlp.plugins.media.forms import VLCOptionsWidget
# Copied from VLC source code: modules/access/v4l2/v4l2.c
VIDEO_STANDARDS_VLC = [
@ -651,25 +652,6 @@ class CaptureVideoDirectShowWidget(CaptureVideoQtDetectWidget):
class Ui_StreamSelector(object):
def setup_ui(self, stream_selector):
stream_selector.setObjectName('stream_selector')
self.combobox_size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.Fixed)
stream_selector.setSizePolicy(
QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding))
self.main_layout = QtWidgets.QVBoxLayout(stream_selector)
self.main_layout.setObjectName('main_layout')
self.top_widget = QtWidgets.QWidget(stream_selector)
self.top_widget.setObjectName('top_widget')
self.top_layout = QtWidgets.QFormLayout(self.top_widget)
self.top_layout.setObjectName('top_layout')
# Stream name
if not self.theme_stream:
self.stream_name_label = QtWidgets.QLabel(self.top_widget)
self.stream_name_label.setObjectName('stream_name_label')
self.stream_name_edit = QtWidgets.QLineEdit(self.top_widget)
self.stream_name_edit.setObjectName('stream_name_edit')
self.top_layout.addRow(self.stream_name_label, self.stream_name_edit)
# Mode combobox
self.capture_mode_label = QtWidgets.QLabel(self.top_widget)
self.capture_mode_label.setObjectName('capture_mode_label')
@ -719,33 +701,8 @@ class Ui_StreamSelector(object):
for i in range(self.stacked_modes_layout.count()):
self.stacked_modes_layout.widget(i).find_devices()
self.stacked_modes_layout.widget(i).retranslate_ui()
# Groupbox for more options
self.more_options_group = QtWidgets.QGroupBox(self)
self.more_options_group.setObjectName('more_options_group')
self.more_options_group_layout = QtWidgets.QFormLayout(self.more_options_group)
self.more_options_group_layout.setObjectName('more_options_group_layout')
# Caching spinbox
self.caching_label = QtWidgets.QLabel(self)
self.caching_label.setObjectName('caching_label')
self.caching = QtWidgets.QSpinBox(self)
self.caching.setAlignment(QtCore.Qt.AlignRight)
self.caching.setSuffix(' ms')
self.caching.setSingleStep(100)
self.caching.setMaximum(65535)
self.caching.setValue(300)
self.more_options_group_layout.addRow(self.caching_label, self.caching)
# MRL
self.mrl_label = QtWidgets.QLabel(self)
self.mrl_label.setObjectName('mrl_label')
self.mrl_lineedit = QtWidgets.QLineEdit(self)
self.mrl_lineedit.setObjectName('mrl_lineedit')
self.more_options_group_layout.addRow(self.mrl_label, self.mrl_lineedit)
# VLC options
self.vlc_options_label = QtWidgets.QLabel(self)
self.vlc_options_label.setObjectName('vlc_options_label')
self.vlc_options_lineedit = QtWidgets.QLineEdit(self)
self.vlc_options_lineedit.setObjectName('vlc_options_lineedit')
self.more_options_group_layout.addRow(self.vlc_options_label, self.vlc_options_lineedit)
# Add groupbox for VLC options
self.more_options_group = VLCOptionsWidget(self)
# Add groupbox for more options to main layout
self.main_layout.addWidget(self.more_options_group)
# Save and close buttons
@ -760,7 +717,7 @@ class Ui_StreamSelector(object):
self.retranslate_ui(stream_selector)
# connect
self.capture_mode_combo_box.currentIndexChanged.connect(stream_selector.on_capture_mode_combo_box)
self.caching.valueChanged.connect(stream_selector.on_capture_mode_combo_box)
self.more_options_group.caching.valueChanged.connect(stream_selector.on_capture_mode_combo_box)
self.button_box.accepted.connect(stream_selector.accept)
self.button_box.rejected.connect(stream_selector.reject)
@ -769,7 +726,3 @@ class Ui_StreamSelector(object):
if not self.theme_stream:
self.stream_name_label.setText(translate('MediaPlugin.StreamSelector', 'Stream name'))
self.capture_mode_label.setText(translate('MediaPlugin.StreamSelector', 'Capture Mode'))
self.more_options_group.setTitle(translate('MediaPlugin.StreamSelector', 'More options'))
self.caching_label.setText(translate('MediaPlugin.StreamSelector', 'Caching'))
self.mrl_label.setText(translate('MediaPlugin.StreamSelector', 'MRL'))
self.vlc_options_label.setText(translate('MediaPlugin.StreamSelector', 'VLC options'))

View File

@ -20,18 +20,16 @@
##########################################################################
import logging
from PyQt5 import QtCore, QtWidgets
import re
from openlp.plugins.media.forms.streamselectordialog import Ui_StreamSelector
from openlp.core.ui.media import parse_devicestream_path
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.common.i18n import translate
from openlp.core.ui.media import parse_stream_path
from openlp.plugins.media.forms import StreamSelectorFormBase
log = logging.getLogger(__name__)
class StreamSelectorForm(QtWidgets.QDialog, Ui_StreamSelector):
class StreamSelectorForm(StreamSelectorFormBase, Ui_StreamSelector):
"""
Class to manage the clip selection
"""
@ -41,53 +39,14 @@ class StreamSelectorForm(QtWidgets.QDialog, Ui_StreamSelector):
"""
Constructor
"""
super(StreamSelectorForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint |
QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint)
self.callback = callback
self.theme_stream = theme_stream
super(StreamSelectorForm, self).__init__(parent, callback, theme_stream)
self.type = 'devicestream'
self.setup_ui(self)
# setup callbacks
for i in range(self.stacked_modes_layout.count()):
self.stacked_modes_layout.widget(i).set_callback(self.update_mrl_options)
self.stacked_modes_layout.currentWidget().update_mrl()
def exec(self):
"""
Start dialog
"""
return QtWidgets.QDialog.exec(self)
def accept(self):
"""
Saves the current stream as a clip to the mediamanager
"""
log.debug('in StreamSelectorForm.accept')
if not self.theme_stream:
# Verify that a stream name exists
if not self.stream_name_edit.text().strip():
critical_error_message_box(message=translate('MediaPlugin.StreamSelector', 'A Stream name is needed.'))
return
stream_name = self.stream_name_edit.text().strip()
else:
stream_name = ' '
# Verify that a MRL exists
if not self.mrl_lineedit.text().strip():
critical_error_message_box(message=translate('MediaPlugin.StreamSelector', 'A MRL is needed.'), parent=self)
return
stream_string = 'devicestream:{name}&&{mrl}&&{options}'.format(name=stream_name,
mrl=self.mrl_lineedit.text().strip(),
options=self.vlc_options_lineedit.text().strip())
self.callback(stream_string)
return QtWidgets.QDialog.accept(self)
def update_mrl_options(self, mrl, options):
"""
Callback method used to fill the MRL and Options text fields
"""
options += ' :live-caching={cache}'.format(cache=self.caching.value())
self.mrl_lineedit.setText(mrl)
self.vlc_options_lineedit.setText(options)
def on_capture_mode_combo_box(self):
self.stacked_modes_layout.setCurrentIndex(self.capture_mode_combo_box.currentIndex())
self.stacked_modes_layout.currentWidget().update_mrl()
@ -97,12 +56,14 @@ class StreamSelectorForm(QtWidgets.QDialog, Ui_StreamSelector):
Setup the stream widgets based on the saved devicestream. This is best effort as the string is
editable for the user.
"""
(name, mrl, options) = parse_devicestream_path(device_stream_str)
(name, mrl, options) = parse_stream_path(device_stream_str)
for i in range(self.stacked_modes_layout.count()):
if self.stacked_modes_layout.widget(i).has_support_for_mrl(mrl, options):
self.stacked_modes_layout.setCurrentIndex(i)
self.stacked_modes_layout.widget(i).set_mrl(mrl, options)
break
self.mrl_lineedit.setText(mrl)
self.vlc_options_lineedit.setText(options)
cache = re.search(r'live-caching=(\d+)', options)
if cache:
self.more_options_group.caching.setValue(int(cache.group(1)))
self.more_options_group.mrl_lineedit.setText(mrl)
self.more_options_group.vlc_options_lineedit.setText(options)

View File

@ -35,12 +35,13 @@ from openlp.core.lib.serviceitem import ItemCapabilities
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.state import State
from openlp.core.ui.icons import UiIcons
from openlp.core.ui.media import parse_optical_path, parse_devicestream_path, format_milliseconds, AUDIO_EXT, VIDEO_EXT
from openlp.core.ui.media import parse_optical_path, parse_stream_path, format_milliseconds, AUDIO_EXT, VIDEO_EXT
from openlp.core.ui.media.vlcplayer import get_vlc
if get_vlc() is not None:
from openlp.plugins.media.forms.mediaclipselectorform import MediaClipSelectorForm
from openlp.plugins.media.forms.streamselectorform import StreamSelectorForm
from openlp.plugins.media.forms.networkstreamselectorform import NetworkStreamSelectorForm
log = logging.getLogger(__name__)
@ -129,6 +130,13 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
text=device_stream_button_text,
tooltip=device_stream_button_tooltip,
triggers=self.on_open_device_stream)
network_stream_button_text = translate('MediaPlugin.MediaItem', 'Open network stream')
network_stream_button_tooltip = translate('MediaPlugin.MediaItem', 'Open network stream')
self.open_network_stream = self.toolbar.add_toolbar_action('open_network_stream',
icon=UiIcons().network_stream,
text=network_stream_button_text,
tooltip=network_stream_button_tooltip,
triggers=self.on_open_network_stream)
def generate_slide_data(self, service_item, *, item=None, remote=False, context=ServiceItemContext.Service,
**kwargs):
@ -165,9 +173,9 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
service_item.start_time = start
service_item.end_time = end
service_item.add_capability(ItemCapabilities.IsOptical)
elif filename.startswith('devicestream:'):
elif filename.startswith('devicestream:') or filename.startswith('networkstream:'):
# Special handling if the filename is a devicestream
(name, mrl, options) = parse_devicestream_path(filename)
(name, mrl, options) = parse_stream_path(filename)
service_item.processor = 'vlc'
service_item.add_from_command(filename, name, self.clapperboard)
service_item.title = name
@ -249,10 +257,13 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
item_name.setToolTip('{name}@{start}-{end}'.format(name=file_name,
start=format_milliseconds(start),
end=format_milliseconds(end)))
elif track_str.startswith('devicestream:'):
(name, mrl, options) = parse_devicestream_path(track_str)
elif track_str.startswith('devicestream:') or track_str.startswith('networkstream:'):
(name, mrl, options) = parse_stream_path(track_str)
item_name = QtWidgets.QListWidgetItem(name)
item_name.setIcon(UiIcons().device_stream)
if track_str.startswith('devicestream:'):
item_name.setIcon(UiIcons().device_stream)
else:
item_name.setIcon(UiIcons().network_stream)
item_name.setData(QtCore.Qt.UserRole, track)
item_name.setToolTip(mrl)
elif not os.path.exists(track):
@ -366,3 +377,32 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
file_paths.append(stream)
self.load_list([str(stream)])
self.settings.setValue(self.settings_section + '/media files', file_paths)
def on_open_network_stream(self):
"""
When the open network stream button is clicked, open the stream selector window.
"""
if get_vlc():
stream_selector_form = NetworkStreamSelectorForm(self.main_window, self.add_network_stream)
stream_selector_form.exec()
del stream_selector_form
else:
critical_error_message_box(translate('MediaPlugin.MediaItem', 'VLC is not available'),
translate('MediaPlugin.MediaItem', 'Network streaming support requires VLC.'))
def add_network_stream(self, stream):
"""
Add a network stream based clip to the mediamanager, called from stream_selector_form.
:param stream: The clip to add.
"""
file_paths = self.get_file_list()
# If the clip already is in the media list it isn't added and an error message is displayed.
if stream in file_paths:
critical_error_message_box(translate('MediaPlugin.MediaItem', 'Stream already saved'),
translate('MediaPlugin.MediaItem', 'This stream has already been saved'))
return
# Append the device stream string to the media list
file_paths.append(stream)
self.load_list([str(stream)])
self.settings.setValue(self.settings_section + '/media files', file_paths)