diff --git a/app.py b/app.py index 2268f38..91ef396 100644 --- a/app.py +++ b/app.py @@ -146,7 +146,7 @@ def songindex(): song_info = db.execute(''' select - song_album.song_id, album_id, track, song.name, song.name_jp, count(clip.song_id) + song_album.song_id, album_id, (select code from album where id = (select min(album_id) from song_album where song_album.song_id=song.id)) as earliest_album, track, song.name, song.name_jp, count(clip.song_id) from song_album join @@ -213,44 +213,6 @@ def motifindex(): motif_info = motif_info ) -# ALBUM PAGES - -@app.route('/album/') -def albumpage(id): - - db = openbook() - - album_info = db.execute(''' - select - name, date - from - album - where - id = ? - ''', - (id,) - ).fetchone() - - song_info = db.execute(''' - select - song_id, track, song.name, song.name_jp - from - song_album - join - song - on - song_album.song_id=song.id - where - album_id = ? - ''', - (id,) - ).fetchall() - - return flask.render_template('album.jinja', - album_info=album_info, - song_info=song_info - ) - # MOTIF PAGES @app.route('/motif/') @@ -275,7 +237,7 @@ def motifpage(id): clip_info = db.execute(''' select - song_id, motif_id, song.name, feature + song_id, motif_id, song.name, feature, song.location from clip join @@ -385,7 +347,11 @@ def songpage(id): clip_info = db.execute(''' select - song_id, motif_id, motif.name + song_id, + motif_id, + motif.name, + (select song_id from clip where motif_id = motif.id and feature == 1) as feature_clip, + (select name from song where (select song_id from clip where motif_id = motif.id and feature == 1) = song.id) as feature_name from clip join diff --git a/build_clips.py b/build_clips.py index 39b3fab..386a403 100644 --- a/build_clips.py +++ b/build_clips.py @@ -29,7 +29,7 @@ for song, motif, start, duration, album, track in db.execute(''' album_folder = f'{album - 1:02}. *' song_file = f'{track:03}. *.flac' - source = glob.glob(f"/mnt/petal/media/Audio/ffxiv/{album_folder}/{song_file}") + source = glob.glob(f"/mnt/petal/media/Audio/Music/Soundtracks/Final Fantasy XIV/{album_folder}/{song_file}") assert(len(source) == 1) source = source[0] diff --git a/docker-compose.yaml b/docker-compose.yaml index 7f8c236..5529235 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -7,4 +7,5 @@ services: volumes: - type: bind source: ./suggestions.csv - target: /opt/app/suggestions.csv \ No newline at end of file + target: /opt/app/suggestions.csv + restart: unless-stopped \ No newline at end of file diff --git a/reference.ods b/reference.ods index 15e5b3e..4d90331 100644 Binary files a/reference.ods and b/reference.ods differ diff --git a/reference_clips.ods b/reference_clips.ods index 0d28268..b9be1b7 100644 Binary files a/reference_clips.ods and b/reference_clips.ods differ diff --git a/static/albumicons/1.0.svg b/static/albumicons/1.0.svg deleted file mode 100644 index 5fe785a..0000000 --- a/static/albumicons/1.0.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - diff --git a/static/albumicons/2.0.svg b/static/albumicons/2.0.svg deleted file mode 100644 index e03231c..0000000 --- a/static/albumicons/2.0.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - diff --git a/static/albumicons/2.5.svg b/static/albumicons/2.5.svg deleted file mode 100644 index 31601e3..0000000 --- a/static/albumicons/2.5.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - diff --git a/static/albumicons/3.0.svg b/static/albumicons/3.0.svg deleted file mode 100644 index 988e051..0000000 --- a/static/albumicons/3.0.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - diff --git a/static/albumicons/3.5.svg b/static/albumicons/3.5.svg deleted file mode 100644 index 8399ed2..0000000 --- a/static/albumicons/3.5.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - diff --git a/static/albumicons/4.0.svg b/static/albumicons/4.0.svg deleted file mode 100644 index e76ba5d..0000000 --- a/static/albumicons/4.0.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - diff --git a/static/albumicons/5.0.svg b/static/albumicons/5.0.svg deleted file mode 100644 index 750c9d0..0000000 --- a/static/albumicons/5.0.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - diff --git a/static/albumicons/5.5.svg b/static/albumicons/5.5.svg deleted file mode 100644 index be0833c..0000000 --- a/static/albumicons/5.5.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - diff --git a/static/albumicons/6.0.svg b/static/albumicons/6.0.svg deleted file mode 100644 index 0088180..0000000 --- a/static/albumicons/6.0.svg +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - diff --git a/static/cliparrow-horizontal.svg b/static/cliparrow-horizontal.svg new file mode 100644 index 0000000..0c6ce0b --- /dev/null +++ b/static/cliparrow-horizontal.svg @@ -0,0 +1,55 @@ + + + + + + + + + + diff --git a/static/cliparrow-vertical.svg b/static/cliparrow-vertical.svg new file mode 100644 index 0000000..d525e85 --- /dev/null +++ b/static/cliparrow-vertical.svg @@ -0,0 +1,48 @@ + + + + + + + + + + diff --git a/static/style.css b/static/style.css index a4eabd8..45dd1aa 100644 --- a/static/style.css +++ b/static/style.css @@ -18,7 +18,7 @@ :root{ --block: lch(20% 0 75); --box: lch(40% 2.5 75); - --back: radial-gradient(lch(10% 0 75) 50%, black ); + --back: radial-gradient(lch(10% 0 75) 42%, black ); --search-back: lch(35% 0 75); --thick-border: lch(60% 10 75); --thin-border: lch(60% 10 75); @@ -198,13 +198,13 @@ header{ color: lch(90% 25 85); } -.home-title p{ +.home-title h4{ position: absolute; right: .3rem; bottom: .3rem; - /* font-size: small; */ + font-size: small; margin: 0; - color: lch(80% 50 50); + color: lch(90% 25 85); } .home-body{ @@ -217,13 +217,12 @@ header{ } .home-body p{ - margin-left: 1.2rem; margin-right: 1.2rem; margin-top: .6rem; + margin-left: 1.2rem; margin-right: 1.2rem; margin-top: .6rem; margin-bottom: 1.2rem; text-align: center; } .home-nav{ display: flex; gap: 1.2rem; - /* border-style: solid; border-width: 1px; border-color: var(--thin-border); border-left: 0; border-top: 0; border-right: 0; */ margin: 1.2rem; margin-bottom: 0; padding-bottom: 1.2rem; } @@ -238,6 +237,13 @@ header{ padding-top: 1.2rem; padding-bottom: 1.2rem; } +.home-clips{ + display: flex; flex-direction: column; justify-items: center; + width: min-content; + gap: .6rem; + margin: auto; +} + /* SEARCH BAR */ @@ -327,7 +333,7 @@ header{ display: block; } -.search-icon.album img{ +.search-icon.album svg{ display: block; height: 1.2rem; margin-left: auto; @@ -357,6 +363,7 @@ main ul{ display: flex; flex-direction: column; margin: 0; padding-left: 0; + align-items: center; } /* FOOTER */ @@ -411,6 +418,7 @@ footer{ .album-icon{ display: inline-block; align-self: center; + fill: var(--text); } /* INDEX BLOCKS */ @@ -518,6 +526,15 @@ footer{ color: var(--light-box-text); } +.track-earliest{ + line-height: 0; + margin-right: .3rem; +} + +.track-earliest svg{ + height: 1.2rem; +} + .empty{ color: var(--light-text); } @@ -596,7 +613,7 @@ footer{ margin-left: auto; } -.title-album img{ +.title-album svg{ margin: 0; display: block; margin-right: .6rem; @@ -629,7 +646,7 @@ footer{ } .songpage-detail li{ - display: flex; align-items: baseline; gap: .6rem; + display: flex; align-items: flex-end; gap: .6rem; flex-wrap: wrap; } @@ -640,6 +657,10 @@ footer{ } .songpage-img{ + display: flex; +} + +.songpage-img svg{ display: inline-block; height: 1.2rem; align-self: flex-end; @@ -655,8 +676,6 @@ footer{ text-align: center; } - - .entrypage-connections{ display: flex; flex-direction: column; width: 100%; @@ -667,12 +686,22 @@ footer{ margin-bottom: 1.2rem; } -.entrypage-clip{ - display: flex; flex-wrap: wrap-reverse; +.clip{ + display: flex; flex-direction: column; align-items: center; + gap: .3rem; padding: .3rem; +} + +.entrypage-motif{ + display: flex; flex-direction: column; align-items: center; margin-bottom: 1.2rem; - column-gap: 2.4rem; row-gap: .6rem; - justify-content: center; - align-items: center; + +} + +.entrypage-clips{ + display: flex; align-items: stretch; flex-wrap: wrap; + margin-top: .6rem; + background-color: var(--box); + max-width: 42rem; } .connection-icon{ @@ -680,19 +709,111 @@ footer{ width: 1.2rem; height: 1.2rem; } -.clip-pointer{ +.clip-title{ display: flex; justify-content: center; align-items: center; - width: 18rem; + max-width: 19rem; gap: .3rem; text-align: center; + margin: auto; +} + +.clip-title::after{ + content: ''; + width: 1.2rem; } .clip-album-icon{ + line-height: 0; +} + +.clip-album-icon svg{ height: 1.2rem; } -.clip-pointer a{ +.clip-title a, p{ + flex-shrink: 1; margin: 0; +} + +.soundslike{ + width: 2.4rem; + background-color: var(--block); + mask-image: url( '/static/cliparrow-vertical.svg' ); + mask-size: 100% 100%; +} + + +.motiflist{ + align-items: unset; +} + +.motifpage-list{ + align-items: center; +} + +.motifpage-list li{ + list-style: none; +} + +.motifpage-songs{ + display: flex; align-items: center; flex-wrap: wrap; + /* gap: .3rem; */ + width: 42rem; + background-color: var(--box); + margin-bottom: 1.8rem; + margin-top: .6rem; +} + +.motifpage-info{ + display: flex; align-items: center; + gap: .6rem; flex-shrink: 1; + width: 50%; + padding: .3rem; +} + +.motifpage-albums{ + height: min-content; +} + +.motifpage-albums div{ + display: flex; +} + +.motifpage-location p{ + text-align: left; + line-height: 1.2rem; +} + +@media (max-width: 42.1rem){ + .soundslike{ + /* display: none; */ + mask-image: url( '/static/cliparrow-horizontal.svg' ); + height: 1.2rem; + width: auto; + } + + .entrypage-clips{ + flex-direction: column; + } + + .motifpage-songs{ + width: auto; + flex-direction: column; + gap: unset; + } + + .motifpage-info{ + width: unset; + flex-direction: column; + } + + .motifpage-location{ + width: 18rem; + } + + .motifpage-location p{ + text-align: center; + } } /* SUGGEST PAGE */ diff --git a/templates/albumicon.jinja b/templates/albumicon.jinja new file mode 100644 index 0000000..2e91c05 --- /dev/null +++ b/templates/albumicon.jinja @@ -0,0 +1,242 @@ +{% if album_code == '1.0' %} + + + + + + Before Meteor + + +{% endif %} + +{% if album_code == '2.0' %} + + + + + + A Realm Reborn + +{% endif %} + +{% if album_code == '2.5' %} + + + + + + Before the Fall + +{% endif %} + +{% if album_code == '3.0' %} + + + + + + Heavensward + +{% endif %} + +{% if album_code == '3.5' %} + + + + + + The Far Edge of Fate + +{% endif %} + +{% if album_code == '4.0' %} + + + + + + Stormblood + +{% endif %} + +{% if album_code == '5.0' %} + + + + + + Shadowbringers + + +{% endif %} + +{% if album_code == '5.5' %} + + + + + + Death Unto Dawn + + +{% endif %} + +{% if album_code == '6.0' %} + + + + + + Endwalker + +{% endif %} + +{% if album_code == '6.5' %} + + + + + + Growing Light + +{% endif %} \ No newline at end of file diff --git a/templates/clipget.jinja b/templates/clipget.jinja index c32f5ef..601d671 100644 --- a/templates/clipget.jinja +++ b/templates/clipget.jinja @@ -1,11 +1,34 @@ -
  • - - - Motif Icon - {{ motif }} - +
  • + +
    +
    + {% if feature != song_id %} +
    + Song Icon +

    {% for name, _, _ in song_info %}{{ name }}{% endfor %}

    +
    + {% endif %} + +
    + {% if feature != song_id %} +
    +
    + + +
    + {% endif %} +
  • \ No newline at end of file diff --git a/templates/credits.jinja b/templates/credits.jinja index 887bf82..c056b19 100644 --- a/templates/credits.jinja +++ b/templates/credits.jinja @@ -13,7 +13,6 @@
  • 'they' pronouns
  • Aristide Majnheld - Bismarck
  • @esbylion@octodon.social
  • -
  • @esbylion@etheirys.masto.host
  • eorzeasongbook@gmail.com
  • @@ -26,13 +25,13 @@

    Made with

      -
    • Jinja, Flask, Python, sqlite, VS Code, Gunicorn, Docker, Caddy, HTMX, sox, pyexcel_odsr, LibreOffice Calc, Audacity
    • +
    • Jinja, Flask, Python, sqlite, VS Code, Gunicorn, Docker, Caddy, HTMX, sox, pyexcel_odsr, LibreOffice Calc, Audacity, MusicBee
    • +
    • Many other motif hunters - with special thanks to those who have suggested connections here, and to Rides1283man for their Compendium.
    • Osar'a Johve - Bismarck ♥

    Shout-outs to No Stress and Rainbow Connection

    - diff --git a/templates/footer.jinja b/templates/footer.jinja index c4c2695..8307ead 100644 --- a/templates/footer.jinja +++ b/templates/footer.jinja @@ -12,7 +12,7 @@ diff --git a/templates/home.jinja b/templates/home.jinja index 164c34e..1012b5b 100644 --- a/templates/home.jinja +++ b/templates/home.jinja @@ -9,6 +9,7 @@

    The Eorzea Songbook

    An unofficial library of motifs in the Final Fantasy XIV soundtrack.

    +

    2024-04-09: Updated for Growing Light!

    @@ -24,52 +25,62 @@
    + +

    Spoiler Warning!

    +

    Though no specific parts of the story are described, + this website includes the names of characters and locations that are important to the main story of Final Fantasy XIV. + If you haven't caught up on that story yet, and you want absolutely no information about who or what you see in it, + consider coming back when you're up to date.

    +

    What's a motif?

    Have you ever been playing Final Fantasy XIV and thought you heard a song that sounds familiar?

    -

    When a specific part of a song is used repeatedly to point out a character, location, or idea, it's called a motif (or leitmotif). Sometimes, a motif that was created in one song can be used in another, to draw a connection between two ideas in the story.

    +

    When a specific part of a song is used repeatedly to point out a character, location, or idea, it's called a motif (or leitmotif, depending on who you ask). Sometimes, a motif that was created in one song can be used in another, to draw a connection between two ideas in the story.

    For example, here's what you hear when you enter the city of Ishgard.

    -
    +
    + + Song Icon +

    Solid

    +
    - - Song Icon - Solid -

    And here's what you hear in Ishgard at night. The same notes are played, but this time it's slowed down, and played on just a piano. Sounds much easier to fall asleep to, doesn't it?

    -
    + +

    Here's another song that plays in Ishgard at night - when you're in the Pillars, where the wealthy residents live. Notice how there are more instruments; a grand pipe organ and booming drums. A fancier tune for fancier people, but still using the same melody.

    -
    + +

    Or this song, which plays during an important battle. Now our motif sounds like we're getting ready for a fight!

    -
    + +

    Once we know that this is melody represents Ishgard, the same tune can be used in all kinds of different songs to connect them back to that city. The composers of Final Fantasy XIV can tell us all kinds of things about Ishgard, just by changing up how they play the motif.

    Final Fantasy XIV's soundtrack is full of these! Not just for cities, but for dungeons, characters, expansions, and more. Discover the repeated melodies in your favorite songs, or check the motif pages to see where they show up.

    diff --git a/templates/motif.jinja b/templates/motif.jinja index 1944a7b..0754c94 100644 --- a/templates/motif.jinja +++ b/templates/motif.jinja @@ -11,14 +11,14 @@
    -