forked from openlp/openlp
Add support for network streams.
This commit is contained in:
parent
b51829d4d6
commit
127ee05ac3
@ -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
|
||||
|
@ -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'},
|
||||
|
@ -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]
|
||||
|
@ -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:
|
||||
|
@ -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'))
|
||||
|
81
openlp/plugins/media/forms/networkstreamselectorform.py
Normal file
81
openlp/plugins/media/forms/networkstreamselectorform.py
Normal 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(), '')
|
@ -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'))
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user