import { LengthMetrics, promptAction, router } from '@kit.ArkUI';
import window from '@ohos.window';
import common from '@ohos.app.ability.common';
import { media } from '@kit.MediaKit';
import { levelComponent } from '../components/levelComponent'
import { WorkApi } from '../api/work'
import { DateUtil, StrUtil, ToastUtil } from '@pura/harmony-utils';
import { WorksDetail, ShowDetail } from '../model/Works'
import { AudioPlayer } from '../utils/AudioPlayer'
import { BusinessError, emitter } from '@kit.BasicServicesKit';
import { MediaService } from '@ohos/hearingplayer/src/main/ets/utils/MediaService';
import { faceDetector } from '@kit.CoreVisionKit';
import {
BreakpointConstants,
CertificateStudyDialog,
CommentInputDialog,
CommonApi,
commonTs,
dateUtil, FollowButton,
paintByPoint,
paintByPoint2X,
PullUpLoadMoreComponents,
replaceLpx2Vp,
ShareDialog,
SoundPlayer,
VipImageView,
WorkTextDubbing} from '@ohos/common'
import { CommonObj, FzWebview } from 'fzlibrary';
import { openBuyVip, openNeedVip, openVipAndBuy, openVipOrBuy } from '@ohos/hearingplayer/src/main/ets/module/Pops';
import { VajraApi } from '@ohos/vajra/src/main/ets/api/VajraApi';
import { CommentModel, reply } from '@ohos/vajra/src/main/ets/model/CommentModel';
import { CommentDetailParams } from './CommentDetailsPage';
import { it } from '@ohos/hypium';
import RdbUtils from '../utils/RdbUtils';
import { relationalStore } from '@kit.ArkData';
import text from '@ohos.graphics.text';
import { NewWorksListModel } from '../model/NewWorksListModel';
import { DubbingSentence } from '@ohos/dubbing/src/main/ets/viewmodel/VideoDetails';
import { getSecond } from '@ohos/common/src/main/ets/utils/TimeFormat';
import { webview } from '@kit.ArkWeb';
import { AbilityConstant, OpenLinkOptions } from '@kit.AbilityKit';
import { format } from '../utils/formart';
import { FZJSExportObjectOfVideo } from '@ohos/dubbing/src/main/ets/utils/FZJSExportObjectOfVideo';
import { ComposeAudioRet } from '@ohos/dubbing/src/main/ets/viewmodel/AudioBean';
import { AlbumApi } from '../api/album';
import { AlbumDetails } from '../model/Album';
import { image } from '@kit.ImageKit';
import { Certificate } from '@ohos/common/src/main/ets/model/CertificateBean';
import { ShareApi } from '@ohos/common/src/main/ets/api/ShareApi';
import { ShareData } from '@ohos/im_chat';
let isKeepScreenOn: boolean = true;
@Extend(Slider)
function videoSlider() {
.trackColor('#ffffff')
.trackThickness(replaceLpx2Vp(5))
.selectedColor('#2ACF6F')
.blockBorderColor('rgba(44, 211, 215, 0.3)')
.blockBorderWidth(replaceLpx2Vp(8))
.blockColor('#2CD3D7')
.blockSize({ width: replaceLpx2Vp(25), height: replaceLpx2Vp(25) })
}
@Extend(Image)
function imageWith(isFull:boolean,breakPoint:string){
.width(isFull ? '18lpx' : breakPoint === BreakpointConstants.BREAKPOINT_MD ? '36lpx' : replaceLpx2Vp(36))
}
interface audioInfo {
id: string,
title: string,
price: string,
vip_price: string,
uid: string,
}
class resultClass {
id: number = 0;
src: ResourceStr = '';
title: string = '';
desc: string = '';
constructor(id: number, src: ResourceStr, title: string, desc: string) {
this.id = id;
this.src = src;
this.title = title;
this.desc = desc
}
}
interface dubbingParams {
srt: string,
courseId: string
dubId: number
resource_type:string
videoSrc:string
}
interface params {
albumId: string,
courseId: string,
index: number,
}
@Builder
export function TabPageBuilder() {
WorksDetailPage()
}
interface userParams {
courseId: string,
uid: string
showId: string
tabIndex: number,
comments: string,
videoTime?:number,
}
interface like_db_model{
id : string
comment_id : string
is_like: boolean
uid:string
}
const PAGE_NAME: string = 'WorksDetailPage';
@Component
export struct WorksDetailPage {
@Consume("pathStack") pathStack: NavPathStack
windowClass = AppStorage.get<window.Window>('windowClass') as window.Window;
difLevelList = ['A', 'A1', 'A2', 'B3', 'B2', 'C1']
@State courseId: string = ''
@StorageLink('isLogin') isLogin: boolean = false;
@State dubId: number = -1;
@State srt: string = ''
@State tabsIndex: number = 0
@State comments: string = '0'
@State is_support: string = ''
@State supports: number = 0
@State userUid: string = ''
@StorageLink('showId') showId: string = ''
@State courseTitle: string = ''
@State album_id: string = ''
@State courseCreateTime: string = ''
@State score: string = '0'
@State score_show: string = '0'
@State accuracy: string = ''
@State fluency: string = ''
@State integrate: string = ''
@State userShowInfo: WorksDetail = {
"uid": '',
"nickname": '',
"avatar": '',
"video": '',
"audio": '',
"pic": '',
"course_title": '',
"user_number": 0,
"create_time": '',
"start_time": '',
"fans": '',
"shows": '',
"dv_type": '',
"talent_status": '',
"is_talent": '',
"dav": '',
"is_vip": '',
"is_svip": '',
"is_free_learn": '',
"is_aisvip": '',
"community_level": 0,
"subtitle_num": '',
"id": '',
"duration": '',
"comments": '0',
"supports": '0',
"is_support": '',
"isSupport": false,
}
@State userShowList: WorksDetail [] = []
@State currentIndex: number = 0
@State avatar: string = ''
@State avatar_frame:string=''
@State is_following: string = '0'
@State is_aisvip: string = '0'
@State is_svip: string = '0'
@State grow_level: string = '0'
@State nickname: string = ''
@State collect_id:string=''
@State collect_num:string=''
audioPlayer: AudioPlayer = new AudioPlayer();
@StorageLink('breakPoint') breakPoint: string = BreakpointConstants.BREAKPOINT_MD
@State audioType: boolean = false
@State @Watch('onVideoPlayChange') videoPlay: boolean = true
@State start:number=0
onVideoPlayChange(){
if (this.videoPlay) {
window.getLastWindow(getContext(this)).then((data) =>{
data.setWindowKeepScreenOn
(true)})
}else {
window.getLastWindow(getContext(this)).then((data) =>{
data.setWindowKeepScreenOn
(false)})
}
}
@State detailsId: string = ''
@State sateVideoState: boolean = false
@State showController: boolean = true
@State vol: number = 0
private xcomponentController: XComponentController = new XComponentController();
@LocalStorageLink('sessionManager') sessionManager: MediaService | null = null
@State surfaceID: string = '';
@State isBestShow: number = 0;
@State videoSrc: string = ''
@State b_id: string = ''
@State audioSrc: string = ''
@State videoCover: string = ''
@State is_needbuy: string = ''
@State isFull: boolean = false;
@State PCFull: boolean = false
@State isNeedVip: boolean = false
@State is_NeedBuy: boolean = false
@StorageProp('isVip') isVip: string = '';
@StorageProp('isSVip') isSVip: string = '';
@State share_desc: string = ''
@StorageProp('resource_type') resource_type:string=''
@State b_title:string=''
@State duration:string=''
@State bg_pic:string=''
@State album_sort: number = 0
@State share_url: string = ''
@State share_title: string = ''
@State videoProportion: number = 0
@State videoHeight: number = 1
@State videoWidth: number = 1
@State windowHeight: number = 1
@State windowWidth: number = 1
@State @Watch('setVideoTime') videoTime: number = 0
@State continueTime:number = 0
@State endTime: number = 0
@State detailsVip: string = ''
@State isNeedBuy: string = ''
@Provide currentTime: number = 0;
@State audioTime: number = 0
@State dubbingSentences: DubbingSentence[] = [];
@State isShowNewWorksList: boolean = false
@State show_listData: NewWorksListModel[] = []
@State chinese:string=''
@State english:string=''
@State avPlayer: media.AVPlayer | null = null;
@StorageLink('certificateList')@Watch('certificateChange') certificateList: Certificate = new Certificate()
@State isShow:boolean=false
@State albumInfo: AlbumDetails = {
id: '',
pic: '',
album_title: '',
resource_desc: '',
sub_title: '',
course_num: '',
tag: '',
category_id: '',
dif_level: '',
is_vip: '',
album_isbuy: '',
description: '',
album_vip_price: '',
is_needbuy: '',
album_price: '',
resource_type: '',
"learn_plan_id": 0,
"learn_plan_course_id": 0,
"learn_plan_finish": 0,
}
private tabsController: TabsController = new TabsController()
controller: webview.WebviewController = new webview.WebviewController();
fzJSExportObject: FZJSExportObjectOfVideo = new FZJSExportObjectOfVideo(true)
@State isHide:boolean=false
@State collect:boolean=false
setVideoTime(){
if (this.routerParam) {
this.routerParam.videoTime = this.videoTime
}
}
getTime(){
if (this.resource_type=='5'){
this.dubbingSentences.forEach((item: DubbingSentence, index)=>{
if (this.videoTime>=item.start && this.videoTime<=item.end) {
console.log('当前时长--'+this.videoTime+'当句开始--'+item.start+'当句结束--'+item.end)
this.chinese=item.captionZh
this.english=item.caption
}
})
}else{
if (Number(this.videoTime/1000)>=Number(this.duration)) {
this.avPlayer?.seek(0)
}
}
}
openVipPage() {
if (this.isLogin === false) {
this.pathStack.pushPathByName('LoginPage', {
"from": 'WorksDetailPage'
} as CommonObj)
} else {
this.pathStack.pushPathByName('fakeTab', 'listen', () => {
this.GetUserShowInfo()
})
}
}
VipPricePage() {
if (this.isLogin === false) {
this.pathStack.pushPathByName('LoginPage', {
"from": 'WorksDetailPage'
} as CommonObj)
} else {
const uid = AppStorage.get<string>('uid');
this.pathStack.pushPathByName('PayPage', {
id: this.courseId,
title:this.albumInfo.album_title,
price: this.albumInfo.album_price?.toString(),
vip_price: this.albumInfo.album_vip_price,
uid: uid
} as audioInfo)
}
}
pricePage() {
if (this.isLogin === false) {
this.pathStack.pushPathByName('LoginPage', {
"from": 'WorksDetailPage'
} as CommonObj)
} else {
const uid = AppStorage.get<string>('uid');
this.pathStack.pushPathByName('PayPage', {
id: this.albumInfo.id,
title: this.albumInfo.album_title,
price: this.albumInfo.album_price?.toString(),
vip_price: this.albumInfo.album_vip_price?.toString(),
uid: uid
} as audioInfo, () => {
this.GetUserShowInfo()
})
}
}
private hearingopenNeedVipPop: CustomDialogController = new CustomDialogController({
builder: openNeedVip({
openVip: () => {
this.openVipPage()
}
}),
alignment: DialogAlignment.Center,
customStyle: true,
cornerRadius: 10,
isModal: true
});
private hearingVipOrBuyPop: CustomDialogController = new CustomDialogController({
builder: openVipOrBuy({
data: this.albumInfo.album_price,
price: () => {
this.pricePage()
},
openVip: () => {
this.openVipPage()
}
}),
alignment: DialogAlignment.Center,
customStyle: true,
cornerRadius: 10,
isModal: true
});
private hearingVipAndBuyPop: CustomDialogController = new CustomDialogController({
builder: openVipAndBuy({
dataPrice: this.albumInfo.album_price,
dataVipPrice: this.albumInfo.album_vip_price,
price: () => {
this.pricePage()
},
openVip: () => {
this.openVipPage()
}
}),
alignment: DialogAlignment.Center,
customStyle: true,
cornerRadius: 10,
isModal: true
});
private hearingopenBuyVipPop: CustomDialogController = new CustomDialogController({
builder: openBuyVip({
dataPrice: this.isSVip=='1'?this.albumInfo.album_vip_price:this.albumInfo.album_price,
price: () => {
this.VipPricePage()
},
}),
alignment: DialogAlignment.Center,
customStyle: true,
cornerRadius: 10,
isModal: true
});
certificateStudyDialog: CustomDialogController | null = new CustomDialogController({
builder: CertificateStudyDialog({
current_plan_certificateList: this.certificateList
}),
maskColor: 'rgba(0, 0, 0, 0.70)',
alignment: DialogAlignment.Center,
customStyle: true,
})
certificateChange(){
if (this.isShow){
if (this.certificateList!=undefined ) {
if (this.certificateList.is_look =='0'){
if (this.certificateStudyDialog!=null) {
this.certificateStudyDialog.open()
SoundPlayer.getSoundPlayer().play('winning_bg_all.wav')
}
}
}
}
}
private setAVPlayerCallback(avPlayer: media.AVPlayer) {
avPlayer.on('startRenderFrame', () => {
console.info(`AVPlayer start render frame`);
})
avPlayer.on('volumeChange', (vol: number) => {
this.vol = vol
})
avPlayer.on('seekDone', async (seekDoneTime: number) => {
console.info('zzz=== avPlayer seek操作', ` seek time is ${seekDoneTime}`);
let ap = await this.audioPlayer.getPlayer();
console.info('zzz=== avPlayer seek操作', ` 当前ap状态:${ap?.state}`);
if (ap?.state == 'paused' || ap?.state == 'playing' || ap?.state == 'prepared') {
ap?.seek(seekDoneTime);
}
})
avPlayer.on('error', (err: BusinessError) => {
console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
avPlayer.reset();
emitter.emit('VedioError')
})
avPlayer.on('timeUpdate', async (time: number) => {
this.videoTime = time
this.getTime()
let ap = await this.audioPlayer.getPlayer();
if (ap) {
let difference = (ap.currentTime > (time + 300) || ap.currentTime < (time - 300))
if (difference && (ap?.state == 'paused' || ap?.state == 'playing' || ap?.state == 'prepared')) {
console.log('zzz=== avPlayer时间校准', `当前视频时间:${time} ;当前音频时间 ${ap.currentTime}`)
ap.seek(time);
}
}
})
avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
console.log('zzz=== avPlayer状态变化', `当前状态${state}`)
switch (state) {
case 'idle':
console.log('AVPlayer状态机 state idle called.');
let ap = await this.audioPlayer.getPlayer()
ap?.reset()
break;
case 'initialized':
console.log('AVPlayer状态机 state initialized called.');
avPlayer.url
if (this.resource_type!='5') {
avPlayer.surfaceId = this.surfaceID;
}
emitter.emit('VideoInitialized')
avPlayer.prepare();
break;
case 'prepared':
console.log('AVPlayer状态机 state prepared called.');
this.endTime = avPlayer.duration
avPlayer.videoScaleType = 1
avPlayer.setVolume(0)
this.listenVieoVoice()
if (this.continueTime) {
avPlayer.seek(this.continueTime)
this.continueTime = 0
}
this.doPlay()
break;
case 'playing':
console.log('AVPlayer状态机 state playing called.');
this.videoPlay = true
emitter.emit('VideoPlaying')
break;
case 'paused':
console.log('AVPlayer状态机 state paused called.');
this.videoPlay = false
emitter.emit('VideoPaused')
break;
case 'completed':
console.log('AVPlayer状态机 state completed called.');
this.videoPlay = false
emitter.emit('VideoCompleted')
break;
case 'stopped':
console.log('AVPlayer状态机 state stopped called.');
avPlayer.reset();
emitter.emit('VideoStopped')
break;
case 'released':
console.log('AVPlayer状态机 state released called.');
break;
default:
console.info('AVPlayer state unknown called.');
break;
}
})
avPlayer.on('videoSizeChange', (width: number, height: number) => {
console.log('myTag', '获取视频比例')
this.videoProportion = (height / width)
})
}
private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
@StorageLink('ContinueRouterParams') continueRouterParams:CommonObj = {}
setContinueStateParams() {
if (!this.continueRouterParams) {
this.continueRouterParams = {}
}
this.continueRouterParams.uid = AppStorage.get('uid')
this.continueRouterParams.name = PAGE_NAME
let courseInfo: userParams[]
if (this.courseInfo.length == 0) {
this.courseInfo = this.pathStack.getParamByName(PAGE_NAME) as userParams[]
}
courseInfo = this.courseInfo
this.routerParam = courseInfo[courseInfo.length-1]
this.continueRouterParams.params = courseInfo
}
openContinueState(){
this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
console.info('setMissionContinueState ACTIVE result: ', JSON.stringify(result));
});
}
closeContinueState(){
this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE, (result) => {
console.info(`setMissionContinueState: ${JSON.stringify(result)}`);
});
}
@State courseInfo:userParams[] = []
@State routerParam:userParams|undefined = undefined
async aboutToAppear() {
let courseInfo: userParams [] = this.pathStack.getParamByName('WorksDetailPage') as userParams[]
this.routerParam = courseInfo[courseInfo.length-1]
if (this.routerParam.videoTime) {
this.continueTime = this.routerParam.videoTime
}
console.log('courseInfo', JSON.stringify(courseInfo))
this.courseId = courseInfo[courseInfo.length - 1].courseId
this.userUid = courseInfo[courseInfo.length - 1].uid
this.showId = courseInfo[courseInfo.length - 1].showId
this.tabsIndex = courseInfo[courseInfo.length - 1].tabIndex || 0
this.comments = courseInfo[courseInfo.length - 1].comments
await this.bindAudio()
this.avPlayer = await media.createAVPlayer();
this.setAVPlayerCallback(this.avPlayer);
this.show_list()
await this.GetUserShowInfo()
if(Number(this.courseId)>0) {
await this.GetCourseInfo()
}
await this.detailsSupport()
this.windowClass.on('windowSizeChange', (size) => {
let viewWidth = px2vp(size.width);
let viewHeight = px2vp(size.height);
if (viewWidth > viewHeight) {
} else {
}
});
if (this.showController == true) {
setTimeout(() => {
this.changeController()
}, 2000)
}
this.tabsController.preloadItems([0,1])
try {
if (!this.continueRouterParams) {
this.continueRouterParams = {}
}
this.continueRouterParams.name = PAGE_NAME
this.continueRouterParams.params = this.pathStack.getParamByName(PAGE_NAME)
this.setContinueStateParams()
this.openContinueState()
}catch (e) {
}
}
async aboutToDisappear() {
if (this.breakPoint!='xl'){
this.setOrientation(window.Orientation.PORTRAIT)
}
this.avPlayer?.release()
let ap = await this.audioPlayer.getPlayer()
ap?.release()
this.onWindowSizeChange()
}
onWindowSizeChange(){
this.windowClass.on('windowSizeChange', (size) => {
let viewWidth = px2vp(size.width);
let viewHeight = px2vp(size.height);
this.videoWidth = viewWidth
if (viewWidth > viewHeight) {
console.log('横屏viewHeight', viewHeight)
} else {
console.log('竖屏viewHeight', viewHeight)
}
});
}
async onPageShow() {
this.sessionManager?.release()
this.sessionManager = null
this.setContinueStateParams()
}
async onPageHide() {
console.log('页面隐藏')
this.doPause()
if (this.breakPoint!='xl'){
this.setOrientation(window.Orientation.PORTRAIT)
}
}
_reqSrtText(srt: string) {
return new Promise<DubbingSentence[]>((resolve: Function, reject: Function) => {
CommonApi.GetSrt(srt)
.then((result: string) => {
const ret = result
console.log('zzz=== 获取字幕完整字段', ret)
let dubbing = ret.split(/\n\s*\n/).filter(item => item != "").map((item, index) => {
console.log('zzz=== 获取字幕单句字段', item)
let textItem = item.split(/\n/)
console.info('get srt result:', textItem);
let timeStr = textItem[1].replace(/\s/, "").split("-->")
let savedAddress = `${index}`
if (textItem.length==4) {
return new DubbingSentence(index, textItem[2], textItem[3], getSecond(timeStr[0]), getSecond(timeStr[1]),
savedAddress)
}else{
return new DubbingSentence(index, '', textItem[2], getSecond(timeStr[0]), getSecond(timeStr[1]),
savedAddress)
}
})
resolve(dubbing)
})
})
}
setOrientation(orientation: number) {
window.getLastWindow(getContext(this)).then((win) => {
win.setPreferredOrientation(orientation).then((data) => {
console.log('setWindowOrientation: ' + orientation + ' Succeeded. Data: ' + JSON.stringify(data));
}).catch((err: string) => {
console.log('setWindowOrientation: Failed. Cause: ' + JSON.stringify(err));
});
}).catch((err: string) => {
console.log('setWindowOrientation: Failed to obtain the top window. Cause: ' + JSON.stringify(err));
});
}
async GetUserShowInfo() {
await WorkApi.getUserShowInfo(this.showId).then(async (res) => {
this.isHide=res.is_hide=='1'?true:false
this.is_needbuy = res.is_needbuy
this.videoSrc = res.course_video
this.duration=res.duration
this.resource_type='1'
this.share_desc = res.share_desc
this.album_sort = Number(res.album_sort)
this.share_title = res.share_title
this.detailsVip = res.is_vip
this.isNeedBuy = res.is_needbuy
this.share_url = res.share_url
console.log('首页点击work---------' + this.isNeedBuy + "-----" + this.detailsVip)
this.detailsId = res.id
this.audioSrc = res.audio
this.videoCover = res.pic
this.srt = res.subtitle_en
this.courseTitle = res.course_title
this.album_id = res.album_id
this.courseCreateTime = res.create_time
this.courseId = res.course_id
this.nickname = res.nickname
this.avatar = res.avatar
this.avatar_frame=res.avatar_frame
this.isBestShow = res.is_best_show
this.comments = res.comments
this.is_following = res.is_following
this.supports = res.supports
this.userUid = res.uid
this.is_aisvip = res.is_aisvip
this.is_svip = res.is_svip
this.grow_level = res.grow_level
this.collect_id=res.collect_id
this.collect_num=res.collects
this.collect=this.collect_id?true:false
this.score = res.score
this.score_show = res.score_show
this.accuracy = res.accuracy
this.fluency = res.fluency
this.integrate = res.integrate
if (res.resource_type=='2'){
this.resource_type=res.resource_type
this.videoSrc = res.embed_video_url
this.audioSrc = res.audio
this.b_id = res.b_id
this.b_title=res.course_title
}else if (res.resource_type=='5'){
this.resource_type=res.resource_type
this.duration=res.duration
this.bg_pic=res.bg_pic
this.videoSrc=res.original_audio
this.dubbingSentences= await this._reqSrtText(res.subtitle_en)
}
setTimeout(()=>{
if (this.avPlayer) {
this.avPlayer.url = this.videoSrc
}
this.audioPlayer.setUrl(this.audioSrc)
this.audioPlayer.avPlayerLive()
},300)
this.getDubid()
if (res.is_needbuy=='1') {
this.albumDetails(res.album_id)
}
}).catch((msg: string) => {
ToastUtil.showToast(msg);
})
}
albumDetails(albumId:string){
AlbumApi.getAlbumDetails(albumId, 0).then((res) => {
console.log('albumList', res)
this.albumInfo = res
let listenByBuy = this.albumInfo.album_isbuy == '1';
let listenByVip = this.albumInfo.is_vip == '2' && this.isSVip == '1';
this.is_NeedBuy = Number(this.albumInfo.album_price??0) != 0 && !(listenByBuy || listenByVip);
this.isNeedVip = this.albumInfo.is_vip == '2' && !(this.isSVip == '1' || listenByBuy);
}).catch((msg: string) => {
ToastUtil.showToast(msg);
})
}
async GetCourseByIndex(index: number) {
let data = this.userShowList[index]
this.showId = data.id
await this.GetUserShowInfo()
console.log('showid---------'+this.showId+'---------'+index)
}
async ToPlayShow(data: WorksDetail) {
this.videoSrc = data.video
if (this.avPlayer) {
this.avPlayer.url = this.videoSrc
}
this.audioSrc = data.audio
this.audioPlayer.setUrl(this.audioSrc)
this.audioPlayer.avPlayerLive()
this.videoCover = data.pic
this.courseTitle = data.course_title
this.courseCreateTime = data.create_time
this.nickname = data.nickname
this.avatar = data.avatar
this.is_aisvip = data.is_aisvip
this.is_svip = data.is_svip
}
async detailsSupport() {
WorkApi.detailsSupport(this.showId).then((res) => {
this.is_support = res.is_support
console.log('是否点赞--------' + this.is_support)
})
}
async GetCourseInfo() {
WorkApi.getUserShowList(this.userUid, this.courseId,this.start,10).then((res) => {
if (this.userShowList.length==0) {
this.userShowList = res
}else {
this.userShowList=[...this.userShowList,...res]
}
}).catch((msg: string) => {
ToastUtil.showToast(msg);
})
}
async sliderOnchange(value: number, mode: SliderChangeMode) {
}
async doPlay() {
let ap = await this.audioPlayer.getPlayer()
let mp = this.avPlayer
console.log('zzz=== doPlay方法', `mp状态:${mp?.state};ap状态:${ap?.state}`)
if (ap?.state == mp?.state && (mp?.state == "prepared" || mp?.state == "paused" || mp?.state == "completed")) {
mp.play()
ap?.play()
} else {
}
}
async doPause() {
let ap = await this.audioPlayer.getPlayer()
let mp = this.avPlayer
console.log('zzz=== doPause方法', `mp状态:${mp?.state};ap状态:${ap?.state}`)
if (ap?.state == 'playing') {
ap?.pause()
}
if (mp?.state == 'playing') {
mp.pause()
}
}
changeController() {
animateTo({ duration: 600 }, () => {
this.showController = !this.showController
})
}
async listenVieoVoice() {
let mp = this.avPlayer
let ap = await this.audioPlayer.getPlayer()
if (ap?.state == mp?.state &&
(mp?.state == "prepared" || mp?.state == "playing" || mp?.state == "paused" || mp?.state == "completed")) {
this.audioType = !this.audioType
mp?.setVolume(this.audioType ? 0 : 1)
ap?.setVolume(!this.audioType ? 0 : 1)
console.log('设置音频音量')
}
}
async bindAudio() {
let ap = await this.audioPlayer.getPlayer()
if (ap) {
ap.on('volumeChange', (vol: number) => {
this.vol = vol
})
}
emitter.on('AudioPrepared', () => {
if (this.isShow) {
this.listenVieoVoice()
this.doPlay()
}
})
emitter.on('VideoPaused', () => {
ap?.pause()
})
emitter.on('completed', () => {
})
emitter.on('VedioError', () => {
ap?.reset()
})
emitter.on('idle', () => {
console.log('视频初始化')
ap?.reset()
})
}
VideoSize(height: number, width: number) {
console.log('视频比例调整-宽', width, '视频比例调整-高',height)
console.log('计算高度', width * 9/16)
let countHeight = width * 9/16
if (this.isFull) {
this.videoWidth = width - 150
} else {
this.videoWidth = width
}
if(this.PCFull) {
setTimeout(()=>{
this.videoHeight = countHeight
},100)
} else {
let normalHeight = this.videoWidth * 9/16
this.videoHeight = normalHeight
}
}
horVerSwitch() {
let context = getContext(this) as common.UIAbilityContext;
window.getLastWindow(context).then((lastWindow) => {
lastWindow.setPreferredOrientation(this.isFull ? window.Orientation.PORTRAIT : window.Orientation.LANDSCAPE)
this.isFull = !this.isFull
})
}
getDubid() {
emitter.on('queryDubIdSuccess', (data) => {
let id: number = (data.data)?.['id'];
this.dubId = id
})
emitter.emit('queryDubIdByCourseId', { data: { courseId: Number(this.courseId) } })
}
show_list(){
WorkApi.show_list(this.courseId, 0, 5).then((res: NewWorksListModel[]) => {
this.show_listData = res;
})
}
openBilibiliApp(){
const link = `https:
let context = getContext(this) as common.UIAbilityContext;
let openLinkOptions: OpenLinkOptions = {
appLinkingOnly: true,
};
try {
context.openLink(link, openLinkOptions, (err, result) => {
console.log(err+''+result)
}
).then(() => {
console.log('跳转成功')
}).catch((err: BusinessError) => {
console.log('err')
ToastUtil.showToast('未安装哔哩哔哩,请安装后重试');
});
}
catch (e) {
}
}
private injectPlayScript() {
try {
const jsCode = "(function() { " +
"var videos = document.getElementsByTagName('audio');" +
" for(var i=0;i<videos.length;i++){videos[i].play();} return videos})()";
this.controller.runJavaScript(jsCode).then((result) => {
console.log('result: ' + result);
const basePlayedJsInjection =
"window.player.on("%s",function(){window.player.playerPromise.then(function(player){" +
"window.fZJSExportObject.jsCallback(JSON.stringify({action: 51,content: JSON.stringify({isPlaying: %s})}))})})"
const endedJs = format(basePlayedJsInjection, "video_media_ended", false);
const pauseJs = format(basePlayedJsInjection, "video_media_pause", false);
const playJs = format(basePlayedJsInjection, "video_media_play", true);
this.controller.runJavaScript(endedJs)
this.controller.runJavaScript(pauseJs)
this.controller.runJavaScript(playJs)
this.controller.runJavaScript("window.player.play()").then(res => {
setTimeout(()=>{
this.controller.runJavaScript("window.player.seek(0.1)")
},500)
})
.catch((error: BusinessError) => {
console.error("error: " + error);
})
})
.catch((error: BusinessError) => {
console.error("error: " + error);
})
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
}
openDubbing(){
console.log('去配音页面')
console.log('去配音页面 ' + this.courseId)
if (this.isLogin) {
if (this.isNeedVip && this.is_NeedBuy) {
this.hearingVipOrBuyPop.open()
} else if (this.isNeedVip && !this.is_NeedBuy) {
this.hearingopenNeedVipPop.open()
} else if (this.is_NeedBuy == true && this.isSVip != '1') {
this.hearingVipAndBuyPop.open()
} else if (this.is_NeedBuy == true && this.isSVip == '1') {
this.hearingopenBuyVipPop.open()
} else{
this.avPlayer?.pause()
this.pathStack.pushPathByName('QPYDubbing', {courseId: this.courseId, srt: this.srt,dubId:this.dubId,resource_type:this.resource_type,videoSrc:this.videoSrc} as dubbingParams,
(data) => {
if (data.info.param) {
let param = data.info.param as dubbingParams
this.dubId = param.dubId
}
})
}
} else {
this.pathStack.replacePathByName('LoginPage', {
"from": 'WorksDetailPage'
} as CommonObj)
}
}
doBack(){
this.pathStack.pop({})
this.closeContinueState()
}
build() {
NavDestination() {
Flex({justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center,wrap: FlexWrap.Wrap}) {
Column() {
Stack({ alignContent: Alignment.Top }) {
if (this.resource_type=='2'){
Column(){
Row(){
Stack(){
Text(this.b_title)
.width('100%')
.fontSize(replaceLpx2Vp(36))
.fontColor('#333333')
.textAlign(TextAlign.Center)
.fontWeight(500)
Row(){
Image($r('app.media.ic_close')).width(replaceLpx2Vp(45)).height(replaceLpx2Vp(45)).margin({left:replaceLpx2Vp(34)})
.onClick(()=>{
this.doBack()
})
}.width('100%')
}.width('100%')
}.height(replaceLpx2Vp(88)).width('100%').padding({right:replaceLpx2Vp(35)})
Row(){
Image($r('app.media.ic_bilibili')).height(replaceLpx2Vp(58)).margin({left:replaceLpx2Vp(34),top:replaceLpx2Vp(13),bottom:replaceLpx2Vp(16)})
Blank().layoutWeight(1)
}.height(replaceLpx2Vp(88)).width('100%')
Web({src:this.resource_type=='2'?this.videoSrc:'',controller: this.controller })
.onControllerAttached(() => {
if (this.fzJSExportObject) {
this.controller.registerJavaScriptProxy(this.fzJSExportObject.getInstance(this.pathStack,
this.controller), "fZJSExportObject", ["jsCallback"])
}
})
.onPageEnd(() => {
this.injectPlayScript();
})
.width('100%').zIndex(11).height(this.breakPoint===BreakpointConstants.BREAKPOINT_XL?replaceLpx2Vp(622):replaceLpx2Vp(422))
}.backgroundColor('#ffffff')
.width('100%').height(replaceLpx2Vp(510))
}else if (this.resource_type=='5') {
Stack(){
WorkTextDubbing({avPlayer: this.avPlayer, windowHeight:replaceLpx2Vp(422).toString(),bg_pic:this.bg_pic,
subtitle_en:this.srt, endTime:Number(this.duration),chinese:this.chinese,english:this.english,videoTime:this.videoTime})
Column(){
Image($r('app.media.ic_arrow_left_02')).width(replaceLpx2Vp(45)).height(replaceLpx2Vp(45)).margin({top:replaceLpx2Vp(24),left:replaceLpx2Vp(24)})
.onClick(()=>{
this.doBack()
})
Blank().layoutWeight(1)
}.width('100%').height(replaceLpx2Vp(422)).alignItems(HorizontalAlign.Start)
}.width('100%').height(replaceLpx2Vp(422))
}else {
XComponent({ type: XComponentType.SURFACE, controller: this.xcomponentController })
.onLoad(async e => {
this.surfaceID = this.xcomponentController.getXComponentSurfaceId()
})
.height(this.isFull ? '100%' : this.videoHeight)
.width(this.videoWidth)
.onClick(() => {
animateTo({ duration: 600 }, () => {
if (this.showController == true) {
return
}
this.showController = !this.showController
setTimeout(() => {
this.changeController()
}, 2000)
})
})
if (this.showController) {
Row() {
Image($r('app.media.ic_arrow_left_02'))
.width(this.isFull ? '22lpx' : this.breakPoint === BreakpointConstants.BREAKPOINT_MD ? '44lpx' : replaceLpx2Vp(44))
.onClick(() => {
if(this.breakPoint==BreakpointConstants.BREAKPOINT_XL) {
if(this.PCFull) {
animateTo({ duration: 500 }, () => {
this.PCFull = !this.PCFull
})
} else {
this.doBack()
}
} else {
if (this.isFull) {
this.isFull = !this.isFull
if (this.breakPoint!='xl'){
this.setOrientation(window.Orientation.PORTRAIT)
}
} else {
this.doBack()
}
}
})
}.padding({ left: replaceLpx2Vp(25), right: replaceLpx2Vp(25), top: replaceLpx2Vp(25) }).width('100%')
.transition({ type: TransitionType.Delete, opacity: 0 })
Row() {
if (this.videoPlay) {
Image($r('app.media.ic_play_on'))
.imageWith(this.isFull,this.breakPoint)
.margin({ right: replaceLpx2Vp(24) }).onClick(() => {
this.videoPlay = false
this.doPause()
})
} else {
Image($r('app.media.ic_play_off'))
.imageWith(this.isFull,this.breakPoint)
.margin({ right: replaceLpx2Vp(24) }).onClick(() => {
this.videoPlay = true
this.doPlay()
})
}
Text(commonTs.GetTimeString(this.videoTime)).fontColor(Color.White)
.fontSize(this.isFull ? '16lpx' :
this.breakPoint === BreakpointConstants.BREAKPOINT_MD ? '22lpx' : replaceLpx2Vp(22))
Stack() {
Slider({
value: this.videoTime,
min: 0,
max: this.endTime,
step: 0.01,
})
.width(this.videoWidth-135)
.videoSlider()
.onChange((value: number, mode: SliderChangeMode) => {
if (mode == 0) {
console.log('zzz=== 进度条点击事件')
this.sateVideoState = this.videoPlay
this.doPause()
}
if (mode == 1) {
this.avPlayer?.seek(value, media.SeekMode.SEEK_CLOSEST)
}
if (mode == 2) {
console.log('zzz=== 进度条松开事件')
this.avPlayer?.seek(value, media.SeekMode.SEEK_CLOSEST)
if (this.sateVideoState) {
this.doPlay()
}
}
})
}
Text(commonTs.GetTimeString(this.endTime)).fontColor(Color.White)
.fontSize(this.isFull ? '16lpx' :
this.breakPoint === BreakpointConstants.BREAKPOINT_MD ? '22lpx' : replaceLpx2Vp(22))
Image($r('app.media.ic_full_screen'))
.imageWith(this.isFull,this.breakPoint)
.margin({ left: replaceLpx2Vp(24) })
.onClick(() => {
console.log('全屏事件')
if (this.avPlayer) {
if(this.breakPoint == BreakpointConstants.BREAKPOINT_MD) {
this.isFull = !this.isFull
if (this.isFull) {
this.setOrientation(window.Orientation.LANDSCAPE)
} else {
if (this.breakPoint!='xl'){
this.setOrientation(window.Orientation.PORTRAIT)
}
}
} else {
animateTo({ duration: 500 }, () => {
this.PCFull = !this.PCFull
})
}
}
})
}
.width('100%')
.height(this.isFull ? '34lpx' :
this.breakPoint === BreakpointConstants.BREAKPOINT_MD ? '68lpx' : replaceLpx2Vp(68))
.position({ bottom: 0, left: 0 })
.backgroundColor('rgba(23,23,26,0.8)')
.alignItems(VerticalAlign.Center)
.padding({ left: replaceLpx2Vp(24), right: replaceLpx2Vp(24) })
.justifyContent(FlexAlign.SpaceBetween)
.transition({ type: TransitionType.Insert, opacity: 0 })
.transition({ type: TransitionType.Delete, opacity: 0 })
}
}
}
.constraintSize(this.isFull ? {} : {
minHeight: this.windowWidth / 4
})
.width('100%')
.zIndex(1)
.backgroundColor('#333333')
.height(this.isFull ? '100%' :
this.breakPoint === BreakpointConstants.BREAKPOINT_XL ? this.videoHeight : this.videoHeight)
.onAreaChange((oldValue: Area, newValue: Area) => {
if (this.isFull == false) {
this.VideoSize(Number(newValue.height), Number(newValue.width))
}else {
if (!this.PCFull) {
this.VideoSize(Number(newValue.height), Number(newValue.width))
}
}
})
.clip(true)
}.width(this.breakPoint == BreakpointConstants.BREAKPOINT_MD ? '100%' : this.breakPoint == BreakpointConstants.BREAKPOINT_XL ? this.PCFull? '100%' : '60%' : this.PCFull? '100%' : '49%').zIndex(900)
.height(this.breakPoint == BreakpointConstants.BREAKPOINT_MD ? '' : '100%')
}
.width('100%')
.height('100%')
.backgroundColor(Color.White)
}
.hideTitleBar(true)
.onKeyEvent((e)=>{
if (e.keyText=='KEYCODE_ESCAPE') {
this.avPlayer?.release()
this.doBack()
}
})
.onWillShow(() => {
this.onPageShow()
this.isShow=true
})
.onHidden(() => {
this.isShow=false
this.onPageHide()
})
.onReady((context: NavDestinationContext) => {
})
.onBackPressed(() => {
this.closeContinueState()
this.doBack()
return true
})
}
Support() {
if (this.is_support == '0') {
WorkApi.getSupport(this.showId, this.detailsId, '1').then(() => {
this.is_support = '1'
this.supports ++
})
} else {
WorkApi.getSupport(this.showId, this.detailsId, '0').then(() => {
this.is_support = '0'
this.supports --
})
}
}
}
@Component
struct IconAndCount{
@Prop count: string | number = ''
@Prop icon: string | Resource = ''
build() {
Row() {
Image(this.icon)
.width(replaceLpx2Vp(24))
.height(replaceLpx2Vp(24))
.margin({ right: replaceLpx2Vp(5) })
Text(this.count + '')
.fontSize(11)
.fontColor('#ffaeaeae')
}
}
}
import { AVPlayerDemo, MediaType } from './AVPlayer';
export class AudioPlayer extends AVPlayerDemo {
constructor() {
super(MediaType.Audio);
}
}
import { media } from '@kit.MediaKit';
import { BusinessError } from '@kit.BasicServicesKit';
import emitter from '@ohos.events.emitter';
import { common } from '@kit.AbilityKit';
import { fileIo as fs } from '@kit.CoreFileKit';
export enum MediaType {
Video,
Audio
}
export class AVPlayerDemo {
private surfaceID: string | null = null;
private fileSize: number = -1;
private fd: number = 0;
private url: string = '';
private type: MediaType = MediaType.Video
private player: media.AVPlayer | null = null;
private promiseA: Promise<void> | null = null;
private isSeek: boolean = true;
private context = getContext(this) as common.UIAbilityContext;
private filesDir = this.context.filesDir;
private file = fs.openSync(this.filesDir + '/02.m4a', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
constructor(type: MediaType) {
this.type = type ?? this.type;
this.initPlayer();
}
async initPlayer() {
if (!this.promiseA) {
this.promiseA = new Promise(async (resolve, reject) => {
this.player = await media.createAVPlayer();
resolve()
})
}
return this.promiseA;
}
async reset() {
let avPlayer: media.AVPlayer = (await this.getPlayer())!;
avPlayer.reset();
}
async getPlayer() {
await this.initPlayer();
return this.player;
}
setType(value: MediaType) {
this.type = value;
}
setUrl(value: string) {
this.url = value
}
setSurfaceID(value: string) {
this.surfaceID = value
}
setAVPlayerCallback(avPlayer: media.AVPlayer) {
avPlayer.on('startRenderFrame', () => {
console.info(`AVPlayer start render frame`);
})
avPlayer.on('error', (err: BusinessError) => {
console.error(`调用avPlayer失败, code is ${err.code}, message is ${err.message},mod:${this.type}`);
avPlayer.reset();
})
avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
switch (state) {
case 'idle':
console.info('zzz===, AVPlayer state idle called.');
if (this.file.fd) {
fs.close(this.file.fd)
}
break;
case 'initialized':
console.info('zzz===, AVPlayer state initialized called.');
if (this.type === MediaType.Video && this.surfaceID != null) {
avPlayer.surfaceId = this.surfaceID;
}
avPlayer.prepare();
break;
case 'prepared':
if (this.type === MediaType.Video) {
let data: emitter.EventData = {
data: {
duration: avPlayer.duration
}
}
emitter.emit('MediaPrepared', data);
}
if (this.type === MediaType.Audio) {
emitter.emit('AudioPrepared');
}
console.log(`@@@, duration: ${avPlayer.duration}`)
break;
case 'playing':
console.info('@@@, AVPlayer state playing called.');
if (this.type === MediaType.Video) {
emitter.emit('IsPlaying');
}
if (this.type === MediaType.Audio) {
emitter.emit('IsAudioPlaying');
}
break;
case 'paused':
console.info('@@@, AVPlayer state paused called.');
if (this.type === MediaType.Video) {
emitter.emit('IsPaused');
}
break;
case 'completed':
console.info('@@@, AVPlayer state completed called.');
console.log('zzz=== 配音单播放结束')
emitter.emit('IsCompleted');
break;
case 'stopped':
console.info('@@@, AVPlayer state stopped called.');
avPlayer.release();
break;
case 'released':
console.info('@@@, AVPlayer state released called.');
break;
default:
console.info('@@@, AVPlayer state unknown called.');
break;
}
})
}
async avPlayerLive(): Promise<void> {
let avPlayer: media.AVPlayer = (await this.getPlayer())!;
this.setAVPlayerCallback(avPlayer);
avPlayer.url = this.url
}
async avPlayerUrl(savedAddress:string , setAVPlayerCallback?: (avPlayer: media.AVPlayer) => void) {
await this.getPlayer();
if (setAVPlayerCallback !== undefined) {
setAVPlayerCallback(this.player!)
} else {
this.setAVPlayerCallback(this.player!);
}
let fdPath = 'fd://';
let path = getContext().filesDir + '/' + savedAddress + '.mp3';
if(!fs.accessSync(path)) {
return;
}
this.file = await fs.open(path);
fdPath = fdPath + '' + this.file.fd;
this.isSeek = true;
if(this.player !== null) {
this.player.url = fdPath;
}
}
}
const GetTimeString = (duration: number) => {
let time: number = parseInt((duration / 1000).toString());
let second: number = parseFloat((time % 60).toFixed(0));
let min: number = parseInt((time / 60).toString());
let head = min < 10 ? `${0}${min}` : min;
let end = second < 10 ? `${0}${second}` : second;
let durationStringTime = `${head}:${end}`;
return durationStringTime
}
const FormatSeconds = (seconds: number) => {
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
}