Skip to content

Commit a5193ee

Browse files
authored
Expand asset upload script to include audio files and rename tasks (#143)
- Rename upload task from upload-images to upload-assets for clarity - Update script to handle additional audio file types (m4a, mp3, wav, ogg, aac, flac) alongside images - Modify scanning, hashing, uploading, and manifest generation to support both images and audio - Improve manifest and variable names to reflect broader asset coverage - Ensure idempotence by skipping already uploaded assets - Update logs to reflect total assets processed, enhancing monitoring and debugging
1 parent d8bd726 commit a5193ee

2 files changed

Lines changed: 30 additions & 25 deletions

File tree

mise.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ run = "vitest --ui"
3636
[tasks.prefix-codes]
3737
run = "bun scripts/prefix-codes.ts"
3838

39-
[tasks.upload-images]
40-
run = "bun scripts/upload-images.ts"
41-
description = "Upload images to R2 and update manifest"
39+
[tasks.upload-assets]
40+
run = "bun scripts/upload-assets.ts"
41+
description = "Upload assets (images, audio) to R2 and update manifest"
4242

4343
[tasks.deploy]
4444
run = "wrangler deploy"
Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
#!/usr/bin/env bun
22

33
/**
4-
* Upload images from public/assets to Cloudflare R2
4+
* Upload assets (images, audio) from public/assets to Cloudflare R2
55
*
66
* This script:
7-
* - Scans public/assets for images (png, jpg, jpeg, webp, gif, svg)
8-
* - Generates SHA-256 hash for each image
7+
* - Scans public/assets for media files (images: png, jpg, jpeg, webp, gif, svg; audio: m4a, mp3, wav, ogg)
8+
* - Generates SHA-256 hash for each file
99
* - Uploads to R2 with content-addressable key (hash.ext)
1010
* - Creates manifest mapping original paths to R2 URLs
11-
* - Skips uploads for images already in R2 (idempotent)
11+
* - Skips uploads for files already in R2 (idempotent)
1212
*
1313
* Path structure:
1414
* - Local: public/assets/talks/codegen-in-rust/slide-1.png
@@ -29,10 +29,10 @@ import { join, relative } from "path";
2929

3030
const BUCKET_NAME = "just-be-dev-assets";
3131
const CUSTOM_DOMAIN = "https://assets.just-be.dev";
32-
const IMAGE_DIR = join(import.meta.dir, "../public/assets");
32+
const ASSETS_DIR = join(import.meta.dir, "../public/assets");
3333
const MANIFEST_PATH = join(import.meta.dir, "../src/content/image-manifest.json");
3434

35-
interface ImageManifest {
35+
interface AssetManifest {
3636
version: string;
3737
images: Record<string, {
3838
hash: string;
@@ -46,8 +46,13 @@ async function hashFile(filePath: string): Promise<string> {
4646
return createHash("sha256").update(content).digest("hex");
4747
}
4848

49-
async function findImages(dir: string): Promise<string[]> {
50-
const imageExtensions = ['.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg'];
49+
async function findAssets(dir: string): Promise<string[]> {
50+
const assetExtensions = [
51+
// Images
52+
'.png', '.jpg', '.jpeg', '.webp', '.gif', '.svg',
53+
// Audio
54+
'.m4a', '.mp3', '.wav', '.ogg', '.aac', '.flac'
55+
];
5156
const results: string[] = [];
5257

5358
async function walk(currentDir: string): Promise<void> {
@@ -58,7 +63,7 @@ async function findImages(dir: string): Promise<string[]> {
5863
await walk(fullPath);
5964
} else if (entry.isFile()) {
6065
const ext = entry.name.toLowerCase().slice(entry.name.lastIndexOf('.'));
61-
if (imageExtensions.includes(ext)) {
66+
if (assetExtensions.includes(ext)) {
6267
results.push(fullPath);
6368
}
6469
}
@@ -83,27 +88,27 @@ async function uploadToR2(localPath: string, key: string): Promise<void> {
8388
await $`wrangler r2 object put ${BUCKET_NAME}/${key} --file ${localPath} --remote`;
8489
}
8590

86-
async function uploadImages() {
87-
console.log(`Scanning images in: ${IMAGE_DIR}\n`);
91+
async function uploadAssets() {
92+
console.log(`Scanning assets in: ${ASSETS_DIR}\n`);
8893

89-
const imagePaths = await findImages(IMAGE_DIR);
90-
console.log(`Found ${imagePaths.length} images\n`);
94+
const assetPaths = await findAssets(ASSETS_DIR);
95+
console.log(`Found ${assetPaths.length} assets\n`);
9196

92-
const manifest: ImageManifest = {
97+
const manifest: AssetManifest = {
9398
version: "1.0",
9499
images: {},
95100
};
96101

97102
let uploadCount = 0;
98103
let skipCount = 0;
99104

100-
for (const imagePath of imagePaths) {
101-
const hash = await hashFile(imagePath);
102-
const ext = imagePath.split(".").pop()!;
105+
for (const assetPath of assetPaths) {
106+
const hash = await hashFile(assetPath);
107+
const ext = assetPath.split(".").pop()!;
103108
const key = `${hash}.${ext}`;
104109

105110
// Store path relative to public/assets (e.g., "talks/codegen-in-rust/slide-1.png")
106-
const originalPath = relative(join(import.meta.dir, "../public/assets"), imagePath);
111+
const originalPath = relative(join(import.meta.dir, "../public/assets"), assetPath);
107112

108113
const exists = await checkR2Exists(key);
109114

@@ -112,11 +117,11 @@ async function uploadImages() {
112117
skipCount++;
113118
} else {
114119
console.log(`⬆ ${originalPath}${key}`);
115-
await uploadToR2(imagePath, key);
120+
await uploadToR2(assetPath, key);
116121
uploadCount++;
117122
}
118123

119-
const stats = await Bun.file(imagePath).stat();
124+
const stats = await Bun.file(assetPath).stat();
120125
manifest.images[originalPath] = {
121126
hash,
122127
size: stats.size,
@@ -126,10 +131,10 @@ async function uploadImages() {
126131

127132
await writeFile(MANIFEST_PATH, JSON.stringify(manifest, null, 2) + "\n");
128133
console.log(`\n✅ Manifest written to ${MANIFEST_PATH}`);
129-
console.log(`\nUploaded: ${uploadCount}, Skipped: ${skipCount}, Total: ${imagePaths.length}`);
134+
console.log(`\nUploaded: ${uploadCount}, Skipped: ${skipCount}, Total: ${assetPaths.length}`);
130135
}
131136

132-
uploadImages().catch((error) => {
137+
uploadAssets().catch((error) => {
133138
console.error("Fatal error:", error);
134139
process.exit(1);
135140
});

0 commit comments

Comments
 (0)