diff --git a/.dockerignore b/.dockerignore index d15341f..6bc6787 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,4 +2,6 @@ __pycache__/ *.pyc .git .env -metadata \ No newline at end of file +/to_convert/ +/converted/ +/metadata/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index ea00c38..21dfb0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -metadata \ No newline at end of file +/to_convert/ +/converted/ +/metadata/ \ No newline at end of file diff --git a/src/public/index.html b/src/public/index.html index 0b4bc55..9aa94cb 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -3,51 +3,28 @@ - Metadata Editor + Melies +
-

Metadata Editor

+ +

Melies

- - -
-
- - -
-
episode(s)
-
-
-
- - -
-
- - -
-
- - -
-
-
+
\ No newline at end of file diff --git a/src/public/edit/index.html b/src/public/metadata/edit/index.html similarity index 98% rename from src/public/edit/index.html rename to src/public/metadata/edit/index.html index 8fc6535..7f90d6c 100644 --- a/src/public/edit/index.html +++ b/src/public/metadata/edit/index.html @@ -3,7 +3,7 @@ - Edit + Melies - Metadata Editor @@ -14,7 +14,7 @@
- Home + Back diff --git a/src/public/metadata/index.html b/src/public/metadata/index.html new file mode 100644 index 0000000..515adef --- /dev/null +++ b/src/public/metadata/index.html @@ -0,0 +1,54 @@ + + + + + + Melies - Metadata Editor + + + + + + +
+

Metadata Editor

