Compare commits

..
14 Commits
Author SHA1 Message Date
death-claw 9b7818c5a7 v2.2.1 2019-10-01 22:44:19 +01:00
death-claw 1f2dcff353 Update dependency list for deb package 2019-10-01 22:42:28 +01:00
death-claw 4d82622fdb v2.2.0 2019-10-01 21:26:27 +01:00
death-claw e7393caf01 Fix 'Start' on pending items
Upgrade to Java 11
Embedd JRE within the app
2019-10-01 21:24:02 +01:00
death-claw 8e5cb958ea v2.1.0 2019-09-30 21:12:56 +01:00
death-claw bc2cf02ec6 Add a create a subfolder per thread option 2019-09-30 21:06:40 +01:00
death-claw 7a8ecb306f v2.0.4 2019-09-27 20:52:25 +01:00
death-claw 76c3901afa Fix some issues with ImxHost 2019-09-27 20:50:51 +01:00
death-claw f16b2227a1 v2.0.3 2019-09-23 17:38:36 +01:00
death-claw d8d7ca5408 Update deb dependencies 2019-09-23 17:36:10 +01:00
death-claw 28c6b71e99 v2.0.2 2019-09-23 16:46:37 +01:00
death-claw c40d3e2377 Add portable mode 2019-09-23 16:43:52 +01:00
death-claw 2217fea0ad v2.0.1 2019-09-22 21:55:30 +01:00
death-claw 94746441b5 Update app name 2019-09-22 21:48:35 +01:00
28 changed files with 139 additions and 31 deletions
+1
View File
@@ -2,4 +2,5 @@
/**/*/node_modules
/**/*/target
/**/*/build-dir
/**/*/java-runtime
.idea
+31
View File
@@ -1,5 +1,36 @@
# Changelog
## [2.2.1] - 2019-10-01
### Changed
- Update dependency list for deb package
## [2.2.0] - 2019-10-01
### Added
- Embedd JRE within the app
### Changed
- Fix 'Start' on pending items
- Upgrade to Java 11
## [2.1.0] - 2019-09-30
### Added
- Add a create a subfolder per thread option
## [2.0.4] - 2019-09-27
### Changed
- Fix some issues with ImxHost
## [2.0.3] - 2019-09-23
### Changed
- Update deb dependencies
## [2.0.2] - 2019-09-23
### Changed
- Add portable mode
## [2.0.1] - 2019-09-22
### Changed
- Change app name
## [2.0.0] - 2019-09-22
### Added
- Add sub folder option
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>tn.mnlr</groupId>
<artifactId>vripper</artifactId>
<version>2.0.0</version>
<version>2.2.1</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
+2 -1
View File
@@ -73,7 +73,8 @@ getPort().then(port => {
ipcMain.on("get-port", event => {
event.reply("port", port);
});
vripperServer = spawn("java", [
vripperServer = spawn(appDir !== undefined ? path.join(appDir, "java-runtime/bin/java") :
path.join(app.getPath('exe'), "../java-runtime/bin/java"), [
"-Xms256m",
"-Xmx1024m",
"-Dvripper.server.port=" + port,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vripper-electron",
"version": "2.0.0",
"version": "2.2.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+23 -3
View File
@@ -1,13 +1,14 @@
{
"name": "vripper-electron",
"version": "2.0.0",
"description": "A ripper for vipergirls.to",
"version": "2.2.1",
"description": "A ripper for vipergirls.to built using web technolgies",
"main": "main.js",
"author": "death-claw <53543762+death-claw@users.noreply.github.com>",
"homepage": "https://github.com/death-claw/vripper-project",
"license": "ISC",
"build": {
"appId": "tn.mnlr.vripper",
"productName": "V Ripper",
"files": [
"**/*",
"!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
@@ -30,6 +31,10 @@
{
"from": "../vripper-server/target/vripper-server-${version}-electron.jar",
"to": "bin/vripper-server.jar"
},
{
"from": "java-runtime",
"to": "java-runtime"
}
],
"win": {
@@ -38,14 +43,29 @@
"nsis"
]
},
"nsis": {
"createDesktopShortcut": "always",
"oneClick": false,
"perMachine": false
},
"linux": {
"synopsis": "A ripper for vipergirls.to",
"synopsis": "vipergirls.to ripper",
"category": "Utility",
"packageCategory": "Utility",
"icon": "icons",
"target": [
"deb"
]
},
"deb": {
"depends": [
"gconf2",
"gconf-service",
"libnotify4",
"libappindicator1",
"libxtst6",
"libnss3"
]
}
},
"scripts": {
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>tn.mnlr</groupId>
<artifactId>vripper</artifactId>
<version>2.0.0</version>
<version>2.2.1</version>
</parent>
<artifactId>vripper-electron</artifactId>
<name>vripper-electron</name>
+4 -1
View File
@@ -2,7 +2,7 @@ const cheerio = require('cheerio');
const fs = require('fs');
const rimraf = require("rimraf");
const copydir = require("copy-dir");
const { execSync } = require("child_process");
rimraf.sync('./build/vripper-ui');
copydir.sync('../vripper-ui/dist/vripper-ui', './build/vripper-ui', {});
@@ -16,3 +16,6 @@ $('body').prepend(`<script>require('../renderer.js')</script>`);
fs.writeFileSync('./build/vripper-ui/index.html', $.html());
console.log('Building runtime environment');
rimraf.sync('java-runtime');
execSync('jlink --no-header-files --no-man-pages --compress=2 --strip-debug --add-modules java.base,java.desktop,java.instrument,java.management,java.naming,java.prefs,java.rmi,java.scripting,java.security.jgss,java.sql,jdk.httpserver,jdk.unsupported,jdk.crypto.ec --output java-runtime');
+2 -2
View File
@@ -5,14 +5,14 @@
<parent>
<groupId>tn.mnlr</groupId>
<artifactId>vripper</artifactId>
<version>2.0.0</version>
<version>2.2.1</version>
</parent>
<artifactId>vripper-server</artifactId>
<name>vripper-server</name>
<description>vripper-server</description>
<properties>
<java.version>1.8</java.version>
<java.version>11</java.version>
</properties>
<dependencies>
@@ -1,8 +1,10 @@
package tn.mnlr.vripper;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -12,6 +14,7 @@ import tn.mnlr.vripper.services.AppSettingsService;
import tn.mnlr.vripper.services.PersistenceService;
import tn.mnlr.vripper.services.VipergirlsAuthService;
import javax.annotation.PostConstruct;
import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -21,7 +24,7 @@ public class VripperApplication {
private static final Logger logger = LoggerFactory.getLogger(VripperApplication.class);
public static final String dataPath = System.getProperty("user.home", ".") + File.separator + ".vripper" + File.separator + "data.json";
// public static final String dataPath = System.getProperty("user.home", ".") + File.separator + ".vripper" + File.separator + "data.json";
public static final ExecutorService commonExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
@@ -46,6 +49,17 @@ public class VripperApplication {
@Autowired
private AppSettingsService appSettingsService;
@Value("${base.dir}")
private String baseDir;
@Getter
private String dataPath;
@PostConstruct
public void init() {
dataPath = baseDir + File.separator + ".vripper" + File.separator + "data.json";
}
@Override
public void run(String... args) {
@@ -26,6 +26,8 @@ public class Post {
private String postId;
private String threadTitle;
private String threadId;
private String title;
@@ -46,7 +48,7 @@ public class Post {
private String forum;
public Post(String title, String url, List<Image> images, Map<String, Object> metadata, String postId, String threadId, String forum) {
public Post(String title, String url, List<Image> images, Map<String, Object> metadata, String postId, String threadId, String threadTitle, String forum) {
this.title = title;
this.url = url;
this.images = images;
@@ -54,6 +56,7 @@ public class Post {
this.postId = postId;
this.forum = forum;
this.threadId = threadId;
this.threadTitle = threadTitle;
if (this.images.contains(null)) {
System.out.println("Oops");
}
@@ -41,25 +41,34 @@ public class ImxHost extends Host {
}
@Override
protected void setNameAndUrl(final String url, final ImageFileData imageFileData) throws HostException {
protected void setNameAndUrl(final String _url, final ImageFileData imageFileData) throws HostException {
Document doc = getResponse(url).getDocument();
String url = _url.replace("http://", "https://");
Response resp = getResponse(url);
Document doc = resp.getDocument();
Node contDiv;
String value = null;
try {
logger.info(String.format("Looking for xpath expression %s in %s", CONTINUE_BUTTON_XPATH, url));
contDiv = xpathService.getAsNode(doc, CONTINUE_BUTTON_XPATH);
Node node = contDiv.getAttributes().getNamedItem("value");
if (node != null) {
value = node.getTextContent();
}
} catch (XpathException e) {
throw new HostException(e);
}
if (contDiv != null) {
if (value == null) {
throw new HostException("Failed to obtain value attribute from continue input");
}
logger.info(String.format("Click button found for %s", url));
HttpClient client = cm.getClient().build();
HttpPost httpPost = cm.buildHttpPost(url);
httpPost.addHeader("Referer", url);
List<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("imgContinue", "Continue to image ... "));
params.add(new BasicNameValuePair("imgContinue", value));
try {
httpPost.setEntity(new UrlEncodedFormEntity(params));
} catch (Exception e) {
@@ -37,6 +37,7 @@ public class AppSettingsService {
private final String DESKTOP_CLIPBOARD = "DESKTOP_CLIPBOARD";
private final String FORCE_ORDER = "FORCE_ORDER";
private final String SUBFOLDER = "SUBFOLDER";
private final String THREADSUBFOLDER = "THREADSUBFOLDER";
private final String CLEAR = "CLEAR";
private final String DARK_THEME = "DARK_THEME";
@@ -49,6 +50,7 @@ public class AppSettingsService {
private boolean vThanks;
private boolean desktopClipboard;
private boolean subLocation;
private boolean threadSubLocation;
private boolean forceOrder;
private boolean clearCompleted;
private boolean darkTheme;
@@ -73,6 +75,7 @@ public class AppSettingsService {
desktopClipboard = prefs.getBoolean(DESKTOP_CLIPBOARD, false);
forceOrder = prefs.getBoolean(FORCE_ORDER, false);
subLocation = prefs.getBoolean(SUBFOLDER, false);
threadSubLocation = prefs.getBoolean(THREADSUBFOLDER, false);
clearCompleted = prefs.getBoolean(CLEAR, false);
darkTheme = prefs.getBoolean(DARK_THEME, false);
}
@@ -90,6 +93,7 @@ public class AppSettingsService {
prefs.putBoolean(DESKTOP_CLIPBOARD, desktopClipboard);
prefs.putBoolean(FORCE_ORDER, forceOrder);
prefs.putBoolean(SUBFOLDER, subLocation);
prefs.putBoolean(THREADSUBFOLDER, threadSubLocation);
prefs.putBoolean(CLEAR, clearCompleted);
prefs.putBoolean(DARK_THEME, darkTheme);
@@ -163,10 +167,12 @@ public class AppSettingsService {
private boolean forceOrder;
@JsonProperty("subLocation")
private boolean subLocation;
@JsonProperty("threadSubLocation")
private boolean threadSubLocation;
@JsonProperty("clearCompleted")
private boolean clearCompleted;
public Settings(String downloadPath, int maxThreads, boolean autoStart, boolean vLogin, String vUsername, String vPassword, boolean vThanks, boolean desktopClipboard, boolean forceOrder, boolean subLocation, boolean clearCompleted) {
public Settings(String downloadPath, int maxThreads, boolean autoStart, boolean vLogin, String vUsername, String vPassword, boolean vThanks, boolean desktopClipboard, boolean forceOrder, boolean subLocation, boolean threadSubLocation, boolean clearCompleted) {
this.downloadPath = downloadPath;
this.maxThreads = maxThreads;
this.autoStart = autoStart;
@@ -177,6 +183,7 @@ public class AppSettingsService {
this.desktopClipboard = desktopClipboard;
this.forceOrder = forceOrder;
this.subLocation = subLocation;
this.threadSubLocation = threadSubLocation;
this.clearCompleted = clearCompleted;
}
}
@@ -7,6 +7,7 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.stereotype.Service;
@@ -38,6 +39,7 @@ public class ConnectionManager {
public HttpClientBuilder getClient() {
return HttpClients.custom()
.setConnectionManager(pcm)
.setRedirectStrategy(new LaxRedirectStrategy())
.setRetryHandler(new DefaultHttpRequestRetryHandler(5, true))
.setDefaultRequestConfig(rc);
}
@@ -22,7 +22,9 @@ public class PathService {
public final File getDownloadDestinationFolder(String postId) {
Post post = appStateService.getCurrentPosts().get(postId);
String postTitle = post.getTitle();
String threadTitle = post.getThreadTitle();
File sourceFolder = appSettingsService.isSubLocation() ? new File(appSettingsService.getDownloadPath(), sanitize(post.getForum())) : new File(appSettingsService.getDownloadPath());
sourceFolder = appSettingsService.isThreadSubLocation() ? new File(sourceFolder, threadTitle) : sourceFolder;
return new File(sourceFolder, sanitize(postTitle + "_" + postId));
}
@@ -39,6 +39,9 @@ public class PersistenceService {
@Autowired
private AppStateService stateService;
@Autowired
private VripperApplication.AppCommandRunner appCommandRunner;
private ObjectMapper om;
private Disposable subscription;
@@ -61,7 +64,7 @@ public class PersistenceService {
@PostConstruct
public void init() {
File dataFile = new File(VripperApplication.dataPath);
File dataFile = new File(appCommandRunner.getDataPath());
if (!dataFile.exists()) {
try {
dataFile.getParentFile().mkdirs();
@@ -95,7 +98,7 @@ public class PersistenceService {
public void persist(Map<String, Post> currentPosts) {
try (PrintWriter out = new PrintWriter(VripperApplication.dataPath, "UTF-8")) {
try (PrintWriter out = new PrintWriter(appCommandRunner.getDataPath(), "UTF-8")) {
out.print(om.writeValueAsString(currentPosts));
} catch (IOException e) {
logger.error("Failed to persist app state", e);
@@ -109,11 +112,11 @@ public class PersistenceService {
} catch (IOException e) {
logger.error("Failed to read app state", e);
long timestamp = new Date().getTime();
logger.warn(String.format("trying to rename old data file from %s to %s", VripperApplication.dataPath, VripperApplication.dataPath + "." + timestamp + ".old"));
logger.warn(String.format("trying to rename old data file from %s to %s", appCommandRunner.getDataPath(), appCommandRunner.getDataPath() + "." + timestamp + ".old"));
try {
Files.move(new File(VripperApplication.dataPath).toPath(), new File(VripperApplication.dataPath + "." + timestamp + ".old").toPath());
Files.move(new File(appCommandRunner.getDataPath()).toPath(), new File(appCommandRunner.getDataPath() + "." + timestamp + ".old").toPath());
} catch (IOException ex) {
logger.error(String.format("Failed to rename %s to %s", VripperApplication.dataPath, VripperApplication.dataPath + ".old"));
logger.error(String.format("Failed to rename %s to %s", appCommandRunner.getDataPath(), appCommandRunner.getDataPath() + ".old"));
SpringContext.close();
}
}
@@ -125,7 +128,7 @@ public class PersistenceService {
String jsonContent = null;
try {
jsonContent = Files.readAllLines(Paths.get(VripperApplication.dataPath), StandardCharsets.UTF_8).stream().collect(Collectors.joining());
jsonContent = Files.readAllLines(Paths.get(appCommandRunner.getDataPath()), StandardCharsets.UTF_8).stream().collect(Collectors.joining());
} catch (Exception e) {
logger.error("data file cannot be read, previous state cannot be restored", e);
SpringContext.close();
@@ -376,6 +376,7 @@ public class PostParser {
metadata,
postId,
threadId,
threadTitle,
forum
);
}
@@ -75,6 +75,7 @@ public class SettingsRestEndpoint {
this.settings.setDesktopClipboard(settings.isDesktopClipboard());
this.settings.setForceOrder(settings.isForceOrder());
this.settings.setSubLocation(settings.isSubLocation());
this.settings.setThreadSubLocation(settings.isThreadSubLocation());
this.settings.setClearCompleted(settings.isClearCompleted());
this.settings.save();
@@ -99,6 +100,7 @@ public class SettingsRestEndpoint {
settings.isDesktopClipboard(),
settings.isForceOrder(),
settings.isSubLocation(),
settings.isThreadSubLocation(),
settings.isClearCompleted()
);
}
@@ -0,0 +1 @@
base.dir=${user.home}
@@ -0,0 +1 @@
base.dir=${user.dir}
@@ -1,8 +1,10 @@
logging.level.org.springframework.web=INFO
logging.level.root=INFO
logging.level.org.apache.http=INFO
logging.file=${user.home}/.vripper/vripper.log
logging.file=${base.dir}/.vripper/vripper.log
server.port=${vripper.server.port:8080}
management.endpoints.web.exposure.include=shutdown
management.endpoint.shutdown.enabled=true
endpoints.shutdown.enabled=true
endpoints.shutdown.enabled=true
#spring.profiles.active=portable
spring.profiles.active=installer
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vripper-ui",
"version": "2.0.0",
"version": "2.2.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vripper-ui",
"version": "2.0.0",
"version": "2.2.1",
"scripts": {
"ng": "ng",
"start": "ng serve",
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>tn.mnlr</groupId>
<artifactId>vripper</artifactId>
<version>2.0.0</version>
<version>2.2.1</version>
</parent>
<artifactId>vripper-ui</artifactId>
<name>vripper-ui</name>
@@ -42,7 +42,6 @@
<button
(click)="restart()"
*ngIf="
postState.status === 'PENDING' ||
(postState.status === 'COMPLETE' && postState.progress !== 100) ||
postState.status === 'ERROR' ||
postState.status === 'STOPPED'
@@ -54,7 +53,7 @@
</button>
<button
(click)="stop()"
*ngIf="postState.status === 'DOWNLOADING' || postState.status === 'PARTIAL'"
*ngIf="postState.status === 'DOWNLOADING' || postState.status === 'PARTIAL' || postState.status === 'PENDING'"
mat-menu-item
>
<ng-container>
@@ -42,6 +42,11 @@
>Save posts in sub folders
</mat-checkbox>
<mat-checkbox *ngIf="generalSettingsForm.get('subLocation').value" color="primary"
formControlName="threadSubLocation" name="threadSubLocation" style="margin-left: 15px"
>Create a subfolder per thread
</mat-checkbox>
<mat-checkbox color="primary" formControlName="forceOrder" name="forceOrder"
>Force image ordering (prepend incremental numbers)
</mat-checkbox>
@@ -30,6 +30,7 @@ export class SettingsComponent implements OnInit {
autoStart: new FormControl(false),
forceOrder: new FormControl(false),
subLocation: new FormControl(false),
threadSubLocation: new FormControl(false),
clearCompleted: new FormControl(false),
vLogin: new FormControl(false),
vUsername: new FormControl(''),
+1 -1
View File
@@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>VripperUi</title>
<title>V Ripper</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">