Apr-21-2023, 03:58 PM
I admit that I'm not an expert of python, I'm working in a python version 2.6.6 (for compatibility with command like unicode). I explain why I use this version: I'm working with a program created several years ago (find the project here https://github.com/danthedeckie/OpenLP-T...-Converter). In short, the program takes a sqlite database and converts it into a proprietary database of the ProPresenter program. The problem is that I get a Unicode error that I can't resolve: . I attach the file, if you solve it a huge thank you.
There is also a db test if you want to try.. sqlite database trial
I was forgetting.. the original code was written 9 years ago for OSX (I tried to fit everything I could, for example I tried another expression for reload)
This program is a free license, thank you for all the replys
I think the error is on line 351
There is also a db test if you want to try.. sqlite database trial
I was forgetting.. the original code was written 9 years ago for OSX (I tried to fit everything I could, for example I tried another expression for reload)
This program is a free license, thank you for all the replys
I think the error is on line 351
Error:for v in ParseLyric(song['lyrics'])])
#!/usr/bin/python # -*- coding: utf-8 -*- ############################################################################# # # # OpenLP to ProPresentor Converter # # (C) 2012 Daniel Fairhead, OMNIvision # # # #---------------------------------------------------------------------------# # # # This program is free to use, change, edit, do what you want with. # # Please consider a donation to OMNIvision if you find it useful. :-) # # # # http://www.omnivision.om.org/ # # # # OMNIvision is the media & events team of Operation Mobilisation (OM) # # # # http://www.om.org/ # # # ############################################################################# # Configuration Options: OPENLP_DATABASE = r"C:\Users\Daniele Uni\Downloads\Telegram Desktop\songs.sqlite" OUTPUT_DIRECTORY = "C:/Users/Daniele/Downloads" DEFAULT_FONT = "Helvetica" DEFAULT_COLOR = ('255', '255', '255') # RGB, white. BACKGROUND_COLOR = '0 0 0 1' # RGBA black. MAX_LINES = 4 OPEN_DIR_ON_EXIT = True # If you want to open the dir of new files... # Options you probably don't need to edit, but can easily enough: MAX_VERSE_COLORS = 14 # Number of defined colors for verses VERSE_COLORS = ['0 0 1 1', # 0 = Blue '0 1 0 1', # 1 = Green '1 0.5 0 1', # 2 = Orange '1 0 1 1', # 3 = Purple '0 1 1 1', # 4 = Yellow '0.5 1 0 1', # ... '1 1 0.5 1', '0 0 1 1', '0 1 0 1', '1 0.5 0 1', '1 0 1 1', '0 1 1 1', '0.5 1 0 1', '1 1 0.5 1'] CHORUS_COLOR = '1 0 0 1' VERBOSE_NAMES = {'c': 'Chorus', 'v': 'Verse', 'b': 'Bridge', 'e': 'Ending', 'p': 'Pre-Chorus'} # Cramming everything into this one file as opposed to separating it out # into modules may be rather messy and bad practice. I do apologise. # My intention is to have this as a stand alone python script, to make # it as easy as possible to distribute. import sqlite3 as sql # To import from OpenLP import os # File loading stuff. import sys # For UTF-8 settings. try: # Python 2: "reload" is built-in reload except NameError: from importlib import reload # wasn't loaded originally. sys.stdout.encoding 'UTF-8' # Oh for Py3k everywhere. import re # For regexy stuff. import xml.parsers.expat # For Parsing OpenLP Lyrics Data from base64 import b64encode # For ProPresenter RTF Data blobs from uuid import uuid1 # For ProPresenter Slide UUIDs from datetime import datetime # Guess. # Apparently it's faster / better to compile regex: __re_uni_x = re.compile(r'\\x..') # Unicode \x form __re_uni_u = re.compile(r'\\u....') # Unicode \u form __re_year = re.compile(r'^\d\d\d\d') # Year from Copyright String __re_xml_attr = re.compile('[<>\n"]') # Stuff to strip from XML attributes ################################################### # # Short generic(ish) useful functions: # ################################################### def xml_attr(text): return (re.sub(__re_xml_attr, ' ', text.replace('&', '&').replace('"', '"')) if text != None else '') def make_uuid(): return uuid1().__str__().upper() def Verbose_names(key): # Could use a collections.defaultdict, but I can't be bothered right now. if key in VERBOSE_NAMES: return VERBOSE_NAMES[key] else: return key # Stupid BLASTED RTF. This took me a *very* long time to figure out, # and I'm not sure it's in any way anything like bullet proof now anyway. # At least it works for our database, currently. # I *never* want to work with RTF again. def AntiUnicode(text): def escape_u(t): # """ turns a '\u####' type hexadecimal unicode escape char into # it's RTF '\uxxxx' decimal. # For use in a re.sub function as the callback. """ # return r'\u' + unicode(int(t.group()[2:], 16)) + ' ' return r'\u' + unicode(int(t.group()[2:], 16)) + ' ' return re.sub(__re_uni_x, escape_u, re.sub(__re_uni_u, escape_u, text.encode('unicode-escape')))\ .replace(r'\n','\\\n') def MakeRTFBlob(text): return b64encode( '{\\rtf1\\ansi\\ansicpg1252\\cocoartf1038\\cocoasubrtf360\n{\\fonttbl\\f0\\fswiss\\fcharset0 '+DEFAULT_FONT+';}\n' + '{\\colortbl;\\red'+DEFAULT_COLOR[0]+'\\green'+DEFAULT_COLOR[1]+'\\blue'+DEFAULT_COLOR[2]+';}\n' + '\\pard\\tx560\\tx1120\\tx1680\\tx2240\\tx2800\\tx3360\\tx3920\\tx4480\\tx5040\\tx5600\\tx6160\\tx6720\\qc\\pardirnatural\n\n' + '\\f0\\fs102\\fsmilli51200 \\cf1 \\expnd0\\expndtw0\\kerning0\n\\outl0\\strokewidth-20 \\strokec0 \\uc0 ' + AntiUnicode(text) + '}') ######################## # # XML sections. (To be written to ProPresenter files) # ######################## # This is messy, and there is no way to help it, # besides having a separate template file. def VerseBlock(block_name, block_type, text_sections, color='0 0 0 0'): def list_split_substrings_by_lines(max_lines, oldlist): # Very imperative, I know. There's probably a better # (functional/pythonic) way to do this... new_list = [] for raw_item in oldlist: x = 1 new_item = '' for line in raw_item.splitlines(): if x < max_lines: new_item += line + '\n' x += 1 else: new_item += line new_list.append(new_item) new_item = '' x = 1 if new_item != '': new_list.append(new_item) new_list.reverse() # Not sure why reverse is needed... return new_list def list_split_substrings(split_by, oldlist): newlist = [] for item in oldlist: newlist += item.split(split_by) return newlist all_sections = map(unicode.strip( list_split_substrings_by_lines(MAX_LINES, list_split_substrings('\n\n', list_split_substrings ('[---]', text_sections))))) return ('<RVSlideGrouping name="' + block_name + '" uuid="' + make_uuid() + '" color="' + color + '" serialization-array-index="0"><slides containerClass="NSMutableArray">' + ''.join(map(SlideBlock, all_sections)) + '</slides></RVSlideGrouping>') def SlideBlock(text): return '<RVDisplaySlide backgroundColor="' + BACKGROUND_COLOR + '" enabled="1" highlightColor="0 0 0 0" hotKey="" label="" notes="" slideType="1" sort_index="0" UUID="' + make_uuid() + '" drawingBackgroundColor="0" chordChartPath="" serialization-array-index="0"><cues containerClass="NSMutableArray"></cues><displayElements containerClass="NSMutableArray"><RVTextElement displayDelay="0" displayName="Default" locked="0" persistent="0" typeID="0" fromTemplate="1" bezelRadius="0" drawingFill="0" drawingShadow="1" drawingStroke="0" fillColor="0 0 0 0" rotation="0" source="" adjustsHeightToFit="0" verticalAlignment="0" RTFData="' + MakeRTFBlob(text) + '" revealType="0" serialization-array-index="0"><_-RVRect3D-_position x="30" y="30" z="0" width="964" height="708"></_-RVRect3D-_position><_-D-_serializedShadow containerClass="NSMutableDictionary"><NSNumber serialization-native-value="4" serialization-dictionary-key="shadowBlurRadius"></NSNumber><NSColor serialization-native-value="0 0 0 1" serialization-dictionary-key="shadowColor"></NSColor><NSMutableString serialization-native-value="{2.82843, -2.82843}" serialization-dictionary-key="shadowOffset"></NSMutableString></_-D-_serializedShadow><stroke containerClass="NSMutableDictionary"><NSColor serialization-native-value="0 0 0 0" serialization-dictionary-key="RVShapeElementStrokeColorKey"></NSColor><NSNumber serialization-native-value="0" serialization-dictionary-key="RVShapeElementStrokeWidthKey"></NSNumber></stroke></RVTextElement></displayElements><_-RVProTransitionObject-_transitionObject transitionType="-1" transitionDuration="1" motionEnabled="0" motionDuration="20" motionSpeed="100"></_-RVProTransitionObject-_transitionObject></RVDisplaySlide>' def HeaderBlock(Name='New Song', Authors='', Artist='', CCLICopyRightInfo='', CCLILicenceNumber='', Publisher='', Notes=''): return '<RVPresentationDocument height="768" width="1024" versionNumber="500" docType="0" creatorCode="1349676880" lastDateUsed="' + datetime.now().strftime('%Y-%m-%dT%H:%M:%S') + '" usedCount="0" category="Song" resourcesDirectory="" backgroundColor="0 0 0 1" drawingBackgroundColor="0" notes="' + Notes + '" artist="' + Artist + '" author="' + Authors + '" album="" CCLIDisplay="0" CCLIArtistCredits="" CCLISongTitle="' + Name + '" CCLIPublisher="' + Publisher + '" CCLICopyrightInfo="' + CCLICopyRightInfo + '" CCLILicenseNumber="' + CCLILicenceNumber + '" chordChartPath=""><timeline timeOffSet="0" selectedMediaTrackIndex="0" unitOfMeasure="60" duration="0" loop="0"><timeCues containerClass="NSMutableArray"></timeCues><mediaTracks containerClass="NSMutableArray"></mediaTracks></timeline><bibleReference containerClass="NSMutableDictionary"></bibleReference><_-RVProTransitionObject-_transitionObject transitionType="-1" transitionDuration="1" motionEnabled="0" motionDuration="20" motionSpeed="100"></_-RVProTransitionObject-_transitionObject><groups containerClass="NSMutableArray">' #def FooterBlock(): FooterBlock = '</groups><arrangements containerClass="NSMutableArray"></arrangements></RVPresentationDocument>' ########### # # (OpenLP) Lyrics XML Parsing # ########### def ParseLyric(text): current_verses = [] def _element(name, attrs): if name == 'verse': current_verses.append(attrs) current_verses[-1]['text'] = [] def _chardata(data): current_verses[-1]['text'].append(data) parser = xml.parsers.expat.ParserCreate() parser.StartElementHandler = _element parser.CharacterDataHandler = _chardata parser.buffer_text = True parser.Parse(text,True) current_verses.reverse() #Not really sure why we need this... return current_verses ####### # # Database functions: # ####### def filterbyfield(id,table,field='id'): return [row for row in table if row[field] == id] ################################################### # # Actually do stuff: # ################################################### def main(): # First load the data from the OpenLP database: try: con = None # Fetch all the data first. Gets it in memory to use, rather than # loads of SQLlite queries. This seems to be faster, with a little # profiling. If re-writing it as loads of sqlite queries # works better for you, I'm cool with that too. print ("OpenLP to Pro-Presenter 5 converter.\n") print ("Loading Database:\n " + os.path.expanduser(OPENLP_DATABASE) + "\n") con = sql.connect(os.path.expanduser(OPENLP_DATABASE)) con.row_factory = sql.Row cur = con.cursor() cur.execute('SELECT id, title, ccli_number, copyright, comments, lyrics FROM songs') songs = cur.fetchall() cur.execute('SELECT id, display_name FROM authors') authors = dict() for author in cur.fetchall(): authors[author['id']] = author['display_name'] cur.execute('SELECT song_id, author_id FROM authors_songs') authors_songs = cur.fetchall() print (' Cool. ' + str(len(songs)) + ' songs loaded.\n' ); # Database helper functions: def get_song_authornames(song_id): return ' & '.join( [authors[id] for id in [row['author_id'] for row in authors_songs if row['song_id'] == song_id]]) except: print ("Sorry - There was a problem loading the OpenLP Database.\n" + "(" + OPENLP_DATABASE + ")\n" + "Maybe OpenLP isn't set up on this user?\n\n" + "If you know where the database is, you can edit the path at \n" + "the top of this script and set it manually.") exit() # Now go through songs and output the files. print ("And writing the new files to\n " + OUTPUT_DIRECTORY + "\n") for song in songs: song_authors = get_song_authornames(song['id']) # Find the copyright year (this would be briefer in perl...) get_year = re.match(__re_year, xml_attr(song['copyright'])) if get_year != None: copyright_year = get_year.group() copyright = song['copyright'][4:].strip() else: copyright_year = '' copyright = '' # Prepare Header Block to write: to_write = ( HeaderBlock(Name = xml_attr(song['title']), CCLILicenceNumber = xml_attr(song['ccli_number']), Notes = xml_attr(song['comments']), CCLICopyRightInfo = xml_attr(copyright_year), Publisher = xml_attr(copyright), Authors = xml_attr(song_authors)) ) # Prepare Verses to write: (funny python syntax...) to_write += ''.join( [VerseBlock(Verbose_names(v['type']) + ' ' + v['label'], v['type'], v['text'], color = CHORUS_COLOR if v['type'] == 'c'\ else VERSE_COLORS[min(MAX_VERSE_COLORS-1,int(v['label']))]) for v in ParseLyric(song['lyrics'])]) try: # Now actually write the thing. f = open(OUTPUT_DIRECTORY + song['title'].replace('/','') + '.pro5','w') f.write ( to_write + FooterBlock ) f.close() except: print ('Oh dear. Something went wrong with writing "' + song['title'] + '".\nSorry.\n\n' + 'Maybe it\'s a file-write permissions issue?\n' + 'You could try changing where this script is writing to, it\'s the line\n' + ' OUTPUT_DIRECTORY="' + OUTPUT_DIRECTORY + '"\n' + 'that will need to be changed.' ) try: f.close() except: pass exit(); print ("Finished.") if OPEN_DIR_ON_EXIT: os.system('open "' + OUTPUT_DIRECTORY + '"') if __name__ == "__main__": main()