|
@@ -64,6 +64,22 @@ const DB_COLUMNS: DBColumnsInterface = {
|
|
|
REMOTE_REL_PATH: 'remote_rel_path'
|
|
REMOTE_REL_PATH: 'remote_rel_path'
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 统一的 filePath 规范化方法:
|
|
|
|
|
+ * - WebDAV 的 HTTP/HTTPS 地址转为相对路径,便于与数据库匹配
|
|
|
|
|
+ * - 其他路径保持不变
|
|
|
|
|
+ */
|
|
|
|
|
+function normalizeFilePath(filePath: string): string {
|
|
|
|
|
+ if (!filePath) {
|
|
|
|
|
+ return filePath;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (WebDavUrlUtil.isHttpUrl(filePath)) {
|
|
|
|
|
+ const relative = WebDavUrlUtil.toStoragePath(filePath);
|
|
|
|
|
+ return relative || filePath;
|
|
|
|
|
+ }
|
|
|
|
|
+ return filePath;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
export default class MediaTable {
|
|
export default class MediaTable {
|
|
|
private accountTable = new RdbUtils(RdbUtils.MEDIA_TABLE.tableName, RdbUtils.MEDIA_TABLE.sqlCreate,
|
|
private accountTable = new RdbUtils(RdbUtils.MEDIA_TABLE.tableName, RdbUtils.MEDIA_TABLE.sqlCreate,
|
|
|
RdbUtils.MEDIA_TABLE.columns);
|
|
RdbUtils.MEDIA_TABLE.columns);
|
|
@@ -77,10 +93,11 @@ export default class MediaTable {
|
|
|
* 判断指定 filePath 是否已存在
|
|
* 判断指定 filePath 是否已存在
|
|
|
*/
|
|
*/
|
|
|
private async existsByFilePath(filePath: string): Promise<boolean> {
|
|
private async existsByFilePath(filePath: string): Promise<boolean> {
|
|
|
|
|
+ const normalizedPath = normalizeFilePath(filePath);
|
|
|
return new Promise<boolean>((resolve, reject) => {
|
|
return new Promise<boolean>((resolve, reject) => {
|
|
|
try {
|
|
try {
|
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- predicates.equalTo(DB_COLUMNS.FILE_PATH, filePath);
|
|
|
|
|
|
|
+ predicates.equalTo(DB_COLUMNS.FILE_PATH, normalizedPath);
|
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
|
try {
|
|
try {
|
|
|
resolve(resultSet.rowCount > 0);
|
|
resolve(resultSet.rowCount > 0);
|
|
@@ -115,6 +132,7 @@ export default class MediaTable {
|
|
|
item.remote_rel_path = relativePath;
|
|
item.remote_rel_path = relativePath;
|
|
|
// 更新filePath为相对路径
|
|
// 更新filePath为相对路径
|
|
|
item.filePath = relativePath;
|
|
item.filePath = relativePath;
|
|
|
|
|
+ item.dbFilePath = relativePath;
|
|
|
Logger.info(RdbUtils.RDB_TAG, `WebDAV URL转换: 原始URL -> 存储路径: ${relativePath}`);
|
|
Logger.info(RdbUtils.RDB_TAG, `WebDAV URL转换: 原始URL -> 存储路径: ${relativePath}`);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -160,6 +178,38 @@ export default class MediaTable {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 保存或更新WebDAV歌曲的完整元数据
|
|
|
|
|
+ */
|
|
|
|
|
+ public async saveOrUpdateWebDavItem(item: VideoItem): Promise<boolean> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (!item || !item.filePath) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ item.filePath = normalizeFilePath(item.filePath);
|
|
|
|
|
+ if (!item.remote_rel_path) {
|
|
|
|
|
+ item.remote_rel_path = item.filePath;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!item.parentPath) {
|
|
|
|
|
+ const idx = item.filePath.lastIndexOf('/');
|
|
|
|
|
+ item.parentPath = idx > 0 ? item.filePath.substring(0, idx) : '';
|
|
|
|
|
+ }
|
|
|
|
|
+ const exists = await this.existsByFilePath(item.filePath);
|
|
|
|
|
+ if (exists) {
|
|
|
|
|
+ return await new Promise<boolean>((resolve) => {
|
|
|
|
|
+ const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
|
|
+ predicates.equalTo(DB_COLUMNS.FILE_PATH, item.filePath);
|
|
|
|
|
+ const bucket = generateBucket(item);
|
|
|
|
|
+ this.accountTable.updateData(predicates, bucket, (success: boolean) => resolve(success));
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ return await this.insertWebDavItem(item);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Logger.error(RdbUtils.RDB_TAG, 'saveOrUpdateWebDavItem 失败: ' + (error as Error).message);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
getRdbStore(context:Context,callback: Function = () => {
|
|
getRdbStore(context:Context,callback: Function = () => {
|
|
|
}) {
|
|
}) {
|
|
|
this.accountTable.getRdbStore(context,callback);
|
|
this.accountTable.getRdbStore(context,callback);
|
|
@@ -178,8 +228,9 @@ export default class MediaTable {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
deleteDataFilePath(filePath: string, callback: Function) {
|
|
deleteDataFilePath(filePath: string, callback: Function) {
|
|
|
|
|
+ const normalizedPath = normalizeFilePath(filePath);
|
|
|
let predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
let predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- predicates.equalTo('filePath', filePath);
|
|
|
|
|
|
|
+ predicates.equalTo('filePath', normalizedPath);
|
|
|
this.accountTable.deleteData(predicates, callback);
|
|
this.accountTable.deleteData(predicates, callback);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -191,9 +242,10 @@ export default class MediaTable {
|
|
|
|
|
|
|
|
//更新音乐封面地址
|
|
//更新音乐封面地址
|
|
|
public updatePixelMapPath(filePath: string, newPixelMapPath: string, callback: Function) {
|
|
public updatePixelMapPath(filePath: string, newPixelMapPath: string, callback: Function) {
|
|
|
|
|
+ const normalizedPath = normalizeFilePath(filePath);
|
|
|
// Step 1: 构建查询条件验证文件存在性
|
|
// Step 1: 构建查询条件验证文件存在性
|
|
|
const queryPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
const queryPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- queryPredicates.equalTo('filePath', filePath);
|
|
|
|
|
|
|
+ queryPredicates.equalTo('filePath', normalizedPath);
|
|
|
|
|
|
|
|
// Step 2: 执行存在性验证
|
|
// Step 2: 执行存在性验证
|
|
|
this.accountTable.query(queryPredicates, (resultSet: relationalStore.ResultSet) => {
|
|
this.accountTable.query(queryPredicates, (resultSet: relationalStore.ResultSet) => {
|
|
@@ -206,7 +258,7 @@ export default class MediaTable {
|
|
|
|
|
|
|
|
// Step 3: 构建更新条件与数据
|
|
// Step 3: 构建更新条件与数据
|
|
|
const updatePredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
const updatePredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- updatePredicates.equalTo('filePath', filePath);
|
|
|
|
|
|
|
+ updatePredicates.equalTo('filePath', normalizedPath);
|
|
|
const valueBucket: relationalStore.ValuesBucket = {
|
|
const valueBucket: relationalStore.ValuesBucket = {
|
|
|
pixelMapPath: newPixelMapPath
|
|
pixelMapPath: newPixelMapPath
|
|
|
};
|
|
};
|
|
@@ -241,21 +293,22 @@ export default class MediaTable {
|
|
|
callback(false);
|
|
callback(false);
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
- console.info('onecold filePath updateMediaInfo= '+filePath)
|
|
|
|
|
|
|
+ const normalizedPath = normalizeFilePath(filePath);
|
|
|
|
|
+ console.info('onecold filePath updateMediaInfo= '+normalizedPath)
|
|
|
// Step 1: Create a predicate to find the record by filePath
|
|
// Step 1: Create a predicate to find the record by filePath
|
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- predicates.equalTo('filePath', filePath);
|
|
|
|
|
|
|
+ predicates.equalTo('filePath', normalizedPath);
|
|
|
|
|
|
|
|
// Step 2: Query the database to check if the record exists
|
|
// Step 2: Query the database to check if the record exists
|
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
|
if (resultSet.rowCount === 0) {
|
|
if (resultSet.rowCount === 0) {
|
|
|
- Logger.info(RdbUtils.RDB_TAG, `No record found with filePath ${filePath}.`);
|
|
|
|
|
- console.info('onecold No record found with filePath '+filePath)
|
|
|
|
|
|
|
+ Logger.info(RdbUtils.RDB_TAG, `No record found with filePath ${normalizedPath}.`);
|
|
|
|
|
+ console.info('onecold No record found with filePath '+normalizedPath)
|
|
|
callback(false);
|
|
callback(false);
|
|
|
resultSet.close();
|
|
resultSet.close();
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- console.info('onecold filePath updateMediaInfo1= '+filePath)
|
|
|
|
|
|
|
+ console.info('onecold filePath updateMediaInfo1= '+normalizedPath)
|
|
|
// Step 3: Prepare the values to update
|
|
// Step 3: Prepare the values to update
|
|
|
const valuesToUpdate: relationalStore.ValuesBucket = {};
|
|
const valuesToUpdate: relationalStore.ValuesBucket = {};
|
|
|
if (title !== '') {
|
|
if (title !== '') {
|
|
@@ -314,9 +367,11 @@ export default class MediaTable {
|
|
|
|
|
|
|
|
//更新重命名数据操作
|
|
//更新重命名数据操作
|
|
|
public updateRename(newName: string, oldPath: string, newPath: string, callback: Function) {
|
|
public updateRename(newName: string, oldPath: string, newPath: string, callback: Function) {
|
|
|
|
|
+ const normalizedOldPath = normalizeFilePath(oldPath);
|
|
|
|
|
+ const normalizedNewPath = normalizeFilePath(newPath);
|
|
|
// Step 1: 查询原始记录
|
|
// Step 1: 查询原始记录
|
|
|
const queryPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
const queryPredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- queryPredicates.equalTo('filePath', oldPath);
|
|
|
|
|
|
|
+ queryPredicates.equalTo('filePath', normalizedOldPath);
|
|
|
|
|
|
|
|
this.accountTable.query(queryPredicates, (resultSet: relationalStore.ResultSet) => {
|
|
this.accountTable.query(queryPredicates, (resultSet: relationalStore.ResultSet) => {
|
|
|
if (resultSet.rowCount === 0) {
|
|
if (resultSet.rowCount === 0) {
|
|
@@ -331,8 +386,8 @@ export default class MediaTable {
|
|
|
const currentFileName = resultSet.getString(resultSet.getColumnIndex('fileName'));
|
|
const currentFileName = resultSet.getString(resultSet.getColumnIndex('fileName'));
|
|
|
|
|
|
|
|
let obj: relationalStore.ValuesBucket = {};
|
|
let obj: relationalStore.ValuesBucket = {};
|
|
|
- obj.id = newPath
|
|
|
|
|
- obj.filePath = newPath;
|
|
|
|
|
|
|
+ obj.id = normalizedNewPath
|
|
|
|
|
+ obj.filePath = normalizedNewPath;
|
|
|
if (currentName === currentFileName) {
|
|
if (currentName === currentFileName) {
|
|
|
obj.name = newName;
|
|
obj.name = newName;
|
|
|
obj.fileName = newName;
|
|
obj.fileName = newName;
|
|
@@ -382,7 +437,7 @@ export default class MediaTable {
|
|
|
const valueBucket: relationalStore.ValuesBucket = obj
|
|
const valueBucket: relationalStore.ValuesBucket = obj
|
|
|
// Step 4: 执行更新
|
|
// Step 4: 执行更新
|
|
|
const updatePredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
const updatePredicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- updatePredicates.equalTo('filePath', oldPath);
|
|
|
|
|
|
|
+ updatePredicates.equalTo('filePath', normalizedOldPath);
|
|
|
|
|
|
|
|
this.accountTable.updateData(updatePredicates, valueBucket, (success: boolean) => {
|
|
this.accountTable.updateData(updatePredicates, valueBucket, (success: boolean) => {
|
|
|
callback(success, success ? null : 'Update failed');
|
|
callback(success, success ? null : 'Update failed');
|
|
@@ -404,8 +459,9 @@ export default class MediaTable {
|
|
|
|
|
|
|
|
// 根据filePath更新isFav的值
|
|
// 根据filePath更新isFav的值
|
|
|
public updateIsFavByFilePath(filePath: string, isFav: number, callback: (success: boolean, error?: string) => void) {
|
|
public updateIsFavByFilePath(filePath: string, isFav: number, callback: (success: boolean, error?: string) => void) {
|
|
|
|
|
+ const normalizedPath = normalizeFilePath(filePath);
|
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- predicates.equalTo('filePath', filePath);
|
|
|
|
|
|
|
+ predicates.equalTo('filePath', normalizedPath);
|
|
|
|
|
|
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
|
if (resultSet.rowCount === 0) {
|
|
if (resultSet.rowCount === 0) {
|
|
@@ -579,8 +635,9 @@ export default class MediaTable {
|
|
|
|
|
|
|
|
// 根据filePath更新lastPlayedStr的值同时playCount值加1
|
|
// 根据filePath更新lastPlayedStr的值同时playCount值加1
|
|
|
public updateLastPlayedStrByFilePath(filePath: string, lastPlayedStr: string, callback: (success: boolean, error?: string) => void) {
|
|
public updateLastPlayedStrByFilePath(filePath: string, lastPlayedStr: string, callback: (success: boolean, error?: string) => void) {
|
|
|
|
|
+ const normalizedPath = normalizeFilePath(filePath);
|
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- predicates.equalTo('filePath', filePath);
|
|
|
|
|
|
|
+ predicates.equalTo('filePath', normalizedPath);
|
|
|
|
|
|
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
|
if (resultSet.rowCount === 0) {
|
|
if (resultSet.rowCount === 0) {
|
|
@@ -798,16 +855,17 @@ export default class MediaTable {
|
|
|
* @returns Promise that resolves with the lyric content (string) or null if not found
|
|
* @returns Promise that resolves with the lyric content (string) or null if not found
|
|
|
*/
|
|
*/
|
|
|
public getLyricContentByFilePath(filePath: string): Promise<string | null> {
|
|
public getLyricContentByFilePath(filePath: string): Promise<string | null> {
|
|
|
|
|
+ const normalizedPath = normalizeFilePath(filePath);
|
|
|
return new Promise((resolve, reject) => {
|
|
return new Promise((resolve, reject) => {
|
|
|
// Create query predicates
|
|
// Create query predicates
|
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- predicates.equalTo(DB_COLUMNS.FILE_PATH, filePath);
|
|
|
|
|
|
|
+ predicates.equalTo(DB_COLUMNS.FILE_PATH, normalizedPath);
|
|
|
|
|
|
|
|
// Execute the query
|
|
// Execute the query
|
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
|
try {
|
|
try {
|
|
|
if (resultSet.rowCount === 0) {
|
|
if (resultSet.rowCount === 0) {
|
|
|
- Logger.info(RdbUtils.RDB_TAG, `No record found for filePath: ${filePath}`);
|
|
|
|
|
|
|
+ Logger.info(RdbUtils.RDB_TAG, `No record found for filePath: ${normalizedPath}`);
|
|
|
resolve(null);
|
|
resolve(null);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -843,11 +901,12 @@ export default class MediaTable {
|
|
|
* @returns Promise that resolves to true if record exists, false otherwise
|
|
* @returns Promise that resolves to true if record exists, false otherwise
|
|
|
*/
|
|
*/
|
|
|
public isRecordExists(filePath: string): Promise<boolean> {
|
|
public isRecordExists(filePath: string): Promise<boolean> {
|
|
|
|
|
+ const normalizedPath = normalizeFilePath(filePath);
|
|
|
return new Promise((resolve, reject) => {
|
|
return new Promise((resolve, reject) => {
|
|
|
try {
|
|
try {
|
|
|
// Create query predicates
|
|
// Create query predicates
|
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- predicates.equalTo(DB_COLUMNS.FILE_PATH, filePath);
|
|
|
|
|
|
|
+ predicates.equalTo(DB_COLUMNS.FILE_PATH, normalizedPath);
|
|
|
|
|
|
|
|
// Execute the query
|
|
// Execute the query
|
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
@@ -875,17 +934,18 @@ export default class MediaTable {
|
|
|
* @returns Promise that resolves with the VideoItem or null if not found
|
|
* @returns Promise that resolves with the VideoItem or null if not found
|
|
|
*/
|
|
*/
|
|
|
public queryVideoByFilePath(filePath: string): Promise<VideoItem | null> {
|
|
public queryVideoByFilePath(filePath: string): Promise<VideoItem | null> {
|
|
|
|
|
+ const normalizedPath = normalizeFilePath(filePath);
|
|
|
return new Promise((resolve, reject) => {
|
|
return new Promise((resolve, reject) => {
|
|
|
try {
|
|
try {
|
|
|
// Create query predicates
|
|
// Create query predicates
|
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
const predicates = new relationalStore.RdbPredicates(RdbUtils.MEDIA_TABLE.tableName);
|
|
|
- predicates.equalTo(DB_COLUMNS.FILE_PATH, filePath);
|
|
|
|
|
|
|
+ predicates.equalTo(DB_COLUMNS.FILE_PATH, normalizedPath);
|
|
|
|
|
|
|
|
// Execute the query
|
|
// Execute the query
|
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
this.accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
|
|
|
try {
|
|
try {
|
|
|
if (resultSet.rowCount === 0) {
|
|
if (resultSet.rowCount === 0) {
|
|
|
- Logger.info(RdbUtils.RDB_TAG, `No record found for filePath: ${filePath}`);
|
|
|
|
|
|
|
+ Logger.info(RdbUtils.RDB_TAG, `No record found for filePath: ${normalizedPath}`);
|
|
|
resolve(null);
|
|
resolve(null);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -976,7 +1036,8 @@ function generateBucket(item: VideoItem): relationalStore.ValuesBucket {
|
|
|
let obj: relationalStore.ValuesBucket = {};
|
|
let obj: relationalStore.ValuesBucket = {};
|
|
|
obj.id = item.id
|
|
obj.id = item.id
|
|
|
obj.name = item.name;
|
|
obj.name = item.name;
|
|
|
- obj.filePath = item.filePath;
|
|
|
|
|
|
|
+ const normalizedPath = normalizeFilePath(item.filePath);
|
|
|
|
|
+ obj.filePath = normalizedPath;
|
|
|
obj.mtype = item.type;
|
|
obj.mtype = item.type;
|
|
|
obj.videoSize = item.videoSize;
|
|
obj.videoSize = item.videoSize;
|
|
|
obj.cTime = item.cTime;
|
|
obj.cTime = item.cTime;
|