aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-08-06 17:13:39 +0200
committerChocobozzz <me@florianbigard.com>2018-08-08 09:30:31 +0200
commitce33919c24e7402d92d81f3cd8e545df52d98240 (patch)
tree7e131a2f8df649899d0a71294665cf386ffb50d4 /client/src
parent788487140c500abeb69ca44daf3a9e26efa8d36f (diff)
downloadPeerTube-ce33919c24e7402d92d81f3cd8e545df52d98240.tar.gz
PeerTube-ce33919c24e7402d92d81f3cd8e545df52d98240.tar.zst
PeerTube-ce33919c24e7402d92d81f3cd8e545df52d98240.zip
Import magnets with webtorrent
Diffstat (limited to 'client/src')
-rw-r--r--client/src/app/shared/video-import/video-import.service.ts58
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html60
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.scss37
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts132
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html2
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss2
-rw-r--r--client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts2
-rw-r--r--client/src/app/videos/+video-edit/video-add.component.html6
-rw-r--r--client/src/app/videos/+video-edit/video-add.component.ts13
-rw-r--r--client/src/app/videos/+video-edit/video-add.module.ts4
10 files changed, 287 insertions, 29 deletions
diff --git a/client/src/app/shared/video-import/video-import.service.ts b/client/src/app/shared/video-import/video-import.service.ts
index 59b58ab38..002412bd7 100644
--- a/client/src/app/shared/video-import/video-import.service.ts
+++ b/client/src/app/shared/video-import/video-import.service.ts
@@ -26,8 +26,43 @@ export class VideoImportService {
26 private serverService: ServerService 26 private serverService: ServerService
27 ) {} 27 ) {}
28 28
29 importVideo (targetUrl: string, video: VideoUpdate): Observable<VideoImport> { 29 importVideoUrl (targetUrl: string, video: VideoUpdate): Observable<VideoImport> {
30 const url = VideoImportService.BASE_VIDEO_IMPORT_URL 30 const url = VideoImportService.BASE_VIDEO_IMPORT_URL
31
32 const body = this.buildImportVideoObject(video)
33 body.targetUrl = targetUrl
34
35 const data = objectToFormData(body)
36 return this.authHttp.post<VideoImport>(url, data)
37 .pipe(catchError(res => this.restExtractor.handleError(res)))
38 }
39
40 importVideoTorrent (target: string | Blob, video: VideoUpdate): Observable<VideoImport> {
41 const url = VideoImportService.BASE_VIDEO_IMPORT_URL
42 const body: VideoImportCreate = this.buildImportVideoObject(video)
43
44 if (typeof target === 'string') body.magnetUri = target
45 else body.torrentfile = target
46
47 const data = objectToFormData(body)
48 return this.authHttp.post<VideoImport>(url, data)
49 .pipe(catchError(res => this.restExtractor.handleError(res)))
50 }
51
52 getMyVideoImports (pagination: RestPagination, sort: SortMeta): Observable<ResultList<VideoImport>> {
53 let params = new HttpParams()
54 params = this.restService.addRestGetParams(params, pagination, sort)
55
56 return this.authHttp
57 .get<ResultList<VideoImport>>(UserService.BASE_USERS_URL + '/me/videos/imports', { params })
58 .pipe(
59 switchMap(res => this.extractVideoImports(res)),
60 map(res => this.restExtractor.convertResultListDateToHuman(res)),
61 catchError(err => this.restExtractor.handleError(err))
62 )
63 }
64
65 private buildImportVideoObject (video: VideoUpdate): VideoImportCreate {
31 const language = video.language || null 66 const language = video.language || null
32 const licence = video.licence || null 67 const licence = video.licence || null
33 const category = video.category || null 68 const category = video.category || null
@@ -35,9 +70,7 @@ export class VideoImportService {
35 const support = video.support || null 70 const support = video.support || null
36 const scheduleUpdate = video.scheduleUpdate || null 71 const scheduleUpdate = video.scheduleUpdate || null
37 72
38 const body: VideoImportCreate = { 73 return {
39 targetUrl,
40
41 name: video.name, 74 name: video.name,
42 category, 75 category,
43 licence, 76 licence,
@@ -54,23 +87,6 @@ export class VideoImportService {
54 previewfile: video.previewfile, 87 previewfile: video.previewfile,
55 scheduleUpdate 88 scheduleUpdate
56 } 89 }
57
58 const data = objectToFormData(body)
59 return this.authHttp.post<VideoImport>(url, data)
60 .pipe(catchError(res => this.restExtractor.handleError(res)))
61 }
62
63 getMyVideoImports (pagination: RestPagination, sort: SortMeta): Observable<ResultList<VideoImport>> {
64 let params = new HttpParams()
65 params = this.restService.addRestGetParams(params, pagination, sort)
66
67 return this.authHttp
68 .get<ResultList<VideoImport>>(UserService.BASE_USERS_URL + '/me/videos/imports', { params })
69 .pipe(
70 switchMap(res => this.extractVideoImports(res)),
71 map(res => this.restExtractor.convertResultListDateToHuman(res)),
72 catchError(err => this.restExtractor.handleError(err))
73 )
74 } 90 }
75 91
76 private extractVideoImports (result: ResultList<VideoImport>): Observable<ResultList<VideoImport>> { 92 private extractVideoImports (result: ResultList<VideoImport>): Observable<ResultList<VideoImport>> {
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html
new file mode 100644
index 000000000..409e4de5e
--- /dev/null
+++ b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.html
@@ -0,0 +1,60 @@
1<div *ngIf="!hasImportedVideo" class="upload-video-container">
2 <div class="import-video-torrent">
3 <div class="icon icon-upload"></div>
4
5 <div class="form-group">
6 <label i18n for="magnetUri">Magnet URI</label>
7 <my-help
8 helpType="custom" i18n-customHtml
9 customHtml="You can import any torrent file that points to a mp4 file. You should make sure you have diffusion rights over the content it points to, otherwise it could cause legal trouble to yourself and your instance."
10 ></my-help>
11
12 <input type="text" id="magnetUri" [(ngModel)]="magnetUri" />
13 </div>
14
15 <div class="form-group">
16 <label i18n for="first-step-channel">Channel</label>
17 <div class="peertube-select-container">
18 <select id="first-step-channel" [(ngModel)]="firstStepChannelId">
19 <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
20 </select>
21 </div>
22 </div>
23
24 <div class="form-group">
25 <label i18n for="first-step-privacy">Privacy</label>
26 <div class="peertube-select-container">
27 <select id="first-step-privacy" [(ngModel)]="firstStepPrivacyId">
28 <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
29 </select>
30 </div>
31 </div>
32
33 <input
34 type="button" i18n-value value="Import"
35 [disabled]="!isMagnetUrlValid() || isImportingVideo" (click)="importVideo()"
36 />
37 </div>
38</div>
39
40<div *ngIf="hasImportedVideo" class="alert alert-info" i18n>
41 Congratulations, the video will be imported with BitTorrent! You can already add information about this video.
42</div>
43
44<!-- Hidden because we want to load the component -->
45<form [hidden]="!hasImportedVideo" novalidate [formGroup]="form">
46 <my-video-edit
47 [form]="form" [formErrors]="formErrors" [videoCaptions]="videoCaptions" [schedulePublicationPossible]="false"
48 [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies" [userVideoChannels]="userVideoChannels"
49 ></my-video-edit>
50
51 <div class="submit-container">
52 <div class="submit-button"
53 (click)="updateSecondStep()"
54 [ngClass]="{ disabled: !form.valid || isUpdatingVideo === true }"
55 >
56 <span class="icon icon-validate"></span>
57 <input type="button" i18n-value value="Update" />
58 </div>
59 </div>
60</form>
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.scss b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.scss
new file mode 100644
index 000000000..1ef5adc25
--- /dev/null
+++ b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.scss
@@ -0,0 +1,37 @@
1@import 'variables';
2@import 'mixins';
3
4$width-size: 190px;
5
6.peertube-select-container {
7 @include peertube-select-container($width-size);
8}
9
10.import-video-torrent {
11 display: flex;
12 flex-direction: column;
13 align-items: center;
14
15 .icon.icon-upload {
16 @include icon(90px);
17 margin-bottom: 25px;
18 cursor: default;
19
20 background-image: url('../../../../assets/images/video/upload.svg');
21 }
22
23 input[type=text] {
24 @include peertube-input-text($width-size);
25 display: block;
26 }
27
28 input[type=button] {
29 @include peertube-button;
30 @include orange-button;
31
32 width: $width-size;
33 margin-top: 30px;
34 }
35}
36
37
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts
new file mode 100644
index 000000000..330c37718
--- /dev/null
+++ b/client/src/app/videos/+video-edit/video-add-components/video-import-torrent.component.ts
@@ -0,0 +1,132 @@
1import { Component, EventEmitter, OnInit, Output } from '@angular/core'
2import { Router } from '@angular/router'
3import { NotificationsService } from 'angular2-notifications'
4import { VideoPrivacy, VideoUpdate } from '../../../../../../shared/models/videos'
5import { AuthService, ServerService } from '../../../core'
6import { VideoService } from '../../../shared/video/video.service'
7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { LoadingBarService } from '@ngx-loading-bar/core'
9import { VideoSend } from '@app/videos/+video-edit/video-add-components/video-send'
10import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.service'
11import { VideoEdit } from '@app/shared/video/video-edit.model'
12import { FormValidatorService } from '@app/shared'
13import { VideoCaptionService } from '@app/shared/video-caption'
14import { VideoImportService } from '@app/shared/video-import'
15
16@Component({
17 selector: 'my-video-import-torrent',
18 templateUrl: './video-import-torrent.component.html',
19 styleUrls: [
20 '../shared/video-edit.component.scss',
21 './video-import-torrent.component.scss'
22 ]
23})
24export class VideoImportTorrentComponent extends VideoSend implements OnInit, CanComponentDeactivate {
25 @Output() firstStepDone = new EventEmitter<string>()
26
27 videoFileName: string
28 magnetUri = ''
29
30 isImportingVideo = false
31 hasImportedVideo = false
32 isUpdatingVideo = false
33
34 video: VideoEdit
35
36 protected readonly DEFAULT_VIDEO_PRIVACY = VideoPrivacy.PRIVATE
37
38 constructor (
39 protected formValidatorService: FormValidatorService,
40 protected loadingBar: LoadingBarService,
41 protected notificationsService: NotificationsService,
42 protected authService: AuthService,
43 protected serverService: ServerService,
44 protected videoService: VideoService,
45 protected videoCaptionService: VideoCaptionService,
46 private router: Router,
47 private videoImportService: VideoImportService,
48 private i18n: I18n
49 ) {
50 super()
51 }
52
53 ngOnInit () {
54 super.ngOnInit()
55 }
56
57 canDeactivate () {
58 return { canDeactivate: true }
59 }
60
61 isMagnetUrlValid () {
62 return !!this.magnetUri
63 }
64
65 importVideo () {
66 this.isImportingVideo = true
67
68 const videoUpdate: VideoUpdate = {
69 privacy: this.firstStepPrivacyId,
70 waitTranscoding: false,
71 commentsEnabled: true,
72 channelId: this.firstStepChannelId
73 }
74
75 this.loadingBar.start()
76
77 this.videoImportService.importVideoTorrent(this.magnetUri, videoUpdate).subscribe(
78 res => {
79 this.loadingBar.complete()
80 this.firstStepDone.emit(res.video.name)
81 this.isImportingVideo = false
82 this.hasImportedVideo = true
83
84 this.video = new VideoEdit(Object.assign(res.video, {
85 commentsEnabled: videoUpdate.commentsEnabled,
86 support: null,
87 thumbnailUrl: null,
88 previewUrl: null
89 }))
90 this.hydrateFormFromVideo()
91 },
92
93 err => {
94 this.loadingBar.complete()
95 this.isImportingVideo = false
96 this.notificationsService.error(this.i18n('Error'), err.message)
97 }
98 )
99 }
100
101 updateSecondStep () {
102 if (this.checkForm() === false) {
103 return
104 }
105
106 this.video.patch(this.form.value)
107
108 this.isUpdatingVideo = true
109
110 // Update the video
111 this.updateVideoAndCaptions(this.video)
112 .subscribe(
113 () => {
114 this.isUpdatingVideo = false
115 this.notificationsService.success(this.i18n('Success'), this.i18n('Video to import updated.'))
116
117 this.router.navigate([ '/my-account', 'video-imports' ])
118 },
119
120 err => {
121 this.isUpdatingVideo = false
122 this.notificationsService.error(this.i18n('Error'), err.message)
123 console.error(err)
124 }
125 )
126
127 }
128
129 private hydrateFormFromVideo () {
130 this.form.patchValue(this.video.toFormPatch())
131 }
132}
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html
index 6b431f6f6..9f5fc6d22 100644
--- a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html
+++ b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.html
@@ -1,5 +1,5 @@
1<div *ngIf="!hasImportedVideo" class="upload-video-container"> 1<div *ngIf="!hasImportedVideo" class="upload-video-container">
2 <div class="import-video"> 2 <div class="import-video-url">
3 <div class="icon icon-upload"></div> 3 <div class="icon icon-upload"></div>
4 4
5 <div class="form-group"> 5 <div class="form-group">
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss
index 5e713ab97..7c6deda1d 100644
--- a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss
+++ b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.scss
@@ -7,7 +7,7 @@ $width-size: 190px;
7 @include peertube-select-container($width-size); 7 @include peertube-select-container($width-size);
8} 8}
9 9
10.import-video { 10.import-video-url {
11 display: flex; 11 display: flex;
12 flex-direction: column; 12 flex-direction: column;
13 align-items: center; 13 align-items: center;
diff --git a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts
index dbe69409f..842ede732 100644
--- a/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts
+++ b/client/src/app/videos/+video-edit/video-add-components/video-import-url.component.ts
@@ -74,7 +74,7 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
74 74
75 this.loadingBar.start() 75 this.loadingBar.start()
76 76
77 this.videoImportService.importVideo(this.targetUrl, videoUpdate).subscribe( 77 this.videoImportService.importVideoUrl(this.targetUrl, videoUpdate).subscribe(
78 res => { 78 res => {
79 this.loadingBar.complete() 79 this.loadingBar.complete()
80 this.firstStepDone.emit(res.video.name) 80 this.firstStepDone.emit(res.video.name)
diff --git a/client/src/app/videos/+video-edit/video-add.component.html b/client/src/app/videos/+video-edit/video-add.component.html
index 7a50372e9..340820180 100644
--- a/client/src/app/videos/+video-edit/video-add.component.html
+++ b/client/src/app/videos/+video-edit/video-add.component.html
@@ -10,8 +10,12 @@
10 <my-video-upload #videoUpload (firstStepDone)="onFirstStepDone('upload', $event)"></my-video-upload> 10 <my-video-upload #videoUpload (firstStepDone)="onFirstStepDone('upload', $event)"></my-video-upload>
11 </tab> 11 </tab>
12 12
13 <tab *ngIf="isVideoImportEnabled()" i18n-heading heading="Import with URL"> 13 <tab *ngIf="isVideoImportHttpEnabled()" i18n-heading heading="Import with URL">
14 <my-video-import-url #videoImportUrl (firstStepDone)="onFirstStepDone('import-url', $event)"></my-video-import-url> 14 <my-video-import-url #videoImportUrl (firstStepDone)="onFirstStepDone('import-url', $event)"></my-video-import-url>
15 </tab> 15 </tab>
16
17 <tab *ngIf="isVideoImportTorrentEnabled()" i18n-heading heading="Import with torrent">
18 <my-video-import-torrent #videoImportTorrent (firstStepDone)="onFirstStepDone('import-torrent', $event)"></my-video-import-torrent>
19 </tab>
16 </tabset> 20 </tabset>
17</div> 21</div>
diff --git a/client/src/app/videos/+video-edit/video-add.component.ts b/client/src/app/videos/+video-edit/video-add.component.ts
index e74fa1f15..7d360598d 100644
--- a/client/src/app/videos/+video-edit/video-add.component.ts
+++ b/client/src/app/videos/+video-edit/video-add.component.ts
@@ -3,6 +3,7 @@ import { CanComponentDeactivate } from '@app/shared/guards/can-deactivate-guard.
3import { VideoImportUrlComponent } from '@app/videos/+video-edit/video-add-components/video-import-url.component' 3import { VideoImportUrlComponent } from '@app/videos/+video-edit/video-add-components/video-import-url.component'
4import { VideoUploadComponent } from '@app/videos/+video-edit/video-add-components/video-upload.component' 4import { VideoUploadComponent } from '@app/videos/+video-edit/video-add-components/video-upload.component'
5import { ServerService } from '@app/core' 5import { ServerService } from '@app/core'
6import { VideoImportTorrentComponent } from '@app/videos/+video-edit/video-add-components/video-import-torrent.component'
6 7
7@Component({ 8@Component({
8 selector: 'my-videos-add', 9 selector: 'my-videos-add',
@@ -12,15 +13,16 @@ import { ServerService } from '@app/core'
12export class VideoAddComponent implements CanComponentDeactivate { 13export class VideoAddComponent implements CanComponentDeactivate {
13 @ViewChild('videoUpload') videoUpload: VideoUploadComponent 14 @ViewChild('videoUpload') videoUpload: VideoUploadComponent
14 @ViewChild('videoImportUrl') videoImportUrl: VideoImportUrlComponent 15 @ViewChild('videoImportUrl') videoImportUrl: VideoImportUrlComponent
16 @ViewChild('videoImportTorrent') videoImportTorrent: VideoImportTorrentComponent
15 17
16 secondStepType: 'upload' | 'import-url' 18 secondStepType: 'upload' | 'import-url' | 'import-torrent'
17 videoName: string 19 videoName: string
18 20
19 constructor ( 21 constructor (
20 private serverService: ServerService 22 private serverService: ServerService
21 ) {} 23 ) {}
22 24
23 onFirstStepDone (type: 'upload' | 'import-url', videoName: string) { 25 onFirstStepDone (type: 'upload' | 'import-url' | 'import-torrent', videoName: string) {
24 this.secondStepType = type 26 this.secondStepType = type
25 this.videoName = videoName 27 this.videoName = videoName
26 } 28 }
@@ -28,11 +30,16 @@ export class VideoAddComponent implements CanComponentDeactivate {
28 canDeactivate () { 30 canDeactivate () {
29 if (this.secondStepType === 'upload') return this.videoUpload.canDeactivate() 31 if (this.secondStepType === 'upload') return this.videoUpload.canDeactivate()
30 if (this.secondStepType === 'import-url') return this.videoImportUrl.canDeactivate() 32 if (this.secondStepType === 'import-url') return this.videoImportUrl.canDeactivate()
33 if (this.secondStepType === 'import-torrent') return this.videoImportTorrent.canDeactivate()
31 34
32 return { canDeactivate: true } 35 return { canDeactivate: true }
33 } 36 }
34 37
35 isVideoImportEnabled () { 38 isVideoImportHttpEnabled () {
39 return this.serverService.getConfig().import.videos.http.enabled
40 }
41
42 isVideoImportTorrentEnabled () {
36 return this.serverService.getConfig().import.videos.http.enabled 43 return this.serverService.getConfig().import.videos.http.enabled
37 } 44 }
38} 45}
diff --git a/client/src/app/videos/+video-edit/video-add.module.ts b/client/src/app/videos/+video-edit/video-add.module.ts
index a1324b397..3ecf96459 100644
--- a/client/src/app/videos/+video-edit/video-add.module.ts
+++ b/client/src/app/videos/+video-edit/video-add.module.ts
@@ -7,6 +7,7 @@ import { VideoAddComponent } from './video-add.component'
7import { CanDeactivateGuard } from '../../shared/guards/can-deactivate-guard.service' 7import { CanDeactivateGuard } from '../../shared/guards/can-deactivate-guard.service'
8import { VideoUploadComponent } from '@app/videos/+video-edit/video-add-components/video-upload.component' 8import { VideoUploadComponent } from '@app/videos/+video-edit/video-add-components/video-upload.component'
9import { VideoImportUrlComponent } from '@app/videos/+video-edit/video-add-components/video-import-url.component' 9import { VideoImportUrlComponent } from '@app/videos/+video-edit/video-add-components/video-import-url.component'
10import { VideoImportTorrentComponent } from '@app/videos/+video-edit/video-add-components/video-import-torrent.component'
10 11
11@NgModule({ 12@NgModule({
12 imports: [ 13 imports: [
@@ -18,7 +19,8 @@ import { VideoImportUrlComponent } from '@app/videos/+video-edit/video-add-compo
18 declarations: [ 19 declarations: [
19 VideoAddComponent, 20 VideoAddComponent,
20 VideoUploadComponent, 21 VideoUploadComponent,
21 VideoImportUrlComponent 22 VideoImportUrlComponent,
23 VideoImportTorrentComponent
22 ], 24 ],
23 exports: [ 25 exports: [
24 VideoAddComponent 26 VideoAddComponent