Merge lp:~tomasgroth/openlp/song-import-fixes24 into lp:openlp/2.4

Proposed by Tomas Groth
Status: Merged
Merged at revision: 2666
Proposed branch: lp:~tomasgroth/openlp/song-import-fixes24
Merge into: lp:openlp/2.4
Diff against target: 407 lines (+174/-29)
12 files modified
openlp/core/__init__.py (+38/-2)
openlp/core/lib/treewidgetwithdnd.py (+6/-1)
openlp/core/ui/slidecontroller.py (+2/-0)
openlp/plugins/songs/lib/__init__.py (+2/-2)
openlp/plugins/songs/lib/importers/easyslides.py (+12/-12)
openlp/plugins/songs/lib/importers/songbeamer.py (+7/-3)
openlp/plugins/songs/lib/importers/videopsalm.py (+4/-2)
tests/functional/openlp_plugins/songs/test_easyslidesimport.py (+2/-0)
tests/resources/easyslidessongs/Amazing Grace.json (+6/-6)
tests/resources/easyslidessongs/Export_2017-01-12_BB.json (+44/-0)
tests/resources/easyslidessongs/Export_2017-01-12_BB.xml (+50/-0)
tests/resources/videopsalmsongs/videopsalm-as-safe-a-stronghold.json (+1/-1)
To merge this branch: bzr merge lp:~tomasgroth/openlp/song-import-fixes24
Reviewer Review Type Date Requested Status
Tim Bentley Approve
Review via email: mp+315319@code.launchpad.net

Description of the change

Fixes:
Bug #1487788: Importing photos does not give focus to OpenLP
Bug #1512040: Loop tooltip gets stuck to "Stop playing..."
Bug #1624661: Missing DB in unmounted disk results in Traceback
Clean search lyrics for formatting tags. Fixes bug #1655988.
Fix an issue with easyslide import not handling verse order correctly. Fixes bug #1655985.
Improve the songbeamer encoding detection. Fixes bug #1530597.
Handle a few videopsalm quirks. Fixes bug #1652851.

