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

View File

@ -16,13 +16,12 @@
# - Support both paths to folders (like now) and to files directly # - 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 # 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) # - Input locations should be set in a config file (init to CWD, overwrite by CMD arguments)
# - Use config supportedExtensions
import lib.dataStructures import lib.dataStructures
import lib.config
import os 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 """!@brief Creates and inits a Song object
This function creates a new Song object and sets the internal variables correctly 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 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 """!@brief Returns the list of all Song objects created
This function gets all supported input files in the specified input location(s) 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 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 @return list of intialised Song objects
""" """
def getSongObjects(): def getSongObjects():
# Get config variables
configObj = lib.config.config['input']
# path to song folders, which MAY contain a .txt source file # path to song folders, which MAY contain a .txt source file
txtFileLocations = [] txtFileLocations = []
# list of Song objects # list of Song objects
songList = [] songList = []
# go through all input locations. find .txt files. # 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 root, dirs, files in os.walk(inputFolder):
for name in files: for name in files:
if(name[name.rfind('.'):] == ".txt"): if(name[name.rfind('.'):] in configObj['supportedextensions']):
filePath = os.path.join(root, name) filePath = os.path.join(root, name)
#print("Found .txt file '{}'".format(filePath)) #print("Found .txt file '{}'".format(filePath))
txtFileLocations.append(filePath) txtFileLocations.append(filePath)

View File

