@rocksky/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -9
- package/dist/drizzle/0000_parallel_paper_doll.sql +220 -0
- package/dist/drizzle/meta/0000_snapshot.json +1559 -0
- package/dist/drizzle/meta/_journal.json +13 -0
- package/dist/index.js +8280 -254
- package/drizzle/0000_parallel_paper_doll.sql +220 -0
- package/drizzle/meta/0000_snapshot.json +1559 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +18 -0
- package/package.json +31 -3
- package/src/client.ts +32 -14
- package/src/cmd/scrobble-api.ts +457 -0
- package/src/cmd/scrobble.ts +14 -61
- package/src/cmd/search.ts +27 -25
- package/src/cmd/sync.ts +812 -0
- package/src/cmd/whoami.ts +36 -7
- package/src/context.ts +24 -0
- package/src/drizzle.ts +53 -0
- package/src/index.ts +66 -26
- package/src/jetstream.ts +285 -0
- package/src/lexicon/index.ts +1321 -0
- package/src/lexicon/lexicons.ts +5453 -0
- package/src/lexicon/types/app/bsky/actor/profile.ts +38 -0
- package/src/lexicon/types/app/rocksky/actor/defs.ts +146 -0
- package/src/lexicon/types/app/rocksky/actor/getActorAlbums.ts +56 -0
- package/src/lexicon/types/app/rocksky/actor/getActorArtists.ts +56 -0
- package/src/lexicon/types/app/rocksky/actor/getActorCompatibility.ts +48 -0
- package/src/lexicon/types/app/rocksky/actor/getActorLovedSongs.ts +52 -0
- package/src/lexicon/types/app/rocksky/actor/getActorNeighbours.ts +48 -0
- package/src/lexicon/types/app/rocksky/actor/getActorPlaylists.ts +52 -0
- package/src/lexicon/types/app/rocksky/actor/getActorScrobbles.ts +52 -0
- package/src/lexicon/types/app/rocksky/actor/getActorSongs.ts +56 -0
- package/src/lexicon/types/app/rocksky/actor/getProfile.ts +43 -0
- package/src/lexicon/types/app/rocksky/album/defs.ts +85 -0
- package/src/lexicon/types/app/rocksky/album/getAlbum.ts +43 -0
- package/src/lexicon/types/app/rocksky/album/getAlbumTracks.ts +48 -0
- package/src/lexicon/types/app/rocksky/album/getAlbums.ts +50 -0
- package/src/lexicon/types/app/rocksky/album.ts +51 -0
- package/src/lexicon/types/app/rocksky/apikey/createApikey.ts +51 -0
- package/src/lexicon/types/app/rocksky/apikey/defs.ts +31 -0
- package/src/lexicon/types/app/rocksky/apikey/getApikeys.ts +50 -0
- package/src/lexicon/types/app/rocksky/apikey/removeApikey.ts +43 -0
- package/src/lexicon/types/app/rocksky/apikey/updateApikey.ts +53 -0
- package/src/lexicon/types/app/rocksky/apikeys/defs.ts +7 -0
- package/src/lexicon/types/app/rocksky/artist/defs.ts +140 -0
- package/src/lexicon/types/app/rocksky/artist/getArtist.ts +43 -0
- package/src/lexicon/types/app/rocksky/artist/getArtistAlbums.ts +48 -0
- package/src/lexicon/types/app/rocksky/artist/getArtistListeners.ts +52 -0
- package/src/lexicon/types/app/rocksky/artist/getArtistTracks.ts +52 -0
- package/src/lexicon/types/app/rocksky/artist/getArtists.ts +52 -0
- package/src/lexicon/types/app/rocksky/artist.ts +41 -0
- package/src/lexicon/types/app/rocksky/charts/defs.ts +44 -0
- package/src/lexicon/types/app/rocksky/charts/getScrobblesChart.ts +49 -0
- package/src/lexicon/types/app/rocksky/dropbox/defs.ts +71 -0
- package/src/lexicon/types/app/rocksky/dropbox/downloadFile.ts +42 -0
- package/src/lexicon/types/app/rocksky/dropbox/getFiles.ts +43 -0
- package/src/lexicon/types/app/rocksky/dropbox/getMetadata.ts +43 -0
- package/src/lexicon/types/app/rocksky/dropbox/getTemporaryLink.ts +43 -0
- package/src/lexicon/types/app/rocksky/feed/defs.ts +182 -0
- package/src/lexicon/types/app/rocksky/feed/describeFeedGenerator.ts +48 -0
- package/src/lexicon/types/app/rocksky/feed/generator.ts +29 -0
- package/src/lexicon/types/app/rocksky/feed/getFeed.ts +47 -0
- package/src/lexicon/types/app/rocksky/feed/getFeedGenerator.ts +48 -0
- package/src/lexicon/types/app/rocksky/feed/getFeedGenerators.ts +43 -0
- package/src/lexicon/types/app/rocksky/feed/getFeedSkeleton.ts +56 -0
- package/src/lexicon/types/app/rocksky/feed/getNowPlayings.ts +43 -0
- package/src/lexicon/types/app/rocksky/feed/search.ts +43 -0
- package/src/lexicon/types/app/rocksky/googledrive/defs.ts +42 -0
- package/src/lexicon/types/app/rocksky/googledrive/downloadFile.ts +42 -0
- package/src/lexicon/types/app/rocksky/googledrive/getFile.ts +43 -0
- package/src/lexicon/types/app/rocksky/googledrive/getFiles.ts +43 -0
- package/src/lexicon/types/app/rocksky/graph/defs.ts +47 -0
- package/src/lexicon/types/app/rocksky/graph/follow.ts +28 -0
- package/src/lexicon/types/app/rocksky/graph/followAccount.ts +50 -0
- package/src/lexicon/types/app/rocksky/graph/getFollowers.ts +56 -0
- package/src/lexicon/types/app/rocksky/graph/getFollows.ts +56 -0
- package/src/lexicon/types/app/rocksky/graph/getKnownFollowers.ts +52 -0
- package/src/lexicon/types/app/rocksky/graph/unfollowAccount.ts +50 -0
- package/src/lexicon/types/app/rocksky/like/dislikeShout.ts +49 -0
- package/src/lexicon/types/app/rocksky/like/dislikeSong.ts +49 -0
- package/src/lexicon/types/app/rocksky/like/likeShout.ts +49 -0
- package/src/lexicon/types/app/rocksky/like/likeSong.ts +49 -0
- package/src/lexicon/types/app/rocksky/like.ts +27 -0
- package/src/lexicon/types/app/rocksky/player/addDirectoryToQueue.ts +40 -0
- package/src/lexicon/types/app/rocksky/player/addItemsToQueue.ts +39 -0
- package/src/lexicon/types/app/rocksky/player/defs.ts +57 -0
- package/src/lexicon/types/app/rocksky/player/getCurrentlyPlaying.ts +44 -0
- package/src/lexicon/types/app/rocksky/player/getPlaybackQueue.ts +42 -0
- package/src/lexicon/types/app/rocksky/player/next.ts +34 -0
- package/src/lexicon/types/app/rocksky/player/pause.ts +34 -0
- package/src/lexicon/types/app/rocksky/player/play.ts +34 -0
- package/src/lexicon/types/app/rocksky/player/playDirectory.ts +38 -0
- package/src/lexicon/types/app/rocksky/player/playFile.ts +35 -0
- package/src/lexicon/types/app/rocksky/player/previous.ts +34 -0
- package/src/lexicon/types/app/rocksky/player/seek.ts +36 -0
- package/src/lexicon/types/app/rocksky/playlist/createPlaylist.ts +37 -0
- package/src/lexicon/types/app/rocksky/playlist/defs.ts +86 -0
- package/src/lexicon/types/app/rocksky/playlist/getPlaylist.ts +43 -0
- package/src/lexicon/types/app/rocksky/playlist/getPlaylists.ts +50 -0
- package/src/lexicon/types/app/rocksky/playlist/insertDirectory.ts +39 -0
- package/src/lexicon/types/app/rocksky/playlist/insertFiles.ts +38 -0
- package/src/lexicon/types/app/rocksky/playlist/removePlaylist.ts +35 -0
- package/src/lexicon/types/app/rocksky/playlist/removeTrack.ts +37 -0
- package/src/lexicon/types/app/rocksky/playlist/startPlaylist.ts +39 -0
- package/src/lexicon/types/app/rocksky/playlist.ts +43 -0
- package/src/lexicon/types/app/rocksky/radio/defs.ts +63 -0
- package/src/lexicon/types/app/rocksky/radio.ts +37 -0
- package/src/lexicon/types/app/rocksky/scrobble/createScrobble.ts +91 -0
- package/src/lexicon/types/app/rocksky/scrobble/defs.ts +93 -0
- package/src/lexicon/types/app/rocksky/scrobble/getScrobble.ts +43 -0
- package/src/lexicon/types/app/rocksky/scrobble/getScrobbles.ts +54 -0
- package/src/lexicon/types/app/rocksky/scrobble.ts +75 -0
- package/src/lexicon/types/app/rocksky/shout/createShout.ts +49 -0
- package/src/lexicon/types/app/rocksky/shout/defs.ts +58 -0
- package/src/lexicon/types/app/rocksky/shout/getAlbumShouts.ts +52 -0
- package/src/lexicon/types/app/rocksky/shout/getArtistShouts.ts +52 -0
- package/src/lexicon/types/app/rocksky/shout/getProfileShouts.ts +52 -0
- package/src/lexicon/types/app/rocksky/shout/getShoutReplies.ts +52 -0
- package/src/lexicon/types/app/rocksky/shout/getTrackShouts.ts +48 -0
- package/src/lexicon/types/app/rocksky/shout/removeShout.ts +43 -0
- package/src/lexicon/types/app/rocksky/shout/replyShout.ts +51 -0
- package/src/lexicon/types/app/rocksky/shout/reportShout.ts +51 -0
- package/src/lexicon/types/app/rocksky/shout.ts +30 -0
- package/src/lexicon/types/app/rocksky/song/createSong.ts +71 -0
- package/src/lexicon/types/app/rocksky/song/defs.ts +103 -0
- package/src/lexicon/types/app/rocksky/song/getSong.ts +43 -0
- package/src/lexicon/types/app/rocksky/song/getSongs.ts +50 -0
- package/src/lexicon/types/app/rocksky/song.ts +74 -0
- package/src/lexicon/types/app/rocksky/spotify/defs.ts +35 -0
- package/src/lexicon/types/app/rocksky/spotify/getCurrentlyPlaying.ts +43 -0
- package/src/lexicon/types/app/rocksky/spotify/next.ts +32 -0
- package/src/lexicon/types/app/rocksky/spotify/pause.ts +32 -0
- package/src/lexicon/types/app/rocksky/spotify/play.ts +32 -0
- package/src/lexicon/types/app/rocksky/spotify/previous.ts +32 -0
- package/src/lexicon/types/app/rocksky/spotify/seek.ts +35 -0
- package/src/lexicon/types/app/rocksky/stats/defs.ts +33 -0
- package/src/lexicon/types/app/rocksky/stats/getStats.ts +43 -0
- package/src/lexicon/types/com/atproto/repo/strongRef.ts +26 -0
- package/src/lexicon/util.ts +13 -0
- package/src/lib/agent.ts +56 -0
- package/src/lib/cleanUpJetstreamLock.ts +66 -0
- package/src/lib/cleanUpSyncLock.ts +56 -0
- package/src/lib/didUnstorageCache.ts +72 -0
- package/src/lib/env.ts +25 -0
- package/src/lib/extractPdsFromDid.ts +33 -0
- package/src/lib/getDidAndHandle.ts +39 -0
- package/src/lib/idResolver.ts +52 -0
- package/src/lib/lastfm.ts +26 -0
- package/src/lib/matchTrack.ts +47 -0
- package/src/logger.ts +18 -0
- package/src/schema/album-tracks.ts +30 -0
- package/src/schema/albums.ts +29 -0
- package/src/schema/artist-albums.ts +29 -0
- package/src/schema/artist-genres.ts +17 -0
- package/src/schema/artist-tracks.ts +29 -0
- package/src/schema/artists.ts +30 -0
- package/src/schema/auth-session.ts +18 -0
- package/src/schema/genres.ts +18 -0
- package/src/schema/index.ts +33 -0
- package/src/schema/loved-tracks.ts +27 -0
- package/src/schema/scrobbles.ts +30 -0
- package/src/schema/tracks.ts +39 -0
- package/src/schema/user-albums.ts +31 -0
- package/src/schema/user-artists.ts +32 -0
- package/src/schema/user-tracks.ts +31 -0
- package/src/schema/users.ts +21 -0
- package/src/scrobble.ts +410 -0
- package/src/sqliteKv.ts +173 -0
- package/src/types.ts +308 -0
- package/tsconfig.json +26 -29
package/src/sqliteKv.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { Kysely, SqliteDialect } from "kysely";
|
|
3
|
+
import { defineDriver } from "unstorage";
|
|
4
|
+
|
|
5
|
+
interface TableSchema {
|
|
6
|
+
[k: string]: {
|
|
7
|
+
id: string;
|
|
8
|
+
value: string;
|
|
9
|
+
created_at: string;
|
|
10
|
+
updated_at: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type KvDb = Kysely<TableSchema>;
|
|
15
|
+
|
|
16
|
+
const DRIVER_NAME = "sqlite";
|
|
17
|
+
|
|
18
|
+
export default defineDriver<
|
|
19
|
+
{
|
|
20
|
+
location?: string;
|
|
21
|
+
table: string;
|
|
22
|
+
getDb?: () => KvDb;
|
|
23
|
+
},
|
|
24
|
+
KvDb
|
|
25
|
+
>(
|
|
26
|
+
({
|
|
27
|
+
location,
|
|
28
|
+
table = "kv",
|
|
29
|
+
getDb = (): KvDb => {
|
|
30
|
+
let _db: KvDb | null = null;
|
|
31
|
+
|
|
32
|
+
return (() => {
|
|
33
|
+
if (_db) {
|
|
34
|
+
return _db;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!location) {
|
|
38
|
+
throw new Error("SQLite location is required");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const sqlite = new Database(location, { fileMustExist: false });
|
|
42
|
+
|
|
43
|
+
// Enable WAL mode
|
|
44
|
+
sqlite.pragma("journal_mode = WAL");
|
|
45
|
+
|
|
46
|
+
_db = new Kysely<TableSchema>({
|
|
47
|
+
dialect: new SqliteDialect({
|
|
48
|
+
database: sqlite,
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Create table if not exists
|
|
53
|
+
_db.schema
|
|
54
|
+
.createTable(table)
|
|
55
|
+
.ifNotExists()
|
|
56
|
+
.addColumn("id", "text", (col) => col.primaryKey())
|
|
57
|
+
.addColumn("value", "text", (col) => col.notNull())
|
|
58
|
+
.addColumn("created_at", "text", (col) => col.notNull())
|
|
59
|
+
.addColumn("updated_at", "text", (col) => col.notNull())
|
|
60
|
+
.execute();
|
|
61
|
+
|
|
62
|
+
return _db;
|
|
63
|
+
})();
|
|
64
|
+
},
|
|
65
|
+
}) => {
|
|
66
|
+
return {
|
|
67
|
+
name: DRIVER_NAME,
|
|
68
|
+
options: { location, table },
|
|
69
|
+
getInstance: getDb,
|
|
70
|
+
|
|
71
|
+
async hasItem(key) {
|
|
72
|
+
const result = await getDb()
|
|
73
|
+
.selectFrom(table)
|
|
74
|
+
.select(["id"])
|
|
75
|
+
.where("id", "=", key)
|
|
76
|
+
.executeTakeFirst();
|
|
77
|
+
return !!result;
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
async getItem(key) {
|
|
81
|
+
const result = await getDb()
|
|
82
|
+
.selectFrom(table)
|
|
83
|
+
.select(["value"])
|
|
84
|
+
.where("id", "=", key)
|
|
85
|
+
.executeTakeFirst();
|
|
86
|
+
return result?.value ?? null;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
async setItem(key: string, value: string) {
|
|
90
|
+
const now = new Date().toISOString();
|
|
91
|
+
await getDb()
|
|
92
|
+
.insertInto(table)
|
|
93
|
+
.values({
|
|
94
|
+
id: key,
|
|
95
|
+
value,
|
|
96
|
+
created_at: now,
|
|
97
|
+
updated_at: now,
|
|
98
|
+
})
|
|
99
|
+
.onConflict((oc) =>
|
|
100
|
+
oc.column("id").doUpdateSet({
|
|
101
|
+
value,
|
|
102
|
+
updated_at: now,
|
|
103
|
+
}),
|
|
104
|
+
)
|
|
105
|
+
.execute();
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
async setItems(items) {
|
|
109
|
+
const now = new Date().toISOString();
|
|
110
|
+
|
|
111
|
+
await getDb()
|
|
112
|
+
.transaction()
|
|
113
|
+
.execute(async (trx) => {
|
|
114
|
+
await Promise.all(
|
|
115
|
+
items.map(({ key, value }) => {
|
|
116
|
+
return trx
|
|
117
|
+
.insertInto(table)
|
|
118
|
+
.values({
|
|
119
|
+
id: key,
|
|
120
|
+
value,
|
|
121
|
+
created_at: now,
|
|
122
|
+
updated_at: now,
|
|
123
|
+
})
|
|
124
|
+
.onConflict((oc) =>
|
|
125
|
+
oc.column("id").doUpdateSet({
|
|
126
|
+
value,
|
|
127
|
+
updated_at: now,
|
|
128
|
+
}),
|
|
129
|
+
)
|
|
130
|
+
.execute();
|
|
131
|
+
}),
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
async removeItem(key: string) {
|
|
137
|
+
await getDb().deleteFrom(table).where("id", "=", key).execute();
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
async getMeta(key: string) {
|
|
141
|
+
const result = await getDb()
|
|
142
|
+
.selectFrom(table)
|
|
143
|
+
.select(["created_at", "updated_at"])
|
|
144
|
+
.where("id", "=", key)
|
|
145
|
+
.executeTakeFirst();
|
|
146
|
+
if (!result) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
birthtime: new Date(result.created_at),
|
|
151
|
+
mtime: new Date(result.updated_at),
|
|
152
|
+
};
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
async getKeys(base = "") {
|
|
156
|
+
const results = await getDb()
|
|
157
|
+
.selectFrom(table)
|
|
158
|
+
.select(["id"])
|
|
159
|
+
.where("id", "like", `${base}%`)
|
|
160
|
+
.execute();
|
|
161
|
+
return results.map((r) => r.id);
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
async clear() {
|
|
165
|
+
await getDb().deleteFrom(table).execute();
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
async dispose() {
|
|
169
|
+
await getDb().destroy();
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
);
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export namespace WebScrobbler {
|
|
4
|
+
/* -------------------------------- Connector -------------------------------- */
|
|
5
|
+
|
|
6
|
+
export const ConnectorSchema = z.object({
|
|
7
|
+
id: z.string(),
|
|
8
|
+
js: z.string(),
|
|
9
|
+
label: z.string(),
|
|
10
|
+
matches: z.array(z.string()),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/* ----------------------- IsRegrexEditedByUser ----------------------- */
|
|
14
|
+
|
|
15
|
+
export const IsRegrexEditedByUserSchema = z.object({
|
|
16
|
+
album: z.boolean(),
|
|
17
|
+
albumArtist: z.boolean(),
|
|
18
|
+
artist: z.boolean(),
|
|
19
|
+
track: z.boolean(),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/* ---------------------------------- Flags ---------------------------------- */
|
|
23
|
+
|
|
24
|
+
export const FlagsSchema = z.object({
|
|
25
|
+
finishedProcessing: z.boolean(),
|
|
26
|
+
hasBlockedTag: z.boolean(),
|
|
27
|
+
isAlbumFetched: z.boolean(),
|
|
28
|
+
isCorrectedByUser: z.boolean(),
|
|
29
|
+
isLovedInService: z.boolean().optional(),
|
|
30
|
+
isMarkedAsPlaying: z.boolean(),
|
|
31
|
+
isRegexEditedByUser: IsRegrexEditedByUserSchema,
|
|
32
|
+
isReplaying: z.boolean(),
|
|
33
|
+
isScrobbled: z.boolean(),
|
|
34
|
+
isSkipped: z.boolean(),
|
|
35
|
+
isValid: z.boolean(),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/* -------------------------------- Metadata -------------------------------- */
|
|
39
|
+
|
|
40
|
+
export const MetadataSchema = z.object({
|
|
41
|
+
albumUrl: z.string().url().optional(),
|
|
42
|
+
artistUrl: z.string().url().optional(),
|
|
43
|
+
label: z.string(),
|
|
44
|
+
startTimestamp: z.number().int().nonnegative(),
|
|
45
|
+
trackUrl: z.string().url().optional(),
|
|
46
|
+
userPlayCount: z.number().int().nonnegative().optional(),
|
|
47
|
+
userloved: z.boolean().optional(),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/* -------------------------------- NoRegex -------------------------------- */
|
|
51
|
+
|
|
52
|
+
export const NoRegexSchema = z.object({
|
|
53
|
+
album: z.string().optional(),
|
|
54
|
+
albumArtist: z.string().optional(),
|
|
55
|
+
artist: z.string(),
|
|
56
|
+
duration: z.number().int().nonnegative().optional(),
|
|
57
|
+
track: z.string(),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/* ---------------------------------- Parsed --------------------------------- */
|
|
61
|
+
|
|
62
|
+
export const ParsedSchema = z.object({
|
|
63
|
+
album: z.string().optional(),
|
|
64
|
+
albumArtist: z.string().optional(),
|
|
65
|
+
artist: z.string(),
|
|
66
|
+
currentTime: z.number().int().nonnegative().optional(),
|
|
67
|
+
duration: z.number().int().nonnegative().optional(),
|
|
68
|
+
isPlaying: z.boolean(),
|
|
69
|
+
isPodcast: z.boolean(),
|
|
70
|
+
originUrl: z.string().url().optional(),
|
|
71
|
+
scrobblingDisallowedReason: z.string().optional(),
|
|
72
|
+
track: z.string(),
|
|
73
|
+
trackArt: z.string().url().optional(),
|
|
74
|
+
uniqueID: z.string().optional(),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/* ----------------------------------- Song ---------------------------------- */
|
|
78
|
+
|
|
79
|
+
export const SongSchema = z.object({
|
|
80
|
+
connector: ConnectorSchema,
|
|
81
|
+
controllerTabId: z.number().int().nonnegative(),
|
|
82
|
+
flags: FlagsSchema,
|
|
83
|
+
metadata: MetadataSchema,
|
|
84
|
+
noRegex: NoRegexSchema,
|
|
85
|
+
parsed: ParsedSchema,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/* -------------------------------- Processed -------------------------------- */
|
|
89
|
+
|
|
90
|
+
export const ProcessedSchema = z.object({
|
|
91
|
+
album: z.string(),
|
|
92
|
+
albumArtist: z.string().optional(),
|
|
93
|
+
artist: z.string(),
|
|
94
|
+
duration: z.number().int().nonnegative(),
|
|
95
|
+
track: z.string(),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
/* --------------------------------- Scrobble -------------------------------- */
|
|
99
|
+
|
|
100
|
+
export const ScrobbleSchema = z.object({
|
|
101
|
+
song: SongSchema,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/* ------------------------------ ScrobbleRequest ----------------------------- */
|
|
105
|
+
|
|
106
|
+
export const ScrobbleRequestSchema = z.object({
|
|
107
|
+
data: ScrobbleSchema,
|
|
108
|
+
eventName: z.string(),
|
|
109
|
+
time: z.number().int().nonnegative(),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
export type Song = z.infer<typeof SongSchema>;
|
|
113
|
+
export type Processed = z.infer<typeof ProcessedSchema>;
|
|
114
|
+
export type Scrobble = z.infer<typeof ScrobbleSchema>;
|
|
115
|
+
export type ScrobbleRequest = z.infer<typeof ScrobbleRequestSchema>;
|
|
116
|
+
export type ScrobbleRequestData = z.infer<
|
|
117
|
+
typeof ScrobbleRequestSchema
|
|
118
|
+
>["data"];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export namespace Lastfm {
|
|
122
|
+
/* -------------------------------- Legacy API Schemas -------------------------------- */
|
|
123
|
+
|
|
124
|
+
export const LegacyNowPlayingRequestSchema = z.object({
|
|
125
|
+
s: z.string(), // session ID
|
|
126
|
+
a: z.string(), // artist
|
|
127
|
+
t: z.string(), // track
|
|
128
|
+
b: z.string().optional(), // album
|
|
129
|
+
l: z.string().optional(), // length in seconds
|
|
130
|
+
n: z.string().optional(), // track number
|
|
131
|
+
m: z.string().optional(), // MusicBrainz ID
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
export const LegacySubmissionRequestSchema = z.object({
|
|
135
|
+
s: z.string(), // session ID
|
|
136
|
+
"a[0]": z.string(), // artist
|
|
137
|
+
"t[0]": z.string(), // track
|
|
138
|
+
"i[0]": z.string(), // timestamp
|
|
139
|
+
"o[0]": z.string().optional(), // source (P/R/E/L/U/B)
|
|
140
|
+
"r[0]": z.string().optional(), // rating (L/B/S)
|
|
141
|
+
"l[0]": z.string().optional(), // length in seconds
|
|
142
|
+
"b[0]": z.string().optional(), // album
|
|
143
|
+
"n[0]": z.string().optional(), // track number
|
|
144
|
+
"m[0]": z.string().optional(), // MusicBrainz ID
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
/* -------------------------------- Auth Request -------------------------------- */
|
|
148
|
+
|
|
149
|
+
export const AuthRequestSchema = z.object({
|
|
150
|
+
method: z.string(),
|
|
151
|
+
api_key: z.string(),
|
|
152
|
+
api_sig: z.string(),
|
|
153
|
+
format: z.string().optional(),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
/* -------------------------------- Auth GetSession Request -------------------------------- */
|
|
157
|
+
|
|
158
|
+
export const AuthGetSessionRequestSchema = z.object({
|
|
159
|
+
method: z.literal("auth.getSession"),
|
|
160
|
+
api_key: z.string(),
|
|
161
|
+
token: z.string(),
|
|
162
|
+
api_sig: z.string(),
|
|
163
|
+
format: z.string().optional(),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
/* -------------------------------- Auth GetSession Response -------------------------------- */
|
|
167
|
+
|
|
168
|
+
export const AuthGetSessionResponseSchema = z.object({
|
|
169
|
+
session: z.object({
|
|
170
|
+
name: z.string(),
|
|
171
|
+
key: z.string(),
|
|
172
|
+
subscriber: z.number(),
|
|
173
|
+
}),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
/* -------------------------------- Track Scrobble Request -------------------------------- */
|
|
177
|
+
|
|
178
|
+
export const TrackScrobbleRequestSchema = z.object({
|
|
179
|
+
method: z.literal("track.scrobble"),
|
|
180
|
+
api_key: z.string(),
|
|
181
|
+
api_sig: z.string(),
|
|
182
|
+
sk: z.string(),
|
|
183
|
+
"track[0]": z.string(),
|
|
184
|
+
"artist[0]": z.string(),
|
|
185
|
+
"timestamp[0]": z.string(),
|
|
186
|
+
"album[0]": z.string().optional(),
|
|
187
|
+
"albumArtist[0]": z.string().optional(),
|
|
188
|
+
"duration[0]": z.string().optional(),
|
|
189
|
+
format: z.string().optional(),
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
/* -------------------------------- Track Update Now Playing Request -------------------------------- */
|
|
193
|
+
|
|
194
|
+
export const TrackUpdateNowPlayingRequestSchema = z.object({
|
|
195
|
+
method: z.literal("track.updateNowPlaying"),
|
|
196
|
+
api_key: z.string(),
|
|
197
|
+
api_sig: z.string(),
|
|
198
|
+
sk: z.string(),
|
|
199
|
+
track: z.string(),
|
|
200
|
+
artist: z.string(),
|
|
201
|
+
album: z.string().optional(),
|
|
202
|
+
albumArtist: z.string().optional(),
|
|
203
|
+
duration: z.string().optional(),
|
|
204
|
+
format: z.string().optional(),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
/* -------------------------------- Scrobble Response -------------------------------- */
|
|
208
|
+
|
|
209
|
+
export const ScrobbleResponseSchema = z.object({
|
|
210
|
+
scrobbles: z.object({
|
|
211
|
+
"@attr": z.object({
|
|
212
|
+
accepted: z.number(),
|
|
213
|
+
ignored: z.number(),
|
|
214
|
+
}),
|
|
215
|
+
scrobble: z
|
|
216
|
+
.object({
|
|
217
|
+
artist: z.object({ "#text": z.string() }),
|
|
218
|
+
track: z.object({ "#text": z.string() }),
|
|
219
|
+
album: z.object({ "#text": z.string() }).optional(),
|
|
220
|
+
albumArtist: z.object({ "#text": z.string() }).optional(),
|
|
221
|
+
timestamp: z.string(),
|
|
222
|
+
ignoredMessage: z
|
|
223
|
+
.object({ code: z.string(), "#text": z.string() })
|
|
224
|
+
.optional(),
|
|
225
|
+
})
|
|
226
|
+
.optional(),
|
|
227
|
+
}),
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
/* -------------------------------- Error Response -------------------------------- */
|
|
231
|
+
|
|
232
|
+
export const ErrorResponseSchema = z.object({
|
|
233
|
+
error: z.number(),
|
|
234
|
+
message: z.string(),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
export type LegacyNowPlayingRequest = z.infer<
|
|
238
|
+
typeof LegacyNowPlayingRequestSchema
|
|
239
|
+
>;
|
|
240
|
+
export type LegacySubmissionRequest = z.infer<
|
|
241
|
+
typeof LegacySubmissionRequestSchema
|
|
242
|
+
>;
|
|
243
|
+
export type AuthRequest = z.infer<typeof AuthRequestSchema>;
|
|
244
|
+
export type AuthGetSessionRequest = z.infer<
|
|
245
|
+
typeof AuthGetSessionRequestSchema
|
|
246
|
+
>;
|
|
247
|
+
export type AuthGetSessionResponse = z.infer<
|
|
248
|
+
typeof AuthGetSessionResponseSchema
|
|
249
|
+
>;
|
|
250
|
+
export type TrackScrobbleRequest = z.infer<typeof TrackScrobbleRequestSchema>;
|
|
251
|
+
export type TrackUpdateNowPlayingRequest = z.infer<
|
|
252
|
+
typeof TrackUpdateNowPlayingRequestSchema
|
|
253
|
+
>;
|
|
254
|
+
export type ScrobbleResponse = z.infer<typeof ScrobbleResponseSchema>;
|
|
255
|
+
export type ErrorResponse = z.infer<typeof ErrorResponseSchema>;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export namespace Listenbrainz {
|
|
259
|
+
/* -------------------------------- TrackMetadata -------------------------------- */
|
|
260
|
+
|
|
261
|
+
export const TrackMetadataSchema = z.object({
|
|
262
|
+
artist_name: z.string(),
|
|
263
|
+
track_name: z.string(),
|
|
264
|
+
release_name: z.string().optional(),
|
|
265
|
+
additional_info: z.record(z.any()).optional(),
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
/* -------------------------------- Payload -------------------------------- */
|
|
269
|
+
|
|
270
|
+
export const PayloadSchema = z.object({
|
|
271
|
+
listened_at: z.number().int().nonnegative().optional(),
|
|
272
|
+
track_metadata: TrackMetadataSchema,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
/* -------------------------------- SubmitListensRequest -------------------------------- */
|
|
276
|
+
|
|
277
|
+
export const SubmitListensRequestSchema = z.object({
|
|
278
|
+
listen_type: z.enum(["single", "playing_now", "import"]),
|
|
279
|
+
payload: z.array(PayloadSchema),
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
/* -------------------------------- SubmitListensResponse -------------------------------- */
|
|
283
|
+
|
|
284
|
+
export const SubmitListensResponseSchema = z.object({
|
|
285
|
+
status: z.string(),
|
|
286
|
+
code: z.number().int(),
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
/* -------------------------------- ValidateTokenResponse -------------------------------- */
|
|
290
|
+
|
|
291
|
+
export const ValidateTokenResponseSchema = z.object({
|
|
292
|
+
code: z.number().int(),
|
|
293
|
+
message: z.string(),
|
|
294
|
+
valid: z.boolean(),
|
|
295
|
+
user_name: z.string().optional(),
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
export type TrackMetadata = z.infer<typeof TrackMetadataSchema>;
|
|
299
|
+
|
|
300
|
+
export type Payload = z.infer<typeof PayloadSchema>;
|
|
301
|
+
export type SubmitListensRequest = z.infer<typeof SubmitListensRequestSchema>;
|
|
302
|
+
export type SubmitListensResponse = z.infer<
|
|
303
|
+
typeof SubmitListensResponseSchema
|
|
304
|
+
>;
|
|
305
|
+
export type ValidateTokenResponse = z.infer<
|
|
306
|
+
typeof ValidateTokenResponseSchema
|
|
307
|
+
>;
|
|
308
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,31 +1,28 @@
|
|
|
1
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
|
-
"exclude": ["node_modules", "dist", "tests"],
|
|
30
|
-
"include": ["src", "scripts"]
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowJs": true,
|
|
4
|
+
"allowSyntheticDefaultImports": true,
|
|
5
|
+
"baseUrl": "src",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"sourceMap": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"inlineSourceMap": false,
|
|
10
|
+
"lib": ["esnext", "DOM"],
|
|
11
|
+
"listEmittedFiles": false,
|
|
12
|
+
"listFiles": false,
|
|
13
|
+
"moduleResolution": "node",
|
|
14
|
+
"noFallthroughCasesInSwitch": true,
|
|
15
|
+
"pretty": true,
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"rootDir": ".",
|
|
18
|
+
"skipLibCheck": true,
|
|
19
|
+
"strict": false,
|
|
20
|
+
"traceResolution": false,
|
|
21
|
+
"outDir": "",
|
|
22
|
+
"target": "esnext",
|
|
23
|
+
"module": "esnext",
|
|
24
|
+
"types": ["@types/node", "@types/express"],
|
|
25
|
+
},
|
|
26
|
+
"exclude": ["node_modules", "dist", "tests"],
|
|
27
|
+
"include": ["src", "scripts"],
|
|
31
28
|
}
|