+
+
+ + +
+
+ + +
+
episode(s)
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + \ No newline at end of file diff --git a/src/public/static/css/index.css b/src/public/static/css/index.css index 5117ddb..eee346f 100644 --- a/src/public/static/css/index.css +++ b/src/public/static/css/index.css @@ -1,12 +1,23 @@ -#files { +header { + align-items: center; + img { + width: 4em; + height: 4em; + object-fit: contain; + } +} + +#pages { display: grid; + max-width: calc(max(50%, 20em)); + margin: 0 auto; grid-template-columns: repeat(auto-fit, minmax(15em, 1fr)); grid-auto-rows: 15em; gap: 0.8em; place-items: center; padding: 0.8em 0; - .file { + .page { display: flex; flex-direction: column; align-items: center; @@ -20,10 +31,6 @@ padding: 0.4em; border-radius: 1.2em; - &.hidden { - display: none; - } - &:hover { background-color: #f8f8f8; } @@ -33,64 +40,11 @@ height: 10em; } - .title { + .name { overflow-wrap: anywhere; text-align: center; font-weight: bold; - } - } -} - -.toolbar { - display: flex; - gap: 1.2em; - border-bottom: solid black 1px; - padding: 0.4em 0; - - .tool { - display: flex; - flex-direction: column; - gap: 0.2em; - - label[for] { - font-weight: bold; - } - - input, select { - font-family: inherit; - font-size: inherit; - height: 100%; - } - - .toggle { - height: 2em; - border-radius: 1em; - display: grid; - grid-template-columns: 1fr 1fr; - user-select: none; - cursor: pointer; - - input { - display: none; - - &:not(:checked) ~ .off, &:checked ~ .on { - background-color: #6ee74a; - } - } - - div { - padding: 0 0.4em; - display: grid; - place-items: center; - - &.off { - border-radius: 1em 0 0 1em; - } - - &.on { - border-radius: 0 1em 1em 0; - } - } + font-size: 150%; } } } \ No newline at end of file diff --git a/src/public/static/css/metadata.css b/src/public/static/css/metadata.css new file mode 100644 index 0000000..5117ddb --- /dev/null +++ b/src/public/static/css/metadata.css @@ -0,0 +1,96 @@ +#files { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(15em, 1fr)); + grid-auto-rows: 15em; + gap: 0.8em; + place-items: center; + padding: 0.8em 0; + + .file { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + width: 100%; + height: 100%; + text-decoration: none; + color: black; + font-family: inherit; + font-size: inherit; + padding: 0.4em; + border-radius: 1.2em; + + &.hidden { + display: none; + } + + &:hover { + background-color: #f8f8f8; + } + + img { + width: 10em; + height: 10em; + } + + .title { + overflow-wrap: anywhere; + text-align: center; + font-weight: bold; + } + } +} + +.toolbar { + display: flex; + gap: 1.2em; + border-bottom: solid black 1px; + padding: 0.4em 0; + + .tool { + display: flex; + flex-direction: column; + gap: 0.2em; + + label[for] { + font-weight: bold; + } + + input, select { + font-family: inherit; + font-size: inherit; + height: 100%; + } + + .toggle { + height: 2em; + border-radius: 1em; + display: grid; + grid-template-columns: 1fr 1fr; + user-select: none; + cursor: pointer; + + input { + display: none; + + &:not(:checked) ~ .off, &:checked ~ .on { + background-color: #6ee74a; + } + } + + div { + padding: 0 0.4em; + display: grid; + place-items: center; + + &.off { + border-radius: 1em 0 0 1em; + } + + &.on { + border-radius: 0 1em 1em 0; + } + } + } + } +} \ No newline at end of file diff --git a/src/public/static/images/conversion.svg b/src/public/static/images/conversion.svg new file mode 100644 index 0000000..68d2e57 --- /dev/null +++ b/src/public/static/images/conversion.svg @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/public/static/images/icon3.svg b/src/public/static/images/icon3.svg new file mode 100644 index 0000000..6ee5229 --- /dev/null +++ b/src/public/static/images/icon3.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/public/static/images/metadata.svg b/src/public/static/images/metadata.svg new file mode 100644 index 0000000..3fbe23a --- /dev/null +++ b/src/public/static/images/metadata.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/public/static/js/index.js b/src/public/static/js/index.js index 1a0c566..e69de29 100644 --- a/src/public/static/js/index.js +++ b/src/public/static/js/index.js @@ -1,107 +0,0 @@ -let fileNodes = [] - -function makeFilm(meta) { - const file = document.getElementById("film-template").cloneNode(true) - file.querySelector(".title").innerText = meta.title - return file -} - -function makeSeries(meta) { - const file = document.getElementById("series-template").cloneNode(true) - file.querySelector(".title").innerText = meta.title - file.querySelector(".episodes .num").innerText = meta.episodes - return file -} - -function makeFile(meta) { - let file - switch (meta.type) { - case "film": - file = makeFilm(meta) - break - case "series": - file = makeSeries(meta) - break - default: - throw new Error(`Invalid file type '${meta.type}'`) - } - - file.title = meta.filename - file.id = null - file.classList.remove("template") - const url = new URL("/edit/", window.location.origin) - url.searchParams.set("f", meta.filename) - file.href = url.href - return file -} - -/** - * - * @param {object[]} files - */ -function addFiles(files) { - const list = document.getElementById("files") - list.innerHTML = "" - const filenames = files.map(meta => meta.filename) - // Copy array because sort changes it in place - Array.from(filenames).sort().forEach(filename => { - const i = filenames.indexOf(filename) - const meta = files[i] - const file = makeFile(meta) - list.appendChild(file) - fileNodes.push([meta, file]) - }) -} - -function sortFiles() { - const sortBy = document.getElementById("sort-by").value - const sortDesc = document.getElementById("sort-desc").checked - const filter = document.getElementById("filter").value - - fileNodes.forEach(([meta, node]) => { - if (node.classList.contains(filter) || filter === "all") { - node.classList.remove("hidden") - } else { - node.classList.add("hidden") - } - }) - - let changed = false - do { - changed = false - for (let i = 0; i < fileNodes.length - 1; i++) { - /** @type {[object, HTMLElement]} */ - const pair1 = fileNodes[i] - /** @type {[object, HTMLElement]} */ - const pair2 = fileNodes[i + 1] - const [meta1, node1] = pair1 - const [meta2, node2] = pair2 - - let swap = false - if (sortDesc) { - swap = !(meta1[sortBy] >= meta2[sortBy]) - } else { - swap = !(meta2[sortBy] >= meta1[sortBy]) - } - if (swap) { - fileNodes[i] = pair2 - fileNodes[i + 1] = pair1 - node2.parentElement.insertBefore(node2, node1) - changed = true - } - } - } while (changed) -} - -window.addEventListener("load", () => { - fetch("/api/files").then(res => { - return res.json() - }).then(files => { - addFiles(files) - sortFiles() - }) - - document.getElementById("sort-by").addEventListener("change", () => sortFiles()) - document.getElementById("sort-desc").addEventListener("click", () => sortFiles()) - document.getElementById("filter").addEventListener("change", () => sortFiles()) -}) \ No newline at end of file diff --git a/src/public/static/js/metadata.js b/src/public/static/js/metadata.js new file mode 100644 index 0000000..b264ef0 --- /dev/null +++ b/src/public/static/js/metadata.js @@ -0,0 +1,107 @@ +let fileNodes = [] + +function makeFilm(meta) { + const file = document.getElementById("film-template").cloneNode(true) + file.querySelector(".title").innerText = meta.title + return file +} + +function makeSeries(meta) { + const file = document.getElementById("series-template").cloneNode(true) + file.querySelector(".title").innerText = meta.title + file.querySelector(".episodes .num").innerText = meta.episodes + return file +} + +function makeFile(meta) { + let file + switch (meta.type) { + case "film": + file = makeFilm(meta) + break + case "series": + file = makeSeries(meta) + break + default: + throw new Error(`Invalid file type '${meta.type}'`) + } + + file.title = meta.filename + file.id = null + file.classList.remove("template") + const url = new URL("/metadata/edit/", window.location.origin) + url.searchParams.set("f", meta.filename) + file.href = url.href + return file +} + +/** + * + * @param {object[]} files + */ +function addFiles(files) { + const list = document.getElementById("files") + list.innerHTML = "" + const filenames = files.map(meta => meta.filename) + // Copy array because sort changes it in place + Array.from(filenames).sort().forEach(filename => { + const i = filenames.indexOf(filename) + const meta = files[i] + const file = makeFile(meta) + list.appendChild(file) + fileNodes.push([meta, file]) + }) +} + +function sortFiles() { + const sortBy = document.getElementById("sort-by").value + const sortDesc = document.getElementById("sort-desc").checked + const filter = document.getElementById("filter").value + + fileNodes.forEach(([meta, node]) => { + if (node.classList.contains(filter) || filter === "all") { + node.classList.remove("hidden") + } else { + node.classList.add("hidden") + } + }) + + let changed = false + do { + changed = false + for (let i = 0; i < fileNodes.length - 1; i++) { + /** @type {[object, HTMLElement]} */ + const pair1 = fileNodes[i] + /** @type {[object, HTMLElement]} */ + const pair2 = fileNodes[i + 1] + const [meta1, node1] = pair1 + const [meta2, node2] = pair2 + + let swap = false + if (sortDesc) { + swap = !(meta1[sortBy] >= meta2[sortBy]) + } else { + swap = !(meta2[sortBy] >= meta1[sortBy]) + } + if (swap) { + fileNodes[i] = pair2 + fileNodes[i + 1] = pair1 + node2.parentElement.insertBefore(node2, node1) + changed = true + } + } + } while (changed) +} + +window.addEventListener("load", () => { + fetch("/api/files").then(res => { + return res.json() + }).then(files => { + addFiles(files) + sortFiles() + }) + + document.getElementById("sort-by").addEventListener("change", () => sortFiles()) + document.getElementById("sort-desc").addEventListener("click", () => sortFiles()) + document.getElementById("filter").addEventListener("change", () => sortFiles()) +}) \ No newline at end of file