Browse Source

上传qq音乐播放控制优化

newdev_shunjiawei
liwei1dao 1 year ago
parent
commit
2844784d9b
  1. 12
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  2. 136
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/QQMusicSystemFunctionHandler.kt
  3. 37
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicManager.kt
  4. 15
      local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicSingleton.kt

12
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt

@ -131,6 +131,9 @@ object AgentService : CoroutineScope {
// 语音识别模式
private var currentRecognitionMode = "normal" // 当前识别模式:normal, ble_wakeup, phone_call, push_to_talk
//是否播放音乐中
private var isplaymusic = false;
// 打断防抖相关
private var lastInterruptTime = 0L
private val INTERRUPT_DEBOUNCE_MS = 300L
@ -358,6 +361,11 @@ object AgentService : CoroutineScope {
setPackage(context.packageName)
}
context.sendBroadcast(intent)
QQMusicSingleton.getInstance().autopauseMusic { result ->
if (result.isSuccess) {
isplaymusic = true
}
}
}
TtsEventType.PLAYBACK_COMPLETED -> { //结束AI播报内容
@ -369,6 +377,10 @@ object AgentService : CoroutineScope {
setPackage(context.packageName)
}
context.sendBroadcast(intent)
if(isplaymusic){
isplaymusic = false
QQMusicSingleton.getInstance().resumeMusic { }
}
}
TtsEventType.ERROR -> {

136
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/QQMusicSystemFunctionHandler.kt

@ -4,6 +4,8 @@ import android.content.Context
import android.util.Log
import kotlinx.coroutines.delay
import com.yunqiinnovation.qq_music.QQMusicSingleton
import kotlin.coroutines.suspendCoroutine
import kotlin.coroutines.resume
/**
* QQ音乐系统功能处理器
* 负责注册QQ音乐相关的MCP函数
@ -195,60 +197,110 @@ private class QQMusicLoginHandler(private val context: Context?) : FunctionHandl
}
}
}
private class QQMusicSearchHandler(private val context: Context?) : FunctionHandler {
override suspend fun handle(arguments: Map<String, Any>): String {
return try {
val keyword = arguments["keyword"] as? String ?: return "{\"success\": false, \"message\": \"缺少搜索关键词\"}"
val searchType = arguments["searchType"] as? Int ?: 0 // 默认搜索歌曲
val qqMusicSingleton = QQMusicSingleton.getInstance()
var result = ""
// 直接调用搜索,内部会自动处理登录检查
qqMusicSingleton.searchSongs(keyword) { searchResult ->
result = if (searchResult.isSuccess) {
val songs = searchResult.getOrNull() ?: emptyList()
// 根据搜索类型处理结果
val processedSongs = when (searchType) {
0 -> { // 歌曲搜索,只返回第一个结果
// if (songs.isNotEmpty()) listOf(songs.first()) else emptyList()
songs
// 使用 suspendCoroutine 将回调转换为协程
suspendCoroutine { continuation ->
qqMusicSingleton.searchSongs(keyword) { searchResult ->
val result = if (searchResult.isSuccess) {
val songs = searchResult.getOrNull() ?: emptyList()
// 根据搜索类型处理结果
val processedSongs = when (searchType) {
0 -> { // 歌曲搜索,返回所有结果
songs
}
else -> { // 专辑、歌手等其他类型,返回所有结果
songs
}
}
else -> { // 专辑、歌手等其他类型,返回所有结果
songs
}
}
val songsJson = processedSongs.joinToString(",") { song ->
val songMap = song as Map<String, Any?>
// 添加调试日志
Log.d("QQMusicSearchHandler", "原始数据: $songMap")
// 修正字段名映射
val mid = songMap["mid"] ?: "unknown"
val name = songMap["name"] ?: "unknown"
val artist = songMap["singer"] ?: "unknown"
val album = songMap["album"] ?: ""
val pic = songMap["pic"] ?: ""
val duration = songMap["duration"] ?: 0
"{\"mid\": \"$mid\", \"name\": \"$name\", \"artist\": \"$artist\", \"album\": \"$album\", \"duration\":$duration,\"pic\": \"$pic\"}"
val songsJson = processedSongs.joinToString(",") { song ->
val songMap = song as Map<String, Any?>
// 添加调试日志
Log.d("QQMusicSearchHandler", "原始数据: $songMap")
// 修正字段名映射
val mid = songMap["mid"] ?: "unknown"
val name = songMap["name"] ?: "unknown"
val artist = songMap["singer"] ?: "unknown"
val album = songMap["album"] ?: ""
val pic = songMap["pic"] ?: ""
val duration = songMap["duration"] ?: 0
"{\"mid\": \"$mid\", \"name\": \"$name\", \"artist\": \"$artist\", \"album\": \"$album\", \"duration\":$duration,\"pic\": \"$pic\"}"
}
"{\"success\": true, \"broadcast\": false, \"message\": \"搜索成功,正在播放\", \"data\": [$songsJson]}"
} else {
val error = searchResult.exceptionOrNull()
"{\"success\": false, \"message\": \"搜索失败: ${error?.message}\"}"
}
"{\"success\": true, \"broadcast\": false, \"message\": \"搜索成功,增在播放\", \"data\": [$songsJson]}"
} else {
val error = searchResult.exceptionOrNull()
"{\"success\": false, \"message\": \"搜索失败: ${error?.message}\"}"
// 恢复协程执行
continuation.resume(result)
}
}
// 等待回调完成
delay(5000)
result.ifEmpty { "{\"success\": false, \"message\": \"搜索超时\"}" }
} catch (e: Exception) {
Log.e("QQMusicSearchHandler", "QQ音乐搜索失败", e)
"{\"success\": false, \"message\": \"搜索异常: ${e.message}\"}"
}
}
}
//private class QQMusicSearchHandler(private val context: Context?) : FunctionHandler {
// override suspend fun handle(arguments: Map<String, Any>): String {
// return try {
// val keyword = arguments["keyword"] as? String ?: return "{\"success\": false, \"message\": \"缺少搜索关键词\"}"
// val searchType = arguments["searchType"] as? Int ?: 0 // 默认搜索歌曲
// val qqMusicSingleton = QQMusicSingleton.getInstance()
// var result = ""
//
// // 直接调用搜索,内部会自动处理登录检查
// qqMusicSingleton.searchSongs(keyword) { searchResult ->
// result = if (searchResult.isSuccess) {
// val songs = searchResult.getOrNull() ?: emptyList()
//
// // 根据搜索类型处理结果
// val processedSongs = when (searchType) {
// 0 -> { // 歌曲搜索,只返回第一个结果
//// if (songs.isNotEmpty()) listOf(songs.first()) else emptyList()
// songs
// }
// else -> { // 专辑、歌手等其他类型,返回所有结果
// songs
// }
// }
//
// val songsJson = processedSongs.joinToString(",") { song ->
// val songMap = song as Map<String, Any?>
// // 添加调试日志
// Log.d("QQMusicSearchHandler", "原始数据: $songMap")
// // 修正字段名映射
// val mid = songMap["mid"] ?: "unknown"
// val name = songMap["name"] ?: "unknown"
// val artist = songMap["singer"] ?: "unknown"
// val album = songMap["album"] ?: ""
// val pic = songMap["pic"] ?: ""
// val duration = songMap["duration"] ?: 0
// "{\"mid\": \"$mid\", \"name\": \"$name\", \"artist\": \"$artist\", \"album\": \"$album\", \"duration\":$duration,\"pic\": \"$pic\"}"
// }
// "{\"success\": true, \"broadcast\": false, \"message\": \"搜索成功,增在播放\", \"data\": [$songsJson]}"
// } else {
// val error = searchResult.exceptionOrNull()
// "{\"success\": false, \"message\": \"搜索失败: ${error?.message}\"}"
// }
// }
//
// // 等待回调完成
// delay(5000)
// result.ifEmpty { "{\"success\": false, \"message\": \"搜索超时\"}" }
// } catch (e: Exception) {
// Log.e("QQMusicSearchHandler", "QQ音乐搜索失败", e)
// "{\"success\": false, \"message\": \"搜索异常: ${e.message}\"}"
// }
// }
//}
private class QQMusicPlaySongsHandler(private val context: Context?) : FunctionHandler {
override suspend fun handle(arguments: Map<String, Any>): String {
@ -310,7 +362,7 @@ private class QQMusicPauseHandler(private val context: Context?) : FunctionHandl
qqMusicSingleton.pauseMusic { pauseResult ->
result = if (pauseResult.isSuccess) {
"{\"notreply\": true, \"success\": true, \"message\": \"暂停播放\"}"
"{\"broadcast\": false, \"success\": true, \"message\": \"暂停播放\"}"
} else {
val error = pauseResult.exceptionOrNull()
"{\"success\": false, \"message\": \"暂停失败: ${error?.message}\"}"
@ -334,7 +386,7 @@ private class QQMusicResumeHandler(private val context: Context?) : FunctionHand
qqMusicSingleton.resumeMusic { resumeResult ->
result = if (resumeResult.isSuccess) {
"{\"notreply\": true, \"success\": true, \"message\": \"恢复播放\"}"
"{\"broadcast\": false, \"success\": true, \"message\": \"恢复播放\"}"
} else {
val error = resumeResult.exceptionOrNull()
"{\"success\": false, \"message\": \"恢复播放失败: ${error?.message}\"}"
@ -358,7 +410,7 @@ private class QQMusicStopHandler(private val context: Context?) : FunctionHandle
qqMusicSingleton.stopMusic { stopResult ->
result = if (stopResult.isSuccess) {
"{\"notreply\": true, \"success\": true, \"message\": \"停止播放\"}"
"{\"broadcast\": false, \"success\": true, \"message\": \"停止播放\"}"
} else {
val error = stopResult.exceptionOrNull()
"{\"success\": false, \"message\": \"停止失败: ${error?.message}\"}"
@ -382,7 +434,7 @@ private class QQMusicNextHandler(private val context: Context?) : FunctionHandle
qqMusicSingleton.skipToNext { nextResult ->
result = if (nextResult.isSuccess) {
"{\"notreply\": true, \"success\": true, \"message\": \"切换到下一首\"}"
"{\"broadcast\": false, \"success\": true, \"message\": \"切换到下一首\"}"
} else {
val error = nextResult.exceptionOrNull()
"{\"success\": false, \"message\": \"切换失败: ${error?.message}\"}"
@ -406,7 +458,7 @@ private class QQMusicPreviousHandler(private val context: Context?) : FunctionHa
qqMusicSingleton.skipToPrevious { previousResult ->
result = if (previousResult.isSuccess) {
"{\"notreply\": true, \"success\": true, \"message\": \"切换到上一首\"}"
"{\"broadcast\": false, \"success\": true, \"message\": \"切换到上一首\"}"
} else {
val error = previousResult.exceptionOrNull()
"{\"success\": false, \"message\": \"切换失败: ${error?.message}\"}"
@ -434,7 +486,7 @@ private class QQMusicGetPlaybackStateHandler(private val context: Context?) : Fu
val stateJson = state.entries.joinToString(",") { (key, value) ->
"\"$key\": \"$value\""
}
"{\"notreply\": true, \"success\": true, \"message\": \"获取播放状态成功\", \"data\": {$stateJson}}"
"{\"broadcast\": false, \"success\": true, \"message\": \"获取播放状态成功\", \"data\": {$stateJson}}"
} else {
val error = stateResult.exceptionOrNull()
"{\"success\": false, \"message\": \"获取播放状态失败: ${error?.message}\"}"

37
local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicManager.kt

@ -319,6 +319,43 @@ class QQMusicManager(private val activity: Activity) {
}
}
fun autopauseMusic(callback: PlayCallback){
if (!isBindQQMusicService || qqmusicApi == null) {
callback.onError(-1, "QQ音乐服务未连接")
return
}
// 获取播放状态
val stateResult = qqmusicApi?.execute("getPlaybackState", null)
val stateCode = stateResult?.getInt(Keys.API_RETURN_KEY_CODE) ?: -1
// 处理播放状态 - 使用正确的常量
if (stateCode == ErrorCodes.ERROR_OK) {
val playState = stateResult?.getInt(Keys.API_RETURN_KEY_DATA) ?: 0
val isPlaying = when (playState) {
PlayState.STARTED -> true
PlayState.PREPARED -> true
PlayState.BUFFERING -> true
PlayState.PAUSED -> false
PlayState.STOPPED -> false
PlayState.ERROR -> false
else -> false
}
if(isPlaying){
val result = qqmusicApi?.execute("pauseMusic", null)
val code = result?.getInt(Keys.API_RETURN_KEY_CODE) ?: -1
if (code == ErrorCodes.ERROR_OK) {
callback.onSuccess()
} else {
callback.onError(code, "暂停失败")
}
}else {
callback.onError(-1, "未播放")
}
} else {
callback.onError(-1, "未播放")
}
}
/**
* 暂停音乐
*/

15
local_plugins/qq_music/android/src/main/kotlin/com/yunqiinnovation/qq_music/QQMusicSingleton.kt

@ -25,8 +25,8 @@ class QQMusicSingleton private constructor() {
private const val KEY_EXPIRE_TIME = "expire_time"
private const val LOGIN_WAIT_DELAY_MS = 30000L
// 添加搜索重试相关常量
private const val SEARCH_TIMEOUT_MS = 50000L
private const val MAX_RETRY_COUNT = 3
private const val SEARCH_TIMEOUT_MS = 10000L
private const val MAX_RETRY_COUNT = 1
private const val RETRY_DELAY_MS = 1000L
@Volatile
@ -194,7 +194,7 @@ class QQMusicSingleton private constructor() {
callback(Result.failure(Exception("未安装QQ音乐软件,请先安装QQ音乐软件")))
return
}
searchWithRetry(keyword, searchType, 0, callback)
searchWithRetry(keyword, searchType, 1, callback)
}
/**
@ -414,6 +414,15 @@ class QQMusicSingleton private constructor() {
}(callback)
}
/**
* 暂停音乐
*/
fun autopauseMusic(callback: (Result<Unit>) -> Unit) {
executePlaybackAction("自动暂停") { musicManager, playCallback ->
musicManager.autopauseMusic(playCallback)
}(callback)
}
/**
* 恢复播放
*/

Loading…
Cancel
Save