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

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

Loading…
Cancel
Save