| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- import { common, ConfigurationConstant } from '@kit.AbilityKit';
- import { CommonConstants } from '../common/constants/CommonConstants';
- import { VideoItem } from '../viewmodel/VideoItem';
- import { LazyDataSource } from '../common/util/LazyDataSource';
- import { AppUtil, LogUtil, PreferencesUtil, StrUtil, ToastUtil } from '@pura/harmony-utils';
- import fs from '@ohos.file.fs';
- import { changeMusicCover, findLocalCoverImage, getApiLyric, repairAudioMetadata,
- searchCover,
- syncLyricToDB} from '../common/util/MusicTagUtils';
- import { util } from '@kit.ArkTS';
- import { extractHwMediaMetadata, FFMpegTags, Utility } from '../common/util/Utility';
- import { DialogHelper } from '@pura/harmony-dialog';
- import MediaTable from '../common/util/MediaTable';
- // 批量乱码修正,元数据接口用华为的接口获取
- @Component
- export struct FixMessyView {
- private listScroller: ListScroller = new ListScroller()
- onFixResult = (_result: boolean) => {
- }
- @Link isFixMessy: boolean;
- @State bundleName: string = ''
- @State dataSource: LazyDataSource<VideoItem> = new LazyDataSource([])
- @Prop selectedFiles: Array<VideoItem>
- @StorageProp('topRectHeight') topRectHeight: number = 0;
- @State isDarkMode: boolean = false
- @StorageProp('currentColorMode') @Watch('onColorModeChange') currentMode: number =
- ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;
- @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
- context = this.getUIContext().getHostContext() as common.UIAbilityContext
- // 新增状态用于进度显示
- @State currentProgress: number = 0;
- @State totalFiles: number = 0;
- @State currentFileIndex: number = 0;
- @State currentFileName: string = '';
- @State isEmbedding: boolean = false;
- @State embedSuccessCount: number = 0;
- @State embedFailedCount: number = 0;
- private table: MediaTable = new MediaTable(this.context);
- // 新增状态用于跟踪每个文件的嵌入状态
- @State embedStatusMap: Map<string, boolean> = new Map<string, boolean>();
- onColorModeChange() {
- this.isDarkMode = this.currentMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK
- }
- async aboutToAppear() {
- this.bundleName = AppUtil.getBundleName()
- // 确保 selectedFiles 有默认值后再初始化 dataSource
- if (this.selectedFiles && this.selectedFiles.length > 0) {
- this.dataSource = new LazyDataSource(this.selectedFiles)
- } else {
- this.dataSource = new LazyDataSource([])
- }
- await new Promise<void>((resolve, reject) => {
- this.table.getRdbStore(this.context, (err:Error) => {
- err ? reject(err) : resolve();
- });
- });
- }
- build() {
- Column(){
- this.topTitleBar()
- this.startButton()
- this.listView()
- }
- .height('100%')
- .width('100%')
- .backgroundColor($r('app.color.index_background'))
- }
- @Builder
- listView() {
- Column(){
- List({ scroller: this.listScroller }) {
- ListItemGroup({ header: this.listHeader() }) {
- LazyForEach(this.dataSource, (item: VideoItem, index: number) => {
- ListItem() {
- Column() {
- this.MusicItem(item, index)
- }
- }
- .transition(TransitionEffect.asymmetric(
- TransitionEffect.scale({ x: 1.2, y: 1.2 }).animation({ duration: 500 }),
- TransitionEffect.scale({ x: 0, y: 0 })
- ))
- .clickEffect({ level: ClickEffectLevel.LIGHT })
- }, (item: VideoItem) => item.filePath)
- }
- }
- .cachedCount(2)
- // .layoutWeight(1)
- // .height('100%')
- .transition(TransitionEffect.asymmetric(
- TransitionEffect.scale({ x: 1.2, y: 1.2 }).animation({ duration: 500 }),
- TransitionEffect.scale({ x: 0, y: 0 })
- ))
- }
- .borderRadius(20)
- .padding({top:10,bottom:6})
- .height('auto')
- .backgroundColor($r('app.color.start_window_background'))
- .margin({top:20,right:20,left:20})
- .layoutWeight(1)
- .height('100%')
- }
- @Builder
- listHeader() {
- Stack({alignContent:Alignment.Start}){
- Text(`共${this.selectedFiles.length}首歌`)
- .fontSize(14)
- .maxLines(1)
- .textAlign(TextAlign.Start)
- .padding({ left: 25 })
- .fontColor($r('app.color.text_color'))
- .margin({bottom:6,top:4,left: 6 })
- }
- .height('auto').width('100%')
- }
- @Builder
- startButton(){
- Stack(){
- Progress({ value: this.currentProgress, total: this.totalFiles,
- type: ProgressType.Capsule }).height(55)
- .margin({top:20,right:20,left:20})
- .backgroundColor($r('app.color.index_background'))
- Button({ type: ButtonType.Capsule, stateEffect: true }) {
- Row() {
- // 菜单图标
- SymbolGlyph($r('sys.symbol.star_trophy'))// .size({ width: 22, height: 22 })
- .fontSize(22)
- .fontColor([this.themeColor])
- .alignSelf(ItemAlign.Center)
- .margin({ left: 25 })
- // 菜单标题
- Text('开始乱码修正')
- .margin({ left: 10, right: 20 })
- .fontSize(15)
- .fontWeight(480)
- Blank()
- // 右侧箭头
- Image($r('app.media.arrow_right'))
- .width(22)
- .height(22)
- .margin({ left: 0, right: 20 })
- .align(Alignment.Center)
- }
- .width('100%')
- }
- .margin({top:20,right:20,left:20})
- .height(55)
- .enabled(!this.isEmbedding)
- .backgroundColor($r('app.color.start_window_background'))
- .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
- .onClick(()=>{
- this.startEmbed()
- })
- }
- }
- /**
- * 开始修复
- */
- async startEmbed() {
- if (this.isEmbedding || !this.selectedFiles || this.selectedFiles.length === 0) {
- return;
- }
- this.isEmbedding = true;
- this.currentProgress = 0;
- this.currentFileIndex = 0;
- this.embedSuccessCount = 0;
- this.embedFailedCount = 0;
- this.totalFiles = this.selectedFiles.length;
- // 清空之前的嵌入状态
- this.embedStatusMap = new Map<string, boolean>();
- // 创建异步任务数组
- const embedTasks = this.selectedFiles.map(async (item: VideoItem, index: number) => {
- try {
- this.currentFileIndex = index + 1;
- this.currentFileName = item.name;
- let success = true;
- let hwTags = await extractHwMediaMetadata(item.filePath)
- let artist = hwTags?.artist|| '';
- let title = hwTags?.title|| '';
- let album = hwTags?.album|| '';
- console.info('onecold 乱码修正 artist='+artist);
- console.info('onecold 乱码修正 title='+title);
- console.info('onecold 乱码修正 album='+album);
- const metadata:FFMpegTags = {
- title: title,
- artist: artist,
- album: album,
- };
- //内嵌下标签的值,设置overwrite为true会直接修改原文件
- const result:boolean = await repairAudioMetadata(
- item.filePath,
- item.lyricContent||'',
- metadata,
- true
- );
- //入库
- if (result) {
- this.table.updateMediaInfo(item.filePath,title, artist, album,
- item.lyricContent||'','','','','','','','','', (success: boolean, error?: string) => {
- if (success) {
- console.info('onecold 乱码修正 更新库成功')
- }else{
- console.info('onecold 乱码修正 更新库失败')
- }
- });
- }
- // 更新嵌入状态
- if (item.filePath) {
- this.embedStatusMap.set(item.filePath, success);
- }
- if (success) {
- this.embedSuccessCount++;
- } else {
- this.embedFailedCount++;
- }
- // 滚动到当前处理的项目位置
- // this.listScroller.scrollToIndex(index);
- this.currentProgress = Math.floor(((index + 1) / this.totalFiles) * 100);
- return success;
- } catch (error) {
- console.error(`处理文件 ${item.name} 失败: ${JSON.stringify(error)}`);
- this.embedFailedCount++;
- return false;
- }
- });
- try {
- // 并发执行所有嵌入任务
- await Promise.all(embedTasks);
- } catch (error) {
- this.onFixResult(false)
- console.error(`批量修复过程出错: ${JSON.stringify(error)}`);
- } finally {
- ToastUtil.showToast(`修复完成: 成功 ${this.embedSuccessCount}, 失败 ${this.embedFailedCount}`)
- this.isEmbedding = false;
- this.currentProgress = 0;
- this.totalFiles = this.selectedFiles.length;
- this.onFixResult(true)
- // 可以在这里添加完成后的提示或回调
- console.info(`修复完成: 成功 ${this.embedSuccessCount}, 失败 ${this.embedFailedCount}`);
- }
- }
- @Builder
- topTitleBar() {
- // 顶部安全区和自定义标题栏
- Column() {
- // 顶部安全区
- Blank()
- .height(this.topRectHeight)
- .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
- .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
- // 自定义标题栏(Stack实现绝对居中)
- Stack() {
- // 居中标题
- Text('批量乱码修正')
- .fontSize(18)
- .fontColor(Color.White)
- .align(Alignment.Center)
- // 左右按钮
- Row() {
- Image($r('app.media.left_back_white'))
- .width(26)
- .height(26)
- .margin({ left: 12, right: 8 })
- .onClick(() => {
- this.getUIContext().animateTo({ duration: 666 }, () => {
- // 动画闭包内控制Image组件的出现和消失
- this.isFixMessy = !this.isFixMessy
- })
- })
- Blank().flexGrow(1)
- Blank().width(32)
- }
- .height(48)
- .width('100%')
- .alignItems(VerticalAlign.Center)
- }
- .height(48)
- .width('100%')
- .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
- }
- }
- @Builder
- private MusicItem(item: VideoItem, index?: number) {
- Button({ type: ButtonType.Normal, stateEffect: true }) {
- Column() {
- Row() {
- SymbolGlyph($r('sys.symbol.music'))
- .fontSize(20)
- .fontColor([this.themeColor])
- .alignSelf(ItemAlign.Center)
- .padding({ left: 25 })
- Text(item.name)
- .fontSize(14)
- .maxLines(1)
- .padding({ left: 5 })
- .textOverflow({ overflow: TextOverflow.MARQUEE })
- .animation({
- duration: 555,
- curve: 'Linear',
- })
- .fontColor(Color.Gray)
- .margin({ left: 8 })
- Blank()
- // 根据嵌入状态显示勾选图标
- SymbolGlyph($r('sys.symbol.checkmark_circle'))
- .fontSize(20)
- .fontColor([this.themeColor])
- .alignSelf(ItemAlign.Center)
- .padding({ right: 25 })
- .animation({
- duration: 666,
- curve: 'ease-in-out' // 可选动画曲线
- })
- .visibility(this.embedStatusMap.get(item.filePath)?Visibility.Visible:Visibility.None)
- }
- .layoutWeight(1)
- .height('100%')
- .width('100%')
- }
- }
- .backgroundColor(Color.Transparent)
- .width('100%')
- .height(40)
- }
- }
|