← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~tomasgroth/openlp/bugfixes4 into lp:openlp

 

Tomas Groth has proposed merging lp:~tomasgroth/openlp/bugfixes4 into lp:openlp.

Requested reviews:
  OpenLP Core (openlp-core)
Related bugs:
  Bug #1387293 in OpenLP: "Unable to set Start/End time on DVD clip"
  https://bugs.launchpad.net/openlp/+bug/1387293
  Bug #1388768 in OpenLP: "Import of Words Of Worship file (WSG) fails"
  https://bugs.launchpad.net/openlp/+bug/1388768
  Bug #1388850 in OpenLP: "Find duplicate songs doesn't work on windows"
  https://bugs.launchpad.net/openlp/+bug/1388850

For more details, see:
https://code.launchpad.net/~tomasgroth/openlp/bugfixes4/+merge/240831

Change duplicate check to pass int-string tuples to workers, to workaround bug #1388850, also added multiprocessing.freeze_support to __main__ to support multiprocessing in windows builds.
Try to fix DVD 0 track length by waiting. Fixes bug 1387293.
Fix for import of Words of Worship file, bug 1388768, added tests.
-- 
https://code.launchpad.net/~tomasgroth/openlp/bugfixes4/+merge/240831
Your team OpenLP Core is requested to review the proposed merge of lp:~tomasgroth/openlp/bugfixes4 into lp:openlp.
=== modified file 'openlp.py'
--- openlp.py	2014-09-04 20:10:34 +0000
+++ openlp.py	2014-11-06 09:51:19 +0000
@@ -28,7 +28,9 @@
 ###############################################################################
 
 import sys
+import multiprocessing
 
+from openlp.core.common import is_win, is_macosx
 from openlp.core import main
 
 
@@ -36,9 +38,13 @@
     """
     Instantiate and run the application.
     """
+    # Add support for using multiprocessing from frozen Windows executable (built using PyInstaller),
+    # see https://docs.python.org/3/library/multiprocessing.html#multiprocessing.freeze_support
+    if is_win():
+        multiprocessing.freeze_support()
     # Mac OS X passes arguments like '-psn_XXXX' to the application. This argument is actually a process serial number.
     # However, this causes a conflict with other OpenLP arguments. Since we do not use this argument we can delete it
     # to avoid any potential conflicts.
-    if sys.platform.startswith('darwin'):
+    if is_macosx():
         sys.argv = [x for x in sys.argv if not x.startswith('-psn')]
     main()

=== modified file 'openlp/core/ui/projector/__init__.py'
--- openlp/core/ui/projector/__init__.py	2014-10-31 19:47:36 +0000
+++ openlp/core/ui/projector/__init__.py	2014-11-06 09:51:19 +0000
@@ -28,4 +28,4 @@
 ###############################################################################
 """
 The Projector driver module.
-"""
\ No newline at end of file
+"""

=== modified file 'openlp/plugins/media/forms/mediaclipselectorform.py'
--- openlp/plugins/media/forms/mediaclipselectorform.py	2014-10-31 22:44:22 +0000
+++ openlp/plugins/media/forms/mediaclipselectorform.py	2014-11-06 09:51:19 +0000
@@ -446,6 +446,13 @@
         # Set media length info
         self.playback_length = self.vlc_media_player.get_length()
         log.debug('playback_length: %d ms' % self.playback_length)
+        # if length is 0, wait a bit, maybe vlc will change its mind...
+        loop_count = 0
+        while self.playback_length == 0 and loop_count < 20:
+            sleep(0.1)
+            self.playback_length = self.vlc_media_player.get_length()
+            loop_count += 1
+            log.debug('in loop, playback_length: %d ms' % self.playback_length)
         self.position_slider.setMaximum(self.playback_length)
         # setup start and end time
         rounded_vlc_ms_length = int(round(self.playback_length / 100.0) * 100.0)

=== modified file 'openlp/plugins/songs/forms/duplicatesongremovalform.py'
--- openlp/plugins/songs/forms/duplicatesongremovalform.py	2014-04-21 09:49:17 +0000
+++ openlp/plugins/songs/forms/duplicatesongremovalform.py	2014-11-06 09:51:19 +0000
@@ -48,14 +48,15 @@
 
 def song_generator(songs):
     """
