|
@@ -31,6 +31,7 @@ import { http } from '@kit.NetworkKit';
|
|
|
import { JSON } from '@kit.ArkTS';
|
|
import { JSON } from '@kit.ArkTS';
|
|
|
import { simpleCalculateMD5, simplePrecreateFile, simpleUploadFilePart, simpleCreateFile, TaskPrecreateRequest, TaskPrecreateResponse, TaskUploadPartResponse, TaskCreateFileRequest, TaskCreateFileResponse, convertToBaiduPath, simpleLocateUploadServer } from './TaskPoolHelper';
|
|
import { simpleCalculateMD5, simplePrecreateFile, simpleUploadFilePart, simpleCreateFile, TaskPrecreateRequest, TaskPrecreateResponse, TaskUploadPartResponse, TaskCreateFileRequest, TaskCreateFileResponse, convertToBaiduPath, simpleLocateUploadServer } from './TaskPoolHelper';
|
|
|
import { JellyfinApi, JellyfinAlbum, JellyfinArtist, JellyfinSong } from '../network/JellyfinApi';
|
|
import { JellyfinApi, JellyfinAlbum, JellyfinArtist, JellyfinSong } from '../network/JellyfinApi';
|
|
|
|
|
+import { EmbyApi, EmbyAlbum, EmbyArtist, EmbySong } from '../network/EmbyApi';
|
|
|
|
|
|
|
|
const TAG = 'heanup RemoteDriveManager';
|
|
const TAG = 'heanup RemoteDriveManager';
|
|
|
|
|
|
|
@@ -326,6 +327,7 @@ export class RemoteDriveManager {
|
|
|
|
|
|
|
|
private navidromeApi: NavidromeApi = new NavidromeApi();
|
|
private navidromeApi: NavidromeApi = new NavidromeApi();
|
|
|
private jellyfinApi: JellyfinApi = new JellyfinApi();
|
|
private jellyfinApi: JellyfinApi = new JellyfinApi();
|
|
|
|
|
+ private embyApi: EmbyApi = new EmbyApi();
|
|
|
private pathDisplayNames: Map<string, string> = new Map();
|
|
private pathDisplayNames: Map<string, string> = new Map();
|
|
|
private lastAccountId: number | null = null;
|
|
private lastAccountId: number | null = null;
|
|
|
|
|
|
|
@@ -413,6 +415,7 @@ export class RemoteDriveManager {
|
|
|
const smbDomainIndex = resultSet.getColumnIndex('smbDomain');
|
|
const smbDomainIndex = resultSet.getColumnIndex('smbDomain');
|
|
|
const navBaseIndex = resultSet.getColumnIndex('navidromeBasePath');
|
|
const navBaseIndex = resultSet.getColumnIndex('navidromeBasePath');
|
|
|
const jellyfinBaseIndex = resultSet.getColumnIndex('jellyfinBasePath');
|
|
const jellyfinBaseIndex = resultSet.getColumnIndex('jellyfinBasePath');
|
|
|
|
|
+ const embyBaseIndex = resultSet.getColumnIndex('embyBasePath');
|
|
|
const ftpEncodingIndex = resultSet.getColumnIndex('ftpEncoding');
|
|
const ftpEncodingIndex = resultSet.getColumnIndex('ftpEncoding');
|
|
|
const baiduAccessIndex = resultSet.getColumnIndex('baiduAccessToken');
|
|
const baiduAccessIndex = resultSet.getColumnIndex('baiduAccessToken');
|
|
|
const baiduRefreshIndex = resultSet.getColumnIndex('baiduRefreshToken');
|
|
const baiduRefreshIndex = resultSet.getColumnIndex('baiduRefreshToken');
|
|
@@ -435,6 +438,12 @@ export class RemoteDriveManager {
|
|
|
} else {
|
|
} else {
|
|
|
account.jellyfinBasePath = '';
|
|
account.jellyfinBasePath = '';
|
|
|
}
|
|
}
|
|
|
|
|
+ if (embyBaseIndex >= 0) {
|
|
|
|
|
+ const stored = resultSet.getString(embyBaseIndex);
|
|
|
|
|
+ account.embyBasePath = stored ?? '';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ account.embyBasePath = '';
|
|
|
|
|
+ }
|
|
|
if (ftpEncodingIndex >= 0) {
|
|
if (ftpEncodingIndex >= 0) {
|
|
|
const encodingValue = resultSet.getString(ftpEncodingIndex);
|
|
const encodingValue = resultSet.getString(ftpEncodingIndex);
|
|
|
account.ftpEncoding = encodingValue && encodingValue.length > 0 ? encodingValue : 'utf-8';
|
|
account.ftpEncoding = encodingValue && encodingValue.length > 0 ? encodingValue : 'utf-8';
|
|
@@ -586,6 +595,27 @@ export class RemoteDriveManager {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if (currentSong.type === CommonConstants.TYPE_EMBY) {
|
|
|
|
|
+ if (currentSong.webdav_account_id) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const account = await this.getWebDavAccountById(currentSong.webdav_account_id);
|
|
|
|
|
+ if (account) {
|
|
|
|
|
+ const authHeaders = await this.embyApi.getAuthHeaders(account);
|
|
|
|
|
+ authHeaders.forEach((value, key) => {
|
|
|
|
|
+ headers.set(key, value);
|
|
|
|
|
+ });
|
|
|
|
|
+ Logger.info(TAG, `Emby 认证头已构建`);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Logger.warn(TAG, `Emby 账号不可用: ${currentSong.webdav_account_id}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Logger.error(TAG, `Emby 认证头构建失败: ${(error as Error).message}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Logger.warn(TAG, 'Emby歌曲缺少webdav_account_id,无法构建认证头');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (currentSong.type === CommonConstants.TYPE_BAIDU) {
|
|
if (currentSong.type === CommonConstants.TYPE_BAIDU) {
|
|
|
headers.set('User-Agent', BaiduConstants.STREAMING_USER_AGENT);
|
|
headers.set('User-Agent', BaiduConstants.STREAMING_USER_AGENT);
|
|
|
headers.set('Host', 'pan.baidu.com');
|
|
headers.set('Host', 'pan.baidu.com');
|
|
@@ -617,7 +647,7 @@ export class RemoteDriveManager {
|
|
|
// 查询所有TYPE_WEBDAV且webdav_account_id为空或null的记录
|
|
// 查询所有TYPE_WEBDAV且webdav_account_id为空或null的记录
|
|
|
const querySql = `
|
|
const querySql = `
|
|
|
SELECT id, filePath FROM mediaTable
|
|
SELECT id, filePath FROM mediaTable
|
|
|
- WHERE mtype IN (${CommonConstants.TYPE_WEBDAV}, ${CommonConstants.TYPE_SMB}, ${CommonConstants.TYPE_FTP}, ${CommonConstants.TYPE_BAIDU}, ${CommonConstants.TYPE_JELLYFIN})
|
|
|
|
|
|
|
+ WHERE mtype IN (${CommonConstants.TYPE_WEBDAV}, ${CommonConstants.TYPE_SMB}, ${CommonConstants.TYPE_FTP}, ${CommonConstants.TYPE_BAIDU}, ${CommonConstants.TYPE_JELLYFIN}, ${CommonConstants.TYPE_EMBY})
|
|
|
AND (webdav_account_id IS NULL OR webdav_account_id = '')
|
|
AND (webdav_account_id IS NULL OR webdav_account_id = '')
|
|
|
`;
|
|
`;
|
|
|
|
|
|
|
@@ -777,6 +807,7 @@ export class RemoteDriveManager {
|
|
|
smbDomain TEXT,
|
|
smbDomain TEXT,
|
|
|
navidromeBasePath TEXT,
|
|
navidromeBasePath TEXT,
|
|
|
jellyfinBasePath TEXT,
|
|
jellyfinBasePath TEXT,
|
|
|
|
|
+ embyBasePath TEXT,
|
|
|
ftpEncoding TEXT
|
|
ftpEncoding TEXT
|
|
|
)`;
|
|
)`;
|
|
|
|
|
|
|
@@ -849,6 +880,14 @@ export class RemoteDriveManager {
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ Logger.info(TAG, '尝试添加embyBasePath字段...');
|
|
|
|
|
+ const addEmbyBaseSql = `ALTER TABLE ${this.webDavTable} ADD COLUMN embyBasePath TEXT`;
|
|
|
|
|
+ await this.dataBaseUtil.executeSql(addEmbyBaseSql);
|
|
|
|
|
+ Logger.info(TAG, 'embyBasePath字段添加成功');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
Logger.info(TAG, '尝试添加ftpEncoding字段...');
|
|
Logger.info(TAG, '尝试添加ftpEncoding字段...');
|
|
|
const addFtpEncodingSql = `ALTER TABLE ${this.webDavTable} ADD COLUMN ftpEncoding TEXT`;
|
|
const addFtpEncodingSql = `ALTER TABLE ${this.webDavTable} ADD COLUMN ftpEncoding TEXT`;
|
|
@@ -877,7 +916,7 @@ export class RemoteDriveManager {
|
|
|
// 先查询基础字段(确保这些字段在旧版本中存在)
|
|
// 先查询基础字段(确保这些字段在旧版本中存在)
|
|
|
const baseColumns = ['id', 'name', 'isActivate', 'host', 'localHost', 'isUseLocalHost',
|
|
const baseColumns = ['id', 'name', 'isActivate', 'host', 'localHost', 'isUseLocalHost',
|
|
|
'port', 'filepath', 'imageFilePath', 'lyricFilePath', 'uploadFilePath',
|
|
'port', 'filepath', 'imageFilePath', 'lyricFilePath', 'uploadFilePath',
|
|
|
- 'account', 'password', 'enableHttps', 'coverPath', 'webType', 'smbShare', 'smbDomain', 'navidromeBasePath', 'jellyfinBasePath', 'ftpEncoding',
|
|
|
|
|
|
|
+ 'account', 'password', 'enableHttps', 'coverPath', 'webType', 'smbShare', 'smbDomain', 'navidromeBasePath', 'jellyfinBasePath', 'embyBasePath', 'ftpEncoding',
|
|
|
'baiduAccessToken', 'baiduRefreshToken', 'baiduTokenExpiresAt'];
|
|
'baiduAccessToken', 'baiduRefreshToken', 'baiduTokenExpiresAt'];
|
|
|
|
|
|
|
|
const resultSet = await this.dataBaseUtil.queryData(this.webDavTable, baseColumns, predicates);
|
|
const resultSet = await this.dataBaseUtil.queryData(this.webDavTable, baseColumns, predicates);
|
|
@@ -928,6 +967,13 @@ export class RemoteDriveManager {
|
|
|
} else {
|
|
} else {
|
|
|
account.jellyfinBasePath = '';
|
|
account.jellyfinBasePath = '';
|
|
|
}
|
|
}
|
|
|
|
|
+ const embyBaseIndex = resultSet.getColumnIndex('embyBasePath');
|
|
|
|
|
+ if (embyBaseIndex >= 0) {
|
|
|
|
|
+ const stored = resultSet.getString(embyBaseIndex);
|
|
|
|
|
+ account.embyBasePath = stored ?? '';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ account.embyBasePath = '';
|
|
|
|
|
+ }
|
|
|
const ftpEncodingIndex = resultSet.getColumnIndex('ftpEncoding');
|
|
const ftpEncodingIndex = resultSet.getColumnIndex('ftpEncoding');
|
|
|
if (ftpEncodingIndex >= 0) {
|
|
if (ftpEncodingIndex >= 0) {
|
|
|
const encodingValue = resultSet.getString(ftpEncodingIndex);
|
|
const encodingValue = resultSet.getString(ftpEncodingIndex);
|
|
@@ -982,6 +1028,7 @@ export class RemoteDriveManager {
|
|
|
smbDomain: string = '',
|
|
smbDomain: string = '',
|
|
|
navidromeBasePath: string = '/rest',
|
|
navidromeBasePath: string = '/rest',
|
|
|
jellyfinBasePath: string = '',
|
|
jellyfinBasePath: string = '',
|
|
|
|
|
+ embyBasePath: string = '',
|
|
|
ftpEncoding: string = 'utf-8',
|
|
ftpEncoding: string = 'utf-8',
|
|
|
baiduAccessToken: string = '',
|
|
baiduAccessToken: string = '',
|
|
|
baiduRefreshToken: string = '',
|
|
baiduRefreshToken: string = '',
|
|
@@ -1008,6 +1055,7 @@ export class RemoteDriveManager {
|
|
|
'smbDomain': smbDomain,
|
|
'smbDomain': smbDomain,
|
|
|
'navidromeBasePath': navidromeBasePath,
|
|
'navidromeBasePath': navidromeBasePath,
|
|
|
'jellyfinBasePath': jellyfinBasePath,
|
|
'jellyfinBasePath': jellyfinBasePath,
|
|
|
|
|
+ 'embyBasePath': embyBasePath,
|
|
|
'ftpEncoding': ftpEncoding,
|
|
'ftpEncoding': ftpEncoding,
|
|
|
'baiduAccessToken': baiduAccessToken,
|
|
'baiduAccessToken': baiduAccessToken,
|
|
|
'baiduRefreshToken': baiduRefreshToken,
|
|
'baiduRefreshToken': baiduRefreshToken,
|
|
@@ -1050,6 +1098,7 @@ export class RemoteDriveManager {
|
|
|
'smbDomain': account.smbDomain,
|
|
'smbDomain': account.smbDomain,
|
|
|
'navidromeBasePath': account.navidromeBasePath,
|
|
'navidromeBasePath': account.navidromeBasePath,
|
|
|
'jellyfinBasePath': account.jellyfinBasePath,
|
|
'jellyfinBasePath': account.jellyfinBasePath,
|
|
|
|
|
+ 'embyBasePath': account.embyBasePath,
|
|
|
'ftpEncoding': account.ftpEncoding,
|
|
'ftpEncoding': account.ftpEncoding,
|
|
|
'baiduAccessToken': account.baiduAccessToken,
|
|
'baiduAccessToken': account.baiduAccessToken,
|
|
|
'baiduRefreshToken': account.baiduRefreshToken,
|
|
'baiduRefreshToken': account.baiduRefreshToken,
|
|
@@ -1150,6 +1199,8 @@ export class RemoteDriveManager {
|
|
|
await this.loadNavidromeFiles(account, normalizedFullPath);
|
|
await this.loadNavidromeFiles(account, normalizedFullPath);
|
|
|
} else if (account.webType === RemoteDriveType.Jellyfin) {
|
|
} else if (account.webType === RemoteDriveType.Jellyfin) {
|
|
|
await this.loadJellyfinFiles(account, normalizedFullPath);
|
|
await this.loadJellyfinFiles(account, normalizedFullPath);
|
|
|
|
|
+ } else if (account.webType === RemoteDriveType.Emby) {
|
|
|
|
|
+ await this.loadEmbyFiles(account, normalizedFullPath);
|
|
|
} else if (account.webType === RemoteDriveType.Ftp) {
|
|
} else if (account.webType === RemoteDriveType.Ftp) {
|
|
|
await this.loadFtpFiles(account, normalizedFullPath);
|
|
await this.loadFtpFiles(account, normalizedFullPath);
|
|
|
} else if (account.webType === RemoteDriveType.Baidu) {
|
|
} else if (account.webType === RemoteDriveType.Baidu) {
|
|
@@ -1730,6 +1781,64 @@ export class RemoteDriveManager {
|
|
|
throw new Error(`不支持的Jellyfin路径: ${fullPath}`);
|
|
throw new Error(`不支持的Jellyfin路径: ${fullPath}`);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private async loadEmbyFiles(account: WebDavAccount, fullPath: string): Promise<void> {
|
|
|
|
|
+ const normalized = this.normalizeFullPath(fullPath);
|
|
|
|
|
+ this.registerPathLabel('/', '根目录');
|
|
|
|
|
+ const segments = normalized.split('/').filter(part => part.length > 0);
|
|
|
|
|
+ void ServerLogUtil.info(TAG, `浏览 Emby 路径: ${normalized} account=${ServerLogUtil.sanitizeAccount(account)}`);
|
|
|
|
|
+
|
|
|
|
|
+ if (normalized === '/' || segments.length === 0) {
|
|
|
|
|
+ const artists: EmbyArtist[] = await this.embyApi.getArtists(account);
|
|
|
|
|
+ artists.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
|
+ this.webDavFiles = artists.map(artist => {
|
|
|
|
|
+ const info = this.createEmbyDirectory(artist.name, `/artist/${artist.id}`);
|
|
|
|
|
+ this.registerPathLabel(info.href, artist.name);
|
|
|
|
|
+ return info;
|
|
|
|
|
+ });
|
|
|
|
|
+ this.webDavSongs = [];
|
|
|
|
|
+ void ServerLogUtil.info(TAG, `获取 Emby 艺术家列表,数量: ${artists.length}`);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (segments[0] === 'artist' && segments[1]) {
|
|
|
|
|
+ const artistId = segments[1];
|
|
|
|
|
+ const albums: EmbyAlbum[] = await this.embyApi.getArtistAlbums(account, artistId);
|
|
|
|
|
+ const artistLabel = this.pathDisplayNames.get(`/artist/${artistId}`) ?? (albums[0]?.artist ?? '艺术家');
|
|
|
|
|
+ if (artistLabel) {
|
|
|
|
|
+ this.registerPathLabel(`/artist/${artistId}`, artistLabel);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.webDavFiles = albums.map(album => {
|
|
|
|
|
+ const info = this.createEmbyDirectory(album.name, `/album/${album.id}`);
|
|
|
|
|
+ this.registerPathLabel(info.href, album.name);
|
|
|
|
|
+ return info;
|
|
|
|
|
+ });
|
|
|
|
|
+ this.webDavSongs = [];
|
|
|
|
|
+ void ServerLogUtil.info(TAG, `进入 Emby 艺术家 ${artistLabel},专辑数: ${albums.length}`);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (segments[0] === 'album' && segments[1]) {
|
|
|
|
|
+ const albumId = segments[1];
|
|
|
|
|
+ const album = await this.embyApi.getAlbum(account, albumId);
|
|
|
|
|
+ const albumName = album?.name ?? this.pathDisplayNames.get(`/album/${albumId}`) ?? '专辑';
|
|
|
|
|
+ this.registerPathLabel(`/album/${albumId}`, albumName);
|
|
|
|
|
+ const songs = await this.embyApi.getAlbumSongs(account, albumId);
|
|
|
|
|
+ let coverUrl: string | undefined = undefined;
|
|
|
|
|
+ try {
|
|
|
|
|
+ coverUrl = await this.embyApi.buildPrimaryImageUrl(account, album?.id ?? albumId);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Logger.warn(TAG, `Emby 专辑封面获取失败: ${(error as Error).message}`);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.webDavFiles = songs.map(song => this.createEmbySongFileInfo(song, albumId));
|
|
|
|
|
+ this.webDavSongs = songs.map(song => this.buildEmbyVideoItem(song, account, album, coverUrl));
|
|
|
|
|
+ await this.enrichSongsWithDatabase(this.webDavSongs);
|
|
|
|
|
+ void ServerLogUtil.info(TAG, `进入 Emby 专辑 ${albumName},歌曲数: ${songs.length}`);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ throw new Error(`不支持的Emby路径: ${fullPath}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private async enrichSongsWithDatabase(videoItems: VideoItem[]): Promise<void> {
|
|
private async enrichSongsWithDatabase(videoItems: VideoItem[]): Promise<void> {
|
|
|
if (!this.context || !videoItems || videoItems.length === 0) {
|
|
if (!this.context || !videoItems || videoItems.length === 0) {
|
|
|
return;
|
|
return;
|
|
@@ -2252,6 +2361,59 @@ export class RemoteDriveManager {
|
|
|
return videoItem;
|
|
return videoItem;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private createEmbyDirectory(name: string, href: string): FileInfo {
|
|
|
|
|
+ const info = new FileInfo('', name, 0, Date.now());
|
|
|
|
|
+ info.fileName = name;
|
|
|
|
|
+ info.href = this.normalizeFullPath(href);
|
|
|
|
|
+ info.isDirectory = true;
|
|
|
|
|
+ return info;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private createEmbySongFileInfo(song: EmbySong, albumId: string): FileInfo {
|
|
|
|
|
+ const displayName = song.title ?? '未知曲目';
|
|
|
|
|
+ const info = new FileInfo('', displayName, song.size ?? 0, Date.now());
|
|
|
|
|
+ info.fileName = `${displayName}${song.suffix ? '.' + song.suffix : ''}`;
|
|
|
|
|
+ info.href = this.normalizeFullPath(`/album/${albumId}/${song.id}`);
|
|
|
|
|
+ info.isDirectory = false;
|
|
|
|
|
+ info.contentLength = song.size ?? 0;
|
|
|
|
|
+ return info;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private buildEmbyVideoItem(song: EmbySong, account: WebDavAccount, album?: EmbyAlbum | null, coverUrl?: string): VideoItem {
|
|
|
|
|
+ const title = song.title ?? '未知曲目';
|
|
|
|
|
+ const fileName = `${title}${song.suffix ? '.' + song.suffix : ''}`;
|
|
|
|
|
+ const videoItem = new VideoItem(
|
|
|
|
|
+ title,
|
|
|
|
|
+ song.id,
|
|
|
|
|
+ `emby://${account.id ?? 0}/${song.id}`,
|
|
|
|
|
+ CommonConstants.TYPE_EMBY,
|
|
|
|
|
+ song.size ?? 0,
|
|
|
|
|
+ Utility.getFormatDateStr(Date.now(), 'yyyy-MM-dd HH:mm'),
|
|
|
|
|
+ Utility.formatFSize(song.size ?? 0),
|
|
|
|
|
+ undefined,
|
|
|
|
|
+ song.artist ?? album?.artist ?? Constants.UNKNOWN_ARTIST,
|
|
|
|
|
+ album?.name ?? song.album,
|
|
|
|
|
+ fileName
|
|
|
|
|
+ );
|
|
|
|
|
+ videoItem.size = Utility.formatFSize(song.size ?? 0);
|
|
|
|
|
+ videoItem.webdav_account_id = account.id?.toString();
|
|
|
|
|
+ videoItem.remote_rel_path = song.id;
|
|
|
|
|
+ videoItem.bit_rate = song.bitRate ? song.bitRate.toString() : undefined;
|
|
|
|
|
+ videoItem.sampleRate = song.sampleRate ? song.sampleRate.toString() : undefined;
|
|
|
|
|
+ videoItem.pixelMapPath = coverUrl;
|
|
|
|
|
+ if (song.track !== undefined && song.track !== null) {
|
|
|
|
|
+ videoItem.track = song.track.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (song.year !== undefined && song.year !== null) {
|
|
|
|
|
+ videoItem.year = song.year.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+ // 存储歌词索引
|
|
|
|
|
+ if (song.lyricIndex !== undefined && song.lyricIndex !== null) {
|
|
|
|
|
+ videoItem.lyricIndex = song.lyricIndex;
|
|
|
|
|
+ }
|
|
|
|
|
+ return videoItem;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private registerPathLabel(path: string, label: string): void {
|
|
private registerPathLabel(path: string, label: string): void {
|
|
|
if (!path || label === undefined) {
|
|
if (!path || label === undefined) {
|
|
|
return;
|
|
return;
|