commit a726f5b33ceeba5393f80ea1dd9bd3324ff91304
parent 70f3228dfc5c8701229890a43e9467675bd23614
Author: AsherMorgan <59518073+AsherMorgan@users.noreply.github.com>
Date: Sun, 10 May 2020 13:50:14 -0700
Merge pull request #4 from AsherMorgan/web-settings
Implement customizable settings in the web app.
Diffstat:
8 files changed, 342 insertions(+), 82 deletions(-)
diff --git a/README.md b/README.md
@@ -6,7 +6,7 @@ Not all features are currently available in the web interface.
- Can parse any song given the artist and title.
- New slides are automatically started at the beginning of verses, bridges, choruses, etc.
- The user can easily review and edit the slides.
-- The default powerpoint format may be changed at anytime in `Songs2Slides/config.py`.
+- The format of the powerpoint can be customized.
- The slides can be added to a new or existing powerpoint.
## Usage
@@ -18,8 +18,8 @@ To use the command line interface, run `cliapp.py`.
```
python cliapp.py
```
-To use the web interface, run `webapp.py` and then go to https://localhost:5000 in your web browser.
+To use the web interface, run `webapp.py` and then open http://localhost:5000 in your web browser.
```
python webapp.py
-start https://localhost:5000
+start http://localhost:5000
```
\ No newline at end of file
diff --git a/Songs2Slides/config.py b/Songs2Slides/config.py
@@ -1,32 +1,29 @@
-parsing = {
+defaultSettings = {
+ # Parsing settings
"title-slides": True,
"lines-per-slide": 4,
"remove-parentheses": False,
-}
-
-slide = {
- "width": 13.333,
- "height": 7.5,
- "color": [0, 0, 0],
-}
-margin = {
- "left": 0.5,
- "right": 0.5,
- "top": 0.5,
- "bottom": 0.5,
-}
+ # Slide settings
+ "slide-width": 13.333,
+ "slide-height": 7.5,
+ "slide-color": "#000000",
+
+ # Margin settings
+ "margin-left": 0.5,
+ "margin-right": 0.5,
+ "margin-top": 0.5,
+ "margin-bottom": 0.5,
-font = {
- "family": "Calibri",
- "size": 40,
- "bold": False,
- "italic": False,
- "color": [255, 255, 255],
-}
+ # Font settings
+ "font-family": "Calibri",
+ "font-size": 40,
+ "font-bold": False,
+ "font-italic": False,
+ "font-color": "#FFFFFF",
-paragraph = {
- "vertical-alignment": "middle", # Can be top, middle, or bottom
+ # Paragraph settings
+ "vertical-alignment": "Middle", # Can be Top, Middle, or Bottom
"line-spacing": 1.25,
"word-wrap": True
-}
-\ No newline at end of file
+}
diff --git a/Songs2Slides/models.py b/Songs2Slides/models.py
@@ -1,6 +1,5 @@
# Import dependencies
from bs4 import BeautifulSoup
-from Songs2Slides import config
from pptx import Presentation
from pptx.dml.color import RGBColor
from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
@@ -42,12 +41,12 @@ def GetLyrics(title, artist):
# Parses the lyrics of a song into slides
-def ParseLyrics(title, artist):
+def ParseLyrics(title, artist, settings):
# Get lyrics
rawLyrics, title, artist = GetLyrics(title, artist)
# Remove content in parentheses
- if (config.parsing["remove-parentheses"]):
+ if (settings["remove-parentheses"]):
rawLyrics = re.sub(r'\([^)]*\)', '', rawLyrics)
# Parse Lyrics
@@ -61,11 +60,11 @@ def ParseLyrics(title, artist):
# Add title slide
slides = []
- if (config.parsing["title-slides"]):
+ if (settings["title-slides"]):
slides += ["{0}\n{1}".format(title, artist)]
# Parse lyrics into slides
- slideSize = config.parsing["lines-per-slide"]
+ slideSize = settings["lines-per-slide"]
for i in range(0, len(rawLines)):
if (rawLines[i] == ""):
# Start a new slide without content
@@ -74,7 +73,7 @@ def ParseLyrics(title, artist):
elif (rawLines[i][0] == "["):
# Ignore
pass
- elif (slideSize == config.parsing["lines-per-slide"]):
+ elif (slideSize == settings["lines-per-slide"]):
# Start a new slide with content
slides.append(rawLines[i])
slideSize = 1
@@ -97,7 +96,7 @@ def ParseLyrics(title, artist):
# Create powerpoint
-def CreatePptx(parsedLyrics, filepath, openFirst):
+def CreatePptx(parsedLyrics, filepath, settings, openFirst):
if (openFirst):
try:
# Open presentation
@@ -107,24 +106,24 @@ def CreatePptx(parsedLyrics, filepath, openFirst):
prs = Presentation()
# Set slide width and height
- prs.slide_width = Inches(config.slide["width"])
- prs.slide_height = Inches(config.slide["height"])
+ prs.slide_width = Inches(settings["slide-width"])
+ prs.slide_height = Inches(settings["slide-height"])
else:
# Create presentation
prs = Presentation()
# Set slide width and height
- prs.slide_width = Inches(config.slide["width"])
- prs.slide_height = Inches(config.slide["height"])
+ prs.slide_width = Inches(settings["slide-width"])
+ prs.slide_height = Inches(settings["slide-height"])
# Get blank slide
blank_slide_layout = prs.slide_layouts[6]
# Get margins
- left = Inches(config.margin["left"])
- top = Inches(config.margin["top"])
- width = prs.slide_width - Inches(config.margin["left"] + config.margin["right"])
- height = prs.slide_height - Inches(config.margin["top"] + config.margin["bottom"])
+ left = Inches(settings["margin-left"])
+ top = Inches(settings["margin-top"])
+ width = prs.slide_width - Inches(settings["margin-left"] + settings["margin-right"])
+ height = prs.slide_height - Inches(settings["margin-top"] + settings["margin-bottom"])
for lyric in parsedLyrics:
# Add slide
@@ -132,7 +131,7 @@ def CreatePptx(parsedLyrics, filepath, openFirst):
# Apply slide formating
slide.background.fill.solid()
- slide.background.fill.fore_color.rgb = RGBColor(config.slide["color"][0], config.slide["color"][1], config.slide["color"][2])
+ slide.background.fill.fore_color.rgb = RGBColor.from_string(settings["slide-color"][1:])
# Add text box
txBox = slide.shapes.add_textbox(left, top, width, height)
@@ -140,10 +139,10 @@ def CreatePptx(parsedLyrics, filepath, openFirst):
tf.clear()
# Apply text formating
- tf.word_wrap = config.paragraph["word-wrap"]
- if (config.paragraph["vertical-alignment"].lower() == "top"):
+ tf.word_wrap = settings["word-wrap"]
+ if (settings["vertical-alignment"].lower() == "top"):
tf.vertical_anchor = MSO_ANCHOR.TOP
- elif (config.paragraph["vertical-alignment"].lower() == "bottom"):
+ elif (settings["vertical-alignment"].lower() == "bottom"):
tf.vertical_anchor = MSO_ANCHOR.BOTTOM
else:
tf.vertical_anchor = MSO_ANCHOR.MIDDLE
@@ -153,13 +152,13 @@ def CreatePptx(parsedLyrics, filepath, openFirst):
p.text = lyric
# Apply pharagraph formating
- p.font.name = config.font["family"]
- p.font.size = Pt(config.font["size"])
- p.font.bold = config.font["bold"]
- p.font.italic = config.font["italic"]
- p.font.color.rgb = RGBColor(config.font["color"][0], config.font["color"][1], config.font["color"][2])
+ p.font.name = settings["font-family"]
+ p.font.size = Pt(settings["font-size"])
+ p.font.bold = settings["font-bold"]
+ p.font.italic = settings["font-italic"]
+ p.font.color.rgb = RGBColor.from_string(settings["font-color"][1:])
p.alignment = PP_ALIGN.CENTER
- p.line_spacing = config.paragraph["line-spacing"]
+ p.line_spacing = settings["line-spacing"]
# Save powerpoint
- prs.save(filepath)
-\ No newline at end of file
+ prs.save(filepath)
diff --git a/Songs2Slides/routes.py b/Songs2Slides/routes.py
@@ -3,7 +3,8 @@ from flask import render_template, request, send_file, url_for, jsonify
import io
import json
import os
-from Songs2Slides import app, models, config
+from Songs2Slides import app, models
+from Songs2Slides.config import defaultSettings
import tempfile
@@ -15,16 +16,27 @@ def index():
+# Settings JSON file
+@app.route("/settings.json", methods=["GET"])
+def settings():
+ return jsonify(defaultSettings)
+
+
+
# Get Powerpoint
@app.route("/pptx", methods=["POST"])
def pptx():
- # Parse POST parameters
+ # Get settings
+ if "settings" in request.json:
+ settings = request.json["settings"]
+ else:
+ settings = defaultSettings
+
if "songs" in request.json:
- # Get lyrics
lyrics = []
for song in request.json["songs"]:
try:
- lyrics += models.ParseLyrics(song[0], song[1])
+ lyrics += models.ParseLyrics(song[0], song[1], settings)
except:
pass
elif "lyrics" in request.json:
@@ -34,7 +46,7 @@ def pptx():
try:
# Create powerpoint
temp = tempfile.NamedTemporaryFile(mode="w+t", suffix=".pptx", delete=False)
- models.CreatePptx(lyrics, temp.name, False)
+ models.CreatePptx(lyrics, temp.name, settings, False)
temp.close()
# Read file into stream
@@ -52,13 +64,19 @@ def pptx():
# Get lyrics
@app.route("/lyrics", methods=["POST"])
def lyrics():
+ # Get settings
+ if "settings" in request.json:
+ settings = request.json["settings"]
+ else:
+ settings = defaultSettings
+
# Get lyrics
lyrics = []
for song in request.json["songs"]:
try:
- lyrics += models.ParseLyrics(song[0], song[1])
+ lyrics += models.ParseLyrics(song[0], song[1], settings)
except:
pass
# Return lyrics
- return jsonify({"lyrics": lyrics})
-\ No newline at end of file
+ return jsonify({"lyrics": lyrics})
diff --git a/Songs2Slides/static/index.css b/Songs2Slides/static/index.css
@@ -3,6 +3,16 @@ body {
text-align: center;
font-family: Arial, Helvetica, sans-serif;
touch-action: manipulation;
+ margin-bottom: 50px;
+}
+
+#footer {
+ position: fixed;
+ bottom: 0px;
+ padding-bottom: 5px;
+ padding-top: 5px;
+ width: 100%;
+ background-color: #FFFFFF;
}
@@ -12,13 +22,6 @@ button {
height: 25px;
}
-#title {
- font-size: 25px;
- font-weight: bold;
- margin-bottom: 20px;
- display: inline-block;
-}
-
/******** Songs styles ********/
@@ -72,4 +75,36 @@ button {
#lyricsBack {
float: left;
+}
+
+
+
+/******** Settings styles ********/
+#settings {
+ max-width: 300px;
+ margin: auto;
+ text-align: left;
+ line-height: 30px;
+ background-color: #F0F0F0;
+ padding: 10px;
+}
+
+h3:first-child {
+ margin-top: 0px;
+}
+
+h3 {
+ margin-bottom: 0px;
+}
+
+input[type="number"] {
+ width: 75px;
+}
+
+#settingsFooter {
+ margin-top: 15px;
+}
+
+#resetButton {
+ float: right;
}
\ No newline at end of file
diff --git a/Songs2Slides/static/index.js b/Songs2Slides/static/index.js
@@ -3,6 +3,121 @@ setId = 0; // Next valid song id number
+// Finishes setting up the page
+function onLoad() {
+ // Load settings
+ if (localStorage.getItem("settings") == null) {
+ resetSettings();
+ }
+ else {
+ loadSettings(JSON.parse(localStorage.getItem("settings")));
+ }
+
+ // Add song
+ AddSong();
+}
+
+
+
+// Shows settings interface
+function showSettings() {
+ document.getElementById("songs").hidden = true;
+ document.getElementById("lyricsContainer").hidden = true;
+ document.getElementById("thankyou").hidden = true;
+ document.getElementById("settings").hidden = false;
+}
+
+
+
+// Loads settings
+function loadSettings(settings) {
+ // Parsing settings
+ document.getElementById("title-slides").checked = settings["title-slides"];
+ document.getElementById("lines-per-slide").value = settings["lines-per-slide"];
+ document.getElementById("remove-parentheses").checked = settings["remove-parentheses"];
+
+ // Slide settings
+ document.getElementById("slide-width").value = settings["slide-width"];
+ document.getElementById("slide-height").value = settings["slide-height"];
+ document.getElementById("slide-color").value = settings["slide-color"];
+
+ // Margin settings
+ document.getElementById("margin-left").value = settings["margin-left"];
+ document.getElementById("margin-right").value = settings["margin-right"];
+ document.getElementById("margin-top").value = settings["margin-top"];
+ document.getElementById("margin-bottom").value = settings["margin-bottom"];
+
+ // Font settings
+ document.getElementById("font-family").value = settings["font-family"];
+ document.getElementById("font-size").value = settings["font-size"];
+ document.getElementById("font-bold").checked = settings["font-bold"];
+ document.getElementById("font-italic").checked = settings["font-italic"];
+ document.getElementById("font-color").value = settings["font-color"];
+
+ // Pharagraph settings
+ document.getElementById("vertical-alignment").value = settings["vertical-alignment"];
+ document.getElementById("line-spacing").value = settings["line-spacing"];
+ document.getElementById("word-wrap").checked = settings["word-wrap"];
+}
+
+
+
+// Saves settings to local storage
+function saveSettings() {
+ // Get settings
+ const settings = {
+ // Parsing settings
+ "title-slides": document.getElementById("title-slides").checked,
+ "lines-per-slide": Number(document.getElementById("lines-per-slide").value),
+ "remove-parentheses": document.getElementById("remove-parentheses").checked,
+
+ // Slide settings
+ "slide-width": Number(document.getElementById("slide-width").value),
+ "slide-height": Number(document.getElementById("slide-height").value),
+ "slide-color": document.getElementById("slide-color").value,
+
+ // Margin settings
+ "margin-left": Number(document.getElementById("margin-left").value),
+ "margin-right": Number(document.getElementById("margin-right").value),
+ "margin-top": Number(document.getElementById("margin-top").value),
+ "margin-bottom": Number(document.getElementById("margin-bottom").value),
+
+ // Font settings
+ "font-family": document.getElementById("font-family").value,
+ "font-size": Number(document.getElementById("font-size").value),
+ "font-bold": document.getElementById("font-bold").checked,
+ "font-italic": document.getElementById("font-italic").checked,
+ "font-color": document.getElementById("font-color").value,
+
+ // Pharagraph settings
+ "vertical-alignment": document.getElementById("vertical-alignment").value,
+ "line-spacing": Number(document.getElementById("line-spacing").value),
+ "word-wrap": document.getElementById("word-wrap").checked,
+ }
+
+ // Save settings
+ localStorage.setItem("settings", JSON.stringify(settings));
+}
+
+
+
+// Resets all settings to their default values
+async function resetSettings() {
+ // Send POST request
+ const rawResponse = await fetch("/settings.json", {
+ method: 'GET'
+ });
+ const json = await rawResponse.json();
+
+ // Load changes
+ loadSettings(json);
+
+ // Save changes
+ localStorage.setItem("settings", JSON.stringify(json));
+}
+
+
+
// Adds a song
function AddSong() {
// Create row
@@ -58,7 +173,7 @@ async function SubmitSongs() {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
- }, body: JSON.stringify({"songs":songs})
+ }, body: JSON.stringify({"songs":songs,"settings":JSON.parse(localStorage.getItem("settings"))})
});
// Download powerpoint
@@ -82,7 +197,7 @@ async function ReviewLyrics() {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
- }, body: JSON.stringify({"songs":songs})
+ }, body: JSON.stringify({"songs":songs,"settings":JSON.parse(localStorage.getItem("settings"))})
});
const json = await rawResponse.json();
@@ -107,7 +222,7 @@ async function SubmitLyrics() {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
- }, body: JSON.stringify({"lyrics":lyrics})
+ }, body: JSON.stringify({"lyrics":lyrics,"settings":JSON.parse(localStorage.getItem("settings"))})
});
// Download powerpoint
@@ -125,6 +240,7 @@ function Back() {
document.getElementById("songs").hidden = false;
document.getElementById("lyricsContainer").hidden = true;
document.getElementById("thankyou").hidden = true;
+ document.getElementById("settings").hidden = true;
}
@@ -143,4 +259,4 @@ function Reset() {
// Makes songs visible
Back();
-}
-\ No newline at end of file
+}
diff --git a/Songs2Slides/templates/index.html b/Songs2Slides/templates/index.html
@@ -7,8 +7,8 @@
<script src="{{ url_for('static', filename='index.js') }}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/downloadjs/1.4.8/download.min.js"></script>
</head>
- <body onload="AddSong()">
- <label id="title">Songs2Slides</label>
+ <body onload="onLoad();">
+ <h1>Songs2Slides</h1>
<div id="songs">
<div id="songsButtons">
@@ -42,5 +42,104 @@
<p>Thankyou for using Songs2Slides</p>
<p>Click <a href="javascript:Reset()">here</a> to create another PowerPoint.</p>
</div>
+
+ <div id="settings" hidden>
+ <h3>Parsing:</h3>
+ <div>
+ Add title slides:
+ <input type="checkbox" id="title-slides" onchange="saveSettings();"/>
+ </div>
+ <div>
+ Default lines per slide:
+ <input type="number" id="lines-per-slide" onchange="saveSettings();"/>
+ </div>
+ <div>
+ Remove content in parentheses:
+ <input type="checkbox" id="remove-parentheses" onchange="saveSettings();"/>
+ </div>
+
+ <h3>Slide settings</h3>
+ <div>
+ Width (Inches):
+ <input type="number" id="slide-width" onchange="saveSettings();"/>
+ </div>
+ <div>
+ Height (Inches):
+ <input type="number" id="slide-height" onchange="saveSettings();"/>
+ </div>
+ <div>
+ Background color:
+ <input type="color" id="slide-color" onchange="saveSettings();"/>
+ </div>
+
+ <h3>Margin settings</h3>
+ <div>
+ Left (Inches):
+ <input type="number" id="margin-left" onchange="saveSettings();"/>
+ </div>
+ <div>
+ Right (Inches):
+ <input type="number" id="margin-right" onchange="saveSettings();"/>
+ </div>
+ <div>
+ Top (Inches):
+ <input type="number" id="margin-top" onchange="saveSettings();"/>
+ </div>
+ <div>
+ Bottom (Inches):
+ <input type="number" id="margin-bottom" onchange="saveSettings();"/>
+ </div>
+
+ <h3>Font settings</h3>
+ <div>
+ Family:
+ <select id="font-family" onchange="saveSettings();">
+ <option>Calibri</option>
+ </select>
+ </div>
+ <div>
+ Size:
+ <input type="number" id="font-size" onchange="saveSettings();"/>
+ </div>
+ <div>
+ Bold:
+ <input type="checkbox" id = "font-bold" onchange="saveSettings();"/>
+ </div>
+ <div>
+ Italic:
+ <input type="checkbox" id="font-italic" onchange="saveSettings();"/>
+ </div>
+ <div>
+ Color:
+ <input type="color" id="font-color" onchange="saveSettings();"/>
+ </div>
+
+ <h3>Pharagraph settings</h3>
+ <div>
+ Vertical Alignment:
+ <select id="vertical-alignment" onchange="saveSettings();">
+ <option>Top</option>
+ <option>Middle</option>
+ <option>Bottom</option>
+ </select>
+ </div>
+ <div>
+ Line spacing:
+ <input type="number" id="line-spacing" onchange="saveSettings();"/>
+ </div>
+ <div>
+ Word wrap:
+ <input type="checkbox" id="word-wrap" onchange="saveSettings();"/>
+ </div>
+
+ <div id="settingsFooter">
+ <a href="javascript:Back();">Back</a>
+ <button id="resetButton" onclick="resetSettings();">Reset</button>
+ </div>
+ </div>
+
+ <div id="footer">
+ <a href="javascript:showSettings();">Settings</a>
+ </div>
</body>
</html>
\ No newline at end of file
diff --git a/cliapp.py b/cliapp.py
@@ -1,7 +1,8 @@
# Import dependencies
import json
import os
-from Songs2Slides import models, config
+from Songs2Slides import models
+from Songs2Slides.config import defaultSettings
import subprocess
import sys
import tempfile
@@ -24,7 +25,7 @@ if (__name__ == "__main__"):
# Get song lyrics
try:
- lyrics += models.ParseLyrics(title, artist)
+ lyrics += models.ParseLyrics(title, artist, defaultSettings)
except:
print("The song could not be found. Make sure that you spelled it correctly.")
song -= 1
@@ -79,7 +80,7 @@ if (__name__ == "__main__"):
# Create powerpoint
try:
- models.CreatePptx(lyrics, filepath, openFirst)
+ models.CreatePptx(lyrics, filepath, defaultSettings, openFirst)
except:
print("There was an error while creating the powerpoint.")
input("Press enter to exit...")
@@ -92,4 +93,4 @@ if (__name__ == "__main__"):
except:
print("There was an error while opening the powerpoint.")
input("Press enter to exit...")
- sys.exit()
-\ No newline at end of file
+ sys.exit()