Merging trunk on 15/3/16

This commit is contained in:
suutari-olli 2016-03-15 21:14:40 +02:00
commit 87cdde1575
75 changed files with 61265 additions and 60638 deletions

View File

@ -44,3 +44,4 @@ __pycache__
cover
*.kdev4
coverage
tags

View File

@ -1 +1 @@
2.3.2
2.4

View File

@ -252,68 +252,56 @@ class Settings(QtCore.QSettings):
'shortcuts/blankScreen': [QtGui.QKeySequence(QtCore.Qt.Key_Period)],
'shortcuts/collapse': [QtGui.QKeySequence(QtCore.Qt.Key_Minus)],
'shortcuts/desktopScreen': [QtGui.QKeySequence(QtCore.Qt.Key_D)],
'shortcuts/delete': [QtGui.QKeySequence(QtGui.QKeySequence.Delete),
QtGui.QKeySequence(QtCore.Qt.Key_Delete)],
'shortcuts/delete': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
'shortcuts/down': [QtGui.QKeySequence(QtCore.Qt.Key_Down)],
'shortcuts/editSong': [],
'shortcuts/escapeItem': [QtGui.QKeySequence(QtCore.Qt.Key_Escape)],
'shortcuts/expand': [QtGui.QKeySequence(QtCore.Qt.Key_Plus)],
'shortcuts/exportThemeItem': [],
'shortcuts/fileNewItem': [QtGui.QKeySequence(QtGui.QKeySequence.New),
QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_N)],
'shortcuts/fileSaveAsItem': [QtGui.QKeySequence(QtGui.QKeySequence.SaveAs),
QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.SHIFT + QtCore.Qt.Key_S)],
'shortcuts/fileExitItem': [QtGui.QKeySequence(QtGui.QKeySequence.Quit),
QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_F4)],
'shortcuts/fileSaveItem': [QtGui.QKeySequence(QtGui.QKeySequence.Save),
QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_S)],
'shortcuts/fileOpenItem': [QtGui.QKeySequence(QtGui.QKeySequence.Open),
QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_O)],
'shortcuts/fileNewItem': [QtGui.QKeySequence(QtGui.QKeySequence.New)],
'shortcuts/fileSaveAsItem': [QtGui.QKeySequence(QtGui.QKeySequence.SaveAs)],
'shortcuts/fileExitItem': [QtGui.QKeySequence(QtGui.QKeySequence.Quit)],
'shortcuts/fileSaveItem': [QtGui.QKeySequence(QtGui.QKeySequence.Save)],
'shortcuts/fileOpenItem': [QtGui.QKeySequence(QtGui.QKeySequence.Open)],
'shortcuts/goLive': [],
'shortcuts/importThemeItem': [],
'shortcuts/importBibleItem': [],
'shortcuts/listViewBiblesDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete),
QtGui.QKeySequence(QtCore.Qt.Key_Delete)],
'shortcuts/listViewBiblesDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
'shortcuts/listViewBiblesPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
'shortcuts/listViewBiblesLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)],
'shortcuts/listViewBiblesServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus),
QtGui.QKeySequence(QtCore.Qt.Key_Equal)],
'shortcuts/listViewCustomDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete),
QtGui.QKeySequence(QtCore.Qt.Key_Delete)],
'shortcuts/listViewCustomDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
'shortcuts/listViewCustomPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
'shortcuts/listViewCustomLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)],
'shortcuts/listViewCustomServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus),
QtGui.QKeySequence(QtCore.Qt.Key_Equal)],
'shortcuts/listViewImagesDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete),
QtGui.QKeySequence(QtCore.Qt.Key_Delete)],
'shortcuts/listViewImagesDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
'shortcuts/listViewImagesPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
'shortcuts/listViewImagesLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)],
'shortcuts/listViewImagesServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus),
QtGui.QKeySequence(QtCore.Qt.Key_Equal)],
'shortcuts/listViewMediaDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete),
QtGui.QKeySequence(QtCore.Qt.Key_Delete)],
'shortcuts/listViewMediaDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
'shortcuts/listViewMediaPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
'shortcuts/listViewMediaLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)],
'shortcuts/listViewMediaServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus),
QtGui.QKeySequence(QtCore.Qt.Key_Equal)],
'shortcuts/listViewPresentationsDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete),
QtGui.QKeySequence(QtCore.Qt.Key_Delete)],
'shortcuts/listViewPresentationsDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
'shortcuts/listViewPresentationsPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
'shortcuts/listViewPresentationsLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)],
'shortcuts/listViewPresentationsServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus),
QtGui.QKeySequence(QtCore.Qt.Key_Equal)],
'shortcuts/listViewSongsDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete),
QtGui.QKeySequence(QtCore.Qt.Key_Delete)],
'shortcuts/listViewSongsDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
'shortcuts/listViewSongsPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
'shortcuts/listViewSongsLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
@ -337,8 +325,7 @@ class Settings(QtCore.QSettings):
'shortcuts/nextService': [QtGui.QKeySequence(QtCore.Qt.Key_Right)],
'shortcuts/newService': [],
'shortcuts/offlineHelpItem': [QtGui.QKeySequence(QtGui.QKeySequence.HelpContents)],
'shortcuts/onlineHelpItem': [QtGui.QKeySequence(QtGui.QKeySequence.HelpContents),
QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_F1)],
'shortcuts/onlineHelpItem': [QtGui.QKeySequence(QtGui.QKeySequence.HelpContents)],
'shortcuts/openService': [],
'shortcuts/saveService': [],
'shortcuts/previousItem_live': [QtGui.QKeySequence(QtCore.Qt.Key_Up),
@ -351,12 +338,10 @@ class Settings(QtCore.QSettings):
'shortcuts/previousService': [QtGui.QKeySequence(QtCore.Qt.Key_Left)],
'shortcuts/previousItem_preview': [QtGui.QKeySequence(QtCore.Qt.Key_Up),
QtGui.QKeySequence(QtCore.Qt.Key_PageUp)],
'shortcuts/printServiceItem': [QtGui.QKeySequence(QtGui.QKeySequence.Print),
QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_P)],
'shortcuts/printServiceItem': [QtGui.QKeySequence(QtGui.QKeySequence.Print)],
'shortcuts/songExportItem': [],
'shortcuts/songUsageStatus': [QtGui.QKeySequence(QtCore.Qt.Key_F4)],
'shortcuts/searchShortcut': [QtGui.QKeySequence(QtGui.QKeySequence.Find),
QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_F)],
'shortcuts/searchShortcut': [QtGui.QKeySequence(QtGui.QKeySequence.Find)],
'shortcuts/settingsShortcutsItem': [],
'shortcuts/settingsImportItem': [],
'shortcuts/settingsPluginListItem': [QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_F7)],

View File

@ -122,8 +122,8 @@ class UiStrings(object):
self.Projectors = translate('OpenLP.Ui', 'Projectors', 'Plural')
self.ReplaceBG = translate('OpenLP.Ui', 'Replace Background')
self.ReplaceLiveBG = translate('OpenLP.Ui', 'Replace live background.')
self.ReplaceLiveBGDisabled = translate('OpenLP.Ui', 'Replace live background is not available on this '
'platform in this version of OpenLP.')
self.ReplaceLiveBGDisabled = translate('OpenLP.Ui', 'Replace live background is not available when the WebKit '
'player is disabled.')
self.ResetBG = translate('OpenLP.Ui', 'Reset Background')
self.ResetLiveBG = translate('OpenLP.Ui', 'Reset live background.')
self.Seconds = translate('OpenLP.Ui', 's', 'The abbreviated unit for seconds')

View File

@ -515,7 +515,7 @@ class PJLink1(QTcpSocket):
self.socket_timer.start()
try:
self.projectorNetwork.emit(S_NETWORK_SENDING)
sent = self.write(out)
sent = self.write(out.encode('ascii'))
self.waitForBytesWritten(2000) # 2 seconds should be enough
if sent == -1:
# Network error?
@ -665,7 +665,15 @@ class PJLink1(QTcpSocket):
:param data: Class that projector supports.
"""
self.pjlink_class = data
# bug 1550891: Projector returns non-standard class response:
# : Expected: %1CLSS=1
# : Received: %1CLSS=Class 1
if len(data) > 1:
# Split non-standard information from response
clss = data.split()[-1]
else:
clss = data
self.pjlink_class = clss
log.debug('(%s) Setting pjlink_class for this projector to "%s"' % (self.ip, self.pjlink_class))
return

View File

@ -20,6 +20,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import re
from PyQt5 import QtGui, QtCore, QtWebKitWidgets
@ -441,7 +442,7 @@ class Renderer(OpenLPMixin, RegistryMixin, RegistryProperties):
previous_raw = line + line_end
continue
# Figure out how many words of the line will fit on screen as the line will not fit as a whole.
raw_words = Renderer.words_split(line)
raw_words = words_split(line)
html_words = list(map(expand_tags, raw_words))
previous_html, previous_raw = \
self._binary_chop(formatted, previous_html, previous_raw, html_words, raw_words, ' ', line_end)
@ -528,8 +529,7 @@ def words_split(line):
:param line: Line to be split
"""
# this parse we are to be wordy
line = line.replace('\n', ' ')
return line.split(' ')
return re.split('\s+', line)
def get_start_tags(raw_text):

View File

