mirror of
https://github.com/dev-claw/vripper-project.git
synced 2026-08-19 08:35:41 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67c605e640 | ||
|
|
8d4b878656 | ||
|
|
bf532dd1d0 | ||
|
|
eb1a125e59 | ||
|
|
256659a72d | ||
|
|
dbf3aa7e7d | ||
|
|
a75ce181ff | ||
|
|
37ae6b4ebf | ||
|
|
6631a6a95b | ||
|
|
918f8578ad | ||
|
|
10fa9f68ae | ||
|
|
6b5c9ccb0f |
+25
-2
@@ -1,5 +1,28 @@
|
||||
# Changelog
|
||||
|
||||
## [2.12.1] - 2020-05-22
|
||||
### Changed
|
||||
- Fix alternative name bug
|
||||
|
||||
## [2.12.0] - 2020-05-21
|
||||
### Changed
|
||||
- Use new user hash from the api
|
||||
|
||||
## [2.11.7] - 2020-05-08
|
||||
### Changed
|
||||
- Fix bug with paths creation
|
||||
- Move Open Link button
|
||||
- Rename rename all title
|
||||
|
||||
## [2.11.6] - 2020-05-04
|
||||
### Changed
|
||||
- PNG filenames are now correctly named
|
||||
- Fix issue with special characters in post titles
|
||||
|
||||
## [2.11.5] - 2020-05-03
|
||||
### Changed
|
||||
- Reduce rows buffer size
|
||||
|
||||
## [2.11.4] - 2020-05-03
|
||||
### Changed
|
||||
- Code cleanup
|
||||
@@ -11,7 +34,7 @@
|
||||
- UI fixes and enhancements
|
||||
### Added
|
||||
- Add menu entry to rename a gallery to first alternative title
|
||||
-Add menu on toolbar for massive rename to first alternative title
|
||||
- Add menu on toolbar for massive rename to first alternative title
|
||||
|
||||
## [2.11.2] - 2020-04-26
|
||||
### Changed
|
||||
@@ -350,4 +373,4 @@
|
||||
- Add real time communication for progress updates via websocket (SockJS)
|
||||
- Add settings for download path, max concurrent download, enable/disable integration with vipergirls
|
||||
- Add integration with vipergitls.to (possibility to auto leave thanks after grab post)
|
||||
- Add support for acidimg.cc, imagezilla.net, imgbox.com, imx.to, pixhost.to, turboimagehost.com
|
||||
- Add support for acidimg.cc, imagezilla.net, imgbox.com, imx.to, pixhost.to, turboimagehost.com
|
||||
|
||||
@@ -20,3 +20,5 @@ To build the desktop app:
|
||||
mvn clean install -Pelectron -DskipTests
|
||||
|
||||
Maven will automatically handle front end compilation. However, for development, you will need to install a recent version of nodejs on your system.
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SC7M5LNWZ528Q)
|
||||
@@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>tn.mnlr</groupId>
|
||||
<artifactId>vripper</artifactId>
|
||||
<version>2.11.4</version>
|
||||
<version>2.12.1</version>
|
||||
<packaging>pom</packaging>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vripper-electron",
|
||||
"version": "2.11.4",
|
||||
"version": "2.12.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vripper-electron",
|
||||
"version": "2.11.4",
|
||||
"version": "2.12.1",
|
||||
"description": "A ripper for vipergirls.to built using web technolgies",
|
||||
"main": "main.js",
|
||||
"author": "death-claw <53543762+death-claw@users.noreply.github.com>",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tn.mnlr</groupId>
|
||||
<artifactId>vripper</artifactId>
|
||||
<version>2.11.4</version>
|
||||
<version>2.12.1</version>
|
||||
</parent>
|
||||
<artifactId>vripper-electron</artifactId>
|
||||
<name>vripper-electron</name>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tn.mnlr</groupId>
|
||||
<artifactId>vripper</artifactId>
|
||||
<version>2.11.4</version>
|
||||
<version>2.12.1</version>
|
||||
</parent>
|
||||
<artifactId>vripper-server</artifactId>
|
||||
<name>vripper-server</name>
|
||||
|
||||
@@ -43,7 +43,7 @@ public class Post {
|
||||
|
||||
private Map<String, Object> metadata;
|
||||
|
||||
private AtomicInteger done = new AtomicInteger(0);
|
||||
private final AtomicInteger done = new AtomicInteger(0);
|
||||
|
||||
private int total;
|
||||
|
||||
@@ -53,6 +53,8 @@ public class Post {
|
||||
|
||||
private String forum;
|
||||
|
||||
private String securityToken;
|
||||
|
||||
@Setter
|
||||
private String postFolderName;
|
||||
|
||||
@@ -60,7 +62,7 @@ public class Post {
|
||||
this.appStateService = SpringContext.getBean(AppStateService.class);
|
||||
}
|
||||
|
||||
public Post(String title, String url, List<Image> images, Map<String, Object> metadata, String postId, String threadId, String threadTitle, String forum) throws PostParseException {
|
||||
public Post(String title, String url, List<Image> images, Map<String, Object> metadata, String postId, String threadId, String threadTitle, String forum, String securityToken) throws PostParseException {
|
||||
this();
|
||||
this.title = title;
|
||||
this.url = url;
|
||||
@@ -70,6 +72,7 @@ public class Post {
|
||||
this.forum = forum;
|
||||
this.threadId = threadId;
|
||||
this.threadTitle = threadTitle;
|
||||
this.securityToken = securityToken;
|
||||
this.hosts = this.images.stream().map(Image::getHost).map(Host::getHost).collect(Collectors.toSet());
|
||||
total = images.size();
|
||||
status = Status.STOPPED;
|
||||
|
||||
@@ -178,6 +178,19 @@ abstract public class Host {
|
||||
imageName = imageName.substring(0, imageName.length() - toReplace.length()) + ".jpg";
|
||||
}
|
||||
}
|
||||
} else if (reader.getFormatName().toUpperCase().equals("PNG")) {
|
||||
String imageNameLC = imageName.toLowerCase();
|
||||
if (!imageNameLC.endsWith("_png")) {
|
||||
imageName += ".png";
|
||||
} else {
|
||||
String toReplace = null;
|
||||
if (imageNameLC.endsWith("_png")) {
|
||||
toReplace = "_png";
|
||||
}
|
||||
if (toReplace != null) {
|
||||
imageName = imageName.substring(0, imageName.length() - toReplace.length()) + ".png";
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new HostException("Failed to guess image format", e);
|
||||
|
||||
@@ -46,7 +46,7 @@ public class PathService {
|
||||
}
|
||||
|
||||
public synchronized final void createDefaultPostFolder(Post post) {
|
||||
File sourceFolder = _getDownloadDestinationFolder(post.getForum(), post.getThreadTitle(), post.getTitle());
|
||||
File sourceFolder = _getDownloadDestinationFolder(post.getForum(), post.getThreadTitle(), sanitize(post.getTitle()));
|
||||
File destFolder = makeDirs(sourceFolder);
|
||||
post.setPostFolderName(destFolder.getName());
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public class PathService {
|
||||
if (post.getPostFolderName() == null) {
|
||||
return;
|
||||
}
|
||||
File newDestFolder = makeDirs(_getDownloadDestinationFolder(post.getForum(), post.getThreadTitle(), altName));
|
||||
File newDestFolder = makeDirs(_getDownloadDestinationFolder(post.getForum(), post.getThreadTitle(), sanitize(altName)));
|
||||
File currentDesFolder = getDownloadDestinationFolder(post);
|
||||
post.setPostFolderName(newDestFolder.getName());
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import tn.mnlr.vripper.host.Host;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static tn.mnlr.vripper.services.PostParser.VR_API;
|
||||
@@ -131,18 +132,25 @@ class VRPostParser {
|
||||
|
||||
private List<String> findTitleInContent(Node node) {
|
||||
List<String> altTitle = new ArrayList<>();
|
||||
findTitle(node, altTitle);
|
||||
findTitle(node, altTitle, new AtomicBoolean(true));
|
||||
return altTitle;
|
||||
}
|
||||
|
||||
private boolean findTitle(Node node, List<String> altTitle) {
|
||||
private void findTitle(Node node, List<String> altTitle, AtomicBoolean keepGoing) {
|
||||
if (!keepGoing.get()) {
|
||||
return;
|
||||
}
|
||||
if (node.getNodeName().equals("a") || node.getNodeName().equals("img")) {
|
||||
return false;
|
||||
keepGoing.set(false);
|
||||
return;
|
||||
}
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
for (int i = 0; i < node.getChildNodes().getLength(); i++) {
|
||||
Node item = node.getChildNodes().item(i);
|
||||
findTitle(item, altTitle);
|
||||
findTitle(item, altTitle, keepGoing);
|
||||
if (!keepGoing.get()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (node.getNodeType() == Node.TEXT_NODE) {
|
||||
@@ -151,7 +159,6 @@ class VRPostParser {
|
||||
altTitle.add(text);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +177,7 @@ class VRApiPostHandler extends DefaultHandler {
|
||||
private String threadTitle;
|
||||
private String postTitle;
|
||||
private String forum;
|
||||
private String userHash;
|
||||
private int previewCounter = 0;
|
||||
private int index = 0;
|
||||
private int imageCount;
|
||||
@@ -200,6 +208,9 @@ class VRApiPostHandler extends DefaultHandler {
|
||||
case "forum":
|
||||
forum = attributes.getValue("title").trim();
|
||||
break;
|
||||
case "user":
|
||||
userHash = attributes.getValue("hash").trim();
|
||||
break;
|
||||
case "thread":
|
||||
threadTitle = attributes.getValue("title").trim();
|
||||
break;
|
||||
@@ -249,7 +260,8 @@ class VRApiPostHandler extends DefaultHandler {
|
||||
postId,
|
||||
threadId,
|
||||
threadTitle,
|
||||
forum);
|
||||
forum,
|
||||
userHash);
|
||||
} catch (PostParseException e) {
|
||||
logger.error(String.format("Error occurred while parsing postId %s", postId));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import lombok.Getter;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.cookie.Cookie;
|
||||
@@ -17,13 +16,11 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.w3c.dom.Document;
|
||||
import tn.mnlr.vripper.entities.Post;
|
||||
import tn.mnlr.vripper.exception.VripperException;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@@ -32,13 +29,11 @@ public class VipergirlsAuthService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(VipergirlsAuthService.class);
|
||||
|
||||
private final ConnectionManager cm;
|
||||
private final HtmlProcessorService htmlProcessorService;
|
||||
private final XpathService xpathService;
|
||||
private final AppSettingsService appSettingsService;
|
||||
private final CommonExecutor commonExecutor;
|
||||
|
||||
@Getter
|
||||
private HttpClientContext context = HttpClientContext.create();
|
||||
private final HttpClientContext context = HttpClientContext.create();
|
||||
|
||||
@Getter
|
||||
private boolean authenticated = false;
|
||||
@@ -47,13 +42,11 @@ public class VipergirlsAuthService {
|
||||
private String loggedUser;
|
||||
|
||||
@Getter
|
||||
private PublishProcessor<String> loggedInUser = PublishProcessor.create();
|
||||
private final PublishProcessor<String> loggedInUser = PublishProcessor.create();
|
||||
|
||||
@Autowired
|
||||
public VipergirlsAuthService(ConnectionManager cm, HtmlProcessorService htmlProcessorService, XpathService xpathService, AppSettingsService appSettingsService, CommonExecutor commonExecutor) {
|
||||
public VipergirlsAuthService(ConnectionManager cm, AppSettingsService appSettingsService, CommonExecutor commonExecutor) {
|
||||
this.cm = cm;
|
||||
this.htmlProcessorService = htmlProcessorService;
|
||||
this.xpathService = xpathService;
|
||||
this.appSettingsService = appSettingsService;
|
||||
this.commonExecutor = commonExecutor;
|
||||
}
|
||||
@@ -95,14 +88,10 @@ public class VipergirlsAuthService {
|
||||
HttpPost postAuth = cm.buildHttpPost("https://vipergirls.to/login.php?do=login");
|
||||
List<NameValuePair> params = new ArrayList<>();
|
||||
params.add(new BasicNameValuePair("vb_login_username", username));
|
||||
params.add(new BasicNameValuePair("vb_login_password", ""));
|
||||
params.add(new BasicNameValuePair("vb_login_password_hint", "Password"));
|
||||
|
||||
params.add(new BasicNameValuePair("cookieuser", "1"));
|
||||
params.add(new BasicNameValuePair("securitytoken", "guest"));
|
||||
params.add(new BasicNameValuePair("do", "login"));
|
||||
params.add(new BasicNameValuePair("vb_login_md5password", password));
|
||||
params.add(new BasicNameValuePair("vb_login_md5password_utf", password));
|
||||
try {
|
||||
postAuth.setEntity(new UrlEncodedFormEntity(params));
|
||||
} catch (Exception e) {
|
||||
@@ -163,56 +152,21 @@ public class VipergirlsAuthService {
|
||||
}
|
||||
commonExecutor.getGeneralExecutor().submit(() -> {
|
||||
try {
|
||||
postThanks(post, getSecurityToken(post.getUrl()));
|
||||
postThanks(post);
|
||||
} catch (Exception e) {
|
||||
logger.error(String.format("Failed to leave a thanks for url %s, post id %s", post.getUrl(), post.getPostId()), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getSecurityToken(String url) throws VripperException {
|
||||
|
||||
String securityToken;
|
||||
|
||||
HttpGet httpGet = cm.buildHttpGet(url);
|
||||
httpGet.addHeader("Referer", "https://vipergirls.to/");
|
||||
httpGet.addHeader("Host", "vipergirls.to");
|
||||
|
||||
CloseableHttpClient client = cm.getClient().build();
|
||||
|
||||
try (CloseableHttpResponse response = client.execute(httpGet, context)) {
|
||||
|
||||
String postPage = EntityUtils.toString(response.getEntity());
|
||||
Document document = htmlProcessorService.clean(postPage);
|
||||
|
||||
String thanksUrl = xpathService
|
||||
.getAsNode(document, "//li[contains(@id,'post_')][not(contains(@id,'post_thank'))]//a[@class='post_thanks_button']")
|
||||
.getAttributes()
|
||||
.getNamedItem("href")
|
||||
.getTextContent()
|
||||
.trim();
|
||||
|
||||
securityToken = Arrays.stream(thanksUrl.split("&"))
|
||||
.filter(v -> v.startsWith("securitytoken"))
|
||||
.findAny()
|
||||
.orElse("")
|
||||
.replace("securitytoken=", "");
|
||||
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
} catch (Exception e) {
|
||||
throw new VripperException(e);
|
||||
}
|
||||
return securityToken;
|
||||
}
|
||||
|
||||
private void postThanks(Post post, String securityKey) throws VripperException {
|
||||
private void postThanks(Post post) throws VripperException {
|
||||
|
||||
HttpPost postThanks = cm.buildHttpPost("https://vipergirls.to/post_thanks.php");
|
||||
List<NameValuePair> params = new ArrayList<>();
|
||||
params.add(new BasicNameValuePair("do", "post_thanks_add"));
|
||||
params.add(new BasicNameValuePair("using_ajax", "1"));
|
||||
params.add(new BasicNameValuePair("p", post.getPostId()));
|
||||
params.add(new BasicNameValuePair("securitytoken", securityKey));
|
||||
params.add(new BasicNameValuePair("securitytoken", post.getSecurityToken()));
|
||||
try {
|
||||
postThanks.setEntity(new UrlEncodedFormEntity(params));
|
||||
} catch (Exception e) {
|
||||
@@ -225,10 +179,16 @@ public class VipergirlsAuthService {
|
||||
CloseableHttpClient client = cm.getClient().build();
|
||||
|
||||
try (CloseableHttpResponse response = client.execute(postThanks, context)) {
|
||||
post.getMetadata().put(Post.METADATA.THANKED.name(), true);
|
||||
if (response.getStatusLine().getStatusCode() / 100 == 2) {
|
||||
post.getMetadata().put(Post.METADATA.THANKED.name(), true);
|
||||
}
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
} catch (Exception e) {
|
||||
throw new VripperException(e);
|
||||
}
|
||||
|
||||
if (!((Boolean) post.getMetadata().get(Post.METADATA.THANKED.name()))) {
|
||||
throw new VripperException("Failed to leave");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vripper-ui",
|
||||
"version": "2.11.4",
|
||||
"version": "2.12.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vripper-ui",
|
||||
"version": "2.11.4",
|
||||
"version": "2.12.1",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tn.mnlr</groupId>
|
||||
<artifactId>vripper</artifactId>
|
||||
<version>2.11.4</version>
|
||||
<version>2.12.1</version>
|
||||
</parent>
|
||||
<artifactId>vripper-ui</artifactId>
|
||||
<name>vripper-ui</name>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
<mat-card class="mat-elevation-z4">
|
||||
<mat-action-list>
|
||||
<button (click)="goTo()" ngxClipboard [cbContent]="postState.url" mat-list-item>
|
||||
<mat-icon>open_in_new</mat-icon>
|
||||
<span>Open link</span>
|
||||
</button>
|
||||
<button
|
||||
(click)="restart()"
|
||||
*ngIf="
|
||||
@@ -32,10 +28,6 @@
|
||||
<mat-icon>list</mat-icon>
|
||||
<span>Files</span>
|
||||
</button>
|
||||
<button *ngIf="isViewEnabled | async" (click)="openGallery()" mat-list-item>
|
||||
<mat-icon>photo_library</mat-icon>
|
||||
<span>View Photos</span>
|
||||
</button>
|
||||
<button (click)="useAltTitle()" mat-list-item>
|
||||
<mat-icon>edit</mat-icon>
|
||||
<span>Use alternative title</span>
|
||||
@@ -44,6 +36,10 @@
|
||||
<mat-icon>edit</mat-icon>
|
||||
<span>Rename</span>
|
||||
</button>
|
||||
<button (click)="goTo()" ngxClipboard [cbContent]="postState.url" mat-list-item>
|
||||
<mat-icon>open_in_new</mat-icon>
|
||||
<span>Open link</span>
|
||||
</button>
|
||||
<button (click)="open()" *ngIf="electronService.isElectronApp" mat-list-item>
|
||||
<mat-icon>folder</mat-icon>
|
||||
<span>Download Location</span>
|
||||
|
||||
@@ -35,7 +35,6 @@ export class PostsComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
headerCheckboxSelectionFilteredOnly: true
|
||||
}
|
||||
],
|
||||
rowBuffer: 200,
|
||||
rowHeight: 60,
|
||||
animateRows: true,
|
||||
rowSelection: 'multiple',
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
title="Stop selected">
|
||||
<mat-icon>pause</mat-icon>
|
||||
</button>
|
||||
<button (click)="rename()" [disabled]="disableSelection | async" aria-label="Rename selected" mat-icon-button
|
||||
title="Rename selected">
|
||||
<button (click)="rename()" [disabled]="disableSelection | async" aria-label="Rename selected to alternative title"
|
||||
mat-icon-button
|
||||
title="Rename selected to alternative title">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<div class="or-spacer-vertical left">
|
||||
|
||||
@@ -2,5 +2,5 @@ export const environment = {
|
||||
production: true,
|
||||
localhost: `${window.location.protocol}//${window.location.host}`,
|
||||
ws: `${window.location.protocol === 'http:' ? 'ws:' : 'wss:'}//${window.location.host}`,
|
||||
version: '2.11.4'
|
||||
version: '2.12.1'
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ export const environment = {
|
||||
production: false,
|
||||
localhost: 'http://localhost:8080',
|
||||
ws: 'ws://localhost:8080',
|
||||
version: '2.11.4'
|
||||
version: '2.12.1'
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user