diff --git a/examples/calendar/src/calendarActionsSchema.ts b/examples/calendar/src/calendarActionsSchema.ts index 5c006c82..07a8bb81 100644 --- a/examples/calendar/src/calendarActionsSchema.ts +++ b/examples/calendar/src/calendarActionsSchema.ts @@ -67,7 +67,7 @@ export type EventTimeRange = { }; export type Event = { - // date (example: March 22, 2024) or relative date (example: after EventReference) + // date (example: March 22, 2024) or relative date (example: after EventReference) day: string; timeRange: EventTimeRange; description: string; diff --git a/examples/music/src/dbInterface.ts b/examples/music/src/dbInterface.ts index 09d001a6..5f5c87e6 100644 --- a/examples/music/src/dbInterface.ts +++ b/examples/music/src/dbInterface.ts @@ -10,7 +10,7 @@ function executeQuery(query: string, params: any[] = []): Row[] | void { console.log(`Error executing query: ${query} against ${dbPath}`); return; } - + db.all(query, params, (error: Error, rows: Row[]) => { db.close(); @@ -24,6 +24,15 @@ function executeQuery(query: string, params: any[] = []): Row[] | void { }); } +export function insertTracks(tracks: SpotifyApi.TrackObjectFull[]) { + let insertQuery = 'INSERT INTO tracks (id, title, artist_id, album_id, duration, release_data, genre)\nVALUES\n'; + for (const track of tracks) { + // TODO: genre + insertQuery += ` (${track.id},${track.name},${track.artists[0].id},${track.album.id},${track.duration_ms},${track.album.release_date})`; + } + + +} export function getArtists() { const query = "SELECT * FROM artists"; const artists = executeQuery(query); diff --git a/examples/music/src/endpoints.ts b/examples/music/src/endpoints.ts index e645e8f7..637af9cd 100644 --- a/examples/music/src/endpoints.ts +++ b/examples/music/src/endpoints.ts @@ -109,31 +109,20 @@ export async function getArtist(service: SpotifyService, id: string) { return undefined; } -export async function getHistory( - service: SpotifyService, - limit = limitMax, - offset = 0 -) { +export async function getHistoryURL(service: SpotifyService, url: string) { const config = { headers: { Authorization: `Bearer ${service.retrieveUser().token}`, }, }; - const recentlyPlayedUrl = getUrlWithParams( - "https://api.spotify.com/v1/me/player/recently-played", - { - limit, - offset, - } - ); - console.log(recentlyPlayedUrl); - // params += `&after=${Date.parse('2023-01-01T00:00:00.000Z')}`; - // params += `&before=${Date.now()}`; + console.log(url); try { - const spotifyResult = await axios.get(recentlyPlayedUrl, config); + const spotifyResult = await axios.get(url, config); - return spotifyResult.data as SpotifyApi.UsersRecentlyPlayedTracksResponse; + const spotData = + spotifyResult.data as SpotifyApi.UsersRecentlyPlayedTracksResponse; + return spotData; } catch (e) { if (e instanceof axios.AxiosError) { console.log(e.message); @@ -144,30 +133,30 @@ export async function getHistory( return undefined; } -export async function getKRecent(service: SpotifyService, k = 100) { - if (k > limitMax) { - const playHistory = [] as SpotifyApi.PlayHistoryObject[]; - let offset = 0; - while (k > 0) { - let count = limitMax; - if (k < count) { - count = k; - } - const hist = await getHistory(service, count, offset); - if (hist && hist.items) { - playHistory.push(...hist.items); - } - k -= limitMax; - offset += limitMax; - } - return playHistory; - } else { - const hist = await getHistory(service, k); +export async function getRecent( + service: SpotifyService, + after = Date.parse("2023-01-01T00:00:00.000Z") +) { + const playHistory = [] as SpotifyApi.PlayHistoryObject[]; + console.log(new Date(after).toLocaleString()); + const params = { + limit: 50, + after, + }; + let nextURL: string | null | undefined = getUrlWithParams( + "https://api.spotify.com/v1/me/player/recently-played", + params + ); + while (nextURL) { + const hist = await getHistoryURL(service, nextURL); if (hist && hist.items) { - return hist.items; + console.log(hist.items.length); + playHistory.push(...hist.items); } + nextURL = hist?.next; + console.log(nextURL); } - return undefined; + return playHistory; } export async function getUserProfile(service: SpotifyService) { @@ -261,13 +250,12 @@ export async function play( if (contextUri) { smallTrack.context_uri = contextUri; if (trackNumber) { - smallTrack.offset = { position: trackNumber}; + smallTrack.offset = { position: trackNumber }; if (seekms) { smallTrack.position_ms = seekms; } } - } - else if (uris) { + } else if (uris) { smallTrack.uris = uris; } const playUrl = getUrlWithParams( @@ -344,7 +332,7 @@ export async function getQueue(service: SpotifyService) { }; try { const spotifyResult = await axios.get( - `https://api.spotify.com/v1/me/player/queue`, + `https://api.spotify.com/v1/me/player/queue?limit=50`, config ); @@ -456,10 +444,7 @@ export async function getPlaylists(service: SpotifyService) { return undefined; } -export async function getAlbumTracks( - service: SpotifyService, - albumId: string -) { +export async function getAlbumTracks(service: SpotifyService, albumId: string) { const config = { headers: { Authorization: `Bearer ${service.retrieveUser().token}`, diff --git a/examples/music/src/localParser.ts b/examples/music/src/localParser.ts index 168c61a4..99d8df3a 100644 --- a/examples/music/src/localParser.ts +++ b/examples/music/src/localParser.ts @@ -1,7 +1,31 @@ import chalk from "chalk"; +import axios from "axios"; +import path from "path"; +import dotenv from "dotenv"; + +dotenv.config({ path: path.join(__dirname, "../../../.env") }); + +export async function parseOut(request: string, surl: string) { + try { + const result = await axios.post(surl, { + Text: request, + }); + console.log(result.data); + } catch (e) { + if (e instanceof axios.AxiosError) { + console.log(e.message); + } else { + throw e; + } + } +} export function localParser(userPrompt: string) { userPrompt = userPrompt.trim(); + const surl = process.env.PARSER_SERVICE_ENDPOINT; + if (surl) { + parseOut(userPrompt, surl); + } if ( userPrompt === "play" || userPrompt === "resume" || @@ -45,10 +69,9 @@ export function localParser(userPrompt: string) { if (matchedShuffleSet) { const shuffleArg = matchedShuffleSet[1]; let shuffleFunc = ""; - if (["on","true","yes"].includes(shuffleArg)) { + if (["on", "true", "yes"].includes(shuffleArg)) { shuffleFunc = "shuffleOn"; - } - else if (["off","false","no"].includes(shuffleArg)) { + } else if (["off", "false", "no"].includes(shuffleArg)) { shuffleFunc = "shuffleOff"; } if (shuffleFunc.length > 0) { @@ -60,7 +83,6 @@ export function localParser(userPrompt: string) { }, ], }); - } } } diff --git a/examples/music/src/main.ts b/examples/music/src/main.ts index 58bfda72..5facd0b5 100644 --- a/examples/music/src/main.ts +++ b/examples/music/src/main.ts @@ -1,5 +1,6 @@ import fs from "fs"; import path from "path"; +import readline from "readline/promises"; import { Authzor } from "./authz"; import chalk from "chalk"; import dotenv from "dotenv"; @@ -7,7 +8,6 @@ import * as Filter from "./trackFilter"; import { createLanguageModel, createProgramTranslator, - processRequests, Program, createModuleTextFromProgram, evaluateJsonProgram, @@ -39,6 +39,7 @@ import { shuffle, getAlbumTracks, getQueue, + getRecent, } from "./endpoints"; import { listAvailableDevices, printStatus, selectDevice } from "./playback"; import { SpotifyService, User } from "./service"; @@ -75,7 +76,7 @@ async function printTrackNames( let count = 1; for (const track of tracks) { let prefix = ""; - if (context && (tracks.length > 1)) { + if (context && tracks.length > 1) { prefix = `T${count}: `; } console.log(chalk.cyanBright(`${prefix}${track.name}`)); @@ -236,14 +237,20 @@ async function handleCall( const currentQueue = await getQueue(clientContext.service); if (currentQueue) { // not yet supporting episidoes - const filtered = currentQueue.queue.filter((item) => item.type === "track") as SpotifyApi.TrackObjectFull[]; + const filtered = currentQueue.queue.filter( + (item) => item.type === "track" + ) as SpotifyApi.TrackObjectFull[]; console.log(chalk.magentaBright("Current Queue:")); console.log( - chalk.cyanBright(`--------------------------------------------`) + chalk.cyanBright( + `--------------------------------------------` + ) ); await printTrackNames(filtered, clientContext); console.log( - chalk.cyanBright(`--------------------------------------------`) + chalk.cyanBright( + `--------------------------------------------` + ) ); await printStatus(clientContext); } @@ -311,7 +318,10 @@ async function handleCall( break; } case "setVolume": { - const newVolumeLevel = args[0] as number; + let newVolumeLevel = args[0] as number; + if (newVolumeLevel > 50) { + newVolumeLevel = 50; + } console.log( chalk.yellowBright(`setting volume to ${newVolumeLevel} ...`) ); @@ -326,8 +336,8 @@ async function handleCall( let nv = Math.floor( (1.0 + volumeChangeAmount / 100.0) * volpct ); - if (nv > 100) { - nv = 100; + if (nv > 50) { + nv = 50; } console.log(chalk.yellowBright(`setting volume to ${nv} ...`)); await setVolume(clientContext.service, nv); @@ -495,10 +505,7 @@ async function handleCall( const playlistCollection = args[0] as PlaylistTrackCollection; if (playlistCollection) { const playlist = playlistCollection.getPlaylist(); - await deletePlaylist( - clientContext.service, - playlist.id - ); + await deletePlaylist(clientContext.service, playlist.id); console.log( chalk.magentaBright(`playlist ${playlist.name} deleted`) ); @@ -527,7 +534,34 @@ async function handleCall( // set this to false to just look at llm generation without Spotify connection const spotifyConnect = true; -// Process requests interactively or from the input file specified on the command line +export async function index(context: IClientContext) { + let playHistory = await getRecent( + context.service, + Date.parse("2018-01-01T00:00:00.00Z") + ); + if (playHistory) { + console.log(playHistory?.length); + let trackNames = ''; + playHistory.map((item) => { + trackNames += item.track.name + '\n'; + }); + fs.writeFileSync("bigFetch.txt", trackNames); + } +} + +function checkAck(input: string, program: Program): Program | undefined { + const linput = input.toLocaleLowerCase(); + if (["y","yes","ok"].includes(linput)) { + return program; + } else { + return undefined; + } +} + +// whether to confirm each action with the user +const confirmMode = true; + +// Process requests interactively (no batch mode for now) async function musicApp() { const authz = new Authzor(); authz.authorize(spotifyConnect, async (token) => { @@ -541,7 +575,15 @@ async function musicApp() { ) ); } - processRequests("🎵> ", process.argv[2], async (request) => { + const musicPrompt = "🎵> "; + const confirmPrompt = "👍👎 (answer y/n)> "; + const stdio = readline.createInterface({ input: process.stdin, output: process.stdout }); + while (true) { + const request = await stdio.question(musicPrompt); + if (request.toLowerCase() === "quit" || request.toLowerCase() === "exit") { + stdio.close(); + return; + } const localResult = localParser(request); let program: Program | undefined = undefined; if (localResult) { @@ -550,13 +592,21 @@ async function musicApp() { const response = await translator.translate(request); if (!response.success) { console.log(response.message); - return; + continue; } program = response.data; } if (program !== undefined) { chalkPlan(program); console.log(getData(createModuleTextFromProgram(program))); + if (confirmMode && (!localResult)) { + const input = await stdio.question(confirmPrompt); + program = checkAck(input, program); + if (program === undefined) { + console.log("Thanks for the feedback. Canceling execution...") + continue; + } + } if (context !== undefined) { const result = await evaluateJsonProgram( program, @@ -576,7 +626,7 @@ async function musicApp() { } } } - }); + } }); }