|
#!/usr/bin/env -S deno run --allow-read --allow-write |
|
import * as stdPath from "jsr:@std/[email protected]"; |
|
|
|
interface ListEntry { |
|
stat: Deno.FileInfo; |
|
name: string; |
|
pathname: stdPath.ParsedPath; |
|
} |
|
|
|
interface CopyArgs { |
|
src: string; |
|
dest: string; |
|
} |
|
|
|
async function* list(indir: string): AsyncGenerator<ListEntry> { |
|
for await (const { name: basename } of Deno.readDir(indir)) { |
|
const resolve = stdPath.resolve(indir, basename); |
|
yield { |
|
stat: await Deno.stat(resolve), |
|
name: resolve, |
|
pathname: stdPath.parse(resolve), |
|
}; |
|
} |
|
} |
|
|
|
function padNumber(n: number): string { |
|
return n.toString().padStart(2, "0"); |
|
} |
|
|
|
async function* paths( |
|
indir: string, |
|
outdir: string, |
|
): AsyncGenerator<CopyArgs, void> { |
|
for await (const { stat, name, pathname } of list(indir)) { |
|
if (stat.birthtime === null) { |
|
console.error("atime null:", name); |
|
continue; |
|
} |
|
const year = stat.birthtime.getFullYear().toString(); |
|
const month = padNumber(stat.birthtime.getMonth() + 1); |
|
const date = padNumber(stat.birthtime.getDate()); |
|
const hours = padNumber(stat.birthtime.getHours()); |
|
const minutes = padNumber(stat.birthtime.getMinutes()); |
|
const dest = stdPath.resolve( |
|
outdir, |
|
year, |
|
month, |
|
`${year}-${month}-${date}-${hours}h${minutes}m${pathname.ext}`, |
|
); |
|
yield { |
|
src: name, |
|
dest, |
|
}; |
|
} |
|
} |
|
|
|
async function cpRecursive(src: string, dest: string) { |
|
await Deno.mkdir(stdPath.dirname(dest), { |
|
recursive: true, |
|
}); |
|
await Deno.copyFile(src, dest); |
|
console.log(src, "->", dest); |
|
} |
|
|
|
async function unlink(f: string) { |
|
await Deno.remove(f); |
|
console.log("Removed", f); |
|
} |
|
|
|
function usage() { |
|
console.error( |
|
"Usage: %s INDIR OUTDIR", |
|
import.meta.filename ?? import.meta.url, |
|
); |
|
} |
|
|
|
async function main(args: string[]): Promise<number> { |
|
if (args.length < 2) { |
|
usage(); |
|
return 1; |
|
} |
|
try { |
|
const [indir, outdir] = args; |
|
const infiles: string[] = []; |
|
for await (const { src, dest } of paths(indir, outdir)) { |
|
await cpRecursive(src, dest); |
|
infiles.push(src); |
|
} |
|
if (infiles.length > 0 && confirm("Remove source files?")) { |
|
await Promise.all(infiles.map((i) => unlink(i))); |
|
} |
|
} catch (error) { |
|
console.error(error); |
|
return 2; |
|
} |
|
return 0; |
|
} |
|
|
|
if (import.meta.main) { |
|
Deno.exit(await main(Deno.args.slice())); |
|
} |