process music tags
This commit is contained in:
parent
b4a35efbd1
commit
44c945fbc3
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
pom.xml
6
pom.xml
@ -65,6 +65,12 @@
|
|||||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.jthink</groupId>
|
||||||
|
<artifactId>jaudiotagger</artifactId>
|
||||||
|
<version>3.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import top.baogutang.music.domain.req.search.MusicSearchReq;
|
|||||||
import top.baogutang.music.domain.res.download.MusicDownloadRes;
|
import top.baogutang.music.domain.res.download.MusicDownloadRes;
|
||||||
import top.baogutang.music.domain.res.search.*;
|
import top.baogutang.music.domain.res.search.*;
|
||||||
import top.baogutang.music.enums.ChannelEnum;
|
import top.baogutang.music.enums.ChannelEnum;
|
||||||
|
import top.baogutang.music.processor.AbstractAudioProcessor;
|
||||||
import top.baogutang.music.properties.NetEaseMusicProperties;
|
import top.baogutang.music.properties.NetEaseMusicProperties;
|
||||||
import top.baogutang.music.service.IMusicRecordService;
|
import top.baogutang.music.service.IMusicRecordService;
|
||||||
import top.baogutang.music.utils.OkHttpUtil;
|
import top.baogutang.music.utils.OkHttpUtil;
|
||||||
@ -101,19 +102,13 @@ public class NetEaseMusicClient implements ChannelClient<MusicSearchReq, MusicSe
|
|||||||
if (!Files.exists(baseDir)) {
|
if (!Files.exists(baseDir)) {
|
||||||
Files.createDirectories(baseDir);
|
Files.createDirectories(baseDir);
|
||||||
}
|
}
|
||||||
if (res.getPic() != null && !res.getPic().isEmpty()) {
|
String[] split = res.getUrl().split("\\.");
|
||||||
String[] split = res.getPic().split("\\.");
|
String fileType = split[split.length - 1];
|
||||||
downloadFile(res.getPic(), baseDir.resolve(res.getName() + "." + split[split.length - 1]));
|
AbstractAudioProcessor audioProcessor = AbstractAudioProcessor.getAudioProcessor(fileType);
|
||||||
}
|
try (InputStream musicIn = audioProcessor.processAudioTags(new URL(res.getUrl()).openStream(), res)) {
|
||||||
if (res.getUrl() != null && !res.getUrl().isEmpty()) {
|
if (Objects.nonNull(musicIn)) {
|
||||||
String[] split = res.getUrl().split("\\.");
|
Files.copy(musicIn, baseDir.resolve(res.getName() + "." + fileType), StandardCopyOption.REPLACE_EXISTING);
|
||||||
downloadFile(res.getUrl(), baseDir.resolve(res.getName() + "." + split[split.length - 1]));
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void downloadFile(String fileUrl, Path targetPath) throws IOException {
|
|
||||||
try (InputStream in = new URL(fileUrl).openStream()) {
|
|
||||||
Files.copy(in, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -38,4 +38,6 @@ public class MusicDownloadRes extends AbstractMusicRes implements Serializable {
|
|||||||
|
|
||||||
private String level;
|
private String level;
|
||||||
|
|
||||||
|
private String lyric;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
package top.baogutang.music.enums;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
*
|
||||||
|
* @author: N1KO
|
||||||
|
* @date: 2024/12/12 : 10:28
|
||||||
|
*/
|
||||||
|
public enum AudioFileTypeEnum {
|
||||||
|
|
||||||
|
FLAC,
|
||||||
|
|
||||||
|
MP3,
|
||||||
|
|
||||||
|
MP4,
|
||||||
|
|
||||||
|
WAV,
|
||||||
|
|
||||||
|
OTHERS,
|
||||||
|
;
|
||||||
|
|
||||||
|
public static AudioFileTypeEnum parse(String fileType) {
|
||||||
|
if (StringUtils.isBlank(fileType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Arrays.stream(AudioFileTypeEnum.values())
|
||||||
|
.filter(f -> StringUtils.equalsIgnoreCase(fileType, f.name()))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(OTHERS);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
package top.baogutang.music.processor;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.CannotWriteException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||||
|
import org.jaudiotagger.tag.TagException;
|
||||||
|
import top.baogutang.music.domain.res.download.MusicDownloadRes;
|
||||||
|
import top.baogutang.music.enums.AudioFileTypeEnum;
|
||||||
|
import top.baogutang.music.exceptions.BusinessException;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
*
|
||||||
|
* @author: N1KO
|
||||||
|
* @date: 2024/12/12 : 12:15
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public abstract class AbstractAudioProcessor {
|
||||||
|
|
||||||
|
public static AbstractAudioProcessor getAudioProcessor(String fileType) {
|
||||||
|
AudioFileTypeEnum fileTypeEnum = AudioFileTypeEnum.parse(fileType);
|
||||||
|
switch (fileTypeEnum) {
|
||||||
|
case FLAC:
|
||||||
|
return new FlacAudioProcessor();
|
||||||
|
case MP3:
|
||||||
|
return new Mp3AudioProcessor();
|
||||||
|
default:
|
||||||
|
throw new BusinessException("不支持文件格式");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream processAudioTags(InputStream inputStream, MusicDownloadRes res) {
|
||||||
|
File file = null;
|
||||||
|
try {
|
||||||
|
file = convertInputStreamToTempFile(inputStream);
|
||||||
|
return process(file, res);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(">>>>>>>>>>processAudioTags error:{}<<<<<<<<<<", e.getMessage(), e);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (file != null && file.exists()) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract InputStream process(File file, MusicDownloadRes res) throws TagException, CannotWriteException, IOException, CannotReadException, InvalidAudioFrameException, ReadOnlyFileException;
|
||||||
|
|
||||||
|
|
||||||
|
protected byte[] fetchImageData(String imageUrl) {
|
||||||
|
try (InputStream inputStream = new URL(imageUrl).openStream();
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error downloading image from URL: {}", imageUrl, e);
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private File convertInputStreamToTempFile(InputStream inputStream) throws IOException {
|
||||||
|
// 创建临时文件
|
||||||
|
File tempFile = File.createTempFile("tempFile", ".tmp");
|
||||||
|
// 写入 InputStream 数据到临时文件
|
||||||
|
try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tempFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
package top.baogutang.music.processor;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jaudiotagger.audio.AudioFile;
|
||||||
|
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.CannotWriteException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||||
|
import org.jaudiotagger.audio.flac.FlacFileReader;
|
||||||
|
import org.jaudiotagger.audio.flac.FlacFileWriter;
|
||||||
|
import org.jaudiotagger.tag.FieldDataInvalidException;
|
||||||
|
import org.jaudiotagger.tag.FieldKey;
|
||||||
|
import org.jaudiotagger.tag.Tag;
|
||||||
|
import org.jaudiotagger.tag.TagException;
|
||||||
|
import org.jaudiotagger.tag.images.StandardArtwork;
|
||||||
|
import top.baogutang.music.domain.res.download.MusicDownloadRes;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
*
|
||||||
|
* @author: N1KO
|
||||||
|
* @date: 2024/12/12 : 10:40
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class FlacAudioProcessor extends AbstractAudioProcessor {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
InputStream process(File file, MusicDownloadRes res) throws TagException, CannotWriteException, IOException, CannotReadException, InvalidAudioFrameException, ReadOnlyFileException {
|
||||||
|
FlacFileReader flacFileReader = new FlacFileReader();
|
||||||
|
AudioFile audioFile = flacFileReader.read(file);
|
||||||
|
Tag tag = audioFile.getTag();
|
||||||
|
tag.setField(FieldKey.TITLE, res.getName());
|
||||||
|
tag.setField(FieldKey.ALBUM, res.getAlbumName());
|
||||||
|
tag.setField(FieldKey.ARTIST, res.getArtistName());
|
||||||
|
tag.setField(FieldKey.LYRICS, res.getLyric());
|
||||||
|
StandardArtwork artwork = new StandardArtwork();
|
||||||
|
artwork.setImageUrl(res.getPic());
|
||||||
|
artwork.setBinaryData(fetchImageData(res.getPic()));
|
||||||
|
tag.setField(artwork);
|
||||||
|
FlacFileWriter flacFileWriter = new FlacFileWriter();
|
||||||
|
flacFileWriter.write(audioFile);
|
||||||
|
return new FileInputStream(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package top.baogutang.music.processor;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.jaudiotagger.audio.AudioFile;
|
||||||
|
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.CannotWriteException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||||
|
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||||
|
import org.jaudiotagger.audio.flac.FlacFileWriter;
|
||||||
|
import org.jaudiotagger.audio.mp3.MP3FileReader;
|
||||||
|
import org.jaudiotagger.tag.FieldKey;
|
||||||
|
import org.jaudiotagger.tag.Tag;
|
||||||
|
import org.jaudiotagger.tag.TagException;
|
||||||
|
import org.jaudiotagger.tag.images.StandardArtwork;
|
||||||
|
import top.baogutang.music.domain.res.download.MusicDownloadRes;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @description:
|
||||||
|
*
|
||||||
|
* @author: N1KO
|
||||||
|
* @date: 2024/12/12 : 13:32
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class Mp3AudioProcessor extends AbstractAudioProcessor {
|
||||||
|
@Override
|
||||||
|
InputStream process(File file, MusicDownloadRes res) throws TagException, CannotWriteException, IOException, CannotReadException, InvalidAudioFrameException, ReadOnlyFileException {
|
||||||
|
MP3FileReader fileReader = new MP3FileReader();
|
||||||
|
AudioFile audioFile = fileReader.read(file);
|
||||||
|
Tag tag = audioFile.getTag();
|
||||||
|
tag.setField(FieldKey.TITLE, res.getName());
|
||||||
|
tag.setField(FieldKey.ALBUM, res.getAlbumName());
|
||||||
|
tag.setField(FieldKey.ARTIST, res.getArtistName());
|
||||||
|
tag.setField(FieldKey.LYRICS, res.getLyric());
|
||||||
|
StandardArtwork artwork = new StandardArtwork();
|
||||||
|
artwork.setImageUrl(res.getPic());
|
||||||
|
artwork.setBinaryData(fetchImageData(res.getPic()));
|
||||||
|
tag.setField(artwork);
|
||||||
|
FlacFileWriter flacFileWriter = new FlacFileWriter();
|
||||||
|
flacFileWriter.write(audioFile);
|
||||||
|
return new FileInputStream(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package top.baogutang.music.service;
|
package top.baogutang.music.service;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import top.baogutang.music.client.ChannelClient;
|
import top.baogutang.music.client.ChannelClient;
|
||||||
import top.baogutang.music.domain.req.AbstractMusicReq;
|
import top.baogutang.music.domain.req.AbstractMusicReq;
|
||||||
import top.baogutang.music.domain.req.search.MusicSearchReq;
|
import top.baogutang.music.domain.req.search.MusicSearchReq;
|
||||||
@ -68,21 +69,28 @@ public abstract class AbstractMusicService<Q extends AbstractMusicReq, S extends
|
|||||||
ChannelEnum channelEnum = getChannelEnum();
|
ChannelEnum channelEnum = getChannelEnum();
|
||||||
ChannelClient<Q, S> channelClient = channelClientFactory.getClient(channelEnum);
|
ChannelClient<Q, S> channelClient = channelClientFactory.getClient(channelEnum);
|
||||||
MusicDownloadRes res = channelClient.download(id);
|
MusicDownloadRes res = channelClient.download(id);
|
||||||
channelClient.saveMusic(res);
|
if (StringUtils.isNotBlank(res.getArtistName())) {
|
||||||
// CompletableFuture.runAsync(() -> {
|
String[] split = res.getArtistName().split("/");
|
||||||
// try {
|
String artistName = String.join(",", split);
|
||||||
// channelClient.processFile(res);
|
res.setArtistName(artistName);
|
||||||
// } catch (IOException e) {
|
|
||||||
// log.error(">>>>>>>>>>download error:{}<<<<<<<<<<", e.getMessage(), e);
|
|
||||||
// throw new BusinessException("下载异常");
|
|
||||||
// }
|
|
||||||
// }, commonExecutor);
|
|
||||||
try {
|
|
||||||
channelClient.processFile(res);
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error(">>>>>>>>>>download error:{}<<<<<<<<<<", e.getMessage(), e);
|
|
||||||
throw new BusinessException("下载异常");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channelClient.saveMusic(res);
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
try {
|
||||||
|
channelClient.processFile(res);
|
||||||
|
log.info(">>>>>>>>>>download file:{} success<<<<<<<<<<", res.getName());
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error(">>>>>>>>>>download error:{}<<<<<<<<<<", e.getMessage(), e);
|
||||||
|
throw new BusinessException("下载异常");
|
||||||
|
}
|
||||||
|
}, commonExecutor);
|
||||||
|
// try {
|
||||||
|
// channelClient.processFile(res);
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// log.error(">>>>>>>>>>download error:{}<<<<<<<<<<", e.getMessage(), e);
|
||||||
|
// throw new BusinessException("下载异常");
|
||||||
|
// }
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,5 +45,5 @@ baogutang:
|
|||||||
album-base-url: http://117.72.78.133:5173/album?id=%d
|
album-base-url: http://117.72.78.133:5173/album?id=%d
|
||||||
artist-base-url: http://117.72.78.133:5173/artists?id=%d
|
artist-base-url: http://117.72.78.133:5173/artists?id=%d
|
||||||
download-base-url: https://api.sooooooooooooooooootheby.top/Netease_url/Song_V1?level=jymaster&type=json&ids=%d
|
download-base-url: https://api.sooooooooooooooooootheby.top/Netease_url/Song_V1?level=jymaster&type=json&ids=%d
|
||||||
download-path: /baogutang-music/download/music
|
download-path: /Users/nikooh/Desktop/Download
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user