mirror of https://github.com/Chocobozzz/PeerTube
Client: Handle NSFW video
parent
1d49e1e27d
commit
92fb909c9b
|
@ -1,7 +1,7 @@
|
||||||
import { Component, OnInit, ViewContainerRef } from '@angular/core';
|
import { Component, OnInit, ViewContainerRef } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { AuthService } from './core';
|
import { AuthService, ConfigService } from './core';
|
||||||
import { VideoService } from './videos';
|
import { VideoService } from './videos';
|
||||||
import { UserService } from './shared';
|
import { UserService } from './shared';
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ export class AppComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
|
private configService: ConfigService,
|
||||||
private userService: UserService,
|
private userService: UserService,
|
||||||
private videoService: VideoService,
|
private videoService: VideoService,
|
||||||
viewContainerRef: ViewContainerRef
|
viewContainerRef: ViewContainerRef
|
||||||
|
@ -38,6 +39,7 @@ export class AppComponent implements OnInit {
|
||||||
this.userService.checkTokenValidity();
|
this.userService.checkTokenValidity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.configService.loadConfig();
|
||||||
this.videoService.loadVideoCategories();
|
this.videoService.loadVideoCategories();
|
||||||
this.videoService.loadVideoLicences();
|
this.videoService.loadVideoLicences();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ export class AuthUser extends User {
|
||||||
private static KEYS = {
|
private static KEYS = {
|
||||||
ID: 'id',
|
ID: 'id',
|
||||||
ROLE: 'role',
|
ROLE: 'role',
|
||||||
USERNAME: 'username'
|
USERNAME: 'username',
|
||||||
|
DISPLAY_NSFW: 'display_nsfw'
|
||||||
};
|
};
|
||||||
|
|
||||||
tokens: Tokens;
|
tokens: Tokens;
|
||||||
|
@ -17,7 +18,8 @@ export class AuthUser extends User {
|
||||||
{
|
{
|
||||||
id: parseInt(localStorage.getItem(this.KEYS.ID)),
|
id: parseInt(localStorage.getItem(this.KEYS.ID)),
|
||||||
username: localStorage.getItem(this.KEYS.USERNAME),
|
username: localStorage.getItem(this.KEYS.USERNAME),
|
||||||
role: localStorage.getItem(this.KEYS.ROLE)
|
role: localStorage.getItem(this.KEYS.ROLE),
|
||||||
|
displayNSFW: localStorage.getItem(this.KEYS.DISPLAY_NSFW) === 'true'
|
||||||
},
|
},
|
||||||
Tokens.load()
|
Tokens.load()
|
||||||
);
|
);
|
||||||
|
@ -30,10 +32,16 @@ export class AuthUser extends User {
|
||||||
localStorage.removeItem(this.KEYS.USERNAME);
|
localStorage.removeItem(this.KEYS.USERNAME);
|
||||||
localStorage.removeItem(this.KEYS.ID);
|
localStorage.removeItem(this.KEYS.ID);
|
||||||
localStorage.removeItem(this.KEYS.ROLE);
|
localStorage.removeItem(this.KEYS.ROLE);
|
||||||
|
localStorage.removeItem(this.KEYS.DISPLAY_NSFW);
|
||||||
Tokens.flush();
|
Tokens.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(userHash: { id: number, username: string, role: string }, hashTokens: any) {
|
constructor(userHash: {
|
||||||
|
id: number,
|
||||||
|
username: string,
|
||||||
|
role: string,
|
||||||
|
displayNSFW: boolean
|
||||||
|
}, hashTokens: any) {
|
||||||
super(userHash);
|
super(userHash);
|
||||||
this.tokens = new Tokens(hashTokens);
|
this.tokens = new Tokens(hashTokens);
|
||||||
}
|
}
|
||||||
|
@ -59,6 +67,7 @@ export class AuthUser extends User {
|
||||||
localStorage.setItem(AuthUser.KEYS.ID, this.id.toString());
|
localStorage.setItem(AuthUser.KEYS.ID, this.id.toString());
|
||||||
localStorage.setItem(AuthUser.KEYS.USERNAME, this.username);
|
localStorage.setItem(AuthUser.KEYS.USERNAME, this.username);
|
||||||
localStorage.setItem(AuthUser.KEYS.ROLE, this.role);
|
localStorage.setItem(AuthUser.KEYS.ROLE, this.role);
|
||||||
|
localStorage.setItem(AuthUser.KEYS.DISPLAY_NSFW, JSON.stringify(this.displayNSFW);
|
||||||
this.tokens.save();
|
this.tokens.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Http } from '@angular/http';
|
||||||
|
|
||||||
|
import { RestExtractor } from '../../shared/rest';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ConfigService {
|
||||||
|
private static BASE_CONFIG_URL = '/api/v1/config/';
|
||||||
|
|
||||||
|
private config: {
|
||||||
|
signup: {
|
||||||
|
enabled: boolean
|
||||||
|
}
|
||||||
|
} = {
|
||||||
|
signup: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private http: Http,
|
||||||
|
private restExtractor: RestExtractor,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
loadConfig() {
|
||||||
|
this.http.get(ConfigService.BASE_CONFIG_URL)
|
||||||
|
.map(this.restExtractor.extractDataGet)
|
||||||
|
.subscribe(data => {
|
||||||
|
this.config = data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfig() {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './config.service';
|
|
@ -7,6 +7,7 @@ import { SimpleNotificationsModule } from 'angular2-notifications';
|
||||||
import { ModalModule } from 'ng2-bootstrap/modal';
|
import { ModalModule } from 'ng2-bootstrap/modal';
|
||||||
|
|
||||||
import { AuthService } from './auth';
|
import { AuthService } from './auth';
|
||||||
|
import { ConfigService } from './config';
|
||||||
import { ConfirmComponent, ConfirmService } from './confirm';
|
import { ConfirmComponent, ConfirmService } from './confirm';
|
||||||
import { MenuComponent, MenuAdminComponent } from './menu';
|
import { MenuComponent, MenuAdminComponent } from './menu';
|
||||||
import { throwIfAlreadyLoaded } from './module-import-guard';
|
import { throwIfAlreadyLoaded } from './module-import-guard';
|
||||||
|
@ -37,7 +38,8 @@ import { throwIfAlreadyLoaded } from './module-import-guard';
|
||||||
|
|
||||||
providers: [
|
providers: [
|
||||||
AuthService,
|
AuthService,
|
||||||
ConfirmService
|
ConfirmService,
|
||||||
|
ConfigService
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CoreModule {
|
export class CoreModule {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './auth';
|
export * from './auth';
|
||||||
|
export * from './config';
|
||||||
export * from './confirm';
|
export * from './confirm';
|
||||||
export * from './menu';
|
export * from './menu';
|
||||||
export * from './core.module'
|
export * from './core.module'
|
||||||
|
|
|
@ -2,12 +2,20 @@ export class User {
|
||||||
id: number;
|
id: number;
|
||||||
username: string;
|
username: string;
|
||||||
role: string;
|
role: string;
|
||||||
|
displayNSFW: boolean;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|
||||||
constructor(hash: { id: number, username: string, role: string, createdAt?: Date }) {
|
constructor(hash: {
|
||||||
|
id: number,
|
||||||
|
username: string,
|
||||||
|
role: string,
|
||||||
|
displayNSFW?: boolean,
|
||||||
|
createdAt?: Date,
|
||||||
|
}) {
|
||||||
this.id = hash.id;
|
this.id = hash.id;
|
||||||
this.username = hash.username;
|
this.username = hash.username;
|
||||||
this.role = hash.role;
|
this.role = hash.role;
|
||||||
|
this.displayNSFW = hash.displayNSFW;
|
||||||
|
|
||||||
if (hash.createdAt) {
|
if (hash.createdAt) {
|
||||||
this.createdAt = hash.createdAt;
|
this.createdAt = hash.createdAt;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { User } from '../../shared';
|
||||||
|
|
||||||
export class Video {
|
export class Video {
|
||||||
author: string;
|
author: string;
|
||||||
by: string;
|
by: string;
|
||||||
|
@ -16,6 +18,7 @@ export class Video {
|
||||||
views: number;
|
views: number;
|
||||||
likes: number;
|
likes: number;
|
||||||
dislikes: number;
|
dislikes: number;
|
||||||
|
nsfw: boolean;
|
||||||
|
|
||||||
private static createByString(author: string, podHost: string) {
|
private static createByString(author: string, podHost: string) {
|
||||||
return author + '@' + podHost;
|
return author + '@' + podHost;
|
||||||
|
@ -47,6 +50,7 @@ export class Video {
|
||||||
views: number,
|
views: number,
|
||||||
likes: number,
|
likes: number,
|
||||||
dislikes: number,
|
dislikes: number,
|
||||||
|
nsfw: boolean
|
||||||
}) {
|
}) {
|
||||||
this.author = hash.author;
|
this.author = hash.author;
|
||||||
this.createdAt = new Date(hash.createdAt);
|
this.createdAt = new Date(hash.createdAt);
|
||||||
|
@ -64,11 +68,17 @@ export class Video {
|
||||||
this.views = hash.views;
|
this.views = hash.views;
|
||||||
this.likes = hash.likes;
|
this.likes = hash.likes;
|
||||||
this.dislikes = hash.dislikes;
|
this.dislikes = hash.dislikes;
|
||||||
|
this.nsfw = hash.nsfw;
|
||||||
|
|
||||||
this.by = Video.createByString(hash.author, hash.podHost);
|
this.by = Video.createByString(hash.author, hash.podHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
isRemovableBy(user) {
|
isRemovableBy(user: User) {
|
||||||
return this.isLocal === true && user && this.author === user.username;
|
return this.isLocal === true && user && this.author === user.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isVideoNSFWForUser(user: User) {
|
||||||
|
// If the video is NSFW and the user is not logged in, or the user does not want to display NSFW videos...
|
||||||
|
return (this.nsfw && (!user || user.displayNSFW === false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="nsfw">NSFW</label>
|
||||||
|
<input
|
||||||
|
type="checkbox" id="nsfw"
|
||||||
|
formControlName="nsfw"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="category">Category</label>
|
<label for="category">Category</label>
|
||||||
<select class="form-control" id="category" formControlName="category">
|
<select class="form-control" id="category" formControlName="category">
|
||||||
|
|
|
@ -71,6 +71,7 @@ export class VideoAddComponent extends FormReactive implements OnInit {
|
||||||
buildForm() {
|
buildForm() {
|
||||||
this.form = this.formBuilder.group({
|
this.form = this.formBuilder.group({
|
||||||
name: [ '', VIDEO_NAME.VALIDATORS ],
|
name: [ '', VIDEO_NAME.VALIDATORS ],
|
||||||
|
nsfw: [ false ],
|
||||||
category: [ '', VIDEO_CATEGORY.VALIDATORS ],
|
category: [ '', VIDEO_CATEGORY.VALIDATORS ],
|
||||||
licence: [ '', VIDEO_LICENCE.VALIDATORS ],
|
licence: [ '', VIDEO_LICENCE.VALIDATORS ],
|
||||||
description: [ '', VIDEO_DESCRIPTION.VALIDATORS ],
|
description: [ '', VIDEO_DESCRIPTION.VALIDATORS ],
|
||||||
|
@ -93,12 +94,14 @@ export class VideoAddComponent extends FormReactive implements OnInit {
|
||||||
|
|
||||||
this.uploader.onBuildItemForm = (item, form) => {
|
this.uploader.onBuildItemForm = (item, form) => {
|
||||||
const name = this.form.value['name'];
|
const name = this.form.value['name'];
|
||||||
|
const nsfw = this.form.value['nsfw'];
|
||||||
const category = this.form.value['category'];
|
const category = this.form.value['category'];
|
||||||
const licence = this.form.value['licence'];
|
const licence = this.form.value['licence'];
|
||||||
const description = this.form.value['description'];
|
const description = this.form.value['description'];
|
||||||
|
|
||||||
form.append('name', name);
|
form.append('name', name);
|
||||||
form.append('category', category);
|
form.append('category', category);
|
||||||
|
form.append('nsfw', nsfw);
|
||||||
form.append('licence', licence);
|
form.append('licence', licence);
|
||||||
form.append('description', description);
|
form.append('description', description);
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,11 @@
|
||||||
[routerLink]="['/videos/watch', video.id]" [attr.title]="video.description"
|
[routerLink]="['/videos/watch', video.id]" [attr.title]="video.description"
|
||||||
class="video-miniature-thumbnail"
|
class="video-miniature-thumbnail"
|
||||||
>
|
>
|
||||||
<img [attr.src]="video.thumbnailPath" alt="video thumbnail" />
|
<img *ngIf="isVideoNSFWForThisUser() === false" [attr.src]="video.thumbnailPath" alt="video thumbnail" />
|
||||||
|
<div *ngIf="isVideoNSFWForThisUser()" class="thumbnail-nsfw">
|
||||||
|
NSFW
|
||||||
|
</div>
|
||||||
|
|
||||||
<span class="video-miniature-duration">{{ video.duration }}</span>
|
<span class="video-miniature-duration">{{ video.duration }}</span>
|
||||||
</a>
|
</a>
|
||||||
<span
|
<span
|
||||||
|
@ -13,7 +17,7 @@
|
||||||
|
|
||||||
<div class="video-miniature-informations">
|
<div class="video-miniature-informations">
|
||||||
<span class="video-miniature-name-tags">
|
<span class="video-miniature-name-tags">
|
||||||
<a [routerLink]="['/videos/watch', video.id]" [attr.title]="video.name" class="video-miniature-name">{{ video.name }}</a>
|
<a [routerLink]="['/videos/watch', video.id]" [attr.title]="getVideoName()" class="video-miniature-name">{{ getVideoName() }}</a>
|
||||||
|
|
||||||
<div class="video-miniature-tags">
|
<div class="video-miniature-tags">
|
||||||
<span *ngFor="let tag of video.tags" class="video-miniature-tag">
|
<span *ngFor="let tag of video.tags" class="video-miniature-tag">
|
||||||
|
|
|
@ -15,6 +15,21 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-nsfw {
|
||||||
|
background-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 30px;
|
||||||
|
line-height: 110px;
|
||||||
|
|
||||||
|
width: 200px;
|
||||||
|
height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
.video-miniature-duration {
|
.video-miniature-duration {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
|
||||||
import { NotificationsService } from 'angular2-notifications';
|
import { NotificationsService } from 'angular2-notifications';
|
||||||
|
|
||||||
import { ConfirmService } from '../../core';
|
import { ConfirmService, ConfigService } from '../../core';
|
||||||
import { SortField, Video, VideoService } from '../shared';
|
import { SortField, Video, VideoService } from '../shared';
|
||||||
import { User } from '../../shared';
|
import { User } from '../../shared';
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ export class VideoMiniatureComponent {
|
||||||
constructor(
|
constructor(
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
private confirmService: ConfirmService,
|
private confirmService: ConfirmService,
|
||||||
|
private configService: ConfigService,
|
||||||
private videoService: VideoService
|
private videoService: VideoService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@ -31,6 +32,13 @@ export class VideoMiniatureComponent {
|
||||||
return this.hovering && this.video.isRemovableBy(this.user);
|
return this.hovering && this.video.isRemovableBy(this.user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getVideoName() {
|
||||||
|
if (this.isVideoNSFWForThisUser())
|
||||||
|
return 'NSFW';
|
||||||
|
|
||||||
|
return this.video.name;
|
||||||
|
}
|
||||||
|
|
||||||
onBlur() {
|
onBlur() {
|
||||||
this.hovering = false;
|
this.hovering = false;
|
||||||
}
|
}
|
||||||
|
@ -52,4 +60,8 @@ export class VideoMiniatureComponent {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isVideoNSFWForThisUser() {
|
||||||
|
return this.video.isVideoNSFWForUser(this.user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
import { Component, ElementRef, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { Subscription } from 'rxjs/Subscription';
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
|
|
||||||
import * as videojs from 'video.js';
|
import * as videojs from 'video.js';
|
||||||
import { MetaService } from '@nglibs/meta';
|
import { MetaService } from '@nglibs/meta';
|
||||||
import { NotificationsService } from 'angular2-notifications';
|
import { NotificationsService } from 'angular2-notifications';
|
||||||
|
|
||||||
import { AuthService } from '../../core';
|
import { AuthService, ConfirmService } from '../../core';
|
||||||
import { VideoMagnetComponent } from './video-magnet.component';
|
import { VideoMagnetComponent } from './video-magnet.component';
|
||||||
import { VideoShareComponent } from './video-share.component';
|
import { VideoShareComponent } from './video-share.component';
|
||||||
import { VideoReportComponent } from './video-report.component';
|
import { VideoReportComponent } from './video-report.component';
|
||||||
|
@ -47,7 +48,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
private elementRef: ElementRef,
|
private elementRef: ElementRef,
|
||||||
private ngZone: NgZone,
|
private ngZone: NgZone,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
private videoService: VideoService,
|
private videoService: VideoService,
|
||||||
|
private confirmService: ConfirmService,
|
||||||
private metaService: MetaService,
|
private metaService: MetaService,
|
||||||
private webTorrentService: WebTorrentService,
|
private webTorrentService: WebTorrentService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
|
@ -58,15 +61,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.paramsSub = this.route.params.subscribe(routeParams => {
|
this.paramsSub = this.route.params.subscribe(routeParams => {
|
||||||
let id = routeParams['id'];
|
let id = routeParams['id'];
|
||||||
this.videoService.getVideo(id).subscribe(
|
this.videoService.getVideo(id).subscribe(
|
||||||
video => {
|
video => this.onVideoFetched(video),
|
||||||
this.video = video;
|
|
||||||
this.setOpenGraphTags();
|
error => this.videoNotFound = true
|
||||||
this.loadVideo();
|
|
||||||
this.checkUserRating();
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
this.videoNotFound = true;
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -92,7 +89,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
window.clearInterval(this.torrentInfosInterval);
|
window.clearInterval(this.torrentInfosInterval);
|
||||||
window.clearTimeout(this.errorTimer);
|
window.clearTimeout(this.errorTimer);
|
||||||
|
|
||||||
if (this.video !== null) {
|
if (this.video !== null && this.webTorrentService.has(this.video.magnetUri)) {
|
||||||
this.webTorrentService.remove(this.video.magnetUri);
|
this.webTorrentService.remove(this.video.magnetUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +203,29 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onVideoFetched(video: Video) {
|
||||||
|
this.video = video;
|
||||||
|
|
||||||
|
let observable;
|
||||||
|
if (this.video.isVideoNSFWForUser(this.authService.getUser())) {
|
||||||
|
observable = this.confirmService.confirm('This video is not safe for work. Are you sure you want to watch it?', 'NSFW');
|
||||||
|
} else {
|
||||||
|
observable = Observable.of(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
observable.subscribe(
|
||||||
|
res => {
|
||||||
|
if (res === false) {
|
||||||
|
return this.router.navigate([ '/videos/list' ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setOpenGraphTags();
|
||||||
|
this.loadVideo();
|
||||||
|
this.checkUserRating();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private updateVideoRating(oldRating: RateType, newRating: RateType) {
|
private updateVideoRating(oldRating: RateType, newRating: RateType) {
|
||||||
let likesToIncrement = 0;
|
let likesToIncrement = 0;
|
||||||
let dislikesToIncrement = 0;
|
let dislikesToIncrement = 0;
|
||||||
|
|
|
@ -26,4 +26,8 @@ export class WebTorrentService {
|
||||||
remove(magnetUri: string) {
|
remove(magnetUri: string) {
|
||||||
return this.client.remove(magnetUri);
|
return this.client.remove(magnetUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
has(magnetUri: string) {
|
||||||
|
return this.client.get(magnetUri) !== null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue