|
|
@@ -1,6 +1,7 @@
|
|
|
import { http } from '@kit.NetworkKit';
|
|
|
import { WebDavAccount } from '../../viewmodel/WebDavAccount';
|
|
|
import { ServerLogUtil } from '../util/ServerLogUtil';
|
|
|
+import { navidromeApi } from './NavidromeApi';
|
|
|
|
|
|
const TAG = 'heanup NavidromeRestApi';
|
|
|
|
|
|
@@ -96,23 +97,89 @@ export class NavidromeRestApi {
|
|
|
}
|
|
|
|
|
|
async fetchArtistPage(account: WebDavAccount, start: number): Promise<NavidromePagedResponse<NavidromeRestArtist>> {
|
|
|
- return this.fetchPage<NavidromeRestArtist>(account, '/api/artist', start, 'name', 'ASC');
|
|
|
+ return this.fetchPage<NavidromeRestArtist>(account, '/api/artist', start, 'name', 'ASC','albumartist');
|
|
|
}
|
|
|
|
|
|
async fetchAlbumPage(account: WebDavAccount, start: number): Promise<NavidromePagedResponse<NavidromeRestAlbum>> {
|
|
|
return this.fetchPage<NavidromeRestAlbum>(account, '/api/album', start, 'name', 'ASC');
|
|
|
}
|
|
|
|
|
|
- async fetchSongsByArtist(account: WebDavAccount, artistId: string): Promise<NavidromeRestSong[]> {
|
|
|
- return this.fetchSongsWithFilter(account, [new QueryParam('artist_id', artistId)]);
|
|
|
+ async fetchSongsByArtist(account: WebDavAccount, artistId: string, label?: string): Promise<NavidromeRestSong[]> {
|
|
|
+ // 这里判断fetchSongsWithFilter如果返回空数据,则用搜索的接口去搜索艺术家 label就是艺术家名称
|
|
|
+ const songs = await this.fetchSongsWithFilter(account, [new QueryParam('artist_id', artistId)]);
|
|
|
+
|
|
|
+ // 如果通过 artist_id 查询结果为空,且有艺术家名称,则使用搜索接口
|
|
|
+ if ((!songs || songs.length === 0) && label && label.trim().length > 0) {
|
|
|
+ void ServerLogUtil.warn(TAG, `通过 artist_id 查询为空,尝试搜索艺术家: "${label}"`);
|
|
|
+ try {
|
|
|
+ const searchResults = await navidromeApi.searchSongs(account, label.trim(), 500);
|
|
|
+
|
|
|
+ // 将 NavidromeSong 转换为 NavidromeRestSong
|
|
|
+ if (searchResults && searchResults.length > 0) {
|
|
|
+ void ServerLogUtil.info(TAG, `搜索接口返回 ${searchResults.length} 首歌曲`);
|
|
|
+ return this.convertToRestSongs(searchResults);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ const err = error as Error;
|
|
|
+ void ServerLogUtil.error(TAG, `搜索艺术家歌曲失败: ${err.message}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return songs ?? [];
|
|
|
}
|
|
|
|
|
|
- async fetchSongsByAlbum(account: WebDavAccount, albumId: string): Promise<NavidromeRestSong[]> {
|
|
|
- return this.fetchSongsWithFilter(account, [new QueryParam('album_id', albumId)]);
|
|
|
+ async fetchSongsByAlbum(account: WebDavAccount, albumId: string, label?: string): Promise<NavidromeRestSong[]> {
|
|
|
+ // 这里判断fetchSongsWithFilter如果返回空数据,则用搜索的接口去搜索专辑 label就是专辑名称
|
|
|
+ const songs = await this.fetchSongsWithFilter(account, [new QueryParam('album_id', albumId)]);
|
|
|
+
|
|
|
+ // 如果通过 album_id 查询结果为空,且有专辑名称,则使用搜索接口
|
|
|
+ if ((!songs || songs.length === 0) && label && label.trim().length > 0) {
|
|
|
+ void ServerLogUtil.warn(TAG, `通过 album_id 查询为空,尝试搜索专辑: "${label}"`);
|
|
|
+ try {
|
|
|
+
|
|
|
+ const searchResults = await navidromeApi.searchSongs(account, label.trim(), 500);
|
|
|
+
|
|
|
+ // 将 NavidromeSong 转换为 NavidromeRestSong
|
|
|
+ if (searchResults && searchResults.length > 0) {
|
|
|
+ void ServerLogUtil.info(TAG, `搜索接口返回 ${searchResults.length} 首歌曲`);
|
|
|
+ return this.convertToRestSongs(searchResults);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ const err = error as Error;
|
|
|
+ void ServerLogUtil.error(TAG, `搜索专辑歌曲失败: ${err.message}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return songs ?? [];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 将 NavidromeSong 转换为 NavidromeRestSong
|
|
|
+ private convertToRestSongs(songs: import('./NavidromeApi').NavidromeSong[]): NavidromeRestSong[] {
|
|
|
+ return songs.map((song): NavidromeRestSong => ({
|
|
|
+ id: song.id,
|
|
|
+ title: song.title,
|
|
|
+ artist: song.artist,
|
|
|
+ artistId: song.artistId,
|
|
|
+ album: song.album,
|
|
|
+ albumId: song.albumId,
|
|
|
+ duration: song.duration,
|
|
|
+ bitRate: song.bitRate,
|
|
|
+ suffix: song.suffix,
|
|
|
+ size: song.size,
|
|
|
+ createdAt: song.created,
|
|
|
+ genre: song.genre,
|
|
|
+ track: song.track,
|
|
|
+ year: song.year,
|
|
|
+ contentType: song.contentType,
|
|
|
+ coverArt: song.coverArt,
|
|
|
+ coverArtId: song.coverArt,
|
|
|
+ coverArtPath: undefined,
|
|
|
+ embedArtPath: undefined
|
|
|
+ }));
|
|
|
}
|
|
|
|
|
|
private async fetchPage<T extends object>(account: WebDavAccount, path: string,
|
|
|
- start: number, sortField: string, order: 'ASC' | 'DESC'): Promise<NavidromePagedResponse<T>> {
|
|
|
+ start: number, sortField: string, order: 'ASC' | 'DESC',role?:string): Promise<NavidromePagedResponse<T>> {
|
|
|
const end = start + this.PAGE_SIZE;
|
|
|
const params: Array<QueryParam> = [
|
|
|
new QueryParam('_start', `${start}`),
|
|
|
@@ -120,6 +187,9 @@ export class NavidromeRestApi {
|
|
|
new QueryParam('_sort', sortField),
|
|
|
new QueryParam('_order', order)
|
|
|
];
|
|
|
+ if(role){
|
|
|
+ params.push(new QueryParam('_role', role));
|
|
|
+ }
|
|
|
void ServerLogUtil.debug(TAG, `${path} 分页请求: start=${start}, end=${end}`);
|
|
|
const chunk = await this.get<T[]>(account, path, params);
|
|
|
const nextStart = chunk && chunk.length === this.PAGE_SIZE ? end : null;
|
|
|
@@ -221,7 +291,9 @@ export class NavidromeRestApi {
|
|
|
readTimeout: 15000,
|
|
|
expectDataType: http.HttpDataType.STRING,
|
|
|
header: {
|
|
|
- 'Content-Type': 'application/json'
|
|
|
+ 'Content-Type': 'application/json; charset=utf-8',
|
|
|
+ 'Accept': 'application/json; charset=utf-8',
|
|
|
+ 'Accept-Charset': 'utf-8'
|
|
|
},
|
|
|
extraData: JSON.stringify(this.buildLoginBody(username, password))
|
|
|
});
|
|
|
@@ -252,7 +324,8 @@ export class NavidromeRestApi {
|
|
|
return {
|
|
|
'x-nd-authorization': `Bearer ${auth.token}`,
|
|
|
'x-nd-client-unique-id': auth.clientId,
|
|
|
- 'Accept': 'application/json'
|
|
|
+ 'Accept': 'application/json; charset=utf-8',
|
|
|
+ 'Accept-Charset': 'utf-8'
|
|
|
};
|
|
|
}
|
|
|
|