Browse Source

上传 音乐播放连接 过期的问题

weicu
liwei1dao 1 year ago
parent
commit
5a047caa56
  1. 2
      lib/data/services/music_manager.dart
  2. 9
      lib/data/services/network/api.dart
  3. 2
      lib/data/services/network/nw_api.dart
  4. 28
      lib/modules/agent/controllers/agent_controller.dart
  5. 3
      lib/modules/agent/views/message_bubble.dart
  6. 9
      lib/modules/agent/views/music_card.dart
  7. 48
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  8. 26
      local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicService.kt
  9. 4
      local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServicePlugin.kt
  10. 6
      local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServiceStarter.kt
  11. 2
      local_plugins/music_service/lib/music_service.dart

2
lib/data/services/music_manager.dart

@ -52,7 +52,7 @@ class MusicManager extends GetxService {
}
//播放
Future<void> play(Map<String, String> song) async {
Future<void> play(Map<String, dynamic> song) async {
await _musicService.playMusic(song);
}

9
lib/data/services/network/api.dart

@ -98,4 +98,13 @@ class Api {
params: params,
);
}
//获取音乐连接
static getMusicUrl([params]) {
return DioManager().request(
NWMethod.post,
'/api/home/music_getmusicurl',
params: params,
);
}
}

2
lib/data/services/network/nw_api.dart

@ -1,5 +1,5 @@
class NWApi {
// static const baseApi = "https://www.deapsound.com";
static const baseApi = "https://api.deapsound.com";
//static const baseApi = "https://api.ideapsound.com";
// static const baseApi = "https://api.ideapsound.com";
}

28
lib/modules/agent/controllers/agent_controller.dart

