ソースを参照

修复webdav一些问题

chendeben 9 ヶ月 前
コミット
ff8cdd5a69

+ 1 - 0
entry/src/main/ets/common/constants/CommonConstants.ets

@@ -199,6 +199,7 @@ export class CommonConstants {
    * Network video ID.
    */
   static readonly TYPE_INTERNET: number = 1;//网络视频
+  static readonly TYPE_WEBDAV: number = 3;//webdav文件
 
   static readonly TYPE_LOCK: number = 200;//私密视频
   static readonly TYPE_LIKE: number = 201;//我收藏的视频

+ 0 - 10
entry/src/main/ets/common/util/RcpSocketUtil.ets

@@ -579,7 +579,6 @@ export class RcpSocket {
 
       // 获取完整的href路径
       const fullHref = this.decodeXMLEntities(decodeURI(hrefMatch[1]));
-      console.info(UtilName, 'testTag', '提取到href:', fullHref);
 
       // 尝试提取 <D:displayname> 作为文件名
       let displayNameMatch = responseBlock.match(/<D:displayname>(.*?)<\/D:displayname>/i);
@@ -606,7 +605,6 @@ export class RcpSocket {
             name = withoutTrailingSlash.substring(secondLastSlash + 1) + '/';
           }
         }
-        console.info(UtilName, 'testTag', '从href提取文件名:', name);
       }
 
       // 跳过根目录本身
@@ -634,17 +632,9 @@ export class RcpSocket {
       fileInfo.contentLength = size;
       // 判断是否为文件夹(以/结尾或没有contentLength)
       fileInfo.isDirectory = fullHref.endsWith('/') || size === 0;
-
-      console.info(UtilName, 'testTag', '创建FileInfo对象:', 'name=', name, 'fileName=', fileInfo.fileName, 'href=', fileInfo.href, 'isDirectory=', fileInfo.isDirectory.toString());
       filesInfo.push(fileInfo);
     }
 
-    // 验证返回前的文件信息
-    for (let i = 0; i < filesInfo.length; i++) {
-      const file = filesInfo[i];
-      console.info(UtilName, 'testTag', '返回前检查', i.toString(), ': fileName=', file.fileName, ', name=', file.name);
-    }
-
     return filesInfo;
   }
 

+ 34 - 43
entry/src/main/ets/common/util/WebdavManager.ets

@@ -1,11 +1,10 @@
 // WebdavManager - WebDAV管理器(简化版)
-import { Song } from '../../viewmodel/Song';
+import { VideoItem } from '../../viewmodel/VideoItem';
 import { common } from '@kit.AbilityKit';
 import { FileInfo } from '../../viewmodel/FileInfo';
 import FileManager, { merge2paths } from './FileManager';
 import { BusinessError } from '@kit.BasicServicesKit';
 import { WebdavManagerStates } from '../enums/WebdavManagerStates';
-import { SongType } from '../enums/SongType';
 import { RcpSocket } from './RcpSocketUtil';
 import { DataBaseUtil } from './DataBaseUtil';
 import { WebDavAccount } from '../../viewmodel/WebDavAccount';
@@ -13,6 +12,7 @@ import { relationalStore } from '@kit.ArkData';
 import { Constants } from '../../Constants';
 import Logger from './Logger';
 import { buffer } from '@kit.ArkTS';
+import { CommonConstants } from '../constants/CommonConstants';
 
 const TAG = 'heanup WebdavManager';
 
@@ -29,7 +29,7 @@ export interface StreamAuthInfo {
 }
 
 export interface TransferTask {
-  song: Song;
+  song: VideoItem;
   account: WebDavAccount;
 }
 
@@ -53,7 +53,7 @@ export class WebdavManager {
   public totalSize: number = 0;
 
   public webDavAccounts: WebDavAccount[] = [];
-  public webDavSongs: Song[] = [];
+  public webDavSongs: VideoItem[] = [];
   public webDavFiles: FileInfo[] = [];  // 当前目录的所有文件(包括文件夹)
 
   // 路径导航
@@ -366,7 +366,7 @@ export class WebdavManager {
           folderCount++;
         } else if (this.isAudioFile(fileName)) {
           audioCount++;
-          const song = this.fileInfoToSong(file, account);
+          const song = this.fileInfoToVideoItem(file, account);
           this.webDavSongs.push(song);
         }
       }
@@ -426,7 +426,7 @@ export class WebdavManager {
           folderCount++;
         } else if (this.isAudioFile(fileName)) {
           audioCount++;
-          const song = this.fileInfoToSong(file, account);
+          const song = this.fileInfoToVideoItem(file, account);
           this.webDavSongs.push(song);
         }
       }
@@ -438,29 +438,34 @@ export class WebdavManager {
     }
   }
 
