Эх сурвалжийг харах

feat(webdav): 添加WebDAV账号ID字段并重构认证逻辑

chendeben 9 сар өмнө
parent
commit
966f06aa54

+ 7 - 2
entry/src/main/ets/common/util/MediaTable.ets

@@ -30,6 +30,7 @@ interface DBColumnsInterface {
   LAST_PLAYED_STR: string;
   PLAY_COUNT: string;
   LYRIC_CONTENT: string;
+  WEBDAV_ACCOUNT_ID: string;
 }
 
 /**
@@ -55,7 +56,8 @@ const DB_COLUMNS: DBColumnsInterface = {
   SAMPLE_RATE: 'sampleRate',
   LAST_PLAYED_STR: 'lastPlayedStr',
   PLAY_COUNT: 'playCount',
-  LYRIC_CONTENT: 'lyricContent'
+  LYRIC_CONTENT: 'lyricContent',
+  WEBDAV_ACCOUNT_ID: 'webdav_account_id'
 };
 
 export default class MediaTable {
@@ -622,6 +624,7 @@ export default class MediaTable {
     item.LYRICIST = safeGet('LYRICIST');
     item.COMMENT = safeGet('COMMENT');
     item.disc = safeGet('disc');
+    item.webdav_account_id = safeGet('webdav_account_id');
 
     return item;
   }
@@ -991,7 +994,9 @@ function generateBucket(item: VideoItem): relationalStore.ValuesBucket {
   if(item.disc){
     obj.disc = item.disc;
   }
-
+  if(item.webdav_account_id){
+    obj.webdav_account_id = item.webdav_account_id;
+  }
 
   return obj;
 }

+ 3 - 1
entry/src/main/ets/common/util/RdbUtils.ets

@@ -80,6 +80,7 @@ export default class RdbUtils {
       '        LYRICIST TEXT,\n' +
       '        COMMENT TEXT,\n' +
       '        disc TEXT,\n' +
+      '        webdav_account_id TEXT,\n' +
 
       '        mimeType TEXT' +
       ')',
@@ -88,7 +89,7 @@ export default class RdbUtils {
       'duration',   'sampleRate','playCount',  'lastPlayedStr', 'trackCount',
       'lyricContent','md5Str','extra_json','pyStr','bit_rate','probe_score','year','nb_streams','nb_programs',
       'genre','track',  'bits_per_raw_sample','channels',  'channel_layout','start_time',
-      'ALBUMARTIST',  'COMPOSER','LYRICIST','COMMENT','COMMENT','disc',
+      'ALBUMARTIST',  'COMPOSER','LYRICIST','COMMENT','COMMENT','disc','webdav_account_id',
       'mimeType']
   };
 
@@ -216,6 +217,7 @@ export default class RdbUtils {
             'LYRICIST': 'TEXT',
             'COMMENT': 'TEXT',
             'disc': 'TEXT',
+            'webdav_account_id': 'TEXT',
           };
 
           // 逐个添加列,不依赖于检查结果

+ 288 - 20
entry/src/main/ets/common/util/WebdavManager.ets

@@ -22,6 +22,7 @@ const TAG = 'heanup WebdavManager';
 export interface WebDavAuthInfo {
   headers: Record<string, string>;
   url: string;
+  accountId?: string; // 添加账号ID字段,用于标识认证信息对应的账号
 }
 
 // 流媒体认证信息接口
@@ -124,6 +125,271 @@ export class WebdavManager {
 
   // ==================== 数据库操作 ====================
 
+  /**
+   * 根据webdav_account_id查询WebDAV账号信息
+   * @param accountId WebDAV账号ID
+   * @returns Promise<WebDavAccount | null> 返回WebDAV账号信息或null
+   */
+  public async getWebDavAccountById(accountId: string): Promise<WebDavAccount | null> {
+    if (!accountId) {
+      Logger.warn(TAG, 'webdav_account_id为空,无法查询账号信息');
+      return null;
+    }
+
+    try {
+      const db = await this.dataBaseUtil.getDB();
+      const predicates = new relationalStore.RdbPredicates(this.webDavTable);
+      predicates.equalTo('id', accountId);
+
+      const resultSet = await db.query(predicates);
+      if (resultSet.rowCount > 0) {
+        resultSet.goToFirstRow();
+        const account = new WebDavAccount();
+        account.id = resultSet.getLong(resultSet.getColumnIndex('id'));
+        account.name = resultSet.getString(resultSet.getColumnIndex('name'));
+        account.host = resultSet.getString(resultSet.getColumnIndex('host'));
+        account.localHost = resultSet.getString(resultSet.getColumnIndex('localHost'));
+        account.isUseLocalHost = resultSet.getLong(resultSet.getColumnIndex('isUseLocalHost')) === 1;
+        account.port = resultSet.getLong(resultSet.getColumnIndex('port'));
+        account.filepath = resultSet.getString(resultSet.getColumnIndex('filepath'));
+        account.imageFilePath = resultSet.getString(resultSet.getColumnIndex('imageFilePath'));
+        account.lyricFilePath = resultSet.getString(resultSet.getColumnIndex('lyricFilePath'));
+        account.uploadFilePath = resultSet.getString(resultSet.getColumnIndex('uploadFilePath'));
+        account.account = resultSet.getString(resultSet.getColumnIndex('account'));
+        account.password = resultSet.getString(resultSet.getColumnIndex('password'));
+        account.enableHttps = resultSet.getLong(resultSet.getColumnIndex('enableHttps')) === 1;
+        account.coverPath = resultSet.getString(resultSet.getColumnIndex('coverPath'));
+        account.isActivate = resultSet.getLong(resultSet.getColumnIndex('isActivate')) === 1;
+
+        resultSet.close();
+        Logger.info(TAG, `成功查询到WebDAV账号: ${account.name}, ID: ${account.id}`);
+        return account;
+      } else {
+        Logger.warn(TAG, `未找到ID为 ${accountId} 的WebDAV账号`);
+        resultSet.close();
+        return null;
+      }
+    } catch (error) {
+      const err = error as Error;
+      Logger.error(TAG, `查询WebDAV账号失败: ${err.message}`);
+      return null;
+    }
+  }
+
+  /**
+   * 根据WebDAV账号构建认证信息
+   * @param accountId WebDAV账号ID
+   * @returns Promise<WebDavAuthInfo | null> 返回认证信息或null
+   */
+  public async buildAuthInfoByAccountId(accountId: string): Promise<WebDavAuthInfo | null> {
+    const account = await this.getWebDavAccountById(accountId);
+    if (!account) {
+      Logger.warn(TAG, `无法构建认证信息,账号ID ${accountId} 对应的账号不存在`);
+      return null;
+    }
+
+    try {
+      // 构建基础URL
+      const protocol = account.enableHttps ? 'https' : 'http';
+      const host = account.isUseLocalHost && account.localHost ? account.localHost : account.host;
+      const port = account.port && account.port !== 80 && account.port !== 443 ? `:${account.port}` : '';
+      const baseUrl = `${protocol}://${host}${port}`;
+
+      // 构建认证头
+      const headers: Record<string, string> = {};
+      if (account.account && account.password) {
+        const credentials = `${account.account}:${account.password}`;
+        // 简化base64编码实现,避免复杂的类型转换
+        try {
+          const bufferObj = buffer.from(credentials, 'utf-8');
+          const base64Credentials = bufferObj.toString('base64');
+          headers['Authorization'] = `Basic ${base64Credentials}`;
+        } catch (error) {
+          const err = error as Error;
+          Logger.warn(TAG, `base64编码失败,使用原始凭据: ${err.message}`);
+          headers['Authorization'] = `Basic ${credentials}`;
+        }
+      }
+
+      Logger.info(TAG, `成功构建WebDAV认证信息,账号: ${account.name}, URL: ${baseUrl}`);
+
+      return {
+        headers: headers,
+        url: baseUrl,
+        accountId: accountId
+      };
+    } catch (error) {
+      const err = error as Error;
+      Logger.error(TAG, `构建WebDAV认证信息失败: ${err.message}`);
+      return null;
+    }
+  }
+
+  /**
+   * 构建HTTP请求头,通过webdav_account_id查询认证信息
+   * @param currentSong - 当前播放的歌曲信息
+   * @param videoUrl - 当前歌曲的URL
+   * @returns Promise<Map<string, string>> HTTP请求头
+   */
+  public async buildHttpHeadersWithAccountId(
+    currentSong: VideoItem | undefined,
+    videoUrl: string
+  ): Promise<Map<string, string>> {
+    const headers = new Map<string, string>();
+
+    if (!currentSong) {
+      return headers;
+    }
+
+    Logger.info(`heanup 当前歌曲信息 - name: ${currentSong.name}, type: ${currentSong.type}, filePath: ${currentSong.filePath}`);
+
+    // 检查是否为WebDAV歌曲
+    if (currentSong.type === CommonConstants.TYPE_WEBDAV) {
+      // 优先使用webdav_account_id查询认证信息
+      if (currentSong.webdav_account_id) {
+        Logger.info(`heanup 使用webdav_account_id查询认证信息,账号ID: ${currentSong.webdav_account_id}`);
+
+        try {
+          const authInfo = await this.buildAuthInfoByAccountId(currentSong.webdav_account_id);
+          if (authInfo) {
+            // 添加WebDAV认证头
+            Object.keys(authInfo.headers).forEach(key => {
+              headers.set(key, authInfo.headers[key]);
+            });
+            Logger.info(`heanup 成功通过webdav_account_id获取WebDAV认证信息`);
+          } else {
+            Logger.warn(`heanup 无法通过webdav_account_id ${currentSong.webdav_account_id} 获取认证信息`);
+          }
+        } catch (error) {
+          const err = error as Error;
+          Logger.error(`heanup 查询webdav_account_id认证信息时出错: ${err.message}`);
+        }
+      } else {
+        Logger.warn(`heanup WebDAV歌曲缺少webdav_account_id字段,无法查询认证信息`);
+      }
+
+      // 如果没有认证信息,记录警告
+      if (headers.size === 0) {
+        Logger.warn(`heanup WebDAV歌曲缺少认证信息,可能需要手动配置WebDAV账户`);
+      }
+    }
+
+    return headers;
+  }
+
+  /**
+   * 更新现有WebDAV歌曲的webdav_account_id字段
+   * 用于修复旧版本数据库中缺少webdav_account_id的记录
+   * @param accountId WebDAV账号ID
+   */
+  public async updateWebDavSongsAccountId(accountId: string): Promise<void> {
+    try {
+      const db = await this.dataBaseUtil.getDB();
+
+      // 查询所有TYPE_WEBDAV且webdav_account_id为空或null的记录
+      const querySql = `
+        SELECT id, filePath FROM mediaTable
+        WHERE mtype = ${CommonConstants.TYPE_WEBDAV}
+        AND (webdav_account_id IS NULL OR webdav_account_id = '')
+      `;
+
+      const resultSet = await db.querySql(querySql);
+      const updateCount = resultSet.rowCount;
+
+      if (updateCount > 0) {
+        Logger.info(TAG, `发现 ${updateCount} 个WebDAV歌曲缺少webdav_account_id,开始更新`);
+
+        // 更新这些记录的webdav_account_id
+        const updateSql = `
+          UPDATE mediaTable
+          SET webdav_account_id = ?
+          WHERE mtype = ${CommonConstants.TYPE_WEBDAV}
+          AND (webdav_account_id IS NULL OR webdav_account_id = '')
+        `;
+
+        await db.executeSql(updateSql, [accountId]);
+        Logger.info(TAG, `成功更新 ${updateCount} 个WebDAV歌曲的webdav_account_id为: ${accountId}`);
+      } else {
+        Logger.info(TAG, `所有WebDAV歌曲都已有webdav_account_id,无需更新`);
+      }
+
+      resultSet.close();
+    } catch (error) {
+      const err = error as Error;
+      Logger.error(TAG, `更新WebDAV歌曲webdav_account_id失败: ${err.message}`);
+    }
+  }
+
+  /**
+   * 修复所有WebDAV歌曲的webdav_account_id字段
+   * 用于全局修复旧版本数据库中缺少webdav_account_id的记录
+   * @param accountId WebDAV账号ID(可选,如果不提供则使用当前激活的账号)
+   */
+  public async fixAllWebDavSongsAccountId(accountId?: string): Promise<void> {
+    try {
+      // 如果没有提供accountId,尝试使用当前激活的账号
+      if (!accountId) {
+        const activeAccount = await this.getActiveWebDavAccount();
+        if (activeAccount && activeAccount.id) {
+          accountId = activeAccount.id.toString();
+        } else {
+          Logger.warn(TAG, `无法找到激活的WebDAV账号来修复歌曲记录`);
+          return;
+        }
+      }
+
+      Logger.info(TAG, `开始修复所有WebDAV歌曲的webdav_account_id,使用账号ID: ${accountId}`);
+      await this.updateWebDavSongsAccountId(accountId);
+      Logger.info(TAG, `WebDAV歌曲webdav_account_id修复完成`);
+    } catch (error) {
+      const err = error as Error;
+      Logger.error(TAG, `修复WebDAV歌曲webdav_account_id失败: ${err.message}`);
+    }
+  }
+
+  /**
+   * 获取当前激活的WebDAV账号
+   */
+  private async getActiveWebDavAccount(): Promise<WebDavAccount | null> {
+    try {
+      const db = await this.dataBaseUtil.getDB();
+      const predicates = new relationalStore.RdbPredicates(this.webDavTable);
+      predicates.equalTo('isActivate', 1);
+      predicates.limitAs(1);
+
+      const resultSet = await db.query(predicates);
+      if (resultSet.rowCount > 0) {
+        resultSet.goToFirstRow();
+        const account = new WebDavAccount();
+        account.id = resultSet.getLong(resultSet.getColumnIndex('id'));
+        account.name = resultSet.getString(resultSet.getColumnIndex('name'));
+        account.host = resultSet.getString(resultSet.getColumnIndex('host'));
+        account.localHost = resultSet.getString(resultSet.getColumnIndex('localHost'));
+        account.isUseLocalHost = resultSet.getLong(resultSet.getColumnIndex('isUseLocalHost')) === 1;
+        account.port = resultSet.getLong(resultSet.getColumnIndex('port'));
+        account.filepath = resultSet.getString(resultSet.getColumnIndex('filepath'));
+        account.imageFilePath = resultSet.getString(resultSet.getColumnIndex('imageFilePath'));
+        account.lyricFilePath = resultSet.getString(resultSet.getColumnIndex('lyricFilePath'));
+        account.uploadFilePath = resultSet.getString(resultSet.getColumnIndex('uploadFilePath'));
+        account.account = resultSet.getString(resultSet.getColumnIndex('account'));
+        account.password = resultSet.getString(resultSet.getColumnIndex('password'));
+        account.enableHttps = resultSet.getLong(resultSet.getColumnIndex('enableHttps')) === 1;
+        account.coverPath = resultSet.getString(resultSet.getColumnIndex('coverPath'));
+        account.isActivate = resultSet.getLong(resultSet.getColumnIndex('isActivate')) === 1;
+
+        resultSet.close();
+        return account;
+      } else {
+        resultSet.close();
+        return null;
+      }
+    } catch (error) {
+      const err = error as Error;
+      Logger.error(TAG, `获取激活的WebDAV账号失败: ${err.message}`);
+      return null;
+    }
+  }
+
   // 创建WebDAV账户表
   public createWebDavTableInDB(): Promise<void> {
     const createTableSql = `CREATE TABLE IF NOT EXISTS ${this.webDavTable} (
@@ -440,6 +706,11 @@ export class WebdavManager {
   public async loadFilesInfoFromAccount(account: WebDavAccount,customPath?: string): Promise<void> {
     this.currentAccount = account;
 
+    // 更新现有WebDAV歌曲的webdav_account_id字段
+    if (account && account.id) {
+      await this.updateWebDavSongsAccountId(account.id.toString());
+    }
+
     try {
       this.notifyObservers(WebdavManagerStates.LoadFilesInfoStart);
 
@@ -531,6 +802,12 @@ export class WebdavManager {
       videoItem.artist = musicData.artist
     }
 
+    // 设置WebDAV账号ID,用于后续认证信息查询
+    if (account && account.id) {
+      videoItem.webdav_account_id = account.id.toString();
+      Logger.info(TAG, `设置WebDAV歌曲 ${videoItem.name} 的账号ID: ${videoItem.webdav_account_id}`);
+    }
+
     return videoItem;
   }
 
@@ -740,17 +1017,17 @@ export class WebdavManager {
   // ==================== 安全认证方法 ====================
 
   // 获取WebDAV认证信息(用于播放器)
-  public getWebDavAuthHeaders(accountId: number): WebDavAuthInfo | null {
-    const account = this.getWebDavAccountById(accountId);
+  public async getWebDavAuthHeaders(accountId: string): Promise<WebDavAuthInfo | null> {
+    const account = await 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 protocol: string = account.enableHttps ? 'https' : 'http';
+    const host: string = account.isUseLocalHost && account.localHost ? account.localHost : account.host;
+    const port: number = account.port;
 
     // 构建认证头
     const credentials = buffer
@@ -762,21 +1039,12 @@ export class WebdavManager {
         'Authorization': `Basic ${credentials}`,
         'User-Agent': 'TTMusic/1.0'
       },
-      url: `${protocol}://${host}:${port}`
+      url: `${protocol}://${host}:${port}`,
+      accountId: accountId
     };
     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;
