diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml index ceb30777..56b0adff 100644 --- a/.github/workflows/deploy-test.yml +++ b/.github/workflows/deploy-test.yml @@ -16,13 +16,13 @@ jobs: - name: Build with Project run: | bash ./package.sh - version=$(cat pom.xml | grep -oPm1 '(?<=).*?(?=)') + version=$(cat ani-rss-application/pom.xml | grep -oPm1 '(?<=).*?(?=)') echo "version=v$version" >> $GITHUB_ENV - name: Upload to Artifacts uses: actions/upload-artifact@v4 with: name: Artifacts - path: ./ani-rss-application/target/ani-rss-jar-with-dependencies.jar + path: ./ani-rss-application/target/ani-rss.jar - name: Login to Docker Hub uses: docker/login-action@v3 with: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2ef68d8e..ee9e9ce7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,10 +16,9 @@ jobs: - name: Build with Project run: | bash ./package.sh - bash ./package-win.sh time=$(date +%s%3N) - version=$(cat pom.xml | grep -oPm1 '(?<=).*?(?=)') + version=$(cat ani-rss-application/pom.xml | grep -oPm1 '(?<=).*?(?=)') echo "{\"time\":$time,\"version\":\"$version\"}" > info.json jq --arg content "$(cat UPDATE.md)" '. += {markdown: $content}' info.json > temp.json && mv temp.json info.json @@ -35,12 +34,10 @@ jobs: append_body: false token: ${{ secrets.GITHUB_TOKEN }} files: | - ./ani-rss-application/target/ani-rss-jar-with-dependencies.jar - ./ani-rss-application/target/ani-rss-jar-with-dependencies.jar.md5 - ./ani-rss-application/target/ani-rss-launcher.exe - ./ani-rss-application/target/ani-rss-launcher.exe.md5 - ./ani-rss-application/target/ani-rss.win.x86_64.zip - ./ani-rss-application/target/ani-rss.win.x86_64.zip.md5 + ./ani-rss-application/target/ani-rss.jar + ./ani-rss-application/target/ani-rss.jar.md5 + ./ani-rss-application/target/ani-rss.exe + ./ani-rss-application/target/ani-rss.exe.md5 ./info.json - name: Set up QEMU uses: docker/setup-qemu-action@v3 diff --git a/.gitignore b/.gitignore index 8e25a6f4..c1f52111 100644 --- a/.gitignore +++ b/.gitignore @@ -1,57 +1,7 @@ -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -package-lock.json -pnpm-lock.yaml - -### IntelliJ IDEA ### .idea -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ -*.iws -*.iml -*.ipr - -### Eclipse ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ - -### Mac OS ### -.DS_Store - -config.json -ani.json -files +target logs -*.log /config -.git - -java-17-openjdk-17.0.3.0.6-1.jre.win.x86_64.zip +*.log ani-rss-update.exe -/info.json -build_info -*.versionsBackup -jre.zip +build_info \ No newline at end of file diff --git a/UPDATE.md b/UPDATE.md index d3947cb8..cf7988a0 100644 --- a/UPDATE.md +++ b/UPDATE.md @@ -1,4 +1,35 @@ -- refactor: 优化通知测试 +## 增加Swagger接口文档 + +设置环境变量 `SWAGGER_ENABLED=true` + +通过链接访问 http://127.0.0.1:7789/swagger-ui/index.html + +## 破坏性改动 + +### 环境变量与参数 + +| 旧 | 新 | +|----------|--------------------| +| `--port` | `--server.port` | +| `--host` | `--server.address` | +| `PORT` | `SERVER_PORT` | +| `HOST` | `SERVER_ADDRESS` | + +### emby点格子 + +旧: `http://[IP]:7789/api/web_hook?s=[ApiKey]` + +新: `http://[IP]:7789/api/embyWebhook?s=[ApiKey]` + +## Windows端 + +不再提供内置jdk的压缩包, 需自行安装 + +## 此次更新方式 + +Docker需要重新部署 + +Windows需要手动重新下载 [请不要将本项目在国内宣传](https://github.com/wushuo894/ani-rss/discussions/504) diff --git a/ani-rss-application/.gitignore b/ani-rss-application/.gitignore new file mode 100644 index 00000000..667aaef0 --- /dev/null +++ b/ani-rss-application/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/ani-rss-application/pom.xml b/ani-rss-application/pom.xml index 044d9fe0..78986e09 100644 --- a/ani-rss-application/pom.xml +++ b/ani-rss-application/pom.xml @@ -1,12 +1,11 @@ - + 4.0.0 ani.rss ani-rss - 2.5.10 + 3.0.0 ani-rss-application @@ -17,14 +16,88 @@ UTF-8 + + + central + https://repo.maven.apache.org/maven2 + + + ebml-reader + https://raw.github.com/wushuo894/EBMLReader/mvn-repo + + + tmdb-api + https://raw.github.com/wushuo894/tmdb-api/mvn-repo + + + + + org.springframework.boot + spring-boot-starter-aop + 4.0.0-M2 + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + + + org.eclipse + bittorrent + + + org.xerial + sqlite-jdbc + + + com.sun.mail + jakarta.mail + + + ch.qos.logback + logback-classic + + + io.github.biezhi + TinyPinyin + + + org.jsoup + jsoup + + + com.google.code.gson + gson + + + ebml.reader + ebml-reader + + + wushuo.tmdb.api + tmdb-api + + + cn.hutool + hutool-all + ani.rss ani-rss-ui - ani.rss - ani-rss-web + org.springframework.boot + spring-boot-starter-webmvc + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-webmvc-test + test @@ -32,28 +105,8 @@ ani-rss - org.apache.maven.plugins - maven-assembly-plugin - - - jar-with-dependencies - - - - true - ani.rss.ApplicationMain - - - - - - make-assembly - package - - single - - - + org.springframework.boot + spring-boot-maven-plugin com.akathist.maven.plugins.launch4j @@ -67,8 +120,8 @@ gui - target/ani-rss-launcher.exe - target/ani-rss-jar-with-dependencies.jar + target/ani-rss.exe + target/ani-rss.jar Java environment is required! --gui . @@ -79,11 +132,11 @@ false ${project.parent.basedir}/ani-rss-ui/public/favicon.ico - ani-rss-launcher (${project.version}) + ani-rss (${project.version}) ani-rss (${project.version}) - jre/bin;%JAVA_HOME%/bin;%PATH% + %JAVA_HOME%/bin;%PATH% 17 -Xms60m -Xmx1g -Xss256k @@ -99,7 +152,7 @@ Copyright (C) 2024-2025 ${project.artifactId} ${project.artifactId} - ani-rss-launcher.exe + ani-rss.exe SIMPLIFIED_CHINESE @@ -144,4 +197,5 @@ + diff --git a/ani-rss-application/src/main/java/ani/rss/AniRssApplication.java b/ani-rss-application/src/main/java/ani/rss/AniRssApplication.java new file mode 100644 index 00000000..416f24aa --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/AniRssApplication.java @@ -0,0 +1,33 @@ +package ani.rss; + +import ani.rss.entity.Global; +import ani.rss.util.other.MenuUtil; +import cn.hutool.core.util.ObjectUtil; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +import java.security.Security; +import java.util.List; + +@EnableScheduling +@SpringBootApplication +public class AniRssApplication { + + public static void main(String[] args) { + Global.ARGS = List.of(ObjectUtil.defaultIfNull(args, new String[]{})); + loadProperty(); + MenuUtil.start(); + SpringApplication.run(AniRssApplication.class, args); + } + + public static void loadProperty() { + // 启用Basic认证 + System.setProperty("jdk.http.auth.tunneling.disabledSchemes", ""); + // DNS解析成功过期时间 + Security.setProperty("networkaddress.cache.ttl", "30"); + // DNS解析失败过期时间 + Security.setProperty("networkaddress.cache.negative.ttl", "5"); + } + +} diff --git a/ani-rss-application/src/main/java/ani/rss/ApplicationMain.java b/ani-rss-application/src/main/java/ani/rss/ApplicationMain.java deleted file mode 100644 index b390b120..00000000 --- a/ani-rss-application/src/main/java/ani/rss/ApplicationMain.java +++ /dev/null @@ -1,54 +0,0 @@ -package ani.rss; - -import ani.rss.commons.ExceptionUtils; -import ani.rss.commons.MavenUtils; -import ani.rss.entity.Global; -import ani.rss.other.Cron; -import ani.rss.service.TaskService; -import ani.rss.util.other.AniUtil; -import ani.rss.util.other.ConfigUtil; -import ani.rss.util.other.MenuUtil; -import ani.rss.web.util.ServerUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.RuntimeUtil; -import lombok.extern.slf4j.Slf4j; - -import java.security.Security; -import java.util.List; - -@Slf4j -public class ApplicationMain { - - public static void main(String[] args) { - Global.ARGS = List.of(ObjectUtil.defaultIfNull(args, new String[]{})); - loadProperty(); - try { - ConfigUtil.load(); - ConfigUtil.backup(); - MenuUtil.start(); - ServerUtil.start(); - - AniUtil.load(); - TaskService.start(); - String version = MavenUtils.getVersion(); - log.info("version {}", version); - - Cron.start(); - } catch (Exception e) { - String message = ExceptionUtils.getMessage(e); - log.error(message, e); - System.exit(1); - } - RuntimeUtil.addShutdownHook(() -> log.info("程序退出...")); - } - - public static void loadProperty() { - // 启用Basic认证 - System.setProperty("jdk.http.auth.tunneling.disabledSchemes", ""); - // DNS解析成功过期时间 - Security.setProperty("networkaddress.cache.ttl", "30"); - // DNS解析失败过期时间 - Security.setProperty("networkaddress.cache.negative.ttl", "5"); - } - -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/AboutAction.java b/ani-rss-application/src/main/java/ani/rss/action/AboutAction.java deleted file mode 100644 index 36e345cd..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/AboutAction.java +++ /dev/null @@ -1,24 +0,0 @@ -package ani.rss.action; - -import ani.rss.util.other.UpdateUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -/** - * 关于 - */ -@Auth -@Slf4j -@Path("/about") -public class AboutAction implements BaseAction { - - @Override - public void doAction(HttpServerRequest req, HttpServerResponse res) { - resultSuccess(UpdateUtil.about()); - } - -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/AfdianAction.java b/ani-rss-application/src/main/java/ani/rss/action/AfdianAction.java deleted file mode 100644 index 8c8d16f2..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/AfdianAction.java +++ /dev/null @@ -1,102 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.Config; -import ani.rss.entity.Result; -import ani.rss.entity.TryOut; -import ani.rss.util.basic.HttpReq; -import ani.rss.util.other.AfdianUtil; -import ani.rss.util.other.ConfigUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import ani.rss.web.auth.enums.AuthType; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.http.HttpResponse; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; -import java.util.Date; - -/** - * 爱发电 - */ -@Auth(type = { - AuthType.IP_WHITE_LIST, - AuthType.HEADER, - AuthType.FORM -}) -@Slf4j -@Path("/afdian") -public class AfdianAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String type = request.getParam("type"); - - if (type.equals("verifyNo")) { - Config config = getBody(Config.class); - String outTradeNo = config.getOutTradeNo(); - Result result = AfdianUtil.verifyNo(outTradeNo); - result(result); - - int code = result.getCode(); - if (code == 200) { - Long time = DateUtil.offsetYear(new Date(), 999).getTime(); - ConfigUtil.CONFIG.setOutTradeNo(outTradeNo) - .setExpirationTime(time) - .setTryOut(false); - ConfigUtil.sync(); - } - return; - } - - if (type.equals("tryOut")) { - Config config = getBody(Config.class); - if (AfdianUtil.verifyExpirationTime()) { - resultError(result -> - result - .setMessage("还在试用中!") - ); - return; - } - - String githubToken = config.getGithubToken(); - Assert.notBlank(githubToken, "GithubToken 不能为空"); - - Boolean ok = HttpReq.get("https://api.github.com/user/starred/wushuo894/ani-rss") - .header("Authorization", "Bearer " + githubToken) - .thenFunction(HttpResponse::isOk); - - Assert.isTrue(ok, "未点击star"); - - TryOut tryOut = AfdianUtil.getTryOut(); - - Boolean enable = tryOut.getEnable(); - Boolean renewal = tryOut.getRenewal(); - Integer day = tryOut.getDay(); - String message = tryOut.getMessage(); - - Assert.isTrue(enable, message); - - if (config.getTryOut()) { - // 已经有过试用 - Assert.isTrue(renewal, message); - } - - long time = DateUtil.offsetDay(new Date(), day).getTime(); - ConfigUtil.CONFIG - .setGithubToken(githubToken) - .setExpirationTime(time) - .setTryOut(true); - ConfigUtil.sync(); - resultSuccess(result -> - result - .setMessage(message) - .setData(time) - ); - } - } - -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/BgmAction.java b/ani-rss-application/src/main/java/ani/rss/action/BgmAction.java deleted file mode 100644 index 473107bf..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/BgmAction.java +++ /dev/null @@ -1,77 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.Ani; -import ani.rss.entity.BgmInfo; -import ani.rss.util.other.AniUtil; -import ani.rss.util.other.BgmUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.lang.Opt; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import com.google.gson.JsonObject; -import lombok.extern.slf4j.Slf4j; -import wushuo.tmdb.api.entity.Tmdb; - -import java.io.IOException; -import java.util.Objects; - -/** - * bgm - */ -@Auth -@Slf4j -@Path("/bgm") -public class BgmAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String type = request.getParam("type"); - switch (type) { - case "search" -> { - // 搜索 - String name = request.getParam("name"); - resultSuccess(BgmUtil.search(name)); - } - case "getAniBySubjectId" -> { - // 将指定id的BGM番剧转换为订阅 - String id = request.getParam("id"); - BgmInfo bgmInfo = BgmUtil.getBgmInfo(id, true); - Ani ani = BgmUtil.toAni(bgmInfo, AniUtil.createAni()); - ani - .setCustomDownloadPath(true); - resultSuccess(ani); - } - case "getTitle" -> { - // 获取BGM标题 - Ani ani = getBody(Ani.class); - Tmdb tmdb = ani.getTmdb(); - BgmInfo bgmInfo = BgmUtil.getBgmInfo(ani); - resultSuccess(BgmUtil.getFinalName(bgmInfo, tmdb)); - } - case "rate" -> { - // 评分 - Ani ani = getBody(Ani.class); - String subjectId = BgmUtil.getSubjectId(ani); - Integer score = Opt.ofNullable(ani.getScore()) - .map(Double::intValue) - .orElse(null); - resultSuccess(result -> { - result.setData(BgmUtil.rate(subjectId, score)) - .setMessage("保存评分成功"); - if (Objects.isNull(score)) { - result.setMessage(""); - } - }); - } - case "me" -> { - // 获取当前BGM账号信息 - Long expiresDays = BgmUtil.getExpiresDays(); - JsonObject me = BgmUtil.me(); - me.addProperty("expires_days", expiresDays); - resultSuccess(me); - } - } - - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/BgmCallbackAction.java b/ani-rss-application/src/main/java/ani/rss/action/BgmCallbackAction.java deleted file mode 100644 index adefb2db..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/BgmCallbackAction.java +++ /dev/null @@ -1,53 +0,0 @@ -package ani.rss.action; - -import ani.rss.commons.GsonStatic; -import ani.rss.entity.Config; -import ani.rss.util.basic.HttpReq; -import ani.rss.util.other.ConfigUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import com.google.gson.JsonObject; - -import java.io.IOException; -import java.util.Map; - -/** - * BGM 授权回调 - */ -@Auth -@Path("/bgm/oauth/callback") -public class BgmCallbackAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String code = request.getParam("code"); - Config config = ConfigUtil.CONFIG; - String bgmAppID = config.getBgmAppID(); - String bgmAppSecret = config.getBgmAppSecret(); - String bgmRedirectUri = config.getBgmRedirectUri(); - - Map map = Map.of( - "grant_type", "authorization_code", - "client_id", bgmAppID, - "client_secret", bgmAppSecret, - "code", code, - "redirect_uri", bgmRedirectUri - ); - - HttpReq.post("https://bgm.tv/oauth/access_token") - .body(GsonStatic.toJson(map)) - .then(res -> { - HttpReq.assertStatus(res); - JsonObject jsonObject = GsonStatic.fromJson(res.body(), JsonObject.class); - String accessToken = jsonObject.get("access_token").getAsString(); - String refreshToken = jsonObject.get("refresh_token").getAsString(); - config.setBgmToken(accessToken) - .setBgmRefreshToken(refreshToken); - }); - ConfigUtil.sync(); - - resultSuccessMsg("授权成功, 现在你可以关闭此窗口"); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/ClearCacheAction.java b/ani-rss-application/src/main/java/ani/rss/action/ClearCacheAction.java deleted file mode 100644 index d9903c67..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/ClearCacheAction.java +++ /dev/null @@ -1,72 +0,0 @@ -package ani.rss.action; - -import ani.rss.commons.FileUtils; -import ani.rss.entity.Ani; -import ani.rss.service.ClearService; -import ani.rss.util.other.AniUtil; -import ani.rss.util.other.ConfigUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.NumberUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.File; -import java.io.IOException; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * 缓存清理 - */ -@Slf4j -@Auth -@Path("/clearCache") -public class ClearCacheAction implements BaseAction { - - @Override - public synchronized void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - File configDir = ConfigUtil.getConfigDir(); - String configDirStr = FileUtils.getAbsolutePath(configDir); - - Set covers = AniUtil.ANI_LIST - .stream() - .map(Ani::getCover) - .map(s -> FileUtils.getAbsolutePath(new File(configDirStr + "/files/" + s))) - .collect(Collectors.toSet()); - - FileUtil.mkdir(configDirStr + "/files"); - FileUtil.mkdir(configDirStr + "/img"); - - Set files = FileUtil.loopFiles(configDirStr + "/files") - .stream() - .filter(file -> { - String fileName = FileUtils.getAbsolutePath(file); - return !covers.contains(fileName); - }).collect(Collectors.toSet()); - long filesSize = files.stream() - .mapToLong(File::length) - .sum(); - long imgSize = FileUtil.size(new File(configDirStr + "/img")); - - long sumSize = filesSize + imgSize; - - if (sumSize < 1) { - resultSuccessMsg("清理完成, 共清理{}MB", 0); - return; - } - - for (File file : files) { - FileUtil.del(file); - ClearService.clearParentFile(file); - } - - FileUtil.del(configDirStr + "/img"); - - resultSuccessMsg("清理完成, 共清理{}MB", NumberUtil.decimalFormat("0.00", sumSize / 1024.0 / 1024.0)); - } - -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/ConfigAction.java b/ani-rss-application/src/main/java/ani/rss/action/ConfigAction.java deleted file mode 100644 index fc6d08e4..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/ConfigAction.java +++ /dev/null @@ -1,123 +0,0 @@ -package ani.rss.action; - -import ani.rss.commons.MavenUtils; -import ani.rss.entity.Config; -import ani.rss.entity.Login; -import ani.rss.service.TaskService; -import ani.rss.util.other.AfdianUtil; -import ani.rss.util.other.ConfigUtil; -import ani.rss.util.other.TorrentUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.bean.copier.CopyOptions; -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; - -import java.io.IOException; -import java.util.Objects; - -/** - * 设置 - */ -@Auth -@Path("/config") -public class ConfigAction implements BaseAction { - - @Override - public void doAction(HttpServerRequest req, HttpServerResponse res) throws IOException { - String method = req.getMethod(); - if (method.equals("GET")) { - String version = MavenUtils.getVersion(); - String buildInfo = buildInfo(); - Config config = ObjectUtil.clone(ConfigUtil.CONFIG); - config.getLogin().setPassword(""); - config.setVersion(version) - .setBuildInfo(buildInfo) - .setVerifyExpirationTime(AfdianUtil.verifyExpirationTime()); - resultSuccess(config); - return; - } - - if (!method.equals("POST")) { - return; - } - Config config = ConfigUtil.CONFIG; - Login login = config.getLogin(); - String username = login.getUsername(); - String password = login.getPassword(); - Integer renameSleepSeconds = config.getRenameSleepSeconds(); - Integer sleep = config.getRssSleepMinutes(); - String download = config.getDownloadToolType(); - - Config newConfig = getBody(Config.class); - newConfig.setExpirationTime(null) - .setOutTradeNo(null) - .setTryOut(null); - - CopyOptions copyOptions = CopyOptions - .create() - .setIgnoreNullValue(true); - - BeanUtil.copyProperties( - newConfig, - config, - copyOptions - ); - - String loginPassword = config.getLogin().getPassword(); - // 密码未发生修改 - if (StrUtil.isBlank(loginPassword)) { - config.getLogin().setPassword(password); - } - String loginUsername = config.getLogin().getUsername(); - if (StrUtil.isBlank(loginUsername)) { - config.getLogin().setUsername(username); - } - - Boolean proxy = config.getProxy(); - if (proxy) { - String proxyHost = config.getProxyHost(); - Integer proxyPort = config.getProxyPort(); - if (StrUtil.isBlank(proxyHost) || Objects.isNull(proxyPort)) { - resultErrorMsg("代理参数不完整"); - return; - } - } - - ConfigUtil.sync(); - Integer newRenameSleepSeconds = config.getRenameSleepSeconds(); - Integer newSleep = config.getRssSleepMinutes(); - - // 时间间隔发生改变,重启任务 - if ( - !Objects.equals(newSleep, sleep) || - !Objects.equals(newRenameSleepSeconds, renameSleepSeconds) - ) { - TaskService.restart(); - } - // 下载工具发生改变 - if (!download.equals(config.getDownloadToolType())) { - TorrentUtil.load(); - } - - resultSuccessMsg("修改成功"); - } - - /** - * 构建信息 - */ - public String buildInfo() { - String buildInfo = ""; - try { - buildInfo = ResourceUtil.readUtf8Str("build_info"); - } catch (Exception ignored) { - } - return buildInfo; - } - -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/CoverAction.java b/ani-rss-application/src/main/java/ani/rss/action/CoverAction.java deleted file mode 100644 index 41469135..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/CoverAction.java +++ /dev/null @@ -1,27 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.Ani; -import ani.rss.util.other.AniUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; - -/** - * 刷新封面 - */ -@Slf4j -@Auth -@Path("/cover") -public class CoverAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - Ani ani = getBody(Ani.class); - String s = AniUtil.saveJpg(ani.getImage(), true); - resultSuccess(s); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/CustomCssAction.java b/ani-rss-application/src/main/java/ani/rss/action/CustomCssAction.java deleted file mode 100644 index 22da5e38..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/CustomCssAction.java +++ /dev/null @@ -1,33 +0,0 @@ -package ani.rss.action; - -import ani.rss.util.other.ConfigUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.util.StrUtil; -import cn.hutool.http.Header; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; - -/** - * 自定义css - */ -@Slf4j -@Auth(value = false) -@Path("/custom.css") -public class CustomCssAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - response.setHeader(Header.CACHE_CONTROL, "no-store, no-cache, must-revalidate, max-age=0"); - response.setHeader(Header.PRAGMA, "no-cache"); - response.setHeader("Expires", "0"); - - String customCss = ConfigUtil.CONFIG.getCustomCss(); - customCss = StrUtil.blankToDefault(customCss, "/* empty css */"); - String contentType = "text/css"; - response.write(customCss, contentType); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/CustomJsAction.java b/ani-rss-application/src/main/java/ani/rss/action/CustomJsAction.java deleted file mode 100644 index 88a03c60..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/CustomJsAction.java +++ /dev/null @@ -1,33 +0,0 @@ -package ani.rss.action; - -import ani.rss.util.other.ConfigUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.util.StrUtil; -import cn.hutool.http.Header; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; - -/** - * 自定义js - */ -@Slf4j -@Auth(value = false) -@Path("/custom.js") -public class CustomJsAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - response.setHeader(Header.CACHE_CONTROL, "no-store, no-cache, must-revalidate, max-age=0"); - response.setHeader(Header.PRAGMA, "no-cache"); - response.setHeader("Expires", "0"); - - String customJs = ConfigUtil.CONFIG.getCustomJs(); - customJs = StrUtil.blankToDefault(customJs, "// empty js"); - String contentType = "application/javascript; charset=utf-8"; - response.write(customJs, contentType); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/DownloadLogsAction.java b/ani-rss-application/src/main/java/ani/rss/action/DownloadLogsAction.java deleted file mode 100644 index 661e8392..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/DownloadLogsAction.java +++ /dev/null @@ -1,53 +0,0 @@ -package ani.rss.action; - -import ani.rss.util.other.ConfigUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.text.StrFormatter; -import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.ZipUtil; -import cn.hutool.http.Header; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.Cleanup; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; - -/** - * 下载日志 - */ -@Auth -@Path("/downloadLogs") -public class DownloadLogsAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - File configDir = ConfigUtil.getConfigDir(); - String logsPath = configDir + "/logs"; - - String filename = "logs.zip"; - - String contentType = getContentType(filename); - - response.setContentType(contentType); - response.setHeader(Header.CONTENT_DISPOSITION, StrFormatter.format("inline; filename=\"{}\"", filename)); - - @Cleanup - OutputStream outputStream = response.getOut(); - - ZipUtil.zip(outputStream, StandardCharsets.UTF_8, false, name -> { - if (FileUtil.isDirectory(name)) { - return true; - } - String extName = FileUtil.extName(name); - if (StrUtil.isBlank(extName)) { - return false; - } - return extName.equals("log"); - }, new File(logsPath)); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/DownloadPathAction.java b/ani-rss-application/src/main/java/ani/rss/action/DownloadPathAction.java deleted file mode 100644 index c67052a2..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/DownloadPathAction.java +++ /dev/null @@ -1,45 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.Ani; -import ani.rss.service.DownloadService; -import ani.rss.util.other.AniUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; - -import java.io.IOException; -import java.util.Map; -import java.util.Optional; - -/** - * 获取下载位置 - */ -@Auth -@Path("/downloadPath") -public class DownloadPathAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - Ani ani = getBody(Ani.class); - String downloadPath = DownloadService.getDownloadPath(ani); - - boolean change = false; - Optional first = AniUtil.ANI_LIST.stream() - .filter(it -> it.getId().equals(ani.getId())) - .findFirst(); - if (first.isPresent()) { - Ani oldAni = ObjectUtil.clone(first.get()); - // 只在名称改变时移动 - oldAni.setSeason(ani.getSeason()); - String oldDownloadPath = DownloadService.getDownloadPath(oldAni); - change = !downloadPath.equals(oldDownloadPath); - } - - resultSuccess(Map.of( - "change", change, - "downloadPath", downloadPath - )); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/DownloadTestAction.java b/ani-rss-application/src/main/java/ani/rss/action/DownloadTestAction.java deleted file mode 100644 index ef2c4d96..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/DownloadTestAction.java +++ /dev/null @@ -1,36 +0,0 @@ -package ani.rss.action; - -import ani.rss.download.BaseDownload; -import ani.rss.entity.Config; -import ani.rss.util.other.ConfigUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.util.ClassUtil; -import cn.hutool.core.util.ReflectUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; - -import java.io.IOException; - -/** - * 测试下载工具 - */ -@Auth -@Path("/downloadLoginTest") -public class DownloadTestAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - Config config = getBody(Config.class); - ConfigUtil.format(config); - String download = config.getDownloadToolType(); - Class loadClass = ClassUtil.loadClass("ani.rss.download." + download); - BaseDownload baseDownload = (BaseDownload) ReflectUtil.newInstance(loadClass); - Boolean login = baseDownload.login(true, config); - if (login) { - resultSuccessMsg("登录成功"); - return; - } - resultErrorMsg("登录失败"); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/EmbyAction.java b/ani-rss-application/src/main/java/ani/rss/action/EmbyAction.java deleted file mode 100644 index b4e65425..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/EmbyAction.java +++ /dev/null @@ -1,39 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.EmbyViews; -import ani.rss.entity.NotificationConfig; -import ani.rss.util.other.EmbyUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; - -import java.io.IOException; -import java.util.List; - -/** - * Emby - */ -@Auth -@Path("/emby") -public class EmbyAction implements BaseAction { - - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - NotificationConfig notificationConfig = getBody(NotificationConfig.class); - String type = request.getParam("type"); - - if (type.equals("getViews")) { - List views = EmbyUtil.getViews(notificationConfig); - resultSuccess(views); - return; - } - - if (type.equals("refresh")) { - EmbyUtil.refresh(notificationConfig); - resultSuccess(); - } - - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/FileAction.java b/ani-rss-application/src/main/java/ani/rss/action/FileAction.java deleted file mode 100644 index 59975151..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/FileAction.java +++ /dev/null @@ -1,209 +0,0 @@ -package ani.rss.action; - -import ani.rss.commons.ExceptionUtils; -import ani.rss.util.basic.HttpReq; -import ani.rss.util.other.ConfigUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import ani.rss.web.util.ServerUtil; -import cn.hutool.core.codec.Base64; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.text.StrFormatter; -import cn.hutool.core.util.ReflectUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.URLUtil; -import cn.hutool.http.Header; -import cn.hutool.http.HttpConnection; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.Cleanup; -import lombok.extern.slf4j.Slf4j; - -import java.io.*; -import java.net.URI; -import java.nio.channels.Channels; -import java.nio.channels.FileChannel; -import java.util.function.Consumer; - -/** - * 文件 - */ -@Slf4j -@Auth -@Path("/file") -public class FileAction implements BaseAction { - - public void getImg(String url, Consumer consumer) { - URI host = URLUtil.getHost(URLUtil.url(url)); - HttpReq.get(url) - .then(res -> { - HttpConnection httpConnection = (HttpConnection) ReflectUtil.getFieldValue(res, "httpConnection"); - URI host1 = URLUtil.getHost(httpConnection.getUrl()); - if (host.toString().equals(host1.toString())) { - try { - @Cleanup - InputStream inputStream = res.bodyStream(); - consumer.accept(inputStream); - } catch (Exception ignored) { - } - return; - } - String newUrl = url.replace(host.toString(), host1.toString()); - getImg(newUrl, consumer); - }); - } - - /** - * 处理图片文件 - * - * @param img 图片名 - */ - public void doImg(String img) { - HttpServerResponse response = ServerUtil.RESPONSE.get(); - - // 30 天 - long maxAge = 86400 * 30; - - response.setHeader(Header.CACHE_CONTROL, "private, max-age=" + maxAge); - - String contentType = getContentType(URLUtil.getPath(img)); - - File configDir = ConfigUtil.getConfigDir(); - - File file = new File(URLUtil.getPath(img)); - configDir = new File(configDir + "/img/" + file.getParentFile().getName()); - FileUtil.mkdir(configDir); - - File imgFile = new File(configDir, file.getName()); - if (imgFile.exists()) { - try { - @Cleanup - InputStream inputStream = FileUtil.getInputStream(imgFile); - response.write(inputStream, (int) imgFile.length(), contentType); - } catch (Exception ignored) { - } - return; - } - - getImg(img, is -> { - try { - FileUtil.writeFromStream(is, imgFile, true); - @Cleanup - BufferedInputStream inputStream = FileUtil.getInputStream(imgFile); - response.write(inputStream, (int) imgFile.length(), contentType); - } catch (Exception ignored) { - } - }); - } - - /** - * 处理文件 - * - * @param filename 文件名 - */ - private void doFile(String filename) { - HttpServerRequest request = ServerUtil.REQUEST.get(); - HttpServerResponse response = ServerUtil.RESPONSE.get(); - - File file = new File(filename); - if (!file.exists()) { - File configDir = ConfigUtil.getConfigDir(); - file = new File(configDir + "/files/" + filename); - if (!file.exists()) { - BaseAction.writeNotFound(); - return; - } - } - - boolean hasRange = false; - long fileLength = file.length(); - long start = 0; - long end = fileLength - 1; - - String contentType = getContentType(file.getName()); - - response.setHeader(Header.CONTENT_DISPOSITION, StrFormatter.format("inline; filename=\"{}\"", URLUtil.encode(file.getName()))); - if (contentType.startsWith("video/")) { - response.setContentType(contentType); - response.setHeader("Accept-Ranges", "bytes"); - String rangeHeader = request.getHeader("Range"); - if (StrUtil.isNotBlank(rangeHeader) && rangeHeader.startsWith("bytes=")) { - String[] range = rangeHeader.substring(6).split("-"); - if (range.length > 0) { - start = Long.parseLong(range[0]); - } - if (range.length > 1) { - end = Long.parseLong(range[1]); - } else { - long maxEnd = start + (1024 * 1024 * 10); - end = Math.min(end, maxEnd); - } - } - response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength); - hasRange = true; - } else { - long maxAge = 0; - - // 小于或者等于 3M 缓存 - if (fileLength <= 1024 * 1024 * 3) { - // 30 天 - maxAge = 86400 * 30; - } - - response.setHeader(Header.CACHE_CONTROL, "private, max-age=" + maxAge); - response.setContentType(contentType); - } - - try { - if (hasRange) { - long length = end - start; - response.send(206, length); - @Cleanup - OutputStream out = response.getOut(); - @Cleanup - RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); - randomAccessFile.seek(start); - @Cleanup - FileChannel channel = randomAccessFile.getChannel(); - @Cleanup - InputStream inputStream = Channels.newInputStream(channel); - IoUtil.copy(inputStream, out, 40960, length, null); - } else { - @Cleanup - InputStream inputStream = FileUtil.getInputStream(file); - response.write(inputStream, (int) fileLength); - } - } catch (Exception e) { - String message = ExceptionUtils.getMessage(e); - log.debug(message, e); - } - } - - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String img = request.getParam("img"); - if (StrUtil.isNotBlank(img)) { - if (Base64.isBase64(img)) { - img = Base64.decodeStr(img); - } - doImg(img); - return; - } - - String filename = request.getParam("filename"); - - if (StrUtil.isBlank(filename)) { - BaseAction.writeNotFound(); - return; - } - - if (Base64.isBase64(filename)) { - filename = Base64.decodeStr(filename); - } - - doFile(filename); - } - -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/ImportAction.java b/ani-rss-application/src/main/java/ani/rss/action/ImportAction.java deleted file mode 100644 index a5e05be4..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/ImportAction.java +++ /dev/null @@ -1,77 +0,0 @@ -package ani.rss.action; - -import ani.rss.dto.ImportAniDataDTO; -import ani.rss.entity.Ani; -import ani.rss.util.other.AniUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.lang.UUID; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.Synchronized; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; -import java.util.List; -import java.util.Optional; - -/** - * 导入订阅 - */ -@Slf4j -@Auth -@Path("/ani/import") -public class ImportAction implements BaseAction { - - static final List ANI_LIST = AniUtil.ANI_LIST; - - @Override - @Synchronized("ANI_LIST") - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - ImportAniDataDTO dto = getBody(ImportAniDataDTO.class); - List aniList = dto.getAniList(); - if (aniList.isEmpty()) { - resultErrorMsg("导入列表为空"); - return; - } - - ImportAniDataDTO.Conflict conflict = dto.getConflict(); - - for (Ani ani : aniList) { - AniUtil.verify(ani); - - String title = ani.getTitle(); - int season = ani.getSeason(); - Optional first = AniUtil.ANI_LIST.stream() - .filter(it -> it.getTitle().equals(title) && it.getSeason() == season) - .findFirst(); - - if (first.isEmpty()) { - String image = ani.getImage(); - String cover = AniUtil.saveJpg(image); - ani.setCover(cover) - .setId(UUID.fastUUID().toString()); - ANI_LIST.add(ani); - continue; - } - - if (conflict == ImportAniDataDTO.Conflict.SKIP) { - log.info("存在冲突,已跳过 {} 第{}季", title, season); - continue; - } - - log.info("存在冲突,已替换 {} 第{}季", title, season); - String image = ani.getImage(); - String cover = AniUtil.saveJpg(image); - ani.setCover(cover); - - String[] ignoreProperties = new String[]{"id", "currentEpisodeNumber", "lastDownloadTime"}; - BeanUtil.copyProperties(ani, first.get(), ignoreProperties); - } - - AniUtil.sync(); - resultSuccessMsg("导入成功"); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/ItemsAction.java b/ani-rss-application/src/main/java/ani/rss/action/ItemsAction.java deleted file mode 100644 index b465a6c0..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/ItemsAction.java +++ /dev/null @@ -1,52 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.Ani; -import ani.rss.entity.Item; -import ani.rss.service.DownloadService; -import ani.rss.util.other.ItemsUtil; -import ani.rss.util.other.TorrentUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; - -import java.io.File; -import java.io.IOException; -import java.util.List; -import java.util.Map; - -/** - * 预览订阅 - */ -@Auth -@Path("/items") -public class ItemsAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - Ani ani = getBody(Ani.class); - List items = ItemsUtil.getItems(ani); - - String downloadPath = DownloadService.getDownloadPath(ani); - - for (Item item : items) { - item.setLocal(false); - File torrent = TorrentUtil.getTorrent(ani, item); - if (torrent.exists()) { - item.setLocal(true); - continue; - } - if (DownloadService.itemDownloaded(ani, item, false)) { - item.setLocal(true); - } - } - - List omitList = ItemsUtil.omitList(ani, items); - - resultSuccess(Map.of( - "downloadPath", downloadPath, - "items", items, - "omitList", omitList - )); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/LogsAction.java b/ani-rss-application/src/main/java/ani/rss/action/LogsAction.java deleted file mode 100644 index 2cf92b20..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/LogsAction.java +++ /dev/null @@ -1,37 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.Log; -import ani.rss.util.basic.LogUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.http.Method; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.Synchronized; -import lombok.extern.slf4j.Slf4j; - -import java.util.List; - -/** - * 日志 - */ -@Slf4j -@Auth -@Path("/logs") -public class LogsAction implements BaseAction { - List LOG_LIST = LogUtil.LOG_LIST; - - @Override - @Synchronized("LOG_LIST") - public void doAction(HttpServerRequest req, HttpServerResponse res) { - String method = req.getMethod(); - if (Method.DELETE.name().equals(method)) { - LOG_LIST.clear(); - log.info("清理日志"); - resultSuccess(); - return; - } - resultSuccess(LOG_LIST); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/MikanAction.java b/ani-rss-application/src/main/java/ani/rss/action/MikanAction.java deleted file mode 100644 index 2eb8c651..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/MikanAction.java +++ /dev/null @@ -1,25 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.Mikan; -import ani.rss.util.other.MikanUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; - -import java.io.IOException; - -/** - * Mikan搜索 - */ -@Auth -@Path("/mikan") -public class MikanAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String text = request.getParam("text"); - Mikan.Season season = getBody(Mikan.Season.class); - resultSuccess(MikanUtil.list(text, season)); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/MikanGroupAction.java b/ani-rss-application/src/main/java/ani/rss/action/MikanGroupAction.java deleted file mode 100644 index 4508ea64..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/MikanGroupAction.java +++ /dev/null @@ -1,68 +0,0 @@ -package ani.rss.action; - -import ani.rss.commons.GsonStatic; -import ani.rss.entity.Mikan; -import ani.rss.entity.TorrentsInfo; -import ani.rss.util.other.MikanUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ReUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Mikan字幕组 - */ -@Auth -@Path("/mikan/group") -public class MikanGroupAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String url = request.getParam("url"); - List groups = MikanUtil.getGroups(url); - - List regexItemList = List.of( - "1920[Xx]1080", "3840[Xx]2160", "1080[Pp]", "4[Kk]", "720[Pp]", - "繁", "简", "日", - "cht|Cht|CHT", "chs|Chs|CHS", "hevc|Hevc|HEVC", - "10bit|10Bit|10BIT", "h265|H265", "h264|H264", - "内嵌", "内封", "外挂", - "mp4|MP4", "mkv|MKV" - ); - - for (Mikan.Group group : groups) { - Set tags = new HashSet<>(); - List> regexList = new ArrayList<>(); - List items = group.getItems(); - for (TorrentsInfo item : items) { - String name = item.getName(); - List regexItems = new ArrayList<>(); - for (String regex : regexItemList) { - if (!ReUtil.contains(regex, name)) { - continue; - } - String label = ReUtil.get(regex, name, 0); - label = label.toUpperCase(); - Mikan.RegexItem regexItem = new Mikan.RegexItem(label, regex); - regexItems.add(regexItem); - tags.add(label); - } - regexItems = CollUtil.distinct(regexItems, GsonStatic::toJson, true); - regexList.add(regexItems); - } - - regexList = CollUtil.distinct(regexList, GsonStatic::toJson, true); - group.setRegexList(regexList) - .setTags(tags); - } - resultSuccess(groups); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/PlayItemAction.java b/ani-rss-application/src/main/java/ani/rss/action/PlayItemAction.java deleted file mode 100644 index a1ca7a8b..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/PlayItemAction.java +++ /dev/null @@ -1,100 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.PlayItem; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.codec.Base64; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import com.google.gson.JsonObject; -import com.matthewn4444.ebml.EBMLReader; -import com.matthewn4444.ebml.subtitles.Subtitles; -import lombok.Cleanup; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * 视频操作 - */ -@Auth -@Slf4j -@Path("/playitem") -public class PlayItemAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - JsonObject jsonObject = getBody(JsonObject.class); - String type = jsonObject.get("type").getAsString(); - String file = jsonObject.get("file").getAsString(); - if ("getSubtitles".equalsIgnoreCase(type)) { - getSubtitles(file); - return; - } - - resultErrorMsg("未知操作"); - } - - /** - * 获取内封字幕并返回到客户端 - * - * @param file - * @throws IOException - */ - public void getSubtitles(String file) throws IOException { - Assert.notBlank(file); - - if (Base64.isBase64(file)) { - file = Base64.decodeStr(file); - } - - List subtitlesList = new ArrayList<>(); - - String extName = FileUtil.extName(file); - if (StrUtil.isBlank(extName)) { - resultSuccess(subtitlesList); - return; - } - - if (!"mkv".equals(extName)) { - resultSuccess(subtitlesList); - return; - } - - Assert.isTrue(FileUtil.exist(file), "视频文件不存在"); - - @Cleanup - EBMLReader reader = new EBMLReader(file); - if (!reader.readHeader()) { - resultSuccess(subtitlesList); - return; - } - reader.readTracks(); - reader.readCues(); - - for (int i = 0; i < reader.getCuesCount(); i++) { - reader.readSubtitlesInCueFrame(i); - } - - List subtitles = reader.getSubtitles(); - for (Subtitles subtitle : subtitles) { - String name = subtitle.getName(); - String presentableName = subtitle.getPresentableName(); - String contents = subtitle.getContentsToVTT(); - PlayItem.Subtitles sub = new PlayItem.Subtitles(); - sub.setContent(contents) - .setName(name) - .setHtml(presentableName) - .setUrl("") - .setType("vtt"); - subtitlesList.add(sub); - } - - resultSuccess(subtitlesList); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/ProxyAction.java b/ani-rss-application/src/main/java/ani/rss/action/ProxyAction.java deleted file mode 100644 index 7c5b60ef..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/ProxyAction.java +++ /dev/null @@ -1,64 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.Config; -import ani.rss.entity.ProxyTest; -import ani.rss.entity.Result; -import ani.rss.util.basic.HttpReq; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.codec.Base64; -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.text.StrFormatter; -import cn.hutool.http.HttpRequest; -import cn.hutool.http.HttpStatus; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; -import org.jsoup.Jsoup; - -import java.io.IOException; - -/** - * 代理 - */ -@Slf4j -@Auth -@Path("/proxy") -public class ProxyAction implements BaseAction { - - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String url = request.getParam("url"); - Config config = getBody(Config.class); - url = Base64.decodeStr(url); - - log.info(url); - - HttpRequest httpRequest = HttpReq.get(url); - HttpReq.setProxy(httpRequest, config); - - ProxyTest proxyTest = new ProxyTest(); - Result result = Result.success(proxyTest); - - long start = LocalDateTimeUtil.toEpochMilli(LocalDateTimeUtil.now()); - try { - httpRequest - .then(res -> { - int status = res.getStatus(); - proxyTest.setStatus(status); - - String title = Jsoup.parse(res.body()) - .title(); - result.setMessage(StrFormatter.format("测试成功 {}", title)); - }); - } catch (Exception e) { - result.setMessage(e.getMessage()) - .setCode(HttpStatus.HTTP_INTERNAL_ERROR); - } - - long end = LocalDateTimeUtil.toEpochMilli(LocalDateTimeUtil.now()); - proxyTest.setTime(end - start); - result(result); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/RssAction.java b/ani-rss-application/src/main/java/ani/rss/action/RssAction.java deleted file mode 100644 index 0b0a5355..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/RssAction.java +++ /dev/null @@ -1,49 +0,0 @@ -package ani.rss.action; - -import ani.rss.commons.ExceptionUtils; -import ani.rss.entity.Ani; -import ani.rss.util.other.AniUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ReUtil; -import cn.hutool.core.util.URLUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; - - -/** - * 根据rss解析为订阅 - */ -@Slf4j -@Auth -@Path("/rss") -public class RssAction implements BaseAction { - @Override - public void doAction(HttpServerRequest req, HttpServerResponse res) throws IOException { - if (!req.getMethod().equals("POST")) { - return; - } - Ani ani = getBody(Ani.class); - String url = ani.getUrl(); - String type = ani.getType(); - String bgmUrl = ani.getBgmUrl(); - Assert.notBlank(url, "RSS地址 不能为空"); - if (!ReUtil.contains("http(s*)://", url)) { - url = "https://" + url; - } - url = URLUtil.decode(url, "utf-8"); - try { - Ani newAni = AniUtil.getAni(url, type, bgmUrl); - resultSuccess(newAni); - } catch (Exception e) { - String message = ExceptionUtils.getMessage(e); - log.error(message, e); - resultErrorMsg("RSS解析失败 {}", message); - } - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/ScrapeAction.java b/ani-rss-application/src/main/java/ani/rss/action/ScrapeAction.java deleted file mode 100644 index 83abe3a5..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/ScrapeAction.java +++ /dev/null @@ -1,36 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.Ani; -import ani.rss.service.ScrapeService; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.thread.ThreadUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; - -/** - * 刮削 - */ -@Auth -@Slf4j -@Path("/scrape") -public class ScrapeAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - Ani ani = getBody(Ani.class); - - String force = request.getParam("force"); - - ThreadUtil.execute(() -> - ScrapeService.scrape(ani, Boolean.parseBoolean(force)) - ); - - String title = ani.getTitle(); - - resultSuccessMsg("已开始刮削 {}", title); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/StopAction.java b/ani-rss-application/src/main/java/ani/rss/action/StopAction.java deleted file mode 100644 index 16c14000..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/StopAction.java +++ /dev/null @@ -1,48 +0,0 @@ -package ani.rss.action; - -import ani.rss.commons.MavenUtils; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import ani.rss.web.util.ServerUtil; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.thread.ThreadUtil; -import cn.hutool.core.util.RuntimeUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.File; -import java.io.IOException; -import java.util.List; - -/** - * 关闭或重启 - */ -@Slf4j -@Auth -@Path("/stop") -public class StopAction implements BaseAction { - - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String statusStr = request.getParam("status"); - int status = Integer.parseInt(statusStr); - String s = List.of("重启", "关闭").get(status); - log.info("正在{}", s); - resultSuccessMsg("正在{}", s); - ThreadUtil.execute(() -> { - ThreadUtil.sleep(3000); - File jar = MavenUtils.getJar(); - String extName = FileUtil.extName(jar); - ServerUtil.stop(); - if ("exe".equals(extName) && status == 0) { - log.info("正在重启 {}", jar.getName()); - RuntimeUtil.exec(jar.getName()); - System.exit(status); - return; - } - System.exit(status); - }); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/TelegramAction.java b/ani-rss-application/src/main/java/ani/rss/action/TelegramAction.java deleted file mode 100644 index 71c8e694..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/TelegramAction.java +++ /dev/null @@ -1,31 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.NotificationConfig; -import ani.rss.notification.TelegramNotification; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; - -import java.io.IOException; -import java.util.Map; - -/** - * 电报 - */ -@Auth -@Path("/telegram") -public class TelegramAction implements BaseAction { - - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - NotificationConfig notificationConfig = getBody(NotificationConfig.class); - String method = request.getParam("method"); - - if ("getUpdates".equals(method)) { - Map map = TelegramNotification.getUpdates(notificationConfig); - resultSuccess(map); - } - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/TestAction.java b/ani-rss-application/src/main/java/ani/rss/action/TestAction.java deleted file mode 100644 index d3ad6939..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/TestAction.java +++ /dev/null @@ -1,29 +0,0 @@ -package ani.rss.action; - -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import ani.rss.web.auth.fun.IpWhitelist; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; - -import java.io.IOException; - -/** - * 用于检测是否处于白名单内 - */ -@Auth(false) -@Path("/test") -public class TestAction implements BaseAction { - private final IpWhitelist ipWhitelist = new IpWhitelist(); - - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - Boolean b = ipWhitelist.apply(request); - if (b) { - resultSuccess(); - return; - } - resultError(); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/ThemoviedbAction.java b/ani-rss-application/src/main/java/ani/rss/action/ThemoviedbAction.java deleted file mode 100644 index 71a62ba9..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/ThemoviedbAction.java +++ /dev/null @@ -1,50 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.Ani; -import ani.rss.entity.Result; -import ani.rss.util.other.TmdbUtils; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import cn.hutool.http.HttpStatus; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import wushuo.tmdb.api.entity.Tmdb; - -import java.io.IOException; - -/** - * TMDB - */ -@Auth -@Path("/tmdb") -public class ThemoviedbAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String s = request.getParam("method"); - if ("getThemoviedbName".equals(s)) { - Ani ani = getBody(Ani.class); - String themoviedbName = TmdbUtils.getFinalName(ani); - Result result = new Result() - .setCode(HttpStatus.HTTP_OK) - .setMessage("获取TMDB成功") - .setData(ani.setThemoviedbName(themoviedbName)); - if (StrUtil.isBlank(themoviedbName)) { - result.setCode(HttpStatus.HTTP_INTERNAL_ERROR) - .setMessage("获取TMDB失败"); - } - result(result); - return; - } - - if ("getTmdbGroup".equals(s)) { - Ani ani = getBody(Ani.class); - Tmdb tmdb = ani.getTmdb(); - Assert.notNull(tmdb, "tmdb is null"); - Assert.notBlank(tmdb.getId(), "tmdb is null"); - resultSuccess(TmdbUtils.getTmdbGroup(tmdb)); - } - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/TorrentAction.java b/ani-rss-application/src/main/java/ani/rss/action/TorrentAction.java deleted file mode 100644 index 37551d23..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/TorrentAction.java +++ /dev/null @@ -1,67 +0,0 @@ -package ani.rss.action; - -import ani.rss.commons.FileUtils; -import ani.rss.entity.Ani; -import ani.rss.util.other.AniUtil; -import ani.rss.util.other.TorrentUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import ani.rss.web.util.ServerUtil; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.http.Method; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.File; -import java.io.IOException; -import java.util.List; -import java.util.Optional; - -/** - * 种子管理 - */ -@Slf4j -@Auth -@Path("/torrent") -public class TorrentAction implements BaseAction { - - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - if (Method.DELETE.name().equals(request.getMethod())) { - del(); - } - } - - - /** - * 删除缓存种子 - */ - public void del() { - HttpServerRequest req = ServerUtil.REQUEST.get(); - String id = req.getParam("id"); - String infoHash = req.getParam("infoHash"); - Optional first = AniUtil.ANI_LIST.stream().filter(ani -> id.equals(ani.getId())) - .findFirst(); - if (first.isEmpty()) { - resultErrorMsg("此订阅不存在"); - return; - } - - List infoHashList = StrUtil.split(infoHash, ",", true, true); - - Ani ani = first.get(); - File torrentDir = TorrentUtil.getTorrentDir(ani); - File[] files = FileUtils.listFiles(torrentDir); - for (File file : files) { - String s = FileUtil.mainName(file); - if (infoHashList.contains(s)) { - log.info("删除种子 {}", file); - FileUtil.del(file); - } - } - resultSuccessMsg("删除完成"); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/TorrentsInfosAction.java b/ani-rss-application/src/main/java/ani/rss/action/TorrentsInfosAction.java deleted file mode 100644 index 3f08a68a..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/TorrentsInfosAction.java +++ /dev/null @@ -1,27 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.TorrentsInfo; -import ani.rss.util.other.TorrentUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; -import java.util.List; - -/** - * 下载器任务列表 - */ -@Slf4j -@Auth -@Path("/torrentsInfos") -public class TorrentsInfosAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - List torrentsInfos = TorrentUtil.getTorrentsInfos(); - resultSuccess(torrentsInfos); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/TrackersUpdateAction.java b/ani-rss-application/src/main/java/ani/rss/action/TrackersUpdateAction.java deleted file mode 100644 index 9969609f..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/TrackersUpdateAction.java +++ /dev/null @@ -1,25 +0,0 @@ -package ani.rss.action; - -import ani.rss.entity.Config; -import ani.rss.other.Cron; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; - -import java.io.IOException; - -/** - * Trackers - */ -@Auth -@Path("/trackersUpdate") -public class TrackersUpdateAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - Config config = getBody(Config.class); - Cron.updateTrackers(config); - resultSuccessMsg("更新完成"); - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/UpdateAction.java b/ani-rss-application/src/main/java/ani/rss/action/UpdateAction.java deleted file mode 100644 index d8a93d35..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/UpdateAction.java +++ /dev/null @@ -1,35 +0,0 @@ -package ani.rss.action; - -import ani.rss.commons.ExceptionUtils; -import ani.rss.entity.About; -import ani.rss.util.other.UpdateUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; - - -/** - * 更新 - */ -@Slf4j -@Auth -@Path("/update") -public class UpdateAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - About about = UpdateUtil.about(); - try { - UpdateUtil.update(about); - resultSuccessMsg("更新成功, 正在重启..."); - } catch (Exception e) { - String message = ExceptionUtils.getMessage(e); - log.info("更新失败 {}, {}", about.getLatest(), message); - resultErrorMsg("更新失败 {}, {}", about.getLatest(), message); - } - } -} diff --git a/ani-rss-application/src/main/java/ani/rss/action/UploadAction.java b/ani-rss-application/src/main/java/ani/rss/action/UploadAction.java deleted file mode 100644 index 09911e13..00000000 --- a/ani-rss-application/src/main/java/ani/rss/action/UploadAction.java +++ /dev/null @@ -1,53 +0,0 @@ -package ani.rss.action; - - -import ani.rss.entity.Result; -import ani.rss.util.other.ConfigUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import cn.hutool.core.codec.Base64; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.net.multipart.MultipartFormData; -import cn.hutool.core.net.multipart.UploadFile; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; - -/** - * 上传文件 - */ -@Slf4j -@Auth -@Path("/upload") -public class UploadAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String type = request.getParam("type"); - MultipartFormData multipart = request.getMultipart(); - UploadFile file = multipart.getFile("file"); - if (file.size() > 1024 * 1024 * 50) { - resultErrorMsg("文件大小超过 50M"); - return; - } - byte[] fileContent = file.getFileContent(); - if ("getBase64".equals(type)) { - resultSuccess(Base64.encode(fileContent)); - return; - } - - String s = SecureUtil.md5(new ByteArrayInputStream(fileContent)); - String fileName = file.getFileName(); - String saveName = s + "." + FileUtil.extName(fileName); - - File configDir = ConfigUtil.getConfigDir(); - FileUtil.mkdir(configDir + "/files/" + s.charAt(0)); - FileUtil.writeBytes(fileContent, configDir + "/files/" + s.charAt(0) + "/" + saveName); - resultSuccess(new Result<>().setMessage("上传完成").setData(s.charAt(0) + "/" + saveName)); - } -} diff --git a/ani-rss-web/src/main/java/ani/rss/web/annotation/Auth.java b/ani-rss-application/src/main/java/ani/rss/annotation/Auth.java similarity index 78% rename from ani-rss-web/src/main/java/ani/rss/web/annotation/Auth.java rename to ani-rss-application/src/main/java/ani/rss/annotation/Auth.java index b51f79ec..e844caf3 100644 --- a/ani-rss-web/src/main/java/ani/rss/web/annotation/Auth.java +++ b/ani-rss-application/src/main/java/ani/rss/annotation/Auth.java @@ -1,6 +1,6 @@ -package ani.rss.web.annotation; +package ani.rss.annotation; -import ani.rss.web.auth.enums.AuthType; +import ani.rss.auth.enums.AuthType; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -11,7 +11,7 @@ import java.lang.annotation.Target; * 鉴权 */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD}) +@Target(ElementType.METHOD) public @interface Auth { boolean value() default true; diff --git a/ani-rss-application/src/main/java/ani/rss/auth/AuthAspect.java b/ani-rss-application/src/main/java/ani/rss/auth/AuthAspect.java new file mode 100644 index 00000000..0f794dbb --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/auth/AuthAspect.java @@ -0,0 +1,32 @@ +package ani.rss.auth; + +import ani.rss.annotation.Auth; +import ani.rss.entity.Global; +import ani.rss.entity.Result; +import ani.rss.exception.ResultException; +import ani.rss.util.other.AuthUtil; +import jakarta.servlet.http.HttpServletRequest; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class AuthAspect { + @Before("@annotation(auth)") + public void before(JoinPoint joinPoint, Auth auth) throws Exception { + HttpServletRequest request = Global.REQUEST.get(); + if (AuthUtil.test(request, auth)) { + // 鉴权通过 + return; + } + + throw new ResultException( + Result.error(r -> + r.setCode(403) + .setMessage("登录已失效") + ) + ); + } +} diff --git a/ani-rss-web/src/main/java/ani/rss/web/auth/enums/AuthType.java b/ani-rss-application/src/main/java/ani/rss/auth/enums/AuthType.java similarity index 50% rename from ani-rss-web/src/main/java/ani/rss/web/auth/enums/AuthType.java rename to ani-rss-application/src/main/java/ani/rss/auth/enums/AuthType.java index 80438b44..1b04ff37 100644 --- a/ani-rss-web/src/main/java/ani/rss/web/auth/enums/AuthType.java +++ b/ani-rss-application/src/main/java/ani/rss/auth/enums/AuthType.java @@ -1,10 +1,10 @@ -package ani.rss.web.auth.enums; +package ani.rss.auth.enums; -import ani.rss.web.auth.fun.ApiKey; -import ani.rss.web.auth.fun.Form; -import ani.rss.web.auth.fun.Header; -import ani.rss.web.auth.fun.IpWhitelist; -import cn.hutool.http.server.HttpServerRequest; +import ani.rss.auth.fun.ApiKey; +import ani.rss.auth.fun.Form; +import ani.rss.auth.fun.Header; +import ani.rss.auth.fun.IpWhitelist; +import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; import lombok.Getter; @@ -21,5 +21,5 @@ public enum AuthType { IP_WHITE_LIST(IpWhitelist.class); @Getter - private final Class> clazz; + private final Class> clazz; } diff --git a/ani-rss-web/src/main/java/ani/rss/web/auth/fun/ApiKey.java b/ani-rss-application/src/main/java/ani/rss/auth/fun/ApiKey.java similarity index 57% rename from ani-rss-web/src/main/java/ani/rss/web/auth/fun/ApiKey.java rename to ani-rss-application/src/main/java/ani/rss/auth/fun/ApiKey.java index 78ceec41..3efee4b6 100644 --- a/ani-rss-web/src/main/java/ani/rss/web/auth/fun/ApiKey.java +++ b/ani-rss-application/src/main/java/ani/rss/auth/fun/ApiKey.java @@ -1,24 +1,24 @@ -package ani.rss.web.auth.fun; +package ani.rss.auth.fun; import ani.rss.entity.Config; import ani.rss.util.other.ConfigUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.http.server.HttpServerRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.function.Function; /** * api key 鉴权 */ -public class ApiKey implements Function { +public class ApiKey implements Function { @Override - public Boolean apply(HttpServerRequest request) { + public Boolean apply(HttpServletRequest request) { Config config = ConfigUtil.CONFIG; String apiKey = config.getApiKey(); if (StrUtil.isBlank(apiKey)) { return false; } - String s = StrUtil.blankToDefault(request.getParam("s"), request.getHeader("s")); + String s = StrUtil.blankToDefault(request.getParameter("s"), request.getHeader("s")); return StrUtil.equals(apiKey, s); } } diff --git a/ani-rss-web/src/main/java/ani/rss/web/auth/fun/Form.java b/ani-rss-application/src/main/java/ani/rss/auth/fun/Form.java similarity index 50% rename from ani-rss-web/src/main/java/ani/rss/web/auth/fun/Form.java rename to ani-rss-application/src/main/java/ani/rss/auth/fun/Form.java index 7c0a810a..c55c0435 100644 --- a/ani-rss-web/src/main/java/ani/rss/web/auth/fun/Form.java +++ b/ani-rss-application/src/main/java/ani/rss/auth/fun/Form.java @@ -1,19 +1,19 @@ -package ani.rss.web.auth.fun; +package ani.rss.auth.fun; import ani.rss.entity.Login; -import ani.rss.web.util.AuthUtil; +import ani.rss.util.other.AuthUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.http.server.HttpServerRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.function.Function; /** * 表单鉴权 */ -public class Form implements Function { +public class Form implements Function { @Override - public Boolean apply(HttpServerRequest request) { - String s = request.getParam("s"); + public Boolean apply(HttpServletRequest request) { + String s = request.getParameter("s"); Login login = AuthUtil.getLogin(); String auth = AuthUtil.getAuth(login); return StrUtil.equals(auth, s); diff --git a/ani-rss-web/src/main/java/ani/rss/web/auth/fun/Header.java b/ani-rss-application/src/main/java/ani/rss/auth/fun/Header.java similarity index 69% rename from ani-rss-web/src/main/java/ani/rss/web/auth/fun/Header.java rename to ani-rss-application/src/main/java/ani/rss/auth/fun/Header.java index 9ee20fd1..621cf129 100644 --- a/ani-rss-web/src/main/java/ani/rss/web/auth/fun/Header.java +++ b/ani-rss-application/src/main/java/ani/rss/auth/fun/Header.java @@ -1,18 +1,18 @@ -package ani.rss.web.auth.fun; +package ani.rss.auth.fun; import ani.rss.entity.Login; -import ani.rss.web.util.AuthUtil; +import ani.rss.util.other.AuthUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.http.server.HttpServerRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.function.Function; /** * 请求头鉴权 */ -public class Header implements Function { +public class Header implements Function { @Override - public Boolean apply(HttpServerRequest request) { + public Boolean apply(HttpServletRequest request) { String s = request.getHeader("Authorization"); if (StrUtil.isBlank(s)) { return false; diff --git a/ani-rss-web/src/main/java/ani/rss/web/auth/fun/IpWhitelist.java b/ani-rss-application/src/main/java/ani/rss/auth/fun/IpWhitelist.java similarity index 94% rename from ani-rss-web/src/main/java/ani/rss/web/auth/fun/IpWhitelist.java rename to ani-rss-application/src/main/java/ani/rss/auth/fun/IpWhitelist.java index 1c03f48e..077de48b 100644 --- a/ani-rss-web/src/main/java/ani/rss/web/auth/fun/IpWhitelist.java +++ b/ani-rss-application/src/main/java/ani/rss/auth/fun/IpWhitelist.java @@ -1,15 +1,15 @@ -package ani.rss.web.auth.fun; +package ani.rss.auth.fun; import ani.rss.commons.CacheUtils; import ani.rss.entity.Config; import ani.rss.util.basic.CidrRangeChecker; +import ani.rss.util.other.AuthUtil; import ani.rss.util.other.ConfigUtil; -import ani.rss.web.util.AuthUtil; import cn.hutool.core.lang.PatternPool; import cn.hutool.core.net.Ipv4Util; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; -import cn.hutool.http.server.HttpServerRequest; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import java.util.List; @@ -18,9 +18,9 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; @Slf4j -public class IpWhitelist implements Function { +public class IpWhitelist implements Function { @Override - public Boolean apply(HttpServerRequest request) { + public Boolean apply(HttpServletRequest request) { String ip = AuthUtil.getIp(); Config config = ConfigUtil.CONFIG; String ipWhitelistStr = config.getIpWhitelistStr(); diff --git a/ani-rss-commons/src/main/java/ani/rss/commons/CacheUtils.java b/ani-rss-application/src/main/java/ani/rss/commons/CacheUtils.java similarity index 100% rename from ani-rss-commons/src/main/java/ani/rss/commons/CacheUtils.java rename to ani-rss-application/src/main/java/ani/rss/commons/CacheUtils.java diff --git a/ani-rss-commons/src/main/java/ani/rss/commons/ExceptionUtils.java b/ani-rss-application/src/main/java/ani/rss/commons/ExceptionUtils.java similarity index 100% rename from ani-rss-commons/src/main/java/ani/rss/commons/ExceptionUtils.java rename to ani-rss-application/src/main/java/ani/rss/commons/ExceptionUtils.java diff --git a/ani-rss-commons/src/main/java/ani/rss/commons/FileUtils.java b/ani-rss-application/src/main/java/ani/rss/commons/FileUtils.java similarity index 98% rename from ani-rss-commons/src/main/java/ani/rss/commons/FileUtils.java rename to ani-rss-application/src/main/java/ani/rss/commons/FileUtils.java index 745b9ef5..d6b626e7 100644 --- a/ani-rss-commons/src/main/java/ani/rss/commons/FileUtils.java +++ b/ani-rss-application/src/main/java/ani/rss/commons/FileUtils.java @@ -88,7 +88,7 @@ public class FileUtils { public static String normalize(String path) { path = path.trim(); - String s = cn.hutool.core.io.FileUtil.normalize(path); + String s = FileUtil.normalize(path); while (s.endsWith("/")) { s = s.substring(0, s.length() - 1); } diff --git a/ani-rss-commons/src/main/java/ani/rss/commons/GsonStatic.java b/ani-rss-application/src/main/java/ani/rss/commons/GsonStatic.java similarity index 96% rename from ani-rss-commons/src/main/java/ani/rss/commons/GsonStatic.java rename to ani-rss-application/src/main/java/ani/rss/commons/GsonStatic.java index 69735ee5..4184808b 100644 --- a/ani-rss-commons/src/main/java/ani/rss/commons/GsonStatic.java +++ b/ani-rss-application/src/main/java/ani/rss/commons/GsonStatic.java @@ -12,7 +12,7 @@ import java.util.TimeZone; @Slf4j public class GsonStatic { - private static final Gson GSON = new GsonBuilder() + public static final Gson GSON = new GsonBuilder() .disableHtmlEscaping() .disableJdkUnsafe() .disableInnerClassSerialization() diff --git a/ani-rss-commons/src/main/java/ani/rss/commons/MavenUtils.java b/ani-rss-application/src/main/java/ani/rss/commons/MavenUtils.java similarity index 88% rename from ani-rss-commons/src/main/java/ani/rss/commons/MavenUtils.java rename to ani-rss-application/src/main/java/ani/rss/commons/MavenUtils.java index c0e5538e..e4f225e3 100644 --- a/ani-rss-commons/src/main/java/ani/rss/commons/MavenUtils.java +++ b/ani-rss-application/src/main/java/ani/rss/commons/MavenUtils.java @@ -4,10 +4,13 @@ import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.XmlUtil; import cn.hutool.system.OsInfo; import cn.hutool.system.SystemUtil; import lombok.Cleanup; import lombok.extern.slf4j.Slf4j; +import org.w3c.dom.Document; +import org.w3c.dom.Element; import java.io.File; import java.io.IOException; @@ -75,8 +78,9 @@ public class MavenUtils { } File file = new File("pom.xml"); if (file.exists()) { - String s = FileUtil.readUtf8String(file); - version = ReUtil.get("(.*?)", s, 1); + Document document = XmlUtil.readXML(file); + Element element = XmlUtil.getElement(document.getDocumentElement(), "version"); + version = element.getTextContent(); } return version; } diff --git a/ani-rss-commons/src/main/java/ani/rss/commons/Md5Utils.java b/ani-rss-application/src/main/java/ani/rss/commons/Md5Utils.java similarity index 100% rename from ani-rss-commons/src/main/java/ani/rss/commons/Md5Utils.java rename to ani-rss-application/src/main/java/ani/rss/commons/Md5Utils.java diff --git a/ani-rss-commons/src/main/java/ani/rss/commons/NumberFormatUtils.java b/ani-rss-application/src/main/java/ani/rss/commons/NumberFormatUtils.java similarity index 100% rename from ani-rss-commons/src/main/java/ani/rss/commons/NumberFormatUtils.java rename to ani-rss-application/src/main/java/ani/rss/commons/NumberFormatUtils.java diff --git a/ani-rss-commons/src/main/java/ani/rss/commons/TimeZoneSerializer.java b/ani-rss-application/src/main/java/ani/rss/commons/TimeZoneSerializer.java similarity index 100% rename from ani-rss-commons/src/main/java/ani/rss/commons/TimeZoneSerializer.java rename to ani-rss-application/src/main/java/ani/rss/commons/TimeZoneSerializer.java diff --git a/ani-rss-commons/src/main/java/ani/rss/commons/URLUtils.java b/ani-rss-application/src/main/java/ani/rss/commons/URLUtils.java similarity index 100% rename from ani-rss-commons/src/main/java/ani/rss/commons/URLUtils.java rename to ani-rss-application/src/main/java/ani/rss/commons/URLUtils.java diff --git a/ani-rss-core/src/main/java/ani/rss/other/Cron.java b/ani-rss-application/src/main/java/ani/rss/config/CronConfig.java similarity index 67% rename from ani-rss-core/src/main/java/ani/rss/other/Cron.java rename to ani-rss-application/src/main/java/ani/rss/config/CronConfig.java index f0e1c620..499502fd 100644 --- a/ani-rss-core/src/main/java/ani/rss/other/Cron.java +++ b/ani-rss-application/src/main/java/ani/rss/config/CronConfig.java @@ -1,4 +1,4 @@ -package ani.rss.other; +package ani.rss.config; import ani.rss.download.BaseDownload; import ani.rss.entity.About; @@ -10,22 +10,68 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.cron.CronUtil; import cn.hutool.http.ContentType; import cn.hutool.http.Header; import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -/** - * 定时任务 - */ @Slf4j -public class Cron { - public static void updateTrackers(Config config) { +@Component +public class CronConfig { + @Scheduled(cron = "0 0 0 * * *") + public void backupConfig() { + ConfigUtil.backup(); + } + + @Scheduled(cron = "0 0 1 * * *") + public void autoTrackersUpdate() { + Config config = ConfigUtil.CONFIG; + Boolean autoTrackersUpdate = config.getAutoTrackersUpdate(); + if (!autoTrackersUpdate) { + // 未开启自动更新 Trackers + return; + } + log.info("定时任务 开始更新 Trackers"); + try { + updateTrackers(config); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + @Scheduled(cron = "0 0 6 * * *") + public void autoUpdate() { + Config config = ConfigUtil.CONFIG; + Boolean autoUpdate = config.getAutoUpdate(); + if (!autoUpdate) { + // 未开启 自动更新 + return; + } + log.info("定时任务 自动更新"); + try { + About about = UpdateUtil.about(); + Boolean update = about.getUpdate(); + autoUpdate = about.getAutoUpdate(); + if (!autoUpdate) { + // 禁止非跨小版本的自动更新 + return; + } + if (update) { + log.info("检测到可更新版本 v{}", about.getLatest()); + } + UpdateUtil.update(about); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + public void updateTrackers(Config config) { String trackersUpdateUrls = config.getTrackersUpdateUrls(); Assert.notBlank(trackersUpdateUrls, "Trackers更新地址 为空"); @@ -75,54 +121,4 @@ public class Cron { baseDownload.updateTrackers(trackers); } - public static void autoUpdate(Config config) { - About about = UpdateUtil.about(); - Boolean update = about.getUpdate(); - Boolean autoUpdate = about.getAutoUpdate(); - if (!autoUpdate) { - // 禁止非跨小版本的自动更新 - return; - } - if (update) { - log.info("检测到可更新版本 v{}", about.getLatest()); - } - UpdateUtil.update(about); - } - - public static void start() { - Config config = ConfigUtil.CONFIG; - - // 自动备份设置 - CronUtil.schedule("0 0 * * *", (Runnable) ConfigUtil::backup); - - CronUtil.schedule("0 1 * * *", (Runnable) () -> { - Boolean autoTrackersUpdate = config.getAutoTrackersUpdate(); - if (!autoTrackersUpdate) { - // 未开启自动更新 Trackers - return; - } - log.info("定时任务 开始更新 Trackers"); - try { - Cron.updateTrackers(config); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - }); - CronUtil.schedule("0 6 * * *", (Runnable) () -> { - Boolean autoUpdate = config.getAutoUpdate(); - if (!autoUpdate) { - // 未开启 自动更新 - return; - } - log.info("定时任务 自动更新"); - try { - Cron.autoUpdate(config); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - }); - - CronUtil.start(); - } - } diff --git a/ani-rss-application/src/main/java/ani/rss/config/CustomExceptionHandler.java b/ani-rss-application/src/main/java/ani/rss/config/CustomExceptionHandler.java new file mode 100644 index 00000000..2cc22787 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/config/CustomExceptionHandler.java @@ -0,0 +1,36 @@ +package ani.rss.config; + +import ani.rss.entity.Result; +import ani.rss.exception.ResultException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.resource.NoResourceFoundException; + +@Slf4j +@RestControllerAdvice +public class CustomExceptionHandler { + + @ExceptionHandler(IllegalArgumentException.class) + public Result handleException(IllegalArgumentException e) { + return Result.error(e.getMessage()); + } + + @ExceptionHandler(ResultException.class) + public Result handleException(ResultException e) { + return e.getResult(); + } + + @ExceptionHandler({NoResourceFoundException.class, HttpRequestMethodNotSupportedException.class}) + public Result handleException(NoResourceFoundException e) { + return Result.error(e.getMessage()); + } + + @ExceptionHandler(Exception.class) + public Result handleException(Exception e) { + log.error(e.getMessage(), e); + return Result.error(); + } + +} diff --git a/ani-rss-application/src/main/java/ani/rss/config/Runner.java b/ani-rss-application/src/main/java/ani/rss/config/Runner.java new file mode 100644 index 00000000..bd2f0e36 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/config/Runner.java @@ -0,0 +1,51 @@ +package ani.rss.config; + +import ani.rss.commons.ExceptionUtils; +import ani.rss.commons.MavenUtils; +import ani.rss.service.TaskService; +import ani.rss.util.other.AniUtil; +import ani.rss.util.other.ConfigUtil; +import cn.hutool.core.net.NetUtil; +import cn.hutool.core.util.RuntimeUtil; +import lombok.extern.slf4j.Slf4j; +import org.jspecify.annotations.NonNull; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +import java.net.InetSocketAddress; + +@Slf4j +@Component +public class Runner implements ApplicationRunner { + + @Value("${server.port}") + private String port; + + @Override + public void run(@NonNull ApplicationArguments args) { + try { + ConfigUtil.load(); + ConfigUtil.backup(); + + AniUtil.load(); + TaskService.start(); + String version = MavenUtils.getVersion(); + log.info("version {}", version); + + + for (String ip : NetUtil.localIpv4s()) { + InetSocketAddress inetSocketAddress = new InetSocketAddress(ip, Integer.parseInt(port)); + if (NetUtil.isOpen(inetSocketAddress, 100)) { + log.info("http://{}:{}", ip, port); + } + } + } catch (Exception e) { + String message = ExceptionUtils.getMessage(e); + log.error(message, e); + System.exit(1); + } + RuntimeUtil.addShutdownHook(() -> log.info("程序退出...")); + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/config/SwaggerConfig.java b/ani-rss-application/src/main/java/ani/rss/config/SwaggerConfig.java new file mode 100644 index 00000000..9824b9aa --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/config/SwaggerConfig.java @@ -0,0 +1,39 @@ +package ani.rss.config; + +import ani.rss.commons.MavenUtils; +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + @Bean + public OpenAPI springShopOpenAPI() { + Info info = new Info(); + + License license = new License() + .name("GPL-2.0") + .url("https://github.com/wushuo894/ani-rss/blob/master/LICENSE"); + + String version = MavenUtils.getVersion(); + + info.title("ANI-RSS") + .contact(new Contact()) + .description("基于RSS自动追番、订阅、下载、刮削") + .version("v" + version) + .license(license); + + ExternalDocumentation externalDocumentation = new ExternalDocumentation() + .description("外部文档") + .url("https://docs.wushuo.top/"); + + + return new OpenAPI() + .info(info) + .externalDocs(externalDocumentation); + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/config/WebFilter.java b/ani-rss-application/src/main/java/ani/rss/config/WebFilter.java new file mode 100644 index 00000000..a5773745 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/config/WebFilter.java @@ -0,0 +1,24 @@ +package ani.rss.config; + +import ani.rss.entity.Global; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class WebFilter implements Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { + Global.REQUEST.set((HttpServletRequest) request); + Global.RESPONSE.set((HttpServletResponse) response); + try { + filterChain.doFilter(request, response); + } finally { + Global.REQUEST.remove(); + Global.RESPONSE.remove(); + } + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/config/WebMvcConfig.java b/ani-rss-application/src/main/java/ani/rss/config/WebMvcConfig.java new file mode 100644 index 00000000..ed25e7e3 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/config/WebMvcConfig.java @@ -0,0 +1,36 @@ +package ani.rss.config; + +import ani.rss.commons.GsonStatic; +import com.google.gson.Gson; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverters; +import org.springframework.http.converter.json.GsonHttpMessageConverter; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.nio.charset.StandardCharsets; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + @Override + public void configurePathMatch(PathMatchConfigurer configurer) { + configurer.addPathPrefix("/api", c -> c.isAnnotationPresent(RestController.class)); + } + + @Bean + public Gson gson() { + return GsonStatic.GSON; + } + + @Override + public void configureMessageConverters(HttpMessageConverters.ServerBuilder builder) { + builder.configureMessageConvertersList(converters -> { + GsonHttpMessageConverter converter = new GsonHttpMessageConverter(); + converter.setGson(gson()); + converter.setDefaultCharset(StandardCharsets.UTF_8); + converters.add(0, converter); + }); + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/controller/AboutController.java b/ani-rss-application/src/main/java/ani/rss/controller/AboutController.java new file mode 100644 index 00000000..5e124c78 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/AboutController.java @@ -0,0 +1,83 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.auth.fun.IpWhitelist; +import ani.rss.commons.ExceptionUtils; +import ani.rss.commons.MavenUtils; +import ani.rss.entity.About; +import ani.rss.entity.Global; +import ani.rss.entity.Result; +import ani.rss.util.other.UpdateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.RuntimeUtil; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.io.File; +import java.util.List; + +@Slf4j +@RestController +public class AboutController extends BaseController { + + @Auth + @Operation(summary = "查看关于信息") + @PostMapping("/about") + public Result about() { + return Result.success(UpdateUtil.about()); + } + + @Auth + @Operation(summary = "停止服务") + @PostMapping("/stop") + public Result stop(@RequestParam("status") Integer status) { + String s = List.of("重启", "关闭").get(status); + log.info("正在{}", s); + ThreadUtil.execute(() -> { + ThreadUtil.sleep(3000); + File jar = MavenUtils.getJar(); + String extName = FileUtil.extName(jar); + if ("exe".equals(extName) && status == 0) { + log.info("正在重启 {}", jar.getName()); + RuntimeUtil.exec(jar.getName()); + System.exit(status); + return; + } + System.exit(status); + }); + return Result.success("正在{}", s); + } + + @Auth + @Operation(summary = "更新") + @PostMapping("/update") + public Result update() { + About about = UpdateUtil.about(); + try { + UpdateUtil.update(about); + return Result.success("更新成功, 正在重启..."); + } catch (Exception e) { + String message = ExceptionUtils.getMessage(e); + log.info("更新失败 {}, {}", about.getLatest(), message); + return Result.success("更新失败 {}, {}", about.getLatest(), message); + } + } + + private final IpWhitelist ipWhitelist = new IpWhitelist(); + + @Operation(summary = "IP白名单测试") + @PostMapping("/testIpWhitelist") + public Result testIpWhitelist() { + HttpServletRequest request = Global.REQUEST.get(); + Boolean b = ipWhitelist.apply(request); + if (b) { + return Result.success(); + } + return Result.error(); + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/controller/AfdianController.java b/ani-rss-application/src/main/java/ani/rss/controller/AfdianController.java new file mode 100644 index 00000000..14ae7cf3 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/AfdianController.java @@ -0,0 +1,80 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.entity.Config; +import ani.rss.entity.Result; +import ani.rss.entity.TryOut; +import ani.rss.util.basic.HttpReq; +import ani.rss.util.other.AfdianUtil; +import ani.rss.util.other.ConfigUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.http.HttpResponse; +import io.swagger.v3.oas.annotations.Hidden; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; + +@Hidden +@RestController +public class AfdianController extends BaseController { + + @Auth + @PostMapping("/verifyNo") + public Result verifyNo(@RequestBody Config config) { + String outTradeNo = config.getOutTradeNo(); + Result result = AfdianUtil.verifyNo(outTradeNo); + + int code = result.getCode(); + if (code == 200) { + Long time = DateUtil.offsetYear(new Date(), 999).getTime(); + ConfigUtil.CONFIG.setOutTradeNo(outTradeNo) + .setExpirationTime(time) + .setTryOut(false); + ConfigUtil.sync(); + } + + return result; + } + + @Auth + @PostMapping("/tryOut") + public Result tryOut(@RequestBody Config config) { + if (AfdianUtil.verifyExpirationTime()) { + return Result.error("还在试用中!"); + } + + String githubToken = config.getGithubToken(); + Assert.notBlank(githubToken, "GithubToken 不能为空"); + + Boolean ok = HttpReq.get("https://api.github.com/user/starred/wushuo894/ani-rss") + .header("Authorization", "Bearer " + githubToken) + .thenFunction(HttpResponse::isOk); + + Assert.isTrue(ok, "未点击star"); + + TryOut tryOut = AfdianUtil.getTryOut(); + + Boolean enable = tryOut.getEnable(); + Boolean renewal = tryOut.getRenewal(); + Integer day = tryOut.getDay(); + String message = tryOut.getMessage(); + + Assert.isTrue(enable, message); + + if (config.getTryOut()) { + // 已经有过试用 + Assert.isTrue(renewal, message); + } + + long time = DateUtil.offsetDay(new Date(), day).getTime(); + ConfigUtil.CONFIG + .setGithubToken(githubToken) + .setExpirationTime(time) + .setTryOut(true); + ConfigUtil.sync(); + return Result.success(time).setMessage(message); + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/action/AniAction.java b/ani-rss-application/src/main/java/ani/rss/controller/AniController.java similarity index 61% rename from ani-rss-application/src/main/java/ani/rss/action/AniAction.java rename to ani-rss-application/src/main/java/ani/rss/controller/AniController.java index bd5fce34..6a796eca 100644 --- a/ani-rss-application/src/main/java/ani/rss/action/AniAction.java +++ b/ani-rss-application/src/main/java/ani/rss/controller/AniController.java @@ -1,7 +1,9 @@ -package ani.rss.action; +package ani.rss.controller; +import ani.rss.annotation.Auth; import ani.rss.commons.ExceptionUtils; import ani.rss.commons.FileUtils; +import ani.rss.dto.ImportAniDataDTO; import ani.rss.entity.*; import ani.rss.enums.SortTypeEnum; import ani.rss.service.AniService; @@ -9,10 +11,6 @@ import ani.rss.service.ClearService; import ani.rss.service.DownloadService; import ani.rss.task.RssTask; import ani.rss.util.other.*; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import ani.rss.web.util.ServerUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.comparator.PinyinComparator; @@ -21,80 +19,35 @@ import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.UUID; import cn.hutool.core.text.StrFormatter; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.URLUtil; import cn.hutool.extra.pinyin.PinyinUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import java.io.File; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.ToLongFunction; -/** - * 订阅 增删改查 - */ -@Auth @Slf4j -@Path("/ani") -public class AniAction implements BaseAction { - +@RestController +public class AniController extends BaseController { public static final AtomicBoolean DOWNLOAD = new AtomicBoolean(false); - /** - * 手动刷新订阅 - */ - private void refreshAni() { - Ani ani = getBody(Ani.class); - - if (Objects.isNull(ani)) { - // 未传Body, 刷新所有订阅 - RssTask.sync(); - ThreadUtil.execute(() -> RssTask.download(new AtomicBoolean(true))); - resultSuccessMsg("已开始刷新RSS"); - return; - } - - Optional first = AniUtil.ANI_LIST.stream() - .filter(it -> it.getId().equals(ani.getId())) - .findFirst(); - if (first.isEmpty()) { - resultErrorMsg("修改失败"); - return; - } - synchronized (DOWNLOAD) { - if (DOWNLOAD.get()) { - resultErrorMsg("存在未完成任务,请等待..."); - return; - } - DOWNLOAD.set(true); - } - Ani downloadAni = first.get(); - ThreadUtil.execute(() -> { - try { - if (TorrentUtil.login()) { - DownloadService.downloadAni(downloadAni); - } - } catch (Exception e) { - String message = ExceptionUtils.getMessage(e); - log.error(message, e); - } - DOWNLOAD.set(false); - }); - resultSuccessMsg("已开始刷新RSS {}", downloadAni.getTitle()); - } - - /** - * 添加订阅 - */ - private void post() { - Ani ani = getBody(Ani.class); + @Auth + @Operation(summary = "添加订阅") + @PostMapping("/addAni") + public Result addAni(@RequestBody Ani ani) { ani.setTitle(ani.getTitle().trim()) .setUrl(ani.getUrl().trim()); AniUtil.verify(ani); @@ -104,8 +57,7 @@ public class AniAction implements BaseAction { .findFirst(); if (first.isPresent()) { - resultErrorMsg("此订阅已存在"); - return; + return Result.error("此订阅已存在"); } first = AniUtil.ANI_LIST.stream() @@ -122,8 +74,7 @@ public class AniAction implements BaseAction { AniUtil.ANI_LIST.remove(first.get()); log.info("自动替换 {} 第{}季", title, season); } else { - resultErrorMsg("订阅标题重复"); - return; + return Result.error("订阅标题重复"); } } @@ -148,15 +99,15 @@ public class AniAction implements BaseAction { } }); } - resultSuccessMsg("添加订阅成功"); log.info("添加订阅 {} {} {}", title, ani.getUrl(), ani.getId()); + + return Result.success("添加订阅成功"); } - /** - * 修改订阅 - */ - private void put() { - Ani ani = getBody(Ani.class); + @Auth + @Operation(summary = "修改订阅") + @PostMapping("/setAni") + public Result setAni(@RequestBody Ani ani) { ani.setTitle(ani.getTitle().trim()) .setUrl(ani.getUrl().trim()); AniUtil.verify(ani); @@ -165,19 +116,17 @@ public class AniAction implements BaseAction { .filter(it -> it.getTitle().equals(ani.getTitle()) && it.getSeason().equals(ani.getSeason())) .findFirst(); if (first.isPresent()) { - resultErrorMsg("订阅标题重复"); - return; + return Result.error("订阅标题重复"); } first = AniUtil.ANI_LIST.stream() .filter(it -> it.getId().equals(ani.getId())) .findFirst(); if (first.isEmpty()) { - resultErrorMsg("修改失败"); - return; + return Result.error("修改失败"); } - HttpServerRequest request = ServerUtil.REQUEST.get(); - String move = request.getParam("move"); + HttpServletRequest request = Global.REQUEST.get(); + String move = request.getParameter("move"); if (Boolean.parseBoolean(move)) { Ani get = ObjectUtil.clone(first.get()); ThreadUtil.execute(() -> { @@ -237,14 +186,74 @@ public class AniAction implements BaseAction { FileUtil.move(torrentDir, newTorrentDir.getParentFile(), true); } AniUtil.sync(); - resultSuccessMsg("修改成功"); + log.info("修改订阅 {} {} {}", ani.getTitle(), ani.getUrl(), ani.getId()); + return Result.success("修改成功"); } - /** - * 返回订阅列表 - */ - private void get() { + @Auth + @Operation(summary = "删除订阅") + @PostMapping("/deleteAni") + public Result deleteAni(@RequestBody List ids, @RequestParam("deleteFiles") Boolean deleteFiles) { + Assert.notEmpty(ids, "未选择订阅"); + List anis = AniUtil.ANI_LIST.stream() + .filter(it -> ids.contains(it.getId())) + .toList(); + if (anis.isEmpty()) { + return Result.error("删除失败"); + } + for (Ani ani : anis) { + AniUtil.ANI_LIST.remove(ani); + } + + AniUtil.sync(); + ThreadUtil.execute(() -> { + for (Ani ani : anis) { + File torrentDir = TorrentUtil.getTorrentDir(ani); + FileUtil.del(torrentDir); + ClearService.clearParentFile(torrentDir); + log.info("删除订阅 {} {} {}", ani.getTitle(), ani.getUrl(), ani.getId()); + } + if (!deleteFiles) { + // 不删除本地文件 + return; + } + + List files = anis + .stream() + .map(DownloadService::getDownloadPath) + .map(File::new) + .toList(); + + Boolean login = TorrentUtil.login(); + List torrentsInfos = new ArrayList<>(); + if (login) { + torrentsInfos = TorrentUtil.getTorrentsInfos(); + } + for (File file : files) { + String path = FileUtils.getAbsolutePath(file); + for (TorrentsInfo torrentsInfo : torrentsInfos) { + String downloadDir = torrentsInfo.getDownloadDir(); + if (downloadDir.equals(path)) { + TorrentUtil.delete(torrentsInfo, true, true); + } + } + if (!file.exists()) { + continue; + } + ThreadUtil.sleep(3000); + log.info("删除 {}", file); + FileUtil.del(file); + ClearService.clearParentFile(file); + } + }); + return Result.success("删除订阅成功"); + } + + @Auth + @Operation(summary = "订阅列表") + @PostMapping("/listAni") + public Result> list() { Config config = ConfigUtil.CONFIG; SortTypeEnum sortType = config.getSortType(); @@ -297,101 +306,13 @@ public class AniAction implements BaseAction { }).reversed()); } - resultSuccess(list); + return Result.success(list); } - /** - * 删除订阅 - */ - public void delete() { - JsonArray jsonArray = getBody(JsonArray.class); - List ids = jsonArray.asList() - .stream().map(JsonElement::getAsString) - .toList(); - Assert.notEmpty(ids, "未选择订阅"); - List anis = AniUtil.ANI_LIST.stream() - .filter(it -> ids.contains(it.getId())) - .toList(); - if (anis.isEmpty()) { - resultErrorMsg("删除失败"); - return; - } - for (Ani ani : anis) { - AniUtil.ANI_LIST.remove(ani); - } - - AniUtil.sync(); - resultSuccessMsg("删除订阅成功"); - HttpServerRequest request = ServerUtil.REQUEST.get(); - String deleteFiles = request.getParam("deleteFiles"); - ThreadUtil.execute(() -> { - for (Ani ani : anis) { - File torrentDir = TorrentUtil.getTorrentDir(ani); - FileUtil.del(torrentDir); - ClearService.clearParentFile(torrentDir); - log.info("删除订阅 {} {} {}", ani.getTitle(), ani.getUrl(), ani.getId()); - } - if (!Boolean.parseBoolean(deleteFiles)) { - // 不删除本地文件 - return; - } - - List files = anis - .stream() - .map(DownloadService::getDownloadPath) - .map(File::new) - .toList(); - - Boolean login = TorrentUtil.login(); - List torrentsInfos = new ArrayList<>(); - if (login) { - torrentsInfos = TorrentUtil.getTorrentsInfos(); - } - for (File file : files) { - String path = FileUtils.getAbsolutePath(file); - for (TorrentsInfo torrentsInfo : torrentsInfos) { - String downloadDir = torrentsInfo.getDownloadDir(); - if (downloadDir.equals(path)) { - TorrentUtil.delete(torrentsInfo, true, true); - } - } - if (!file.exists()) { - continue; - } - ThreadUtil.sleep(3000); - log.info("删除 {}", file); - FileUtil.del(file); - ClearService.clearParentFile(file); - } - }); - } - - private void batchEnable(boolean enable) { - List ids = getBody(JsonArray.class) - .asList() - .stream() - .map(JsonElement::getAsString) - .toList(); - Assert.notEmpty(ids, "未选择订阅"); - for (Ani ani : AniUtil.ANI_LIST) { - String id = ani.getId(); - if (!ids.contains(id)) { - continue; - } - ani.setEnable(enable); - } - AniUtil.sync(); - resultSuccessMsg("修改完成"); - } - - private void updateTotalEpisodeNumber() { - HttpServerRequest request = ServerUtil.REQUEST.get(); - boolean force = Boolean.parseBoolean(request.getParam("force")); - List ids = getBody(JsonArray.class) - .asList() - .stream() - .map(JsonElement::getAsString) - .toList(); + @Auth + @Operation(summary = "更新总集数") + @PostMapping("/updateTotalEpisodeNumber") + public Result updateTotalEpisodeNumber(@RequestParam("force") Boolean force, @RequestBody List ids) { Assert.notEmpty(ids, "未选择订阅"); ThreadUtil.execute(() -> { log.info("开始手动更新总集数"); @@ -416,53 +337,196 @@ public class AniAction implements BaseAction { AniUtil.sync(); log.info("手动更新总集数完成 共更新{}条订阅", count); }); - resultSuccessMsg("已开始更新总集数"); + return Result.success("已开始更新总集数"); } - @Override - public void doAction(HttpServerRequest req, HttpServerResponse res) { - String method = req.getMethod(); - String type = StrUtil.blankToDefault(req.getParam("type"), ""); - switch (type) { - case "refreshAni" -> { - // 刷新订阅 - refreshAni(); - return; + @Auth + @Operation(summary = "批量 启用/禁用 订阅") + @PostMapping("/batchEnable") + public Result batchEnable(@RequestParam("value") Boolean value, @RequestBody List ids) { + Assert.notEmpty(ids, "未选择订阅"); + for (Ani ani : AniUtil.ANI_LIST) { + String id = ani.getId(); + if (!ids.contains(id)) { + continue; } - case "batchEnable" -> { - // 批量 启用/禁用 - boolean enable = Boolean.parseBoolean(req.getParam("value")); - batchEnable(enable); - return; + ani.setEnable(value); + } + AniUtil.sync(); + return Result.success("修改完成"); + } + + @Auth + @Operation(summary = "手动刷新订阅") + @PostMapping("/refreshAll") + public Result refreshAni() { + // 未传Body, 刷新所有订阅 + RssTask.sync(); + ThreadUtil.execute(() -> RssTask.download(new AtomicBoolean(true))); + return Result.success("已开始刷新RSS"); + } + + @Auth + @Operation(summary = "手动刷新订阅") + @PostMapping("/refreshAni") + public Result refreshAni(@RequestBody Ani ani) { + Optional first = AniUtil.ANI_LIST.stream() + .filter(it -> it.getId().equals(ani.getId())) + .findFirst(); + if (first.isEmpty()) { + return Result.error("修改失败"); + } + synchronized (DOWNLOAD) { + if (DOWNLOAD.get()) { + return Result.error("存在未完成任务,请等待..."); } - case "updateTotalEpisodeNumber" -> { - // 更新总集数 - updateTotalEpisodeNumber(); - return; + DOWNLOAD.set(true); + } + Ani downloadAni = first.get(); + ThreadUtil.execute(() -> { + try { + if (TorrentUtil.login()) { + DownloadService.downloadAni(downloadAni); + } + } catch (Exception e) { + String message = ExceptionUtils.getMessage(e); + log.error(message, e); + } + DOWNLOAD.set(false); + }); + return Result.success("已开始刷新RSS {}", downloadAni.getTitle()); + } + + @Auth + @Operation(summary = "将RSS转换为订阅") + @PostMapping("/rssToAni") + public Result rssToAni(@RequestBody Ani ani) { + String url = ani.getUrl(); + String type = ani.getType(); + String bgmUrl = ani.getBgmUrl(); + Assert.notBlank(url, "RSS地址 不能为空"); + if (!ReUtil.contains("http(s*)://", url)) { + url = "https://" + url; + } + url = URLUtil.decode(url, "utf-8"); + try { + Ani newAni = AniUtil.getAni(url, type, bgmUrl); + return Result.success(newAni); + } catch (Exception e) { + String message = ExceptionUtils.getMessage(e); + log.error(message, e); + return Result.error("RSS解析失败 {}", message); + } + } + + @Auth + @Operation(summary = "预览订阅") + @PostMapping("/previewAni") + public Result> previewAni(@RequestBody Ani ani) { + List items = ItemsUtil.getItems(ani); + + String downloadPath = DownloadService.getDownloadPath(ani); + + for (Item item : items) { + item.setLocal(false); + File torrent = TorrentUtil.getTorrent(ani, item); + if (torrent.exists()) { + item.setLocal(true); + continue; + } + if (DownloadService.itemDownloaded(ani, item, false)) { + item.setLocal(true); } } - switch (method) { - case "POST": { - // 添加订阅 - post(); - return; - } - case "PUT": { - // 修改订阅 - put(); - return; - } - case "GET": { - // 获取订阅列表 - get(); - return; - } - case "DELETE": { - // 删除订阅 - delete(); - break; - } - } + List omitList = ItemsUtil.omitList(ani, items); + + Map map = Map.of( + "downloadPath", downloadPath, + "items", items, + "omitList", omitList + ); + return Result.success(map); } + + @Auth + @Operation(summary = "获取订阅的下载位置") + @PostMapping("/downloadPath") + public Result> downloadPath(@RequestBody Ani ani) { + String downloadPath = DownloadService.getDownloadPath(ani); + + boolean change = false; + Optional first = AniUtil.ANI_LIST.stream() + .filter(it -> it.getId().equals(ani.getId())) + .findFirst(); + if (first.isPresent()) { + Ani oldAni = ObjectUtil.clone(first.get()); + // 只在名称改变时移动 + oldAni.setSeason(ani.getSeason()); + String oldDownloadPath = DownloadService.getDownloadPath(oldAni); + change = !downloadPath.equals(oldDownloadPath); + } + + Map map = Map.of( + "change", change, + "downloadPath", downloadPath + ); + return Result.success(map); + } + + @Auth + @Operation(summary = "导入订阅") + @PostMapping("/importAni") + public Result importAni(@RequestBody ImportAniDataDTO dto) { + List aniList = dto.getAniList(); + if (aniList.isEmpty()) { + return Result.error("导入列表为空"); + } + + ImportAniDataDTO.Conflict conflict = dto.getConflict(); + + for (Ani ani : aniList) { + AniUtil.verify(ani); + + String title = ani.getTitle(); + int season = ani.getSeason(); + Optional first = AniUtil.ANI_LIST.stream() + .filter(it -> it.getTitle().equals(title) && it.getSeason() == season) + .findFirst(); + + if (first.isEmpty()) { + String image = ani.getImage(); + String cover = AniUtil.saveJpg(image); + ani.setCover(cover) + .setId(UUID.fastUUID().toString()); + AniUtil.ANI_LIST.add(ani); + continue; + } + + if (conflict == ImportAniDataDTO.Conflict.SKIP) { + log.info("存在冲突,已跳过 {} 第{}季", title, season); + continue; + } + + log.info("存在冲突,已替换 {} 第{}季", title, season); + String image = ani.getImage(); + String cover = AniUtil.saveJpg(image); + ani.setCover(cover); + + String[] ignoreProperties = new String[]{"id", "currentEpisodeNumber", "lastDownloadTime"}; + BeanUtil.copyProperties(ani, first.get(), ignoreProperties); + } + + AniUtil.sync(); + return Result.success("导入成功"); + } + + @Auth + @Operation(summary = "刷新封面") + @PostMapping("/refreshCover") + public Result refreshCover(@RequestBody Ani ani) { + String s = AniUtil.saveJpg(ani.getImage(), true); + return Result.success(r -> r.setData(s)); + } + } diff --git a/ani-rss-application/src/main/java/ani/rss/controller/BaseController.java b/ani-rss-application/src/main/java/ani/rss/controller/BaseController.java new file mode 100644 index 00000000..575ce993 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/BaseController.java @@ -0,0 +1,64 @@ +package ani.rss.controller; + +import ani.rss.entity.Global; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.ContentType; +import cn.hutool.http.HttpStatus; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Cleanup; + +import java.io.OutputStream; + +public class BaseController { + /** + * 根据文件扩展名获得ContentType + * + * @param filename 文件名 + * @return ContentType + */ + public static String getContentType(String filename) { + if (StrUtil.isBlank(filename)) { + return ContentType.OCTET_STREAM.getValue(); + } + + String extName = FileUtil.extName(filename); + + if (StrUtil.isBlank(extName)) { + return ContentType.OCTET_STREAM.getValue(); + } + + if (extName.equalsIgnoreCase("mkv")) { + return "video/x-matroska"; + } + + String mimeType = FileUtil.getMimeType(filename); + if (StrUtil.isNotBlank(mimeType)) { + return mimeType; + } + + return ContentType.OCTET_STREAM.getValue(); + } + + public static void writeNotFound() { + writeHtml(HttpStatus.HTTP_NOT_FOUND, "404 Not Found !"); + } + + public static void writeHtml(Integer status, String text) { + HttpServletResponse response = Global.RESPONSE.get(); + String html = ResourceUtil.readUtf8Str("template.html"); + html = html.replace("${text}", text); + try { + response.setStatus(status); + response.setContentType("text/html;charset=UTF-8"); + + @Cleanup + OutputStream outputStream = response.getOutputStream(); + IoUtil.writeUtf8(outputStream, true, html); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/controller/BgmController.java b/ani-rss-application/src/main/java/ani/rss/controller/BgmController.java new file mode 100644 index 00000000..b1d10678 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/BgmController.java @@ -0,0 +1,107 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.commons.GsonStatic; +import ani.rss.entity.*; +import ani.rss.util.basic.HttpReq; +import ani.rss.util.other.AniUtil; +import ani.rss.util.other.BgmUtil; +import ani.rss.util.other.ConfigUtil; +import cn.hutool.core.lang.Opt; +import com.google.gson.JsonObject; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import wushuo.tmdb.api.entity.Tmdb; + +import java.util.List; +import java.util.Map; + +@RestController +public class BgmController extends BaseController { + + @Auth + @Operation(summary = "搜索BGM条目") + @PostMapping("/searchBgm") + public Result> searchBgm(@RequestParam("name") String name) { + List search = BgmUtil.search(name); + return Result.success(search); + } + + @Auth + @Operation(summary = "将指定id的BGM番剧转换为订阅") + @PostMapping("/getAniBySubjectId") + public Result getAniBySubjectId(@RequestParam("id") String id) { + BgmInfo bgmInfo = BgmUtil.getBgmInfo(id, true); + Ani ani = BgmUtil.toAni(bgmInfo, AniUtil.createAni()); + ani + .setCustomDownloadPath(true); + return Result.success(ani); + } + + @Auth + @Operation(summary = "获取BGM标题") + @PostMapping("/getBgmTitle") + public Result getBgmTitle(@RequestBody Ani ani) { + Tmdb tmdb = ani.getTmdb(); + BgmInfo bgmInfo = BgmUtil.getBgmInfo(ani); + String finalName = BgmUtil.getFinalName(bgmInfo, tmdb); + return Result.success(r -> r.setData(finalName)); + } + + @Auth + @Operation(summary = "评分") + @PostMapping("/rate") + public Result rate(@RequestBody Ani ani) { + String subjectId = BgmUtil.getSubjectId(ani); + Integer score = Opt.ofNullable(ani.getScore()) + .map(Double::intValue) + .orElse(null); + + Integer rate = BgmUtil.rate(subjectId, score); + return Result.success(rate).setMessage("保存评分成功"); + } + + @Auth + @Operation(summary = "获取当前BGM账号信息") + @PostMapping("/meBgm") + public Result meBgm() { + int expiresDays = BgmUtil.getExpiresDays(); + BgmMe me = BgmUtil.me(); + me.setExpiresDays(expiresDays); + return Result.success(me); + } + + @Auth + @Operation(summary = "BGM授权回调") + @PostMapping("/bgm/oauth/callback") + public Result callback(@RequestParam("code") String code) { + Config config = ConfigUtil.CONFIG; + String bgmAppID = config.getBgmAppID(); + String bgmAppSecret = config.getBgmAppSecret(); + String bgmRedirectUri = config.getBgmRedirectUri(); + + Map map = Map.of( + "grant_type", "authorization_code", + "client_id", bgmAppID, + "client_secret", bgmAppSecret, + "code", code, + "redirect_uri", bgmRedirectUri + ); + + HttpReq.post("https://bgm.tv/oauth/access_token") + .body(GsonStatic.toJson(map)) + .then(res -> { + HttpReq.assertStatus(res); + JsonObject jsonObject = GsonStatic.fromJson(res.body(), JsonObject.class); + String accessToken = jsonObject.get("access_token").getAsString(); + String refreshToken = jsonObject.get("refresh_token").getAsString(); + config.setBgmToken(accessToken) + .setBgmRefreshToken(refreshToken); + }); + ConfigUtil.sync(); + return Result.success("授权成功, 现在你可以关闭此窗口"); + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/action/CollectionAction.java b/ani-rss-application/src/main/java/ani/rss/controller/CollectionController.java similarity index 83% rename from ani-rss-application/src/main/java/ani/rss/action/CollectionAction.java rename to ani-rss-application/src/main/java/ani/rss/controller/CollectionController.java index 0440d44f..4f6f2603 100644 --- a/ani-rss-application/src/main/java/ani/rss/action/CollectionAction.java +++ b/ani-rss-application/src/main/java/ani/rss/controller/CollectionController.java @@ -1,5 +1,6 @@ -package ani.rss.action; +package ani.rss.controller; +import ani.rss.annotation.Auth; import ani.rss.commons.FileUtils; import ani.rss.download.qBittorrent; import ani.rss.entity.*; @@ -8,9 +9,6 @@ import ani.rss.util.basic.HttpReq; import ani.rss.util.other.ConfigUtil; import ani.rss.util.other.RenameUtil; import ani.rss.util.other.TorrentUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; import cn.hutool.core.codec.Base64; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.FileUtil; @@ -19,24 +17,162 @@ import cn.hutool.core.text.StrFormatter; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.*; import cn.hutool.http.HttpResponse; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; +import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; import org.eclipse.bittorrent.TorrentFile; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; import java.io.File; +import java.io.IOException; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; -/** - * 合集 - */ @Slf4j -@Auth -@Path("/collection") -public class CollectionAction implements BaseAction { +@RestController +public class CollectionController extends BaseController { + + @Auth + @Operation(summary = "开始下载合集") + @PostMapping("/startCollection") + public Result startCollection(@RequestBody CollectionInfo collectionInfo) throws IOException { + String torrent = collectionInfo.getTorrent(); + File tempFile = FileUtil.createTempFile(); + Base64.decodeToFile(torrent, tempFile); + TorrentFile torrentFile; + try { + torrentFile = new TorrentFile(tempFile); + } catch (Exception e) { + throw new RuntimeException(e); + } + Ani ani = collectionInfo.getAni(); + String title = ani.getTitle(); + String subgroup = ani.getSubgroup(); + String downloadPath = ani.getDownloadPath(); + + String name = StrFormatter.format("[{}] {} 第{}季", subgroup, title, ani.getSeason()); + download(name, tempFile, downloadPath, List.of("ANI-RSS合集下载", subgroup)); + + TorrentsInfo torrentsInfo = new TorrentsInfo() + .setHash(torrentFile.getHexHash()); + + Config config = ConfigUtil.CONFIG; + + List files = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + ThreadUtil.sleep(500); + try { + files.addAll(qBittorrent.files(torrentsInfo, false, config)); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + if (!files.isEmpty()) { + // 添加下载完成 + break; + } + } + + Map reNameMap = preview(collectionInfo) + .stream() + .map(item -> { + Optional fileEntity = files.stream() + .filter(f -> new File(f.getName()).getName().equals(new File(item.getTitle()).getName())) + .filter(f -> f.getSize().longValue() == item.getLength()) + .findFirst(); + if (fileEntity.isEmpty()) { + return null; + } + String oldPath = fileEntity.get().getName(); + return item.setTitle(oldPath); + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap( + Item::getTitle, + Item::getReName + )); + + String host = config.getDownloadToolHost(); + + for (int i = 0; i < 30; i++) { + for (qBittorrent.FileEntity file : files) { + String oldPath = file.getName(); + String newPath = reNameMap.get(oldPath); + + if (!reNameMap.containsKey(oldPath)) { + if (!reNameMap.containsValue(oldPath) && file.getPriority() > 0) { + HttpReq.post(host + "/api/v2/torrents/filePrio") + .form("hash", torrentFile.getHexHash()) + .form("id", file.getIndex()) + .form("priority", 0) + .thenFunction(HttpResponse::isOk); + } + continue; + } + log.info("重命名 {} ==> {}", oldPath, newPath); + HttpReq.post(host + "/api/v2/torrents/renameFile") + .form("hash", torrentFile.getHexHash()) + .form("oldPath", oldPath) + .form("newPath", newPath) + .thenFunction(HttpResponse::isOk); + } + files.clear(); + files.addAll(qBittorrent.files(torrentsInfo, false, config)); + + if (CollUtil.containsAll(files.stream() + .map(qBittorrent.FileEntity::getName) + .toList(), reNameMap.values())) { + // 所有命名已完成 + break; + } + // 命名有遗漏 继续 + ThreadUtil.sleep(1000); + } + + qBittorrent.start(torrentsInfo, config); + return Result.success("已经开始下载合集"); + } + + @Auth + @Operation(summary = "合集预览") + @PostMapping("/previewCollection") + public Result> previewCollection(@RequestBody CollectionInfo collectionInfo) { + List preview = preview(collectionInfo); + preview = CollUtil.sort(new ArrayList<>(preview), Comparator.comparingDouble(it -> { + Double episode = it.getEpisode(); + return ObjectUtil.defaultIfNull(episode, 0.0); + })); + return Result.success(preview); + } + + @Auth + @Operation(summary = "获取合集字幕组") + @PostMapping("/getCollectionSubgroup") + public Result getCollectionSubgroup(@RequestBody CollectionInfo collectionInfo) { + List preview = preview(collectionInfo); + preview = CollUtil.sort(new ArrayList<>(preview), Comparator.comparingDouble(it -> { + Double episode = it.getEpisode(); + return ObjectUtil.defaultIfNull(episode, 0.0); + })); + String subgroup = "未知字幕组"; + String reg = "^\\[(.+?)]"; + for (Item item : preview) { + String name = new File(item.getTitle()).getName(); + if (!ReUtil.contains(reg, name)) { + continue; + } + subgroup = ReUtil.get(reg, name, 1); + break; + } + + Result result = Result.success(); + result.setData(subgroup); + return result; + } + + public static synchronized List preview(CollectionInfo collectionInfo) { String torrent = collectionInfo.getTorrent(); File tempFile = FileUtil.createTempFile(); @@ -185,139 +321,4 @@ public class CollectionAction implements BaseAction { .then(HttpReq::assertStatus); } - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) { - String type = request.getParam("type"); - - CollectionInfo collectionInfo = getBody(CollectionInfo.class); - String torrent = collectionInfo.getTorrent(); - - // 预览 - if (type.equals("preview") || type.equals("subgroup")) { - List preview = preview(collectionInfo); - preview = CollUtil.sort(new ArrayList<>(preview), Comparator.comparingDouble(it -> { - Double episode = it.getEpisode(); - return ObjectUtil.defaultIfNull(episode, 0.0); - })); - - if (type.equals("subgroup")) { - String subgroup = "未知字幕组"; - String reg = "^\\[(.+?)]"; - for (Item item : preview) { - String name = new File(item.getTitle()).getName(); - if (!ReUtil.contains(reg, name)) { - continue; - } - subgroup = ReUtil.get(reg, name, 1); - break; - } - resultSuccess(subgroup); - return; - } - resultSuccess(preview); - } - - // 开始下载 - if (!type.equals("start")) { - return; - } - - File tempFile = FileUtil.createTempFile(); - Base64.decodeToFile(torrent, tempFile); - TorrentFile torrentFile; - try { - torrentFile = new TorrentFile(tempFile); - } catch (Exception e) { - throw new RuntimeException(e); - } - Ani ani = collectionInfo.getAni(); - String title = ani.getTitle(); - String subgroup = ani.getSubgroup(); - String downloadPath = ani.getDownloadPath(); - - String name = StrFormatter.format("[{}] {} 第{}季", subgroup, title, ani.getSeason()); - download(name, tempFile, downloadPath, List.of("ANI-RSS合集下载", subgroup)); - - TorrentsInfo torrentsInfo = new TorrentsInfo() - .setHash(torrentFile.getHexHash()); - - Config config = ConfigUtil.CONFIG; - - List files = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - ThreadUtil.sleep(500); - try { - files.addAll(qBittorrent.files(torrentsInfo, false, config)); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - if (!files.isEmpty()) { - // 添加下载完成 - break; - } - } - - Map reNameMap = preview(collectionInfo) - .stream() - .map(item -> { - Optional fileEntity = files.stream() - .filter(f -> new File(f.getName()).getName().equals(new File(item.getTitle()).getName())) - .filter(f -> f.getSize().longValue() == item.getLength()) - .findFirst(); - if (fileEntity.isEmpty()) { - return null; - } - String oldPath = fileEntity.get().getName(); - return item.setTitle(oldPath); - }) - .filter(Objects::nonNull) - .collect(Collectors.toMap( - Item::getTitle, - Item::getReName - )); - - String host = config.getDownloadToolHost(); - - for (int i = 0; i < 30; i++) { - for (qBittorrent.FileEntity file : files) { - String oldPath = file.getName(); - String newPath = reNameMap.get(oldPath); - - if (!reNameMap.containsKey(oldPath)) { - if (!reNameMap.containsValue(oldPath) && file.getPriority() > 0) { - HttpReq.post(host + "/api/v2/torrents/filePrio") - .form("hash", torrentFile.getHexHash()) - .form("id", file.getIndex()) - .form("priority", 0) - .thenFunction(HttpResponse::isOk); - } - continue; - } - log.info("重命名 {} ==> {}", oldPath, newPath); - HttpReq.post(host + "/api/v2/torrents/renameFile") - .form("hash", torrentFile.getHexHash()) - .form("oldPath", oldPath) - .form("newPath", newPath) - .thenFunction(HttpResponse::isOk); - } - files.clear(); - files.addAll(qBittorrent.files(torrentsInfo, false, config)); - - if (CollUtil.containsAll(files.stream() - .map(qBittorrent.FileEntity::getName) - .toList(), reNameMap.values())) { - // 所有命名已完成 - break; - } - // 命名有遗漏 继续 - ThreadUtil.sleep(1000); - } - - qBittorrent.start(torrentsInfo, config); - resultSuccess(result -> - result.setMessage("已经开始下载合集") - ); - } - - } diff --git a/ani-rss-application/src/main/java/ani/rss/controller/ConfigController.java b/ani-rss-application/src/main/java/ani/rss/controller/ConfigController.java new file mode 100644 index 00000000..afca1741 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/ConfigController.java @@ -0,0 +1,278 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.commons.FileUtils; +import ani.rss.commons.MavenUtils; +import ani.rss.config.CronConfig; +import ani.rss.download.BaseDownload; +import ani.rss.entity.*; +import ani.rss.service.ClearService; +import ani.rss.service.TaskService; +import ani.rss.util.basic.HttpReq; +import ani.rss.util.other.AfdianUtil; +import ani.rss.util.other.AniUtil; +import ani.rss.util.other.ConfigUtil; +import ani.rss.util.other.TorrentUtil; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import cn.hutool.core.codec.Base64; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.*; +import cn.hutool.http.Header; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpStatus; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Cleanup; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.jsoup.Jsoup; +import org.springframework.web.bind.annotation.*; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class ConfigController extends BaseController { + + private final CronConfig cronConfig; + + /** + * 构建信息 + */ + public String buildInfo() { + String buildInfo = ""; + try { + buildInfo = ResourceUtil.readUtf8Str("build_info"); + } catch (Exception ignored) { + } + return buildInfo; + } + + @Auth + @Operation(summary = "获取设置") + @PostMapping("/config") + public Result config() { + String version = MavenUtils.getVersion(); + String buildInfo = buildInfo(); + Config config = ObjectUtil.clone(ConfigUtil.CONFIG); + config.getLogin().setPassword(""); + config.setVersion(version) + .setBuildInfo(buildInfo) + .setVerifyExpirationTime(AfdianUtil.verifyExpirationTime()); + return Result.success(config); + } + + @Auth + @Operation(summary = "修改设置") + @PostMapping("/setConfig") + public Result setConfig(@RequestBody Config newConfig) { + Config config = ConfigUtil.CONFIG; + Login login = config.getLogin(); + String username = login.getUsername(); + String password = login.getPassword(); + Integer renameSleepSeconds = config.getRenameSleepSeconds(); + Integer sleep = config.getRssSleepMinutes(); + String download = config.getDownloadToolType(); + + newConfig.setExpirationTime(null) + .setOutTradeNo(null) + .setTryOut(null); + + CopyOptions copyOptions = CopyOptions + .create() + .setIgnoreNullValue(true); + + BeanUtil.copyProperties( + newConfig, + config, + copyOptions + ); + + String loginPassword = config.getLogin().getPassword(); + // 密码未发生修改 + if (StrUtil.isBlank(loginPassword)) { + config.getLogin().setPassword(password); + } + String loginUsername = config.getLogin().getUsername(); + if (StrUtil.isBlank(loginUsername)) { + config.getLogin().setUsername(username); + } + + Boolean proxy = config.getProxy(); + if (proxy) { + String proxyHost = config.getProxyHost(); + Integer proxyPort = config.getProxyPort(); + if (StrUtil.isBlank(proxyHost) || Objects.isNull(proxyPort)) { + return Result.error("代理参数不完整"); + } + } + + ConfigUtil.sync(); + Integer newRenameSleepSeconds = config.getRenameSleepSeconds(); + Integer newSleep = config.getRssSleepMinutes(); + + // 时间间隔发生改变,重启任务 + if ( + !Objects.equals(newSleep, sleep) || + !Objects.equals(newRenameSleepSeconds, renameSleepSeconds) + ) { + TaskService.restart(); + } + // 下载工具发生改变 + if (!download.equals(config.getDownloadToolType())) { + TorrentUtil.load(); + } + + return Result.success("修改成功"); + } + + @Auth + @Operation(summary = "清理缓存") + @PostMapping("/clearCache") + public Result clearCache() { + File configDir = ConfigUtil.getConfigDir(); + String configDirStr = FileUtils.getAbsolutePath(configDir); + + Set covers = AniUtil.ANI_LIST + .stream() + .map(Ani::getCover) + .map(s -> FileUtils.getAbsolutePath(new File(configDirStr + "/files/" + s))) + .collect(Collectors.toSet()); + + FileUtil.mkdir(configDirStr + "/files"); + FileUtil.mkdir(configDirStr + "/img"); + + Set files = FileUtil.loopFiles(configDirStr + "/files") + .stream() + .filter(file -> { + String fileName = FileUtils.getAbsolutePath(file); + return !covers.contains(fileName); + }).collect(Collectors.toSet()); + long filesSize = files.stream() + .mapToLong(File::length) + .sum(); + long imgSize = FileUtil.size(new File(configDirStr + "/img")); + + long sumSize = filesSize + imgSize; + + if (sumSize < 1) { + return Result.success("清理完成, 共清理{}MB", 0); + } + + for (File file : files) { + FileUtil.del(file); + ClearService.clearParentFile(file); + } + + FileUtil.del(configDirStr + "/img"); + + String mb = NumberUtil.decimalFormat("0.00", sumSize / 1024.0 / 1024.0); + + return Result.success("清理完成, 共清理{}MB", mb); + } + + @Auth + @Operation(summary = "更新trackers") + @PostMapping("/trackersUpdate") + public Result trackersUpdate(@RequestBody Config config) { + cronConfig.updateTrackers(config); + return Result.success(); + } + + @Auth + @Operation(summary = "代理测试") + @PostMapping("/testProxy") + public Result testProxy(@RequestParam("url") String url, @RequestBody Config config) { + url = Base64.decodeStr(url); + + log.info(url); + + HttpRequest httpRequest = HttpReq.get(url); + HttpReq.setProxy(httpRequest, config); + + ProxyTest proxyTest = new ProxyTest(); + Result result = Result.success(proxyTest); + + long start = LocalDateTimeUtil.toEpochMilli(LocalDateTimeUtil.now()); + try { + httpRequest + .then(res -> { + int status = res.getStatus(); + proxyTest.setStatus(status); + + String title = Jsoup.parse(res.body()) + .title(); + result.setMessage(StrFormatter.format("测试成功 {}", title)); + }); + } catch (Exception e) { + result.setMessage(e.getMessage()) + .setCode(HttpStatus.HTTP_INTERNAL_ERROR); + } + + long end = LocalDateTimeUtil.toEpochMilli(LocalDateTimeUtil.now()); + proxyTest.setTime(end - start); + return result; + } + + @Auth + @Operation(summary = "下载器测试") + @PostMapping("/downloadLoginTest") + public Result downloadLoginTest(@RequestBody Config config) { + ConfigUtil.format(config); + String download = config.getDownloadToolType(); + Class loadClass = ClassUtil.loadClass("ani.rss.download." + download); + BaseDownload baseDownload = (BaseDownload) ReflectUtil.newInstance(loadClass); + Boolean login = baseDownload.login(true, config); + if (login) { + return Result.success("登录成功"); + } + return Result.error("登录失败"); + } + + @Operation(summary = "自定义JS") + @GetMapping("/custom.js") + public void customJs() throws IOException { + HttpServletResponse response = Global.RESPONSE.get(); + response.setHeader(Header.CACHE_CONTROL.toString(), "no-store, no-cache, must-revalidate, max-age=0"); + response.setHeader(Header.PRAGMA.toString(), "no-cache"); + response.setHeader("Expires", "0"); + + String customJs = ConfigUtil.CONFIG.getCustomJs(); + customJs = StrUtil.blankToDefault(customJs, "// empty js"); + String contentType = "application/javascript; charset=utf-8"; + + response.setContentType(contentType); + @Cleanup + OutputStream outputStream = response.getOutputStream(); + IoUtil.writeUtf8(outputStream, true, customJs); + } + + @Operation(summary = "自定义CSS") + @GetMapping("/custom.css") + public void customCss() throws IOException { + HttpServletResponse response = Global.RESPONSE.get(); + response.setHeader(Header.CACHE_CONTROL.toString(), "no-store, no-cache, must-revalidate, max-age=0"); + response.setHeader(Header.PRAGMA.toString(), "no-cache"); + response.setHeader("Expires", "0"); + + String customCss = ConfigUtil.CONFIG.getCustomCss(); + customCss = StrUtil.blankToDefault(customCss, "/* empty css */"); + String contentType = "text/css"; + + response.setContentType(contentType); + @Cleanup + OutputStream outputStream = response.getOutputStream(); + IoUtil.writeUtf8(outputStream, true, customCss); + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/action/WebHookAction.java b/ani-rss-application/src/main/java/ani/rss/controller/EmbyController.java similarity index 81% rename from ani-rss-application/src/main/java/ani/rss/action/WebHookAction.java rename to ani-rss-application/src/main/java/ani/rss/controller/EmbyController.java index 739adb39..70081dba 100644 --- a/ani-rss-application/src/main/java/ani/rss/action/WebHookAction.java +++ b/ani-rss-application/src/main/java/ani/rss/controller/EmbyController.java @@ -1,41 +1,38 @@ -package ani.rss.action; +package ani.rss.controller; -import ani.rss.commons.GsonStatic; -import ani.rss.entity.Ani; -import ani.rss.entity.Config; -import ani.rss.entity.EmbyWebHook; +import ani.rss.annotation.Auth; +import ani.rss.entity.*; import ani.rss.enums.StringEnum; import ani.rss.service.DownloadService; import ani.rss.util.other.*; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import ani.rss.web.auth.enums.AuthType; -import ani.rss.web.util.AuthUtil; -import cn.hutool.core.lang.Assert; import cn.hutool.core.thread.ExecutorBuilder; import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.Synchronized; +import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import wushuo.tmdb.api.entity.Tmdb; import java.io.File; -import java.io.IOException; import java.util.List; import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; -/** - * WebHook - */ @Slf4j -@Auth(type = AuthType.API_KEY) -@Path("/web_hook") -public class WebHookAction implements BaseAction { +@RestController +@RequestMapping +public class EmbyController extends BaseController { + @Auth + @Operation(summary = "获取媒体库") + @PostMapping("/getEmbyViews") + public Result> getEmbyViews(@RequestBody NotificationConfig notificationConfig) { + List views = EmbyUtil.getViews(notificationConfig); + return Result.success(views); + } private static final ExecutorService EXECUTOR = ExecutorBuilder.create() .setCorePoolSize(1) @@ -43,25 +40,19 @@ public class WebHookAction implements BaseAction { .setWorkQueue(new LinkedBlockingQueue<>(256)) .build(); - @Override - @Synchronized("EXECUTOR") - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String body = getBody(); - - Assert.notBlank(body, "WebHook body is empty"); - - log.debug("webhook: {}", body); + @Auth + @Operation(summary = "BGM自动点格子") + @PostMapping("/embyWebHook") + public Result embyWebHook(@RequestBody EmbyWebHook embyWebHook) { + log.debug("webhook: {}", embyWebHook); Config config = ConfigUtil.CONFIG; String bgmToken = config.getBgmToken(); if (StrUtil.isBlank(bgmToken)) { log.info("bgmToken 为空"); - response.sendOk(); - return; + return Result.success(); } - EmbyWebHook embyWebHook = GsonStatic.fromJson(body, EmbyWebHook.class); - String event = embyWebHook.getEvent(); if (List.of("system.webhooktest", "system.notificationtest").contains(event)) { @@ -83,8 +74,7 @@ public class WebHookAction implements BaseAction { String ip = AuthUtil.getIp(); log.info(s, ip, id, name, version); // 测试 - response.sendOk(); - return; + return Result.success(); } EmbyWebHook.Item item = embyWebHook.getItem(); @@ -92,32 +82,27 @@ public class WebHookAction implements BaseAction { String seriesName = item.getSeriesName(); String fileName = item.getFileName(); if (!ReUtil.contains(StringEnum.SEASON_REG, fileName)) { - response.sendOk(); - return; + return Result.success(); } // 季 int season = Integer.parseInt(ReUtil.get(StringEnum.SEASON_REG, fileName, 1)); // 番外 if (season < 1) { - response.sendOk(); - return; + return Result.success(); } // 集 x.5 double episode = Double.parseDouble(ReUtil.get(StringEnum.SEASON_REG, fileName, 2)); if (ItemsUtil.is5(episode)) { - response.sendOk(); - return; + return Result.success(); } - response.sendOk(); - int type = getType(embyWebHook); if (type < 0) { // 播放状态未正确获取 - return; + return Result.success(); } EXECUTOR.execute(() -> { @@ -142,6 +127,8 @@ public class WebHookAction implements BaseAction { BgmUtil.collections(subjectId); BgmUtil.collectionsEpisodes(episodeId, type); }); + + return Result.success(); } /** diff --git a/ani-rss-application/src/main/java/ani/rss/controller/FileController.java b/ani-rss-application/src/main/java/ani/rss/controller/FileController.java new file mode 100644 index 00000000..ada0330d --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/FileController.java @@ -0,0 +1,130 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.commons.ExceptionUtils; +import ani.rss.entity.Global; +import ani.rss.util.other.ConfigUtil; +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.http.Header; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Cleanup; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; + +@Slf4j +@RestController +public class FileController extends BaseController { + + @Auth + @Operation(summary = "获取文件") + @GetMapping("/file") + public void file(@RequestParam("filename") String filename) { + if (Base64.isBase64(filename)) { + filename = filename.replace(" ", "+"); + filename = Base64.decodeStr(filename); + } + + doFile(filename); + } + + /** + * 处理文件 + * + * @param filename 文件名 + */ + private void doFile(String filename) { + HttpServletRequest request = Global.REQUEST.get(); + HttpServletResponse response = Global.RESPONSE.get(); + + File file = new File(filename); + if (!file.exists()) { + File configDir = ConfigUtil.getConfigDir(); + file = new File(configDir + "/files/" + filename); + if (!file.exists()) { + writeNotFound(); + return; + } + } + + boolean hasRange = false; + long fileLength = file.length(); + long start = 0; + long end = fileLength - 1; + + String contentType = getContentType(file.getName()); + + response.setHeader(Header.CONTENT_DISPOSITION.toString(), StrFormatter.format("inline; filename=\"{}\"", URLUtil.encode(file.getName()))); + if (contentType.startsWith("video/")) { + response.setContentType(contentType); + response.setHeader("Accept-Ranges", "bytes"); + String rangeHeader = request.getHeader("Range"); + if (StrUtil.isNotBlank(rangeHeader) && rangeHeader.startsWith("bytes=")) { + String[] range = rangeHeader.substring(6).split("-"); + if (range.length > 0) { + start = Long.parseLong(range[0]); + } + if (range.length > 1) { + end = Long.parseLong(range[1]); + } else { + long maxEnd = start + (1024 * 1024 * 10); + end = Math.min(end, maxEnd); + } + } + response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength); + hasRange = true; + } else { + long maxAge = 0; + + // 小于或者等于 3M 缓存 + if (fileLength <= 1024 * 1024 * 3) { + // 30 天 + maxAge = 86400 * 30; + } + + response.setHeader(Header.CACHE_CONTROL.toString(), "private, max-age=" + maxAge); + response.setContentType(contentType); + } + + try { + if (hasRange) { + long length = end - start; + response.setStatus(206); + @Cleanup + OutputStream out = response.getOutputStream(); + @Cleanup + RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); + randomAccessFile.seek(start); + @Cleanup + FileChannel channel = randomAccessFile.getChannel(); + @Cleanup + InputStream inputStream = Channels.newInputStream(channel); + IoUtil.copy(inputStream, out, 40960, length, null); + } else { + @Cleanup + InputStream inputStream = FileUtil.getInputStream(file); + @Cleanup + OutputStream outputStream = response.getOutputStream(); + IoUtil.copy(inputStream, outputStream); + } + } catch (Exception e) { + String message = ExceptionUtils.getMessage(e); + log.debug(message, e); + } + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/action/LoginAction.java b/ani-rss-application/src/main/java/ani/rss/controller/LoginController.java similarity index 64% rename from ani-rss-application/src/main/java/ani/rss/action/LoginAction.java rename to ani-rss-application/src/main/java/ani/rss/controller/LoginController.java index 082f5252..4228e1b6 100644 --- a/ani-rss-application/src/main/java/ani/rss/action/LoginAction.java +++ b/ani-rss-application/src/main/java/ani/rss/controller/LoginController.java @@ -1,37 +1,29 @@ -package ani.rss.action; +package ani.rss.controller; import ani.rss.commons.CacheUtils; import ani.rss.entity.Config; import ani.rss.entity.Login; +import ani.rss.entity.Result; +import ani.rss.util.other.AuthUtil; import ani.rss.util.other.ConfigUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; -import ani.rss.web.util.AuthUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.RandomUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; +import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; -import java.io.IOException; - -import static ani.rss.web.util.AuthUtil.limitLoginAttempts; - -/** - * 登录 - */ @Slf4j -@Auth(value = false) -@Path("/login") -public class LoginAction implements BaseAction { +@RestController +public class LoginController extends BaseController { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - limitLoginAttempts(false); + @Operation(summary = "登录") + @PostMapping("/login") + public Result login(@RequestBody Login myLogin) { + AuthUtil.limitLoginAttempts(false); - Login myLogin = getBody(Login.class); Config config = ConfigUtil.CONFIG; Login login = config.getLogin(); @@ -57,25 +49,25 @@ public class LoginAction implements BaseAction { clearLimitLoginAttempts(); log.info("登录成功 {} ip: {}", username, ip); String s = AuthUtil.getAuth(myLogin); - resultSuccess(s); - return; + return new Result() + .setCode(200) + .setMessage("登录成功") + .setData(s); } - limitLoginAttempts(true); + AuthUtil.limitLoginAttempts(true); log.warn("登陆失败 {} ip: {}", myUsername, ip); ThreadUtil.sleep(RandomUtil.randomInt(500, 5000)); - resultErrorMsg("用户名或密码错误"); + return Result.error("用户名或密码错误"); } /** * 清除限制尝试次数 */ - public static void clearLimitLoginAttempts() { + private void clearLimitLoginAttempts() { String ip = AuthUtil.getIp(); String key = "LimitLoginAttempts#" + ip; if (CacheUtils.containsKey(key)) { CacheUtils.remove(key); } } - - } diff --git a/ani-rss-application/src/main/java/ani/rss/controller/LogsController.java b/ani-rss-application/src/main/java/ani/rss/controller/LogsController.java new file mode 100644 index 00000000..e20addc1 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/LogsController.java @@ -0,0 +1,79 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.entity.Global; +import ani.rss.entity.Log; +import ani.rss.entity.Result; +import ani.rss.util.basic.LogUtil; +import ani.rss.util.other.ConfigUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.ZipUtil; +import cn.hutool.http.Header; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Cleanup; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; + +@Slf4j +@RestController +public class LogsController extends BaseController { + List LOG_LIST = LogUtil.LOG_LIST; + + @Auth + @Operation(summary = "日志") + @PostMapping("/logs") + public Result> list() { + return Result.success(LOG_LIST); + } + + @Auth + @Operation(summary = "清理日志") + @PostMapping("/clearLogs") + public Result clearLogs() { + LOG_LIST.clear(); + log.info("清理日志"); + return Result.success(); + } + + @Auth + @Operation(summary = "下载日志") + @GetMapping("/downloadLogs") + public void downloadLogs() throws IOException { + File configDir = ConfigUtil.getConfigDir(); + String logsPath = configDir + "/logs"; + + String filename = "logs.zip"; + + String contentType = getContentType(filename); + + HttpServletResponse response = Global.RESPONSE.get(); + + response.setContentType(contentType); + response.setHeader(Header.CONTENT_DISPOSITION.toString(), StrFormatter.format("inline; filename=\"{}\"", filename)); + + @Cleanup + OutputStream outputStream = response.getOutputStream(); + + ZipUtil.zip(outputStream, StandardCharsets.UTF_8, false, name -> { + if (FileUtil.isDirectory(name)) { + return true; + } + String extName = FileUtil.extName(name); + if (StrUtil.isBlank(extName)) { + return false; + } + return extName.equals("log"); + }, new File(logsPath)); + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/controller/MikanController.java b/ani-rss-application/src/main/java/ani/rss/controller/MikanController.java new file mode 100644 index 00000000..10bc5ffd --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/MikanController.java @@ -0,0 +1,162 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.commons.GsonStatic; +import ani.rss.entity.Global; +import ani.rss.entity.Mikan; +import ani.rss.entity.Result; +import ani.rss.entity.TorrentsInfo; +import ani.rss.util.basic.HttpReq; +import ani.rss.util.other.ConfigUtil; +import ani.rss.util.other.MikanUtil; +import cn.hutool.core.codec.Base64; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.http.Header; +import cn.hutool.http.HttpConnection; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Cleanup; +import org.springframework.web.bind.annotation.*; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +@RestController +public class MikanController extends BaseController { + + @Auth + @Operation(summary = "获取Mikan番剧列表") + @PostMapping("/mikan") + public Result mikan(@RequestParam("text") String text, @RequestBody Mikan.Season season) { + Mikan list = MikanUtil.list(text, season); + return Result.success(list); + } + + @Auth + @Operation(summary = "获取Mikan番剧的字幕组列表") + @PostMapping("/mikanGroup") + public Result> mikanGroup(@RequestParam("url") String url) { + List groups = MikanUtil.getGroups(url); + + List regexItemList = List.of( + "1920[Xx]1080", "3840[Xx]2160", "1080[Pp]", "4[Kk]", "720[Pp]", + "繁", "简", "日", + "cht|Cht|CHT", "chs|Chs|CHS", "hevc|Hevc|HEVC", + "10bit|10Bit|10BIT", "h265|H265", "h264|H264", + "内嵌", "内封", "外挂", + "mp4|MP4", "mkv|MKV" + ); + + for (Mikan.Group group : groups) { + Set tags = new HashSet<>(); + List> regexList = new ArrayList<>(); + List items = group.getItems(); + for (TorrentsInfo item : items) { + String name = item.getName(); + List regexItems = new ArrayList<>(); + for (String regex : regexItemList) { + if (!ReUtil.contains(regex, name)) { + continue; + } + String label = ReUtil.get(regex, name, 0); + label = label.toUpperCase(); + Mikan.RegexItem regexItem = new Mikan.RegexItem(label, regex); + regexItems.add(regexItem); + tags.add(label); + } + regexItems = CollUtil.distinct(regexItems, GsonStatic::toJson, true); + regexList.add(regexItems); + } + + regexList = CollUtil.distinct(regexList, GsonStatic::toJson, true); + group.setRegexList(regexList) + .setTags(tags); + } + return Result.success(groups); + } + + @Auth + @Operation(summary = "获取Mikan封面") + @GetMapping("/mikanCover") + public void MikanCover(@RequestParam("img") String img) { + if (Base64.isBase64(img)) { + img = img.replace(" ", "+"); + img = Base64.decodeStr(img); + } + HttpServletResponse response = Global.RESPONSE.get(); + + // 30 天 + long maxAge = 86400 * 30; + + response.setHeader(Header.CACHE_CONTROL.toString(), "private, max-age=" + maxAge); + + String contentType = getContentType(URLUtil.getPath(img)); + + File configDir = ConfigUtil.getConfigDir(); + + File file = new File(URLUtil.getPath(img)); + configDir = new File(configDir + "/img/" + file.getParentFile().getName()); + FileUtil.mkdir(configDir); + + File imgFile = new File(configDir, file.getName()); + if (imgFile.exists()) { + try { + response.setContentType(contentType); + @Cleanup + InputStream inputStream = FileUtil.getInputStream(imgFile); + @Cleanup + OutputStream outputStream = response.getOutputStream(); + IoUtil.copy(inputStream, outputStream); + } catch (Exception ignored) { + } + return; + } + + getImg(img, is -> { + try { + response.setContentType(contentType); + FileUtil.writeFromStream(is, imgFile, true); + @Cleanup + BufferedInputStream inputStream = FileUtil.getInputStream(imgFile); + @Cleanup + ServletOutputStream outputStream = response.getOutputStream(); + IoUtil.copy(inputStream, outputStream); + } catch (Exception ignored) { + } + }); + } + + public void getImg(String url, Consumer consumer) { + URI host = URLUtil.getHost(URLUtil.url(url)); + HttpReq.get(url) + .then(res -> { + HttpConnection httpConnection = (HttpConnection) ReflectUtil.getFieldValue(res, "httpConnection"); + URI host1 = URLUtil.getHost(httpConnection.getUrl()); + if (host.toString().equals(host1.toString())) { + try { + @Cleanup + InputStream inputStream = res.bodyStream(); + consumer.accept(inputStream); + } catch (Exception ignored) { + } + return; + } + String newUrl = url.replace(host.toString(), host1.toString()); + getImg(newUrl, consumer); + }); + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/action/NotificationAction.java b/ani-rss-application/src/main/java/ani/rss/controller/NotificationController.java similarity index 55% rename from ani-rss-application/src/main/java/ani/rss/action/NotificationAction.java rename to ani-rss-application/src/main/java/ani/rss/controller/NotificationController.java index 43ec7d30..990296e3 100644 --- a/ani-rss-application/src/main/java/ani/rss/action/NotificationAction.java +++ b/ani-rss-application/src/main/java/ani/rss/controller/NotificationController.java @@ -1,53 +1,35 @@ -package ani.rss.action; +package ani.rss.controller; +import ani.rss.annotation.Auth; import ani.rss.entity.Ani; import ani.rss.entity.BgmInfo; import ani.rss.entity.NotificationConfig; +import ani.rss.entity.Result; import ani.rss.enums.NotificationStatusEnum; import ani.rss.enums.NotificationTypeEnum; import ani.rss.notification.BaseNotification; +import ani.rss.notification.TelegramNotification; import ani.rss.util.other.AniUtil; import ani.rss.util.other.BgmUtil; import ani.rss.util.other.NotificationUtil; import ani.rss.util.other.TmdbUtils; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; import cn.hutool.core.util.ReflectUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; import wushuo.tmdb.api.entity.Tmdb; -import java.io.IOException; +import java.util.Map; import java.util.Optional; -/** - * 通知 - */ -@Auth -@Path("/notification") -public class NotificationAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - String type = request.getParam("type"); - if ("test".equals(type)) { - test(); - return; - } +@RestController +public class NotificationController extends BaseController { - if ("add".equals(type)) { - add(); - } - - } - - private void add() { - NotificationConfig notificationConfig = NotificationConfig.createNotificationConfig(); - resultSuccess(notificationConfig); - } - - private void test() { - NotificationConfig notificationConfig = getBody(NotificationConfig.class); + @Auth + @Operation(summary = "测试通知") + @PostMapping("/testNotification") + public Result testNotification(@RequestBody NotificationConfig notificationConfig) { NotificationTypeEnum notificationType = notificationConfig.getNotificationType(); Class aClass = NotificationUtil.NOTIFICATION_MAP.get(notificationType); BaseNotification baseNotification = ReflectUtil.newInstance(aClass); @@ -69,10 +51,26 @@ public class NotificationAction implements BaseAction { try { baseNotification.test(notificationConfig, ani, "test", NotificationStatusEnum.DOWNLOAD_START); - resultSuccess(); + return Result.success(); } catch (Exception e) { - resultErrorMsg(e.getMessage()); + return Result.error(e.getMessage()); } } + @Auth + @Operation(summary = "新的通知") + @PostMapping("/newNotification") + public Result newNotification() { + NotificationConfig notificationConfig = NotificationConfig.createNotificationConfig(); + return Result.success(notificationConfig); + } + + @Auth + @Operation(summary = "获取TG最近消息") + @PostMapping("/getTgUpdates") + public Result> getUpdates(@RequestBody NotificationConfig notificationConfig) { + Map map = TelegramNotification.getUpdates(notificationConfig); + return Result.success(map); + } + } diff --git a/ani-rss-application/src/main/java/ani/rss/action/PlaylistAction.java b/ani-rss-application/src/main/java/ani/rss/controller/PlayController.java similarity index 67% rename from ani-rss-application/src/main/java/ani/rss/action/PlaylistAction.java rename to ani-rss-application/src/main/java/ani/rss/controller/PlayController.java index 72880b22..e1fc6025 100644 --- a/ani-rss-application/src/main/java/ani/rss/action/PlaylistAction.java +++ b/ani-rss-application/src/main/java/ani/rss/controller/PlayController.java @@ -1,45 +1,98 @@ -package ani.rss.action; +package ani.rss.controller; +import ani.rss.annotation.Auth; import ani.rss.commons.FileUtils; import ani.rss.entity.Ani; import ani.rss.entity.PlayItem; +import ani.rss.entity.Result; import ani.rss.enums.StringEnum; import ani.rss.service.DownloadService; import ani.rss.util.other.AniUtil; -import ani.rss.web.action.BaseAction; -import ani.rss.web.annotation.Auth; -import ani.rss.web.annotation.Path; import cn.hutool.core.codec.Base64; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.FileUtil; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.http.server.HttpServerRequest; -import cn.hutool.http.server.HttpServerResponse; -import lombok.extern.slf4j.Slf4j; +import com.matthewn4444.ebml.EBMLReader; +import com.matthewn4444.ebml.subtitles.Subtitles; +import io.swagger.v3.oas.annotations.Operation; +import lombok.Cleanup; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; import java.io.File; import java.io.IOException; import java.util.*; -/** - * 视频列表 - */ -@Auth -@Slf4j -@Path("/playlist") -public class PlaylistAction implements BaseAction { - @Override - public void doAction(HttpServerRequest request, HttpServerResponse response) throws IOException { - Ani ani = getBody(Ani.class); +@RestController +public class PlayController extends BaseController { + + @Auth + @Operation(summary = "获取内封字幕") + @PostMapping("/getSubtitles") + public Result> getSubtitles(@RequestBody Map map) throws IOException { + String file = map.get("file"); + Assert.notBlank(file); + + if (Base64.isBase64(file)) { + file = Base64.decodeStr(file); + } + + List subtitlesList = new ArrayList<>(); + + String extName = FileUtil.extName(file); + if (StrUtil.isBlank(extName)) { + return Result.success(subtitlesList); + } + + if (!"mkv".equals(extName)) { + return Result.success(subtitlesList); + } + + Assert.isTrue(FileUtil.exist(file), "视频文件不存在"); + + @Cleanup + EBMLReader reader = new EBMLReader(file); + if (!reader.readHeader()) { + return Result.success(subtitlesList); + } + reader.readTracks(); + reader.readCues(); + + for (int i = 0; i < reader.getCuesCount(); i++) { + reader.readSubtitlesInCueFrame(i); + } + + List subtitles = reader.getSubtitles(); + for (Subtitles subtitle : subtitles) { + String name = subtitle.getName(); + String presentableName = subtitle.getPresentableName(); + String contents = subtitle.getContentsToVTT(); + PlayItem.Subtitles sub = new PlayItem.Subtitles(); + sub.setContent(contents) + .setName(name) + .setHtml(presentableName) + .setUrl("") + .setType("vtt"); + subtitlesList.add(sub); + } + + return Result.success(subtitlesList); + } + + @Auth + @Operation(summary = "获取视频列表") + @PostMapping("/playList") + public Result> playList(@RequestBody Ani ani) { String url = ani.getUrl(); Optional first = AniUtil.ANI_LIST .stream() .filter(it -> url.equals(it.getUrl())) .findFirst(); if (first.isEmpty()) { - resultError(); - return; + return Result.error(); } ani = first.get(); @@ -49,7 +102,7 @@ public class PlaylistAction implements BaseAction { // 按照集数排序 CollUtil.sort(collect, Comparator.comparingDouble(PlayItem::getEpisode)); - resultSuccess(collect); + return Result.success(collect); } /** diff --git a/ani-rss-application/src/main/java/ani/rss/controller/ScrapeController.java b/ani-rss-application/src/main/java/ani/rss/controller/ScrapeController.java new file mode 100644 index 00000000..772824b6 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/ScrapeController.java @@ -0,0 +1,29 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.entity.Ani; +import ani.rss.entity.Result; +import ani.rss.service.ScrapeService; +import cn.hutool.core.thread.ThreadUtil; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ScrapeController extends BaseController { + + @Auth + @Operation(summary = "刮削") + @PostMapping("/scrape") + public Result scrape(@RequestParam("force") Boolean force, @RequestBody Ani ani) { + ThreadUtil.execute(() -> + ScrapeService.scrape(ani, force) + ); + + String title = ani.getTitle(); + + return Result.success("已开始刮削 {}", title); + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/controller/ThemoviedbController.java b/ani-rss-application/src/main/java/ani/rss/controller/ThemoviedbController.java new file mode 100644 index 00000000..e00c1431 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/ThemoviedbController.java @@ -0,0 +1,48 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.entity.Ani; +import ani.rss.entity.Result; +import ani.rss.util.other.TmdbUtils; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpStatus; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import wushuo.tmdb.api.entity.Tmdb; +import wushuo.tmdb.api.entity.TmdbGroup; + +import java.util.List; + +@RestController +public class ThemoviedbController extends BaseController { + + @Auth + @Operation(summary = "获取TMDB标题") + @PostMapping("/getThemoviedbName") + public Result getThemoviedbName(@RequestBody Ani ani) { + String themoviedbName = TmdbUtils.getFinalName(ani); + Result result = new Result() + .setCode(HttpStatus.HTTP_OK) + .setMessage("获取TMDB成功") + .setData(ani.setThemoviedbName(themoviedbName)); + if (StrUtil.isBlank(themoviedbName)) { + result.setCode(HttpStatus.HTTP_INTERNAL_ERROR) + .setMessage("获取TMDB失败"); + } + return result; + } + + @Auth + @Operation(summary = "获取TMDB剧集组") + @PostMapping("/getThemoviedbGroup") + public Result> getThemoviedbGroup(@RequestBody Ani ani) { + Tmdb tmdb = ani.getTmdb(); + Assert.notNull(tmdb, "tmdb is null"); + Assert.notBlank(tmdb.getId(), "tmdb is null"); + List tmdbGroup = TmdbUtils.getTmdbGroup(tmdb); + return Result.success(tmdbGroup); + } +} diff --git a/ani-rss-application/src/main/java/ani/rss/controller/TorrentController.java b/ani-rss-application/src/main/java/ani/rss/controller/TorrentController.java new file mode 100644 index 00000000..70cd3184 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/TorrentController.java @@ -0,0 +1,49 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.commons.FileUtils; +import ani.rss.entity.Ani; +import ani.rss.entity.Result; +import ani.rss.util.other.AniUtil; +import ani.rss.util.other.TorrentUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.io.File; +import java.util.List; +import java.util.Optional; + +@Slf4j +@RestController +public class TorrentController extends BaseController { + + @Auth + @Operation(summary = "删除缓存种子") + @PostMapping("/deleteTorrent") + public Result del(@RequestParam("id") String id, @RequestParam("hash") String hash) { + Optional first = AniUtil.ANI_LIST.stream() + .filter(ani -> id.equals(ani.getId())) + .findFirst(); + if (first.isEmpty()) { + return Result.error("此订阅不存在"); + } + + List hashList = StrUtil.split(hash, ",", true, true); + Ani ani = first.get(); + File torrentDir = TorrentUtil.getTorrentDir(ani); + File[] files = FileUtils.listFiles(torrentDir); + for (File file : files) { + String name = FileUtil.mainName(file); + if (hashList.contains(name)) { + log.info("删除种子 {}", file); + FileUtil.del(file); + } + } + return Result.success("删除完成"); + } +} \ No newline at end of file diff --git a/ani-rss-application/src/main/java/ani/rss/controller/TorrentsInfosController.java b/ani-rss-application/src/main/java/ani/rss/controller/TorrentsInfosController.java new file mode 100644 index 00000000..b4c3017b --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/TorrentsInfosController.java @@ -0,0 +1,24 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.entity.Result; +import ani.rss.entity.TorrentsInfo; +import ani.rss.util.other.TorrentUtil; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +public class TorrentsInfosController extends BaseController { + + @Auth + @Operation(summary = "下载列表") + @PostMapping("/torrentsInfos") + public Result> torrentsInfos() { + List torrentsInfos = TorrentUtil.getTorrentsInfos(); + return Result.success(torrentsInfos); + } + +} diff --git a/ani-rss-application/src/main/java/ani/rss/controller/UploadController.java b/ani-rss-application/src/main/java/ani/rss/controller/UploadController.java new file mode 100644 index 00000000..ee2dc3dc --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/controller/UploadController.java @@ -0,0 +1,47 @@ +package ani.rss.controller; + +import ani.rss.annotation.Auth; +import ani.rss.entity.Global; +import ani.rss.entity.Result; +import ani.rss.util.other.ConfigUtil; +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.FileUtil; +import cn.hutool.crypto.SecureUtil; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; + +@RestController +public class UploadController extends BaseController { + @Auth + @Operation(summary = "上传文件") + @PostMapping("/upload") + public Result upload(@RequestParam("file") MultipartFile file) throws IOException { + HttpServletRequest request = Global.REQUEST.get(); + String type = request.getParameter("type"); + byte[] fileContent = file.getBytes(); + if ("getBase64".equals(type)) { + return Result.success(r -> + r.setData(Base64.encode(fileContent)) + ); + } + + String s = SecureUtil.md5(new ByteArrayInputStream(fileContent)); + String fileName = file.getOriginalFilename(); + String saveName = s + "." + FileUtil.extName(fileName); + + File configDir = ConfigUtil.getConfigDir(); + FileUtil.mkdir(configDir + "/files/" + s.charAt(0)); + FileUtil.writeBytes(fileContent, configDir + "/files/" + s.charAt(0) + "/" + saveName); + return new Result<>() + .setMessage("上传完成") + .setData(s.charAt(0) + "/" + saveName); + } +} diff --git a/ani-rss-core/src/main/java/ani/rss/download/Aria2.java b/ani-rss-application/src/main/java/ani/rss/download/Aria2.java similarity index 98% rename from ani-rss-core/src/main/java/ani/rss/download/Aria2.java rename to ani-rss-application/src/main/java/ani/rss/download/Aria2.java index 90e7a408..3ad21592 100644 --- a/ani-rss-core/src/main/java/ani/rss/download/Aria2.java +++ b/ani-rss-application/src/main/java/ani/rss/download/Aria2.java @@ -19,7 +19,9 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpResponse; 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.*; @@ -28,6 +30,8 @@ import java.util.*; * Aria2 */ @Slf4j +@Service +@RequiredArgsConstructor public class Aria2 implements BaseDownload { private Config config; diff --git a/ani-rss-core/src/main/java/ani/rss/download/BaseDownload.java b/ani-rss-application/src/main/java/ani/rss/download/BaseDownload.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/download/BaseDownload.java rename to ani-rss-application/src/main/java/ani/rss/download/BaseDownload.java diff --git a/ani-rss-core/src/main/java/ani/rss/download/OpenList.java b/ani-rss-application/src/main/java/ani/rss/download/OpenList.java similarity index 99% rename from ani-rss-core/src/main/java/ani/rss/download/OpenList.java rename to ani-rss-application/src/main/java/ani/rss/download/OpenList.java index 6aebe6f3..d58b45f4 100644 --- a/ani-rss-core/src/main/java/ani/rss/download/OpenList.java +++ b/ani-rss-application/src/main/java/ani/rss/download/OpenList.java @@ -25,13 +25,17 @@ import cn.hutool.http.HttpResponse; import cn.hutool.http.Method; 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.*; import java.util.stream.Stream; @Slf4j +@Service +@RequiredArgsConstructor public class OpenList implements BaseDownload { private Config config; diff --git a/ani-rss-core/src/main/java/ani/rss/download/Transmission.java b/ani-rss-application/src/main/java/ani/rss/download/Transmission.java similarity index 98% rename from ani-rss-core/src/main/java/ani/rss/download/Transmission.java rename to ani-rss-application/src/main/java/ani/rss/download/Transmission.java index 77f9c627..5632886b 100644 --- a/ani-rss-core/src/main/java/ani/rss/download/Transmission.java +++ b/ani-rss-application/src/main/java/ani/rss/download/Transmission.java @@ -22,7 +22,9 @@ 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; @@ -34,15 +36,15 @@ import java.util.Set; * Transmission */ @Slf4j +@Service +@RequiredArgsConstructor public class Transmission implements BaseDownload { private String host = ""; private String authorization = ""; private String sessionId = ""; - private Config config; @Override public Boolean login(Boolean test, Config config) { - this.config = config; String username = config.getDownloadToolUsername(); String password = config.getDownloadToolPassword(); host = config.getDownloadToolHost(); diff --git a/ani-rss-core/src/main/java/ani/rss/download/qBittorrent.java b/ani-rss-application/src/main/java/ani/rss/download/qBittorrent.java similarity index 99% rename from ani-rss-core/src/main/java/ani/rss/download/qBittorrent.java rename to ani-rss-application/src/main/java/ani/rss/download/qBittorrent.java index cbbf3419..81c96314 100644 --- a/ani-rss-core/src/main/java/ani/rss/download/qBittorrent.java +++ b/ani-rss-application/src/main/java/ani/rss/download/qBittorrent.java @@ -24,8 +24,10 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import lombok.Data; +import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; import java.io.File; import java.util.*; @@ -34,7 +36,10 @@ import java.util.*; * qBittorrent */ @Slf4j +@Service +@RequiredArgsConstructor public class qBittorrent implements BaseDownload { + private Config config; /** diff --git a/ani-rss-core/src/main/java/ani/rss/dto/ImportAniDataDTO.java b/ani-rss-application/src/main/java/ani/rss/dto/ImportAniDataDTO.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/dto/ImportAniDataDTO.java rename to ani-rss-application/src/main/java/ani/rss/dto/ImportAniDataDTO.java diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/About.java b/ani-rss-application/src/main/java/ani/rss/entity/About.java similarity index 64% rename from ani-rss-domain/src/main/java/ani/rss/entity/About.java rename to ani-rss-application/src/main/java/ani/rss/entity/About.java index 6d4d260a..54a483ab 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/About.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/About.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -11,39 +12,47 @@ import java.util.Date; */ @Data @Accessors(chain = true) +@Schema(description = "关于") public class About implements Serializable { /** * 版本 */ + @Schema(description = "版本") private String version; /** * 最新版本 */ + @Schema(description = "最新版本") private String latest; /** * 是否需要更新 */ + @Schema(description = "是否需要更新") private Boolean update; /** * 是否允许自动更新 */ + @Schema(description = "是否允许自动更新") private Boolean autoUpdate; /** * 下载地址 */ + @Schema(description = "下载地址") private String downloadUrl; /** * 更新内容 */ + @Schema(description = "更新内容") private String markdownBody; /** * 发布时间 */ + @Schema(description = "发布时间") private Date date; } diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/Ani.java b/ani-rss-application/src/main/java/ani/rss/entity/Ani.java similarity index 62% rename from ani-rss-domain/src/main/java/ani/rss/entity/Ani.java rename to ani-rss-application/src/main/java/ani/rss/entity/Ani.java index 1aeb601f..5eb9fd43 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/Ani.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/Ani.java @@ -1,6 +1,7 @@ package ani.rss.entity; import com.google.gson.annotations.SerializedName; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; import wushuo.tmdb.api.entity.Tmdb; @@ -13,20 +14,24 @@ import java.util.List; */ @Data @Accessors(chain = true) +@Schema(description = "订阅") public class Ani implements Serializable { /** * id */ + @Schema(description = "id") private String id; /** * 不在页面显示 */ + @Schema(description = "不在页面显示") private String mikanTitle; /** * RSS URL */ + @Schema(description = "RSS URL") private String url; private Boolean exists; @@ -34,239 +39,288 @@ public class Ani implements Serializable { /** * 备用rss */ + @Schema(description = "备用rss") private List standbyRssList; /** * 标题 */ + @Schema(description = "标题") private String title; /** * 日语标题 来源于BGM */ + @Schema(description = "日语标题 来源于BGM") private String jpTitle; /** * 剧集偏移 */ + @Schema(description = "剧集偏移") private Integer offset; /** * 年度 */ + @Schema(description = "年度") private Integer year; /** * 月 */ + @Schema(description = "月") private Integer month; /** * 日 */ + @Schema(description = "日") private Integer date; /** * 星期 1表示周日,2表示周一 */ + @Schema(description = "星期 1表示周日,2表示周一") private Integer week; /** * 季度 */ + @Schema(description = "季度") private Integer season; /** * 封面本地保存位置 */ + @Schema(description = "封面本地保存位置") private String cover; /** * 图片 https:// */ + @Schema(description = "图片 https://") private String image; /** * 字幕组 */ + @Schema(description = "字幕组") private String subgroup; /** * 匹配 */ + @Schema(description = "匹配") private List match; /** * 排除 */ + @Schema(description = "排除") private List exclude; /** * 是否启用全局排除 */ + @Schema(description = "是否启用全局排除") private Boolean globalExclude; /** * 剧场版 or OVA */ + @Schema(description = "剧场版 or OVA") private Boolean ova; /** * 拼音 */ + @Schema(description = "拼音") private String pinyin; /** * 拼音 */ + @Schema(description = "拼音首字母") private String pinyinInitials; /** * 启用 */ + @Schema(description = "启用") private Boolean enable; /** * 当前集数 */ + @Schema(description = "当前集数") private Integer currentEpisodeNumber; /** * 总集数 */ + @Schema(description = "总集数") private Integer totalEpisodeNumber; + @Schema(description = "TheMovieDB 名称") private String themoviedbName; + @Schema(description = "类型") private String type; + @Schema(description = "BGM 地址") private String bgmUrl; /** * 自定义下载位置 */ + @Schema(description = "自定义下载位置") private Boolean customDownloadPath; /** * 自定义下载位置 */ + @Schema(description = "自定义下载位置路径") private String downloadPath; /** * 评分 */ + @Schema(description = "评分") private Double score; /** * 自定义集数获取规则 */ + @Schema(description = "自定义集数获取规则") private Boolean customEpisode; /** * 自定义集数获取规则 */ + @Schema(description = "自定义集数获取规则表达式") private String customEpisodeStr; /** * 自定义集数获取规则 groupIndex */ + @Schema(description = "自定义集数获取规则 groupIndex") private Integer customEpisodeGroupIndex; /** * 遗漏检测 */ + @Schema(description = "遗漏检测") private Boolean omit; /** * 只下载最新集 */ + @Schema(description = "只下载最新集") private Boolean downloadNew; /** * 不进行下载的集 */ + @Schema(description = "不进行下载的集") private List notDownload; /** * tmdb 相关信息 */ + @Schema(description = "TMDB 相关信息") private Tmdb tmdb; /** * 自动上传 */ + @Schema(description = "自动上传") private Boolean upload; /** * 摸鱼 */ + @Schema(description = "摸鱼") private Boolean procrastinating; /** * 自定义重命名模版 */ + @Schema(description = "自定义重命名模版开关") private Boolean customRenameTemplateEnable; /** * 自定义重命名模版 */ + @Schema(description = "自定义重命名模版") private String customRenameTemplate; /** * 自定义优先保留开关 */ + @Schema(description = "自定义优先保留开关") private Boolean customPriorityKeywordsEnable; /** * 自定义优先保留关键词列表 */ + @Schema(description = "自定义优先保留关键词列表") private List customPriorityKeywords; /** * 上次下载完成时间 */ + @Schema(description = "上次下载完成时间") private Long lastDownloadTime; /** * 自定义上传 */ @SerializedName(value = "customUploadEnable", alternate = "customAlistPath") + @Schema(description = "自定义上传开关") private Boolean customUploadEnable; /** * 自定义上传 */ @SerializedName(value = "customUploadPathTarget", alternate = "alistPath") + @Schema(description = "自定义上传目标路径") private String customUploadPathTarget; /** * 消息通知 */ + @Schema(description = "消息通知") private Boolean message; /** * 完结迁移 */ + @Schema(description = "完结迁移") private Boolean completed; /** * 自定义完结迁移 */ + @Schema(description = "自定义完结迁移开关") private Boolean customCompleted; /** * 自定义完结迁移 */ + @Schema(description = "自定义完结迁移路径模版") private String customCompletedPathTemplate; /** * 自定义标签开关 */ + @Schema(description = "自定义标签开关") private Boolean customTagsEnable; /** * 单个订阅自定义标签 */ + @Schema(description = "单个订阅自定义标签") private List customTags; diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/BgmInfo.java b/ani-rss-application/src/main/java/ani/rss/entity/BgmInfo.java similarity index 71% rename from ani-rss-domain/src/main/java/ani/rss/entity/BgmInfo.java rename to ani-rss-application/src/main/java/ani/rss/entity/BgmInfo.java index 88a0ac1c..ea66d183 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/BgmInfo.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/BgmInfo.java @@ -1,6 +1,7 @@ package ani.rss.entity; import com.google.gson.annotations.SerializedName; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -14,6 +15,7 @@ import java.util.Map; */ @Data @Accessors(chain = true) +@Schema(description = "Bgm番剧信息") public class BgmInfo implements Serializable { private String id; @@ -22,37 +24,44 @@ public class BgmInfo implements Serializable { /** * 名称 */ + @Schema(description = "名称") private String name; /** * 中文名称 */ @SerializedName(value = "nameCn", alternate = "name_cn") + @Schema(description = "中文名称") private String nameCn; /** * 集数 */ + @Schema(description = "集数") private Integer eps; /** * 时间 */ + @Schema(description = "时间") private Date date; /** * 图片 */ + @Schema(description = "图片") private Images images; /** * 季度 */ + @Schema(description = "季度") private Integer season; /** * 平台 OVA/剧场版 */ + @Schema(description = "平台 OVA/剧场版") private String platform; private List tags; @@ -64,6 +73,7 @@ public class BgmInfo implements Serializable { */ @Data @Accessors(chain = true) + @Schema(description = "封面图片") public static class Images implements Serializable { private String small; private String grid; @@ -77,12 +87,16 @@ public class BgmInfo implements Serializable { */ @Data @Accessors(chain = true) + @Schema(description = "标签") public static class Tag implements Serializable { + @Schema(description = "标签名") private String name; + @Schema(description = "计数") private String count; @SerializedName(value = "totalCont", alternate = "total_cont") + @Schema(description = "总计数") private String totalCont; } @@ -91,25 +105,30 @@ public class BgmInfo implements Serializable { */ @Data @Accessors(chain = true) + @Schema(description = "评分") public static class Rating implements Serializable { /** * 级别 */ + @Schema(description = "级别") private Integer rank; /** * 评分 */ + @Schema(description = "评分") private Double score; /** * 评分数 */ + @Schema(description = "评分数") private Integer total; /** * 各阶段评分数 */ + @Schema(description = "各阶段评分数") private Map count; } } diff --git a/ani-rss-application/src/main/java/ani/rss/entity/BgmMe.java b/ani-rss-application/src/main/java/ani/rss/entity/BgmMe.java new file mode 100644 index 00000000..32a76003 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/entity/BgmMe.java @@ -0,0 +1,54 @@ +package ani.rss.entity; + +import com.google.gson.annotations.SerializedName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +@Data +@Accessors(chain = true) +@Schema(description = "BGM 用户信息") +public class BgmMe implements Serializable { + @Schema(description = "头像") + private Avatar avatar; + @Schema(description = "用户ID") + private Integer id; + @Schema(description = "签名") + private String sign; + @Schema(description = "主页地址") + private String url; + @Schema(description = "用户名") + private String username; + @Schema(description = "昵称") + private String nickname; + @SerializedName(value = "userGroup", alternate = "user_group") + @Schema(description = "用户组") + private String userGroup; + @SerializedName(value = "regTime", alternate = "reg_time") + @Schema(description = "注册时间") + private Date regTime; + @Schema(description = "邮箱") + private String email; + @SerializedName(value = "timeOffset", alternate = "time_offset") + @Schema(description = "时区偏移") + private Integer timeOffset; + @SerializedName(value = "expiresDays", alternate = "expires_days") + @Schema(description = "过期天数") + private Integer expiresDays; + + @Data + @Accessors(chain = true) + @Schema(description = "头像") + public static class Avatar implements Serializable { + @Schema(description = "large") + private String large; + @Schema(description = "medium") + private String medium; + @Schema(description = "small") + private String small; + } +} + diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/CollectionInfo.java b/ani-rss-application/src/main/java/ani/rss/entity/CollectionInfo.java similarity index 66% rename from ani-rss-domain/src/main/java/ani/rss/entity/CollectionInfo.java rename to ani-rss-application/src/main/java/ani/rss/entity/CollectionInfo.java index b8a74a19..2f93954c 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/CollectionInfo.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/CollectionInfo.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -10,19 +11,23 @@ import java.io.Serializable; */ @Data @Accessors(chain = true) +@Schema(description = "合集") public class CollectionInfo implements Serializable { /** * 种子文件 base64 */ + @Schema(description = "种子文件 base64") private String torrent; /** * 订阅 */ + @Schema(description = "订阅") private Ani ani; /** * bgm */ + @Schema(description = "bgm") private BgmInfo bgmInfo; } diff --git a/ani-rss-core/src/main/java/ani/rss/entity/Config.java b/ani-rss-application/src/main/java/ani/rss/entity/Config.java similarity index 62% rename from ani-rss-core/src/main/java/ani/rss/entity/Config.java rename to ani-rss-application/src/main/java/ani/rss/entity/Config.java index 2fafa545..c4151c04 100644 --- a/ani-rss-core/src/main/java/ani/rss/entity/Config.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/Config.java @@ -2,6 +2,7 @@ package ani.rss.entity; import ani.rss.enums.BgmTokenTypeEnum; import ani.rss.enums.SortTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -13,305 +14,366 @@ import java.util.List; */ @Data @Accessors(chain = true) +@Schema(description = "设置") public class Config implements Serializable { /** * Mikan Host */ + @Schema(description = "Mikan Host") private String mikanHost; /** * tmdbApi */ + @Schema(description = "TMDB API") private String tmdbApi; /** * tmdbApiKey */ + @Schema(description = "TMDB API Key") private String tmdbApiKey; /** * 仅获取动漫 */ + @Schema(description = "仅获取动漫") private Boolean tmdbAnime; /** * 下载工具 */ + @Schema(description = "下载工具类型") private String downloadToolType; /** * 下载重试次数 */ + @Schema(description = "下载重试次数") private Integer downloadRetry; /** * 下载工具 地址 */ + @Schema(description = "下载工具地址") private String downloadToolHost; /** * 下载工具 用户名 */ + @Schema(description = "下载工具用户名") private String downloadToolUsername; /** * 下载工具 密码 */ + @Schema(description = "下载工具密码") private String downloadToolPassword; /** * qb下载时,使用qb自身的保存路径配置(未下载完成的使用临时目录,复制种子文件) */ + @Schema(description = "使用 qb 自身保存路径") private Boolean qbUseDownloadPath; /** * 分享率 */ + @Schema(description = "分享率") private Integer ratioLimit; /** * 总做种时长 */ + @Schema(description = "总做种时长") private Integer seedingTimeLimit; /** * 非活跃时长 */ + @Schema(description = "非活跃时长") private Integer inactiveSeedingTimeLimit; /** * 下载路径 */ + @Schema(description = "下载路径模版") private String downloadPathTemplate; /** * 剧场版下载路径 */ + @Schema(description = "剧场版下载路径模版") private String ovaDownloadPathTemplate; /** * 自定义标签 */ + @Schema(description = "自定义标签") private List customTags; /* * 优先保留开关 */ + @Schema(description = "优先保留开关") private Boolean priorityKeywordsEnable; /** * 优先保留关键词列表 */ + @Schema(description = "优先保留关键词列表") private List priorityKeywords; /** * 延迟下载 */ + @Schema(description = "延迟下载(分钟)") private Integer delayedDownload; /** * 显示评分 */ + @Schema(description = "显示评分") private Boolean scoreShow; /** * RSS 间隔(分钟) */ + @Schema(description = "RSS 间隔(分钟)") private Integer rssSleepMinutes; /** * 重命名间隔(秒) */ + @Schema(description = "重命名间隔(秒)") private Integer renameSleepSeconds; /** * 自动重命名 */ + @Schema(description = "自动重命名") private Boolean rename; /** * rss开关 */ + @Schema(description = "RSS 开关") private Boolean rss; /** * rss 超时时间 秒 */ + @Schema(description = "RSS 超时时间(秒)") private Integer rssTimeout; /** * 文件已下载自动跳过 */ + @Schema(description = "文件已下载自动跳过") private Boolean fileExist; /** * 等待做种完毕 */ + @Schema(description = "等待做种完毕") private Boolean awaitStalledUP; /** * 自动删除已完成任务 */ + @Schema(description = "自动删除已完成任务") private Boolean delete; /** * 仅在主RSS更新后删除备用RSS */ + @Schema(description = "主RSS更新后删除备用RSS") private Boolean deleteStandbyRSSOnly; /** * 自动推断剧集偏移 */ + @Schema(description = "自动推断剧集偏移") private Boolean offset; /** * 获取标题时带上年份 */ + @Schema(description = "获取标题时带上年份") private Boolean titleYear; /** * 自动禁用已完结番剧的订阅 */ + @Schema(description = "自动禁用已完结番剧订阅") private Boolean autoDisabled; /** * 自动跳过 x.5 集数 */ + @Schema(description = "自动跳过 x.5 集数") private Boolean skip5; /** * 备用RSS */ + @Schema(description = "备用RSS") private Boolean standbyRss; /** * 多字幕组共存模式 */ + @Schema(description = "多字幕组共存模式") private Boolean coexist; /** * 最大日志条数 */ + @Schema(description = "最大日志条数") private Integer logsMax; /** * DEBUG */ + @Schema(description = "DEBUG") private Boolean debug; /** * 仅启用主rss摸鱼检测 */ + @Schema(description = "仅启用主RSS摸鱼检测") private Boolean procrastinatingMasterOnly; /** * 代理是否开启 */ + @Schema(description = "代理是否开启") private Boolean proxy; /** * 代理host */ + @Schema(description = "代理 host") private String proxyHost; /** * 代理端口 */ + @Schema(description = "代理端口") private Integer proxyPort; /** * 代理用户名 */ + @Schema(description = "代理用户名") private String proxyUsername; /** * 代理密码 */ + @Schema(description = "代理密码") private String proxyPassword; /** * 同时下载数量限制 */ + @Schema(description = "同时下载数量限制") private Integer downloadCount; /** * 登录信息 */ + @Schema(description = "登录信息") private Login login; /** * 禁止多端登录 */ + @Schema(description = "禁止多端登录") private Boolean multiLoginForbidden; /** * 登录有效时间/小时 */ + @Schema(description = "登录有效时间(小时)") private Integer loginEffectiveHours; /** * 全局排除 */ + @Schema(description = "全局排除") private List exclude; /** * 默认导入全局排除 */ + @Schema(description = "默认导入全局排除") private Boolean importExclude; /** * 默认启用全局排除 */ + @Schema(description = "默认启用全局排除") private Boolean enabledExclude; /** * BGM日语标题 */ + @Schema(description = "BGM日语标题") private Boolean bgmJpName; /** * tmdb */ + @Schema(description = "启用 TMDB") private Boolean tmdb; /** * 获取标题时带有tmdbId */ + @Schema(description = "标题带 TMDB ID") private Boolean tmdbId; /** * 剧集标题是否支持plex命名方式 */ + @Schema(description = "Plex 命名方式") private Boolean tmdbIdPlexMode; /** * tmdb 语言 */ + @Schema(description = "TMDB 语言") private String tmdbLanguage; /** * 获取罗马音 */ + @Schema(description = "获取罗马音") private Boolean tmdbRomaji; /** * 开启ip白名单 */ + @Schema(description = "开启 IP 白名单") private Boolean ipWhitelist; /** * ip白名单 */ + @Schema(description = "IP 白名单") private String ipWhitelistStr; /** * 显示已下载视频列表 */ + @Schema(description = "显示已下载视频列表") private Boolean showPlaylist; /** * 检测遗漏集数 */ + @Schema(description = "检测遗漏集数") private Boolean omit; /** @@ -319,295 +381,354 @@ public class Config implements Serializable { *

