Skip to content

Commit f15721c

Browse files
remove unsafe type assertion and unused packages
improve unit tests and add useful type
1 parent c09484a commit f15721c

22 files changed

+263
-279
lines changed

ffprobe.output.example.txt

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
index=0
2+
codec_name=av1
3+
codec_long_name=Alliance for Open Media AV1
4+
profile=Main
5+
codec_type=video
6+
codec_tag_string=av01
7+
codec_tag=0x31307661
8+
width=7680
9+
height=4086
10+
coded_width=7680
11+
coded_height=4086
12+
closed_captions=0
13+
film_grain=0
14+
has_b_frames=0
15+
sample_aspect_ratio=N/A
16+
display_aspect_ratio=N/A
17+
pix_fmt=yuv420p
18+
level=16
19+
color_range=tv
20+
color_space=bt709
21+
color_transfer=bt709
22+
color_primaries=bt709
23+
chroma_location=unspecified
24+
field_order=unknown
25+
refs=1
26+
id=0x1
27+
r_frame_rate=25/1
28+
avg_frame_rate=25/1
29+
time_base=1/12800
30+
start_pts=0
31+
start_time=0.000000
32+
duration_ts=75264
33+
duration=5.880000
34+
bit_rate=31789625
35+
max_bit_rate=N/A
36+
bits_per_raw_sample=N/A
37+
nb_frames=147
38+
nb_read_frames=N/A
39+
nb_read_packets=N/A
40+
extradata_size=21
41+
DISPOSITION:default=1
42+
DISPOSITION:dub=0
43+
DISPOSITION:original=0
44+
DISPOSITION:comment=0
45+
DISPOSITION:lyrics=0
46+
DISPOSITION:karaoke=0
47+
DISPOSITION:forced=0
48+
DISPOSITION:hearing_impaired=0
49+
DISPOSITION:visual_impaired=0
50+
DISPOSITION:clean_effects=0
51+
DISPOSITION:attached_pic=0
52+
DISPOSITION:timed_thumbnails=0
53+
DISPOSITION:non_diegetic=0
54+
DISPOSITION:captions=0
55+
DISPOSITION:descriptions=0
56+
DISPOSITION:metadata=0
57+
DISPOSITION:dependent=0
58+
DISPOSITION:still_image=0
59+
DISPOSITION:multilayer=0
60+
TAG:language=und
61+
TAG:handler_name=ISO Media file produced by Google Inc.
62+
TAG:vendor_id=[0][0][0][0]
63+
filename=/Users/panjunyu/Downloads/process/test.mp4
64+
nb_streams=2
65+
nb_programs=0
66+
nb_stream_groups=0
67+
format_name=mov,mp4,m4a,3gp,3g2,mj2
68+
format_long_name=QuickTime / MOV
69+
start_time=0.000000
70+
duration=5.880000
71+
size=23465085
72+
bit_rate=31925285
73+
probe_score=100
74+
TAG:major_brand=isom
75+
TAG:minor_version=512
76+
TAG:compatible_brands=isomav01iso2mp41
77+
TAG:title=Magic of Hong Kong. Mind-blowing cyberpunk drone video of the craziest Asia’s city by Timelab.pro
78+
TAG:encoder=Lavf60.3.100
79+
TAG:comment=www.mediahuman.com

real-test-input/1-1/2-1/3-1/image.jpeg

Loading

real-test-input/1-1/2-1/3-1/video.wmv

4.17 MB
Binary file not shown.

real-test-input/1-1/2-1/video.mp4

4.17 MB
Binary file not shown.

real-test-input/1-1/video.flv

4.17 MB
Binary file not shown.

real-test-input/1-2/2-2/image.png

Loading

real-test-input/1-2/2-2/video.mov

4.17 MB
Binary file not shown.

real-test-input/1-2/2-3/image.jpg

Loading

real-test-input/1-2/2-3/video.rmvb

4.17 MB
Binary file not shown.

real-test-input/1-2/video.mkv

4.17 MB
Binary file not shown.

real-test-input/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DO NOT DELETE THIS FOLDER!
2+
this folder is for testing purpose.

real-test-input/video.avi

4.17 MB
Binary file not shown.

src/constants.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// TODO: add ffmpeg support video ext
2-
export const COMMONVIDEOEXT = [
2+
export const SUPPORTVIDEOEXT = [
33
"avi",
44
"flv",
55
"mp4",

src/index.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
import { select } from "@inquirer/prompts";
44

5-
import { defaultInputDir, defaultOutputDir } from "./path.js";
6-
import { compressImages } from "./tasks/compress-images.js";
7-
import { compressVideos } from "./tasks/compress-video.js";
5+
import { defaultInputDir, defaultOutputDir } from "./path";
6+
import { compressImages } from "./tasks/compress-images";
7+
import { compressVideos } from "./tasks/compress-videos";
8+
import type { TaskType, VideoType } from "./types";
89

9-
async function askToplevelTask() {
10+
async function askToplevelTask(): Promise<TaskType> {
1011
return await select({
1112
message: "Select a task",
1213
choices: [
@@ -23,7 +24,7 @@ async function askToplevelTask() {
2324
});
2425
}
2526

26-
async function askVideoType() {
27+
async function askVideoType(): Promise<VideoType> {
2728
return await select({
2829
message: "Select video type",
2930
choices: [
@@ -37,7 +38,7 @@ async function askVideoType() {
3738
});
3839
}
3940

40-
async function runTask(taskType: string) {
41+
async function runTask(taskType: TaskType) {
4142
if (taskType === "compress videos") {
4243
const videoType = await askVideoType();
4344
await compressVideos({

src/path.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import "dotenv/config";
2-
2+
import path from "node:path";
33
import process from "node:process";
44

55
export const defaultInputDir = process.env["DEFAULTINPUTDIR"] as string;
66
export const defaultOutputDir = process.env["DEFAULTOUTPUTDIR"] as string;
7+
8+
export const realTestInputPath = path.join(
9+
import.meta.dir,
10+
"../real-test-input",
11+
);
12+
export const realTestOutputPath = path.join(
13+
import.meta.dir,
14+
"../real-test-output",
15+
);

src/tasks/compress-images.test.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { describe, expect, test } from "bun:test";
2+
3+
import { realTestInputPath } from "../path";
4+
import { walkDir } from "./compress-images";
5+
6+
describe("walkDir", () => {
7+
test("should return exact number of dirs of the real-test folder", async () => {
8+
const dirs = await walkDir([realTestInputPath]);
9+
expect(dirs.length).toBeGreaterThan(0);
10+
expect(dirs.length).toBe(7);
11+
});
12+
});
13+
14+
describe("integration test", () => {
15+
test.todo("compress images without reject", async () => {}, 60 * 1000);
16+
});

src/tasks/compress-images.ts

+21-34
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,42 @@
1-
import type { Dirent } from "node:fs";
21
import fs from "node:fs";
32
import fsp from "node:fs/promises";
43
import path from "node:path";
54

65
import sharp from "sharp";
76

8-
import { getAllImagesWithinDir, getFileNameFromPath } from "../utils";
7+
import { getAllImagesWithinPath, getFileNameFromPath } from "../utils";
98

109
interface CompressImagesArgs {
1110
inputDir: string;
1211
outputDir: string;
1312
}
1413

15-
async function dfsWalkDirFromPath(filepath: string) {
16-
const dirPathes: string[] = [];
17-
18-
await walk(filepath);
19-
20-
async function walk(filepath: string) {
21-
const files = await fsp.readdir(filepath, { withFileTypes: true });
22-
23-
for (let j = 0; j < files.length; j++) {
24-
const file = files[j] as Dirent;
25-
if (file.isDirectory()) {
26-
await walk(path.join(file.parentPath, file.name));
27-
}
28-
if (j === files.length - 1) {
29-
dirPathes.push(file.parentPath);
30-
}
31-
}
14+
export async function walkDir(remainingDirs: string[], accDirs: string[] = []) {
15+
if (remainingDirs.length === 0) {
16+
return accDirs;
3217
}
33-
34-
return dirPathes;
18+
const currentPath = remainingDirs.pop();
19+
if (!currentPath) {
20+
throw new Error("pop element from an empty array");
21+
}
22+
const files = await fsp.readdir(currentPath, { withFileTypes: true });
23+
const dirs = files
24+
.filter((file) => file.isDirectory())
25+
.map((dir) => path.join(dir.parentPath, dir.name));
26+
accDirs.push(currentPath);
27+
Array.prototype.push.apply(remainingDirs, dirs);
28+
return walkDir(remainingDirs, accDirs);
3529
}
3630

3731
export async function compressImages(args: CompressImagesArgs) {
3832
const { inputDir, outputDir } = args;
39-
40-
const dirs = await dfsWalkDirFromPath(inputDir);
41-
42-
for (let i = 0; i < dirs.length; i++) {
43-
const dir = dirs[i] as string;
44-
45-
const images = await getAllImagesWithinDir(dir);
46-
47-
for (let j = 0; j < images.length; j++) {
48-
const input = images[j] as string;
49-
33+
const dirs = await walkDir([inputDir]);
34+
for (const dir of dirs) {
35+
const images = await getAllImagesWithinPath(dir);
36+
for (const image of images) {
5037
await compressImage(
51-
input,
52-
path.join(outputDir, path.dirname(path.relative(inputDir, input))),
38+
image,
39+
path.join(outputDir, path.dirname(path.relative(inputDir, image))),
5340
);
5441
}
5542
}

0 commit comments

Comments
 (0)