add config file, created if it does not exist
This commit is contained in:
Marco van Dijk 2021-07-08 00:46:54 +02:00
parent 5be0f83c0d
commit e6a0e920af
6 changed files with 139 additions and 73 deletions

18
config.ini Normal file
View File

@ -0,0 +1,18 @@
[input]
inputfolders = /mnt/koios/Band/1-sugmesties,/mnt/koios/Band/2-oefenen,/mnt/koios/Band/3-uitgewerkt
supportedextensions = .txt
[output]
metafontfamily = fonts/CourierPrime-Regular.ttf
metafontweight = 8
lyricfontfamily = fonts/CourierPrime-Regular.ttf
tablaturefontfamliy = fonts/CourierPrime-Bold.ttf
songfontweight = 14
imagewidth = 595
imageheight = 842
backgroundcolour = 255,255,255
fontcolour = 0,0,0
metadatacolour = 128,128,128
topmargin = 10
leftmargin = 25

47
lib/config.py Normal file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env python3
##
# @file config.py
#
# @brief This program contains functions to load & create the program configuration
#
# @section description Description
# Contains the default values for all parameters
# Can create the config file update it with new settings
#
# @section notes Notes
# -
#
# @section todo TODO
# - if CMD arguments: load CMD arguments to override defaults
import os
import configparser
"""!@brief Load, creates and keeps the config up to date
"""
def initConfig():
# Default config
global config
config = configparser.ConfigParser()
# If a config file exists, load it
if os.path.isfile('./config.ini'):
config.read('config.ini')
# Else load defaults
else:
config['input'] = {'inputFolders': "/mnt/koios/Band/1-sugmesties,/mnt/koios/Band/2-oefenen,/mnt/koios/Band/3-uitgewerkt",
'supportedExtensions': ".txt"}
config['output'] = {'metafontfamily': 'fonts/CourierPrime-Regular.ttf',
'metaFontWeight': 8,
'lyricfontfamily': 'fonts/CourierPrime-Regular.ttf',
'tablaturefontfamliy': 'fonts/CourierPrime-Bold.ttf',
'songFontWeight': 14,
'imageWidth': 595, 'imageHeight': 842, # A4 at 72dpi
'backgroundColour': '255,255,255',
'fontColour': '0,0,0',
'metadataColour': '128,128,128',
'topMargin': 10,
'leftMargin': 25
}
# (if CMD arguments: load CMD arguments to override specific settings)
with open('config.ini', 'w') as configfile:
config.write(configfile)

View File

