VideoBase-Frontend/js/components.js

122 lines
3.7 KiB
JavaScript
Raw Normal View History

2025-02-11 23:04:28 +01:00
/**
* @param {string} type
* @param {?{classNames: ?string[], content: ?string, attributes: ?Object.<string, string>, onClick: ?(() => void)}} props
* @param {?HTMLElement[]} children
* @returns {HTMLElement}
*/
function element(type, props, children) {
let element = document.createElement(type);
if (props?.classNames != null && props.classNames.length > 0) {
element.classList.add(props.classNames);
}
if (props?.content != null) {
element.innerText = props.content;
}
if (props?.attributes != null) {
for (let attribute in props.attributes) {
element.setAttribute(attribute, props.attributes[attribute]);
}
}
if (props?.onClick != null) {
element.onclick = props.onClick;
}
if (children != null) {
for (let child of children) {
element.appendChild(child);
}
}
return element;
}
const components = {
/**
* @param {string} name
* @param {Video[]} videos
* @returns {HTMLElement}
*/
series(name, videos) {
return element("div", { classNames: ["series"] }, [
element("a", { content: name, attributes: { "href": "/series.html?name=" + encodeURIComponent(name) } }),
element("div", { content: [...new Set(videos.map(v => v.metadata.author))].join(", "), classNames: ["series-authors"] }),
element("div", { classNames: ["series-videos"] }, videos.map(v => components.video(v)))
]);
},
/**
* @param {Video} video
* @returns {HTMLElement}
*/
video(video) {
return element("a", { classNames: ["video"], attributes: { "href": "/watch.html?id=" + encodeURIComponent(video.id), "target": "_blank" } }, [
element("img", { attributes: { "src": API_URL + "/library/video/" + encodeURIComponent(video.id) + "/thumbnail" } }),
element("span", { content: video.metadata.title }),
]);
},
/**
* @param {Video} video
* @returns {HTMLElement}
*/
player(video) {
const streamURL = API_URL + "/library/video/" + encodeURIComponent(video.id) + "/stream";
const downloadVideo = () => {
window.location.href = streamURL;
};
return element("div", { classNames: ["player"] }, [
element("div", { content: video.metadata.title, classNames: ["player-title"] }),
element("div", { content: video.metadata.author, classNames: ["player-author"] }),
2025-02-14 21:46:04 +01:00
element("video", { attributes: { "poster": API_URL + "/library/video/" + encodeURIComponent(video.id) + "/thumbnail", "controls": "" } }, [
2025-02-11 23:04:28 +01:00
element("source", { attributes: { "src": streamURL } })
]),
element("button", { onClick: downloadVideo }, [
element("img", { attributes: { "src": "/icon/download.svg", "width": "24", "height": "24" } }),
element("span", { content: "Download" }),
]),
components.videoMetadata(video)
]);
},
/**
* @param {Video} video
* @returns {HTMLElement}
*/
videoMetadata(video) {
return element("details", { classNames: ["video-metadata"] }, [
element("summary", { content: "Metadata" }),
element("div", { classNames: ["video-metadata-table"] }, [
element("span", { content: "Title" }),
element("input", { attributes: { "value": video.metadata.title } }),
element("span", { content: "Series" }),
element("input", { attributes: { "value": video.metadata.series } }),
element("span", { content: "Author" }),
element("input", { attributes: { "value": video.metadata.author } }),
element("span", { content: "Index" }),
element("input", { attributes: { "value": video.metadata.index } }),
]),
element("button", { content: "Update" })
]);
},
/**
* @param {any} error
* @returns {HTMLElement}
*/
error(error) {
return element("div", { classNames: ["error"] }, [
element("b", { content: "Oops, an unexpected error occurred" }),
element("span", { content: errorToString(error) }),
element("button", { content: "Reload", onClick: () => window.location.reload() })
2025-02-11 23:04:28 +01:00
]);
}
}