Prechádzať zdrojové kódy

feat(webdav): 实现安全的WebDAV认证和播放功能

chendeben 9 mesiacov pred
rodič
commit
b5880d3826

+ 71 - 12
entry/src/main/ets/common/util/WebdavManager.ets

@@ -12,9 +12,22 @@ import { WebDavAccount } from '../../viewmodel/WebDavAccount';
 import { relationalStore } from '@kit.ArkData';
 import { Constants } from '../../Constants';
 import Logger from './Logger';
+import { buffer } from '@kit.ArkTS';
 
 const TAG = 'heanup WebdavManager';
 
+// WebDAV认证信息接口
+export interface WebDavAuthInfo {
+  headers: Record<string, string>;
+  url: string;
+}
+
+// 流媒体认证信息接口
+export interface StreamAuthInfo {
+  url: string;
+  headers: Record<string, string>;
+}
+
 export interface TransferTask {
   song: Song;
   account: WebDavAccount;
@@ -367,22 +380,13 @@ export class WebdavManager {
     song.artist = Constants.UNKNOWN_ARTIST;
     song.name = fileInfo.fileName;
 
-    // 构建完整的WebDAV URL
+    // 构建安全的WebDAV URL(不包含认证信息)
     const protocol = account.enableHttps ? 'https' : 'http';
     const host = account.isUseLocalHost ? account.localHost : account.host;
     const port = account.port;
 
-    // 构建带认证信息的URL(如果提供了用户名和密码)
-    let authUrl = '';
-    if (account.account && account.password) {
-      // 对用户名和密码进行URL编码以处理特殊字符
-      const encodedUsername = encodeURIComponent(account.account);
-      const encodedPassword = encodeURIComponent(account.password);
-      authUrl = `${encodedUsername}:${encodedPassword}@`;
-    }
-
-    // href已经包含完整路径,直接使用
-    song.src = `${protocol}://${authUrl}${host}:${port}${fileInfo.href}`;
+    // 构建基础URL,不包含认证信息
+    song.src = `${protocol}://${host}:${port}${fileInfo.href}`;
 
     song.songType = SongType.WebDav;
     song.webDavAccountId = account.id;
@@ -552,4 +556,59 @@ export class WebdavManager {
     Logger.info(TAG, '清空上传队列');
     this.notifyObservers(WebdavManagerStates.UploadQueueChanged);
   }
+
+  // ==================== 安全认证方法 ====================
+
+  // 获取WebDAV认证信息(用于播放器)
+  public getWebDavAuthHeaders(accountId: number): WebDavAuthInfo | null {
+    const account = this.getWebDavAccountById(accountId);
+    if (!account || !account.account || !account.password) {
+      Logger.error(TAG, '无效的WebDAV账户或缺少认证信息');
+      return null;
+    }
+
+    // 构建基础URL
+    const protocol = account.enableHttps ? 'https' : 'http';
+    const host = account.isUseLocalHost ? account.localHost : account.host;
+    const port = account.port;
+
+    // 构建认证头
+    const credentials = buffer
+      .from(`${account.account}:${account.password}`)
+      .toString("base64");
+
+    const authInfo: WebDavAuthInfo = {
+      headers: {
+        'Authorization': `Basic ${credentials}`,
+        'User-Agent': 'TTMusic/1.0'
+      },
+      url: `${protocol}://${host}:${port}`
+    };
+    return authInfo;
+  }
+
+  // 根据ID获取WebDAV账户
+  private getWebDavAccountById(accountId: number): WebDavAccount | null {
+    for (let i = 0; i < this.webDavAccounts.length; i++) {
+      const account = this.webDavAccounts[i];
+      if (account.id === accountId) {
+        return account;
+      }
+    }
+    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;
+  }
 }

+ 58 - 2
entry/src/main/ets/pages/WebDavMainPage.ets

@@ -21,6 +21,19 @@ interface PlaylistEventData {
   songCount: number;
   startIndex: number;
   songFilePaths: string[];
+  webDavAuthInfo?: WebDavAuthInfo; // 新增WebDAV认证信息
+}
+
+/**
+ * WebDAV认证信息
+ */
+interface WebDavAuthInfo {
+  accountId: number;
+  host: string;
+  port: number;
+  account: string;
+  password: string;
+  enableHttps: boolean;
 }
 
 const TAG = 'heanup WebDavMainPage';
@@ -329,6 +342,21 @@ export struct WebDavMainPage {
       Logger.info(TAG, 'heanup 播放指定歌曲: ' + song.title + ', 索引: ' + index);
       Logger.info(TAG, 'heanup 歌曲列表长度: ' + this.songs.length);
 
+      // 保存WebDAV认证信息到全局上下文(用于播放器认证)
+      const globalContext = GlobalContext.getContext();
+      if (this.selectedAccount && this.selectedAccount.account && this.selectedAccount.password) {
+        const webDavAuthInfo: WebDavAuthInfo = {
+          accountId: this.selectedAccount.id,
+          host: this.selectedAccount.host,
+          port: this.selectedAccount.port,
+          account: this.selectedAccount.account,
+          password: this.selectedAccount.password,
+          enableHttps: this.selectedAccount.enableHttps
+        };
+        globalContext.setObject('webDavAuthInfo', webDavAuthInfo);
+        Logger.info(TAG, 'heanup 已保存WebDAV认证信息到全局上下文'+JSON.stringify(webDavAuthInfo));
+      }
+
       // 将当前歌曲列表转换为VideoItem数组
       const videoItems: VideoItem[] = [];
       const songFilePaths: string[] = [];
@@ -342,19 +370,47 @@ export struct WebDavMainPage {
       Logger.info(TAG, 'heanup 所有WebDAV歌曲文件路径: ' + JSON.stringify(songFilePaths));
 
       // 保存WebDAV歌曲数据到全局上下文
-      const globalContext = GlobalContext.getContext();
       globalContext.setObject('videoItems', videoItems);
       globalContext.setObject('currentPlayIndex', index);
       Logger.info(TAG, 'heanup 已将WebDAV歌曲列表保存到全局上下文,长度:' + videoItems.length);
 
+      // 验证保存是否成功
+      const savedVideoItems = globalContext.getObject('videoItems') as VideoItem[];
+      const savedIndex = globalContext.getObject('currentPlayIndex') as number;
+      Logger.info(TAG, 'heanup 验证保存结果 - videoItems长度: ' + (savedVideoItems?.length || 0) + ', currentPlayIndex: ' + savedIndex);
+
+      // 检查认证信息是否还在
+      const savedAuthInfo = globalContext.getObject('webDavAuthInfo') as WebDavAuthInfo;
+      if (savedAuthInfo) {
+        Logger.info(TAG, 'heanup 认证信息验证成功,账户ID: ' + savedAuthInfo.accountId);
+      } else {
+        Logger.error(TAG, 'heanup 认证信息验证失败,webDavAuthInfo为空');
+      }
+
       // 发送播放事件,类似歌单播放的方式
       const eventPlaylistPlay: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_PLAY };
+
+      // 准备WebDAV认证信息用于传递
+      let authInfoForEvent: WebDavAuthInfo | undefined = undefined;
+      if (this.selectedAccount && this.selectedAccount.account && this.selectedAccount.password) {
+        authInfoForEvent = {
+          accountId: this.selectedAccount.id,
+          host: this.selectedAccount.host,
+          port: this.selectedAccount.port,
+          account: this.selectedAccount.account,
+          password: this.selectedAccount.password,
+          enableHttps: this.selectedAccount.enableHttps
+        };
+        Logger.info(TAG, 'heanup 将WebDAV认证信息包含在播放事件中');
+      }
+
       const playlistData: PlaylistEventData = {
         playlistId: 'webdav-playlist', // 使用特殊的ID标识WebDAV播放列表
         playlistName: 'WebDAV - ' + (this.selectedAccount?.name || '未知账户'),
         songCount: this.songs.length,
         startIndex: index,
-        songFilePaths: songFilePaths
+        songFilePaths: songFilePaths,
+        webDavAuthInfo: authInfoForEvent // 直接传递认证信息
       };
 
       Logger.info(TAG, 'heanup 准备发送WebDAV播放事件,eventId: ' + eventPlaylistPlay.eventId);

+ 289 - 29
entry/src/main/ets/view/LocalMusic.ets

@@ -96,7 +96,7 @@ import { convertPlaylistSongsToVideoItems, emptyView } from '../pages/PlaylistDe
 import { Playlist, PlaylistSong } from '../viewmodel/Playlist';
 import { Song } from '../viewmodel/Song';
 import { SongType } from '../common/enums/SongType';
-import { WebdavManager } from '../common/util/WebdavManager';
+import { WebdavManager, WebDavAuthInfo as WebDavManagerAuthInfo } from '../common/util/WebdavManager';
 const TAG = 'LocalMusic';
 
 /**
@@ -108,6 +108,19 @@ interface PlaylistEventData {
   songCount: number;
   startIndex: number;
   songFilePaths: string[];
+  webDavAuthInfo?: WebDavAuthInfo; // 新增WebDAV认证信息
+}
+
+/**
+ * WebDAV认证信息(LocalMusic专用)
+ */
+interface WebDavAuthInfo {
+  accountId: number;
+  host: string;
+  port: number;
+  account: string;
+  password: string;
+  enableHttps: boolean;
 }
 
 const DEFAULT_INDEX =
@@ -667,6 +680,23 @@ export struct LocalMusic {
       // 检查歌单播放数据结构
       if (data.playlistId && data.songFilePaths && data.startIndex !== undefined) {
         Logger.info('heanup eventPlaylistPlay: 接收到歌单播放数据')
+
+        // 提取WebDAV认证信息(如果存在)
+        let webDavAuthInfo: WebDavAuthInfo | undefined = undefined;
+        if (data.webDavAuthInfo) {
+          // 将webDavAuthInfo转换为正确的类型
+          const authData = data.webDavAuthInfo as Record<string, Object>;
+          webDavAuthInfo = {
+            accountId: authData.accountId as number,
+            host: authData.host as string,
+            port: authData.port as number,
+            account: authData.account as string,
+            password: authData.password as string,
+            enableHttps: authData.enableHttps as boolean
+          };
+          Logger.info(`heanup 从事件中获取到WebDAV认证信息,账户ID: ${webDavAuthInfo.accountId}`);
+        }
+
         // 手动构建数据对象以避免类型转换问题
         const playlistData: PlaylistEventData = {
           playlistId: data.playlistId as string,
@@ -675,12 +705,13 @@ export struct LocalMusic {
           startIndex: data.startIndex as number,
           songFilePaths: data.songFilePaths as string[]
         }
-        // 根据文件路径重新构建歌曲列表
+        // 根据文件路径重新构建歌曲列表,并传递WebDAV认证信息
         this.handlePlaylistPlayRequest(
           playlistData.playlistId,
           playlistData.playlistName,
           playlistData.songFilePaths,
-          playlistData.startIndex
+          playlistData.startIndex,
+          webDavAuthInfo
         )
         return
       }
@@ -11776,28 +11807,209 @@ export struct LocalMusic {
       ["referer", "https://www.bilibili.com"]
     ]);
 
-    // 如果是WebDAV网络音频,添加适当的HTTP头
-    if (this.currentSong && this.currentSong.type === CommonConstants.TYPE_INTERNET) {
+    // 如果是WebDAV网络音频,添加安全的HTTP头认证
+    Logger.info(`heanup 开始检查WebDAV认证需求 - currentSong存在: ${!!this.currentSong}`);
+    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播放`);
+        } else {
+          Logger.warn(`heanup URL特征检测未发现WebDAV标识`);
+        }
 
-      // 安全地检查是否为WebDAV歌曲(通过检查URL格式)
-      const isWebDavSong = currentSong.filePath.startsWith('http://') ||
-                          currentSong.filePath.startsWith('https://');
+        // 尝试从全局上下文获取认证信息(作为备用)
+        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认证信息`);
+          }
+        } catch (error) {
+          Logger.error(`heanup 获取全局上下文失败:`, error.toString());
+        }
+
+        // 如果识别为WebDAV但没有认证信息,尝试使用默认配置或提示用户
+        if (isWebDavSong && !webDavAuthInfo) {
+          Logger.warn(`heanup 识别为WebDAV播放但缺少认证信息,可能需要手动配置WebDAV账户`);
+          // TODO: 可以在这里添加默认认证逻辑或用户提示
+        }
+      }
 
       if (isWebDavSong) {
-        Logger.info(`heanup 检测到WebDAV播放,添加专用HTTP头`);
+        Logger.info(`heanup 检测到WebDAV播放,添加安全认证头`);
+        Logger.info(`heanup 歌曲URL: ${currentSong.filePath}`);
 
-        // 为WebDAV添加适当的请求头
+        // 重新获取WebDAV认证信息(因为可能在上面作用域中获取过)
+        try {
+          const globalContext = GlobalContext.getContext();
+          const webDavAuthInfo = globalContext.getObject('webDavAuthInfo') as WebDavAuthInfo;
+
+          if (webDavAuthInfo && webDavAuthInfo.accountId) {
+            Logger.info(`heanup 找到WebDAV认证信息,账户ID: ${webDavAuthInfo.accountId}`);
+            // 使用WebdavManager获取认证头
+            const webdavManager = WebdavManager.getInstance();
+            const authHeaders = webdavManager.getWebDavAuthHeaders(webDavAuthInfo.accountId);
+
+            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());
+        }
+
+        // 添加标准的WebDAV请求头
         headers.set("user_agent", "TTMusic-WebDAV/1.0");
         headers.set("accept", "*/*");
         headers.set("accept-range", "bytes");
 
-        // 注意:认证信息已经在URL中(在WebdavManager中处理),无需额外添加认证头
-        Logger.info(`heanup WebDAV认证信息已在URL中,跳过额外认证头设置`);
+        Logger.info(`heanup WebDAV安全认证头设置完成`);
+      }
+    }
+
+    // 输出所有设置的头部信息用于调试
+    Logger.info(`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}`);
+      }
+      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选项`);
+
+      // 网络超时设置(单位:微秒,文档显示应该是字符串格式)
+      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "timeout", "30000000"); // 30秒
+      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "connect_timeout", "30000000"); // 连接超时30秒
+      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "listen_timeout", "30000000"); // 监听超时30秒
+      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "dns_cache_timeout", "30000000"); // DNS缓存超时30秒
+
+      // 缓冲和播放优化设置
+      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "max-buffer-size", "1024000"); // 增大缓冲区
+      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, "infbuf", "1"); // 无限制收流
+
+      // 网络相关设置
+      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "http_redirect", "1"); // 启用HTTP重定向
+      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "user_agent", "TTMusic-WebDAV/1.0"); // 用户代理
+
+      // 重连设置
+      this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "reconnect", "3"); // 重连3次
+
+      Logger.info(`heanup WebDAV IjkPlayer选项设置完成`);
+    }
+
     // if(PreferencesUtil.getBooleanSync(SettingPage.IS_MIDIACODEC_OPEN,false)){
     //   this.mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", "1"); // 启用硬件解码,会导致无法顺序播放
     // }
@@ -12096,21 +12308,63 @@ export struct LocalMusic {
       onError: (what: number, extra: number) => {
         this.stopProgressTask();
         LogUtils.getInstance().LOGI("OnErrorListener-->go:" + what + "===" + extra + " this.videoUrl=" + this.videoUrl)
-        that.hideLoadIng();
 
-        // 检查文件是否存在,但跳过网络URL(WebDAV)
-        if (StrUtil.isNotEmpty(this.videoUrl) &&
-            !this.videoUrl.startsWith('http://') &&
-            !this.videoUrl.startsWith('https://') &&
-            !FileUtil.accessSync(this.videoUrl)) {
-          ToastUtil.showToast(Utility.resourceToString(getContext(), $r('app.string.file_not_exist')))
-        } else {
-          ToastUtil.showToast(getContext()
-            .resourceManager
-            .getStringByNameSync("Honey_the_video_is_playing_errant_The_system_is_wandering"))
+        // 检查是否为WebDAV播放错误
+        let isWebDavError = 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) {
+              isWebDavError = true;
+            } else {
+              // 通过URL特征判断
+              const filePath = this.currentSong.filePath;
+              if (filePath.includes('/webdav') || filePath.includes(':5005') || filePath.includes(':5000') ||
+                  filePath.includes('/remote.php') || filePath.includes('/dav/')) {
+                isWebDavError = 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/')) {
+              isWebDavError = true;
+            }
+          }
+        }
 
+        if (isWebDavError) {
+          Logger.error(`heanup WebDAV播放错误 - what: ${what}, extra: ${extra}, URL: ${this.videoUrl}`);
+
+          // 根据错误代码提供更具体的错误信息
+          let errorMessage = "WebDAV播放失败";
+          if (what === -1) { // 网络错误
+            errorMessage = "网络连接失败,请检查WebDAV服务器连接";
+          } else if (what === -1004) { // HTTP 404
+            errorMessage = "文件未找到,请检查WebDAV服务器上的文件";
+          } else if (what === -1001) { // 超时
+            errorMessage = "连接超时,请检查网络或WebDAV服务器状态";
+          } else if (what === -1003) { // 无法解析主机
+            errorMessage = "无法连接到WebDAV服务器";
+          }
+
+          ToastUtil.showToast(errorMessage);
+        } else {
+          if (StrUtil.isNotEmpty(this.videoUrl) &&
+              !this.videoUrl.startsWith('http://') &&
+              !this.videoUrl.startsWith('https://') &&
+              !FileUtil.accessSync(this.videoUrl)) {
+            ToastUtil.showToast(Utility.resourceToString(getContext(), $r('app.string.file_not_exist')))
+          } else {
+            ToastUtil.showToast(getContext()
+              .resourceManager
+              .getStringByNameSync("Honey_the_video_is_playing_errant_The_system_is_wandering"))
+          }
         }
 
+        that.hideLoadIng();
       }
     }
 
@@ -13331,19 +13585,25 @@ export struct LocalMusic {
   /**
    * 处理歌单播放请求
    */
-  private handlePlaylistPlayRequest(playlistId: string, playlistName: string, songFilePaths: string[], startIndex: number) {
+  private handlePlaylistPlayRequest(playlistId: string, playlistName: string, songFilePaths: string[], startIndex: number, webDavAuthInfo?: WebDavAuthInfo) {
     try {
       Logger.info(`heanup 处理歌单播放请求: ${playlistName}, 歌曲数量: ${songFilePaths.length}, 开始索引: ${startIndex}`)
 
       // 检查是否为WebDAV播放请求
       if (playlistId === 'webdav-playlist') {
-        Logger.info(`heanup 检测到WebDAV播放请求,直接从全局上下文获取歌曲数据`)
-        const globalContext = GlobalContext.getContext();
+        Logger.info(`heanup 检测到WebDAV播放请求,使用事件传递的认证信息`)
 
-        // 先检查GlobalContext中是否有数据
-        const allKeys = globalContext['_objects'] ? Array.from(globalContext['_objects'].keys()) : [];
-        Logger.info(`heanup GlobalContext中的所有键: ${JSON.stringify(allKeys)}`);
+        // 如果有WebDAV认证信息,保存到全局上下文中供后续使用
+        if (webDavAuthInfo) {
+          const globalContext = GlobalContext.getContext();
+          globalContext.setObject('webDavAuthInfo', webDavAuthInfo);
+          Logger.info(`heanup 已将WebDAV认证信息保存到全局上下文,账户ID: ${webDavAuthInfo.accountId}`);
+        } else {
+          Logger.warn(`heanup WebDAV播放请求中没有认证信息`);
+        }
 
+        // 尝试从全局上下文获取videoItems(如果WebDavMainPage已经保存了的话)
+        const globalContext = GlobalContext.getContext();
         const videoItems = globalContext.getObject('videoItems') as VideoItem[];
         const currentPlayIndex = globalContext.getObject('currentPlayIndex') as number;