sa2kit 1.6.2 → 1.6.3
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/dist/audioDetection/index.js.map +1 -1
- package/dist/audioDetection/index.mjs.map +1 -1
- package/dist/{chunk-5XUE72Y3.mjs → chunk-6LEA37ZM.mjs} +2 -2
- package/dist/{chunk-5XUE72Y3.mjs.map → chunk-6LEA37ZM.mjs.map} +1 -1
- package/dist/chunk-EBP7AE6F.js +167 -0
- package/dist/chunk-EBP7AE6F.js.map +1 -0
- package/dist/chunk-MBG4DBGP.mjs +154 -0
- package/dist/chunk-MBG4DBGP.mjs.map +1 -0
- package/dist/{chunk-DQVPZTVC.js → chunk-QKXKXAAV.js} +2 -2
- package/dist/{chunk-DQVPZTVC.js.map → chunk-QKXKXAAV.js.map} +1 -1
- package/dist/imageCrop/index.js.map +1 -1
- package/dist/imageCrop/index.mjs.map +1 -1
- package/dist/index-DtLpANUB.d.mts +70 -0
- package/dist/index-DtLpANUB.d.ts +70 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/mmd/admin/index.d.mts +1 -1
- package/dist/mmd/admin/index.d.ts +1 -1
- package/dist/mmd/index.d.mts +16 -2
- package/dist/mmd/index.d.ts +16 -2
- package/dist/mmd/index.js +280 -130
- package/dist/mmd/index.js.map +1 -1
- package/dist/mmd/index.mjs +280 -130
- package/dist/mmd/index.mjs.map +1 -1
- package/dist/mmd/server/index.d.mts +1 -1
- package/dist/mmd/server/index.d.ts +1 -1
- package/dist/music/index.d.mts +30 -0
- package/dist/music/index.d.ts +30 -0
- package/dist/music/index.js +458 -0
- package/dist/music/index.js.map +1 -0
- package/dist/music/index.mjs +427 -0
- package/dist/music/index.mjs.map +1 -0
- package/dist/music/server/index.d.mts +1 -0
- package/dist/music/server/index.d.ts +1 -0
- package/dist/music/server/index.js +29 -0
- package/dist/music/server/index.js.map +1 -0
- package/dist/music/server/index.mjs +4 -0
- package/dist/music/server/index.mjs.map +1 -0
- package/dist/testYourself/admin/index.js +3 -3
- package/dist/testYourself/admin/index.mjs +1 -1
- package/dist/testYourself/index.js +7 -7
- package/dist/testYourself/index.js.map +1 -1
- package/dist/testYourself/index.mjs +2 -2
- package/dist/testYourself/index.mjs.map +1 -1
- package/dist/{types-DxYJqqes.d.mts → types-B60F7EZZ.d.mts} +16 -1
- package/dist/{types-DxYJqqes.d.ts → types-B60F7EZZ.d.ts} +16 -1
- package/dist/universalFile/server/index.js +5 -5
- package/dist/universalFile/server/index.mjs +1 -1
- package/package.json +32 -19
- package/tailwind.animations.js +5 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { M as MmdPlaylist, f as MmdPlaylistNode, j as MmdPresetItem, h as MmdResourceOption, N as NewMmdPlaylist, g as NewMmdPlaylistNode, k as NewMmdPresetItem, i as NewMmdResourceOption, a as mmdPlaylistNodes, e as mmdPlaylistNodesRelations, m as mmdPlaylists, d as mmdPlaylistsRelations, c as mmdPresetItems, b as mmdResourceOptions } from '../../drizzle-schema-BNhqj2AZ.mjs';
|
|
2
|
-
import { M as MMDPlaylistConfig } from '../../types-
|
|
2
|
+
import { M as MMDPlaylistConfig } from '../../types-B60F7EZZ.mjs';
|
|
3
3
|
import { U as UniversalFileService } from '../../UniversalFileService-CGGzYeeF.mjs';
|
|
4
4
|
import { F as FileMetadata } from '../../types-CK4We_aI.mjs';
|
|
5
5
|
import 'drizzle-orm';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { M as MmdPlaylist, f as MmdPlaylistNode, j as MmdPresetItem, h as MmdResourceOption, N as NewMmdPlaylist, g as NewMmdPlaylistNode, k as NewMmdPresetItem, i as NewMmdResourceOption, a as mmdPlaylistNodes, e as mmdPlaylistNodesRelations, m as mmdPlaylists, d as mmdPlaylistsRelations, c as mmdPresetItems, b as mmdResourceOptions } from '../../drizzle-schema-BNhqj2AZ.js';
|
|
2
|
-
import { M as MMDPlaylistConfig } from '../../types-
|
|
2
|
+
import { M as MMDPlaylistConfig } from '../../types-B60F7EZZ.js';
|
|
3
3
|
import { U as UniversalFileService } from '../../UniversalFileService-BuHN-jrR.js';
|
|
4
4
|
import { F as FileMetadata } from '../../types-CK4We_aI.js';
|
|
5
5
|
import 'drizzle-orm';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { S as SearchOptions, a as SearchResult } from '../index-DtLpANUB.mjs';
|
|
2
|
+
export { f as MetingService, b as MusicApiResponse, M as MusicTrack, e as createLyricHandler, c as createSearchHandler, d as createSongUrlHandler, m as musicService } from '../index-DtLpANUB.mjs';
|
|
3
|
+
import React__default from 'react';
|
|
4
|
+
|
|
5
|
+
declare const MusicPlayer: React__default.FC;
|
|
6
|
+
|
|
7
|
+
declare function useMusic(): {
|
|
8
|
+
search: (options: SearchOptions) => void;
|
|
9
|
+
searchResult: SearchResult | undefined;
|
|
10
|
+
isSearching: boolean;
|
|
11
|
+
searchError: any;
|
|
12
|
+
getSongUrl: (id: string, source?: string) => Promise<string | undefined>;
|
|
13
|
+
getLyric: (id: string, source?: string) => Promise<string | undefined>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
interface MusicSourceAdapter {
|
|
17
|
+
parseSearchResult(data: any): SearchResult;
|
|
18
|
+
parseGetSongUrl(data: any): string | null;
|
|
19
|
+
parseGetLyric(data: any): any;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare const kugouAdapter: MusicSourceAdapter;
|
|
23
|
+
|
|
24
|
+
declare const neteaseAdapter: MusicSourceAdapter;
|
|
25
|
+
|
|
26
|
+
declare const tencentAdapter: MusicSourceAdapter;
|
|
27
|
+
|
|
28
|
+
declare const xiamiAdapter: MusicSourceAdapter;
|
|
29
|
+
|
|
30
|
+
export { MusicPlayer, type MusicSourceAdapter, SearchOptions, SearchResult, kugouAdapter, neteaseAdapter, tencentAdapter, useMusic, xiamiAdapter };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { S as SearchOptions, a as SearchResult } from '../index-DtLpANUB.js';
|
|
2
|
+
export { f as MetingService, b as MusicApiResponse, M as MusicTrack, e as createLyricHandler, c as createSearchHandler, d as createSongUrlHandler, m as musicService } from '../index-DtLpANUB.js';
|
|
3
|
+
import React__default from 'react';
|
|
4
|
+
|
|
5
|
+
declare const MusicPlayer: React__default.FC;
|
|
6
|
+
|
|
7
|
+
declare function useMusic(): {
|
|
8
|
+
search: (options: SearchOptions) => void;
|
|
9
|
+
searchResult: SearchResult | undefined;
|
|
10
|
+
isSearching: boolean;
|
|
11
|
+
searchError: any;
|
|
12
|
+
getSongUrl: (id: string, source?: string) => Promise<string | undefined>;
|
|
13
|
+
getLyric: (id: string, source?: string) => Promise<string | undefined>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
interface MusicSourceAdapter {
|
|
17
|
+
parseSearchResult(data: any): SearchResult;
|
|
18
|
+
parseGetSongUrl(data: any): string | null;
|
|
19
|
+
parseGetLyric(data: any): any;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare const kugouAdapter: MusicSourceAdapter;
|
|
23
|
+
|
|
24
|
+
declare const neteaseAdapter: MusicSourceAdapter;
|
|
25
|
+
|
|
26
|
+
declare const tencentAdapter: MusicSourceAdapter;
|
|
27
|
+
|
|
28
|
+
declare const xiamiAdapter: MusicSourceAdapter;
|
|
29
|
+
|
|
30
|
+
export { MusicPlayer, type MusicSourceAdapter, SearchOptions, SearchResult, kugouAdapter, neteaseAdapter, tencentAdapter, useMusic, xiamiAdapter };
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkEBP7AE6F_js = require('../chunk-EBP7AE6F.js');
|
|
4
|
+
require('../chunk-DGUM43GV.js');
|
|
5
|
+
var React = require('react');
|
|
6
|
+
var lucideReact = require('lucide-react');
|
|
7
|
+
var useSWR = require('swr');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
12
|
+
var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
|
|
13
|
+
|
|
14
|
+
// src/music/adapters/kugou.ts
|
|
15
|
+
var kugouAdapter = {
|
|
16
|
+
parseSearchResult(data) {
|
|
17
|
+
const root = typeof data === "string" ? JSON.parse(data) : data;
|
|
18
|
+
const info = root.data?.data?.info || root.data?.info || root.info || [];
|
|
19
|
+
const total = root.data?.data?.total || root.data?.total || info.length;
|
|
20
|
+
return {
|
|
21
|
+
tracks: info.map((item) => {
|
|
22
|
+
let pic = item.pic || "";
|
|
23
|
+
if (item.trans_param?.union_cover) {
|
|
24
|
+
pic = item.trans_param.union_cover.replace("{size}", "400");
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
id: item.hash || item.id,
|
|
28
|
+
name: item.songname || item.filename || "Unknown",
|
|
29
|
+
artist: item.singername || "Unknown Artist",
|
|
30
|
+
album: item.album_name || item.album || "",
|
|
31
|
+
pic,
|
|
32
|
+
url: item.url,
|
|
33
|
+
lrc: item.lrc,
|
|
34
|
+
source: "kugou",
|
|
35
|
+
isVip: item.privilege >= 8,
|
|
36
|
+
playable: item.status !== 0
|
|
37
|
+
};
|
|
38
|
+
}),
|
|
39
|
+
total
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
parseGetSongUrl(data) {
|
|
43
|
+
return data.url?.url || data.url?.backup_url?.[0] || null;
|
|
44
|
+
},
|
|
45
|
+
parseGetLyric(data) {
|
|
46
|
+
const root = typeof data === "string" ? JSON.parse(data) : data;
|
|
47
|
+
return root.lyric || root.lrc || root.data?.lyric || root.data || "";
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// src/music/adapters/netease.ts
|
|
52
|
+
var neteaseAdapter = {
|
|
53
|
+
parseSearchResult(data) {
|
|
54
|
+
const root = typeof data === "string" ? JSON.parse(data) : data;
|
|
55
|
+
const songs = root.result?.songs || root.songs || (Array.isArray(root) ? root : []);
|
|
56
|
+
return {
|
|
57
|
+
tracks: songs.map((item) => ({
|
|
58
|
+
id: item.id,
|
|
59
|
+
name: item.name,
|
|
60
|
+
artist: Array.isArray(item.artist) ? item.artist.join(", ") : item.artist,
|
|
61
|
+
album: item.album?.name || item.album,
|
|
62
|
+
pic: item.pic || item.album?.picUrl,
|
|
63
|
+
url: item.url,
|
|
64
|
+
lrc: item.lrc,
|
|
65
|
+
source: "netease",
|
|
66
|
+
isVip: item.fee === 1 || item.fee === 4,
|
|
67
|
+
playable: item.noCopyrightRcmd === null
|
|
68
|
+
})),
|
|
69
|
+
total: root.result?.songCount || songs.length
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
parseGetSongUrl(data) {
|
|
73
|
+
const root = typeof data === "string" ? JSON.parse(data) : data;
|
|
74
|
+
const item = root.data?.[0] || root[0] || root;
|
|
75
|
+
return item.url || null;
|
|
76
|
+
},
|
|
77
|
+
parseGetLyric(data) {
|
|
78
|
+
const root = typeof data === "string" ? JSON.parse(data) : data;
|
|
79
|
+
return root.lyric || root.lrc || root.data?.lyric || "";
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// src/music/adapters/tencent.ts
|
|
84
|
+
var tencentAdapter = {
|
|
85
|
+
parseSearchResult(data) {
|
|
86
|
+
const root = typeof data === "string" ? JSON.parse(data) : data;
|
|
87
|
+
const songData = root.data?.data?.song || root.data?.song || root.data || root;
|
|
88
|
+
const list = songData.list || root.songs || [];
|
|
89
|
+
const total = songData.totalnum || root.total || list.length;
|
|
90
|
+
return {
|
|
91
|
+
tracks: list.map((item) => {
|
|
92
|
+
const artist = Array.isArray(item.singer) ? item.singer.map((s) => s.name).join(", ") : item.singer?.[0]?.name || item.artist || "Unknown";
|
|
93
|
+
let pic = item.pic;
|
|
94
|
+
if (!pic && item.album?.mid) {
|
|
95
|
+
pic = `https://y.gtimg.cn/music/photo_new/T002R300x300M000${item.album.mid}.jpg`;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
id: item.mid || item.id || item.songid,
|
|
99
|
+
name: item.name || item.title || item.songname,
|
|
100
|
+
artist,
|
|
101
|
+
album: item.album?.name || item.albumname || item.album,
|
|
102
|
+
pic: pic || "",
|
|
103
|
+
url: item.url,
|
|
104
|
+
lrc: item.lrc,
|
|
105
|
+
source: "tencent",
|
|
106
|
+
isVip: item.pay?.pay_play === 1,
|
|
107
|
+
playable: item.action?.switch !== 0
|
|
108
|
+
};
|
|
109
|
+
}),
|
|
110
|
+
total
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
parseGetSongUrl(data) {
|
|
114
|
+
const root = data;
|
|
115
|
+
const urlData = root.url.url;
|
|
116
|
+
let finalUrl = Object.values(urlData)[0];
|
|
117
|
+
console.log("finalUrl2", finalUrl);
|
|
118
|
+
return finalUrl.startsWith("http") ? finalUrl : `http://${finalUrl}`;
|
|
119
|
+
},
|
|
120
|
+
parseGetLyric(data) {
|
|
121
|
+
const root = typeof data === "string" ? JSON.parse(data) : data;
|
|
122
|
+
return root.lyric || root.lrc || root.data?.lyric || "";
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// src/music/adapters/xiami.ts
|
|
127
|
+
var xiamiAdapter = {
|
|
128
|
+
parseSearchResult(data) {
|
|
129
|
+
const root = typeof data === "string" ? JSON.parse(data) : data;
|
|
130
|
+
const result = root.data?.result || root.result || root;
|
|
131
|
+
const songs = result.songs || (Array.isArray(root) ? root : []);
|
|
132
|
+
return {
|
|
133
|
+
tracks: songs.map((item) => ({
|
|
134
|
+
id: item.id?.toString() || "",
|
|
135
|
+
name: item.name || "Unknown",
|
|
136
|
+
artist: Array.isArray(item.ar) ? item.ar.map((a) => a.name).join(", ") : item.artist || "Unknown Artist",
|
|
137
|
+
album: item.al?.name || item.album || "",
|
|
138
|
+
pic: item.al?.picUrl || item.pic || "",
|
|
139
|
+
url: item.url,
|
|
140
|
+
lrc: item.lrc,
|
|
141
|
+
source: "xiami",
|
|
142
|
+
// 这里的逻辑参考提供的数据结构
|
|
143
|
+
isVip: item.fee === 1 || item.fee === 8,
|
|
144
|
+
playable: item.copyright !== 0
|
|
145
|
+
})),
|
|
146
|
+
total: result.songCount || songs.length
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
parseGetSongUrl(data) {
|
|
150
|
+
const root = typeof data === "string" ? JSON.parse(data) : data;
|
|
151
|
+
const item = root.data?.[0] || root.data || root[0] || root;
|
|
152
|
+
return item.url || null;
|
|
153
|
+
},
|
|
154
|
+
parseGetLyric(data) {
|
|
155
|
+
const root = typeof data === "string" ? JSON.parse(data) : data;
|
|
156
|
+
return root.lyric || root.lrc || root.data?.lyric || root.data || "";
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// src/music/hooks/useMusic.ts
|
|
161
|
+
var fetcher = (url) => fetch(url).then((res) => res.json());
|
|
162
|
+
var ADAPTERS = {
|
|
163
|
+
kugou: kugouAdapter,
|
|
164
|
+
netease: neteaseAdapter,
|
|
165
|
+
tencent: tencentAdapter,
|
|
166
|
+
xiami: xiamiAdapter
|
|
167
|
+
};
|
|
168
|
+
function useMusic() {
|
|
169
|
+
const [searchOptions, setSearchOptions] = React.useState(null);
|
|
170
|
+
const { data: rawData, error: searchError, isLoading: isSearching } = useSWR__default.default(
|
|
171
|
+
searchOptions ? `/api/music/search?keyword=${encodeURIComponent(searchOptions.keyword)}&source=${searchOptions.source || chunkEBP7AE6F_js.DEFAULT_MUSIC_SOURCE}&limit=${searchOptions.limit || 20}&offset=${searchOptions.offset || 0}` : null,
|
|
172
|
+
fetcher
|
|
173
|
+
);
|
|
174
|
+
const searchResult = React.useMemo(() => {
|
|
175
|
+
if (!rawData?.data || !searchOptions) return void 0;
|
|
176
|
+
const adapter = ADAPTERS[searchOptions.source || chunkEBP7AE6F_js.DEFAULT_MUSIC_SOURCE];
|
|
177
|
+
if (adapter) {
|
|
178
|
+
return adapter.parseSearchResult(rawData.data);
|
|
179
|
+
}
|
|
180
|
+
return void 0;
|
|
181
|
+
}, [rawData, searchOptions]);
|
|
182
|
+
const search = React.useCallback((options) => {
|
|
183
|
+
setSearchOptions(options);
|
|
184
|
+
}, []);
|
|
185
|
+
const getSongUrl = React.useCallback(async (id, source = chunkEBP7AE6F_js.DEFAULT_MUSIC_SOURCE) => {
|
|
186
|
+
try {
|
|
187
|
+
const res = await fetch(`/api/music/url?id=${id}&source=${source}`);
|
|
188
|
+
const json = await res.json();
|
|
189
|
+
const adapter = ADAPTERS[source];
|
|
190
|
+
console.log("json2", json.data, source, adapter);
|
|
191
|
+
if (adapter && json.data) {
|
|
192
|
+
console.log("getSongUrl2", json.data);
|
|
193
|
+
return adapter.parseGetSongUrl(json.data) || void 0;
|
|
194
|
+
}
|
|
195
|
+
return json.data?.url;
|
|
196
|
+
} catch (err) {
|
|
197
|
+
console.error("[Music] Failed to get song URL:", err);
|
|
198
|
+
return void 0;
|
|
199
|
+
}
|
|
200
|
+
}, []);
|
|
201
|
+
const getLyric = React.useCallback(async (id, source = chunkEBP7AE6F_js.DEFAULT_MUSIC_SOURCE) => {
|
|
202
|
+
try {
|
|
203
|
+
const res = await fetch(`/api/music/lyric?id=${id}&source=${source}`);
|
|
204
|
+
const json = await res.json();
|
|
205
|
+
const adapter = ADAPTERS[source];
|
|
206
|
+
if (adapter && json.data) {
|
|
207
|
+
return adapter.parseGetLyric(json.data);
|
|
208
|
+
}
|
|
209
|
+
return json.data?.lyric;
|
|
210
|
+
} catch (err) {
|
|
211
|
+
console.error("[Music] Failed to get lyric:", err);
|
|
212
|
+
return void 0;
|
|
213
|
+
}
|
|
214
|
+
}, []);
|
|
215
|
+
return {
|
|
216
|
+
search,
|
|
217
|
+
searchResult,
|
|
218
|
+
isSearching,
|
|
219
|
+
searchError,
|
|
220
|
+
getSongUrl,
|
|
221
|
+
getLyric
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/music/components/MusicPlayer.tsx
|
|
226
|
+
var MusicPlayer = () => {
|
|
227
|
+
const { search, searchResult: searchData, isSearching, getSongUrl } = useMusic();
|
|
228
|
+
const [keyword, setKeyword] = React.useState("");
|
|
229
|
+
const [selectedSource, setSelectedSource] = React.useState(chunkEBP7AE6F_js.DEFAULT_MUSIC_SOURCE);
|
|
230
|
+
const [currentPage, setCurrentPage] = React.useState(0);
|
|
231
|
+
const [layoutMode, setLayoutMode] = React.useState("grid");
|
|
232
|
+
const pageSize = 20;
|
|
233
|
+
const [currentTrack, setCurrentTrack] = React.useState(null);
|
|
234
|
+
const [isPlaying, setIsPlaying] = React.useState(false);
|
|
235
|
+
const [progress, setProgress] = React.useState(0);
|
|
236
|
+
const [duration, setDuration] = React.useState(0);
|
|
237
|
+
const [volume, setVolume] = React.useState(0.7);
|
|
238
|
+
const [isMuted, setIsMuted] = React.useState(false);
|
|
239
|
+
const [isLoadingUrl, setIsLoadingUrl] = React.useState(false);
|
|
240
|
+
const audioRef = React.useRef(null);
|
|
241
|
+
const handleSearch = (e) => {
|
|
242
|
+
e.preventDefault();
|
|
243
|
+
if (keyword.trim()) {
|
|
244
|
+
setCurrentPage(0);
|
|
245
|
+
search({ keyword, source: selectedSource, offset: 0, limit: pageSize });
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
const handlePageChange = (newPage) => {
|
|
249
|
+
setCurrentPage(newPage);
|
|
250
|
+
search({ keyword, source: selectedSource, offset: newPage, limit: pageSize });
|
|
251
|
+
};
|
|
252
|
+
const playTrack = async (track) => {
|
|
253
|
+
setIsLoadingUrl(true);
|
|
254
|
+
const url = await getSongUrl(track.id, track.source);
|
|
255
|
+
setIsLoadingUrl(false);
|
|
256
|
+
if (url) {
|
|
257
|
+
const fullTrack = { ...track, url };
|
|
258
|
+
setCurrentTrack(fullTrack);
|
|
259
|
+
setIsPlaying(true);
|
|
260
|
+
if (audioRef.current) {
|
|
261
|
+
audioRef.current.src = url;
|
|
262
|
+
audioRef.current.play();
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
alert("\u65E0\u6CD5\u83B7\u53D6\u64AD\u653E\u94FE\u63A5");
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
const togglePlay = () => {
|
|
269
|
+
if (!audioRef.current || !currentTrack) return;
|
|
270
|
+
if (isPlaying) {
|
|
271
|
+
audioRef.current.pause();
|
|
272
|
+
} else {
|
|
273
|
+
audioRef.current.play();
|
|
274
|
+
}
|
|
275
|
+
setIsPlaying(!isPlaying);
|
|
276
|
+
};
|
|
277
|
+
const handleTimeUpdate = () => {
|
|
278
|
+
if (audioRef.current) {
|
|
279
|
+
setProgress(audioRef.current.currentTime);
|
|
280
|
+
setDuration(audioRef.current.duration);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
const handleSeek = (e) => {
|
|
284
|
+
const time = parseFloat(e.target.value);
|
|
285
|
+
setProgress(time);
|
|
286
|
+
if (audioRef.current) {
|
|
287
|
+
audioRef.current.currentTime = time;
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
const formatTime = (time) => {
|
|
291
|
+
if (isNaN(time)) return "00:00";
|
|
292
|
+
const mins = Math.floor(time / 60);
|
|
293
|
+
const secs = Math.floor(time % 60);
|
|
294
|
+
return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
|
|
295
|
+
};
|
|
296
|
+
return /* @__PURE__ */ React__default.default.createElement("div", { className: "flex flex-col h-full bg-black text-white font-sans" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "p-6 border-b border-gray-800 max-w-400 mx-auto" }, /* @__PURE__ */ React__default.default.createElement("form", { onSubmit: handleSearch, className: "flex flex-row items-center gap-4 max-w-screen mx-auto" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "relative flex-1" }, /* @__PURE__ */ React__default.default.createElement(lucideReact.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-500" }), /* @__PURE__ */ React__default.default.createElement(
|
|
297
|
+
"input",
|
|
298
|
+
{
|
|
299
|
+
type: "text",
|
|
300
|
+
value: keyword,
|
|
301
|
+
onChange: (e) => setKeyword(e.target.value),
|
|
302
|
+
placeholder: "\u641C\u7D22\u6B4C\u66F2\u3001\u6B4C\u624B...",
|
|
303
|
+
className: "w-full bg-gray-900 border border-gray-700 rounded-full py-2 pl-10 pr-4 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
|
|
304
|
+
}
|
|
305
|
+
)), /* @__PURE__ */ React__default.default.createElement(
|
|
306
|
+
"select",
|
|
307
|
+
{
|
|
308
|
+
value: selectedSource,
|
|
309
|
+
onChange: (e) => setSelectedSource(e.target.value),
|
|
310
|
+
className: "bg-gray-900 p-2 border border-gray-700 text-gray-300 text-sm rounded-full px-4 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all cursor-pointer hover:bg-gray-800"
|
|
311
|
+
},
|
|
312
|
+
chunkEBP7AE6F_js.MUSIC_SOURCES.map((src) => /* @__PURE__ */ React__default.default.createElement("option", { key: src, value: src }, chunkEBP7AE6F_js.MUSIC_SOURCE_NAMES[src]))
|
|
313
|
+
), /* @__PURE__ */ React__default.default.createElement("div", { className: " flex items-center justify-center max-w-lg bg-gray-900 rounded-full p-2 border border-gray-700" }, /* @__PURE__ */ React__default.default.createElement(
|
|
314
|
+
"button",
|
|
315
|
+
{
|
|
316
|
+
onClick: () => setLayoutMode("list"),
|
|
317
|
+
className: `p-1.5 rounded-full transition-all ${layoutMode === "list" ? "bg-blue-600 text-white" : "text-gray-500 hover:text-gray-300"}`,
|
|
318
|
+
title: "\u5217\u8868\u89C6\u56FE"
|
|
319
|
+
},
|
|
320
|
+
/* @__PURE__ */ React__default.default.createElement(lucideReact.List, { className: "w-4 h-4" })
|
|
321
|
+
), /* @__PURE__ */ React__default.default.createElement(
|
|
322
|
+
"button",
|
|
323
|
+
{
|
|
324
|
+
onClick: () => setLayoutMode("grid"),
|
|
325
|
+
className: `p-1.5 rounded-full transition-all ${layoutMode === "grid" ? "bg-blue-600 text-white" : "text-gray-500 hover:text-gray-300"}`,
|
|
326
|
+
title: "\u7F51\u683C\u89C6\u56FE"
|
|
327
|
+
},
|
|
328
|
+
/* @__PURE__ */ React__default.default.createElement(lucideReact.LayoutGrid, { className: "w-4 h-4" })
|
|
329
|
+
)), /* @__PURE__ */ React__default.default.createElement(
|
|
330
|
+
"button",
|
|
331
|
+
{
|
|
332
|
+
type: "submit",
|
|
333
|
+
disabled: isSearching,
|
|
334
|
+
className: "bg-blue-600 hover:bg-blue-700 disabled:bg-blue-800 text-white rounded-full px-8 py-2 font-medium transition-all hover:scale-105 active:scale-95 flex items-center justify-center gap-2 shrink-0"
|
|
335
|
+
},
|
|
336
|
+
isSearching ? /* @__PURE__ */ React__default.default.createElement(lucideReact.Loader2, { className: "w-4 h-4 animate-spin" }) : "\u641C\u7D22"
|
|
337
|
+
))), /* @__PURE__ */ React__default.default.createElement("div", { className: "flex-1 flex overflow-hidden" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "flex-1 overflow-y-auto p-6 scrollbar-thin scrollbar-thumb-gray-800" }, Array.isArray(searchData?.tracks) && searchData.tracks.length > 0 ? /* @__PURE__ */ React__default.default.createElement("div", { className: layoutMode === "grid" ? "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" : "flex flex-col gap-2 max-w-5xl mx-auto" }, searchData.tracks.map((track) => /* @__PURE__ */ React__default.default.createElement(
|
|
338
|
+
"div",
|
|
339
|
+
{
|
|
340
|
+
key: track.id,
|
|
341
|
+
onClick: () => playTrack(track),
|
|
342
|
+
className: `flex items-center gap-4 p-3 rounded-xl cursor-pointer transition-all ${currentTrack?.id === track.id ? "bg-blue-600/20 border border-blue-500/50 shadow-[0_0_15px_rgba(59,130,246,0.2)]" : "bg-gray-900/50 border border-gray-800 hover:bg-gray-800/80 hover:border-gray-700"} ${layoutMode === "list" ? "hover:pl-5" : ""}`
|
|
343
|
+
},
|
|
344
|
+
/* @__PURE__ */ React__default.default.createElement("div", { className: `relative rounded-lg overflow-hidden shrink-0 bg-gray-800 transition-all ${layoutMode === "grid" ? "w-14 h-14" : "w-12 h-12"}` }, track.pic ? /* @__PURE__ */ React__default.default.createElement("img", { src: track.pic, alt: track.name, className: "w-full h-full object-cover" }) : /* @__PURE__ */ React__default.default.createElement("div", { className: "w-full h-full flex items-center justify-center" }, /* @__PURE__ */ React__default.default.createElement(lucideReact.Music, { className: "w-6 h-6 text-gray-600" })), currentTrack?.id === track.id && isPlaying && /* @__PURE__ */ React__default.default.createElement("div", { className: "absolute inset-0 bg-black/40 flex items-center justify-center" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "flex gap-0.5 items-end h-4" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "w-0.5 bg-blue-400 animate-music-bar", style: { height: "60%" } }), /* @__PURE__ */ React__default.default.createElement("div", { className: "w-0.5 bg-blue-400 animate-music-bar", style: { height: "100%", animationDelay: "0.2s" } }), /* @__PURE__ */ React__default.default.createElement("div", { className: "w-0.5 bg-blue-400 animate-music-bar", style: { height: "40%", animationDelay: "0.4s" } })))),
|
|
345
|
+
/* @__PURE__ */ React__default.default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React__default.default.createElement("h3", { className: `font-semibold truncate ${layoutMode === "grid" ? "text-sm" : "text-base"}` }, track.name), /* @__PURE__ */ React__default.default.createElement("span", { className: "text-[10px] px-1.5 py-0.5 rounded-md bg-gray-800 text-gray-400 shrink-0" }, chunkEBP7AE6F_js.MUSIC_SOURCE_NAMES[track.source].replace("\u97F3\u4E50", "")), track.isVip && /* @__PURE__ */ React__default.default.createElement("span", { className: "text-[10px] px-1.5 py-0.5 rounded-md bg-yellow-500/10 text-yellow-500 border border-yellow-500/20 shrink-0 font-bold" }, "VIP")), /* @__PURE__ */ React__default.default.createElement("p", { className: "text-xs text-gray-400 truncate mt-1" }, track.artist)),
|
|
346
|
+
layoutMode === "list" && /* @__PURE__ */ React__default.default.createElement("div", { className: "hidden sm:block text-xs text-gray-500 px-4 truncate max-w-[200px]" }, track.album)
|
|
347
|
+
))) : /* @__PURE__ */ React__default.default.createElement("div", { className: "h-full flex flex-col items-center justify-center text-gray-500 gap-4" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "w-16 h-16 rounded-full bg-gray-900 flex items-center justify-center" }, /* @__PURE__ */ React__default.default.createElement(lucideReact.Music, { className: "w-8 h-8" })), /* @__PURE__ */ React__default.default.createElement("p", null, isSearching ? "\u6B63\u5728\u641C\u7D22..." : "\u641C\u7D22\u4F60\u559C\u6B22\u7684\u97F3\u4E50")), searchData?.tracks && searchData.tracks.length > 0 && /* @__PURE__ */ React__default.default.createElement("div", { className: "mt-8 flex items-center justify-center gap-4 pb-8" }, /* @__PURE__ */ React__default.default.createElement(
|
|
348
|
+
"button",
|
|
349
|
+
{
|
|
350
|
+
onClick: () => handlePageChange(Math.max(0, currentPage - 1)),
|
|
351
|
+
disabled: currentPage === 0 || isSearching,
|
|
352
|
+
className: "p-2 rounded-full bg-gray-900 border border-gray-800 text-gray-400 hover:text-white hover:border-gray-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
|
353
|
+
},
|
|
354
|
+
/* @__PURE__ */ React__default.default.createElement(lucideReact.ChevronLeft, { className: "w-5 h-5" })
|
|
355
|
+
), /* @__PURE__ */ React__default.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React__default.default.createElement("span", { className: "text-sm font-medium text-blue-500 bg-blue-500/10 px-3 py-1 rounded-md" }, "\u7B2C ", currentPage + 1, " \u9875"), searchData.total > 0 && /* @__PURE__ */ React__default.default.createElement("span", { className: "text-xs text-gray-500" }, "\u5171 ", Math.ceil(searchData.total / pageSize), " \u9875")), /* @__PURE__ */ React__default.default.createElement(
|
|
356
|
+
"button",
|
|
357
|
+
{
|
|
358
|
+
onClick: () => handlePageChange(currentPage + 1),
|
|
359
|
+
disabled: isSearching || searchData.total > 0 && (currentPage + 1) * pageSize >= searchData.total,
|
|
360
|
+
className: "p-2 rounded-full bg-gray-900 border border-gray-800 text-gray-400 hover:text-white hover:border-gray-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
|
|
361
|
+
},
|
|
362
|
+
/* @__PURE__ */ React__default.default.createElement(lucideReact.ChevronRight, { className: "w-5 h-5" })
|
|
363
|
+
)))), /* @__PURE__ */ React__default.default.createElement("div", { className: "bg-gray-900/80 backdrop-blur-xl border-t border-gray-800 p-4 pb-8 md:pb-4" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "max-w-7xl mx-auto flex flex-col md:flex-row items-center gap-4" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "flex items-center gap-4 w-full md:w-1/4" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "w-12 h-12 rounded-lg overflow-hidden bg-gray-800 shrink-0" }, currentTrack?.pic ? /* @__PURE__ */ React__default.default.createElement("img", { src: currentTrack.pic, alt: "", className: "w-full h-full object-cover" }) : /* @__PURE__ */ React__default.default.createElement("div", { className: "w-full h-full flex items-center justify-center" }, /* @__PURE__ */ React__default.default.createElement(lucideReact.Music, { className: "w-5 h-5 text-gray-600" }))), /* @__PURE__ */ React__default.default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React__default.default.createElement("h4", { className: "font-medium text-sm truncate" }, currentTrack?.name || "\u672A\u5728\u64AD\u653E"), /* @__PURE__ */ React__default.default.createElement("p", { className: "text-xs text-gray-400 truncate mt-0.5" }, currentTrack?.artist || "-"))), /* @__PURE__ */ React__default.default.createElement("div", { className: "flex flex-col items-center gap-2 flex-1 w-full" }, /* @__PURE__ */ React__default.default.createElement("div", { className: "flex items-center gap-6" }, /* @__PURE__ */ React__default.default.createElement("button", { className: "text-gray-400 hover:text-white transition-colors" }, /* @__PURE__ */ React__default.default.createElement(lucideReact.SkipBack, { className: "w-5 h-5 fill-current" })), /* @__PURE__ */ React__default.default.createElement(
|
|
364
|
+
"button",
|
|
365
|
+
{
|
|
366
|
+
onClick: togglePlay,
|
|
367
|
+
disabled: !currentTrack || isLoadingUrl,
|
|
368
|
+
className: "w-10 h-10 rounded-full bg-white text-black flex items-center justify-center hover:scale-105 transition-transform disabled:opacity-50 disabled:scale-100"
|
|
369
|
+
},
|
|
370
|
+
isLoadingUrl ? /* @__PURE__ */ React__default.default.createElement(lucideReact.Loader2, { className: "w-5 h-5 animate-spin" }) : isPlaying ? /* @__PURE__ */ React__default.default.createElement(lucideReact.Pause, { className: "w-5 h-5 fill-current" }) : /* @__PURE__ */ React__default.default.createElement(lucideReact.Play, { className: "w-5 h-5 fill-current ml-0.5" })
|
|
371
|
+
), /* @__PURE__ */ React__default.default.createElement("button", { className: "text-gray-400 hover:text-white transition-colors" }, /* @__PURE__ */ React__default.default.createElement(lucideReact.SkipForward, { className: "w-5 h-5 fill-current" }))), /* @__PURE__ */ React__default.default.createElement("div", { className: "flex items-center gap-3 w-full max-w-2xl text-xs text-gray-400" }, /* @__PURE__ */ React__default.default.createElement("span", null, formatTime(progress)), /* @__PURE__ */ React__default.default.createElement("div", { className: "flex-1 relative h-1 group cursor-pointer" }, /* @__PURE__ */ React__default.default.createElement(
|
|
372
|
+
"input",
|
|
373
|
+
{
|
|
374
|
+
type: "range",
|
|
375
|
+
min: "0",
|
|
376
|
+
max: duration || 0,
|
|
377
|
+
step: "0.1",
|
|
378
|
+
value: progress,
|
|
379
|
+
onChange: handleSeek,
|
|
380
|
+
className: "absolute inset-0 w-full h-full opacity-0 z-10 cursor-pointer"
|
|
381
|
+
}
|
|
382
|
+
), /* @__PURE__ */ React__default.default.createElement("div", { className: "absolute inset-0 bg-gray-700 rounded-full overflow-hidden" }, /* @__PURE__ */ React__default.default.createElement(
|
|
383
|
+
"div",
|
|
384
|
+
{
|
|
385
|
+
className: "h-full bg-blue-500 group-hover:bg-blue-400 transition-colors",
|
|
386
|
+
style: { width: `${progress / (duration || 1) * 100}%` }
|
|
387
|
+
}
|
|
388
|
+
))), /* @__PURE__ */ React__default.default.createElement("span", null, formatTime(duration)))), /* @__PURE__ */ React__default.default.createElement("div", { className: "hidden md:flex items-center gap-3 w-1/4 justify-end" }, /* @__PURE__ */ React__default.default.createElement(
|
|
389
|
+
"button",
|
|
390
|
+
{
|
|
391
|
+
onClick: () => setIsMuted(!isMuted),
|
|
392
|
+
className: "text-gray-400 hover:text-white transition-colors"
|
|
393
|
+
},
|
|
394
|
+
isMuted || volume === 0 ? /* @__PURE__ */ React__default.default.createElement(lucideReact.VolumeX, { className: "w-5 h-5" }) : /* @__PURE__ */ React__default.default.createElement(lucideReact.Volume2, { className: "w-5 h-5" })
|
|
395
|
+
), /* @__PURE__ */ React__default.default.createElement("div", { className: "w-24 relative h-1 group" }, /* @__PURE__ */ React__default.default.createElement(
|
|
396
|
+
"input",
|
|
397
|
+
{
|
|
398
|
+
type: "range",
|
|
399
|
+
min: "0",
|
|
400
|
+
max: "1",
|
|
401
|
+
step: "0.01",
|
|
402
|
+
value: isMuted ? 0 : volume,
|
|
403
|
+
onChange: (e) => {
|
|
404
|
+
const val = parseFloat(e.target.value);
|
|
405
|
+
setVolume(val);
|
|
406
|
+
setIsMuted(false);
|
|
407
|
+
if (audioRef.current) audioRef.current.volume = val;
|
|
408
|
+
},
|
|
409
|
+
className: "absolute inset-0 w-full h-full opacity-0 z-10 cursor-pointer"
|
|
410
|
+
}
|
|
411
|
+
), /* @__PURE__ */ React__default.default.createElement("div", { className: "absolute inset-0 bg-gray-700 rounded-full overflow-hidden" }, /* @__PURE__ */ React__default.default.createElement(
|
|
412
|
+
"div",
|
|
413
|
+
{
|
|
414
|
+
className: "h-full bg-gray-300 group-hover:bg-blue-400 transition-colors",
|
|
415
|
+
style: { width: `${(isMuted ? 0 : volume) * 100}%` }
|
|
416
|
+
}
|
|
417
|
+
)))))), /* @__PURE__ */ React__default.default.createElement(
|
|
418
|
+
"audio",
|
|
419
|
+
{
|
|
420
|
+
ref: audioRef,
|
|
421
|
+
...{ referrerPolicy: "no-referrer" },
|
|
422
|
+
onTimeUpdate: handleTimeUpdate,
|
|
423
|
+
onEnded: () => setIsPlaying(false),
|
|
424
|
+
onPlay: () => setIsPlaying(true),
|
|
425
|
+
onPause: () => setIsPlaying(false),
|
|
426
|
+
style: { display: "none" }
|
|
427
|
+
}
|
|
428
|
+
));
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
Object.defineProperty(exports, "MetingService", {
|
|
432
|
+
enumerable: true,
|
|
433
|
+
get: function () { return chunkEBP7AE6F_js.MetingService; }
|
|
434
|
+
});
|
|
435
|
+
Object.defineProperty(exports, "createLyricHandler", {
|
|
436
|
+
enumerable: true,
|
|
437
|
+
get: function () { return chunkEBP7AE6F_js.createLyricHandler; }
|
|
438
|
+
});
|
|
439
|
+
Object.defineProperty(exports, "createSearchHandler", {
|
|
440
|
+
enumerable: true,
|
|
441
|
+
get: function () { return chunkEBP7AE6F_js.createSearchHandler; }
|
|
442
|
+
});
|
|
443
|
+
Object.defineProperty(exports, "createSongUrlHandler", {
|
|
444
|
+
enumerable: true,
|
|
445
|
+
get: function () { return chunkEBP7AE6F_js.createSongUrlHandler; }
|
|
446
|
+
});
|
|
447
|
+
Object.defineProperty(exports, "musicService", {
|
|
448
|
+
enumerable: true,
|
|
449
|
+
get: function () { return chunkEBP7AE6F_js.musicService; }
|
|
450
|
+
});
|
|
451
|
+
exports.MusicPlayer = MusicPlayer;
|
|
452
|
+
exports.kugouAdapter = kugouAdapter;
|
|
453
|
+
exports.neteaseAdapter = neteaseAdapter;
|
|
454
|
+
exports.tencentAdapter = tencentAdapter;
|
|
455
|
+
exports.useMusic = useMusic;
|
|
456
|
+
exports.xiamiAdapter = xiamiAdapter;
|
|
457
|
+
//# sourceMappingURL=index.js.map
|
|
458
|
+
//# sourceMappingURL=index.js.map
|