* INPUT or AUTO */ + @Schema(description = "BGM Token 类型") private BgmTokenTypeEnum bgmTokenType; /** * bgmToken */ + @Schema(description = "BGM Token") private String bgmToken; /** * bgmAppID */ + @Schema(description = "BGM App ID") private String bgmAppID; /** * bgmAppID */ + @Schema(description = "BGM App Secret") private String bgmAppSecret; /** * bgmRefreshToken */ + @Schema(description = "BGM Refresh Token") private String bgmRefreshToken; /** * bgmRedirectUri */ + @Schema(description = "BGM Redirect URI") private String bgmRedirectUri; /** * api key */ + @Schema(description = "API Key") private String apiKey; /** * 按星期展示 */ + @Schema(description = "按星期展示") private Boolean weekShow; /** * 只下载最新集 */ + @Schema(description = "只下载最新集") private Boolean downloadNew; /** * 仅允许内网ip访问 */ + @Schema(description = "仅允许内网 IP 访问") private Boolean innerIP; /** * 重命名模版 */ + @Schema(description = "重命名模版") private String renameTemplate; /** * 重命名时剔除 年份 如 (2024) */ + @Schema(description = "重命名剔除年份") private Boolean renameDelYear; /** * 重命名时剔除 tmdbId [tmdbid=242143] */ + @Schema(description = "重命名剔除 TMDB ID") private Boolean renameDelTmdbId; /** * 校验登录IP */ + @Schema(description = "校验登录 IP") private Boolean verifyLoginIp; /** * 自动更新 trackers */ + @Schema(description = "自动更新 trackers") private Boolean autoTrackersUpdate; /** * Trackers更新地址 */ + @Schema(description = "Trackers 更新地址") private String trackersUpdateUrls; /** * 消息模版 */ + @Schema(description = "消息模版") private String notificationTemplate; /** * 自动更新 */ + @Schema(description = "自动更新") private Boolean autoUpdate; /** * 版本 */ + @Schema(description = "版本") private String version; /** * 获取BGM封面图片质量 */ + @Schema(description = "BGM 封面图片质量") private String bgmImage; /** * 自定义CSS */ + @Schema(description = "自定义 CSS") private String customCss; /** * 自定义JS */ + @Schema(description = "自定义 JS") private String customJs; /** * 自定义集数获取规则 */ + @Schema(description = "自定义集数获取规则") private Boolean customEpisode; /** * 自定义集数获取规则 */ + @Schema(description = "自定义集数获取规则表达式") private String customEpisodeStr; /** * 自定义集数获取规则 groupIndex */ + @Schema(description = "自定义集数获取规则 groupIndex") private Integer customEpisodeGroupIndex; /** * OpenList driver */ + @Schema(description = "OpenList Driver") private String provider; /** * 添加行订阅是是否开启自动上传 */ + @Schema(description = "新增订阅自动上传") private Boolean upload; /** * 上传速度限制 */ + @Schema(description = "上传速度限制") private Long upLimit; /** * 下载速度限制 */ + @Schema(description = "下载速度限制") private Long dlLimit; /** * 捐赠过期时间 */ + @Schema(description = "捐赠过期时间") private Long expirationTime; /** * 爱发电订单号 */ + @Schema(description = "爱发电订单号") private String outTradeNo; /** * 捐赠或试用是否过期 */ + @Schema(description = "捐赠或试用是否过期") private Boolean verifyExpirationTime; /** * 试用 */ + @Schema(description = "试用") private Boolean tryOut; /** * 摸鱼 */ + @Schema(description = "摸鱼") private Boolean procrastinating; /** * 摸鱼天数 */ + @Schema(description = "摸鱼天数") private Integer procrastinatingDay; /** * github 加速 */ + @Schema(description = "GitHub 加速") private String github; /** * 自定义github加速 */ + @Schema(description = "自定义 GitHub 加速") private Boolean customGithub; /** * 自定义github加速网址 */ + @Schema(description = "自定义 GitHub 加速地址") private String customGithubUrl; /** * github Token */ + @Schema(description = "GitHub Token") private String githubToken; /** * 开启 OpenList 列表刷新 */ + @Schema(description = "开启 OpenList 列表刷新") private Boolean alistRefresh; /** * OpenList 刷新延迟 */ + @Schema(description = "OpenList 刷新延迟") private Long alistRefreshDelayed; /** * 自动更新总集数信息 */ + @Schema(description = "自动更新总集数信息") private Boolean updateTotalEpisodeNumber; /** * 强制更新总集数信息 */ + @Schema(description = "强制更新总集数信息") private Boolean forceUpdateTotalEpisodeNumber; /** * OpenList 离线超时 分钟 */ + @Schema(description = "OpenList 离线超时(分钟)") private Integer alistDownloadTimeout; /** * OpenList 下载重试次数 */ + @Schema(description = "OpenList 下载重试次数") private Long alistDownloadRetryNumber; /** * 设置备份 */ + @Schema(description = "设置备份") private Boolean configBackup; /** * 备份天数 */ + @Schema(description = "备份天数") private Integer configBackupDay; /** * 展示最后更新时间 */ + @Schema(description = "展示最后更新时间") private Boolean showLastDownloadTime; /** * 番剧完结迁移 */ + @Schema(description = "番剧完结迁移") private Boolean completed; /** * 番剧完结迁移位置 */ + @Schema(description = "番剧完结迁移位置") private String completedPathTemplate; /** * 通知 */ + @Schema(description = "通知配置列表") private List notificationConfigList; /** * 添加订阅时自动复制主rss至备用rss */ + @Schema(description = "添加订阅时复制主RSS至备用") private Boolean copyMasterToStandby; /** * 排序方式 */ + @Schema(description = "排序方式") private SortTypeEnum sortType; /** * 代理列表 */ + @Schema(description = "代理列表") private String proxyList; /** * 刮削开关 */ + @Schema(description = "刮削开关") private Boolean scrape; /** * 重名的订阅将允许被替换 */ + @Schema(description = "重名订阅允许替换") private Boolean replace; /** * 最大文件名长度 不包含后缀 如: .mkv .mp4 */ + @Schema(description = "最大文件名长度(不含后缀)") private Integer maxFileNameLength; /** * 限制尝试次数 */ + @Schema(description = "限制尝试次数") private Boolean limitLoginAttempts; /** * 构建信息 */ + @Schema(description = "构建信息") private String buildInfo; } diff --git a/ani-rss-core/src/main/java/ani/rss/entity/CustomTmdbConfig.java b/ani-rss-application/src/main/java/ani/rss/entity/CustomTmdbConfig.java similarity index 93% rename from ani-rss-core/src/main/java/ani/rss/entity/CustomTmdbConfig.java rename to ani-rss-application/src/main/java/ani/rss/entity/CustomTmdbConfig.java index 5351ae22..689be0cd 100644 --- a/ani-rss-core/src/main/java/ani/rss/entity/CustomTmdbConfig.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/CustomTmdbConfig.java @@ -2,8 +2,10 @@ package ani.rss.entity; import ani.rss.util.other.ConfigUtil; import cn.hutool.core.util.StrUtil; +import io.swagger.v3.oas.annotations.media.Schema; import wushuo.tmdb.api.entity.TmdbConfig; +@Schema(description = "自定义 TMDB 配置") public class CustomTmdbConfig extends TmdbConfig { public final static Config CONFIG = ConfigUtil.CONFIG; diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/EmbyViews.java b/ani-rss-application/src/main/java/ani/rss/entity/EmbyViews.java similarity index 62% rename from ani-rss-domain/src/main/java/ani/rss/entity/EmbyViews.java rename to ani-rss-application/src/main/java/ani/rss/entity/EmbyViews.java index c8f529a5..3ba06965 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/EmbyViews.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/EmbyViews.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -10,7 +11,10 @@ import java.io.Serializable; */ @Data @Accessors(chain = true) +@Schema(description = "Emby 媒体库") public class EmbyViews implements Serializable { + @Schema(description = "id") private String id; + @Schema(description = "名称") private String name; } diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/EmbyWebHook.java b/ani-rss-application/src/main/java/ani/rss/entity/EmbyWebHook.java similarity index 74% rename from ani-rss-domain/src/main/java/ani/rss/entity/EmbyWebHook.java rename to ani-rss-application/src/main/java/ani/rss/entity/EmbyWebHook.java index 2f5108af..4dbe2925 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/EmbyWebHook.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/EmbyWebHook.java @@ -1,6 +1,7 @@ package ani.rss.entity; import com.google.gson.annotations.SerializedName; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -11,33 +12,43 @@ import java.io.Serializable; */ @Data @Accessors(chain = true) +@Schema(description = "EmbyWebHook") public class EmbyWebHook implements Serializable { @SerializedName(value = "title", alternate = "Title") + @Schema(description = "标题") private String title; @SerializedName(value = "description", alternate = "Description") + @Schema(description = "描述") private String description; @SerializedName(value = "date", alternate = "Date") + @Schema(description = "日期") private String date; @SerializedName(value = "event", alternate = "Event") + @Schema(description = "事件") private String event; @SerializedName(value = "severity", alternate = "Severity") + @Schema(description = "严重级别") private String severity; @SerializedName(value = "user", alternate = "User") + @Schema(description = "用户信息") private User user; @SerializedName(value = "server", alternate = "Server") + @Schema(description = "服务器信息") private Server server; @SerializedName(value = "item", alternate = "Item") + @Schema(description = "项目信息") private Item item; @SerializedName(value = "playbackInfo", alternate = "PlaybackInfo") + @Schema(description = "播放信息") private PlaybackInfo playbackInfo; /** @@ -45,23 +56,27 @@ public class EmbyWebHook implements Serializable { */ @Data @Accessors(chain = true) + @Schema(description = "项目信息") public static class Item implements Serializable { /** * 文件路径 */ @SerializedName(value = "path", alternate = "Path") + @Schema(description = "文件路径") private String path; /** * 剧集名 */ @SerializedName(value = "seriesName", alternate = "SeriesName") + @Schema(description = "剧集名") private String seriesName; /** * 文件名 */ @SerializedName(value = "fileName", alternate = "FileName") + @Schema(description = "文件名") private String fileName; } @@ -70,17 +85,20 @@ public class EmbyWebHook implements Serializable { */ @Data @Accessors(chain = true) + @Schema(description = "用户信息") public static class User implements Serializable { /** * 用户 Id */ @SerializedName(value = "id", alternate = "Id") + @Schema(description = "用户 Id") private String id; /** * 用户名称 */ @SerializedName(value = "name", alternate = "Name") + @Schema(description = "用户名称") private String name; } @@ -89,23 +107,27 @@ public class EmbyWebHook implements Serializable { */ @Data @Accessors(chain = true) + @Schema(description = "服务器信息") public static class Server implements Serializable { /** * 服务器 Id */ @SerializedName(value = "id", alternate = "Id") + @Schema(description = "服务器 Id") private String id; /** * 服务器名称 */ @SerializedName(value = "name", alternate = "Name") + @Schema(description = "服务器名称") private String name; /** * 服务器版本号 */ @SerializedName(value = "version", alternate = "Version") + @Schema(description = "服务器版本号") private String version; } @@ -114,11 +136,13 @@ public class EmbyWebHook implements Serializable { */ @Data @Accessors(chain = true) + @Schema(description = "播放信息") public static class PlaybackInfo implements Serializable { /** * 是否播放完成 */ @SerializedName(value = "playedToCompletion", alternate = "PlayedToCompletion") + @Schema(description = "是否播放完成") private Boolean playedToCompletion; } diff --git a/ani-rss-application/src/main/java/ani/rss/entity/Global.java b/ani-rss-application/src/main/java/ani/rss/entity/Global.java new file mode 100644 index 00000000..35454b6f --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/entity/Global.java @@ -0,0 +1,21 @@ +package ani.rss.entity; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@Data +@Accessors(chain = true) +@Schema(description = "全局变量") +public class Global implements Serializable { + public static List ARGS = new ArrayList<>(); + + public static final ThreadLocal REQUEST = new ThreadLocal<>(); + public static final ThreadLocal RESPONSE = new ThreadLocal<>(); +} diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/Item.java b/ani-rss-application/src/main/java/ani/rss/entity/Item.java similarity index 63% rename from ani-rss-domain/src/main/java/ani/rss/entity/Item.java rename to ani-rss-application/src/main/java/ani/rss/entity/Item.java index b2a4d61f..66b2b75a 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/Item.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/Item.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -11,59 +12,71 @@ import java.util.Date; */ @Data @Accessors(chain = true) +@Schema(description = "下载项") public class Item implements Serializable { /** * 标题 */ + @Schema(description = "标题") private String title; /** * 重命名 */ + @Schema(description = "重命名") private String reName; /** * 种子 */ + @Schema(description = "种子") private String torrent; /** * infoHash */ + @Schema(description = "infoHash") private String infoHash; /** * 集数 */ + @Schema(description = "集数") private Double episode; /** * 大小 */ + @Schema(description = "大小") private String size; /** * 大小 */ + @Schema(description = "大小") private Long length; /** * 本地已存在 */ + @Schema(description = "本地已存在") private Boolean local; /** * 主 rss */ + @Schema(description = "主 rss") private Boolean master; /** * 字幕组 */ + @Schema(description = "字幕组") private String subgroup; /** * 发布时间 */ + @Schema(description = "发布时间") private Date pubDate; } diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/Log.java b/ani-rss-application/src/main/java/ani/rss/entity/Log.java similarity index 65% rename from ani-rss-domain/src/main/java/ani/rss/entity/Log.java rename to ani-rss-application/src/main/java/ani/rss/entity/Log.java index ddced99e..c635a0d9 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/Log.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/Log.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -10,25 +11,30 @@ import java.io.Serializable; */ @Data @Accessors(chain = true) +@Schema(description = "日志") public class Log implements Serializable { /** * 日志信息 */ + @Schema(description = "日志信息") private String message; /** * 日志级别 */ + @Schema(description = "日志级别") private String level; /** * 类路径 */ + @Schema(description = "类路径") private String loggerName; /** * 线程名 */ + @Schema(description = "线程名") private String threadName; } diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/Login.java b/ani-rss-application/src/main/java/ani/rss/entity/Login.java similarity index 65% rename from ani-rss-domain/src/main/java/ani/rss/entity/Login.java rename to ani-rss-application/src/main/java/ani/rss/entity/Login.java index e3b36e4b..a47127d9 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/Login.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/Login.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -10,21 +11,26 @@ import java.io.Serializable; */ @Data @Accessors(chain = true) +@Schema(description = "登录") public class Login implements Serializable { /** * 用户名 */ + @Schema(description = "用户名") private String username; /** * 密码 */ + @Schema(description = "密码") private String password; /** * ip */ + @Schema(description = "ip") private String ip; /** * key */ + @Schema(description = "key") private String key; } diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/Mikan.java b/ani-rss-application/src/main/java/ani/rss/entity/Mikan.java similarity index 83% rename from ani-rss-domain/src/main/java/ani/rss/entity/Mikan.java rename to ani-rss-application/src/main/java/ani/rss/entity/Mikan.java index a3327af4..8034aac0 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/Mikan.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/Mikan.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -14,12 +15,16 @@ import java.util.Set; */ @Data @Accessors(chain = true) +@Schema(description = "mikan") public class Mikan implements Serializable { + @Schema(description = "季度信息") private List seasons; + @Schema(description = "星期信息") private List items; + @Schema(description = "总番剧数") private Integer totalItem; /** @@ -31,10 +36,12 @@ public class Mikan implements Serializable { /** * 年 */ + @Schema(description = "年") private Integer year; /** * 季度 */ + @Schema(description = "季度") private String season; private Boolean select; } @@ -48,10 +55,12 @@ public class Mikan implements Serializable { /** * 星期 */ + @Schema(description = "星期") private String label; /** * 番剧 */ + @Schema(description = "番剧") private List items; } diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/MikanInfo.java b/ani-rss-application/src/main/java/ani/rss/entity/MikanInfo.java similarity index 65% rename from ani-rss-domain/src/main/java/ani/rss/entity/MikanInfo.java rename to ani-rss-application/src/main/java/ani/rss/entity/MikanInfo.java index 9c49b211..0f4eccea 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/MikanInfo.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/MikanInfo.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -11,45 +12,54 @@ import java.util.List; */ @Data @Accessors(chain = true) +@Schema(description = "mikan 番剧信息") public class MikanInfo implements Serializable { /** * 番剧 id */ + @Schema(description = "番剧 id") private String bangumiId; /** * 封面 */ + @Schema(description = "封面") private String cover; /** * mikan url */ + @Schema(description = "mikan url") private String url; /** * 已存在 */ + @Schema(description = "已存在") private Boolean exists; /** * 评分 */ + @Schema(description = "评分") private Double score; /** * 标题 */ + @Schema(description = "标题") private String title; /** * BGM */ + @Schema(description = "BGM") private String bgmUrl; /** * 字幕组 */ + @Schema(description = "字幕组") private List groups; } diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/NotificationConfig.java b/ani-rss-application/src/main/java/ani/rss/entity/NotificationConfig.java similarity index 72% rename from ani-rss-domain/src/main/java/ani/rss/entity/NotificationConfig.java rename to ani-rss-application/src/main/java/ani/rss/entity/NotificationConfig.java index 23e0e9c4..f826a68c 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/NotificationConfig.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/NotificationConfig.java @@ -3,6 +3,7 @@ package ani.rss.entity; import ani.rss.enums.NotificationStatusEnum; import ani.rss.enums.NotificationTypeEnum; import ani.rss.enums.ServerChanTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -12,215 +13,268 @@ import java.util.List; @Data @Accessors(chain = true) +@Schema(description = "通知配置") public class NotificationConfig implements Serializable { /** * 启用 */ + @Schema(description = "启用") private Boolean enable; /** * 重试次数 */ + @Schema(description = "重试次数") private Integer retry; /** * 备注 */ + @Schema(description = "备注") private String comment; /** * 通知模版 */ + @Schema(description = "通知模版") private String notificationTemplate; /** * 通知类型 */ + @Schema(description = "通知类型") private NotificationTypeEnum notificationType; /** * 邮箱 smtp */ + @Schema(description = "邮箱 SMTP") private String mailSMTPHost; /** * 邮箱 端口 */ + @Schema(description = "邮箱 端口") private Integer mailSMTPPort; /** * 邮箱 发件人 */ + @Schema(description = "邮箱 发件人") private String mailFrom; /** * 邮箱 密码 */ + @Schema(description = "邮箱 密码") private String mailPassword; /** * 邮箱 SSL */ + @Schema(description = "邮箱 SSL") private Boolean mailSSLEnable; /** * 邮箱 TLS */ + @Schema(description = "邮箱 TLS") private Boolean mailTLSEnable; /** * 邮箱 收件人 */ + @Schema(description = "邮箱 收件人") private String mailAddressee; /** * 邮箱 发送图片 */ + @Schema(description = "邮箱 发送图片") private Boolean mailImage; /** * server酱类型:server酱和server酱3 */ + @Schema(description = "server酱类型") private ServerChanTypeEnum serverChanType; /** * server酱 sendKey */ + @Schema(description = "server酱 sendKey") private String serverChanSendKey; /** * server酱3 apiUrl */ + @Schema(description = "server酱3 apiUrl") private String serverChan3ApiUrl; /** * server酱 标题事件 */ + @Schema(description = "server酱 标题事件") private Boolean serverChanTitleAction; /** * 系统通知 */ + @Schema(description = "系统通知") private Boolean systemMsg; /** * telegram bot token */ + @Schema(description = "telegram bot token") private String telegramBotToken; /** * telegram chat_id */ + @Schema(description = "telegram chat_id") private String telegramChatId; /** * telegram topic id */ + @Schema(description = "telegram topic id") private Integer telegramTopicId; /** * telegram Api Host */ + @Schema(description = "telegram Api Host") private String telegramApiHost; /** * telegram 发送图片 */ + @Schema(description = "telegram 发送图片") private Boolean telegramImage; /** * telegram 格式 */ + @Schema(description = "telegram 格式") private String telegramFormat; /** * webHookMethod */ + @Schema(description = "WebHook 方法") private String webHookMethod; /** * webHookUrl */ + @Schema(description = "WebHook 地址") private String webHookUrl; /** * webHookHeader */ + @Schema(description = "WebHook Header") private String webHookHeader; /** * webHookBody */ + @Schema(description = "WebHook Body") private String webHookBody; /** * emby扫描媒体库 */ + @Schema(description = "Emby 扫描媒体库") private Boolean embyRefresh; /** * emby地址 */ + @Schema(description = "Emby 地址") private String embyHost; /** * emby api密钥 */ + @Schema(description = "Emby API 密钥") private String embyApiKey; /** * emby扫描媒体库 */ + @Schema(description = "Emby 扫描媒体库 ID 列表") private List embyRefreshViewIds; /** * emby延迟扫描 */ + @Schema(description = "Emby 延迟扫描") private Long embyDelayed; + @Schema(description = "Shell 命令") private String shell; /** * 存活限制 秒 */ + @Schema(description = "存活限制 秒") private Integer aliveLimit; /** * 文件移动目标位置 */ + @Schema(description = "文件移动目标位置") private String fileMoveTarget; /** * 文件移动目标位置 OVA */ + @Schema(description = "文件移动目标位置 OVA") private String fileMoveOvaTarget; /** * 文件移动时删除旧的同集视频 */ + @Schema(description = "文件移动时删除旧的同集视频") private Boolean fileMoveDeleteOldEpisode; + /** + * 文件移动 复制模式 + */ + @Schema(description = "文件移动 复制模式") + private Boolean fileMoveCopyModel; + /** * OpenList Host */ + @Schema(description = "OpenList Host") private String openListUploadHost; /** * OpenList ApiKey */ + @Schema(description = "OpenList ApiKey") private String openListUploadApiKey; /** * OpenList 上传位置 */ + @Schema(description = "OpenList 上传位置") private String openListUploadPath; /** * OpenList OVA/剧场版 上传位置 */ + @Schema(description = "OpenList OVA/剧场版 上传位置") private String openListUploadOvaPath; /** * 上传完成后删除本地文件 */ + @Schema(description = "上传完成后删除本地文件") private Boolean openListUploadDeleteLocalFile; /** * 删除同及文件 */ + @Schema(description = "删除同集文件") private Boolean openListUploadDeleteOldEpisode; /** * 通知 状态 */ + @Schema(description = "通知状态") private List statusList; /** * 顺序 */ + @Schema(description = "顺序") private Long sort; public static NotificationConfig createNotificationConfig() { @@ -288,7 +342,8 @@ public class NotificationConfig implements Serializable { notificationConfig .setFileMoveTarget("/CD2/115/Media/番剧/${title}/Season ${season}") .setFileMoveOvaTarget("/CD2/115/Media/剧场版/${title}") - .setFileMoveDeleteOldEpisode(false); + .setFileMoveDeleteOldEpisode(false) + .setFileMoveCopyModel(false); // OpenList notificationConfig diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/OpenListFileInfo.java b/ani-rss-application/src/main/java/ani/rss/entity/OpenListFileInfo.java similarity index 59% rename from ani-rss-domain/src/main/java/ani/rss/entity/OpenListFileInfo.java rename to ani-rss-application/src/main/java/ani/rss/entity/OpenListFileInfo.java index efdf7429..439082b0 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/OpenListFileInfo.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/OpenListFileInfo.java @@ -1,6 +1,7 @@ package ani.rss.entity; import com.google.gson.annotations.SerializedName; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -9,13 +10,20 @@ import java.util.Date; @Data @Accessors(chain = true) +@Schema(description = "OpenList 文件信息") public class OpenListFileInfo implements Serializable { + @Schema(description = "名称") private String name; + @Schema(description = "大小") private Long size; @SerializedName(value = "isDir", alternate = "is_dir") + @Schema(description = "是否为目录") private Boolean isDir; + @Schema(description = "修改时间") private Date modified; + @Schema(description = "创建时间") private Date created; + @Schema(description = "路径") private String path; } diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/PlayItem.java b/ani-rss-application/src/main/java/ani/rss/entity/PlayItem.java similarity index 58% rename from ani-rss-domain/src/main/java/ani/rss/entity/PlayItem.java rename to ani-rss-application/src/main/java/ani/rss/entity/PlayItem.java index 85e2c2b2..ce7e01a3 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/PlayItem.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/PlayItem.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -11,52 +12,67 @@ import java.util.List; */ @Data @Accessors(chain = true) +@Schema(description = "视频列表") public class PlayItem implements Serializable { /** * 显示标题 */ + @Schema(description = "显示标题") private String title; /** * 路径+文件名 bash64 */ + @Schema(description = "路径+文件名 base64") private String filename; /** * 文件名 */ + @Schema(description = "文件名") private String name; /** * 最后修改日期 */ + @Schema(description = "最后修改日期") private Long lastModify; /** * 集数 */ + @Schema(description = "集数") private Double episode; /** * 文件大小 MB */ + @Schema(description = "文件大小 MB") private String size; /** * 扩展名 */ + @Schema(description = "扩展名") private String extName; + @Schema(description = "字幕列表") private List subtitles; @Data @Accessors(chain = true) + @Schema(description = "字幕") public static class Subtitles { + @Schema(description = "字幕 HTML") private String html; + @Schema(description = "字幕名") private String name; + @Schema(description = "字幕地址") private String url; + @Schema(description = "字幕内容") private String content; + @Schema(description = "字幕类型") private String type; } diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/ProxyTest.java b/ani-rss-application/src/main/java/ani/rss/entity/ProxyTest.java similarity index 66% rename from ani-rss-domain/src/main/java/ani/rss/entity/ProxyTest.java rename to ani-rss-application/src/main/java/ani/rss/entity/ProxyTest.java index 42993b00..558c677d 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/ProxyTest.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/ProxyTest.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -10,14 +11,17 @@ import java.io.Serializable; */ @Data @Accessors(chain = true) +@Schema(description = "代理测试响应体") public class ProxyTest implements Serializable { /** * 状态码 */ + @Schema(description = "状态码") private Integer status; /** * 耗时 */ + @Schema(description = "耗时") private Long time; } diff --git a/ani-rss-application/src/main/java/ani/rss/entity/Result.java b/ani-rss-application/src/main/java/ani/rss/entity/Result.java new file mode 100644 index 00000000..c03483b6 --- /dev/null +++ b/ani-rss-application/src/main/java/ani/rss/entity/Result.java @@ -0,0 +1,95 @@ +package ani.rss.entity; + +import cn.hutool.core.text.StrFormatter; +import cn.hutool.http.HttpStatus; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.function.Consumer; + +/** + * 返回包装体 + * + * @param + */ +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@Schema(description = "返回包装体") +public class Result implements Serializable { + @Schema(description = "状态码", defaultValue = "200") + private Integer code; + @Schema(description = "响应消息") + private String message; + @Schema(description = "数据", defaultValue = "null") + private T data; + @Schema(description = "时间戳", defaultValue = "current time") + private Long t = System.currentTimeMillis(); + + public static Result success() { + return new Result() + .setCode(HttpStatus.HTTP_OK) + .setMessage("success"); + } + + public static Result success(T data) { + return new Result() + .setCode(HttpStatus.HTTP_OK) + .setMessage("success") + .setData(data); + } + + public static Result success(String message) { + return new Result() + .setCode(HttpStatus.HTTP_OK) + .setMessage(message); + } + + public static Result success(String message, Object... argArray) { + return new Result() + .setCode(HttpStatus.HTTP_OK) + .setMessage(StrFormatter.format(message, argArray)); + } + + public static Result success(Consumer> consumer) { + Result success = success(); + consumer.accept(success); + return success; + } + + public static Result error() { + return new Result() + .setCode(HttpStatus.HTTP_INTERNAL_ERROR) + .setMessage("error"); + } + + public static Result error(T data) { + return new Result() + .setCode(HttpStatus.HTTP_INTERNAL_ERROR) + .setMessage("error") + .setData(data); + } + + public static Result error(String message) { + return new Result() + .setCode(HttpStatus.HTTP_INTERNAL_ERROR) + .setMessage(message); + } + + public static Result error(String message, Object... argArray) { + return new Result() + .setCode(HttpStatus.HTTP_INTERNAL_ERROR) + .setMessage(StrFormatter.format(message, argArray)); + } + + public static Result error(Consumer> consumer) { + Result error = error(); + consumer.accept(error); + return error; + } +} diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/StandbyRss.java b/ani-rss-application/src/main/java/ani/rss/entity/StandbyRss.java similarity index 64% rename from ani-rss-domain/src/main/java/ani/rss/entity/StandbyRss.java rename to ani-rss-application/src/main/java/ani/rss/entity/StandbyRss.java index b4d7c7e8..2a1db397 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/StandbyRss.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/StandbyRss.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -7,17 +8,21 @@ import java.io.Serializable; @Data @Accessors(chain = true) +@Schema(description = "备用rss") public class StandbyRss implements Serializable { /** * 字幕组 */ + @Schema(description = "字幕组") private String label; /** * url */ + @Schema(description = "url") private String url; /** * 剧集偏移 */ + @Schema(description = "剧集偏移") private Integer offset; } diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/TorrentsInfo.java b/ani-rss-application/src/main/java/ani/rss/entity/TorrentsInfo.java similarity index 81% rename from ani-rss-domain/src/main/java/ani/rss/entity/TorrentsInfo.java rename to ani-rss-application/src/main/java/ani/rss/entity/TorrentsInfo.java index fecad46b..55df158f 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/TorrentsInfo.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/TorrentsInfo.java @@ -1,6 +1,7 @@ package ani.rss.entity; import cn.hutool.core.util.NumberUtil; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -13,72 +14,87 @@ import java.util.function.Supplier; */ @Data @Accessors(chain = true) +@Schema(description = "种子信息") public class TorrentsInfo implements Serializable { + @Schema(description = "id") private String id; /** * hash */ + @Schema(description = "hash") private String hash; /** * 名称 */ + @Schema(description = "名称") private String name; /** * 状态 */ + @Schema(description = "状态") private State state; /** * 标签 */ + @Schema(description = "标签") private List tags; /** * 磁链 */ + @Schema(description = "磁链") private String magnet; /** * 已下载的大小 */ + @Schema(description = "已下载的大小") private Long completed; /** * 大小 */ + @Schema(description = "大小") private Long size; /** * 进度 */ + @Schema(description = "进度") private Double progress; /** * 大小 */ + @Schema(description = "大小(字符串)") private String sizeStr; /** * 时间 */ + @Schema(description = "时间") private String dateStr; /** * 下载位置 */ + @Schema(description = "下载位置") private String downloadDir; /** * 种子地址 */ + @Schema(description = "种子地址") private String torrent; /** * 文件列表 */ + @Schema(description = "文件列表") private Supplier> files; public TorrentsInfo progress(long completed, long size) { diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/TryOut.java b/ani-rss-application/src/main/java/ani/rss/entity/TryOut.java similarity index 56% rename from ani-rss-domain/src/main/java/ani/rss/entity/TryOut.java rename to ani-rss-application/src/main/java/ani/rss/entity/TryOut.java index 0212fd78..9d72949c 100644 --- a/ani-rss-domain/src/main/java/ani/rss/entity/TryOut.java +++ b/ani-rss-application/src/main/java/ani/rss/entity/TryOut.java @@ -1,5 +1,6 @@ package ani.rss.entity; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; @@ -7,9 +8,14 @@ import java.io.Serializable; @Data @Accessors(chain = true) +@Schema(description = "试用配置") public class TryOut implements Serializable { + @Schema(description = "启用") private Boolean enable; + @Schema(description = "续期") private Boolean renewal; + @Schema(description = "天数") private Integer day; + @Schema(description = "消息") private String message; } diff --git a/ani-rss-domain/src/main/java/ani/rss/enums/BgmTokenTypeEnum.java b/ani-rss-application/src/main/java/ani/rss/enums/BgmTokenTypeEnum.java similarity index 100% rename from ani-rss-domain/src/main/java/ani/rss/enums/BgmTokenTypeEnum.java rename to ani-rss-application/src/main/java/ani/rss/enums/BgmTokenTypeEnum.java diff --git a/ani-rss-domain/src/main/java/ani/rss/enums/NotificationStatusEnum.java b/ani-rss-application/src/main/java/ani/rss/enums/NotificationStatusEnum.java similarity index 100% rename from ani-rss-domain/src/main/java/ani/rss/enums/NotificationStatusEnum.java rename to ani-rss-application/src/main/java/ani/rss/enums/NotificationStatusEnum.java diff --git a/ani-rss-domain/src/main/java/ani/rss/enums/NotificationTypeEnum.java b/ani-rss-application/src/main/java/ani/rss/enums/NotificationTypeEnum.java similarity index 100% rename from ani-rss-domain/src/main/java/ani/rss/enums/NotificationTypeEnum.java rename to ani-rss-application/src/main/java/ani/rss/enums/NotificationTypeEnum.java diff --git a/ani-rss-domain/src/main/java/ani/rss/enums/ServerChanTypeEnum.java b/ani-rss-application/src/main/java/ani/rss/enums/ServerChanTypeEnum.java similarity index 100% rename from ani-rss-domain/src/main/java/ani/rss/enums/ServerChanTypeEnum.java rename to ani-rss-application/src/main/java/ani/rss/enums/ServerChanTypeEnum.java diff --git a/ani-rss-domain/src/main/java/ani/rss/enums/SortTypeEnum.java b/ani-rss-application/src/main/java/ani/rss/enums/SortTypeEnum.java similarity index 100% rename from ani-rss-domain/src/main/java/ani/rss/enums/SortTypeEnum.java rename to ani-rss-application/src/main/java/ani/rss/enums/SortTypeEnum.java diff --git a/ani-rss-domain/src/main/java/ani/rss/enums/StringEnum.java b/ani-rss-application/src/main/java/ani/rss/enums/StringEnum.java similarity index 100% rename from ani-rss-domain/src/main/java/ani/rss/enums/StringEnum.java rename to ani-rss-application/src/main/java/ani/rss/enums/StringEnum.java diff --git a/ani-rss-domain/src/main/java/ani/rss/enums/TorrentsTags.java b/ani-rss-application/src/main/java/ani/rss/enums/TorrentsTags.java similarity index 100% rename from ani-rss-domain/src/main/java/ani/rss/enums/TorrentsTags.java rename to ani-rss-application/src/main/java/ani/rss/enums/TorrentsTags.java diff --git a/ani-rss-core/src/main/java/ani/rss/exception/ResultException.java b/ani-rss-application/src/main/java/ani/rss/exception/ResultException.java similarity index 91% rename from ani-rss-core/src/main/java/ani/rss/exception/ResultException.java rename to ani-rss-application/src/main/java/ani/rss/exception/ResultException.java index 7b604a6f..fc710cbd 100644 --- a/ani-rss-core/src/main/java/ani/rss/exception/ResultException.java +++ b/ani-rss-application/src/main/java/ani/rss/exception/ResultException.java @@ -17,5 +17,5 @@ import java.io.Serializable; @EqualsAndHashCode(callSuper = true) public class ResultException extends RuntimeException implements Serializable { - public final Result result; + private final Result result; } diff --git a/ani-rss-core/src/main/java/ani/rss/list/FixedSizeLinkedList.java b/ani-rss-application/src/main/java/ani/rss/list/FixedSizeLinkedList.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/list/FixedSizeLinkedList.java rename to ani-rss-application/src/main/java/ani/rss/list/FixedSizeLinkedList.java diff --git a/ani-rss-core/src/main/java/ani/rss/notification/BaseNotification.java b/ani-rss-application/src/main/java/ani/rss/notification/BaseNotification.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/notification/BaseNotification.java rename to ani-rss-application/src/main/java/ani/rss/notification/BaseNotification.java diff --git a/ani-rss-core/src/main/java/ani/rss/notification/EmbyRefreshNotification.java b/ani-rss-application/src/main/java/ani/rss/notification/EmbyRefreshNotification.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/notification/EmbyRefreshNotification.java rename to ani-rss-application/src/main/java/ani/rss/notification/EmbyRefreshNotification.java diff --git a/ani-rss-core/src/main/java/ani/rss/notification/FileMoveNotification.java b/ani-rss-application/src/main/java/ani/rss/notification/FileMoveNotification.java similarity index 93% rename from ani-rss-core/src/main/java/ani/rss/notification/FileMoveNotification.java rename to ani-rss-application/src/main/java/ani/rss/notification/FileMoveNotification.java index 1e17fcf8..f850c86b 100644 --- a/ani-rss-core/src/main/java/ani/rss/notification/FileMoveNotification.java +++ b/ani-rss-application/src/main/java/ani/rss/notification/FileMoveNotification.java @@ -19,6 +19,8 @@ import java.util.stream.Collectors; @Slf4j public class FileMoveNotification implements BaseNotification { + private NotificationConfig notificationConfig; + /** * 测试 * @@ -50,6 +52,8 @@ public class FileMoveNotification implements BaseNotification { return true; } + this.notificationConfig = notificationConfig; + // 首先就要深度克隆 防止影响原订阅设置 ani = ObjectUtil.clone(ani); @@ -88,6 +92,8 @@ public class FileMoveNotification implements BaseNotification { } public void startMoveOva(String src, String target) { + Boolean copyModel = notificationConfig.getFileMoveCopyModel(); + for (File file : FileUtils.listFiles(src)) { if (!file.isFile()) { continue; @@ -96,12 +102,17 @@ public class FileMoveNotification implements BaseNotification { log.info("OVA/剧场版 文件移动: {} => {}", file, target); FileUtil.copy(file, new File(target), true); + if (copyModel) { + continue; + } // 复制后再删除 确保不会中途失败 FileUtil.del(file); } } public void startMove(String src, String target) { + Boolean copyModel = notificationConfig.getFileMoveCopyModel(); + for (File file : FileUtils.listFileList(src)) { if (!file.isFile()) { // 过滤掉文件夹 @@ -120,6 +131,10 @@ public class FileMoveNotification implements BaseNotification { log.info("文件移动: {} => {}", file, target); FileUtil.copy(file, new File(target), true); + if (copyModel) { + continue; + } + // 复制后再删除 确保不会中途失败 FileUtil.del(file); } diff --git a/ani-rss-core/src/main/java/ani/rss/notification/MailNotification.java b/ani-rss-application/src/main/java/ani/rss/notification/MailNotification.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/notification/MailNotification.java rename to ani-rss-application/src/main/java/ani/rss/notification/MailNotification.java diff --git a/ani-rss-core/src/main/java/ani/rss/notification/OpenListUploadNotification.java b/ani-rss-application/src/main/java/ani/rss/notification/OpenListUploadNotification.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/notification/OpenListUploadNotification.java rename to ani-rss-application/src/main/java/ani/rss/notification/OpenListUploadNotification.java diff --git a/ani-rss-core/src/main/java/ani/rss/notification/ServerChanNotification.java b/ani-rss-application/src/main/java/ani/rss/notification/ServerChanNotification.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/notification/ServerChanNotification.java rename to ani-rss-application/src/main/java/ani/rss/notification/ServerChanNotification.java diff --git a/ani-rss-core/src/main/java/ani/rss/notification/ShellNotification.java b/ani-rss-application/src/main/java/ani/rss/notification/ShellNotification.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/notification/ShellNotification.java rename to ani-rss-application/src/main/java/ani/rss/notification/ShellNotification.java diff --git a/ani-rss-core/src/main/java/ani/rss/notification/SystemNotification.java b/ani-rss-application/src/main/java/ani/rss/notification/SystemNotification.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/notification/SystemNotification.java rename to ani-rss-application/src/main/java/ani/rss/notification/SystemNotification.java diff --git a/ani-rss-core/src/main/java/ani/rss/notification/TelegramNotification.java b/ani-rss-application/src/main/java/ani/rss/notification/TelegramNotification.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/notification/TelegramNotification.java rename to ani-rss-application/src/main/java/ani/rss/notification/TelegramNotification.java diff --git a/ani-rss-core/src/main/java/ani/rss/notification/WebHookNotification.java b/ani-rss-application/src/main/java/ani/rss/notification/WebHookNotification.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/notification/WebHookNotification.java rename to ani-rss-application/src/main/java/ani/rss/notification/WebHookNotification.java diff --git a/ani-rss-core/src/main/java/ani/rss/service/AniService.java b/ani-rss-application/src/main/java/ani/rss/service/AniService.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/service/AniService.java rename to ani-rss-application/src/main/java/ani/rss/service/AniService.java diff --git a/ani-rss-core/src/main/java/ani/rss/service/ClearService.java b/ani-rss-application/src/main/java/ani/rss/service/ClearService.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/service/ClearService.java rename to ani-rss-application/src/main/java/ani/rss/service/ClearService.java diff --git a/ani-rss-core/src/main/java/ani/rss/service/DownloadService.java b/ani-rss-application/src/main/java/ani/rss/service/DownloadService.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/service/DownloadService.java rename to ani-rss-application/src/main/java/ani/rss/service/DownloadService.java diff --git a/ani-rss-core/src/main/java/ani/rss/service/NfoGenerator.java b/ani-rss-application/src/main/java/ani/rss/service/NfoGenerator.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/service/NfoGenerator.java rename to ani-rss-application/src/main/java/ani/rss/service/NfoGenerator.java diff --git a/ani-rss-core/src/main/java/ani/rss/service/ScrapeService.java b/ani-rss-application/src/main/java/ani/rss/service/ScrapeService.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/service/ScrapeService.java rename to ani-rss-application/src/main/java/ani/rss/service/ScrapeService.java diff --git a/ani-rss-core/src/main/java/ani/rss/service/TaskService.java b/ani-rss-application/src/main/java/ani/rss/service/TaskService.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/service/TaskService.java rename to ani-rss-application/src/main/java/ani/rss/service/TaskService.java diff --git a/ani-rss-core/src/main/java/ani/rss/task/BgmTask.java b/ani-rss-application/src/main/java/ani/rss/task/BgmTask.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/task/BgmTask.java rename to ani-rss-application/src/main/java/ani/rss/task/BgmTask.java diff --git a/ani-rss-core/src/main/java/ani/rss/task/RenameTask.java b/ani-rss-application/src/main/java/ani/rss/task/RenameTask.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/task/RenameTask.java rename to ani-rss-application/src/main/java/ani/rss/task/RenameTask.java diff --git a/ani-rss-core/src/main/java/ani/rss/task/RssTask.java b/ani-rss-application/src/main/java/ani/rss/task/RssTask.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/task/RssTask.java rename to ani-rss-application/src/main/java/ani/rss/task/RssTask.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/basic/CidrRangeChecker.java b/ani-rss-application/src/main/java/ani/rss/util/basic/CidrRangeChecker.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/basic/CidrRangeChecker.java rename to ani-rss-application/src/main/java/ani/rss/util/basic/CidrRangeChecker.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/basic/HttpReq.java b/ani-rss-application/src/main/java/ani/rss/util/basic/HttpReq.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/basic/HttpReq.java rename to ani-rss-application/src/main/java/ani/rss/util/basic/HttpReq.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/basic/HttpRequestPlus.java b/ani-rss-application/src/main/java/ani/rss/util/basic/HttpRequestPlus.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/basic/HttpRequestPlus.java rename to ani-rss-application/src/main/java/ani/rss/util/basic/HttpRequestPlus.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/basic/LogUtil.java b/ani-rss-application/src/main/java/ani/rss/util/basic/LogUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/basic/LogUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/basic/LogUtil.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/basic/RenameCacheUtil.java b/ani-rss-application/src/main/java/ani/rss/util/basic/RenameCacheUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/basic/RenameCacheUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/basic/RenameCacheUtil.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/AfdianUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/AfdianUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/other/AfdianUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/AfdianUtil.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/AniListUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/AniListUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/other/AniListUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/AniListUtil.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/AniUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/AniUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/other/AniUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/AniUtil.java diff --git a/ani-rss-web/src/main/java/ani/rss/web/util/AuthUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/AuthUtil.java similarity index 83% rename from ani-rss-web/src/main/java/ani/rss/web/util/AuthUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/AuthUtil.java index 5da715d4..7ee5ef9a 100644 --- a/ani-rss-web/src/main/java/ani/rss/web/util/AuthUtil.java +++ b/ani-rss-application/src/main/java/ani/rss/util/other/AuthUtil.java @@ -1,23 +1,22 @@ -package ani.rss.web.util; +package ani.rss.util.other; +import ani.rss.annotation.Auth; +import ani.rss.auth.enums.AuthType; import ani.rss.commons.CacheUtils; import ani.rss.commons.ExceptionUtils; import ani.rss.commons.GsonStatic; import ani.rss.entity.Config; +import ani.rss.entity.Global; import ani.rss.entity.Login; import ani.rss.entity.Result; import ani.rss.exception.ResultException; -import ani.rss.util.other.ConfigUtil; -import ani.rss.web.annotation.Auth; -import ani.rss.web.auth.enums.AuthType; import cn.hutool.core.text.StrFormatter; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; -import cn.hutool.http.server.HttpServerRequest; -import com.sun.net.httpserver.HttpExchange; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import java.util.HashMap; @@ -32,7 +31,7 @@ import java.util.function.Function; */ @Slf4j public class AuthUtil { - private static final Map> MAP = new HashMap<>(); + private static final Map> MAP = new HashMap<>(); static { resetKey(); @@ -98,9 +97,8 @@ public class AuthUtil { */ public static String getIp() { try { - HttpServerRequest request = ServerUtil.REQUEST.get(); - HttpExchange httpExchange = (HttpExchange) ReflectUtil.getFieldValue(request, "httpExchange"); - return httpExchange.getRemoteAddress().getAddress().getHostAddress(); + HttpServletRequest request = Global.REQUEST.get(); + return request.getRemoteAddr(); } catch (Exception e) { String message = ExceptionUtils.getMessage(e); log.error(message, e); @@ -115,7 +113,7 @@ public class AuthUtil { * @param auth * @return */ - public static Boolean test(HttpServerRequest request, Auth auth) { + public static Boolean test(HttpServletRequest request, Auth auth) { limitLoginAttempts(false); if (!auth.value()) { // 不进行校验 @@ -138,10 +136,10 @@ public class AuthUtil { * @param authType * @return */ - public static Boolean test(HttpServerRequest request, AuthType authType) { - Class> clazz = authType.getClazz(); + public static Boolean test(HttpServletRequest request, AuthType authType) { + Class> clazz = authType.getClazz(); String name = clazz.getName(); - Function function = MAP.get(name); + Function function = MAP.get(name); if (Objects.isNull(function)) { function = ReflectUtil.newInstance(clazz); MAP.put(name, function); diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/BgmUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/BgmUtil.java similarity index 97% rename from ani-rss-core/src/main/java/ani/rss/util/other/BgmUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/BgmUtil.java index 9b8a2040..0c0eaba4 100644 --- a/ani-rss-core/src/main/java/ani/rss/util/other/BgmUtil.java +++ b/ani-rss-application/src/main/java/ani/rss/util/other/BgmUtil.java @@ -4,6 +4,7 @@ import ani.rss.commons.CacheUtils; import ani.rss.commons.GsonStatic; import ani.rss.entity.Ani; import ani.rss.entity.BgmInfo; +import ani.rss.entity.BgmMe; import ani.rss.entity.Config; import ani.rss.enums.BgmTokenTypeEnum; import ani.rss.service.DownloadService; @@ -248,7 +249,7 @@ public class BgmUtil { }); } - public static JsonObject me() { + public static BgmMe me() { Config config = ConfigUtil.CONFIG; String bgmToken = config.getBgmToken(); Assert.notBlank(bgmToken, "BgmToken 未填写"); @@ -257,17 +258,17 @@ public class BgmUtil { String me = CacheUtils.get(key); if (StrUtil.isNotBlank(me)) { - return GsonStatic.fromJson(me, JsonObject.class); + return GsonStatic.fromJson(me, BgmMe.class); } - JsonObject jsonObject = setToken(HttpReq.get(host + "/v0/me")) + BgmMe bgmMe = setToken(HttpReq.get(host + "/v0/me")) .thenFunction(res -> { HttpReq.assertStatus(res); - return GsonStatic.fromJson(res.body(), JsonObject.class); + return GsonStatic.fromJson(res.body(), BgmMe.class); }); - CacheUtils.put(key, GsonStatic.toJson(jsonObject), TimeUnit.MINUTES.toMillis(10)); - return jsonObject; + CacheUtils.put(key, GsonStatic.toJson(bgmMe), TimeUnit.MINUTES.toMillis(10)); + return bgmMe; } /** @@ -276,13 +277,12 @@ public class BgmUtil { * @return */ public static String username() { - JsonObject me = me(); + BgmMe me = me(); return Opt.of(me) - .map(o -> o.get("username")) + .map(BgmMe::getUsername) .filter(Objects::nonNull) - .map(JsonElement::getAsString) .filter(StrUtil::isNotBlank) - .orElse(String.valueOf(me.get("id").getAsInt())); + .orElse(String.valueOf(me.getId())); } /** @@ -582,11 +582,11 @@ public class BgmUtil { * * @return */ - public static Long getExpiresDays() { + public static Integer getExpiresDays() { Config config = ConfigUtil.CONFIG; String bgmToken = config.getBgmToken(); if (StrUtil.isBlank(bgmToken)) { - return 0L; + return 0; } long expires = HttpReq.post("https://bgm.tv/oauth/token_status") .form("access_token", bgmToken) @@ -598,10 +598,10 @@ public class BgmUtil { long currentTimeMillis = System.currentTimeMillis(); - long days = 0; + int days = 0; if (expires > currentTimeMillis) { - days = TimeUnit.MILLISECONDS.toDays(expires - currentTimeMillis); + days = Math.toIntExact(TimeUnit.MILLISECONDS.toDays(expires - currentTimeMillis)); } return days; } diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/ConfigUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/ConfigUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/other/ConfigUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/ConfigUtil.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/EmbyUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/EmbyUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/other/EmbyUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/EmbyUtil.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/ItemsUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/ItemsUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/other/ItemsUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/ItemsUtil.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/MenuUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/MenuUtil.java similarity index 94% rename from ani-rss-core/src/main/java/ani/rss/util/other/MenuUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/MenuUtil.java index 299158e2..fdf63818 100644 --- a/ani-rss-core/src/main/java/ani/rss/util/other/MenuUtil.java +++ b/ani-rss-application/src/main/java/ani/rss/util/other/MenuUtil.java @@ -3,6 +3,7 @@ package ani.rss.util.other; import ani.rss.entity.Global; import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.extra.spring.SpringUtil; import lombok.extern.slf4j.Slf4j; import javax.swing.*; @@ -51,7 +52,6 @@ public class MenuUtil { double uiScaleX = tx.getScaleX(); double uiScaleY = tx.getScaleY(); - SystemTray tray = SystemTray.getSystemTray(); trayIcon = new TrayIcon( Toolkit.getDefaultToolkit().getImage( @@ -83,7 +83,8 @@ public class MenuUtil { webui.addActionListener(e -> { if (Desktop.isDesktopSupported()) { try { - Desktop.getDesktop().browse(new URL("http://127.0.0.1:" + Global.HTTP_PORT).toURI()); + String port = SpringUtil.getProperty("server.port"); + Desktop.getDesktop().browse(new URL("http://127.0.0.1:" + port).toURI()); } catch (Exception ex) { log.error("打开webui失败", ex); } @@ -107,7 +108,8 @@ public class MenuUtil { clicked.set(false); // 直接打开webui try { - Desktop.getDesktop().browse(new URL("http://127.0.0.1:" + Global.HTTP_PORT).toURI()); + String port = SpringUtil.getProperty("server.port"); + Desktop.getDesktop().browse(new URL("http://127.0.0.1:" + port).toURI()); } catch (Exception ex) { log.error("打开webui失败", ex); } diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/MikanUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/MikanUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/other/MikanUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/MikanUtil.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/NotificationUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/NotificationUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/other/NotificationUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/NotificationUtil.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/RenameUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/RenameUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/other/RenameUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/RenameUtil.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/TmdbUtils.java b/ani-rss-application/src/main/java/ani/rss/util/other/TmdbUtils.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/other/TmdbUtils.java rename to ani-rss-application/src/main/java/ani/rss/util/other/TmdbUtils.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/TorrentUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/TorrentUtil.java similarity index 100% rename from ani-rss-core/src/main/java/ani/rss/util/other/TorrentUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/TorrentUtil.java diff --git a/ani-rss-core/src/main/java/ani/rss/util/other/UpdateUtil.java b/ani-rss-application/src/main/java/ani/rss/util/other/UpdateUtil.java similarity index 97% rename from ani-rss-core/src/main/java/ani/rss/util/other/UpdateUtil.java rename to ani-rss-application/src/main/java/ani/rss/util/other/UpdateUtil.java index 88db092f..4fa37c63 100644 --- a/ani-rss-core/src/main/java/ani/rss/util/other/UpdateUtil.java +++ b/ani-rss-application/src/main/java/ani/rss/util/other/UpdateUtil.java @@ -76,10 +76,10 @@ public class UpdateUtil { .setUpdate(VersionComparator.INSTANCE.compare(latest, version) > 0) .setLatest(latest) .setMarkdownBody(jsonObject.get("markdown").getAsString()); - String filename = "ani-rss-jar-with-dependencies.jar"; + String filename = "ani-rss.jar"; File jar = MavenUtils.getJar(); if ("exe".equals(FileUtil.extName(jar))) { - filename = "ani-rss-launcher.exe"; + filename = "ani-rss.exe"; } String downloadUrl = StrFormatter.format("https://github.com/wushuo894/ani-rss/releases/download/v{}/{}", latest, filename); about.setDownloadUrl(downloadUrl); diff --git a/ani-rss-application/src/main/resources/application.yaml b/ani-rss-application/src/main/resources/application.yaml new file mode 100644 index 00000000..33f567d0 --- /dev/null +++ b/ani-rss-application/src/main/resources/application.yaml @@ -0,0 +1,26 @@ +spring: + application: + name: ani-rss-application + web: + resources: + static-locations: classpath:/META-INF/dist + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + servlet: + multipart: + max-request-size: 50MB + max-file-size: 50MB + enabled: true + jmx: + enabled: false +server: + port: 7789 + address: 0.0.0.0 + +springdoc: + swagger-ui: + enabled: ${SWAGGER_ENABLED:false} + url: /v3/api-docs.yaml + api-docs: + enabled: ${SWAGGER_ENABLED:false} \ No newline at end of file diff --git a/ani-rss-core/src/main/resources/aria2/addTorrent.json b/ani-rss-application/src/main/resources/aria2/addTorrent.json similarity index 100% rename from ani-rss-core/src/main/resources/aria2/addTorrent.json rename to ani-rss-application/src/main/resources/aria2/addTorrent.json diff --git a/ani-rss-core/src/main/resources/aria2/addUri.json b/ani-rss-application/src/main/resources/aria2/addUri.json similarity index 100% rename from ani-rss-core/src/main/resources/aria2/addUri.json rename to ani-rss-application/src/main/resources/aria2/addUri.json diff --git a/ani-rss-core/src/main/resources/aria2/changeGlobalOption.json b/ani-rss-application/src/main/resources/aria2/changeGlobalOption.json similarity index 100% rename from ani-rss-core/src/main/resources/aria2/changeGlobalOption.json rename to ani-rss-application/src/main/resources/aria2/changeGlobalOption.json diff --git a/ani-rss-core/src/main/resources/aria2/getGlobalStat.json b/ani-rss-application/src/main/resources/aria2/getGlobalStat.json similarity index 100% rename from ani-rss-core/src/main/resources/aria2/getGlobalStat.json rename to ani-rss-application/src/main/resources/aria2/getGlobalStat.json diff --git a/ani-rss-core/src/main/resources/aria2/removeDownloadResult.json b/ani-rss-application/src/main/resources/aria2/removeDownloadResult.json similarity index 100% rename from ani-rss-core/src/main/resources/aria2/removeDownloadResult.json rename to ani-rss-application/src/main/resources/aria2/removeDownloadResult.json diff --git a/ani-rss-core/src/main/resources/aria2/tellActive.json b/ani-rss-application/src/main/resources/aria2/tellActive.json similarity index 100% rename from ani-rss-core/src/main/resources/aria2/tellActive.json rename to ani-rss-application/src/main/resources/aria2/tellActive.json diff --git a/ani-rss-core/src/main/resources/aria2/tellStopped.json b/ani-rss-application/src/main/resources/aria2/tellStopped.json similarity index 100% rename from ani-rss-core/src/main/resources/aria2/tellStopped.json rename to ani-rss-application/src/main/resources/aria2/tellStopped.json diff --git a/ani-rss-core/src/main/resources/aria2/tellWaiting.json b/ani-rss-application/src/main/resources/aria2/tellWaiting.json similarity index 100% rename from ani-rss-core/src/main/resources/aria2/tellWaiting.json rename to ani-rss-application/src/main/resources/aria2/tellWaiting.json diff --git a/ani-rss-core/src/main/resources/logback-template.xml b/ani-rss-application/src/main/resources/logback-template.xml similarity index 100% rename from ani-rss-core/src/main/resources/logback-template.xml rename to ani-rss-application/src/main/resources/logback-template.xml diff --git a/ani-rss-application/src/main/resources/logback.xml b/ani-rss-application/src/main/resources/logback.xml new file mode 100644 index 00000000..bfc1cf5c --- /dev/null +++ b/ani-rss-application/src/main/resources/logback.xml @@ -0,0 +1,14 @@ + + + + + + %d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n + + + + + + + + diff --git a/ani-rss-web/src/main/resources/template.html b/ani-rss-application/src/main/resources/template.html similarity index 100% rename from ani-rss-web/src/main/resources/template.html rename to ani-rss-application/src/main/resources/template.html diff --git a/ani-rss-core/src/main/resources/transmission/torrent-add.json b/ani-rss-application/src/main/resources/transmission/torrent-add.json similarity index 100% rename from ani-rss-core/src/main/resources/transmission/torrent-add.json rename to ani-rss-application/src/main/resources/transmission/torrent-add.json diff --git a/ani-rss-core/src/main/resources/transmission/torrent-get.json b/ani-rss-application/src/main/resources/transmission/torrent-get.json similarity index 100% rename from ani-rss-core/src/main/resources/transmission/torrent-get.json rename to ani-rss-application/src/main/resources/transmission/torrent-get.json diff --git a/ani-rss-core/src/main/resources/transmission/torrent-remove.json b/ani-rss-application/src/main/resources/transmission/torrent-remove.json similarity index 100% rename from ani-rss-core/src/main/resources/transmission/torrent-remove.json rename to ani-rss-application/src/main/resources/transmission/torrent-remove.json diff --git a/ani-rss-core/src/main/resources/transmission/torrent-rename-path.json b/ani-rss-application/src/main/resources/transmission/torrent-rename-path.json similarity index 100% rename from ani-rss-core/src/main/resources/transmission/torrent-rename-path.json rename to ani-rss-application/src/main/resources/transmission/torrent-rename-path.json diff --git a/ani-rss-core/src/main/resources/transmission/torrent-set-location.json b/ani-rss-application/src/main/resources/transmission/torrent-set-location.json similarity index 100% rename from ani-rss-core/src/main/resources/transmission/torrent-set-location.json rename to ani-rss-application/src/main/resources/transmission/torrent-set-location.json diff --git a/ani-rss-core/src/main/resources/transmission/torrent-set.json b/ani-rss-application/src/main/resources/transmission/torrent-set.json similarity index 100% rename from ani-rss-core/src/main/resources/transmission/torrent-set.json rename to ani-rss-application/src/main/resources/transmission/torrent-set.json diff --git a/ani-rss-application/src/test/java/ani/rss/AniRssApplicationTests.java b/ani-rss-application/src/test/java/ani/rss/AniRssApplicationTests.java new file mode 100644 index 00000000..aabedd1a --- /dev/null +++ b/ani-rss-application/src/test/java/ani/rss/AniRssApplicationTests.java @@ -0,0 +1,13 @@ +package ani.rss; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AniRssApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/ani-rss-commons/pom.xml b/ani-rss-commons/pom.xml deleted file mode 100644 index f9a4cebf..00000000 --- a/ani-rss-commons/pom.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - 4.0.0 - - ani.rss - ani-rss - 2.5.10 - - - ani-rss-commons - - - 17 - 17 - UTF-8 - - - - - com.google.code.gson - gson - - - ch.qos.logback - logback-classic - - - cn.hutool - hutool-all - - - diff --git a/ani-rss-commons/src/main/java/ani/rss/CommonsMain.java b/ani-rss-commons/src/main/java/ani/rss/CommonsMain.java deleted file mode 100644 index fae8855b..00000000 --- a/ani-rss-commons/src/main/java/ani/rss/CommonsMain.java +++ /dev/null @@ -1,19 +0,0 @@ -package ani.rss; - -import cn.hutool.core.util.ClassUtil; -import lombok.extern.slf4j.Slf4j; - -import java.util.Set; - -@Slf4j -public class CommonsMain { - public static void main(String[] args) { - Set> classSet = ClassUtil.scanPackage("ani.rss.commons"); - for (Class clazz : classSet) { - if (clazz.isMemberClass()) { - continue; - } - log.info(clazz.getName()); - } - } -} diff --git a/ani-rss-core/pom.xml b/ani-rss-core/pom.xml deleted file mode 100644 index fe882185..00000000 --- a/ani-rss-core/pom.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - 4.0.0 - - ani.rss - ani-rss - 2.5.10 - - - ani-rss-core - - - 17 - 17 - UTF-8 - - - - - ani.rss - ani-rss-domain - - - org.eclipse - bittorrent - - - org.xerial - sqlite-jdbc - - - com.sun.mail - jakarta.mail - - - ch.qos.logback - logback-classic - - - io.github.biezhi - TinyPinyin - - - org.jsoup - jsoup - - - diff --git a/ani-rss-core/src/main/java/ani/rss/CoreMain.java b/ani-rss-core/src/main/java/ani/rss/CoreMain.java deleted file mode 100644 index 11ed890d..00000000 --- a/ani-rss-core/src/main/java/ani/rss/CoreMain.java +++ /dev/null @@ -1,12 +0,0 @@ -package ani.rss; - -import ani.rss.commons.MavenUtils; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class CoreMain { - public static void main(String[] args) { - String version = MavenUtils.getVersion(); - log.info("version: {}", version); - } -} diff --git a/ani-rss-core/src/test/java/TestEbmlReader.java b/ani-rss-core/src/test/java/TestEbmlReader.java deleted file mode 100644 index f6aa72cb..00000000 --- a/ani-rss-core/src/test/java/TestEbmlReader.java +++ /dev/null @@ -1,34 +0,0 @@ -import cn.hutool.core.lang.Assert; -import com.matthewn4444.ebml.EBMLReader; -import com.matthewn4444.ebml.subtitles.Subtitles; -import lombok.Cleanup; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.util.ArrayList; - -@Slf4j -public class TestEbmlReader { - @Test - public void test() throws IOException { - @Cleanup - EBMLReader reader = new EBMLReader("/Users/wushuo/Movies/test/[LoliHouse] 蓝色管弦乐 S02E11.mkv"); - Assert.isTrue(reader.readHeader(), "文件格式错误"); - - reader.readTracks(); - reader.readCues(); - - for (int i = 0; i < reader.getCuesCount(); i++) { - reader.readSubtitlesInCueFrame(i); - } - - ArrayList subtitles = reader.getSubtitles(); - for (Subtitles subtitle : subtitles) { - String name = subtitle.getName(); - String language = subtitle.getLanguage(); - String presentableName = subtitle.getPresentableName(); - log.info("{} {} {}", name, language, presentableName); - } - } -} diff --git a/ani-rss-domain/pom.xml b/ani-rss-domain/pom.xml deleted file mode 100644 index 2d8dda85..00000000 --- a/ani-rss-domain/pom.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - 4.0.0 - - ani.rss - ani-rss - 2.5.10 - - - ani-rss-domain - - - 17 - 17 - UTF-8 - - - - - ebml-reader - https://raw.github.com/wushuo894/EBMLReader/mvn-repo - - - tmdb-api - https://raw.github.com/wushuo894/tmdb-api/mvn-repo - - - - - - ebml.reader - ebml-reader - - - wushuo.tmdb.api - tmdb-api - - - ani.rss - ani-rss-commons - - - - diff --git a/ani-rss-domain/src/main/java/ani/rss/ApiMain.java b/ani-rss-domain/src/main/java/ani/rss/ApiMain.java deleted file mode 100644 index 20796790..00000000 --- a/ani-rss-domain/src/main/java/ani/rss/ApiMain.java +++ /dev/null @@ -1,19 +0,0 @@ -package ani.rss; - -import cn.hutool.core.util.ClassUtil; -import lombok.extern.slf4j.Slf4j; - -import java.util.Set; - -@Slf4j -public class ApiMain { - public static void main(String[] args) { - Set> classSet = ClassUtil.scanPackage("ani.rss"); - for (Class clazz : classSet) { - if (clazz.isMemberClass()) { - continue; - } - log.info(clazz.getName()); - } - } -} diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/Global.java b/ani-rss-domain/src/main/java/ani/rss/entity/Global.java deleted file mode 100644 index 18bdb3d2..00000000 --- a/ani-rss-domain/src/main/java/ani/rss/entity/Global.java +++ /dev/null @@ -1,17 +0,0 @@ -package ani.rss.entity; - -import lombok.Data; -import lombok.experimental.Accessors; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; - -@Data -@Accessors(chain = true) -public class Global implements Serializable { - public static List ARGS = new ArrayList<>(); - - public static String HTTP_HOST = "0.0.0.0"; - public static String HTTP_PORT = "7789"; -} diff --git a/ani-rss-domain/src/main/java/ani/rss/entity/Result.java b/ani-rss-domain/src/main/java/ani/rss/entity/Result.java deleted file mode 100644 index 99a972ea..00000000 --- a/ani-rss-domain/src/main/java/ani/rss/entity/Result.java +++ /dev/null @@ -1,51 +0,0 @@ -package ani.rss.entity; - -import cn.hutool.http.HttpStatus; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; - -import java.io.Serializable; - -/** - * 返回包装体 - * - * @param - */ -@Data -@Accessors(chain = true) -@AllArgsConstructor -@NoArgsConstructor -public class Result implements Serializable { - private Integer code; - private String message; - private T data; - private Long t; - - public static Result success() { - return new Result() - .setCode(HttpStatus.HTTP_OK) - .setMessage("success"); - } - - public static Result success(T data) { - return new Result() - .setCode(HttpStatus.HTTP_OK) - .setMessage("success") - .setData(data); - } - - public static Result error() { - return new Result() - .setCode(HttpStatus.HTTP_INTERNAL_ERROR) - .setMessage("error"); - } - - public static Result error(T data) { - return new Result() - .setCode(HttpStatus.HTTP_INTERNAL_ERROR) - .setMessage("error") - .setData(data); - } -} diff --git a/ani-rss-ui/.gitignore b/ani-rss-ui/.gitignore index a28c28fb..f594e22a 100644 --- a/ani-rss-ui/.gitignore +++ b/ani-rss-ui/.gitignore @@ -23,3 +23,4 @@ dist-ssr *.sln *.sw? node +pnpm-lock.yaml \ No newline at end of file diff --git a/ani-rss-ui/package.json b/ani-rss-ui/package.json index f9a03fe3..0f7f5a08 100644 --- a/ani-rss-ui/package.json +++ b/ani-rss-ui/package.json @@ -19,11 +19,11 @@ "element-plus": "^2.13.2", "markdown-it": "^14.1.1", "markdown-it-github-alerts": "^1.0.1", - "vue": "^3.5.28" + "vue": "^3.5.29" }, "devDependencies": { "@vitejs/plugin-vue": "^6.0.4", - "shiki": "^3.22.0", + "shiki": "^3.23.0", "terser": "^5.46.0", "unplugin-auto-import": "^21.0.0", "unplugin-vue-components": "^31.0.0", diff --git a/ani-rss-ui/pom.xml b/ani-rss-ui/pom.xml index 820c8a83..a2d49ac7 100644 --- a/ani-rss-ui/pom.xml +++ b/ani-rss-ui/pom.xml @@ -6,7 +6,7 @@ ani.rss ani-rss - 2.5.10 + 3.0.0 ani-rss-ui @@ -92,7 +92,7 @@ - ${project.build.outputDirectory}/dist + ${project.build.outputDirectory}/META-INF/dist diff --git a/ani-rss-ui/src/Login.vue b/ani-rss-ui/src/Login.vue index 24b785e4..dcfcf4f8 100644 --- a/ani-rss-ui/src/Login.vue +++ b/ani-rss-ui/src/Login.vue @@ -56,7 +56,7 @@ import {onMounted, ref} from "vue"; import CryptoJS from "crypto-js" -import api from "./js/api.js"; +import * as http from "./js/http.js"; import {Key} from "@element-plus/icons-vue"; import {ElMessage} from "element-plus"; import {authorization, rememberThePassword} from "@/js/global.js"; @@ -82,7 +82,7 @@ let login = () => { loading.value = true - api.post('api/login', { + http.login({ username: user.value.username, password: CryptoJS['MD5'](user.value.password).toString() }) @@ -110,8 +110,7 @@ let test = () => { if (authorization.value) { return } - fetch('api/test') - .then(res => res.json()) + http.testIpWhitelist() .then(res => { if (res.code === 200) { authorization.value = new Date().getTime() + ''; diff --git a/ani-rss-ui/src/bgm-oauth-callback/App.vue b/ani-rss-ui/src/bgm-oauth-callback/App.vue index 99f5b791..212312e2 100644 --- a/ani-rss-ui/src/bgm-oauth-callback/App.vue +++ b/ani-rss-ui/src/bgm-oauth-callback/App.vue @@ -28,15 +28,15 @@ - {{ me.reg_time }} + {{ me.regTime }} - - {{ me.expires_days }} 天 + + {{ me.expiresDays }} 天 - {{ me.expires_days }} 天 + {{ me.expiresDays }} 天 @@ -70,6 +70,7 @@ import {onMounted, ref} from 'vue' import {init} from "@/js/global.js"; import api from "@/js/api.js"; +import * as http from "@/js/http.js"; const type = ref('success') const text = ref('') @@ -81,19 +82,9 @@ const close = () => { } const loadMe = async () => { - return api.post('api/bgm?type=me') + return http.meBgm() .then(res => { me.value = res.data - const formatter = new Intl.DateTimeFormat('zh-CN', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false - }) - me.value.reg_time = formatter.format(new Date(me.value.reg_time)) }); } diff --git a/ani-rss-ui/src/config/About.vue b/ani-rss-ui/src/config/About.vue index ca221575..4a2e548d 100644 --- a/ani-rss-ui/src/config/About.vue +++ b/ani-rss-ui/src/config/About.vue @@ -111,6 +111,7 @@ import 'markdown-it-github-alerts/styles/github-colors-dark-media.css' import 'markdown-it-github-alerts/styles/github-base.css' import {authorization} from "@/js/global.js"; +import * as http from "@/js/http.js"; let md = markdownit({ html: true, @@ -129,7 +130,7 @@ const actionLoading = ref(false) const stop = (status) => { actionLoading.value = true - api.post("api/stop?status=" + status) + http.stop(status) .then(res => { ElMessage.success(res.message) setTimeout(() => { @@ -144,7 +145,7 @@ const stop = (status) => { const update = () => { actionLoading.value = true - api.post("api/update") + http.update() .then(res => { ElMessage.success(res.message) setTimeout(() => { @@ -165,7 +166,7 @@ const about = ref({ }) onMounted(() => { - api.get('api/about') + http.about() .then(res => { about.value = res.data }) diff --git a/ani-rss-ui/src/config/Afdian.vue b/ani-rss-ui/src/config/Afdian.vue index 92cd345a..538a1c27 100644 --- a/ani-rss-ui/src/config/Afdian.vue +++ b/ani-rss-ui/src/config/Afdian.vue @@ -135,10 +135,10 @@