音乐改成数据库形式(防止内存爆炸)

新增FFmpeg获取元数据方式(无法保证所有数据都能获取到)
预置音乐收藏夹功能
机器人登录后向群里通告
移除广告相关接口
This commit is contained in:
yutou
2020-11-27 18:33:21 +08:00
parent 68bf2d96fb
commit fb9f02a751
22 changed files with 3375 additions and 407 deletions

View File

@@ -0,0 +1,20 @@
package com.yutou.tools.utils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 服务启动后执行
*/
@Component
public class ApplicationInit implements ApplicationRunner {
@Resource
MusicTools musicTools;
@Override
public void run(ApplicationArguments args) throws Exception {
musicTools.init();//初始化音乐库
}
}

View File

@@ -0,0 +1,29 @@
package com.yutou.tools.utils.Interfaces;
import com.yutou.tools.mybatis.model.MusicData;
import java.util.List;
public interface MusicToolsService {
void init();
void scanMusic();
List<MusicData> getPath(String path, boolean isDir);
MusicData getMusicData(String md5);
List<MusicData> findOfTitle(String title);
List<MusicData> findOfArtist(String by);
List<MusicData> getMusicList();
int getLength();
boolean isScan();
String getMusicPath();
byte[] readImage(String path) throws Exception;
}

View File

