mirror of
https://github.com/dev-claw/vripper-project.git
synced 2026-08-19 08:35:41 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d960b13a9f | ||
|
|
b26f78a576 | ||
|
|
733f5c926e | ||
|
|
0012e3b8ca | ||
|
|
564033eb03 | ||
|
|
776f996673 | ||
|
|
06942c70c8 | ||
|
|
0d95b97aee | ||
|
|
e3598769cd |
@@ -3,7 +3,7 @@
|
||||
This is my spin for a cross-platform gallery ripper for [vipergirls.to](https://vipergirls.to).
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## Requirements
|
||||
@@ -13,7 +13,7 @@ Direct access to vipergirls.to (or one of its [alternative domains](https://vipe
|
||||
|
||||
<img src="https://github.com/stashapp/stash/raw/develop/docs/readme_assets/windows_logo.svg" width="100%" height="75"> Windows | <img src="https://github.com/stashapp/stash/raw/develop/docs/readme_assets/mac_logo.svg" width="100%" height="75"> macOS | <img src="https://github.com/stashapp/stash/raw/develop/docs/readme_assets/linux_logo.svg" width="100%" height="75"> Linux | <img src="https://images.vexels.com/media/users/3/166401/isolated/preview/b82aa7ac3f736dd78570dd3fa3fa9e24-java-programming-language-icon-by-vexels.png" width="100%" height="75"> Java
|
||||
:---:|:---:|:---:|:---:
|
||||
[Installer (EXE)](https://github.com/death-claw/vripper-project/releases/download/5.4.0/vripper-windows-installer-5.4.0.exe) <br /> [Installer (MSI)](https://github.com/death-claw/vripper-project/releases/download/5.4.0/vripper-windows-installer-5.4.0.msi) <br /> [Portable (ZIP)](https://github.com/death-claw/vripper-project/releases/download/5.4.0/vripper-windows-portable-5.4.0.zip) | [Installer (DMG)](https://github.com/death-claw/vripper-project/releases/download/5.4.0/vripper-macos-5.4.0.dmg) <br /> [Installer (PKG)](https://github.com/death-claw/vripper-project/releases/download/5.4.0/vripper-macos-5.4.0.pkg) <br /> [Portable (ZIP)](https://github.com/death-claw/vripper-project/releases/download/5.4.0/vripper-macos-portable-5.4.0.zip) | [Linux (amd64) (DEB)](https://github.com/death-claw/vripper-project/releases/download/5.4.0/vripper-linux-5.4.0_amd64.deb) <br /> [Linux (x86_64) (RPM)](https://github.com/death-claw/vripper-project/releases/download/5.4.0/vripper-linux-5.4.0.x86_64.rpm) <br /> [Portable (ZIP)](https://github.com/death-claw/vripper-project/releases/download/5.4.0/vripper-linux-portable-5.4.0.zip) | [Java GUI (noarch)](https://github.com/death-claw/vripper-project/releases/download/5.4.0/vripper-noarch-gui-5.4.0.jar) <br /> [Java Web (noarch)](https://github.com/death-claw/vripper-project/releases/download/5.4.0/vripper-noarch-web-5.4.0.jar)
|
||||
[Installer (EXE)](https://github.com/death-claw/vripper-project/releases/download/5.5.0/vripper-windows-installer-5.5.0.exe) <br /> [Installer (MSI)](https://github.com/death-claw/vripper-project/releases/download/5.5.0/vripper-windows-installer-5.5.0.msi) <br /> [Portable (ZIP)](https://github.com/death-claw/vripper-project/releases/download/5.5.0/vripper-windows-portable-5.5.0.zip) | [Installer (DMG)](https://github.com/death-claw/vripper-project/releases/download/5.5.0/vripper-macos-5.5.0.dmg) <br /> [Installer (PKG)](https://github.com/death-claw/vripper-project/releases/download/5.5.0/vripper-macos-5.5.0.pkg) <br /> [Portable (ZIP)](https://github.com/death-claw/vripper-project/releases/download/5.5.0/vripper-macos-portable-5.5.0.zip) | [Linux (amd64) (DEB)](https://github.com/death-claw/vripper-project/releases/download/5.5.0/vripper-linux-5.5.0_amd64.deb) <br /> [Linux (x86_64) (RPM)](https://github.com/death-claw/vripper-project/releases/download/5.5.0/vripper-linux-5.5.0.x86_64.rpm) <br /> [Portable (ZIP)](https://github.com/death-claw/vripper-project/releases/download/5.5.0/vripper-linux-portable-5.5.0.zip) | [Java GUI (noarch)](https://github.com/death-claw/vripper-project/releases/download/5.5.0/vripper-noarch-gui-5.5.0.jar) <br /> [Java Web (noarch)](https://github.com/death-claw/vripper-project/releases/download/5.5.0/vripper-noarch-web-5.5.0.jar)
|
||||
|
||||
Source code and previous versions are available on the [Releases page](https://github.com/death-claw/vripper-project/releases).
|
||||
|
||||
|
||||
@@ -1,209 +1,209 @@
|
||||
<?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>me.vripper</groupId>
|
||||
<artifactId>vripper-project</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>${revision}</version>
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<maven.deploy.skip>false</maven.deploy.skip>
|
||||
<timestamp>${maven.build.timestamp}</timestamp>
|
||||
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<revision>5.5.0</revision>
|
||||
<kotlin.version>1.9.22</kotlin.version>
|
||||
<slf4j.version>2.0.12</slf4j.version>
|
||||
<logback.version>1.5.0</logback.version>
|
||||
<kotlinx-coroutines.version>1.8.0</kotlinx-coroutines.version>
|
||||
<exposed.version>0.47.0</exposed.version>
|
||||
<snakeyaml.version>2.0</snakeyaml.version>
|
||||
<koin.version>3.5.3</koin.version>
|
||||
<h2.version>2.2.224</h2.version>
|
||||
<htmlcleaner.version>2.29</htmlcleaner.version>
|
||||
<failsafe.version>2.4.4</failsafe.version>
|
||||
<caffeine.version>3.1.8</caffeine.version>
|
||||
<jna-platform.version>5.14.0</jna-platform.version>
|
||||
<kotlinx-serialization-json.version>1.6.2</kotlinx-serialization-json.version>
|
||||
<httpclient5.version>5.3.1</httpclient5.version>
|
||||
<commons-codec.version>1.16.1</commons-codec.version>
|
||||
<liquibase-core.version>4.26.0</liquibase-core.version>
|
||||
<liquibase-slf4j.version>5.0.0</liquibase-slf4j.version>
|
||||
<grpc.kotlin.version>1.4.1</grpc.kotlin.version>
|
||||
<java.grpc.version>1.62.2</java.grpc.version>
|
||||
<protobuf.version>3.25.3</protobuf.version>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>flatten-maven-plugin</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<configuration>
|
||||
<updatePomFile>true</updatePomFile>
|
||||
<flattenMode>resolveCiFriendliesOnly</flattenMode>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>flatten</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>flatten</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>flatten.clean</id>
|
||||
<phase>clean</phase>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<modules>
|
||||
<module>vripper-core</module>
|
||||
<module>vripper-web-ui</module>
|
||||
<module>vripper-web</module>
|
||||
<module>vripper-gui</module>
|
||||
</modules>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-core-jvm</artifactId>
|
||||
<version>${kotlinx-coroutines.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-javafx</artifactId>
|
||||
<version>${kotlinx-coroutines.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.exposed</groupId>
|
||||
<artifactId>exposed-jdbc</artifactId>
|
||||
<version>${exposed.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.exposed</groupId>
|
||||
<artifactId>exposed-java-time</artifactId>
|
||||
<version>${exposed.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.insert-koin</groupId>
|
||||
<artifactId>koin-core-jvm</artifactId>
|
||||
<version>${koin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>h2</artifactId>
|
||||
<groupId>com.h2database</groupId>
|
||||
<version>${h2.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>htmlcleaner</artifactId>
|
||||
<groupId>net.sourceforge.htmlcleaner</groupId>
|
||||
<version>${htmlcleaner.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>failsafe</artifactId>
|
||||
<groupId>net.jodah</groupId>
|
||||
<version>${failsafe.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<version>${caffeine.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna-platform</artifactId>
|
||||
<version>${jna-platform.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-serialization-json</artifactId>
|
||||
<version>${kotlinx-serialization-json.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<version>${httpclient5.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<groupId>commons-codec</groupId>
|
||||
<version>${commons-codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>liquibase-core</artifactId>
|
||||
<groupId>org.liquibase</groupId>
|
||||
<version>${liquibase-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>liquibase-slf4j</artifactId>
|
||||
<groupId>com.mattbertolini</groupId>
|
||||
<version>${liquibase-slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-kotlin-stub</artifactId>
|
||||
<version>${grpc.kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
<version>${java.grpc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-kotlin</artifactId>
|
||||
<version>${protobuf.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty-shaded</artifactId>
|
||||
<version>${java.grpc.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
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>me.vripper</groupId>
|
||||
<artifactId>vripper-project</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>${revision}</version>
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<maven.deploy.skip>false</maven.deploy.skip>
|
||||
<timestamp>${maven.build.timestamp}</timestamp>
|
||||
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<revision>5.8.0</revision>
|
||||
<kotlin.version>1.9.22</kotlin.version>
|
||||
<slf4j.version>2.0.12</slf4j.version>
|
||||
<logback.version>1.5.0</logback.version>
|
||||
<kotlinx-coroutines.version>1.8.0</kotlinx-coroutines.version>
|
||||
<exposed.version>0.47.0</exposed.version>
|
||||
<snakeyaml.version>2.0</snakeyaml.version>
|
||||
<koin.version>3.5.3</koin.version>
|
||||
<h2.version>2.2.224</h2.version>
|
||||
<htmlcleaner.version>2.29</htmlcleaner.version>
|
||||
<failsafe.version>2.4.4</failsafe.version>
|
||||
<caffeine.version>3.1.8</caffeine.version>
|
||||
<jna-platform.version>5.14.0</jna-platform.version>
|
||||
<kotlinx-serialization-json.version>1.6.2</kotlinx-serialization-json.version>
|
||||
<httpclient5.version>5.3.1</httpclient5.version>
|
||||
<commons-codec.version>1.16.1</commons-codec.version>
|
||||
<liquibase-core.version>4.26.0</liquibase-core.version>
|
||||
<liquibase-slf4j.version>5.0.0</liquibase-slf4j.version>
|
||||
<grpc.kotlin.version>1.4.1</grpc.kotlin.version>
|
||||
<java.grpc.version>1.62.2</java.grpc.version>
|
||||
<protobuf.version>3.25.3</protobuf.version>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>flatten-maven-plugin</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<configuration>
|
||||
<updatePomFile>true</updatePomFile>
|
||||
<flattenMode>resolveCiFriendliesOnly</flattenMode>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>flatten</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>flatten</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>flatten.clean</id>
|
||||
<phase>clean</phase>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<modules>
|
||||
<module>vripper-core</module>
|
||||
<module>vripper-web-ui</module>
|
||||
<module>vripper-web</module>
|
||||
<module>vripper-gui</module>
|
||||
</modules>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-core-jvm</artifactId>
|
||||
<version>${kotlinx-coroutines.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-coroutines-javafx</artifactId>
|
||||
<version>${kotlinx-coroutines.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.exposed</groupId>
|
||||
<artifactId>exposed-jdbc</artifactId>
|
||||
<version>${exposed.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.exposed</groupId>
|
||||
<artifactId>exposed-java-time</artifactId>
|
||||
<version>${exposed.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.insert-koin</groupId>
|
||||
<artifactId>koin-core-jvm</artifactId>
|
||||
<version>${koin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>h2</artifactId>
|
||||
<groupId>com.h2database</groupId>
|
||||
<version>${h2.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>htmlcleaner</artifactId>
|
||||
<groupId>net.sourceforge.htmlcleaner</groupId>
|
||||
<version>${htmlcleaner.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>failsafe</artifactId>
|
||||
<groupId>net.jodah</groupId>
|
||||
<version>${failsafe.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<version>${caffeine.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna-platform</artifactId>
|
||||
<version>${jna-platform.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>kotlin-reflect</artifactId>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-serialization-json</artifactId>
|
||||
<version>${kotlinx-serialization-json.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<version>${httpclient5.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<groupId>commons-codec</groupId>
|
||||
<version>${commons-codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>liquibase-core</artifactId>
|
||||
<groupId>org.liquibase</groupId>
|
||||
<version>${liquibase-core.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>liquibase-slf4j</artifactId>
|
||||
<groupId>com.mattbertolini</groupId>
|
||||
<version>${liquibase-slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-kotlin-stub</artifactId>
|
||||
<version>${grpc.kotlin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-protobuf</artifactId>
|
||||
<version>${java.grpc.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-kotlin</artifactId>
|
||||
<version>${protobuf.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty-shaded</artifactId>
|
||||
<version>${java.grpc.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
|
||||
@@ -105,7 +105,6 @@ class DownloadService(
|
||||
continue
|
||||
}
|
||||
if (isPending(post.postId)) {
|
||||
log.warn("Cannot restart, jobs are currently running for post id $posts")
|
||||
continue
|
||||
}
|
||||
toProcess[post] = images
|
||||
|
||||
@@ -118,10 +118,7 @@ class ImageDownloadRunnable(
|
||||
} finally {
|
||||
try {
|
||||
Files.delete(downloadedImage.path)
|
||||
} catch (e: IOException) {
|
||||
log.warn(
|
||||
"Failed to delete temporary file ${downloadedImage.path}"
|
||||
)
|
||||
} catch (_: IOException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class PostLookupAPIParser(private val threadId: Long, private val postId: Long)
|
||||
private val dataTransaction: DataTransaction by inject()
|
||||
|
||||
@Throws(PostParseException::class)
|
||||
fun parse(): PostItem {
|
||||
fun parse(): ThreadItem? {
|
||||
log.debug("Parsing post $postId")
|
||||
val httpGet =
|
||||
HttpGet(URIBuilder("${settingsService.settings.viperSettings.host}/vr.php").also {
|
||||
@@ -58,7 +58,7 @@ class PostLookupAPIParser(private val threadId: Long, private val postId: Long)
|
||||
}
|
||||
factory.newSAXParser()
|
||||
.parse(response.entity.content, threadLookupAPIResponseHandler)
|
||||
threadLookupAPIResponseHandler.result.postItemList.first()
|
||||
threadLookupAPIResponseHandler.result
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -5,5 +5,6 @@ data class ThreadItem(
|
||||
val title: String,
|
||||
val securityToken: String,
|
||||
val forum: String,
|
||||
val postItemList: List<PostItem>
|
||||
val postItemList: List<PostItem>,
|
||||
val error: String
|
||||
)
|
||||
|
||||
@@ -8,7 +8,6 @@ import org.xml.sax.Attributes
|
||||
import org.xml.sax.helpers.DefaultHandler
|
||||
|
||||
class ThreadLookupAPIResponseHandler : KoinComponent, DefaultHandler() {
|
||||
private val log by me.vripper.delegate.LoggerDelegate()
|
||||
private val supportedHosts: List<Host> = getKoin().getAll()
|
||||
private val settingsService: SettingsService by inject()
|
||||
private var error: String = ""
|
||||
@@ -17,13 +16,13 @@ class ThreadLookupAPIResponseHandler : KoinComponent, DefaultHandler() {
|
||||
private val imageItemList: MutableList<ImageItem> = mutableListOf()
|
||||
private lateinit var threadItem: ThreadItem
|
||||
private var threadId: Long = -1
|
||||
private lateinit var threadTitle: String
|
||||
private lateinit var forum: String
|
||||
private lateinit var securityToken: String
|
||||
private var threadTitle: String = ""
|
||||
private var forum: String = ""
|
||||
private var securityToken: String = ""
|
||||
private var postId: Long = -1
|
||||
private lateinit var postTitle: String
|
||||
private var postCounter: Int = 0
|
||||
private var postTitle: String = ""
|
||||
|
||||
private var postCounter: Int = 0
|
||||
val result: ThreadItem
|
||||
get() = threadItem
|
||||
|
||||
@@ -89,6 +88,6 @@ class ThreadLookupAPIResponseHandler : KoinComponent, DefaultHandler() {
|
||||
}
|
||||
|
||||
override fun endDocument() {
|
||||
threadItem = ThreadItem(threadId, threadTitle, securityToken, forum, postItemList.toList())
|
||||
threadItem = ThreadItem(threadId, threadTitle, securityToken, forum, postItemList.toList(), error)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package me.vripper.services
|
||||
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import kotlinx.coroutines.time.sample
|
||||
import me.vripper.download.DownloadService
|
||||
import me.vripper.entities.LogEntryEntity
|
||||
@@ -17,9 +15,12 @@ import me.vripper.utilities.GLOBAL_EXECUTOR
|
||||
import me.vripper.utilities.PathUtils
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.concurrent.withLock
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
|
||||
class AppEndpointService(
|
||||
private val downloadService: DownloadService,
|
||||
@@ -30,12 +31,11 @@ class AppEndpointService(
|
||||
) : IAppEndpointService {
|
||||
private val log by me.vripper.delegate.LoggerDelegate()
|
||||
|
||||
private val semaphore = Semaphore(1)
|
||||
private val lock = ReentrantLock()
|
||||
|
||||
override suspend fun scanLinks(postLinks: String) {
|
||||
semaphore.withPermit {
|
||||
lock.withLock {
|
||||
if (postLinks.isBlank()) {
|
||||
log.warn("Nothing to scan")
|
||||
return
|
||||
}
|
||||
val urlList = postLinks.split(Pattern.compile("\\r?\\n")).dropLastWhile { it.isEmpty() }.map { it.trim() }
|
||||
@@ -82,13 +82,12 @@ class AppEndpointService(
|
||||
}
|
||||
|
||||
override suspend fun restartAll(posIds: List<Long>) {
|
||||
semaphore.withPermit {
|
||||
lock.withLock {
|
||||
downloadService.restartAll(posIds.map { dataTransaction.findPostByPostId(it) }.filter { it.isPresent }
|
||||
.map { it.get() })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override suspend fun download(posts: List<ThreadPostId>) {
|
||||
CompletableFuture.runAsync(
|
||||
AddPostRunnable(posts), GLOBAL_EXECUTOR
|
||||
@@ -96,26 +95,26 @@ class AppEndpointService(
|
||||
}
|
||||
|
||||
override suspend fun stopAll(postIdList: List<Long>) {
|
||||
semaphore.withPermit {
|
||||
lock.withLock {
|
||||
downloadService.stop(postIdList)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun remove(postIdList: List<Long>) {
|
||||
semaphore.withPermit {
|
||||
lock.withLock {
|
||||
downloadService.stop(postIdList)
|
||||
dataTransaction.removeAll(postIdList)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun clearCompleted(): List<Long> {
|
||||
semaphore.withPermit {
|
||||
lock.withLock {
|
||||
return dataTransaction.clearCompleted()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun grab(threadId: Long): List<PostSelection> {
|
||||
semaphore.withPermit {
|
||||
lock.withLock {
|
||||
return try {
|
||||
val thread = dataTransaction.findThreadByThreadId(threadId).orElseThrow {
|
||||
PostParseException(
|
||||
@@ -160,7 +159,7 @@ class AppEndpointService(
|
||||
}
|
||||
|
||||
override suspend fun threadRemove(threadIdList: List<Long>) {
|
||||
semaphore.withPermit {
|
||||
lock.withLock {
|
||||
threadIdList.forEach {
|
||||
dataTransaction.removeThread(it)
|
||||
}
|
||||
@@ -168,13 +167,25 @@ class AppEndpointService(
|
||||
}
|
||||
|
||||
override suspend fun threadClear() {
|
||||
semaphore.withPermit {
|
||||
lock.withLock {
|
||||
dataTransaction.clearQueueLinks()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun logClear() {
|
||||
dataTransaction.deleteAllLogs()
|
||||
lock.withLock {
|
||||
dataTransaction.deleteAllLogs()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun renameToFirst(postIds: List<Long>) {
|
||||
postIds.forEach { postId ->
|
||||
dataTransaction
|
||||
.findMetadataByPostId(postId)
|
||||
.map { it.data.resolvedNames }
|
||||
.filter { it.isNotEmpty() }
|
||||
.getOrNull()?.let { rename(postId, it.first()) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun rename(postId: Long, newName: String) {
|
||||
|
||||
@@ -44,4 +44,5 @@ interface IAppEndpointService {
|
||||
fun onUpdateSettings(): Flow<Settings>
|
||||
suspend fun loggedInUser(): String
|
||||
suspend fun getVersion(): String
|
||||
suspend fun renameToFirst(postIds: List<Long>)
|
||||
}
|
||||
@@ -4,12 +4,13 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import me.vripper.entities.MetadataEntity
|
||||
import me.vripper.exception.DownloadException
|
||||
import me.vripper.exception.VripperException
|
||||
import me.vripper.utilities.HtmlUtils
|
||||
import me.vripper.utilities.RequestLimit
|
||||
import me.vripper.utilities.Tasks
|
||||
import me.vripper.utilities.XpathUtils
|
||||
import org.apache.hc.client5.http.classic.methods.HttpGet
|
||||
import org.apache.hc.core5.net.URIBuilder
|
||||
@@ -17,7 +18,6 @@ import org.w3c.dom.Node
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.stream.Collectors
|
||||
|
||||
|
||||
class MetadataService(
|
||||
private val httpService: HTTPService,
|
||||
private val settingsService: SettingsService,
|
||||
@@ -26,51 +26,55 @@ class MetadataService(
|
||||
) {
|
||||
|
||||
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
private val requestSemaphore = Semaphore(6)
|
||||
private val dictionary: List<String> =
|
||||
mutableListOf("download", "link", "rapidgator", "filefactory", "filefox")
|
||||
private val dictionary: List<String> = mutableListOf("download", "link", "rapidgator", "filefactory", "filefox")
|
||||
|
||||
fun init() {
|
||||
dataTransaction.findAllPosts()
|
||||
.filter { dataTransaction.findMetadataByPostId(it.postId).isEmpty }.map { it.postId }
|
||||
.forEach(::fetchMetadata)
|
||||
dataTransaction.findAllPosts().filter { dataTransaction.findMetadataByPostId(it.postId).isEmpty }
|
||||
.map { it.postId }.forEach(::fetchMetadata)
|
||||
}
|
||||
|
||||
fun fetchMetadata(postId: Long) {
|
||||
Tasks.increment()
|
||||
coroutineScope.launch {
|
||||
requestSemaphore.withPermit {
|
||||
val httpGet =
|
||||
HttpGet(URIBuilder(settingsService.settings.viperSettings.host + "/threads/").also {
|
||||
RequestLimit.semaphore.withPermit {
|
||||
try {
|
||||
val httpGet = HttpGet(URIBuilder(settingsService.settings.viperSettings.host + "/threads/").also {
|
||||
it.setParameter(
|
||||
"p", postId.toString()
|
||||
)
|
||||
}.build())
|
||||
|
||||
val metadataEntity = httpService.client.execute(httpGet, vgAuthService.context) {
|
||||
if (it.code / 100 != 2) {
|
||||
throw DownloadException("Unexpected response code '${it.code}' for $httpGet")
|
||||
val metadataEntity = httpService.client.execute(httpGet, vgAuthService.context) {
|
||||
if (it.code / 100 != 2) {
|
||||
throw DownloadException("Unexpected response code '${it.code}' for $httpGet")
|
||||
}
|
||||
val document = HtmlUtils.clean(it.entity.content)
|
||||
val postNode: Node = XpathUtils.getAsNode(
|
||||
document,
|
||||
"//li[@id='post_$postId']/div[contains(@class, 'postdetails')]",
|
||||
|
||||
) ?: throw VripperException("Unable to find post #'$postId'")
|
||||
|
||||
val postedBy: String = XpathUtils.getAsNode(
|
||||
postNode, "./div[contains(@class, 'userinfo')]//a[contains(@class, 'username')]//font"
|
||||
)?.textContent?.trim()
|
||||
?: throw VripperException("Unable to find the poster for post #'$postId'")
|
||||
|
||||
|
||||
val node: Node = XpathUtils.getAsNode(
|
||||
document, java.lang.String.format("//div[@id='post_message_%s']", postId)
|
||||
) ?: throw VripperException("Unable to locate post content")
|
||||
val titles = findTitleInContent(node)
|
||||
MetadataEntity(postId, MetadataEntity.Data(postedBy, titles))
|
||||
}
|
||||
val document = HtmlUtils.clean(it.entity.content)
|
||||
val postNode: Node = XpathUtils.getAsNode(
|
||||
document,
|
||||
"//li[@id='post_$postId']/div[contains(@class, 'postdetails')]",
|
||||
|
||||
) ?: throw VripperException("Unable to find post #'$postId'")
|
||||
|
||||
val postedBy: String = XpathUtils.getAsNode(
|
||||
postNode,
|
||||
"./div[contains(@class, 'userinfo')]//a[contains(@class, 'username')]//font"
|
||||
)?.textContent?.trim()
|
||||
?: throw VripperException("Unable to find the poster for post #'$postId'")
|
||||
|
||||
|
||||
val node: Node = XpathUtils.getAsNode(
|
||||
document, java.lang.String.format("//div[@id='post_message_%s']", postId)
|
||||
) ?: throw VripperException("Unable to locate post content")
|
||||
val titles = findTitleInContent(node)
|
||||
MetadataEntity(postId, MetadataEntity.Data(postedBy, titles))
|
||||
try {
|
||||
dataTransaction.saveMetadata(metadataEntity)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
} finally {
|
||||
Tasks.decrement()
|
||||
}
|
||||
dataTransaction.saveMetadata(metadataEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ class SettingsService(private val eventBus: EventBus) {
|
||||
customProxiesPath.readText()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
emptyList()
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -2,15 +2,14 @@ package me.vripper.services
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import me.vripper.event.EventBus
|
||||
import me.vripper.event.SettingsUpdateEvent
|
||||
import me.vripper.parser.ThreadItem
|
||||
import me.vripper.parser.ThreadLookupAPIParser
|
||||
import me.vripper.utilities.RequestLimit
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -29,12 +28,16 @@ class ThreadCacheService(val eventBus: EventBus) {
|
||||
|
||||
private val cache: LoadingCache<Long, ThreadItem> =
|
||||
Caffeine.newBuilder().expireAfterWrite(20, TimeUnit.MINUTES).build { threadId ->
|
||||
ThreadLookupAPIParser(threadId).parse()
|
||||
runBlocking {
|
||||
RequestLimit.semaphore.withPermit {
|
||||
ThreadLookupAPIParser(threadId).parse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(ExecutionException::class)
|
||||
operator fun get(threadId: Long): ThreadItem {
|
||||
return cache[threadId] ?: throw NoSuchElementException("$threadId does not exist")
|
||||
return cache[threadId]
|
||||
}
|
||||
|
||||
fun getIfPresent(threadId: Long): ThreadItem? {
|
||||
|
||||
@@ -115,7 +115,6 @@ class VGAuthService(
|
||||
}
|
||||
authenticated = true
|
||||
loggedUser = username
|
||||
log.info(String.format("Authenticated: %s", username))
|
||||
|
||||
coroutineScope.launch {
|
||||
eventBus.publishEvent(VGUserLoginEvent(loggedUser))
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package me.vripper.tasks
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import me.vripper.download.DownloadService
|
||||
import me.vripper.entities.LogEntryEntity
|
||||
import me.vripper.model.ThreadPostId
|
||||
import me.vripper.parser.PostItem
|
||||
import me.vripper.parser.PostLookupAPIParser
|
||||
@@ -8,6 +11,7 @@ import me.vripper.services.DataTransaction
|
||||
import me.vripper.services.MetadataService
|
||||
import me.vripper.services.SettingsService
|
||||
import me.vripper.services.ThreadCacheService
|
||||
import me.vripper.utilities.RequestLimit
|
||||
import me.vripper.utilities.Tasks
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
@@ -26,25 +30,76 @@ class AddPostRunnable(private val items: List<ThreadPostId>) : KoinComponent, Ru
|
||||
val toProcess = mutableListOf<PostItem>()
|
||||
for ((threadId, postId) in items) {
|
||||
if (dataTransaction.exists(postId)) {
|
||||
log.warn(String.format("skipping %s, already loaded", postId))
|
||||
continue
|
||||
}
|
||||
|
||||
val threadItem = threadCacheService.getIfPresent(threadId)
|
||||
val postItem: PostItem =
|
||||
threadItem?.postItemList?.find { it.postId == postId } ?: PostLookupAPIParser(
|
||||
threadId,
|
||||
postId
|
||||
).parse()
|
||||
val link =
|
||||
"https://${settingsService.settings.viperSettings.host}/threads/$threadId?p=$postId&viewfull=1#post$postId"
|
||||
|
||||
val cachedThread = threadCacheService.getIfPresent(threadId)
|
||||
val threadItem = cachedThread ?: runBlocking {
|
||||
RequestLimit.semaphore.withPermit {
|
||||
PostLookupAPIParser(
|
||||
threadId, postId
|
||||
).parse()
|
||||
}
|
||||
}
|
||||
|
||||
if (threadItem == null) {
|
||||
dataTransaction.saveLog(
|
||||
LogEntryEntity(
|
||||
type = LogEntryEntity.Type.POST,
|
||||
status = LogEntryEntity.Status.ERROR,
|
||||
message = "Failed to load $link"
|
||||
)
|
||||
)
|
||||
continue
|
||||
} else {
|
||||
if (threadItem.error.isNotBlank()) {
|
||||
dataTransaction.saveLog(
|
||||
LogEntryEntity(
|
||||
type = LogEntryEntity.Type.POST,
|
||||
status = LogEntryEntity.Status.ERROR,
|
||||
message = "Error loading $link: ${threadItem.error}"
|
||||
)
|
||||
)
|
||||
continue
|
||||
} else if (threadItem.postItemList.isEmpty()) {
|
||||
dataTransaction.saveLog(
|
||||
LogEntryEntity(
|
||||
type = LogEntryEntity.Type.POST,
|
||||
status = LogEntryEntity.Status.ERROR,
|
||||
message = "Nothing found for $link"
|
||||
)
|
||||
)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
val postItem = threadItem.postItemList.firstOrNull { it.postId == postId }
|
||||
if (postItem == null) {
|
||||
dataTransaction.saveLog(
|
||||
LogEntryEntity(
|
||||
type = LogEntryEntity.Type.POST,
|
||||
status = LogEntryEntity.Status.ERROR,
|
||||
message = "Unable to load $link"
|
||||
)
|
||||
)
|
||||
continue
|
||||
}
|
||||
toProcess.add(postItem)
|
||||
}
|
||||
|
||||
val posts = dataTransaction.newPosts(toProcess.toList())
|
||||
val posts = try {
|
||||
dataTransaction.newPosts(toProcess.toList())
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
emptyList()
|
||||
}
|
||||
posts.forEach {
|
||||
metadataService.fetchMetadata(it.postId)
|
||||
}
|
||||
if (settingsService.settings.downloadSettings.autoStart) {
|
||||
log.debug("Auto start downloads option is enabled")
|
||||
downloadService.restartAll(posts)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package me.vripper.tasks
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import me.vripper.entities.LogEntryEntity
|
||||
import me.vripper.entities.LogEntryEntity.Status.ERROR
|
||||
import me.vripper.entities.ThreadEntity
|
||||
import me.vripper.model.Settings
|
||||
import me.vripper.model.ThreadPostId
|
||||
import me.vripper.parser.ThreadItem
|
||||
import me.vripper.services.AppEndpointService
|
||||
import me.vripper.services.DataTransaction
|
||||
import me.vripper.services.SettingsService
|
||||
import me.vripper.services.ThreadCacheService
|
||||
@@ -18,13 +15,11 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class ThreadLookupRunnable(private val threadId: Long, private val settings: Settings) :
|
||||
KoinComponent, Runnable {
|
||||
class ThreadLookupRunnable(private val threadId: Long, private val settings: Settings) : KoinComponent, Runnable {
|
||||
private val log by me.vripper.delegate.LoggerDelegate()
|
||||
private val dataTransaction by inject<DataTransaction>()
|
||||
private val settingsService by inject<SettingsService>()
|
||||
private val threadCacheService by inject<ThreadCacheService>()
|
||||
private val appEndpointService by inject<AppEndpointService>()
|
||||
private val link: String = "${settingsService.settings.viperSettings.host}/threads/$threadId"
|
||||
|
||||
override fun run() {
|
||||
@@ -32,28 +27,47 @@ class ThreadLookupRunnable(private val threadId: Long, private val settings: Set
|
||||
Tasks.increment()
|
||||
if (dataTransaction.findThreadByThreadId(threadId).isEmpty) {
|
||||
val threadLookupResult = threadCacheService[threadId]
|
||||
if (threadLookupResult.postItemList.isEmpty()) {
|
||||
val message = "Nothing found for $link"
|
||||
if (threadLookupResult.error.isNotBlank()) {
|
||||
dataTransaction.saveLog(
|
||||
LogEntryEntity(
|
||||
type = LogEntryEntity.Type.THREAD,
|
||||
status = ERROR,
|
||||
message = message
|
||||
status = LogEntryEntity.Status.ERROR,
|
||||
message = "Error loading $link: ${threadLookupResult.error}"
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
dataTransaction.save(
|
||||
ThreadEntity(
|
||||
title = threadLookupResult.title,
|
||||
link = link,
|
||||
threadId = threadId,
|
||||
total = threadLookupResult.postItemList.size
|
||||
if (threadLookupResult.postItemList.isEmpty()) {
|
||||
dataTransaction.saveLog(
|
||||
LogEntryEntity(
|
||||
type = LogEntryEntity.Type.THREAD,
|
||||
status = LogEntryEntity.Status.ERROR,
|
||||
message = "Nothing found for $link"
|
||||
)
|
||||
)
|
||||
)
|
||||
autostart(threadLookupResult)
|
||||
} else {
|
||||
log.info("Link $link is already loaded")
|
||||
return
|
||||
}
|
||||
|
||||
if (threadLookupResult.postItemList.size <= settings.downloadSettings.autoQueueThreshold) {
|
||||
CompletableFuture.runAsync(AddPostRunnable(threadLookupResult.postItemList.map {
|
||||
ThreadPostId(
|
||||
it.threadId, it.postId
|
||||
)
|
||||
}), GLOBAL_EXECUTOR)
|
||||
} else {
|
||||
try {
|
||||
dataTransaction.save(
|
||||
ThreadEntity(
|
||||
title = threadLookupResult.title,
|
||||
link = link,
|
||||
threadId = threadId,
|
||||
total = threadLookupResult.postItemList.size
|
||||
)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
val error = "Error when processing $link"
|
||||
@@ -70,18 +84,4 @@ class ThreadLookupRunnable(private val threadId: Long, private val settings: Set
|
||||
Tasks.decrement()
|
||||
}
|
||||
}
|
||||
|
||||
private fun autostart(lookupResult: ThreadItem) {
|
||||
if (lookupResult.postItemList.size <= settings.downloadSettings.autoQueueThreshold) {
|
||||
runBlocking {
|
||||
appEndpointService.threadRemove(listOf(lookupResult.threadId))
|
||||
}
|
||||
CompletableFuture.runAsync(
|
||||
AddPostRunnable(
|
||||
lookupResult.postItemList.map { ThreadPostId(it.threadId, it.postId) }
|
||||
),
|
||||
GLOBAL_EXECUTOR
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,8 @@ object PathUtils {
|
||||
if (currentDownloadDirectory.listDirectoryEntries().isEmpty()) {
|
||||
try {
|
||||
Files.delete(currentDownloadDirectory)
|
||||
} catch (ignored: Exception) {
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package me.vripper.utilities
|
||||
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
|
||||
object RequestLimit {
|
||||
val semaphore: Semaphore = Semaphore(6)
|
||||
}
|
||||
@@ -42,6 +42,9 @@ message Rename {
|
||||
int64 postId = 1;
|
||||
string name = 2;
|
||||
}
|
||||
message RenameToFirst {
|
||||
repeated int64 postIds = 1;
|
||||
}
|
||||
message PostSelection {
|
||||
int64 threadId = 1;
|
||||
string threadTitle = 2;
|
||||
@@ -94,6 +97,7 @@ service EndpointService {
|
||||
rpc remove (IdList) returns (EmptyResponse) {}
|
||||
rpc clearCompleted (EmptyRequest) returns (IdList) {}
|
||||
rpc rename (Rename) returns (EmptyResponse) {}
|
||||
rpc renameToFirst (RenameToFirst) returns (EmptyResponse) {}
|
||||
rpc onStopped (EmptyRequest) returns (stream Id) {}
|
||||
rpc logClear (EmptyRequest) returns (EmptyResponse) {}
|
||||
rpc findAllLogs (EmptyRequest) returns (Logs) {}
|
||||
|
||||
@@ -165,4 +165,8 @@
|
||||
<column name="FILENAME" type="VARCHAR(260)" afterColumn="STATUS" defaultValue=""/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
<changeSet id="0009" author="vripper">
|
||||
<addUniqueConstraint tableName="POST" columnNames="POST_ID" constraintName="POST__POST_ID_UQ"/>
|
||||
<addUniqueConstraint tableName="THREAD" columnNames="THREAD_ID" constraintName="THREAD__THREAD_ID_UQ"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender class="ch.qos.logback.core.ConsoleAppender" name="STDOUT">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender class="ch.qos.logback.core.ConsoleAppender" name="STDOUT">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender class="ch.qos.logback.core.rolling.RollingFileAppender"
|
||||
name="FILE">
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</Pattern>
|
||||
</encoder>
|
||||
<file>${VRIPPER_DIR}/vripper.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${VRIPPER_DIR}/vripper.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<maxHistory>7</maxHistory>
|
||||
<totalSizeCap>1GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
<appender class="ch.qos.logback.core.rolling.RollingFileAppender"
|
||||
name="FILE">
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</Pattern>
|
||||
</encoder>
|
||||
<file>${VRIPPER_DIR}/vripper.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${VRIPPER_DIR}/vripper.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<maxHistory>7</maxHistory>
|
||||
<totalSizeCap>1GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
<logger name="Exposed" level="OFF"/>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
</configuration>
|
||||
|
||||
@@ -131,6 +131,19 @@ class ActionBarView : View() {
|
||||
postsTableView.renameSelected()
|
||||
}
|
||||
}
|
||||
button("Bulk rename", FontIcon.of(Feather.EDIT_3)) {
|
||||
contentDisplay = ContentDisplay.GRAPHIC_ONLY
|
||||
tooltip("Bulk rename selected [Ctrl+Shift+R]")
|
||||
shortcut(KeyCodeCombination(KeyCode.R, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN))
|
||||
enableWhen(
|
||||
postsTableView.tableView.selectionModel.selectedItems.sizeProperty.greaterThan(
|
||||
0
|
||||
)
|
||||
)
|
||||
action {
|
||||
postsTableView.bulkRenameSelected()
|
||||
}
|
||||
}
|
||||
button("Delete", FontIcon.of(Feather.TRASH)) {
|
||||
contentDisplay = ContentDisplay.GRAPHIC_ONLY
|
||||
tooltip("Delete selected posts [Del]")
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package me.vripper.gui.components.views
|
||||
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.scene.control.TabPane
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import me.vripper.delegate.LoggerDelegate
|
||||
import me.vripper.gui.controller.PostController
|
||||
@@ -110,7 +112,16 @@ class PostInfoView : View() {
|
||||
return
|
||||
}
|
||||
coroutineScope.launch {
|
||||
val model = async { postController.find(postId) }.await()
|
||||
val model: PostModel? = async {
|
||||
try {
|
||||
postController.find(postId)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}.await()
|
||||
if (model == null) {
|
||||
return@launch
|
||||
}
|
||||
runLater {
|
||||
postModel.apply {
|
||||
this.postId = model.postId
|
||||
@@ -136,6 +147,8 @@ class PostInfoView : View() {
|
||||
postController.onUpdatePosts().catch {
|
||||
logger.error("gRPC error", it)
|
||||
currentCoroutineContext().cancel(null)
|
||||
}.filter {
|
||||
it.postId == postModel.postId
|
||||
}.collect { post ->
|
||||
runLater {
|
||||
postModel.status = post.status.stringValue.lowercase().replaceFirstChar { it.uppercase() }
|
||||
@@ -152,5 +165,19 @@ class PostInfoView : View() {
|
||||
}
|
||||
}
|
||||
}.also { jobs.add(it) }
|
||||
|
||||
coroutineScope.launch {
|
||||
postController.onUpdateMetadata().catch {
|
||||
logger.error("gRPC error", it)
|
||||
currentCoroutineContext().cancel(null)
|
||||
}.filter {
|
||||
it.postId == postModel.postId
|
||||
}.collect {
|
||||
runLater {
|
||||
postModel.altTitles = FXCollections.observableArrayList(it.data.resolvedNames)
|
||||
postModel.postedBy = it.data.postedBy
|
||||
}
|
||||
}
|
||||
}.also { jobs.add(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,13 @@ class PostsTableView : View() {
|
||||
graphic = FontIcon.of(Feather.EDIT)
|
||||
}
|
||||
|
||||
val bulkRenameItem = MenuItem("Bulk rename").apply {
|
||||
setOnAction {
|
||||
bulkRenameSelected()
|
||||
}
|
||||
graphic = FontIcon.of(Feather.EDIT_3)
|
||||
}
|
||||
|
||||
val deleteItem = MenuItem("Delete").apply {
|
||||
setOnAction {
|
||||
deleteSelected()
|
||||
@@ -122,7 +129,14 @@ class PostsTableView : View() {
|
||||
|
||||
val contextMenu = ContextMenu()
|
||||
contextMenu.items.addAll(
|
||||
startItem, stopItem, renameItem, deleteItem, SeparatorMenuItem(), locationItem, urlItem
|
||||
startItem,
|
||||
stopItem,
|
||||
renameItem,
|
||||
bulkRenameItem,
|
||||
deleteItem,
|
||||
SeparatorMenuItem(),
|
||||
locationItem,
|
||||
urlItem
|
||||
)
|
||||
tableRow.contextMenuProperty()
|
||||
.bind(tableRow.emptyProperty().map { empty -> if (empty) null else contextMenu })
|
||||
@@ -430,6 +444,13 @@ class PostsTableView : View() {
|
||||
}
|
||||
}
|
||||
|
||||
fun bulkRenameSelected() {
|
||||
val selectedItems = tableView.selectionModel.selectedItems
|
||||
coroutineScope.launch {
|
||||
postController.renameToFirst(selectedItems.map { it.postId })
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteSelected() {
|
||||
val postIdList = tableView.selectionModel.selectedItems.map { it.postId }
|
||||
confirm(
|
||||
|
||||
@@ -88,6 +88,10 @@ class PostController : Controller() {
|
||||
appEndpointService.rename(postId, value)
|
||||
}
|
||||
|
||||
suspend fun renameToFirst(postIds: List<Long>) {
|
||||
appEndpointService.renameToFirst(postIds)
|
||||
}
|
||||
|
||||
fun onNewPosts() =
|
||||
appEndpointService.onNewPosts().catch {
|
||||
logger.error("gRPC error", it)
|
||||
|
||||
@@ -58,6 +58,10 @@ class GrpcEndpointService : IAppEndpointService {
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun renameToFirst(postIds: List<Long>) {
|
||||
endpointServiceCoroutineStub!!.renameToFirst(RenameToFirst.newBuilder().addAllPostIds(postIds).build())
|
||||
}
|
||||
|
||||
override fun onNewPosts(): Flow<Post> =
|
||||
endpointServiceCoroutineStub!!.onNewPosts(EmptyRequest.getDefaultInstance()).map { mapper(it) }
|
||||
|
||||
@@ -153,7 +157,6 @@ class GrpcEndpointService : IAppEndpointService {
|
||||
override fun onDownloadSpeed(): Flow<Long> =
|
||||
endpointServiceCoroutineStub!!.onDownloadSpeed(EmptyRequest.getDefaultInstance()).map { it.speed }
|
||||
|
||||
|
||||
override fun onVGUserUpdate(): Flow<String> =
|
||||
endpointServiceCoroutineStub!!.onVGUserUpdate(EmptyRequest.getDefaultInstance()).map { it.user }
|
||||
|
||||
@@ -170,7 +173,6 @@ class GrpcEndpointService : IAppEndpointService {
|
||||
override suspend fun getSettings(): Settings =
|
||||
mapper(endpointServiceCoroutineStub!!.getSettings(EmptyRequest.getDefaultInstance()))
|
||||
|
||||
|
||||
override suspend fun saveSettings(settings: Settings) {
|
||||
val viperSettings = with(SettingsOuterClass.ViperSettings.newBuilder()) {
|
||||
login = settings.viperSettings.login
|
||||
|
||||
@@ -89,7 +89,7 @@ class Preview(owner: Stage, private val images: List<String>, private val cacheP
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
log.warn("Failed to load preview $url")
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +95,16 @@ class GrpcServerAppEndpointService : EndpointServiceGrpcKt.EndpointServiceCorout
|
||||
return IdList.newBuilder().addAllIds(appEndpointService.clearCompleted()).build()
|
||||
}
|
||||
|
||||
override suspend fun rename(request: EndpointServiceOuterClass.Rename): EmptyResponse {
|
||||
override suspend fun rename(request: Rename): EmptyResponse {
|
||||
appEndpointService.rename(request.postId, request.name)
|
||||
return EmptyResponse.getDefaultInstance()
|
||||
}
|
||||
|
||||
override suspend fun renameToFirst(request: RenameToFirst): EmptyResponse {
|
||||
appEndpointService.renameToFirst(request.postIdsList)
|
||||
return EmptyResponse.getDefaultInstance()
|
||||
}
|
||||
|
||||
override fun onStopped(request: EmptyRequest): Flow<Id> {
|
||||
return appEndpointService.onStopped().map { Id.newBuilder().setId(it).build() }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user