-  }
 }
 
 
@@ -786,13 +1054,13 @@ export class WebdavManager {
  * @param currentSong - 当前播放的歌曲信息
  * @param videoUrl - 当前歌曲的URL
  * @param webDavAuthItem - 当前WebDAV认证信息(实例变量)
- * @returns Map<string, string> HTTP请求头
+ * @returns Promise<Map<string, string>> HTTP请求头
  */
-export function buildHttpHeadersWithWebDav(
+export async function buildHttpHeadersWithWebDav(
   currentSong: VideoItem | undefined,
   videoUrl: string,
   webDavAuthItem: WebDavAuthItem
-): Map<string, string> {
+): Promise<Map<string, string>> {
   const headers = new Map<string, string>();
 
   let isWebDavSong = false;
@@ -837,7 +1105,7 @@ export function buildHttpHeadersWithWebDav(
           Logger.info(`heanup 找到WebDAV认证信息,账户ID: ${webDavAuthItem.accountId}`);
           // 使用WebdavManager获取认证头
           const webdavManager = WebdavManager.getInstance();
-          const authHeaders = webdavManager.getWebDavAuthHeaders(webDavAuthItem.accountId);
+          const authHeaders = await webdavManager.getWebDavAuthHeaders(webDavAuthItem.accountId.toString());
 
           if (authHeaders) {
             // 添加Basic认证头

+ 19 - 30
entry/src/main/ets/pages/WebDavMainPage.ets

@@ -22,19 +22,7 @@ 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;
+  // 移除webDavAuthInfo,现在直接使用videoItems中的webdav_account_id
 }
 
 const TAG = 'heanup WebDavMainPage';
@@ -279,21 +267,21 @@ export struct WebDavMainPage {
       Logger.info(TAG, 'heanup 播放指定歌曲: ' + song.name + ', 索引: ' + index);
       Logger.info(TAG, 'heanup 歌曲列表长度: ' + this.songs.length);
 
-      // 保存WebDAV认证信息到全局上下文(用于播放器认证)
-      const globalContext = GlobalContext.getContext();
-      let webDavAuthInfo: WebDavAuthInfo | undefined = undefined;
-
-      if (this.selectedAccount && this.selectedAccount.account && this.selectedAccount.password) {
-        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));
+      // 检查歌曲是否有webdav_account_id
+      Logger.info(TAG, `heanup 播放歌曲的webdav_account_id: ${song.webdav_account_id || '未设置'}`);
+
+      // 确保所有歌曲都设置了正确的webdav_account_id
+      if (this.selectedAccount && this.selectedAccount.id) {
+        const accountIds = this.songs.map(s => s.webdav_account_id).filter(id => id);
+        Logger.info(TAG, `heanup 当前歌曲列表中有 ${accountIds.length}/${this.songs.length} 首歌曲设置了webdav_account_id`);
+
+        // 如果发现歌曲缺少webdav_account_id,立即设置
+        this.songs.forEach((item, idx) => {
+          if (!item.webdav_account_id) {
+            item.webdav_account_id = this.selectedAccount!.id.toString();
+            Logger.info(TAG, `heanup 为歌曲 "${item.name}" 设置webdav_account_id: ${item.webdav_account_id}`);
+          }
+        });
       }
 
       // 直接使用当前的VideoItem数组
@@ -306,6 +294,7 @@ export struct WebDavMainPage {
       }
 
       // 保存WebDAV歌曲数据到全局上下文
+      const globalContext = GlobalContext.getContext();
       globalContext.setObject('videoItems', videoItems);
       globalContext.setObject('currentPlayIndex', index);
       Logger.info(TAG, 'heanup 已将WebDAV歌曲列表保存到全局上下文,长度:' + videoItems.length);
@@ -318,8 +307,8 @@ export struct WebDavMainPage {
         playlistName: 'WebDAV - ' + (this.selectedAccount?.name || '未知账户'),
         songCount: this.songs.length,
         startIndex: index,
-        songFilePaths: songFilePaths,
-        webDavAuthInfo: webDavAuthInfo // 直接传递认证信息
+        songFilePaths: songFilePaths
+        // 移除webDavAuthInfo,现在直接使用videoItems中的webdav_account_id
       };
 
       Logger.info(TAG, 'heanup 准备发送WebDAV播放事件,eventId: ' + eventPlaylistPlay.eventId);

+ 46 - 94
entry/src/main/ets/view/LocalMusic.ets

@@ -674,21 +674,7 @@ export struct LocalMusic {
       if (data.playlistId && data.songFilePaths && data.startIndex !== undefined) {
         Logger.info('heanup eventPlaylistPlay: 接收到歌单播放数据')
 
-        // 提取WebDAV认证信息(如果存在)
-        let webDavAuthInfo: WebDavAuthItem | 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}`);
-        }
+        // 不再提取WebDAV认证信息,直接使用videoItems中的webdav_account_id
 
         // 手动构建数据对象以避免类型转换问题
         const playlistData: PlaylistEventData = {
@@ -698,13 +684,12 @@ export struct LocalMusic {
           startIndex: data.startIndex as number,
           songFilePaths: data.songFilePaths as string[]
         }
-        // 根据文件路径重新构建歌曲列表,传递WebDAV认证信息
+        // 根据文件路径重新构建歌曲列表,不再传递WebDAV认证信息
         this.handlePlaylistPlayRequest(
           playlistData.playlistId,
           playlistData.playlistName,
           playlistData.songFilePaths,
-          playlistData.startIndex,
-          webDavAuthInfo
+          playlistData.startIndex
         )
         return
       }
@@ -11705,62 +11690,7 @@ export struct LocalMusic {
     this.loadingVisible = Visibility.None;
     this.replayVisible = Visibility.Visible;
   }
-  /**
-   * 为WebDAV请求添加认证头
-   */
-  private addWebDavAuthHeader(headers: Map<string, string>, accountId: number): void {
-    try {
-      // 从WebDAV管理器获取账户信息
-      const webdavManager = WebdavManager.getInstance();
-      const accounts = webdavManager.getAllWebDavAccounts();
-      const account = accounts.find(acc => acc.id === accountId);
-
-      if (account && account.account && account.password) {
-        // 构建Basic认证头
-        const credentials = `${account.account}:${account.password}`;
-
-        // 简单的Base64编码实现
-        const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
-        let encoded = '';
-        let i = 0;
-
-        while (i < credentials.length) {
-          const a = credentials.charCodeAt(i++);
-          const b = i < credentials.length ? credentials.charCodeAt(i++) : 0;
-          const c = i < credentials.length ? credentials.charCodeAt(i++) : 0;
-
-          const bitmap = (a << 16) | (b << 8) | c;
-
-          encoded += chars.charAt((bitmap >> 18) & 63);
-          encoded += chars.charAt((bitmap >> 12) & 63);
-          encoded += chars.charAt((bitmap >> 6) & 63);
-          encoded += chars.charAt(bitmap & 63);
-        }
-
-        // 处理剩余字符
-        const remainder = credentials.length % 3;
-        if (remainder === 1) {
-          const bitmap = credentials.charCodeAt(credentials.length - 1) << 16;
-          encoded += chars.charAt((bitmap >> 18) & 63);
-          encoded += chars.charAt((bitmap >> 12) & 63);
-          encoded += '==';
-        } else if (remainder === 2) {
-          const bitmap = (credentials.charCodeAt(credentials.length - 2) << 16) | (credentials.charCodeAt(credentials.length - 1) << 8);
-          encoded += chars.charAt((bitmap >> 18) & 63);
-          encoded += chars.charAt((bitmap >> 12) & 63);
-          encoded += chars.charAt((bitmap >> 6) & 63);
-          encoded += '=';
-        }
-
-        headers.set("Authorization", `Basic ${encoded}`);
-        Logger.info(`heanup 为WebDAV添加Basic认证头,账户: ${account.account}`);
-      } else {
-        Logger.warn(`heanup 未找到WebDAV账户信息或账户缺少认证信息,accountId: ${accountId}`);
-      }
-    } catch (error) {
-      Logger.error(`heanup 添加WebDAV认证头失败: ${error}`);
-    }
-  }
+
   private async play(url: string,startOffset?:number) {
     let that = this;
     that.showLoadIng();
@@ -11796,9 +11726,34 @@ export struct LocalMusic {
       ["referer", "https://www.bilibili.com"]
     ]);
 
-    // 如果是WebDAV网络音频,添加安全的HTTP头认证
-    if (this.currentSong&&this.currentSong.type === CommonConstants.TYPE_WEBDAV&&this.currentWebDavAuthInfo) {
-      headers = buildHttpHeadersWithWebDav(this.currentSong,this.videoUrl,this.currentWebDavAuthInfo)
+    // 如果是WebDAV网络音频,使用webdav_account_id获取认证信息
+    if (this.currentSong&&this.currentSong.type === CommonConstants.TYPE_WEBDAV) {
+      Logger.info(`heanup WebDAV歌曲认证 - 歌曲名: ${this.currentSong.name}, webdav_account_id: ${this.currentSong.webdav_account_id || '未设置'}`);
+
+      // 使用webdav_account_id查询认证信息
+      if (this.currentSong.webdav_account_id) {
+        const webdavManager = WebdavManager.getInstance();
+        webdavManager.buildHttpHeadersWithAccountId(this.currentSong, this.videoUrl)
+          .then(webDavHeaders => {
+            if (webDavHeaders && webDavHeaders.size > 0) {
+              // 合并WebDAV认证头到基础请求头
+              webDavHeaders.forEach((value, key) => {
+                headers.set(key, value);
+              });
+              Logger.info(`heanup 成功通过webdav_account_id获取并设置WebDAV认证头`);
+              // 重新设置带认证的请求头
+              this.mIjkMediaPlayer.setDataSourceHeader(headers);
+            } else {
+              Logger.warn(`heanup 通过webdav_account_id获取认证头失败,播放可能无法继续`);
+            }
+          })
+          .catch((error: Error) => {
+            Logger.error(`heanup 获取WebDAV认证头时出错: ${error.message}`);
+          });
+      } else {
+        Logger.error(`heanup WebDAV歌曲 "${this.currentSong.name}" 缺少webdav_account_id,无法进行认证`);
+        Logger.error(`heanup 请重新扫描WebDAV账户以修复此问题`);
+      }
     }
     this.mIjkMediaPlayer.setDataSourceHeader(headers);
     if (this.currentSong&&this.currentSong.type === CommonConstants.TYPE_WEBDAV) {
@@ -13419,28 +13374,13 @@ export struct LocalMusic {
    * 处理歌单播放请求
    */
   private handlePlaylistPlayRequest(playlistId: string, playlistName:
-    string, songFilePaths: string[], startIndex: number, webDavAuthInfo?: WebDavAuthItem) {
+    string, songFilePaths: string[], startIndex: number) {
     try {
       Logger.info(`heanup 处理歌单播放请求: ${playlistName}, 歌曲数量: ${songFilePaths.length}, 开始索引: ${startIndex}`)
 
       // 检查是否为WebDAV播放请求
       if (playlistId === 'webdav-playlist') {
-        Logger.info(`heanup 检测到WebDAV播放请求,使用事件传递的认证信息`)
-
-        // 如果有WebDAV认证信息,保存到实例变量和全局上下文中
-        if (webDavAuthInfo) {
-          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播放请求中没有认证信息`);
-        }
+        Logger.info(`heanup 检测到WebDAV播放请求,直接使用videoItems中的webdav_account_id进行认证`)
 
         // 尝试从全局上下文获取videoItems(如果WebDavMainPage已经保存了的话)
         const globalContext = GlobalContext.getContext();
@@ -13449,6 +13389,18 @@ export struct LocalMusic {
 
         Logger.info(`heanup 从GlobalContext获取数据: videoItems长度=${videoItems?.length || 0}, currentPlayIndex=${currentPlayIndex}`);
 
+        // 检查videoItems中的webdav_account_id设置情况
+        if (videoItems && videoItems.length > 0) {
+          const accountIds = videoItems.map(item => item.webdav_account_id).filter(id => id);
+          Logger.info(`heanup videoItems中有 ${accountIds.length}/${videoItems.length} 首歌曲设置了webdav_account_id`);
+
+          // 显示前几首歌曲的webdav_account_id状态
+          for (let i = 0; i < Math.min(3, videoItems.length); i++) {
+            const item = videoItems[i];
+            Logger.info(`heanup 歌曲[${i}]: ${item?.name}, webdav_account_id: ${item?.webdav_account_id || '未设置'}`);
+          }
+        }
+
         if (videoItems && videoItems.length > 0) {
           Logger.info(`heanup 从全局上下文获取到WebDAV歌曲列表,长度:${videoItems.length}`)
           // 验证歌曲数据是否完整

+ 2 - 0
entry/src/main/ets/viewmodel/VideoItem.ets

@@ -68,6 +68,8 @@ export class VideoItem  {
   COMMENT?:string//注释
   disc?:string//碟号
 
+  webdav_account_id?: string// WebDAV账号ID,用于获取认证信息
+
   constructor(name: string, id: string, filePath: string, type:number,videoSize:number,cTime: string,pixelMap?: image.PixelMap,
     size?: string,pixelMapPath?: string,artist?: string,album?: string,fileName?: string, lastPlayed?:Date) {
     this.name = name;