Skip to content

Commit 9b483d4

Browse files
authored
Fix Open Folder and update to new Deno.spawn API (#23)
1 parent 1f987d1 commit 9b483d4

File tree

7 files changed

+85
-74
lines changed

7 files changed

+85
-74
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,15 @@ The same folder in Windows Explorer. AkaiGrid is better, right?
6161

6262
Extra requirements:
6363

64-
- Install Deno 2.3.1 or higher (https://docs.deno.com/runtime/)
64+
- Install Deno 2.7.1 or higher (https://docs.deno.com/runtime/)
6565
- Install Git (https://git-scm.com/downloads)
6666

6767
1. Clone this repository and install dependencies:
6868

6969
```bash
7070
git clone https://github.com/louislam/akaigrid
7171
cd akaigrid
72-
git checkout 1.0.2
72+
git checkout 1.2.0
7373
deno task setup
7474
```
7575

backend/akaigrid.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,19 +202,18 @@ export class AkaiGrid {
202202
// If player is set, use it to open the file
203203
if (this.config.player && this.config.player.trim() !== "") {
204204
log.debug(`Using player ${this.config.player} to open the file.`);
205-
const command = new Deno.Command(this.config.player, {
206-
args: [path],
207-
stdout: "null",
208-
stderr: "null",
209-
});
210-
command.spawn();
205+
Deno.spawn(this.config.player, [path]);
211206
} else {
212207
start(path);
213208
}
214209

215210
// Also update the date accessed for the parent directory
216-
if (this.config.bringFolderToTop) {
217-
await this.bringFolderToTop(path);
211+
try {
212+
if (this.config.bringFolderToTop) {
213+
await this.bringFolderToTop(path);
214+
}
215+
} catch (e) {
216+
// If it is already the top, it will throw an error, we can ignore it
218217
}
219218

220219
// Update the date accessed
@@ -223,6 +222,12 @@ export class AkaiGrid {
223222
}
224223
}
225224

225+
async openFolder(folder: string) {
226+
this.checkAllowedPath(folder);
227+
log.debug(`Opening folder ${folder}`);
228+
Deno.spawn("explorer.exe", [folder]);
229+
}
230+
226231
async setDone(path: string, done: boolean) {
227232
this.checkAllowedPath(path);
228233
log.debug(`Setting path ${path} to done: ${done}`);

backend/history.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,13 @@ import { ObjectAsArray } from "../common/util.ts";
33

44
export async function getAllMPCHCMediaHistory(): Promise<ObjectAsArray<number>> {
55
const key = "HKEY_CURRENT_USER\\Software\\MPC-HC\\MPC-HC\\MediaHistory";
6-
const command = new Deno.Command("reg.exe", {
7-
args: [
8-
"query",
9-
key,
10-
"/s",
11-
"/v",
12-
"FilePosition",
13-
],
14-
});
15-
16-
let { code, stdout, stderr } = await command.output();
6+
const { code, stdout, stderr } = await Deno.spawnAndWait("reg.exe", [
7+
"query",
8+
key,
9+
"/s",
10+
"/v",
11+
"FilePosition",
12+
]);
1713

1814
if (code !== 0) {
1915
console.error("Error executing command:", code, stderr);

backend/server.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as path from "@std/path";
66
import { serveDir, serveFile } from "@std/http/file-server";
77
import { DirConfigSchema, EntryDisplayObject, ObjectAsArray } from "../common/util.ts";
88
import { getAllMPCHCMediaHistory } from "./history.ts";
9-
import {kv} from "./db/kv.ts";
9+
import { kv } from "./db/kv.ts";
1010

1111
export class Server {
1212
akaiGrid: AkaiGrid;
@@ -186,6 +186,28 @@ export class Server {
186186
}
187187
});
188188

189+
// Open Folder file
190+
this.router.add("POST", "/api/open-folder/:path", async (_req, params) => {
191+
try {
192+
const path = params.path;
193+
if (!path) {
194+
return this.errorResponse(new Error("No path specified"));
195+
}
196+
log.info("Open:", path);
197+
await this.akaiGrid.openFolder(path);
198+
const res = Response.json({
199+
status: true,
200+
});
201+
allowDevAllOrigin(res);
202+
return res;
203+
} catch (error) {
204+
if (error instanceof Error) {
205+
log.error(error.message);
206+
}
207+
return this.errorResponse(error);
208+
}
209+
});
210+
189211
// Set the path to done?
190212
// :yes = true/false
191213
this.router.add("POST", "/api/done/:path/:yes", async (_req, params) => {
@@ -245,7 +267,6 @@ export class Server {
245267
if (file.isFile && file.name.endsWith(".jpg")) {
246268
const thumbnailPath = path.join(dir, file.name);
247269

248-
249270
// Check if there is a corresponding file in the kv store
250271
const originalPath = await kv().get<string>(["thumbnail", thumbnailPath]);
251272
if (originalPath.value) {

backend/util.ts

Lines changed: 38 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as log from "@std/log";
22
import process from "node:process";
3-
import childProcess from "node:child_process";
43
import { z } from "zod";
54
import * as path from "@std/path";
65
import { createHash } from "node:crypto";
@@ -107,8 +106,7 @@ export function getFrontendDir(): string {
107106
}
108107

109108
export function start(path: string) {
110-
const escapedPath = escapeString(path);
111-
childProcess.exec(`start "" ${escapedPath}`);
109+
Deno.spawn("cmd", ["/c", "start", "", path]);
112110
}
113111

114112
export function isDev() {
@@ -181,23 +179,19 @@ export function getShortHash(bytes: Uint8Array): string {
181179
}
182180

183181
export async function getVideoInfo(videoPath: string): Promise<VideoInfo> {
184-
// ffprobe -v error -select_streams v:0 -show_entries stream=codec_name,width,height,duration -of json input_video.mp4
185-
const command = new Deno.Command(ffprobe, {
186-
args: [
187-
"-v",
188-
"error",
189-
"-select_streams",
190-
"v:0",
191-
"-show_entries",
192-
"stream=codec_name,width,height",
193-
"-show_entries",
194-
"format=duration",
195-
"-of",
196-
"json",
197-
videoPath,
198-
],
199-
});
200-
const output = await command.output();
182+
const output = await Deno.spawnAndWait(ffprobe, [
183+
"-v",
184+
"error",
185+
"-select_streams",
186+
"v:0",
187+
"-show_entries",
188+
"stream=codec_name,width,height",
189+
"-show_entries",
190+
"format=duration",
191+
"-of",
192+
"json",
193+
videoPath,
194+
]);
201195
if (output.code !== 0) {
202196
const errorMsg = new TextDecoder().decode(output.stderr);
203197
throw new Error(`Error executing ffprobe: ${errorMsg}, ${output.code}`);
@@ -220,18 +214,15 @@ export async function getVideoInfo(videoPath: string): Promise<VideoInfo> {
220214
}
221215

222216
export async function generateThumbnail(videoPath: string, thumbnailPath: string) {
223-
let command = new Deno.Command(ffprobe, {
224-
args: [
225-
"-v",
226-
"error",
227-
"-show_entries",
228-
"format=duration",
229-
"-of",
230-
"default=noprint_wrappers=1:nokey=1",
231-
videoPath,
232-
],
233-
});
234-
let output = await command.output();
217+
let output = await Deno.spawnAndWait(ffprobe, [
218+
"-v",
219+
"error",
220+
"-show_entries",
221+
"format=duration",
222+
"-of",
223+
"default=noprint_wrappers=1:nokey=1",
224+
videoPath,
225+
]);
235226

236227
if (output.code !== 0) {
237228
const errorMsg = new TextDecoder().decode(output.stderr);
@@ -248,25 +239,23 @@ export async function generateThumbnail(videoPath: string, thumbnailPath: string
248239

249240
const targetWidth = 512;
250241

251-
command = new Deno.Command(ffmpeg, {
252-
args: [
253-
"-ss",
254-
target + "",
255-
"-i",
256-
videoPath,
257-
"-vf",
258-
// Generate thumbnail's width is always fixed
259-
// SAR = Storage Aspect Ratio, some videos let's say original resolution is 1440x1080, but the video is 16:9, so the SAR is 1.3333
260-
// Width: 512px
261-
// Height Formula = Height * (512 / Width) * (1 / SAR)
262-
`scale=${targetWidth}:ih*(1/sar)*(${targetWidth}/iw)`,
263-
"-vframes",
264-
"1",
265-
thumbnailPath,
266-
],
242+
output = await Deno.spawnAndWait(ffmpeg, [
243+
"-ss",
244+
target + "",
245+
"-i",
246+
videoPath,
247+
"-vf",
248+
// Generate thumbnail's width is always fixed
249+
// SAR = Storage Aspect Ratio, some videos let's say original resolution is 1440x1080, but the video is 16:9, so the SAR is 1.3333
250+
// Width: 512px
251+
// Height Formula = Height * (512 / Width) * (1 / SAR)
252+
`scale=${targetWidth}:ih*(1/sar)*(${targetWidth}/iw)`,
253+
"-vframes",
254+
"1",
255+
thumbnailPath,
256+
], {
267257
stdout: "piped",
268258
});
269-
output = await command.output();
270259

271260
if (output.code !== 0) {
272261
log.error("Error executing ffmpeg:", output.stderr);

deno.jsonc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
// App Version
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44

55
"unstable": [
66
// So format Vue components

frontend/src/pages/List.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ function upper() {
313313
* Open the current path in the file explorer
314314
*/
315315
async function openExplorer() {
316-
const response = await fetch(baseURL + "/api/open/" + encodeURIComponent(path.value), {
316+
const response = await fetch(baseURL + "/api/open-folder/" + encodeURIComponent(path.value), {
317317
method: "POST",
318318
});
319319
if (!response.ok) {

0 commit comments

Comments
 (0)