-  // 将FileInfo转换为Song
-  private fileInfoToSong(fileInfo: FileInfo, account: WebDavAccount): Song {
-    const song = new Song(-1, '');
-    song.title = this.getFileNameWithoutExtension(fileInfo.fileName);
-    song.artist = Constants.UNKNOWN_ARTIST;
-    song.name = fileInfo.fileName;
-
+  // 将FileInfo转换为VideoItem
+  private fileInfoToVideoItem(fileInfo: FileInfo, account: WebDavAccount): VideoItem {
     // 构建安全的WebDAV URL(不包含认证信息)
     const protocol = account.enableHttps ? 'https' : 'http';
     const host = account.isUseLocalHost ? account.localHost : account.host;
     const port = account.port;
 
     // 构建基础URL,不包含认证信息
-    song.src = `${protocol}://${host}:${port}${fileInfo.href}`;
-
-    song.songType = SongType.WebDav;
-    song.webDavAccountId = account.id;
-    song.webFilePath = fileInfo.href;
-    song.fileSize = fileInfo.contentLength;
-    song.time = fileInfo.time;
-    song.img = Constants.COMMON_SONG_DEFAULT_IMAGE;
-
-    return song;
+    const filePath = `${protocol}://${host}:${port}${fileInfo.href}`;
+
+    // 创建VideoItem对象
+    // 构造函数签名: (name, id, filePath, type, videoSize, cTime, pixelMap?, size?, pixelMapPath?, artist?, album?, fileName?, lastPlayed?)
+    const videoItem = new VideoItem(
+      this.getFileNameWithoutExtension(fileInfo.fileName), // name: 歌曲名
+      '', // id: 空字符串,WebDAV文件无本地ID
+      filePath, // filePath: 文件路径
+      CommonConstants.TYPE_WEBDAV, // type: WebDAV类型
+      fileInfo.contentLength, // videoSize: 文件大小
+      fileInfo.time.toString(), // cTime: 修改时间
+      undefined, // pixelMap
+      undefined, // size
+      undefined, // pixelMapPath: 对于WebDAV歌曲不设置图片路径
+      Constants.UNKNOWN_ARTIST, // artist: 艺术家
+      undefined, // album: 专辑
+      fileInfo.fileName // fileName: 真实文件名
+    );
+
+    return videoItem;
   }
 
   // 判断是否为音频文件
@@ -615,10 +620,10 @@ export class WebdavManager {
   // ==================== 下载队列管理 ====================
 
   // 添加到下载队列
-  public addToDownloadQueue(song: Song, account: WebDavAccount): void {
+  public addToDownloadQueue(song: VideoItem, account: WebDavAccount): void {
     const task: TransferTask = { song, account };
     this.downloadQueue.push(task);
-    Logger.info(TAG, '添加到下载队列:', song.title);
+    Logger.info(TAG, '添加到下载队列:', song.name);
     this.notifyObservers(WebdavManagerStates.DownloadQueueChanged);
   }
 
@@ -627,7 +632,7 @@ export class WebdavManager {
     if (index >= 0 && index < this.downloadQueue.length) {
       const task = this.downloadQueue[index];
       this.downloadQueue.splice(index, 1);
-      Logger.info(TAG, '从下载队列移除:', task.song.title);
+      Logger.info(TAG, '从下载队列移除:', task.song.name);
       this.notifyObservers(WebdavManagerStates.DownloadQueueChanged);
     }
   }
@@ -642,10 +647,10 @@ export class WebdavManager {
   // ==================== 上传队列管理 ====================
 
   // 添加到上传队列
-  public addToUploadQueue(song: Song, account: WebDavAccount): void {
+  public addToUploadQueue(song: VideoItem, account: WebDavAccount): void {
     const task: TransferTask = { song, account };
     this.uploadQueue.push(task);
-    Logger.info(TAG, '添加到上传队列:', song.title);
+    Logger.info(TAG, '添加到上传队列:', song.name);
     this.notifyObservers(WebdavManagerStates.UploadQueueChanged);
   }
 
@@ -654,7 +659,7 @@ export class WebdavManager {
     if (index >= 0 && index < this.uploadQueue.length) {
       const task = this.uploadQueue[index];
       this.uploadQueue.splice(index, 1);
-      Logger.info(TAG, '从上传队列移除:', task.song.title);
+      Logger.info(TAG, '从上传队列移除:', task.song.name);
       this.notifyObservers(WebdavManagerStates.UploadQueueChanged);
     }
   }
@@ -706,18 +711,4 @@ export class WebdavManager {
     }
     return null;
   }
-
-  // 获取安全的播放URL(不包含认证信息)
-  public getSecurePlayUrl(song: Song): string | null {
-    if (song.songType !== SongType.WebDav || !song.webDavAccountId) {
-      return song.src;
-    }
-
-    const authInfo = this.getWebDavAuthHeaders(song.webDavAccountId);
-    if (!authInfo) {
-      return null;
-    }
-
-    return authInfo.url + song.webFilePath;
-  }
 }

+ 18 - 33
entry/src/main/ets/pages/WebDavMainPage.ets

@@ -56,8 +56,8 @@ export struct WebDavMainPage {
   @State webdavManager: WebdavManager = WebdavManager.getInstance();
   @State accounts: WebDavAccount[] = [];
   @Prop @Watch('onSwitchAccount') selectedAccount: WebDavAccount;
-  @State songs: Song[] = [];
-  @State  dataSource:LazyDataSource<Song> = new LazyDataSource(this.songs)
+  @State songs: VideoItem[] = [];
+  @State  dataSource:LazyDataSource<VideoItem> = new LazyDataSource(this.songs)
   @Link mType: number;
   @Link offsetX: number;
   @Link isShowDrawer: boolean;
@@ -75,6 +75,11 @@ export struct WebDavMainPage {
     this.songs = [];
     this.visibleFoldersState = [];
     this.updateListData(this.songs)
+    
+    // 注意:不再需要清空全局上下文,因为LocalMusic已改用实例变量缓存
+    // 当新账户加载时,新的认证信息会自动覆盖旧的
+    Logger.info(TAG, 'heanup 切换账户,准备加载新账户文件');
+    
     this.isLoading = true;
     await this.webdavManager.loadFilesInfoFromAccount(this.selectedAccount)
       .catch((error: Error) => {
@@ -84,7 +89,7 @@ export struct WebDavMainPage {
     this.breadcrumbs = this.webdavManager.getBreadcrumbs();
   }
 
-  updateListData(mList:Array<Song>){
+  updateListData(mList:Array<VideoItem>){
     this.dataSource.pushArrayData(mList)
   }
 
@@ -269,30 +274,11 @@ export struct WebDavMainPage {
 
 
 
-  // 将Song转换为VideoItem
-  private convertSongToVideoItem(song: Song, index: number): VideoItem {
-    const videoItem = new VideoItem(
-      song.title,
-      index.toString(),
-      song.src, // WebDAV URL作为文件路径
-      CommonConstants.TYPE_INTERNET, // 使用网络类型
-      song.fileSize,
-      song.time.toString(),
-      undefined, // pixelMap
-      undefined, // size
-      typeof song.img === 'string' ? song.img : undefined, // pixelMapPath
-      song.artist,
-      undefined, // album
-      song.name // fileName
-    );
-    return videoItem;
-  }
-
   // 播放WebDAV歌曲
-  private playSong(song: Song, index: number): void {
+  private playSong(song: VideoItem, index: number): void {
     try {
       Logger.info(TAG, `heanup === WebDAV playSong 方法被调用 ===`);
-      Logger.info(TAG, 'heanup 播放指定歌曲: ' + song.title + ', 索引: ' + index);
+      Logger.info(TAG, 'heanup 播放指定歌曲: ' + song.name + ', 索引: ' + index);
       Logger.info(TAG, 'heanup 歌曲列表长度: ' + this.songs.length);
 
       // 保存WebDAV认证信息到全局上下文(用于播放器认证)
@@ -310,13 +296,12 @@ export struct WebDavMainPage {
         Logger.info(TAG, 'heanup 已保存WebDAV认证信息到全局上下文'+JSON.stringify(webDavAuthInfo));
       }
 
-      // 将当前歌曲列表转换为VideoItem数组
-      const videoItems: VideoItem[] = [];
+      // 直接使用当前的VideoItem数组
+      const videoItems: VideoItem[] = this.songs;
       const songFilePaths: string[] = [];
 
       for (let i = 0; i < this.songs.length; i++) {
-        const item = this.convertSongToVideoItem(this.songs[i], i);
-        videoItems.push(item);
+        const item = this.songs[i];
         songFilePaths.push(item.filePath); // 使用filePath作为文件路径
       }
 
@@ -585,7 +570,7 @@ export struct WebDavMainPage {
           })
 
           // 显示歌曲
-          LazyForEach(this.dataSource, (song: Song, index: number) => {
+          LazyForEach(this.dataSource, (song: VideoItem, index: number) => {
             ListItem() {
               this.buildSongItem(song, index)
             }
@@ -658,12 +643,12 @@ export struct WebDavMainPage {
 
   // 歌曲列表项
   @Builder
-  buildSongItem(song: Song, index: number) {
+  buildSongItem(song: VideoItem, index: number) {
     Button({ type: ButtonType.Normal, stateEffect: false }) {
       Row({ space: 12 }) {
         // 序号
         // 歌曲封面
-        Image(song.img)
+        Image(song.pixelMap)
           .width(48)
           .height(48)
           .borderRadius(4)
@@ -673,13 +658,13 @@ export struct WebDavMainPage {
 
         // 歌曲信息
         Column({ space: 4 }) {
-          Text(decodeUrlEncodedString(song.title))
+          Text(decodeUrlEncodedString(song.name))
             .fontSize(15)
             .fontColor($r('app.color.index_tab_font_color'))
             .maxLines(1)
             .textOverflow({ overflow: TextOverflow.Ellipsis })
 
-          Text(decodeUrlEncodedString(song.artist))
+          Text(decodeUrlEncodedString(song.artist||""))
             .fontSize(13)
             .fontColor($r('app.color.index_tab_font_color'))
             .opacity(0.6)

+ 79 - 165
entry/src/main/ets/view/LocalMusic.ets

@@ -284,6 +284,9 @@ export struct LocalMusic {
   @StorageProp('currentColorMode') @Watch('onColorModeChange') currentMode: number =
     ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET;
 
+  // WebDAV认证信息缓存(作为实例变量,比全局上下文更可靠)
+  private currentWebDavAuthInfo: WebDavAuthInfo | null = null;
+
   //瀑布流的列数横竖屏动态切换
   onIsLandscapeChange() {
      if(this.isLandscape){
@@ -610,7 +613,6 @@ export struct LocalMusic {
     this.getSortedFiles(this.currentPath)
 
   }
-
   // 组件生命周期
   aboutToAppear() {
     // 初始化 PlaylistTable
@@ -800,7 +802,6 @@ export struct LocalMusic {
     });
 
   }
-
   //载入媒体库,艺术家,专辑等缓存
   initLoadCache(){
     this.mediaKuCount = PreferencesUtil.getNumberSync('mediaKuCount', 0)
@@ -1141,7 +1142,7 @@ export struct LocalMusic {
           this.isFirstStartPlay = false
           this.currentSong = this.songList[0]
         }
-        this.videoUrl = this.currentSong.filePath
+        this.videoUrl = this.currentSong.filePath.replace(/ /g, '%20');
         this.name = this.currentSong.name
         this.cover = this.currentSong.pixelMapPath
         AppStorage.setOrCreate('currentSong',this.currentSong) ;
@@ -1397,7 +1398,6 @@ export struct LocalMusic {
     }
     return mediaItems;
   }
-
   updateListData(mList: Array<VideoItem>, noSort?: boolean) {
 
     animateTo({ duration: 666 }, () => {
@@ -1524,7 +1524,6 @@ export struct LocalMusic {
       this.isHasDir = false
     }
   }
-
   showRankDialog() {
     if ((this.modeType === 2&&!this.isCanBack) || (this.modeType === 3&&!this.isCanBack)) {
       //动作面板
@@ -2135,7 +2134,6 @@ export struct LocalMusic {
 
 
   }
-
   //复制 到另个文件夹的对话框
 
   showCopyDialog(isCurrent: boolean, item: VideoItem, index: number, id: string) {
@@ -2880,7 +2878,6 @@ export struct LocalMusic {
     }
 
   }
-
   @Builder
   private DirItem(item: VideoItem, index?: number) {
     Button({ type: ButtonType.Normal, stateEffect: true }) {
@@ -4877,7 +4874,6 @@ export struct LocalMusic {
     this.pinchValue = 1;
     this.scaleValue = 1;
   }
-
   @Builder
   getList(){
     Refresh({ refreshing: $$this.isRefreshing, refreshingContent: this.contentNode }) {
@@ -5529,7 +5525,7 @@ export struct LocalMusic {
         }
 
         AppStorage.setOrCreate('currentSong',this.currentSong) ;
-        this.videoUrl = this.currentSong.filePath
+        this.videoUrl = this.currentSong.filePath.replace(/ /g, '%20');
         this.name = this.currentSong.name
         this.cover = this.currentSong.pixelMapPath
         this.artist = this.currentSong.artist
@@ -5538,7 +5534,7 @@ export struct LocalMusic {
         this.startPlayOrResumePlay()
         break;
 
-      case CommonConstants.TYPE_INTERNET:
+      case CommonConstants.TYPE_WEBDAV:
         // 处理网络音频播放(WebDAV)
         Logger.info(`heanup 处理网络音频播放: ${item.name}, URL: ${item.filePath}`)
 
@@ -5564,7 +5560,7 @@ export struct LocalMusic {
           Logger.info(`heanup 网络音频 isFromSonPlayList分支 - 当前播放列表长度: ${this.songList.length}, 当前索引: ${this.curIndex}`)
         } else {
           // 从全局网络音频列表中查找
-          let globalVideoList = this.videoLocalList.filter(video => video.type === CommonConstants.TYPE_INTERNET) as VideoItem[];
+          let globalVideoList = this.videoLocalList.filter(video => video.type === CommonConstants.TYPE_WEBDAV) as VideoItem[];
           this.curIndex = globalVideoList.findIndex(video => video.filePath === item.filePath);
           if (this.curIndex === -1 && index !== undefined) {
             this.curIndex = index;
@@ -5576,12 +5572,13 @@ export struct LocalMusic {
         }
 
         AppStorage.setOrCreate('currentSong', this.currentSong);
-        this.videoUrl = this.currentSong.filePath  // 网络URL
+        // 对WebDAV URL进行编码处理,确保空格等特殊字符被正确编码
+        this.videoUrl = this.currentSong.filePath.replace(/ /g, '%20');
         this.name = this.currentSong.name
         this.cover = this.currentSong.pixelMapPath
         this.artist = this.currentSong.artist
 
-        Logger.info(`heanup 准备播放网络音频 - 歌名: ${this.name}, 艺术家: ${this.artist}, URL: ${this.videoUrl}`)
+        Logger.info(`heanup 准备播放网络音频 - 歌名: ${this.name}, 艺术家: ${this.artist}, 编码后URL: ${this.videoUrl}`)
 
         this.startPlayOrResumePlay()
         break;
@@ -5639,7 +5636,7 @@ export struct LocalMusic {
           }
           this.songList = globalVideoList
           this.sonDataSource.pushArrayData(this.songList)
-          this.videoUrl = this.currentSong.filePath
+          this.videoUrl = this.currentSong.filePath.replace(/ /g, '%20');
           this.name = item.title||this.currentSong.name
           this.cover = this.currentSong.pixelMapPath
           this.artist = item.performer||this.currentSong.artist
@@ -5653,8 +5650,6 @@ export struct LocalMusic {
       }
     })
   }
-
-
   @Builder
   PlayController() {
     Column() {
@@ -6046,9 +6041,6 @@ export struct LocalMusic {
     .width('100%')
     .height('100%')
   }
-
-
-
   @Builder
   editDetail(item: VideoItem) {
     Column() {
@@ -11565,7 +11557,7 @@ export struct LocalMusic {
 
   updateLastPlayTimeStr(filePath: string) {
     // 检查是否为网络音频(WebDAV),如果是则不更新本地数据库
-    if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_INTERNET) {
+    if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_WEBDAV) {
       Logger.info(`heanup 检测到网络音频播放,跳过数据库更新: ${filePath}`)
       return;
     }
@@ -11770,7 +11762,6 @@ export struct LocalMusic {
       Logger.error(`heanup 添加WebDAV认证头失败: ${error}`);
     }
   }
-
   private async play(url: string,startOffset?:number) {
     let that = this;
     that.showLoadIng();
@@ -11802,72 +11793,55 @@ export struct LocalMusic {
     this.mIjkMediaPlayer.setDataSource(url);
     //设置视频源http请求头
     let headers = new Map([
-      ["user_agent", "Mozilla/5.0 BiliDroid/7.30.0 (bbcallen@gmail.com)"],
+      ["user_agent", "Mozilla/5.0 BiliDroid/7.30.0 (ttmusic)"],
       ["referer", "https://www.bilibili.com"]
     ]);
 
     // 如果是WebDAV网络音频,添加安全的HTTP头认证
     Logger.info(`heanup 开始检查WebDAV认证需求 - currentSong存在: ${!!this.currentSong}`);
+    let isWebDavSong = false;
+    let webDavAuthInfo: WebDavAuthInfo | null = null;
     if (this.currentSong) {
       const currentSong = this.currentSong;
       Logger.info(`heanup 当前歌曲信息 - name: ${currentSong.name}, type: ${currentSong.type}, filePath: ${currentSong.filePath}`);
-      let isWebDavSong = false;
-
       // 检查是否为WebDAV歌曲(优先通过URL特征判断,因为全局上下文可能失效)
-      if (currentSong.type === CommonConstants.TYPE_INTERNET) {
-        Logger.info(`heanup 检测到网络音频,开始WebDAV识别流程`);
-
-        // 直接通过URL特征判断WebDAV
-        const filePath = currentSong.filePath || '';
-        Logger.info(`heanup 开始URL特征检测 - filePath: ${filePath}`);
-
-        const hasWebDavPath = filePath.includes('/webdav');
-        const hasPort8080 = filePath.includes(':8080');
-        const hasPort5005 = filePath.includes(':5005'); // 匹配实际的WebDAV服务器端口
-        const hasPort5000 = filePath.includes(':5000'); // 通用WebDAV端口
-        const hasRemotePhp = filePath.includes('/remote.php');
-        const hasDavPath = filePath.includes('/dav/');
-        const hasSharePath = filePath.includes('/share/'); // 特定于这个服务器
-
-        Logger.info(`heanup URL特征检测结果 - webdav: ${hasWebDavPath}, 8080: ${hasPort8080}, 5005: ${hasPort5005}, 5000: ${hasPort5000}, remote.php: ${hasRemotePhp}, dav/: ${hasDavPath}, share/: ${hasSharePath}`);
-
-        if (hasWebDavPath || hasPort8080 || hasPort5005 || hasPort5000 || hasRemotePhp || hasDavPath || hasSharePath) {
-          isWebDavSong = true;
-          Logger.info(`heanup 通过URL特征检测到WebDAV播放`);
+      if (currentSong.type === CommonConstants.TYPE_WEBDAV) {
+        isWebDavSong=true;
+        
+        // 优先从实例变量获取认证信息(更可靠)
+        if (this.currentWebDavAuthInfo) {
+          webDavAuthInfo = this.currentWebDavAuthInfo;
+          Logger.info(`heanup 从实例变量获取到WebDAV认证信息,账户ID: ${webDavAuthInfo.accountId}`);
         } else {
-          Logger.warn(`heanup URL特征检测未发现WebDAV标识`);
-        }
-
-        // 尝试从全局上下文获取认证信息(作为备用)
-        let webDavAuthInfo: WebDavAuthInfo | null = null;
-        try {
-          const globalContext = GlobalContext.getContext();
-          webDavAuthInfo = globalContext.getObject('webDavAuthInfo') as WebDavAuthInfo;
-          if (webDavAuthInfo) {
-            Logger.info(`heanup 从全局上下文获取到WebDAV认证信息,账户ID: ${webDavAuthInfo.accountId}`);
-          } else {
-            Logger.warn(`heanup 全局上下文中没有WebDAV认证信息`);
+          // 回退到全局上下文
+          try {
+            const globalContext = GlobalContext.getContext();
+            webDavAuthInfo = globalContext.getObject('webDavAuthInfo') as WebDavAuthInfo;
+            if (webDavAuthInfo) {
+              Logger.info(`heanup 从全局上下文获取到WebDAV认证信息,账户ID: ${webDavAuthInfo.accountId}`);
+              // 保存到实例变量中作为备份
+              this.currentWebDavAuthInfo = webDavAuthInfo;
+            } else {
+              Logger.warn(`heanup 全局上下文中没有WebDAV认证信息`);
+            }
+          } catch (error) {
+            Logger.error(`heanup 获取全局上下文失败:`, error.toString());
           }
-        } catch (error) {
-          Logger.error(`heanup 获取全局上下文失败:`, error.toString());
         }
-
+        
         // 如果识别为WebDAV但没有认证信息,尝试使用默认配置或提示用户
-        if (isWebDavSong && !webDavAuthInfo) {
+        if (!webDavAuthInfo) {
           Logger.warn(`heanup 识别为WebDAV播放但缺少认证信息,可能需要手动配置WebDAV账户`);
           // TODO: 可以在这里添加默认认证逻辑或用户提示
         }
       }
 
-      if (isWebDavSong) {
+      if (isWebDavSong&&webDavAuthInfo) {
+        // URL已在doPlay中编码过,这里无需再编码
+        // 但确保当前使用的this.videoUrl是已编码的版本
         Logger.info(`heanup 检测到WebDAV播放,添加安全认证头`);
-        Logger.info(`heanup 歌曲URL: ${currentSong.filePath}`);
-
-        // 重新获取WebDAV认证信息(因为可能在上面作用域中获取过)
+        Logger.info(`heanup 歌曲URL: ${this.videoUrl}`);
         try {
-          const globalContext = GlobalContext.getContext();
-          const webDavAuthInfo = globalContext.getObject('webDavAuthInfo') as WebDavAuthInfo;
-
           if (webDavAuthInfo && webDavAuthInfo.accountId) {
             Logger.info(`heanup 找到WebDAV认证信息,账户ID: ${webDavAuthInfo.accountId}`);
             // 使用WebdavManager获取认证头
@@ -11877,16 +11851,11 @@ export struct LocalMusic {
             if (authHeaders) {
               // 添加Basic认证头
               headers.set("authorization", authHeaders.headers.Authorization);
-              Logger.info(`heanup 已添加WebDAV Basic认证头`);
-              Logger.info(`heanup 认证URL基础地址: ${authHeaders.url}`);
             } else {
               Logger.error(`heanup 无法获取WebDAV认证头`);
             }
           } else {
             Logger.error(`heanup WebDAV认证信息无效或缺少accountId`);
-            // 尝试使用简单的用户名密码认证(基于常见的WebDAV配置)
-            Logger.warn(`heanup 尝试使用常见的WebDAV认证模式`);
-            // 这里可以根据实际服务器配置添加认证逻辑
           }
         } catch (error) {
           Logger.error(`heanup 获取WebDAV认证信息失败:`, error.toString());
@@ -11902,88 +11871,26 @@ export struct LocalMusic {
     }
 
     // 输出所有设置的头部信息用于调试
-    Logger.info(`heanup 设置的HTTP头部信息:`);
+    console.log(`heanup 设置的HTTP头部信息:`);
     const headerIterator = headers.entries();
     let headerEntry = headerIterator.next();
     while (!headerEntry.done) {
       const key = headerEntry.value[0];
       const value = headerEntry.value[1];
-      if (key.toLowerCase().includes('auth')) {
-        Logger.info(`heanup ${key}: [认证信息已隐藏]`);
-      } else {
-        Logger.info(`heanup ${key}: ${value}`);
-      }
+      console.log(`heanup ${key}: ${value}`);
+      // if (key.toLowerCase().includes('auth')) {
+      //   Logger.info(`heanup ${key}: [认证信息已隐藏]`);
+      // } else {
+      //   Logger.info(`heanup ${key}: ${value}`);
+      // }
       headerEntry = headerIterator.next();
     }
 
-    // 对于WebDAV播放,进行额外的连接验证
-    let isWebDavSong = false;
-    if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_INTERNET && this.currentSong.filePath) {
-      try {
-        const globalContext = GlobalContext.getContext();
-        const webDavAuthInfo = globalContext.getObject('webDavAuthInfo') as WebDavAuthInfo;
-        if (webDavAuthInfo) {
-          isWebDavSong = true;
-        } else {
-          // 通过URL特征判断
-          const filePath = this.currentSong.filePath;
-          if (filePath.includes('/webdav') || filePath.includes(':8080') || filePath.includes(':5000') ||
-              filePath.includes('/remote.php') || filePath.includes('/dav/')) {
-            isWebDavSong = true;
-          }
-        }
-      } catch (error) {
-        // 通过URL特征判断
-        const filePath = this.currentSong.filePath;
-        if (filePath.includes('/webdav') || filePath.includes(':8080') || filePath.includes(':5000') ||
-            filePath.includes('/remote.php') || filePath.includes('/dav/')) {
-          isWebDavSong = true;
-        }
-      }
-    }
-
-    if (isWebDavSong) {
-      Logger.info(`heanup 开始WebDAV播放,URL: ${this.videoUrl}`);
-
-      // 验证认证信息是否已设置
-      const authHeader = headers.get("authorization");
-      if (!authHeader) {
-        Logger.warn(`heanup 警告:WebDAV播放未设置认证头,可能导致播放失败`);
-      } else {
-        Logger.info(`heanup WebDAV认证头已设置完成`);
-      }
-    }
-
     this.mIjkMediaPlayer.setDataSourceHeader(headers);
 
-    // 为WebDAV播放设置IjkPlayer选项
-    let shouldApplyWebDavSettings = false;
-    if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_INTERNET && this.currentSong.filePath) {
-      try {
-        const globalContext = GlobalContext.getContext();
-        const webDavAuthInfo = globalContext.getObject('webDavAuthInfo') as WebDavAuthInfo;
-        if (webDavAuthInfo) {
-          shouldApplyWebDavSettings = true;
-        } else {
-          // 通过URL特征判断
-          const filePath = this.currentSong.filePath;
-          if (filePath.includes('/webdav') || filePath.includes(':8080') || filePath.includes(':5000') ||
-              filePath.includes('/remote.php') || filePath.includes('/dav/')) {
-            shouldApplyWebDavSettings = true;
-          }
-        }
-      } catch (error) {
-        // 通过URL特征判断
-        const filePath = this.currentSong.filePath;
-        if (filePath.includes('/webdav') || filePath.includes(':8080') || filePath.includes(':5000') ||
-            filePath.includes('/remote.php') || filePath.includes('/dav/')) {
-          shouldApplyWebDavSettings = true;
-        }
-      }
-    }
 
-    if (shouldApplyWebDavSettings) {
-      Logger.info(`heanup 为WebDAV播放设置IjkPlayer选项`);
+    if (isWebDavSong) {
+      console.log(`heanup 为WebDAV播放设置IjkPlayer选项`);
 
       // 网络超时设置(单位:微秒,文档显示应该是字符串格式)
       this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", "30000000"); // 30秒
@@ -11996,7 +11903,7 @@ export struct LocalMusic {
       this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "min-frames", "50"); // 减少最小帧数
       this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", "1"); // 预加载启动
       this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", "0"); // 无缓冲播放
-      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", "10000"); // 最大缓存10秒
+      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max_cached_duration", "30000"); // 最大缓存30秒
       this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "infbuf", "1"); // 无限制收流
 
       // 网络相关设置
@@ -12006,7 +11913,7 @@ export struct LocalMusic {
       // 重连设置
       this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", "3"); // 重连3次
 
-      Logger.info(`heanup WebDAV IjkPlayer选项设置完成`);
+      console.log(`heanup WebDAV IjkPlayer选项设置完成`);
     }
 
     // if(PreferencesUtil.getBooleanSync(SettingPage.IS_MIDIACODEC_OPEN,false)){
@@ -12310,7 +12217,7 @@ export struct LocalMusic {
 
         // 检查是否为WebDAV播放错误
         let isWebDavError = false;
-        if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_INTERNET && this.currentSong.filePath) {
+        if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_WEBDAV && this.currentSong.filePath) {
           try {
             const globalContext = GlobalContext.getContext();
             const webDavAuthInfo = globalContext.getObject('webDavAuthInfo') as WebDavAuthInfo;
@@ -12336,6 +12243,10 @@ export struct LocalMusic {
 
         if (isWebDavError) {
           Logger.error(`heanup WebDAV播放错误 - what: ${what}, extra: ${extra}, URL: ${this.videoUrl}`);
+          
+          // 播放失败时清空实例变量中的WebDAV认证信息,防止错误账户持续使用
+          // this.currentWebDavAuthInfo = null;
+          Logger.info('heanup WebDAV播放失败,保留认证信息用于后续歌曲切换');
 
           // 根据错误代码提供更具体的错误信息
           let errorMessage = "WebDAV播放失败";
@@ -12640,7 +12551,6 @@ export struct LocalMusic {
     this.durationTime = Math.floor(this.duration / 1000);
     this.durationStringTime = secondToTime((this.durationTime));
   }
-
   private playbackStateChangeListener = (playbackState: avSession.AVPlaybackState) => {
     const duration = playbackState?.extras?.duration;
     if (typeof duration === 'number') {
@@ -13006,7 +12916,7 @@ export struct LocalMusic {
       this.CONTROL_PlayStatus = PlayStatus.INIT;
       this.stop();
       this.currentSong = this.songList[this.curIndex]
-      this.videoUrl = this.songList[this.curIndex].filePath;
+      this.videoUrl = this.songList[this.curIndex].filePath.replace(/ /g, '%20');
       this.artist = this.songList[this.curIndex].artist
       this.name = this.songList[this.curIndex].name
       //this.cover = this.songList[this.curIndex].pixelMapPath
@@ -13094,7 +13004,7 @@ export struct LocalMusic {
       this.CONTROL_PlayStatus = PlayStatus.INIT;
       this.stop();
       this.currentSong = this.songList[this.curIndex];
-      this.videoUrl = this.songList[this.curIndex].filePath;
+      this.videoUrl = this.songList[this.curIndex].filePath.replace(/ /g, '%20');
       this.name = this.songList[this.curIndex].name;
       this.artist = this.songList[this.curIndex].artist
       // this.cover = this.songList[this.curIndex].pixelMapPath
@@ -13111,7 +13021,7 @@ export struct LocalMusic {
       this.CONTROL_PlayStatus = PlayStatus.INIT;
       this.stop();
       this.currentSong = this.songList[this.curIndex]
-      this.videoUrl = this.songList[index].filePath;
+      this.videoUrl = this.songList[index].filePath.replace(/ /g, '%20');
       this.name = this.songList[index].name
       this.artist = this.songList[this.curIndex].artist
       this.curIndex = index;
@@ -13139,7 +13049,7 @@ export struct LocalMusic {
     this.CONTROL_PlayStatus = PlayStatus.INIT;
     this.stop();
     this.currentSong = this.songList[this.curIndex]
-    this.videoUrl = this.songList[this.curIndex].filePath;
+    this.videoUrl = this.songList[this.curIndex].filePath.replace(/ /g, '%20');
     this.name = this.songList[this.curIndex].name
     this.artist = this.songList[this.curIndex].artist
     this.changeImageAnimation()
@@ -13154,7 +13064,7 @@ export struct LocalMusic {
       this.CONTROL_PlayStatus = PlayStatus.INIT;
       this.stop();
       this.currentSong = prevSong;
-      this.videoUrl = prevSong.filePath;
+      this.videoUrl = prevSong.filePath.replace(/ /g, '%20');
       this.cover = prevSong.pixelMapPath
       this.artist = prevSong.artist
       this.name = prevSong.name;
@@ -13170,7 +13080,7 @@ export struct LocalMusic {
       this.CONTROL_PlayStatus = PlayStatus.INIT;
       this.stop();
       this.currentSong = this.songList[this.curIndex];
-      this.videoUrl = this.songList[this.curIndex].filePath;
+      this.videoUrl = this.songList[this.curIndex].filePath.replace(/ /g, '%20');
       this.artist = this.songList[this.curIndex].artist
       this.name = this.songList[this.curIndex].name;
       this.changeImageAnimation()
@@ -13592,11 +13502,17 @@ export struct LocalMusic {
       if (playlistId === 'webdav-playlist') {
         Logger.info(`heanup 检测到WebDAV播放请求,使用事件传递的认证信息`)
 
-        // 如果有WebDAV认证信息,保存到全局上下文中供后续使用
+        // 如果有WebDAV认证信息,保存到实例变量和全局上下文中
         if (webDavAuthInfo) {
-          const globalContext = GlobalContext.getContext();
-          globalContext.setObject('webDavAuthInfo', webDavAuthInfo);
-          Logger.info(`heanup 已将WebDAV认证信息保存到全局上下文,账户ID: ${webDavAuthInfo.accountId}`);
+          this.currentWebDavAuthInfo = webDavAuthInfo;
+          // 同时保存到全局上下文作为持久化备份(防止实例变量被清空)
+          try {
+            const globalContext = GlobalContext.getContext();
+            globalContext.setObject('webDavAuthInfo', webDavAuthInfo as Object);
+            Logger.info(`heanup 已将WebDAV认证信息保存到实例变量和全局上下文,账户ID: ${webDavAuthInfo.accountId}`);
+          } catch (error) {
+            Logger.error(`heanup 保存到全局上下文失败:`, error.toString());
+          }
         } else {
           Logger.warn(`heanup WebDAV播放请求中没有认证信息`);
         }
@@ -13625,15 +13541,15 @@ export struct LocalMusic {
             const fileName = songFilePaths[i].split('/').pop() || `WebDAV歌曲${i+1}`;
             const videoItem = new VideoItem(
               fileName.replace(/\.(flac|mp3|wav|m4a)$/i, ''), // name (去掉扩展名)
-              i.toString(), // id
+              '', // id
               songFilePaths[i], // filePath (URL)
-              CommonConstants.TYPE_INTERNET, // type
-              0, // fileSize
-              "0", // time
+              CommonConstants.TYPE_WEBDAV, // type
+              0, // videoSize
+              "0", // cTime
               undefined, // pixelMap
               undefined, // size
               undefined, // pixelMapPath
-              "WebDAV艺术家", // artist
+              "WebDAV", // artist
               undefined, // album
               fileName // fileName
             );
@@ -13940,6 +13856,4 @@ async function scanCurrentDirectoryTask(context: Context, dirPath: string, lockP
   } catch (error) {
     console.error(' 扫描当前目录失败:', error);
   }
-}
-
-
+}

+ 1 - 0
entry/src/main/resources/base/media/folder.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761353872982" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4674" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M855.04 385.024q19.456 2.048 38.912 10.24t33.792 23.04 21.504 37.376 2.048 54.272q-2.048 8.192-8.192 40.448t-14.336 74.24-18.432 86.528-19.456 76.288q-5.12 18.432-14.848 37.888t-25.088 35.328-36.864 26.112-51.2 10.24l-567.296 0q-21.504 0-44.544-9.216t-42.496-26.112-31.744-40.96-12.288-53.76l0-439.296q0-62.464 33.792-97.792t95.232-35.328l503.808 0q22.528 0 46.592 8.704t43.52 24.064 31.744 35.84 12.288 44.032l0 11.264-53.248 0q-40.96 0-95.744-0.512t-116.736-0.512-115.712-0.512-92.672-0.512l-47.104 0q-26.624 0-41.472 16.896t-23.04 44.544q-8.192 29.696-18.432 62.976t-18.432 61.952q-10.24 33.792-20.48 65.536-2.048 8.192-2.048 13.312 0 17.408 11.776 29.184t29.184 11.776q31.744 0 43.008-39.936l54.272-198.656q133.12 1.024 243.712 1.024l286.72 0z" p-id="4675"></path></svg>