# HG changeset patch # User drewp@bigasterisk.com # Date 1733446913 28800 # Node ID b5b29f6ef5cb055883e9dea487cdad39e396ca7e # Parent 0aea9e55899b227c51c73e652b53405ee2013346 cleanup and refactor diff -r 0aea9e55899b -r b5b29f6ef5cb src/PagePlayer.ts --- a/src/PagePlayer.ts Wed Dec 04 21:50:16 2024 -0800 +++ b/src/PagePlayer.ts Thu Dec 05 17:01:53 2024 -0800 @@ -22,6 +22,7 @@ } div.big { inset: 5em; + background: #2c2c2c; } div.hidden { display: none; diff -r 0aea9e55899b -r b5b29f6ef5cb src/VideoPage.ts --- a/src/VideoPage.ts Wed Dec 04 21:50:16 2024 -0800 +++ b/src/VideoPage.ts Thu Dec 05 17:01:53 2024 -0800 @@ -1,4 +1,4 @@ -import { LitElement, PropertyValues, css, html, unsafeCSS } from "lit"; +import { LitElement, PropertyValues, TemplateResult, css, html, unsafeCSS } from "lit"; import { customElement, property } from "lit/decorators.js"; import { PagePlayer, ShakaVideoElement } from "./PagePlayer"; import maincss from "./main.css?inline"; @@ -9,67 +9,85 @@ interface VideoFile { webRelPath: string; + webDataPath: string; label: string; - thumbRelPath: string; - webDataPath: string; } + interface Subdir { label: string; path: string; } +interface VideoListings { + videos: VideoFile[]; + subdirs: Subdir[]; +} + function subdirQuery(subdir: string): string { return "subdir=" + encodeURIComponent(subdir); } +class route { + path = "/video/*"; + videoListings: VideoListings | null = null; + showVid: string | null = null; + dirName?: string; + + link(wrp: string): string { + return "/video/" + wrp; + } + + async enter(params: { [key: string]: string | undefined }): Promise { + const webRelPath = "/" + params[0]!; + this.dirName = webRelPath.replace(/.*\/([^/]+)/, "$1"); + const resp = await fetch("/video/api/videos?" + subdirQuery(webRelPath)); + if (resp.status == 404) { + return false; + } + this.videoListings = await resp.json(); + + if (webRelPath.endsWith("/")) { + this.showVid = null; + } else { + this.showVid = webRelPath; + } + + return true; + } + + render(p: { [key: string]: string | undefined }) { + return html``; + } +} + @customElement("video-page") export class VideoPage extends LitElement { static styles = [unsafeCSS(maincss)]; - private _router = new Router( - this, - [ - { - path: "/video/*", - enter: async (params: { [key: string]: string | undefined }): Promise => { - const webRelPath = '/' + params[0]!; - console.log("enter", webRelPath); - // this.updatePathSegs(subdir); - const resp = await (await fetch("/video/api/videos?" + subdirQuery(webRelPath))).json(); - console.log("resp", resp); - // 404 page goes here - this.resp = resp; - - - let vid: string | null; - let dir: string; - - if (webRelPath.endsWith("/")) { - vid = null; - dir = webRelPath; - } else { - vid = webRelPath; - dir = webRelPath.slice(0, webRelPath.lastIndexOf("/")); - } - this.showVid = vid - this.linkx = (wrp: string) => { return '/video/' + wrp; }; - - return true; - }, - render: (p: { [key: string]: string | undefined }) => { - return html``; - }, - }, - ], - {} - ); + private _router = new Router(this, [new route()], {}); render() { const requestedPath = this._router.params[0]; + const segs = ["."].concat(requestedPath || "".split("/")); + const crumbs = []; + // todo: goal is to have '🏠 TOP' and every level not including current dir + for (let i = 0; i < segs.length; i++) { + const seg = segs[i]; + const href = "../".repeat(segs.length - i - 1); + const label = i == 0 ? html`` : seg; + console.log(href, label); + crumbs.push(html`${label}`); + } return html` -
TOP > ${requestedPath}
-
- ${this._router.outlet()} -
+
+ + ${crumbs} +
+
${this._router.outlet()}
foot
`; } @@ -78,17 +96,9 @@ @customElement("video-page2") export class VideoPage2 extends LitElement { @property() showVid?: string; - @property() resp?: any; + @property() videoListings?: VideoListings; @property() link!: (s: string) => string; - videos: VideoFile[]; - subdirs: Subdir[]; - @property() pathSegs: { label: string; subdir: string }[]; - constructor() { - super(); - this.videos = []; - this.subdirs = []; - this.pathSegs = []; - } + @property() dirName?: string; protected firstUpdated(_changedProperties: PropertyValues): void { document.addEventListener("keydown", (e) => { @@ -97,12 +107,10 @@ } }); } + protected update(changedProperties: PropertyValues): void { - const resp = changedProperties.has('resp'); + const resp = changedProperties.has("videoListings"); if (resp) { - this.videos = (this.resp.videos || []) as VideoFile[]; - this.subdirs = (this.resp.subdirs || []) as Subdir[]; - console.log('set', this.videos, this.subdirs); // if (this.showVid) { // this.openPlayer(); // } else { @@ -111,19 +119,6 @@ } super.update(changedProperties); } - updatePathSegs(subdir: string) { - this.pathSegs = [{ label: "TOP", subdir: "/" }]; - if (subdir != "/") { - let acc = ""; - const segs = subdir.split("/"); - segs.splice(0, 1); // the root - for (let seg of segs) { - acc += "/" + seg; - this.pathSegs.push({ label: seg, subdir: acc }); - } - } - } - static styles = [ unsafeCSS(maincss), @@ -155,41 +150,47 @@ } `, ]; + + thumbSrc(v: VideoFile) { + return "/video/api/thumbnail?webRelPath=" + encodeURIComponent(v.webRelPath); + } + + renderSubdir(subdir: Subdir): TemplateResult { + return html``; + } + + renderVideoListing(video: VideoFile) { + return html``; + } + render() { - const thumbSrc = (v: VideoFile) => { - return "/video/api/thumbnail?webRelPath=" + encodeURIComponent(v.webRelPath); - }; + if (this.videoListings == null) { + return html`
Loading...
`; + } + const listings = [ + html`${this.videoListings.subdirs.map((s) => this.renderSubdir(s))}`, // + html`${this.videoListings.videos.map((v) => this.renderVideoListing(v))}`, + ]; return html` - - ${this.pathSegs.map( - (seg, i) => - html` - ${i == 0 ? html`` : ""} ${seg.label} ` - )} - +

${this.dirName}

+
${listings}
-
- ${this.subdirs.map((s) => html``)} - ${this.videos.map( - (v) => - html`` - )} -

Add new videos...

`; } + escapeALittle(fileUri: string): string { return fileUri.replace("#", encodeURIComponent("#")); } + playVideo(ev: CustomEvent) { const player = this.shadowRoot!.querySelector("page-player")! as PagePlayer; diff -r 0aea9e55899b -r b5b29f6ef5cb video.py --- a/video.py Wed Dec 04 21:50:16 2024 -0800 +++ b/video.py Thu Dec 05 17:01:53 2024 -0800 @@ -34,16 +34,16 @@ if not subdir.endswith('/'): # raise ValueError(f'not a dir {subdir=}') # ok we'll list the parent dir underneath - subdir = '/'.join(subdir.split('/')[:-1]) # todo move to FE + subdir = '/'.join(subdir.split('/')[:-1]) # todo move to FE else: subdir = subdir[:-1] - if subdir=="": + if subdir == "": subdir = "./" - if not (subdir.startswith('/') or subdir=='./'): + if not (subdir.startswith('/') or subdir == './'): raise ValueError(f'not a dir {subdir=}') subdir = subdir[1:] log.debug(f'videos request corrected to: {subdir=}') - + vfInDir = store.findInDir(subdir) return JSONResponse({ "videos": [{ @@ -81,13 +81,16 @@ return EventSourceResponse(g()) + def getDiskPath(fs, webRelPath): doc = fs.find_one({'webRelPath': webRelPath}) if doc is None: raise ValueError return doc['diskPath'] -async def getThumbnail(db: pymongo.database.Database, req: Request) -> Response: + +async def getThumbnail(db: pymongo.database.Database, + req: Request) -> Response: webRelPath = req.query_params['webRelPath'] fs = db.get_collection('fs') diskPath = getDiskPath(fs, webRelPath) @@ -95,7 +98,6 @@ async with asyncio.timeout(10): data = await thumbnail.getThumbnailData(th, diskPath) return Response(content=data, media_type='image/jpeg') - db = open_mongo_or_die().get_database('video') @@ -129,6 +131,7 @@ app.state.processTask = asyncio.create_task(dl_queue.process()) return app + uvicorn.run(main, host="0.0.0.0", port=8004,