diff --git a/entry/src/main/ets/common/EventConstants.ets b/entry/src/main/ets/common/EventConstants.ets index 9235d26..4f94329 100644 --- a/entry/src/main/ets/common/EventConstants.ets +++ b/entry/src/main/ets/common/EventConstants.ets @@ -7,6 +7,7 @@ export class EventConstants { static readonly MineRefreshEvent = "MineRefreshEvent" static readonly MediaActionEvent = "MediaActionEvent" static readonly JumpToRecordEvent = "JumpToRecordEvent" + static readonly JumpToToolsEvent = "JumpToToolsEvent" static readonly DownloadHistoryRefreshEvent = "DownloadHistoryRefreshEvent" static readonly MaterialListRefreshEvent = "MaterialListRefreshEvent" diff --git a/entry/src/main/ets/common/RouterUrls.ets b/entry/src/main/ets/common/RouterUrls.ets index a9b3238..676e53f 100644 --- a/entry/src/main/ets/common/RouterUrls.ets +++ b/entry/src/main/ets/common/RouterUrls.ets @@ -42,7 +42,12 @@ export class RouterUrls { /** * 添加水印页 */ - static readonly ADD_WATER_MARKER_PAGE = "pages/main/home/tools/AddWaterMarkerPage" + static readonly ADD_WATERMARK_PAGE = "pages/main/home/tools/AddWatermarkPage" + + /** + * 视频去水印页 + */ + static readonly REMOVE_WATERMARK_PAGE = "pages/main/home/tools/RemoveWatermarkPage" /** * MD5去重页 @@ -77,7 +82,12 @@ export class RouterUrls { /** * 视频转音频页 */ - static readonly TAKE_AUDIO_PAGE = "pages/main/home/tools/TakeAudioPage" + static readonly VIDEO_TO_AUDIO_PAGE = "pages/main/home/tools/VideoToAudioPage" + + /** + * 长图拼接页 + */ + static readonly IMAGE_MERGE_PAGE = "pages/main/home/tools/ImageMergePage" /** * 素材详情页 diff --git a/entry/src/main/ets/dialog/DownloadDialog.ets b/entry/src/main/ets/dialog/DownloadDialog.ets index df07083..c43b60d 100644 --- a/entry/src/main/ets/dialog/DownloadDialog.ets +++ b/entry/src/main/ets/dialog/DownloadDialog.ets @@ -1,5 +1,5 @@ import { ComponentContent } from '@kit.ArkUI'; -import { AppUtil, DisplayUtil, FileUtil, FormatUtil, NumberUtil, StrUtil, WindowUtil } from '@pura/harmony-utils'; +import { AppUtil, DisplayUtil, FileUtil, FormatUtil } from '@pura/harmony-utils'; import { DialogCallback } from '../callback/DialogCallback'; export enum DownloadStatus { @@ -29,7 +29,7 @@ function defaultBuilder(option: DownloadDialogOption) { option.status === DownloadStatus.VIDEO_DOWNLOADING ? '视频下载中' : option.status === DownloadStatus.AUDIO_DOWNLOADING ? '音频下载中' : '处理中') + (option.totalCount > 1 ? ` ${option.index + 1}/${option.totalCount}` : '')) - .fontColor($r('app.color.color_90ffffff')) + .fontColor($r('app.color.color_212226')) .fontSize(16) .visibility(option.status === DownloadStatus.COMPLETED ? Visibility.None : Visibility.Visible) @@ -44,8 +44,8 @@ function defaultBuilder(option: DownloadDialogOption) { Progress({ value: option.progress, total: option.totalSize, type: ProgressType.Linear }) .width('100%') .style({ strokeWidth: 12, strokeRadius: 6 }) - .color('#FC4F54') - .colorBlend($r('app.color.color_10ffffff')) + .color($r('app.color.color_466afd')) + // .colorBlend('#F1F2F6') .borderRadius(6) Text(FormatUtil.getFormatPercentage(option.progress / option.totalSize, 1)) @@ -56,14 +56,14 @@ function defaultBuilder(option: DownloadDialogOption) { .fontSize(10) .borderRadius(10) .borderWidth(1) - .borderColor($r('app.color.color_80ffffff')) + .borderColor(Color.White) .backgroundColor($r("app.color.color_466afd")) .translate({ x: (AppUtil.getUIContext().px2vp(DisplayUtil.getWidth()) * 0.8 - 80) * option.progress / option.totalSize }) } Text(`${FileUtil.getFormatFileSize(option.progress)}/${option.totalSize !== 0 ? FileUtil.getFormatFileSize(option.totalSize) : '获取中'}`) - .fontColor($r('app.color.color_999999')) + .fontColor($r('app.color.color_727686')) .fontSize(12) .margin({ top: 16 }) .visibility(option.status === DownloadStatus.PROCESSING ? Visibility.Hidden : Visibility.Visible) @@ -72,8 +72,8 @@ function defaultBuilder(option: DownloadDialogOption) { Button('取消下载', { type: ButtonType.Capsule, stateEffect: true }) .width(110) .height(36) - .backgroundColor($r('app.color.color_333333')) - .fontColor($r('app.color.color_50ffffff')) + .backgroundColor('#F1F2F6') + .fontColor('#80859B') .fontSize(15) .onClick(() => { if (option.callback?.cancel) { @@ -85,11 +85,8 @@ function defaultBuilder(option: DownloadDialogOption) { Button('后台下载', { type: ButtonType.Capsule, stateEffect: true }) .width(110) .height(36) - .linearGradient({ - colors: [['#F62C6C', 0.0], ['#FC4F54', 1.0]], - direction: GradientDirection.Right - }) - .fontColor($r('app.color.color_90ffffff')) + .backgroundColor($r('app.color.color_466afd')) + .fontColor(Color.White) .fontSize(15) .margin({ left: 10 }) .onClick(() => { @@ -106,10 +103,10 @@ function defaultBuilder(option: DownloadDialogOption) { .visibility(option.status === DownloadStatus.COMPLETED ? Visibility.None : Visibility.Visible) Column() { - Text(option.isAudio ? '已保存到本地' : '已保存到系统相册中').fontColor($r('app.color.color_90ffffff')).fontSize(16) + Text(option.isAudio ? '已保存到本地' : '已保存到系统相册中').fontColor($r('app.color.color_466afd')).fontSize(16) Text(option.isAudio ? '文件管理/我的手机/Download/素材魔方' : '文件管理/我的手机/Download/图库') - .fontColor($r('app.color.color_999999')) + .fontColor($r('app.color.color_727686')) .fontSize(12) .margin({ top: 10 }) @@ -117,8 +114,8 @@ function defaultBuilder(option: DownloadDialogOption) { Button('取消', { type: ButtonType.Capsule, stateEffect: true }) .width(110) .height(36) - .backgroundColor($r('app.color.color_333333')) - .fontColor($r('app.color.color_50ffffff')) + .backgroundColor('#F1F2F6') + .fontColor('#80859B') .fontSize(15) .onClick(() => { if (option.callback?.cancel) { @@ -132,11 +129,8 @@ function defaultBuilder(option: DownloadDialogOption) { Button('前往查看', { type: ButtonType.Capsule, stateEffect: true }) .width(110) .height(36) - .linearGradient({ - colors: [['#F62C6C', 0.0], ['#FC4F54', 1.0]], - direction: GradientDirection.Right - }) - .fontColor($r('app.color.color_90ffffff')) + .backgroundColor($r('app.color.color_466afd')) + .fontColor(Color.White) .fontSize(15) .onClick(() => { if (option.callback?.confirm) { @@ -153,7 +147,7 @@ function defaultBuilder(option: DownloadDialogOption) { } .width('80%') .borderRadius(10) - .backgroundColor($r('app.color.color_222222')) + .backgroundColor(Color.White) .padding(20) } diff --git a/entry/src/main/ets/dialog/JoinWxGroupCourseDialog.ets b/entry/src/main/ets/dialog/JoinWxGroupCourseDialog.ets index 76a7398..753a732 100644 --- a/entry/src/main/ets/dialog/JoinWxGroupCourseDialog.ets +++ b/entry/src/main/ets/dialog/JoinWxGroupCourseDialog.ets @@ -5,6 +5,7 @@ import { SaveUtils } from '../utils/SaveUtils'; import { LoginManager } from '../manager/LoginGlobalManager'; import { Want } from '@kit.AbilityKit'; import { WXApi } from '../utils/wechat/WXApiEventHandlerImpl'; +import { Constants } from '../common/Constants'; @CustomDialog export struct JoinWxGroupCourseDialog { @@ -15,22 +16,22 @@ export struct JoinWxGroupCourseDialog { isPlayback: boolean = true images: Array = [ - $r('app.media.ic_wx_group_tip1'), - $r("app.media.ic_wx_group_tip2"), - $r("app.media.ic_wx_group_tip3"), - $r("app.media.ic_wx_group_tip4"), - $r('app.media.ic_wx_group_tip5') + $r('app.media.ic_join_wx_group_tip1'), + $r('app.media.ic_join_wx_group_tip2'), + $r("app.media.ic_join_wx_group_tip3"), + $r("app.media.ic_join_wx_group_tip4"), + $r("app.media.ic_join_wx_group_tip5"), ]; steps: Array = ['第一步', '第二步', '第三步', '第四步', '第五步'] - qrCodePath = 'https://cdn.batiao8.com/kct/mp/kcsp_qrcode.png' + qrCodePath = 'https://cdn.batiao8.com/kct/mp/scmf_qrcode.png' @State currentIndex: number = 0 downloadImage() { try { - const cachePath = FileUtil.getCacheDirPath() + FileUtil.separator + 'kcsp_wx_group_qrcode.jpg'; + const cachePath = FileUtil.getCacheDirPath() + FileUtil.separator + 'scmf_wx_group_qrcode.jpg'; if (FileUtil.accessSync(cachePath)) { FileUtil.unlink(cachePath) } @@ -43,7 +44,7 @@ export struct JoinWxGroupCourseDialog { SaveUtils.saveImageVideoToAlbumDialog([cachePath], false) .then((saved) => { if (saved) { - PasteboardUtil.setDataTextSync(LoginManager.getUserInfo()!!.user_id) + PasteboardUtil.setDataTextSync(`${LoginManager.getUserInfo()!!.user_id}|${Constants.APP_ID}`) ToastUtils.show('ID复制成功') this.jumpToWxScan() this.controller.close() @@ -78,11 +79,11 @@ export struct JoinWxGroupCourseDialog { build() { RelativeContainer() { - Image($r('app.media.ic_wx_group_tip_bg')).width('100%').height(320) + Image($r('app.media.ic_join_wx_group_tip_bg')).width('100%').height(320) Text(this.isPlayback ? '添加直播回放助手流程' : '添加视频助手流程') .width('auto') - .fontColor(Color.White) + .fontColor($r('app.color.color_1a1a1a')) .fontSize(16) .fontWeight(FontWeight.Medium) .margin({ top: 16 }) @@ -95,10 +96,17 @@ export struct JoinWxGroupCourseDialog { List({space: 16}) { ForEach(this.steps, (item: string, index) => { ListItem() { - Text(item) - .fontColor(index == this.currentIndex ? Color.White : $r('app.color.color_bebebe')) - .fontSize(index == this.currentIndex ? 16 : 14) - .fontWeight(index == this.currentIndex ? FontWeight.Medium : FontWeight.Normal) + Stack() { + if (index === this.currentIndex) { + Image($r('app.media.ic_join_wx_group_tip_indicator')).width(44).height(10) + .margin({top: 10, left: 5}) + } + Text(item) + .fontColor(index === this.currentIndex ? $r('app.color.color_466afd') : '#858D9F') + .fontSize(index === this.currentIndex ? 16 : 14) + .fontWeight(index === this.currentIndex ? FontWeight.Medium : FontWeight.Normal) + .fontFamily(index === this.currentIndex ? 'almmsht' : '') + } } .onClick(() => { this.swiperController.changeIndex(index, true) @@ -137,9 +145,9 @@ export struct JoinWxGroupCourseDialog { Row() { Button('取消', { type: ButtonType.Capsule, stateEffect: false }) - .fontColor($r('app.color.color_50ffffff')) + .fontColor($r('app.color.color_1a1a1a')) .fontSize(15) - .backgroundColor($r('app.color.color_333333')) + .backgroundColor($r('app.color.color_eeeeee')) .width(126) .height(46) .onClick(() => { @@ -149,11 +157,7 @@ export struct JoinWxGroupCourseDialog { Button('前往微信扫码加群', { type: ButtonType.Capsule, stateEffect: false }) .fontColor(Color.White) .fontSize(15) - .backgroundColor(Color.Transparent) - .linearGradient({ - colors: [['#F62C6C', 0.0], ['#FC4F54', 1.0]], - direction: GradientDirection.Right - }) + .backgroundColor($r('app.color.color_466afd')) .layoutWeight(1) .height(46) .onClick(() => { @@ -164,7 +168,7 @@ export struct JoinWxGroupCourseDialog { this.downloadImage() }) } - .backgroundColor($r('app.color.color_222222')) + .backgroundColor(Color.White) .alignRules({ top: {anchor: 'swiper', align: VerticalAlign.Bottom} }) diff --git a/entry/src/main/ets/dialog/MaterialLoadingDialog.ets b/entry/src/main/ets/dialog/MaterialLoadingDialog.ets index faafc9f..d73adce 100644 --- a/entry/src/main/ets/dialog/MaterialLoadingDialog.ets +++ b/entry/src/main/ets/dialog/MaterialLoadingDialog.ets @@ -4,12 +4,12 @@ import { ComponentContent } from '@kit.ArkUI'; function defaultBuilder(text: string) { Column() { LoadingProgress() - .color(Color.White) + .color($r('app.color.color_466afd')) .width(50) .height(50) Text(text) - .fontColor(Color.White) + .fontColor($r('app.color.color_466afd')) .fontSize(12) .maxLines(1) .textOverflow({ overflow: TextOverflow.Ellipsis }) @@ -22,7 +22,7 @@ function defaultBuilder(text: string) { .height(124) .justifyContent(FlexAlign.Center) .borderRadius(6) - .backgroundColor($r('app.color.color_222222')) + .backgroundColor(Color.White) .padding({left: 10, right: 10}) } diff --git a/entry/src/main/ets/entity/MaterialInfoEntity.ets b/entry/src/main/ets/entity/MaterialInfoEntity.ets index de861fd..f9de540 100644 --- a/entry/src/main/ets/entity/MaterialInfoEntity.ets +++ b/entry/src/main/ets/entity/MaterialInfoEntity.ets @@ -52,11 +52,11 @@ export class MediaEntity { initFileName(): string { if (!this.name) { if (this instanceof VideoMaterial) { - this.name = `kcsp_${systemDateTime.getTime() + RandomUtil.getRandomInt(1000, 2000)}.mp4` + this.name = `scmf_${systemDateTime.getTime() + RandomUtil.getRandomInt(1000, 2000)}.mp4` } else if (this instanceof AudioMaterial) { - this.name = `kcsp_${systemDateTime.getTime() + RandomUtil.getRandomInt(1000, 2000)}.mp3` + this.name = `scmf_${systemDateTime.getTime() + RandomUtil.getRandomInt(1000, 2000)}.mp3` } else if (this instanceof ImageMaterial) { - this.name = `kcsp_${systemDateTime.getTime() + RandomUtil.getRandomInt(1000, 2000)}.jpeg` + this.name = `scmf_${systemDateTime.getTime() + RandomUtil.getRandomInt(1000, 2000)}.jpeg` } } return this.name diff --git a/entry/src/main/ets/entity/MenuEntity.ets b/entry/src/main/ets/entity/MenuEntity.ets index 29bfa4f..78e4485 100644 --- a/entry/src/main/ets/entity/MenuEntity.ets +++ b/entry/src/main/ets/entity/MenuEntity.ets @@ -1,5 +1,5 @@ -import { ArrayList } from "@kit.ArkTS"; -import { LoginManager } from "../manager/LoginGlobalManager"; +import { ArrayList } from '@kit.ArkTS'; +import { LoginManager } from '../manager/LoginGlobalManager'; export class MenuEntity { icon: Resource | null = null; diff --git a/entry/src/main/ets/entity/ToolMenuEntity.ets b/entry/src/main/ets/entity/ToolMenuEntity.ets new file mode 100644 index 0000000..59fa3cf --- /dev/null +++ b/entry/src/main/ets/entity/ToolMenuEntity.ets @@ -0,0 +1,32 @@ +import { ArrayList } from '@kit.ArkTS'; + +export class ToolMenuEntity { + icon: Resource | null = null; + title: string = ""; + alias: string = ""; + desc: string = ""; + count: string = ""; + colors: Array = []; + + constructor(icon: Resource, title: string, alias: string, desc: string, count: string, colors: Array) { + this.icon = icon; + this.title = title; + this.alias = alias; + this.desc = desc; + this.count = count + this.colors = colors + } +} + +export function toolsList(): ArrayList { + let list = new ArrayList() + list.add(new ToolMenuEntity($r("app.media.ic_tool_icon4"), "MD5修改", "resetMD5", '修改视频MD5', '0.2', ['#BCFFDE', '#EEFFF7'])) + list.add(new ToolMenuEntity($r("app.media.ic_tool_icon5"), "视频转文字", "videoToText", '文案轻松提取', '0.3', ['#DFE8FF', '#F8FAFF'])) + list.add(new ToolMenuEntity($r("app.media.ic_tool_icon6"), "视频转音频", "videoToAudio", '提取背景音乐', '1.2', ['#E9E3FF', '#F9F7FF'])) + list.add(new ToolMenuEntity($r("app.media.ic_tool_icon7"), "语音转文字", "audioToText", '内容轻松记录', '1.1', ['#FFE1E1', '#FFF3F3'])) + list.add(new ToolMenuEntity($r("app.media.ic_tool_icon8"), "视频加水印", "addWatermark", '提高原创识别度', '0.5', ['#C2EFFF', '#E3F8FF'])) + list.add(new ToolMenuEntity($r("app.media.ic_tool_icon9"), "长图拼接", "longImageMerge", '多张图形成一张', '0.4', ['#FFF4C6', '#FDFAEF'])) + list.add(new ToolMenuEntity($r("app.media.ic_tool_icon10"), "视频打码", "removeWatermark", '敏感信息无处漏', '0.1', ['#DFE8FF', '#F8FAFF'])) + list.add(new ToolMenuEntity($r("app.media.ic_tool_icon11"), "视频去原声", "removeAudio", '一键去背景音乐', '0.7', ['#F0DDFF', '#FAFAFE'])) + return list; +} \ No newline at end of file diff --git a/entry/src/main/ets/net/Api.ets b/entry/src/main/ets/net/Api.ets index 173d942..cef1747 100644 --- a/entry/src/main/ets/net/Api.ets +++ b/entry/src/main/ets/net/Api.ets @@ -44,6 +44,11 @@ export class Api { */ static readonly USER_DESTROY = '/api/user/destroy'; + /** + * 退出登陆 + */ + static readonly USER_LOGOUT = '/api/user/logout'; + /** * 用户账号列表 */ diff --git a/entry/src/main/ets/net/ApiService.ets b/entry/src/main/ets/net/ApiService.ets index a52d300..570f0fb 100644 --- a/entry/src/main/ets/net/ApiService.ets +++ b/entry/src/main/ets/net/ApiService.ets @@ -147,6 +147,14 @@ class ApiService { return AxiosRequest.post(Api.USER_DESTROY) } + /** + * 退出登陆 + * @returns + */ + logout(): Promise { + return AxiosRequest.post(Api.USER_LOGOUT) + } + /** * 解绑账号 * @returns diff --git a/entry/src/main/ets/pages/main/MainPage.ets b/entry/src/main/ets/pages/main/MainPage.ets index 5069732..03994f6 100644 --- a/entry/src/main/ets/pages/main/MainPage.ets +++ b/entry/src/main/ets/pages/main/MainPage.ets @@ -14,6 +14,7 @@ import { ConfigManager } from '../../manager/UserConfigManager'; import { EventReportGlobalManager } from '../../manager/EventReportGlobalManager'; import { PasteboardUtils } from '../../utils/PasteboardUtils'; import { MaterialPage } from './material/MaterialPage'; +import { ToolsPage } from './mine/tool/ToolsPage'; @Entry @ComponentV2 @@ -51,6 +52,7 @@ struct MainPage { aboutToDisappear(): void { AppUtil.getContext().eventHub.off(EventConstants.LoginSuccessEvent); AppUtil.getContext().eventHub.off(EventConstants.JumpToRecordEvent); + AppUtil.getContext().eventHub.off(EventConstants.JumpToToolsEvent); } onPageShow(): void { @@ -71,6 +73,9 @@ struct MainPage { this.tabController.changeIndex(3) this.currentIndex = 1 }) + AppUtil.getContext().eventHub.on(EventConstants.JumpToToolsEvent, () => { + this.tabController.changeIndex(2) + }) } checkPasteboard() { @@ -112,7 +117,7 @@ struct MainPage { .tabBar(this.tabBuilder(this.titles[1], 1, $r('app.media.ic_material_select'), $r('app.media.ic_material_default'))) TabContent() { - + ToolsPage() } .tabBar(this.tabBuilder(this.titles[2], 2, $r('app.media.ic_tool_select'), $r('app.media.ic_tool_default'))) diff --git a/entry/src/main/ets/pages/main/home/HomePage.ets b/entry/src/main/ets/pages/main/home/HomePage.ets index e0fabbd..f57c87a 100644 --- a/entry/src/main/ets/pages/main/home/HomePage.ets +++ b/entry/src/main/ets/pages/main/home/HomePage.ets @@ -340,20 +340,22 @@ export struct HomePage { .onClick(() => { switch (item.alias) { case 'videoToAudio': { - this.getUIContext().getRouter().pushUrl({url: RouterUrls.TAKE_AUDIO_PAGE}) + this.getUIContext().getRouter().pushUrl({url: RouterUrls.VIDEO_TO_AUDIO_PAGE}) break } case 'addWatermark': { - this.getUIContext().getRouter().pushUrl({url: RouterUrls.ADD_WATER_MARKER_PAGE}) + this.getUIContext().getRouter().pushUrl({url: RouterUrls.ADD_WATERMARK_PAGE}) break } case 'videoToText': { break } case 'longImageMerge': { + this.getUIContext().getRouter().pushUrl({url: RouterUrls.IMAGE_MERGE_PAGE}) break } case 'moreTools': { + AppUtil.getContext().eventHub.emit(EventConstants.JumpToToolsEvent); break } } diff --git a/entry/src/main/ets/pages/main/home/link/TakeMaterialPage.ets b/entry/src/main/ets/pages/main/home/link/TakeMaterialPage.ets index 08795b7..d1c56bc 100644 --- a/entry/src/main/ets/pages/main/home/link/TakeMaterialPage.ets +++ b/entry/src/main/ets/pages/main/home/link/TakeMaterialPage.ets @@ -380,6 +380,7 @@ struct TakeMaterialPage { parseUrl(url: string) { if (StrUtil.isNotEmpty(this.inputText)) { this.viewModel.getMaterialInfo(url); + KeyboardUtil.hide() } } @@ -499,7 +500,6 @@ struct TakeMaterialPage { .backgroundColor($r('app.color.color_466afd')) .onClick(() => { if (StrUtil.isNotEmpty(this.inputText)) { - KeyboardUtil.hide() this.parseUrl(this.inputText) EventReportGlobalManager.eventReport(EventConstants.GET_MATERIAL, "material-button", this.inputText) } else { diff --git a/entry/src/main/ets/pages/main/home/material/MaterialDetailPage.ets b/entry/src/main/ets/pages/main/home/material/MaterialDetailPage.ets index 9b7348c..17d2b03 100644 --- a/entry/src/main/ets/pages/main/home/material/MaterialDetailPage.ets +++ b/entry/src/main/ets/pages/main/home/material/MaterialDetailPage.ets @@ -87,7 +87,7 @@ struct MaterialDetailPage { download() { LoadingDialog.show(this.getUIContext()) - let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.jpeg` + let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.jpeg` let config: request.agent.Config = { action: request.agent.Action.DOWNLOAD, url: this.material!!.pic!!.url, diff --git a/entry/src/main/ets/pages/main/home/tools/AddAudioPage.ets b/entry/src/main/ets/pages/main/home/tools/AddAudioPage.ets index 4c5eb1c..304edf6 100644 --- a/entry/src/main/ets/pages/main/home/tools/AddAudioPage.ets +++ b/entry/src/main/ets/pages/main/home/tools/AddAudioPage.ets @@ -43,7 +43,7 @@ struct AddAudioPage { // 复制音频文件到缓存目录下 FileUtil.copyFileSync(audioFile.fd, cacheAudioPath) - let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp4` + let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` let cmd = `ffmpeg -i ${cacheVideoPath} -stream_loop -1 -i ${cacheAudioPath} -c:v copy -c:a aac -shortest -map 0:v -map 1:a ${outputPath}` MP4Parser.ffmpegCmd(cmd, { callBackResult: (code: number) => { diff --git a/entry/src/main/ets/pages/main/home/tools/AddWaterMarkerPage.ets b/entry/src/main/ets/pages/main/home/tools/AddWatermarkPage.ets similarity index 74% rename from entry/src/main/ets/pages/main/home/tools/AddWaterMarkerPage.ets rename to entry/src/main/ets/pages/main/home/tools/AddWatermarkPage.ets index 3210aa8..eb5ebfe 100644 --- a/entry/src/main/ets/pages/main/home/tools/AddWaterMarkerPage.ets +++ b/entry/src/main/ets/pages/main/home/tools/AddWatermarkPage.ets @@ -21,7 +21,7 @@ import { avSessionManager } from '../../../../manager/AVSessionManager' @Entry @ComponentV2 -struct AddWaterMarkerPage { +struct AddWatermarkPage { @Local uri?: string @Local currentTime: number = 0 @Local durationTime: number = 0 @@ -29,7 +29,7 @@ struct AddWaterMarkerPage { @Local isSuccess: boolean = false @Local playerSize: media.PixelMapParams = { width: 0, height: 0 } - @Local showWaterMaker: boolean = false + @Local showWatermark: boolean = false @Local textContent: string = '' @Local imagePath: string = '' @@ -37,10 +37,10 @@ struct AddWaterMarkerPage { private videoSize: media.PixelMapParams = { width: 0, height: 0 } private rect: RectPosition = { x: 0, y: 0, width: 0, height: 0 } - addWaterMarker() { + addWatermark() { LoadingDialog.show(this.getUIContext()) this.isSuccess = false - let cacheVideoPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp4` + let cacheVideoPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` if (FileUtil.accessSync(cacheVideoPath)) { FileUtil.unlinkSync(cacheVideoPath) } @@ -58,7 +58,7 @@ struct AddWaterMarkerPage { this.getUIContext().getComponentSnapshot().get(StrUtil.isNotEmpty(this.textContent) ? 'textWaterMarker' : 'imageWaterMarker') .then(async (image: image.PixelMap) => { let imagePath = await ImageUtil.savePixelMap(image, FileUtil.getCacheDirPath(), `cache_${systemDateTime.getTime()}.png`) - let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp4` + let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` let cmd = `ffmpeg -i ${cacheVideoPath} -i ${imagePath} -filter_complex [1:v]scale=${Math.round(imageWidth)}:${Math.round(imageHeight)}[wm];[0:v][wm]overlay=${imageX}:${imageY} -c:v h264 -pix_fmt yuv420p -y ${outputPath}` MP4Parser.ffmpegCmd(cmd, { callBackResult: (code: number) => { @@ -93,7 +93,7 @@ struct AddWaterMarkerPage { this.isSuccess = false this.uri = uris[0] - this.showWaterMaker = false + this.showWatermark = false this.textContent = '' this.imagePath = '' @@ -101,7 +101,7 @@ struct AddWaterMarkerPage { .then((size) => { this.videoSize = size if (size.width && size.height) { - const ratio = (DisplayUtil.getWidth() - 180) / size.width + const ratio = (DisplayUtil.getWidth() - 300) / size.width this.playerSize = {width: Math.ceil(size.width * ratio), height: Math.ceil(size.height * ratio)} } }) @@ -168,37 +168,28 @@ struct AddWaterMarkerPage { build() { Column() { - TitleBar({ title: '加水印' }) + TitleBar({ title: '视频加水印' }) - Column() { - Row() { - Text('上传视频').fontColor($r('app.color.color_90ffffff')).fontSize(16).fontWeight(FontWeight.Medium) - Text('(仅支持mp4格式)').fontColor($r('app.color.color_50ffffff')).fontSize(12) - }.alignSelf(ItemAlign.Start) - - RelativeContainer() { - Stack() { - Image($r('app.media.ic_add_video')).width(44).height(44) + Stack() { + Stack() { + Column() { + Image($r('app.media.ic_add_video')).width(40).height(40) + Text('请上传视频').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium).margin({ top: 8}) } - .width(140) - .height(140) - .borderRadius(10) - .backgroundColor($r('app.color.color_333333')) - .alignRules({ - start: { anchor: '__container__', align: HorizontalAlign.Start }, - top: { anchor: '__container__', align: VerticalAlign.Top }, - end: { anchor: '__container__', align: HorizontalAlign.End }, - bottom: { anchor: '__container__', align: VerticalAlign.Bottom } - }) - .onClick(() => { - this.selectVideo() - }) } - .height(220) - .margin({ top: 12 }) - .borderRadius(8) - .backgroundColor($r('app.color.color_222222')) - }.margin({ left: 16, top: 16, right: 16 }) + .width('100%') + .aspectRatio(1) + .borderRadius(20) + .backgroundColor(Color.White) + .shadow({radius: 10, color: '#1a9399a1'}) + .onClick(() => { + this.selectVideo() + }) + } + .width('100%') + .height('auto') + .padding({left: 32, right: 32}) + .margin({top: 40}) .visibility(this.uri ? Visibility.None : Visibility.Visible) Column() { @@ -305,7 +296,7 @@ struct AddWaterMarkerPage { bottom: { anchor: 'video', align: VerticalAlign.Bottom } }) - if (this.showWaterMaker && this.uri && !this.isSuccess) { + if (this.showWatermark && this.uri && !this.isSuccess) { WaterMarkerView({ content: this.textContent, imagePath: this.imagePath, @@ -313,7 +304,7 @@ struct AddWaterMarkerPage { this.rect = rect }, onClose: () => { - this.showWaterMaker = false + this.showWatermark = false this.textContent = '' this.imagePath = '' } @@ -332,25 +323,36 @@ struct AddWaterMarkerPage { Row() { Column(){ - Image($r('app.media.ic_text_water_marker')).width(50).height(50) - Text('文字').fontColor($r('app.color.color_90ffffff')).fontSize(14).margin({ top: 8 }) + Image($r('app.media.ic_watermark_icon1')).width(26).height(26) + Text('水印').fontColor($r('app.color.color_212226')).fontSize(12).margin({ top: 8 }) } + .layoutWeight(1) .onClick(() => { - if (!this.showWaterMaker) { + + }) + + Column(){ + Image($r('app.media.ic_watermark_icon2')).width(26).height(26) + Text('文字').fontColor($r('app.color.color_212226')).fontSize(12).margin({ top: 8 }) + } + .layoutWeight(1) + .onClick(() => { + if (!this.showWatermark) { this.controller.stop() EditTextDialog.show(this.getUIContext(), {title: '添加水印', hintText: '请输入文字', confirm: (text) => { this.textContent = text - this.showWaterMaker = true + this.showWatermark = true }}) } }) + Column(){ - Image($r('app.media.ic_image_water_marker')).width(50).height(50) - Text('图片').fontColor($r('app.color.color_90ffffff')).fontSize(14).margin({ top: 8 }) + Image($r('app.media.ic_watermark_icon3')).width(26).height(26) + Text('图片').fontColor($r('app.color.color_212226')).fontSize(12).margin({ top: 8 }) } - .margin({ left: 50 }) + .layoutWeight(1) .onClick(() => { - if (!this.showWaterMaker) { + if (!this.showWatermark) { this.controller.stop() PhotoHelper.selectEasy({ MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE, @@ -363,60 +365,88 @@ struct AddWaterMarkerPage { if (uris.length != 0) { this.isSuccess = false this.imagePath = uris[0] - this.showWaterMaker = true + this.showWatermark = true } }) } }) } - .margin({ top: 20 }) - - Row() { - Text(this.isSuccess ? '重新上传' : '取消').fontColor($r('app.color.color_90ffffff')) - .fontSize(17) - .margin({ left: 16 }) - .onClick(() => { - this.controller.stop() - if (this.isSuccess) { - this.selectVideo() - } else { - this.getUIContext().getRouter().back() - } - }) - Blank().layoutWeight(1) - Text(this.isSuccess ? '保存' : '确定') - .fontColor($r("app.color.color_466afd")) - .fontSize(17) - .margin({ right: 16 }) - .onClick(() => { - this.controller.stop() - if (this.isSuccess) { - SaveUtils.saveImageVideoToAlbumDialog([this.uri!!]) - .then((saved) => { - if (saved) { - this.uri = undefined - this.showDownloadDialog() - } else { - ToastUtils.show('保存失败') - } - }) - .catch((e: BusinessError) => { - ToastUtils.show('保存失败:' + e.message) - }) - - } else { - if (this.showWaterMaker) { - this.addWaterMarker() - } else { - ToastUtils.show('请添加水印') - } - } - }) - } - .margin({ top: 20, bottom: 30 }) + .width('90%') + .height(82) + .margin({ top: 20, bottom: 20 }) + .borderRadius(10) + .backgroundColor(Color.White) } .layoutWeight(1) .visibility(this.uri ? Visibility.Visible : Visibility.None) + + Blank().layoutWeight(1).visibility(this.uri ? Visibility.None : Visibility.Visible) + + Stack() { + Button('确认处理', { type: ButtonType.Capsule, stateEffect: true }) + .width('100%') + .height(46) + .fontColor(Color.White) + .fontSize(15) + .fontWeight(FontWeight.Medium) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + if (this.uri) { + this.addWatermark() + } else { + ToastUtils.show('请上传视频') + } + }) + .visibility(!this.isSuccess ? Visibility.Visible : Visibility.None) + + Row() { + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_reupload')).width(20).height(20) + Text('重新上传').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .borderWidth(1) + .borderColor($r('app.color.color_466afd')) + .backgroundColor(Color.Transparent) + .onClick(() => { + this.controller.stop() + this.selectVideo() + }) + + Blank().width(9) + + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_download3')).width(20).height(20) + Text('保存').fontColor(Color.White).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + this.controller.stop() + SaveUtils.saveImageVideoToAlbumDialog([this.uri!!]) + .then((saved) => { + if (saved) { + this.uri = undefined + this.showDownloadDialog() + } else { + ToastUtils.show('保存失败') + } + }) + .catch((e: BusinessError) => { + ToastUtils.show('保存失败:' + e.message) + }) + }) + } + .visibility(this.isSuccess ? Visibility.Visible : Visibility.None) + } + .padding({left: 16, top: 9, right: 16, bottom: 30 }) + .backgroundColor(Color.White) } .width('100%') .height('100%') diff --git a/entry/src/main/ets/pages/main/home/tools/ClipVideoPage.ets b/entry/src/main/ets/pages/main/home/tools/ClipVideoPage.ets index b894281..2f00f9c 100644 --- a/entry/src/main/ets/pages/main/home/tools/ClipVideoPage.ets +++ b/entry/src/main/ets/pages/main/home/tools/ClipVideoPage.ets @@ -35,7 +35,7 @@ struct ClipVideoPage { clipVideo() { LoadingDialog.show(this.getUIContext()) this.isSuccess = false - let cacheVideoPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp4` + let cacheVideoPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` if (FileUtil.accessSync(cacheVideoPath)) { FileUtil.unlinkSync(cacheVideoPath) } @@ -68,7 +68,7 @@ struct ClipVideoPage { clipY = clipHeight === originPlayerSize.height ? 0 : (originPlayerSize.height!! - this.playerSize.height!!) / 2 * (this.videoSize.height!!) / originPlayerSize.height!! } - let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp4` + let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` let cmd = `ffmpeg -i ${cacheVideoPath} -vf \"crop=${Math.ceil(clipWidth)}:${Math.ceil(clipHeight)}:${Math.ceil(clipX)}:${Math.ceil(clipY)}\" -c:v h264 -pix_fmt yuv420p -y ${outputPath}` MP4Parser.ffmpegCmd(cmd, { callBackResult: (code: number) => { diff --git a/entry/src/main/ets/pages/main/home/tools/ImageMergePage.ets b/entry/src/main/ets/pages/main/home/tools/ImageMergePage.ets new file mode 100644 index 0000000..afe99bc --- /dev/null +++ b/entry/src/main/ets/pages/main/home/tools/ImageMergePage.ets @@ -0,0 +1,251 @@ +import { PhotoHelper } from '@pura/picker_utils' +import { TitleBar } from '../../../../view/TitleBar' +import { photoAccessHelper } from '@kit.MediaLibraryKit' +import { BusinessError, systemDateTime } from '@kit.BasicServicesKit' +import { AppUtil, FileUtil } from '@pura/harmony-utils' +import { ToastUtils } from '../../../../utils/ToastUtils' +import { fileIo } from '@kit.CoreFileKit' +import { SaveUtils } from '../../../../utils/SaveUtils' +import { LoadingDialog } from '../../../../dialog/LoadingDialog' +import { DownloadDialog, DownloadStatus } from '../../../../dialog/DownloadDialog' +import { EventConstants } from '../../../../common/EventConstants' +import { TipDialog } from '../../../../dialog/TipDialog' +import { avSessionManager } from '../../../../manager/AVSessionManager' + +@Entry +@ComponentV2 +struct ImageMergePage { + @Local uri?: string = undefined + @Local selectedImage?: string = undefined + @Local imageUris: Array = [] + @Local currentTime: number = 0 + @Local durationTime: number = 0 + @Local isPlaying: boolean = false + @Local isSuccess: boolean = false + + private selectedImages: Array = [] + + mergeImage() { + + } + + selectPhotos() { + PhotoHelper.select({ + MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE, + maxSelectNumber: 9, + preselectedUris: this.selectedImages, + isOriginalSupported: true, + }) + .then((result: photoAccessHelper.PhotoSelectResult) => { + if (result.photoUris.length != 0) { + this.isSuccess = false + this.selectedImages = result.photoUris + this.imageUris = result.photoUris + this.selectedImage = result.photoUris[0] + } + }) + .catch((e: BusinessError) => { + ToastUtils.show(e.message) + }) + } + + showDownloadDialog() { + DownloadDialog.show(this.getUIContext(), { status: DownloadStatus.COMPLETED, totalSize: 0, progress: 0, totalCount: 1, index: 0, callback: { + confirm: () => { + AppUtil.getContext().eventHub.emit(EventConstants.JumpToRecordEvent, 0) + this.getUIContext().getRouter().back() + } + } }) + } + + onBackPress(): boolean | void { + if (this.isSuccess) { + TipDialog.show(this.getUIContext(), {title:'温馨提示', content:'图片尚未保存,是否确定退出?', callback: { + confirm: () => { + this.getUIContext().getRouter().back() + } + }}) + return true + } + return false + } + + build() { + Column() { + TitleBar({ title: '长图拼接' }) + + Stack() { + Stack() { + Stack() { + Column() { + Image($r('app.media.ic_add_image')).width(40).height(40) + Text('请上传图片').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium).margin({ top: 8}) + } + } + .width('100%') + .height('100%') + .onClick(() => { + this.selectPhotos() + }) + .visibility(this.imageUris.length !== 0 ? Visibility.None : Visibility.Visible) + + Image(this.selectedImage).width('100%').height('100%') + .borderRadius(20) + .visibility(this.imageUris.length === 0 ? Visibility.None : Visibility.Visible) + } + .width('100%') + .aspectRatio(1) + .borderRadius(20) + .backgroundColor(Color.White) + .shadow({radius: 10, color: '#1a9399a1'}) + } + .width('100%') + .height('auto') + .padding({left: 32, right: 32}) + .margin({top: 40}) + + Blank().layoutWeight(1) + + Scroll() { + List({space: 8}) { + ForEach(this.imageUris, (item: string, index) => { + ListItem() { + RelativeContainer() { + Stack() { + Image(item).width('100%').height('100%').borderRadius(6) + Text(`${index + 1}`) + .width(20) + .height(20) + .textAlign(TextAlign.Center) + .fontColor(Color.White) + .fontSize(14) + .borderRadius(10) + .backgroundColor('#99000000') + } + .width(80) + .height(80) + .margin({top: 11, right: 11}) + + Image($r('app.media.ic_delete_image')).width(22).height(22) + .alignRules({ + right: {anchor: '__container__', align: HorizontalAlign.End} + }) + .onClick(() => { + this.imageUris.splice(index, 1) + if (this.imageUris.length === 0) { + this.selectedImage = undefined + } else { + if (item !== this.selectedImage) { + this.selectedImage = this.imageUris[0] + } + } + }) + } + .height('100%') + .aspectRatio(1) + } + .onClick(() => { + this.selectedImage = item + }) + }) + + if (this.imageUris.length > 0 && this.imageUris.length < 9) { + ListItem() { + Stack() { + Column() { + Image($r('app.media.ic_add_image')).width(24).height(24) + Text('请上传图片').fontColor($r('app.color.color_466afd')).fontSize(10).margin({top: 4}) + } + } + .width(80) + .height(80) + .borderRadius(6) + .backgroundColor(Color.White) + .margin({top: 11, right: 11}) + .onClick(() => { + this.selectPhotos() + }) + } + } + } + .width('100%') + .scrollBar(BarState.Off) + .listDirection(Axis.Horizontal) + .padding({left: 32, right: 32}) + } + .width('100%') + .height(92) + .scrollBar(BarState.Off) + .scrollable(ScrollDirection.Horizontal) + .margin({bottom: 20}) + + + Stack() { + Button('确认处理', { type: ButtonType.Capsule, stateEffect: true }) + .width('100%') + .height(46) + .fontColor(Color.White) + .fontSize(15) + .fontWeight(FontWeight.Medium) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + if (this.imageUris) { + this.mergeImage() + } else { + ToastUtils.show('请上传图片') + } + }) + .visibility(!this.isSuccess ? Visibility.Visible : Visibility.None) + + Row() { + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_reupload')).width(20).height(20) + Text('重新上传').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .borderWidth(1) + .borderColor($r('app.color.color_466afd')) + .backgroundColor(Color.Transparent) + .onClick(() => { + this.selectPhotos() + }) + + Blank().width(9) + + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_download3')).width(20).height(20) + Text('保存').fontColor(Color.White).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + SaveUtils.saveImageVideoToAlbumDialog([this.uri!!]) + .then((saved) => { + if (saved) { + this.imageUris = [] + this.showDownloadDialog() + } else { + ToastUtils.show('保存失败') + } + }) + .catch((e: BusinessError) => { + ToastUtils.show('保存失败:' + e.message) + }) + }) + } + .visibility(this.isSuccess ? Visibility.Visible : Visibility.None) + } + .padding({left: 16, top: 9, right: 16, bottom: 30 }) + .backgroundColor(Color.White) + } + .width('100%') + .height('100%') + .backgroundColor($r('app.color.window_background')) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/main/home/tools/MD5ResetPage.ets b/entry/src/main/ets/pages/main/home/tools/MD5ResetPage.ets index b23c078..60495ee 100644 --- a/entry/src/main/ets/pages/main/home/tools/MD5ResetPage.ets +++ b/entry/src/main/ets/pages/main/home/tools/MD5ResetPage.ets @@ -25,7 +25,7 @@ struct MD5ResetPage { modifyMD5() { LoadingDialog.show(this.getUIContext()) this.isSuccess = false - let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp4` + let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` if (FileUtil.accessSync(outputPath)) { FileUtil.unlinkSync(outputPath) } @@ -115,172 +115,201 @@ struct MD5ResetPage { Column() { TitleBar({ title: 'MD5去重' }) - Column() { - Row() { - Text('上传视频').fontColor($r('app.color.color_90ffffff')).fontSize(16).fontWeight(FontWeight.Medium) - Text('(仅支持mp4格式)').fontColor($r('app.color.color_50ffffff')).fontSize(12) - }.alignSelf(ItemAlign.Start) - - RelativeContainer() { + Stack() { + Stack() { Stack() { - Image($r('app.media.ic_add_video')).width(44).height(44) + Column() { + Image($r('app.media.ic_add_video')).width(40).height(40) + Text('请上传视频').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium).margin({ top: 8}) + } } - .width(140) - .height(140) - .borderRadius(10) - .backgroundColor($r('app.color.color_333333')) - .alignRules({ - start: { anchor: '__container__', align: HorizontalAlign.Start }, - top: { anchor: '__container__', align: VerticalAlign.Top }, - end: { anchor: '__container__', align: HorizontalAlign.End }, - bottom: { anchor: '__container__', align: VerticalAlign.Bottom } - }) + .width('100%') + .height('100%') .onClick(() => { this.selectVideo() }) - } - .height(220) - .margin({ top: 12 }) - .borderRadius(8) - .backgroundColor($r('app.color.color_222222')) - }.margin({ left: 16, top: 16, right: 16 }) - .visibility(this.uri ? Visibility.None : Visibility.Visible) + .visibility(this.uri ? Visibility.None : Visibility.Visible) - Column() { - RelativeContainer() { - Video({ - src: this.uri, // 设置视频源 - controller: this.controller, //设置视频控制器,可以控制视频的播放状态 - posterOptions: { showFirstFrame: true } - }) - .width('100%') - .height('100%') - .backgroundColor($r('app.color.window_background')) - .controls(false) // 设置是否显示默认控制条 - .autoPlay(false) // 设置是否自动播放 - .loop(false) // 设置是否循环播放 - .objectFit(ImageFit.Contain) // 设置视频填充模式 - .onPrepared((event) => { - if (event) { - this.durationTime = event.duration - } + RelativeContainer() { + Video({ + src: this.uri, // 设置视频源 + controller: this.controller, //设置视频控制器,可以控制视频的播放状态 + posterOptions: { showFirstFrame: true } }) - .onUpdate((event) => { - if (event) { - this.currentTime = event.time - } - }) - .onStart(() => { - this.isPlaying = true - }) - .onPause(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onStop(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onFinish(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onError(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onDisAppear(() => { - avSessionManager.deactivate() - }) - - Image($r('app.media.ic_play_video')) - .width(50) - .height(50) - .visibility(this.isPlaying ? Visibility.None : Visibility.Visible) - .onClick(async () => { - await avSessionManager.activate() - this.controller.start() - }) - .alignRules({ - left: { anchor: '__container__', align: HorizontalAlign.Start }, - top: { anchor: '__container__', align: VerticalAlign.Top }, - right: { anchor: '__container__', align: HorizontalAlign.End }, - bottom: { anchor: '__container__', align: VerticalAlign.Bottom }, - }) - - Row() { - Image(this.isPlaying ? $r('app.media.ic_player_controls_pause') : $r('app.media.ic_player_controls_play')) - .width(20) - .height(20) - .margin({ right: 20 }) - .onClick(async () => { - if (this.isPlaying) { - this.controller.pause() - } else { - await avSessionManager.activate() - this.controller.start() + .width('100%') + .height('100%') + .borderRadius(20) + .backgroundColor(Color.White) + .controls(false) // 设置是否显示默认控制条 + .autoPlay(false) // 设置是否自动播放 + .loop(false) // 设置是否循环播放 + .objectFit(ImageFit.Contain) // 设置视频填充模式 + .onPrepared((event) => { + if (event) { + this.durationTime = event.duration } }) - Text(this.formatTime(this.currentTime)).width(35).fontColor(Color.White).fontSize(12) - Slider({ - value: this.currentTime, - min: 0, - max: this.durationTime - }) - .blockColor(Color.White) - .trackColor($r('app.color.color_60ffffff')) - .onChange((value: number, mode: SliderChangeMode) => { - this.controller.setCurrentTime(value); // 设置视频播放的进度跳转到value处 + .onUpdate((event) => { + if (event) { + this.currentTime = event.time + } }) - .layoutWeight(1) - Text(this.formatTime(this.durationTime)).width(35).fontColor(Color.White).fontSize(12) + .onStart(() => { + this.isPlaying = true + }) + .onPause(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onStop(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onFinish(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onError(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onDisAppear(() => { + avSessionManager.deactivate() + }) + + Image($r('app.media.ic_play_video')) + .width(50) + .height(50) + .visibility(this.isPlaying ? Visibility.None : Visibility.Visible) + .onClick(async () => { + await avSessionManager.activate() + this.controller.start() + }) + .alignRules({ + left: { anchor: '__container__', align: HorizontalAlign.Start }, + top: { anchor: '__container__', align: VerticalAlign.Top }, + right: { anchor: '__container__', align: HorizontalAlign.End }, + bottom: { anchor: '__container__', align: VerticalAlign.Bottom }, + }) + + Row() { + Image(this.isPlaying ? $r('app.media.ic_player_controls_pause') : $r('app.media.ic_player_controls_play')) + .width(20) + .height(20) + .margin({ right: 20 }) + .onClick(async () => { + if (this.isPlaying) { + this.controller.pause() + } else { + await avSessionManager.activate() + this.controller.start() + } + }) + Text(this.formatTime(this.currentTime)).width(35).fontColor(Color.White).fontSize(12) + Slider({ + value: this.currentTime, + min: 0, + max: this.durationTime + }) + .blockColor(Color.White) + .trackColor($r('app.color.color_60ffffff')) + .onChange((value: number, mode: SliderChangeMode) => { + this.controller.setCurrentTime(value); // 设置视频播放的进度跳转到value处 + }) + .layoutWeight(1) + Text(this.formatTime(this.durationTime)).width(35).fontColor(Color.White).fontSize(12) + } + .opacity(0.8) + .width("100%") + .borderRadius({bottomLeft: 20, bottomRight: 20}) + .backgroundColor('#1A000000') + .padding({ left: 30, right: 30 }) + .alignRules({ + bottom: { anchor: '__container__', align: VerticalAlign.Bottom } + }) } - .opacity(0.8) - .width("100%") - .padding({ left: 30, right: 30 }) - .alignRules({ - bottom: { anchor: '__container__', align: VerticalAlign.Bottom } - }) + .width('100%') + .height('100%') + .visibility(this.uri ? Visibility.Visible : Visibility.None) } - .layoutWeight(1) + .width('100%') + .aspectRatio(1) + .borderRadius(20) + .backgroundColor(Color.White) + .shadow({radius: 10, color: '#1a9399a1'}) + } + .width('100%') + .height('auto') + .padding({left: 32, right: 32}) + .margin({top: 40}) + + Blank().layoutWeight(1) + + Stack() { + Button('确认处理', { type: ButtonType.Capsule, stateEffect: true }) + .width('100%') + .height(46) + .fontColor(Color.White) + .fontSize(15) + .fontWeight(FontWeight.Medium) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + if (this.uri) { + this.modifyMD5() + } else { + ToastUtils.show('请上传视频') + } + }) + .visibility(!this.isSuccess ? Visibility.Visible : Visibility.None) Row() { - Text(this.isSuccess ? '重新上传' : '取消').fontColor($r('app.color.color_90ffffff')).fontSize(17).margin({ left: 16 }) - .onClick(() => { - this.controller.stop() - if (this.isSuccess) { - this.selectVideo() - } else { - this.getUIContext().getRouter().back() - } - }) - Blank().layoutWeight(1) - Text(this.isSuccess ? '保存' : '确定').fontColor($r("app.color.color_466afd")).fontSize(17).margin({ right: 16 }) - .onClick(() => { - this.controller.stop() - if (this.isSuccess) { - SaveUtils.saveImageVideoToAlbumDialog([this.uri!!]) - .then((saved) => { - if (saved) { - this.uri = undefined - this.showDownloadDialog() - } else { - ToastUtils.show('保存失败') - } - }) - .catch((e: BusinessError) => { - ToastUtils.show('保存失败:' + e.message) - }) + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_reupload')).width(20).height(20) + Text('重新上传').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .borderWidth(1) + .borderColor($r('app.color.color_466afd')) + .backgroundColor(Color.Transparent) + .onClick(() => { + this.controller.stop() + this.selectVideo() + }) - } else { - this.modifyMD5() - } - }) + Blank().width(9) + + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_download3')).width(20).height(20) + Text('保存').fontColor(Color.White).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + this.controller.stop() + SaveUtils.saveImageVideoToAlbumDialog([this.uri!!]) + .then((saved) => { + if (saved) { + this.uri = undefined + this.showDownloadDialog() + } else { + ToastUtils.show('保存失败') + } + }) + .catch((e: BusinessError) => { + ToastUtils.show('保存失败:' + e.message) + }) + }) } - .margin({ top: 140, bottom: 30 }) + .visibility(this.isSuccess ? Visibility.Visible : Visibility.None) } - .layoutWeight(1) - .visibility(this.uri ? Visibility.Visible : Visibility.None) + .padding({left: 16, top: 9, right: 16, bottom: 30 }) + .backgroundColor(Color.White) } .width('100%') .height('100%') diff --git a/entry/src/main/ets/pages/main/home/tools/RemoveAudioPage.ets b/entry/src/main/ets/pages/main/home/tools/RemoveAudioPage.ets index 74f5344..7848aca 100644 --- a/entry/src/main/ets/pages/main/home/tools/RemoveAudioPage.ets +++ b/entry/src/main/ets/pages/main/home/tools/RemoveAudioPage.ets @@ -23,7 +23,7 @@ struct RemoveAudioPage { @Local isPlaying: boolean = false @Local isSuccess: boolean = false - mirrorVideo() { + removeAudio() { LoadingDialog.show(this.getUIContext()) this.isSuccess = false let cachePath = FileUtil.getCacheDirPath() + FileUtil.separator + `cache_${systemDateTime.getTime()}.mp4` @@ -34,7 +34,7 @@ struct RemoveAudioPage { // 复制文件到缓存目录下 FileUtil.copyFileSync(file.fd, cachePath) - let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp4` + let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` let cmd = `ffmpeg -i ${cachePath} -an -c:v copy ${outputPath}` MP4Parser.ffmpegCmd(cmd, { callBackResult: (code: number) => { @@ -119,174 +119,203 @@ struct RemoveAudioPage { build() { Column() { - TitleBar({ title: '去音乐' }) + TitleBar({ title: '视频去原声' }) - Column() { - Row() { - Text('上传视频').fontColor($r('app.color.color_90ffffff')).fontSize(16).fontWeight(FontWeight.Medium) - Text('(仅支持mp4格式)').fontColor($r('app.color.color_50ffffff')).fontSize(12) - }.alignSelf(ItemAlign.Start) - - RelativeContainer() { + Stack() { + Stack() { Stack() { - Image($r('app.media.ic_add_video')).width(44).height(44) + Column() { + Image($r('app.media.ic_add_video')).width(40).height(40) + Text('请上传视频').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium).margin({ top: 8}) + } } - .width(140) - .height(140) - .borderRadius(10) - .backgroundColor($r('app.color.color_333333')) - .alignRules({ - start: { anchor: '__container__', align: HorizontalAlign.Start }, - top: { anchor: '__container__', align: VerticalAlign.Top }, - end: { anchor: '__container__', align: HorizontalAlign.End }, - bottom: { anchor: '__container__', align: VerticalAlign.Bottom } - }) + .width('100%') + .height('100%') .onClick(() => { this.selectVideo() }) - } - .height(220) - .margin({ top: 12 }) - .borderRadius(8) - .backgroundColor($r('app.color.color_222222')) - }.margin({ left: 16, top: 16, right: 16 }) - .visibility(this.uri ? Visibility.None : Visibility.Visible) + .visibility(this.uri ? Visibility.None : Visibility.Visible) - Column() { - RelativeContainer() { - Video({ - src: this.uri, // 设置视频源 - controller: this.controller, //设置视频控制器,可以控制视频的播放状态 - posterOptions: { showFirstFrame: true } - }) - .width('100%') - .height('100%') - .backgroundColor($r('app.color.window_background')) - .controls(false) // 设置是否显示默认控制条 - .autoPlay(false) // 设置是否自动播放 - .loop(false) // 设置是否循环播放 - .objectFit(ImageFit.Contain) // 设置视频填充模式 - .onPrepared((event) => { - if (event) { - this.durationTime = event.duration - } + RelativeContainer() { + Video({ + src: this.uri, // 设置视频源 + controller: this.controller, //设置视频控制器,可以控制视频的播放状态 + posterOptions: { showFirstFrame: true } }) - .onUpdate((event) => { - if (event) { - this.currentTime = event.time - } - }) - .onStart(() => { - this.isPlaying = true - }) - .onPause(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onStop(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onFinish(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onError(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onDisAppear(() => { - avSessionManager.deactivate() - }) - - Image($r('app.media.ic_play_video')) - .width(50) - .height(50) - .visibility(this.isPlaying ? Visibility.None : Visibility.Visible) - .onClick(async () => { - await avSessionManager.activate() - this.controller.start() - }) - .alignRules({ - left: { anchor: '__container__', align: HorizontalAlign.Start }, - top: { anchor: '__container__', align: VerticalAlign.Top }, - right: { anchor: '__container__', align: HorizontalAlign.End }, - bottom: { anchor: '__container__', align: VerticalAlign.Bottom }, - }) - - Row() { - Image(this.isPlaying ? $r('app.media.ic_player_controls_pause') : $r('app.media.ic_player_controls_play')) - .width(20) - .height(20) - .margin({ right: 20 }) - .onClick(async () => { - if (this.isPlaying) { - this.controller.pause() - } else { - await avSessionManager.activate() - this.controller.start() + .width('100%') + .height('100%') + .borderRadius(20) + .backgroundColor(Color.White) + .controls(false) // 设置是否显示默认控制条 + .autoPlay(false) // 设置是否自动播放 + .loop(false) // 设置是否循环播放 + .objectFit(ImageFit.Contain) // 设置视频填充模式 + .onPrepared((event) => { + if (event) { + this.durationTime = event.duration } }) - Text(this.formatTime(this.currentTime)).width(35).fontColor(Color.White).fontSize(12) - Slider({ - value: this.currentTime, - min: 0, - max: this.durationTime - }) - .blockColor(Color.White) - .trackColor($r('app.color.color_60ffffff')) - .onChange((value: number, mode: SliderChangeMode) => { - this.controller.setCurrentTime(value); // 设置视频播放的进度跳转到value处 + .onUpdate((event) => { + if (event) { + this.currentTime = event.time + } }) - .layoutWeight(1) - Text(this.formatTime(this.durationTime)).width(35).fontColor(Color.White).fontSize(12) + .onStart(() => { + this.isPlaying = true + }) + .onPause(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onStop(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onFinish(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onError(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onDisAppear(() => { + avSessionManager.deactivate() + }) + + Image($r('app.media.ic_play_video')) + .width(50) + .height(50) + .visibility(this.isPlaying ? Visibility.None : Visibility.Visible) + .onClick(async () => { + await avSessionManager.activate() + this.controller.start() + }) + .alignRules({ + left: { anchor: '__container__', align: HorizontalAlign.Start }, + top: { anchor: '__container__', align: VerticalAlign.Top }, + right: { anchor: '__container__', align: HorizontalAlign.End }, + bottom: { anchor: '__container__', align: VerticalAlign.Bottom }, + }) + + Row() { + Image(this.isPlaying ? $r('app.media.ic_player_controls_pause') : $r('app.media.ic_player_controls_play')) + .width(20) + .height(20) + .margin({ right: 20 }) + .onClick(async () => { + if (this.isPlaying) { + this.controller.pause() + } else { + await avSessionManager.activate() + this.controller.start() + } + }) + Text(this.formatTime(this.currentTime)).width(35).fontColor(Color.White).fontSize(12) + Slider({ + value: this.currentTime, + min: 0, + max: this.durationTime + }) + .blockColor(Color.White) + .trackColor($r('app.color.color_60ffffff')) + .onChange((value: number, mode: SliderChangeMode) => { + this.controller.setCurrentTime(value); // 设置视频播放的进度跳转到value处 + }) + .layoutWeight(1) + Text(this.formatTime(this.durationTime)).width(35).fontColor(Color.White).fontSize(12) + } + .opacity(0.8) + .width("100%") + .borderRadius({bottomLeft: 20, bottomRight: 20}) + .backgroundColor('#1A000000') + .padding({ left: 30, right: 30 }) + .alignRules({ + bottom: { anchor: '__container__', align: VerticalAlign.Bottom } + }) } - .opacity(0.8) - .width("100%") - .padding({ left: 30, right: 30 }) - .alignRules({ - bottom: { anchor: '__container__', align: VerticalAlign.Bottom } - }) + .width('100%') + .height('100%') + .visibility(this.uri ? Visibility.Visible : Visibility.None) } - .layoutWeight(1) + .width('100%') + .aspectRatio(1) + .borderRadius(20) + .backgroundColor(Color.White) + .shadow({radius: 10, color: '#1a9399a1'}) + } + .width('100%') + .height('auto') + .padding({left: 32, right: 32}) + .margin({top: 40}) + + Blank().layoutWeight(1) + + Stack() { + Button('确认处理', { type: ButtonType.Capsule, stateEffect: true }) + .width('100%') + .height(46) + .fontColor(Color.White) + .fontSize(15) + .fontWeight(FontWeight.Medium) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + if (this.uri) { + this.removeAudio() + } else { + ToastUtils.show('请上传视频') + } + }) + .visibility(!this.isSuccess ? Visibility.Visible : Visibility.None) Row() { - Text(this.isSuccess ? '重新上传' : '取消').fontColor($r('app.color.color_90ffffff')).fontSize(17).margin({ left: 16 }) - .onClick(() => { - this.controller.stop() - if (this.isSuccess) { - this.selectVideo() - } else { - this.getUIContext().getRouter().back() - } - }) - Blank().layoutWeight(1) - Text(this.isSuccess ? '保存' : '确定').fontColor($r("app.color.color_466afd")).fontSize(17).margin({ right: 16 }) - .onClick(() => { - this.controller.stop() - if (this.isSuccess) { - SaveUtils.saveImageVideoToAlbumDialog([this.uri!!]) - .then((saved) => { - if (saved) { - this.uri = undefined - this.showDownloadDialog() - } else { - ToastUtils.show('保存失败') - } - }) - .catch((e: BusinessError) => { - ToastUtils.show('保存失败:' + e.message) - }) + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_reupload')).width(20).height(20) + Text('重新上传').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .borderWidth(1) + .borderColor($r('app.color.color_466afd')) + .backgroundColor(Color.Transparent) + .onClick(() => { + this.controller.stop() + this.selectVideo() + }) - } else { - this.mirrorVideo() - } - }) + Blank().width(9) + + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_download3')).width(20).height(20) + Text('保存').fontColor(Color.White).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + this.controller.stop() + SaveUtils.saveImageVideoToAlbumDialog([this.uri!!]) + .then((saved) => { + if (saved) { + this.uri = undefined + this.showDownloadDialog() + } else { + ToastUtils.show('保存失败') + } + }) + .catch((e: BusinessError) => { + ToastUtils.show('保存失败:' + e.message) + }) + }) } - .margin({ top: 140, bottom: 30 }) + .visibility(this.isSuccess ? Visibility.Visible : Visibility.None) } - .layoutWeight(1) - .visibility(this.uri ? Visibility.Visible : Visibility.None) + .padding({left: 16, top: 9, right: 16, bottom: 30 }) + .backgroundColor(Color.White) } .width('100%') .height('100%') diff --git a/entry/src/main/ets/pages/main/home/tools/RemoveWatermarkPage.ets b/entry/src/main/ets/pages/main/home/tools/RemoveWatermarkPage.ets new file mode 100644 index 0000000..a8ba4ab --- /dev/null +++ b/entry/src/main/ets/pages/main/home/tools/RemoveWatermarkPage.ets @@ -0,0 +1,400 @@ +import { PhotoHelper } from '@pura/picker_utils' +import { TitleBar } from '../../../../view/TitleBar' +import { photoAccessHelper } from '@kit.MediaLibraryKit' +import { BusinessError, systemDateTime } from '@kit.BasicServicesKit' +import { AppUtil, DisplayUtil, FileUtil, ImageUtil, StrUtil } from '@pura/harmony-utils' +import { ToastUtils } from '../../../../utils/ToastUtils' +import { fileIo } from '@kit.CoreFileKit' +import { SaveUtils } from '../../../../utils/SaveUtils' +import { LoadingDialog } from '../../../../dialog/LoadingDialog' +import { DownloadDialog, DownloadStatus } from '../../../../dialog/DownloadDialog' +import { EventConstants } from '../../../../common/EventConstants' +import { RectPosition } from '../../../../view/RectCropView' +import { media } from '@kit.MediaKit' +import { MediaUtils } from '../../../../utils/MediaUtils' +import { MP4Parser } from '@ohos/mp4parser' +import { TipDialog } from '../../../../dialog/TipDialog' +import { WaterMarkerView } from '../../../../view/WaterMarkerView' +import { EditTextDialog } from '../../../../dialog/EditTextDialog' +import { image } from '@kit.ImageKit' +import { avSessionManager } from '../../../../manager/AVSessionManager' +import { SelectBoundsView } from '../../../../view/SelectBoundsView' + +@Entry +@ComponentV2 +struct RemoveWatermarkPage { + @Local uri?: string + @Local currentTime: number = 0 + @Local durationTime: number = 0 + @Local isPlaying: boolean = false + @Local isSuccess: boolean = false + @Local playerSize: media.PixelMapParams = { width: 0, height: 0 } + + @Local showBound: boolean = false + + private controller: VideoController = new VideoController() + private videoSize: media.PixelMapParams = { width: 0, height: 0 } + private rect: RectPosition = { x: 0, y: 0, width: 0, height: 0 } + + addWatermark() { + LoadingDialog.show(this.getUIContext()) + this.isSuccess = false + let cacheVideoPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` + if (FileUtil.accessSync(cacheVideoPath)) { + FileUtil.unlinkSync(cacheVideoPath) + } + + let file = FileUtil.openSync(this.uri!!, fileIo.OpenMode.READ_ONLY) + // 复制文件到缓存目录下 + FileUtil.copyFileSync(file.fd, cacheVideoPath) + + let rectX = (vp2px(this.rect.x) * this.videoSize.width!!) / this.playerSize.width!! + let rectY = (vp2px(this.rect.y) * this.videoSize.height!!) / this.playerSize.height!! + + let rectWidth = (vp2px(this.rect.width * this.videoSize.width!!) / this.playerSize.width!!) + let rectHeight = (vp2px(this.rect.height * this.videoSize.height!!) / this.playerSize.height!!) + + let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` + let cmd = `ffmpeg -i ${cacheVideoPath} -filter_complex "[0:v]crop=${Math.round(rectWidth)}:${Math.round(rectHeight)}:${rectX}:${rectY},avgblur=15:15[fg]; [0:v][fg]overlay=${rectX}:${rectY}" -c:v h264 -pix_fmt yuv420p -y ${outputPath}` + MP4Parser.ffmpegCmd(cmd, { + callBackResult: (code: number) => { + if (code === 0) { + this.uri = FileUtil.getUriFromPath(outputPath) + this.isSuccess = true + this.isPlaying = false + ToastUtils.show('处理成功') + } else { + ToastUtils.show('处理失败') + } + LoadingDialog.dismiss() + } + }) + } + + selectVideo() { + PhotoHelper.selectEasy({ + MIMEType: photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE, + maxSelectNumber: 1, + isPhotoTakingSupported: false, + isEditSupported: false, + isOriginalSupported: false + }) + .then((uris) => { + if (uris.length != 0) { + this.isSuccess = false + this.uri = uris[0] + this.showBound = false + MediaUtils.getVideoSize(this.uri) + .then((size) => { + this.videoSize = size + if (size.width && size.height) { + const ratio = (DisplayUtil.getWidth() - 300) / size.width + this.playerSize = {width: Math.ceil(size.width * ratio), height: Math.ceil(size.height * ratio)} + } + }) + } + }) + } + + showDownloadDialog() { + DownloadDialog.show(this.getUIContext(), { + status: DownloadStatus.COMPLETED, + totalSize: 0, + progress: 0, + totalCount: 1, + index: 0, + callback: { + confirm: () => { + AppUtil.getContext().eventHub.emit(EventConstants.JumpToRecordEvent, 0) + this.getUIContext().getRouter().back() + } + } + }) + } + + formatTime(time: number): string { + let minute: number = 0 + let second: number = 0 + if (time > 60) { + minute = Math.trunc(time / 60) + second = time % 60 + if (minute < 10) { + if (second < 10) { + return `0${minute}:0${second}` + } else { + return `0${minute}:${second}` + } + } else { + if (second < 10) { + return `${minute}:0${second}` + } else { + return `${minute}:${second}` + } + } + } else { + second = time + if (second < 10) { + return `00:0${second}` + } else { + return `00:${second}` + } + } + } + + onBackPress(): boolean | void { + if (this.isSuccess) { + TipDialog.show(this.getUIContext(), {title:'温馨提示', content:'视频尚未保存,是否确定退出?', callback: { + confirm: () => { + this.getUIContext().getRouter().back() + } + }}) + return true + } + return false + } + + build() { + Column() { + TitleBar({ title: '视频去水印' }) + + Stack() { + Stack() { + Column() { + Image($r('app.media.ic_add_video')).width(40).height(40) + Text('请上传视频').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium).margin({ top: 8}) + } + } + .width('100%') + .aspectRatio(1) + .borderRadius(20) + .backgroundColor(Color.White) + .shadow({radius: 10, color: '#1a9399a1'}) + .onClick(() => { + this.selectVideo() + }) + } + .width('100%') + .height('auto') + .padding({left: 32, right: 32}) + .margin({top: 40}) + .visibility(this.uri ? Visibility.None : Visibility.Visible) + + Column() { + RelativeContainer() { + Video({ + src: this.uri, // 设置视频源 + controller: this.controller, //设置视频控制器,可以控制视频的播放状态 + posterOptions: { showFirstFrame: true } + }) + .id('video') + .width(this.playerSize ? px2vp(this.playerSize.width) : '100%') + .height(this.playerSize ? px2vp(this.playerSize.height) : '100%') + .backgroundColor($r('app.color.window_background')) + .controls(false) // 设置是否显示默认控制条 + .autoPlay(false) // 设置是否自动播放 + .loop(false) // 设置是否循环播放 + .objectFit(ImageFit.Cover) // 设置视频填充模式 + .alignRules({ + left: { anchor: '__container__', align: HorizontalAlign.Start }, + top: { anchor: '__container__', align: VerticalAlign.Top }, + right: { anchor: '__container__', align: HorizontalAlign.End }, + bottom: { anchor: '__container__', align: VerticalAlign.Bottom } + }) + .onPrepared((event) => { + if (event) { + this.durationTime = event.duration + } + }) + .onUpdate((event) => { + if (event) { + this.currentTime = event.time + } + }) + .onStart(() => { + this.isPlaying = true + }) + .onPause(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onStop(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onFinish(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onError(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onDisAppear(() => { + avSessionManager.deactivate() + }) + + Image($r('app.media.ic_play_video')) + .width(50) + .height(50) + .visibility(this.isPlaying ? Visibility.None : Visibility.Visible) + .onClick(async () => { + await avSessionManager.activate() + this.controller.start() + }) + .alignRules({ + left: { anchor: 'video', align: HorizontalAlign.Start }, + top: { anchor: 'video', align: VerticalAlign.Top }, + right: { anchor: 'video', align: HorizontalAlign.End }, + bottom: { anchor: 'video', align: VerticalAlign.Bottom } + }) + + Row() { + Image(this.isPlaying ? $r('app.media.ic_player_controls_pause') : $r('app.media.ic_player_controls_play')) + .width(20) + .height(20) + .margin({ right: 20 }) + .onClick(async () => { + if (this.isPlaying) { + this.controller.pause() + } else { + await avSessionManager.activate() + this.controller.start() + } + }) + Text(this.formatTime(this.currentTime)).width(35).fontColor(Color.White).fontSize(12) + Slider({ + value: this.currentTime, + min: 0, + max: this.durationTime + }) + .blockColor(Color.White) + .trackColor($r('app.color.color_60ffffff')) + .onChange((value: number, mode: SliderChangeMode) => { + this.controller.setCurrentTime(value); // 设置视频播放的进度跳转到value处 + }) + .layoutWeight(1) + Text(this.formatTime(this.durationTime)).width(35).fontColor(Color.White).fontSize(12) + } + .opacity(0.8) + .width(this.playerSize ? px2vp(this.playerSize.width) : "100%") + .alignRules({ + left: { anchor: 'video', align: HorizontalAlign.Start }, + right: { anchor: 'video', align: HorizontalAlign.End }, + bottom: { anchor: 'video', align: VerticalAlign.Bottom } + }) + + if (this.showBound && this.uri && !this.isSuccess) { + SelectBoundsView({ + onRectChange: (rect) => { + this.rect = rect + }, + onClose: () => { + this.showBound = false + } + }) + .width(this.playerSize ? px2vp(this.playerSize.width) : '100%') + .height(this.playerSize ? px2vp(this.playerSize.height) : '100%') + .alignRules({ + left: { anchor: 'video', align: HorizontalAlign.Start }, + top: { anchor: 'video', align: VerticalAlign.Top }, + right: { anchor: 'video', align: HorizontalAlign.End }, + bottom: { anchor: 'video', align: VerticalAlign.Bottom } + }) + } + } + .layoutWeight(1) + + Stack() { + Column(){ + Image($r('app.media.ic_remove_watermark')).width(26).height(26) + Text('马赛克').fontColor($r('app.color.color_212226')).fontSize(12).margin({ top: 8 }) + } + .onClick(() => { + if (!this.showBound) { + this.controller.stop() + this.showBound = true + } + }) + } + .width(82) + .height(82) + .margin({ top: 20, bottom: 20 }) + .borderRadius(10) + .backgroundColor(Color.White) + } + .layoutWeight(1) + .visibility(this.uri ? Visibility.Visible : Visibility.None) + + Blank().layoutWeight(1).visibility(this.uri ? Visibility.None : Visibility.Visible) + + Stack() { + Button('确认处理', { type: ButtonType.Capsule, stateEffect: true }) + .width('100%') + .height(46) + .fontColor(Color.White) + .fontSize(15) + .fontWeight(FontWeight.Medium) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + if (this.uri) { + this.addWatermark() + } else { + ToastUtils.show('请上传视频') + } + }) + .visibility(!this.isSuccess ? Visibility.Visible : Visibility.None) + + Row() { + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_reupload')).width(20).height(20) + Text('重新上传').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .borderWidth(1) + .borderColor($r('app.color.color_466afd')) + .backgroundColor(Color.Transparent) + .onClick(() => { + this.controller.stop() + this.selectVideo() + }) + + Blank().width(9) + + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_download3')).width(20).height(20) + Text('保存').fontColor(Color.White).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + this.controller.stop() + SaveUtils.saveImageVideoToAlbumDialog([this.uri!!]) + .then((saved) => { + if (saved) { + this.uri = undefined + this.showDownloadDialog() + } else { + ToastUtils.show('保存失败') + } + }) + .catch((e: BusinessError) => { + ToastUtils.show('保存失败:' + e.message) + }) + }) + } + .visibility(this.isSuccess ? Visibility.Visible : Visibility.None) + } + .padding({left: 16, top: 9, right: 16, bottom: 30 }) + .backgroundColor(Color.White) + } + .width('100%') + .height('100%') + .backgroundColor($r('app.color.window_background')) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/main/home/tools/TakeAudioPage.ets b/entry/src/main/ets/pages/main/home/tools/TakeAudioPage.ets deleted file mode 100644 index a1cab76..0000000 --- a/entry/src/main/ets/pages/main/home/tools/TakeAudioPage.ets +++ /dev/null @@ -1,287 +0,0 @@ -import { PhotoHelper } from '@pura/picker_utils' -import { TitleBar } from '../../../../view/TitleBar' -import { photoAccessHelper } from '@kit.MediaLibraryKit' -import { BusinessError, systemDateTime } from '@kit.BasicServicesKit' -import { AppUtil, FileUtil } from '@pura/harmony-utils' -import { ToastUtils } from '../../../../utils/ToastUtils' -import { fileIo } from '@kit.CoreFileKit' -import { SaveUtils } from '../../../../utils/SaveUtils' -import { MP4Parser } from '@ohos/mp4parser' -import { LoadingDialog } from '../../../../dialog/LoadingDialog' -import { DownloadDialog, DownloadStatus } from '../../../../dialog/DownloadDialog' -import { EventConstants } from '../../../../common/EventConstants' -import { avSessionManager } from '../../../../manager/AVSessionManager' - -@Entry -@ComponentV2 -struct TakeAudioPage { - private controller: VideoController = new VideoController() - @Local uri?: string - @Local currentTime: number = 0 - @Local durationTime: number = 0 - @Local isPlaying: boolean = false - @Local isSuccess: boolean = false - - takeAudio() { - LoadingDialog.show(this.getUIContext()) - this.isSuccess = false - let cachePath = FileUtil.getCacheDirPath() + FileUtil.separator + `cache_${systemDateTime.getTime()}.mp4` - if (FileUtil.accessSync(cachePath)) { - FileUtil.unlinkSync(cachePath) - } - let file = FileUtil.openSync(this.uri!!, fileIo.OpenMode.READ_ONLY) - // 复制文件到缓存目录下 - FileUtil.copyFileSync(file.fd, cachePath) - - let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp3` - let cmd = `ffmpeg -i ${cachePath} -vn -c:a mp3 ${outputPath}` - MP4Parser.ffmpegCmd(cmd, { - callBackResult: (code: number) => { - if (code === 0) { - SaveUtils.saveAudioToMusic([outputPath]) - .then(() => { - this.uri = undefined - this.isSuccess = true - this.isPlaying = false - this.showDownloadDialog() - }) - .catch((e: BusinessError) => { - ToastUtils.show('提取失败:' + e.message) - }) - } else { - ToastUtils.show('提取失败') - } - LoadingDialog.dismiss() - } - }) - } - - selectVideo() { - PhotoHelper.selectEasy({ - MIMEType: photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE, - maxSelectNumber: 1, - isPhotoTakingSupported: false, - isEditSupported: false, - isOriginalSupported: false - }) - .then((uris) => { - if (uris.length != 0) { - this.isSuccess = false - this.uri = uris[0] - } - }) - } - - showDownloadDialog() { - DownloadDialog.show(this.getUIContext(), { status: DownloadStatus.COMPLETED, isAudio: true, totalSize: 0, progress: 0, totalCount: 1, index: 0, callback: { - confirm: () => { - AppUtil.getContext().eventHub.emit(EventConstants.JumpToRecordEvent, 2) - this.getUIContext().getRouter().back() - } - } }) - } - - formatTime(time: number): string { - let minute: number = 0 - let second: number = 0 - if (time > 60) { - minute = Math.trunc(time / 60) - second = time % 60 - if (minute < 10) { - if (second < 10) { - return `0${minute}:0${second}` - } else { - return `0${minute}:${second}` - } - } else { - if (second < 10) { - return `${minute}:0${second}` - } else { - return `${minute}:${second}` - } - } - } else { - second = time - if (second < 10) { - return `00:0${second}` - } else { - return `00:${second}` - } - } - } - - build() { - Column() { - TitleBar({ title: '视频转音频' }) - - Column() { - Row() { - Text('上传视频').fontColor($r('app.color.color_90ffffff')).fontSize(16).fontWeight(FontWeight.Medium) - Text('(仅支持mp4格式)').fontColor($r('app.color.color_50ffffff')).fontSize(12) - }.alignSelf(ItemAlign.Start) - - RelativeContainer() { - Stack() { - Stack() { - Image($r('app.media.ic_add_video')).width(44).height(44) - } - .width(140) - .height(140) - .borderRadius(10) - .backgroundColor($r('app.color.color_333333')) - .margin({top: 40}) - .onClick(() => { - this.selectVideo() - }) - .visibility(this.uri ? Visibility.None : Visibility.Visible) - - RelativeContainer() { - Video({ - src: this.uri, // 设置视频源 - controller: this.controller, //设置视频控制器,可以控制视频的播放状态 - posterOptions: { showFirstFrame: true } - }) - .width('100%') - .height('100%') - .borderRadius(12) - .backgroundColor($r('app.color.window_background')) - .controls(false) // 设置是否显示默认控制条 - .autoPlay(false) // 设置是否自动播放 - .loop(false) // 设置是否循环播放 - .objectFit(ImageFit.Contain) // 设置视频填充模式 - .onPrepared((event) => { - if (event) { - this.durationTime = event.duration - } - }) - .onUpdate((event) => { - if (event) { - this.currentTime = event.time - } - }) - .onStart(() => { - this.isPlaying = true - }) - .onPause(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onStop(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onFinish(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onError(() => { - this.isPlaying = false - avSessionManager.deactivate() - }) - .onDisAppear(() => { - avSessionManager.deactivate() - }) - - Image($r('app.media.ic_play_video')) - .width(50) - .height(50) - .visibility(this.isPlaying ? Visibility.None : Visibility.Visible) - .onClick(async () => { - await avSessionManager.activate() - this.controller.start() - }) - .alignRules({ - left: { anchor: '__container__', align: HorizontalAlign.Start }, - top: { anchor: '__container__', align: VerticalAlign.Top }, - right: { anchor: '__container__', align: HorizontalAlign.End }, - bottom: { anchor: '__container__', align: VerticalAlign.Bottom }, - }) - - Row() { - Image(this.isPlaying ? $r('app.media.ic_player_controls_pause') : $r('app.media.ic_player_controls_play')) - .width(20) - .height(20) - .margin({ right: 20 }) - .onClick(async () => { - if (this.isPlaying) { - this.controller.pause() - } else { - await avSessionManager.activate() - this.controller.start() - } - }) - Text(this.formatTime(this.currentTime)).width(35).fontColor(Color.White).fontSize(12) - Slider({ - value: this.currentTime, - min: 0, - max: this.durationTime - }) - .blockColor(Color.White) - .trackColor($r('app.color.color_60ffffff')) - .onChange((value: number, mode: SliderChangeMode) => { - this.controller.setCurrentTime(value); // 设置视频播放的进度跳转到value处 - }) - .layoutWeight(1) - Text(this.formatTime(this.durationTime)).width(35).fontColor(Color.White).fontSize(12) - } - .opacity(0.8) - .width("100%") - .padding({ left: 20, right: 20 }) - .alignRules({ - bottom: { anchor: '__container__', align: VerticalAlign.Bottom } - }) - } - .height(350) - .margin({top: 50}) - .visibility(this.uri ? Visibility.Visible : Visibility.None) - } - .alignRules({ - start: { anchor: '__container__', align: HorizontalAlign.Start }, - end: { anchor: '__container__', align: HorizontalAlign.End } - }) - .id('layout_content') - - Image($r('app.media.ic_reupload_video')).width(20).height(20) - .alignRules({ - right: {anchor: '__container__', align: HorizontalAlign.End} - }) - .margin({top: 20}) - .onClick(() => { - this.controller.stop() - this.selectVideo() - }) - .visibility(this.uri ? Visibility.Visible : Visibility.None) - - Button('确认提取', {type: ButtonType.Capsule ,stateEffect:true}) - .width('100%') - .height(42) - .fontColor($r('app.color.color_90ffffff')) - .fontSize(16) - .linearGradient({ - colors: [['#F62C6C', 0.0], ['#FC4F54', 1.0]], - direction: GradientDirection.Right - }) - .alignRules({ - top: {anchor: 'layout_content', align: VerticalAlign.Bottom} - }) - .margin({top: 40}) - .onClick(() => { - if (this.uri) { - this.takeAudio() - } else { - ToastUtils.show('请先上传视频') - } - }) - } - .height('auto') - .margin({ top: 12 }) - .borderRadius(8) - .backgroundColor($r('app.color.color_222222')) - .padding({left: 20, right: 20, bottom: 30}) - }.margin({ left: 16, top: 16, right: 16 }) - } - .width('100%') - .height('100%') - .backgroundColor($r('app.color.window_background')) - } -} \ No newline at end of file diff --git a/entry/src/main/ets/pages/main/home/tools/VideoMirrorPage.ets b/entry/src/main/ets/pages/main/home/tools/VideoMirrorPage.ets index f478d14..4c8c12b 100644 --- a/entry/src/main/ets/pages/main/home/tools/VideoMirrorPage.ets +++ b/entry/src/main/ets/pages/main/home/tools/VideoMirrorPage.ets @@ -28,7 +28,7 @@ struct VideoMirrorPage { mirrorVideo() { LoadingDialog.show(this.getUIContext()) this.isSuccess = false - let cacheVideoPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp4` + let cacheVideoPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` if (FileUtil.accessSync(cacheVideoPath)) { FileUtil.unlinkSync(cacheVideoPath) } @@ -37,7 +37,7 @@ struct VideoMirrorPage { // 复制文件到缓存目录下 FileUtil.copyFileSync(file.fd, cacheVideoPath) - let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp4` + let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` let cmd = `ffmpeg -i ${cacheVideoPath} -vf ${this.orientation === 1 ? "hflip" : "vflip"} -c:v h264 -pix_fmt yuv420p -y ${outputPath}` MP4Parser.ffmpegCmd(cmd, { callBackResult: (code: number) => { diff --git a/entry/src/main/ets/pages/main/home/tools/VideoReversePage.ets b/entry/src/main/ets/pages/main/home/tools/VideoReversePage.ets index e382dd7..4c6a269 100644 --- a/entry/src/main/ets/pages/main/home/tools/VideoReversePage.ets +++ b/entry/src/main/ets/pages/main/home/tools/VideoReversePage.ets @@ -26,7 +26,7 @@ struct VideoReversePage { videoReverse() { LoadingDialog.show(this.getUIContext()) this.isSuccess = false - let cacheVideoPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp4` + let cacheVideoPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` if (FileUtil.accessSync(cacheVideoPath)) { FileUtil.unlinkSync(cacheVideoPath) } @@ -35,7 +35,7 @@ struct VideoReversePage { // 复制文件到缓存目录下 FileUtil.copyFileSync(file.fd, cacheVideoPath) - let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `kcsp_${systemDateTime.getTime()}.mp4` + let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4` let cmd = `ffmpeg -i ${cacheVideoPath} -vf reverse -af areverse -c:v h264 -pix_fmt yuv420p -y ${outputPath}` MP4Parser.ffmpegCmd(cmd, { callBackResult: (code: number) => { diff --git a/entry/src/main/ets/pages/main/home/tools/VideoToAudioPage.ets b/entry/src/main/ets/pages/main/home/tools/VideoToAudioPage.ets new file mode 100644 index 0000000..231d7b1 --- /dev/null +++ b/entry/src/main/ets/pages/main/home/tools/VideoToAudioPage.ets @@ -0,0 +1,313 @@ +import { PhotoHelper } from '@pura/picker_utils' +import { TitleBar } from '../../../../view/TitleBar' +import { photoAccessHelper } from '@kit.MediaLibraryKit' +import { BusinessError, systemDateTime } from '@kit.BasicServicesKit' +import { AppUtil, FileUtil } from '@pura/harmony-utils' +import { ToastUtils } from '../../../../utils/ToastUtils' +import { fileIo } from '@kit.CoreFileKit' +import { SaveUtils } from '../../../../utils/SaveUtils' +import { MP4Parser } from '@ohos/mp4parser' +import { LoadingDialog } from '../../../../dialog/LoadingDialog' +import { DownloadDialog, DownloadStatus } from '../../../../dialog/DownloadDialog' +import { EventConstants } from '../../../../common/EventConstants' +import { avSessionManager } from '../../../../manager/AVSessionManager' + +@Entry +@ComponentV2 +struct VideoToAudioPage { + private controller: VideoController = new VideoController() + @Local videoUri?: string + @Local currentTime: number = 0 + @Local durationTime: number = 0 + @Local isPlaying: boolean = false + @Local isSuccess: boolean = false + @Local audioUri?: string + + videoToAudio() { + LoadingDialog.show(this.getUIContext()) + this.isSuccess = false + let cachePath = FileUtil.getCacheDirPath() + FileUtil.separator + `cache_${systemDateTime.getTime()}.mp4` + if (FileUtil.accessSync(cachePath)) { + FileUtil.unlinkSync(cachePath) + } + let file = FileUtil.openSync(this.videoUri!!, fileIo.OpenMode.READ_ONLY) + // 复制文件到缓存目录下 + FileUtil.copyFileSync(file.fd, cachePath) + + let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp3` + let cmd = `ffmpeg -i ${cachePath} -vn -c:a mp3 ${outputPath}` + MP4Parser.ffmpegCmd(cmd, { + callBackResult: (code: number) => { + if (code === 0) { + this.audioUri = outputPath + this.isSuccess = true + this.isPlaying = false + ToastUtils.show('处理成功') + } else { + ToastUtils.show('处理失败') + } + LoadingDialog.dismiss() + } + }) + } + + selectVideo() { + PhotoHelper.selectEasy({ + MIMEType: photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE, + maxSelectNumber: 1, + isPhotoTakingSupported: false, + isEditSupported: false, + isOriginalSupported: false + }) + .then((uris) => { + if (uris.length != 0) { + this.isSuccess = false + this.videoUri = uris[0] + this.audioUri = undefined + } + }) + } + + showDownloadDialog() { + DownloadDialog.show(this.getUIContext(), { status: DownloadStatus.COMPLETED, isAudio: true, totalSize: 0, progress: 0, totalCount: 1, index: 0, callback: { + confirm: () => { + AppUtil.getContext().eventHub.emit(EventConstants.JumpToRecordEvent, 2) + this.getUIContext().getRouter().back() + } + } }) + } + + formatTime(time: number): string { + let minute: number = 0 + let second: number = 0 + if (time > 60) { + minute = Math.trunc(time / 60) + second = time % 60 + if (minute < 10) { + if (second < 10) { + return `0${minute}:0${second}` + } else { + return `0${minute}:${second}` + } + } else { + if (second < 10) { + return `${minute}:0${second}` + } else { + return `${minute}:${second}` + } + } + } else { + second = time + if (second < 10) { + return `00:0${second}` + } else { + return `00:${second}` + } + } + } + + build() { + Column() { + TitleBar({ title: '视频转音频' }) + + Stack() { + Stack() { + Stack() { + Column() { + Image($r('app.media.ic_add_video')).width(40).height(40) + Text('请上传视频').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium).margin({ top: 8}) + } + } + .width('100%') + .height('100%') + .onClick(() => { + this.selectVideo() + }) + .visibility(this.videoUri ? Visibility.None : Visibility.Visible) + + RelativeContainer() { + Video({ + src: this.videoUri, // 设置视频源 + controller: this.controller, //设置视频控制器,可以控制视频的播放状态 + posterOptions: { showFirstFrame: true } + }) + .width('100%') + .height('100%') + .borderRadius(20) + .backgroundColor(Color.White) + .controls(false) // 设置是否显示默认控制条 + .autoPlay(false) // 设置是否自动播放 + .loop(false) // 设置是否循环播放 + .objectFit(ImageFit.Contain) // 设置视频填充模式 + .onPrepared((event) => { + if (event) { + this.durationTime = event.duration + } + }) + .onUpdate((event) => { + if (event) { + this.currentTime = event.time + } + }) + .onStart(() => { + this.isPlaying = true + }) + .onPause(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onStop(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onFinish(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onError(() => { + this.isPlaying = false + avSessionManager.deactivate() + }) + .onDisAppear(() => { + avSessionManager.deactivate() + }) + + Image($r('app.media.ic_play_video')) + .width(50) + .height(50) + .visibility(this.isPlaying ? Visibility.None : Visibility.Visible) + .onClick(async () => { + await avSessionManager.activate() + this.controller.start() + }) + .alignRules({ + left: { anchor: '__container__', align: HorizontalAlign.Start }, + top: { anchor: '__container__', align: VerticalAlign.Top }, + right: { anchor: '__container__', align: HorizontalAlign.End }, + bottom: { anchor: '__container__', align: VerticalAlign.Bottom }, + }) + + Row() { + Image(this.isPlaying ? $r('app.media.ic_player_controls_pause') : $r('app.media.ic_player_controls_play')) + .width(20) + .height(20) + .margin({ right: 20 }) + .onClick(async () => { + if (this.isPlaying) { + this.controller.pause() + } else { + await avSessionManager.activate() + this.controller.start() + } + }) + Text(this.formatTime(this.currentTime)).width(35).fontColor(Color.White).fontSize(12) + Slider({ + value: this.currentTime, + min: 0, + max: this.durationTime + }) + .blockColor(Color.White) + .trackColor($r('app.color.color_60ffffff')) + .onChange((value: number, mode: SliderChangeMode) => { + this.controller.setCurrentTime(value); // 设置视频播放的进度跳转到value处 + }) + .layoutWeight(1) + Text(this.formatTime(this.durationTime)).width(35).fontColor(Color.White).fontSize(12) + } + .opacity(0.8) + .width("100%") + .borderRadius({bottomLeft: 20, bottomRight: 20}) + .backgroundColor('#1A000000') + .padding({ left: 30, right: 30 }) + .alignRules({ + bottom: { anchor: '__container__', align: VerticalAlign.Bottom } + }) + } + .width('100%') + .height('100%') + .visibility(this.videoUri ? Visibility.Visible : Visibility.None) + } + .width('100%') + .aspectRatio(1) + .borderRadius(20) + .backgroundColor(Color.White) + .shadow({radius: 10, color: '#1a9399a1'}) + } + .width('100%') + .height('auto') + .padding({left: 32, right: 32}) + .margin({top: 40}) + + Blank().layoutWeight(1) + + Stack() { + Button('确认处理', { type: ButtonType.Capsule, stateEffect: true }) + .width('100%') + .height(46) + .fontColor(Color.White) + .fontSize(15) + .fontWeight(FontWeight.Medium) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + if (this.videoUri) { + this.videoToAudio() + } else { + ToastUtils.show('请上传视频') + } + }) + .visibility(!this.isSuccess ? Visibility.Visible : Visibility.None) + + Row() { + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_reupload')).width(20).height(20) + Text('重新上传').fontColor($r('app.color.color_466afd')).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .borderWidth(1) + .borderColor($r('app.color.color_466afd')) + .backgroundColor(Color.Transparent) + .onClick(() => { + this.controller.stop() + this.selectVideo() + }) + + Blank().width(9) + + Button({ type: ButtonType.Capsule, stateEffect: true }) { + Row() { + Image($r('app.media.ic_download3')).width(20).height(20) + Text('保存').fontColor(Color.White).fontSize(15).fontWeight(FontWeight.Medium) + } + } + .height(46) + .layoutWeight(1) + .backgroundColor($r('app.color.color_466afd')) + .onClick(() => { + this.controller.stop() + SaveUtils.saveAudioToMusic([this.audioUri!!]) + .then((saved) => { + if (saved) { + this.videoUri = undefined + this.showDownloadDialog() + } else { + ToastUtils.show('保存失败') + } + }) + .catch((e: BusinessError) => { + ToastUtils.show('保存失败:' + e.message) + }) + }) + } + .visibility(this.isSuccess ? Visibility.Visible : Visibility.None) + } + .padding({left: 16, top: 9, right: 16, bottom: 30 }) + .backgroundColor(Color.White) + } + .width('100%') + .height('100%') + .backgroundColor($r('app.color.window_background')) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/pages/main/mine/record/AudioRecordPage.ets b/entry/src/main/ets/pages/main/mine/record/AudioRecordPage.ets index 25912c0..d103d75 100644 --- a/entry/src/main/ets/pages/main/mine/record/AudioRecordPage.ets +++ b/entry/src/main/ets/pages/main/mine/record/AudioRecordPage.ets @@ -9,6 +9,7 @@ import { TipDialog } from '../../../../dialog/TipDialog'; import { photoAccessHelper } from '@kit.MediaLibraryKit'; import { LocalMediaManager } from '../../../../manager/LocalMediaManager'; import { fileIo } from '@kit.CoreFileKit'; +import { ShareManager } from '../../../../manager/ShareManager'; @ComponentV2 export struct AudioRecordPage { @@ -68,6 +69,11 @@ export struct AudioRecordPage { ListItem() { AudioRecordItemView({ media: item }) } + .swipeAction({ + end: this.itemEnd(item) + }) + .borderRadius(8) + .backgroundColor(Color.White) }) } .width('auto') @@ -83,4 +89,51 @@ export struct AudioRecordPage { }) } } + + @Builder + itemEnd(media: MediaRecordEntity) { + Row() { + Button({ type: ButtonType.Normal, stateEffect: true }) { + Column() { + Image($r('app.media.ic_share_material')).width(18).height(18) + Text('分享').fontColor(Color.White).fontSize(10).margin({top: 4}) + } + } + .width(56) + .height('100%') + .backgroundColor('#FF9E43') + .onClick(() => { + ShareManager.shareFile(media.uri!!) + }) + + Button({ type: ButtonType.Normal, stateEffect: true }) { + Column() { + Image($r('app.media.ic_delete_material')).width(18).height(18) + Text('删除').fontColor(Color.White).fontSize(10).margin({top: 4}) + } + } + .width(56) + .height('100%') + .backgroundColor('#FF3E3E') + .borderRadius({topRight: 8, bottomRight: 8}) + .onClick(() => { + TipDialog.show(this.getUIContext(), { + title: '提示', content: '确定删除该音频?', callback: { + confirm: () => { + fileIo.unlink(media.uri!!) + .then(() => { + ToastUtils.show('删除成功') + LocalMediaManager.delete(media.name!!) + AppUtil.getContext().eventHub.emit(EventConstants.MediaActionEvent, MediaType.AUDIO, MediaAction.DELETE) + }) + .catch(() => { + ToastUtils.show('删除失败, 请到文件管理中手动删除') + }) + } + } + }) + }) + } + .height(74) + } } \ No newline at end of file diff --git a/entry/src/main/ets/pages/main/mine/setting/SettingsPage.ets b/entry/src/main/ets/pages/main/mine/setting/SettingsPage.ets index b2521bc..af1b315 100644 --- a/entry/src/main/ets/pages/main/mine/setting/SettingsPage.ets +++ b/entry/src/main/ets/pages/main/mine/setting/SettingsPage.ets @@ -29,6 +29,12 @@ struct SettingsPage { ToastUtils.show('账户已注销'); } + @Monitor('viewModel.logout') + onLogout(monitor: IMonitor) { + EventReportGlobalManager.eventReport(EventConstants.EXIT_LOGIN) + this.logout(); + } + aboutToAppear(): void { this.getCache() } @@ -141,8 +147,7 @@ struct SettingsPage { .onClick(() => { TipDialog.show(this.getUIContext(), {title: '温馨提示', content: '确定退出登录?', callback: { confirm: () => { - EventReportGlobalManager.eventReport(EventConstants.EXIT_LOGIN) - this.logout(); + this.viewModel.userLogout() } }}) }) diff --git a/entry/src/main/ets/pages/main/mine/tool/ToolsPage.ets b/entry/src/main/ets/pages/main/mine/tool/ToolsPage.ets new file mode 100644 index 0000000..ccd3961 --- /dev/null +++ b/entry/src/main/ets/pages/main/mine/tool/ToolsPage.ets @@ -0,0 +1,67 @@ +import { RouterUrls } from '../../../../common/RouterUrls' +import { ToolMenuEntity, toolsList } from '../../../../entity/ToolMenuEntity' +import { ToolItemView } from '../../../../view/ToolItemView' + +@ComponentV2 +export struct ToolsPage { + build() { + Scroll() { + Column() { + Image($r('app.media.ic_tools_top_bg')).width('100%').aspectRatio(2.925) + + Grid() { + ForEach(toolsList().convertToArray(), (item: ToolMenuEntity) => { + GridItem() { + ToolItemView({ menu: item }) + } + .onClick(() => { + switch (item.alias) { + case 'resetMD5': { + this.getUIContext().getRouter().pushUrl({url: RouterUrls.MD5_RESET_PAGE}) + break + } + case 'videoToText': { + break + } + case 'videoToAudio': { + this.getUIContext().getRouter().pushUrl({url: RouterUrls.VIDEO_TO_AUDIO_PAGE}) + break + } + case 'audioToText': { + break + } + case 'addWatermark': { + this.getUIContext().getRouter().pushUrl({url: RouterUrls.ADD_WATERMARK_PAGE}) + break + } + case 'longImageMerge': { + this.getUIContext().getRouter().pushUrl({url: RouterUrls.IMAGE_MERGE_PAGE}) + break + } + case 'removeWatermark': { + this.getUIContext().getRouter().pushUrl({url: RouterUrls.REMOVE_WATERMARK_PAGE}) + break + } + case 'removeAudio': { + this.getUIContext().getRouter().pushUrl({url: RouterUrls.REMOVE_AUDIO_PAGE}) + break + } + } + }) + }) + } + .scrollBar(BarState.Off) + .columnsTemplate('1fr 1fr') + .rowsGap(9) + .columnsGap(9) + .margin({top: 20}) + } + .width('100%') + .height('auto') + .padding({ left: 12, top: 70, right: 12, bottom: 10 }) + } + .height('100%') + .scrollBar(BarState.Off) + .backgroundColor($r('app.color.window_background')) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/utils/SaveUtils.ets b/entry/src/main/ets/utils/SaveUtils.ets index 7b5910a..fc7d0ca 100644 --- a/entry/src/main/ets/utils/SaveUtils.ets +++ b/entry/src/main/ets/utils/SaveUtils.ets @@ -78,7 +78,7 @@ export class SaveUtils { */ static async saveVideoToAlbum(path: string, name: string): Promise { try { - let name = `kcsp_${systemDateTime.getTime() + RandomUtil.getRandomInt(1000, 2000)}.mp4` + let name = `scmf_${systemDateTime.getTime() + RandomUtil.getRandomInt(1000, 2000)}.mp4` const uri = await PhotoHelper.save(photoAccessHelper.PhotoType.VIDEO, 'mp4', { title: name.replace('.mp4', '') }); let file = FileUtil.openSync(uri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); await FileUtil.copyFile(path, file.fd) @@ -101,7 +101,7 @@ export class SaveUtils { */ static async saveImageToAlbum(path: string, name: string): Promise { try { - if (FileUtil.accessSync(path)) name = `kcsp_${systemDateTime.getTime() + RandomUtil.getRandomInt(1000, 2000)}.jpeg` + if (FileUtil.accessSync(path)) name = `scmf_${systemDateTime.getTime() + RandomUtil.getRandomInt(1000, 2000)}.jpeg` const uri = await PhotoHelper.save(photoAccessHelper.PhotoType.IMAGE, 'jpeg', { title: name.replace('.jpeg', '') }); let file = FileUtil.openSync(uri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); await FileUtil.copyFile(path, file.fd) diff --git a/entry/src/main/ets/view/RecordItemView.ets b/entry/src/main/ets/view/RecordItemView.ets index f063a5f..f8af4cc 100644 --- a/entry/src/main/ets/view/RecordItemView.ets +++ b/entry/src/main/ets/view/RecordItemView.ets @@ -138,61 +138,6 @@ export struct ImageRecordItemView { export struct AudioRecordItemView { @Param media?: MediaRecordEntity = undefined; - build() { - RelativeContainer() { - Text(this.media?.name) - .layoutWeight(1) - .fontColor($r('app.color.color_90ffffff')) - .fontSize(15) - .maxLines(1) - .textOverflow({ overflow: TextOverflow.Ellipsis }) - .alignRules({ - bottom: { anchor: '__container__', align: VerticalAlign.Center } - }) - .margin({ bottom: 2 }) - - Text(this.formatTime(Math.trunc(this.media!!.duration / 1000))) - .fontColor($r('app.color.color_60ffffff')) - .fontSize(14) - .alignRules({ - top: { anchor: '__container__', align: VerticalAlign.Center } - }) - .margin({ top: 2 }) - .id('tv_duration') - - Text(DateUtil.getFormatDateStr(this.media!!.createTime, 'yyyy年MM月dd日 HH:mm:ss')) - .fontColor($r('app.color.color_30ffffff')) - .fontSize(12) - .alignRules({ - left: { anchor: 'tv_duration', align: HorizontalAlign.End }, - top: { anchor: 'tv_duration', align: VerticalAlign.Top }, - bottom: { anchor: 'tv_duration', align: VerticalAlign.Bottom } - }) - .margin({ left: 12 }) - - Image($r('app.media.ic_arrow_dp22')).width(24).height(24) - .alignRules({ - top: { anchor: '__container__', align: VerticalAlign.Top }, - bottom: { anchor: '__container__', align: VerticalAlign.Bottom }, - right: { anchor: '__container__', align: HorizontalAlign.End } - }) - - Divider().color($r('app.color.color_10ffffff')).width('100%').strokeWidth(1) - .alignRules({ - bottom: { anchor: '__container__', align: VerticalAlign.Bottom } - }) - } - .height(74) - .onClick(() => { - this.getUIContext() - .getRouter() - .pushUrl({ - url: RouterUrls.AUDIO_PLAYER_PAGE, - params: { title: this.media?.name, uri: this.media?.uri, showActions: true } - }, router.RouterMode.Single) - }) - } - formatTime(time: number): string { let minute: number = 0 let second: number = 0 @@ -221,4 +166,60 @@ export struct AudioRecordItemView { } } } + + build() { + RelativeContainer() { + Text(this.media?.name) + .layoutWeight(1) + .fontColor($r('app.color.color_212226')) + .fontSize(15) + .maxLines(1) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .alignRules({ + bottom: { anchor: '__container__', align: VerticalAlign.Center } + }) + .margin({ bottom: 2 }) + + Text(this.formatTime(Math.trunc(this.media!!.duration / 1000))) + .fontColor($r('app.color.color_666666')) + .fontSize(12) + .alignRules({ + top: { anchor: '__container__', align: VerticalAlign.Center } + }) + .margin({ top: 2 }) + .id('tv_duration') + + Text(DateUtil.getFormatDateStr(this.media!!.createTime, 'yyyy年MM月dd日 HH:mm:ss')) + .fontColor($r('app.color.color_30ffffff')) + .fontSize(12) + .alignRules({ + left: { anchor: 'tv_duration', align: HorizontalAlign.End }, + top: { anchor: 'tv_duration', align: VerticalAlign.Top }, + bottom: { anchor: 'tv_duration', align: VerticalAlign.Bottom } + }) + .margin({ left: 12 }) + + Image($r('app.media.ic_arrow_dp16')).width(16).height(16) + .alignRules({ + top: { anchor: '__container__', align: VerticalAlign.Top }, + bottom: { anchor: '__container__', align: VerticalAlign.Bottom }, + right: { anchor: '__container__', align: HorizontalAlign.End } + }) + + Divider().color($r('app.color.color_10ffffff')).width('100%').strokeWidth(1) + .alignRules({ + bottom: { anchor: '__container__', align: VerticalAlign.Bottom } + }) + } + .height(74) + .padding({left: 14, right: 14}) + .onClick(() => { + this.getUIContext() + .getRouter() + .pushUrl({ + url: RouterUrls.AUDIO_PLAYER_PAGE, + params: { title: this.media?.name, uri: this.media?.uri, showActions: true } + }, router.RouterMode.Single) + }) + } } \ No newline at end of file diff --git a/entry/src/main/ets/view/SelectBoundsView.ets b/entry/src/main/ets/view/SelectBoundsView.ets new file mode 100644 index 0000000..b1db248 --- /dev/null +++ b/entry/src/main/ets/view/SelectBoundsView.ets @@ -0,0 +1,201 @@ +import { ActionType, Position, RectPosition } from './RectCropView'; + +@ComponentV2 +export struct SelectBoundsView { + private settings: RenderingContextSettings = new RenderingContextSettings(true); + private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); + private actionType: ActionType = ActionType.move; + private touchPosition: Position = { x: 0, y: 0 }; + private sw: number = 0; //图片展示框固定宽度 + private sh: number = 0; //图片展示框固定高度 + + @Param onRectChange?: (rect: RectPosition) => void = undefined + @Param onClose?: () => void = undefined + + @Local clipRect: RectPosition = { + x: 0, + y: 0, + width: 80, + height: 40 + }; + @Local initPosition: Position = { + x: 0, + y: 0 + } + @Local fontSize: number = 15 + + build() { + Stack() { + Stack() + .position({ + x: this.clipRect.x, + y: this.clipRect.y + }) + .width(this.clipRect.width) + .height(this.clipRect.height) + .blur(20) + .id('mosaic') + + Image($r('app.media.ic_right_bottom_rect')) + .position({ + x: this.clipRect.x + this.clipRect.width - 9, + y: this.clipRect.y + this.clipRect.height - 9 + }) + .width(15) + .height(15) + + // 裁剪框 + Canvas(this.canvasContext) + .position({ + x: this.clipRect.x, + y: this.clipRect.y + }) + .width(this.clipRect.width) + .height(this.clipRect.height) + .onReady(() => { + this.drawClipImage() + }) + .onTouch(event => { + if (event.type === TouchType.Down) { + this.isMove(event.target.area, event.touches[0]); + this.touchPosition = { + x: event.touches[0].screenX, + y: event.touches[0].screenY + } + } else if (event.type === TouchType.Move) { + let moveX = event.changedTouches[0].screenX - this.touchPosition.x; + let moveY = event.changedTouches[0].screenY - this.touchPosition.y; + this.touchPosition = { + x: event.changedTouches[0].screenX, + y: event.changedTouches[0].screenY + } + this.moveClipCanvas(moveX, moveY); + } + }) + + Image($r('app.media.ic_left_top_rect')) + .position({ + x: this.clipRect.x - 7, + y: this.clipRect.y - 7 + }) + .width(16) + .height(16) + .onClick(() => { + if (this.onClose) { + this.onClose() + } + }) + } + .width('100%') + .height('100%') + .onAreaChange((_oldArea, newArea) => { + this.sw = newArea.width as number + this.sh = newArea.height as number + if (this.onRectChange) { + this.onRectChange(this.clipRect) + } + }) + } + + // 绘制裁剪框 + drawClipImage() { + this.canvasContext.clearRect(0, 0, this.clipRect.width, this.clipRect.height); + this.canvasContext.lineWidth = 2 + this.canvasContext.strokeStyle = Color.White + this.canvasContext.beginPath() + this.canvasContext.rect(0, 0, this.clipRect.width, this.clipRect.height) + this.canvasContext.stroke() + } + + // 裁剪框位置和大小变化 初始位置为图片的初始坐标 移动的坐标 + moveClipCanvas(moveX: number, moveY: number) { + let clipRect: RectPosition = { + x: this.clipRect.x, + y: this.clipRect.y, + width: this.clipRect.width, + height: this.clipRect.height + } + switch (this.actionType) { + case ActionType.move: + clipRect.x += moveX; + clipRect.y += moveY; + break; + case ActionType.topLeft: + clipRect.x += moveX; + clipRect.y += moveY; + clipRect.width += -moveX; + clipRect.height += -moveY; + break; + case ActionType.topRight: + clipRect.y += moveY; + clipRect.width += moveX; + clipRect.height += -moveY; + break; + case ActionType.bottomLeft: + clipRect.x += moveX; + clipRect.width += -moveX; + clipRect.height += moveY; + break; + case ActionType.bottomRight: + clipRect.width += moveX; + clipRect.height += moveY; + break; + default: + break; + } + + // 偏移坐标小于初始位置 + if (clipRect.x < this.initPosition.x) { + clipRect.x = this.initPosition.x; + } + + if (clipRect.y < this.initPosition.y) { + clipRect.y = this.initPosition.y; + } + + // 横坐标限制位置 + if (clipRect.width + clipRect.x > this.sw + this.initPosition.x) { + if (this.actionType === ActionType.move) { + clipRect.x = this.sw + this.initPosition.x - clipRect.width; + } else { + clipRect.width = this.sw + this.initPosition.x - clipRect.x; + } + } + + // 纵坐标限制 + if (clipRect.height + clipRect.y > this.sh + this.initPosition.y) { + if (this.actionType === ActionType.move) { + clipRect.y = this.sh + this.initPosition.y - clipRect.height; + } else { + clipRect.height = this.sh + this.initPosition.y - clipRect.y; + } + } + + //裁剪框位置大小 + this.clipRect = { + x: Math.round(clipRect.x), + y: Math.round(clipRect.y), + width: Math.max(Math.round(clipRect.width), 80), + height: Math.max(Math.round(clipRect.height), 40) + }; + + if (this.onRectChange) { + this.onRectChange(this.clipRect) + } + } + + // 判断操作类型 + isMove(area: Area, touch: TouchObject) { + if (touch.x < 30 && touch.y < 30) { // 左上角 + this.actionType = ActionType.topLeft + } else if (touch.x < 30 && touch.y > (Number(area.height) - 30)) { // 左下 + this.actionType = ActionType.bottomLeft + } else if (touch.x > Number(area.width) - 30 && touch.y < 30) { // 右上 + this.actionType = ActionType.topRight + } else if (touch.x > Number(area.width) - 30 && touch.y > (Number(area.height) - 30)) { // 右下 + this.actionType = ActionType.bottomRight + } else { + this.actionType = ActionType.move + } + } +} \ No newline at end of file diff --git a/entry/src/main/ets/view/ToolItemView.ets b/entry/src/main/ets/view/ToolItemView.ets new file mode 100644 index 0000000..8db530d --- /dev/null +++ b/entry/src/main/ets/view/ToolItemView.ets @@ -0,0 +1,37 @@ +import { ToolMenuEntity } from '../entity/ToolMenuEntity' + +@ComponentV2 +export struct ToolItemView { + @Param menu?: ToolMenuEntity = undefined + + build() { + Column() { + Row() { + Image(this.menu?.icon).width(44).height(44).margin({left: 12, top: 16, bottom: 16}) + Column() { + Text(this.menu?.title).fontColor($r('app.color.color_212226')).fontSize(16).fontWeight(FontWeight.Medium) + Text(this.menu?.desc).fontColor($r('app.color.color_666666')).fontSize(12).margin({top: 4}) + } + .margin({left: 12}) + .alignItems(HorizontalAlign.Start) + } + .width('100%') + .borderRadius({topLeft: 8, topRight: 8}) + .linearGradient({ + direction: GradientDirection.Bottom, + colors: [[this.menu?.colors[0], 0.0], [this.menu?.colors[1], 1.0]] + }) + + Row() { + Text(this.menu?.count + 'w人使用').fontColor($r('app.color.color_999999')).fontSize(12).layoutWeight(1) + Image($r('app.media.ic_tool_arrow')).width(20).height(20) + } + .padding({left:12, top: 8, right: 12, bottom: 8 }) + } + .width('100%') + .borderRadius(8) + .backgroundColor(Color.White) + .padding(1) + .alignItems(HorizontalAlign.Start) + } +} \ No newline at end of file diff --git a/entry/src/main/ets/viewModel/SettingsViewModel.ets b/entry/src/main/ets/viewModel/SettingsViewModel.ets index 38e2c0c..b3c755a 100644 --- a/entry/src/main/ets/viewModel/SettingsViewModel.ets +++ b/entry/src/main/ets/viewModel/SettingsViewModel.ets @@ -5,6 +5,7 @@ import { BaseViewModel } from './BaseViewModel'; @ObservedV2 export class SettingsViewModel extends BaseViewModel { @Trace destroy?: object; + @Trace logout?: object; async userDestroy() { this.showLoading(); @@ -22,4 +23,21 @@ export class SettingsViewModel extends BaseViewModel { this.dismissLoading(); } } + + async userLogout() { + this.showLoading(); + try { + const result = await apiService.logout(); + if (result.isSuccess()) { + this.logout = new Object(); + } else { + ToastUtils.show(result.message, true); + } + this.dismissLoading(); + } catch (e) { + console.log(e); + ToastUtils.show(e); + this.dismissLoading(); + } + } } \ No newline at end of file diff --git a/entry/src/main/resources/base/element/color.json b/entry/src/main/resources/base/element/color.json index 32894dc..029cb8c 100644 --- a/entry/src/main/resources/base/element/color.json +++ b/entry/src/main/resources/base/element/color.json @@ -52,6 +52,10 @@ "name": "color_cccccc", "value": "#CCCCCC" }, + { + "name": "color_eeeeee", + "value": "#EEEEEE" + }, { diff --git a/entry/src/main/resources/base/media/ic_add_image.webp b/entry/src/main/resources/base/media/ic_add_image.webp index 5958091..4869507 100644 Binary files a/entry/src/main/resources/base/media/ic_add_image.webp and b/entry/src/main/resources/base/media/ic_add_image.webp differ diff --git a/entry/src/main/resources/base/media/ic_add_video.png b/entry/src/main/resources/base/media/ic_add_video.png deleted file mode 100644 index a5ed379..0000000 Binary files a/entry/src/main/resources/base/media/ic_add_video.png and /dev/null differ diff --git a/entry/src/main/resources/base/media/ic_add_video.webp b/entry/src/main/resources/base/media/ic_add_video.webp new file mode 100644 index 0000000..bd2e343 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_add_video.webp differ diff --git a/entry/src/main/resources/base/media/ic_completed.png b/entry/src/main/resources/base/media/ic_completed.png index 335d851..e7e0c4b 100644 Binary files a/entry/src/main/resources/base/media/ic_completed.png and b/entry/src/main/resources/base/media/ic_completed.png differ diff --git a/entry/src/main/resources/base/media/ic_delete_image.webp b/entry/src/main/resources/base/media/ic_delete_image.webp new file mode 100644 index 0000000..44d61a3 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_delete_image.webp differ diff --git a/entry/src/main/resources/base/media/ic_download3.webp b/entry/src/main/resources/base/media/ic_download3.webp new file mode 100644 index 0000000..a0356bf Binary files /dev/null and b/entry/src/main/resources/base/media/ic_download3.webp differ diff --git a/entry/src/main/resources/base/media/ic_downloading.png b/entry/src/main/resources/base/media/ic_downloading.png index a5d627e..4faa98e 100644 Binary files a/entry/src/main/resources/base/media/ic_downloading.png and b/entry/src/main/resources/base/media/ic_downloading.png differ diff --git a/entry/src/main/resources/base/media/ic_image_water_marker.webp b/entry/src/main/resources/base/media/ic_image_water_marker.webp deleted file mode 100644 index a43c4a2..0000000 Binary files a/entry/src/main/resources/base/media/ic_image_water_marker.webp and /dev/null differ diff --git a/entry/src/main/resources/base/media/ic_join_wx_group_tip1.webp b/entry/src/main/resources/base/media/ic_join_wx_group_tip1.webp new file mode 100644 index 0000000..639f32a Binary files /dev/null and b/entry/src/main/resources/base/media/ic_join_wx_group_tip1.webp differ diff --git a/entry/src/main/resources/base/media/ic_join_wx_group_tip2.webp b/entry/src/main/resources/base/media/ic_join_wx_group_tip2.webp new file mode 100644 index 0000000..d52ab3a Binary files /dev/null and b/entry/src/main/resources/base/media/ic_join_wx_group_tip2.webp differ diff --git a/entry/src/main/resources/base/media/ic_join_wx_group_tip3.webp b/entry/src/main/resources/base/media/ic_join_wx_group_tip3.webp new file mode 100644 index 0000000..26b7730 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_join_wx_group_tip3.webp differ diff --git a/entry/src/main/resources/base/media/ic_join_wx_group_tip4.webp b/entry/src/main/resources/base/media/ic_join_wx_group_tip4.webp new file mode 100644 index 0000000..c7cd235 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_join_wx_group_tip4.webp differ diff --git a/entry/src/main/resources/base/media/ic_join_wx_group_tip5.webp b/entry/src/main/resources/base/media/ic_join_wx_group_tip5.webp new file mode 100644 index 0000000..b527f82 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_join_wx_group_tip5.webp differ diff --git a/entry/src/main/resources/base/media/ic_join_wx_group_tip_bg.webp b/entry/src/main/resources/base/media/ic_join_wx_group_tip_bg.webp new file mode 100644 index 0000000..4cca8e4 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_join_wx_group_tip_bg.webp differ diff --git a/entry/src/main/resources/base/media/ic_join_wx_group_tip_indicator.webp b/entry/src/main/resources/base/media/ic_join_wx_group_tip_indicator.webp new file mode 100644 index 0000000..e5e45c0 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_join_wx_group_tip_indicator.webp differ diff --git a/entry/src/main/resources/base/media/ic_left_top_rect.webp b/entry/src/main/resources/base/media/ic_left_top_rect.webp index 4a5f027..02ebdb2 100644 Binary files a/entry/src/main/resources/base/media/ic_left_top_rect.webp and b/entry/src/main/resources/base/media/ic_left_top_rect.webp differ diff --git a/entry/src/main/resources/base/media/ic_remove_watermark.webp b/entry/src/main/resources/base/media/ic_remove_watermark.webp new file mode 100644 index 0000000..a6c9b6c Binary files /dev/null and b/entry/src/main/resources/base/media/ic_remove_watermark.webp differ diff --git a/entry/src/main/resources/base/media/ic_reupload.webp b/entry/src/main/resources/base/media/ic_reupload.webp new file mode 100644 index 0000000..c114483 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_reupload.webp differ diff --git a/entry/src/main/resources/base/media/ic_reupload_video.png b/entry/src/main/resources/base/media/ic_reupload_video.png deleted file mode 100644 index bcf3398..0000000 Binary files a/entry/src/main/resources/base/media/ic_reupload_video.png and /dev/null differ diff --git a/entry/src/main/resources/base/media/ic_text_water_marker.webp b/entry/src/main/resources/base/media/ic_text_water_marker.webp deleted file mode 100644 index 680d14c..0000000 Binary files a/entry/src/main/resources/base/media/ic_text_water_marker.webp and /dev/null differ diff --git a/entry/src/main/resources/base/media/ic_tool_arrow.webp b/entry/src/main/resources/base/media/ic_tool_arrow.webp new file mode 100644 index 0000000..185d4af Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_arrow.webp differ diff --git a/entry/src/main/resources/base/media/ic_tool_icon1.webp b/entry/src/main/resources/base/media/ic_tool_icon1.webp new file mode 100644 index 0000000..8c057a8 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_icon1.webp differ diff --git a/entry/src/main/resources/base/media/ic_tool_icon10.webp b/entry/src/main/resources/base/media/ic_tool_icon10.webp new file mode 100644 index 0000000..409ef40 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_icon10.webp differ diff --git a/entry/src/main/resources/base/media/ic_tool_icon11.webp b/entry/src/main/resources/base/media/ic_tool_icon11.webp new file mode 100644 index 0000000..2b38eac Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_icon11.webp differ diff --git a/entry/src/main/resources/base/media/ic_tool_icon2.webp b/entry/src/main/resources/base/media/ic_tool_icon2.webp new file mode 100644 index 0000000..2f87121 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_icon2.webp differ diff --git a/entry/src/main/resources/base/media/ic_tool_icon3.webp b/entry/src/main/resources/base/media/ic_tool_icon3.webp new file mode 100644 index 0000000..c153b4b Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_icon3.webp differ diff --git a/entry/src/main/resources/base/media/ic_tool_icon4.webp b/entry/src/main/resources/base/media/ic_tool_icon4.webp new file mode 100644 index 0000000..203b895 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_icon4.webp differ diff --git a/entry/src/main/resources/base/media/ic_tool_icon5.webp b/entry/src/main/resources/base/media/ic_tool_icon5.webp new file mode 100644 index 0000000..c16e0f0 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_icon5.webp differ diff --git a/entry/src/main/resources/base/media/ic_tool_icon6.webp b/entry/src/main/resources/base/media/ic_tool_icon6.webp new file mode 100644 index 0000000..d5cf650 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_icon6.webp differ diff --git a/entry/src/main/resources/base/media/ic_tool_icon7.webp b/entry/src/main/resources/base/media/ic_tool_icon7.webp new file mode 100644 index 0000000..5125e60 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_icon7.webp differ diff --git a/entry/src/main/resources/base/media/ic_tool_icon8.webp b/entry/src/main/resources/base/media/ic_tool_icon8.webp new file mode 100644 index 0000000..7c7f90c Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_icon8.webp differ diff --git a/entry/src/main/resources/base/media/ic_tool_icon9.webp b/entry/src/main/resources/base/media/ic_tool_icon9.webp new file mode 100644 index 0000000..663da30 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tool_icon9.webp differ diff --git a/entry/src/main/resources/base/media/ic_tools_top_bg.webp b/entry/src/main/resources/base/media/ic_tools_top_bg.webp new file mode 100644 index 0000000..0355db3 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_tools_top_bg.webp differ diff --git a/entry/src/main/resources/base/media/ic_watermark_icon1.webp b/entry/src/main/resources/base/media/ic_watermark_icon1.webp new file mode 100644 index 0000000..a5b4b43 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_watermark_icon1.webp differ diff --git a/entry/src/main/resources/base/media/ic_watermark_icon2.webp b/entry/src/main/resources/base/media/ic_watermark_icon2.webp new file mode 100644 index 0000000..8490ead Binary files /dev/null and b/entry/src/main/resources/base/media/ic_watermark_icon2.webp differ diff --git a/entry/src/main/resources/base/media/ic_watermark_icon3.webp b/entry/src/main/resources/base/media/ic_watermark_icon3.webp new file mode 100644 index 0000000..5a7f7ea Binary files /dev/null and b/entry/src/main/resources/base/media/ic_watermark_icon3.webp differ diff --git a/entry/src/main/resources/base/media/ic_wx_group_tip1.webp b/entry/src/main/resources/base/media/ic_wx_group_tip1.webp deleted file mode 100644 index a253c30..0000000 Binary files a/entry/src/main/resources/base/media/ic_wx_group_tip1.webp and /dev/null differ diff --git a/entry/src/main/resources/base/media/ic_wx_group_tip2.png b/entry/src/main/resources/base/media/ic_wx_group_tip2.png deleted file mode 100644 index 84ce9c7..0000000 Binary files a/entry/src/main/resources/base/media/ic_wx_group_tip2.png and /dev/null differ diff --git a/entry/src/main/resources/base/media/ic_wx_group_tip3.webp b/entry/src/main/resources/base/media/ic_wx_group_tip3.webp deleted file mode 100644 index 7903606..0000000 Binary files a/entry/src/main/resources/base/media/ic_wx_group_tip3.webp and /dev/null differ diff --git a/entry/src/main/resources/base/media/ic_wx_group_tip4.webp b/entry/src/main/resources/base/media/ic_wx_group_tip4.webp deleted file mode 100644 index 5d5a5f3..0000000 Binary files a/entry/src/main/resources/base/media/ic_wx_group_tip4.webp and /dev/null differ diff --git a/entry/src/main/resources/base/media/ic_wx_group_tip5.webp b/entry/src/main/resources/base/media/ic_wx_group_tip5.webp deleted file mode 100644 index 7f2da14..0000000 Binary files a/entry/src/main/resources/base/media/ic_wx_group_tip5.webp and /dev/null differ diff --git a/entry/src/main/resources/base/media/ic_wx_group_tip_bg.webp b/entry/src/main/resources/base/media/ic_wx_group_tip_bg.webp deleted file mode 100644 index 087f78e..0000000 Binary files a/entry/src/main/resources/base/media/ic_wx_group_tip_bg.webp and /dev/null differ diff --git a/entry/src/main/resources/base/media/ic_wx_group_tip_indicator.webp b/entry/src/main/resources/base/media/ic_wx_group_tip_indicator.webp deleted file mode 100644 index 7eb1629..0000000 Binary files a/entry/src/main/resources/base/media/ic_wx_group_tip_indicator.webp and /dev/null differ diff --git a/entry/src/main/resources/base/profile/main_pages.json b/entry/src/main/resources/base/profile/main_pages.json index 41c749c..1441ebe 100644 --- a/entry/src/main/resources/base/profile/main_pages.json +++ b/entry/src/main/resources/base/profile/main_pages.json @@ -9,14 +9,16 @@ "pages/main/home/link/TakeMaterialPage", "pages/main/home/course/CoursePage", "pages/main/home/wx/WxVideoPage", - "pages/main/home/tools/AddWaterMarkerPage", + "pages/main/home/tools/AddWatermarkPage", + "pages/main/home/tools/RemoveWatermarkPage", "pages/main/home/tools/MD5ResetPage", "pages/main/home/tools/VideoReversePage", "pages/main/home/tools/VideoMirrorPage", "pages/main/home/tools/ClipVideoPage", "pages/main/home/tools/RemoveAudioPage", "pages/main/home/tools/AddAudioPage", - "pages/main/home/tools/TakeAudioPage", + "pages/main/home/tools/VideoToAudioPage", + "pages/main/home/tools/ImageMergePage", "pages/main/home/material/MaterialDetailPage", "pages/main/mine/user/UserSettingsPage", "pages/main/mine/vip/VipPage", diff --git a/entry/src/main/resources/rawfile/effect/download_complete.pag b/entry/src/main/resources/rawfile/effect/download_complete.pag index 42b4991..3c84063 100644 Binary files a/entry/src/main/resources/rawfile/effect/download_complete.pag and b/entry/src/main/resources/rawfile/effect/download_complete.pag differ diff --git a/entry/src/main/resources/rawfile/effect/downloading.pag b/entry/src/main/resources/rawfile/effect/downloading.pag index 3f17501..a0cc788 100644 Binary files a/entry/src/main/resources/rawfile/effect/downloading.pag and b/entry/src/main/resources/rawfile/effect/downloading.pag differ diff --git a/entry/src/main/resources/rawfile/effect/processing.pag b/entry/src/main/resources/rawfile/effect/processing.pag index 9e6bb48..0055aa5 100644 Binary files a/entry/src/main/resources/rawfile/effect/processing.pag and b/entry/src/main/resources/rawfile/effect/processing.pag differ