This commit is contained in:
N1KO 2024-12-16 15:02:11 +08:00
parent b6f3ff6dce
commit b5bd70e0df
5 changed files with 144 additions and 64 deletions

View File

@ -0,0 +1,16 @@
package top.baogutang.music.constants;
import java.util.Arrays;
import java.util.List;
/**
*
* @description:
*
* @author: N1KO
* @date: 2024/12/16 : 14:11
*/
public class BusinessKey {
public static final List<Long> ADMIN_ID_LIST = Arrays.asList(1L);
}

View File

@ -26,7 +26,7 @@ public class MusicDownloadController {
@Resource
private IMusicService musicService;
@GetMapping
@PostMapping
@Vip
public Results<String> download(@RequestBody MusicDownloadReq req) {
String downloadUrl = musicService.getMusicService(req.getChannel()).downloads(req.getIdList(), UserThreadLocal.get());

View File

@ -31,6 +31,8 @@ import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static top.baogutang.music.constants.BusinessKey.ADMIN_ID_LIST;
/**
*
* @description:
@ -116,36 +118,54 @@ public abstract class AbstractMusicService<Q extends AbstractMusicReq, S extends
if (CollectionUtils.isEmpty(idList)) {
throw new BusinessException("请选择下载内容");
}
if (idList.size() > 100) {
if (!ADMIN_ID_LIST.contains(userId) && idList.size() > 100) {
throw new BusinessException("单次下载限制最多100首");
}
ChannelEnum channelEnum = getChannelEnum();
ChannelClient<Q, S> channelClient = channelClientFactory.getClient(channelEnum);
String batchNo = UUID.randomUUID().toString();
idList.stream()
.map(id ->
CompletableFuture.runAsync(() -> {
MusicDownloadRes res = channelClient.download(id);
if (Objects.isNull(res)) {
log.error(">>>>>>>>>>query detail error! channel:{},song id:{}<<<<<<<<<<", channelEnum.getDesc(), id);
return;
}
if (StringUtils.isNotBlank(res.getArtistName())) {
String[] split = res.getArtistName().split("/");
String artistName = String.join(",", split);
res.setArtistName(artistName);
}
channelClient.saveMusic(res);
channelClient.saveDownloadRecord(userId, batchNo, id);
try {
channelClient.processFile(res);
log.info(">>>>>>>>>>download file:{} success<<<<<<<<<<", res.getName());
} catch (IOException e) {
log.error(">>>>>>>>>>download error:{}<<<<<<<<<<", e.getMessage(), e);
throw new BusinessException("下载异常");
}
}))
.forEach(CompletableFuture::join);
idList.forEach(id -> {
MusicRecordEntity entity = channelClient.queryByPlatform(id);
if (Objects.nonNull(entity)) {
channelClient.saveDownloadRecord(userId, batchNo, id);
return;
}
MusicDownloadRes res = channelClient.download(id);
if (Objects.isNull(res)) {
log.error(">>>>>>>>>>query detail error! channel:{},song id:{}<<<<<<<<<<", channelEnum.getDesc(), id);
return;
}
if (StringUtils.isNotBlank(res.getArtistName())) {
String[] split = res.getArtistName().split("/");
String artistName = String.join(",", split);
res.setArtistName(artistName);
}
channelClient.saveMusic(res);
channelClient.saveDownloadRecord(userId, batchNo, id);
try {
channelClient.processFile(res);
log.info(">>>>>>>>>>download file:{} success<<<<<<<<<<", res.getName());
} catch (IOException e) {
log.error(">>>>>>>>>>download error:{}<<<<<<<<<<", e.getMessage(), e);
throw new BusinessException("下载异常");
}
// CompletableFuture.runAsync(() -> {
// if (StringUtils.isNotBlank(res.getArtistName())) {
// String[] split = res.getArtistName().split("/");
// String artistName = String.join(",", split);
// res.setArtistName(artistName);
// }
// channelClient.saveMusic(res);
// channelClient.saveDownloadRecord(userId, batchNo, id);
// 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);
});
return batchNo;
}

View File

@ -49,15 +49,15 @@ 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: /downloads/music
download-path: /Users/nikooh/Desktop/downloads/music
download-path: /downloads/music
# download-path: /Users/nikooh/Desktop/downloads/music
qq-music:
query-base-url: http://117.72.78.133:5175/search?key=%s&pageNo=%d&pageSize=%d&t=%d
playlist-base-url: http://117.72.78.133:5175/songlist?id=%s
album-base-url: http://117.72.78.133:5175/album/songs?albummid=%s
download-base-url: http://117.72.78.133:5176/song?url=https://y.qq.com/n/ryqq/songDetail/%s
# download-path: /downloads/music
download-path: /Users/nikooh/Desktop/downloads/music
download-path: /downloads/music
# download-path: /Users/nikooh/Desktop/downloads/music

View File

@ -902,50 +902,94 @@
}
function downloadSelected() {
if (selectedSongs.length === 0) return;
const channel = selectedPlatformCode;
if (selectedSongs.length === 0) {
alert("请选择至少一首歌曲!");
return;
}
const channel = selectedPlatformCode; // 渠道编号
const progressContainer = document.getElementById('progress-container');
const progressBar = document.getElementById('progress-bar');
const progressText = document.getElementById('progress-text');
// 初始化进度条
progressContainer.style.display = 'block';
progressBar.value = 0;
progressText.textContent = '0%';
let completedCount = 0;
const total = selectedSongs.length;
const promises = selectedSongs.map(id => {
const url = `/api/v1/music/download?channel=${channel}&id=${id}`;
return fetch(url, {
credentials: 'include' // 确保请求携带cookie
})
.then(res => res.json())
.then(data => {
if (data.code === 200) {
// 下载成功,可以处理成功逻辑
} else if (data.code === -200) {
showMessage(data.msg);
}
completedCount++;
const percent = Math.floor((completedCount / total) * 100);
progressBar.value = percent;
progressText.textContent = percent + '%';
})
.catch(err => {
console.error(`下载ID:${id}时出错`, err);
completedCount++;
const percent = Math.floor((completedCount / total) * 100);
progressBar.value = percent;
progressText.textContent = percent + '%';
const fetchBatchAndDownloadFile = async () => {
try {
// 1. 调用第一个接口,获取 batchNo
const batchRequestBody = {
channel: channel,
idList: selectedSongs, // 假设selectedSongs是歌曲ID列表
};
const batchResponse = await fetch('/api/v1/music/download', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(batchRequestBody),
credentials: 'include',
});
});
// const idListParam = selectedSongs.join(','); // 拼接ID集合
// const batchUrl = `/api/v1/music/download?channel=${channel}&idList=${encodeURIComponent(idListParam)}`;
//
// const batchResponse = await fetch(batchUrl, {
// method: 'GET',
// credentials: 'include',
// });
Promise.all(promises).then(() => {
progressBar.value = 100;
progressText.textContent = '100%';
alert("下载完成!文件已存放到服务器指定目录中。");
});
const batchData = await batchResponse.json();
if (batchData.code === 200) {
const batchNo = batchData.data; // 成功获取 batchNo
// 更新进度条
progressBar.value = 50;
progressText.textContent = '50%';
// 2. 使用 batchNo 请求下载文件流
const fileUrl = `/api/v1/music/download/file?batchNo=${batchNo}&channel=${channel}`;
const fileResponse = await fetch(fileUrl, {
method: 'GET',
credentials: 'include',
});
if (fileResponse.ok) {
// 处理文件流并触发下载
const blob = await fileResponse.blob();
const objectUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = objectUrl;
a.download = 'music_files.zip'; // 下载文件名
document.body.appendChild(a);
a.click();
// 清理资源
window.URL.revokeObjectURL(objectUrl);
a.remove();
alert('文件下载成功!');
} else {
alert('文件下载失败,请稍后重试!');
}
} else {
alert(`批次号获取失败: ${batchData.msg}`);
}
} catch (error) {
console.error("下载过程中发生错误:", error);
alert("请求失败,请检查网络连接!");
} finally {
// 更新进度条为 100%
progressBar.value = 100;
progressText.textContent = '100%';
// alert("下载完成!");
}
};
// 调用主函数
fetchBatchAndDownloadFile();
}
// 显示用户名