process music tags

This commit is contained in:
N1KO 2024-12-12 13:43:18 +08:00
parent b4a35efbd1
commit 44c945fbc3
10 changed files with 263 additions and 28 deletions

6
.idea/vcs.xml generated Normal file
View 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>

View File

@ -65,6 +65,12 @@
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>net.jthink</groupId>
<artifactId>jaudiotagger</artifactId>
<version>3.0.1</version>
</dependency>
</dependencies>
<build>

View File

@ -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.search.*;
import top.baogutang.music.enums.ChannelEnum;
import top.baogutang.music.processor.AbstractAudioProcessor;
import top.baogutang.music.properties.NetEaseMusicProperties;
import top.baogutang.music.service.IMusicRecordService;
import top.baogutang.music.utils.OkHttpUtil;
@ -101,19 +102,13 @@ public class NetEaseMusicClient implements ChannelClient<MusicSearchReq, MusicSe
if (!Files.exists(baseDir)) {
Files.createDirectories(baseDir);
}
if (res.getPic() != null && !res.getPic().isEmpty()) {
String[] split = res.getPic().split("\\.");
downloadFile(res.getPic(), baseDir.resolve(res.getName() + "." + split[split.length - 1]));
}
if (res.getUrl() != null && !res.getUrl().isEmpty()) {
String[] split = res.getUrl().split("\\.");
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);
String[] split = res.getUrl().split("\\.");
String fileType = split[split.length - 1];
AbstractAudioProcessor audioProcessor = AbstractAudioProcessor.getAudioProcessor(fileType);
try (InputStream musicIn = audioProcessor.processAudioTags(new URL(res.getUrl()).openStream(), res)) {
if (Objects.nonNull(musicIn)) {
Files.copy(musicIn, baseDir.resolve(res.getName() + "." + fileType), StandardCopyOption.REPLACE_EXISTING);
}
}
}

View File

@ -38,4 +38,6 @@ public class MusicDownloadRes extends AbstractMusicRes implements Serializable {
private String level;
private String lyric;
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View 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);
}
}

View File

@ -1,6 +1,7 @@
package top.baogutang.music.service;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import top.baogutang.music.client.ChannelClient;
import top.baogutang.music.domain.req.AbstractMusicReq;
import top.baogutang.music.domain.req.search.MusicSearchReq;
@ -68,21 +69,28 @@ public abstract class AbstractMusicService<Q extends AbstractMusicReq, S extends
ChannelEnum channelEnum = getChannelEnum();
ChannelClient<Q, S> channelClient = channelClientFactory.getClient(channelEnum);
MusicDownloadRes res = channelClient.download(id);
channelClient.saveMusic(res);
// CompletableFuture.runAsync(() -> {
// try {
// channelClient.processFile(res);
// } 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("下载异常");
if (StringUtils.isNotBlank(res.getArtistName())) {
String[] split = res.getArtistName().split("/");
String artistName = String.join(",", split);
res.setArtistName(artistName);
}
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;
}
}

View File

@ -45,5 +45,5 @@ baogutang:
album-base-url: http://117.72.78.133:5173/album?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-path: /baogutang-music/download/music
download-path: /Users/nikooh/Desktop/Download