songs2slides

A tool that automatically finds song lyrics and creates lyric slideshows
git clone https://git.ashermorgan.net/songs2slides/
Log | Files | Refs | README

commit 2d7e234b8f3a2b67b285db5703bf1b8de0f4a6b9
parent e5a1478fceb3b5e2f72fee32c070db0be9560807
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Tue, 26 Mar 2024 11:45:43 -0700

Combine create steps into one template

Diffstat:
Msongs2slides/routes.py | 4++--
Msongs2slides/static/create.css | 2+-
Msongs2slides/static/create.js | 21+++++++++++++++------
Dsongs2slides/templates/create-step-1.html | 70----------------------------------------------------------------------
Dsongs2slides/templates/create-step-2.html | 85-------------------------------------------------------------------------------
Asongs2slides/templates/create.html | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/test_routes.py | 4++--
7 files changed, 196 insertions(+), 166 deletions(-)

diff --git a/songs2slides/routes.py b/songs2slides/routes.py @@ -41,7 +41,7 @@ def home(): @bp.get('/create/') def create(): - return render_template('create-step-1.html') + return render_template('create.html', step=1, songs=[], missing=0) @bp.post('/create/') def get_lyrics(): @@ -61,7 +61,7 @@ def get_lyrics(): missing = sum([1 for x in songs if x.lyrics == None]) # Return song data - return render_template('create-step-2.html', songs=songs, missing=missing) + return render_template('create.html', step=2, songs=songs, missing=missing) @bp.post('/slides/') def create_slides(): diff --git a/songs2slides/static/create.css b/songs2slides/static/create.css @@ -66,7 +66,7 @@ textarea { resize: vertical; } -.missing { +.missing summary { color: red; } .missing summary span { diff --git a/songs2slides/static/create.js b/songs2slides/static/create.js @@ -1,10 +1,11 @@ -addEventListener("submit", () => { - if (document.querySelector('input[value=html]:checked]') === null) { - // Don't show #after-submit for HTML slide creation - document.getElementById('after-submit').hidden = false - } - if (document.getElementById('step-2')) { +addEventListener('submit', () => { + if (document.getElementById('step-1').hidden == false) { + // Show step 1 spinner + document.getElementById('post-step-1').hidden = false + } else if (document.querySelector('input[value=pptx]').checked) { + // Show step 2 downloading message document.getElementById('step-2').hidden = true + document.getElementById('post-step-2').hidden = false } }); @@ -30,3 +31,11 @@ function renumber_songs() { songs[i].children[3].children[0].onclick = () => remove_song(i) } } + +/* step 2 functions */ +function back() { + document.getElementById('step-1').hidden = false + document.getElementById('post-step-1').hidden = true + document.getElementById('step-2').hidden = true + document.getElementById('post-step-2').hidden = true +} diff --git a/songs2slides/templates/create-step-1.html b/songs2slides/templates/create-step-1.html @@ -1,70 +0,0 @@ -{% extends "layout.html" %} - -{% block head %} -<link rel="stylesheet" href="{{ url_for('static', filename='create.css') }}"/> -<script src="{{ url_for('static', filename='create.js') }}"></script> -{% endblock head %} - -{% block main %} -<form id="step-1" method="POST"> - <h1>Step 1: Select Songs</h1> - - <p> - Select the songs to include in the slide show by their title and artist. - </p> - - <template id="row-template"> - <tr> - <td>.</td> - <td> - <input name="title-" placeholder="Song title" required/> - </td> - <td> - <input name="artist-" placeholder="Song artist"/> - </td> - <td> - <input type="button" value="Remove"/> - </td> - </tr> - </template> - - <table> - <thead> - <tr> - <th></th> - <th>Title</th> - <th>Artist</th> - <th></th> - </tr> - </thead> - <tbody id="songs"> - <tr> - <td>1.</td> - <td> - <input type="text" name="title-1" placeholder="Song title" required/> - </td> - <td> - <input type="text" name="artist-1" placeholder="Song artist"/> - </td> - <td> - <input type="button" value="Remove" onclick="remove_song(1)"/> - </td> - </tr> - </tbody> - </table> - - <div id="actions"> - <input type="button" value="Add song" onclick="add_song()"/> - <button> - Next - </button> - </div> -</form> - -<div id="after-submit" class="loading-modal" hidden> - <div> - <p>Loading your song lyrics...</p> - <div class="spinner"></div> - </div> -</div> -{% endblock main %} diff --git a/songs2slides/templates/create-step-2.html b/songs2slides/templates/create-step-2.html @@ -1,85 +0,0 @@ -{% extends "layout.html" %} - -{% block head %} -<link rel="stylesheet" href="{{ url_for('static', filename='create.css') }}"/> -<script src="{{ url_for('static', filename='create.js') }}"></script> -{% endblock head %} - -{% block main %} -<form id="step-2" method="POST" action="{{ url_for('.create_slides') }}"> - <h1>Step 2: Preview Slides</h1> - <p> - Review the parsed song lyrics below and make any necessary corrections. - One blank line represents the start of a new slide and three blank - lines represent an empty slide. - </p> - - {% if missing > 0 %} - <p class="missing"> - Lyrics must be entered manually for {{ missing }} - {% if missing == 1 %} song. {% else %} songs. {% endif %} - </p> - {% endif %} - - <div id="song-lyrics"> - {% for song in songs %} - <details {% if not song.lyrics %} open class="missing" {% endif %} > - <input hidden name="title-{{ loop.index }}" - value="{{ song.title }}"/> - <input hidden name="artist-{{ loop.index }}" - value="{{ song.artist }}"/> - - <summary> - <i>{{ song.title }}</i> - - {% if song.artist %} - ({{ song.artist }}) - {% endif %} - - {% if not song.lyrics %} - <span>lyrics not found</span> - {% endif %} - </summary> - - {% if song.lyrics %} - <textarea name="lyrics-{{ loop.index }}">{{ song.lyrics }}</textarea> - {% else %} - <textarea name="lyrics-{{ loop.index }}" placeholder="{{ - 'Lyrics not found, please enter them here manually.\n\nOne ' + - 'blank line represents the start of a new slide and three ' + - 'blank lines represent an empty slide.'}}"></textarea> - {% endif %} - </details> - {% endfor %} - </div> - - <fieldset> - <legend>Output type:</legend> - <label> - <input type="radio" name="output-type" value="pptx" checked/> - PowerPoint Download - </label> - <label> - <input type="radio" name="output-type" value="html"/> - Online View - </label> - </fieldset> - - <div id="actions"> - <a href="{{ url_for('.create') }}">Back</a> - <button> - Create Slide Show - </button> - </div> -</form> - -<div id="after-submit" hidden> - <p> - Your slide show is being downloaded, - thank you for using Songs2Slides. - </p> - <p> - <a href="{{ url_for('.create') }}">Create another slide show</a> - </p> -</div> -{% endblock main %} diff --git a/songs2slides/templates/create.html b/songs2slides/templates/create.html @@ -0,0 +1,176 @@ +{% extends "layout.html" %} + +{% block head %} +<link rel="stylesheet" href="{{ url_for('static', filename='create.css') }}"/> +<script src="{{ url_for('static', filename='create.js') }}"></script> +{% endblock head %} + +{% block main %} +<form id="step-1" method="POST" {% if step == 2 %} hidden {% endif %}> + <h1>Step 1: Select Songs</h1> + + <p> + Select the songs to include in the slide show by their title and artist. + </p> + + <template id="row-template"> + <tr> + <td></td> + <td> + <input name="title-" placeholder="Song title" required/> + </td> + <td> + <input name="artist-" placeholder="Song artist"/> + </td> + <td> + <input type="button" value="Remove"/> + </td> + </tr> + </template> + + <table> + <thead> + <tr> + <th></th> + <th>Title</th> + <th>Artist</th> + <th></th> + </tr> + </thead> + <tbody id="songs"> + {% if step == 1 %} + <tr> + <td>1.</td> + <td> + <input type="text" name="title-1" placeholder="Song title" required/> + </td> + <td> + <input type="text" name="artist-1" placeholder="Song artist"/> + </td> + <td> + <input type="button" value="Remove" onclick="remove_song(1)"/> + </td> + </tr> + {% else %} + {% for song in songs %} + <tr> + <td>{{ loop.index }}.</td> + <td> + <input type="text" name="title-{{ loop.index }}" required + placeholder="Song title" value="{{ song.title }}"/> + </td> + <td> + <input type="text" name="artist-{{ loop.index }}" + placeholder="Song artist" value="{{ song.artist }}"/> + </td> + <td> + <input type="button" value="Remove" + onclick="remove_song({{ loop.index }})"/> + </td> + </tr> + {% endfor %} + {% endif %} + </tbody> + </table> + + <div id="actions"> + <input type="button" value="Add song" onclick="add_song()"/> + <button> + Next + </button> + </div> +</form> + +<div id="post-step-1" class="loading-modal" hidden> + <div> + <p>Loading your song lyrics...</p> + <div class="spinner"></div> + </div> +</div> + +<form id="step-2" method="POST" {% if step == 1 %} hidden {% endif %} + action="{{ url_for('.create_slides') }}"> + + {% set format_hint = + 'One blank line represents the start of a new slide and three blank ' + + 'lines represent an empty slide.' + %} + + <h1>Step 2: Preview Slides</h1> + <p> + Review the parsed song lyrics below and make any necessary corrections. + {{ format_hint }} + </p> + + {% if missing > 0 %} + <p> + Lyrics must be entered manually for + {{ missing }} {% if missing == 1 %} song. {% else %} songs. {% endif %} + </p> + {% endif %} + + <div> + {% for song in songs %} + <details + {% if missing == 0 and loop.index == 1 %} open {% endif %} + {% if not song.lyrics %} open class="missing" {% endif %}> + + <input hidden name="title-{{ loop.index }}" + value="{{ song.title }}"/> + <input hidden name="artist-{{ loop.index }}" + value="{{ song.artist }}"/> + + <summary> + <i>{{ song.title }}</i> + + {% if song.artist %} + ({{ song.artist }}) + {% endif %} + + {% if not song.lyrics %} + <span>lyrics not found</span> + {% endif %} + </summary> + + {% if song.lyrics %} + <textarea name="lyrics-{{ loop.index }}" + placeholder="{{format_hint}}">{{ song.lyrics }}</textarea> + {% else %} + <textarea name="lyrics-{{ loop.index }}" placeholder="{{ + 'Lyrics not found, please enter them here manually.\n\n' + + format_hint }}"></textarea> + {% endif %} + </details> + {% endfor %} + </div> + + <fieldset> + <legend>Output type:</legend> + <label> + <input type="radio" name="output-type" value="pptx" checked/> + PowerPoint Download + </label> + <label> + <input type="radio" name="output-type" value="html"/> + Online View + </label> + </fieldset> + + <div id="actions"> + <input onclick="back()" type="button" value="Back"/> + <button> + Create Slide Show + </button> + </div> +</form> + +<div id="post-step-2" hidden> + <p> + Your slide show is being downloaded, + thank you for using Songs2Slides. + </p> + <p> + <a href="{{ url_for('.create') }}">Create another slide show</a> + </p> +</div> +{% endblock main %} diff --git a/tests/test_routes.py b/tests/test_routes.py @@ -32,7 +32,7 @@ class TestRoutes(unittest.TestCase): # Assert mocks called correctly mocked_get.assert_has_calls([call('T1', 'A1'), call('T2', 'A2')]) mocked_parse.assert_has_calls([call('L1', 4), call('L2', 4)]) - mocked_render.assert_called_with('create-step-2.html', songs=songs, missing=0) + mocked_render.assert_called_with('create.html', step=2, songs=songs, missing=0) def test_get_lyrics_one_error(self): with patch('songs2slides.core.get_song_data') as mocked_get, \ @@ -58,7 +58,7 @@ class TestRoutes(unittest.TestCase): # Assert mocks called correctly mocked_get.assert_has_calls([call('T1', 'A1'), call('T2', 'A2')]) mocked_parse.assert_has_calls([call('L2', 4)]) - mocked_render.assert_called_with('create-step-2.html', songs=songs, missing=1) + mocked_render.assert_called_with('create.html', step=2, songs=songs, missing=1) def test_get_lyrics_missing_artist(self): with patch('songs2slides.core.get_song_data') as mocked_get: