Skip to content

Commit 0dabc70

Browse files
authored
add confirmation, parse server, local db to music app (#126)
1 parent ec3a37c commit 0dabc70

File tree

5 files changed

+134
-68
lines changed

5 files changed

+134
-68
lines changed

examples/calendar/src/calendarActionsSchema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export type EventTimeRange = {
6767
};
6868

6969
export type Event = {
70-
// date (example: March 22, 2024) or relative date (example: after EventReference)
70+
// date (example: March 22, 2024) or relative date (example: after EventReference)
7171
day: string;
7272
timeRange: EventTimeRange;
7373
description: string;

examples/music/src/dbInterface.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function executeQuery(query: string, params: any[] = []): Row[] | void {
1010
console.log(`Error executing query: ${query} against ${dbPath}`);
1111
return;
1212
}
13-
13+
1414
db.all(query, params, (error: Error, rows: Row[]) => {
1515
db.close();
1616

@@ -24,6 +24,15 @@ function executeQuery(query: string, params: any[] = []): Row[] | void {
2424
});
2525
}
2626

27+
export function insertTracks(tracks: SpotifyApi.TrackObjectFull[]) {
28+
let insertQuery = 'INSERT INTO tracks (id, title, artist_id, album_id, duration, release_data, genre)\nVALUES\n';
29+
for (const track of tracks) {
30+
// TODO: genre
31+
insertQuery += ` (${track.id},${track.name},${track.artists[0].id},${track.album.id},${track.duration_ms},${track.album.release_date})`;
32+
}
33+
34+
35+
}
2736
export function getArtists() {
2837
const query = "SELECT * FROM artists";
2938
const artists = executeQuery(query);

examples/music/src/endpoints.ts

Lines changed: 31 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -109,31 +109,20 @@ export async function getArtist(service: SpotifyService, id: string) {
109109
return undefined;
110110
}
111111

112-
export async function getHistory(
113-
service: SpotifyService,
114-
limit = limitMax,
115-
offset = 0
116-
) {
112+
export async function getHistoryURL(service: SpotifyService, url: string) {
117113
const config = {
118114
headers: {
119115
Authorization: `Bearer ${service.retrieveUser().token}`,
120116
},
121117
};
122118

123-
const recentlyPlayedUrl = getUrlWithParams(
124-
"https://api.spotify.com/v1/me/player/recently-played",
125-
{
126-
limit,
127-
offset,
128-
}
129-
);
130-
console.log(recentlyPlayedUrl);
131-
// params += `&after=${Date.parse('2023-01-01T00:00:00.000Z')}`;
132-
// params += `&before=${Date.now()}`;
119+
console.log(url);
133120
try {
134-
const spotifyResult = await axios.get(recentlyPlayedUrl, config);
121+
const spotifyResult = await axios.get(url, config);
135122

136-
return spotifyResult.data as SpotifyApi.UsersRecentlyPlayedTracksResponse;
123+
const spotData =
124+
spotifyResult.data as SpotifyApi.UsersRecentlyPlayedTracksResponse;
125+
return spotData;
137126
} catch (e) {
138127
if (e instanceof axios.AxiosError) {
139128
console.log(e.message);
@@ -144,30 +133,30 @@ export async function getHistory(
144133
return undefined;
145134
}
146135

147-
export async function getKRecent(service: SpotifyService, k = 100) {
148-
if (k > limitMax) {
149-
const playHistory = [] as SpotifyApi.PlayHistoryObject[];
150-
let offset = 0;
151-
while (k > 0) {
152-
let count = limitMax;
153-
if (k < count) {
154-
count = k;
155-
}
156-
const hist = await getHistory(service, count, offset);
157-
if (hist && hist.items) {
158-
playHistory.push(...hist.items);
159-
}
160-
k -= limitMax;
161-
offset += limitMax;
162-
}
163-
return playHistory;
164-
} else {
165-
const hist = await getHistory(service, k);
136+
export async function getRecent(
137+
service: SpotifyService,
138+
after = Date.parse("2023-01-01T00:00:00.000Z")
139+
) {
140+
const playHistory = [] as SpotifyApi.PlayHistoryObject[];
141+
console.log(new Date(after).toLocaleString());
142+
const params = {
143+
limit: 50,
144+
after,
145+
};
146+
let nextURL: string | null | undefined = getUrlWithParams(
147+
"https://api.spotify.com/v1/me/player/recently-played",
148+
params
149+
);
150+
while (nextURL) {
151+
const hist = await getHistoryURL(service, nextURL);
166152
if (hist && hist.items) {
167-
return hist.items;
153+
console.log(hist.items.length);
154+
playHistory.push(...hist.items);
168155
}
156+
nextURL = hist?.next;
157+
console.log(nextURL);
169158
}
170-
return undefined;
159+
return playHistory;
171160
}
172161

173162
export async function getUserProfile(service: SpotifyService) {
@@ -261,13 +250,12 @@ export async function play(
261250
if (contextUri) {
262251
smallTrack.context_uri = contextUri;
263252
if (trackNumber) {
264-
smallTrack.offset = { position: trackNumber};
253+
smallTrack.offset = { position: trackNumber };
265254
if (seekms) {
266255
smallTrack.position_ms = seekms;
267256
}
268257
}
269-
}
270-
else if (uris) {
258+
} else if (uris) {
271259
smallTrack.uris = uris;
272260
}
273261
const playUrl = getUrlWithParams(
@@ -344,7 +332,7 @@ export async function getQueue(service: SpotifyService) {
344332
};
345333
try {
346334
const spotifyResult = await axios.get(
347-
`https://api.spotify.com/v1/me/player/queue`,
335+
`https://api.spotify.com/v1/me/player/queue?limit=50`,
348336
config
349337
);
350338

@@ -456,10 +444,7 @@ export async function getPlaylists(service: SpotifyService) {
456444
return undefined;
457445
}
458446

459-
export async function getAlbumTracks(
460-
service: SpotifyService,
461-
albumId: string
462-
) {
447+
export async function getAlbumTracks(service: SpotifyService, albumId: string) {
463448
const config = {
464449
headers: {
465450
Authorization: `Bearer ${service.retrieveUser().token}`,

examples/music/src/localParser.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,31 @@
11
import chalk from "chalk";
2+
import axios from "axios";
3+
import path from "path";
4+
import dotenv from "dotenv";
5+
6+
dotenv.config({ path: path.join(__dirname, "../../../.env") });
7+
8+
export async function parseOut(request: string, surl: string) {
9+
try {
10+
const result = await axios.post(surl, {
11+
Text: request,
12+
});
13+
console.log(result.data);
14+
} catch (e) {
15+
if (e instanceof axios.AxiosError) {
16+
console.log(e.message);
17+
} else {
18+
throw e;
19+
}
20+
}
21+
}
222

323
export function localParser(userPrompt: string) {
424
userPrompt = userPrompt.trim();
25+
const surl = process.env.PARSER_SERVICE_ENDPOINT;
26+
if (surl) {
27+
parseOut(userPrompt, surl);
28+
}
529
if (
630
userPrompt === "play" ||
731
userPrompt === "resume" ||
@@ -45,10 +69,9 @@ export function localParser(userPrompt: string) {
4569
if (matchedShuffleSet) {
4670
const shuffleArg = matchedShuffleSet[1];
4771
let shuffleFunc = "";
48-
if (["on","true","yes"].includes(shuffleArg)) {
72+
if (["on", "true", "yes"].includes(shuffleArg)) {
4973
shuffleFunc = "shuffleOn";
50-
}
51-
else if (["off","false","no"].includes(shuffleArg)) {
74+
} else if (["off", "false", "no"].includes(shuffleArg)) {
5275
shuffleFunc = "shuffleOff";
5376
}
5477
if (shuffleFunc.length > 0) {
@@ -60,7 +83,6 @@ export function localParser(userPrompt: string) {
6083
},
6184
],
6285
});
63-
6486
}
6587
}
6688
}

examples/music/src/main.ts

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import fs from "fs";
22
import path from "path";
3+
import readline from "readline/promises";
34
import { Authzor } from "./authz";
45
import chalk from "chalk";
56
import dotenv from "dotenv";
67
import * as Filter from "./trackFilter";
78
import {
89
createLanguageModel,
910
createProgramTranslator,
10-
processRequests,
1111
Program,
1212
createModuleTextFromProgram,
1313
evaluateJsonProgram,
@@ -39,6 +39,7 @@ import {
3939
shuffle,
4040
getAlbumTracks,
4141
getQueue,
42+
getRecent,
4243
} from "./endpoints";
4344
import { listAvailableDevices, printStatus, selectDevice } from "./playback";
4445
import { SpotifyService, User } from "./service";
@@ -75,7 +76,7 @@ async function printTrackNames(
7576
let count = 1;
7677
for (const track of tracks) {
7778
let prefix = "";
78-
if (context && (tracks.length > 1)) {
79+
if (context && tracks.length > 1) {
7980
prefix = `T${count}: `;
8081
}
8182
console.log(chalk.cyanBright(`${prefix}${track.name}`));
@@ -236,14 +237,20 @@ async function handleCall(
236237
const currentQueue = await getQueue(clientContext.service);
237238
if (currentQueue) {
238239
// not yet supporting episidoes
239-
const filtered = currentQueue.queue.filter((item) => item.type === "track") as SpotifyApi.TrackObjectFull[];
240+
const filtered = currentQueue.queue.filter(
241+
(item) => item.type === "track"
242+
) as SpotifyApi.TrackObjectFull[];
240243
console.log(chalk.magentaBright("Current Queue:"));
241244
console.log(
242-
chalk.cyanBright(`--------------------------------------------`)
245+
chalk.cyanBright(
246+
`--------------------------------------------`
247+
)
243248
);
244249
await printTrackNames(filtered, clientContext);
245250
console.log(
246-
chalk.cyanBright(`--------------------------------------------`)
251+
chalk.cyanBright(
252+
`--------------------------------------------`
253+
)
247254
);
248255
await printStatus(clientContext);
249256
}
@@ -311,7 +318,10 @@ async function handleCall(
311318
break;
312319
}
313320
case "setVolume": {
314-
const newVolumeLevel = args[0] as number;
321+
let newVolumeLevel = args[0] as number;
322+
if (newVolumeLevel > 50) {
323+
newVolumeLevel = 50;
324+
}
315325
console.log(
316326
chalk.yellowBright(`setting volume to ${newVolumeLevel} ...`)
317327
);
@@ -326,8 +336,8 @@ async function handleCall(
326336
let nv = Math.floor(
327337
(1.0 + volumeChangeAmount / 100.0) * volpct
328338
);
329-
if (nv > 100) {
330-
nv = 100;
339+
if (nv > 50) {
340+
nv = 50;
331341
}
332342
console.log(chalk.yellowBright(`setting volume to ${nv} ...`));
333343
await setVolume(clientContext.service, nv);
@@ -495,10 +505,7 @@ async function handleCall(
495505
const playlistCollection = args[0] as PlaylistTrackCollection;
496506
if (playlistCollection) {
497507
const playlist = playlistCollection.getPlaylist();
498-
await deletePlaylist(
499-
clientContext.service,
500-
playlist.id
501-
);
508+
await deletePlaylist(clientContext.service, playlist.id);
502509
console.log(
503510
chalk.magentaBright(`playlist ${playlist.name} deleted`)
504511
);
@@ -527,7 +534,34 @@ async function handleCall(
527534
// set this to false to just look at llm generation without Spotify connection
528535
const spotifyConnect = true;
529536

530-
// Process requests interactively or from the input file specified on the command line
537+
export async function index(context: IClientContext) {
538+
let playHistory = await getRecent(
539+
context.service,
540+
Date.parse("2018-01-01T00:00:00.00Z")
541+
);
542+
if (playHistory) {
543+
console.log(playHistory?.length);
544+
let trackNames = '';
545+
playHistory.map((item) => {
546+
trackNames += item.track.name + '\n';
547+
});
548+
fs.writeFileSync("bigFetch.txt", trackNames);
549+
}
550+
}
551+
552+
function checkAck(input: string, program: Program): Program | undefined {
553+
const linput = input.toLocaleLowerCase();
554+
if (["y","yes","ok"].includes(linput)) {
555+
return program;
556+
} else {
557+
return undefined;
558+
}
559+
}
560+
561+
// whether to confirm each action with the user
562+
const confirmMode = true;
563+
564+
// Process requests interactively (no batch mode for now)
531565
async function musicApp() {
532566
const authz = new Authzor();
533567
authz.authorize(spotifyConnect, async (token) => {
@@ -541,7 +575,15 @@ async function musicApp() {
541575
)
542576
);
543577
}
544-
processRequests("🎵> ", process.argv[2], async (request) => {
578+
const musicPrompt = "🎵> ";
579+
const confirmPrompt = "👍👎 (answer y/n)> ";
580+
const stdio = readline.createInterface({ input: process.stdin, output: process.stdout });
581+
while (true) {
582+
const request = await stdio.question(musicPrompt);
583+
if (request.toLowerCase() === "quit" || request.toLowerCase() === "exit") {
584+
stdio.close();
585+
return;
586+
}
545587
const localResult = localParser(request);
546588
let program: Program | undefined = undefined;
547589
if (localResult) {
@@ -550,13 +592,21 @@ async function musicApp() {
550592
const response = await translator.translate(request);
551593
if (!response.success) {
552594
console.log(response.message);
553-
return;
595+
continue;
554596
}
555597
program = response.data;
556598
}
557599
if (program !== undefined) {
558600
chalkPlan(program);
559601
console.log(getData(createModuleTextFromProgram(program)));
602+
if (confirmMode && (!localResult)) {
603+
const input = await stdio.question(confirmPrompt);
604+
program = checkAck(input, program);
605+
if (program === undefined) {
606+
console.log("Thanks for the feedback. Canceling execution...")
607+
continue;
608+
}
609+
}
560610
if (context !== undefined) {
561611
const result = await evaluateJsonProgram(
562612
program,
@@ -576,7 +626,7 @@ async function musicApp() {
576626
}
577627
}
578628
}
579-
});
629+
}
580630
});
581631
}
582632

0 commit comments

Comments
 (0)