@ -86,12 +86,6 @@ class Display(QtWidgets.QGraphicsView):
super(Display, self).__init__()
self.controller = parent
self.screen = {}
# FIXME: On Mac OS X (tested on 10.7) the display screen is corrupt with
# OpenGL. Only white blank screen is shown on the 2nd monitor all the
# time. We need to investigate more how to use OpenGL properly on Mac OS
# X.
if not is_macosx() and not is_win():
self.setViewport(QtOpenGL.QGLWidget())
def setup(self):
"""
@ -332,6 +326,9 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
else:
self.setVisible(False)
self.setGeometry(self.screen['size'])
# Workaround for bug #1531319, should not be needed with PyQt 5.6.
if is_win():
self.shake_web_view()
def direct_image(self, path, background):
"""
@ -401,8 +398,17 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
# Wait for the fade to finish before geting the preview.
# Important otherwise preview will have incorrect text if at all!
if self.service_item.theme_data and self.service_item.theme_data.display_slide_transition:
# Workaround for bug #1531319, should not be needed with PyQt 5.6.
if is_win():
fade_shake_timer = QtCore.QTimer(self)
fade_shake_timer.setInterval(25)
fade_shake_timer.timeout.connect(self.shake_web_view)
fade_shake_timer.start()
while not self.frame.evaluateJavaScript('show_text_completed()'):
self.application.process_events()
# Workaround for bug #1531319, should not be needed with PyQt 5.6.
if is_win():
fade_shake_timer.stop()
# Wait for the webview to update before getting the preview.
# Important otherwise first preview will miss the background !
while not self.web_loaded:
@ -420,6 +426,9 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
self.setVisible(True)
else:
self.setVisible(True)
# Workaround for bug #1531319, should not be needed with PyQt 5.6.
if is_win():
self.shake_web_view()
return self.grab()
def build_html(self, service_item, image_path=''):
@ -499,6 +508,9 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
if self.isHidden():
self.setVisible(True)
self.web_view.setVisible(True)
# Workaround for bug #1531319, should not be needed with PyQt 5.6.
if is_win():
self.shake_web_view()
self.hide_mode = mode
def show_display(self):
@ -517,6 +529,9 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
# Trigger actions when display is active again.
if self.is_live:
Registry().execute('live_display_active')
# Workaround for bug #1531319, should not be needed with PyQt 5.6.
if is_win():
self.shake_web_view()
def _hide_mouse(self):
"""
@ -559,6 +574,13 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
if window_id == main_window_id:
self.main_window.raise_()
def shake_web_view(self):
"""
Resizes the web_view a bit to force an update. Workaround for bug #1531319, should not be needed with PyQt 5.6.
"""
self.web_view.setGeometry(0, 0, self.width(), self.height() - 1)
self.web_view.setGeometry(0, 0, self.width(), self.height())
class AudioPlayer(OpenLPMixin, QtCore.QObject):
"""
@ -576,6 +598,7 @@ class AudioPlayer(OpenLPMixin, QtCore.QObject):
self.player = QtMultimedia.QMediaPlayer()
self.playlist = QtMultimedia.QMediaPlaylist(self.player)
self.volume_slider = None
self.player.setPlaylist(self.playlist)
self.player.positionChanged.connect(self._on_position_changed)
def __del__(self):
@ -643,7 +666,7 @@ class AudioPlayer(OpenLPMixin, QtCore.QObject):
if not isinstance(file_names, list):
file_names = [file_names]
for file_name in file_names:
self.playlist.addMedia(QtCore.QUrl(file_name))
self.playlist.addMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file_name)))
def next(self):
"""

View File

@ -49,7 +49,7 @@ import functools
from inspect import getargspec
__version__ = "N/A"
build_date = "Thu Nov 5 23:41:43 2015"
build_date = "Mon Jan 25 19:40:05 2016"
# The libvlc doc states that filenames are expected to be in UTF8, do
# not rely on sys.getfilesystemencoding() which will be confused,
@ -425,20 +425,10 @@ class EventType(_Enum):
273: 'MediaPlayerLengthChanged',
274: 'MediaPlayerVout',
275: 'MediaPlayerScrambledChanged',
276: 'MediaPlayerESAdded',
277: 'MediaPlayerESDeleted',
278: 'MediaPlayerESSelected',
279: 'MediaPlayerCorked',
280: 'MediaPlayerUncorked',
281: 'MediaPlayerMuted',
282: 'MediaPlayerUnmuted',
283: 'MediaPlayerAudioVolume',
284: 'MediaPlayerAudioDevice',
0x200: 'MediaListItemAdded',
513: 'MediaListWillAddItem',
514: 'MediaListItemDeleted',
515: 'MediaListWillDeleteItem',
516: 'MediaListEndReached',
0x300: 'MediaListViewItemAdded',
769: 'MediaListViewWillAddItem',
770: 'MediaListViewItemDeleted',
@ -464,7 +454,6 @@ EventType.MediaDiscovererEnded = EventType(1281)
EventType.MediaDiscovererStarted = EventType(0x500)
EventType.MediaDurationChanged = EventType(2)
EventType.MediaFreed = EventType(4)
EventType.MediaListEndReached = EventType(516)
EventType.MediaListItemAdded = EventType(0x200)
EventType.MediaListItemDeleted = EventType(514)
EventType.MediaListPlayerNextItemSet = EventType(1025)
@ -478,20 +467,13 @@ EventType.MediaListWillAddItem = EventType(513)
EventType.MediaListWillDeleteItem = EventType(515)
EventType.MediaMetaChanged = EventType(0)
EventType.MediaParsedChanged = EventType(3)
EventType.MediaPlayerAudioDevice = EventType(284)
EventType.MediaPlayerAudioVolume = EventType(283)
EventType.MediaPlayerBackward = EventType(264)
EventType.MediaPlayerBuffering = EventType(259)
EventType.MediaPlayerCorked = EventType(279)
EventType.MediaPlayerESAdded = EventType(276)
EventType.MediaPlayerESDeleted = EventType(277)
EventType.MediaPlayerESSelected = EventType(278)
EventType.MediaPlayerEncounteredError = EventType(266)
EventType.MediaPlayerEndReached = EventType(265)
EventType.MediaPlayerForward = EventType(263)
EventType.MediaPlayerLengthChanged = EventType(273)
EventType.MediaPlayerMediaChanged = EventType(0x100)
EventType.MediaPlayerMuted = EventType(281)
EventType.MediaPlayerNothingSpecial = EventType(257)
EventType.MediaPlayerOpening = EventType(258)
EventType.MediaPlayerPausableChanged = EventType(270)
@ -504,8 +486,6 @@ EventType.MediaPlayerSnapshotTaken = EventType(272)
EventType.MediaPlayerStopped = EventType(262)
EventType.MediaPlayerTimeChanged = EventType(267)
EventType.MediaPlayerTitleChanged = EventType(271)
EventType.MediaPlayerUncorked = EventType(280)
EventType.MediaPlayerUnmuted = EventType(282)
EventType.MediaPlayerVout = EventType(274)
EventType.MediaStateChanged = EventType(5)
EventType.MediaSubItemAdded = EventType(1)
@ -549,19 +529,15 @@ class Meta(_Enum):
20: 'Episode',
21: 'ShowName',
22: 'Actors',
23: 'AlbumArtist',
24: 'DiscNumber',
}
Meta.Actors = Meta(22)
Meta.Album = Meta(4)
Meta.AlbumArtist = Meta(23)
Meta.Artist = Meta(1)
Meta.ArtworkURL = Meta(15)
Meta.Copyright = Meta(3)
Meta.Date = Meta(8)
Meta.Description = Meta(6)
Meta.Director = Meta(18)
Meta.DiscNumber = Meta(24)
Meta.EncodedBy = Meta(14)
Meta.Episode = Meta(20)
Meta.Genre = Meta(2)
@ -619,40 +595,6 @@ TrackType.text = TrackType(2)
TrackType.unknown = TrackType(-1)
TrackType.video = TrackType(1)
class MediaType(_Enum):
'''Media type
See libvlc_media_get_type.
'''
_enum_names_ = {
0: 'unknown',
1: 'file',
2: 'directory',
3: 'disc',
4: 'stream',
5: 'playlist',
}
MediaType.directory = MediaType(2)
MediaType.disc = MediaType(3)
MediaType.file = MediaType(1)
MediaType.playlist = MediaType(5)
MediaType.stream = MediaType(4)
MediaType.unknown = MediaType(0)
class MediaParseFlag(_Enum):
'''Parse flags used by libvlc_media_parse_with_options()
See libvlc_media_parse_with_options.
'''
_enum_names_ = {
0x00: 'local',
0x01: 'network',
0x02: 'local',
0x04: 'network',
}
MediaParseFlag.local = MediaParseFlag(0x00)
MediaParseFlag.local = MediaParseFlag(0x02)
MediaParseFlag.network = MediaParseFlag(0x01)
MediaParseFlag.network = MediaParseFlag(0x04)
class PlaybackMode(_Enum):
'''Defines playback modes for playlist.
'''
@ -823,7 +765,7 @@ class Callback(ctypes.c_void_p):
class LogCb(ctypes.c_void_p):
"""Callback prototype for LibVLC log message handler.
\param data data pointer as given to L{libvlc_log_set}()
\param level message level (@ref libvlc_log_level)
\param level message level (@ref enum libvlc_log_level)
\param ctx message context (meta-information about the message)
\param fmt printf() format string (as defined by ISO C11)
\param args variable argument list for the format
@ -832,49 +774,6 @@ class LogCb(ctypes.c_void_p):
variable arguments are only valid until the callback returns.
"""
pass
class MediaOpenCb(ctypes.c_void_p):
"""Callback prototype to open a custom bitstream input media.
The same media item can be opened multiple times. Each time, this callback
is invoked. It should allocate and initialize any instance-specific
resources, then store them in *datap. The instance resources can be freed
in the @ref libvlc_media_close_cb callback.
\param opaque private pointer as passed to L{libvlc_media_new_callbacks}()
\param datap storage space for a private data pointer [OUT]
\param sizep byte length of the bitstream or UINT64_MAX if unknown [OUT]
\note For convenience, *datap is initially NULL and *sizep is initially 0.
\return 0 on success, non-zero on error. In case of failure, the other
callbacks will not be invoked and any value stored in *datap and *sizep is
discarded.
"""
pass
class MediaReadCb(ctypes.c_void_p):
"""Callback prototype to read data from a custom bitstream input media.
\param opaque private pointer as set by the @ref libvlc_media_open_cb
callback
\param buf start address of the buffer to read data into
\param len bytes length of the buffer
\return strictly positive number of bytes read, 0 on end-of-stream,
or -1 on non-recoverable error
\note If no data is immediately available, then the callback should sleep.
\warning The application is responsible for avoiding deadlock situations.
In particular, the callback should return an error if playback is stopped;
if it does not return, then L{libvlc_media_player_stop}() will never return.
"""
pass
class MediaSeekCb(ctypes.c_void_p):
"""Callback prototype to seek a custom bitstream input media.
\param opaque private pointer as set by the @ref libvlc_media_open_cb
callback
\param offset absolute byte offset to seek to
\return 0 on success, -1 on error.
"""
pass
class MediaCloseCb(ctypes.c_void_p):
"""Callback prototype to close a custom bitstream input media.
\param opaque private pointer as set by the @ref libvlc_media_open_cb
callback
"""
pass
class VideoLockCb(ctypes.c_void_p):
"""Callback prototype to allocate and lock a picture buffer.
Whenever a new video frame needs to be decoded, the lock callback is
@ -932,7 +831,7 @@ the number of bytes per pixel multiplied by the pixel width.
Similarly, the number of scanlines must be bigger than of equal to
the pixel height.
Furthermore, we recommend that pitches and lines be multiple of 32
to not break assumptions that might be held by optimized code
to not break assumption that might be made by various optimizations
in the video decoders, video filters and/or video converters.
"""
pass
@ -1009,7 +908,7 @@ class CallbackDecorators(object):
LogCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, Log_ptr, ctypes.c_char_p, ctypes.c_void_p)
LogCb.__doc__ = '''Callback prototype for LibVLC log message handler.
\param data data pointer as given to L{libvlc_log_set}()
\param level message level (@ref libvlc_log_level)
\param level message level (@ref enum libvlc_log_level)
\param ctx message context (meta-information about the message)
\param fmt printf() format string (as defined by ISO C11)
\param args variable argument list for the format
@ -1017,45 +916,6 @@ class CallbackDecorators(object):
\warning The message context pointer, the format string parameters and the
variable arguments are only valid until the callback returns.
'''
MediaOpenCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_int), ctypes.c_void_p, ListPOINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.c_uint64))
MediaOpenCb.__doc__ = '''Callback prototype to open a custom bitstream input media.
The same media item can be opened multiple times. Each time, this callback
is invoked. It should allocate and initialize any instance-specific
resources, then store them in *datap. The instance resources can be freed
in the @ref libvlc_media_close_cb callback.
\param opaque private pointer as passed to L{libvlc_media_new_callbacks}()
\param datap storage space for a private data pointer [OUT]
\param sizep byte length of the bitstream or UINT64_MAX if unknown [OUT]
\note For convenience, *datap is initially NULL and *sizep is initially 0.
\return 0 on success, non-zero on error. In case of failure, the other
callbacks will not be invoked and any value stored in *datap and *sizep is
discarded.
'''
MediaReadCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_ssize_t), ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t)
MediaReadCb.__doc__ = '''Callback prototype to read data from a custom bitstream input media.
\param opaque private pointer as set by the @ref libvlc_media_open_cb
callback
\param buf start address of the buffer to read data into
\param len bytes length of the buffer
\return strictly positive number of bytes read, 0 on end-of-stream,
or -1 on non-recoverable error
\note If no data is immediately available, then the callback should sleep.
\warning The application is responsible for avoiding deadlock situations.
In particular, the callback should return an error if playback is stopped;
if it does not return, then L{libvlc_media_player_stop}() will never return.
'''
MediaSeekCb = ctypes.CFUNCTYPE(ctypes.POINTER(ctypes.c_int), ctypes.c_void_p, ctypes.c_uint64)
MediaSeekCb.__doc__ = '''Callback prototype to seek a custom bitstream input media.
\param opaque private pointer as set by the @ref libvlc_media_open_cb
callback
\param offset absolute byte offset to seek to
\return 0 on success, -1 on error.
'''
MediaCloseCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
MediaCloseCb.__doc__ = '''Callback prototype to close a custom bitstream input media.
\param opaque private pointer as set by the @ref libvlc_media_open_cb
callback
'''
VideoLockCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ListPOINTER(ctypes.c_void_p))
VideoLockCb.__doc__ = '''Callback prototype to allocate and lock a picture buffer.
Whenever a new video frame needs to be decoded, the lock callback is
@ -1110,7 +970,7 @@ the number of bytes per pixel multiplied by the pixel width.
Similarly, the number of scanlines must be bigger than of equal to
the pixel height.
Furthermore, we recommend that pitches and lines be multiple of 32
to not break assumptions that might be held by optimized code
to not break assumption that might be made by various optimizations
in the video decoders, video filters and/or video converters.
'''
VideoCleanupCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p)
@ -1760,19 +1620,6 @@ class Instance(_Ctype):
return libvlc_media_new_fd(self, fd)
def media_new_callbacks(self, open_cb, read_cb, seek_cb, close_cb, opaque):
'''Create a media with custom callbacks to read the data from.
@param open_cb: callback to open the custom bitstream input media.
@param read_cb: callback to read data (must not be None).
@param seek_cb: callback to seek, or None if seeking is not supported.
@param close_cb: callback to close the media, or None if unnecessary.
@param opaque: data pointer for the open callback.
@return: the newly created media or None on error @note If open_cb is None, the opaque pointer will be passed to read_cb, seek_cb and close_cb, and the stream size will be treated as unknown. @note The callbacks may be called asynchronously (from another thread). A single stream instance need not be reentrant. However the open_cb needs to be reentrant if the media is used by multiple player instances. @warning The callbacks may be used until all or any player instances that were supplied the media item are stopped. See L{media_release}.
@version: LibVLC 3.0.0 and later.
'''
return libvlc_media_new_callbacks(self, open_cb, read_cb, seek_cb, close_cb, opaque)
def media_new_as_node(self, psz_name):
'''Create a media as an empty node with a given name.
See L{media_release}.
@ -1782,22 +1629,12 @@ class Instance(_Ctype):
return libvlc_media_new_as_node(self, str_to_bytes(psz_name))
def media_discoverer_new(self, psz_name):
'''Create a media discoverer object by name.
After this object is created, you should attach to events in order to be
notified of the discoverer state.
You should also attach to media_list events in order to be notified of new
items discovered.
You need to call L{media_discoverer_start}() in order to start the
discovery.
See L{media_discoverer_media_list}
See L{media_discoverer_event_manager}
See L{media_discoverer_start}.
def media_discoverer_new_from_name(self, psz_name):
'''Discover media service by name.
@param psz_name: service name.
@return: media discover object or None in case of error.
@version: LibVLC 3.0.0 or later.
'''
return libvlc_media_discoverer_new(self, str_to_bytes(psz_name))
return libvlc_media_discoverer_new_from_name(self, str_to_bytes(psz_name))
def media_library_new(self):
@ -1809,7 +1646,7 @@ class Instance(_Ctype):
def audio_output_list_get(self):
'''Gets the list of available audio output modules.
@return: list of available audio outputs. It must be freed with In case of error, None is returned.
@return: list of available audio outputs. It must be freed it with In case of error, None is returned.
'''
return libvlc_audio_output_list_get(self)
@ -1825,7 +1662,7 @@ class Instance(_Ctype):
some circumstances. By default, it is recommended to not specify any
explicit audio device.
@param psz_aout: audio output name (as returned by L{audio_output_list_get}()).
@return: A None-terminated linked list of potential audio output devices. It must be freed with L{audio_output_device_list_release}().
@return: A None-terminated linked list of potential audio output devices. It must be freed it with L{audio_output_device_list_release}().
@version: LibVLC 2.1.0 or later.
'''
return libvlc_audio_output_device_list_get(self, str_to_bytes(aout))
@ -2255,7 +2092,7 @@ class Media(_Ctype):
def parse(self):
'''Parse a media.
This fetches (local) art, meta data and tracks information.
This fetches (local) meta data and tracks information.
The method is synchronous.
See L{parse_async}
See L{get_meta}
@ -2266,7 +2103,7 @@ class Media(_Ctype):
def parse_async(self):
'''Parse a media.
This fetches (local) art, meta data and tracks information.
This fetches (local) meta data and tracks information.
The method is the asynchronous of L{parse}().
To track when this is over you can listen to libvlc_MediaParsedChanged
event. However if the media was already parsed you will not receive this
@ -2279,27 +2116,6 @@ class Media(_Ctype):
return libvlc_media_parse_async(self)
def parse_with_options(self, parse_flag):
'''Parse the media asynchronously with options.
This fetches (local or network) art, meta data and/or tracks information.
This method is the extended version of L{parse_async}().
To track when this is over you can listen to libvlc_MediaParsedChanged
event. However if this functions returns an error, you will not receive this
event.
It uses a flag to specify parse options (see libvlc_media_parse_flag_t). All
these flags can be combined. By default, media is parsed if it's a local
file.
See libvlc_MediaParsedChanged
See L{get_meta}
See L{tracks_get}
See libvlc_media_parse_flag_t.
@param parse_flag: parse options:
@return: -1 in case of error, 0 otherwise.
@version: LibVLC 3.0.0 or later.
'''
return libvlc_media_parse_with_options(self, parse_flag)
def is_parsed(self):
'''Get Parsed status for media descriptor object.
See libvlc_MediaParsedChanged.
@ -2325,14 +2141,6 @@ class Media(_Ctype):
return libvlc_media_get_user_data(self)
def get_type(self):
'''Get the media type of the media descriptor object.
@return: media type.
@version: LibVLC 3.0.0 and later. See libvlc_media_type_t.
'''
return libvlc_media_get_type(self)
def player_new_from_media(self):
'''Create a Media Player object from a Media.
@return: a new media player object, or None on error.
@ -2348,25 +2156,6 @@ class MediaDiscoverer(_Ctype):
'''
return _Constructor(cls, ptr)
def start(self):
'''Start media discovery.
To stop it, call L{stop}() or
L{release}() directly.
See L{stop}.
@return: -1 in case of error, 0 otherwise.
@version: LibVLC 3.0.0 or later.
'''
return libvlc_media_discoverer_start(self)
def stop(self):
'''Stop media discovery.
See L{start}.
@version: LibVLC 3.0.0 or later.
'''
return libvlc_media_discoverer_stop(self)
def release(self):
'''Release media discover object. If the reference count reaches 0, then
the object will be released.
@ -2647,13 +2436,6 @@ class MediaListPlayer(_Ctype):
return libvlc_media_list_player_set_media_player(self, p_mi)
def get_media_player(self):
'''Get media player of the media_list_player instance.
@return: media player instance @note the caller is responsible for releasing the returned instance.
'''
return libvlc_media_list_player_get_media_player(self)
def set_media_list(self, p_mlist):
'''Set the media list associated with the player.
@param p_mlist: list of media.
@ -3008,7 +2790,7 @@ class MediaPlayer(_Ctype):
Use the vout called "macosx".
The drawable is an NSObject that follow the VLCOpenGLVideoViewEmbedding
protocol:
@code.m
@begincode
\@protocol VLCOpenGLVideoViewEmbedding <NSObject>
- (void)addVoutSubview:(NSView *)view;
- (void)removeVoutSubview:(NSView *)view;
@ -3017,7 +2799,7 @@ class MediaPlayer(_Ctype):
Or it can be an NSView object.
If you want to use it along with Qt4 see the QMacCocoaViewContainer. Then
the following code should work:
@code.mm
@begincode
NSView *video = [[NSView alloc] init];
QMacCocoaViewContainer *container = new QMacCocoaViewContainer(video, parent);
@ -3039,36 +2821,29 @@ class MediaPlayer(_Ctype):
def set_agl(self, drawable):
'''\deprecated Use L{set_nsobject} instead.
'''Set the agl handler where the media player should render its video output.
@param drawable: the agl handler.
'''
return libvlc_media_player_set_agl(self, drawable)
def get_agl(self):
'''\deprecated Use L{get_nsobject} instead.
'''Get the agl handler previously set with L{set_agl}().
@return: the agl handler or 0 if none where set.
'''
return libvlc_media_player_get_agl(self)
def set_xwindow(self, drawable):
'''Set an X Window System drawable where the media player should render its
video output. The call takes effect when the playback starts. If it is
already started, it might need to be stopped before changes apply.
If LibVLC was built without X11 output support, then this function has no
effects.
By default, LibVLC will capture input events on the video rendering area.
Use L{video_set_mouse_input}() and L{video_set_key_input}() to
disable that and deliver events to the parent window / to the application
instead. By design, the X11 protocol delivers input events to only one
recipient.
@warning
The application must call the XInitThreads() function from Xlib before
L{new}(), and before any call to XOpenDisplay() directly or via any
other library. Failure to call XInitThreads() will seriously impede LibVLC
performance. Calling XOpenDisplay() before XInitThreads() will eventually
crash the process. That is a limitation of Xlib.
@param drawable: X11 window ID @note The specified identifier must correspond to an existing Input/Output class X11 window. Pixmaps are B{not} currently supported. The default X11 server is assumed, i.e. that specified in the DISPLAY environment variable. @warning LibVLC can deal with invalid X11 handle errors, however some display drivers (EGL, GLX, VA and/or VDPAU) can unfortunately not. Thus the window handle must remain valid until playback is stopped, otherwise the process may abort or crash.
@bug No more than one window handle per media player instance can be specified. If the media has multiple simultaneously active video tracks, extra tracks will be rendered into external windows beyond the control of the application.
video output. If LibVLC was built without X11 output support, then this has
no effects.
The specified identifier must correspond to an existing Input/Output class
X11 window. Pixmaps are B{not} supported. The caller shall ensure that
the X11 server is the same as the one the VLC instance has been configured
with. This function must be called before video playback is started;
otherwise it will only take effect after playback stop and restart.
@param drawable: the ID of the X window.
'''
return libvlc_media_player_set_xwindow(self, drawable)
@ -3092,15 +2867,6 @@ class MediaPlayer(_Ctype):
return libvlc_media_player_get_hwnd(self)
def set_android_context(self, p_jvm, p_awindow_handler):
'''Set the android context.
@param p_jvm: the Java VM of the android process.
@param awindow_handler: org.videolan.libvlc.IAWindowNativeHandler jobject implemented by the org.videolan.libvlc.MediaPlayer class from the libvlc-android project.
@version: LibVLC 3.0.0 and later.
'''
return libvlc_media_player_set_android_context(self, p_jvm, p_awindow_handler)
def audio_set_callbacks(self, play, pause, resume, flush, drain, opaque):
'''Set callbacks and private data for decoded audio.
Use L{audio_set_format}() or L{audio_set_format_callbacks}()
@ -3668,7 +3434,7 @@ class MediaPlayer(_Ctype):
@warning: Some audio output devices in the list might not actually work in
some circumstances. By default, it is recommended to not specify any
explicit audio device.
@return: A None-terminated linked list of potential audio output devices. It must be freed with L{audio_output_device_list_release}().
@return: A None-terminated linked list of potential audio output devices. It must be freed it with L{audio_output_device_list_release}().
@version: LibVLC 2.2.0 or later.
'''
return libvlc_audio_output_device_enum(self)
@ -3702,24 +3468,6 @@ class MediaPlayer(_Ctype):
return libvlc_audio_output_device_set(self, str_to_bytes(module), str_to_bytes(device_id))
def audio_output_device_get(self):
'''Get the current audio output device identifier.
This complements L{audio_output_device_set}().
@warning: The initial value for the current audio output device identifier
may not be set or may be some unknown value. A LibVLC application should
compare this value against the known device identifiers (e.g. those that
were previously retrieved by a call to L{audio_output_device_enum} or
L{audio_output_device_list_get}) to find the current audio output device.
It is possible that the selected audio output device changes (an external
change) without a call to L{audio_output_device_set}. That may make this
method unsuitable to use if a LibVLC application is attempting to track
dynamic audio device changes as they happen.
@return: the current audio output device identifier None if no device is selected or in case of error (the result must be released with free() or L{free}()).
@version: LibVLC 3.0.0 or later.
'''
return libvlc_audio_output_device_get(self)
def audio_toggle_mute(self):
'''Toggle mute status.
'''
@ -3872,27 +3620,6 @@ def libvlc_new(argc, argv):
'''Create and initialize a libvlc instance.
This functions accept a list of "command line" arguments similar to the
main(). These arguments affect the LibVLC instance default configuration.
@note
LibVLC may create threads. Therefore, any thread-unsafe process
initialization must be performed before calling L{libvlc_new}(). In particular
and where applicable:
- setlocale() and textdomain(),
- setenv(), unsetenv() and putenv(),
- with the X11 display system, XInitThreads()
(see also L{libvlc_media_player_set_xwindow}()) and
- on Microsoft Windows, SetErrorMode().
- sigprocmask() shall never be invoked; pthread_sigmask() can be used.
On POSIX systems, the SIGCHLD signal must B{not} be ignored, i.e. the
signal handler must set to SIG_DFL or a function pointer, not SIG_IGN.
Also while LibVLC is active, the wait() function shall not be called, and
any call to waitpid() shall use a strictly positive value for the first
parameter (i.e. the PID). Failure to follow those rules may lead to a
deadlock or a busy loop.
Also on POSIX systems, it is recommended that the SIGPIPE signal be blocked,
even if it is not, in principles, necessary.
On Microsoft Windows Vista/2008, the process error mode
SEM_FAILCRITICALERRORS flag B{must} with the SetErrorMode() function
before using LibVLC. On later versions, it is optional and unnecessary.
@param argc: the number of arguments (should be 0).
@param argv: list of arguments (should be None).
@return: the libvlc instance or None in case of error.
@ -4202,22 +3929,6 @@ def libvlc_media_new_fd(p_instance, fd):
ctypes.c_void_p, Instance, ctypes.c_int)
return f(p_instance, fd)
def libvlc_media_new_callbacks(instance, open_cb, read_cb, seek_cb, close_cb, opaque):
'''Create a media with custom callbacks to read the data from.
@param instance: LibVLC instance.
@param open_cb: callback to open the custom bitstream input media.
@param read_cb: callback to read data (must not be None).
@param seek_cb: callback to seek, or None if seeking is not supported.
@param close_cb: callback to close the media, or None if unnecessary.
@param opaque: data pointer for the open callback.
@return: the newly created media or None on error @note If open_cb is None, the opaque pointer will be passed to read_cb, seek_cb and close_cb, and the stream size will be treated as unknown. @note The callbacks may be called asynchronously (from another thread). A single stream instance need not be reentrant. However the open_cb needs to be reentrant if the media is used by multiple player instances. @warning The callbacks may be used until all or any player instances that were supplied the media item are stopped. See L{libvlc_media_release}.
@version: LibVLC 3.0.0 and later.
'''
f = _Cfunctions.get('libvlc_media_new_callbacks', None) or \
_Cfunction('libvlc_media_new_callbacks', ((1,), (1,), (1,), (1,), (1,), (1,),), class_result(Media),
ctypes.c_void_p, Instance, MediaOpenCb, MediaReadCb, MediaSeekCb, MediaCloseCb, ctypes.c_void_p)
return f(instance, open_cb, read_cb, seek_cb, close_cb, opaque)
def libvlc_media_new_as_node(p_instance, psz_name):
'''Create a media as an empty node with a given name.
See L{libvlc_media_release}.
@ -4413,7 +4124,7 @@ def libvlc_media_get_duration(p_md):
def libvlc_media_parse(p_md):
'''Parse a media.
This fetches (local) art, meta data and tracks information.
This fetches (local) meta data and tracks information.
The method is synchronous.
See L{libvlc_media_parse_async}
See L{libvlc_media_get_meta}
@ -4427,7 +4138,7 @@ def libvlc_media_parse(p_md):
def libvlc_media_parse_async(p_md):
'''Parse a media.
This fetches (local) art, meta data and tracks information.
This fetches (local) meta data and tracks information.
The method is the asynchronous of L{libvlc_media_parse}().
To track when this is over you can listen to libvlc_MediaParsedChanged
event. However if the media was already parsed you will not receive this
@ -4443,30 +4154,6 @@ def libvlc_media_parse_async(p_md):
None, Media)
return f(p_md)
def libvlc_media_parse_with_options(p_md, parse_flag):
'''Parse the media asynchronously with options.
This fetches (local or network) art, meta data and/or tracks information.
This method is the extended version of L{libvlc_media_parse_async}().
To track when this is over you can listen to libvlc_MediaParsedChanged
event. However if this functions returns an error, you will not receive this
event.
It uses a flag to specify parse options (see libvlc_media_parse_flag_t). All
these flags can be combined. By default, media is parsed if it's a local
file.
See libvlc_MediaParsedChanged
See L{libvlc_media_get_meta}
See L{libvlc_media_tracks_get}
See libvlc_media_parse_flag_t.
@param p_md: media descriptor object.
@param parse_flag: parse options:
@return: -1 in case of error, 0 otherwise.
@version: LibVLC 3.0.0 or later.
'''
f = _Cfunctions.get('libvlc_media_parse_with_options', None) or \
_Cfunction('libvlc_media_parse_with_options', ((1,), (1,),), None,
ctypes.c_int, Media, MediaParseFlag)
return f(p_md, parse_flag)
def libvlc_media_is_parsed(p_md):
'''Get Parsed status for media descriptor object.
See libvlc_MediaParsedChanged.
@ -4516,18 +4203,6 @@ def libvlc_media_tracks_get(p_md, tracks):
ctypes.c_uint, Media, ctypes.POINTER(ctypes.POINTER(MediaTrack)))
return f(p_md, tracks)
def libvlc_media_get_codec_description(i_type, i_codec):
'''Get codec description from media elementary stream.
@param i_type: i_type from L{MediaTrack}.
@param i_codec: i_codec or i_original_fourcc from L{MediaTrack}.
@return: codec description.
@version: LibVLC 3.0.0 and later. See L{MediaTrack}.
'''
f = _Cfunctions.get('libvlc_media_get_codec_description', None) or \
_Cfunction('libvlc_media_get_codec_description', ((1,), (1,),), None,
ctypes.c_char_p, TrackType, ctypes.c_uint32)
return f(i_type, i_codec)
def libvlc_media_tracks_release(p_tracks, i_count):
'''Release media descriptor's elementary streams description array.
@param p_tracks: tracks info array to release.
@ -4539,63 +4214,17 @@ def libvlc_media_tracks_release(p_tracks, i_count):
None, ctypes.POINTER(MediaTrack), ctypes.c_uint)
return f(p_tracks, i_count)
def libvlc_media_get_type(p_md):
'''Get the media type of the media descriptor object.
@param p_md: media descriptor object.
@return: media type.
@version: LibVLC 3.0.0 and later. See libvlc_media_type_t.
'''
f = _Cfunctions.get('libvlc_media_get_type', None) or \
_Cfunction('libvlc_media_get_type', ((1,),), None,
MediaType, Media)
return f(p_md)
def libvlc_media_discoverer_new(p_inst, psz_name):
'''Create a media discoverer object by name.
After this object is created, you should attach to events in order to be
notified of the discoverer state.
You should also attach to media_list events in order to be notified of new
items discovered.
You need to call L{libvlc_media_discoverer_start}() in order to start the
discovery.
See L{libvlc_media_discoverer_media_list}
See L{libvlc_media_discoverer_event_manager}
See L{libvlc_media_discoverer_start}.
def libvlc_media_discoverer_new_from_name(p_inst, psz_name):
'''Discover media service by name.
@param p_inst: libvlc instance.
@param psz_name: service name.
@return: media discover object or None in case of error.
@version: LibVLC 3.0.0 or later.
'''
f = _Cfunctions.get('libvlc_media_discoverer_new', None) or \
_Cfunction('libvlc_media_discoverer_new', ((1,), (1,),), class_result(MediaDiscoverer),
f = _Cfunctions.get('libvlc_media_discoverer_new_from_name', None) or \
_Cfunction('libvlc_media_discoverer_new_from_name', ((1,), (1,),), class_result(MediaDiscoverer),
ctypes.c_void_p, Instance, ctypes.c_char_p)
return f(p_inst, psz_name)
def libvlc_media_discoverer_start(p_mdis):
'''Start media discovery.
To stop it, call L{libvlc_media_discoverer_stop}() or
L{libvlc_media_discoverer_release}() directly.
See L{libvlc_media_discoverer_stop}.
@param p_mdis: media discover object.
@return: -1 in case of error, 0 otherwise.
@version: LibVLC 3.0.0 or later.
'''
f = _Cfunctions.get('libvlc_media_discoverer_start', None) or \
_Cfunction('libvlc_media_discoverer_start', ((1,),), None,
ctypes.c_int, MediaDiscoverer)
return f(p_mdis)
def libvlc_media_discoverer_stop(p_mdis):
'''Stop media discovery.
See L{libvlc_media_discoverer_start}.
@param p_mdis: media discover object.
@version: LibVLC 3.0.0 or later.
'''
f = _Cfunctions.get('libvlc_media_discoverer_stop', None) or \
_Cfunction('libvlc_media_discoverer_stop', ((1,),), None,
None, MediaDiscoverer)
return f(p_mdis)
def libvlc_media_discoverer_release(p_mdis):
'''Release media discover object. If the reference count reaches 0, then
the object will be released.
@ -4916,16 +4545,6 @@ def libvlc_media_list_player_set_media_player(p_mlp, p_mi):
None, MediaListPlayer, MediaPlayer)
return f(p_mlp, p_mi)
def libvlc_media_list_player_get_media_player(p_mlp):
'''Get media player of the media_list_player instance.
@param p_mlp: media list player instance.
@return: media player instance @note the caller is responsible for releasing the returned instance.
'''
f = _Cfunctions.get('libvlc_media_list_player_get_media_player', None) or \
_Cfunction('libvlc_media_list_player_get_media_player', ((1,),), class_result(MediaPlayer),
ctypes.c_void_p, MediaListPlayer)
return f(p_mlp)
def libvlc_media_list_player_set_media_list(p_mlp, p_mlist):
'''Set the media list associated with the player.
@param p_mlp: media list player instance.
@ -5210,7 +4829,7 @@ def libvlc_media_player_set_nsobject(p_mi, drawable):
Use the vout called "macosx".
The drawable is an NSObject that follow the VLCOpenGLVideoViewEmbedding
protocol:
@code.m
@begincode
\@protocol VLCOpenGLVideoViewEmbedding <NSObject>
- (void)addVoutSubview:(NSView *)view;
- (void)removeVoutSubview:(NSView *)view;
@ -5219,7 +4838,7 @@ def libvlc_media_player_set_nsobject(p_mi, drawable):
Or it can be an NSView object.
If you want to use it along with Qt4 see the QMacCocoaViewContainer. Then
the following code should work:
@code.mm
@begincode
NSView *video = [[NSView alloc] init];
QMacCocoaViewContainer *container = new QMacCocoaViewContainer(video, parent);
@ -5247,7 +4866,9 @@ def libvlc_media_player_get_nsobject(p_mi):
return f(p_mi)
def libvlc_media_player_set_agl(p_mi, drawable):
'''\deprecated Use L{libvlc_media_player_set_nsobject} instead.
'''Set the agl handler where the media player should render its video output.
@param p_mi: the Media Player.
@param drawable: the agl handler.
'''
f = _Cfunctions.get('libvlc_media_player_set_agl', None) or \
_Cfunction('libvlc_media_player_set_agl', ((1,), (1,),), None,
@ -5255,7 +4876,9 @@ def libvlc_media_player_set_agl(p_mi, drawable):
return f(p_mi, drawable)
def libvlc_media_player_get_agl(p_mi):
'''\deprecated Use L{libvlc_media_player_get_nsobject} instead.
'''Get the agl handler previously set with L{libvlc_media_player_set_agl}().
@param p_mi: the Media Player.
@return: the agl handler or 0 if none where set.
'''
f = _Cfunctions.get('libvlc_media_player_get_agl', None) or \
_Cfunction('libvlc_media_player_get_agl', ((1,),), None,
@ -5264,24 +4887,15 @@ def libvlc_media_player_get_agl(p_mi):
def libvlc_media_player_set_xwindow(p_mi, drawable):
'''Set an X Window System drawable where the media player should render its
video output. The call takes effect when the playback starts. If it is
already started, it might need to be stopped before changes apply.
If LibVLC was built without X11 output support, then this function has no
effects.
By default, LibVLC will capture input events on the video rendering area.
Use L{libvlc_video_set_mouse_input}() and L{libvlc_video_set_key_input}() to
disable that and deliver events to the parent window / to the application
instead. By design, the X11 protocol delivers input events to only one
recipient.
@warning
The application must call the XInitThreads() function from Xlib before
L{libvlc_new}(), and before any call to XOpenDisplay() directly or via any
other library. Failure to call XInitThreads() will seriously impede LibVLC
performance. Calling XOpenDisplay() before XInitThreads() will eventually
crash the process. That is a limitation of Xlib.
@param p_mi: media player.
@param drawable: X11 window ID @note The specified identifier must correspond to an existing Input/Output class X11 window. Pixmaps are B{not} currently supported. The default X11 server is assumed, i.e. that specified in the DISPLAY environment variable. @warning LibVLC can deal with invalid X11 handle errors, however some display drivers (EGL, GLX, VA and/or VDPAU) can unfortunately not. Thus the window handle must remain valid until playback is stopped, otherwise the process may abort or crash.
@bug No more than one window handle per media player instance can be specified. If the media has multiple simultaneously active video tracks, extra tracks will be rendered into external windows beyond the control of the application.
video output. If LibVLC was built without X11 output support, then this has
no effects.
The specified identifier must correspond to an existing Input/Output class
X11 window. Pixmaps are B{not} supported. The caller shall ensure that
the X11 server is the same as the one the VLC instance has been configured
with. This function must be called before video playback is started;
otherwise it will only take effect after playback stop and restart.
@param p_mi: the Media Player.
@param drawable: the ID of the X window.
'''
f = _Cfunctions.get('libvlc_media_player_set_xwindow', None) or \
_Cfunction('libvlc_media_player_set_xwindow', ((1,), (1,),), None,
@ -5325,18 +4939,6 @@ def libvlc_media_player_get_hwnd(p_mi):
ctypes.c_void_p, MediaPlayer)
return f(p_mi)
def libvlc_media_player_set_android_context(p_mi, p_jvm, p_awindow_handler):
'''Set the android context.
@param p_mi: the media player.
@param p_jvm: the Java VM of the android process.
@param awindow_handler: org.videolan.libvlc.IAWindowNativeHandler jobject implemented by the org.videolan.libvlc.MediaPlayer class from the libvlc-android project.
@version: LibVLC 3.0.0 and later.
'''
f = _Cfunctions.get('libvlc_media_player_set_android_context', None) or \
_Cfunction('libvlc_media_player_set_android_context', ((1,), (1,), (1,),), None,
None, MediaPlayer, ctypes.c_void_p, ctypes.c_void_p)
return f(p_mi, p_jvm, p_awindow_handler)
def libvlc_audio_set_callbacks(mp, play, pause, resume, flush, drain, opaque):
'''Set callbacks and private data for decoded audio.
Use L{libvlc_audio_set_format}() or L{libvlc_audio_set_format_callbacks}()
@ -5842,7 +5444,7 @@ def libvlc_video_get_spu_count(p_mi):
def libvlc_video_get_spu_description(p_mi):
'''Get the description of available video subtitles.
@param p_mi: the media player.
@return: list containing description of available video subtitles. It must be freed with L{libvlc_track_description_list_release}().
@return: list containing description of available video subtitles.
'''
f = _Cfunctions.get('libvlc_video_get_spu_description', None) or \
_Cfunction('libvlc_video_get_spu_description', ((1,),), None,
@ -5898,52 +5500,26 @@ def libvlc_video_set_spu_delay(p_mi, i_delay):
ctypes.c_int, MediaPlayer, ctypes.c_int64)
return f(p_mi, i_delay)
def libvlc_media_player_get_full_title_descriptions(p_mi, titles):
'''Get the full description of available titles.
def libvlc_video_get_title_description(p_mi):
'''Get the description of available titles.
@param p_mi: the media player.
@param address: to store an allocated array of title descriptions descriptions (must be freed with L{libvlc_title_descriptions_release}() by the caller) [OUT].
@return: the number of titles (-1 on error).
@version: LibVLC 3.0.0 and later.
@return: list containing description of available titles.
'''
f = _Cfunctions.get('libvlc_media_player_get_full_title_descriptions', None) or \
_Cfunction('libvlc_media_player_get_full_title_descriptions', ((1,), (1,),), None,
ctypes.c_int, MediaPlayer, ctypes.POINTER(ctypes.POINTER(TitleDescription)))
return f(p_mi, titles)
f = _Cfunctions.get('libvlc_video_get_title_description', None) or \
_Cfunction('libvlc_video_get_title_description', ((1,),), None,
ctypes.POINTER(TrackDescription), MediaPlayer)
return f(p_mi)
def libvlc_title_descriptions_release(p_titles, i_count):
'''Release a title description.
@param title: description array to release.
@param number: of title descriptions to release.
@version: LibVLC 3.0.0 and later.
'''
f = _Cfunctions.get('libvlc_title_descriptions_release', None) or \
_Cfunction('libvlc_title_descriptions_release', ((1,), (1,),), None,
None, ctypes.POINTER(TitleDescription), ctypes.c_uint)
return f(p_titles, i_count)
def libvlc_media_player_get_full_chapter_descriptions(p_mi, i_chapters_of_title, pp_chapters):
'''Get the full description of available chapters.
def libvlc_video_get_chapter_description(p_mi, i_title):
'''Get the description of available chapters for specific title.
@param p_mi: the media player.
@param index: of the title to query for chapters (uses current title if set to -1).
@param address: to store an allocated array of chapter descriptions descriptions (must be freed with L{libvlc_chapter_descriptions_release}() by the caller) [OUT].
@return: the number of chapters (-1 on error).
@version: LibVLC 3.0.0 and later.
@param i_title: selected title.
@return: list containing description of available chapter for title i_title.
'''
f = _Cfunctions.get('libvlc_media_player_get_full_chapter_descriptions', None) or \
_Cfunction('libvlc_media_player_get_full_chapter_descriptions', ((1,), (1,), (1,),), None,
ctypes.c_int, MediaPlayer, ctypes.c_int, ctypes.POINTER(ctypes.POINTER(ChapterDescription)))
return f(p_mi, i_chapters_of_title, pp_chapters)
def libvlc_chapter_descriptions_release(p_chapters, i_count):
'''Release a chapter description.
@param chapter: description array to release.
@param number: of chapter descriptions to release.
@version: LibVLC 3.0.0 and later.
'''
f = _Cfunctions.get('libvlc_chapter_descriptions_release', None) or \
_Cfunction('libvlc_chapter_descriptions_release', ((1,), (1,),), None,
None, ctypes.POINTER(ChapterDescription), ctypes.c_uint)
return f(p_chapters, i_count)
f = _Cfunctions.get('libvlc_video_get_chapter_description', None) or \
_Cfunction('libvlc_video_get_chapter_description', ((1,), (1,),), None,
ctypes.POINTER(TrackDescription), MediaPlayer, ctypes.c_int)
return f(p_mi, i_title)
def libvlc_video_get_crop_geometry(p_mi):
'''Get current crop filter geometry.
@ -6007,7 +5583,7 @@ def libvlc_video_get_track_count(p_mi):
def libvlc_video_get_track_description(p_mi):
'''Get the description of available video tracks.
@param p_mi: media player.
@return: list with description of available video tracks, or None on error. It must be freed with L{libvlc_track_description_list_release}().
@return: list with description of available video tracks, or None on error.
'''
f = _Cfunctions.get('libvlc_video_get_track_description', None) or \
_Cfunction('libvlc_video_get_track_description', ((1,),), None,
@ -6194,7 +5770,7 @@ def libvlc_video_set_adjust_float(p_mi, option, value):
def libvlc_audio_output_list_get(p_instance):
'''Gets the list of available audio output modules.
@param p_instance: libvlc instance.
@return: list of available audio outputs. It must be freed with In case of error, None is returned.
@return: list of available audio outputs. It must be freed it with In case of error, None is returned.
'''
f = _Cfunctions.get('libvlc_audio_output_list_get', None) or \
_Cfunction('libvlc_audio_output_list_get', ((1,),), None,
@ -6233,7 +5809,7 @@ def libvlc_audio_output_device_enum(mp):
some circumstances. By default, it is recommended to not specify any
explicit audio device.
@param mp: media player.
@return: A None-terminated linked list of potential audio output devices. It must be freed with L{libvlc_audio_output_device_list_release}().
@return: A None-terminated linked list of potential audio output devices. It must be freed it with L{libvlc_audio_output_device_list_release}().
@version: LibVLC 2.2.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_output_device_enum', None) or \
@ -6253,7 +5829,7 @@ def libvlc_audio_output_device_list_get(p_instance, aout):
explicit audio device.
@param p_instance: libvlc instance.
@param psz_aout: audio output name (as returned by L{libvlc_audio_output_list_get}()).
@return: A None-terminated linked list of potential audio output devices. It must be freed with L{libvlc_audio_output_device_list_release}().
@return: A None-terminated linked list of potential audio output devices. It must be freed it with L{libvlc_audio_output_device_list_release}().
@version: LibVLC 2.1.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_output_device_list_get', None) or \
@ -6302,27 +5878,6 @@ def libvlc_audio_output_device_set(mp, module, device_id):
None, MediaPlayer, ctypes.c_char_p, ctypes.c_char_p)
return f(mp, module, device_id)
def libvlc_audio_output_device_get(mp):
'''Get the current audio output device identifier.
This complements L{libvlc_audio_output_device_set}().
@warning: The initial value for the current audio output device identifier
may not be set or may be some unknown value. A LibVLC application should
compare this value against the known device identifiers (e.g. those that
were previously retrieved by a call to L{libvlc_audio_output_device_enum} or
L{libvlc_audio_output_device_list_get}) to find the current audio output device.
It is possible that the selected audio output device changes (an external
change) without a call to L{libvlc_audio_output_device_set}. That may make this
method unsuitable to use if a LibVLC application is attempting to track
dynamic audio device changes as they happen.
@param mp: media player.
@return: the current audio output device identifier None if no device is selected or in case of error (the result must be released with free() or L{libvlc_free}()).
@version: LibVLC 3.0.0 or later.
'''
f = _Cfunctions.get('libvlc_audio_output_device_get', None) or \
_Cfunction('libvlc_audio_output_device_get', ((1,),), None,
ctypes.c_char_p, MediaPlayer)
return f(mp)
def libvlc_audio_toggle_mute(p_mi):
'''Toggle mute status.
@param p_mi: media player @warning Toggling mute atomically is not always possible: On some platforms, other processes can mute the VLC audio playback stream asynchronously. Thus, there is a small race condition where toggling will not work. See also the limitations of L{libvlc_audio_set_mute}().
@ -6386,7 +5941,7 @@ def libvlc_audio_get_track_count(p_mi):
def libvlc_audio_get_track_description(p_mi):
'''Get the description of available audio tracks.
@param p_mi: media player.
@return: list with description of available audio tracks, or None. It must be freed with L{libvlc_track_description_list_release}().
@return: list with description of available audio tracks, or None.
'''
f = _Cfunctions.get('libvlc_audio_get_track_description', None) or \
_Cfunction('libvlc_audio_get_track_description', ((1,),), None,
@ -6939,7 +6494,7 @@ def libvlc_vlm_get_event_manager(p_instance):
# libvlc_printerr
# libvlc_set_exit_handler
# 31 function(s) not wrapped as methods:
# 28 function(s) not wrapped as methods:
# libvlc_audio_equalizer_get_amp_at_index
# libvlc_audio_equalizer_get_band_count
# libvlc_audio_equalizer_get_band_frequency
@ -6953,7 +6508,6 @@ def libvlc_vlm_get_event_manager(p_instance):
# libvlc_audio_equalizer_set_preamp
# libvlc_audio_output_device_list_release
# libvlc_audio_output_list_release
# libvlc_chapter_descriptions_release
# libvlc_clearerr
# libvlc_clock
# libvlc_errmsg
@ -6964,11 +6518,9 @@ def libvlc_vlm_get_event_manager(p_instance):
# libvlc_get_version
# libvlc_log_get_context
# libvlc_log_get_object
# libvlc_media_get_codec_description
# libvlc_media_tracks_release
# libvlc_module_description_list_release
# libvlc_new
# libvlc_title_descriptions_release
# libvlc_track_description_list_release
# libvlc_vprinterr

View File

@ -320,9 +320,17 @@ class ShortcutListForm(QtWidgets.QDialog, Ui_ShortcutListDialog, RegistryPropert
"""
if not toggled:
return
self.on_primary_push_button_clicked(False)
self.on_alternate_push_button_clicked(False)
action = self._current_item_action()
shortcuts = self._action_shortcuts(action)
self.refresh_shortcut_list()
primary_button_text = ''
alternate_button_text = ''
if shortcuts:
primary_button_text = self.get_shortcut_string(shortcuts[0], for_display=True)
if len(shortcuts) == 2:
alternate_button_text = self.get_shortcut_string(shortcuts[1], for_display=True)
self.primary_push_button.setText(primary_button_text)
self.alternate_push_button.setText(alternate_button_text)
def save(self):
"""

View File

@ -31,7 +31,7 @@ from threading import Lock
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry, RegistryProperties, Settings, SlideLimits, UiStrings, translate, \
RegistryMixin, OpenLPMixin
RegistryMixin, OpenLPMixin, is_win
from openlp.core.lib import OpenLPToolbar, ItemCapabilities, ServiceItem, ImageSource, ServiceItemAction, \
ScreenList, build_icon, build_html
from openlp.core.ui import HideMode, MainDisplay, Display, DisplayControllerType
@ -601,13 +601,21 @@ class SlideController(DisplayController, RegistryProperties):
def __add_actions_to_widget(self, widget):
"""
Add actions to the widget specified by `widget`
This defines the controls available when Live display has stolen focus.
Examples of this happening: Clicking anything in the live window or certain single screen mode scenarios.
Needles to say, blank to modes should not be removed from here.
For some reason this required a test. It may be found in test_slidecontroller.py as
"live_stolen_focus_shortcuts_test. If you want to modify things here, you must also modify them there. (Duh)
:param widget: The UI widget for the actions
"""
widget.addActions([
self.previous_item, self.next_item,
self.previous_service, self.next_service,
self.escape_item])
self.escape_item,
self.desktop_screen,
self.theme_screen,
self.blank_screen])
def preview_size_changed(self):
"""
@ -1125,8 +1133,8 @@ class SlideController(DisplayController, RegistryProperties):
self.log_debug('update_preview %s ' % self.screens.current['primary'])
if self.service_item and self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
# Grab now, but try again in a couple of seconds if slide change is slow
QtCore.QTimer.singleShot(0.5, self.grab_maindisplay)
QtCore.QTimer.singleShot(2.5, self.grab_maindisplay)
QtCore.QTimer.singleShot(500, self.grab_maindisplay)
QtCore.QTimer.singleShot(2500, self.grab_maindisplay)
else:
self.slide_image = self.display.preview()
self.slide_image.setDevicePixelRatio(self.main_window.devicePixelRatio())
@ -1421,7 +1429,7 @@ class SlideController(DisplayController, RegistryProperties):
:param time: the time remaining
"""
seconds = self.display.audio_player.media_object.remainingTime() // 1000
seconds = (self.display.audio_player.player.duration() - self.display.audio_player.player.position()) // 1000
minutes = seconds // 60
seconds %= 60
self.audio_time_label.setText(' %02d:%02d ' % (minutes, seconds))

View File

@ -288,7 +288,7 @@ class BGExtract(RegistryProperties):
except UnicodeDecodeError:
page_source = str(page_source, 'cp1251')
try:
soup = BeautifulSoup(page_source)
soup = BeautifulSoup(page_source, 'lxml')
except Exception:
log.error('BeautifulSoup could not parse the Bible page.')
send_error_message('parse')
@ -759,7 +759,7 @@ def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, pre
page_source = re.sub(pre_parse_regex, pre_parse_substitute, page_source.decode())
soup = None
try:
soup = BeautifulSoup(page_source)
soup = BeautifulSoup(page_source, 'lxml')
CLEANER_REGEX.sub('', str(soup))
except Exception:
log.exception('BeautifulSoup could not parse the bible page.')

View File

@ -63,6 +63,7 @@ class Controller(object):
if not self.doc.load_presentation():
# Display error message to user
# Inform slidecontroller that the action failed?
self.doc.slidenumber = 0
return
self.doc.slidenumber = slide_no
self.hide_mode = hide_mode

View File

@ -324,7 +324,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
if self.topics_combo_box.hasFocus() and self.topics_combo_box.currentText():
self.on_topic_add_button_clicked()
return
if self.songbooks_combo_box.hasFocus() and self.songbooks_combo_box.currentText():
if self.songbooks_combo_box.hasFocus() or self.songbook_entry_edit.hasFocus():
self.on_songbook_add_button_clicked()
return
QtWidgets.QDialog.keyPressEvent(self, event)
@ -514,6 +514,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
topic_name.setData(QtCore.Qt.UserRole, topic.id)
self.topics_list_view.addItem(topic_name)
self.songbooks_list_view.clear()
self.songbook_entry_edit.clear()
for songbook_entry in self.song.songbook_entries:
self.add_songbook_entry_to_list(songbook_entry.songbook.id, songbook_entry.songbook.name,
songbook_entry.entry)
@ -843,7 +844,9 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
:param text: The text of the verse order edit (ignored).
"""
# First make sure that all letters entered in the verse order field are uppercase
pos = self.verse_order_edit.cursorPosition()
self.verse_order_edit.setText(text.upper())
self.verse_order_edit.setCursorPosition(pos)
# Extract all verses which were used in the order.
verses_in_order = self._extract_verse_order(self.verse_order_edit.text())
# Find the verses which were not used in the order.

