aaaa
This commit is contained in:
commit
b80852e7a0
|
@ -0,0 +1,39 @@
|
|||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
.kotlin
|
||||
|
||||
### IntelliJ 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
|
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="KotlinMultiplatform">
|
||||
<allAvailableExecutables />
|
||||
</component>
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="25" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.dengqn.app</groupId>
|
||||
<artifactId>lingyin-api</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>25</maven.compiler.source>
|
||||
<maven.compiler.target>25</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>3.5.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>3.5.5</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.21.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.40</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>25</source>
|
||||
<target>25</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,23 @@
|
|||
package com.dengqn.app.lingyinapi;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dengqn
|
||||
* @since 2025/9/23 18:45
|
||||
*/
|
||||
@Slf4j
|
||||
@SpringBootApplication
|
||||
public class Main {
|
||||
|
||||
void main(String[] args) {
|
||||
try {
|
||||
SpringApplication.run(Main.class, args);
|
||||
} catch (Throwable e) {
|
||||
log.info("error on app starting: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.dengqn.app.lingyinapi.apis;
|
||||
|
||||
import com.dengqn.app.lingyinapi.beans.SeedListItem;
|
||||
import com.dengqn.app.lingyinapi.config.CookieConfig;
|
||||
import com.dengqn.app.lingyinapi.html.HtmlTool;
|
||||
import com.dengqn.app.lingyinapi.http.HttpTool;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 获取种子分页情况
|
||||
*
|
||||
* @author dengqn
|
||||
* @since 2025/9/23 19:11
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/seed")
|
||||
public class Seed {
|
||||
|
||||
private OkHttpClient okHttpClient;
|
||||
|
||||
public Seed(OkHttpClient okHttpClient) {
|
||||
this.okHttpClient = okHttpClient;
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
public ResponseEntity<List<SeedListItem>> getSeedPage(
|
||||
@RequestParam(required = false, defaultValue = "0", name = "inclbookmarked") String inclbookmarked,
|
||||
@RequestParam(required = false, defaultValue = "1", name = "incldead") String incldead,
|
||||
@RequestParam(required = false, defaultValue = "0", name = "spstate") String spstate,
|
||||
@RequestParam(required = false, defaultValue = "1", name = "page") String page
|
||||
) throws IOException {
|
||||
|
||||
String url = "https://pt.soulvoice.club/special.php?inclbookmarked=%s&incldead=%s&spstate=%s&page=%s"
|
||||
.formatted(inclbookmarked, incldead, spstate, page);
|
||||
String html = HttpTool.getHTML(url, okHttpClient);
|
||||
log.info("html:{}", html);
|
||||
Document doc = Jsoup.parse(html);
|
||||
List<SeedListItem> listItem = HtmlTool.getSeedListItem(doc);
|
||||
return ResponseEntity.ok(listItem);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.dengqn.app.lingyinapi.beans;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dengqn
|
||||
* @since 2025/9/25 18:22
|
||||
*/
|
||||
public record SeedListItem(String seedCover, String seedName, String seedUrl, boolean isFree, boolean isHitAndRun,
|
||||
String downloadUrl, String time, String size, String seeders, String views,
|
||||
boolean isAnonymous) {
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.dengqn.app.lingyinapi.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dengqn
|
||||
* @since 2025/9/23 19:10
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "site.config.cookies")
|
||||
public class CookieConfig {
|
||||
private String secureLogin;
|
||||
private String securePass;
|
||||
private String secureUid;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package com.dengqn.app.lingyinapi.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dengqn
|
||||
* @since 2025/9/24 11:45
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class HttpClientConfig {
|
||||
|
||||
private CookieConfig cookieConfig;
|
||||
|
||||
public HttpClientConfig(CookieConfig cookieConfig) {
|
||||
this.cookieConfig = cookieConfig;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OkHttpClient okHttpClient() {
|
||||
return new OkHttpClient.Builder()
|
||||
.cookieJar(new CookieJar() {
|
||||
@Override
|
||||
public void saveFromResponse(@NotNull HttpUrl httpUrl, @NotNull List<Cookie> list) {
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<Cookie> loadForRequest(@NotNull HttpUrl httpUrl) {
|
||||
return List.of(
|
||||
new Cookie.Builder().domain("pt.soulvoice.club").name("c_secure_login").value(cookieConfig.getSecureLogin()).build(),
|
||||
new Cookie.Builder().domain("pt.soulvoice.club").name("c_secure_uid").value(cookieConfig.getSecureUid()).build(),
|
||||
new Cookie.Builder().domain("pt.soulvoice.club").name("c_secure_pass").value(cookieConfig.getSecurePass()).build()
|
||||
);
|
||||
}
|
||||
})
|
||||
.callTimeout(Duration.of(10, ChronoUnit.MINUTES))
|
||||
.addInterceptor(new HTTPClientInterceptor())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
class HTTPClientInterceptor implements Interceptor {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Response intercept(@NotNull Chain chain) throws IOException {
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("\n------------------------\n");
|
||||
query.append("URL:\n");
|
||||
query.append("[").append(chain.request().method()).append("]-").append(chain.request().url()).append("\n");
|
||||
Headers headers = chain.request().headers();
|
||||
query.append("\nHEADER:\n");
|
||||
for (String name : headers.names()) {
|
||||
String value = headers.get(name);
|
||||
query.append("| ").append(name).append(":").append(value).append("\n");
|
||||
}
|
||||
Optional.ofNullable(chain.request().body()).ifPresent(op -> {
|
||||
try {
|
||||
query.append("body length: ").append(op.contentLength()).append("\n");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
query.append("------------------------\n");
|
||||
log.info(query.toString());
|
||||
return chain.proceed(chain.request());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package com.dengqn.app.lingyinapi.html;
|
||||
|
||||
import com.dengqn.app.lingyinapi.beans.SeedListItem;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dengqn
|
||||
* @since 2025/9/25 18:28
|
||||
*/
|
||||
@Slf4j
|
||||
public class HtmlTool {
|
||||
public static List<SeedListItem> getSeedListItem(Document doc) {
|
||||
List<SeedListItem> items = new ArrayList<>();
|
||||
|
||||
Element torrents;
|
||||
try {
|
||||
torrents = doc.getElementsByClass("torrents").getFirst();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return torrents.select("tr").stream().skip(1)
|
||||
.map(tr -> {
|
||||
try {
|
||||
/**
|
||||
* 标题部分
|
||||
*/
|
||||
Element torrentNames = tr.getElementsByTag("table").first();
|
||||
Elements seedNames = torrentNames.getElementsByTag("td");
|
||||
String seedCover = seedNames.get(0).getElementsByTag("img").first().attr("data-src");
|
||||
String seedName = seedNames.get(1).getElementsByTag("a").first().attr("title");
|
||||
String seedUrl = seedNames.get(1).getElementsByTag("a").first().attr("href");
|
||||
boolean isFree = seedNames.get(1).getElementsByClass("pro_free").size() > 0;
|
||||
boolean isHitAndRun = seedNames.get(1).getElementsByClass("hitandrun").size() > 0;
|
||||
String downloadUrl = seedNames.get(3).getElementsByTag("a").first().attr("href");
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
Elements tds = tr.selectXpath("td");
|
||||
// skip 3
|
||||
// 时间(title属性)
|
||||
String time = tds.get(3).getElementsByTag("span").first().attr("title");
|
||||
String size = tds.get(4).text().trim();
|
||||
String seeders = tds.get(5).getElementsByTag("b").first().text().trim();
|
||||
String views = tds.get(7).getElementsByTag("b") == null || tds.get(7).getElementsByTag("b").size() == 0 ? "0" : tds.get(7).getElementsByTag("b").first().text().trim();
|
||||
boolean isAnonymous = tds.get(8).text().trim().contains("匿名");
|
||||
|
||||
|
||||
return new SeedListItem(seedCover, seedName, seedUrl, isFree, isHitAndRun, downloadUrl, time, size, seeders, views, isAnonymous);
|
||||
} catch (Exception e) {
|
||||
log.error("解析报错跳过:" + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).toList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.dengqn.app.lingyinapi.http;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import okhttp3.internal.http.HttpMethod;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author dengqn
|
||||
* @since 2025/9/24 14:23
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpTool {
|
||||
public static String getHTML(String url, OkHttpClient okHttpClient) {
|
||||
try {
|
||||
Response response = okHttpClient.newCall(new Request.Builder()
|
||||
.url(url)
|
||||
.method("GET", null)
|
||||
.build())
|
||||
.execute();
|
||||
ResponseBody body = response.body();
|
||||
if (body == null) return "";
|
||||
|
||||
byte[] bytes = body.bytes();
|
||||
return new String(bytes);
|
||||
} catch (IOException e) {
|
||||
log.error(e.getLocalizedMessage(), e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
server.port=46578
|
||||
server.servlet.context-path=/lingyin-api
|
||||
|
||||
site.config.cookies.secureLogin=bm9wZQ%3D%3D
|
||||
site.config.cookies.securePass=69da36c4a07a6ce260aab777dc99b9d5
|
||||
site.config.cookies.secureUid=MTQ5MTkw
|
|
@ -0,0 +1 @@
|
|||
spring.profiles.active=dev
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
|||
package com.dengqn.app.lingyinapi.http;
|
||||
|
||||
|
||||
import com.dengqn.app.lingyinapi.beans.SeedListItem;
|
||||
import com.dengqn.app.lingyinapi.html.HtmlTool;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
public class HttpToolTest {
|
||||
|
||||
@Test
|
||||
public void testGetHtml() throws IOException {
|
||||
String htmlData = new String(this.getClass().getResourceAsStream("/test-seed-list-page.html").readAllBytes());
|
||||
// log.info("htmlData: {}", htmlData);
|
||||
Document parse = Jsoup.parse(htmlData);
|
||||
List<SeedListItem> listItem = HtmlTool.getSeedListItem(parse);
|
||||
for (SeedListItem seedListItem : listItem) {
|
||||
log.info("item-->{}", seedListItem);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue