mirror of
https://github.com/wushuo894/ani-rss.git
synced 2026-03-13 09:20:23 +00:00
299 lines
12 KiB
Java
299 lines
12 KiB
Java
package ani.rss.download;
|
|
|
|
import ani.rss.commons.FileUtils;
|
|
import ani.rss.commons.GsonStatic;
|
|
import ani.rss.entity.Ani;
|
|
import ani.rss.entity.Config;
|
|
import ani.rss.entity.Item;
|
|
import ani.rss.entity.TorrentsInfo;
|
|
import ani.rss.enums.TorrentsTags;
|
|
import ani.rss.util.basic.HttpReq;
|
|
import ani.rss.util.basic.RenameCacheUtil;
|
|
import cn.hutool.core.codec.Base64;
|
|
import cn.hutool.core.io.FileUtil;
|
|
import cn.hutool.core.io.resource.ResourceUtil;
|
|
import cn.hutool.core.lang.Assert;
|
|
import cn.hutool.core.text.StrFormatter;
|
|
import cn.hutool.core.thread.ThreadUtil;
|
|
import cn.hutool.core.util.ReUtil;
|
|
import cn.hutool.core.util.StrUtil;
|
|
import cn.hutool.http.Header;
|
|
import cn.hutool.http.HttpResponse;
|
|
import com.google.gson.JsonArray;
|
|
import com.google.gson.JsonElement;
|
|
import com.google.gson.JsonObject;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.io.File;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
|
|
/**
|
|
* Transmission
|
|
*/
|
|
@Slf4j
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
public class Transmission implements BaseDownload {
|
|
private String host = "";
|
|
private String authorization = "";
|
|
private String sessionId = "";
|
|
|
|
@Override
|
|
public Boolean login(Boolean test, Config config) {
|
|
String username = config.getDownloadToolUsername();
|
|
String password = config.getDownloadToolPassword();
|
|
host = config.getDownloadToolHost();
|
|
|
|
if (StrUtil.isBlank(host) || StrUtil.isBlank(username)
|
|
|| StrUtil.isBlank(password)) {
|
|
log.warn("Transmission 未配置完成");
|
|
return false;
|
|
}
|
|
|
|
authorization = StrFormatter.format("Basic {}", Base64.encode(username + ":" + password));
|
|
Boolean isOk = HttpReq.get(host)
|
|
.header(Header.AUTHORIZATION, authorization)
|
|
.thenFunction(HttpResponse::isOk);
|
|
if (!isOk) {
|
|
log.error("登录 Transmission 失败");
|
|
return false;
|
|
}
|
|
getTorrentsInfos();
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public List<TorrentsInfo> getTorrentsInfos() {
|
|
String body = ResourceUtil.readUtf8Str("transmission/torrent-get.json");
|
|
try {
|
|
return HttpReq.post(host + "/transmission/rpc")
|
|
.header(Header.AUTHORIZATION, authorization)
|
|
.header("X-Transmission-Session-Id", sessionId)
|
|
.body(body)
|
|
.thenFunction(res -> {
|
|
String id = res.header("X-Transmission-Session-Id");
|
|
if (StrUtil.isNotBlank(id)) {
|
|
sessionId = id;
|
|
return getTorrentsInfos();
|
|
}
|
|
List<TorrentsInfo> torrentsInfos = new ArrayList<>();
|
|
JsonObject jsonObject = GsonStatic.fromJson(res.body(), JsonObject.class);
|
|
JsonArray torrents = jsonObject.get("arguments")
|
|
.getAsJsonObject()
|
|
.get("torrents")
|
|
.getAsJsonArray();
|
|
for (JsonElement jsonElement : torrents.asList()) {
|
|
JsonObject item = jsonElement.getAsJsonObject();
|
|
List<String> tags = item.get("labels").getAsJsonArray()
|
|
.asList().stream().map(JsonElement::getAsString)
|
|
.toList();
|
|
if (!tags.contains(TorrentsTags.ANI_RSS.getValue())) {
|
|
continue;
|
|
}
|
|
List<String> files = item.get("files").getAsJsonArray().asList()
|
|
.stream().map(JsonElement::getAsJsonObject)
|
|
.map(o -> o.get("name").getAsString())
|
|
.toList();
|
|
|
|
// 状态: https://github.com/jayzcoder/TrguiNG/blob/zh/src/rpc/transmission.ts
|
|
|
|
TorrentsInfo.State state = TorrentsInfo.State.downloading;
|
|
|
|
// 做种中
|
|
if (item.get("status").getAsInt() == 6) {
|
|
state = TorrentsInfo.State.stalledUP;
|
|
}
|
|
|
|
// 已完成
|
|
if (item.get("isFinished").getAsBoolean()) {
|
|
state = TorrentsInfo.State.pausedUP;
|
|
}
|
|
|
|
String downloadDir = item.get("downloadDir").getAsString();
|
|
long size = item.get("totalSize").getAsLong();
|
|
long completed = item.get("haveValid").getAsLong();
|
|
|
|
TorrentsInfo torrentsInfo = new TorrentsInfo();
|
|
torrentsInfo.progress(completed, size)
|
|
.setName(item.get("name").getAsString())
|
|
.setTags(tags)
|
|
.setHash(item.get("hashString").getAsString())
|
|
.setState(state)
|
|
.setId(item.get("id").getAsString())
|
|
.setDownloadDir(FileUtils.getAbsolutePath(downloadDir))
|
|
.setFiles(() -> files);
|
|
torrentsInfos.add(torrentsInfo);
|
|
}
|
|
return torrentsInfos;
|
|
});
|
|
} catch (Exception e) {
|
|
log.error(e.getMessage(), e);
|
|
}
|
|
return new ArrayList<>();
|
|
}
|
|
|
|
@Override
|
|
public Boolean download(Ani ani, Item item, String savePath, File torrentFile, Boolean ova) {
|
|
String name = item.getReName();
|
|
String body = ResourceUtil.readUtf8Str("transmission/torrent-add.json");
|
|
String extName = FileUtil.extName(torrentFile);
|
|
if (StrUtil.isBlank(extName)) {
|
|
return false;
|
|
}
|
|
|
|
List<String> tags = newTags(ani, item);
|
|
|
|
String torrent;
|
|
if ("txt".equals(extName)) {
|
|
torrent = FileUtil.readUtf8String(torrentFile);
|
|
body = StrFormatter.format(body, GsonStatic.toJson(tags), savePath, "", torrent);
|
|
} else {
|
|
if (torrentFile.length() > 0) {
|
|
torrent = Base64.encode(torrentFile);
|
|
body = StrFormatter.format(body, GsonStatic.toJson(tags), savePath, torrent, "");
|
|
} else {
|
|
torrent = "magnet:?xt=urn:btih:" + FileUtil.mainName(torrentFile);
|
|
body = StrFormatter.format(body, GsonStatic.toJson(tags), savePath, "", torrent);
|
|
}
|
|
}
|
|
|
|
String id = HttpReq.post(host + "/transmission/rpc")
|
|
.timeout(1000 * 60)
|
|
.header(Header.AUTHORIZATION, authorization)
|
|
.header("X-Transmission-Session-Id", sessionId)
|
|
.body(body)
|
|
.thenFunction(res -> {
|
|
JsonObject jsonObject = GsonStatic.fromJson(res.body(), JsonObject.class);
|
|
return jsonObject.getAsJsonObject("arguments")
|
|
.getAsJsonObject("torrent-added")
|
|
.get("id").getAsString();
|
|
});
|
|
|
|
log.info("tr 添加下载 => name: {} id: {}", name, id);
|
|
|
|
if (!ova) {
|
|
RenameCacheUtil.put(id, name);
|
|
}
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
ThreadUtil.sleep(1000 * 10);
|
|
List<TorrentsInfo> torrentsInfos = getTorrentsInfos();
|
|
Optional<TorrentsInfo> optionalTorrentsInfo = torrentsInfos
|
|
.stream()
|
|
.filter(torrentsInfo -> torrentsInfo.getId().equals(id))
|
|
.findFirst();
|
|
if (optionalTorrentsInfo.isEmpty()) {
|
|
continue;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public Boolean delete(TorrentsInfo torrentsInfo, Boolean deleteFiles) {
|
|
String body = ResourceUtil.readUtf8Str("transmission/torrent-remove.json");
|
|
body = StrFormatter.format(body, torrentsInfo.getId(), deleteFiles);
|
|
try {
|
|
return HttpReq.post(host + "/transmission/rpc")
|
|
.header(Header.AUTHORIZATION, authorization)
|
|
.header("X-Transmission-Session-Id", sessionId)
|
|
.body(body)
|
|
.thenFunction(HttpResponse::isOk);
|
|
} catch (Exception e) {
|
|
log.error(e.getMessage(), e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Boolean rename(TorrentsInfo torrentsInfo) {
|
|
String id = torrentsInfo.getId();
|
|
String name = torrentsInfo.getName();
|
|
|
|
if (ReUtil.contains("^\\w{40}$", name)) {
|
|
log.debug("{} 磁力链接还在获取原数据中", name);
|
|
return false;
|
|
}
|
|
|
|
String reName = RenameCacheUtil.get(id);
|
|
if (StrUtil.isBlank(reName)) {
|
|
log.debug("未获取到重命名 => id: {}", id);
|
|
return false;
|
|
}
|
|
|
|
String extName = FileUtil.extName(name);
|
|
if (StrUtil.isNotBlank(extName)) {
|
|
reName = reName + "." + extName;
|
|
}
|
|
|
|
String body = ResourceUtil.readUtf8Str("transmission/torrent-rename-path.json");
|
|
body = StrFormatter.format(body, id, name, reName);
|
|
|
|
log.info("重命名 {} ==> {}", name, reName);
|
|
|
|
Boolean ok = HttpReq.post(host + "/transmission/rpc")
|
|
.header(Header.AUTHORIZATION, authorization)
|
|
.header("X-Transmission-Session-Id", sessionId)
|
|
.body(body)
|
|
.thenFunction(HttpResponse::isOk);
|
|
Assert.isTrue(ok, "重命名失败 {} ==> {}", name, reName);
|
|
RenameCacheUtil.remove(id);
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
ThreadUtil.sleep(1000);
|
|
Optional<TorrentsInfo> first = getTorrentsInfos().stream()
|
|
.filter(info -> info.getId().equals(id))
|
|
.findFirst();
|
|
if (first.isEmpty()) {
|
|
break;
|
|
}
|
|
if (first.get().getName().equals(reName)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
log.warn("重命名貌似出现了问题?{}", reName);
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public Boolean addTags(TorrentsInfo torrentsInfo, String tag) {
|
|
String id = torrentsInfo.getId();
|
|
List<String> tags = torrentsInfo.getTags();
|
|
List<String> strings = new ArrayList<>(tags);
|
|
strings.add(tag);
|
|
|
|
String body = ResourceUtil.readUtf8Str("transmission/torrent-set.json");
|
|
body = StrFormatter.format(body, GsonStatic.toJson(strings), id);
|
|
return HttpReq.post(host + "/transmission/rpc")
|
|
.header(Header.AUTHORIZATION, authorization)
|
|
.header("X-Transmission-Session-Id", sessionId)
|
|
.body(body)
|
|
.thenFunction(HttpResponse::isOk);
|
|
}
|
|
|
|
@Override
|
|
public void updateTrackers(Set<String> trackers) {
|
|
log.info("Transmission暂时还不支持 自动更新Trackers");
|
|
}
|
|
|
|
@Override
|
|
public void setSavePath(TorrentsInfo torrentsInfo, String path) {
|
|
String id = torrentsInfo.getId();
|
|
String body = ResourceUtil.readUtf8Str("transmission/torrent-set-location.json");
|
|
body = StrFormatter.format(body, id, path);
|
|
HttpReq.post(host + "/transmission/rpc")
|
|
.header(Header.AUTHORIZATION, authorization)
|
|
.header("X-Transmission-Session-Id", sessionId)
|
|
.body(body)
|
|
.thenFunction(HttpResponse::isOk);
|
|
}
|
|
}
|