import TitleBar from '../view/TitleBar' import { router } from '@kit.ArkUI' import { FileUtil, LogUtil, PreferencesUtil, ToastUtil } from '@pura/harmony-utils' import lottie from '@ohos/lottie' import { AnimationItem } from '@ohos/lottie' import { MessageEvents, taskpool, worker } from '@kit.ArkTS' import { fileUri, picker } from '@kit.CoreFileKit' import { LocalMusic } from '../view/LocalMusic' import { Utility } from '../common/util/Utility' import MediaTable from '../common/util/MediaTable' import { VideoItem } from '../viewmodel/VideoItem' import Logger from '../common/util/Logger' import { CommonConstants } from '../common/constants/CommonConstants' import { emitter } from '@kit.BasicServicesKit' @Preview @Entry @Component export struct ScanFilePage{ @State rootPath:string = '' //音频根目录 @State lockPath:string = '' @State strText:string = '' @State isStart:boolean = false @State appName:string = '' //lottie动画构建渲染上下文 private mainRenderingSettings: RenderingContextSettings = new RenderingContextSettings(true) private path:string = "common/lottie/leida.json" private successpath:string = "common/lottie/success2.json" private mainCanvasRenderingContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.mainRenderingSettings) private animateItem: AnimationItem | null = null; initLottie(lpath:string,isLoop:boolean){ lottie.destroy(); //加载动画前先销毁之前加载的动画 this.animateItem = lottie.loadAnimation({ container: this.mainCanvasRenderingContext, // 渲染上下文 renderer: 'canvas', // 渲染方式 loop: isLoop, // 是否循环播放,默认true autoplay: true, // 是否自动播放,默认true name: '2024', // 动画名称 contentMode: 'Contain', // 填充的模式 frameRate: 30, //设置animator的刷帧率为30 imagePath: 'lottie/images/', // 加载读取指定路径下的图片资源 path: lpath, // json路径 // initialSegment: [0,70] }) lottie.setSpeed(0.7) } // 组件生命周期 async aboutToAppear() { Utility.getAppName(getContext(this)).then((appName:string)=>{ this.appName = appName }) const documentViewPicker = new picker.DocumentViewPicker() let documentSaveResult = await documentViewPicker.save({ pickerMode: picker.DocumentPickerMode.DOWNLOAD }) this.rootPath = new fileUri.FileUri(documentSaveResult[0]).path this.lockPath = this.rootPath +'/'+ LocalMusic.STR_LOCK_VIDEO LogUtil.info('onecold 文件扫描 aboutToAppear') } endScan(){ const eventData: emitter.EventData = {}; emitter.emit({ eventId: 101 }, eventData); // 发送视频打开广播事件 ToastUtil.showToast('扫描文件入库成功!') this.isStart = false this.strText = '' //结束动画 lottie.pause() lottie.stop() this.initLottie(this.successpath,false) lottie.play() } // 组件生命周期 aboutToDisappear() { lottie.destroy(); } @State titleBarModel: TitleBar.Model = new TitleBar.Model() .setTitleTextStyle(FontStyle.Normal) .setTitleBarStyle(TitleBar.BarStyle.TRANSPARENT) .setLeftIcon($r('app.media.left_back_white')) .setTitleName("文件扫描") .setTitleFontColor(Color.White) .setTitleBarBackground($r('app.color.title_bar_bg')) .setLeftTitleBackground($r('app.color.title_bar_bg')) .setTitleBarBottomLineColor($r('app.color.title_bar_bg')) .setOnLeftClickListener(() => { router.back() }) initState(){ this.initLottie(this.path,true) this.isStart = true this.strText = Utility.resourceToString(getContext(this),$r('app.string.scaning')) lottie.play() } doOptimize(){ this.initState() const task = new taskpool.Task(scanDirectoryTask, getContext(this), this.rootPath, this.lockPath,PreferencesUtil.getStringSync('COVER_API','')); taskpool.execute(task, taskpool.Priority.HIGH).then(()=>{ this.endScan() }).catch((e:object)=>{ console.info("task1 catch e: " + e); }) } build() { Column() { TitleBar({ model: $titleBarModel }) Scroll(){ Column() { Stack(){ Image($r('app.media.file_search')) .width(80) .height(80) .alignSelf(ItemAlign.Center) //lottie动画 Canvas(this.mainCanvasRenderingContext) .width(160) .height(160) .onReady(()=>{ //抗锯齿的设置 this.mainCanvasRenderingContext.imageSmoothingEnabled = true; this.mainCanvasRenderingContext.imageSmoothingQuality = 'medium' }) } Text(this.strText) .margin({ right: 20,top:20,bottom:20 }) .fontSize(16) .fontColor(Color.Black) .fontWeight(480) Column() { Row() { Image($r('app.media.selected')) .width(16) .height(16) .margin({ left:10}) .alignSelf(ItemAlign.Center) Text(Utility.resourceToString(getContext(this),$r('app.string.file_scan_tip_one')) +`${this.appName} `+ Utility.resourceToString(getContext(this),$r('app.string.file_scan_tip_one_1'))) .margin({ left: 10, right: 20 }) .fontSize(11) .fontColor(Color.Gray) .fontWeight(480) .layoutWeight(1) } .width('100%') .margin({ left:25,right: 10 ,top:20 }) Row() { Image($r('app.media.selected')) .width(16) .height(16) .margin({ left:10}) .alignSelf(ItemAlign.Center) Text($r('app.string.file_scan_tip_two')) .margin({ left: 10, right: 20 }) .fontSize(11) .fontColor(Color.Gray) .fontWeight(480) .layoutWeight(1) } .width('100%') .margin({ left:25,right: 10 ,top:20 }) Column() { Button($r('app.string.start_scan'), { type: ButtonType.Capsule, stateEffect: false }) .width('80%') .height(55) .margin({ top: 50, bottom: 20 }) .linearGradient({ direction: GradientDirection.Right, colors: [['#ff37a0fc', 0.0], ['#67e667', 0.5], ['#f5856e', 1.0]] }) .visibility(this.isStart ? Visibility.Hidden : Visibility.Visible) .onClick(() => { this.doOptimize() }) .alignSelf(ItemAlign.Center) } } } .height('100%') .width('100%') .align(Alignment.Top) } .height(1000) } } } // async function scanDirectoryTask(context: Context, curPath: string, lockPath: string) { // const table: MediaTable = new MediaTable(context); // // // try { // // 使用 Promise 包装数据库操作 // await new Promise((resolve, reject) => { // table.getRdbStore(context, async (err:Error) => { // if (err) { // reject(err); // return; // } // // const files = FileUtil.listFileSync(curPath); // const pendingTasks: Promise[] = []; // // for (const file of files) { // const fPath = `${curPath}/${file}`; // if (fPath === lockPath) continue; // // if (FileUtil.isDirectory(fPath)) { // // 用立即执行函数处理异步递归 // pendingTasks.push((async () => { // await scanDirectoryTask(context, fPath, lockPath); // })()); // } else { // if(!fPath.endsWith('.lrc')&&Utility.isMeidaByExtension(fPath)&&!fPath.endsWith('.srt')){ // // 用 Promise 包装媒体处理逻辑 // pendingTasks.push((async () => { // let mediaItem:VideoItem = await Utility.uriGetMusicAssetsFromFile(context, fPath, CommonConstants.TYPE_LOCAL, true); // ; // await new Promise((resolve: (value: void) => void) => { // table.insert(mediaItem, (id: number) => { // resolve(); // 明确调用 resolve 且无返回值 // }); // }); // })()); // } // } // } // // // 关键点:等待所有异步操作完成 // await Promise.all(pendingTasks); // resolve(); // }); // }); // // // Logger.info('onecold scanDirectory 全部扫描完成后执行 = ') // // } catch (err) { // Logger.error(`Scan failed: ${err.code} - ${err.message}`); // } // // // } @Concurrent async function scanDirectoryTask(context: Context, dirPath: string, lockPath: string,cover_api:string) { const stack: string[] = [dirPath]; const table: MediaTable = new MediaTable(context); while (stack.length > 0) { const currentPath = stack.pop()!; const files = FileUtil.listFileSync(currentPath); await Promise.all(files.map(async (file) => { const fPath = `${currentPath}/${file}`; if (fPath === lockPath) return; if (FileUtil.isDirectory(fPath)) { stack.push(fPath); // 使用栈结构代替递归 } else { if (fPath.endsWith('.lrc') || fPath.endsWith('.srt')) return; if (Utility.isMeidaByExtension(fPath)) { const mediaItem = await Utility.uriGetMusicAssetsFromFile( context, fPath, CommonConstants.TYPE_LOCAL, true ); table.insert(mediaItem, (id: number) => { },cover_api); } } })); } }