@ -45,12 +45,12 @@ def isTablatureData(inputString):
#print("Checking '{}' for line type".format(inputString))
# Assume tablature line if any digit
if any(char.isdigit() for char in inputString):
#print("'{}' is a CHORD line, since it contains a number".format(inputString))
#print("'{}' is a tablature line, since it contains a number".format(inputString))
return True
# Assume tablature line if any character {/, #, (, ), }
chordSpecificCharacterString = r"/#"
if any(elem in inputString for elem in chordSpecificCharacterString):
#print("'{}' is a CHORD line, since it contains a chord specific character".format(inputString))
tablatureSpecificCharacterString = r"/#"
if any(elem in inputString for elem in tablatureSpecificCharacterString):
#print("'{}' is a tablature line, since it contains a tablature specific character".format(inputString))
return True
# Assume LYRIC line if any TEXT character OTHER THAN {a, b, c, d, e, f, g, h, b, x, m}
lyricSpecificCharacterString = r"abcdefghbxm"
@ -64,7 +64,7 @@ def isTablatureData(inputString):
if any(elem in inputString for elem in lyricSpecialChars):
#print("'{}' is a LYRIC line, since it contains lyric specific special characters".format(inputString))
return False
# Else warn and assume chord line
# Else warn and assume tablature line
#print("Unable to identify if '{}' is a lyric or tablature line. Assuming it is a tablature line. Please improve the isTablatureData function".format(inputString))
return True
@ -74,11 +74,11 @@ class Section:
def __init__(self):
# List of lines of lyrics strings
self.lyrics = []
# List of lines of chord strings
self.chords = []
# List of lines of tablature strings
self.tablatures = []
# section type string
self.header = ""
# string of chord and lyric data
# string of tablature and lyric data
self.rawData = ""
# Flag for succesfully parsed
self.isParsed = False
@ -86,49 +86,49 @@ class Section:
"""!@brief Converts raw buffered data into separate Lyric and tablature lines
@return None
"""
# Parses self.rawData into lyrics and chord strings
# Parses self.rawData into lyrics and tablature strings
def parseMe(self):
isFirstLine = True
# Input sections may have chord-only or lyric-only sections
# So we have to insert empty lines if we have subsequent chord or lyric lines
# Input sections may have tablature-only or lyric-only sections
# So we have to insert empty lines if we have subsequent tablature or lyric lines
lines = self.rawData.split('\r\n')
for line in lines:
# Determine lyric or chord line
currentIsChord = isTablatureData(line)
# Determine lyric or tablature line
currentIsTablature = isTablatureData(line)
# Initially just fill in the first line correctly
if isFirstLine:
isFirstLine = False
if currentIsChord:
self.chords.append(line)
if currentIsTablature:
self.tablatures.append(line)
else:
self.lyrics.append(line)
# We want alternating lines, so if the prev is of the same type
# we need to insert an empty line of the other type
elif currentIsChord == prevWasChord:
if currentIsChord:
elif currentIsTablature == prevWasTablature:
if currentIsTablature:
#print("Inserting empty Lyric line")
self.chords.append(line)
self.tablatures.append(line)
self.lyrics.append("")
else:
#print("Inserting empty Chord line")
#print("Inserting empty tablature line")
self.lyrics.append(line)
self.chords.append("")
self.tablatures.append("")
# also insert the current line
elif currentIsChord:
elif currentIsTablature:
#print("Inserting empty Lyric line")
self.chords.append(line)
self.tablatures.append(line)
else:
self.lyrics.append(line)
# move on to next line, save current type
prevWasChord = currentIsChord
prevWasTablature = currentIsTablature
# Simple check to see if it probably exported correctly
if abs(len(self.lyrics) - len(self.chords)) > 1:
print("Unable to parse section, since there is a mismatch between the amount of chord and lyric lines.")
if abs(len(self.lyrics) - len(self.tablatures)) > 1:
print("Unable to parse section, since there is a mismatch between the amount of tablature and lyric lines.")
return
# Add a trailing empty line if necessary
elif len(self.lyrics) > len(self.chords):
self.chords.append("")
elif len(self.lyrics) < len(self.chords):
elif len(self.lyrics) > len(self.tablatures):
self.tablatures.append("")
elif len(self.lyrics) < len(self.tablatures):
self.lyrics.append("")
self.isParsed = True

View File