@ -7,6 +7,7 @@ import 'package:deep_voice/data/models/user_Info.dart';
import 'package:deep_voice/data/services/location_manager.dart';
import 'package:deep_voice/data/services/music_manager.dart';
import 'package:deep_voice/data/services/navigation_manager.dart';
import 'package:deep_voice/data/services/network/api.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:agent_service/agent_service.dart';
@ -805,7 +806,32 @@ class AgentController extends GetxController {
}
// 播放音乐
void musicPlay(Map<String, String> song) {
void musicPlay(Map<String, dynamic> song) async {
final id = song['id'] ?? "";
final timestampMs = song['ts'] ?? 0;
// 当前时间
final now = DateTime.now();
// 将时间戳转换为 DateTime
final tsTime = DateTime.fromMillisecondsSinceEpoch(timestampMs).add(
const Duration(minutes: 10), // 转换为北京时间
);
// 判断是否过期
if (tsTime.isBefore(now)) {
//获取新的连接
var response = await Api.getMusicUrl({
"ids": [id]
});
// 2. 将响应数据转换为User对象
try {
print('getMusicUrl 响应数据: ${response.toString()}'); // 调试输出原始数据
final urldata = response['urls'][0] as Map<String, dynamic>;
song["url"] = urldata['url'] ?? '';
} catch (e) {
Logger.e(TAG, '转换响应数据失败: $e');
return;
}
}
_musiceManager.play(song);
isPlaying.value = true;
currentUrl.value = song['url'] ?? '';

3
lib/modules/agent/views/message_bubble.dart

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:ffi';
import 'package:common_utils/common_utils.dart';
import 'package:deep_voice/modules/agent/views/navigation_card.dart';
@ -233,7 +234,7 @@ class MessageBubble extends GetView<AgentController> {
if (meta!.keys.contains('music_play')) {
final card_music =
meta!["music_play"]['card_music'] as Map<String, dynamic>;
final musicId = card_music!['id'].toString();
final musicId = card_music!['id'];
final musicName = card_music!['name'] as String;
final musicUrl = card_music!['url'] as String;
final musicSinger = card_music!['sgener'] as String;

9
lib/modules/agent/views/music_card.dart

@ -3,12 +3,12 @@ import 'package:get/get.dart';
import '../controllers/agent_controller.dart';
/// 构建音乐项目
Widget buildMusicItem(BuildContext context, String id, String name, String url,
Widget buildMusicItem(BuildContext context, int id, String name, String url,
String singer, String image) {
final controller = Get.find<AgentController>(); // 获取控制器
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
Map<String, String> song = {
Map<String, dynamic> song = {
'id': id,
'url': url,
'title': name,
@ -19,7 +19,7 @@ Widget buildMusicItem(BuildContext context, String id, String name, String url,
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: isDarkMode ? Color(0xFF1E1E1E) : Colors.white,
color: isDarkMode ? const Color(0xFF1E1E1E) : Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
@ -154,6 +154,7 @@ Widget buildMusicContent(
Icon(
Icons.music_note,
size: 16,
// ignore: deprecated_member_use
color: Theme.of(context).primaryColor.withOpacity(0.7),
),
const SizedBox(width: 6),
@ -175,7 +176,7 @@ Widget buildMusicContent(
...playlist.asMap().entries.take(4).map((entry) {
final index = entry.key;
final item = entry.value;
final id = item["id"]?.toString() ?? "";
final id = item["id"] ?? 0;
final name = item["name"] ?? "未知歌曲";
final url = item["url"] ?? "";
final singer = item["sgener"] ?? "未知歌手";

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

@ -612,7 +612,7 @@ object AgentService : CoroutineScope {
val responseBuilder = StringBuilder()
var aiMetadata:String = ""
var userMetadata = JSONObject()
var nobroadcast = false
var broadcast = false //是否播发
// 如果有图片,添加图片信息到元数据
if (hasImage) {
userMetadata.put("hasImage", true)
@ -673,16 +673,18 @@ object AgentService : CoroutineScope {
object : StreamCallback {
override fun onToken(token: String) {
responseBuilder.append(token)
if (speakResponse && !nobroadcast) {
if (speakResponse && broadcast) {
ttsService?.speakStream(token)
}
// 发送流式回复token
sendEvent("assistant_token", mapOf("token" to token))
if (broadcast){
// 发送流式回复token
sendEvent("assistant_token", mapOf("token" to token))
}
}
override fun onComplete() {
// 视情况决定是否朗读回复
if (speakResponse && !nobroadcast) {
if (speakResponse && broadcast) {
ttsService?.flushStream()
}
val response = responseBuilder.toString()
@ -737,15 +739,14 @@ object AgentService : CoroutineScope {
override fun onFunctionCallResult(functionCall: JSONObject, functionCallResult: JSONObject) {
audioPlayer?.stopAudio()
val name = functionCall.get("name") as String;
val (metestr, broadcast)= autoHandleFcunCallResult(name,functionCallResult);
val (metestr, _broadcast)= autoHandleFcunCallResult(name,functionCallResult);
aiMetadata = metestr;
nobroadcast = broadcast
broadcast = _broadcast
sendEvent("function_call_result", mapOf(
"function_call" to functionCall.toString(),
"result" to functionCallResult.toString(),
"meta" to metestr,
))
}
}
)
@ -859,7 +860,7 @@ object AgentService : CoroutineScope {
)
if (userMessageId != -1L) {
if (assistantMessage.isNotEmpty() ) {
if (assistantMessage.isNotEmpty() || aiMetadata.isNotEmpty()) {
// 保存AI回复
val assistantMessageId = chatStorageHelper.saveMessage(
sessionId = sessionId,
@ -1124,54 +1125,58 @@ object AgentService : CoroutineScope {
val textStr = content.optString("text")
println("协议工具返回数据 检查是否存在卡片! textStr: ${textStr}")
val metadata: MutableMap<String, Any> = mutableMapOf()
var broadcast = false;
println("协议工具返回数据 检查是否存在卡片!")
var broadcast = true;
if (textStr.isNotEmpty()) {
val meta = JSONObject(textStr)
metadata[toolname] = meta // 现在可以赋值
val metaStr = JSONObject(metadata as Map<*, *>).toString()
if (meta.has("broadcast")){
broadcast = meta["broadcast"] as Boolean
}
// Log.d(TAG, "mcp调用结果: $metaStr")
if (meta.has("card_music")) { //音乐卡片
val cardMusic = meta.getJSONObject("card_music")
val id = cardMusic.optString("id", "")
val id = cardMusic.optInt("id", 0)
val url = cardMusic.optString("url", "")
val name = cardMusic.optString("name", "")
val sgener = cardMusic.optString("sgener", "")
val image = cardMusic.optString("image", "")
val ts = cardMusic.optInt("ts", 0)
processMusicPlay(
mapOf(
"id" to id,
"url" to url,
"title" to name,
"artist" to sgener,
"coverUrl" to image
"coverUrl" to image,
"ts" to ts
)
)
broadcast = true;
} else if (meta.has("card_musiclist")) { //音乐列表
val cardMusiclist = meta.getJSONObject("card_musiclist")
val musics = cardMusiclist.optJSONArray("musics")
if (musics != null) {
val playlist = mutableListOf<Map<String, String>>()
val playlist = mutableListOf<Map<String, Any>>()
for (i in 0 until musics.length()) {
val item = musics.optJSONObject(i) ?: continue
val id = item.optString("id", "")
val id = item.optInt("id", 0)
val url = item.optString("url", "")
val name = item.optString("name", "")
val sgener = item.optString("sgener", "")
val image = item.optString("image", "")
val ts = item.optInt("ts", 0)
playlist.add(
mapOf(
"id" to id,
"url" to url,
"title" to name,
"artist" to sgener,
"coverUrl" to image
"coverUrl" to image,
"ts" to ts
)
)
}
if (playlist.isNotEmpty()) {
broadcast = true;
processMusicPlayList(playlist)
} else {
Log.w(TAG, "card_musiclist 中没有有效的音乐条目")
@ -1183,7 +1188,6 @@ object AgentService : CoroutineScope {
val cardNavigation = meta.getJSONObject("card_navigation")
val start = cardNavigation.optString("start", "")
val end = cardNavigation.optString("end", "")
broadcast = true;
processNavigation(start, end);
}
return Pair(metaStr, broadcast);
@ -1192,7 +1196,7 @@ object AgentService : CoroutineScope {
// 捕获其他类型的异常(可选)
println("为解析到卡片数据!")
}
return Pair("", false);
return Pair("", true);
}
/**
@ -1205,7 +1209,7 @@ object AgentService : CoroutineScope {
* @param singer 歌手名称,默认为空
* @return 是否成功开始处理
*/
fun processMusicPlay(song: Map<String,String>){
fun processMusicPlay(song: Map<String,Any>){
// 在其他 Service、BroadcastReceiver 或 Application 中调用
Log.i(TAG, "播放音乐: ${song}")
MusicServiceStarter.startServiceWithCommand(context, command = "play", song = song)
@ -1220,7 +1224,7 @@ object AgentService : CoroutineScope {
* @param singer 歌手,默认为false
* @return 是否成功开始处理
*/
fun processMusicPlayList(songs:List<Map<String,String>>){
fun processMusicPlayList(songs:List<Map<String,Any>>){
// 如果 playlist 中有有效的歌曲,开始播放
if (songs.isNotEmpty()) {
MusicServiceStarter.startServiceWithPlaylist(context, command = "playlist", songs = songs)

26
local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicService.kt

@ -37,7 +37,7 @@ class MusicService : MediaBrowserServiceCompat() {
private val isRunning = AtomicBoolean(false)
private var currentUrlInternal: String? = null
private var currentStatus = false
private var playlist = mutableListOf<Map<String,String>>()
private var playlist = mutableListOf<Map<String,Any>>()
private var currentIndex = 0
fun isServiceRunning() = isRunning.get()
@ -86,7 +86,7 @@ class MusicService : MediaBrowserServiceCompat() {
val command = intent?.getStringExtra("command") ?: return START_NOT_STICKY
when (command) {
"play" -> intent.getSerializableExtra("song")?.let { url ->
val songMap = intent.getSerializableExtra("song") as? Map<String, String>
val songMap = intent.getSerializableExtra("song") as? Map<String, Any>
songMap?.let { song ->
val songId = song["id"] ?: return@let
@ -319,15 +319,15 @@ class MusicService : MediaBrowserServiceCompat() {
}
}
private fun play(song: Map<String,String>) {
private fun play(song: Map<String,Any>) {
val url = song["url"] ?: return
Log.i(TAG, "开始播放: $url")
val title = song["title"] ?: "未知标题"
val artist = song["artist"] ?: "未知歌手"
val coverUrl = song["coverUrl"]
val title = song["title"] as? String ?: "未知标题"
val artist = song["artist"] as? String ?: "未知歌手"
val coverUrl = song["coverUrl"] as String
mediaPlayer?.release()
currentStatus = true
currentUrlInternal = url
currentUrlInternal = url as String
mediaPlayer = MediaPlayer().apply {
setDataSource(url)
setAudioStreamType(AudioManager.STREAM_MUSIC)
@ -356,9 +356,9 @@ class MusicService : MediaBrowserServiceCompat() {
.build()
)
val song = playlist[currentIndex]
val title = song["title"] ?: "未知标题"
val artist = song["artist"] ?: "未知歌手"
val coverUrl = song["coverUrl"]
val title = song["title"] as? String ?: "未知标题"
val artist = song["artist"] as? String ?: "未知歌手"
val coverUrl = song["coverUrl"] as String
updateNotification(title,artist,coverUrl)
setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
Log.i(TAG, "已暂停")
@ -376,9 +376,9 @@ class MusicService : MediaBrowserServiceCompat() {
.build()
)
val song = playlist[currentIndex]
val title = song["title"] ?: "未知标题"
val artist = song["artist"] ?: "未知歌手"
val coverUrl = song["coverUrl"]
val title = song["title"] as? String ?: "未知标题"
val artist = song["artist"] as? String ?: "未知歌手"
val coverUrl = song["coverUrl"] as String
updateNotification(title,artist,coverUrl)
setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
// Log.i(TAG, "继续播放")

4
local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServicePlugin.kt

@ -27,7 +27,7 @@ class MusicServicePlugin: FlutterPlugin, MethodCallHandler {
Log.d(TAG, "Method call: ${call.method}")
when (call.method) {
"play" -> {
val song = call.argument<Map<String, String>>("song")
val song = call.argument<Map<String, Any>>("song")
if (song != null) {
MusicServiceStarter.startServiceWithCommand(context, "play", song)
result.success(null)
@ -36,7 +36,7 @@ class MusicServicePlugin: FlutterPlugin, MethodCallHandler {
}
}
"playlist" -> {
val songs = call.argument<List<Map<String, String>>>("songs")
val songs = call.argument<List<Map<String, Any>>>("songs")
if (songs != null && songs.isNotEmpty()) {
MusicServiceStarter.startServiceWithPlaylist(context, "playlist", songs)
result.success(null)

6
local_plugins/music_service/android/src/main/kotlin/com/yunqiinnovation/music_service/MusicServiceStarter.kt

@ -18,7 +18,7 @@ import java.util.jar.Manifest
object MusicServiceStarter {
private val TAG = "MusicServiceStarter"
//播放单曲
fun startServiceWithCommand(context: Context, command: String, song: Map<String,String>? = null, volume: Float? = null) {
fun startServiceWithCommand(context: Context, command: String, song: Map<String,Any>? = null, volume: Float? = null) {
Log.i(TAG, "liwei------------------- startServiceWithCommand: $command $song")
val intent = Intent(context, MusicService::class.java).apply {
putExtra("command", command)
@ -38,9 +38,9 @@ object MusicServiceStarter {
}
//启动播放列表
fun startServiceWithPlaylist(context: Context,command: String, songs: List<Map<String,String>>,volume: Float? = null) {
fun startServiceWithPlaylist(context: Context,command: String, songs: List<Map<String,Any>>,volume: Float? = null) {
Log.i(TAG, "liwei------------------- startServiceWithPlaylist: $command $songs")
val serializableList = ArrayList<HashMap<String, String>>().apply {
val serializableList = ArrayList<HashMap<String, Any>>().apply {
songs.forEach { add(HashMap(it)) }
}
val intent = Intent(context, MusicService::class.java).apply {

2
local_plugins/music_service/lib/music_service.dart

@ -39,7 +39,7 @@ class MusicService {
_statusStream = _statusEventChannel.receiveBroadcastStream();
}
Future<void> playMusic(Map<String, String> song) async {
Future<void> playMusic(Map<String, dynamic> song) async {
await _methodChannel.invokeMethod('play', {'song': song});
}

Loading…
Cancel
Save