-
Notifications
You must be signed in to change notification settings - Fork 0
/
player.ts
140 lines (112 loc) · 3.72 KB
/
player.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { BunShell } from "@jhuggett/terminal";
import { SubscribableEvent } from "@jhuggett/terminal/subscribable-event";
import { exec } from "child_process";
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
const execute = async (
command: string
): Promise<{ stdout: string; stderr: string }> => {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) throw error;
resolve({
stdout,
stderr,
});
});
});
};
export class Player {
constructor(public shell: BunShell) {}
async start() {
await execute(`osascript -e 'tell application "Music" to play'`);
await this.getPlayerState();
}
async stop() {
await execute(`osascript -e 'tell application "Music" to pause'`);
await this.getPlayerState();
}
async next() {
await execute(`osascript -e 'tell application "Music" to next track'`);
await this.getCurrentTrackInformation();
await this.getProgress();
}
async previous() {
await execute(`osascript -e 'tell application "Music" to back track'`);
await this.getCurrentTrackInformation();
await this.getProgress();
}
currentTrackDuration?: number;
async getCurrentTrackDuration() {
const result = await execute(
`osascript -e 'tell application "Music" to get duration of current track'`
);
this.currentTrackDuration = parseFloat(result.stdout.trim());
}
currentTrackName?: string;
async getCurrentTrackName() {
const result = await execute(
`osascript -e 'tell application "Music" to get name of current track'`
);
this.currentTrackName = result.stdout.trim();
}
currentTrackAlbumName?: string;
async getCurrentTrackAlbumName() {
const result = await execute(
`osascript -e 'tell application "Music" to get album of current track'`
);
this.currentTrackAlbumName = result.stdout.trim();
}
currentTrackArtistName?: string;
async getCurrentTrackArtistName() {
const result = await execute(
`osascript -e 'tell application "Music" to get artist of current track'`
);
this.currentTrackArtistName = result.stdout.trim();
}
async getCurrentTrackInformation() {
await this.getCurrentTrackDuration();
await this.getCurrentTrackName();
await this.getCurrentTrackAlbumName();
await this.getCurrentTrackArtistName();
}
onProgressChange: SubscribableEvent<number> = new SubscribableEvent();
currentTrackProgress?: number;
async getProgress() {
const result = await execute(
`osascript -e 'tell application "Music" to get player position'`
);
const state = result.stdout.trim();
this.currentTrackProgress = parseFloat(state);
this.onProgressChange.emit(this.currentTrackProgress);
}
async followProgress() {
if (!this.currentTrackProgress) {
await this.getCurrentTrackInformation();
}
while (this.currentState === "playing") {
const lastProgress = this.currentTrackProgress;
await this.getProgress();
const isNewTrack =
(lastProgress &&
this.currentTrackProgress &&
lastProgress > this.currentTrackProgress) ||
(lastProgress && !this.currentTrackProgress);
if (isNewTrack) {
await this.getCurrentTrackInformation();
}
await sleep(1000);
}
}
currentState: "playing" | "paused" = "paused";
async getPlayerState(): Promise<"playing" | "paused"> {
const result = await execute(
`osascript -e 'tell application "Music" to get player state'`
);
const state = result.stdout.trim();
this.currentState = state as "playing" | "paused";
if (this.currentState === "playing") {
this.followProgress();
}
return this.currentState;
}
}