@@ -1,9 +1,10 @@
package com.yutou.tools.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yutou.tools.home.nas.Data.MusicData;
import com.yutou.tools.home.nas.MusicController;
import com.yutou.tools.mybatis.dao.MusicDataDao;
import com.yutou.tools.mybatis.model.MusicData;
import com.yutou.tools.mybatis.model.MusicDataExample;
import com.yutou.tools.utils.Interfaces.MusicToolsService;
import ealvatag.audio.AudioFile;
import ealvatag.audio.AudioFileIO;
import ealvatag.audio.AudioHeader;
@@ -11,31 +12,44 @@ import ealvatag.audio.exceptions.CannotReadException;
import ealvatag.tag.FieldKey;
import ealvatag.tag.NullTag;
import ealvatag.tag.Tag;
import ealvatag.tag.flac.FlacTag;
import ealvatag.tag.images.NullArtwork;
import net.bramp.ffmpeg.FFmpeg;
import net.bramp.ffmpeg.FFprobe;
import net.bramp.ffmpeg.probe.FFmpegFormat;
import net.bramp.ffmpeg.probe.FFmpegProbeResult;
import net.bramp.ffmpeg.probe.FFmpegStream;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.*;
import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
public class MusicTools {
@Service("musicToolsService")
public class MusicTools implements MusicToolsService {
public static final int FIND_TITLE = 1;
public static final int FIND_ARTIST = 2;
private static MusicTools tools;
private String musicPath = "/media/yutou/4t/public/音乐";
private final List<MusicData> musicList = new ArrayList<>();
private HashMap<String, List<MusicData>> musicMap = new HashMap<String, List<MusicData>>();
private boolean isScan = false;
public static MusicTools getInstance() {
if (tools == null) {
tools = new MusicTools();
}
return tools;
}
@Resource
MusicDataDao musicDataDao;
private MusicTools() {
}
@Override
public void init() {
musicPath = (String) ConfigTools.load(ConfigTools.CONFIG, "musicDir");
scanMusic();
new Timer().schedule(new TimerTask() {
@Override
@@ -49,36 +63,26 @@ public class MusicTools {
}, 0, 10 * 1000);
}
public synchronized void scanMusic() {
if (isScan) {
@Override
public void scanMusic() {
if (isScan || true) {
return;
}
System.out.println("执行扫描");
musicList.clear();
musicMap.clear();
musicPath = (String) ConfigTools.load(ConfigTools.CONFIG, "musicDir");
musicDataDao.truncate();
System.out.println("执行扫描:" + musicPath);
new Thread(() -> {
long startTime = System.currentTimeMillis();
QQBotManager.getInstance().sendMessage("开始扫描音乐夹");
isScan = true;
scan(new File(musicPath));
isScan = false;
System.out.println("扫描完成");
QQBotManager.getInstance().sendMessage("音乐扫描完成,共" + getLength() + "首歌,耗时:"
+ (System.currentTimeMillis() - startTime));
}).start();
}
public List<MusicData> scan(File path, boolean isScanDir) {
if (isScanDir) {
scan(path);
return musicList;
} else {
if (path.exists() && path.isDirectory()) {
List<MusicData> list = new ArrayList<>();
for (File file : path.listFiles()) {
list.add(getMetadata(file));
}
return list;
}
}
return null;
}
private void scan(File path) {
if (path.isFile()) {
@@ -105,6 +109,7 @@ public class MusicTools {
}
}
/**
* 获取指定目录下的音乐
*
@@ -112,46 +117,48 @@ public class MusicTools {
* @param isDir 是否扫描目录下的所有文件false则仅为当前目录
* @return 音乐列表
*/
@Override
public List<MusicData> getPath(String path, boolean isDir) {
List<MusicData> list = new ArrayList<>();
List<MusicData> main;
MusicDataExample example = new MusicDataExample();
String replacement = ConfigTools.load(ConfigTools.CONFIG, "os").equals("windows") ? "\\\\" : "/";
if (isDir) {
if (new File(path).isDirectory() && !path.equals(MusicController.defaultMusicPath)) {
for (String key : musicMap.keySet()) {
if (key.startsWith(path)) {
list.addAll(musicMap.get(key));
}
}
return list;
}
}
if (musicMap.containsKey(path)) {
MusicData tmp = musicMap.get(path).isEmpty() ? null : musicMap.get(path).get(0);
if (tmp != null) {
if (!tmp.getFile().getParent().equals(MusicController.defaultMusicPath)) {
MusicData t2 = new MusicData();
t2.setTitle("返回");
t2.setFile(new File(tmp.getLastDir()));
list.add(t2);
}
}
getDirList(path, list);
list.addAll(musicMap.get(path));
return list;
example.createCriteria().andFileLike(path.replace(File.separator, replacement) + "%");
main = musicDataDao.selectByExample(example);
} else {
if (path.contains(MusicController.defaultMusicPath)) {
MusicData t2 = new MusicData();
t2.setTitle("返回");
t2.setFile(new File(path).getParentFile());
if (!path.equals(MusicController.defaultMusicPath)) {
list.add(t2);
}
getDirList(path, list);
return list;
}
main = musicDataDao.selectByRegexp(path.replace(File.separator, replacement) + replacement + "([^" + replacement + "]+)$");
}
return new ArrayList<>();
System.out.println(path + " " + isDir);
if (!path.equals(MusicController.defaultMusicPath) && !path.equals("root") && main.size() > 0) {
MusicData tmp = main.get(0);
System.out.println(tmp);
MusicData t2 = new MusicData();
t2.setTitle("返回");
t2.setFile(new File(tmp.getLastdir()).getAbsolutePath());
t2.setIsdir(1);
list.add(t2);
}
getDirList(path, list);
list.addAll(main);
System.out.println(list.size());
return list;
}
public List<String> getAllAlbum() {
return musicDataDao.selectAllAlbum();
}
public List<String> getAllArtist() {
return musicDataDao.selectAllArtist();
}
public List<MusicData> selectAlbum(String album) {
return musicDataDao.selectAlbum(album);
}
public List<MusicData> selectArtist(String artist) {
return musicDataDao.selectArtist(artist);
}
private void getDirList(String path, List<MusicData> list) {
@@ -160,7 +167,8 @@ public class MusicTools {
if (listFile.isDirectory()) {
MusicData data = new MusicData();
data.setTitle(listFile.getName());
data.setFile(listFile);
data.setIsdir(1);
data.setFile(listFile.getAbsolutePath());
list.add(data);
}
}
@@ -169,19 +177,28 @@ public class MusicTools {
private void add(File file) {
MusicData data = getMetadata(file);
if (data != null) {
musicList.add(data);
String path = file.getAbsolutePath().replace(File.separator + file.getName(), "");
List<MusicData> list;
if (musicMap.containsKey(path)) {
list = musicMap.get(path);
} else {
list = new ArrayList<>();
try {
musicDataDao.insert(data);
} catch (Exception e) {
e.printStackTrace();
QQBotManager.getInstance().sendMessage("音乐文件添加失败:" + data.toString());
}
list.add(data);
musicMap.put(path, list);
}
}
@Override
public MusicData getMusicData(String path) {
MusicDataExample example = new MusicDataExample();
example.createCriteria().andFileEqualTo(Tools.base64ToString(path));
List<MusicData> list = musicDataDao.selectByExample(example);
if (list != null && list.size() > 0) {
return list.get(0);
}
return null;
}
public MusicData getMetadata(File file) {
try {
if (file.getName().endsWith(".lrc")
@@ -208,7 +225,7 @@ public class MusicTools {
} catch (Exception e) {
}
try {
data.setArtist_sort(tag.getFirst(FieldKey.ARTIST_SORT));
data.setArtistSort(tag.getFirst(FieldKey.ARTIST_SORT));
} catch (Exception e) {
}
try {
@@ -220,7 +237,7 @@ public class MusicTools {
} catch (Exception e) {
}
try {
data.setDisc_no(tag.getFirst(FieldKey.DISC_NO));
data.setDiscNo(tag.getFirst(FieldKey.DISC_NO));
} catch (Exception e) {
}
try {
@@ -240,30 +257,27 @@ public class MusicTools {
data.setYear(tag.getFirst(FieldKey.YEAR));
} catch (Exception e) {
}
data.setFile(file);
data.setFile(file.getAbsolutePath());
data.setIsdir(file.isDirectory() ? 1 : 0);
data.setLastdir(file.getParentFile().getParent());
AudioHeader header = audioFile.getAudioHeader();
data.setBitRate(header.getBitRate());
data.setSampleRate(header.getSampleRate());
data.setNoOfSamples(header.getNoOfSamples());
data.setChannelCount(header.getChannelCount());
data.setEncodingType(header.getEncodingType());
data.setDurationAsDouble(header.getDurationAsDouble());
data.setLossless(header.isLossless());
data.setVariableBitRate(header.isVariableBitRate());
data.setBitrate(header.getBitRate());
data.setSamplerate(header.getSampleRate());
data.setNoofsamples(header.getNoOfSamples());
data.setChannelcount(header.getChannelCount());
data.setEncodingtype(header.getEncodingType());
data.setDurationasdouble(header.getDurationAsDouble());
data.setLossless(header.isLossless() ? 1 : 0);
data.setVariablebitrate(header.isVariableBitRate() ? 1 : 0);
try {
data.setMd5(header.getClass().getMethod("getMd5").invoke(header).toString());
} catch (Exception ignored) {
data.setMd5(Base64.getEncoder().encodeToString(file.getAbsolutePath().getBytes()));
data.setMd5(Tools.getFileMD5(file));
}
return data;
} catch (CannotReadException e) {
MusicData data = getMetadata_jthink(file);
if (data == null)
data = new MusicData();
data.setTitle(file.getName());
data.setFile(file);
return data;
return getMetadata_jthink(file);
} catch (Exception e) {
e.printStackTrace();
}
@@ -284,7 +298,7 @@ public class MusicTools {
} catch (Exception ignored) {
}
try {
data.setArtist_sort(tag.getFirst(org.jaudiotagger.tag.FieldKey.ARTIST_SORT));
data.setArtistSort(tag.getFirst(org.jaudiotagger.tag.FieldKey.ARTIST_SORT));
} catch (Exception ignored) {
}
try {
@@ -296,7 +310,7 @@ public class MusicTools {
} catch (Exception ignored) {
}
try {
data.setDisc_no(tag.getFirst(org.jaudiotagger.tag.FieldKey.DISC_NO));
data.setDiscNo(tag.getFirst(org.jaudiotagger.tag.FieldKey.DISC_NO));
} catch (Exception ignored) {
}
try {
@@ -316,60 +330,166 @@ public class MusicTools {
data.setYear(tag.getFirst(org.jaudiotagger.tag.FieldKey.YEAR));
} catch (Exception ignored) {
}
data.setFile(file);
data.setFile(file.getAbsolutePath());
data.setIsdir(file.isDirectory() ? 1 : 0);
data.setLastdir(file.getParentFile().getParent());
org.jaudiotagger.audio.AudioHeader header = audioFile.getAudioHeader();
data.setBitRate(Integer.parseInt(header.getBitRate()));
data.setSampleRate(Integer.parseInt(header.getSampleRate()));
data.setNoOfSamples(header.getSampleRateAsNumber());
data.setChannelCount(Integer.parseInt(header.getChannels()));
data.setEncodingType(header.getEncodingType() + "");
data.setDurationAsDouble(header.getTrackLength());
data.setLossless(header.isLossless());
data.setVariableBitRate(header.isVariableBitRate());
data.setBitrate(Integer.parseInt(header.getBitRate()));
data.setSamplerate(Integer.parseInt(header.getSampleRate()));
data.setNoofsamples(Long.parseLong(header.getSampleRateAsNumber() + ""));
data.setChannelcount(Integer.parseInt(header.getChannels()));
data.setEncodingtype(header.getEncodingType() + "");
data.setDurationasdouble(Double.parseDouble(header.getTrackLength() + ""));
data.setLossless(header.isLossless() ? 1 : 0);
data.setVariablebitrate(header.isVariableBitRate() ? 1 : 0);
try {
data.setMd5(header.getClass().getMethod("getMd5").invoke(header).toString());
} catch (Exception ignored) {
data.setMd5(Base64.getEncoder().encodeToString(file.getAbsolutePath().getBytes()));
data.setMd5(Tools.getFileMD5(file));
}
return data;
} catch (Exception e) {
e.printStackTrace();
return getMetadataOfFFmpeg(file);
}
return null;
}
private MusicData getMetadataOfFFmpeg(File file) {
MusicData data;
try {
data = new MusicData();
FFprobe fFprobe = new FFprobe((String) ConfigTools.load(ConfigTools.CONFIG, "ffprobe"));
FFmpegProbeResult result = fFprobe.probe(file.getAbsolutePath());
FFmpegFormat format = result.getFormat();
FFmpegStream stream = null;
for (FFmpegStream tmp : result.getStreams()) {
if (tmp.index == 0) {
stream = tmp;
}
}
Map<String, String> tag = format.tags;
data.setTitle(getTitle(tag));
data.setAlbum(getAlbum(tag));
data.setArtist(getArtist(tag));
data.setDiscNo(tag.get("disc") == null ? tag.get("disc".toUpperCase()) : tag.get("disc"));
data.setTrack(tag.get("track") == null ? tag.get("track".toUpperCase()) : tag.get("track"));
data.setYear(getYear(tag));
data.setArtistSort(tag.get("album_artist") == null ? tag.get("album_artist".toUpperCase()) : tag.get("album_artist"));
data.setDurationasdouble(format.duration);
data.setBitrate((int) (format.bit_rate / 1000));
if (stream != null) {
if (data.getBitrate() == 0)
data.setBitrate((int) (stream.bit_rate / 1000));
data.setChannelcount(stream.channels);
data.setLossless(0);
data.setSamplerate(stream.sample_rate);
data.setNoofsamples(stream.duration_ts);
}
data.setEncodingtype(format.format_long_name);
data.setComment("");
data.setComposer("");
data.setVariablebitrate(0);
data.setFile(file.getAbsolutePath());
data.setLastdir(file.getParentFile().getParent());
data.setIsdir(file.isDirectory() ? 1 : 0);
if (data.getYear() == null) {
data.setYear("0000");
}
data.setMd5(Tools.getFileMD5(file));
} catch (Exception e) {
e.printStackTrace();
data = new MusicData();
data.setTitle(file.getName());
data.setFile(file.getAbsolutePath());
data.setIsdir(file.isDirectory() ? 1 : 0);
data.setLastdir(file.getParentFile().getParent());
data.setMd5(Tools.getFileMD5(file));
}
return data;
}
private String getTitle(Map<String, String> tag) {
String title = tag.get("title");
if (StringUtils.isEmpty(title)) {
title = tag.get("TITLE");
if (StringUtils.isEmpty(title)) {
title = tag.get("Title");
}
}
return title;
}
private String getArtist(Map<String, String> tag) {
String title = tag.get("artist");
if (StringUtils.isEmpty(title)) {
title = tag.get("ARTIST");
if (StringUtils.isEmpty(title)) {
title = tag.get("Artist");
}
}
return title;
}
private String getAlbum(Map<String, String> tag) {
String title = tag.get("album");
if (StringUtils.isEmpty(title)) {
title = tag.get("ALBUM");
if (StringUtils.isEmpty(title)) {
title = tag.get("Album");
}
}
return title;
}
private String getYear(Map<String, String> tag) {
String title = tag.get("year");
if (StringUtils.isEmpty(title)) {
title = tag.get("YEAR");
if (StringUtils.isEmpty(title)) {
title = tag.get("Year");
}
}
return title;
}
@Override
public List<MusicData> findOfTitle(String title) {
return find(title, FIND_TITLE);
}
@Override
public List<MusicData> findOfArtist(String by) {
return find(by, FIND_ARTIST);
}
public List<MusicData> getMusicList() {
return musicList;
return musicDataDao.selectByExample(new MusicDataExample());
}
@Override
public int getLength() {
return tools.musicList.size();
return musicDataDao.selectByExample(new MusicDataExample()).size();
}
@Override
public boolean isScan() {
return isScan;
}
private List<MusicData> find(String title, int type) {
List<MusicData> list = new ArrayList<>();
for (MusicData data : musicList) {
if (data.getTitle().contains(title)) {
list.add(data);
}
List<MusicData> list;
MusicDataExample example = new MusicDataExample();
if (type == FIND_TITLE) {
example.createCriteria().andTitleEqualTo(title);
} else if (type == FIND_ARTIST) {
example.createCriteria().andArtistEqualTo(title);
}
list = musicDataDao.selectByExample(example);
return list;
}
@Override
public String getMusicPath() {
return musicPath;
}
@@ -378,26 +498,40 @@ public class MusicTools {
this.musicPath = musicPath;
}
private void saveImage(MusicData data) {
try {
File file = new File(data.getTitle() + ".jpg");
if (!file.exists()) {
if (!file.createNewFile()) {
return;
@Override
public byte[] readImage(String path) throws Exception {
File file = new File(path);
AudioFile audioFile = null;
audioFile = AudioFileIO.read(file);
Tag tag = audioFile.getTag().or(NullTag.INSTANCE);
byte[] bytes = tag.getFirstArtwork().or(NullArtwork.INSTANCE).getBinaryData();
if (bytes.length == 0) {
return readImageFile(file);
}
return bytes;
}
private byte[] readImageFile(File file) throws Exception {
String path = file.getAbsolutePath().replace(file.getName(), "");
File img = new File(path, "cover.jpg");
if (!img.exists()) {
img = new File(path, "Cover.jpg");
if (!img.exists()) {
img = new File(path, "COVER.jpg");
if (!img.exists()) {
throw new NullPointerException("没有cover文件");
}
}
OutputStream outputStream = new FileOutputStream(file);
outputStream.write(data.readImage());
outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return Files.readAllBytes(Paths.get(img.getAbsolutePath()));
}
public static void main(String[] args) throws Exception {
File file = new File("Z:\\音乐\\总之就是非常酸\\ED\\カノエラナ - 月と星空\\カノエラナ.wav");
System.out.println(new MusicTools().getMetadata(file));
file = new File("Z:\\音乐\\终将成为你\\[OP]君にふれて\\rise.flac");
// file = new File("Z:\\音乐\\周董\\2012 十二新作\\03 公公偏头痛.ape");
System.out.println(new MusicTools().getMetadataOfFFmpeg(file));
}

View File

@@ -54,8 +54,11 @@ public class QQBotManager {
}
});
Events.registerEvents(bot, new MessageListener());
bot.login();
sendMessage("姬妻酱上线拉~");
bot.join();
}
}).start();

View File

@@ -18,6 +18,8 @@ import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.Date;
import java.util.Random;
@@ -334,4 +336,44 @@ public class Tools {
headers.add("ETag", String.valueOf(System.currentTimeMillis()));
return ResponseEntity.ok().headers(headers).contentLength(file.length()).contentType(MediaType.parseMediaType("application/octet-stream")).body(new FileSystemResource(file));
}
public static String getFileMD5(File file) {
if (!file.isFile()) {
return null;
}
MessageDigest digest = null;
FileInputStream in = null;
byte buffer[] = new byte[1024];
int len;
try {
digest = MessageDigest.getInstance("MD5");
in = new FileInputStream(file);
while ((len = in.read(buffer, 0, 1024)) != -1) {
digest.update(buffer, 0, len);
}
in.close();
} catch (Exception e) {
e.printStackTrace();
return null;
}
return bytesToHexString(digest.digest());
}
private static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (byte aSrc : src) {
int v = aSrc & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
public static String base64ToString(String base){
base=base.replace(" ","+");
return new String(Base64.getDecoder().decode(base.replace("\r\n","").getBytes()));
}
}