| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618 |
- import { common } from '@kit.AbilityKit';
- import { CommonConstants } from '../common/constants/CommonConstants';
- import { VideoItem } from '../viewmodel/VideoItem';
- import { AppUtil, ArrayUtil, FileUtil, LogUtil, PreferencesUtil, StrUtil, ToastUtil } from '@pura/harmony-utils';
- import MediaTable from '../common/util/MediaTable';
- import PlaylistTable from '../common/util/PlaylistTable';
- import { EventConstants } from '../common/constants/EventConstants';
- import { emitter } from '@kit.BasicServicesKit';
- import { taskpool } from '@kit.ArkTS';
- import PermissionUtil from '../common/util/PermissionUtil'
- // 批量删除
- @Component
- export struct DeleteComptent {
- @State isDeleteYuan: boolean = true
- @State isDeletePicture: boolean = true
- @State isDeleteLrc: boolean = true
- @State onlyRemoveFromPlaylist: boolean = false
- @State packName: string = ''
- @State isDeleting: boolean = false // 删除状态
- @State deleteProgress: number = 0 // 删除进度 (0-100)
- @State currentDeleteFile: string = '' // 当前删除的文件名
- onDeleteResult = (_result: boolean) => {
- }
- onCancel = () => {
- }
- @Prop selectedFiles: Array<VideoItem>
- @Prop isPlaylistMode: boolean = false
- @Prop playlistId: string = ''
- @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
- context = this.getUIContext().getHostContext() as common.UIAbilityContext
- async aboutToAppear() {
- this.packName = AppUtil.getBundleName()
- this.isDeleteYuan = PreferencesUtil.getBooleanSync('isDeleteYuan', true);
- this.isDeletePicture = PreferencesUtil.getBooleanSync('isDeletePicture', true);
- this.isDeleteLrc = PreferencesUtil.getBooleanSync('isDeleteLrc', true)
- if (!this.isPlaylistMode) {
- this.onlyRemoveFromPlaylist = false
- }
- }
- private hasOnlyDirectories(): boolean {
- return this.selectedFiles.length > 0 &&
- this.selectedFiles.every(item => item.type === CommonConstants.TYPE_IS_DIR)
- }
- build() {
- Column() {
- if (this.isDeleting) {
- // 删除进度界面
- Column() {
- Text('正在删除文件').fontSize(18).fontColor(Color.Red).margin({ top: 20, bottom: 15 })
- // 当前文件名显示
- if (this.currentDeleteFile) {
- Text(`正在删除: ${this.currentDeleteFile}`)
- .fontSize(14)
- .fontColor($r('app.color.text_color'))
- .maxLines(2)
- .textOverflow({ overflow: TextOverflow.Ellipsis })
- .width('90%')
- .textAlign(TextAlign.Start)
- .margin({ bottom: 15 })
- }
- // 进度条
- Column() {
- Row() {
- Text(`${Math.round(this.deleteProgress)}%`)
- .fontSize(12)
- .fontColor($r('app.color.text_color'))
- Blank()
- Text(`${Math.round(this.deleteProgress * this.selectedFiles.length / 100)}/${this.selectedFiles.length}`)
- .fontSize(12)
- .fontColor($r('app.color.text_color'))
- }
- .width('90%')
- .margin({ bottom: 8 })
- Progress({
- value: this.deleteProgress,
- total: 100,
- type: ProgressType.Linear
- })
- .width('90%')
- .height(8)
- .color(this.themeColor)
- .backgroundColor('#E0E0E0')
- .borderRadius(4)
- }
- .width('100%')
- .alignItems(HorizontalAlign.Center)
- .margin({ bottom: 20 })
- // 取消按钮
- Button('取消删除')
- .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 })
- .padding(15)
- .width(120)
- .onClick(() => {
- this.onCancel()
- })
- .backgroundColor($r('app.color.silvery'))
- .backgroundBlurStyle(BlurStyle.COMPONENT_THICK)
- .fontColor(Color.Black)
- }
- .backgroundColor($r('app.color.start_window_background'))
- .backgroundBlurStyle(BlurStyle.Regular)
- .padding(20)
- } else {
- // 原始删除确认界面
- Column() {
- Text('温馨提醒').fontSize(20).margin({ top: 10, bottom: 10 })
- Text('是否删除这些文件?').fontSize(16).margin({ top: 10, bottom: 10 })
- Column() {
- // 只移除歌单选项(歌单场景)
- Button({ type: ButtonType.Capsule, stateEffect: true }) {
- Row() {
- SymbolGlyph($r('sys.symbol.music_note_list'))
- .fontSize(20)
- .fontColor([this.themeColor])
- .alignSelf(ItemAlign.Center)
- .margin({ left: 15 })
- Text("只移除歌单")
- .fontSize(16)
- .layoutWeight(1)
- .margin({ left: 10 })
- Toggle({ type: ToggleType.Checkbox, isOn: this.onlyRemoveFromPlaylist })
- .onChange((isOn: boolean) => {
- this.onlyRemoveFromPlaylist = isOn;
- if (isOn) {
- this.isDeleteYuan = false
- this.isDeletePicture = false
- this.isDeleteLrc = false
- }
- })
- .margin({ right: 28 })
- .selectedColor(this.themeColor)
- }
- }
- .backgroundColor(Color.Transparent)
- .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
- .onClick(() => {
- this.onlyRemoveFromPlaylist = !this.onlyRemoveFromPlaylist
- if (this.onlyRemoveFromPlaylist) {
- this.isDeleteYuan = false
- this.isDeletePicture = false
- this.isDeleteLrc = false
- }
- })
- .width('100%')
- .padding(10)
- .margin({ left: 18 })
- .visibility(this.isPlaylistMode ? Visibility.Visible : Visibility.None)
- // 删除源文件选项
- Button({ type: ButtonType.Capsule, stateEffect: true }) {
- Row() {
- SymbolGlyph($r('sys.symbol.doc_text'))
- .fontSize(20)
- .fontColor([this.themeColor])
- .alignSelf(ItemAlign.Center)
- .margin({ left: 15 })
- Text("删除源文件")
- .fontSize(16)
- .layoutWeight(1)
- .margin({ left: 10 })
- Toggle({ type: ToggleType.Checkbox, isOn: this.isDeleteYuan })
- .onChange((isOn: boolean) => {
- this.isDeleteYuan = isOn;
- PreferencesUtil.put('isDeleteYuan', this.isDeleteYuan)
- })
- .margin({ right: 28 })
- .selectedColor(this.themeColor)
- }
- }
- .backgroundColor(Color.Transparent)
- .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
- .onClick(() => {
- this.isDeleteYuan = !this.isDeleteYuan
- })
- .width('100%')
- .padding(10)
- .margin({ left: 18 })
- .visibility(this.onlyRemoveFromPlaylist ? Visibility.None : Visibility.Visible)
- // 删除封面选项
- Button({ type: ButtonType.Capsule, stateEffect: true }) {
- Row() {
- SymbolGlyph($r('sys.symbol.picture'))
- .fontSize(20)
- .fontColor([this.themeColor])
- .alignSelf(ItemAlign.Center)
- .margin({ left: 15 })
- Text("删除封面文件")
- .fontSize(16)
- .layoutWeight(1)
- .margin({ left: 10 })
- Toggle({ type: ToggleType.Checkbox, isOn: this.isDeletePicture })
- .onChange((isOn: boolean) => {
- this.isDeletePicture = isOn;
- PreferencesUtil.put('isDeletePicture', this.isDeletePicture)
- })
- .margin({ right: 28 })
- .selectedColor(this.themeColor)
- }
- }
- .backgroundColor(Color.Transparent)
- .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
- .onClick(() => {
- this.isDeletePicture = !this.isDeletePicture
- })
- .width('100%')
- .padding(10)
- .margin({ left: 18 })
- .visibility(this.onlyRemoveFromPlaylist ? Visibility.None : Visibility.Visible)
- // 删除歌词选项
- Button({ type: ButtonType.Capsule, stateEffect: true }) {
- Row() {
- SymbolGlyph($r('sys.symbol.input_mode'))
- .fontSize(20)
- .fontColor([this.themeColor])
- .alignSelf(ItemAlign.Center)
- .margin({ left: 15 })
- Text("删除歌词文件")
- .fontSize(16)
- .layoutWeight(1)
- .margin({ left: 10 })
- Toggle({ type: ToggleType.Checkbox, isOn: this.isDeleteLrc })
- .onChange((isOn: boolean) => {
- this.isDeleteLrc = isOn;
- PreferencesUtil.put('isDeleteLrc', this.isDeleteLrc)
- })
- .margin({ right: 28 })
- .selectedColor(this.themeColor)
- }
- }
- .backgroundColor(Color.Transparent)
- .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
- .onClick(() => {
- this.isDeleteLrc = !this.isDeleteLrc
- })
- .width('100%')
- .padding(10)
- .margin({ left: 18, bottom: 10 })
- .visibility(this.onlyRemoveFromPlaylist ? Visibility.None : Visibility.Visible)
- }
- .visibility(this.hasOnlyDirectories() ? Visibility.None : Visibility.Visible)
- Flex({ justifyContent: FlexAlign.SpaceAround }) {
- Button($r('app.string.cancel'))
- .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 })
- .padding(15)
- .width(120)
- .onClick(() => {
- this.onCancel()
- })
- .backgroundColor($r('app.color.silvery'))
- .backgroundBlurStyle(BlurStyle.COMPONENT_THICK)
- .fontColor(Color.Black)
- Button($r('app.string.sure'))
- .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 })
- .padding(15)
- .width(120)
- .onClick(() => {
- this.doDeleteTask()
- })
- .backgroundColor($r('app.color.silvery'))
- .backgroundBlurStyle(BlurStyle.COMPONENT_THICK)
- .fontColor(Color.Red)
- }.margin({ bottom: 10 })
- }
- .backgroundColor($r('app.color.start_window_background'))
- .backgroundBlurStyle(BlurStyle.Regular)
- }
- }
- }
- doDeleteTask(){
- if (this.onlyRemoveFromPlaylist && StrUtil.isEmpty(this.playlistId)) {
- ToastUtil.showToast('当前歌单信息无效')
- return
- }
- // 设置删除状态
- this.isDeleting = true;
- this.deleteProgress = 0;
- this.currentDeleteFile = '';
- const totalFiles = this.selectedFiles.length;
- let currentFileIndex = 0;
- // 创建进度监控定时器
- let progressTimer: number = setInterval(() => {
- // 基于时间模拟进度
- if (currentFileIndex < totalFiles) {
- this.deleteProgress = (currentFileIndex / totalFiles) * 95; // 最高到95%
- this.currentDeleteFile = this.selectedFiles[currentFileIndex]?.name || '';
- currentFileIndex++;
- }
- if (this.deleteProgress >= 95) {
- clearInterval(progressTimer);
- }
- }, 300); // 每300ms更新一次,模拟删除进度
- const task = this.onlyRemoveFromPlaylist ?
- new taskpool.Task(
- removeSongsFromPlaylistOnly,
- JSON.stringify(this.selectedFiles),
- this.context,
- this.playlistId
- ) :
- new taskpool.Task(
- deleteMultipleFilesWithProgress,
- JSON.stringify(this.selectedFiles),
- this.isDeleteYuan, this.isDeletePicture, this.isDeleteLrc, this.context, this.packName
- );
- taskpool.execute(task, taskpool.Priority.HIGH).then((result) => {
- clearInterval(progressTimer);
- this.deleteProgress = 100;
- this.currentDeleteFile = '删除完成';
- setTimeout(() => {
- if(result){
- this.onDeleteResult(true)
- }else {
- this.onDeleteResult(false)
- }
- },500)
- }).catch((error:Error) => {
- clearInterval(progressTimer);
- console.error('heanup DeleteComptent: delete failed:', (error as Error).message);
- this.isDeleting = false;
- });
- }
- }
- // 仅从当前歌单移除歌曲
- @Concurrent
- async function removeSongsFromPlaylistOnly(
- selectedFilesStr:string,
- context:Context,
- playlistId:string
- ) {
- const selectedFiles: VideoItem[] = JSON.parse(selectedFilesStr)
- if (!ArrayUtil.isNotEmpty(selectedFiles)) {
- return true
- }
- if (StrUtil.isEmpty(playlistId)) {
- LogUtil.error('heanup DeleteComptent', 'removeSongsFromPlaylistOnly: playlistId 为空')
- return false
- }
- const playlistTable: PlaylistTable = new PlaylistTable(context)
- let hasPlaylistChanges = false
- let hasError = false
- for (let i = 0; i < selectedFiles.length; i++) {
- const item = selectedFiles[i]
- if (!item.filePath) {
- continue
- }
- try {
- const removed = await playlistTable.removeSongFromPlaylist(playlistId, item.filePath)
- if (removed) {
- hasPlaylistChanges = true
- } else {
- hasError = true
- }
- } catch (error) {
- hasError = true
- LogUtil.error('heanup DeleteComptent', `removeSongsFromPlaylistOnly error: ${(error as Error).message}`)
- }
- await new Promise<void>(resolve => setTimeout(resolve, 120))
- }
- if (hasPlaylistChanges) {
- const eventRefresh: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_REFRESH }
- emitter.emit(eventRefresh, {})
- emitter.emit({ eventId: EventConstants.EVENT_PLAYLIST_REFRESH_SORT }, {})
- }
- return !hasError
- }
- //多选删除文件(带进度显示的新版本)
- @Concurrent
- async function deleteMultipleFilesWithProgress(
- selectedFilesStr:string,
- isDeleteYuan:boolean,
- isDeletePicture:boolean,
- isDeleteLrc:boolean,
- context:Context,
- packName:string
- ) {
- const selectedFiles: VideoItem[] = JSON.parse(selectedFilesStr)
- if (ArrayUtil.isNotEmpty(selectedFiles)) {
- const table: MediaTable = new MediaTable(context)
- const playlistTable: PlaylistTable = new PlaylistTable(context)
- // 初始化数据库
- await new Promise<void>((resolve, reject) => {
- table.getRdbStore(context, (err:Error) => {
- err ? reject(err) : resolve();
- });
- });
- let hasPlaylistChanges = false; // 标记是否有歌单变化
- const totalFiles = selectedFiles.length;
- for (let i = 0; i < selectedFiles.length; i++) {
- const item = selectedFiles[i];
- console.info('heanup DeleteComptent: deleting file ' + (i + 1) + '/' + totalFiles + ': ' + item.name);
- if (item.type === CommonConstants.TYPE_IS_DIR) {
- // 删除目录
- await new Promise<void>((resolve) => {
- table.deleteDataForParentPath(item.filePath, async () => {
- try {
- await FileUtil.rmdir(item.filePath);
- console.info('heanup DeleteComptent: directory deleted: ' + item.filePath);
- } catch (error) {
- console.error('heanup DeleteComptent: directory delete error: ' + (error as Error).message);
- }
- resolve();
- });
- });
- } else {
- // 删除文件
- await new Promise<void>((resolve) => {
- table.deleteData(item, async () => {
- try {
- // 删除源文件
- if(isDeleteYuan && item.filePath.toLowerCase().includes(packName)){
- await FileUtil.unlink(item.filePath);
- console.info('heanup DeleteComptent: source file deleted: ' + item.filePath);
- }
- // 删除封面文件
- if (isDeletePicture && item.pixelMapPath) {
- const picPath = FileUtil.getFilePath(item.pixelMapPath);
- if (FileUtil.accessSync(picPath)) {
- await FileUtil.unlink(picPath);
- console.info('heanup DeleteComptent: cover file deleted: ' + picPath);
- }
- }
- // 删除歌词文件
- if(isDeleteLrc) {
- const lyricPath = item.filePath?.replace(/\.[^/.]+$/, ".lrc");
- if (lyricPath && item.filePath.toLowerCase().includes(packName) && FileUtil.accessSync(lyricPath)) {
- await FileUtil.unlink(lyricPath);
- console.info('heanup DeleteComptent: lyric file deleted: ' + lyricPath);
- }
- }
- } catch (error) {
- console.error('heanup DeleteComptent: file delete error: ' + (error as Error).message);
- }
- resolve();
- });
- });
- // 从歌单中移除歌曲
- LogUtil.info('heanup DeleteComptent', `准备从歌单中移除歌曲: ${item.name}, filePath: ${item.filePath}`);
- if (item.filePath) {
- try {
- const affectedPlaylists = await playlistTable.removeSongFromAllPlaylists(item.filePath);
- LogUtil.info('heanup DeleteComptent', `removeSongFromAllPlaylists 返回了 ${affectedPlaylists.length} 个受影响的歌单`);
- if (affectedPlaylists.length > 0) {
- hasPlaylistChanges = true;
- LogUtil.info('heanup DeleteComptent', `歌曲已从 ${affectedPlaylists.length} 个歌单中移除: ${item.name}`);
- } else {
- LogUtil.info('heanup DeleteComptent', `歌曲不在任何歌单中: ${item.name}`);
- }
- } catch (error) {
- LogUtil.error('heanup DeleteComptent', `从歌单移除歌曲失败: ${(error as Error).message}`);
- }
- } else {
- LogUtil.warn('heanup DeleteComptent', `歌曲 ${item.name} 的 filePath 为空`);
- }
- }
- // 短暂延迟,让用户能看到进度变化
- await new Promise<void>(resolve => setTimeout(resolve, 200));
- }
- // 如果有歌单变化,发送刷新事件
- if (hasPlaylistChanges) {
- const eventRefresh: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_REFRESH };
- emitter.emit(eventRefresh, {});
- LogUtil.info('heanup DeleteComptent', '已发送歌单刷新事件,更新歌单数量');
- }
- }
- return true
- }
- // 保留原始删除函数作为备用
- @Concurrent
- async function deleteMultipleFiles(
- selectedFilesStr:string,
- isDeleteYuan:boolean,
- isDeletePicture:boolean,
- isDeleteLrc:boolean,
- context:Context,
- packName:string
- ) {
- const selectedFiles: VideoItem[] = JSON.parse(selectedFilesStr)
- if (ArrayUtil.isNotEmpty(selectedFiles)) {
- const table: MediaTable = new MediaTable(context)
- const playlistTable: PlaylistTable = new PlaylistTable(context)
- // 初始化数据库
- await new Promise<void>((resolve, reject) => {
- table.getRdbStore(context, (err:Error) => {
- err ? reject(err) : resolve();
- });
- });
- let hasPlaylistChanges = false; // 标记是否有歌单变化
- for (const item of selectedFiles) {
- console.info('onecold delete filePath = ' + item.filePath);
- if (item.type === CommonConstants.TYPE_IS_DIR) {
- table.deleteDataForParentPath(item.filePath, () => {
- FileUtil.rmdir(item.filePath).then(() => {
- return true
- }).catch((error:Error) => {
- console.error((error as Error).message);
- return false
- });
- });
- } else {
- table.deleteData(item, async () => {
- if(isDeleteYuan){
- if(item.filePath.toLowerCase().includes(packName)){
- await FileUtil.unlink(item.filePath)
- }
- }
- console.info(`onecold 封面文件=: ${item.pixelMapPath}`);
- if (isDeletePicture && item.pixelMapPath) {
- const picPath = FileUtil.getFilePath(item.pixelMapPath)//获取图
- if (FileUtil.accessSync(picPath)) {
- await FileUtil.unlink(picPath);
- console.info(`onecold 封面文件已删除: ${picPath}`);
- }
- }
- const lyricPath = item.filePath?.replace(/\.[^/.]+$/, ".lrc");
- console.info(`onecold 歌词文件=: ${lyricPath}`);
- if(isDeleteLrc) {
- if (lyricPath&&item.filePath.toLowerCase().includes(packName) && FileUtil.accessSync(lyricPath)) {
- await FileUtil.unlink(lyricPath);
- console.info( `onecold 歌词文件已删除: ${lyricPath}`);
- }
- }
- // this.onDeleteResult(true,item)
- });
- // 先从所有歌单中移除这首歌(如果存在的话)
- LogUtil.info('heanup DeleteComptent', `准备从歌单中移除歌曲: ${item.name}, filePath: ${item.filePath}`);
- if (item.filePath) {
- try {
- const affectedPlaylists = await playlistTable.removeSongFromAllPlaylists(item.filePath);
- LogUtil.info('heanup DeleteComptent', `removeSongFromAllPlaylists 返回了 ${affectedPlaylists.length} 个受影响的歌单`);
- if (affectedPlaylists.length > 0) {
- hasPlaylistChanges = true;
- LogUtil.info('heanup DeleteComptent', `歌曲已从 ${affectedPlaylists.length} 个歌单中移除: ${item.name}`);
- } else {
- LogUtil.info('heanup DeleteComptent', `歌曲不在任何歌单中: ${item.name}`);
- }
- } catch (error) {
- LogUtil.error('heanup DeleteComptent', `从歌单移除歌曲失败: ${(error as Error).message}`);
- }
- } else {
- LogUtil.warn('heanup DeleteComptent', `歌曲 ${item.name} 的 filePath 为空`);
- }
- }
- }
- // 如果有歌单变化,发送刷新事件
- if (hasPlaylistChanges) {
- const eventRefresh: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_REFRESH };
- emitter.emit(eventRefresh, {});
- LogUtil.info('heanup DeleteComptent', '已发送歌单刷新事件,更新歌单数量');
- }
- }
- return true
- }
|