diff --git a/res/template/album.css b/res/template/album.css new file mode 100644 index 0000000..cb687c9 --- /dev/null +++ b/res/template/album.css @@ -0,0 +1,3 @@ +td:first-child { + text-align: right; +} \ No newline at end of file diff --git a/res/template/album.html b/res/template/album.html new file mode 100644 index 0000000..eaa4a81 --- /dev/null +++ b/res/template/album.html @@ -0,0 +1,39 @@ + + + + + + Slopify V2 - Listen to the music you hate + + + + + +
+

Album

+
+ {{artist.name}} + + {{album.name}} +
+ + + + + + + + + {{songs}} + +
NoName
+
+ + + + \ No newline at end of file diff --git a/res/template/artist.css b/res/template/artist.css new file mode 100644 index 0000000..e69de29 diff --git a/res/template/artist.html b/res/template/artist.html new file mode 100644 index 0000000..8ff34c2 --- /dev/null +++ b/res/template/artist.html @@ -0,0 +1,36 @@ + + + + + + Slopify V2 - Listen to the music you hate + + + + + +
+

Artist

+
+ {{artist.name}} +
+ + + + + + + + {{albums}} + +
Name
+
+ + + + \ No newline at end of file diff --git a/res/template/base.css b/res/template/base.css new file mode 100644 index 0000000..9d7336c --- /dev/null +++ b/res/template/base.css @@ -0,0 +1,126 @@ +* { + margin: 0; + box-sizing: border-box; +} + +html, body { + width: 100%; + height: 100%; +} + +body { + background-color: #0d130d; + font-family: Ubuntu; + font-size: 16pt; + color: white; +} + +#navbar { + display: grid; + grid-template-columns: auto 1fr auto; + grid-template-areas: "back title space"; + place-items: center; + text-align: center; + border-bottom: solid #3e5b3e 1px; + padding: 0.4em 0.8em; + gap: 0.8em; +} + +#navbar .back { + grid-area: back; +} + +#back-btn { + width: 2em; + height: 2em; + position: relative; +} + +#back-btn::before { + content: ""; + border: solid white 4px; + border-style: solid none none solid; + position: absolute; + inset: 0; + transform: rotate(-45deg); + width: 0.5em; + height: 0.5em; + margin: auto; +} + +#navbar .title { + grid-area: title; +} + +#body { + display: flex; + flex-direction: column; + padding: 2em; + gap: 1em; +} + +#crumbs { + display: flex; + gap: 1em; + align-items: center; +} + +#crumbs .sep { + position: relative; + width: 0.5em; + height: 0.5em; +} + +#crumbs .sep::before { + content: ""; + position: absolute; + top: 50%; + left: 50%; + width: 100%; + height: 100%; + border: white 0.2em; + border-style: solid solid none none; + transform: translate(-50%, -50%) rotate(45deg); +} + +a { + color: #00bf00; +} + +table { + border-collapse: collapse; +} + +th, td { + border: solid white 1px; + padding: 0.5em 1em; +} + +tr:nth-child(even) { + background-color: #aeaeae21; +} + +th { + cursor: pointer; + position: relative; +} + +th.sort-asc::before { + content: ""; + position: absolute; + top: 50%; + left: 0.5em; + transform: translate(-50%, -25%); + border: solid transparent 0.2em; + border-top-color: white; +} + +th.sort-desc::before { + content: ""; + position: absolute; + top: 50%; + left: 0.5em; + transform: translate(-50%, -55%); + border: solid transparent 0.2em; + border-bottom-color: white; +} diff --git a/res/template/base.js b/res/template/base.js new file mode 100644 index 0000000..cbc4a73 --- /dev/null +++ b/res/template/base.js @@ -0,0 +1,58 @@ +function initTable(table) { + table.tHead.querySelectorAll("th").forEach((header, i) => { + header.addEventListener("click", () => { + toggleSort(table, header, i) + }) + }) +} + +function toggleSort(table, header, colI) { + if (header.classList.contains("sort-asc")) { + header.classList.remove("sort-asc") + header.classList.add("sort-desc") + doSort(table, colI, true) + } else if (header.classList.contains("sort-desc")) { + header.classList.remove("sort-desc") + header.classList.add("sort-asc") + doSort(table, colI, false) + } else { + table.tHead.querySelectorAll("th.sort-asc,th.sort-desc").forEach(th => { + th.classList.remove("sort-asc") + th.classList.remove("sort-desc") + }) + header.classList.add("sort-asc") + doSort(table, colI, false) + } +} + +function doSort(table, colI, desc = false) { + let swapped = true + while (swapped) { + let rows = Array.from(table.rows) + swapped = false + let rowA, rowB + for (let i=1; i valA +} \ No newline at end of file diff --git a/res/template/index.css b/res/template/index.css new file mode 100644 index 0000000..e69de29 diff --git a/res/template/index.html b/res/template/index.html new file mode 100644 index 0000000..cb9b782 --- /dev/null +++ b/res/template/index.html @@ -0,0 +1,33 @@ + + + + + + Slopify V2 - Listen to the music you hate + + + + + +
+

Artists

+
+ + + + + + + + {{artists}} + +
Name
+
+ + + + \ No newline at end of file diff --git a/res/template/song.css b/res/template/song.css new file mode 100644 index 0000000..bd3fa8b --- /dev/null +++ b/res/template/song.css @@ -0,0 +1,61 @@ +@keyframes shadow-anim { + 0% { + box-shadow: 0.0em 0.2em 0.6em #344c34a0; + } + + 25% { + box-shadow: 0.1em 0.3em 0.8em #344c34a0; + } + + 50% { + box-shadow: 0.2em 0.4em 1.2em #344c34a0; + } + + 75% { + box-shadow: 0.1em 0.3em 0.8em #344c34a0; + } + + 100% { + box-shadow: 0.0em 0.2em 0.6em #344c34a0; + } + +} + +#song { + display: grid; + animation: shadow-anim 10s infinite alternate; + border-radius: 0.8em; + padding: 0.8em; + grid-template-columns: auto 1fr auto; + grid-template-rows: auto 1fr; + grid-template-areas: + "num title duration" + "cover cover cover"; + max-height: 20em; + min-width: 50%; + max-width: 30em; + margin: auto; + gap: 0.4em; + align-items: center; +} + +#song .number { + grid-area: num; +} + +#song .title { + grid-area: title; +} + +#song .duration { + grid-area: duration; +} + +#song .cover { + grid-area: cover; + object-fit: contain; + + background-color: #393939; + width: 8em; + height: 8em; +} \ No newline at end of file diff --git a/res/template/song.html b/res/template/song.html new file mode 100644 index 0000000..c9a2159 --- /dev/null +++ b/res/template/song.html @@ -0,0 +1,33 @@ + + + + + + Slopify V2 - Listen to the music you hate + + + + + +
+

Song

+ +
+

{{song.number}}

+

{{song.title}}

+
+ +
+
+ + + \ No newline at end of file diff --git a/src/ch/hevs/isc/slopify_v2/DataBaseHelper.scala b/src/ch/hevs/isc/slopify_v2/DataBaseHelper.scala index 470503a..504f246 100644 --- a/src/ch/hevs/isc/slopify_v2/DataBaseHelper.scala +++ b/src/ch/hevs/isc/slopify_v2/DataBaseHelper.scala @@ -1,6 +1,8 @@ package ch.hevs.isc.slopify_v2 -import java.io.{File, FileInputStream, FileOutputStream, ObjectInput, ObjectInputStream, ObjectOutputStream} +import java.io._ +import scala.collection.immutable.HashMap +import scala.io.{BufferedSource, Source} object DataBaseHelper { def create(directory:String) : DataBase = { @@ -66,16 +68,99 @@ object DataBaseHelper { return db } + def exportHTML(directory: String, dataBase: DataBase): Unit = { + var artists: Array[Artist] = dataBase.getArtists().sortBy(_.name) + + var artistRows: String = "" + for (artist: Artist <- artists) { + val artistFileName: String = formatName(artist.name) + val artistUrl: String = s"./$artistFileName.html" + artistRows += s"${artist.name}\n" + + val albums: Array[Album] = artist.getAlbums().sortBy(_.name) + var albumRows: String = "" + for (album: Album <- albums) { + val albumFileName: String = artistFileName + "_" + formatName(album.name) + val albumUrl: String = s"./$albumFileName.html" + albumRows += s"${album.name}\n" + + val songs: Array[Song] = album.getSongs().sortBy(_.number) + var songRows: String = "" + for (song: Song <- songs) { + val songFileName: String = albumFileName + "_" + formatName(song.number + "-" + song.title) + val songUrl: String = s"./$songFileName.html" + songRows += s"${song.number}${song.title}\n" + + formatTemplate("res/template/song.html", HashMap( + "artist.name" -> artist.name, + "artist.page" -> artistUrl, + "album.name" -> album.name, + "album.page" -> albumUrl, + "song.title" -> song.title, + "song.number" -> song.number.toString + ), directory + "/" + songFileName + ".html") + } + + formatTemplate("res/template/album.html", HashMap( + "songs" -> songRows, + "artist.name" -> artist.name, + "artist.page" -> artistUrl, + "album.name" -> album.name + ), directory + "/" + albumFileName + ".html") + } + + formatTemplate("res/template/artist.html", HashMap( + "albums" -> albumRows, + "artist.name" -> artist.name + ), directory + "/" + artistFileName + ".html") + } + formatTemplate("res/template/index.html", HashMap( + "artists" -> artistRows + ), directory + "/" + "index.html") + + copyFile("res/template/base.js", directory + "/base.js") + copyFile("res/template/base.css", directory + "/base.css") + copyFile("res/template/index.css", directory + "/index.css") + copyFile("res/template/artist.css", directory + "/artist.css") + copyFile("res/template/album.css", directory + "/album.css") + copyFile("res/template/song.css", directory + "/song.css") + } + + def copyFile(from: String, to: String): Unit = { + val fis: FileInputStream = new FileInputStream(from) + val fos: FileOutputStream = new FileOutputStream(to) + + fos.write(fis.readAllBytes()) + + fis.close() + fos.close() + } + + def formatName(name: String): String = { + var formatted: String = name.toLowerCase + formatted = formatted.replace(" ", "_") + formatted = formatted.replace("'", "-") + formatted = formatted.replaceAll("[^a-zA-Z_0-9\\-().]", "") + return formatted + } + + def formatTemplate(templateName: String, variables: Map[String, String], outPath: String): Unit = { + val source: BufferedSource = Source.fromFile(templateName) + var template: String = source.getLines().mkString("\n") + source.close() + + for ((key: String, value: String) <- variables) { + template = template.replace(s"{{$key}}", value) + } + + val fos: FileOutputStream = new FileOutputStream(outPath) + fos.write(template.getBytes) + fos.close() + } + def main(args: Array[String]): Unit = { val db: DataBase = create("res/songs") - println(db) - val artists: Array[Artist] = db.getArtists() - println(artists(0)) - save("res/db.bin", db) - val db2: DataBase = load("res/db.bin") - println(db2) - val artists2: Array[Artist] = db2.getArtists() - println(artists2(0)) + exportHTML("/tmp/html", db) } } \ No newline at end of file