View File

@ -292,7 +292,7 @@ class EasyWorshipSongImport(SongImport):
raw_record = db_file.read(record_size)
self.fields = self.record_structure.unpack(raw_record)
self.set_defaults()
self.title = self.get_field(fi_title).decode('unicode-escape')
self.title = self.get_field(fi_title).decode(self.encoding)
# Get remaining fields.
copy = self.get_field(fi_copy)
admin = self.get_field(fi_admin)
@ -300,16 +300,16 @@ class EasyWorshipSongImport(SongImport):
authors = self.get_field(fi_author)
words = self.get_field(fi_words)
if copy:
self.copyright = copy.decode('unicode-escape')
self.copyright = copy.decode(self.encoding)
if admin:
if copy:
self.copyright += ', '
self.copyright += translate('SongsPlugin.EasyWorshipSongImport',
'Administered by %s') % admin.decode('unicode-escape')
'Administered by %s') % admin.decode(self.encoding)
if ccli:
self.ccli_number = ccli.decode('unicode-escape')
self.ccli_number = ccli.decode(self.encoding)
if authors:
authors = authors.decode('unicode-escape')
authors = authors.decode(self.encoding)
else:
authors = ''
# Set the SongImport object members.
@ -497,7 +497,7 @@ class EasyWorshipSongImport(SongImport):
bytes = self.get_bytes(pos, length)
mask = '<' + str(length) + 's'
byte_str, = struct.unpack(mask, bytes)
return byte_str.decode('unicode-escape').replace('\0', '').strip()
return byte_str.decode(self.encoding).replace('\0', '').strip()
def get_i16(self, pos):
"""

View File

@ -157,6 +157,7 @@ class OpenSongImport(SongImport):
if isinstance(fn_or_string, str):
if attr in ['ccli']:
if ustring:
ustring = ''.join(re.findall('\d+', ustring))
setattr(self, fn_or_string, int(ustring))
else:
setattr(self, fn_or_string, None)

View File

@ -26,7 +26,7 @@ import os
import shutil
from PyQt5 import QtCore, QtWidgets
from sqlalchemy.sql import or_
from sqlalchemy.sql import and_, or_
from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \
@ -37,7 +37,7 @@ from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm
from openlp.plugins.songs.forms.songimportform import SongImportForm
from openlp.plugins.songs.forms.songexportform import SongExportForm
from openlp.plugins.songs.lib import VerseType, clean_string, delete_song
from openlp.plugins.songs.lib.db import Author, AuthorType, Song, Book, MediaFile, SongBookEntry
from openlp.plugins.songs.lib.db import Author, AuthorType, Song, Book, MediaFile, SongBookEntry, Topic
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, SongXML
@ -52,8 +52,11 @@ class SongSearch(object):
Titles = 2
Lyrics = 3
Authors = 4
Books = 5
Themes = 6
Topics = 5
Books = 6
Themes = 7
Copyright = 8
CCLInumber = 9
class SongMediaItem(MediaManagerItem):
@ -151,9 +154,17 @@ class SongMediaItem(MediaManagerItem):
translate('SongsPlugin.MediaItem', 'Search Lyrics...')),
(SongSearch.Authors, ':/songs/song_search_author.png', SongStrings.Authors,
translate('SongsPlugin.MediaItem', 'Search Authors...')),
(SongSearch.Topics, ':/songs/song_search_topic.png', SongStrings.Topics,
translate('SongsPlugin.MediaItem', 'Search Topics...')),
(SongSearch.Books, ':/songs/song_book_edit.png', SongStrings.SongBooks,
translate('SongsPlugin.MediaItem', 'Search Songbooks...')),
(SongSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, UiStrings().SearchThemes)
(SongSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, UiStrings().SearchThemes),
(SongSearch.Copyright, ':/songs/song_search_copy.png',
translate('SongsPlugin.MediaItem', 'Copyright'),
translate('SongsPlugin.MediaItem', 'Search Copyright...')),
(SongSearch.CCLInumber, ':/songs/song_search_ccli.png',
translate('SongsPlugin.MediaItem', 'CCLI number'),
translate('SongsPlugin.MediaItem', 'Search CCLI number...'))
])
self.search_text_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section))
self.config_update()
@ -184,14 +195,33 @@ class SongMediaItem(MediaManagerItem):
search_results = self.plugin.manager.get_all_objects(
Author, Author.display_name.like(search_string), Author.display_name.asc())
self.display_results_author(search_results)
elif search_type == SongSearch.Topics:
log.debug('Topics Search')
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(
Topic, Topic.name.like(search_string), Topic.name.asc())
self.display_results_topic(search_results)
elif search_type == SongSearch.Books:
log.debug('Songbook Search')
self.display_results_book(search_keywords)
elif search_type == SongSearch.Themes:
log.debug('Theme Search')
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(Song, Song.theme_name.like(search_string))
search_results = self.plugin.manager.get_all_objects(
Song, Song.theme_name.like(search_string), Song.theme_name.asc())
self.display_results_themes(search_results)
elif search_type == SongSearch.Copyright:
log.debug('Copyright Search')
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(
Song, and_(Song.copyright.like(search_string), Song.copyright != ''))
self.display_results_song(search_results)
elif search_type == SongSearch.CCLInumber:
log.debug('CCLI number Search')
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(
Song, and_(Song.ccli_number.like(search_string), Song.ccli_number != ''))
self.display_results_cclinumber(search_results)
self.check_search_result()
def search_entire(self, search_keywords):
@ -215,6 +245,12 @@ class SongMediaItem(MediaManagerItem):
log.debug('on_song_list_load - finished')
def display_results_song(self, search_results):
"""
Display the song search results in the media manager list
:param search_results: A list of db Song objects
:return: None
"""
log.debug('display results Song')
self.save_auto_select_id()
self.list_view.clear()
@ -234,6 +270,12 @@ class SongMediaItem(MediaManagerItem):
self.auto_select_id = -1
def display_results_author(self, search_results):
"""
Display the song search results in the media manager list, grouped by author
:param search_results: A list of db Author objects
:return: None
"""
log.debug('display results Author')
self.list_view.clear()
for author in search_results:
@ -247,6 +289,13 @@ class SongMediaItem(MediaManagerItem):
self.list_view.addItem(song_name)
def display_results_book(self, search_keywords):
"""
Display the song search results in the media manager list, grouped by book
:param search_keywords: A list of search keywords - book first, then number
:return: None
"""
log.debug('display results Book')
self.list_view.clear()
@ -270,6 +319,64 @@ class SongMediaItem(MediaManagerItem):
song_name.setData(QtCore.Qt.UserRole, songbook_entry.song.id)
self.list_view.addItem(song_name)
def display_results_topic(self, search_results):
"""
Display the song search results in the media manager list, grouped by topic
:param search_results: A list of db Topic objects
:return: None
"""
log.debug('display results Topic')
self.list_view.clear()
search_results = sorted(search_results, key=lambda topic: self._natural_sort_key(topic.name))
for topic in search_results:
songs = sorted(topic.songs, key=lambda song: song.sort_key)
for song in songs:
# Do not display temporary songs
if song.temporary:
continue
song_detail = '%s (%s)' % (topic.name, song.title)
song_name = QtWidgets.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, song.id)
self.list_view.addItem(song_name)
def display_results_themes(self, search_results):
"""
Display the song search results in the media manager list, sorted by theme
:param search_results: A list of db Song objects
:return: None
"""
log.debug('display results Themes')
self.list_view.clear()
for song in search_results:
# Do not display temporary songs
if song.temporary:
continue
song_detail = '%s (%s)' % (song.theme_name, song.title)
song_name = QtWidgets.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, song.id)
self.list_view.addItem(song_name)
def display_results_cclinumber(self, search_results):
"""
Display the song search results in the media manager list, sorted by CCLI number
:param search_results: A list of db Song objects
:return: None
"""
log.debug('display results CCLI number')
self.list_view.clear()
songs = sorted(search_results, key=lambda song: self._natural_sort_key(song.ccli_number))
for song in songs:
# Do not display temporary songs
if song.temporary:
continue
song_detail = '%s (%s)' % (song.ccli_number, song.title)
song_name = QtWidgets.QListWidgetItem(song_detail)
song_name.setData(QtCore.Qt.UserRole, song.id)
self.list_view.addItem(song_name)
def on_clear_text_button_click(self):
"""
Clear the search text.
@ -587,6 +694,14 @@ class SongMediaItem(MediaManagerItem):
# List must be empty at the end
return not author_list
def _natural_sort_key(self, s):
"""
Return a tuple by which s is sorted.
:param s: A string value from the list we want to sort.
"""
return [int(text) if text.isdecimal() else text.lower()
for text in re.split('(\d+)', s)]
def search(self, string, show_error):
"""
Search for some songs

View File

@ -118,7 +118,6 @@ class SongUsagePlugin(Plugin):
self.main_window.status_bar.insertPermanentWidget(1, self.song_usage_active_button)
self.song_usage_active_button.hide()
# Signals and slots
self.song_usage_status.changed.connect(self.toggle_song_usage_state)
self.song_usage_active_button.toggled.connect(self.toggle_song_usage_state)
self.song_usage_menu.menuAction().setVisible(False)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,11 @@
<file>song_search_stop.png</file>
<file>song_search_all.png</file>
<file>song_search_author.png</file>
<file>song_search_ccli.png</file>
<file>song_search_copy.png</file>
<file>song_search_lyrics.png</file>
<file>song_search_title.png</file>
<file>song_search_topic.png</file>
<file>topic_edit.png</file>
<file>author_add.png</file>
<file>author_delete.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

View File

@ -60,3 +60,17 @@ class TestPJLink(TestCase):
"Connection request should have been called with TEST_SALT"))
self.assertTrue(mock_qmd5_hash.called_with(TEST_PIN,
"Connection request should have been called with TEST_PIN"))
def non_standard_class_reply_test(self):
"""
bugfix 1550891 - CLSS request returns non-standard 'Class N' reply
"""
# GIVEN: Test object
pjlink = pjlink_test
# WHEN: Process non-standard reply
pjlink.process_clss('Class 1')
# THEN: Projector class should be set with proper value
self.assertEquals(pjlink.pjlink_class, '1',
'Non-standard class reply should have set proper class')

View File

@ -27,9 +27,10 @@ from unittest import TestCase
from PyQt5 import QtCore
from openlp.core.common import Registry
from openlp.core.lib import Renderer, ScreenList, ServiceItem
from openlp.core.lib import Renderer, ScreenList, ServiceItem, FormattingTags
from openlp.core.lib.renderer import words_split, get_start_tags
from tests.functional import MagicMock
from tests.functional import MagicMock, patch
SCREEN = {
'primary': False,
@ -71,34 +72,39 @@ class TestRenderer(TestCase):
self.assertEqual(renderer.screen_ratio, 0.75, 'The base renderer should be a live controller')
self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller')
def _get_start_tags_test(self):
@patch('openlp.core.lib.renderer.FormattingTags.get_html_tags')
def get_start_tags_test(self, mocked_get_html_tags):
"""
Test the _get_start_tags() method
Test the get_start_tags() method
"""
# GIVEN: A new renderer instance. Broken raw_text (missing closing tags).
renderer = Renderer()
given_raw_text = '{st}{r}Text text text'
expected_tuple = ('{st}{r}Text text text{/r}{/st}', '{st}{r}',
'<strong><span style="-webkit-text-fill-color:red">')
mocked_get_html_tags.return_value = [{'temporary': False, 'end tag': '{/r}', 'desc': 'Red',
'start html': '<span style="-webkit-text-fill-color:red">',
'end html': '</span>', 'start tag': '{r}', 'protected': True},
{'temporary': False, 'end tag': '{/st}', 'desc': 'Bold',
'start html': '<strong>', 'end html': '</strong>', 'start tag': '{st}',
'protected': True}]
# WHEN: The renderer converts the start tags
result = renderer._get_start_tags(given_raw_text)
result = get_start_tags(given_raw_text)
# THEN: Check if the correct tuple is returned.
self.assertEqual(result, expected_tuple), 'A tuple should be returned containing the text with correct ' \
'tags, the opening tags, and the opening html tags.'
def _word_split_test(self):
def word_split_test(self):
"""
Test the _word_split() method
Test the word_split() method
"""
# GIVEN: A line of text
renderer = Renderer()
given_line = 'beginning asdf \n end asdf'
expected_words = ['beginning', 'asdf', 'end', 'asdf']
# WHEN: Split the line based on word split rules
result_words = renderer._words_split(given_line)
result_words = words_split(given_line)
# THEN: The word lists should be the same.
self.assertListEqual(result_words, expected_words)

View File

@ -685,6 +685,34 @@ class TestSlideController(TestCase):
self.assertEqual('mocked_presentation_item_stop', mocked_execute.call_args_list[1][0][0],
'The presentation should have been stopped.')
def live_stolen_focus_shortcuts_test(self):
"""
Test that all the needed shortcuts are available in scenarios where Live has stolen focus.
These are found under def __add_actions_to_widget(self, widget): in slidecontroller.py
"""
# GIVEN: A slide controller, actions needed
slide_controller = SlideController(None)
mocked_widget = MagicMock()
slide_controller.previous_item = MagicMock()
slide_controller.next_item = MagicMock()
slide_controller.previous_service = MagicMock()
slide_controller.next_service = MagicMock()
slide_controller.escape_item = MagicMock()
slide_controller.desktop_screen = MagicMock()
slide_controller.blank_screen = MagicMock()
slide_controller.theme_screen = MagicMock()
# WHEN: __add_actions_to_widget is called
slide_controller._SlideController__add_actions_to_widget(mocked_widget)
# THEN: The call to addActions should be correct
mocked_widget.addActions.assert_called_with([
slide_controller.previous_item, slide_controller.next_item,
slide_controller.previous_service, slide_controller.next_service,
slide_controller.escape_item, slide_controller.desktop_screen,
slide_controller.theme_screen, slide_controller.blank_screen
])
class TestInfoLabel(TestCase):

View File

@ -152,7 +152,7 @@ class TestBSExtract(TestCase):
self.test_html = '<ul><li><a href="/overlay/selectChapter?tocBook=1">Genesis</a></li>' \
'<li><a href="/overlay/selectChapter?tocBook=2"></a></li>' \
'<li><a href="/overlay/selectChapter?tocBook=3">Leviticus</a></li></ul>'
self.test_soup = BeautifulSoup(self.test_html)
self.test_soup = BeautifulSoup(self.test_html, 'lxml')
instance = BSExtract()
self.mock_log.reset_mock()
self.mock_urllib.reset_mock()

View File

@ -26,6 +26,7 @@ from unittest import TestCase
from openlp.core.common import Registry
from openlp.plugins.presentations.lib.mediaitem import MessageListener, PresentationMediaItem
from openlp.plugins.presentations.lib.messagelistener import Controller
from tests.functional import patch, MagicMock
from tests.helpers.testmixin import TestMixin
@ -124,3 +125,26 @@ class TestMessageListener(TestCase, TestMixin):
# THEN: The handler should be set to None
self.assertIsNone(ml.handler, 'The handler should be None')
class TestController(TestCase, TestMixin):
"""
Test the Presentation Controller.
"""
def add_handler_failure_test(self):
"""
Test that add_handler does set doc.slidenumber to 0 in case filed loading
"""
# GIVEN: A Controller, a mocked doc-controller
controller = Controller(True)
mocked_doc_controller = MagicMock()
mocked_doc = MagicMock()
mocked_doc.load_presentation.return_value = False
mocked_doc_controller.add_document.return_value = mocked_doc
# WHEN: calling add_handler that fails
controller.add_handler(mocked_doc_controller, MagicMock(), True, 0)
# THEN: slidenumber should be 0
self.assertEqual(controller.doc.slidenumber, 0, 'doc.slidenumber should be 0')

View File

@ -48,6 +48,12 @@ class TestMediaItem(TestCase, TestMixin):
with patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup'), \
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
self.media_item = SongMediaItem(None, MagicMock())
self.media_item.save_auto_select_id = MagicMock()
self.media_item.list_view = MagicMock()
self.media_item.list_view.save_auto_select_id = MagicMock()
self.media_item.list_view.clear = MagicMock()
self.media_item.list_view.addItem = MagicMock()
self.media_item.auto_select_id = -1
self.media_item.display_songbook = False
self.media_item.display_copyright_symbol = False
self.setup_application()
@ -60,6 +66,151 @@ class TestMediaItem(TestCase, TestMixin):
"""
self.destroy_settings()
def display_results_song_test(self):
"""
Test displaying song search results with basic song
"""
# GIVEN: Search results, plus a mocked QtListWidgetItem
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem, \
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
mock_search_results = []
mock_song = MagicMock()
mock_song.id = 1
mock_song.title = 'My Song'
mock_song.sort_key = 'My Song'
mock_song.authors = []
mock_author = MagicMock()
mock_author.display_name = 'My Author'
mock_song.authors.append(mock_author)
mock_song.temporary = False
mock_search_results.append(mock_song)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
# WHEN: I display song search results
self.media_item.display_results_song(mock_search_results)
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
self.media_item.list_view.clear.assert_called_with()
self.media_item.save_auto_select_id.assert_called_with()
MockedQListWidgetItem.assert_called_with('My Song (My Author)')
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
def display_results_author_test(self):
"""
Test displaying song search results grouped by author with basic song
"""
# GIVEN: Search results grouped by author, plus a mocked QtListWidgetItem
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem, \
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
mock_search_results = []
mock_author = MagicMock()
mock_song = MagicMock()
mock_author.display_name = 'My Author'
mock_author.songs = []
mock_song.id = 1
mock_song.title = 'My Song'
mock_song.sort_key = 'My Song'
mock_song.temporary = False
mock_author.songs.append(mock_song)
mock_search_results.append(mock_author)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
# WHEN: I display song search results grouped by author
self.media_item.display_results_author(mock_search_results)
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
self.media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_with('My Author (My Song)')
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
def display_results_topic_test(self):
"""
Test displaying song search results grouped by topic with basic song
"""
# GIVEN: Search results grouped by topic, plus a mocked QtListWidgetItem
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem, \
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
mock_search_results = []
mock_topic = MagicMock()
mock_song = MagicMock()
mock_topic.name = 'My Topic'
mock_topic.songs = []
mock_song.id = 1
mock_song.title = 'My Song'
mock_song.sort_key = 'My Song'
mock_song.temporary = False
mock_topic.songs.append(mock_song)
mock_search_results.append(mock_topic)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
# WHEN: I display song search results grouped by topic
self.media_item.display_results_topic(mock_search_results)
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
self.media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_with('My Topic (My Song)')
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
def display_results_themes_test(self):
"""
Test displaying song search results sorted by theme with basic song
"""
# GIVEN: Search results sorted by theme, plus a mocked QtListWidgetItem
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem, \
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
mock_search_results = []
mock_song = MagicMock()
mock_song.id = 1
mock_song.title = 'My Song'
mock_song.sort_key = 'My Song'
mock_song.theme_name = 'My Theme'
mock_song.temporary = False
mock_search_results.append(mock_song)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
# WHEN: I display song search results sorted by theme
self.media_item.display_results_themes(mock_search_results)
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
self.media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_with('My Theme (My Song)')
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
def display_results_cclinumber_test(self):
"""
Test displaying song search results sorted by CCLI number with basic song
"""
# GIVEN: Search results sorted by CCLI number, plus a mocked QtListWidgetItem
with patch('openlp.core.lib.QtWidgets.QListWidgetItem') as MockedQListWidgetItem, \
patch('openlp.core.lib.QtCore.Qt.UserRole') as MockedUserRole:
mock_search_results = []
mock_song = MagicMock()
mock_song.id = 1
mock_song.title = 'My Song'
mock_song.sort_key = 'My Song'
mock_song.ccli_number = '12345'
mock_song.temporary = False
mock_search_results.append(mock_song)
mock_qlist_widget = MagicMock()
MockedQListWidgetItem.return_value = mock_qlist_widget
# WHEN: I display song search results sorted by CCLI number
self.media_item.display_results_cclinumber(mock_search_results)
# THEN: The current list view is cleared, the widget is created, and the relevant attributes set
self.media_item.list_view.clear.assert_called_with()
MockedQListWidgetItem.assert_called_with('12345 (My Song)')
mock_qlist_widget.setData.assert_called_with(MockedUserRole, mock_song.id)
self.media_item.list_view.addItem.assert_called_with(mock_qlist_widget)
def build_song_footer_one_author_test(self):
"""
Test build songs footer with basic song and one author
@ -265,6 +416,19 @@ class TestMediaItem(TestCase, TestMixin):
# THEN: They should not match
self.assertFalse(result, "Authors should not match")
def natural_sort_key_test(self):
"""
Test the _natural_sort_key function
"""
# GIVEN: A string to be converted into a sort key
string_sort_key = 'A1B12C'
# WHEN: We attempt to create a sort key
sort_key_result = self.media_item._natural_sort_key(string_sort_key)
# THEN: We should get back a tuple split on integers
self.assertEqual(sort_key_result, ['a', 1, 'b', 12, 'c'])
def build_remote_search_test(self):
"""
Test results for the remote search api

