mirror of
https://github.com/dev-claw/vripper-project.git
synced 2026-08-19 08:35:41 +02:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34b31a51a5 | ||
|
|
99a8aa0ce0 | ||
|
|
bdb67475a6 | ||
|
|
24c961644e | ||
|
|
3c3b79f581 | ||
|
|
8c30e5a02d | ||
|
|
398220d518 | ||
|
|
af9a439d81 | ||
|
|
c9760511b5 | ||
|
|
59fe4d227f | ||
|
|
6df84c0756 | ||
|
|
aa0119f881 | ||
|
|
9283ab7837 | ||
|
|
ca77074ef6 | ||
|
|
3cb251cb36 |
@@ -10,6 +10,9 @@ jobs:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest, macos-13, macos-latest ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -49,6 +52,26 @@ jobs:
|
||||
files: vripper-web/target/vripper-noarch-web-${{ github.event.release.tag_name }}.jar
|
||||
# End building WEB jar in ubuntu only
|
||||
|
||||
# Start building docker image in ubuntu only
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- if: matrix.os == 'ubuntu-latest'
|
||||
name: Build Docker image
|
||||
run: |
|
||||
mkdir docker_build && cp Dockerfile docker_build/Dockerfile && cp vripper-web/target/vripper-noarch-web-${{ github.event.release.tag_name }}.jar docker_build/vripper-web.jar
|
||||
docker build -t ghcr.io/dev-claw/vripper-web docker_build
|
||||
docker tag ghcr.io/dev-claw/vripper-web:latest ghcr.io/dev-claw/vripper-web:${{ github.event.release.tag_name }}
|
||||
docker push ghcr.io/dev-claw/vripper-web:latest
|
||||
docker push ghcr.io/dev-claw/vripper-web:${{ github.event.release.tag_name }}
|
||||
|
||||
# End building docker image in ubuntu only
|
||||
|
||||
- name: Prepare Packaging
|
||||
run: |
|
||||
cp vripper-gui/target/vripper-noarch-gui-${{ github.event.release.tag_name }}.jar jpackage/jar/vripper-gui.jar
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
FROM eclipse-temurin:21-jre-ubi9-minimal
|
||||
WORKDIR /opt/vripper-web
|
||||
ENTRYPOINT ["java", "-jar", "vripper-web.jar"]
|
||||
ADD vripper-web.jar /opt/vripper-web/vripper-web.jar
|
||||
@@ -15,14 +15,31 @@ ETH: 0xDdac82B16dC5E3D742fc915ffF583D8548A301cA
|
||||
|
||||
BTC: bc1qcqudnkrndwyadsjwrxww42svkf8trnzx3c8vlr
|
||||
|
||||
## Requirements
|
||||
Direct access to `vipergirls.to` domain.
|
||||
## Supported Image Hosts
|
||||
|
||||
The following hosts are supported:
|
||||
|
||||
* acidimg.cc
|
||||
* imagetwist.com
|
||||
* imagezilla.com
|
||||
* imgspice.com
|
||||
* imagebam.com
|
||||
* imgbox.com
|
||||
* imx.to
|
||||
* pimpandhost.com
|
||||
* pixhost.to
|
||||
* pixxxels.cc
|
||||
* turboimagehost.com
|
||||
* postimg.cc
|
||||
* imagevenue.com
|
||||
* pixroute.to
|
||||
* vipr.im
|
||||
|
||||
## Installing VRipper
|
||||
|
||||
<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 (Intel) | <img src="https://github.com/stashapp/stash/raw/develop/docs/readme_assets/mac_logo.svg" width="100%" height="75"> macOS (Apple silicon) | <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/dev-claw/vripper-project/releases/download/6.5.5/vripper-windows-installer-6.5.5.exe) <br /> [Installer (MSI)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-windows-installer-6.5.5.msi) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-windows-portable-6.5.5.zip) | [Installer (DMG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-macos-6.5.5.x86_64.dmg) <br /> [Installer (PKG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-macos-6.5.5.x86_64.pkg) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-macos-portable-6.5.5.x86_64.zip) | [Installer (DMG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-macos-6.5.5.arm64.dmg) <br /> [Installer (PKG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-macos-6.5.5.arm64.pkg) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-macos-portable-6.5.5.arm64.zip) | [Linux (amd64) (DEB)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-linux-6.5.3_amd64.deb) <br /> [Linux (x86_64) (RPM)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-linux-6.5.5.x86_64.rpm) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-linux-portable-6.5.5.zip) | [Java GUI (noarch)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-noarch-gui-6.5.5.jar) <br /> [Java Web (noarch)](https://github.com/dev-claw/vripper-project/releases/download/6.5.5/vripper-noarch-web-6.5.5.jar)
|
||||
[Installer (EXE)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-windows-installer-6.5.8.exe) <br /> [Installer (MSI)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-windows-installer-6.5.8.msi) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-windows-portable-6.5.8.zip) | [Installer (DMG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-6.5.8.x86_64.dmg) <br /> [Installer (PKG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-6.5.8.x86_64.pkg) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-portable-6.5.8.x86_64.zip) | [Installer (DMG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-6.5.8.arm64.dmg) <br /> [Installer (PKG)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-6.5.8.arm64.pkg) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-macos-portable-6.5.8.arm64.zip) | [Linux (amd64) (DEB)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-linux-6.5.3_amd64.deb) <br /> [Linux (x86_64) (RPM)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-linux-6.5.8.x86_64.rpm) <br /> [Portable (ZIP)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-linux-portable-6.5.8.zip) | [Java GUI (noarch)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-noarch-gui-6.5.8.jar) <br /> [Java Web (noarch)](https://github.com/dev-claw/vripper-project/releases/download/6.5.8/vripper-noarch-web-6.5.8.jar)
|
||||
|
||||
Source code and previous versions are available on
|
||||
the [Releases page](https://github.com/dev-claw/vripper-project/releases).
|
||||
@@ -31,6 +48,90 @@ Application data (application logs, settings and persisted data) is stored in:
|
||||
* Windows --> `C:\USERS\<your Windows username>\vripper`
|
||||
* Linux and macOS --> `HOME_FOLDER/.config/vripper`
|
||||
|
||||
## Docker Support
|
||||
|
||||
The project is supplied as a container image and can be run locally or in production using Docker. By default:
|
||||
|
||||
- **gRPC server/client** uses port **30000**
|
||||
- **Web UI** runs on port **8080**
|
||||
|
||||
### Run with `docker run`
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 30000:30000 \
|
||||
-p 8080:8080 \
|
||||
-e GRPC_ENABLED=true \
|
||||
-e GRPC_PASSPHRASE=my-secure-secret \
|
||||
ghcr.io/OWNER/IMAGE_NAME:latest
|
||||
```
|
||||
|
||||
### Run with Docker Compose
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
image: ghcr.io/dev-claw/vripper-web:latest
|
||||
environment:
|
||||
- GRPC_ENABLED=true
|
||||
- GRPC_PASSPHRASE=super-secret-passphrase
|
||||
ports:
|
||||
- "30000:30000"
|
||||
- "8080:8080"
|
||||
```
|
||||
|
||||
> **Tip:** For production, prefer storing the passphrase as a secret (see `Security Notes` below) rather than writing it
|
||||
> directly in the Compose file.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
The container supports these environment variables for enabling and securing the gRPC server.
|
||||
|
||||
### `GRPC_ENABLED`
|
||||
|
||||
- **Type:** String (interpreted as Boolean)
|
||||
- **Accepted values:** `true` (case-insensitive) to enable; any other value or unset → disabled
|
||||
- **Default:** `false` (gRPC disabled)
|
||||
- **Behavior:**
|
||||
- When `GRPC_ENABLED=true`, the application will attempt to start the gRPC server (default port: `30000`).
|
||||
- When absent or set to anything other than `true`, the gRPC server will not be started.
|
||||
- **Example (shell):**
|
||||
|
||||
```bash
|
||||
export GRPC_ENABLED=true
|
||||
```
|
||||
|
||||
- **Example (Docker Compose):**
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- GRPC_ENABLED=true
|
||||
```
|
||||
|
||||
- **Notes / Best practices:**
|
||||
- Treat the value as a simple on/off toggle. To avoid ambiguity, set explicitly to `true` or `false`.
|
||||
- If you enable gRPC, make sure any required authentication (see `GRPC_PASSPHRASE`) is also configured.
|
||||
|
||||
### `GRPC_PASSPHRASE`
|
||||
|
||||
- **Type:** String
|
||||
- **Default:** *no default* (empty / unset)
|
||||
- **Required if:** `GRPC_ENABLED=true`
|
||||
- **Purpose:** Shared secret used to authenticate gRPC clients. The application should validate that the passphrase is
|
||||
present when gRPC is enabled and use it to verify client connections.
|
||||
- **Example (shell):**
|
||||
|
||||
```bash
|
||||
export GRPC_PASSPHRASE="my-very-strong-passphrase"
|
||||
```
|
||||
|
||||
## Default Ports
|
||||
|
||||
- **gRPC server/client:** `30000`
|
||||
- **Web UI:** `8080`
|
||||
|
||||
You can map these ports when running the container. The application supports overriding these defaults with environment
|
||||
variables (`GRPC_PORT`, `SERVER_PORT`), configure them as needed.
|
||||
|
||||
## Important Note About Proxies
|
||||
The use of proxies within VRipper is worthless, please stop using them **for now**. You will get **403 error** codes.
|
||||
@@ -39,24 +140,6 @@ The use of proxies within VRipper is worthless, please stop using them **for now
|
||||
|
||||
If your ISP is blocking access to `vipergirls.to` domain, consider using a VPN or [Cloudflare WARP](https://one.one.one.one/) to bypass the block.
|
||||
|
||||
## Supported Image Hosts
|
||||
The following hosts are supported:
|
||||
* acidimg.cc
|
||||
* imagetwist.com
|
||||
* imagezilla.com
|
||||
* imgspice.com
|
||||
* imagebam.com
|
||||
* imgbox.com
|
||||
* imx.to
|
||||
* pimpandhost.com
|
||||
* pixhost.to
|
||||
* pixxxels.cc
|
||||
* turboimagehost.com
|
||||
* postimg.cc
|
||||
* imagevenue.com
|
||||
* pixroute.to
|
||||
* vipr.im
|
||||
|
||||
## Instructions to run from Jar file
|
||||
You need Java 21+, you can download from https://adoptium.net/
|
||||
|
||||
@@ -72,7 +155,6 @@ For the WEB app
|
||||
|
||||
Application data (application logs, settings and persisted data) is stored in the location where you launched the jar for both GUI and WEB
|
||||
|
||||
|
||||
## How to build
|
||||
|
||||
You need JDK 21 and a recent version of maven 3.8.x+
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<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>6.5.5</revision>
|
||||
<revision>6.5.8</revision>
|
||||
<kotlin.version>2.0.21</kotlin.version>
|
||||
<slf4j.version>2.0.16</slf4j.version>
|
||||
<logback.version>1.5.12</logback.version>
|
||||
|
||||
@@ -44,8 +44,9 @@ internal class AcidimgHost(
|
||||
)
|
||||
)
|
||||
)
|
||||
it.setAbsoluteRequestUri(true)
|
||||
}.also { context.requests.add(it) }
|
||||
log.debug(String.format("Requesting %s", httpPost.uri))
|
||||
log.info("{}", httpPost)
|
||||
val doc = try {
|
||||
httpService.client.execute(
|
||||
httpPost, context.httpContext
|
||||
|
||||
@@ -157,8 +157,11 @@ internal abstract class Host(
|
||||
|
||||
@Throws(HostException::class)
|
||||
fun head(url: String, context: ImageDownloadContext): Array<Header> {
|
||||
val httpHead = HttpHead(url).also { context.requests.add(it) }
|
||||
log.debug(String.format("Requesting %s", url))
|
||||
val httpHead = HttpHead(url).also {
|
||||
it.setAbsoluteRequestUri(true)
|
||||
context.requests.add(it)
|
||||
}
|
||||
log.info("{}", httpHead)
|
||||
return httpService.client.execute(
|
||||
httpHead,
|
||||
context.httpContext
|
||||
@@ -177,8 +180,11 @@ internal abstract class Host(
|
||||
transformer: (ClassicHttpResponse) -> T
|
||||
): T {
|
||||
val httpGet =
|
||||
HttpGet(url).also { it.addHeader("Referer", context.imageEntity.url) }.also { context.requests.add(it) }
|
||||
log.debug(String.format("Requesting %s", url))
|
||||
HttpGet(url).also {
|
||||
it.addHeader("Referer", context.imageEntity.url)
|
||||
it.setAbsoluteRequestUri(true)
|
||||
}.also { context.requests.add(it) }
|
||||
log.info("{}", httpGet)
|
||||
return httpService.client.execute(httpGet, context.httpContext) {
|
||||
if (it.code / 100 != 2) {
|
||||
throw DownloadException("Server returned code ${it.code}")
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
package me.vripper.host
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import me.vripper.entities.ImageEntity
|
||||
import me.vripper.exception.HostException
|
||||
import me.vripper.services.DownloadService.ImageDownloadContext
|
||||
import me.vripper.utilities.ApplicationProperties.IMX_SUBDOMAINS
|
||||
import me.vripper.utilities.LoggerDelegate
|
||||
import me.vripper.utilities.RequestLimit
|
||||
import org.apache.hc.client5.http.classic.methods.HttpHead
|
||||
import org.apache.hc.client5.http.config.ConnectionConfig
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClients
|
||||
import org.apache.hc.client5.http.impl.io.BasicHttpClientConnectionManager
|
||||
@@ -16,10 +11,6 @@ import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
internal class ImxHost : Host("imx.to", 8) {
|
||||
companion object {
|
||||
private val resolvedHosts: Cache<Long, String> =
|
||||
Caffeine.newBuilder().expireAfterAccess(5, TimeUnit.MINUTES).build()
|
||||
}
|
||||
|
||||
private val log by LoggerDelegate()
|
||||
|
||||
@@ -39,40 +30,14 @@ internal class ImxHost : Host("imx.to", 8) {
|
||||
): Pair<String, String> {
|
||||
log.debug("Resolving name and image url for ${image.url}")
|
||||
val imgTitle = String.format("IMG_%04d", image.index + 1)
|
||||
synchronized(image.postId.toString().intern()) {
|
||||
val subDomain = resolvedHosts.getIfPresent(image.postId)
|
||||
if (subDomain != null) {
|
||||
val imgUrl = findPattern(image, subDomain)
|
||||
return Pair(
|
||||
imgTitle.ifEmpty { getDefaultImageName(imgUrl) }, imgUrl
|
||||
)
|
||||
} else {
|
||||
IMX_SUBDOMAINS.forEach { subDomain ->
|
||||
val imgUrl = findPattern(image, subDomain)
|
||||
val result = runCatching {
|
||||
RequestLimit.getPermit(1)
|
||||
val httpHead = HttpHead(imgUrl).also { context.requests.add(it) }
|
||||
failFastHttpClient.execute(httpHead) { response ->
|
||||
if (response.code / 100 != 2) {
|
||||
throw HostException("Invalid response code ${response.code}")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result.isSuccess) {
|
||||
resolvedHosts.put(image.postId, subDomain)
|
||||
return Pair(
|
||||
imgTitle.ifEmpty { getDefaultImageName(imgUrl) }, imgUrl
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw HostException("Unable to find full size image for ${image.url}")
|
||||
val imgUrl = findPattern(image)
|
||||
return Pair(
|
||||
imgTitle.ifEmpty { getDefaultImageName(imgUrl) }, imgUrl
|
||||
)
|
||||
}
|
||||
|
||||
private fun findPattern(image: ImageEntity, subDomain: String): String = image.thumbUrl
|
||||
.replace("imx.to", subDomain)
|
||||
.replace("upload/small/", "i/")
|
||||
.replace("u/t/", "i/")
|
||||
.replace("t/", "i/")
|
||||
private fun findPattern(image: ImageEntity): String = image.thumbUrl
|
||||
.replace("http:", "https:")
|
||||
.replace("upload/small/", "u/i/")
|
||||
.replace("u/t/", "u/i/")
|
||||
}
|
||||
@@ -58,8 +58,9 @@ internal class VGAuthService(
|
||||
BasicNameValuePair("vb_login_md5password", password)
|
||||
)
|
||||
)
|
||||
it.setAbsoluteRequestUri(true)
|
||||
}
|
||||
log.info("Authenticating: ${postAuth.uri}")
|
||||
log.info("Authenticating: {}", postAuth)
|
||||
try {
|
||||
val context = HttpClientContext.create().apply {
|
||||
cookieStore =
|
||||
|
||||
@@ -41,10 +41,10 @@ internal class FetchMetadataTask(
|
||||
it.setParameter(
|
||||
"p", postId.toString()
|
||||
)
|
||||
}.build())
|
||||
}.build()).also { it.setAbsoluteRequestUri(true) }
|
||||
|
||||
RequestLimit.getPermit(1)
|
||||
log.debug("Requesting {}", httpGet.uri)
|
||||
log.info("{}", httpGet)
|
||||
val response = httpService.client.execute(httpGet, vgAuthService.createVgContext()) {
|
||||
if (it.code / 100 != 2) {
|
||||
throw DownloadException("Unexpected response code '${it.code}' for $httpGet")
|
||||
|
||||
@@ -40,9 +40,10 @@ internal class LeaveThanksTask(
|
||||
BasicNameValuePair("securitytoken", postEntity.token)
|
||||
)
|
||||
)
|
||||
it.setAbsoluteRequestUri(true)
|
||||
}
|
||||
RequestLimit.getPermit(1)
|
||||
log.info("Posting {}", postThanks.uri)
|
||||
log.info("Posting {}", postThanks)
|
||||
cm.client.execute(postThanks, context) { response ->
|
||||
if (response.code / 100 != 2) {
|
||||
throw VripperException("Unexpected response code '${response.code}' for $postThanks")
|
||||
|
||||
@@ -32,7 +32,7 @@ internal class PostLookupAPIParser(private val threadId: Long, private val postI
|
||||
it.setParameter(
|
||||
"p", postId.toString()
|
||||
)
|
||||
}.build())
|
||||
}.build()).also { it.setAbsoluteRequestUri(true) }
|
||||
val threadLookupAPIResponseHandler = ThreadLookupAPIResponseHandler()
|
||||
Tasks.increment()
|
||||
return try {
|
||||
@@ -42,7 +42,7 @@ internal class PostLookupAPIParser(private val threadId: Long, private val postI
|
||||
)
|
||||
}.get(CheckedSupplier {
|
||||
RequestLimit.getPermit(1)
|
||||
log.info("Requesting {}", httpGet.uri)
|
||||
log.info("{}", httpGet)
|
||||
httpService.client.execute(
|
||||
httpGet,
|
||||
vgAuthService.createClickContext()
|
||||
|
||||
@@ -33,7 +33,7 @@ internal class ThreadLookupAPIParser(private val threadId: Long) : KoinComponent
|
||||
"t",
|
||||
threadId.toString()
|
||||
)
|
||||
}.build())
|
||||
}.build()).also { it.setAbsoluteRequestUri(true) }
|
||||
val threadLookupAPIResponseHandler = ThreadLookupAPIResponseHandler()
|
||||
Tasks.increment()
|
||||
return try {
|
||||
@@ -44,7 +44,7 @@ internal class ThreadLookupAPIParser(private val threadId: Long) : KoinComponent
|
||||
)
|
||||
}.get(CheckedSupplier {
|
||||
RequestLimit.getPermit(1)
|
||||
log.info("Requesting {}", httpGet.uri)
|
||||
log.info("{}", httpGet)
|
||||
cm.client.execute(
|
||||
httpGet, vgAuthService.createClickContext()
|
||||
) { response ->
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
spring.liquibase.enabled=false
|
||||
grpc.enabled=true
|
||||
server.port=8080
|
||||
grpc.enabled=false
|
||||
grpc.passphrase=
|
||||
grpc.port=30000
|
||||
Reference in New Issue
Block a user