From 98eaf7cc7eca4929ed53d8c5bd51277b5ca35989 Mon Sep 17 00:00:00 2001 From: MrLetsplay Date: Tue, 18 Feb 2025 21:25:54 +0100 Subject: [PATCH] Implement series page, Add back to library button --- js/components.js | 34 +++++++++++++++++++++++++- js/series.js | 19 +++++++++++++++ js/watch.js | 1 + style/components.css | 57 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 1 deletion(-) diff --git a/js/components.js b/js/components.js index 7a36ea8..ef5e567 100644 --- a/js/components.js +++ b/js/components.js @@ -36,6 +36,13 @@ function element(type, props, children) { const components = { + /** + * @returns {HTMLElement} + */ + backToLibrary() { + return element("a", { content: "< Back to library", classNames: ["back-to-library"], attributes: { "href": "/" } }); + }, + /** * @param {string} name * @param {Video[]} videos @@ -54,7 +61,7 @@ const components = { * @returns {HTMLElement} */ video(video) { - return element("a", { classNames: ["video"], attributes: { "href": "/watch.html?id=" + encodeURIComponent(video.id), "target": "_blank" } }, [ + return element("a", { classNames: ["video"], attributes: { "href": "/watch.html?id=" + encodeURIComponent(video.id) } }, [ element("img", { attributes: { "src": API_URL + "/library/video/" + encodeURIComponent(video.id) + "/thumbnail" } }), element("span", { content: video.metadata.title }), ]); @@ -149,6 +156,31 @@ const components = { ]; }, + /** + * @param {string} name + * @return {HTMLElement} + */ + fullSeries(name, videos) { + return element("div", { classNames: ["full-series"] }, [ + element("div", { content: name, attributes: { "href": "/series.html?name=" + encodeURIComponent(name) } }), + element("div", { classNames: ["full-series-videos"] }, videos.map(v => components.fullSeriesVideo(v))) + ]); + }, + + /** + * @param {Video} video + * @returns {HTMLElement} + */ + fullSeriesVideo(video) { + return element("a", { classNames: ["full-series-video"], attributes: { "href": "/watch.html?id=" + encodeURIComponent(video.id) } }, [ + element("img", { attributes: { "src": API_URL + "/library/video/" + encodeURIComponent(video.id) + "/thumbnail" } }), + element("div", null, [ + element("span", { content: video.metadata.title }), + element("span", { content: video.metadata.author, classNames: ["full-series-video-author"] }) + ]) + ]); + }, + /** * @param {any} error * @returns {HTMLElement} diff --git a/js/series.js b/js/series.js index 16c762d..885a8c1 100644 --- a/js/series.js +++ b/js/series.js @@ -3,6 +3,25 @@ const api = new API(API_URL); const root = document.getElementById("root"); async function init() { + let query = new URLSearchParams(document.location.search); + + let library; + try { + library = await api.getLibrary("series"); + } catch (e) { + showError(e); + return; + } + + let series = query.get("name"); + if (library.videos[series] == null) { + showError("Series not found"); + return; + } + + root.appendChild(components.backToLibrary()); + root.appendChild(components.fullSeries(series, library.videos[series])); + finishedLoading(); } diff --git a/js/watch.js b/js/watch.js index 64920b7..4ec1648 100644 --- a/js/watch.js +++ b/js/watch.js @@ -13,6 +13,7 @@ async function init() { return; } + root.appendChild(components.backToLibrary()); root.appendChild(await components.player(video)); finishedLoading(); diff --git a/style/components.css b/style/components.css index 82299b1..f4f508a 100644 --- a/style/components.css +++ b/style/components.css @@ -1,3 +1,8 @@ +.back-to-library { + display: block; + margin-bottom: 10px; +} + .series { background-color: var(--foreground); padding: 10px; @@ -104,3 +109,55 @@ .video-metadata>button { margin-top: 5px; } + +.full-series { + background-color: var(--foreground); + padding: 10px; + border-radius: 5px; +} + +.full-series>div:first-child { + font-size: 1.2em; + text-decoration: none; + color: var(--text); +} + +.series-authors { + color: var(--text-alternative); +} + +.full-series-videos { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + flex-direction: row; + overflow-x: scroll; + gap: 10px; + padding-top: 10px; + padding-bottom: 10px; +} + +.full-series-video { + display: flex; + flex-direction: column; + gap: 10px; + text-decoration: none; + border: 1px solid var(gray); + border-radius: 5px; +} + +.full-series-video>img { + width: 100%; + aspect-ratio: 16/9; + background-color: var(--background); + height: auto; + border-radius: 5px; +} + +.full-series-video>div { + display: flex; + flex-direction: column; +} + +.full-series-video-author { + color: var(--text-alternative); +}