完善长图拼接,添加视频转文字(待完善)
This commit is contained in:
parent
ca75abba37
commit
c6797fd97b
Binary file not shown.
|
|
@ -12,6 +12,7 @@
|
|||
"cmccssosdk@../oh_modules/.ohpm/@getui+gysdk@1.0.10/oh_modules/@getui/gysdk/libs/quick_login_hm_1.0.2.har": "cmccssosdk@../oh_modules/.ohpm/@getui+gysdk@1.0.10/oh_modules/@getui/gysdk/libs/quick_login_hm_1.0.2.har",
|
||||
"ctaccount@../oh_modules/.ohpm/@getui+gysdk@1.0.10/oh_modules/@getui/gysdk/libs/ctaccount_v1.1.2.har": "ctaccount@../oh_modules/.ohpm/@getui+gysdk@1.0.10/oh_modules/@getui/gysdk/libs/ctaccount_v1.1.2.har",
|
||||
"dljson@../oh_modules/.ohpm/ctaccount@qccjk9bmoqtng+2vpbi+2wqjznsjx4thqhodhlvlvn0=/oh_modules/ctaccount/library/dlJson.har": "dljson@../oh_modules/.ohpm/ctaccount@qccjk9bmoqtng+2vpbi+2wqjznsjx4thqhodhlvlvn0=/oh_modules/ctaccount/library/dlJson.har",
|
||||
"qcloudfileflash@libs/qcloudfileflash.har": "qcloudfileflash@libs/qcloudfileflash.har",
|
||||
"unicom_login_harmony@../oh_modules/.ohpm/@getui+gysdk@1.0.10/oh_modules/@getui/gysdk/libs/unicom_login_harmony_v1.0.4AR001B0214.har": "unicom_login_harmony@../oh_modules/.ohpm/@getui+gysdk@1.0.10/oh_modules/@getui/gysdk/libs/unicom_login_harmony_v1.0.4AR001B0214.har"
|
||||
},
|
||||
"packages": {
|
||||
|
|
@ -62,6 +63,12 @@
|
|||
"resolved": "../oh_modules/.ohpm/ctaccount@qccjk9bmoqtng+2vpbi+2wqjznsjx4thqhodhlvlvn0=/oh_modules/ctaccount/library/dlJson.har",
|
||||
"registryType": "local"
|
||||
},
|
||||
"qcloudfileflash@libs/qcloudfileflash.har": {
|
||||
"name": "qcloudfileflash",
|
||||
"version": "1.0.0",
|
||||
"resolved": "libs/qcloudfileflash.har",
|
||||
"registryType": "local"
|
||||
},
|
||||
"unicom_login_harmony@../oh_modules/.ohpm/@getui+gysdk@1.0.10/oh_modules/@getui/gysdk/libs/unicom_login_harmony_v1.0.4AR001B0214.har": {
|
||||
"name": "unicom_login_harmony",
|
||||
"version": "1.0.4",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
"license": "",
|
||||
"dependencies": {
|
||||
"@ohos/axios": "^2.2.6",
|
||||
"@getui/gysdk": "1.0.10"
|
||||
"@getui/gysdk": "1.0.10",
|
||||
"qcloudfileflash": "file:./libs/qcloudfileflash.har"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,4 +16,9 @@ export class Constants {
|
|||
//
|
||||
static readonly ENCRYPT = "wE8x4EnIHgyGOyjnoluzI2vk60wz5eNI"
|
||||
static readonly SIGNATURE = "hfLLOtXRjd0e1Ac7O6sAXrECH2E828S9"
|
||||
|
||||
//腾讯云
|
||||
static readonly QCLOUD_APP_ID = "1366199074"
|
||||
static readonly QCLOUD_SECRET_ID = "AKIDYGvVCi06ycDk8ZprfFclgNpFer4D9sPi"
|
||||
static readonly QCLOUD_SECRET_KEY = "72iBPPBj390d2PipqhMmyve9QSFpBKEu"
|
||||
}
|
||||
|
|
@ -89,6 +89,16 @@ export class RouterUrls {
|
|||
*/
|
||||
static readonly IMAGE_MERGE_PAGE = "pages/main/home/tools/ImageMergePage"
|
||||
|
||||
/**
|
||||
* 视频转文字页
|
||||
*/
|
||||
static readonly VIDEO_TO_TEXT_PAGE = "pages/main/home/tools/VideoToTextPage"
|
||||
|
||||
/**
|
||||
* 语音转文字页
|
||||
*/
|
||||
static readonly AUDIO_TO_TEXT_PAGE = "pages/main/home/tools/AudioToTextPage"
|
||||
|
||||
/**
|
||||
* 素材详情页
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
import { Type } from "class-transformer";
|
||||
import "reflect-metadata";
|
||||
|
||||
export class VoiceRecognizeResultEntity {
|
||||
code: number = 0;
|
||||
message: string = '';
|
||||
request_id: string = '';
|
||||
@Type(() => FlashResult)
|
||||
flash_result: Array<FlashResult> = [];
|
||||
audio_duration: number = 0;
|
||||
}
|
||||
|
||||
export class FlashResult {
|
||||
channel_id: number = 0;
|
||||
@Type(() => Sentence)
|
||||
sentence_list: Array<Sentence> = [];
|
||||
text: string = '';
|
||||
}
|
||||
|
||||
export class Sentence {
|
||||
emotional_energy: number = 0;
|
||||
end_time: number = 0;
|
||||
speaker_id: number = 0;
|
||||
speech_speed: number = 0;
|
||||
start_time: number = 0;
|
||||
text: string = '';
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ export class LocalMediaManager {
|
|||
|
||||
static getAllImages(): Array<string> {
|
||||
let array = PrefUtils.getStringArray('local_record')
|
||||
return array.filter(item => item.endsWith('.jpeg'))
|
||||
return array.filter(item => item.endsWith('.jpeg') || item.endsWith('.jpg') || item.endsWith('.png'))
|
||||
}
|
||||
|
||||
static deleteAllImages() {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export class MediaManager {
|
|||
} else {
|
||||
console.error('Create AVImageGenerator failed!');
|
||||
}
|
||||
FileUtil.closeSync(file)
|
||||
mediaList.push(record)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
|
@ -69,9 +70,10 @@ export class MediaManager {
|
|||
for (let i = 0; i < imageUri.length; i++) {
|
||||
try {
|
||||
let uri = imageUri[i]
|
||||
let file = FileUtil.openSync(uri)
|
||||
let file = FileUtil.openSync(uri) //判断图片是否存在 FileUtil.access()无效
|
||||
let record = new MediaRecordEntity(uri)
|
||||
record.name = FileUtil.getFileName(uri)
|
||||
FileUtil.closeSync(file)
|
||||
mediaList.push(record)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
|
|
|||
|
|
@ -144,13 +144,15 @@ export struct HomePage {
|
|||
break;
|
||||
}
|
||||
case 'check_Task': {
|
||||
|
||||
break
|
||||
}
|
||||
case 'course': {
|
||||
this.getUIContext().getRouter().pushUrl({url: RouterUrls.COURSE_PAGE})
|
||||
break
|
||||
}
|
||||
case 'web_link': {
|
||||
WantUtil.toWebBrowser(Constants.WEB_URL)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -348,6 +350,7 @@ export struct HomePage {
|
|||
break
|
||||
}
|
||||
case 'videoToText': {
|
||||
this.getUIContext().getRouter().pushUrl({url: RouterUrls.VIDEO_TO_TEXT_PAGE})
|
||||
break
|
||||
}
|
||||
case 'longImageMerge': {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ struct AddAudioPage {
|
|||
let videoFile = FileUtil.openSync(this.videoUri!!, fileIo.OpenMode.READ_ONLY)
|
||||
// 复制视频文件到缓存目录下
|
||||
FileUtil.copyFileSync(videoFile.fd, cacheVideoPath)
|
||||
FileUtil.closeSync(videoFile)
|
||||
|
||||
let cacheAudioPath = FileUtil.getCacheDirPath() + FileUtil.separator + `cache_${systemDateTime.getTime()}.mp3`
|
||||
if (FileUtil.accessSync(cacheAudioPath)) {
|
||||
|
|
@ -42,6 +43,7 @@ struct AddAudioPage {
|
|||
let audioFile = FileUtil.openSync(this.audioUri!!, fileIo.OpenMode.READ_ONLY)
|
||||
// 复制音频文件到缓存目录下
|
||||
FileUtil.copyFileSync(audioFile.fd, cacheAudioPath)
|
||||
FileUtil.closeSync(audioFile)
|
||||
|
||||
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}`
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ struct AddWatermarkPage {
|
|||
let file = FileUtil.openSync(this.uri!!, fileIo.OpenMode.READ_ONLY)
|
||||
// 复制文件到缓存目录下
|
||||
FileUtil.copyFileSync(file.fd, cacheVideoPath)
|
||||
FileUtil.closeSync(file)
|
||||
|
||||
let imageX = (vp2px(this.rect.x) * this.videoSize.width!!) / this.playerSize.width!!
|
||||
let imageY = (vp2px(this.rect.y) * this.videoSize.height!!) / this.playerSize.height!!
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
@ComponentV2
|
||||
@Entry
|
||||
struct AudioToTextPage {
|
||||
build() {
|
||||
}
|
||||
}
|
||||
|
|
@ -43,6 +43,7 @@ struct ClipVideoPage {
|
|||
let file = FileUtil.openSync(this.uri!!, fileIo.OpenMode.READ_ONLY)
|
||||
// 复制文件到缓存目录下
|
||||
FileUtil.copyFileSync(file.fd, cacheVideoPath)
|
||||
FileUtil.closeSync(file)
|
||||
|
||||
let clipWidth: number = 0
|
||||
let clipHeight: number = 0
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
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 { BusinessError } from '@kit.BasicServicesKit'
|
||||
import { AppUtil, DisplayUtil, FileUtil } from '@pura/harmony-utils'
|
||||
import { ToastUtils } from '../../../../utils/ToastUtils'
|
||||
import { fileIo } from '@kit.CoreFileKit'
|
||||
import { SaveUtils } from '../../../../utils/SaveUtils'
|
||||
|
|
@ -10,23 +10,74 @@ 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'
|
||||
import { image } from '@kit.ImageKit'
|
||||
import { ImageUtils } from '../../../../utils/ImageUtils'
|
||||
import { Luban } from '@ark/luban'
|
||||
|
||||
@Entry
|
||||
@ComponentV2
|
||||
struct ImageMergePage {
|
||||
@Local uri?: string = undefined
|
||||
@Local pixelMap?: image.PixelMap = undefined
|
||||
@Local selectedImage?: string = undefined
|
||||
@Local imageUris: Array<string> = []
|
||||
@Local currentTime: number = 0
|
||||
@Local durationTime: number = 0
|
||||
@Local isPlaying: boolean = false
|
||||
@Local isSuccess: boolean = false
|
||||
|
||||
private selectedImages: Array<string> = []
|
||||
|
||||
mergeImage() {
|
||||
async mergeImage() {
|
||||
LoadingDialog.show(this.getUIContext())
|
||||
try {
|
||||
let pixelArray: Array<image.PixelMap> = []
|
||||
let maxWidth = DisplayUtil.getWidth()
|
||||
for (let i = 0;i < this.imageUris.length;i++) {
|
||||
let compressedUri = await Luban.with(this.imageUris[i]).ignoreBy(100).get()
|
||||
const imageSource: image.ImageSource = image.createImageSource(compressedUri[0])
|
||||
let decodingOptions: image.DecodingOptions = {
|
||||
editable: true,
|
||||
desiredPixelFormat: image.PixelMapFormat.RGB_565,
|
||||
}
|
||||
let pixelMap = imageSource.createPixelMapSync(decodingOptions)
|
||||
if (pixelMap) {
|
||||
let imageInfo = pixelMap.getImageInfoSync()
|
||||
maxWidth = Math.max(imageInfo.size.width)
|
||||
pixelArray.push(pixelMap)
|
||||
} else {
|
||||
ToastUtils.show('处理失败')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let totalHeight = 0
|
||||
let newPixelArray: Array<image.PixelMap> = []
|
||||
for (let i = 0;i < pixelArray.length;i++) {
|
||||
let pixelMap = pixelArray[i]
|
||||
let imageInfo = pixelMap.getImageInfoSync()
|
||||
let height = Math.trunc(imageInfo.size.height * maxWidth / imageInfo.size.width)
|
||||
let newPixel = await ImageUtils.resizeImage(pixelMap, maxWidth, height)
|
||||
newPixelArray.push(newPixel)
|
||||
totalHeight += height
|
||||
}
|
||||
|
||||
let offScreenCanvas = new OffscreenCanvas(maxWidth, totalHeight)
|
||||
let OffScreenContext = offScreenCanvas.getContext('2d')
|
||||
let top = 0
|
||||
newPixelArray.forEach((pixelMap) => {
|
||||
let imageInfo = pixelMap.getImageInfoSync()
|
||||
OffScreenContext.drawImage(pixelMap, 0, top, imageInfo.size.width, imageInfo.size.height)
|
||||
top += imageInfo.size.height
|
||||
})
|
||||
this.pixelMap = OffScreenContext.getPixelMap(0, 0, maxWidth, totalHeight)
|
||||
|
||||
this.selectedImage = undefined
|
||||
this.imageUris = []
|
||||
this.selectedImages = []
|
||||
this.isSuccess = true
|
||||
ToastUtils.show('处理成功')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
ToastUtils.show('处理失败')
|
||||
}
|
||||
LoadingDialog.dismiss()
|
||||
}
|
||||
|
||||
selectPhotos() {
|
||||
|
|
@ -34,11 +85,12 @@ struct ImageMergePage {
|
|||
MIMEType: photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE,
|
||||
maxSelectNumber: 9,
|
||||
preselectedUris: this.selectedImages,
|
||||
isOriginalSupported: true,
|
||||
isOriginalSupported: false,
|
||||
})
|
||||
.then((result: photoAccessHelper.PhotoSelectResult) => {
|
||||
if (result.photoUris.length != 0) {
|
||||
this.isSuccess = false
|
||||
this.pixelMap = undefined
|
||||
this.selectedImages = result.photoUris
|
||||
this.imageUris = result.photoUris
|
||||
this.selectedImage = result.photoUris[0]
|
||||
|
|
@ -75,110 +127,126 @@ struct ImageMergePage {
|
|||
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)
|
||||
Scroll() {
|
||||
Image(this.pixelMap).width('100%').height('auto')
|
||||
}
|
||||
.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})
|
||||
.height('100%')
|
||||
.scrollBar(BarState.Off)
|
||||
.visibility(this.pixelMap ? Visibility.Visible : Visibility.None)
|
||||
|
||||
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() {
|
||||
Column() {
|
||||
Stack() {
|
||||
Stack() {
|
||||
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})
|
||||
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(80)
|
||||
.height(80)
|
||||
.borderRadius(6)
|
||||
.backgroundColor(Color.White)
|
||||
.margin({top: 11, right: 11})
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.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})
|
||||
.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})
|
||||
.borderWidth(1)
|
||||
.borderRadius(6)
|
||||
.borderColor(item === this.selectedImage ? $r('app.color.color_466afd') : Color.Transparent)
|
||||
|
||||
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)
|
||||
}
|
||||
.margin({left: index === 0 ? 32 : 0, right: index === 8 ? 32 : 0})
|
||||
.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})
|
||||
}
|
||||
.margin({right: 32})
|
||||
.onClick(() => {
|
||||
this.selectPhotos()
|
||||
})
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.scrollBar(BarState.Off)
|
||||
.listDirection(Axis.Horizontal)
|
||||
}
|
||||
.width('100%')
|
||||
.height(92)
|
||||
.scrollBar(BarState.Off)
|
||||
.scrollable(ScrollDirection.Horizontal)
|
||||
.margin({bottom: 20})
|
||||
}
|
||||
.visibility(this.pixelMap ? Visibility.None : Visibility.Visible)
|
||||
}
|
||||
.layoutWeight(1)
|
||||
|
||||
Stack() {
|
||||
Button('确认处理', { type: ButtonType.Capsule, stateEffect: true })
|
||||
|
|
@ -225,10 +293,13 @@ struct ImageMergePage {
|
|||
.layoutWeight(1)
|
||||
.backgroundColor($r('app.color.color_466afd'))
|
||||
.onClick(() => {
|
||||
SaveUtils.saveImageVideoToAlbumDialog([this.uri!!])
|
||||
SaveUtils.savePixelMapToAlbum(this.pixelMap!!)
|
||||
.then((saved) => {
|
||||
if (saved) {
|
||||
this.pixelMap = undefined
|
||||
this.selectedImage = undefined
|
||||
this.imageUris = []
|
||||
this.selectedImages = []
|
||||
this.showDownloadDialog()
|
||||
} else {
|
||||
ToastUtils.show('保存失败')
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ struct MD5ResetPage {
|
|||
let file = FileUtil.openSync(this.uri!!, fileIo.OpenMode.READ_ONLY)
|
||||
// 复制文件到缓存目录下
|
||||
FileUtil.copyFileSync(file.fd, outputPath)
|
||||
FileUtil.closeSync(file)
|
||||
|
||||
if (FileUtil.accessSync(outputPath)) {
|
||||
this.uri = FileUtil.getUriFromPath(outputPath)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ struct RemoveAudioPage {
|
|||
let file = FileUtil.openSync(this.uri!!, fileIo.OpenMode.READ_ONLY)
|
||||
// 复制文件到缓存目录下
|
||||
FileUtil.copyFileSync(file.fd, cachePath)
|
||||
FileUtil.closeSync(file)
|
||||
|
||||
let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp4`
|
||||
let cmd = `ffmpeg -i ${cachePath} -an -c:v copy ${outputPath}`
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ struct RemoveWatermarkPage {
|
|||
let file = FileUtil.openSync(this.uri!!, fileIo.OpenMode.READ_ONLY)
|
||||
// 复制文件到缓存目录下
|
||||
FileUtil.copyFileSync(file.fd, cacheVideoPath)
|
||||
FileUtil.closeSync(file)
|
||||
|
||||
let rectX = (vp2px(this.rect.x) * this.videoSize.width!!) / this.playerSize.width!!
|
||||
let rectY = (vp2px(this.rect.y) * this.videoSize.height!!) / this.playerSize.height!!
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ struct VideoMirrorPage {
|
|||
let file = FileUtil.openSync(this.uri!!, fileIo.OpenMode.READ_ONLY)
|
||||
// 复制文件到缓存目录下
|
||||
FileUtil.copyFileSync(file.fd, cacheVideoPath)
|
||||
FileUtil.closeSync(file)
|
||||
|
||||
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}`
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ struct VideoReversePage {
|
|||
let file = FileUtil.openSync(this.uri!!, fileIo.OpenMode.READ_ONLY)
|
||||
// 复制文件到缓存目录下
|
||||
FileUtil.copyFileSync(file.fd, cacheVideoPath)
|
||||
FileUtil.closeSync(file)
|
||||
|
||||
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}`
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ import { avSessionManager } from '../../../../manager/AVSessionManager'
|
|||
struct VideoToAudioPage {
|
||||
private controller: VideoController = new VideoController()
|
||||
@Local videoUri?: string
|
||||
@Local audioUri?: 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())
|
||||
|
|
@ -33,6 +33,7 @@ struct VideoToAudioPage {
|
|||
let file = FileUtil.openSync(this.videoUri!!, fileIo.OpenMode.READ_ONLY)
|
||||
// 复制文件到缓存目录下
|
||||
FileUtil.copyFileSync(file.fd, cachePath)
|
||||
FileUtil.closeSync(file)
|
||||
|
||||
let outputPath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.mp3`
|
||||
let cmd = `ffmpeg -i ${cachePath} -vn -c:a mp3 ${outputPath}`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,386 @@
|
|||
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'
|
||||
import { MP4Parser } from '@ohos/mp4parser'
|
||||
import { QCloud } from 'qcloudfileflash'
|
||||
import { Constants } from '../../../../common/Constants'
|
||||
import { VoiceRecognizeResultEntity } from '../../../../entity/VoiceRecognizeResultEntity'
|
||||
import { plainToInstance } from 'class-transformer'
|
||||
|
||||
@Entry
|
||||
@ComponentV2
|
||||
struct VideoToTextPage {
|
||||
private controller: VideoController = new VideoController()
|
||||
@Local resultText?: string
|
||||
@Local videoUri?: string
|
||||
@Local currentTime: number = 0
|
||||
@Local durationTime: number = 0
|
||||
@Local isPlaying: boolean = false
|
||||
@Local isSuccess: boolean = false
|
||||
|
||||
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)
|
||||
FileUtil.closeSync(file)
|
||||
|
||||
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.recognizeAudio(outputPath)
|
||||
.then((result: VoiceRecognizeResultEntity) => {
|
||||
this.resultText = result.flash_result[0].text
|
||||
this.isSuccess = true
|
||||
this.isPlaying = false
|
||||
ToastUtils.show('处理成功')
|
||||
LoadingDialog.dismiss()
|
||||
})
|
||||
.catch((e: BusinessError) => {
|
||||
console.log(e.message)
|
||||
ToastUtils.show('处理失败')
|
||||
LoadingDialog.dismiss()
|
||||
})
|
||||
} else {
|
||||
ToastUtils.show('处理失败')
|
||||
LoadingDialog.dismiss()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async recognizeAudio(uri: string): Promise<VoiceRecognizeResultEntity> {
|
||||
let builder = new QCloud.FileFlash.Builder()
|
||||
builder.appID = Constants.QCLOUD_APP_ID
|
||||
builder.secretID = Constants.QCLOUD_SECRET_ID
|
||||
builder.secretKey = Constants.QCLOUD_SECRET_KEY
|
||||
// builder.token = this._token
|
||||
builder.setApiParam(QCloud.FileFlash.kEngineType, '16k_zh')
|
||||
builder.setApiParam(QCloud.FileFlash.kVoiceFormat, 'mp3')
|
||||
builder.setApiParam(QCloud.FileFlash.kFilterDirty, 0)
|
||||
builder.setApiParam(QCloud.FileFlash.kFilterModal, 0)
|
||||
builder.setApiParam(QCloud.FileFlash.kFilterPunc, 0)
|
||||
builder.setApiParam(QCloud.FileFlash.kConvertNumMode, 1)
|
||||
builder.setApiParam(QCloud.FileFlash.kWordInfo, 0)
|
||||
builder.setApiParam(QCloud.FileFlash.kSpeakerDiarization, 1)
|
||||
try {
|
||||
let file = FileUtil.openSync(uri, fileIo.OpenMode.READ_ONLY)
|
||||
const stat = FileUtil.lstatSync(uri)
|
||||
const buffer = new ArrayBuffer(stat.size)
|
||||
FileUtil.readSync(file.fd, buffer)
|
||||
FileUtil.closeSync(file)
|
||||
let result = await builder.build(buffer).task
|
||||
const voiceResult = plainToInstance(VoiceRecognizeResultEntity, result)
|
||||
return Promise.resolve(voiceResult)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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() {
|
||||
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})
|
||||
|
||||
Column() {
|
||||
Row() {
|
||||
Image($r('app.media.ic_star')).width(22).height(22)
|
||||
Text('文本结果').fontColor($r('app.color.color_212226')).fontSize(15).fontWeight(FontWeight.Medium).margin({left: 4})
|
||||
}
|
||||
Divider().strokeWidth(1).color($r('app.color.color_eeeeee')).margin({top: 12})
|
||||
Text(this.resultText).width('100%').height('auto').fontColor($r('app.color.color_212226')).fontSize(14).margin({top: 14})
|
||||
}
|
||||
.width('90%')
|
||||
.layoutWeight(1)
|
||||
.borderRadius(10)
|
||||
.borderWidth(1)
|
||||
.borderColor('#DADEE5')
|
||||
.backgroundColor(Color.White)
|
||||
.margin({top: 30, bottom: 20})
|
||||
.padding(12)
|
||||
.visibility(this.resultText ? Visibility.Visible : Visibility.None)
|
||||
|
||||
Blank().layoutWeight(1).visibility(this.resultText ? 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.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_copy_text')).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.videoUri!!])
|
||||
.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'))
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ export struct ToolsPage {
|
|||
break
|
||||
}
|
||||
case 'videoToText': {
|
||||
this.getUIContext().getRouter().pushUrl({url: RouterUrls.VIDEO_TO_TEXT_PAGE})
|
||||
break
|
||||
}
|
||||
case 'videoToAudio': {
|
||||
|
|
@ -28,6 +29,7 @@ export struct ToolsPage {
|
|||
break
|
||||
}
|
||||
case 'audioToText': {
|
||||
this.getUIContext().getRouter().pushUrl({url: RouterUrls.AUDIO_TO_TEXT_PAGE})
|
||||
break
|
||||
}
|
||||
case 'addWatermark': {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import { DisplayUtil } from '@pura/harmony-utils';
|
||||
import { ImagePreview } from '@rv/image-preview';
|
||||
import { TitleBar } from '../../view/TitleBar'
|
||||
import { media } from '@kit.MediaKit';
|
||||
import image from '@ohos.multimedia.image';
|
||||
import { fileIo } from '@kit.CoreFileKit';
|
||||
|
||||
@Entry
|
||||
@ComponentV2
|
||||
|
|
@ -6,8 +11,11 @@ struct PhotoViewPage {
|
|||
@Local title: string = ''
|
||||
@Local uri?: string = ''
|
||||
|
||||
private imageSize: media.PixelMapParams = {}
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.initParams()
|
||||
this.initImageSize()
|
||||
}
|
||||
|
||||
initParams() {
|
||||
|
|
@ -18,12 +26,31 @@ struct PhotoViewPage {
|
|||
}
|
||||
}
|
||||
|
||||
initImageSize() {
|
||||
let file = fileIo.openSync(this.uri, fileIo.OpenMode.READ_ONLY)
|
||||
let imageSource = image.createImageSource(file.fd)
|
||||
let imageInfo = imageSource.getImageInfoSync()
|
||||
this.imageSize.width = imageInfo.size.width
|
||||
this.imageSize.height = imageInfo.size.height
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
TitleBar({title: this.title}).width('100%')
|
||||
Image(this.uri).width('100%').layoutWeight(1)
|
||||
.margin({bottom: 50})
|
||||
.objectFit(ImageFit.Contain)
|
||||
Stack() {
|
||||
ImagePreview() {
|
||||
Image(this.uri)
|
||||
.width('100%')
|
||||
.objectFit(ImageFit.Contain)
|
||||
.draggable(false)
|
||||
.sourceSize({
|
||||
width: px2vp(DisplayUtil.getWidth()),
|
||||
height: px2vp(Math.round(this.imageSize.height!! * DisplayUtil.getWidth() / this.imageSize.width!!))
|
||||
})
|
||||
}
|
||||
}
|
||||
.layoutWeight(1)
|
||||
.margin({bottom: 50})
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
import { common2D, drawing } from "@kit.ArkGraphics2D";
|
||||
import { image } from "@kit.ImageKit";
|
||||
|
||||
export class ImageUtils {
|
||||
|
||||
/**
|
||||
* 调整图片分辨率
|
||||
* @param bitmap
|
||||
* @param w
|
||||
* @param h
|
||||
* @returns
|
||||
*/
|
||||
static async resizeImage(bitmap: PixelMap, w: number, h: number): Promise<image.PixelMap> {
|
||||
// 获取图像像素信息
|
||||
const imageInfo = await bitmap.getImageInfo();
|
||||
const width: number = imageInfo.size.width;
|
||||
const height: number = imageInfo.size.height;
|
||||
const scaleWidth: number = w / width;
|
||||
const scaleHeight: number = h / height;
|
||||
const pixelMapColor: ArrayBuffer = new ArrayBuffer(w * h * 2);
|
||||
const options: image.InitializationOptions = {
|
||||
editable: true,
|
||||
pixelFormat: image.PixelMapFormat.RGB_565,
|
||||
size: { height: h, width: w }
|
||||
};
|
||||
// 采用RGB_565格式创建画布PixelMap
|
||||
const canvasPixelMap = await image.createPixelMap(pixelMapColor, options);
|
||||
// 创建一个以PixelMap作为绘制目标的Canvas对象
|
||||
const canvas = new drawing.Canvas(canvasPixelMap);
|
||||
// 构造矩阵对象
|
||||
const matrix = new drawing.Matrix();
|
||||
// 构造画笔对象
|
||||
let pen = new drawing.Pen();
|
||||
// 绑定画笔到画布上,在画布上进行绘制时,将使用画笔的样式去绘制图形形状的轮廓
|
||||
canvas.attachPen(pen);
|
||||
// 矩形区域
|
||||
let rect: common2D.Rect = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: w,
|
||||
bottom: h
|
||||
};
|
||||
// 将图片绘制到画布的指定区域上
|
||||
canvas.drawImageRect(bitmap, rect);
|
||||
// 将矩阵设置为矩阵右乘围绕轴心点按一定缩放系数缩放后的单位矩阵后得到的矩阵
|
||||
matrix.postScale(scaleWidth, scaleHeight, 0, 0);
|
||||
// 设置矩阵对象参数
|
||||
canvas.setMatrix(matrix);
|
||||
// 将画笔与画布解绑
|
||||
canvas.detachPen();
|
||||
return Promise.resolve(canvasPixelMap)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ export class MediaUtils {
|
|||
let metadata = await avMetaDataExtractor.fetchMetadata()
|
||||
videoSize.width = parseInt(metadata.videoWidth as string);
|
||||
videoSize.height = parseInt(metadata.videoHeight as string);
|
||||
FileUtil.closeSync(file)
|
||||
return Promise.resolve(videoSize)
|
||||
} catch (e) {
|
||||
let cacheFilePath = FileUtil.getCacheDirPath() + '/' + FileUtil.getFileName(uri)
|
||||
|
|
@ -20,6 +21,7 @@ export class MediaUtils {
|
|||
let file = FileUtil.openSync(uri, fileIo.OpenMode.READ_ONLY);
|
||||
// 复制文件到缓存目录下
|
||||
FileUtil.copyFileSync(file.fd, cacheFilePath)
|
||||
FileUtil.closeSync(file)
|
||||
|
||||
avMetaDataExtractor.fdSrc = FileUtil.openSync(cacheFilePath);
|
||||
let metadata = await avMetaDataExtractor.fetchMetadata()
|
||||
|
|
|
|||
|
|
@ -6,9 +6,39 @@ import { EventConstants } from '../common/EventConstants';
|
|||
import { MediaAction, MediaType } from '../manager/MediaManager';
|
||||
import { LocalMediaManager } from '../manager/LocalMediaManager';
|
||||
import { systemDateTime } from '@kit.BasicServicesKit';
|
||||
import { image } from '@kit.ImageKit';
|
||||
|
||||
export class SaveUtils {
|
||||
|
||||
/**
|
||||
* 保存pixelMap到相册
|
||||
* @param pixelMap
|
||||
* @returns
|
||||
*/
|
||||
static async savePixelMapToAlbum(pixelMap: image.PixelMap): Promise<boolean> {
|
||||
try {
|
||||
const packOptions: image.PackingOption = {
|
||||
format: 'image/jpeg',
|
||||
quality: 80
|
||||
}
|
||||
let buffer = await image.createImagePacker().packToData(pixelMap, packOptions)
|
||||
// 应用沙箱路径
|
||||
let cachePath = FileUtil.getCacheDirPath() + FileUtil.separator + `scmf_${systemDateTime.getTime()}.jpeg`
|
||||
// 在沙箱新建并打开文件
|
||||
let file = fileIo.openSync(cachePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE)
|
||||
// 写入pixelMap图片内容
|
||||
fileIo.writeSync(file.fd, buffer)
|
||||
// 关闭文件
|
||||
fileIo.closeSync(file.fd)
|
||||
// 使用showAssetsCreationDialog保存沙箱中的图片
|
||||
let saved = await SaveUtils.saveImageVideoToAlbumDialog([cachePath])
|
||||
return Promise.resolve(saved)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return Promise.resolve(false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存视频和图片到相册, 弹窗授权
|
||||
* @param path
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
import { RouterUrls } from '../common/RouterUrls';
|
||||
import { MediaRecordEntity } from '../entity/MediaRecordEntity';
|
||||
import { router } from '@kit.ArkUI';
|
||||
import { AppUtil, DateUtil, StrUtil } from '@pura/harmony-utils';
|
||||
import { AppUtil, DateUtil, DisplayUtil, StrUtil } from '@pura/harmony-utils';
|
||||
import { ShareManager } from '../manager/ShareManager';
|
||||
import { Want } from '@kit.AbilityKit';
|
||||
import { SimpleTipDialog } from '../dialog/SimpleTipDialog';
|
||||
import { PrefUtils } from '../utils/PrefUtils';
|
||||
import { WantUtils } from '../utils/WantUtils';
|
||||
import { media } from '@kit.MediaKit';
|
||||
import { fileIo } from '@kit.CoreFileKit';
|
||||
import { image } from '@kit.ImageKit';
|
||||
|
||||
@ComponentV2
|
||||
export struct VideoRecordItemView {
|
||||
|
|
@ -85,6 +88,20 @@ export struct ImageRecordItemView {
|
|||
@Param media?: MediaRecordEntity = undefined;
|
||||
@Param rowCount: number = 1;
|
||||
|
||||
private imageSize: media.PixelMapParams = {}
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.initImageSize()
|
||||
}
|
||||
|
||||
initImageSize() {
|
||||
let file = fileIo.openSync(this.media?.uri, fileIo.OpenMode.READ_ONLY)
|
||||
let imageSource = image.createImageSource(file.fd)
|
||||
let imageInfo = imageSource.getImageInfoSync()
|
||||
this.imageSize.width = imageInfo.size.width
|
||||
this.imageSize.height = imageInfo.size.height
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
Image(this.media?.uri)
|
||||
|
|
@ -92,6 +109,10 @@ export struct ImageRecordItemView {
|
|||
.height('100%')
|
||||
.borderRadius(6)
|
||||
.backgroundColor($r('app.color.color_222222'))
|
||||
.sourceSize({
|
||||
width: px2vp(DisplayUtil.getWidth() / 2),
|
||||
height: px2vp(Math.round(this.imageSize.height!! * DisplayUtil.getWidth() / 2 / this.imageSize.width!!))
|
||||
})
|
||||
.onClick(() => {
|
||||
this.getUIContext().getRouter().pushUrl({ url: RouterUrls.PHOTO_VIEW_PAGE, params: { uri : this.media?.uri } })
|
||||
})
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 398 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -19,6 +19,8 @@
|
|||
"pages/main/home/tools/AddAudioPage",
|
||||
"pages/main/home/tools/VideoToAudioPage",
|
||||
"pages/main/home/tools/ImageMergePage",
|
||||
"pages/main/home/tools/VideoToTextPage",
|
||||
"pages/main/home/tools/AudioToTextPage",
|
||||
"pages/main/home/material/MaterialDetailPage",
|
||||
"pages/main/mine/user/UserSettingsPage",
|
||||
"pages/main/mine/vip/VipPage",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
"ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
|
||||
"specifiers": {
|
||||
"@alipay/blueshieldsdk@oh_modules/.ohpm/@cashier_alipay+cashiersdk@15.8.36/oh_modules/@cashier_alipay/cashiersdk/lib/blueshieldsdk-1.0.29.har": "@alipay/blueshieldsdk@oh_modules/.ohpm/@cashier_alipay+cashiersdk@15.8.36/oh_modules/@cashier_alipay/cashiersdk/lib/blueshieldsdk-1.0.29.har",
|
||||
"@ark/luban@^1.0.2": "@ark/luban@1.0.2",
|
||||
"@cashier_alipay/cashiersdk@^15.8.36": "@cashier_alipay/cashiersdk@15.8.36",
|
||||
"@ohos/crypto-js@^2.0.4": "@ohos/crypto-js@2.0.4",
|
||||
"@ohos/hamock@1.0.0": "@ohos/hamock@1.0.0",
|
||||
|
|
@ -38,6 +39,13 @@
|
|||
"libblueshield.so": "file:./src/main/cpp/types/libblueshield"
|
||||
}
|
||||
},
|
||||
"@ark/luban@1.0.2": {
|
||||
"name": "@ark/luban",
|
||||
"version": "1.0.2",
|
||||
"integrity": "sha512-p5YLHNVi8sSE2b3GxeXXg88bmP16qtGv8QRrNfxDSt9aKgt9PZ6xuPKY6+ORrXIbih0n9t97qHRfMlinqv6UdA==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@ark/luban/-/luban-1.0.2.har",
|
||||
"registryType": "ohpm"
|
||||
},
|
||||
"@cashier_alipay/cashiersdk@15.8.36": {
|
||||
"name": "@cashier_alipay/cashiersdk",
|
||||
"version": "15.8.36",
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@
|
|||
"@tencent/libpag": "^4.4.31",
|
||||
"@ohos/mp4parser": "^2.0.7",
|
||||
"@ohos/imageknifepro": "^1.0.12",
|
||||
"@rv/image-preview": "^2.1.2"
|
||||
"@rv/image-preview": "^2.1.2",
|
||||
"@ark/luban": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ohos/hypium": "1.0.21",
|
||||
|
|
|
|||
Loading…
Reference in New Issue