|
|
@@ -48,6 +48,7 @@ import { effectKit } from '@kit.ArkGraphics2D';
|
|
|
import { ColorConversion } from '../common/util/ColorConversion';
|
|
|
import { Lyric, LyricController, LyricParser, LyricView2 } from '@seagazer/cclyric';
|
|
|
import { AvSessionController } from '../controller/AvSessionController';
|
|
|
+import { CastController, CastControllerCallbacks } from '../controller/CastController';
|
|
|
import { repairAudioMetadata, convertDsfToWav, getApiLyric,changeMusicCover} from '../common/util/MusicTagUtils';
|
|
|
import { extractHwMediaMetadata, FFMpegTags,Utility } from '../common/util/Utility';
|
|
|
import { TagsContentCover } from '../view/TagsContentCover';
|
|
|
@@ -70,6 +71,7 @@ import { PlayStatus } from '../common/PlayStatus';
|
|
|
import fs from '@ohos.file.fs';
|
|
|
import { image } from '@kit.ImageKit';
|
|
|
import { AVCastPicker, AVCastPickerState, AVCastPickerStyle, avSession } from '@kit.AVSessionKit';
|
|
|
+import { media } from '@kit.MediaKit';
|
|
|
import { UniversalDetector } from '@ohos/juniversalchardet';
|
|
|
import { SettingPage } from '../pages/SettingPage';
|
|
|
import { secondToTime,getTransverterText } from '../common/util/CommUtils';
|
|
|
@@ -868,16 +870,15 @@ export struct LocalMusic {
|
|
|
|
|
|
let event: Callback<InterruptEvent> = (event) => {
|
|
|
// LogUtils.getInstance().LOGI(`onecold 与其他应用音频冲突 被截断点event: ${JSON.stringify(event)}`);
|
|
|
- console.info(`onecold 与其他应用音频冲突 被截断点event: ${JSON.stringify(event)}`)
|
|
|
+ console.info(`heanup onecold 与其他应用音频冲突 被截断点event: ${JSON.stringify(event)}`)
|
|
|
this.savePlaybackPosition()
|
|
|
if (event.hintType === InterruptHintType.INTERRUPT_HINT_PAUSE) {
|
|
|
this.pause();
|
|
|
} else if (event.hintType === InterruptHintType.INTERRUPT_HINT_RESUME) {
|
|
|
- console.info(`onecold audioInterrupt startPlayOrResumePlay: ${JSON.stringify(event)}`)
|
|
|
- console.info('onecold startPlayOrResumePlay 514')
|
|
|
+ console.info(`heanup onecold audioInterrupt startPlayOrResumePlay: ${JSON.stringify(event)}`)
|
|
|
this.startPlayOrResumePlay();
|
|
|
} else if (event.hintType === InterruptHintType.INTERRUPT_HINT_STOP) {
|
|
|
- console.info(`onecold 与其他应用音频冲突 被截断点 INTERRUPT_HINT_STOP`)
|
|
|
+ console.info(`heanup onecold 与其他应用音频冲突 被截断点 INTERRUPT_HINT_STOP`)
|
|
|
if(!this.isSameTimePlay){
|
|
|
this.stop();
|
|
|
}
|
|
|
@@ -891,7 +892,7 @@ export struct LocalMusic {
|
|
|
if (event.reason === DeviceChangeReason.REASON_NEW_DEVICE_AVAILABLE) { // 音频设备连接
|
|
|
|
|
|
} else if (event.reason === DeviceChangeReason.REASON_OLD_DEVICE_UNAVAILABLE) { // 音频设备断开连接
|
|
|
- console.info('onecold 音频设备断开连接, 暂停播放')
|
|
|
+ console.info('heanup onecold 音频设备断开连接, 暂停播放')
|
|
|
this.pause();
|
|
|
}
|
|
|
}
|
|
|
@@ -901,6 +902,7 @@ export struct LocalMusic {
|
|
|
const eventPause: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAY_PAUSE }
|
|
|
// 监听广播事件(hicar断开暂停播放)
|
|
|
emitter.on(eventPause, (eventData: emitter.EventData) => {
|
|
|
+ console.info('heanup onecold hicar断开暂停播放, 暂停播放')
|
|
|
this.pause();
|
|
|
|
|
|
});
|
|
|
@@ -914,6 +916,7 @@ export struct LocalMusic {
|
|
|
if(deviceChanged.type==1&&
|
|
|
(deviceChanged.deviceDescriptors[0].deviceType== 22||
|
|
|
deviceChanged.deviceDescriptors[0].deviceType==3)){
|
|
|
+ console.info('heanup onecold 有线耳机断开, 暂停播放')
|
|
|
this.pause();
|
|
|
}
|
|
|
});
|
|
|
@@ -9768,6 +9771,9 @@ export struct LocalMusic {
|
|
|
eventHub = getContext().eventHub;
|
|
|
//投播组件
|
|
|
private avSessionController: AvSessionController = AvSessionController.getInstance(false);
|
|
|
+ // 新的投播控制器(封装了官方示例的逻辑)
|
|
|
+ private castControllerWrapper: CastController | undefined = undefined;
|
|
|
+ // 保留原始的 avCastController 引用(用于兼容旧代码)
|
|
|
private castController: avSession.AVCastController | undefined = undefined;
|
|
|
@State isCastPlaying: boolean = false;
|
|
|
@State currentTime2: number = 0;
|
|
|
@@ -9780,6 +9786,7 @@ export struct LocalMusic {
|
|
|
// @State isPlaying: boolean = false;
|
|
|
private castSeek: boolean = false;
|
|
|
private castItem: avSession.AVQueueItem | undefined = undefined;
|
|
|
+ private castFile: fs.File | undefined = undefined; // 保持文件描述符引用,避免被垃圾回收
|
|
|
// @State isBgPlayOpen:boolean = true //是否启用后台播放
|
|
|
@State rotateAngle2: number = -9
|
|
|
@StorageLink('imageColor') imageColor: string = 'rgba(0, 0, 2, 1.00)';
|
|
|
@@ -9884,7 +9891,8 @@ export struct LocalMusic {
|
|
|
}
|
|
|
|
|
|
// 2. 尝试从服务器获取歌词 (Navidrome/Jellyfin/Emby)
|
|
|
- if (this.currentSong && (isJellyfinType(this.currentSong.type)|| isEmbyType(this.currentSong.type))) {
|
|
|
+ if (this.currentSong && (isJellyfinType(this.currentSong.type)||isNavidromeType(this.currentSong.type)
|
|
|
+ || isEmbyType(this.currentSong.type))) {
|
|
|
const manager = RemoteDriveManager.getInstance();
|
|
|
const getAccount = (accountId: string) => manager.getWebDavAccountById(accountId);
|
|
|
|
|
|
@@ -9899,6 +9907,9 @@ export struct LocalMusic {
|
|
|
};
|
|
|
|
|
|
let serverLyric = '';
|
|
|
+ if (!serverLyric && isNavidromeType(songData.type)) {
|
|
|
+ serverLyric = await lyricService.fetchNavidromeLyric(songData, getAccount,this.currentSong.id);
|
|
|
+ }
|
|
|
if (!serverLyric && isJellyfinType(songData.type)) {
|
|
|
serverLyric = await lyricService.fetchJellyfinLyric(songData, getAccount);
|
|
|
}
|
|
|
@@ -14222,6 +14233,25 @@ export struct LocalMusic {
|
|
|
private sessionOutputDeviceChange = async (connectState: avSession.ConnectionState,
|
|
|
device: avSession.OutputDeviceInfo) => {
|
|
|
let currentDevice: avSession.DeviceInfo = device?.devices?.[0];
|
|
|
+
|
|
|
+ // 将 connectState 转换为可读的字符串
|
|
|
+ let stateName = 'UNKNOWN';
|
|
|
+ switch (connectState) {
|
|
|
+ case avSession.ConnectionState.STATE_CONNECTED:
|
|
|
+ stateName = 'CONNECTED(1)';
|
|
|
+ break;
|
|
|
+ case avSession.ConnectionState.STATE_DISCONNECTED:
|
|
|
+ stateName = 'DISCONNECTED(2)';
|
|
|
+ break;
|
|
|
+ case avSession.ConnectionState.STATE_CONNECTING:
|
|
|
+ stateName = 'CONNECTING(3)';
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ stateName = `OTHER(${connectState})`;
|
|
|
+ }
|
|
|
+
|
|
|
+ Logger.info('heanup sessionOutputDeviceChange', `设备状态变化: connectState=${stateName}, castCategory=${currentDevice?.castCategory}, isCastPlaying=${this.isCastPlaying}`);
|
|
|
+
|
|
|
if (currentDevice.castCategory === avSession.AVCastCategory.CATEGORY_REMOTE &&
|
|
|
connectState === avSession.ConnectionState.STATE_CONNECTED) {
|
|
|
|
|
|
@@ -14232,35 +14262,106 @@ export struct LocalMusic {
|
|
|
(currentDevice.castCategory === avSession.AVCastCategory.CATEGORY_LOCAL &&
|
|
|
connectState === avSession.ConnectionState.STATE_CONNECTED)) {
|
|
|
if (device) {
|
|
|
+ Logger.info('heanup sessionOutputDeviceChange', '设备断开连接');
|
|
|
+
|
|
|
+ Logger.info('heanup sessionOutputDeviceChange', '用户主动断开连接或已停止播放,调用endCasting');
|
|
|
this.endCasting();
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
private async startCasting() {
|
|
|
- this.castController = await this.avSessionController.getAvSession()?.getAVCastController();
|
|
|
- this.setPlaybackStateChangeListener();
|
|
|
- this.pause();
|
|
|
- this.initQueueItem();
|
|
|
- this.prepare();
|
|
|
- let refToIsCasting: AbstractProperty<boolean> | undefined = AppStorage.ref('isCasting');
|
|
|
- refToIsCasting?.set(true);
|
|
|
- this.castSeek = true;
|
|
|
- this.isCastPlaying = true;
|
|
|
+ try {
|
|
|
+ Logger.info('heanup startCasting', '========== 开始投播初始化 ==========');
|
|
|
+
|
|
|
+ // 获取原始的 AVCastController
|
|
|
+ let avCastCtrl = await this.avSessionController.getAvSession()?.getAVCastController();
|
|
|
+ if (!avCastCtrl) {
|
|
|
+ Logger.error('heanup startCasting', '获取 AVCastController 失败');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.castController = avCastCtrl;
|
|
|
+ Logger.info('heanup startCasting', '✅ 获取 AVCastController 成功');
|
|
|
+
|
|
|
+ // 创建新的 CastController 封装器
|
|
|
+ this.castControllerWrapper = new CastController(avCastCtrl);
|
|
|
+ Logger.info('heanup startCasting', '✅ 创建 CastController 成功');
|
|
|
+
|
|
|
+ // 设置回调函数
|
|
|
+ let callbacks: CastControllerCallbacks = {
|
|
|
+ onPlayNext: () => {
|
|
|
+ Logger.info('heanup CastController', '收到 playNext 回调');
|
|
|
+ this.playNext();
|
|
|
+ },
|
|
|
+ onPlayPrevious: () => {
|
|
|
+ Logger.info('heanup CastController', '收到 playPrevious 回调');
|
|
|
+ this.playPrevious();
|
|
|
+ },
|
|
|
+ onPositionChanged: (position: number) => {
|
|
|
+ this.positionChange(position);
|
|
|
+ },
|
|
|
+ onDurationChanged: (duration: number) => {
|
|
|
+ this.playDurationChange(duration);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ this.castControllerWrapper.setCallbacks(callbacks);
|
|
|
+ Logger.info('heanup startCasting', '✅ 设置回调成功');
|
|
|
+
|
|
|
+ // 暂停本地播放
|
|
|
+ this.pause();
|
|
|
+ Logger.info('heanup startCasting', '✅ 暂停本地播放成功');
|
|
|
+
|
|
|
+ // 初始化投播会话
|
|
|
+ Logger.info('heanup startCasting', `准备调用 initAVCast: songList.length=${this.songList.length}, curIndex=${this.curIndex}, videoUrl=${this.videoUrl}`);
|
|
|
+
|
|
|
+ await this.castControllerWrapper.initAVCast(
|
|
|
+ this.songList,
|
|
|
+ this.curIndex,
|
|
|
+ this.mIjkMediaPlayer.getCurrentPosition(),
|
|
|
+ this.videoUrl
|
|
|
+ );
|
|
|
+ Logger.info('heanup startCasting', '✅ initAVCast 调用完成');
|
|
|
+
|
|
|
+ let refToIsCasting: AbstractProperty<boolean> | undefined = AppStorage.ref('isCasting');
|
|
|
+ refToIsCasting?.set(true);
|
|
|
+ this.castSeek = true;
|
|
|
+
|
|
|
+ // 从 CastController 获取投播状态
|
|
|
+ this.isCastPlaying = this.castControllerWrapper.getIsCastPlaying();
|
|
|
+ Logger.info('heanup startCasting', `✅ 投播初始化完成, isCastPlaying=${this.isCastPlaying}`);
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('heanup startCasting', `❌ 投播初始化失败: ${error}`);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async changeCasting() {
|
|
|
+ Logger.info('heanup changeCasting', '切换投播');
|
|
|
this.castController = await this.avSessionController.getAvSession()?.getAVCastController();
|
|
|
this.initQueueItem();
|
|
|
- this.prepare();
|
|
|
+ await this.prepare();
|
|
|
this.setPlaybackStateChangeListener();
|
|
|
- this.isCastPlaying = true;
|
|
|
+ // isCastPlaying 会在 playbackStateChangeListener 中根据实际状态更新
|
|
|
this.castSeek = true;
|
|
|
}
|
|
|
|
|
|
public endCasting() {
|
|
|
+ Logger.info('heanup endCasting', 'endCasting被调用');
|
|
|
this.avSessionController.getAvSession()?.stopCasting();
|
|
|
this.stopCast();
|
|
|
+
|
|
|
+ // 关闭文件描述符
|
|
|
+ if (this.castFile) {
|
|
|
+ try {
|
|
|
+ fs.closeSync(this.castFile);
|
|
|
+ this.castFile = undefined;
|
|
|
+ Logger.info('heanup endCasting', '关闭投播文件描述符');
|
|
|
+ } catch (error) {
|
|
|
+ Logger.warn('heanup endCasting', `关闭文件描述符失败: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
this.isCastPlaying = false;
|
|
|
let refToIsCasting: AbstractProperty<boolean> | undefined = AppStorage.ref('isCasting');
|
|
|
refToIsCasting?.set(false);
|
|
|
@@ -14298,10 +14399,48 @@ export struct LocalMusic {
|
|
|
}
|
|
|
|
|
|
private setPlaybackStateChangeListener(): void {
|
|
|
- this.castController?.on('playbackStateChange', 'all', this.playbackStateChangeListener);
|
|
|
- this.castController?.on('endOfStream', this.reloadCasting);
|
|
|
+ // 注意: HarmonyOS 的投播控制器可能不返回 state 字段
|
|
|
+ // 我们需要通过其他方式来判断播放状态
|
|
|
+
|
|
|
+ // 1. 监听位置变化
|
|
|
+ this.castController?.on('playbackStateChange', ['position'], (playbackState: avSession.AVPlaybackState) => {
|
|
|
+ if (playbackState.position && typeof playbackState.position.elapsedTime !== 'undefined') {
|
|
|
+ Logger.info('heanup playbackStateChange[position]', `elapsedTime=${playbackState.position.elapsedTime}`);
|
|
|
+ this.positionChange(playbackState.position.elapsedTime);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 2. 监听时长变化
|
|
|
+ this.castController?.on('playbackStateChange', ['extras'], (playbackState: avSession.AVPlaybackState) => {
|
|
|
+ const duration = playbackState?.extras?.duration;
|
|
|
+ if (typeof duration === 'number') {
|
|
|
+ this.playDurationChange(duration);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 3. 监听所有状态变化(用于调试和状态判断)
|
|
|
+ this.castController?.on('playbackStateChange', 'all', (playbackState: avSession.AVPlaybackState) => {
|
|
|
+ Logger.info('heanup playbackStateChange[all]', `完整状态=${JSON.stringify(playbackState)}`);
|
|
|
+
|
|
|
+ // 检查是否有 state 字段(某些设备可能返回)
|
|
|
+ if (playbackState.state !== undefined) {
|
|
|
+ Logger.info('heanup playbackStateChange', `state字段存在: ${playbackState.state}`);
|
|
|
+ if (playbackState.state === CommonConstants2.PLAYBACK_STATE_PLAY) {
|
|
|
+ this.isCastPlaying = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 4. 监听播放完成事件
|
|
|
+ this.castController?.on('endOfStream', this.playNextCallback);
|
|
|
this.castController?.on('playNext', this.playNextCallback);
|
|
|
this.castController?.on('playPrevious', this.playPreviousCallback);
|
|
|
+
|
|
|
+ // 5. 监听错误事件
|
|
|
+ this.castController?.on('error', (error: BusinessError) => {
|
|
|
+ Logger.error('heanup playbackStateChange', `投播错误: code=${error.code}, message=${error.message}`);
|
|
|
+ this.isCastPlaying = false;
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
private updateSessionPlayState(isPlay: boolean): void {
|
|
|
@@ -14331,61 +14470,12 @@ export struct LocalMusic {
|
|
|
this.durationTime = Math.floor(this.duration / 1000);
|
|
|
this.durationStringTime = secondToTime((this.durationTime));
|
|
|
}
|
|
|
- private playbackStateChangeListener = (playbackState: avSession.AVPlaybackState) => {
|
|
|
- const duration = playbackState?.extras?.duration;
|
|
|
- if (typeof duration === 'number') {
|
|
|
- this.playDurationChange(duration as number);
|
|
|
- }
|
|
|
-
|
|
|
- if (typeof playbackState?.position?.elapsedTime !== 'undefined') {
|
|
|
- this.positionChange(playbackState?.position?.elapsedTime);
|
|
|
- }
|
|
|
-
|
|
|
- if (playbackState.state === CommonConstants2.PLAYBACK_STATE_PAUSE ||
|
|
|
- playbackState.state === CommonConstants2.PLAYBACK_STATE_STOP ||
|
|
|
- playbackState.state === CommonConstants2.PLAYBACK_STATE_PREPARE ||
|
|
|
- playbackState.state === CommonConstants2.PLAYBACK_STATE_INITIAL) {
|
|
|
- this.isCastPlaying = false;
|
|
|
- }
|
|
|
-
|
|
|
- if (playbackState.state === CommonConstants2.PLAYBACK_STATE_PLAY) {
|
|
|
- this.isCastPlaying = true;
|
|
|
- }
|
|
|
- };
|
|
|
+ // playbackStateChangeListener 方法已被删除,改用 setPlaybackStateChangeListener 中的内联函数
|
|
|
private reloadCasting = async () => {
|
|
|
-
|
|
|
- let item = this.songList[this.curIndex];
|
|
|
- let uri = fileUri.getUriFromPath(item.filePath);
|
|
|
- let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
|
|
|
- // description.startPosition = 0;
|
|
|
- this.castItem = {
|
|
|
- itemId: this.curIndex,
|
|
|
- description: {
|
|
|
- assetId: item.filePath,
|
|
|
- title: item.name,
|
|
|
- artist: '',
|
|
|
- // 网络资源投播,设置mediaUri; 本地资源投播,将本地文件打开后,相关的文件描述符设置到fdSrc
|
|
|
- // mediaUri: item.filePath,
|
|
|
- fdSrc: {
|
|
|
- fd: file.fd
|
|
|
- },
|
|
|
- // 该字段大写,音频'AUDIO',视频'VIDEO'
|
|
|
- mediaType: 'VIDEO',
|
|
|
- mediaSize: item.videoSize,
|
|
|
- //startPosition为投播当前进度,设置该字段可将本机播放进度同步到远端
|
|
|
- startPosition: this.mIjkMediaPlayer.getCurrentPosition(),
|
|
|
- // 投播资源播放时长,设置该字段可将本机播放时长同步到远端显示
|
|
|
- duration: this.duration,
|
|
|
- // albumCoverUri: 'https://www.example.jpeg',
|
|
|
- // albumTitle: '《ExampleAlbum》',
|
|
|
- appName: this.appName,
|
|
|
- // DRM资源,需要配置支持的DRM类型, 以chinaDRM为例。
|
|
|
- // drmScheme: '3d5e6d35-9b9a-41e8-b843-dd3c6e72c42c',
|
|
|
- }
|
|
|
- };
|
|
|
- this.prepare();
|
|
|
- this.isCastPlaying = true;
|
|
|
-
|
|
|
+ Logger.info('heanup reloadCasting', '重新加载投播');
|
|
|
+ // 使用 initQueueItem() 方法来准备投播项目,保持逻辑一致
|
|
|
+ this.initQueueItem();
|
|
|
+ await this.prepare();
|
|
|
};
|
|
|
public playNextCallback = (): void => {
|
|
|
this.playNext().catch(() => {
|
|
|
@@ -14399,52 +14489,135 @@ export struct LocalMusic {
|
|
|
};
|
|
|
|
|
|
async prepare() {
|
|
|
- await this.castController?.prepare(this.castItem);
|
|
|
- await this.castController?.start(this.castItem);
|
|
|
+ try {
|
|
|
+ Logger.info('heanup prepare', `开始准备投播: ${this.castItem?.description?.title}`);
|
|
|
+ await this.castController?.prepare(this.castItem);
|
|
|
+ Logger.info('heanup prepare', '投播prepare成功');
|
|
|
+
|
|
|
+ await this.castController?.start(this.castItem);
|
|
|
+ Logger.info('heanup prepare', '投播start成功');
|
|
|
+
|
|
|
+ // 投播启动成功,设置为投播状态
|
|
|
+ this.isCastPlaying = true;
|
|
|
+ Logger.info('heanup prepare', '投播启动成功,设置isCastPlaying=true');
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('heanup prepare', `投播准备失败: ${error}`);
|
|
|
+ this.isCastPlaying = false;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public initQueueItem() {
|
|
|
let item = this.songList[this.curIndex];
|
|
|
|
|
|
- let time = this.currentTime;
|
|
|
- let uri = fileUri.getUriFromPath(item.filePath);
|
|
|
- let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
|
|
|
+ // 判断是否是流媒体地址 - 使用 videoUrl 判断而不是 filePath
|
|
|
+ // 因为 filePath 可能是 navidrome:// 等自定义协议,而 videoUrl 才是真实的流地址
|
|
|
+ let isStreaming = this.isUrl(this.videoUrl);
|
|
|
+
|
|
|
+ Logger.info('heanup initQueueItem', `准备投播歌曲: ${item.name}, 是否流媒体: ${isStreaming}, videoUrl: ${this.videoUrl}`);
|
|
|
+
|
|
|
+ // 关闭之前的文件描述符
|
|
|
+ if (this.castFile) {
|
|
|
+ try {
|
|
|
+ fs.closeSync(this.castFile);
|
|
|
+ Logger.info('heanup initQueueItem', '关闭之前的文件描述符');
|
|
|
+ } catch (error) {
|
|
|
+ Logger.warn('heanup initQueueItem', `关闭文件描述符失败: ${error}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let description: avSession.AVMediaDescription = {
|
|
|
+ assetId: 'AUDIO-' + item.filePath,
|
|
|
+ title: item.name,
|
|
|
+ artist: item.artist || '',
|
|
|
+ // 该字段大写,音频'AUDIO',视频'VIDEO'
|
|
|
+ mediaType: 'AUDIO',
|
|
|
+ mediaSize: item.videoSize,
|
|
|
+ //startPosition为投播当前进度,设置该字段可将本机播放进度同步到远端
|
|
|
+ startPosition: this.mIjkMediaPlayer.getCurrentPosition(),
|
|
|
+ // 投播资源播放时长,设置该字段可将本机播放时长同步到远端显示
|
|
|
+ duration: this.duration,
|
|
|
+ albumCoverUri: item.pixelMapPath, // 本地路径电视可能无法访问,暂时注释
|
|
|
+ appName: this.appName,
|
|
|
+ lyricContent: item.lyricContent,
|
|
|
+ // DRM资源,需要配置支持的DRM类型, 以chinaDRM为例。
|
|
|
+ // drmScheme: '3d5e6d35-9b9a-41e8-b843-dd3c6e72c42c',
|
|
|
+ };
|
|
|
+ console.log('heanup initQueueItem this.lyricContent', item.lyricContent);
|
|
|
+
|
|
|
+ // 网络资源投播,设置mediaUri; 本地资源投播,将本地文件打开后,相关的文件描述符设置到fdSrc
|
|
|
+ if (isStreaming) {
|
|
|
+ // 流媒体地址使用 mediaUri
|
|
|
+ description.mediaUri = this.videoUrl;
|
|
|
+ Logger.info('heanup initQueueItem', `使用流媒体地址投播: ${this.videoUrl}`);
|
|
|
+ } else {
|
|
|
+ // 本地文件使用 fdSrc
|
|
|
+ let uri = fileUri.getUriFromPath(item.filePath);
|
|
|
+ this.castFile = fs.openSync(uri, fs.OpenMode.READ_ONLY);
|
|
|
+ let fdSrc: media.AVFileDescriptor = {
|
|
|
+ fd: this.castFile.fd
|
|
|
+ };
|
|
|
+ description.fdSrc = fdSrc;
|
|
|
+ Logger.info('heanup initQueueItem', `使用本地文件投播: ${item.filePath}, fd: ${this.castFile.fd}`);
|
|
|
+ }
|
|
|
|
|
|
this.castItem = {
|
|
|
itemId: this.curIndex,
|
|
|
- description: {
|
|
|
- assetId: item.filePath,
|
|
|
- title: item.name,
|
|
|
- artist: '',
|
|
|
- // 网络资源投播,设置mediaUri; 本地资源投播,将本地文件打开后,相关的文件描述符设置到fdSrc
|
|
|
- fdSrc: {
|
|
|
- fd: file.fd
|
|
|
- },
|
|
|
- // 该字段大写,音频'AUDIO',视频'VIDEO'
|
|
|
- mediaType: 'AUDIO',
|
|
|
- mediaSize: item.videoSize,
|
|
|
- //startPosition为投播当前进度,设置该字段可将本机播放进度同步到远端
|
|
|
- startPosition: this.mIjkMediaPlayer.getCurrentPosition(),
|
|
|
- // 投播资源播放时长,设置该字段可将本机播放时长同步到远端显示
|
|
|
- duration: this.duration,
|
|
|
- // albumCoverUri: 'https://www.example.jpeg',
|
|
|
- // albumTitle: '《ExampleAlbum》',
|
|
|
- appName: this.appName,
|
|
|
- // DRM资源,需要配置支持的DRM类型, 以chinaDRM为例。
|
|
|
- // drmScheme: '3d5e6d35-9b9a-41e8-b843-dd3c6e72c42c',
|
|
|
- }
|
|
|
+ description: description
|
|
|
};
|
|
|
+
|
|
|
+ Logger.info('heanup initQueueItem', `投播项目创建完成: ${JSON.stringify({
|
|
|
+ title: description.title,
|
|
|
+ mediaType: description.mediaType,
|
|
|
+ hasMediaUri: !!description.mediaUri,
|
|
|
+ hasFdSrc: !!description.fdSrc,
|
|
|
+ duration: description.duration
|
|
|
+ })}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断是否是 URL 或流媒体地址
|
|
|
+ private isUrl(str: string): boolean {
|
|
|
+ if (!str) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 检查常见的流媒体协议
|
|
|
+ if (str.startsWith('http://') || str.startsWith('https://') ||
|
|
|
+ str.startsWith('rtmp://') || str.startsWith('rtsp://') ||
|
|
|
+ str.startsWith('rtp://') || str.startsWith('mms://')) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 扩展的URL正则表达式,支持多种协议
|
|
|
+ const urlPattern = new RegExp(
|
|
|
+ '^((https?|ftp|rtmp|rtsp|mms|ws|rtp|wss):\\/\\/)?' + // 支持多种协议
|
|
|
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // 域名
|
|
|
+ '((\\d{1,3}\\.){3}\\d{1,3})|' + // 或IPv4地址
|
|
|
+ '\\[([a-f\\d]{1,4}:){7}[a-f\\d]{1,4}\\]|' + // 或IPv6地址
|
|
|
+ 'localhost)' + // 或localhost
|
|
|
+ '(\\:\\d+)?' + // 端口号(可选)
|
|
|
+ '(\\/[-a-z\\d%_.~+]*)*' + // 路径
|
|
|
+ '(\\?[;&a-z\\d%_.~+=-]*)?' + // 查询字符串(可选)
|
|
|
+ '(\\#[-a-z\\d_]*)?$', // 片段标识符(可选)
|
|
|
+ 'i' // 不区分大小写
|
|
|
+ );
|
|
|
+
|
|
|
+ return urlPattern.test(str);
|
|
|
}
|
|
|
|
|
|
async stopCast() {
|
|
|
try {
|
|
|
- this.castController?.off('playbackStateChange');
|
|
|
- this.castController?.off('endOfStream');
|
|
|
- this.castController?.off('playNext');
|
|
|
- this.castController?.off('playPrevious');
|
|
|
- this.castController?.off('error');
|
|
|
+ // 使用 CastController 释放资源
|
|
|
+ if (this.castControllerWrapper) {
|
|
|
+ await this.castControllerWrapper.releaseAVCast();
|
|
|
+ Logger.info('heanup stopCast', 'CastController 释放投播控制器成功');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清空引用
|
|
|
+ this.castControllerWrapper = undefined;
|
|
|
+ this.castController = undefined;
|
|
|
+ this.castItem = undefined;
|
|
|
+ this.castFile = undefined;
|
|
|
} catch (error) {
|
|
|
- hilog.info(0x0000, TAG, `Failed to setAVCastController. code: ${error.code}, message: ${error.message}`);
|
|
|
+ Logger.error('heanup stopCast', `停止投播失败: ${error}`);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -14455,20 +14628,29 @@ export struct LocalMusic {
|
|
|
this.pause()
|
|
|
};
|
|
|
|
|
|
- private playOrPause() {
|
|
|
+ private async playOrPause() {
|
|
|
if (!this.debounce()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (this.CONTROL_PlayStatus === PlayStatus.PLAY) {
|
|
|
- this.pause();
|
|
|
-
|
|
|
-
|
|
|
+ await this.pause();
|
|
|
} else {
|
|
|
- console.info('onecold startPlayOrResumePlay 11064')
|
|
|
- this.startPlayOrResumePlay();
|
|
|
- this.playChange()
|
|
|
-
|
|
|
+ // 如果是投播模式,使用 CastController 的 play 方法
|
|
|
+ if (this.castControllerWrapper && this.isCastPlaying) {
|
|
|
+ try {
|
|
|
+ Logger.info('heanup playOrPause', '投播模式播放');
|
|
|
+ await this.castControllerWrapper.setPlaying();
|
|
|
+ Logger.info('heanup playOrPause', '投播播放成功');
|
|
|
+ this.playChange();
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('heanup playOrPause', `投播播放失败: ${error}`);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ console.info('onecold startPlayOrResumePlay 11064')
|
|
|
+ this.startPlayOrResumePlay();
|
|
|
+ this.playChange()
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -14485,25 +14667,32 @@ export struct LocalMusic {
|
|
|
private sessionStopCallback = (): void => {
|
|
|
this.stop()
|
|
|
};
|
|
|
- private sessionFastForwardCallback = (time?: number) => {
|
|
|
+ private sessionFastForwardCallback = async (time?: number) => {
|
|
|
if (!time) {
|
|
|
return;
|
|
|
}
|
|
|
- if (this.mIjkMediaPlayer != null) {
|
|
|
- const curPosition = this.mIjkMediaPlayer.getCurrentPosition()
|
|
|
- let seeTime = curPosition + time * 1000;
|
|
|
- // 限制 seekValue 在合法范围内
|
|
|
- if (seeTime < 0) {
|
|
|
- seeTime = 0;
|
|
|
- } else if (seeTime > this.duration) {
|
|
|
- seeTime = this.duration;
|
|
|
- }
|
|
|
- Logger.info('onecold seeTime= ' + seeTime)
|
|
|
- this.setSeekToActionProgress(seeTime)
|
|
|
- this.seekTo(seeTime + "")
|
|
|
- }
|
|
|
|
|
|
+ let curPosition: number;
|
|
|
+ if (this.castController && this.isCastPlaying) {
|
|
|
+ // 投播模式,使用UI显示的时间
|
|
|
+ curPosition = this.currentTime2 * 1000;
|
|
|
+ } else if (this.mIjkMediaPlayer != null) {
|
|
|
+ // 本地播放器模式
|
|
|
+ curPosition = this.mIjkMediaPlayer.getCurrentPosition();
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
+ let seeTime = curPosition + time * 1000;
|
|
|
+ // 限制 seekValue 在合法范围内
|
|
|
+ if (seeTime < 0) {
|
|
|
+ seeTime = 0;
|
|
|
+ } else if (seeTime > this.duration) {
|
|
|
+ seeTime = this.duration;
|
|
|
+ }
|
|
|
+ Logger.info('heanup sessionFastForwardCallback', `快进: ${time}秒, 目标位置: ${seeTime}ms`);
|
|
|
+ this.setSeekToActionProgress(seeTime);
|
|
|
+ await this.seekTo(seeTime + "");
|
|
|
};
|
|
|
/**
|
|
|
* Gesture method onActionUpdate.
|
|
|
@@ -14511,8 +14700,8 @@ export struct LocalMusic {
|
|
|
* @param event Gesture event.手势拖动设置快进和后退
|
|
|
*/
|
|
|
private setSeekToActionProgress(position: number) {
|
|
|
- // let position = this.mIjkMediaPlayer.getCurrentPosition();
|
|
|
- let duration = this.mIjkMediaPlayer.getDuration();
|
|
|
+ // 投播模式使用this.duration,本地模式使用播放器获取
|
|
|
+ let duration = this.duration > 0 ? this.duration : this.mIjkMediaPlayer.getDuration();
|
|
|
let pos = 0;
|
|
|
if (duration > 0) {
|
|
|
this.slideEnable = true;
|
|
|
@@ -14534,29 +14723,38 @@ export struct LocalMusic {
|
|
|
this.currentTime = this.stringForTime(position);
|
|
|
this.isCurrentTime = false
|
|
|
}
|
|
|
- private sessionRewindCallback = (time?: number) => {
|
|
|
+ private sessionRewindCallback = async (time?: number) => {
|
|
|
if (!time) {
|
|
|
return;
|
|
|
}
|
|
|
- if (this.mIjkMediaPlayer != null) {
|
|
|
- const curPosition = this.mIjkMediaPlayer.getCurrentPosition()
|
|
|
- let seeTime = curPosition - time * 1000;
|
|
|
- // 限制 seekValue 在合法范围内
|
|
|
- if (seeTime < 0) {
|
|
|
- seeTime = 0;
|
|
|
- } else if (seeTime > this.duration) {
|
|
|
- seeTime = this.duration;
|
|
|
- }
|
|
|
- Logger.info('onecold seeTime= ' + seeTime)
|
|
|
- this.setSeekToActionProgress(seeTime)
|
|
|
- this.seekTo(seeTime + "")
|
|
|
+
|
|
|
+ let curPosition: number;
|
|
|
+ if (this.castController && this.isCastPlaying) {
|
|
|
+ // 投播模式,使用UI显示的时间
|
|
|
+ curPosition = this.currentTime2 * 1000;
|
|
|
+ } else if (this.mIjkMediaPlayer != null) {
|
|
|
+ // 本地播放器模式
|
|
|
+ curPosition = this.mIjkMediaPlayer.getCurrentPosition();
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
}
|
|
|
- };
|
|
|
- private sessionSeekCallback = (seekTime: number) => {
|
|
|
- if (this.mIjkMediaPlayer != null) {
|
|
|
- const curPosition = this.mIjkMediaPlayer.getCurrentPosition()
|
|
|
- this.seekTo(seekTime + "")
|
|
|
+
|
|
|
+ let seeTime = curPosition - time * 1000;
|
|
|
+ // 限制 seekValue 在合法范围内
|
|
|
+ if (seeTime < 0) {
|
|
|
+ seeTime = 0;
|
|
|
+ } else if (seeTime > this.duration) {
|
|
|
+ seeTime = this.duration;
|
|
|
}
|
|
|
+ Logger.info('heanup sessionRewindCallback', `快退: ${time}秒, 目标位置: ${seeTime}ms`);
|
|
|
+ this.setSeekToActionProgress(seeTime);
|
|
|
+ await this.seekTo(seeTime + "");
|
|
|
+ };
|
|
|
+ private sessionSeekCallback = async (seekTime: number) => {
|
|
|
+ Logger.info('heanup sessionSeekCallback', `收到seek请求: ${seekTime}ms, 投播模式: ${this.isCastPlaying}`);
|
|
|
+
|
|
|
+ // 投播模式直接调用seekTo,它会自动处理投播控制器的seek命令
|
|
|
+ await this.seekTo(seekTime + "");
|
|
|
};
|
|
|
|
|
|
private async getFileSize(filePath: string): Promise<number> {
|
|
|
@@ -14569,8 +14767,18 @@ export struct LocalMusic {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private pause() {
|
|
|
- if (this.mIjkMediaPlayer.isPlaying()) {
|
|
|
+ private async pause() {
|
|
|
+ // 如果是投播模式,使用 CastController 的 pause 方法
|
|
|
+ if (this.castControllerWrapper && this.isCastPlaying) {
|
|
|
+ try {
|
|
|
+ Logger.info('heanup pause', '投播模式暂停');
|
|
|
+ await this.castControllerWrapper.setPause();
|
|
|
+ Logger.info('heanup pause', '投播暂停成功');
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('heanup pause', `投播暂停失败: ${error}`);
|
|
|
+ }
|
|
|
+ } else if (this.mIjkMediaPlayer.isPlaying()) {
|
|
|
+ // 本地播放器模式
|
|
|
this.savePlaybackPosition();
|
|
|
this.mIjkMediaPlayer.pause();
|
|
|
this.setProgress();
|
|
|
@@ -14621,15 +14829,35 @@ export struct LocalMusic {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private seekTo(value: string) {
|
|
|
+ private async seekTo(value: string) {
|
|
|
if (StrUtil.isNotEmpty(this.videoUrl) && this.videoUrl.toLowerCase().endsWith('.wma')) {
|
|
|
ToastUtil.showToast('wma格式不支持拖动快进。')
|
|
|
return
|
|
|
}
|
|
|
- this.isSeekTo = true
|
|
|
- this.mIjkMediaPlayer.seekTo(value);
|
|
|
- this.setProgress()
|
|
|
- this.isSeekTo = false
|
|
|
+
|
|
|
+ // 如果是投播模式,使用 CastController 的 seek 方法
|
|
|
+ if (this.castControllerWrapper && this.isCastPlaying) {
|
|
|
+ try {
|
|
|
+ let seekPos = Number.parseInt(value);
|
|
|
+ Logger.info('heanup seekTo', `投播模式seek到: ${seekPos}ms, 当前时长: ${this.duration}ms`);
|
|
|
+
|
|
|
+ // 检查是否接近末尾
|
|
|
+ if (this.duration > 0 && seekPos >= this.duration - 500) {
|
|
|
+ Logger.warn('heanup seekTo', `快进位置接近末尾(${seekPos}ms >= ${this.duration}ms),可能触发播放完成`);
|
|
|
+ }
|
|
|
+
|
|
|
+ await this.castControllerWrapper.seek(seekPos);
|
|
|
+ Logger.info('heanup seekTo', `投播seek命令发送成功,位置: ${seekPos}ms`);
|
|
|
+ } catch (error) {
|
|
|
+ Logger.error('heanup seekTo', `投播seek失败: ${error}`);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 本地播放器模式
|
|
|
+ this.isSeekTo = true
|
|
|
+ this.mIjkMediaPlayer.seekTo(value);
|
|
|
+ this.setProgress()
|
|
|
+ this.isSeekTo = false
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 添加到下一首播放
|
|
|
@@ -14660,34 +14888,64 @@ export struct LocalMusic {
|
|
|
}
|
|
|
|
|
|
//后退10s,可再设置设置默认几秒
|
|
|
- backForward() {
|
|
|
- if(this.mIjkMediaPlayer){
|
|
|
- let pos = this.mIjkMediaPlayer.getCurrentPosition();
|
|
|
- let value = pos - Number(this.fastForwardSeconds) * 1000;
|
|
|
- this.seekTo(value+"");
|
|
|
+ async backForward() {
|
|
|
+ // 如果是投播模式,从currentTime2获取当前位置
|
|
|
+ let pos: number;
|
|
|
+ if (this.castController && this.isCastPlaying) {
|
|
|
+ pos = this.currentTime2 * 1000; // 使用秒转毫秒
|
|
|
+ Logger.info('heanup backForward', `投播模式当前位置: ${pos}ms`);
|
|
|
+ } else if (this.mIjkMediaPlayer) {
|
|
|
+ pos = this.mIjkMediaPlayer.getCurrentPosition();
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
+ let value = pos - Number(this.fastForwardSeconds) * 1000;
|
|
|
+ if (value < 0) {
|
|
|
+ value = 0;
|
|
|
+ }
|
|
|
+ await this.seekTo(value + "");
|
|
|
}
|
|
|
|
|
|
//快进10s,可再设置设置默认几秒
|
|
|
- fastForward() {
|
|
|
- if(this.mIjkMediaPlayer){
|
|
|
- let pos = this.mIjkMediaPlayer.getCurrentPosition();
|
|
|
- let value = pos + Number(this.fastForwardSeconds) * 1000;
|
|
|
- this.seekTo(value+"");
|
|
|
+ async fastForward() {
|
|
|
+ // 如果是投播模式,从currentTime2获取当前位置
|
|
|
+ let pos: number;
|
|
|
+ if (this.castController && this.isCastPlaying) {
|
|
|
+ pos = this.currentTime2 * 1000; // 使用秒转毫秒
|
|
|
+ Logger.info('heanup fastForward', `投播模式当前位置: ${pos}ms`);
|
|
|
+ } else if (this.mIjkMediaPlayer) {
|
|
|
+ pos = this.mIjkMediaPlayer.getCurrentPosition();
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ let value = pos + Number(this.fastForwardSeconds) * 1000;
|
|
|
+ // 限制不超过总时长
|
|
|
+ if (this.duration > 0 && value > this.duration) {
|
|
|
+ value = this.duration;
|
|
|
}
|
|
|
+ await this.seekTo(value + "");
|
|
|
}
|
|
|
|
|
|
//下一个
|
|
|
private async playNext() {
|
|
|
+ Logger.info('heanup playNext', '========== playNext 开始 ==========');
|
|
|
+ Logger.info('heanup playNext', `isCastPlaying=${this.isCastPlaying}, castControllerWrapper存在=${!!this.castControllerWrapper}`);
|
|
|
+
|
|
|
if (!this.debounce()) {
|
|
|
+ Logger.warn('heanup playNext', 'debounce() 返回 false,退出');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ Logger.info('heanup playNext', `playType=${this.playType}`);
|
|
|
+
|
|
|
if (this.playType == 3) { //3:随机播放
|
|
|
+ Logger.info('heanup playNext', '随机播放模式');
|
|
|
await this.randomPlay()
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
if (ArrayUtil.isNotEmpty(this.songList)) {
|
|
|
if (this.curIndex >= this.songList.length - 1) {
|
|
|
await this.tryLoadNextPageForPlayback();
|
|
|
@@ -14697,13 +14955,10 @@ export struct LocalMusic {
|
|
|
} else {
|
|
|
this.curIndex++;
|
|
|
}
|
|
|
- this.CONTROL_PlayStatus = PlayStatus.INIT;
|
|
|
- this.stop();
|
|
|
|
|
|
// 直接使用歌曲列表中的歌曲信息,songList中的数据已经是完整的
|
|
|
this.currentSong = this.songList[this.curIndex];
|
|
|
- Logger.info('heanup playNext', `直接使用歌曲列表中的信息: ${this.currentSong.name}, type: ${this.currentSong.type}`);
|
|
|
-
|
|
|
+ console.info('heanup playNext', `直接使用歌曲列表中的信息: ${this.currentSong.name}, type: ${this.currentSong.type}`);
|
|
|
// 设置videoUrl,WebDAV歌曲会通过webdav_account_id构建完整URL
|
|
|
this.videoUrl = await setVideoUrlForSong(this.currentSong, {
|
|
|
context: this.context,
|
|
|
@@ -14713,10 +14968,37 @@ export struct LocalMusic {
|
|
|
this.artist = this.songList[this.curIndex].artist
|
|
|
this.name = this.songList[this.curIndex].name
|
|
|
//this.cover = this.songList[this.curIndex].pixelMapPath
|
|
|
- this.changeImageAnimation()
|
|
|
+ console.info('heanup playNext 1 ', `this.isCastPlaying: ${ this.isCastPlaying}`)
|
|
|
+ console.info('heanup playNext 2 ', `this.castControllerWrapper: ${this.castControllerWrapper}`)
|
|
|
|
|
|
- }
|
|
|
+ // 从 CastController 获取最新的投播状态
|
|
|
+ if (this.castControllerWrapper) {
|
|
|
+ const controllerIsPlaying = this.castControllerWrapper.getIsCastPlaying();
|
|
|
+ console.info('heanup playNext', `从 CastController 获取的 isCastPlaying=${controllerIsPlaying}`);
|
|
|
+ this.isCastPlaying = controllerIsPlaying;
|
|
|
+ }
|
|
|
|
|
|
+ // 如果是投播模式,使用 CastController 播放下一首
|
|
|
+ if (this.castControllerWrapper && this.isCastPlaying) {
|
|
|
+ console.info('heanup playNext', '使用 CastController 播放下一首');
|
|
|
+ try {
|
|
|
+ // 传入最新的歌曲列表、索引和 videoUrl
|
|
|
+ await this.castControllerWrapper.playNext(this.songList, this.curIndex, this.videoUrl);
|
|
|
+ this.isCastPlaying = this.castControllerWrapper.getIsCastPlaying();
|
|
|
+ console.info('heanup playNext', 'CastController 播放下一首成功');
|
|
|
+ return;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('heanup playNext', `CastController 播放下一首失败: ${error}`);
|
|
|
+ // 如果投播失败,回退到本地播放
|
|
|
+ this.isCastPlaying = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 本地播放器模式,需要先stop
|
|
|
+ this.CONTROL_PlayStatus = PlayStatus.INIT;
|
|
|
+ this.stop();
|
|
|
+ this.changeImageAnimation()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
changeImageAnimation() {
|
|
|
@@ -14932,8 +15214,6 @@ export struct LocalMusic {
|
|
|
}
|
|
|
if (ArrayUtil.isNotEmpty(this.songList)) {
|
|
|
|
|
|
- this.CONTROL_PlayStatus = PlayStatus.INIT;
|
|
|
- this.stop();
|
|
|
this.curIndex = index;
|
|
|
this.currentSong = this.songList[index];
|
|
|
|
|
|
@@ -14945,6 +15225,19 @@ export struct LocalMusic {
|
|
|
|
|
|
this.name = this.currentSong.name
|
|
|
this.artist = this.currentSong.artist
|
|
|
+
|
|
|
+ // 如果是投播模式,需要为投播设备准备当前歌曲
|
|
|
+ if (this.castController && this.isCastPlaying) {
|
|
|
+ Logger.info('heanup playIndex', '投播模式播放指定歌曲,不调用本地stop()');
|
|
|
+ this.initQueueItem();
|
|
|
+ await this.prepare();
|
|
|
+ this.cover = this.currentSong.pixelMapPath;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 本地播放器模式,需要先stop
|
|
|
+ this.CONTROL_PlayStatus = PlayStatus.INIT;
|
|
|
+ this.stop();
|
|
|
this.changeImageAnimation()
|
|
|
}
|
|
|
|
|
|
@@ -14967,9 +15260,6 @@ export struct LocalMusic {
|
|
|
this.curIndex--;
|
|
|
}
|
|
|
|
|
|
- this.CONTROL_PlayStatus = PlayStatus.INIT;
|
|
|
- this.stop();
|
|
|
-
|
|
|
// 直接使用歌曲列表中的歌曲信息,songList中的数据已经是完整的
|
|
|
this.currentSong = this.songList[this.curIndex];
|
|
|
Logger.info('heanup playPrevious', `直接使用歌曲列表中的信息: ${this.currentSong.name}, type: ${this.currentSong.type}`);
|
|
|
@@ -14981,6 +15271,20 @@ export struct LocalMusic {
|
|
|
});
|
|
|
this.name = this.songList[this.curIndex].name
|
|
|
this.artist = this.songList[this.curIndex].artist
|
|
|
+
|
|
|
+ // 如果是投播模式,需要为投播设备准备上一首
|
|
|
+ if (this.castController && this.isCastPlaying) {
|
|
|
+ Logger.info('heanup playPrevious', '投播模式播放上一首,不调用本地stop()');
|
|
|
+ this.initQueueItem();
|
|
|
+ await this.prepare();
|
|
|
+ // 投播模式下不需要调用本地的 startPlayOrResumePlay
|
|
|
+ this.cover = this.songList[this.curIndex].pixelMapPath;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 本地播放器模式,需要先stop
|
|
|
+ this.CONTROL_PlayStatus = PlayStatus.INIT;
|
|
|
+ this.stop();
|
|
|
this.changeImageAnimation()
|
|
|
}
|
|
|
|