feat: 后端改为deno

This commit is contained in:
ethan.chen
2025-11-05 11:43:47 +08:00
parent ba3435e1a0
commit f67a3835fa
27 changed files with 1449 additions and 1404 deletions

View File

@@ -0,0 +1,230 @@
/**
* Apple Music 控制和状态获取模块 (Deno 版本)
* 负责与 Apple Music 应用交互的所有功能
*/
export interface MusicStatus {
status: "playing" | "paused" | "stopped" | "notrunning" | "error";
track_name?: string;
artist?: string;
position?: number;
duration?: number;
error?: string;
}
export interface ControlResult {
status: "success" | "error";
message?: string;
}
/**
* 运行 AppleScript 并返回结果
*/
async function runAppleScript(script: string): Promise<string> {
const process = new Deno.Command("osascript", {
args: ["-e", script],
stdout: "piped",
stderr: "piped",
});
try {
const { code, stdout, stderr } = await process.output();
const output = new TextDecoder().decode(stdout).trim();
const error = new TextDecoder().decode(stderr).trim();
if (code !== 0) {
console.warn(`AppleScript 执行失败: ${error}`);
return "";
}
return output;
} catch (error) {
console.error(`执行 AppleScript 时发生错误: ${error}`);
return "";
}
}
/**
* 获取 Apple Music 播放状态 - 适配 macOS Tahoe
*/
export async function getMusicStatus(): Promise<MusicStatus> {
// 先获取播放状态
const statusScript = `
tell application "Music"
if it is running then
set playerState to (get player state) as string
return playerState
else
return "notrunning"
end if
end tell
`;
try {
const playerState = await runAppleScript(statusScript);
if (!playerState) {
console.error("AppleScript 执行失败,返回了空值");
return { status: "error", error: "AppleScript execution failed" };
}
if (playerState === "notrunning") {
return { status: "notrunning" };
}
if (playerState === "stopped") {
return { status: "stopped" };
}
// 尝试获取歌曲信息
try {
const infoScript = `
tell application "Music"
if it is running then
try
set trackName to ""
set artistName to ""
set pos to 0
set dur to 0
if exists current track then
set t to current track
try
set trackName to (name of t) as text
end try
try
set artistName to (artist of t) as text
end try
try
set dur to (duration of t) as real
end try
end if
try
set pos to (player position) as real
end try
return "success|||" & trackName & "|||" & artistName & "|||" & (pos as text) & "|||" & (dur as text)
on error errMsg
return "error|||" & errMsg
end try
else
return "notrunning"
end if
end tell
`;
const infoResult = await runAppleScript(infoScript);
if (infoResult && infoResult.startsWith("success|||")) {
const parts = infoResult.split("|||");
if (parts.length >= 5) {
return {
status: playerState as "playing" | "paused",
track_name: parts[1],
artist: parts[2],
position: parseFloat(parts[3]),
duration: parseFloat(parts[4]),
};
}
}
} catch (error) {
console.warn(`获取歌曲信息失败: ${error}`);
}
// 如果无法获取歌曲信息,至少返回播放状态
return {
status: playerState as "playing" | "paused",
track_name: "无法获取歌曲信息",
artist: "无法获取艺术家信息",
position: 0,
duration: 0,
};
} catch (error) {
console.error(`获取音乐状态时发生异常: ${error}`);
return { status: "error", error: String(error) };
}
}
/**
* 控制 Apple Music 播放
*/
export async function controlMusic(
action: string,
position?: number
): Promise<ControlResult> {
let script = "";
try {
switch (action) {
case "playpause":
script = `
tell application "Music"
if it is running then
playpause
return "ok"
else
return "notrunning"
end if
end tell
`;
break;
case "previous":
script = `
tell application "Music"
if it is running then
previous track
return "ok"
else
return "notrunning"
end if
end tell
`;
break;
case "next":
script = `
tell application "Music"
if it is running then
next track
return "ok"
else
return "notrunning"
end if
end tell
`;
break;
case "seek":
if (position !== undefined) {
script = `
tell application "Music"
if it is running then
set player position to ${position}
return "ok"
else
return "notrunning"
end if
end tell
`;
} else {
return { status: "error", message: "位置参数不能为空" };
}
break;
default:
return { status: "error", message: "未知的命令" };
}
const result = await runAppleScript(script);
if (result === "ok") {
return { status: "success" };
} else {
return { status: "error", message: result };
}
} catch (error) {
return { status: "error", message: String(error) };
}
}