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
+
+
+
+
+ No
+ Name
+
+
+
+ {{songs}}
+
+
+
+
+
+
+
\ 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
+
+
+
+
+ Name
+
+
+
+ {{albums}}
+
+
+
+
+
+
+
\ 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
+
+
+
+
+ Name
+
+
+
+ {{artists}}
+
+
+
+
+
+
+
\ 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