@ -24,9 +24,12 @@ import lib.chordFinder
import lib.dataStructures import lib.dataStructures
import lib.initSongs import lib.initSongs
import lib.transpose import lib.transpose
import lib.config
import output2img import output2img
def main(): def main():
# Init config file
lib.config.initConfig()
# Init Song objects for all songs with compatible inputs # Init Song objects for all songs with compatible inputs
songs = lib.initSongs.getSongObjects() songs = lib.initSongs.getSongObjects()
# Convert all songs into sections # 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) # 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 # - Various prints should be printed at specific log levels, to easily switch between debug, info or warnings only
import lib.config
import os import os
import lib.dataStructures
from PIL import Image, ImageDraw, ImageFont 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 """!@brief Calculates the height of rendered text
This function calculates the dimensions of each line of text This function calculates the dimensions of each line of text
the section contains and returns the sum the section contains and returns the sum
@param section lib.dataStructures.Section object @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 @return the total height of the section
""" """
def calcSectionHeight(section): def calcSectionHeight(section, fontTablature, fontLyrics):
lineIterator = 0 lineIterator = 0
amountOfLines = len(section.lyrics) amountOfLines = len(section.lyrics)
heightSum = 0 heightSum = 0
# consider section title # consider section title
headerWidth, headerHeight = fontChords.getsize(section.header) headerWidth, headerHeight = fontTablature.getsize(section.header)
heightSum += headerHeight heightSum += headerHeight
while lineIterator < amountOfLines: while lineIterator < amountOfLines:
# Get chord&lyric line dimensions # Get chord&lyric line dimensions
lyricTextWidth, lyricTextHeight = fontLyrics.getsize(section.lyrics[lineIterator]) lyricTextWidth, lyricTextHeight = fontLyrics.getsize(section.lyrics[lineIterator])
chordTextWidth, chordTextHeight = fontChords.getsize(section.chords[lineIterator]) tablatureTextWidth, chordTextHeight = fontTablature.getsize(section.tablatures[lineIterator])
heightSum += lyricTextHeight + chordTextHeight heightSum += lyricTextHeight + chordTextHeight
lineIterator += 1 lineIterator += 1
return heightSum return heightSum
@ -67,22 +50,35 @@ def calcSectionHeight(section):
It will overwrite existing images, but will not clear old images It will overwrite existing images, but will not clear old images
@param folderLocation path to where we want the images @param folderLocation path to where we want the images
@param songObj lib.dataStructures.Song object @param songObj lib.dataStructures.Song object
@param configObj configparser object
@return None @return None
""" """
def outputToImage(folderLocation, songObj): def outputToImage(folderLocation, songObj):
# Create target Directory if doesn't exist # Create target Directory if doesn't exist
if not os.path.exists(folderLocation): if not os.path.exists(folderLocation):
os.mkdir(folderLocation) os.mkdir(folderLocation)
print("Directory " , folderLocation , " Created ") print("Directory " , folderLocation , " Created ")
#else: #else:
#print("Directory " , folderLocation , " already exists") #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 # Init image info
imageNumber = 1 imageNumber = 1
currentHeight = topMargin currentHeight = topMargin
# New Image # New Image
a4image = Image.new('RGB',(imageWidth, imageHeight),(background)) a4image = Image.new('RGB',(imageWidth, imageHeight),(backgroundColour))
draw = ImageDraw.Draw(a4image) draw = ImageDraw.Draw(a4image)
# Write metadata # Write metadata
@ -93,7 +89,7 @@ def outputToImage(folderLocation, songObj):
continue continue
#print("meta line '{}'".format(line)) #print("meta line '{}'".format(line))
metadataTextWidth, metadataTextHeight = fontMetadata.getsize(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 currentHeight += metadataTextHeight
# Margin between metadata and the first section # Margin between metadata and the first section
currentHeight += topMargin currentHeight += topMargin
@ -104,32 +100,32 @@ def outputToImage(folderLocation, songObj):
# Reset section specific variables # Reset section specific variables
lineIterator = 0 lineIterator = 0
amountOfLines = len(section.lyrics) 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)) 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 return
# See if the section would fit on the current page - if it does not, write current buffered image & make the next image ready # 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") #print("overflow! starting with a new image")
outputLocation = folderLocation + "/" + str(imageNumber) + ".png" outputLocation = folderLocation + "/" + str(imageNumber) + ".png"
imageNumber += 1 imageNumber += 1
a4image.save(outputLocation) a4image.save(outputLocation)
currentHeight = topMargin currentHeight = topMargin
a4image = Image.new('RGB',(imageWidth, imageHeight),(background)) a4image = Image.new('RGB',(imageWidth, imageHeight),(backgroundColour))
draw = ImageDraw.Draw(a4image) draw = ImageDraw.Draw(a4image)
# write section title # write section title
headerWidth, headerHeight = fontChords.getsize(section.header) headerWidth, headerHeight = fontTablature.getsize(section.header)
draw.text((leftMargin,currentHeight), section.header, fill=(0, 0, 0), font=fontChords) draw.text((leftMargin,currentHeight), section.header, fill=fontColour, font=fontTablature)
currentHeight += headerHeight currentHeight += headerHeight
# Write each line tablature&lyric data # Write each line tablature&lyric data
while lineIterator < amountOfLines: while lineIterator < amountOfLines:
#print("Printing chord line {} and lyrics line {}".format(section.chords[lineIterator], section.lyrics[lineIterator])) #print("Printing tablatures line {} and lyrics line {}".format(section.tablatures[lineIterator], section.lyrics[lineIterator]))
# Get chord&lyric line # Get tablatures&lyric line
lyricTextWidth, lyricTextHeight = fontLyrics.getsize(section.lyrics[lineIterator]) 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 # add to image file
draw.text((leftMargin,currentHeight), section.chords[lineIterator], fill=(0, 0, 0), font=fontChords) draw.text((leftMargin,currentHeight), section.tablatures[lineIterator], fill=fontColour, font=fontTablature)
currentHeight += chordTextHeight currentHeight += tablatureTextHeight
draw.text((leftMargin,currentHeight), section.lyrics[lineIterator], fill=(0, 0, 0), font=fontLyrics) draw.text((leftMargin,currentHeight), section.lyrics[lineIterator], fill=fontColour, font=fontLyrics)
currentHeight += lyricTextHeight currentHeight += lyricTextHeight
lineIterator += 1 lineIterator += 1
#print("currentheight={}".format(currentHeight)) #print("currentheight={}".format(currentHeight))