mirror of
https://github.com/dev-claw/vripper-project.git
synced 2026-08-19 08:35:41 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4eaf0d1c4 | ||
|
|
9d2975faaa | ||
|
|
0c0fdf9d78 | ||
|
|
e17afa0ef3 | ||
|
|
3d3eb6aa36 | ||
|
|
e22132255e |
@@ -1,5 +1,18 @@
|
||||
# Changelog
|
||||
|
||||
## [2.10.7] - 2020-01-25
|
||||
### Changed
|
||||
- Drop SockJs and switch to WebSocket
|
||||
- Autoreconnect front to back
|
||||
|
||||
## [2.10.6] - 2020-01-19
|
||||
### Changed
|
||||
- Limit cache size
|
||||
|
||||
## [2.10.5] - 2020-01-18
|
||||
### Changed
|
||||
- Bug fixes
|
||||
|
||||
## [2.10.4] - 2020-01-18
|
||||
### Changed
|
||||
- Zero value for total max download will disable the limit
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>tn.mnlr</groupId>
|
||||
<artifactId>vripper</artifactId>
|
||||
<version>2.10.4</version>
|
||||
<version>2.10.7</version>
|
||||
<packaging>pom</packaging>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -23,6 +23,11 @@ process.on("uncaughtException", err => {
|
||||
});
|
||||
|
||||
function createWindow() {
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
app.setAppUserModelId("tn.mnlr.vripper");
|
||||
}
|
||||
|
||||
let icon;
|
||||
if(process.platform === "win32") {
|
||||
icon = __dirname + '/icon.ico';
|
||||
@@ -64,7 +69,6 @@ const gotTheLock = app.requestSingleInstanceLock();
|
||||
if (!gotTheLock) {
|
||||
app.quit();
|
||||
} else {
|
||||
app.setAppUserModelId(process.execPath)
|
||||
getPort().then(port => {
|
||||
serverPort = port;
|
||||
ipcMain.on("get-port", event => {
|
||||
|
||||
Generated
+683
-528
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vripper-electron",
|
||||
"version": "2.10.4",
|
||||
"version": "2.10.7",
|
||||
"description": "A ripper for vipergirls.to built using web technolgies",
|
||||
"main": "main.js",
|
||||
"author": "death-claw <53543762+death-claw@users.noreply.github.com>",
|
||||
@@ -70,8 +70,8 @@
|
||||
"dist": "node pre-build.js && electron-builder"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^7.1.7",
|
||||
"electron-builder": "^21.2.0"
|
||||
"electron": "^7.1.9",
|
||||
"electron-builder": "^22.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.0",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tn.mnlr</groupId>
|
||||
<artifactId>vripper</artifactId>
|
||||
<version>2.10.4</version>
|
||||
<version>2.10.7</version>
|
||||
</parent>
|
||||
<artifactId>vripper-electron</artifactId>
|
||||
<name>vripper-electron</name>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tn.mnlr</groupId>
|
||||
<artifactId>vripper</artifactId>
|
||||
<version>2.10.4</version>
|
||||
<version>2.10.7</version>
|
||||
</parent>
|
||||
<artifactId>vripper-server</artifactId>
|
||||
<name>vripper-server</name>
|
||||
|
||||
@@ -18,6 +18,10 @@ import java.util.stream.Collectors;
|
||||
@NoArgsConstructor
|
||||
public class Post {
|
||||
|
||||
public enum METADATA {
|
||||
PREVIEWS, RESOLVED_NAME, POSTED_BY, THANKED
|
||||
}
|
||||
|
||||
private AppStateService appStateService;
|
||||
|
||||
private Status status;
|
||||
|
||||
@@ -40,6 +40,12 @@ public class PostParser {
|
||||
@Autowired
|
||||
private List<Host> supportedHosts;
|
||||
|
||||
@Autowired
|
||||
private HtmlProcessorService htmlProcessorService;
|
||||
|
||||
@Autowired
|
||||
private XpathService xpathService;
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
|
||||
@@ -52,7 +58,7 @@ public class PostParser {
|
||||
return;
|
||||
}
|
||||
|
||||
VRPostParser vrPostParser = new VRPostParser(threadId, postId, cm, vipergirlsAuthService, supportedHosts);
|
||||
VRPostParser vrPostParser = new VRPostParser(threadId, postId, cm, vipergirlsAuthService, supportedHosts, htmlProcessorService, xpathService);
|
||||
Post post = vrPostParser.parse();
|
||||
|
||||
post.setAppStateService(appStateService);
|
||||
|
||||
@@ -3,6 +3,7 @@ package tn.mnlr.vripper.services;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.cache.Weigher;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
@@ -22,6 +23,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@@ -59,7 +61,9 @@ public class ThumbnailGenerator {
|
||||
throw new Exception(String.format("%s could not be created", cacheFolder.toString()));
|
||||
}
|
||||
thumbnails = CacheBuilder.newBuilder()
|
||||
.maximumSize(20000)
|
||||
.expireAfterAccess(Duration.ofMinutes(30))
|
||||
.weigher((Weigher<CacheKey, byte[]>) (k, v) -> v.length)
|
||||
.maximumWeight(104_857_600)
|
||||
.build(loader);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
import tn.mnlr.vripper.entities.Image;
|
||||
@@ -51,13 +53,17 @@ class VRPostParser {
|
||||
private final ConnectionManager cm;
|
||||
private final VipergirlsAuthService vipergirlsAuthService;
|
||||
private final List<Host> supportedHosts;
|
||||
private final HtmlProcessorService htmlProcessorService;
|
||||
private final XpathService xpathService;
|
||||
|
||||
VRPostParser(String threadId, String postId, ConnectionManager cm, VipergirlsAuthService vipergirlsAuthService, List<Host> supportedHosts) {
|
||||
VRPostParser(String threadId, String postId, ConnectionManager cm, VipergirlsAuthService vipergirlsAuthService, List<Host> supportedHosts, HtmlProcessorService htmlProcessorService, XpathService xpathService) {
|
||||
this.threadId = threadId;
|
||||
this.postId = postId;
|
||||
this.cm = cm;
|
||||
this.vipergirlsAuthService = vipergirlsAuthService;
|
||||
this.supportedHosts = supportedHosts;
|
||||
this.htmlProcessorService = htmlProcessorService;
|
||||
this.xpathService = xpathService;
|
||||
}
|
||||
|
||||
public Post parse() throws PostParseException {
|
||||
@@ -74,7 +80,16 @@ class VRPostParser {
|
||||
VRPostHandler handler = new VRPostHandler(threadId, postId, supportedHosts);
|
||||
AtomicReference<Throwable> thr = new AtomicReference<>();
|
||||
logger.debug(String.format("Requesting %s", httpGet));
|
||||
Post post = Failsafe.with(retryPolicy)
|
||||
Post post = getPost(httpGet, handler, thr);
|
||||
if (thr.get() != null || post == null) {
|
||||
logger.error(String.format("parsing failed for thread %s, post %s", threadId, postId), thr.get());
|
||||
throw new PostParseException(thr.get());
|
||||
}
|
||||
return post;
|
||||
}
|
||||
|
||||
private Post getPost(HttpGet httpGet, VRPostHandler handler, AtomicReference<Throwable> thr) {
|
||||
return Failsafe.with(retryPolicy)
|
||||
.onFailure(e -> thr.set(e.getFailure()))
|
||||
.get(() -> {
|
||||
HttpClient connection = cm.getClient().build();
|
||||
@@ -93,11 +108,34 @@ class VRPostParser {
|
||||
}
|
||||
}
|
||||
});
|
||||
if (thr.get() != null || post == null) {
|
||||
logger.error(String.format("parsing failed for thread %s, post %s", threadId, postId), thr.get());
|
||||
throw new PostParseException(thr.get());
|
||||
}
|
||||
return post;
|
||||
}
|
||||
|
||||
private void getPostExtraMetadata(Post post, AtomicReference<Throwable> thr) {
|
||||
HttpGet httpGet = cm.buildHttpGet(post.getUrl());
|
||||
Failsafe.with(retryPolicy)
|
||||
.onFailure(e -> thr.set(e.getFailure()))
|
||||
.get(() -> {
|
||||
HttpClient connection = cm.getClient().build();
|
||||
try (CloseableHttpResponse response = (CloseableHttpResponse) connection.execute(httpGet, vipergirlsAuthService.getContext())) {
|
||||
if (response.getStatusLine().getStatusCode() / 100 != 2) {
|
||||
throw new DownloadException(String.format("Unexpected response code '%d' for %s", response.getStatusLine().getStatusCode(), httpGet));
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Document document = htmlProcessorService.clean(EntityUtils.toString(response.getEntity()));
|
||||
Node postNode = xpathService.getAsNode(document, String.format("//li[@id='post_%s']/div[contains(@class, 'postdetails')]", post.getPostId()));
|
||||
String postedBy = xpathService.getAsNode(postNode, "./div[contains(@class, 'userinfo')]//a[contains(@class, 'username')]//font").getTextContent().trim();
|
||||
HashMap<String, Object> stringObjectHashMap = new HashMap<>();
|
||||
response.getEntity().getContent();
|
||||
return stringObjectHashMap;
|
||||
} catch (Exception e) {
|
||||
throw new PostParseException(String.format("Failed to parse thread %s, post %s", threadId, postId), e);
|
||||
} finally {
|
||||
EntityUtils.consumeQuietly(response.getEntity());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +207,7 @@ class VRPostHandler extends DefaultHandler {
|
||||
if ("post".equals(qName.toLowerCase())) {
|
||||
if (imageCount != 0) {
|
||||
HashMap<String, Object> metadata = new HashMap<>();
|
||||
metadata.put("PREVIEWS", previews);
|
||||
metadata.put(Post.METADATA.PREVIEWS.name(), previews);
|
||||
parsedPost = new Post(
|
||||
postTitle,
|
||||
String.format("https://vipergirls.to/threads/%s/?p=%s&viewfull=1#post%s", threadId, postId, postId),
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package tn.mnlr.vripper.web;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
|
||||
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
|
||||
import tn.mnlr.vripper.web.wsendpoints.WebSocketHandler;
|
||||
|
||||
@Configuration
|
||||
@@ -16,6 +20,24 @@ public class WebSocketConfig implements WebSocketConfigurer {
|
||||
|
||||
@Override
|
||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
||||
registry.addHandler(handler, "/endpoint").setAllowedOrigins("*").withSockJS().setClientLibraryUrl("../../assets/sockjs.min.js");
|
||||
registry.addHandler(handler, "/endpoint").setAllowedOrigins("*");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskScheduler taskScheduler() {
|
||||
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||
|
||||
scheduler.setPoolSize(2);
|
||||
scheduler.setThreadNamePrefix("scheduled-task-");
|
||||
scheduler.setDaemon(true);
|
||||
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletServerContainerFactoryBean createWebSocketContainer() {
|
||||
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
|
||||
container.setMaxSessionIdleTimeout(0L);
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vripper-ui",
|
||||
"version": "2.10.4",
|
||||
"version": "2.10.7",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vripper-ui",
|
||||
"version": "2.10.4",
|
||||
"version": "2.10.7",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>tn.mnlr</groupId>
|
||||
<artifactId>vripper</artifactId>
|
||||
<version>2.10.4</version>
|
||||
<version>2.10.7</version>
|
||||
</parent>
|
||||
<artifactId>vripper-ui</artifactId>
|
||||
<name>vripper-ui</name>
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
<ng-container *ngIf="appState | async as state">
|
||||
<div *ngIf="state === 'CONNECTING'" class="overlay loading" fxLayout="column" fxLayoutAlign="center center">
|
||||
<div *ngIf="loaded | async; else loading" [ngClass]="{ electron: electron | async }" fxLayout="column"
|
||||
id="app-container">
|
||||
<div fxFlex="nogrow" id="app-header">
|
||||
<app-toolbar></app-toolbar>
|
||||
</div>
|
||||
<div fxFlex="grow" id="app-body" style="margin-bottom: 20px;">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<app-status-bar style="width: 100%"></app-status-bar>
|
||||
</div>
|
||||
<ng-template #loading>
|
||||
<div class="overlay loading" fxLayout="column" fxLayoutAlign="center center">
|
||||
<div class="sk-chase">
|
||||
<div class="sk-chase-dot"></div>
|
||||
<div class="sk-chase-dot"></div>
|
||||
@@ -9,17 +19,4 @@
|
||||
<div class="sk-chase-dot"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="state === 'DISCONNECTED'" class="overlay no-connection" fxLayout="column" fxLayoutAlign="center center">
|
||||
<mat-icon class="overlay-icon">error</mat-icon>
|
||||
<h2>Error connecting to daemon, try restarting the application</h2>
|
||||
</div>
|
||||
<div *ngIf="state === 'CONNECTED'" [ngClass]="{ electron: electron | async }" fxLayout="column" id="app-container">
|
||||
<div fxFlex="nogrow" id="app-header">
|
||||
<app-toolbar></app-toolbar>
|
||||
</div>
|
||||
<div fxFlex="grow" id="app-body" style="margin-bottom: 20px;">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<app-status-bar style="width: 100%"></app-status-bar>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ElectronService } from 'ngx-electron';
|
||||
import { Component, OnDestroy, AfterViewInit, Renderer2, ChangeDetectionStrategy, NgZone } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { Subscription, BehaviorSubject, Subject, merge } from 'rxjs';
|
||||
import { WsConnectionService, WSState } from './ws-connection.service';
|
||||
import { WsConnectionService } from './ws-connection.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -15,7 +15,7 @@ import { WsConnectionService, WSState } from './ws-connection.service';
|
||||
export class AppComponent implements OnDestroy, AfterViewInit {
|
||||
constructor(
|
||||
private dialog: MatDialog,
|
||||
private ws: WsConnectionService,
|
||||
public ws: WsConnectionService,
|
||||
public electronService: ElectronService,
|
||||
private clipboardService: ClipboardService,
|
||||
private appService: AppService,
|
||||
@@ -26,28 +26,33 @@ export class AppComponent implements OnDestroy, AfterViewInit {
|
||||
}
|
||||
|
||||
private subscriptions: Subscription[] = [];
|
||||
appState: Subject<string> = new BehaviorSubject('CONNECTING');
|
||||
electron: Subject<boolean>;
|
||||
|
||||
loaded: Subject<boolean> = new BehaviorSubject(false);
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.appService.renderer = this.renderer;
|
||||
this.subscriptions.push(this.ws.state.subscribe(wsState => {
|
||||
if (wsState === WSState.CLOSE || wsState === WSState.ERROR) {
|
||||
setTimeout(() => this.ngZone.run(() => {
|
||||
this.dialog.closeAll();
|
||||
this.appState.next('DISCONNECTED');
|
||||
}), 500);
|
||||
} else if (wsState === WSState.OPEN) {
|
||||
this.clipboardService.init();
|
||||
merge(this.appService.loadTheme(), this.appService.loadSettings())
|
||||
.subscribe(() => this.ngZone.run(() => this.appState.next('CONNECTED')));
|
||||
}
|
||||
}));
|
||||
this.subscriptions.push(
|
||||
this.ws.state.subscribe(online => {
|
||||
if (!online) {
|
||||
this.ngZone.run(() => this.loaded.next(false));
|
||||
setTimeout(
|
||||
() =>
|
||||
this.ngZone.run(() => {
|
||||
this.dialog.closeAll();
|
||||
}),
|
||||
500
|
||||
);
|
||||
} else {
|
||||
this.ngZone.run(() => this.loaded.next(true));
|
||||
this.clipboardService.init();
|
||||
this.subscriptions.push(merge(this.appService.loadTheme(), this.appService.loadSettings()).subscribe());
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscriptions.forEach(e => {
|
||||
e.unsubscribe();
|
||||
});
|
||||
this.subscriptions.forEach(e => e.unsubscribe());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { flatMap, filter } from 'rxjs/operators';
|
||||
import { GrabQueueState } from './grab-queue.model';
|
||||
import { CMD } from './../common/cmd.enum';
|
||||
import { WSMessage } from './../common/ws-message.model';
|
||||
@@ -5,65 +6,66 @@ import { Subscription } from 'rxjs';
|
||||
import { WsConnectionService } from '../ws-connection.service';
|
||||
import { NotificationService } from '../notification.service';
|
||||
import { GridOptions } from 'ag-grid-community';
|
||||
import { WsHandler } from '../ws-handler';
|
||||
import { NgZone } from '@angular/core';
|
||||
|
||||
export class GrabQueueDataSource {
|
||||
constructor(
|
||||
private wsConnectionService: WsConnectionService,
|
||||
private ws: WsConnectionService,
|
||||
private gridOptions: GridOptions,
|
||||
private zone: NgZone,
|
||||
private notificationService: NotificationService
|
||||
) {
|
||||
this.websocketHandlerPromise = this.wsConnectionService.getConnection();
|
||||
}
|
||||
) {}
|
||||
|
||||
websocketHandlerPromise: Promise<WsHandler>;
|
||||
subscriptions: Subscription[] = [];
|
||||
grabQueueSub: Subscription;
|
||||
|
||||
connect() {
|
||||
this.websocketHandlerPromise.then((handler: WsHandler) => {
|
||||
console.log('Connecting to grab queue datasource');
|
||||
this.subscriptions.push(
|
||||
handler.subscribeForGrabQueue((e: GrabQueueState[]) => {
|
||||
this.zone.run(() => {
|
||||
const toAdd = [];
|
||||
const toUpdate = [];
|
||||
const toRemove = [];
|
||||
e.forEach(v => {
|
||||
if (v.removed) {
|
||||
if (this.gridOptions.api.getRowNode(v.link) != null) {
|
||||
toRemove.push(this.gridOptions.api.getRowNode(v.link).data);
|
||||
console.log('Connecting to grab queue datasource');
|
||||
this.subscriptions.push(
|
||||
this.ws.state.subscribe(state => {
|
||||
if (state) {
|
||||
this.grabQueueSub = this.ws.subscribeForGrabQueue().subscribe((e: GrabQueueState[]) => {
|
||||
this.zone.run(() => {
|
||||
const toAdd = [];
|
||||
const toUpdate = [];
|
||||
const toRemove = [];
|
||||
e.forEach(v => {
|
||||
if (v.removed) {
|
||||
if (this.gridOptions.api.getRowNode(v.link) != null) {
|
||||
toRemove.push(this.gridOptions.api.getRowNode(v.link).data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.gridOptions.api.getRowNode(v.link) == null) {
|
||||
toAdd.push(v);
|
||||
} else {
|
||||
toUpdate.push(v);
|
||||
if (this.gridOptions.api.getRowNode(v.link) == null) {
|
||||
toAdd.push(v);
|
||||
} else {
|
||||
toUpdate.push(v);
|
||||
}
|
||||
});
|
||||
this.gridOptions.api.updateRowData({ update: toUpdate, add: toAdd, remove: toRemove });
|
||||
const count = this.gridOptions.api.getDisplayedRowCount();
|
||||
if (count > 0 && toAdd.length > 0) {
|
||||
this.notificationService.notifyFromGrabQueue(
|
||||
'Link Collector',
|
||||
`You have ${count} ${count > 1 ? 'threads' : 'thread'} waiting in the link collector`
|
||||
);
|
||||
}
|
||||
});
|
||||
this.gridOptions.api.updateRowData({ update: toUpdate, add: toAdd, remove: toRemove });
|
||||
const count = this.gridOptions.api.getDisplayedRowCount();
|
||||
if (count > 0 && toAdd.length > 0) {
|
||||
this.notificationService.notifyFromGrabQueue(
|
||||
'Link Collector',
|
||||
`${count} ${count > 1 ? 'threads are' : 'thread is'} in the link collector`
|
||||
);
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
handler.send(new WSMessage(CMD.GRAB_QUEUE_SUB.toString()));
|
||||
});
|
||||
this.ws.send(new WSMessage(CMD.GRAB_QUEUE_SUB.toString()));
|
||||
} else if (this.grabQueueSub != null) {
|
||||
this.grabQueueSub.unsubscribe();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
console.log('Disconnecting from grab queue datasource');
|
||||
this.subscriptions.forEach(e => e.unsubscribe());
|
||||
this.websocketHandlerPromise.then((handler: WsHandler) => {
|
||||
handler.send(new WSMessage(CMD.GRAB_QUEUE_UNSUB.toString()));
|
||||
});
|
||||
this.ws.send(new WSMessage(CMD.GRAB_QUEUE_UNSUB.toString()));
|
||||
if (this.grabQueueSub != null) {
|
||||
this.grabQueueSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Subscription } from 'rxjs';
|
||||
import { LinkCollectorService } from './../link-collector.service';
|
||||
import { ServerService } from './../server-service';
|
||||
import { ElectronService } from 'ngx-electron';
|
||||
@@ -24,8 +25,10 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
public linkCollectorService: LinkCollectorService
|
||||
) {}
|
||||
|
||||
clipboardSub: Subscription;
|
||||
|
||||
ngOnInit() {
|
||||
this.clipboardService.links.subscribe(e => {
|
||||
this.clipboardSub = this.clipboardService.links.subscribe(e => {
|
||||
this.ngZone.run(() => {
|
||||
this.httpClient
|
||||
.post<{ threadId: string; postId: string }>(this.serverService.baseUrl + '/post', { url: e })
|
||||
@@ -45,5 +48,9 @@ export class HomeComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {}
|
||||
ngOnDestroy() {
|
||||
if (this.clipboardSub != null) {
|
||||
this.clipboardSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,7 @@ import { ElectronService } from 'ngx-electron';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class PostDetailsProgressRendererComponent implements AgRendererComponent, OnInit, OnDestroy, AfterViewInit {
|
||||
constructor(
|
||||
private wsConnectionService: WsConnectionService,
|
||||
private zone: NgZone,
|
||||
public electronService: ElectronService
|
||||
) {
|
||||
this.websocketHandlerPromise = this.wsConnectionService.getConnection();
|
||||
}
|
||||
constructor(private ws: WsConnectionService, private zone: NgZone, public electronService: ElectronService) {}
|
||||
|
||||
websocketHandlerPromise: Promise<WsHandler>;
|
||||
subscription: Subscription;
|
||||
@@ -37,22 +31,29 @@ export class PostDetailsProgressRendererComponent implements AgRendererComponent
|
||||
loaded: Subject<boolean> = new BehaviorSubject(false);
|
||||
loading;
|
||||
|
||||
stateSub: Subscription;
|
||||
postDetailsSub: Subscription;
|
||||
|
||||
trunc(value: number): number {
|
||||
return Math.trunc(value);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.websocketHandlerPromise.then((handler: WsHandler) => {
|
||||
this.subscription = handler.subscribeForPostDetails(e => {
|
||||
this.zone.run(() => {
|
||||
e.forEach(v => {
|
||||
if (this.postDetails.url === v.url) {
|
||||
this.postDetails = v;
|
||||
this.postDetails$.emit(this.postDetails);
|
||||
}
|
||||
this.stateSub = this.ws.state.subscribe(state => {
|
||||
if (state) {
|
||||
this.postDetailsSub = this.ws.subscribeForPostDetails().subscribe(e => {
|
||||
this.zone.run(() => {
|
||||
e.forEach(v => {
|
||||
if (this.postDetails.url === v.url) {
|
||||
this.postDetails = v;
|
||||
this.postDetails$.emit(this.postDetails);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else if (this.postDetailsSub != null) {
|
||||
this.postDetailsSub.unsubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -73,6 +74,12 @@ export class PostDetailsProgressRendererComponent implements AgRendererComponent
|
||||
if (this.subscription != null) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
if (this.postDetailsSub != null) {
|
||||
this.postDetailsSub.unsubscribe();
|
||||
}
|
||||
if (this.stateSub != null) {
|
||||
this.stateSub.unsubscribe();
|
||||
}
|
||||
clearTimeout(this.loading);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,54 +1,57 @@
|
||||
import { WsHandler } from './../ws-handler';
|
||||
import { GridOptions } from 'ag-grid-community';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { WsConnectionService, WSState } from '../ws-connection.service';
|
||||
import { WsConnectionService } from '../ws-connection.service';
|
||||
import { WSMessage } from '../common/ws-message.model';
|
||||
import { CMD } from '../common/cmd.enum';
|
||||
import { NgZone } from '@angular/core';
|
||||
|
||||
export class PostDetailsDataSource {
|
||||
constructor(
|
||||
private wsConnectionService: WsConnectionService,
|
||||
private ws: WsConnectionService,
|
||||
private gridOptions: GridOptions,
|
||||
private postId: string,
|
||||
private zone: NgZone
|
||||
) {
|
||||
this.websocketHandlerPromise = this.wsConnectionService.getConnection();
|
||||
}
|
||||
) {}
|
||||
|
||||
websocketHandlerPromise: Promise<WsHandler>;
|
||||
|
||||
subscriptions: Subscription[] = [];
|
||||
postDetailsSub: Subscription;
|
||||
|
||||
connect() {
|
||||
console.log('Connecting to post details datasource');
|
||||
this.websocketHandlerPromise.then((handler: WsHandler) => {
|
||||
this.subscriptions.push(
|
||||
handler.subscribeForPostDetails(e => {
|
||||
this.zone.run(() => {
|
||||
const toAdd = [];
|
||||
const toUpdate = [];
|
||||
e.forEach(v => {
|
||||
if (this.gridOptions.api.getRowNode(v.url) == null) {
|
||||
toAdd.push(v);
|
||||
} else {
|
||||
toUpdate.push(v);
|
||||
}
|
||||
this.subscriptions.push(
|
||||
this.ws.state.subscribe(state => {
|
||||
if (state) {
|
||||
this.postDetailsSub = this.ws.subscribeForPostDetails().subscribe(e => {
|
||||
this.zone.run(() => {
|
||||
const toAdd = [];
|
||||
const toUpdate = [];
|
||||
e.forEach(v => {
|
||||
if (this.gridOptions.api.getRowNode(v.url) == null) {
|
||||
toAdd.push(v);
|
||||
} else {
|
||||
toUpdate.push(v);
|
||||
}
|
||||
});
|
||||
this.gridOptions.api.updateRowData({ update: toUpdate, add: toAdd });
|
||||
});
|
||||
this.gridOptions.api.updateRowData({ update: toUpdate, add: toAdd });
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
handler.send(new WSMessage(CMD.POST_DETAILS_SUB.toString(), this.postId));
|
||||
});
|
||||
this.ws.send(new WSMessage(CMD.POST_DETAILS_SUB.toString(), this.postId));
|
||||
} else if (this.postDetailsSub != null) {
|
||||
this.postDetailsSub.unsubscribe();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
console.log('Disconnecting from post details datasource');
|
||||
this.subscriptions.forEach(e => e.unsubscribe());
|
||||
this.websocketHandlerPromise.then((handler: WsHandler) => {
|
||||
handler.send(new WSMessage(CMD.POST_DETAILS_UNSUB.toString()));
|
||||
});
|
||||
this.ws.send(new WSMessage(CMD.POST_DETAILS_UNSUB.toString()));
|
||||
if (this.postDetailsSub != null) {
|
||||
this.postDetailsSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
NgZone,
|
||||
ChangeDetectionStrategy,
|
||||
AfterViewInit,
|
||||
EventEmitter,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
import { AgRendererComponent } from 'ag-grid-angular';
|
||||
import { Subscription, BehaviorSubject, Subject } from 'rxjs';
|
||||
@@ -25,13 +25,12 @@ import { ContextMenuService } from '../ctxt-menu.service';
|
||||
})
|
||||
export class PostProgressRendererComponent implements AgRendererComponent, OnInit, OnDestroy, AfterViewInit {
|
||||
constructor(
|
||||
private wsConnectionService: WsConnectionService,
|
||||
private ws: WsConnectionService,
|
||||
private zone: NgZone,
|
||||
public electronService: ElectronService,
|
||||
public dialog: MatDialog,
|
||||
private contextMenuService: ContextMenuService) {
|
||||
this.websocketHandlerPromise = this.wsConnectionService.getConnection();
|
||||
}
|
||||
private contextMenuService: ContextMenuService
|
||||
) {}
|
||||
|
||||
websocketHandlerPromise: Promise<WsHandler>;
|
||||
postState$: EventEmitter<PostState> = new EventEmitter();
|
||||
@@ -43,22 +42,29 @@ export class PostProgressRendererComponent implements AgRendererComponent, OnIni
|
||||
node: RowNode;
|
||||
gridApi: GridApi;
|
||||
|
||||
postsSub: Subscription;
|
||||
stateSub: Subscription;
|
||||
|
||||
trunc(value: number): number {
|
||||
return Math.trunc(value);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.websocketHandlerPromise.then((handler: WsHandler) => {
|
||||
this.updatesSubscription = handler.subscribeForPosts(e => {
|
||||
this.zone.run(() => {
|
||||
e.forEach(v => {
|
||||
if (this.postState.postId === v.postId) {
|
||||
this.postState = v;
|
||||
this.postState$.emit(this.postState);
|
||||
}
|
||||
this.stateSub = this.ws.state.subscribe(state => {
|
||||
if (state) {
|
||||
this.postsSub = this.ws.subscribeForPosts().subscribe(e => {
|
||||
this.zone.run(() => {
|
||||
e.forEach(v => {
|
||||
if (this.postState.postId === v.postId) {
|
||||
this.postState = v;
|
||||
this.postState$.emit(this.postState);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else if (this.postsSub != null) {
|
||||
this.postsSub.unsubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,6 +77,12 @@ export class PostProgressRendererComponent implements AgRendererComponent, OnIni
|
||||
if (this.updatesSubscription != null) {
|
||||
this.updatesSubscription.unsubscribe();
|
||||
}
|
||||
if (this.postsSub != null) {
|
||||
this.postsSub.unsubscribe();
|
||||
}
|
||||
if (this.stateSub != null) {
|
||||
this.stateSub.unsubscribe();
|
||||
}
|
||||
clearTimeout(this.loading);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,53 +8,53 @@ import { WsHandler } from '../ws-handler';
|
||||
import { NgZone } from '@angular/core';
|
||||
|
||||
export class PostsDataSource {
|
||||
constructor(
|
||||
private wsConnectionService: WsConnectionService,
|
||||
private gridOptions: GridOptions,
|
||||
private zone: NgZone
|
||||
) {
|
||||
this.websocketHandlerPromise = this.wsConnectionService.getConnection();
|
||||
}
|
||||
constructor(private ws: WsConnectionService, private gridOptions: GridOptions, private zone: NgZone) {}
|
||||
|
||||
websocketHandlerPromise: Promise<WsHandler>;
|
||||
subscriptions: Subscription[] = [];
|
||||
|
||||
connect() {
|
||||
this.websocketHandlerPromise.then((handler: WsHandler) => {
|
||||
console.log('Connecting to posts datasource');
|
||||
this.subscriptions.push(
|
||||
handler.subscribeForPosts((e: PostState[]) => {
|
||||
this.zone.run(() => {
|
||||
const toAdd = [];
|
||||
const toUpdate = [];
|
||||
const toRemove = [];
|
||||
e.forEach(v => {
|
||||
if (v.removed) {
|
||||
if (this.gridOptions.api.getRowNode(v.postId) != null) {
|
||||
toRemove.push(this.gridOptions.api.getRowNode(v.postId).data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
postsSub: Subscription;
|
||||
|
||||
if (this.gridOptions.api.getRowNode(v.postId) == null) {
|
||||
toAdd.push(v);
|
||||
} else {
|
||||
toUpdate.push(v);
|
||||
}
|
||||
connect() {
|
||||
console.log('Connecting to posts datasource');
|
||||
this.subscriptions.push(
|
||||
this.ws.state.subscribe(state => {
|
||||
if (state) {
|
||||
this.postsSub = this.ws.subscribeForPosts().subscribe((e: PostState[]) => {
|
||||
this.zone.run(() => {
|
||||
const toAdd = [];
|
||||
const toUpdate = [];
|
||||
const toRemove = [];
|
||||
e.forEach(v => {
|
||||
if (v.removed) {
|
||||
if (this.gridOptions.api.getRowNode(v.postId) != null) {
|
||||
toRemove.push(this.gridOptions.api.getRowNode(v.postId).data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.gridOptions.api.getRowNode(v.postId) == null) {
|
||||
toAdd.push(v);
|
||||
} else {
|
||||
toUpdate.push(v);
|
||||
}
|
||||
});
|
||||
this.gridOptions.api.updateRowData({ update: toUpdate, add: toAdd, remove: toRemove });
|
||||
});
|
||||
this.gridOptions.api.updateRowData({ update: toUpdate, add: toAdd, remove: toRemove });
|
||||
});
|
||||
})
|
||||
);
|
||||
handler.send(new WSMessage(CMD.POSTS_SUB.toString()));
|
||||
});
|
||||
this.ws.send(new WSMessage(CMD.POSTS_SUB.toString()));
|
||||
} else if (this.postsSub != null) {
|
||||
this.postsSub.unsubscribe();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
console.log('Disconnecting from posts datasource');
|
||||
this.subscriptions.forEach(e => e.unsubscribe());
|
||||
this.websocketHandlerPromise.then((handler: WsHandler) => {
|
||||
handler.send(new WSMessage(CMD.POSTS_UNSUB.toString()));
|
||||
});
|
||||
this.ws.send(new WSMessage(CMD.POSTS_UNSUB.toString()));
|
||||
if (this.postsSub != null) {
|
||||
this.postsSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Injectable } from '@angular/core';
|
||||
export class ServerService {
|
||||
|
||||
private _baseUrl: string;
|
||||
private _wsBaseUrl: string;
|
||||
|
||||
constructor() {}
|
||||
|
||||
@@ -14,4 +15,12 @@ export class ServerService {
|
||||
get baseUrl(): string {
|
||||
return this._baseUrl;
|
||||
}
|
||||
|
||||
set wsBaseUrl(wsBaseUrl: string) {
|
||||
this._wsBaseUrl = wsBaseUrl;
|
||||
}
|
||||
|
||||
get wsBaseUrl(): string {
|
||||
return this._wsBaseUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ServerService } from '../server-service';
|
||||
import { ElectronService } from 'ngx-electron';
|
||||
import { Settings } from '../common/settings.model';
|
||||
import { OpenDialogReturnValue } from 'electron';
|
||||
import { forkJoin, Subject, BehaviorSubject } from 'rxjs';
|
||||
import { Subject, BehaviorSubject } from 'rxjs';
|
||||
|
||||
interface CacheSize {
|
||||
size: string;
|
||||
@@ -68,13 +68,18 @@ export class SettingsComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.darkTheme = this.appService.darkTheme;
|
||||
forkJoin([
|
||||
this.httpClient.get<Settings>(this.serverService.baseUrl + '/settings'),
|
||||
this.httpClient.get<CacheSize>(this.serverService.baseUrl + '/gallery/cache')
|
||||
]).subscribe(data => {
|
||||
this.generalSettingsForm.reset(data[0]);
|
||||
this.desktopSettingsForm.reset(data[0]);
|
||||
this.cacheSize.next(data[1]);
|
||||
this.httpClient.get<Settings>(this.serverService.baseUrl + '/settings')
|
||||
.subscribe(data => {
|
||||
this.generalSettingsForm.reset(data);
|
||||
this.desktopSettingsForm.reset(data);
|
||||
}, error => {
|
||||
this._snackBar.open(error.error || 'Unexpected error, check log file', null, {
|
||||
duration: 5000
|
||||
});
|
||||
});
|
||||
this.httpClient.get<CacheSize>(this.serverService.baseUrl + '/gallery/cache')
|
||||
.subscribe(data => {
|
||||
this.cacheSize.next(data);
|
||||
}, error => {
|
||||
this._snackBar.open(error.error || 'Unexpected error, check log file', null, {
|
||||
duration: 5000
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { flatMap, filter } from 'rxjs/operators';
|
||||
import { SelectionService } from './../selection-service';
|
||||
import {
|
||||
Component,
|
||||
@@ -23,13 +24,7 @@ import { CMD } from '../common/cmd.enum';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class StatusBarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
constructor(
|
||||
private wsConnectionService: WsConnectionService,
|
||||
private ngZone: NgZone,
|
||||
private selectionService: SelectionService
|
||||
) {
|
||||
this.websocketHandlerPromise = this.wsConnectionService.getConnection();
|
||||
}
|
||||
constructor(private ws: WsConnectionService, private ngZone: NgZone, private selectionService: SelectionService) {}
|
||||
|
||||
websocketHandlerPromise: Promise<WsHandler>;
|
||||
downloadSpeed: EventEmitter<DownloadSpeed> = new EventEmitter();
|
||||
@@ -37,6 +32,9 @@ export class StatusBarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
globalState: EventEmitter<GlobalState> = new EventEmitter();
|
||||
selected: EventEmitter<number> = new EventEmitter();
|
||||
|
||||
globalStateSub: Subscription;
|
||||
speedSub: Subscription;
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.ngZone.run(() => {
|
||||
this.selected.emit(0);
|
||||
@@ -46,25 +44,34 @@ export class StatusBarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.websocketHandlerPromise.then((handler: WsHandler) => {
|
||||
console.log('Connecting to global state and download speed');
|
||||
this.subscriptions.push(
|
||||
handler.subscribeForGlobalState((e: GlobalState[]) => {
|
||||
this.ngZone.run(() => {
|
||||
this.globalState.emit(e[0]);
|
||||
console.log('Connecting to global state and download speed');
|
||||
this.subscriptions.push(
|
||||
this.ws.state.subscribe(state => {
|
||||
if (state) {
|
||||
this.globalStateSub = this.ws.subscribeForGlobalState().subscribe((e: GlobalState[]) => {
|
||||
this.ngZone.run(() => {
|
||||
this.globalState.emit(e[0]);
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
this.subscriptions.push(
|
||||
handler.subscribeForSpeed((e: DownloadSpeed[]) => {
|
||||
this.ngZone.run(() => {
|
||||
this.downloadSpeed.emit(e[0]);
|
||||
|
||||
this.speedSub = this.ws.subscribeForSpeed().subscribe((e: DownloadSpeed[]) => {
|
||||
this.ngZone.run(() => {
|
||||
this.downloadSpeed.emit(e[0]);
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
handler.send(new WSMessage(CMD.GLOBAL_STATE_SUB.toString()));
|
||||
handler.send(new WSMessage(CMD.SPEED_SUB.toString()));
|
||||
});
|
||||
this.ws.send(new WSMessage(CMD.GLOBAL_STATE_SUB.toString()));
|
||||
this.ws.send(new WSMessage(CMD.SPEED_SUB.toString()));
|
||||
} else {
|
||||
if (this.globalStateSub != null) {
|
||||
this.globalStateSub.unsubscribe();
|
||||
}
|
||||
if (this.speedSub != null) {
|
||||
this.speedSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.subscriptions.push(
|
||||
this.selectionService.selected$.subscribe(selected => this.ngZone.run(() => this.selected.emit(selected.length)))
|
||||
);
|
||||
@@ -72,9 +79,13 @@ export class StatusBarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscriptions.forEach(e => e.unsubscribe());
|
||||
this.websocketHandlerPromise.then((handler: WsHandler) => {
|
||||
handler.send(new WSMessage(CMD.GLOBAL_STATE_UNSUB.toString()));
|
||||
handler.send(new WSMessage(CMD.SPEED_UNSUB.toString()));
|
||||
});
|
||||
this.ws.send(new WSMessage(CMD.GLOBAL_STATE_UNSUB.toString()));
|
||||
this.ws.send(new WSMessage(CMD.SPEED_UNSUB.toString()));
|
||||
if (this.globalStateSub != null) {
|
||||
this.globalStateSub.unsubscribe();
|
||||
}
|
||||
if (this.speedSub != null) {
|
||||
this.speedSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Component, OnInit, NgZone, OnDestroy, ChangeDetectionStrategy, EventEmitter, AfterViewInit } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
ChangeDetectionStrategy,
|
||||
EventEmitter,
|
||||
AfterViewInit
|
||||
} from '@angular/core';
|
||||
import { RemoveAllResponse } from '../common/remove-all-response.model';
|
||||
import { ServerService } from '../server-service';
|
||||
import { AppService } from '../app.service';
|
||||
@@ -26,7 +34,6 @@ import { PostsDataService } from '../posts-data.service';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
constructor(
|
||||
private serverService: ServerService,
|
||||
private appService: AppService,
|
||||
@@ -38,9 +45,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
private ws: WsConnectionService,
|
||||
private selectionService: SelectionService,
|
||||
private postsDataService: PostsDataService
|
||||
) {
|
||||
this.websocketHandlerPromise = this.ws.getConnection();
|
||||
}
|
||||
) {}
|
||||
|
||||
loggedUser: EventEmitter<LoggedUser> = new EventEmitter();
|
||||
isExtraSmall: Observable<BreakpointState> = this.breakpointObserver.observe(Breakpoints.XSmall);
|
||||
@@ -48,6 +53,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
subscriptions: Subscription[] = [];
|
||||
selected: RowNode[] = [];
|
||||
disableSelection: EventEmitter<boolean> = new EventEmitter();
|
||||
userSub: Subscription;
|
||||
|
||||
openSettings(): void {
|
||||
const dialogRef = this.dialog.open(SettingsComponent, {
|
||||
@@ -92,9 +98,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
filter(e => e === 'yes'),
|
||||
flatMap(e =>
|
||||
this.httpClient.post<RemoveResponse[]>(this.serverService.baseUrl + '/post/remove', toRemove)
|
||||
)
|
||||
flatMap(e => this.httpClient.post<RemoveResponse[]>(this.serverService.baseUrl + '/post/remove', toRemove))
|
||||
)
|
||||
.subscribe(
|
||||
data => {
|
||||
@@ -223,25 +227,34 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.websocketHandlerPromise.then((handler: WsHandler) => {
|
||||
console.log('Connecting to user stream');
|
||||
this.subscriptions.push(
|
||||
handler.subscribeForUser((e: LoggedUser[]) => {
|
||||
this.ngZone.run(() => {
|
||||
this.loggedUser.emit(e[0]);
|
||||
console.log('Connecting to user stream');
|
||||
this.subscriptions.push(
|
||||
this.ws.state.subscribe(state => {
|
||||
if (state) {
|
||||
this.userSub = this.ws.subscribeForUser().subscribe((e: LoggedUser[]) => {
|
||||
this.ngZone.run(() => {
|
||||
this.loggedUser.emit(e[0]);
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
handler.send(new WSMessage(CMD.USER_SUB.toString()));
|
||||
});
|
||||
this.ws.send(new WSMessage(CMD.USER_SUB.toString()));
|
||||
} else if (this.userSub != null) {
|
||||
this.userSub.unsubscribe();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.selectionService.selected$.subscribe(selected => {
|
||||
this.selected = selected;
|
||||
this.ngZone.run(() => this.disableSelection.next(this.selected.length === 0));
|
||||
});
|
||||
this.subscriptions.push(
|
||||
this.selectionService.selected$.subscribe(selected => {
|
||||
this.selected = selected;
|
||||
this.ngZone.run(() => this.disableSelection.next(this.selected.length === 0));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscriptions.forEach(e => e.unsubscribe());
|
||||
if (this.userSub != null) {
|
||||
this.userSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,136 +1,191 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observer, Subject, BehaviorSubject, ConnectableObservable, Observable, Subscription } from 'rxjs';
|
||||
import { Subject, BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import { WebSocketSubject } from 'rxjs/webSocket';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { multicast } from 'rxjs/operators';
|
||||
import { ElectronService } from 'ngx-electron';
|
||||
import { WsHandler } from './ws-handler';
|
||||
import { ServerService } from './server-service';
|
||||
declare var SockJS;
|
||||
|
||||
export enum WSState {
|
||||
INIT,
|
||||
CONNECTING,
|
||||
ERROR,
|
||||
CLOSE,
|
||||
OPEN
|
||||
}
|
||||
|
||||
const maxAttemps = 15;
|
||||
import { GrabQueueState } from './grab-queue/grab-queue.model';
|
||||
import { WSMessage } from './common/ws-message.model';
|
||||
import { PostDetails } from './post-detail/post-details.model';
|
||||
import { PostState } from './posts/post-state.model';
|
||||
import { LoggedUser } from './common/logged-user.model';
|
||||
import { DownloadSpeed } from './common/download-speed.model';
|
||||
import { GlobalState } from './common/global-state.model';
|
||||
import { map, filter } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class WsConnectionService {
|
||||
private websocket: Subject<any>;
|
||||
private state$: BehaviorSubject<WSState> = new BehaviorSubject(WSState.INIT);
|
||||
private sock;
|
||||
private _state: WSState = WSState.INIT;
|
||||
private wsHandlerPromise: Promise<WsHandler>;
|
||||
private wsHandler: WsHandler;
|
||||
private websocket: WebSocketSubject<any>;
|
||||
private online: Subject<boolean> = new BehaviorSubject(false);
|
||||
|
||||
private open = false;
|
||||
|
||||
constructor(private electronService: ElectronService, private serverService: ServerService) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.wsHandlerPromise = new Promise((resolve, reject) => {
|
||||
if (this.wsHandler != null) {
|
||||
return this.wsHandler;
|
||||
}
|
||||
if (this.electronService.isElectronApp) {
|
||||
const portRequest = setInterval(() => {
|
||||
this.electronService.ipcRenderer.send('get-port');
|
||||
}, 1000);
|
||||
if (this.electronService.isElectronApp) {
|
||||
const portRequest = setInterval(() => {
|
||||
this.electronService.ipcRenderer.send('get-port');
|
||||
}, 1000);
|
||||
|
||||
// wait for MainIPC
|
||||
this.electronService.ipcRenderer.once('port', (event, port) => {
|
||||
clearInterval(portRequest);
|
||||
console.log('server running on port', port);
|
||||
this.serverService.baseUrl = 'http://localhost:' + port;
|
||||
this.tryConnect(resolve, reject);
|
||||
});
|
||||
} else {
|
||||
this.serverService.baseUrl = environment.localhost;
|
||||
this.tryConnect(resolve, reject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public get state(): Observable<WSState> {
|
||||
return this.state$.asObservable();
|
||||
}
|
||||
|
||||
tryConnect(resolve, reject) {
|
||||
let attempts = 0;
|
||||
this.connect();
|
||||
const subscription: Subscription = this.state$.subscribe(e => {
|
||||
if (e === WSState.OPEN) {
|
||||
this.wsHandler = new WsHandler(this.websocket);
|
||||
resolve(this.wsHandler);
|
||||
}
|
||||
});
|
||||
const interval = setInterval(() => {
|
||||
if (this._state !== WSState.OPEN && attempts >= maxAttemps) {
|
||||
clearInterval(interval);
|
||||
this._state = WSState.ERROR;
|
||||
this.state$.next(this._state);
|
||||
reject('Failed to connect to server');
|
||||
if (subscription != null) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
return;
|
||||
} else if (this._state === WSState.OPEN) {
|
||||
clearInterval(interval);
|
||||
if (subscription != null) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// wait for MainIPC
|
||||
this.electronService.ipcRenderer.once('port', (event, port) => {
|
||||
clearInterval(portRequest);
|
||||
console.log('server running on port', port);
|
||||
this.serverService.baseUrl = 'http://localhost:' + port;
|
||||
this.serverService.wsBaseUrl = 'ws://localhost:' + port;
|
||||
this.connect();
|
||||
});
|
||||
} else {
|
||||
this.serverService.baseUrl = environment.localhost;
|
||||
this.serverService.wsBaseUrl = environment.ws;
|
||||
this.connect();
|
||||
attempts++;
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
connect() {
|
||||
this._state = WSState.CONNECTING;
|
||||
this.state$.next(this._state);
|
||||
this.sock = new SockJS(this.serverService.baseUrl + '/endpoint');
|
||||
const observable = Observable.create((obs: Observer<string>) => {
|
||||
this.sock.onmessage = message => obs.next(message.data);
|
||||
this.sock.onerror = error => {
|
||||
console.log('Sockjs error', error);
|
||||
obs.error(error);
|
||||
this._state = WSState.ERROR;
|
||||
this.state$.next(this._state);
|
||||
};
|
||||
this.sock.onclose = () => {
|
||||
console.log('Sockjs disconnected');
|
||||
obs.complete();
|
||||
this._state = WSState.CLOSE;
|
||||
this.state$.next(this._state);
|
||||
};
|
||||
});
|
||||
|
||||
this.sock.onopen = () => {
|
||||
console.log('Sockjs connection established');
|
||||
const observer = {
|
||||
next: (data: Object) => {
|
||||
if (this.sock.readyState === WebSocket.OPEN) {
|
||||
this.sock.send(JSON.stringify(data));
|
||||
console.log('Connecting...');
|
||||
if (this.websocket != null) {
|
||||
this.websocket.unsubscribe();
|
||||
}
|
||||
this.websocket = new WebSocketSubject({
|
||||
url: this.serverService.wsBaseUrl + '/endpoint',
|
||||
openObserver: {
|
||||
next: () => {
|
||||
console.log('Connection established');
|
||||
if (this.open !== true) {
|
||||
this.open = true;
|
||||
this.online.next(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.websocket = Subject.create(observer, observable).pipe(multicast(() => new Subject()));
|
||||
(<ConnectableObservable<any>>(<unknown>this.websocket)).connect();
|
||||
this._state = WSState.OPEN;
|
||||
this.state$.next(this._state);
|
||||
};
|
||||
},
|
||||
closeObserver: {
|
||||
next: () => {
|
||||
if (this.open !== false) {
|
||||
this.open = false;
|
||||
this.online.next(false);
|
||||
}
|
||||
setTimeout(() => this.connect(), 1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.websocket.subscribe();
|
||||
}
|
||||
|
||||
getConnection(): Promise<WsHandler> {
|
||||
return this.wsHandlerPromise;
|
||||
public get state(): Observable<boolean> {
|
||||
return this.online.asObservable();
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
console.log('Sockjs disconnecting');
|
||||
this.sock.close();
|
||||
this.websocket.unsubscribe();
|
||||
}
|
||||
|
||||
subscribeForGlobalState(): Observable<GlobalState[]> {
|
||||
return this.websocket.pipe(
|
||||
filter(e => e.length > 0 && e.filter(v => v.type === 'globalState').length > 0),
|
||||
map(e => {
|
||||
const state: Array<GlobalState> = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
state.push(new GlobalState(element.running, element.queued, element.remaining, element.error));
|
||||
});
|
||||
return state;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
subscribeForSpeed(): Observable<DownloadSpeed[]> {
|
||||
return this.websocket.pipe(
|
||||
filter(e => e.length > 0 && e.filter(v => v.type === 'downSpeed').length > 0),
|
||||
map(e => {
|
||||
const speed: Array<DownloadSpeed> = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
speed.push(new DownloadSpeed(element.speed));
|
||||
});
|
||||
return speed;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
subscribeForUser(): Observable<LoggedUser[]> {
|
||||
return this.websocket.pipe(
|
||||
filter(e => e.length > 0 && e.filter(v => v.type === 'user').length > 0),
|
||||
map(e => {
|
||||
const user: Array<LoggedUser> = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
user.push(new LoggedUser(element.user));
|
||||
});
|
||||
return user;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
subscribeForPosts(): Observable<PostState[]> {
|
||||
return this.websocket.pipe(
|
||||
filter(e => e.length > 0 && e.filter(v => v.type === 'post').length > 0),
|
||||
map(e => {
|
||||
const posts: Array<PostState> = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
posts.push(
|
||||
new PostState(
|
||||
element.type,
|
||||
element.postId,
|
||||
element.title,
|
||||
element.done === 0 && element.total === 0 ? 0 : (element.done / element.total) * 100,
|
||||
element.status,
|
||||
element.removed,
|
||||
element.url,
|
||||
element.done,
|
||||
element.total,
|
||||
element.hosts,
|
||||
element.metadata.PREVIEWS
|
||||
)
|
||||
);
|
||||
});
|
||||
return posts;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
subscribeForPostDetails(): Observable<PostDetails[]> {
|
||||
return this.websocket.pipe(
|
||||
filter(e => e.length > 0 && e[0].type === 'img'),
|
||||
map(e => {
|
||||
const values = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
values.push(
|
||||
new PostDetails(
|
||||
element.postId,
|
||||
element.postName,
|
||||
element.url,
|
||||
element.current === 0 && element.total === 0 ? 0 : (element.current / element.total) * 100,
|
||||
element.status,
|
||||
element.index
|
||||
)
|
||||
);
|
||||
});
|
||||
return values;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
subscribeForGrabQueue(): Observable<GrabQueueState[]> {
|
||||
return this.websocket.pipe(
|
||||
filter(e => e.length > 0 && e.filter(v => v.type === 'grabQueue').length > 0),
|
||||
map(e => {
|
||||
const grabQueue: Array<GrabQueueState> = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
grabQueue.push(
|
||||
new GrabQueueState(element.type, element.link, element.threadId, element.postId, element.removed)
|
||||
);
|
||||
});
|
||||
return grabQueue;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
send(wsMessage: WSMessage) {
|
||||
this.websocket.next(wsMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,140 +10,5 @@ import { GlobalState } from './common/global-state.model';
|
||||
import { DownloadSpeed } from './common/download-speed.model';
|
||||
|
||||
export class WsHandler {
|
||||
constructor(private websocket: Subject<any>) {}
|
||||
|
||||
subscribeForGlobalState(callback: (stateStream: Array<GlobalState>) => void): Subscription {
|
||||
return this.websocket
|
||||
.pipe(
|
||||
map(e => JSON.parse(e)),
|
||||
filter(e => e.length > 0 && e.filter(v => v.type === 'globalState').length > 0),
|
||||
map(e => {
|
||||
const state: Array<GlobalState> = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
state.push(new GlobalState(element.running, element.queued, element.remaining, element.error));
|
||||
});
|
||||
return state;
|
||||
})
|
||||
)
|
||||
.subscribe(e => {
|
||||
callback(e);
|
||||
});
|
||||
}
|
||||
|
||||
subscribeForSpeed(callback: (speedStream: Array<DownloadSpeed>) => void): Subscription {
|
||||
return this.websocket
|
||||
.pipe(
|
||||
map(e => JSON.parse(e)),
|
||||
filter(e => e.length > 0 && e.filter(v => v.type === 'downSpeed').length > 0),
|
||||
map(e => {
|
||||
const speed: Array<DownloadSpeed> = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
speed.push(new DownloadSpeed(element.speed));
|
||||
});
|
||||
return speed;
|
||||
})
|
||||
)
|
||||
.subscribe(e => {
|
||||
callback(e);
|
||||
});
|
||||
}
|
||||
|
||||
subscribeForUser(callback: (userStream: Array<LoggedUser>) => void): Subscription {
|
||||
return this.websocket
|
||||
.pipe(
|
||||
map(e => JSON.parse(e)),
|
||||
filter(e => e.length > 0 && e.filter(v => v.type === 'user').length > 0),
|
||||
map(e => {
|
||||
const user: Array<LoggedUser> = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
user.push(new LoggedUser(element.user));
|
||||
});
|
||||
return user;
|
||||
})
|
||||
)
|
||||
.subscribe(e => {
|
||||
callback(e);
|
||||
});
|
||||
}
|
||||
|
||||
subscribeForPosts(callback: (postStream: Array<PostState>) => void): Subscription {
|
||||
return this.websocket
|
||||
.pipe(
|
||||
map(e => JSON.parse(e)),
|
||||
filter(e => e.length > 0 && e.filter(v => v.type === 'post').length > 0),
|
||||
map(e => {
|
||||
const posts: Array<PostState> = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
posts.push(
|
||||
new PostState(
|
||||
element.type,
|
||||
element.postId,
|
||||
element.title,
|
||||
element.done === 0 && element.total === 0 ? 0 : (element.done / element.total) * 100,
|
||||
element.status,
|
||||
element.removed,
|
||||
element.url,
|
||||
element.done,
|
||||
element.total,
|
||||
element.hosts,
|
||||
element.metadata.PREVIEWS
|
||||
)
|
||||
);
|
||||
});
|
||||
return posts;
|
||||
})
|
||||
)
|
||||
.subscribe(e => {
|
||||
callback(e);
|
||||
});
|
||||
}
|
||||
|
||||
subscribeForPostDetails(callback: (stream: Array<PostDetails>) => void): Subscription {
|
||||
return this.websocket
|
||||
.pipe(
|
||||
map(e => JSON.parse(e)),
|
||||
filter(e => e.length > 0 && e[0].type === 'img'),
|
||||
map(e => {
|
||||
const values = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
values.push(
|
||||
new PostDetails(
|
||||
element.postId,
|
||||
element.postName,
|
||||
element.url,
|
||||
element.current === 0 && element.total === 0 ? 0 : (element.current / element.total) * 100,
|
||||
element.status,
|
||||
element.index
|
||||
)
|
||||
);
|
||||
});
|
||||
return values;
|
||||
})
|
||||
)
|
||||
.subscribe(e => callback(e));
|
||||
}
|
||||
|
||||
subscribeForGrabQueue(callback: (grabQueueStream: Array<GrabQueueState>) => void): Subscription {
|
||||
return this.websocket
|
||||
.pipe(
|
||||
map(e => JSON.parse(e)),
|
||||
filter(e => e.length > 0 && e.filter(v => v.type === 'grabQueue').length > 0),
|
||||
map(e => {
|
||||
const grabQueue: Array<GrabQueueState> = [];
|
||||
(<Array<any>>e).forEach(element => {
|
||||
grabQueue.push(
|
||||
new GrabQueueState(element.type, element.link, element.threadId, element.postId, element.removed)
|
||||
);
|
||||
});
|
||||
return grabQueue;
|
||||
})
|
||||
)
|
||||
.subscribe(e => {
|
||||
callback(e);
|
||||
});
|
||||
}
|
||||
|
||||
send(wsMessage: WSMessage) {
|
||||
this.websocket.next(wsMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
-4
File diff suppressed because one or more lines are too long
@@ -1,5 +1,6 @@
|
||||
export const environment = {
|
||||
production: true,
|
||||
localhost: '',
|
||||
version: '2.10.4'
|
||||
localhost: `${window.location.protocol}://${window.location.host}`,
|
||||
ws: `${window.location.protocol === 'https' ? 'wss' : 'ws'}://${window.location.host}`,
|
||||
version: '2.10.7'
|
||||
};
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
export const environment = {
|
||||
production: false,
|
||||
localhost: 'http://localhost:8080',
|
||||
version: '2.10.4'
|
||||
ws: 'ws://localhost:8080',
|
||||
version: '2.10.7'
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||
<link href="favicon.ico" rel="icon" type="image/x-icon"/>
|
||||
<script src="assets/sockjs.min.js"></script>
|
||||
</head>
|
||||
<body class="mat-app-background">
|
||||
<app-root></app-root>
|
||||
|
||||
Reference in New Issue
Block a user