| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880 |
- // WebdavManager - WebDAV管理器(简化版)
- 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 { RcpSocket } from './RcpSocketUtil';
- import { DataBaseUtil } from './DataBaseUtil';
- import { WebDavAccount } from '../../viewmodel/WebDavAccount';
- import { relationalStore } from '@kit.ArkData';
- import { Constants } from '../../Constants';
- import Logger from './Logger';
- import { buffer } from '@kit.ArkTS';
- import { CommonConstants } from '../constants/CommonConstants';
- import { GlobalContext } from '@pura/harmony-utils';
- 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: VideoItem;
- account: WebDavAccount;
- }
- @Observed
- export class WebdavManager {
- public rcpSocket: RcpSocket = RcpSocket.getInstance();
- private dataBaseUtil = DataBaseUtil.getInstance();
- public observers: Array<(event: string) => void> = [];
- public static instance: WebdavManager;
- public context: common.Context | undefined;
- public DownloadDirectoryFilePath: string = '';
- public audioExtensions = Constants.AUDIO_EXTENSIONS;
- public lyricExtensions = Constants.LYRIC_EXTENSIONS;
- public imageExtensions = Constants.IMAGE_EXTENSIONS;
- // 数据表名称
- public preferenceName = Constants.WEBDAV_PREFERENCE_NAME;
- public webDavTable = 'WebDavAccount';
- public receivedSize: number = 0;
- public totalSize: number = 0;
- public webDavAccounts: WebDavAccount[] = [];
- public webDavSongs: VideoItem[] = [];
- public webDavFiles: FileInfo[] = []; // 当前目录的所有文件(包括文件夹)
- // 路径导航
- public currentPath: string = ''; // 当前浏览的路径
- public pathHistory: string[] = []; // 路径历史记录
- // 错误信息
- public ErrorMessage: string | BusinessError = '';
- // 下载队列
- public downloadQueue: TransferTask[] = [];
- public finishDownloadQueue: TransferTask[] = [];
- public isProcessingQueue: boolean = false;
- public currentDownloadTask: TransferTask | null = null;
- public isPauseDownload: boolean = true;
- // 上传队列
- public uploadQueue: TransferTask[] = [];
- public finishUploadQueue: TransferTask[] = [];
- public isProcessingUploadQueue: boolean = false;
- public currentUploadTask: TransferTask | null = null;
- public isPauseUpload: boolean = true;
- public currentAccount:WebDavAccount = new WebDavAccount();
- public constructor() {
- this.rcpSocket.subscribeHTTPDataTransfer((event: string) => {
- this.notifyObservers(event);
- });
- }
- public static getInstance(): WebdavManager {
- if (!WebdavManager.instance) {
- WebdavManager.instance = new WebdavManager();
- }
- return WebdavManager.instance;
- }
- public setContext(context: common.Context): void {
- this.context = context;
- this.DownloadDirectoryFilePath = merge2paths(context.filesDir, 'Download');
- }
- // ==================== 观察者模式 ====================
- public subscribe(callback: (event: string) => void): void {
- this.observers.push(callback);
- }
- public unsubscribe(callback: (event: string) => void): void {
- const index = this.observers.indexOf(callback);
- if (index > -1) {
- this.observers.splice(index, 1);
- }
- }
- public notifyObservers(event: string): void {
- Logger.info(TAG, '通知观察者:', event);
- Logger.info(TAG, '观察者数量:', this.observers.length.toString());
- for (let i = 0; i < this.observers.length; i++) {
- const observer = this.observers[i];
- Logger.info(TAG, '调用观察者', i.toString(), ',事件:', event);
- observer(event);
- }
- }
- // ==================== 数据库操作 ====================
- // 创建WebDAV账户表
- public createWebDavTableInDB(): Promise<void> {
- const createTableSql = `CREATE TABLE IF NOT EXISTS ${this.webDavTable} (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- name TEXT,
- isActivate INTEGER DEFAULT 1,
- host TEXT,
- localHost TEXT,
- isUseLocalHost INTEGER DEFAULT 0,
- port INTEGER,
- filepath TEXT,
- imageFilePath TEXT,
- lyricFilePath TEXT,
- uploadFilePath TEXT,
- account TEXT,
- password TEXT,
- enableHttps INTEGER DEFAULT 0,
- coverPath TEXT
- )`;
- return this.dataBaseUtil.executeSql(createTableSql)
- .then(() => {
- Logger.info(TAG, 'WebDAV账户表创建成功');
- // 检查并添加新字段(用于数据库升级)
- return this.upgradeWebDavTable();
- })
- .catch((err: Error) => {
- Logger.error(TAG, '创建WebDAV账户表失败:', err.message);
- throw err;
- });
- }
- // 升级WebDAV表结构
- private async upgradeWebDavTable(): Promise<void> {
- try {
- // 直接尝试添加coverPath字段,如果字段已存在会失败但不影响应用运行
- Logger.info(TAG, '检查数据库表结构,尝试添加coverPath字段...');
- const addColumnSql = `ALTER TABLE ${this.webDavTable} ADD COLUMN coverPath TEXT`;
- await this.dataBaseUtil.executeSql(addColumnSql);
- Logger.info(TAG, 'coverPath字段添加成功,数据库升级完成');
- } catch (error) {
- // 如果添加字段失败(可能字段已存在),记录但不阻止应用启动
- Logger.info(TAG, 'coverPath字段可能已存在或添加失败,继续正常运行');
- }
- }
- // 从数据库查询所有账户
- public async queryWebDavAccountsFromDB(): Promise<void> {
- try {
- // 确保表结构是最新的
- await this.upgradeWebDavTable();
- const predicates = new relationalStore.RdbPredicates(this.webDavTable);
- // 先查询基础字段(确保这些字段在旧版本中存在)
- const baseColumns = ['id', 'name', 'isActivate', 'host', 'localHost', 'isUseLocalHost',
- 'port', 'filepath', 'imageFilePath', 'lyricFilePath', 'uploadFilePath',
- 'account', 'password', 'enableHttps'];
- const resultSet = await this.dataBaseUtil.queryData(this.webDavTable, baseColumns, predicates);
- this.webDavAccounts = [];
- while (resultSet.goToNextRow()) {
- const account = new WebDavAccount();
- account.id = resultSet.getLong(resultSet.getColumnIndex('id'));
- account.name = resultSet.getString(resultSet.getColumnIndex('name'));
- account.isActivate = resultSet.getLong(resultSet.getColumnIndex('isActivate')) === 1;
- 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;
- // 设置coverPath为默认值undefined,稍后会尝试更新
- account.coverPath = undefined;
- this.webDavAccounts.push(account);
- }
- resultSet.close();
- // 尝试查询coverPath字段(如果升级成功)
- try {
- const coverPathResultSet = await this.dataBaseUtil.queryData(this.webDavTable, ['id', 'coverPath'], predicates);
- if (coverPathResultSet.goToFirstRow()) {
- // 创建一个映射来存储coverPath
- const coverPathMap = new Map<number, string>();
- do {
- const accountId = coverPathResultSet.getLong(coverPathResultSet.getColumnIndex('id'));
- const coverPathIndex = coverPathResultSet.getColumnIndex('coverPath');
- const coverPath = coverPathIndex >= 0 ? coverPathResultSet.getString(coverPathIndex) : undefined;
- if (coverPath) {
- coverPathMap.set(accountId, coverPath);
- }
- } while (coverPathResultSet.goToNextRow());
- // 将coverPath值赋给对应的账户
- for (const account of this.webDavAccounts) {
- if (coverPathMap.has(account.id)) {
- account.coverPath = coverPathMap.get(account.id);
- }
- }
- }
- coverPathResultSet.close();
- } catch (error) {
- // 如果查询coverPath失败,说明字段可能不存在,忽略错误
- Logger.info(TAG, 'coverPath字段不存在或查询失败,使用默认值');
- }
- Logger.info(TAG, '从数据库加载了', this.webDavAccounts.length.toString(), '个WebDAV账户');
- this.notifyObservers(WebdavManagerStates.QueryAccountsSucceed);
- } catch (err) {
- const error = err as Error;
- Logger.error(TAG, '查询WebDAV账户失败:', error.message);
- this.notifyObservers(WebdavManagerStates.QueryAccountsFailed);
- throw error;
- }
- }
- // 插入新账户
- public async insertAccount(
- name: string,
- host: string,
- localHost: string,
- isUseLocalHost: boolean,
- port: number,
- filepath: string,
- lyricFilePath: string,
- uploadFilePath: string,
- imageFilePath: string,
- account: string,
- password: string,
- enableHttps: boolean,
- coverPath?: string
- ): Promise<void> {
- try {
- const values: relationalStore.ValuesBucket = {
- 'name': name,
- 'isActivate': 1,
- 'host': host,
- 'localHost': localHost,
- 'isUseLocalHost': isUseLocalHost ? 1 : 0,
- 'port': port,
- 'filepath': filepath,
- 'imageFilePath': imageFilePath,
- 'lyricFilePath': lyricFilePath,
- 'uploadFilePath': uploadFilePath,
- 'account': account,
- 'password': password,
- 'enableHttps': enableHttps ? 1 : 0,
- 'coverPath': coverPath || null
- };
- await this.dataBaseUtil.insertData(this.webDavTable, values);
- Logger.info(TAG, '插入WebDAV账户成功:', name);
- await this.queryWebDavAccountsFromDB();
- this.notifyObservers(WebdavManagerStates.InsertAccountSucceed);
- } catch (err) {
- const error = err as Error;
- Logger.error(TAG, '插入WebDAV账户失败:', error.message);
- this.notifyObservers(WebdavManagerStates.InsertAccountFailed);
- throw error;
- }
- }
- // 编辑账户
- public async editAccount(account: WebDavAccount): Promise<void> {
- try {
- const values: relationalStore.ValuesBucket = {
- 'name': account.name,
- 'isActivate': account.isActivate ? 1 : 0,
- 'host': account.host,
- 'localHost': account.localHost,
- 'isUseLocalHost': account.isUseLocalHost ? 1 : 0,
- 'port': account.port,
- 'filepath': account.filepath,
- 'imageFilePath': account.imageFilePath,
- 'lyricFilePath': account.lyricFilePath,
- 'uploadFilePath': account.uploadFilePath,
- 'account': account.account,
- 'password': account.password,
- 'enableHttps': account.enableHttps ? 1 : 0,
- 'coverPath': account.coverPath || null
- };
- const predicates = new relationalStore.RdbPredicates(this.webDavTable);
- predicates.equalTo('id', account.id);
- await this.dataBaseUtil.updateData(this.webDavTable, values, predicates);
- Logger.info(TAG, '更新WebDAV账户成功:', account.name);
- await this.queryWebDavAccountsFromDB();
- this.notifyObservers(WebdavManagerStates.EditAccountSucceed);
- } catch (err) {
- const error = err as Error;
- Logger.error(TAG, '更新WebDAV账户失败:', error.message);
- this.notifyObservers(WebdavManagerStates.EditAccountFailed);
- throw error;
- }
- }
- // 删除账户
- public async removeAccount(account: WebDavAccount): Promise<void> {
- try {
- const predicates = new relationalStore.RdbPredicates(this.webDavTable);
- predicates.equalTo('id', account.id);
- await this.dataBaseUtil.deleteData(predicates);
- Logger.info(TAG, '删除WebDAV账户成功:', account.name);
- await this.queryWebDavAccountsFromDB();
- this.notifyObservers(WebdavManagerStates.RemoveAccountSucceed);
- } catch (err) {
- const error = err as Error;
- Logger.error(TAG, '删除WebDAV账户失败:', error.message);
- this.notifyObservers(WebdavManagerStates.RemoveAccountFailed);
- throw error;
- }
- }
- // 获取所有账户
- public getAllWebDavAccounts(): WebDavAccount[] {
- return this.webDavAccounts;
- }
- // 获取激活的账户
- public getActivatedWebDavAccount(): WebDavAccount | null {
- if(this.currentAccount)
- return this.currentAccount;
- for (let i = 0; i < this.webDavAccounts.length; i++) {
- const account = this.webDavAccounts[i];
- if (account.isActivate) {
- return account;
- }
- }
- return null;
- }
- // ==================== 文件操作 ====================
- // 从WebDAV加载文件列表
- public async loadFilesInfoFromWebdav(customPath?: string): Promise<void> {
- const account = this.getActivatedWebDavAccount();
- if (!account) {
- Logger.error(TAG, '没有激活的WebDAV账户');
- this.notifyObservers(WebdavManagerStates.LoadFilesInfoFailed);
- return;
- }
- try {
- this.notifyObservers(WebdavManagerStates.LoadFilesInfoStart);
- // 使用自定义路径或账户默认路径
- const path = customPath !== undefined ? customPath : account.filepath;
- this.currentPath = path;
- const files = await this.rcpSocket.getFileList(
- account.host,
- account.localHost,
- account.isUseLocalHost,
- account.port,
- path,
- account.account,
- account.password,
- account.enableHttps
- );
- // 保存所有文件(包括文件夹)
- // 直接使用从RcpSocketUtil返回的FileInfo对象
- this.webDavFiles = [];
- for (let i = 0; i < files.length; i++) {
- const file = files[i];
- // 直接添加原始文件对象
- this.webDavFiles.push(file);
- }
- // 调试:输出获取到的文件总数
- Logger.info(TAG, '从WebDAV获取到 ' + files.length + ' 个文件/文件夹');
- // 分别统计文件夹和音频文件
- let folderCount = 0;
- let audioCount = 0;
- // 过滤音频文件
- this.webDavSongs = [];
- for (let i = 0; i < files.length; i++) {
- const file = files[i];
- const fileName = file.fileName;
- if (file.isDirectory) {
- folderCount++;
- } else if (this.isAudioFile(fileName)) {
- audioCount++;
- const song = this.fileInfoToVideoItem(file, account);
- this.webDavSongs.push(song);
- }
- }
- this.notifyObservers(WebdavManagerStates.LoadFilesInfoSucceed);
- } catch (error) {
- this.ErrorMessage = error as BusinessError;
- this.notifyObservers(WebdavManagerStates.LoadFilesInfoFailed);
- }
- }
- public async loadFilesInfoFromAccount(account: WebDavAccount,customPath?: string): Promise<void> {
- this.currentAccount = account;
- try {
- this.notifyObservers(WebdavManagerStates.LoadFilesInfoStart);
- // 使用自定义路径或账户默认路径
- const path = customPath !== undefined ? customPath : account.filepath;
- this.currentPath = path;
- const files = await this.rcpSocket.getFileList(
- account.host,
- account.localHost,
- account.isUseLocalHost,
- account.port,
- path,
- account.account,
- account.password,
- account.enableHttps
- );
- // 保存所有文件(包括文件夹)
- // 直接使用从RcpSocketUtil返回的FileInfo对象
- this.webDavFiles = [];
- for (let i = 0; i < files.length; i++) {
- const file = files[i];
- // 直接添加原始文件对象
- this.webDavFiles.push(file);
- }
- // 调试:输出获取到的文件总数
- Logger.info(TAG, '从WebDAV获取到 ' + files.length + ' 个文件/文件夹');
- // 分别统计文件夹和音频文件
- let folderCount = 0;
- let audioCount = 0;
- // 过滤音频文件
- this.webDavSongs = [];
- for (let i = 0; i < files.length; i++) {
- const file = files[i];
- const fileName = file.fileName;
- if (file.isDirectory) {
- folderCount++;
- } else if (this.isAudioFile(fileName)) {
- audioCount++;
- const song = this.fileInfoToVideoItem(file, account);
- this.webDavSongs.push(song);
- }
- }
- this.notifyObservers(WebdavManagerStates.LoadFilesInfoSucceed);
- } catch (error) {
- this.ErrorMessage = error as BusinessError;
- this.notifyObservers(WebdavManagerStates.LoadFilesInfoFailed);
- }
- }
- // 将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,不包含认证信息
- 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;
- }
- // 判断是否为音频文件
- private isAudioFile(fileName: string): boolean {
- const ext = this.getFileExtension(fileName).toLowerCase();
- for (let i = 0; i < this.audioExtensions.length; i++) {
- if (ext === this.audioExtensions[i]) {
- return true;
- }
- }
- return false;
- }
- // 获取文件扩展名
- private getFileExtension(fileName: string): string {
- const lastDotIndex = fileName.lastIndexOf('.');
- if (lastDotIndex === -1) {
- return '';
- }
- return fileName.substring(lastDotIndex);
- }
- // 获取不带扩展名的文件名
- private getFileNameWithoutExtension(fileName: string): string {
- const lastDotIndex = fileName.lastIndexOf('.');
- if (lastDotIndex === -1) {
- return fileName;
- }
- return fileName.substring(0, lastDotIndex);
- }
- // 进入文件夹
- public async enterFolder(folder: FileInfo): Promise<void> {
- if (!folder.isDirectory) {
- Logger.error(TAG, '不是文件夹,无法进入');
- return;
- }
- Logger.info(TAG, '准备进入文件夹:', folder.fileName);
- Logger.info(TAG, '当前路径:', this.currentPath);
- Logger.info(TAG, '目标路径:', folder.href);
- // 保存当前路径到历史记录
- this.pathHistory.push(this.currentPath);
- Logger.info(TAG, '路径历史:', JSON.stringify(this.pathHistory));
- // 加载文件夹内容
- await this.loadFilesInfoFromWebdav(folder.href);
- }
- public async enterFolderFromPath(path: string): Promise<void> {
- Logger.info(TAG, '当前路径:', this.currentPath);
- Logger.info(TAG, '目标路径:', path);
- // 保存当前路径到历史记录
- this.pathHistory.push(this.currentPath);
- Logger.info(TAG, '路径历史:', JSON.stringify(this.pathHistory));
- // 加载文件夹内容
- await this.loadFilesInfoFromWebdav(path);
- }
- // 在 WebdavManager 类中添加以下方法
- navigateToBreadcrumb(breadcrumbIndex: number): Promise<string> {
- return new Promise((resolve, reject) => {
- try {
- // 面包屑索引0是"根目录"
- if (breadcrumbIndex === 0) {
- resolve('/');
- return;
- }
- // 根据当前路径构建目标路径
- const pathParts = this.currentPath.split('/').filter(part => part !== '');
- // 验证索引有效性
- if (breadcrumbIndex > pathParts.length) {
- reject(new Error('Invalid breadcrumb index'));
- return;
- }
- // 构建目标路径
- let targetPath = '/';
- for (let i = 0; i < breadcrumbIndex; i++) {
- targetPath += pathParts[i] + '/';
- }
- resolve(targetPath);
- } catch (error) {
- reject(error);
- }
- });
- }
- // 返回上级目录
- public async goBack(): Promise<void> {
- if (this.pathHistory.length === 0) {
- Logger.info(TAG, '已经在根目录,无法返回');
- return;
- }
- // 从历史记录中取出上一级路径
- const previousPath = this.pathHistory.pop();
- if (previousPath !== undefined) {
- await this.loadFilesInfoFromWebdav(previousPath);
- }
- }
- // 获取面包屑路径数组
- public getBreadcrumbs(): string[] {
- if (!this.currentPath || this.currentPath === '/') {
- return ['根目录'];
- }
- const parts = this.currentPath.split('/').filter(part => part !== '');
- const breadcrumbs = ['根目录'];
- for (let i = 0; i < parts.length; i++) {
- breadcrumbs.push(parts[i]);
- }
- return breadcrumbs;
- }
- // 是否可以返回上级
- public canGoBack(): boolean {
- return this.pathHistory.length > 0;
- }
- // ==================== Preferences操作 ====================
- // 加载历史数据(从Preferences迁移)
- public async loadInfo(): Promise<void> {
- try {
- // 这里可以添加从Preferences加载历史配置的逻辑
- Logger.info(TAG, '从Preferences加载配置');
- } catch (error) {
- Logger.error(TAG, '从Preferences加载配置失败:', error.toString());
- }
- }
- // 保存配置到Preferences
- public async saveInfo(): Promise<void> {
- try {
- // 这里可以添加保存配置到Preferences的逻辑
- Logger.info(TAG, '保存配置到Preferences');
- } catch (error) {
- Logger.error(TAG, '保存配置到Preferences失败:', error.toString());
- }
- }
- // ==================== 下载队列管理 ====================
- // 添加到下载队列
- public addToDownloadQueue(song: VideoItem, account: WebDavAccount): void {
- const task: TransferTask = { song, account };
- this.downloadQueue.push(task);
- Logger.info(TAG, '添加到下载队列:', song.name);
- this.notifyObservers(WebdavManagerStates.DownloadQueueChanged);
- }
- // 从下载队列移除
- public removeFromDownloadQueue(index: number): void {
- if (index >= 0 && index < this.downloadQueue.length) {
- const task = this.downloadQueue[index];
- this.downloadQueue.splice(index, 1);
- Logger.info(TAG, '从下载队列移除:', task.song.name);
- this.notifyObservers(WebdavManagerStates.DownloadQueueChanged);
- }
- }
- // 清空下载队列
- public clearDownloadQueue(): void {
- this.downloadQueue = [];
- Logger.info(TAG, '清空下载队列');
- this.notifyObservers(WebdavManagerStates.DownloadQueueChanged);
- }
- // ==================== 上传队列管理 ====================
- // 添加到上传队列
- public addToUploadQueue(song: VideoItem, account: WebDavAccount): void {
- const task: TransferTask = { song, account };
- this.uploadQueue.push(task);
- Logger.info(TAG, '添加到上传队列:', song.name);
- this.notifyObservers(WebdavManagerStates.UploadQueueChanged);
- }
- // 从上传队列移除
- public removeFromUploadQueue(index: number): void {
- if (index >= 0 && index < this.uploadQueue.length) {
- const task = this.uploadQueue[index];
- this.uploadQueue.splice(index, 1);
- Logger.info(TAG, '从上传队列移除:', task.song.name);
- this.notifyObservers(WebdavManagerStates.UploadQueueChanged);
- }
- }
- // 清空上传队列
- public clearUploadQueue(): void {
- this.uploadQueue = [];
- 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;
- }
- }
- /**
- * 构建HTTP请求头,特别处理WebDAV认证
- * @param currentSong - 当前播放的歌曲信息
- * @param videoUrl - 当前歌曲的URL
- * @param webDavAuthItem - 当前WebDAV认证信息(实例变量)
- * @returns Map<string, string> HTTP请求头
- */
- export function buildHttpHeadersWithWebDav(
- currentSong: VideoItem | undefined,
- videoUrl: string,
- webDavAuthItem: WebDavAuthItem
- ): Map<string, string> {
- const headers = new Map<string, string>();
- let isWebDavSong = false;
- if (currentSong) {
- Logger.info(`heanup 当前歌曲信息 - name: ${currentSong.name}, type: ${currentSong.type}, filePath: ${currentSong.filePath}`);
- // 检查是否为WebDAV歌曲
- if (currentSong.type === CommonConstants.TYPE_WEBDAV) {
- isWebDavSong = true;
- // 优先从实例变量获取认证信息
- if (webDavAuthItem) {
- Logger.info(`heanup 从实例变量获取到WebDAV认证信息,账户ID: ${webDavAuthItem.accountId}`);
- } else {
- // 回退到全局上下文
- try {
- const globalContext = GlobalContext.getContext();
- webDavAuthItem = globalContext.getObject('webDavAuthInfo') as WebDavAuthItem;
- if (webDavAuthItem) {
- Logger.info(`heanup 从全局上下文获取到WebDAV认证信息,账户ID: ${webDavAuthItem.accountId}`);
- } else {
- Logger.warn(`heanup 全局上下文中没有WebDAV认证信息`);
- }
- } catch (error) {
- Logger.error(`heanup 获取全局上下文失败:`, error.toString());
- }
- }
- // 如果识别为WebDAV但没有认证信息,记录警告
- if (!webDavAuthItem) {
- Logger.warn(`heanup 识别为WebDAV播放但缺少认证信息,可能需要手动配置WebDAV账户`);
- }
- }
- if (isWebDavSong && webDavAuthItem) {
- Logger.info(`heanup 检测到WebDAV播放,添加安全认证头`);
- Logger.info(`heanup 歌曲URL: ${videoUrl}`);
- try {
- if (webDavAuthItem && webDavAuthItem.accountId) {
- Logger.info(`heanup 找到WebDAV认证信息,账户ID: ${webDavAuthItem.accountId}`);
- // 使用WebdavManager获取认证头
- const webdavManager = WebdavManager.getInstance();
- const authHeaders = webdavManager.getWebDavAuthHeaders(webDavAuthItem.accountId);
- if (authHeaders) {
- // 添加Basic认证头
- headers.set("authorization", authHeaders.headers.Authorization);
- } else {
- Logger.error(`heanup 无法获取WebDAV认证头`);
- }
- } else {
- Logger.error(`heanup WebDAV认证信息无效或缺少accountId`);
- }
- } 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");
- Logger.info(`heanup WebDAV安全认证头设置完成`);
- }
- }
- // 输出所有设置的头部信息用于调试
- 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];
- console.log(`heanup ${key}: ${value}`);
- headerEntry = headerIterator.next();
- }
-
- return headers;
- }
- /**
- * WebDAV认证信息(LocalMusic专用)
- */
- export interface WebDavAuthItem {
- accountId: number;
- host: string;
- port: number;
- account: string;
- password: string;
- enableHttps: boolean;
- }
|