package com.yutou.nas.Services.impl; import com.yutou.nas.Datas.AppData; import com.yutou.nas.Services.IMusicToolsService; import com.yutou.nas.mybatis.dao.MusicDataDao; import com.yutou.nas.mybatis.model.MusicData; import com.yutou.nas.mybatis.model.MusicDataExample; import com.yutou.nas.utils.*; import ealvatag.audio.AudioFile; import ealvatag.audio.AudioFileIO; import ealvatag.audio.AudioHeader; import ealvatag.tag.FieldKey; import ealvatag.tag.NullTag; import ealvatag.tag.Tag; import ealvatag.tag.images.NullArtwork; 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 javax.annotation.Resource; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.text.Collator; import java.util.*; @Service("MusicToolsService") public class MusicToolsServiceImpl implements IMusicToolsService { public static final int FIND_TITLE = 1; public static final int FIND_ARTIST = 2; private String musicPath = "/media/yutou/disk_lvm/public/音乐"; private boolean isScan = false; @Resource MusicDataDao musicDataDao; @Override public void init() { musicPath = (String) ConfigTools.load(ConfigTools.CONFIG, "musicDir"); scanMusic(); } @Override public void scanMusic() { if (isScan) { return; } if (!ConfigTools.load(ConfigTools.CONFIG, "musicScan", boolean.class, false)) { return; } musicPath = (String) ConfigTools.load(ConfigTools.CONFIG, "musicDir"); com.yutou.nas.utils.Log.i("执行扫描:" + musicPath); new Thread(() -> { long startTime = System.currentTimeMillis(); QQBotManager.getInstance().sendMessage("开始扫描音乐夹"); musicDataDao.truncate(); isScan = true; scan(new File(musicPath)); isScan = false; Log.i("扫描完成"); QQBotManager.getInstance().sendMessage("音乐扫描完成,共" + getLength() + "首歌,耗时:" + (System.currentTimeMillis() - startTime)); }).start(); } private void scan(File path) { if (!path.exists()) { Log.i("MusicScan", "文件夹不存在,取消扫描"); return; } if (path.isFile()) { add(path); } else if (path.isDirectory()) { for (File file : Objects.requireNonNull(path.listFiles())) { if (file.isDirectory()) { scan(file); } else { add(file); } } } } public void getPathOrDir(String path, List list) { File files = new File(path); for (File file : Objects.requireNonNull(files.listFiles())) { if (file.isFile()) { list.add(getMetadata(file)); } else { getPathOrDir(file.getAbsolutePath(), list); } } } @Override public List getPath(String path, boolean isDir, boolean delPath) { return getPath(path, isDir, delPath, false); } /** * 获取指定目录下的音乐 * * @param path 指定目录 * @param isDir 是否扫描目录下的所有文件,false则仅为当前目录 * @return 音乐列表 */ @Override public List getPath(String path, boolean isDir, boolean delPath, boolean filter) { List list = new ArrayList<>(); List main = new ArrayList<>(); MusicDataExample example = new MusicDataExample(); String replacement = "windows".equals(ConfigTools.load(ConfigTools.CONFIG, "os")) ? "\\\\" : "/"; String tmpPath = path; if (StringUtils.isEmpty(path)) { tmpPath = AppData.defaultMusicPath; } if (isDir) { example.createCriteria().andFileLike(tmpPath.replace(File.separator, replacement) + "%"); main = musicDataDao.selectByExample(example, delPath); }else { tmpPath = tmpPath.replace(File.separator, replacement) .replace("+", "\\+") .replace("[", "\\[") .replace("(", "\\(") .replace(")", "\\)") .replace("]", "\\]"); main = new ArrayList<>(musicDataDao.selectByRegexp(tmpPath + replacement + "([^" + replacement + "]+)$", delPath)); } if (!path.equals(AppData.defaultMusicPath) && !"root".equals(path)) { MusicData t2 = new MusicData(); t2.setTitle("返回"); t2.setTrack("-1"); if (main.isEmpty()) { t2.setFile("root"); } else { t2.setFile(new File(path).getParent()); } com.yutou.nas.utils.Log.i("查询地址:" + path + " 设置返回地址:" + t2.getFile()); t2.setIsdir(1); list.add(t2); } getDirList(path, list); list.addAll(main); list.sort((o1, o2) -> { if ("返回".equals(o2.getTitle())) { return 1; } Comparator compare = Collator.getInstance(Locale.CHINA); return compare.compare(o1.getTitle(), o2.getTitle()); }); if (filter) { list.removeIf(data -> data.getTitle().toLowerCase().contains("instrumental")); } return list; } public List getAllAlbum() { return musicDataDao.selectAllAlbum(); } public List getAllArtist() { return musicDataDao.selectAllArtist(); } public List selectAlbum(String album, boolean isDelFile) { return musicDataDao.selectAlbum(album, isDelFile); } public List selectArtist(String artist, boolean isDelFile) { return musicDataDao.selectArtist(artist, isDelFile); } private void getDirList(String path, List list) { File file = new File(path); com.yutou.nas.utils.Log.i("扫描文件:" + path); if (file.isDirectory()) { for (File listFile : Objects.requireNonNull(file.listFiles())) { if (listFile.isDirectory()) { MusicData data = new MusicData(); data.setTitle(listFile.getName()); data.setIsdir(1); data.setFile(listFile.getAbsolutePath()); list.add(data); } } } else { MusicData data = new MusicData(); data.setTitle(file.getName()); data.setIsdir(0); data.setFile(file.getAbsolutePath()); list.add(data); } } private void add(File file) { MusicData data = getMetadata(file); if (data != null) { try { if (getMusicData(file.getAbsolutePath(), false) == null) { // System.out.println(data); musicDataDao.insert(data); } } catch (Exception e) { e.printStackTrace(); QQBotManager.getInstance().sendMessage("音乐文件添加失败:" + data); } } } @Override public MusicData getMusicData(String path, boolean delFile) { MusicDataExample example = new MusicDataExample(); example.createCriteria().andFileEqualTo(path); List list = musicDataDao.selectByExample(example, delFile); if (list != null && list.size() > 0) { return list.get(0); } return null; } @Override public MusicData getMusicDataOfMd5(String md5, boolean isDelFile) { MusicDataExample example = new MusicDataExample(); example.createCriteria().andMd5EqualTo(md5); List list = musicDataDao.selectByExample(example, isDelFile); if (list != null && list.size() > 0) { return list.get(0); } return null; } public MusicData getMetadata(File file) { try { if (file.getName().endsWith(".lrc") || file.getName().endsWith(".jpg") || file.getName().endsWith(".ini") || file.getName().endsWith(".png") || file.getName().endsWith(".torrent") || file.getName().endsWith(".log") || file.getName().endsWith(".mkv") || file.getName().endsWith(".dff") || file.getName().endsWith(".cue") || file.getName().endsWith(".m3u") || file.getName().endsWith(".txt") || file.getName().endsWith(".db") ) { return null; } AudioFile audioFile = AudioFileIO.read(file); Tag tag = audioFile.getTag().or(NullTag.INSTANCE); MusicData data = new MusicData(); try { data.setAlbum(tag.getFirst(FieldKey.ALBUM)); } catch (Exception ignored) { } try { data.setArtist(tag.getFirst(FieldKey.ARTIST)); } catch (Exception ignored) { } try { data.setArtistSort(tag.getFirst(FieldKey.ARTIST_SORT)); } catch (Exception ignored) { } try { data.setComment(tag.getFirst(FieldKey.COMMENT)); } catch (Exception ignored) { } try { data.setComposer(tag.getFirst(FieldKey.COMPOSER)); } catch (Exception ignored) { } try { data.setDiscNo(tag.getFirst(FieldKey.DISC_NO)); } catch (Exception ignored) { } try { if (StringUtils.isEmpty(tag.getFirst(FieldKey.TITLE))) { data.setTitle(file.getName()); } else { data.setTitle(tag.getFirst(FieldKey.TITLE)); } } catch (Exception e) { data.setTitle(file.getName()); } try { data.setTrack(tag.getFirst(FieldKey.TRACK)); } catch (Exception ignored) { } try { data.setYear(tag.getFirst(FieldKey.YEAR)); } catch (Exception ignored) { } 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() ? 1 : 0); data.setVariablebitrate(header.isVariableBitRate() ? 1 : 0); try { data.setMd5(header.getClass().getMethod("getMd5").invoke(header).toString()); if(data.getMd5().startsWith("00000")&&data.getMd5().endsWith("00000")){ throw new NullPointerException("md5 is null"); } } catch (Exception ignored) { data.setMd5(Tools.getFileMD5(file)); } return data; } catch (IOException e) { return getMetadata_jthink(file); } catch (Exception e) { e.printStackTrace(); } return null; } public MusicData getMetadata_jthink(File file) { try { org.jaudiotagger.audio.AudioFile audioFile = org.jaudiotagger.audio.AudioFileIO.read(file); org.jaudiotagger.tag.Tag tag = audioFile.getTag(); MusicData data = new MusicData(); try { data.setAlbum(tag.getFirst(org.jaudiotagger.tag.FieldKey.ALBUM)); } catch (Exception ignored) { } try { data.setArtist(tag.getFirst(org.jaudiotagger.tag.FieldKey.ARTIST)); } catch (Exception ignored) { } try { data.setArtistSort(tag.getFirst(org.jaudiotagger.tag.FieldKey.ARTIST_SORT)); } catch (Exception ignored) { } try { data.setComment(tag.getFirst(org.jaudiotagger.tag.FieldKey.COMMENT)); } catch (Exception ignored) { } try { data.setComposer(tag.getFirst(org.jaudiotagger.tag.FieldKey.COMPOSER)); } catch (Exception ignored) { } try { data.setDiscNo(tag.getFirst(org.jaudiotagger.tag.FieldKey.DISC_NO)); } catch (Exception ignored) { } try { if (StringUtils.isEmpty(tag.getFirst(org.jaudiotagger.tag.FieldKey.TITLE))) { data.setTitle(file.getName()); } else { data.setTitle(tag.getFirst(org.jaudiotagger.tag.FieldKey.TITLE)); } } catch (Exception e) { data.setTitle(file.getName()); } try { data.setTrack(tag.getFirst(org.jaudiotagger.tag.FieldKey.TRACK)); } catch (Exception ignored) { } try { data.setYear(tag.getFirst(org.jaudiotagger.tag.FieldKey.YEAR)); } catch (Exception ignored) { } 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(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()); if(data.getMd5().startsWith("00000")&&data.getMd5().endsWith("00000")){ throw new NullPointerException("md5 is null"); } } catch (Exception ignored) { data.setMd5(Tools.getFileMD5(file)); } return data; } catch (Exception e) { return getMetadataOfFFmpeg(file); } } 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 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)); QQBotManager.getInstance().sendMessage("添加音乐文件失败:\n" + data + "\n" + Tools.getExceptionString(e)); } return data; } private String getTitle(Map 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 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 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 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 findOfTitle(String title, boolean delFile) { return find(title, FIND_TITLE, delFile); } @Override public List findOfArtist(String by, boolean delFile) { return find(by, FIND_ARTIST, delFile); } @Override public List getMusicList(boolean delFile) { return musicDataDao.selectByExample(new MusicDataExample(), delFile); } @Override public int getLength() { return musicDataDao.selectByExample(new MusicDataExample(), true).size(); } @Override public boolean isScan() { return isScan; } private List find(String title, int type, boolean delFile) { List 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, delFile); return list; } @Override public String getMusicPath() { return musicPath; } public void setMusicPath(String musicPath) { this.musicPath = musicPath; } @Override public byte[] readImage(String path) throws Exception { File file = new File(path); AudioFile audioFile; 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; } @Override public File getMusicOfMd5(String md5, boolean delFile) { MusicDataExample example = new MusicDataExample(); example.createCriteria().andMd5EqualTo(md5); List list = musicDataDao.selectByExample(example, delFile); Log.i("Music Size", list.size()); if (!list.isEmpty()) { Log.i("Music File", list.get(0).toString()); return new File(list.get(0).getFile()); } return null; } @Override public File getMusicLrcMd5(String md5) { MusicDataExample example = new MusicDataExample(); example.createCriteria().andMd5EqualTo(md5); List list = musicDataDao.selectByExample(example, false); if (!list.isEmpty()) { String fileName = list.get(0).getFile(); fileName = fileName.replace(fileName.substring(fileName.lastIndexOf(".")), ".lrc"); System.out.println("fileName = " + fileName); File file = new File(fileName); if (file.exists()) { return file; } } return null; } 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文件"); } } } return Files.readAllBytes(Paths.get(img.getAbsolutePath())); } public static void main(String[] args) throws Exception { File file = new File("Z:\\音乐\\总之就是非常酸\\ED\\カノエラナ - 月と星空\\カノエラナ.wav"); file = new File("Z:\\音乐\\终将成为你\\[OP]君にふれて\\rise.flac"); // file = new File("Z:\\音乐\\周董\\2012 十二新作\\03 公公偏头痛.ape"); com.yutou.nas.utils.Log.i(new MusicToolsServiceImpl().getMetadataOfFFmpeg(file)); new MusicToolsServiceImpl().getMusicLrcMd5("84d3180883e6447c3b86bfc55b05608e"); } }