فهرست منبع

add:歌曲播放支持歌词显示v1

tujidelv 11 ماه پیش
والد
کامیت
f12902a503

+ 39 - 0
src/main/java/top/lvzhiqiang/controller/MusicInfoController.java

@@ -3,14 +3,17 @@ package top.lvzhiqiang.controller;
 import com.alibaba.fastjson.JSON;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
+import top.lvzhiqiang.config.InitRunner;
 import top.lvzhiqiang.dto.MusicInfoQuery;
 import top.lvzhiqiang.dto.R;
 import top.lvzhiqiang.exception.ParameterException;
 import top.lvzhiqiang.service.MusicInfoService;
 import top.lvzhiqiang.util.DateUtils;
+import top.lvzhiqiang.util.FtpUtil;
 import top.lvzhiqiang.util.StringUtils;
 
 import javax.annotation.Resource;
+import java.io.*;
 import java.time.LocalDate;
 import java.util.HashMap;
 
@@ -103,6 +106,42 @@ public class MusicInfoController {
         return R.ok().data(musicInfoService.musicDetail(symbol, operationType));
     }
 
+    @RequestMapping(value = "/requestFile")
+    public Object requestFile(String url) {
+        String ftpBaseUrl = InitRunner.dicCodeMap.get("ftp_baseurl").getCodeValue();
+        String url_ = url.substring(url.indexOf(ftpBaseUrl) + ftpBaseUrl.length());
+
+        BufferedReader reader = null;
+        StringBuilder lrcContent = new StringBuilder();
+        try {
+            byte[] fileContent = FtpUtil.downloadFile(new File(url_));
+
+            // 将字节数组转换为字符串(LRC 文件的内容)
+            if (fileContent != null) {
+                reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(fileContent)));
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    if (line.contains("\"") || line.contains("offset")){
+                        continue;
+                    }
+                    lrcContent.append(line).append("\n");
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                // 关闭 FTP 连接和文件读取器
+                if (reader != null) {
+                    reader.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return R.ok().data(lrcContent.toString());
+    }
 
     public static void main(String[] args) {
         String s = "https://image.baidu.com/search/down?thumburl=https://baidu.com&url=https://tvax3.sinaimg.cn/large/006BNqYCly1ht929ixe8mj309s0dojsb.jpg";

+ 65 - 4
src/main/java/top/lvzhiqiang/util/FtpUtil.java

@@ -6,10 +6,7 @@ import org.apache.commons.net.ftp.FTPClient;
 import org.apache.commons.net.ftp.FTPFile;
 import org.apache.commons.net.ftp.FTPReply;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.*;
 import java.net.SocketException;
 import java.time.LocalDate;
 import java.util.Random;
@@ -181,6 +178,70 @@ public class FtpUtil {
         }
     }
 
+    public static byte[] downloadFile(File filePath) {
+        FTPClient ftpClient = null;
+        ByteArrayOutputStream outputStream = null;
+        InputStream inputStream = null;
+        try {
+            ftpClient = genFtpClient();
+
+            // 设置为被动模式
+            ftpClient.enterLocalPassiveMode();
+
+            // 下载文件
+            // 转移到FTP服务器目录
+            String replace = filePath.getParent().replace("\\", "/");
+            String[] split = replace.split("/");
+            for (String s : split) {
+                if (StringUtils.isEmpty(s)) {
+                    continue;
+                }
+                ftpClient.changeWorkingDirectory(s);
+            }
+
+            FTPFile[] fs = ftpClient.listFiles();
+            String fileName = filePath.getName();
+            for (FTPFile ff : fs) {
+                if (ff.getName().equals(fileName)) {
+                    inputStream = ftpClient.retrieveFileStream(ff.getName()); // 获取输入流
+                }
+            }
+
+            outputStream = new ByteArrayOutputStream();
+            if (inputStream != null) {
+                // 读取文件内容
+                byte[] buffer = new byte[4096];
+                int bytesRead;
+                while ((bytesRead = inputStream.read(buffer)) != -1) {
+                    outputStream.write(buffer, 0, bytesRead);
+                }
+            } else {
+                return null;
+            }
+        } catch (SocketException e) {
+            log.error("文件下载-连接错误,filePath={}", filePath, e);
+        } catch (Exception e) {
+            log.error("文件下载-失败,filePath={}", filePath, e);
+        } finally {
+            if (ftpClient.isConnected()) {
+                try {
+                    if (inputStream != null) {
+                        inputStream.close();
+                    }
+                    if (outputStream != null) {
+                        outputStream.close();
+                    }
+
+                    ftpClient.logout();
+                    ftpClient.disconnect();
+                } catch (IOException ioe) {
+                }
+            }
+        }
+
+        return outputStream.toByteArray(); // 获取文件内容的字节数组
+    }
+
     public static FTPClient genFtpClient() throws IOException {
         return genFtpClient(host, port, userName, passWord);
     }

+ 7 - 4
src/main/resources/static/coin.html

@@ -537,10 +537,13 @@
     </div>
     <div id="apis-quiet-content" style="display: none;">
         total:<span class="contentSPAN">0</span><span class="contentSPAN2"></span>
-        <audio controls style="display: none;">
-            <source src="" type="audio/mpeg">
-            您的浏览器不支持音频播放。
-        </audio>
+        <div style="display: none;" id="audioDiv">
+            <audio id="audio" controls style="display: inline-block;">
+                <source src="" type="audio/mpeg">
+                您的浏览器不支持音频播放。
+            </audio>
+            <div id="lyrics" style="display: inline-block;"></div>
+        </div>
         <table border="1" cellspacing="0">
             <div class="quiet-loading"><img src='cover/loading.gif'></div>
             <thead>

+ 75 - 11
src/main/resources/static/js/my-coin.js

@@ -288,9 +288,9 @@ function handleSelectChange(objj) {
             }
 
             if (nameEn === 'music') {
-                $("#apis-quiet-content > audio").css("display", "block");
+                $("#audioDiv").css("display", "block");
 
-                $("#apis-quiet-content > audio").on('ended', function () {
+                $("#audio").on('ended', function () {
                     let totalCount = $("div#apis-quiet-content").find("span.contentSPAN").text();
                     if (totalCount === 0) {
                         return;
@@ -367,9 +367,9 @@ function handleSelectChange(objj) {
                     }
                 });
             } else {
-                $("#apis-quiet-content > audio").css("display", "none");
-                $("#apis-quiet-content > audio > source").attr("src", "");
-                $("#apis-quiet-content > audio")[0].load();
+                $("#audioDiv").css("display", "none");
+                $("#audio > source").attr("src", "");
+                $("#audio")[0].load();
             }
 
             if (nameEn === 'currentHolding') {
@@ -882,16 +882,80 @@ function initContentEvent(nameEn) {
                 symbol = symbol2;
             }
 
-            var currentSrc = $("#apis-quiet-content > audio > source").attr("src");
+            var currentSrc = $("#audio > source").attr("src");
+
+            // 歌词展示
+            const audio = $("#audio")[0];
+            // 清除 ontimeupdate 事件处理函数
+            audio.ontimeupdate = null;
+            //$(audio).unbind("timeupdate");
+            const lyricsContainer = $('#lyrics');
+            lyricsContainer.text('');
+            $.ajax({
+                url: "musicInfo/requestFile",  // 替换为实际的 LRC 文件 URL
+                data: {
+                    "url": symbol.replace('mp3', 'lrc')
+                },
+                type: "post", //请求方式
+                async: false, //请求是否异步,默认为异步,这也是ajax重要特性
+                success: function (data) {
+                    if (data == null || $.trim(data) == "" || !data.success) {
+                        lyricsContainer.text("未获取到歌词!");
+                        return;
+                    }
+
+                    const lrc = data.data;
+                    if (lrc === '') {
+                        lyricsContainer.text("未获取到歌词!");
+                        return;
+                    }
+
+                    // 将 LRC 歌词解析为数组
+                    const lyrics = lrc.trim().split('\n').filter(line => {
+                        // 使用正则检查是否符合 [mm:ss.xx] 或 [mm:ss.xxx] 格式
+                        return /^\[\d{2}:\d{2}(\.\d{2,3})?\]/.test(line) && line !== null;
+                    }).map(function (line) {
+                        let indexOf = line.lastIndexOf("]");
+                        const time = line.substring(1, indexOf).split(":");
+                        const text = line.substring(indexOf + 1).trim();
+                        const seconds = parseInt(time[0]) * 60 + parseFloat(time[1]);
+                        return {time: seconds, text: text};
+                    });
+
+                    // 实时更新歌词
+                    $(audio).on('timeupdate', function () {
+                        const currentTime = audio.currentTime;
+                        let currentLyric = null;
+
+                        // 查找当前播放时间对应的歌词
+                        for (let i = 0; i < lyrics.length; i++) {
+                            if (currentTime >= lyrics[i].time) {
+                                currentLyric = lyrics[i];
+                            } else {
+                                break;
+                            }
+                        }
+
+                        if (currentLyric) {
+                            // 更新歌词内容
+                            lyricsContainer.text(currentLyric.text);
+                        }
+                    });
+                },
+                error: function () {
+                    lyricsContainer.text("未获取到歌词!");
+                }
+            });
+
             if (currentSrc === symbol) {
-                $("#apis-quiet-content > audio")[0].play();
+                $("#audio")[0].play();
             } else {
-                $("#apis-quiet-content > audio > source").attr("src", symbol);
-                $("#apis-quiet-content > audio")[0].load();
-                $("#apis-quiet-content > audio")[0].play();
+                $("#audio > source").attr("src", symbol);
+                $("#audio")[0].load();
+                $("#audio")[0].play();
             }
 
-            $("#apis-quiet-content > audio")[0].volume = $("#apis-quiet-div-music-playVolumeField").val();
+            $("#audio")[0].volume = $("#apis-quiet-div-music-playVolumeField").val();
 
             $("#apis-quiet-content").find(".contentTD > tr").find('td:nth-child(2)').removeClass("music_highlight");
             $(this).parent("td").parent("tr").find('td:nth-child(2)').addClass("music_highlight");