@ -16,13 +16,12 @@
# - Support both paths to folders (like now) and to files directly
# When the input is a file, check if it is .txt and init it
# - Input locations should be set in a config file (init to CWD, overwrite by CMD arguments)
# - Use config supportedExtensions
import lib.dataStructures
import lib.config
import os
# For now manually whitelist folders to convert
whitelist = ["/mnt/koios/Band/1-sugmesties", "/mnt/koios/Band/2-oefenen", "/mnt/koios/Band/3-uitgewerkt"]
"""!@brief Creates and inits a Song object
This function creates a new Song object and sets the internal variables correctly
Output folder name is derived from the name of the input file
@ -42,19 +41,22 @@ def initSong(filePath):
"""!@brief Returns the list of all Song objects created
This function gets all supported input files in the specified input location(s)
For each of these files it creates a Song object, ready to be read and then parsed
@param configObj configparser object
@return list of intialised Song objects
"""
def getSongObjects():
# Get config variables
configObj = lib.config.config['input']
# path to song folders, which MAY contain a .txt source file
txtFileLocations = []
# list of Song objects
songList = []
# go through all input locations. find .txt files.
for inputFolder in whitelist:
for inputFolder in configObj['inputfolders'].split(','):
#print("Walking directory '{}'".format(inputFolder))
for root, dirs, files in os.walk(inputFolder):
for name in files:
if(name[name.rfind('.'):] == ".txt"):
if(name[name.rfind('.'):] in configObj['supportedextensions']):
filePath = os.path.join(root, name)
#print("Found .txt file '{}'".format(filePath))
txtFileLocations.append(filePath)

View File

@ -24,9 +24,12 @@ import lib.chordFinder
import lib.dataStructures
import lib.initSongs
import lib.transpose
import lib.config
import output2img
def main():
# Init config file
lib.config.initConfig()
# Init Song objects for all songs with compatible inputs
songs = lib.initSongs.getSongObjects()
# Convert all songs into sections

View File

@ -16,46 +16,29 @@
# to a config file on first boot. Overwrite these if they get passed via CMD arguments (or manually by user)
# - Various prints should be printed at specific log levels, to easily switch between debug, info or warnings only
import lib.config
import os
import lib.dataStructures
from PIL import Image, ImageDraw, ImageFont
# size and font of metadata
metaFontFamily = 'fonts/CourierPrime-Regular.ttf'
metaFontWeight = 8
# size and font of chord and lyric text
lyricFontFamily = 'fonts/CourierPrime-Regular.ttf'
chordFontFamily = 'fonts/CourierPrime-Bold.ttf'
songFontWeight = 14
# image properties
imageWidth, imageHeight = (595, 842) # A4 at 72dpi
background = (255, 255, 255)
fontMetadata = ImageFont.truetype(metaFontFamily, metaFontWeight)
fontLyrics = ImageFont.truetype(lyricFontFamily, songFontWeight)
fontChords = ImageFont.truetype(chordFontFamily, songFontWeight)
fontColour = ()
topMargin = 10
leftMargin = 25
"""!@brief Calculates the height of rendered text
This function calculates the dimensions of each line of text
the section contains and returns the sum
@param section lib.dataStructures.Section object
@param fontTablature ImageFont.truetype object of font we are using for tablature lines
@param fontLyrics ImageFont.truetype object of font we are using for lyric lines
@return the total height of the section
"""
def calcSectionHeight(section):
def calcSectionHeight(section, fontTablature, fontLyrics):
lineIterator = 0
amountOfLines = len(section.lyrics)
heightSum = 0
# consider section title
headerWidth, headerHeight = fontChords.getsize(section.header)
headerWidth, headerHeight = fontTablature.getsize(section.header)
heightSum += headerHeight
while lineIterator < amountOfLines:
# Get chord&lyric line dimensions
lyricTextWidth, lyricTextHeight = fontLyrics.getsize(section.lyrics[lineIterator])
chordTextWidth, chordTextHeight = fontChords.getsize(section.chords[lineIterator])
tablatureTextWidth, chordTextHeight = fontTablature.getsize(section.tablatures[lineIterator])
heightSum += lyricTextHeight + chordTextHeight
lineIterator += 1
return heightSum
@ -67,6 +50,7 @@ def calcSectionHeight(section):
It will overwrite existing images, but will not clear old images
@param folderLocation path to where we want the images
@param songObj lib.dataStructures.Song object
@param configObj configparser object
@return None
"""
def outputToImage(folderLocation, songObj):
@ -77,12 +61,24 @@ def outputToImage(folderLocation, songObj):
#else:
#print("Directory " , folderLocation , " already exists")
configObj = lib.config.config['output']
topMargin = int(configObj['topMargin'])
fontColour = tuple(int(var) for var in configObj['fontColour'].split(','))
backgroundColour = tuple(int(var) for var in configObj['backgroundColour'].split(','))
metadataColour = tuple(int(var) for var in configObj['metadataColour'].split(','))
imageWidth = int(configObj['imageWidth'])
imageHeight = int(configObj['imageHeight'])
leftMargin = int(configObj['leftMargin'])
fontMetadata = ImageFont.truetype(configObj['metafontfamily'], int(configObj['metaFontWeight']))
fontLyrics = ImageFont.truetype(configObj['lyricfontfamily'], int(configObj['songFontWeight']))
fontTablature = ImageFont.truetype(configObj['tablaturefontfamliy'], int(configObj['songFontWeight']))
# Init image info
imageNumber = 1
currentHeight = topMargin
# New Image
a4image = Image.new('RGB',(imageWidth, imageHeight),(background))
a4image = Image.new('RGB',(imageWidth, imageHeight),(backgroundColour))
draw = ImageDraw.Draw(a4image)
# Write metadata
@ -93,7 +89,7 @@ def outputToImage(folderLocation, songObj):
continue
#print("meta line '{}'".format(line))
metadataTextWidth, metadataTextHeight = fontMetadata.getsize(line)
draw.text((leftMargin,currentHeight), line, fill=(128, 128, 128), font=fontMetadata)
draw.text((leftMargin,currentHeight), line, fill=metadataColour, font=fontMetadata)
currentHeight += metadataTextHeight
# Margin between metadata and the first section
currentHeight += topMargin
@ -104,32 +100,32 @@ def outputToImage(folderLocation, songObj):
# Reset section specific variables
lineIterator = 0
amountOfLines = len(section.lyrics)
if (amountOfLines != len(section.chords)):
if (amountOfLines != len(section.tablatures)):
print("Cannot write this section to file, since it was not processed correctly. There are {} tablature lines and {} lyric lines. Aborting...".format(len(section.chords), amountOfLines))
return
# See if the section would fit on the current page - if it does not, write current buffered image & make the next image ready
if currentHeight + calcSectionHeight(section) > imageHeight:
if currentHeight + calcSectionHeight(section, fontTablature, fontLyrics) > imageHeight:
#print("overflow! starting with a new image")
outputLocation = folderLocation + "/" + str(imageNumber) + ".png"
imageNumber += 1
a4image.save(outputLocation)
currentHeight = topMargin
a4image = Image.new('RGB',(imageWidth, imageHeight),(background))
a4image = Image.new('RGB',(imageWidth, imageHeight),(backgroundColour))
draw = ImageDraw.Draw(a4image)
# write section title
headerWidth, headerHeight = fontChords.getsize(section.header)
draw.text((leftMargin,currentHeight), section.header, fill=(0, 0, 0), font=fontChords)
headerWidth, headerHeight = fontTablature.getsize(section.header)
draw.text((leftMargin,currentHeight), section.header, fill=fontColour, font=fontTablature)
currentHeight += headerHeight
# Write each line tablature&lyric data
while lineIterator < amountOfLines:
#print("Printing chord line {} and lyrics line {}".format(section.chords[lineIterator], section.lyrics[lineIterator]))
# Get chord&lyric line
#print("Printing tablatures line {} and lyrics line {}".format(section.tablatures[lineIterator], section.lyrics[lineIterator]))
# Get tablatures&lyric line
lyricTextWidth, lyricTextHeight = fontLyrics.getsize(section.lyrics[lineIterator])
chordTextWidth, chordTextHeight = fontChords.getsize(section.chords[lineIterator])
tablatureTextWidth, tablatureTextHeight = fontTablature.getsize(section.tablatures[lineIterator])
# add to image file
draw.text((leftMargin,currentHeight), section.chords[lineIterator], fill=(0, 0, 0), font=fontChords)
currentHeight += chordTextHeight
draw.text((leftMargin,currentHeight), section.lyrics[lineIterator], fill=(0, 0, 0), font=fontLyrics)
draw.text((leftMargin,currentHeight), section.tablatures[lineIterator], fill=fontColour, font=fontTablature)
currentHeight += tablatureTextHeight
draw.text((leftMargin,currentHeight), section.lyrics[lineIterator], fill=fontColour, font=fontLyrics)
currentHeight += lyricTextHeight
lineIterator += 1
#print("currentheight={}".format(currentHeight))