|
@@ -232,6 +232,22 @@ function extractSmbRelativePath(song: VideoItem, shareNameOverride?: string): st
|
|
|
return remainder;
|
|
return remainder;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function sanitizePlaybackUrl(rawUrl: string | null | undefined): string {
|
|
|
|
|
+ if (!rawUrl) {
|
|
|
|
|
+ return '';
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ const decoded = decodeURI(rawUrl);
|
|
|
|
|
+ const encoded = encodeURI(decoded);
|
|
|
|
|
+ // encodeURI 不会处理 #,手动转义避免播放器把剩余内容当作片段
|
|
|
|
|
+ return encoded.replace(/#/g, '%23');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ const err = error as Error;
|
|
|
|
|
+ Logger.warn(TAG, `URL 编码失败,使用降级方案: ${err?.message}`);
|
|
|
|
|
+ return rawUrl.replace(/ /g, '%20').replace(/#/g, '%23');
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 异步设置videoUrl的辅助方法,处理WebDAV URL的构建
|
|
* 异步设置videoUrl的辅助方法,处理WebDAV URL的构建
|
|
|
* @param song 歌曲对象
|
|
* @param song 歌曲对象
|
|
@@ -252,14 +268,14 @@ async function setVideoUrlForSong(song: VideoItem): Promise<string> {
|
|
|
const fullUrl = await WebDavUrlUtil.buildFullUrlByAccountId(song.webdav_account_id, relativePath);
|
|
const fullUrl = await WebDavUrlUtil.buildFullUrlByAccountId(song.webdav_account_id, relativePath);
|
|
|
if (fullUrl) {
|
|
if (fullUrl) {
|
|
|
Logger.info(TAG, `WebDAV完整URL构建成功: ${fullUrl}`);
|
|
Logger.info(TAG, `WebDAV完整URL构建成功: ${fullUrl}`);
|
|
|
- return fullUrl.replace(/ /g, '%20');
|
|
|
|
|
|
|
+ return sanitizePlaybackUrl(fullUrl);
|
|
|
}
|
|
}
|
|
|
Logger.error(TAG, `WebDAV URL构建失败,使用原始路径: ${relativePath}`);
|
|
Logger.error(TAG, `WebDAV URL构建失败,使用原始路径: ${relativePath}`);
|
|
|
- return relativePath.replace(/ /g, '%20');
|
|
|
|
|
|
|
+ return sanitizePlaybackUrl(relativePath);
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
Logger.error(TAG, `WebDAV URL构建出错: ${(error as Error).message}`);
|
|
Logger.error(TAG, `WebDAV URL构建出错: ${(error as Error).message}`);
|
|
|
const fallbackPath = song.remote_rel_path || song.filePath;
|
|
const fallbackPath = song.remote_rel_path || song.filePath;
|
|
|
- return fallbackPath.replace(/ /g, '%20');
|
|
|
|
|
|
|
+ return sanitizePlaybackUrl(fallbackPath);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -273,7 +289,7 @@ async function setVideoUrlForSong(song: VideoItem): Promise<string> {
|
|
|
const navSongId = song.remote_rel_path || song.id || song.filePath;
|
|
const navSongId = song.remote_rel_path || song.id || song.filePath;
|
|
|
const streamUrl = await navidromeApi.buildStreamUrl(account, navSongId);
|
|
const streamUrl = await navidromeApi.buildStreamUrl(account, navSongId);
|
|
|
Logger.info(TAG, `Navidrome 流地址构建成功: ${streamUrl}`);
|
|
Logger.info(TAG, `Navidrome 流地址构建成功: ${streamUrl}`);
|
|
|
- return streamUrl.replace(/ /g, '%20');
|
|
|
|
|
|
|
+ return sanitizePlaybackUrl(streamUrl);
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
const err = error as Error;
|
|
const err = error as Error;
|
|
|
Logger.error(TAG, `Navidrome URL构建失败: ${err.message}`);
|
|
Logger.error(TAG, `Navidrome URL构建失败: ${err.message}`);
|
|
@@ -301,7 +317,7 @@ async function setVideoUrlForSong(song: VideoItem): Promise<string> {
|
|
|
|
|
|
|
|
if (isWebDavType(song.type)) {
|
|
if (isWebDavType(song.type)) {
|
|
|
Logger.warn(TAG, `WebDAV歌曲缺少webdav_account_id,使用原始路径: ${song.filePath}`);
|
|
Logger.warn(TAG, `WebDAV歌曲缺少webdav_account_id,使用原始路径: ${song.filePath}`);
|
|
|
- return song.filePath.replace(/ /g, '%20');
|
|
|
|
|
|
|
+ return sanitizePlaybackUrl(song.filePath);
|
|
|
}
|
|
}
|
|
|
if (isSmbType(song.type)) {
|
|
if (isSmbType(song.type)) {
|
|
|
Logger.warn(TAG, `SMB歌曲缺少webdav_account_id,使用原始路径: ${song.filePath}`);
|
|
Logger.warn(TAG, `SMB歌曲缺少webdav_account_id,使用原始路径: ${song.filePath}`);
|
|
@@ -6209,7 +6225,7 @@ export struct LocalMusic {
|
|
|
this.songList = globalVideoList
|
|
this.songList = globalVideoList
|
|
|
this.sonDataSource.pushArrayData(this.songList)
|
|
this.sonDataSource.pushArrayData(this.songList)
|
|
|
if(this.currentSong && isRemoteCloudType(this.currentSong.type!)){
|
|
if(this.currentSong && isRemoteCloudType(this.currentSong.type!)){
|
|
|
- this.videoUrl = this.currentSong.filePath.replace(/ /g, '%20');
|
|
|
|
|
|
|
+ this.videoUrl = sanitizePlaybackUrl(this.currentSong.filePath);
|
|
|
}else{
|
|
}else{
|
|
|
this.videoUrl = this.currentSong.filePath
|
|
this.videoUrl = this.currentSong.filePath
|
|
|
}
|
|
}
|