DeleteComptent.ets 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. import { common } from '@kit.AbilityKit';
  2. import { CommonConstants } from '../common/constants/CommonConstants';
  3. import { VideoItem } from '../viewmodel/VideoItem';
  4. import { ArrayUtil, FileUtil, LogUtil, PreferencesUtil, StrUtil, ToastUtil } from '@pura/harmony-utils';
  5. import MediaTable from '../common/util/MediaTable';
  6. import PlaylistTable from '../common/util/PlaylistTable';
  7. import { EventConstants } from '../common/constants/EventConstants';
  8. import { emitter } from '@kit.BasicServicesKit';
  9. import { taskpool } from '@kit.ArkTS';
  10. // 批量删除
  11. @Component
  12. export struct DeleteComptent {
  13. @State isDeleteYuan: boolean = true
  14. @State isDeletePicture: boolean = true
  15. @State isDeleteLrc: boolean = true
  16. onDeleteResult = (_result: boolean) => {
  17. }
  18. onCancel = () => {
  19. }
  20. @Prop selectedFiles: Array<VideoItem>
  21. @StorageProp('themeColor') themeColor: string = CommonConstants.DEFAULT_THEME_COLOR;
  22. context = this.getUIContext().getHostContext() as common.UIAbilityContext
  23. async aboutToAppear() {
  24. this.isDeleteYuan = PreferencesUtil.getBooleanSync('isDeleteYuan', true);
  25. this.isDeletePicture = PreferencesUtil.getBooleanSync('isDeletePicture', true);
  26. this.isDeleteLrc = PreferencesUtil.getBooleanSync('isDeleteLrc', true)
  27. }
  28. build() {
  29. Column() {
  30. Text('温馨提醒').fontSize(20).margin({ top: 10, bottom: 10 })
  31. Text('是否删除这些文件?').fontSize(16).margin({ top: 10, bottom: 10 })
  32. Column() {
  33. // 删除源文件选项
  34. Button({ type: ButtonType.Capsule, stateEffect: true }) {
  35. Row() {
  36. SymbolGlyph($r('sys.symbol.doc_text'))
  37. .fontSize(20)
  38. .fontColor([this.themeColor])
  39. .alignSelf(ItemAlign.Center)
  40. .margin({ left: 15 })
  41. Text("删除源文件")
  42. .fontSize(16)
  43. .layoutWeight(1)
  44. .margin({ left: 10 })
  45. Toggle({ type: ToggleType.Checkbox, isOn: this.isDeleteYuan })
  46. .onChange((isOn: boolean) => {
  47. this.isDeleteYuan = isOn;
  48. PreferencesUtil.put('isDeleteYuan', this.isDeleteYuan)
  49. })
  50. .margin({ right: 28 })
  51. .selectedColor(this.themeColor)
  52. }
  53. }
  54. .backgroundColor(Color.Transparent)
  55. .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
  56. .onClick(() => {
  57. this.isDeleteYuan = !this.isDeleteYuan
  58. })
  59. .width('100%')
  60. .padding(10)
  61. .margin({ left: 18 })
  62. // 删除封面选项
  63. Button({ type: ButtonType.Capsule, stateEffect: true }) {
  64. Row() {
  65. SymbolGlyph($r('sys.symbol.picture'))
  66. .fontSize(20)
  67. .fontColor([this.themeColor])
  68. .alignSelf(ItemAlign.Center)
  69. .margin({ left: 15 })
  70. Text("删除封面文件")
  71. .fontSize(16)
  72. .layoutWeight(1)
  73. .margin({ left: 10 })
  74. Toggle({ type: ToggleType.Checkbox, isOn: this.isDeletePicture })
  75. .onChange((isOn: boolean) => {
  76. this.isDeletePicture = isOn;
  77. PreferencesUtil.put('isDeletePicture', this.isDeletePicture)
  78. })
  79. .margin({ right: 28 })
  80. .selectedColor(this.themeColor)
  81. }
  82. }
  83. .backgroundColor(Color.Transparent)
  84. .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
  85. .onClick(() => {
  86. this.isDeletePicture = !this.isDeletePicture
  87. })
  88. .width('100%')
  89. .padding(10)
  90. .margin({ left: 18 })
  91. // 删除歌词选项
  92. Button({ type: ButtonType.Capsule, stateEffect: true }) {
  93. Row() {
  94. SymbolGlyph($r('sys.symbol.input_mode'))
  95. .fontSize(20)
  96. .fontColor([this.themeColor])
  97. .alignSelf(ItemAlign.Center)
  98. .margin({ left: 15 })
  99. Text("删除歌词文件")
  100. .fontSize(16)
  101. .layoutWeight(1)
  102. .margin({ left: 10 })
  103. Toggle({ type: ToggleType.Checkbox, isOn: this.isDeleteLrc })
  104. .onChange((isOn: boolean) => {
  105. this.isDeleteLrc = isOn;
  106. PreferencesUtil.put('isDeleteLrc', this.isDeleteLrc)
  107. })
  108. .margin({ right: 28 })
  109. .selectedColor(this.themeColor)
  110. }
  111. }
  112. .backgroundColor(Color.Transparent)
  113. .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.6 })
  114. .onClick(() => {
  115. this.isDeleteLrc = !this.isDeleteLrc
  116. })
  117. .width('100%')
  118. .padding(10)
  119. .margin({ left: 18, bottom: 10 })
  120. }
  121. .visibility(this.selectedFiles.length==1&&this.selectedFiles[0].type==CommonConstants.TYPE_IS_DIR?
  122. Visibility.None:Visibility.Visible)
  123. Flex({ justifyContent: FlexAlign.SpaceAround }) {
  124. Button($r('app.string.cancel'))
  125. .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 })
  126. .padding(15)
  127. .width(120)
  128. .onClick(() => {
  129. this.onCancel()
  130. })
  131. .backgroundColor($r('app.color.silvery'))
  132. .backgroundBlurStyle(BlurStyle.COMPONENT_THICK)
  133. .fontColor(Color.Black)
  134. Button($r('app.string.sure'))
  135. .clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.8 })
  136. .padding(15)
  137. .width(120)
  138. .onClick(() => {
  139. this.doDeleteTask()
  140. })
  141. .backgroundColor($r('app.color.silvery'))
  142. .backgroundBlurStyle(BlurStyle.COMPONENT_THICK)
  143. .fontColor(Color.Red)
  144. }.margin({ bottom: 10 })
  145. }
  146. .backgroundColor($r('app.color.start_window_background'))
  147. .backgroundBlurStyle(BlurStyle.Regular)
  148. }
  149. doDeleteTask(){
  150. const task = new taskpool.Task(
  151. deleteMultipleFiles,
  152. JSON.stringify(this.selectedFiles),
  153. this.isDeleteYuan,this.isDeletePicture,this.isDeleteLrc,this.context
  154. );
  155. taskpool.execute(task, taskpool.Priority.HIGH).then((result) => {
  156. if(result){
  157. this.onDeleteResult(true)
  158. }else {
  159. this.onDeleteResult(false)
  160. }
  161. }).catch((error: Error) => {
  162. console.error('Subtitle sync failed:', error);
  163. });
  164. }
  165. }
  166. //多选删除文件
  167. @Concurrent
  168. async function deleteMultipleFiles(
  169. selectedFilesStr:string,
  170. isDeleteYuan:boolean,
  171. isDeletePicture:boolean,
  172. isDeleteLrc:boolean,
  173. context:Context
  174. ) {
  175. const selectedFiles: VideoItem[] = JSON.parse(selectedFilesStr)
  176. if (ArrayUtil.isNotEmpty(selectedFiles)) {
  177. const table: MediaTable = new MediaTable(context)
  178. const playlistTable: PlaylistTable = new PlaylistTable(context)
  179. // 初始化数据库
  180. await new Promise<void>((resolve, reject) => {
  181. table.getRdbStore(context, (err:Error) => {
  182. err ? reject(err) : resolve();
  183. });
  184. });
  185. let hasPlaylistChanges = false; // 标记是否有歌单变化
  186. for (const item of selectedFiles) {
  187. console.info('onecold delete filePath = ' + item.filePath);
  188. if (item.type === CommonConstants.TYPE_IS_DIR) {
  189. table.deleteDataForParentPath(item.filePath, () => {
  190. FileUtil.rmdir(item.filePath).then(() => {
  191. return true
  192. }).catch((error: Error) => {
  193. console.error(error.message);
  194. return false
  195. });
  196. });
  197. } else {
  198. table.deleteData(item, async () => {
  199. if(isDeleteYuan){
  200. await FileUtil.unlink(item.filePath)
  201. }
  202. console.info(`onecold 封面文件=: ${item.pixelMapPath}`);
  203. if (isDeletePicture && item.pixelMapPath) {
  204. const picPath = FileUtil.getFilePath(item.pixelMapPath)//获取图
  205. if (FileUtil.accessSync(picPath)) {
  206. await FileUtil.unlink(picPath);
  207. console.info(`onecold 封面文件已删除: ${picPath}`);
  208. }
  209. }
  210. const lyricPath = item.filePath?.replace(/\.[^/.]+$/, ".lrc");
  211. console.info(`onecold 歌词文件=: ${lyricPath}`);
  212. if(isDeleteLrc) {
  213. if (lyricPath && FileUtil.accessSync(lyricPath)) {
  214. await FileUtil.unlink(lyricPath);
  215. console.info( `onecold 歌词文件已删除: ${lyricPath}`);
  216. }
  217. }
  218. // this.onDeleteResult(true,item)
  219. });
  220. // 先从所有歌单中移除这首歌(如果存在的话)
  221. LogUtil.info('heanup DeleteComptent', `准备从歌单中移除歌曲: ${item.name}, filePath: ${item.filePath}`);
  222. if (item.filePath) {
  223. try {
  224. const affectedPlaylists = await playlistTable.removeSongFromAllPlaylists(item.filePath);
  225. LogUtil.info('heanup DeleteComptent', `removeSongFromAllPlaylists 返回了 ${affectedPlaylists.length} 个受影响的歌单`);
  226. if (affectedPlaylists.length > 0) {
  227. hasPlaylistChanges = true;
  228. LogUtil.info('heanup DeleteComptent', `歌曲已从 ${affectedPlaylists.length} 个歌单中移除: ${item.name}`);
  229. } else {
  230. LogUtil.info('heanup DeleteComptent', `歌曲不在任何歌单中: ${item.name}`);
  231. }
  232. } catch (error) {
  233. LogUtil.error('heanup DeleteComptent', `从歌单移除歌曲失败: ${(error as Error).message}`);
  234. }
  235. } else {
  236. LogUtil.warn('heanup DeleteComptent', `歌曲 ${item.name} 的 filePath 为空`);
  237. }
  238. }
  239. }
  240. // 如果有歌单变化,发送刷新事件
  241. if (hasPlaylistChanges) {
  242. const eventRefresh: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAYLIST_REFRESH };
  243. emitter.emit(eventRefresh, {});
  244. LogUtil.info('heanup DeleteComptent', '已发送歌单刷新事件,更新歌单数量');
  245. }
  246. }
  247. return true
  248. }