View File

@ -74,6 +74,13 @@ author_xml = '<properties>\
</authors>\
</properties>'
songbook_xml = '<properties>\
<songbooks>\
<songbook name="Collection 1" entry="48"/>\
<songbook name="Collection 2" entry="445 A"/>\
</songbooks>\
</properties>'
class TestOpenLyricsImport(TestCase, TestMixin):
"""
@ -166,3 +173,22 @@ class TestOpenLyricsImport(TestCase, TestMixin):
# THEN: add_author should have been called twice
self.assertEquals(mocked_song.method_calls[0][1][1], 'words+music')
self.assertEquals(mocked_song.method_calls[1][1][1], 'words')
def process_songbooks_test(self):
"""
Test that _process_songbooks works
"""
# GIVEN: A OpenLyric XML with songbooks and a mocked out manager
with patch('openlp.plugins.songs.lib.openlyricsxml.Book'):
mocked_manager = MagicMock()
mocked_manager.get_object_filtered.return_value = None
ol = OpenLyrics(mocked_manager)
properties_xml = objectify.fromstring(songbook_xml)
mocked_song = MagicMock()
# WHEN: processing the songbook xml
ol._process_songbooks(properties_xml, mocked_song)
# THEN: add_songbook_entry should have been called twice
self.assertEquals(mocked_song.method_calls[0][1][1], '48')
self.assertEquals(mocked_song.method_calls[1][1][1], '445 A')

View File

@ -52,6 +52,8 @@ class TestOpenSongFileImport(SongImportTestHelper):
self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json')))
self.file_import([os.path.join(TEST_PATH, 'One, Two, Three, Four, Five')],
self.load_external_result_data(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five.json')))
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace2')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
class TestOpenSongImport(TestCase):

View File

@ -44,7 +44,5 @@ class TestPresentationManagerFileImport(SongImportTestHelper):
"""
self.file_import([os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.sng')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.json')))
self.file_import([os.path.join(TEST_PATH, 'Agnus Dei.sng')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Agnus Dei.json')))
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.sng')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))

View File

@ -45,5 +45,3 @@ class TestProPresenterFileImport(SongImportTestHelper):
"""
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.pro4')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
self.file_import([os.path.join(TEST_PATH, 'Vaste Grond.pro4')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Vaste Grond.json')))

View File

@ -45,7 +45,5 @@ class TestSundayPlusFileImport(SongImportTestHelper):
with patch('openlp.plugins.songs.lib.importers.sundayplus.retrieve_windows_encoding') as \
mocked_retrieve_windows_encoding:
mocked_retrieve_windows_encoding.return_value = 'cp1252'
self.file_import([os.path.join(TEST_PATH, 'Abba Fader.ptf')],
self.load_external_result_data(os.path.join(TEST_PATH, 'abba-fader.json')))
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.ptf')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))

View File

@ -49,5 +49,3 @@ class TestWorshipAssistantFileImport(SongImportTestHelper):
self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json')))
self.file_import(os.path.join(TEST_PATH, 'would_you_be_free2.csv'),
self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json')))
self.file_import(os.path.join(TEST_PATH, 'lift_up_your_heads.csv'),
self.load_external_result_data(os.path.join(TEST_PATH, 'lift_up_your_heads.json')))

View File

@ -23,12 +23,23 @@
This module contains tests for the Songusage plugin.
"""
from unittest import TestCase
from unittest.mock import MagicMock, patch
from openlp.core import Registry
from openlp.plugins.songusage.lib import upgrade
from openlp.plugins.songusage.lib.db import init_schema
from openlp.plugins.songusage.songusageplugin import SongUsagePlugin
class TestSongUsage(TestCase):
def test_about_text(self):
def setUp(self):
Registry.create()
def about_text_test(self):
"""
Test the about text of the song usage plugin
"""
# GIVEN: The SongUsagePlugin
# WHEN: Retrieving the about text
# THEN: about() should return a string object
@ -36,3 +47,53 @@ class TestSongUsage(TestCase):
# THEN: about() should return a non-empty string
self.assertNotEquals(len(SongUsagePlugin.about()), 0)
self.assertNotEquals(len(SongUsagePlugin.about()), 0)
@patch('openlp.plugins.songusage.songusageplugin.Manager')
def song_usage_init_test(self, MockedManager):
"""
Test the initialisation of the SongUsagePlugin class
"""
# GIVEN: A mocked database manager
mocked_manager = MagicMock()
MockedManager.return_value = mocked_manager
# WHEN: The SongUsagePlugin class is instantiated
song_usage = SongUsagePlugin()
# THEN: It should be initialised correctly
MockedManager.assert_called_with('songusage', init_schema, upgrade_mod=upgrade)
self.assertEqual(mocked_manager, song_usage.manager)
self.assertFalse(song_usage.song_usage_active)
@patch('openlp.plugins.songusage.songusageplugin.Manager')
def check_pre_conditions_test(self, MockedManager):
"""
Test that check_pre_condition returns true for valid manager session
"""
# GIVEN: A mocked database manager
mocked_manager = MagicMock()
mocked_manager.session = MagicMock()
MockedManager.return_value = mocked_manager
song_usage = SongUsagePlugin()
# WHEN: The calling check_pre_conditions
ret = song_usage.check_pre_conditions()
# THEN: It should return True
self.assertTrue(ret)
@patch('openlp.plugins.songusage.songusageplugin.Manager')
def toggle_song_usage_state_test(self, MockedManager):
"""
Test that toggle_song_usage_state does toggle song_usage_state
"""
# GIVEN: A SongUsagePlugin
song_usage = SongUsagePlugin()
song_usage.set_button_state = MagicMock()
song_usage.song_usage_active = True
# WHEN: calling toggle_song_usage_state
song_usage.toggle_song_usage_state()
# THEN: song_usage_state should have been toogled
self.assertFalse(song_usage.song_usage_active)

View File

@ -28,6 +28,7 @@ import logging
log = logging.getLogger(__name__)
log.debug('test_projectorsourceform loaded')
import os
import time
from unittest import TestCase
from PyQt5.QtWidgets import QDialog

View File

@ -24,11 +24,12 @@ Package to test the openlp.core.ui.shortcutform package.
"""
from unittest import TestCase
from PyQt5 import QtWidgets
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import Registry
from openlp.core.ui.shortcutlistform import ShortcutListForm
from tests.interfaces import patch
from tests.interfaces import MagicMock, patch
from tests.helpers.testmixin import TestMixin
@ -59,13 +60,170 @@ class TestShortcutform(TestCase, TestMixin):
button = QtWidgets.QPushButton()
checked = True
enabled = True
text = "new!"
text = 'new!'
# WHEN: Call the method.
with patch('PyQt5.QtWidgets.QPushButton.setChecked') as mocked_check_method:
self.form._adjust_button(button, checked, enabled, text)
# THEN: The button should be changed.
self.assertEqual(button.text(), text, "The text should match.")
self.assertEqual(button.text(), text, 'The text should match.')
mocked_check_method.assert_called_once_with(True)
self.assertEqual(button.isEnabled(), enabled, "The button should be disabled.")
self.assertEqual(button.isEnabled(), enabled, 'The button should be disabled.')
def space_key_press_event_test(self):
"""
Test the keyPressEvent when the spacebar was pressed
"""
# GIVEN: A key event that is a space
mocked_event = MagicMock()
mocked_event.key.return_value = QtCore.Qt.Key_Space
# WHEN: The event is handled
with patch.object(self.form, 'keyReleaseEvent') as mocked_key_release_event:
self.form.keyPressEvent(mocked_event)
# THEN: The key should be released
mocked_key_release_event.assert_called_with(mocked_event)
self.assertEqual(0, mocked_event.accept.call_count)
def primary_push_button_checked_key_press_event_test(self):
"""
Test the keyPressEvent when the primary push button is checked
"""
# GIVEN: The primary push button is checked
with patch.object(self.form, 'keyReleaseEvent') as mocked_key_release_event, \
patch.object(self.form.primary_push_button, 'isChecked') as mocked_is_checked:
mocked_is_checked.return_value = True
mocked_event = MagicMock()
# WHEN: The event is handled
self.form.keyPressEvent(mocked_event)
# THEN: The key should be released
mocked_key_release_event.assert_called_with(mocked_event)
self.assertEqual(0, mocked_event.accept.call_count)
def alternate_push_button_checked_key_press_event_test(self):
"""
Test the keyPressEvent when the alternate push button is checked
"""
# GIVEN: The primary push button is checked
with patch.object(self.form, 'keyReleaseEvent') as mocked_key_release_event, \
patch.object(self.form.alternate_push_button, 'isChecked') as mocked_is_checked:
mocked_is_checked.return_value = True
mocked_event = MagicMock()
# WHEN: The event is handled
self.form.keyPressEvent(mocked_event)
# THEN: The key should be released
mocked_key_release_event.assert_called_with(mocked_event)
self.assertEqual(0, mocked_event.accept.call_count)
def escape_key_press_event_test(self):
"""
Test the keyPressEvent when the escape key was pressed
"""
# GIVEN: A key event that is an escape
mocked_event = MagicMock()
mocked_event.key.return_value = QtCore.Qt.Key_Escape
# WHEN: The event is handled
with patch.object(self.form, 'close') as mocked_close:
self.form.keyPressEvent(mocked_event)
# THEN: The key should be released
mocked_event.accept.assert_called_with()
mocked_close.assert_called_with()
def on_default_radio_button_not_toggled_test(self):
"""
Test that the default radio button method exits early when the button is not toggled
"""
# GIVEN: A not-toggled custom radio button
with patch.object(self.form, '_current_item_action') as mocked_current_item_action:
# WHEN: The clicked method is called
self.form.on_default_radio_button_clicked(False)
# THEN: The method should exit early (i.e. the rest of the methods are not called)
self.assertEqual(0, mocked_current_item_action.call_count)
def on_default_radio_button_clicked_no_action_test(self):
"""
Test that nothing happens when an action hasn't been selected and you click the default radio button
"""
# GIVEN: Some mocked out methods, a current action, and some shortcuts
with patch.object(self.form, '_current_item_action') as mocked_current_item_action, \
patch.object(self.form, '_action_shortcuts') as mocked_action_shortcuts:
mocked_current_item_action.return_value = None
# WHEN: The default radio button is clicked
self.form.on_default_radio_button_clicked(True)
# THEN: The method should exit early (i.e. the rest of the methods are not called)
mocked_current_item_action.assert_called_with()
self.assertEqual(0, mocked_action_shortcuts.call_count)
def on_default_radio_button_clicked_test(self):
"""
Test that the values are copied across correctly when the default radio button is selected
"""
# GIVEN: Some mocked out methods, a current action, and some shortcuts
with patch.object(self.form, '_current_item_action') as mocked_current_item_action, \
patch.object(self.form, '_action_shortcuts') as mocked_action_shortcuts, \
patch.object(self.form, 'refresh_shortcut_list') as mocked_refresh_shortcut_list, \
patch.object(self.form, 'get_shortcut_string') as mocked_get_shortcut_string, \
patch.object(self.form.primary_push_button, 'setText') as mocked_set_text:
mocked_action = MagicMock()
mocked_action.default_shortcuts = [QtCore.Qt.Key_Escape]
mocked_current_item_action.return_value = mocked_action
mocked_action_shortcuts.return_value = [QtCore.Qt.Key_Escape]
mocked_get_shortcut_string.return_value = 'Esc'
# WHEN: The default radio button is clicked
self.form.on_default_radio_button_clicked(True)
# THEN: The shorcuts should be copied across
mocked_current_item_action.assert_called_with()
mocked_action_shortcuts.assert_called_with(mocked_action)
mocked_refresh_shortcut_list.assert_called_with()
mocked_set_text.assert_called_with('Esc')
def on_custom_radio_button_not_toggled_test(self):
"""
Test that the custom radio button method exits early when the button is not toggled
"""
# GIVEN: A not-toggled custom radio button
with patch.object(self.form, '_current_item_action') as mocked_current_item_action:
# WHEN: The clicked method is called
self.form.on_custom_radio_button_clicked(False)
# THEN: The method should exit early (i.e. the rest of the methods are not called)
self.assertEqual(0, mocked_current_item_action.call_count)
def on_custom_radio_button_clicked_test(self):
"""
Test that the values are copied across correctly when the custom radio button is selected
"""
# GIVEN: Some mocked out methods, a current action, and some shortcuts
with patch.object(self.form, '_current_item_action') as mocked_current_item_action, \
patch.object(self.form, '_action_shortcuts') as mocked_action_shortcuts, \
patch.object(self.form, 'refresh_shortcut_list') as mocked_refresh_shortcut_list, \
patch.object(self.form, 'get_shortcut_string') as mocked_get_shortcut_string, \
patch.object(self.form.primary_push_button, 'setText') as mocked_set_text:
mocked_action = MagicMock()
mocked_current_item_action.return_value = mocked_action
mocked_action_shortcuts.return_value = [QtCore.Qt.Key_Escape]
mocked_get_shortcut_string.return_value = 'Esc'
# WHEN: The custom radio button is clicked
self.form.on_custom_radio_button_clicked(True)
# THEN: The shorcuts should be copied across
mocked_current_item_action.assert_called_with()
mocked_action_shortcuts.assert_called_with(mocked_action)
mocked_refresh_shortcut_list.assert_called_with()
mocked_set_text.assert_called_with('Esc')

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<song>
<title>Amazing Grace (Demonstration)</title>
<author>John Newton, Edwin Excell &amp; John P. Rees</author>
<copyright>Public Domain </copyright>
<presentation>V1 V2 V3 V4 V5</presentation>
<capo print="false"></capo>
<tempo></tempo>
<ccli>CC: 22025 number</ccli>
<theme>God: Assurance/Grace/Salvation</theme>
<alttheme>Worship: Praise</alttheme>
<user1> </user1>
<user2> </user2>
<user3> </user3>
<lyrics>[V]
;Test the chords format
;Chords beging with .
;Verses begin with their verse number
;Link words with _
;Comments begin with ;
. D D7 G D
1A______ma________zing grace! How sweet the sound!
2'Twas grace that taught my heart to fear,
3The Lord has pro____mised good to me,
4Thro' ma________ny dan____gers, toils and snares
5When we've been there ten thou__sand years,
. Bm E A A7
1That saved a wretch like me!
2And grace my fears re___lieved.
3His Word my hope se___cures.
4I have al___rea____dy come.
5Bright shi___ning as the sun,
. D D7 G D
1I once was lost, but now am found;
2How pre___cious did that grace ap____pear,
3He will my shield and por___tion be
4'Tis grace that brought me safe thus far,
5We've no less days to sing God's praise,
. Bm A G D
1Was blind, but now I see.
2The hour I first be_lieved.
3As long as life en_dures.
4And grace will lead me home.
5Than when we first be_gun.
</lyrics>
<hymn_number>Demonstration Songs 0</hymn_number>
<key></key>
<aka></aka>
<key_line></key_line>
<time_sig></time_sig>
<style index="default_style"></style>
</song>

View File

@ -1,14 +0,0 @@
{
"title": "Agnus Dei",
"verse_order_list": ["v1", "v2"],
"verses": [
[
"Alleluia Alleluluia \nfor the Lord almighty reigns \nAlleluia Alleluluia \nHoly holy are you Lord God Almighty \nWorthy is the lamb \nWorthy is the lamb \nHoly holy are you Lord God Almighty",
"v1"
],
[
"Worthy is the lamb \nWorthy is the lamb \nYou are holy holy \nAre you lamb \nWorthy is the lamb \nYou are holy holy \nYou are holy holy",
"v2"
]
]
}

View File

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<song xmlns="creativelifestyles/song">
<attributes>
<title>Agnus Dei</title>
<author></author>
<copyright></copyright>
<ccli_number></ccli_number>
<comments></comments>
</attributes>
<verses>
<verse id="Verse 1">
Alleluia Alleluluia
for the Lord almighty reigns
Alleluia Alleluluia
Holy holy are you Lord God Almighty
Worthy is the lamb
Worthy is the lamb
Holy holy are you Lord God Almighty
</verse>
<verse id="Verse 2">
Worthy is the lamb
Worthy is the lamb
You are holy holy
Are you lamb
Worthy is the lamb
You are holy holy
You are holy holy
</verse>
</verses>
</song>

View File

@ -1,34 +0,0 @@
{
"title": "Vaste Grond",
"verse_order_list": [],
"verses": [
[
"God voor U is niets onmogelijk\nHoe ongelofelijk\nU heeft alles in de hand",
"v1"
],
[
"U bent God en trekt Uw eigen plan\nU bent voor niemand bang\nVoor niets en niemand bang",
"v2"
],
[
"U houd me vast en geeft me moed\nOm door te gaan als ik niet durf\nIk wil van U zijn",
"v3"
],
[
"U geeft me kracht, en bent de vaste grond\nwaarop ik stevig sta\nik wil van U zijn, voor altijd van U zijn\nO God.",
"v4"
],
[
"Grote God, U bent uitzonderlijk\nen ondoorgrondelijk\nU biedt Uw liefde aan",
"v5"
],
[
"Wie ben ik, dat U mij ziet staan\nen met mij om wilt gaan?\nIk kan U niet weerstaan",
"v6"
],
[
"Onweerstaanbaar,\nonweerstaanbare God",
"v7"
]
]
}

File diff suppressed because one or more lines are too long

View File

@ -1,8 +0,0 @@
[#PTFVersion: 2, #GLOBAL_RECT: rect(47,2,1026,770), #opacity: 100, #SHADOW_ON: 0, #SHADOW_COLOR: rgb( 0, 0, 0), #SHADOW_OPACITY: 100, #SHADOW_POSITION: "RB", #SHADOW_OFFSET: [0, 0], #FILE_TYPE: "Song", #title: "Abba Fader", #Author: "Okänd", #Copyright: "ccc", #CELL1: [#MARKER_NAME: "Abba Fader", #Hotkey: "1", #rtf: "{\rtf1\ansi\ansicpg1252\deff0\deflang1053{\fonttbl{\f0\froman\fprq2\fcharset0 Verdana;}{\f1\froman\fcharset0 Verdana;}}
{\colortbl ;\red255\green255\blue0;\red224\green223\blue227;}
\viewkind4\uc1\pard\cf1\b\f0\fs86 Abba Fader\par
\par
Vi \^e4r h\^e4r f\^f6r att prisa Dig\line Vi \^e4r h\^e4r med f\^f6rv\^e4ntan\line Vi \^e4r h\^e4r som ett enat folk\line Vi kommer fram till Dig\line Med v\^e5r lovs\^e5ng\line\fs59\line\fs86 Vi ropar Abba Fader\line Du som har all makt\line Vi ropar Abba Fader\line Till Dig st\^e5r allt v\^e5rt hopp\line Vi ropar Abba Fader\line V\^e5r fr\^e4lsare, befriare \^e4r Du\b0\line\pard\tx720\f1\par
\cf2\par
}
", #Align: #Left]]

View File

@ -1,13 +0,0 @@
{
"authors": [
["Okänd"]
],
"title": "Abba Fader",
"verse_order_list": [],
"verses": [
[
"Abba Fader\n\nVi är här för att prisa Dig\nVi är här med förväntan\nVi är här som ett enat folk\nVi kommer fram till Dig\nMed vår lovsång\n\nVi ropar Abba Fader\nDu som har all makt\nVi ropar Abba Fader\nTill Dig står allt vårt hopp\nVi ropar Abba Fader\nVår frälsare, befriare är Du",
"v1"
]
]
}

View File

@ -1,40 +0,0 @@
"SongID","SongNr","Title","Author","Copyright","FirstLine","PriKey","AltKey","Tempo","Focus","Theme","Scripture","Active","Songbook","TimeSig","Introduced","LastUsed","TimesUsed","CCLINr","User1","User2","User3","User4","User5","Roadmap","Overmap","FileLink1","FileLink2","Updated","Lyrics","Info","Lyrics2","Background"
"000013ab-0000-0000-0000-000000000000","0","Lift Up Your Heads"," Bryan Mierau","Public Domain","Lift up your heads and the doors","Em","NULL","NULL","NULL","NULL","NULL","1","1","NULL","NULL","NULL","0","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","2004-04-07 06:36:18.952",".Em D C D
Lift up your heads and the doors of your heart
. Am B7 Em
And the King of glory will come in
(Repeat)
.G Am D
Who is this King of Glory?
. B7 Em
The Lord strong and mighty!
.G Am D
Who is this King of Glory?
. B7
The Lord, mighty in battle!
.G Am D
Who is this King of Glory?
.B7 Em
Jesus our Messiah!
.G Am D
Who is this King of Glory?
.B7 Em
Jesus, Lord of Lords!
","NULL","Lift up your heads and the doors of your heart
And the King of glory will come in
(Repeat)
Who is this King of Glory?
The Lord strong and mighty!
Who is this King of Glory?
The Lord, mighty in battle!
Who is this King of Glory?
Jesus our Messiah!
Who is this King of Glory?
Jesus, Lord of Lords!
","NULL"
1 SongID SongNr Title Author Copyright FirstLine PriKey AltKey Tempo Focus Theme Scripture Active Songbook TimeSig Introduced LastUsed TimesUsed CCLINr User1 User2 User3 User4 User5 Roadmap Overmap FileLink1 FileLink2 Updated Lyrics Info Lyrics2 Background
2 000013ab-0000-0000-0000-000000000000 0 Lift Up Your Heads Bryan Mierau Public Domain Lift up your heads and the doors Em NULL NULL NULL NULL NULL 1 1 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 2004-04-07 06:36:18.952 .Em D C D Lift up your heads and the doors of your heart . Am B7 Em And the King of glory will come in (Repeat) .G Am D Who is this King of Glory? . B7 Em The Lord strong and mighty! .G Am D Who is this King of Glory? . B7 The Lord, mighty in battle! .G Am D Who is this King of Glory? .B7 Em Jesus our Messiah! .G Am D Who is this King of Glory? .B7 Em Jesus, Lord of Lords! NULL Lift up your heads and the doors of your heart And the King of glory will come in (Repeat) Who is this King of Glory? The Lord strong and mighty! Who is this King of Glory? The Lord, mighty in battle! Who is this King of Glory? Jesus our Messiah! Who is this King of Glory? Jesus, Lord of Lords! NULL

View File

@ -1,13 +0,0 @@
{
"authors": [
"Bryan Mierau"
],
"title": "Lift Up Your Heads",
"verse_order_list": [],
"verses": [
[
"Lift up your heads and the doors of your heart\nAnd the King of glory will come in\n(Repeat)\n\nWho is this King of Glory?\nThe Lord strong and mighty!\nWho is this King of Glory?\nThe Lord, mighty in battle!\n\nWho is this King of Glory?\nJesus our Messiah!\nWho is this King of Glory?\nJesus, Lord of Lords!\n",
"v1"
]
]
}

View File

@ -29,7 +29,7 @@ from subprocess import Popen, PIPE
TAGS1 = {'1.9.0', '1.9.1', '1.9.2', '1.9.3', '1.9.4', '1.9.5', '1.9.6', '1.9.7', '1.9.8', '1.9.9', '1.9.10',
'1.9.11', '1.9.12', '2.0', '2.1.0', '2.1.1', '2.1.2', '2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.2',
'2.3.1', '2.3.2'}
'2.3.1', '2.3.2', '2.3.3', '2.4'}
class TestBzrTags(TestCase):