authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2025-10-12 10:03:32-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2025-10-14 02:40:48-07:00
log5fde9a0d10e1f190f9994c0f3673777365c27902
treef22400b7dde1c780e6945c5ad9897bb8fb0c39b6
parentf171c7f1cd5249ccd190aa99587fe7c3cbb4f209
signature Commit is signed but in an unrecognized format.

feat(site/file-viewer): special case project dates

projects like waterfalls and in the summer now do not show file mtimes, except for on the project itself. this also fixes a bug regarding deleting folders.

7 files changed, 48 insertions(+), 10 deletions(-)

src/file-viewer/bin/scan3.ts+25-4
...@@ -123,6 +123,8 @@ export async function main() {...@@ -123,6 +123,8 @@ export async function main() {
123 dimensions: mediaFile?.dimensions ?? "",123 dimensions: mediaFile?.dimensions ?? "",
124 contents: mediaFile?.contents ?? "",124 contents: mediaFile?.contents ?? "",
125 });125 });
126 const parent = mediaFile.getParent();
127 if (parent) parent.setProcessed(0);
126128
127 node.text = `${label}`;129 node.text = `${label}`;
128 await queueProcessors({130 await queueProcessors({
...@@ -263,7 +265,12 @@ export async function main() {...@@ -263,7 +265,12 @@ export async function main() {
263 }265 }
264 }266 }
265 }267 }
266 if (node.estimate > 0) errorHandler.move();268 if (node.estimate > 0) {
269 errorHandler.move();
270
271 const parent = mediaFile.getParent();
272 if (parent) parent.setProcessed(0);
273 }
267 }274 }
268275
269 // Add the root & recursively iterate!276 // Add the root & recursively iterate!
...@@ -322,6 +329,19 @@ export async function main() {...@@ -322,6 +329,19 @@ export async function main() {
322 }329 }
323 }330 }
324331
332 // Project Date
333 const dateFile = children.find((x) => x.basename === ".date");
334 if (dateFile) {
335 const date = new Date(dateFile.contents);
336 newestDate = date;
337 dir.setProcessors(
338 0,
339 JSON.stringify({ hideChildrenDates: true }),
340 );
341 } else {
342 dir.setProcessors(0, "");
343 }
344
325 const dirHash = crypto345 const dirHash = crypto
326 .createHash("sha1")346 .createHash("sha1")
327 .update(dir.path + allHashes)347 .update(dir.path + allHashes)
...@@ -846,12 +866,13 @@ function skipBasename(basename: string): boolean {...@@ -846,12 +866,13 @@ function skipBasename(basename: string): boolean {
846 // dot files must be incrementally tracked866 // dot files must be incrementally tracked
847 if (basename === ".dirsort") return false;867 if (basename === ".dirsort") return false;
848 if (basename === ".friends") return false;868 if (basename === ".friends") return false;
869 if (basename === ".date") return false;
849870
850 return (871 return (
851 basename.startsWith(".") ||872 basename.startsWith(".") ||
852 basename.startsWith("._") ||873 // basename.startsWith("._") ||
853 basename.startsWith(".tmp") ||874 // basename.startsWith(".tmp") ||
854 basename === ".DS_Store" ||875 // basename === ".DS_Store" ||
855 basename.toLowerCase() === "thumbs.db" ||876 basename.toLowerCase() === "thumbs.db" ||
856 basename.toLowerCase() === "desktop.ini"877 basename.toLowerCase() === "desktop.ini"
857 );878 );
src/file-viewer/models/MediaFile.ts+1-1
...@@ -440,7 +440,7 @@ const deleteCascadeQuery = db.prepare<[{ id: number }]>(/* SQL */ `...@@ -440,7 +440,7 @@ const deleteCascadeQuery = db.prepare<[{ id: number }]>(/* SQL */ `
440 )440 )
441 )441 )
442 delete from media_files442 delete from media_files
443 where id in (select id from items)443 where id in (select id from items order by parent_id nulls last)
444`);444`);
445const getDirectoriesToReindexQuery = db.prepare(`445const getDirectoriesToReindexQuery = db.prepare(`
446 with recursive directory_chain as (446 with recursive directory_chain as (
src/file-viewer/rsync.ts+1-1
...@@ -25,7 +25,7 @@ export interface SpawnOptions {...@@ -25,7 +25,7 @@ export interface SpawnOptions {
25 args: string[];25 args: string[];
26 rsync?: string;26 rsync?: string;
27 progress: progress.Node;27 progress: progress.Node;
28 cwd: string;28 cwd?: string;
29}29}
3030
31export async function spawn(options: SpawnOptions) {31export async function spawn(options: SpawnOptions) {
src/file-viewer/rules.ts+1-1
...@@ -73,7 +73,7 @@ export const extsDuration = new Set([...extsAudio, ...extsVideo]);...@@ -73,7 +73,7 @@ export const extsDuration = new Set([...extsAudio, ...extsVideo]);
73export const extsDimensions = new Set([...extsImage, ...extsVideo, ".gif"]);73export const extsDimensions = new Set([...extsImage, ...extsVideo, ".gif"]);
7474
75/** These files read file contents into `contents`, as-is */75/** These files read file contents into `contents`, as-is */
76export const extsReadContents = new Set([".txt", ".chat", ".dirsort"]);76export const extsReadContents = new Set([".txt", ".chat", ".dirsort", ".date"]);
7777
78export const extsArchive = new Set([78export const extsArchive = new Set([
79 ".zip",79 ".zip",
src/file-viewer/transcode-rules.ts+2-1
...@@ -98,7 +98,8 @@ export const imagePresets = [...@@ -98,7 +98,8 @@ export const imagePresets = [
98];98];
9999
100export async function getVideoInputArgs(path: Path): Promise<InputArgs> {100export async function getVideoInputArgs(path: Path): Promise<InputArgs> {
101 const config = await path.replaceExt(".json").readIfExists<any>("json");101 const config = await path.parent!.join("." + path.replaceExt(".json").base)
102 .readIfExists<any>("json");
102 if (config) {103 if (config) {
103 if (config.encoder && typeof config.encoder.videoSrc === "string") {104 if (config.encoder && typeof config.encoder.videoSrc === "string") {
104 const { videoSrc, audioSrc, rate } = config.encoder;105 const { videoSrc, audioSrc, rate } = config.encoder;
src/file-viewer/views/clofi.css+3
...@@ -695,6 +695,9 @@ h3 {...@@ -695,6 +695,9 @@ h3 {
695 text-decoration: underline;695 text-decoration: underline;
696 }696 }
697}697}
698.nodate {
699 padding-left: 0.5rem;
700}
698701
699/* MOBILE */702/* MOBILE */
700@media (max-width: 1000px) {703@media (max-width: 1000px) {
src/file-viewer/views/clofi.tsx+15-2
...@@ -330,6 +330,10 @@ const sortOverrides: Record<string, (a: MediaFile, b: MediaFile) => number> = {...@@ -330,6 +330,10 @@ const sortOverrides: Record<string, (a: MediaFile, b: MediaFile) => number> = {
330 "/2017": sortChronologicalStartToEnd,330 "/2017": sortChronologicalStartToEnd,
331};331};
332332
333interface DirConfig {
334 hideChildrenDates?: boolean;
335}
336
333function DirView({337function DirView({
334 dir,338 dir,
335 activeFilename,339 activeFilename,
...@@ -363,12 +367,21 @@ function DirView({...@@ -363,12 +367,21 @@ function DirView({
363 const sortedFiles = unsortedFiles.sort(sorter);367 const sortedFiles = unsortedFiles.sort(sorter);
364 const readme = sortedFiles.find((f) => f.basename === readmeFile);368 const readme = sortedFiles.find((f) => f.basename === readmeFile);
365 if (readme && isLast) activeFilename ||= readmeFile;369 if (readme && isLast) activeFilename ||= readmeFile;
370
371 const extraConfig: DirConfig = dir.processors
372 ? JSON.parse(dir.processors)
373 : {};
374
366 const main = (375 const main = (
367 <div class="content primary">376 <div class="content primary">
368 <ul>377 <ul>
369 {sortedFiles.map((file) => {378 {sortedFiles.map((file) => {
370 return (379 return (
371 <ListItem file={file} active={activeFilename === file.basename} />380 <ListItem
381 file={file}
382 active={activeFilename === file.basename}
383 noDate={extraConfig.hideChildrenDates ?? false}
384 />
372 );385 );
373 })}386 })}
374 </ul>387 </ul>
...@@ -434,7 +447,7 @@ function ListItem({...@@ -434,7 +447,7 @@ function ListItem({
434 <li>447 <li>
435 <a448 <a
436 class={(active ? "active " : "") + (isReadme ? "readme " : "") +449 class={(active ? "active " : "") + (isReadme ? "readme " : "") +
437 "li custom"}450 "li custom" + (noDate ? " nodate" : "")}
438 href={file.extension === ".lnk"451 href={file.extension === ".lnk"
439 ? file.contents452 ? file.contents
440 : isReadme453 : isReadme