-
Notifications
You must be signed in to change notification settings - Fork 0
/
subsonic.ts
134 lines (110 loc) · 3.23 KB
/
subsonic.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
/* Subsonic library written by Yourfriend. 2023 */
import localforage from 'localforage';
import md5 from './md5';
import { error, info } from "../logger";
interface Authenication {
username?: string;
password?: string;
}
export default class Subsonic {
authenication: Authenication = {};
server: string;
client: string;
constructor(server: string, username: string, password: string) {
this.server = server||"";
if (!this.server.endsWith('/')) this.server += '/';
this.authenication.username = username;
this.authenication.password = password;
this.client = 'subsonic-ts';
}
private buildURL(endpoint: string, options: Record<string, any> = {}) {
const salt = crypto.randomUUID();
const token = md5(this.authenication.password + salt);
return `${this.server}rest/${endpoint}.view?u=${
this.authenication.username
}&t=${token}&s=${salt}&v=1.15.0&c=${this.client}&f=json${Object.entries(
options
)
.map((z) => `&${z[0]}=${z[1]}`)
.join('')}`;
}
private async createRequest(
endpoint: string,
options: Record<string, any> = {},
type: 'json' | 'blob' | 'raw' = 'json'
) {
let req;
try {
req = await fetch(this.buildURL(endpoint, options));
} catch(e) {
error("Failed connecting to " + this.server + " endpoint "+ endpoint + "! --> " + e)
return;
}
if(type == "raw") return req;
if (type === 'json') {
return (await req.json())['subsonic-response'];
}
return req.blob();
}
async getAlbum(id: string) {
const response = await this.createRequest('getAlbum', { id });
return response.album;
}
async getArtist(id: string) {
const response = await this.createRequest('getArtist', {
id,
});
return response.artist;
}
async getCoverArt(id: string, size = 300) {
const response = await this.createRequest(
'getCoverArt',
{
id,
size,
},
'blob'
);
return response;
}
async scrobble(id: string) {
const response = await this.createRequest('scrobble', {
id,
});
return response;
}
getStreamUrl(id: string) {
return this.buildURL('stream', { id });
}
static async refreshSongs() {
await localforage.removeItem('songs');
}
async getAllSongs() {
const artists = await this.getArtists();
let songs = (await localforage.getItem('songs') as object[]) || [];
if (songs.length === 0) {
await Promise.all(
artists.map(async (partArtist: {id: string}) => {
const fullArtist = await this.getArtist(partArtist.id);
await Promise.all(
fullArtist.album.map(async (partAlbum: {id: string}) => {
const fullAlbum = await this.getAlbum(partAlbum.id);
songs = [...(fullAlbum.song || []), ...songs];
})
);
})
);
localforage.setItem('songs', songs);
}
return songs;
}
async getArtists() {
const response = await this.createRequest('getArtists');
return response.artists.index.map((z: {artist: string}) => z.artist).flat();
}
async test() {
const response = await this.createRequest('ping', {}, "raw");
if (response.status === 'ok') return true;
return false;
}
}