songs2slides

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

commit b451043d51358aaa8843f7ed51fe2c9547fe332e
parent 4a8bb379f5955a44dfdedf282e4773ff2f27daca
Author: Asher Morgan <59518073+ashermorgan@users.noreply.github.com>
Date:   Thu, 11 Jul 2024 15:24:38 -0700

Save custom song lyrics in localStorage

Diffstat:
Msongs2slides/static/create.css | 4++++
Msongs2slides/static/create.js | 55+++++++++++++++++++++++++++++++++++++++++++++++++++----
Msongs2slides/templates/create-step-1.html | 2+-
Msongs2slides/templates/create-step-2.html | 27+++++++++++----------------
Mtests/test_e2e.py | 29++++++++++++++++++++++-------
5 files changed, 89 insertions(+), 28 deletions(-)

diff --git a/songs2slides/static/create.css b/songs2slides/static/create.css @@ -75,7 +75,11 @@ textarea { .missing summary { color: var(--error); } +summary span { + display: none; +} .missing summary span { + display: inline; float: right; font-weight: bold; } diff --git a/songs2slides/static/create.js b/songs2slides/static/create.js @@ -1,11 +1,16 @@ // Global Songs2Slides localStorage prefix const PREFIX = 's2s' +// HTML form +let form = null + // Page load/reload handler addEventListener('pageshow', () => { // Correct page state after returning via browser back button document.getElementById('post-submit').hidden = true + form = document.getElementById('create-form') + if (STEP === 1) { // Load songs for (let row of document.querySelectorAll('tbody tr')) { @@ -18,9 +23,10 @@ addEventListener('pageshow', () => { raw_song.children[1].children[0].value = song.title raw_song.children[2].children[0].value = song.artist } + } else if (STEP === 2) { + load_lyrics() } else if (STEP === 3) { // Load settings - const form = document.getElementById('create-form') form['title-slides'].checked = storage_get('title-slides', true) form['blank-slides'].checked = storage_get('blank-slides', true) form['output-type'].value = storage_get('output-type', 'html') @@ -32,9 +38,10 @@ addEventListener('submit', () => { // Show loading spinner document.getElementById('post-submit').hidden = false - if (STEP === 3) { + if (STEP === 2) { + save_lyrics() + } else if (STEP === 3) { // Save settings - const form = document.getElementById('create-form') storage_set('title-slides', form['title-slides'].checked) storage_set('blank-slides', form['blank-slides'].checked) storage_set('output-type', form['output-type'].value) @@ -86,7 +93,47 @@ function save_songs() { storage_set('songs', songs) } -// Step 3 helper functions +// Step 2 functions +function get_song_key(title, artist) { + return 'lyrics-' + artist.toLowerCase().replaceAll(' ', '-') + + '-' + title.toLowerCase().replaceAll(' ', '-') +} + +function save_lyrics() { + for (let i = 1; `title-${i}` in form; i++) { + const title = form[`title-${i}`].value + const artist = form[`artist-${i}`].value + const lyrics = form[`lyrics-${i}`].value + const key = get_song_key(title, artist) + storage_set(key, lyrics) + } +} + +function load_lyrics() { + songs = document.getElementsByTagName('details') + for (let i = 1; `title-${i}` in form; i++) { + const title = form[`title-${i}`].value + const artist = form[`artist-${i}`].value + const key = get_song_key(title, artist) + const saved_lyrics = storage_get(key, '') + if (saved_lyrics !== '') { + form[`lyrics-${i}`].value = saved_lyrics + songs[i - 1].classList.remove('missing') + songs[i - 1].open = false + } + } + + // Update missing label + const number = document.getElementsByClassName('missing').length + document.getElementById('missing-count').textContent = number + if (number === 0) { + document.getElementById('missing-message').hidden = true + } else { + document.getElementById('missing-message').hidden = false + } +} + +// Local storage helper functions function storage_get(key, default_value) { try { value = JSON.parse(localStorage.getItem(`${PREFIX}.${key}`)) diff --git a/songs2slides/templates/create-step-1.html b/songs2slides/templates/create-step-1.html @@ -8,7 +8,7 @@ {% endblock head %} {% block main %} -<form method="POST" action="{{ url_for('.create_step_2') }}"> +<form id="create-form" method="POST" action="{{ url_for('.create_step_2') }}"> <h1>Step 1: Select Songs</h1> <p> diff --git a/songs2slides/templates/create-step-2.html b/songs2slides/templates/create-step-2.html @@ -14,7 +14,7 @@ %} {% block main %} -<form method="POST" action="{{ url_for('.create_step_3') }}"> +<form id="create-form" method="POST" action="{{ url_for('.create_step_3') }}"> <h1>Step 2: Review Lyrics</h1> <p> Review the parsed song lyrics below and make any necessary corrections. @@ -23,12 +23,10 @@ {{ format_hint }} </p> - {% if missing > 0 %} - <p> - Lyrics must be entered manually for <strong>{{ missing }} - song{% if missing != 1 %}s{% endif %}</strong>. + <p id="missing-message" {% if missing == 0 %} hidden {% endif %}> + Lyrics must be entered manually for + <span id="missing-count">{{ missing }}</span> song(s). </p> - {% endif %} <div> {% for song in songs %} @@ -48,19 +46,16 @@ ({{ song.artist }}) {% endif %} - {% if not song.lyrics %} - <span>lyrics not found</span> - {% endif %} + <span {% if not song.lyrics %} hidden {% endif %}> + lyrics not found + </span> </summary> - {% if song.lyrics %} - <textarea name="lyrics-{{ loop.index }}" placeholder="{{format_hint}}" - aria-label="{{ song.title }} Lyrics">{{ song.lyrics }}</textarea> - {% else %} <textarea name="lyrics-{{ loop.index }}" placeholder="{{ - 'Lyrics not found, please enter them here manually.\n\n' + - format_hint }}" aria-label="{{ song.title }} Lyrics"></textarea> - {% endif %} + 'Lyrics not found, please enter them here manually.\n\n' + if not songs.lyrics else '' }}{{ format_hint }}" + aria-label="{{ song.title }} Lyrics" + >{{ song.lyrics or '' }}</textarea> </details> {% endfor %} </div> diff --git a/tests/test_e2e.py b/tests/test_e2e.py @@ -20,11 +20,12 @@ def test_basic(page: Page): expect(page).to_have_url('http://localhost:5002/create/step-2/') # Assert missing song message is correct - expect(page.get_by_text('Lyrics must be entered manually for 1 song.')).to_be_visible() + expect(page.get_by_text('Lyrics must be entered manually for 1 song(s).')).to_be_visible() # Assert songs are loaded expect(page.get_by_text('Song 1 (Artist A)')).to_be_visible() - expect(page.get_by_text('Song 1 (Artist A) lyrics not found')).to_be_hidden() + expect(page.get_by_text('lyrics not found').first).to_be_hidden() + expect(page.get_by_text('lyrics not found').last).to_be_visible() expect(page.get_by_text('Song 5 lyrics not found')).to_be_visible() # Assert song lyrics are loaded (Song 1 lyrics still collapsed) @@ -127,7 +128,9 @@ def test_localStorage(page: Page): page.get_by_role('button', name='Next').click() expect(page).to_have_url('http://localhost:5002/create/step-2/') - # Fill in missing lyrics + # Update lyrics + page.get_by_text('Song 1 (Artist A)').click() + page.get_by_role('textbox').first.fill('custom song 1 lyrics') page.get_by_role('textbox').last.fill('custom song 5 lyrics') # Click Next @@ -166,8 +169,18 @@ def test_localStorage(page: Page): page.get_by_role('button', name='Next').click() expect(page).to_have_url('http://localhost:5002/create/step-2/') - # Fill in missing lyrics - page.get_by_role('textbox').last.fill('custom song 5 lyrics') + # Assert song lyrics are collapsed and not missing + expect(page.get_by_role('textbox')).to_have_count(0) + expect(page.get_by_text('lyrics not found').first).to_be_hidden() + expect(page.get_by_text('lyrics not found').last).to_be_hidden() + + # Uncollapse songs + page.get_by_text('Song 1 (Artist A)').click() + page.get_by_text('Song 5').click() + + # Assert song lyrics are prefilled + expect(page.get_by_role('textbox').first).to_have_value('custom song 1 lyrics') + expect(page.get_by_role('textbox').last).to_have_value('custom song 5 lyrics') # Click Next page.get_by_role('button', name='Next').click() @@ -231,7 +244,8 @@ def test_back(page: Page): # Assert songs are loaded expect(page.get_by_text('Song 1 (Artist A)')).to_be_visible() - expect(page.get_by_text('Song 1 (Artist A) lyrics not found')).to_be_hidden() + expect(page.get_by_text('lyrics not found').first).to_be_hidden() + expect(page.get_by_text('lyrics not found').last).to_be_visible() expect(page.get_by_text('Song 5 lyrics not found')).to_be_visible() # Uncollapse Song 1 @@ -253,8 +267,9 @@ def test_back(page: Page): page.get_by_role('button', name='Back').click() expect(page).to_have_url('http://localhost:5002/create/step-2/') - # Uncollapse Song 1 + # Uncollapse songs page.get_by_text('Song 1 (Artist A)').click() + page.get_by_text('Song 5').click() # Assert bad song lyrics are still loaded expect(page.get_by_role('textbox')).to_have_count(2)