-    This is a generator function to return tuples of two songs. When completed then all songs have once been returned
-    combined with any other songs.
+    This is a generator function to return tuples of tuple with two songs and their position in the song array.
+    When completed then all songs have once been returned combined with any other songs.
 
     :param songs: All songs in the database.
     """
     for outer_song_counter in range(len(songs) - 1):
         for inner_song_counter in range(outer_song_counter + 1, len(songs)):
-            yield (songs[outer_song_counter], songs[inner_song_counter])
+            yield ((outer_song_counter, songs[outer_song_counter].search_lyrics),
+                   (inner_song_counter, songs[inner_song_counter].search_lyrics))
 
 
 class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
@@ -187,16 +188,17 @@
                 # Do not accept any further tasks. Also this closes the processes if all tasks are done.
                 pool.close()
                 # While the processes are still working, start to look at the results.
-                for song_tuple in result:
+                for pos_tuple in result:
                     self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1)
                     # The call to process_events() will keep the GUI responsive.
                     self.application.process_events()
                     if self.break_search:
                         pool.terminate()
                         return
-                    if song_tuple is None:
+                    if pos_tuple is None:
                         continue
-                    song1, song2 = song_tuple
+                    song1 = songs[pos_tuple[0]]
+                    song2 = songs[pos_tuple[1]]
                     duplicate_added = self.add_duplicates_to_song_list(song1, song2)
                     if duplicate_added:
                         self.found_duplicates_edit.appendPlainText(song1.title + "  =  " + song2.title)

=== modified file 'openlp/plugins/songs/lib/importers/wordsofworship.py'
--- openlp/plugins/songs/lib/importers/wordsofworship.py	2014-07-04 09:35:10 +0000
+++ openlp/plugins/songs/lib/importers/wordsofworship.py	2014-11-06 09:51:19 +0000
@@ -99,7 +99,7 @@
         """
         Initialise the Words of Worship importer.
         """
-        SongImport.__init__(self, manager, **kwargs)
+        super(WordsOfWorshipImport, self).__init__(manager, **kwargs)
 
     def do_import(self):
         """
@@ -112,17 +112,17 @@
                     return
                 self.set_defaults()
                 song_data = open(source, 'rb')
-                if song_data.read(19) != 'WoW File\nSong Words':
+                if song_data.read(19).decode() != 'WoW File\nSong Words':
                     self.log_error(source,
                                    str(translate('SongsPlugin.WordsofWorshipSongImport',
-                                                 'Invalid Words of Worship song file. Missing "Wow File\\nSong '
+                                                 'Invalid Words of Worship song file. Missing "WoW File\\nSong '
                                                  'Words" header.')))
                     continue
                 # Seek to byte which stores number of blocks in the song
                 song_data.seek(56)
                 no_of_blocks = ord(song_data.read(1))
                 song_data.seek(66)
-                if song_data.read(16) != 'CSongDoc::CBlock':
+                if song_data.read(16).decode() != 'CSongDoc::CBlock':
                     self.log_error(source,
                                    str(translate('SongsPlugin.WordsofWorshipSongImport',
                                                  'Invalid Words of Worship song file. Missing "CSongDoc::CBlock" '
@@ -131,11 +131,17 @@
                 # Seek to the beginning of the first block
                 song_data.seek(82)
                 for block in range(no_of_blocks):
+                    skip_char_at_end = True
                     self.lines_to_read = ord(song_data.read(4)[:1])
                     block_text = ''
                     while self.lines_to_read:
                         self.line_text = str(song_data.read(ord(song_data.read(1))), 'cp1252')
-                        song_data.seek(1, os.SEEK_CUR)
+                        if skip_char_at_end:
+                            skip_char = ord(song_data.read(1))
+                            # Check if we really should skip a char. In some wsg files we shouldn't
+                            if skip_char != 0:
+                                song_data.seek(-1, os.SEEK_CUR)
+                                skip_char_at_end = False
                         if block_text:
                             block_text += '\n'
                         block_text += self.line_text

=== modified file 'openlp/plugins/songs/lib/songcompare.py'
--- openlp/plugins/songs/lib/songcompare.py	2014-04-12 16:05:27 +0000
+++ openlp/plugins/songs/lib/songcompare.py	2014-11-06 09:51:19 +0000
@@ -59,12 +59,14 @@
     :param song_tupel: A tuple of two songs to compare.
     """
     song1, song2 = song_tupel
-    if len(song1.search_lyrics) < len(song2.search_lyrics):
-        small = song1.search_lyrics
-        large = song2.search_lyrics
+    pos1, lyrics1 = song1
+    pos2, lyrics2 = song2
+    if len(lyrics1) < len(lyrics2):
+        small = lyrics1
+        large = lyrics2
     else:
-        small = song2.search_lyrics
-        large = song1.search_lyrics
+        small = lyrics2
+        large = lyrics1
     differ = difflib.SequenceMatcher(a=large, b=small)
     diff_tuples = differ.get_opcodes()
     diff_no_typos = _remove_typos(diff_tuples)
@@ -77,7 +79,7 @@
             length_of_equal_blocks += _op_length(element)
 
     if length_of_equal_blocks >= MIN_BLOCK_SIZE:
-        return song1, song2
+        return pos1, pos2
     # Check 2: Similarity based on the relative length of the longest equal block.
     # Calculate the length of the largest equal block of the diff set.
     length_of_longest_equal_block = 0
@@ -85,7 +87,7 @@
         if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block:
             length_of_longest_equal_block = _op_length(element)
     if length_of_longest_equal_block > len(small) * 2 // 3:
-        return song1, song2
+        return pos1, pos2
     # Both checks failed. We assume the songs are not equal.
     return None
 

=== modified file 'tests/functional/openlp_core_ui/test_settingsform.py'
--- tests/functional/openlp_core_ui/test_settingsform.py	2014-10-30 22:53:06 +0000
+++ tests/functional/openlp_core_ui/test_settingsform.py	2014-11-06 09:51:19 +0000
@@ -157,4 +157,4 @@
 
         # THEN: The general tab's cancel() method should have been called, but not the themes tab
         mocked_general_cancel.assert_called_with()
-        self.assertEqual(0, mocked_theme_cancel.call_count, 'The Themes tab\'s cancel() should not have been called')
\ No newline at end of file
+        self.assertEqual(0, mocked_theme_cancel.call_count, 'The Themes tab\'s cancel() should not have been called')

=== modified file 'tests/functional/openlp_plugins/images/test_imagetab.py'
--- tests/functional/openlp_plugins/images/test_imagetab.py	2014-11-01 11:06:17 +0000
+++ tests/functional/openlp_plugins/images/test_imagetab.py	2014-11-06 09:51:19 +0000
@@ -95,4 +95,4 @@
         self.form.save()
         # THEN: the post process should be requested
         self.assertEqual(1, self.form.settings_form.register_post_process.call_count,
-                         'Image Post processing should have been requested')
\ No newline at end of file
+                         'Image Post processing should have been requested')

=== modified file 'tests/functional/openlp_plugins/songs/test_lib.py'
--- tests/functional/openlp_plugins/songs/test_lib.py	2014-05-07 23:52:51 +0000
+++ tests/functional/openlp_plugins/songs/test_lib.py	2014-11-06 09:51:19 +0000
@@ -58,8 +58,6 @@
             i love that old cross where the dearest and best for a world of lost sinners was slain  so ill cherish the
             old rugged cross till my trophies at last i lay down i will cling to the old rugged cross and exchange it
             some day for a crown'''
-        self.song1 = MagicMock()
-        self.song2 = MagicMock()
 
     def clean_string_test(self):
         """
@@ -92,53 +90,53 @@
         Test the songs_probably_equal function with twice the same song.
         """
         # GIVEN: Two equal songs.
-        self.song1.search_lyrics = self.full_lyrics
-        self.song2.search_lyrics = self.full_lyrics
+        song_tuple1 = (2, self.full_lyrics)
+        song_tuple2 = (4, self.full_lyrics)
 
         # WHEN: We compare those songs for equality.
-        result = songs_probably_equal((self.song1, self.song2))
+        result = songs_probably_equal((song_tuple1, song_tuple2))
 
         # THEN: The result should be a tuple..
-        assert result == (self.song1, self.song2), 'The result should be the tuble of songs'
+        assert result == (2, 4), 'The result should be the tuble of song positions'
 
     def songs_probably_equal_short_song_test(self):
         """
         Test the songs_probably_equal function with a song and a shorter version of the same song.
         """
         # GIVEN: A song and a short version of the same song.
-        self.song1.search_lyrics = self.full_lyrics
-        self.song2.search_lyrics = self.short_lyrics
+        song_tuple1 = (1, self.full_lyrics)
+        song_tuple2 = (3, self.short_lyrics)
 
         # WHEN: We compare those songs for equality.
-        result = songs_probably_equal((self.song1, self.song2))
+        result = songs_probably_equal((song_tuple1, song_tuple2))
 
         # THEN: The result should be a tuple..
-        assert result == (self.song1, self.song2), 'The result should be the tuble of songs'
+        assert result == (1, 3), 'The result should be the tuble of song positions'
 
     def songs_probably_equal_error_song_test(self):
         """
         Test the songs_probably_equal function with a song and a  very erroneous version of the same song.
         """
         # GIVEN: A song and the same song with lots of errors.
-        self.song1.search_lyrics = self.full_lyrics
-        self.song2.search_lyrics = self.error_lyrics
+        song_tuple1 = (4, self.full_lyrics)
+        song_tuple2 = (7, self.error_lyrics)
 
         # WHEN: We compare those songs for equality.
-        result = songs_probably_equal((self.song1, self.song2))
+        result = songs_probably_equal((song_tuple1, song_tuple2))
 
-        # THEN: The result should be a tuple of songs..
-        assert result == (self.song1, self.song2), 'The result should be the tuble of songs'
+        # THEN: The result should be a tuple of song positions.
+        assert result == (4, 7), 'The result should be the tuble of song positions'
 
     def songs_probably_equal_different_song_test(self):
         """
         Test the songs_probably_equal function with two different songs.
         """
         # GIVEN: Two different songs.
-        self.song1.search_lyrics = self.full_lyrics
-        self.song2.search_lyrics = self.different_lyrics
+        song_tuple1 = (5, self.full_lyrics)
+        song_tuple2 = (8, self.different_lyrics)
 
         # WHEN: We compare those songs for equality.
-        result = songs_probably_equal((self.song1, self.song2))
+        result = songs_probably_equal((song_tuple1, song_tuple2))
 
         # THEN: The result should be None.
         assert result is None, 'The result should be None'

=== added file 'tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py'
--- tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py	1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py	2014-11-06 09:51:19 +0000
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2014 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
+# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
+# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
+# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
+# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; version 2 of the License.                              #
+#                                                                             #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
+# more details.                                                               #
+#                                                                             #
+# You should have received a copy of the GNU General Public License along     #
+# with this program; if not, write to the Free Software Foundation, Inc., 59  #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
+###############################################################################
+"""
+This module contains tests for the Words of Worship song importer.
+"""
+
+import os
+
+from tests.helpers.songfileimport import SongImportTestHelper
+from openlp.plugins.songs.lib.importers.wordsofworship import WordsOfWorshipImport
+
+TEST_PATH = os.path.abspath(
+    os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'wordsofworshipsongs'))
+
+
+class TestWordsOfWorshipFileImport(SongImportTestHelper):
+
+    def __init__(self, *args, **kwargs):
+        self.importer_class_name = 'WordsOfWorshipImport'
+        self.importer_module_name = 'wordsofworship'
+        super(TestWordsOfWorshipFileImport, self).__init__(*args, **kwargs)
+
+    def test_song_import(self):
+        """
+        Test that loading a Words of Worship file works correctly
+        """
+        self.file_import([os.path.join(TEST_PATH, 'Amazing Grace (6 Verses).wow-song')],
+                         self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace (6 Verses).json')))
+        self.file_import([os.path.join(TEST_PATH, 'When morning gilds the skies.wsg')],
+                         self.load_external_result_data(os.path.join(TEST_PATH, 'When morning gilds the skies.json')))

=== added directory 'tests/resources/wordsofworshipsongs'
=== added file 'tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).json'
--- tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).json	1970-01-01 00:00:00 +0000
+++ tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).json	2014-11-06 09:51:19 +0000
@@ -0,0 +1,33 @@
+{
+    "authors": [
+        "John Newton (1725-1807)"
+    ],
+    "title": "Amazing Grace (6 Verses)",
+    "verse_order_list": [],
+    "verses": [
+        [
+            "Amazing grace! how sweet the sound\nThat saved a wretch like me;\nI once was lost, but now am found,\nWas blind, but now I see.",
+            "V"
+        ],
+        [
+            "'Twas grace that taught my heart to fear,\nAnd grace my fears relieved;\nHow precious did that grace appear,\nThe hour I first believed!",
+            "V"
+        ],
+        [
+            "Through many dangers, toils and snares\nI have already come;\n'Tis grace that brought me safe thus far,\nAnd grace will lead me home.",
+            "V"
+        ],
+        [
+            "The Lord has promised good to me,\nHis word my hope secures;\nHe will my shield and portion be\nAs long as life endures.",
+            "V"
+        ],
+        [
+            "Yes, when this heart and flesh shall fail,\nAnd mortal life shall cease,\nI shall possess within the veil\nA life of joy and peace.",
+            "V"
+        ],
+        [
+            "When we've been there ten thousand years,\nBright shining as the sun,\nWe've no less days to sing God's praise\nThan when we first begun.",
+            "V"
+        ]
+    ]
+}

=== added file 'tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).wow-song'
Binary files tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).wow-song	1970-01-01 00:00:00 +0000 and tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).wow-song	2014-11-06 09:51:19 +0000 differ
=== added file 'tests/resources/wordsofworshipsongs/When morning gilds the skies.json'
--- tests/resources/wordsofworshipsongs/When morning gilds the skies.json	1970-01-01 00:00:00 +0000
+++ tests/resources/wordsofworshipsongs/When morning gilds the skies.json	2014-11-06 09:51:19 +0000
@@ -0,0 +1,29 @@
+{
+    "authors": [
+        "Author Unknown.  Tr. Edward Caswall"
+    ],
+    "title": "When morning gilds the skies",
+    "verse_order_list": [],
+    "verses": [
+        [
+            "When morning gilds the skies\nMy heart awaking cries:\n'May Jesus Christ be prais'd!'\nAlike at work and prayer to Jesus I repair:\n'May Jesus Christ be prais'd!'",
+            "V"
+        ],
+        [
+            "Does sadness fill my mind?\nA solace here I find:\n'May Jesus Christ be praised!'\nWhen evil thoughts molest,\nWith this I shield my breast:\n'May Jesus Christ be prais'd!'",
+            "V"
+        ],
+        [
+            "To God, the Word, on high\nThe hosts of angels cry:\n'May Jesus Christ be prais'd!'\nLet mortals, too, upraise\nTheir voice in hymns of praise:\n'May Jesus Christ be prais'd!'",
+            "V"
+        ],
+        [
+            "Let earth's wide circle round\nIn joyful notes resound:\n'May Jesus Christ be prais'd!'\nLet air, and sea, and sky,\nFrom depth to height, reply:\n'May Jesus Christ be prais'd!'",
+            "V"
+        ],
+        [
+            "Be this while life is mine\nMy canticle divine\n'May Jesus Christ be prais'd!'\nBe this the eternal song,\nThrough all the ages long:\n'May Jesus Christ be prais'd!'",
+            "V"
+        ]
+    ]
+}

=== added file 'tests/resources/wordsofworshipsongs/When morning gilds the skies.wsg'
Binary files tests/resources/wordsofworshipsongs/When morning gilds the skies.wsg	1970-01-01 00:00:00 +0000 and tests/resources/wordsofworshipsongs/When morning gilds the skies.wsg	2014-11-06 09:51:19 +0000 differ

References