To post a comment you must log in.
Revision history for this message
Tomas Groth (tomasgroth) wrote :
Revision history for this message
Tim Bentley (trb143) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/core/__init__.py'
--- openlp/core/__init__.py 2016-12-31 11:05:48 +0000
+++ openlp/core/__init__.py 2017-01-22 20:24:55 +0000
@@ -177,6 +177,38 @@
177 self.shared_memory.create(1)177 self.shared_memory.create(1)
178 return False178 return False
179179
180 def is_data_path_missing(self):
181 """
182 Check if the data folder path exists.
183 """
184 data_folder_path = AppLocation.get_data_path()
185 if not os.path.exists(data_folder_path):
186 log.critical('Database was not found in: ' + data_folder_path)
187 status = QtWidgets.QMessageBox.critical(None, translate('OpenLP', 'Data Directory Error'),
188 translate('OpenLP', 'OpenLP data folder was not found in:\n\n{path}'
189 '\n\nThe location of the data folder was '
190 'previously changed from the OpenLP\'s '
191 'default location. If the data was stored on '
192 'removable device, that device needs to be '
193 'made available.\n\nYou may reset the data '
194 'location back to the default location, '
195 'or you can try to make the current location '
196 'available.\n\nDo you want to reset to the '
197 'default data location? If not, OpenLP will be '
198 'closed so you can try to fix the the problem.')
199 .format(path=data_folder_path),
200 QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
201 QtWidgets.QMessageBox.No),
202 QtWidgets.QMessageBox.No)
203 if status == QtWidgets.QMessageBox.No:
204 # If answer was "No", return "True", it will shutdown OpenLP in def main
205 log.info('User requested termination')
206 return True
207 # If answer was "Yes", remove the custom data path thus resetting the default location.
208 Settings().remove('advanced/data path')
209 log.info('Database location has been reset to the default settings.')
210 return False
211
180 def hook_exception(self, exc_type, value, traceback):212 def hook_exception(self, exc_type, value, traceback):
181 """213 """
182 Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where214 Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where
@@ -213,7 +245,7 @@
213 Settings().setValue('core/application version', openlp_version)245 Settings().setValue('core/application version', openlp_version)
214 # If data_version is different from the current version ask if we should backup the data folder246 # If data_version is different from the current version ask if we should backup the data folder
215 elif data_version != openlp_version:247 elif data_version != openlp_version:
216 if self.splash.isVisible():248 if can_show_splash and self.splash.isVisible():
217 self.splash.hide()249 self.splash.hide()
218 if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'),250 if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'),
219 translate('OpenLP', 'OpenLP has been upgraded, do you want to create '251 translate('OpenLP', 'OpenLP has been upgraded, do you want to create '
@@ -378,9 +410,13 @@
378 Registry.create()410 Registry.create()
379 Registry().register('application', application)411 Registry().register('application', application)
380 application.setApplicationVersion(get_application_version()['version'])412 application.setApplicationVersion(get_application_version()['version'])
381 # Instance check413 # Check if an instance of OpenLP is already running. Quit if there is a running instance and the user only wants one
382 if application.is_already_running():414 if application.is_already_running():
383 sys.exit()415 sys.exit()
416 # If the custom data path is missing and the user wants to restore the data path, quit OpenLP.
417 if application.is_data_path_missing():
418 application.shared_memory.detach()
419 sys.exit()
384 # Remove/convert obsolete settings.420 # Remove/convert obsolete settings.
385 Settings().remove_obsolete_settings()421 Settings().remove_obsolete_settings()
386 # First time checks in settings422 # First time checks in settings
387423
=== modified file 'openlp/core/lib/treewidgetwithdnd.py'
--- openlp/core/lib/treewidgetwithdnd.py 2016-12-31 11:05:48 +0000
+++ openlp/core/lib/treewidgetwithdnd.py 2017-01-22 20:24:55 +0000
@@ -26,7 +26,7 @@
2626
27from PyQt5 import QtCore, QtGui, QtWidgets27from PyQt5 import QtCore, QtGui, QtWidgets
2828
29from openlp.core.common import Registry29from openlp.core.common import Registry, is_win
3030
3131
32class TreeWidgetWithDnD(QtWidgets.QTreeWidget):32class TreeWidgetWithDnD(QtWidgets.QTreeWidget):
@@ -108,6 +108,11 @@
108108
109 :param event: Handle of the event pint passed109 :param event: Handle of the event pint passed
110 """110 """
111 # If we are on Windows, OpenLP window will not be set on top. For example, user can drag images to Library and
112 # the folder stays on top of the group creation box. This piece of code fixes this issue.
113 if is_win():
114 self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
115 self.setWindowState(QtCore.Qt.WindowNoState)
111 if event.mimeData().hasUrls():116 if event.mimeData().hasUrls():
112 event.setDropAction(QtCore.Qt.CopyAction)117 event.setDropAction(QtCore.Qt.CopyAction)
113 event.accept()118 event.accept()
114119
=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py 2016-12-31 11:05:48 +0000
+++ openlp/core/ui/slidecontroller.py 2017-01-22 20:24:55 +0000
@@ -714,8 +714,10 @@
714 # Reset the button714 # Reset the button
715 self.play_slides_once.setChecked(False)715 self.play_slides_once.setChecked(False)
716 self.play_slides_once.setIcon(build_icon(':/media/media_time.png'))716 self.play_slides_once.setIcon(build_icon(':/media/media_time.png'))
717 self.play_slides_once.setText(UiStrings().PlaySlidesToEnd)
717 self.play_slides_loop.setChecked(False)718 self.play_slides_loop.setChecked(False)
718 self.play_slides_loop.setIcon(build_icon(':/media/media_time.png'))719 self.play_slides_loop.setIcon(build_icon(':/media/media_time.png'))
720 self.play_slides_loop.setText(UiStrings().PlaySlidesInLoop)
719 if item.is_text():721 if item.is_text():
720 if (Settings().value(self.main_window.songs_settings_section + '/display songbar') and722 if (Settings().value(self.main_window.songs_settings_section + '/display songbar') and
721 not self.song_menu.menu().isEmpty()):723 not self.song_menu.menu().isEmpty()):
722724
=== modified file 'openlp/plugins/songs/lib/__init__.py'
--- openlp/plugins/songs/lib/__init__.py 2016-12-31 11:05:48 +0000
+++ openlp/plugins/songs/lib/__init__.py 2017-01-22 20:24:55 +0000
@@ -30,7 +30,7 @@
30from PyQt5 import QtWidgets30from PyQt5 import QtWidgets
3131
32from openlp.core.common import AppLocation32from openlp.core.common import AppLocation
33from openlp.core.lib import translate33from openlp.core.lib import translate, clean_tags
34from openlp.core.utils import CONTROL_CHARS34from openlp.core.utils import CONTROL_CHARS
35from openlp.plugins.songs.lib.db import Author, MediaFile, Song, Topic35from openlp.plugins.songs.lib.db import Author, MediaFile, Song, Topic
36from openlp.plugins.songs.lib.ui import SongStrings36from openlp.plugins.songs.lib.ui import SongStrings
@@ -381,7 +381,7 @@
381 if isinstance(song.lyrics, bytes):381 if isinstance(song.lyrics, bytes):
382 song.lyrics = str(song.lyrics, encoding='utf8')382 song.lyrics = str(song.lyrics, encoding='utf8')
383 verses = SongXML().get_verses(song.lyrics)383 verses = SongXML().get_verses(song.lyrics)
384 song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses])384 song.search_lyrics = ' '.join([clean_string(clean_tags(verse[1])) for verse in verses])
385 # The song does not have any author, add one.385 # The song does not have any author, add one.
386 if not song.authors_songs:386 if not song.authors_songs:
387 name = SongStrings.AuthorUnknown387 name = SongStrings.AuthorUnknown
388388
=== modified file 'openlp/plugins/songs/lib/importers/easyslides.py'
--- openlp/plugins/songs/lib/importers/easyslides.py 2016-12-31 11:05:48 +0000
+++ openlp/plugins/songs/lib/importers/easyslides.py 2017-01-22 20:24:55 +0000
@@ -179,7 +179,7 @@
179 reg = default_region179 reg = default_region
180 verses[reg] = {}180 verses[reg] = {}
181 # instance differentiates occurrences of same verse tag181 # instance differentiates occurrences of same verse tag
182 vt = 'V'182 vt = 'v'
183 vn = '1'183 vn = '1'
184 inst = 1184 inst = 1
185 for line in lines:185 for line in lines:
@@ -192,14 +192,14 @@
192 inst += 1192 inst += 1
193 else:193 else:
194 # separators are not used, so empty line starts a new verse194 # separators are not used, so empty line starts a new verse
195 vt = 'V'195 vt = 'v'
196 vn = len(verses[reg].get(vt, {})) + 1196 vn = len(verses[reg].get(vt, {})) + 1
197 inst = 1197 inst = 1
198 elif line[0:7] == '[region':198 elif line[0:7] == '[region':
199 reg = self._extract_region(line)199 reg = self._extract_region(line)
200 verses.setdefault(reg, {})200 verses.setdefault(reg, {})
201 if not regions_in_verses:201 if not regions_in_verses:
202 vt = 'V'202 vt = 'v'
203 vn = '1'203 vn = '1'
204 inst = 1204 inst = 1
205 elif line[0] == '[':205 elif line[0] == '[':
@@ -212,7 +212,7 @@
212 if match:212 if match:
213 marker = match.group(1).strip()213 marker = match.group(1).strip()
214 vn = match.group(2)214 vn = match.group(2)
215 vt = MarkTypes.get(marker, 'O') if marker else 'V'215 vt = MarkTypes.get(marker, 'o') if marker else 'v'
216 if regions_in_verses:216 if regions_in_verses:
217 region = default_region217 region = default_region
218 inst = 1218 inst = 1
@@ -237,13 +237,13 @@
237 lines = '\n'.join(verses[reg][vt][vn][inst])237 lines = '\n'.join(verses[reg][vt][vn][inst])
238 self.add_verse(lines, versetag)238 self.add_verse(lines, versetag)
239 SeqTypes = {239 SeqTypes = {
240 'p': 'P1',240 'p': 'p1',
241 'q': 'P2',241 'q': 'p2',
242 'c': 'C1',242 'c': 'c1',
243 't': 'C2',243 't': 'c2',
244 'b': 'B1',244 'b': 'b1',
245 'w': 'B2',245 'w': 'b2',
246 'e': 'E1'}246 'e': 'e1'}
247 # Make use of Sequence data, determining the order of verses247 # Make use of Sequence data, determining the order of verses
248 try:248 try:
249 order = str(song.Sequence).strip().split(',')249 order = str(song.Sequence).strip().split(',')
@@ -251,7 +251,7 @@
251 if not tag:251 if not tag:
252 continue252 continue
253 elif tag[0].isdigit():253 elif tag[0].isdigit():
254 tag = 'V' + tag254 tag = 'v' + tag
255 elif tag.lower() in SeqTypes:255 elif tag.lower() in SeqTypes:
256 tag = SeqTypes[tag.lower()]256 tag = SeqTypes[tag.lower()]
257 else:257 else:
258258
=== modified file 'openlp/plugins/songs/lib/importers/songbeamer.py'
--- openlp/plugins/songs/lib/importers/songbeamer.py 2016-12-31 11:05:48 +0000
+++ openlp/plugins/songs/lib/importers/songbeamer.py 2017-01-22 20:24:55 +0000
@@ -115,11 +115,15 @@
115 if os.path.isfile(import_file):115 if os.path.isfile(import_file):
116 # First open in binary mode to detect the encoding116 # First open in binary mode to detect the encoding
117 detect_file = open(import_file, 'rb')117 detect_file = open(import_file, 'rb')
118 details = chardet.detect(detect_file.read())118 self.input_file_encoding = chardet.detect(detect_file.read())['encoding']
119 detect_file.close()119 detect_file.close()
120 infile = codecs.open(import_file, 'r', details['encoding'])120 # The encoding should only be ANSI (cp1252), UTF-8, Unicode, Big-Endian-Unicode.
121 # So if it doesn't start with 'u' we default to cp1252. See:
122 # https://forum.songbeamer.com/viewtopic.php?p=419&sid=ca4814924e37c11e4438b7272a98b6f2
123 if self.input_file_encoding.lower().startswith('u'):
124 self.input_file_encoding = 'cp1252'
125 infile = open(import_file, 'rt', encoding=self.input_file_encoding)
121 song_data = infile.readlines()126 song_data = infile.readlines()
122 infile.close()
123 else:127 else:
124 continue128 continue
125 self.title = file_name.split('.sng')[0]129 self.title = file_name.split('.sng')[0]
126130
=== modified file 'openlp/plugins/songs/lib/importers/videopsalm.py'
--- openlp/plugins/songs/lib/importers/videopsalm.py 2016-12-31 11:05:48 +0000
+++ openlp/plugins/songs/lib/importers/videopsalm.py 2017-01-22 20:24:55 +0000
@@ -65,8 +65,8 @@
65 if c == '\n':65 if c == '\n':
66 if inside_quotes:66 if inside_quotes:
67 processed_content += '\\n'67 processed_content += '\\n'
68 # Put keys in quotes68 # Put keys in quotes. The '-' is for handling nagative numbers
69 elif c.isalnum() and not inside_quotes:69 elif (c.isalnum() or c == '-') and not inside_quotes:
70 processed_content += '"' + c70 processed_content += '"' + c
71 c = next(file_content_it)71 c = next(file_content_it)
72 while c.isalnum():72 while c.isalnum():
@@ -121,6 +121,8 @@
121 if 'Memo3' in song:121 if 'Memo3' in song:
122 self.add_comment(song['Memo3'])122 self.add_comment(song['Memo3'])
123 for verse in song['Verses']:123 for verse in song['Verses']:
124 if 'Text' not in verse:
125 continue
124 self.add_verse(verse['Text'], 'v')126 self.add_verse(verse['Text'], 'v')
125 if not self.finish():127 if not self.finish():
126 self.log_error('Could not import %s' % self.title)128 self.log_error('Could not import %s' % self.title)
127129
=== modified file 'tests/functional/openlp_plugins/songs/test_easyslidesimport.py'
--- tests/functional/openlp_plugins/songs/test_easyslidesimport.py 2016-12-31 11:05:48 +0000
+++ tests/functional/openlp_plugins/songs/test_easyslidesimport.py 2017-01-22 20:24:55 +0000
@@ -43,3 +43,5 @@
43 """43 """
44 self.file_import(os.path.join(TEST_PATH, 'amazing-grace.xml'),44 self.file_import(os.path.join(TEST_PATH, 'amazing-grace.xml'),
45 self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))45 self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
46 self.file_import(os.path.join(TEST_PATH, 'Export_2017-01-12_BB.xml'),
47 self.load_external_result_data(os.path.join(TEST_PATH, 'Export_2017-01-12_BB.json')))
4648
=== modified file 'tests/resources/easyslidessongs/Amazing Grace.json'
--- tests/resources/easyslidessongs/Amazing Grace.json 2016-01-08 21:42:36 +0000
+++ tests/resources/easyslidessongs/Amazing Grace.json 2017-01-22 20:24:55 +0000
@@ -6,27 +6,27 @@
6 "verses": [6 "verses": [
7 [7 [
8 "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.",8 "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.",
9 "V1"9 "v1"
10 ],10 ],
11 [11 [
12 "'Twas grace that taught my heart to fear,\nAnd grace my fears relieved;\nHow precious did that grace appear,\nThe hour I first believed!",12 "'Twas grace that taught my heart to fear,\nAnd grace my fears relieved;\nHow precious did that grace appear,\nThe hour I first believed!",
13 "V2"13 "v2"
14 ],14 ],
15 [15 [
16 "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.",16 "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.",
17 "V3"17 "v3"
18 ],18 ],
19 [19 [
20 "The Lord has promised good to me,\nHis word my hope secures;\nHe will my shield and portion be\nAs long as life endures.",20 "The Lord has promised good to me,\nHis word my hope secures;\nHe will my shield and portion be\nAs long as life endures.",
21 "V4"21 "v4"
22 ],22 ],
23 [23 [
24 "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.",24 "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.",
25 "V5"25 "v5"
26 ],26 ],
27 [27 [
28 "When we've been there a thousand years,\nBright shining as the sun,\nWe've no less days to sing God's praise\nThan when we first begun.",28 "When we've been there a thousand years,\nBright shining as the sun,\nWe've no less days to sing God's praise\nThan when we first begun.",
29 "V6"29 "v6"
30 ]30 ]
31 ]31 ]
32}32}
3333
=== added file 'tests/resources/easyslidessongs/Export_2017-01-12_BB.json'
--- tests/resources/easyslidessongs/Export_2017-01-12_BB.json 1970-01-01 00:00:00 +0000
+++ tests/resources/easyslidessongs/Export_2017-01-12_BB.json 2017-01-22 20:24:55 +0000
@@ -0,0 +1,44 @@
1{
2 "title": "BBBBBBBBB",
3 "authors": [
4 "John Newton (1725-1807)"
5 ],
6 "verses": [
7 [
8 "V1V1V1V1V1V1\nV1V1V1V1V1V1",
9 "v1"
10 ],
11 [
12 "V2V2V2V2V2V2\nV2V2V2V2V2V2",
13 "v2"
14 ],
15 [
16 "C1C1C1C1C1C1\nC1C1C1C1C1C1",
17 "c1"
18 ],
19 [
20 "C2C2C2C2C2C2\nC2C2C2C2C2C2",
21 "c2"
22 ],
23 [
24 "B1B1B1B1B1B1\nB1B1B1B1B1B1",
25 "b1"
26 ],
27 [
28 "B2B2B2B2B2B2\nB2B2B2B2B2B2",
29 "b2"
30 ],
31 [
32 "PRE1PRE1PRE1\nPRE1PRE1PRE1",
33 "p1"
34 ],
35 [
36 "PRE2PRE2PRE2\nPRE2PRE2PRE2",
37 "p2"
38 ],
39 [
40 "ENDENDENDEND\nENDENDENDEND",
41 "e1"
42 ]
43 ]
44}
045
=== added file 'tests/resources/easyslidessongs/Export_2017-01-12_BB.xml'
--- tests/resources/easyslidessongs/Export_2017-01-12_BB.xml 1970-01-01 00:00:00 +0000
+++ tests/resources/easyslidessongs/Export_2017-01-12_BB.xml 2017-01-22 20:24:55 +0000
@@ -0,0 +1,50 @@
1<?xml version="1.0" encoding="utf-8"?>
2<EasiSlides>
3 <Item>
4 <Title1>BBBBBBBBB</Title1>
5 <Title2 />
6 <Folder>NAGY</Folder>
7 <SongNumber>0</SongNumber>
8 <Contents>[1]
9V1V1V1V1V1V1
10V1V1V1V1V1V1
11[2]
12V2V2V2V2V2V2
13V2V2V2V2V2V2
14[chorus]
15C1C1C1C1C1C1
16C1C1C1C1C1C1
17[chorus 2]
18C2C2C2C2C2C2
19C2C2C2C2C2C2
20[bridge]
21B1B1B1B1B1B1
22B1B1B1B1B1B1
23[bridge 2]
24B2B2B2B2B2B2
25B2B2B2B2B2B2
26[prechorus]
27PRE1PRE1PRE1
28PRE1PRE1PRE1
29[prechorus 2]
30PRE2PRE2PRE2
31PRE2PRE2PRE2
32[ending]
33ENDENDENDEND
34ENDENDENDEND</Contents>
35 <Notations />
36 <Sequence>1,2,c,t,b,w,p,q,e</Sequence>
37 <Writer />
38 <Copyright />
39 <Category />
40 <Timing />
41 <MusicKey />
42 <Capo>-1</Capo>
43 <LicenceAdmin1 />
44 <LicenceAdmin2 />
45 <BookReference />
46 <UserReference />
47 <FormatData />
48 <Settings>10=&gt;</Settings>
49 </Item>
50</EasiSlides>
0\ No newline at end of file51\ No newline at end of file
152
=== modified file 'tests/resources/videopsalmsongs/videopsalm-as-safe-a-stronghold.json'
--- tests/resources/videopsalmsongs/videopsalm-as-safe-a-stronghold.json 2015-12-17 21:39:52 +0000
+++ tests/resources/videopsalmsongs/videopsalm-as-safe-a-stronghold.json 2017-01-22 20:24:55 +0000
@@ -1,4 +1,4 @@
1{Abbreviation:"SB1",Copyright:"Public domain",Songs:[{ID:3,Composer:"Unknown",Author:"Martin Luther",Copyright:"Public1{Abbreviation:"SB1",Copyright:"Public domain",Songs:[{ID:3,Composer:"Unknown",Author:"Martin Luther",Capo:-1,Copyright:"Public
2Domain",Theme:"tema12Domain",Theme:"tema1
3tema2",CCLI:"12345",Alias:"A safe stronghold",Memo1:"This is3tema2",CCLI:"12345",Alias:"A safe stronghold",Memo1:"This is
4the first comment4the first comment

Subscribers

People subscribed via source and target branches

to all changes: