lastfm.ts 1.0.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 +107 -0
- package/dist/LastFmClient.d.ts +26 -0
- package/dist/LastFmClient.js +186 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/types.d.ts +195 -0
- package/dist/types.js +2 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Last.fm Client (Typed)
|
|
2
|
+
|
|
3
|
+
A robust, fully typed TypeScript client for the Last.fm API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install lastfm-client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Getting Started
|
|
12
|
+
|
|
13
|
+
Initialize the client with your Last.fm API credentials.
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { LastFmClient } from "lastfm-client";
|
|
17
|
+
|
|
18
|
+
const client = new LastFmClient({
|
|
19
|
+
apiKey: "YOUR_API_KEY",
|
|
20
|
+
apiSecret: "YOUR_API_SECRET",
|
|
21
|
+
userAgent: "MyApp/1.0.0 (contact@example.com)", // Optional but recommended
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
### Authentication
|
|
28
|
+
|
|
29
|
+
To scrobble or update "Now Playing", you need a user session.
|
|
30
|
+
|
|
31
|
+
1. **Get Auth URL**: Redirect the user to this URL to approve your app.
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
const authUrl = client.getAuthUrl("http://localhost:3000/callback");
|
|
35
|
+
console.log("Please visit:", authUrl);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
2. **Get Session**: Once the user returns with a `token`, exchange it for a session key.
|
|
39
|
+
```typescript
|
|
40
|
+
const session = await client.getSession("TOKEN_FROM_CALLBACK");
|
|
41
|
+
const sessionKey = session.session.key;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### User Methods
|
|
45
|
+
|
|
46
|
+
#### Get User Info
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
const user = await client.getUser("rj");
|
|
50
|
+
console.log(`User: ${user.user.realname}, Playcount: ${user.user.playcount}`);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
#### Get Recent Tracks & Now Playing
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Get recent tracks
|
|
57
|
+
const recent = await client.getRecentTracks("rj", 10);
|
|
58
|
+
|
|
59
|
+
// Check if user is currently listening to something
|
|
60
|
+
const nowPlaying = await client.getNowPlaying("rj");
|
|
61
|
+
if (nowPlaying) {
|
|
62
|
+
console.log(`Listening to: ${nowPlaying.name} by ${nowPlaying.artist["#text"]}`);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Track Methods
|
|
67
|
+
|
|
68
|
+
#### Scrobbling (Record a listen)
|
|
69
|
+
|
|
70
|
+
Requires `sessionKey`.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
await client.scrobble(
|
|
74
|
+
sessionKey,
|
|
75
|
+
"Daft Punk", // Artist
|
|
76
|
+
"One More Time", // Track
|
|
77
|
+
Math.floor(Date.now() / 1000), // Timestamp (in seconds)
|
|
78
|
+
"Discovery", // Album (Optional)
|
|
79
|
+
);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### Update Now Playing
|
|
83
|
+
|
|
84
|
+
Requires `sessionKey`.
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
await client.updateNowPlaying(sessionKey, "Daft Punk", "Harder, Better, Faster, Stronger");
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### Search & Similar
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const searchResults = await client.searchTrack("Believe");
|
|
94
|
+
const similarTracks = await client.getSimilarTracks("Cher", "Believe");
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Other Methods
|
|
98
|
+
|
|
99
|
+
The client also supports methods for **Albums**, **Artists**, and **Tags**:
|
|
100
|
+
|
|
101
|
+
- `getAlbumInfo`, `searchAlbum`
|
|
102
|
+
- `getArtistInfo`, `getSimilarArtists`, `searchArtist`
|
|
103
|
+
- `getTagInfo`
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
ISC
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AlbumInfoResponse, AlbumSearchResponse, ArtistInfoResponse, ArtistSearchResponse, ArtistSimilarResponse, AuthSessionResponse, LastFmClientOptions, LastFmTrack, RecentTracksResponse, TagInfoResponse, TrackInfoResponse, TrackSearchResponse, TrackSimilarResponse, UserInfoResponse } from "./types";
|
|
2
|
+
export declare class LastFmClient {
|
|
3
|
+
private apiKey;
|
|
4
|
+
private apiSecret;
|
|
5
|
+
private userAgent;
|
|
6
|
+
private baseUrl;
|
|
7
|
+
constructor(options: LastFmClientOptions);
|
|
8
|
+
private sign;
|
|
9
|
+
private request;
|
|
10
|
+
getAuthUrl(callbackUrl?: string): string;
|
|
11
|
+
getSession(token: string): Promise<AuthSessionResponse>;
|
|
12
|
+
getUser(username: string): Promise<UserInfoResponse>;
|
|
13
|
+
getRecentTracks(username: string, limit?: number, page?: number): Promise<RecentTracksResponse>;
|
|
14
|
+
getNowPlaying(username: string): Promise<LastFmTrack | null>;
|
|
15
|
+
getTrackInfo(artist: string, track: string, username?: string): Promise<TrackInfoResponse>;
|
|
16
|
+
updateNowPlaying(sessionKey: string, artist: string, track: string, album?: string, duration?: number): Promise<void>;
|
|
17
|
+
scrobble(sessionKey: string, artist: string, track: string, timestamp: number, album?: string): Promise<void>;
|
|
18
|
+
getAlbumInfo(artist: string, album: string, username?: string): Promise<AlbumInfoResponse>;
|
|
19
|
+
searchAlbum(album: string, limit?: number, page?: number): Promise<AlbumSearchResponse>;
|
|
20
|
+
getArtistInfo(artist: string, username?: string): Promise<ArtistInfoResponse>;
|
|
21
|
+
getSimilarArtists(artist: string, limit?: number): Promise<ArtistSimilarResponse>;
|
|
22
|
+
searchArtist(artist: string, limit?: number, page?: number): Promise<ArtistSearchResponse>;
|
|
23
|
+
getSimilarTracks(artist: string, track: string, limit?: number): Promise<TrackSimilarResponse>;
|
|
24
|
+
searchTrack(track: string, limit?: number, page?: number): Promise<TrackSearchResponse>;
|
|
25
|
+
getTagInfo(tag: string, lang?: string): Promise<TagInfoResponse>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.LastFmClient = void 0;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
class LastFmClient {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.baseUrl = "https://ws.audioscrobbler.com/2.0/";
|
|
11
|
+
this.apiKey = options.apiKey;
|
|
12
|
+
this.apiSecret = options.apiSecret;
|
|
13
|
+
this.userAgent = options.userAgent || "LastFmClient/1.0.0";
|
|
14
|
+
}
|
|
15
|
+
sign(params) {
|
|
16
|
+
const str = Object.keys(params)
|
|
17
|
+
.sort()
|
|
18
|
+
.filter((k) => params[k] !== undefined)
|
|
19
|
+
.map((k) => `${k}${params[k]}`)
|
|
20
|
+
.join("") + this.apiSecret;
|
|
21
|
+
return crypto_1.default.createHash("md5").update(str).digest("hex");
|
|
22
|
+
}
|
|
23
|
+
async request(method, params, signed = false) {
|
|
24
|
+
const searchParams = new URLSearchParams();
|
|
25
|
+
const allParams = {
|
|
26
|
+
api_key: this.apiKey,
|
|
27
|
+
format: "json",
|
|
28
|
+
...params,
|
|
29
|
+
};
|
|
30
|
+
if (signed) {
|
|
31
|
+
allParams.api_sig = this.sign(allParams);
|
|
32
|
+
}
|
|
33
|
+
for (const [key, value] of Object.entries(allParams)) {
|
|
34
|
+
if (value !== undefined)
|
|
35
|
+
searchParams.append(key, String(value));
|
|
36
|
+
}
|
|
37
|
+
if (method === "GET") {
|
|
38
|
+
const url = `${this.baseUrl}?${searchParams.toString()}`;
|
|
39
|
+
const res = await fetch(url, {
|
|
40
|
+
headers: { "User-Agent": this.userAgent },
|
|
41
|
+
});
|
|
42
|
+
if (!res.ok) {
|
|
43
|
+
throw new Error(`Last.fm API Error: ${res.status} ${res.statusText}`);
|
|
44
|
+
}
|
|
45
|
+
return (await res.json());
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const res = await fetch(this.baseUrl, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
body: searchParams,
|
|
51
|
+
headers: { "User-Agent": this.userAgent },
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
throw new Error(`Last.fm API Error: ${res.status} ${res.statusText}`);
|
|
55
|
+
}
|
|
56
|
+
return (await res.json());
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
getAuthUrl(callbackUrl) {
|
|
60
|
+
let url = `https://www.last.fm/api/auth/?api_key=${this.apiKey}`;
|
|
61
|
+
if (callbackUrl) {
|
|
62
|
+
url += `&cb=${encodeURIComponent(callbackUrl)}`;
|
|
63
|
+
}
|
|
64
|
+
return url;
|
|
65
|
+
}
|
|
66
|
+
async getSession(token) {
|
|
67
|
+
return this.request("GET", { method: "auth.getSession", token }, true);
|
|
68
|
+
}
|
|
69
|
+
async getUser(username) {
|
|
70
|
+
return this.request("GET", {
|
|
71
|
+
method: "user.getInfo",
|
|
72
|
+
user: username,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// Track methods
|
|
76
|
+
async getRecentTracks(username, limit = 50, page = 1) {
|
|
77
|
+
return this.request("GET", {
|
|
78
|
+
method: "user.getRecentTracks",
|
|
79
|
+
user: username,
|
|
80
|
+
limit,
|
|
81
|
+
page,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async getNowPlaying(username) {
|
|
85
|
+
const recentTracks = await this.getRecentTracks(username, 2);
|
|
86
|
+
const track = recentTracks.recenttracks.track[0];
|
|
87
|
+
if (track && track["@attr"]?.nowplaying === "true") {
|
|
88
|
+
return track;
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
async getTrackInfo(artist, track, username) {
|
|
93
|
+
return this.request("GET", {
|
|
94
|
+
method: "track.getInfo",
|
|
95
|
+
artist,
|
|
96
|
+
track,
|
|
97
|
+
username,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async updateNowPlaying(sessionKey, artist, track, album, duration) {
|
|
101
|
+
await this.request("POST", {
|
|
102
|
+
method: "track.updateNowPlaying",
|
|
103
|
+
sk: sessionKey,
|
|
104
|
+
artist,
|
|
105
|
+
track,
|
|
106
|
+
album,
|
|
107
|
+
duration,
|
|
108
|
+
}, true);
|
|
109
|
+
}
|
|
110
|
+
async scrobble(sessionKey, artist, track, timestamp, album) {
|
|
111
|
+
await this.request("POST", {
|
|
112
|
+
method: "track.scrobble",
|
|
113
|
+
sk: sessionKey,
|
|
114
|
+
artist,
|
|
115
|
+
track,
|
|
116
|
+
timestamp,
|
|
117
|
+
album,
|
|
118
|
+
}, true);
|
|
119
|
+
}
|
|
120
|
+
// Album Methods
|
|
121
|
+
async getAlbumInfo(artist, album, username) {
|
|
122
|
+
return this.request("GET", {
|
|
123
|
+
method: "album.getInfo",
|
|
124
|
+
artist,
|
|
125
|
+
album,
|
|
126
|
+
username,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
async searchAlbum(album, limit, page) {
|
|
130
|
+
return this.request("GET", {
|
|
131
|
+
method: "album.search",
|
|
132
|
+
album,
|
|
133
|
+
limit,
|
|
134
|
+
page,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// Artist Methods
|
|
138
|
+
async getArtistInfo(artist, username) {
|
|
139
|
+
return this.request("GET", {
|
|
140
|
+
method: "artist.getInfo",
|
|
141
|
+
artist,
|
|
142
|
+
username,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async getSimilarArtists(artist, limit) {
|
|
146
|
+
return this.request("GET", {
|
|
147
|
+
method: "artist.getSimilar",
|
|
148
|
+
artist,
|
|
149
|
+
limit,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async searchArtist(artist, limit, page) {
|
|
153
|
+
return this.request("GET", {
|
|
154
|
+
method: "artist.search",
|
|
155
|
+
artist,
|
|
156
|
+
limit,
|
|
157
|
+
page,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// Track Methods
|
|
161
|
+
async getSimilarTracks(artist, track, limit) {
|
|
162
|
+
return this.request("GET", {
|
|
163
|
+
method: "track.getSimilar",
|
|
164
|
+
artist,
|
|
165
|
+
track,
|
|
166
|
+
limit,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
async searchTrack(track, limit, page) {
|
|
170
|
+
return this.request("GET", {
|
|
171
|
+
method: "track.search",
|
|
172
|
+
track,
|
|
173
|
+
limit,
|
|
174
|
+
page,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
// Tag Methods
|
|
178
|
+
async getTagInfo(tag, lang) {
|
|
179
|
+
return this.request("GET", {
|
|
180
|
+
method: "tag.getInfo",
|
|
181
|
+
tag,
|
|
182
|
+
lang,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
exports.LastFmClient = LastFmClient;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./LastFmClient"), exports);
|
|
18
|
+
__exportStar(require("./types"), exports);
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
export type LastFmImage = {
|
|
2
|
+
size: "small" | "medium" | "large" | "extralarge";
|
|
3
|
+
"#text": string;
|
|
4
|
+
};
|
|
5
|
+
export type LastFmArtistRef = {
|
|
6
|
+
mbid: string;
|
|
7
|
+
name: string;
|
|
8
|
+
"#text": string;
|
|
9
|
+
url?: string;
|
|
10
|
+
};
|
|
11
|
+
export type LastFmAlbumRef = {
|
|
12
|
+
"#text": string;
|
|
13
|
+
mbid: string;
|
|
14
|
+
};
|
|
15
|
+
export type LastFmTrack = {
|
|
16
|
+
name: string;
|
|
17
|
+
mbid: string;
|
|
18
|
+
url: string;
|
|
19
|
+
artist: LastFmArtistRef;
|
|
20
|
+
album: LastFmAlbumRef;
|
|
21
|
+
playcount: number | string;
|
|
22
|
+
duration?: string;
|
|
23
|
+
image?: LastFmImage[];
|
|
24
|
+
date?: {
|
|
25
|
+
uts: string;
|
|
26
|
+
"#text": string;
|
|
27
|
+
};
|
|
28
|
+
"@attr"?: {
|
|
29
|
+
nowplaying: "true" | "false";
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export type LastFmUser = {
|
|
33
|
+
user: {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
realname: string;
|
|
37
|
+
gender: string;
|
|
38
|
+
country: string;
|
|
39
|
+
url: string;
|
|
40
|
+
playcount: string;
|
|
41
|
+
playlists: string;
|
|
42
|
+
bootstrap: string;
|
|
43
|
+
image: LastFmImage[];
|
|
44
|
+
registered: {
|
|
45
|
+
unixtime: string;
|
|
46
|
+
"#text": number;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
export type TrackInfoResponse = {
|
|
51
|
+
track: LastFmTrack;
|
|
52
|
+
};
|
|
53
|
+
export type RecentTracksResponse = {
|
|
54
|
+
recenttracks: {
|
|
55
|
+
track: LastFmTrack[];
|
|
56
|
+
"@attr": {
|
|
57
|
+
user: string;
|
|
58
|
+
page: string;
|
|
59
|
+
perPage: string;
|
|
60
|
+
totalPages: string;
|
|
61
|
+
total: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
export type AuthSessionResponse = {
|
|
66
|
+
session: {
|
|
67
|
+
name: string;
|
|
68
|
+
key: string;
|
|
69
|
+
subscriber: number;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
export type UserInfoResponse = LastFmUser;
|
|
73
|
+
export interface LastFmClientOptions {
|
|
74
|
+
apiKey: string;
|
|
75
|
+
apiSecret: string;
|
|
76
|
+
userAgent?: string;
|
|
77
|
+
}
|
|
78
|
+
export type AlbumInfoResponse = {
|
|
79
|
+
album: {
|
|
80
|
+
name: string;
|
|
81
|
+
artist: string;
|
|
82
|
+
mbid?: string;
|
|
83
|
+
url: string;
|
|
84
|
+
image?: LastFmImage[];
|
|
85
|
+
listeners?: string;
|
|
86
|
+
playcount?: string;
|
|
87
|
+
tracks?: {
|
|
88
|
+
track: LastFmTrack[];
|
|
89
|
+
};
|
|
90
|
+
tags?: {
|
|
91
|
+
tag: LastFmTag[];
|
|
92
|
+
};
|
|
93
|
+
wiki?: {
|
|
94
|
+
published: string;
|
|
95
|
+
summary: string;
|
|
96
|
+
content: string;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
export type AlbumSearchResponse = {
|
|
101
|
+
results: {
|
|
102
|
+
albummatches: {
|
|
103
|
+
album: Array<{
|
|
104
|
+
name: string;
|
|
105
|
+
artist: string;
|
|
106
|
+
url: string;
|
|
107
|
+
image: LastFmImage[];
|
|
108
|
+
streamable: string;
|
|
109
|
+
mbid: string;
|
|
110
|
+
}>;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
export type ArtistInfoResponse = {
|
|
115
|
+
artist: {
|
|
116
|
+
name: string;
|
|
117
|
+
mbid?: string;
|
|
118
|
+
url: string;
|
|
119
|
+
image?: LastFmImage[];
|
|
120
|
+
streamable?: string;
|
|
121
|
+
ontour?: string;
|
|
122
|
+
stats?: {
|
|
123
|
+
listeners: string;
|
|
124
|
+
playcount: string;
|
|
125
|
+
};
|
|
126
|
+
similar?: {
|
|
127
|
+
artist: LastFmArtistRef[];
|
|
128
|
+
};
|
|
129
|
+
tags?: {
|
|
130
|
+
tag: LastFmTag[];
|
|
131
|
+
};
|
|
132
|
+
bio?: {
|
|
133
|
+
published: string;
|
|
134
|
+
summary: string;
|
|
135
|
+
content: string;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
export type ArtistSimilarResponse = {
|
|
140
|
+
similarartists: {
|
|
141
|
+
artist: LastFmArtistRef[];
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
export type ArtistSearchResponse = {
|
|
145
|
+
results: {
|
|
146
|
+
artistmatches: {
|
|
147
|
+
artist: Array<{
|
|
148
|
+
name: string;
|
|
149
|
+
listeners: string;
|
|
150
|
+
mbid: string;
|
|
151
|
+
url: string;
|
|
152
|
+
streamable: string;
|
|
153
|
+
image: LastFmImage[];
|
|
154
|
+
}>;
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
export type TrackSimilarResponse = {
|
|
159
|
+
similartracks: {
|
|
160
|
+
track: LastFmTrack[];
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
export type TrackSearchResponse = {
|
|
164
|
+
results: {
|
|
165
|
+
trackmatches: {
|
|
166
|
+
track: Array<{
|
|
167
|
+
name: string;
|
|
168
|
+
artist: string;
|
|
169
|
+
url: string;
|
|
170
|
+
streamable: string;
|
|
171
|
+
listeners: string;
|
|
172
|
+
image: LastFmImage[];
|
|
173
|
+
mbid: string;
|
|
174
|
+
}>;
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
export type LastFmTag = {
|
|
179
|
+
name: string;
|
|
180
|
+
url: string;
|
|
181
|
+
reach?: string;
|
|
182
|
+
taggings?: string;
|
|
183
|
+
streamable?: string;
|
|
184
|
+
wiki?: {
|
|
185
|
+
published: string;
|
|
186
|
+
summary: string;
|
|
187
|
+
content: string;
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
export type TagInfoResponse = {
|
|
191
|
+
tag: LastFmTag & {
|
|
192
|
+
total?: number;
|
|
193
|
+
reach?: number;
|
|
194
|
+
};
|
|
195
|
+
};
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lastfm.ts",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A typed Last.fm API client",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"prepublishOnly": "npm run build",
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"lastfm",
|
|
14
|
+
"scrobbler",
|
|
15
|
+
"typescript"
|
|
16
|
+
],
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"author": "Rafael",
|
|
21
|
+
"license": "ISC",
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^20.0.0",
|
|
24
|
+
"typescript": "^5.9.3"
|
|
25
|
+
}
|
|
26
|
+
}
|