task 6: HTML export
This commit is contained in:
parent
a93598d8f6
commit
aee3052ad7
3
res/template/album.css
Normal file
3
res/template/album.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
td:first-child {
|
||||||
|
text-align: right;
|
||||||
|
}
|
39
res/template/album.html
Normal file
39
res/template/album.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Slopify V2 - Listen to the music you hate</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="base.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="album.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header id="navbar">
|
||||||
|
<a class="back" id="back-btn" href="{{artist.page}}"></a>
|
||||||
|
<h1 class="title">Slopify</h1>
|
||||||
|
</header>
|
||||||
|
<div id="body">
|
||||||
|
<h2>Album</h2>
|
||||||
|
<div id="crumbs">
|
||||||
|
<a href="{{artist.page}}">{{artist.name}}</a>
|
||||||
|
<span class="sep"></span>
|
||||||
|
<a href="./">{{album.name}}</a>
|
||||||
|
</div>
|
||||||
|
<table id="songs">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>No</th>
|
||||||
|
<th>Name</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{songs}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript" src="base.js"></script>
|
||||||
|
<script>
|
||||||
|
initTable(document.getElementById("songs"))
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
0
res/template/artist.css
Normal file
0
res/template/artist.css
Normal file
36
res/template/artist.html
Normal file
36
res/template/artist.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Slopify V2 - Listen to the music you hate</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="base.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="artist.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header id="navbar">
|
||||||
|
<a class="back" id="back-btn" href="index.html"></a>
|
||||||
|
<h1 class="title">Slopify</h1>
|
||||||
|
</header>
|
||||||
|
<div id="body">
|
||||||
|
<h2>Artist</h2>
|
||||||
|
<div id="crumbs">
|
||||||
|
<a href="./">{{artist.name}}</a>
|
||||||
|
</div>
|
||||||
|
<table id="albums">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{albums}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript" src="base.js"></script>
|
||||||
|
<script>
|
||||||
|
initTable(document.getElementById("albums"))
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
126
res/template/base.css
Normal file
126
res/template/base.css
Normal file
@ -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;
|
||||||
|
}
|
58
res/template/base.js
Normal file
58
res/template/base.js
Normal file
@ -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<rows.length-1; i++) {
|
||||||
|
rowA = rows[i]
|
||||||
|
rowB = rows[i+1]
|
||||||
|
if (desc == compareRows(rowA, rowB, colI)) {
|
||||||
|
swapped = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (swapped) {
|
||||||
|
rowA.parentNode.insertBefore(rowB, rowA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareRows(rowA, rowB, colI) {
|
||||||
|
let valA = rowA.querySelectorAll("td")[colI].innerText
|
||||||
|
let valB = rowB.querySelectorAll("td")[colI].innerText
|
||||||
|
|
||||||
|
if (!Number.isNaN(+valA) && !Number.isNaN(+valB)) {
|
||||||
|
valA = +valA
|
||||||
|
valB = +valB
|
||||||
|
}
|
||||||
|
|
||||||
|
return valB > valA
|
||||||
|
}
|
0
res/template/index.css
Normal file
0
res/template/index.css
Normal file
33
res/template/index.html
Normal file
33
res/template/index.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Slopify V2 - Listen to the music you hate</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="base.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="index.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header id="navbar">
|
||||||
|
<h1 class="title">Slopify</h1>
|
||||||
|
</header>
|
||||||
|
<div id="body">
|
||||||
|
<h2>Artists</h2>
|
||||||
|
<div id="crumbs"></div>
|
||||||
|
<table id="artists">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{artists}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript" src="base.js"></script>
|
||||||
|
<script>
|
||||||
|
initTable(document.getElementById("artists"))
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
61
res/template/song.css
Normal file
61
res/template/song.css
Normal file
@ -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;
|
||||||
|
}
|
33
res/template/song.html
Normal file
33
res/template/song.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Slopify V2 - Listen to the music you hate</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="base.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="song.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header id="navbar">
|
||||||
|
<a class="back" id="back-btn" href="{{album.page}}"></a>
|
||||||
|
<h1 class="title">Slopify</h1>
|
||||||
|
</header>
|
||||||
|
<div id="body">
|
||||||
|
<h2>Song</h2>
|
||||||
|
<div id="crumbs">
|
||||||
|
<a href="{{artist.page}}">{{artist.name}}</a>
|
||||||
|
<span class="sep"></span>
|
||||||
|
<a href="{{album.page}}">{{album.name}}</a>
|
||||||
|
<span class="sep"></span>
|
||||||
|
<a href="./">{{song.title}}</a>
|
||||||
|
</div>
|
||||||
|
<div id="song">
|
||||||
|
<h4 class="number">{{song.number}}</h4>
|
||||||
|
<h3 class="title">{{song.title}}</h3>
|
||||||
|
<div class="cover"></div>
|
||||||
|
<!-- <img src="{{album.image}}" alt="{{album.name}} cover image"> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript" src="base.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,6 +1,8 @@
|
|||||||
package ch.hevs.isc.slopify_v2
|
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 {
|
object DataBaseHelper {
|
||||||
def create(directory:String) : DataBase = {
|
def create(directory:String) : DataBase = {
|
||||||
@ -66,16 +68,99 @@ object DataBaseHelper {
|
|||||||
return db
|
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"<tr><td><a href='$artistUrl'>${artist.name}</a></td></tr>\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"<tr><td><a href='$albumUrl'>${album.name}</a></td></tr>\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"<tr><td>${song.number}</td><td><a href='$songUrl'>${song.title}</a></td></tr>\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 = {
|
def main(args: Array[String]): Unit = {
|
||||||
val db: DataBase = create("res/songs")
|
val db: DataBase = create("res/songs")
|
||||||
println(db)
|
|
||||||
val artists: Array[Artist] = db.getArtists()
|
|
||||||
println(artists(0))
|
|
||||||
|
|
||||||
save("res/db.bin", db)
|
exportHTML("/tmp/html", db)
|
||||||
val db2: DataBase = load("res/db.bin")
|
|
||||||
println(db2)
|
|
||||||
val artists2: Array[Artist] = db2.getArtists()
|
|
||||||
println(artists2(0))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user