From ce7acc08d09e6a326304b23ee2bff2d8ba27d758 Mon Sep 17 00:00:00 2001 From: cph Date: Tue, 8 Oct 2024 22:37:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=AC=E6=AD=8C=E8=AF=86=E6=9B=B2=E5=8D=87?= =?UTF-8?q?=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chrome-plugin-test/background.js | 42 ++++ chrome-plugin-test/content.js | 181 ++++++++++-------- chrome-plugin-test/manifest.json | 8 +- musicBackend/pom.xml | 6 + .../main/java/com/cph/musicbackend/Test.java | 58 ++++++ .../config/ACRCloudRecognizerConfig.java | 26 +++ .../controller/MusicController.java | 11 +- .../cph/musicbackend/rd3/AcrCloudUtil.java | 48 +++++ .../rd3/{ => xunfei}/FileUtil.java | 2 +- .../rd3/{ => xunfei}/HttpUtil.java | 2 +- .../rd3/{ => xunfei}/MusicRecUtil.java | 2 +- src/utils/api.js | 4 +- 12 files changed, 297 insertions(+), 93 deletions(-) create mode 100644 musicBackend/src/main/java/com/cph/musicbackend/Test.java create mode 100644 musicBackend/src/main/java/com/cph/musicbackend/config/ACRCloudRecognizerConfig.java create mode 100644 musicBackend/src/main/java/com/cph/musicbackend/rd3/AcrCloudUtil.java rename musicBackend/src/main/java/com/cph/musicbackend/rd3/{ => xunfei}/FileUtil.java (97%) rename musicBackend/src/main/java/com/cph/musicbackend/rd3/{ => xunfei}/HttpUtil.java (97%) rename musicBackend/src/main/java/com/cph/musicbackend/rd3/{ => xunfei}/MusicRecUtil.java (99%) diff --git a/chrome-plugin-test/background.js b/chrome-plugin-test/background.js index 394d2b9..b04f2ce 100644 --- a/chrome-plugin-test/background.js +++ b/chrome-plugin-test/background.js @@ -1,3 +1,45 @@ chrome.action.onClicked.addListener((tab) => { chrome.tabs.sendMessage(tab.id, {action: "togglePlayer"}); +}); + +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.action === "sendAudio") { + fetch('https://app102.acapp.acwing.com.cn/api/uploadAudio', { + method: 'POST', + body: request.formData + }) + .then(response => response.json()) + .then(data => { + sendResponse({success: true, data: data}); + }) + .catch(error => { + console.error('Error:', error); + sendResponse({success: false, error: error.message}); + }); + return true; // 保持消息通道开放 + } + + if (request.action === "addSong") { + fetch('https://app102.acapp.acwing.com.cn/api/add', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(request.data) + }) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + sendResponse({success: true, data: data}); + }) + .catch(error => { + console.error('Error:', error); + sendResponse({success: false, error: error.message}); + }); + return true; // 保持消息通道开放 + } }); \ No newline at end of file diff --git a/chrome-plugin-test/content.js b/chrome-plugin-test/content.js index 72d96ec..3210a33 100644 --- a/chrome-plugin-test/content.js +++ b/chrome-plugin-test/content.js @@ -1,8 +1,12 @@ +// 在文件顶部添加这行代码来进行调试 +console.log('Content script loaded'); + let playerVisible = false; let player; let mediaRecorder; let audioChunks = []; let recordingTimeout; +let isRecording = false; chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === "togglePlayer") { @@ -16,7 +20,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { function createPlayer() { player = document.createElement('div'); player.id = 'ease-music-player'; - + const targetUrl = "https://app102.acapp.acwing.com.cn"; player.innerHTML = ` @@ -38,66 +42,69 @@ function createPlayer() { function togglePlayer() { playerVisible = !playerVisible; player.style.display = playerVisible ? 'block' : 'none'; - + // 移除这行代码,不再修改body的overflow // document.body.style.overflowY = playerVisible ? 'hidden' : ''; } function toggleRecording() { const recordButton = document.getElementById('ease-music-record'); - if (mediaRecorder && mediaRecorder.state === 'recording') { + if (isRecording) { stopRecording(); recordButton.textContent = '开始识别'; + isRecording = false; } else { startRecording(); recordButton.textContent = '识别中...'; + isRecording = true; } } function startRecording() { + console.log('startRecording function called'); // 添加这行来调试 audioChunks = []; - + // 创建一个新的 AudioContext,设置采样率为 16000Hz const audioContext = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: 16000 }); - + // 创建一个 MediaStreamDestination const destination = audioContext.createMediaStreamDestination(); - + // 获取页面中所有的 audio 和 video 元素 const mediaElements = document.querySelectorAll('audio, video'); - + mediaElements.forEach(element => { if (element.captureStream) { // 捕获媒体元素的音频流 const stream = element.captureStream(); const source = audioContext.createMediaStreamSource(stream); - + // 创建一个 ScriptProcessorNode 用于重采样 const bufferSize = 4096; const scriptNode = audioContext.createScriptProcessor(bufferSize, 1, 1); - + scriptNode.onaudioprocess = (audioProcessingEvent) => { const inputBuffer = audioProcessingEvent.inputBuffer; const outputBuffer = audioProcessingEvent.outputBuffer; - + for (let channel = 0; channel < outputBuffer.numberOfChannels; channel++) { const inputData = inputBuffer.getChannelData(channel); const outputData = outputBuffer.getChannelData(channel); - + // 简单的线性插值重采样 for (let i = 0; i < outputBuffer.length; i++) { const index = i * inputBuffer.sampleRate / 16000; const indexFloor = Math.floor(index); const indexCeil = Math.ceil(index); const fraction = index - indexFloor; - + outputData[i] = (1 - fraction) * inputData[indexFloor] + fraction * inputData[indexCeil]; } } }; - + source.connect(scriptNode); scriptNode.connect(destination); } @@ -105,7 +112,7 @@ function startRecording() { // 创建 MediaRecorder mediaRecorder = new MediaRecorder(destination.stream); - + mediaRecorder.ondataavailable = event => { audioChunks.push(event.data); }; @@ -118,15 +125,19 @@ function startRecording() { }; mediaRecorder.start(); - - console.log('实际采样率:', audioContext.sampleRate); + + console.log('Recording started, actual sample rate:', audioContext.sampleRate); } function stopRecording() { - if (mediaRecorder) { + console.log('stopRecording function called'); // 添加这行来调试 + if (mediaRecorder && mediaRecorder.state === 'recording') { mediaRecorder.stop(); clearTimeout(recordingTimeout); mediaRecorder.stream.getTracks().forEach(track => track.stop()); + console.log('Recording stopped'); + } else { + console.log('No active recording to stop'); } } @@ -135,18 +146,18 @@ function convertToWav(webmBlob) { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const fileReader = new FileReader(); - fileReader.onload = function(event) { + fileReader.onload = function (event) { const audioData = event.target.result; - audioContext.decodeAudioData(audioData, function(buffer) { + audioContext.decodeAudioData(audioData, function (buffer) { const wavBlob = audioBufferToWav(buffer); resolve(wavBlob); - }, function(e) { + }, function (e) { reject('音频解码失败'); }); }; - fileReader.onerror = function(error) { + fileReader.onerror = function (error) { reject('文件读取失败'); }; @@ -252,25 +263,40 @@ function sendAudioToServer(audioBlob) { const formData = new FormData(); var timestamp = Date.parse(new Date()); formData.append('audio', audioBlob, timestamp + '.wav'); - + fetch('https://app102.acapp.acwing.com.cn/api/uploadAudio', { - // fetch('http://localhost:8809/api/uploadAudio', { method: 'POST', + mode: 'cors', + credentials: 'include', + headers: { + 'Origin': 'chrome-extension://' + chrome.runtime.id + }, body: formData }) - .then(response => response.json()) + .then(response => { + console.log('Response status:', response.status); + console.log('Response headers:', response.headers); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); + }) .then(data => { console.log('服务器响应:', data); - if (data.code === "0" && data.data && Array.isArray(data.data)) { - displaySongList(data.data); + if (data.artist && data.title) { + displaySongResult(data); } else { console.error('未能识别到歌曲'); } }) - .catch(error => console.error('上传错误:', error)); + .catch(error => { + console.error('上传错误:', error); + console.error('Error stack:', error.stack); + // 在这里添加错误处理逻辑,比如显示一个错误消息给用户 + }); } -function displaySongList(songs) { +function displaySongResult(song) { const resultDiv = document.createElement('div'); resultDiv.id = 'song-recognition-result'; resultDiv.style.cssText = ` @@ -282,44 +308,36 @@ function displaySongList(songs) { padding: 15px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.5); - max-height: 60vh; width: 300px; - overflow-y: auto; z-index: 10001; font-family: Arial, sans-serif; font-size: 14px; `; let html = '
拖动此处移动结果
'; - html += '

识别结果

'; - + html += '

识别结果

'; + + html += ` +
+
+ ${song.title}
+ ${song.artist} +
+ +
`; + const closeButton = document.createElement('button'); closeButton.id = 'close-result'; closeButton.textContent = '关闭'; @@ -337,7 +355,7 @@ function displaySongList(songs) { border-radius: 4px; transition: background-color 0.3s; `; - + resultDiv.innerHTML = html; resultDiv.appendChild(closeButton); document.body.appendChild(resultDiv); @@ -354,19 +372,15 @@ function displaySongList(songs) { document.body.removeChild(resultDiv); }); - // 为每个"添加"按钮添加点击事件 - resultDiv.querySelectorAll('.add-song-info').forEach(button => { - button.addEventListener('click', function() { - const index = this.getAttribute('data-index'); - const song = displaySongs[index]; - sendSongInfo(song.song, song.singer); - - // 点击后变灰并禁用 - this.style.backgroundColor = '#cccccc'; - this.style.cursor = 'default'; - this.disabled = true; - this.textContent = '已添加'; - }); + // 为"添加"按钮添加点击事件 + document.getElementById('add-song-info').addEventListener('click', function () { + sendSongInfo(song.title, song.artist); + + // 点击后变灰并禁用 + this.style.backgroundColor = '#cccccc'; + this.style.cursor = 'default'; + this.disabled = true; + this.textContent = '已添加'; }); makeDraggable(resultDiv, document.getElementById('result-header')); @@ -374,17 +388,16 @@ function displaySongList(songs) { function sendSongInfo(songName, singerName) { console.log(`添加歌曲信息:${songName} - ${singerName}`); - // fetch('http://localhost:8809/api/add', { - fetch('https://app102.acapp.acwing.com.cn/api/add', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ title: songName, artist: singerName }), - }) - .then(response => response.json()) - .then(data => console.log('API response:', data)) - .catch(error => console.error('Error:', error)); + chrome.runtime.sendMessage({ + action: "addSong", + data: { title: songName, artist: singerName } + }, response => { + if (response.success) { + console.log('API response:', response.data); + } else { + console.error('Error:', response.error); + } + }); } function makeDraggable(element, dragHandle) { diff --git a/chrome-plugin-test/manifest.json b/chrome-plugin-test/manifest.json index 82db1fd..9baee69 100644 --- a/chrome-plugin-test/manifest.json +++ b/chrome-plugin-test/manifest.json @@ -3,7 +3,13 @@ "name": "EaseMusic", "version": "1.0", "description": "一个简单的音乐播放Chrome插件", - "permissions": ["activeTab"], + "permissions": [ + "activeTab", + "https://app102.acapp.acwing.com.cn/*" + ], + "host_permissions": [ + "https://app102.acapp.acwing.com.cn/*" + ], "action": { "default_icon": { "16": "icon16.png", diff --git a/musicBackend/pom.xml b/musicBackend/pom.xml index e757b1a..286e760 100644 --- a/musicBackend/pom.xml +++ b/musicBackend/pom.xml @@ -98,6 +98,12 @@ org.springframework.boot spring-boot-starter-aop + + + com.acrcloud.sdks + com.acrcloud.sdks.recognizer + 1.0.4 + diff --git a/musicBackend/src/main/java/com/cph/musicbackend/Test.java b/musicBackend/src/main/java/com/cph/musicbackend/Test.java new file mode 100644 index 0000000..c69558c --- /dev/null +++ b/musicBackend/src/main/java/com/cph/musicbackend/Test.java @@ -0,0 +1,58 @@ +package com.cph.musicbackend; + +import java.io.*; +import java.util.Map; +import java.util.HashMap; + +import com.acrcloud.utils.ACRCloudRecognizer; + +public class Test { + + public static void main(String[] args) { + Map config = new HashMap(); + + config.put("host", "identify-cn-north-1.acrcloud.cn"); + config.put("access_key", "3076056eb203a361d9341411191e1e25"); + config.put("access_secret", "TRlwfLkzv8orvm7gIePYvaM8wJvLWMJBBTrJujvQ"); + + config.put("debug", false); + config.put("timeout", 10); // seconds + + ACRCloudRecognizer re = new ACRCloudRecognizer(config); + + // It will skip 80 seconds. + String filename = "D:\\audio\\1.mp3"; + String result = re.recognizeByFile(filename, 1); + System.out.println(result); + + File file = new File(filename); + byte[] buffer = new byte[3 * 1024 * 1024]; + if (!file.exists()) { + return; + } + FileInputStream fin = null; + int bufferLen = 0; + try { + fin = new FileInputStream(file); + bufferLen = fin.read(buffer, 0, buffer.length); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (fin != null) { + fin.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + System.out.println("bufferLen=" + bufferLen); + + if (bufferLen <= 0) + return; + + // It will skip 80 seconds from the begginning of (buffer). + result = re.recognizeByFileBuffer(buffer, bufferLen, 1); + System.out.println(result); + } +} \ No newline at end of file diff --git a/musicBackend/src/main/java/com/cph/musicbackend/config/ACRCloudRecognizerConfig.java b/musicBackend/src/main/java/com/cph/musicbackend/config/ACRCloudRecognizerConfig.java new file mode 100644 index 0000000..1f0d3b4 --- /dev/null +++ b/musicBackend/src/main/java/com/cph/musicbackend/config/ACRCloudRecognizerConfig.java @@ -0,0 +1,26 @@ +package com.cph.musicbackend.config; + +import com.acrcloud.utils.ACRCloudRecognizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class ACRCloudRecognizerConfig { + @Bean + public ACRCloudRecognizer acrCloudRecognizer() + { + Map config = new HashMap(); + + config.put("host", "identify-cn-north-1.acrcloud.cn"); + config.put("access_key", "3076056eb203a361d9341411191e1e25"); + config.put("access_secret", "TRlwfLkzv8orvm7gIePYvaM8wJvLWMJBBTrJujvQ"); + + config.put("debug", false); + config.put("timeout", 10); // seconds + + return new ACRCloudRecognizer(config); + } +} \ No newline at end of file diff --git a/musicBackend/src/main/java/com/cph/musicbackend/controller/MusicController.java b/musicBackend/src/main/java/com/cph/musicbackend/controller/MusicController.java index b8e787a..a0ef282 100644 --- a/musicBackend/src/main/java/com/cph/musicbackend/controller/MusicController.java +++ b/musicBackend/src/main/java/com/cph/musicbackend/controller/MusicController.java @@ -8,7 +8,8 @@ import com.cph.musicbackend.entity.User; import com.cph.musicbackend.mapper.MusicMapper; import com.cph.musicbackend.mapper.UserMapper; -import com.cph.musicbackend.rd3.MusicRecUtil; +import com.cph.musicbackend.rd3.AcrCloudUtil; +import com.cph.musicbackend.rd3.xunfei.MusicRecUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.util.Assert; @@ -17,13 +18,11 @@ import java.io.File; import java.io.IOException; -import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.stream.Collectors; @RestController -@CrossOrigin public class MusicController { @@ -36,6 +35,9 @@ public class MusicController { @Autowired UserMapper userMapper; + @Autowired + AcrCloudUtil acrCloudUtil; + @GetMapping("/api/musicList") @RecognizeAddress public List getMusciList() { @@ -114,7 +116,8 @@ public Object recongnizeMusic(@RequestParam("audio") MultipartFile file) { // 保存文件 File destFile = new File(dir.getAbsolutePath() + File.separator + fileName); file.transferTo(destFile); - return MusicRecUtil.recongnizeFile(dir.getAbsolutePath() + File.separator + fileName); + return acrCloudUtil.recongizeByFile(dir.getAbsolutePath() + File.separator + fileName); +// return MusicRecUtil.recongnizeFile(dir.getAbsolutePath() + File.separator + fileName); } catch (IOException e) { return "{\"error\": \"" + e.getMessage() + "\"}"; diff --git a/musicBackend/src/main/java/com/cph/musicbackend/rd3/AcrCloudUtil.java b/musicBackend/src/main/java/com/cph/musicbackend/rd3/AcrCloudUtil.java new file mode 100644 index 0000000..b97b690 --- /dev/null +++ b/musicBackend/src/main/java/com/cph/musicbackend/rd3/AcrCloudUtil.java @@ -0,0 +1,48 @@ +package com.cph.musicbackend.rd3; + +import com.acrcloud.utils.ACRCloudRecognizer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@Component +public class AcrCloudUtil { + @Autowired + public ACRCloudRecognizer re; + + public Map recongizeByFile(String filename) { + HashMap resultMap = new HashMap<>(); + String result = re.recognizeByFile(filename, 1); + // 使用示例结果进行解析 +// String result = "{\"status\":{\"msg\":\"Success\",\"version\":\"1.0\",\"code\":0},\"metadata\":{\"timestamp_utc\":\"2024-10-08 12:08:34\",\"music\":[{\"lan\":\"国语\",\"duration_ms\":200000,\"external_ids\":{},\"db_begin_time_offset_ms\":4080,\"artists\":[{\"name\":\"周杰伦\"}],\"db_end_time_offset_ms\":14600,\"sample_begin_time_offset_ms\":0,\"sample_end_time_offset_ms\":10520,\"play_offset_ms\":15540,\"result_from\":3,\"title\":\"前世情人\",\"label\":\"JVR\",\"score\":100,\"acrid\":\"e3c887cf408db8991a68a843f1afd5a0\",\"language\":\"zh\",\"external_metadata\":{},\"release_date\":\"2016-06-24\",\"album\":{\"name\":\"周杰伦的床边故事\"}},{\"duration_ms\":200460,\"external_ids\":{},\"db_begin_time_offset_ms\":4060,\"artists\":[{\"name\":\"周杰伦\"}],\"label\":\"杰威尔音乐有限公司\",\"db_end_time_offset_ms\":14500,\"sample_begin_time_offset_ms\":0,\"sample_end_time_offset_ms\":10440,\"play_offset_ms\":15520,\"result_from\":3,\"title\":\"前世情人\",\"score\":100,\"language\":\"zh\",\"acrid\":\"0eb1c19384ec445e15c6cb3c11c706ba\",\"release_date\":\"2016-06-24\",\"external_metadata\":{},\"album\":{\"name\":\"周杰伦的床边故事\"}}]},\"result_type\":0,\"cost_time\":0.029999971389771}"; + System.out.println(result); + + try { + JsonObject jsonObject = JsonParser.parseString(result).getAsJsonObject(); + JsonObject metadata = jsonObject.getAsJsonObject("metadata"); + JsonElement musicElement = metadata.get("music"); + + if (musicElement != null && musicElement.isJsonArray() && musicElement.getAsJsonArray().size() > 0) { + JsonObject musicObject = musicElement.getAsJsonArray().get(0).getAsJsonObject(); + + String title = musicObject.get("title").getAsString(); + String artist = musicObject.getAsJsonArray("artists").get(0).getAsJsonObject().get("name").getAsString(); + + resultMap.put("title", title); + resultMap.put("artist", artist); + } + } catch (Exception e) { + System.err.println("解析结果时发生错误: " + e.getMessage()); + } + + return resultMap; + } +} \ No newline at end of file diff --git a/musicBackend/src/main/java/com/cph/musicbackend/rd3/FileUtil.java b/musicBackend/src/main/java/com/cph/musicbackend/rd3/xunfei/FileUtil.java similarity index 97% rename from musicBackend/src/main/java/com/cph/musicbackend/rd3/FileUtil.java rename to musicBackend/src/main/java/com/cph/musicbackend/rd3/xunfei/FileUtil.java index b3acbbd..c316045 100644 --- a/musicBackend/src/main/java/com/cph/musicbackend/rd3/FileUtil.java +++ b/musicBackend/src/main/java/com/cph/musicbackend/rd3/xunfei/FileUtil.java @@ -1,4 +1,4 @@ -package com.cph.musicbackend.rd3; +package com.cph.musicbackend.rd3.xunfei; import java.io.*; diff --git a/musicBackend/src/main/java/com/cph/musicbackend/rd3/HttpUtil.java b/musicBackend/src/main/java/com/cph/musicbackend/rd3/xunfei/HttpUtil.java similarity index 97% rename from musicBackend/src/main/java/com/cph/musicbackend/rd3/HttpUtil.java rename to musicBackend/src/main/java/com/cph/musicbackend/rd3/xunfei/HttpUtil.java index 966837a..476b247 100644 --- a/musicBackend/src/main/java/com/cph/musicbackend/rd3/HttpUtil.java +++ b/musicBackend/src/main/java/com/cph/musicbackend/rd3/xunfei/HttpUtil.java @@ -1,4 +1,4 @@ -package com.cph.musicbackend.rd3; +package com.cph.musicbackend.rd3.xunfei; import java.io.BufferedOutputStream; import java.io.BufferedReader; diff --git a/musicBackend/src/main/java/com/cph/musicbackend/rd3/MusicRecUtil.java b/musicBackend/src/main/java/com/cph/musicbackend/rd3/xunfei/MusicRecUtil.java similarity index 99% rename from musicBackend/src/main/java/com/cph/musicbackend/rd3/MusicRecUtil.java rename to musicBackend/src/main/java/com/cph/musicbackend/rd3/xunfei/MusicRecUtil.java index 27ceb06..900684a 100644 --- a/musicBackend/src/main/java/com/cph/musicbackend/rd3/MusicRecUtil.java +++ b/musicBackend/src/main/java/com/cph/musicbackend/rd3/xunfei/MusicRecUtil.java @@ -1,4 +1,4 @@ -package com.cph.musicbackend.rd3; +package com.cph.musicbackend.rd3.xunfei; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; diff --git a/src/utils/api.js b/src/utils/api.js index de6fa14..0df6ee1 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -1,7 +1,9 @@ import axios from 'axios'; export const instance = axios.create({ - baseURL: 'https://app102.acapp.acwing.com.cn/api', + // baseURL: 'https://app102.acapp.acwing.com.cn/api', + baseURL: 'http://localhost:8809/api', + headers: { 'Content-Type': 'application/json', }