|
@@ -406,8 +406,9 @@ export class Utility {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
// 获取缩略图
|
|
// 获取缩略图
|
|
|
- static async getFetchFrameByTime(filePath: string) {
|
|
|
|
|
|
|
+ static async getFetchFrameByTime(filePath: string,time?:number) {
|
|
|
if(Utility.isMusicByExtension(filePath)){
|
|
if(Utility.isMusicByExtension(filePath)){
|
|
|
return undefined
|
|
return undefined
|
|
|
}
|
|
}
|
|
@@ -420,6 +421,10 @@ export class Utility {
|
|
|
avImageGenerator.fdSrc = avFileDescriptor;
|
|
avImageGenerator.fdSrc = avFileDescriptor;
|
|
|
// 初始化入参
|
|
// 初始化入参
|
|
|
let timeUs = 0
|
|
let timeUs = 0
|
|
|
|
|
+ console.info('onecold time='+time)
|
|
|
|
|
+ if(time){
|
|
|
|
|
+ timeUs = (time > 0) ? time*60 : 0
|
|
|
|
|
+ }
|
|
|
let queryOption = media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC
|
|
let queryOption = media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC
|
|
|
let param: media.PixelMapParams = {
|
|
let param: media.PixelMapParams = {
|
|
|
width : 300,
|
|
width : 300,
|
|
@@ -858,6 +863,7 @@ export class Utility {
|
|
|
if (!name) {
|
|
if (!name) {
|
|
|
name = getFileNameWithoutExtension(inputPath);
|
|
name = getFileNameWithoutExtension(inputPath);
|
|
|
}
|
|
}
|
|
|
|
|
+ let pixelMap:image.PixelMap|undefined|null = undefined
|
|
|
// Create VideoItem
|
|
// Create VideoItem
|
|
|
videoItem = new VideoItem(
|
|
videoItem = new VideoItem(
|
|
|
name,
|
|
name,
|
|
@@ -907,9 +913,13 @@ export class Utility {
|
|
|
}
|
|
}
|
|
|
videoItem.pixelMapPath = imagePath;
|
|
videoItem.pixelMapPath = imagePath;
|
|
|
}else if(Utility.isVideoByExtension(inputPath)){
|
|
}else if(Utility.isVideoByExtension(inputPath)){
|
|
|
- //提取封面
|
|
|
|
|
- await getVideoFFmpegCover(inputPath, imagePath);
|
|
|
|
|
- videoItem.mimeType = getFileFormatByPath(inputPath)
|
|
|
|
|
|
|
+ pixelMap = await Utility.getFetchFrameByTime(inputPath)
|
|
|
|
|
+ if(pixelMap!==undefined&&pixelMap!==null){
|
|
|
|
|
+ imagePath = await ImageUtil.savePixelMap(pixelMap,context.filesDir,md5Name)
|
|
|
|
|
+ imagePath = fileUri.getUriFromPath(imagePath)
|
|
|
|
|
+ }else{
|
|
|
|
|
+ imagePath = ''
|
|
|
|
|
+ }
|
|
|
imagePath = fileUri.getUriFromPath(imagePath)
|
|
imagePath = fileUri.getUriFromPath(imagePath)
|
|
|
videoItem.pixelMapPath = imagePath;
|
|
videoItem.pixelMapPath = imagePath;
|
|
|
}
|
|
}
|
|
@@ -1502,57 +1512,122 @@ function formatDuration(seconds: string, forceHHMMSS: boolean = false): string {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
-// 定义解析结果的数据结构
|
|
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 定义解析结果的数据结构
|
|
|
|
|
+ */
|
|
|
class MusicInfo {
|
|
class MusicInfo {
|
|
|
artist: string = ""; // 艺术家名称
|
|
artist: string = ""; // 艺术家名称
|
|
|
- title: string = ""; // 歌曲名称
|
|
|
|
|
|
|
+ title: string = ""; // 歌曲名称
|
|
|
isValid: boolean = false; // 格式是否有效
|
|
isValid: boolean = false; // 格式是否有效
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
- * 解析音乐文件名
|
|
|
|
|
- * @param fileName - 待解析的文件名(需包含扩展名)
|
|
|
|
|
- * @returns 包含解析结果的MusicInfo对象
|
|
|
|
|
|
|
+ * 精简版常见中文姓氏(音乐场景优化版)
|
|
|
*/
|
|
*/
|
|
|
|
|
+const COMMON_CHINESE_SURNAMES = [
|
|
|
|
|
+// 超常见姓氏(覆盖约80%人口)
|
|
|
|
|
+ '李','王','张','刘','陈','杨','黄','赵','吴','周',
|
|
|
|
|
+ '徐','孙','马','朱','胡','林','郭','何','高','罗',
|
|
|
|
|
+ '郑','梁','谢','宋','唐','许','韩','邓','曹','彭',
|
|
|
|
|
+ '曾','萧','田','董','潘','袁','于','蔡','余','杜',
|
|
|
|
|
+
|
|
|
|
|
+ // 音乐行业常见姓氏(歌手高频姓)
|
|
|
|
|
+ '汪','苏','薛','谭','华','金','魏','陶','姜','窦',
|
|
|
|
|
+ '章','毛','易','方','宋','任','沈','贾','江','孔',
|
|
|
|
|
+
|
|
|
|
|
+ // 组合/乐队常见字
|
|
|
|
|
+ '羽','泉','信','乐','花','飞','龙','风','云','草'
|
|
|
|
|
+];
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
- * 智能解析音乐文件名(支持多种分隔符和前缀序号)
|
|
|
|
|
|
|
+ * 歌手特征关键词(优化版)
|
|
|
|
|
+ */
|
|
|
|
|
+const ARTIST_KEYWORDS = [
|
|
|
|
|
+// 中文特征
|
|
|
|
|
+ '乐队','组合','乐团','和','&','合唱',' featuring',
|
|
|
|
|
+ // 英文特征
|
|
|
|
|
+ 'feat','ft','with','vs','presents','presents',
|
|
|
|
|
+ // 符号特征
|
|
|
|
|
+ '×','X','※','◆','♫'
|
|
|
|
|
+];
|
|
|
|
|
+/**
|
|
|
|
|
+ * 智能解析音乐文件名(支持多种分隔符和格式)
|
|
|
* @param fileName - 待解析的完整文件名
|
|
* @param fileName - 待解析的完整文件名
|
|
|
* @returns 结构化音乐信息
|
|
* @returns 结构化音乐信息
|
|
|
*/
|
|
*/
|
|
|
|
|
+/**
|
|
|
|
|
+ * 智能解析音乐文件名
|
|
|
|
|
+ */
|
|
|
function parseMusicFileName(fileName: string): MusicInfo {
|
|
function parseMusicFileName(fileName: string): MusicInfo {
|
|
|
- const result = new MusicInfo();
|
|
|
|
|
-
|
|
|
|
|
- // 1. 预处理:移除首尾空格(保留中间空格)
|
|
|
|
|
- const cleanName = fileName.trim();
|
|
|
|
|
|
|
+ const result: MusicInfo = new MusicInfo();
|
|
|
|
|
+ if (!fileName) return result;
|
|
|
|
|
|
|
|
- // 2. 提取文件扩展名(以最后一个点分隔)
|
|
|
|
|
- const lastDotIndex = cleanName.lastIndexOf('.');
|
|
|
|
|
- if (lastDotIndex < 0) return result; // 无扩展名
|
|
|
|
|
|
|
+ // 预处理
|
|
|
|
|
+ const cleanName: string = fileName.trim();
|
|
|
|
|
+ const lastDotIndex: number = cleanName.lastIndexOf('.');
|
|
|
|
|
+ const baseName: string = lastDotIndex > 0 ?
|
|
|
|
|
+ cleanName.substring(0, lastDotIndex).trim() :
|
|
|
|
|
+ cleanName;
|
|
|
|
|
|
|
|
- const baseName = cleanName.substring(0, lastDotIndex).trim();
|
|
|
|
|
- const extension = cleanName.substring(lastDotIndex + 1);
|
|
|
|
|
|
|
+ // 支持的分隔符(明确定义类型)
|
|
|
|
|
+ const separators: string[] = ['-', '-', '—', '~', '~'];
|
|
|
|
|
|
|
|
- // 3. 支持多种分隔符(中英文短横线)
|
|
|
|
|
- const separators = ['-', '-', '—']; // 半角/全角短横线
|
|
|
|
|
- let dashIndex = -1;
|
|
|
|
|
|
|
+ // 寻找分隔位置
|
|
|
|
|
+ let bestSplitIndex: number = -1;
|
|
|
|
|
|
|
|
- // 查找最后一个有效分隔符位置
|
|
|
|
|
for (const sep of separators) {
|
|
for (const sep of separators) {
|
|
|
- const index = baseName.lastIndexOf(sep);
|
|
|
|
|
- if (index > dashIndex) dashIndex = index;
|
|
|
|
|
|
|
+ const index: number = baseName.lastIndexOf(sep);
|
|
|
|
|
+ if (index > bestSplitIndex) {
|
|
|
|
|
+ bestSplitIndex = index;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 4. 核心解析逻辑
|
|
|
|
|
- if (dashIndex > 0 && dashIndex < baseName.length - 1) {
|
|
|
|
|
- let artistPart = baseName.substring(0, dashIndex).trim();
|
|
|
|
|
- result.title = baseName.substring(dashIndex + 1).trim();
|
|
|
|
|
|
|
+ // 分割字符串
|
|
|
|
|
+ if (bestSplitIndex > 0 && bestSplitIndex < baseName.length - 1) {
|
|
|
|
|
+ let part1: string = baseName.substring(0, bestSplitIndex).trim();
|
|
|
|
|
+ let part2: string = baseName.substring(bestSplitIndex + 1).trim();
|
|
|
|
|
+
|
|
|
|
|
+ // 处理前缀序号
|
|
|
|
|
+ part1 = part1.replace(/^\d+[\s\.\-- —~~]*/, '').trim();
|
|
|
|
|
+
|
|
|
|
|
+ // 判断歌手部分(明确定义返回类型)
|
|
|
|
|
+ const identifyArtist = (str: string): boolean => {
|
|
|
|
|
+ return COMMON_CHINESE_SURNAMES.some((surname: string) =>
|
|
|
|
|
+ str.startsWith(surname) ||
|
|
|
|
|
+ new RegExp(`[ ,,、&&]${surname}`).test(str)
|
|
|
|
|
+ ) || ARTIST_KEYWORDS.some((keyword: string) =>
|
|
|
|
|
+ str.includes(keyword)
|
|
|
|
|
+ );
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 判断歌手位置
|
|
|
|
|
+ const part1IsArtist: boolean = identifyArtist(part1);
|
|
|
|
|
+ const part2IsArtist: boolean = identifyArtist(part2);
|
|
|
|
|
+
|
|
|
|
|
+ if (part1IsArtist && !part2IsArtist) {
|
|
|
|
|
+ result.artist = part1;
|
|
|
|
|
+ result.title = part2;
|
|
|
|
|
+ } else if (part2IsArtist && !part1IsArtist) {
|
|
|
|
|
+ result.artist = part2;
|
|
|
|
|
+ result.title = part1;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ result.artist = part1.length <= part2.length ? part1 : part2;
|
|
|
|
|
+ result.title = part1.length <= part2.length ? part2 : part1;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 5. 处理前缀序号(如"04 - ")
|
|
|
|
|
- const numPrefixRegex = /^\d+\s*[--—]\s*/; // 匹配数字+分隔符组合
|
|
|
|
|
- artistPart = artistPart.replace(numPrefixRegex, '').trim();
|
|
|
|
|
|
|
+ // 后处理
|
|
|
|
|
+ result.title = result.title
|
|
|
|
|
+ .replace(/(?:\(|()[^))]*(?:)|\))/g, '')
|
|
|
|
|
+ .replace(/\s*[—-]\s*(?:Live|Version|Remix|伴奏).*/i, '')
|
|
|
|
|
+ .trim();
|
|
|
|
|
|
|
|
- // 6. 最终有效性验证
|
|
|
|
|
- result.artist = artistPart;
|
|
|
|
|
- result.isValid = (result.artist.length > 0 && result.title.length > 0);
|
|
|
|
|
|
|
+ // 有效性验证
|
|
|
|
|
+ result.isValid = result.artist.length > 0 &&
|
|
|
|
|
+ result.title.length > 0;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ result.title = baseName;
|
|
|
|
|
+ result.isValid = result.title.length > 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
return result;
|