diff options
author | Chocobozzz <me@florianbigard.com> | 2019-07-31 15:57:32 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2019-08-01 09:11:04 +0200 |
commit | bfbd912886eba17b4aa9a40dcef2fddc685d85bf (patch) | |
tree | 85e0f22980210a8ccd0888eb5e1790b152074677 /client/src/app/shared | |
parent | 85394ba22a07bde1dfccebf3f591a5d6dbe9df56 (diff) | |
download | PeerTube-bfbd912886eba17b4aa9a40dcef2fddc685d85bf.tar.gz PeerTube-bfbd912886eba17b4aa9a40dcef2fddc685d85bf.tar.zst PeerTube-bfbd912886eba17b4aa9a40dcef2fddc685d85bf.zip |
Fix broken playlist api
Diffstat (limited to 'client/src/app/shared')
8 files changed, 191 insertions, 104 deletions
diff --git a/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts b/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts index c6cff03a4..08ceb21bc 100644 --- a/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts +++ b/client/src/app/shared/video-playlist/video-add-to-playlist.component.ts | |||
@@ -37,6 +37,8 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
37 | } | 37 | } |
38 | displayOptions = false | 38 | displayOptions = false |
39 | 39 | ||
40 | private playlistElementId: number | ||
41 | |||
40 | constructor ( | 42 | constructor ( |
41 | protected formValidatorService: FormValidatorService, | 43 | protected formValidatorService: FormValidatorService, |
42 | private authService: AuthService, | 44 | private authService: AuthService, |
@@ -96,6 +98,8 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
96 | startTimestamp: existingPlaylist ? existingPlaylist.startTimestamp : undefined, | 98 | startTimestamp: existingPlaylist ? existingPlaylist.startTimestamp : undefined, |
97 | stopTimestamp: existingPlaylist ? existingPlaylist.stopTimestamp : undefined | 99 | stopTimestamp: existingPlaylist ? existingPlaylist.stopTimestamp : undefined |
98 | }) | 100 | }) |
101 | |||
102 | this.playlistElementId = existingPlaylist ? existingPlaylist.playlistElementId : undefined | ||
99 | } | 103 | } |
100 | 104 | ||
101 | this.cd.markForCheck() | 105 | this.cd.markForCheck() |
@@ -177,7 +181,9 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit, | |||
177 | } | 181 | } |
178 | 182 | ||
179 | private removeVideoFromPlaylist (playlist: PlaylistSummary) { | 183 | private removeVideoFromPlaylist (playlist: PlaylistSummary) { |
180 | this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, this.video.id) | 184 | if (!this.playlistElementId) return |
185 | |||
186 | this.videoPlaylistService.removeVideoFromPlaylist(playlist.id, this.playlistElementId) | ||
181 | .subscribe( | 187 | .subscribe( |
182 | () => { | 188 | () => { |
183 | this.notifier.success(this.i18n('Video removed from {{name}}', { name: playlist.displayName })) | 189 | this.notifier.success(this.i18n('Video removed from {{name}}', { name: playlist.displayName })) |
diff --git a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html index ab5a78928..25d4783fb 100644 --- a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html +++ b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.html | |||
@@ -6,66 +6,82 @@ | |||
6 | </div> | 6 | </div> |
7 | 7 | ||
8 | <my-video-thumbnail | 8 | <my-video-thumbnail |
9 | [video]="video" [nsfw]="isVideoBlur(video)" | 9 | *ngIf="playlistElement.video" |
10 | [video]="playlistElement.video" [nsfw]="isVideoBlur(playlistElement.video)" | ||
10 | [routerLink]="buildRouterLink()" [queryParams]="buildRouterQuery()" | 11 | [routerLink]="buildRouterLink()" [queryParams]="buildRouterQuery()" |
11 | ></my-video-thumbnail> | 12 | ></my-video-thumbnail> |
12 | 13 | ||
14 | <div class="fake-thumbnail" *ngIf="!playlistElement.video"></div> | ||
15 | |||
13 | <div class="video-info"> | 16 | <div class="video-info"> |
14 | <a tabindex="-1" class="video-info-name" | 17 | <ng-container *ngIf="playlistElement.video"> |
15 | [routerLink]="buildRouterLink()" [queryParams]="buildRouterQuery()" | 18 | <a tabindex="-1" class="video-info-name" |
16 | [attr.title]="video.name" | 19 | [routerLink]="buildRouterLink()" [queryParams]="buildRouterQuery()" |
17 | >{{ video.name }}</a> | 20 | [attr.title]="playlistElement.video.name" |
21 | >{{ playlistElement.video.name }}</a> | ||
22 | |||
23 | <a *ngIf="accountLink" tabindex="-1" class="video-info-account" [routerLink]="[ '/accounts', playlistElement.video.byAccount ]"> | ||
24 | {{ playlistElement.video.byAccount }} | ||
25 | </a> | ||
26 | <span *ngIf="!accountLink" tabindex="-1" class="video-info-account">{{ playlistElement.video.byAccount }}</span> | ||
18 | 27 | ||
19 | <a *ngIf="accountLink" tabindex="-1" class="video-info-account" [routerLink]="[ '/accounts', video.byAccount ]">{{ video.byAccount }}</a> | 28 | <span tabindex="-1" class="video-info-timestamp">{{ formatTimestamp(playlistElement) }}</span> |
20 | <span *ngIf="!accountLink" tabindex="-1" class="video-info-account">{{ video.byAccount }}</span> | 29 | </ng-container> |
21 | 30 | ||
22 | <span tabindex="-1" class="video-info-timestamp">{{ formatTimestamp(video) }}</span> | 31 | <span *ngIf="!playlistElement.video" class="video-info-name"> |
32 | <ng-container i18n *ngIf="isUnavailable(playlistElement)">Unavailable</ng-container> | ||
33 | <ng-container i18n *ngIf="isPrivate(playlistElement)">Private</ng-container> | ||
34 | <ng-container i18n *ngIf="isDeleted(playlistElement)">Deleted</ng-container> | ||
35 | </span> | ||
23 | </div> | 36 | </div> |
24 | </a> | 37 | </a> |
25 | 38 | ||
26 | <div *ngIf="owned" class="more" ngbDropdown #moreDropdown="ngbDropdown" placement="bottom-right" (openChange)="onDropdownOpenChange()" | 39 | <div *ngIf="owned" class="more" ngbDropdown #moreDropdown="ngbDropdown" placement="bottom-right" |
27 | autoClose="outside"> | 40 | (openChange)="onDropdownOpenChange()" autoClose="outside" |
41 | > | ||
28 | <my-global-icon iconName="more-vertical" ngbDropdownToggle role="button" class="icon-more" (click)="$event.preventDefault()"></my-global-icon> | 42 | <my-global-icon iconName="more-vertical" ngbDropdownToggle role="button" class="icon-more" (click)="$event.preventDefault()"></my-global-icon> |
29 | 43 | ||
30 | <div ngbDropdownMenu> | 44 | <div ngbDropdownMenu> |
31 | <div class="dropdown-item" (click)="toggleDisplayTimestampsOptions($event, video)"> | 45 | <ng-container *ngIf="playlistElement.video"> |
32 | <my-global-icon iconName="edit"></my-global-icon> | 46 | <div class="dropdown-item" (click)="toggleDisplayTimestampsOptions($event, playlistElement)"> |
33 | <ng-container i18n>Edit starts/stops at</ng-container> | 47 | <my-global-icon iconName="edit"></my-global-icon> |
34 | </div> | 48 | <ng-container i18n>Edit starts/stops at</ng-container> |
49 | </div> | ||
35 | 50 | ||
36 | <div class="timestamp-options" *ngIf="displayTimestampOptions"> | 51 | <div class="timestamp-options" *ngIf="displayTimestampOptions"> |
37 | <div> | 52 | <div> |
38 | <my-peertube-checkbox | 53 | <my-peertube-checkbox |
39 | inputName="startAt" [(ngModel)]="timestampOptions.startTimestampEnabled" | 54 | inputName="startAt" [(ngModel)]="timestampOptions.startTimestampEnabled" |
40 | i18n-labelText labelText="Start at" | 55 | i18n-labelText labelText="Start at" |
41 | ></my-peertube-checkbox> | 56 | ></my-peertube-checkbox> |
42 | 57 | ||
43 | <my-timestamp-input | 58 | <my-timestamp-input |
44 | [timestamp]="timestampOptions.startTimestamp" | 59 | [timestamp]="timestampOptions.startTimestamp" |
45 | [maxTimestamp]="video.duration" | 60 | [maxTimestamp]="playlistElement.video.duration" |
46 | [disabled]="!timestampOptions.startTimestampEnabled" | 61 | [disabled]="!timestampOptions.startTimestampEnabled" |
47 | [(ngModel)]="timestampOptions.startTimestamp" | 62 | [(ngModel)]="timestampOptions.startTimestamp" |
48 | ></my-timestamp-input> | 63 | ></my-timestamp-input> |
49 | </div> | 64 | </div> |
50 | 65 | ||
51 | <div> | 66 | <div> |
52 | <my-peertube-checkbox | 67 | <my-peertube-checkbox |
53 | inputName="stopAt" [(ngModel)]="timestampOptions.stopTimestampEnabled" | 68 | inputName="stopAt" [(ngModel)]="timestampOptions.stopTimestampEnabled" |
54 | i18n-labelText labelText="Stop at" | 69 | i18n-labelText labelText="Stop at" |
55 | ></my-peertube-checkbox> | 70 | ></my-peertube-checkbox> |
56 | 71 | ||
57 | <my-timestamp-input | 72 | <my-timestamp-input |
58 | [timestamp]="timestampOptions.stopTimestamp" | 73 | [timestamp]="timestampOptions.stopTimestamp" |
59 | [maxTimestamp]="video.duration" | 74 | [maxTimestamp]="playlistElement.video.duration" |
60 | [disabled]="!timestampOptions.stopTimestampEnabled" | 75 | [disabled]="!timestampOptions.stopTimestampEnabled" |
61 | [(ngModel)]="timestampOptions.stopTimestamp" | 76 | [(ngModel)]="timestampOptions.stopTimestamp" |
62 | ></my-timestamp-input> | 77 | ></my-timestamp-input> |
63 | </div> | 78 | </div> |
64 | 79 | ||
65 | <input type="submit" i18n-value value="Save" (click)="updateTimestamps(video)"> | 80 | <input type="submit" i18n-value value="Save" (click)="updateTimestamps(playlistElement)"> |
66 | </div> | 81 | </div> |
82 | </ng-container> | ||
67 | 83 | ||
68 | <span class="dropdown-item" (click)="removeFromPlaylist(video)"> | 84 | <span class="dropdown-item" (click)="removeFromPlaylist(playlistElement)"> |
69 | <my-global-icon iconName="delete"></my-global-icon> <ng-container i18n>Delete from {{ playlist?.displayName }}</ng-container> | 85 | <my-global-icon iconName="delete"></my-global-icon> <ng-container i18n>Delete from {{ playlist?.displayName }}</ng-container> |
70 | </span> | 86 | </span> |
71 | </div> | 87 | </div> |
diff --git a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss index cb7072d7f..9f4061b02 100644 --- a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss +++ b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.scss | |||
@@ -2,9 +2,21 @@ | |||
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | @import '_miniature'; | 3 | @import '_miniature'; |
4 | 4 | ||
5 | $thumbnail-width: 130px; | ||
6 | $thumbnail-height: 72px; | ||
7 | |||
5 | my-video-thumbnail { | 8 | my-video-thumbnail { |
6 | @include thumbnail-size-component(130px, 72px); | 9 | @include thumbnail-size-component($thumbnail-width, $thumbnail-height); |
10 | } | ||
7 | 11 | ||
12 | .fake-thumbnail { | ||
13 | width: $thumbnail-width; | ||
14 | height: $thumbnail-height; | ||
15 | background-color: #ececec; | ||
16 | } | ||
17 | |||
18 | my-video-thumbnail, | ||
19 | .fake-thumbnail { | ||
8 | display: flex; // Avoids an issue with line-height that adds space below the element | 20 | display: flex; // Avoids an issue with line-height that adds space below the element |
9 | margin-right: 10px; | 21 | margin-right: 10px; |
10 | } | 22 | } |
@@ -31,6 +43,7 @@ my-video-thumbnail { | |||
31 | a { | 43 | a { |
32 | @include disable-default-a-behaviour; | 44 | @include disable-default-a-behaviour; |
33 | 45 | ||
46 | color: var(--mainForegroundColor); | ||
34 | display: flex; | 47 | display: flex; |
35 | min-width: 0; | 48 | min-width: 0; |
36 | align-items: center; | 49 | align-items: center; |
@@ -58,7 +71,6 @@ my-video-thumbnail { | |||
58 | min-width: 0; | 71 | min-width: 0; |
59 | 72 | ||
60 | a { | 73 | a { |
61 | color: var(--mainForegroundColor); | ||
62 | width: auto; | 74 | width: auto; |
63 | 75 | ||
64 | &:hover { | 76 | &:hover { |
@@ -66,20 +78,20 @@ my-video-thumbnail { | |||
66 | } | 78 | } |
67 | } | 79 | } |
68 | 80 | ||
69 | .video-info-name { | ||
70 | font-size: 18px; | ||
71 | font-weight: $font-semibold; | ||
72 | display: inline-block; | ||
73 | |||
74 | @include ellipsis; | ||
75 | } | ||
76 | |||
77 | .video-info-account, .video-info-timestamp { | 81 | .video-info-account, .video-info-timestamp { |
78 | color: $grey-foreground-color; | 82 | color: $grey-foreground-color; |
79 | } | 83 | } |
80 | } | 84 | } |
81 | } | 85 | } |
82 | 86 | ||
87 | .video-info-name { | ||
88 | font-size: 18px; | ||
89 | font-weight: $font-semibold; | ||
90 | display: inline-block; | ||
91 | |||
92 | @include ellipsis; | ||
93 | } | ||
94 | |||
83 | .more { | 95 | .more { |
84 | justify-self: flex-end; | 96 | justify-self: flex-end; |
85 | margin-left: auto; | 97 | margin-left: auto; |
diff --git a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts index 62cf6536d..a8e5a4885 100644 --- a/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts +++ b/client/src/app/shared/video-playlist/video-playlist-element-miniature.component.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core' | 1 | import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core' |
2 | import { Video } from '@app/shared/video/video.model' | 2 | import { Video } from '@app/shared/video/video.model' |
3 | import { VideoPlaylistElementUpdate } from '@shared/models' | 3 | import { VideoPlaylistElementType, VideoPlaylistElementUpdate } from '@shared/models' |
4 | import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core' | 4 | import { AuthService, ConfirmService, Notifier, ServerService } from '@app/core' |
5 | import { ActivatedRoute } from '@angular/router' | 5 | import { ActivatedRoute } from '@angular/router' |
6 | import { I18n } from '@ngx-translate/i18n-polyfill' | 6 | import { I18n } from '@ngx-translate/i18n-polyfill' |
@@ -9,6 +9,7 @@ import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist. | |||
9 | import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' | 9 | import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap' |
10 | import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' | 10 | import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' |
11 | import { secondsToTime } from '../../../assets/player/utils' | 11 | import { secondsToTime } from '../../../assets/player/utils' |
12 | import { VideoPlaylistElement } from '@app/shared/video-playlist/video-playlist-element.model' | ||
12 | 13 | ||
13 | @Component({ | 14 | @Component({ |
14 | selector: 'my-video-playlist-element-miniature', | 15 | selector: 'my-video-playlist-element-miniature', |
@@ -20,14 +21,14 @@ export class VideoPlaylistElementMiniatureComponent { | |||
20 | @ViewChild('moreDropdown', { static: false }) moreDropdown: NgbDropdown | 21 | @ViewChild('moreDropdown', { static: false }) moreDropdown: NgbDropdown |
21 | 22 | ||
22 | @Input() playlist: VideoPlaylist | 23 | @Input() playlist: VideoPlaylist |
23 | @Input() video: Video | 24 | @Input() playlistElement: VideoPlaylistElement |
24 | @Input() owned = false | 25 | @Input() owned = false |
25 | @Input() playing = false | 26 | @Input() playing = false |
26 | @Input() rowLink = false | 27 | @Input() rowLink = false |
27 | @Input() accountLink = true | 28 | @Input() accountLink = true |
28 | @Input() position: number | 29 | @Input() position: number // Keep this property because we're in the OnPush change detection strategy |
29 | 30 | ||
30 | @Output() elementRemoved = new EventEmitter<Video>() | 31 | @Output() elementRemoved = new EventEmitter<VideoPlaylistElement>() |
31 | 32 | ||
32 | displayTimestampOptions = false | 33 | displayTimestampOptions = false |
33 | 34 | ||
@@ -50,6 +51,18 @@ export class VideoPlaylistElementMiniatureComponent { | |||
50 | private cdr: ChangeDetectorRef | 51 | private cdr: ChangeDetectorRef |
51 | ) {} | 52 | ) {} |
52 | 53 | ||
54 | isUnavailable (e: VideoPlaylistElement) { | ||
55 | return e.type === VideoPlaylistElementType.UNAVAILABLE | ||
56 | } | ||
57 | |||
58 | isPrivate (e: VideoPlaylistElement) { | ||
59 | return e.type === VideoPlaylistElementType.PRIVATE | ||
60 | } | ||
61 | |||
62 | isDeleted (e: VideoPlaylistElement) { | ||
63 | return e.type === VideoPlaylistElementType.DELETED | ||
64 | } | ||
65 | |||
53 | buildRouterLink () { | 66 | buildRouterLink () { |
54 | if (!this.playlist) return null | 67 | if (!this.playlist) return null |
55 | 68 | ||
@@ -57,12 +70,12 @@ export class VideoPlaylistElementMiniatureComponent { | |||
57 | } | 70 | } |
58 | 71 | ||
59 | buildRouterQuery () { | 72 | buildRouterQuery () { |
60 | if (!this.video) return {} | 73 | if (!this.playlistElement || !this.playlistElement.video) return {} |
61 | 74 | ||
62 | return { | 75 | return { |
63 | videoId: this.video.uuid, | 76 | videoId: this.playlistElement.video.uuid, |
64 | start: this.video.playlistElement.startTimestamp, | 77 | start: this.playlistElement.startTimestamp, |
65 | stop: this.video.playlistElement.stopTimestamp | 78 | stop: this.playlistElement.stopTimestamp |
66 | } | 79 | } |
67 | } | 80 | } |
68 | 81 | ||
@@ -70,13 +83,13 @@ export class VideoPlaylistElementMiniatureComponent { | |||
70 | return video.isVideoNSFWForUser(this.authService.getUser(), this.serverService.getConfig()) | 83 | return video.isVideoNSFWForUser(this.authService.getUser(), this.serverService.getConfig()) |
71 | } | 84 | } |
72 | 85 | ||
73 | removeFromPlaylist (video: Video) { | 86 | removeFromPlaylist (playlistElement: VideoPlaylistElement) { |
74 | this.videoPlaylistService.removeVideoFromPlaylist(this.playlist.id, video.id) | 87 | this.videoPlaylistService.removeVideoFromPlaylist(this.playlist.id, playlistElement.id) |
75 | .subscribe( | 88 | .subscribe( |
76 | () => { | 89 | () => { |
77 | this.notifier.success(this.i18n('Video removed from {{name}}', { name: this.playlist.displayName })) | 90 | this.notifier.success(this.i18n('Video removed from {{name}}', { name: this.playlist.displayName })) |
78 | 91 | ||
79 | this.elementRemoved.emit(this.video) | 92 | this.elementRemoved.emit(playlistElement) |
80 | }, | 93 | }, |
81 | 94 | ||
82 | err => this.notifier.error(err.message) | 95 | err => this.notifier.error(err.message) |
@@ -85,19 +98,19 @@ export class VideoPlaylistElementMiniatureComponent { | |||
85 | this.moreDropdown.close() | 98 | this.moreDropdown.close() |
86 | } | 99 | } |
87 | 100 | ||
88 | updateTimestamps (video: Video) { | 101 | updateTimestamps (playlistElement: VideoPlaylistElement) { |
89 | const body: VideoPlaylistElementUpdate = {} | 102 | const body: VideoPlaylistElementUpdate = {} |
90 | 103 | ||
91 | body.startTimestamp = this.timestampOptions.startTimestampEnabled ? this.timestampOptions.startTimestamp : null | 104 | body.startTimestamp = this.timestampOptions.startTimestampEnabled ? this.timestampOptions.startTimestamp : null |
92 | body.stopTimestamp = this.timestampOptions.stopTimestampEnabled ? this.timestampOptions.stopTimestamp : null | 105 | body.stopTimestamp = this.timestampOptions.stopTimestampEnabled ? this.timestampOptions.stopTimestamp : null |
93 | 106 | ||
94 | this.videoPlaylistService.updateVideoOfPlaylist(this.playlist.id, video.id, body) | 107 | this.videoPlaylistService.updateVideoOfPlaylist(this.playlist.id, playlistElement.id, body) |
95 | .subscribe( | 108 | .subscribe( |
96 | () => { | 109 | () => { |
97 | this.notifier.success(this.i18n('Timestamps updated')) | 110 | this.notifier.success(this.i18n('Timestamps updated')) |
98 | 111 | ||
99 | video.playlistElement.startTimestamp = body.startTimestamp | 112 | playlistElement.startTimestamp = body.startTimestamp |
100 | video.playlistElement.stopTimestamp = body.stopTimestamp | 113 | playlistElement.stopTimestamp = body.stopTimestamp |
101 | 114 | ||
102 | this.cdr.detectChanges() | 115 | this.cdr.detectChanges() |
103 | }, | 116 | }, |
@@ -108,9 +121,9 @@ export class VideoPlaylistElementMiniatureComponent { | |||
108 | this.moreDropdown.close() | 121 | this.moreDropdown.close() |
109 | } | 122 | } |
110 | 123 | ||
111 | formatTimestamp (video: Video) { | 124 | formatTimestamp (playlistElement: VideoPlaylistElement) { |
112 | const start = video.playlistElement.startTimestamp | 125 | const start = playlistElement.startTimestamp |
113 | const stop = video.playlistElement.stopTimestamp | 126 | const stop = playlistElement.stopTimestamp |
114 | 127 | ||
115 | const startFormatted = secondsToTime(start, true, ':') | 128 | const startFormatted = secondsToTime(start, true, ':') |
116 | const stopFormatted = secondsToTime(stop, true, ':') | 129 | const stopFormatted = secondsToTime(stop, true, ':') |
@@ -127,7 +140,7 @@ export class VideoPlaylistElementMiniatureComponent { | |||
127 | this.displayTimestampOptions = false | 140 | this.displayTimestampOptions = false |
128 | } | 141 | } |
129 | 142 | ||
130 | toggleDisplayTimestampsOptions (event: Event, video: Video) { | 143 | toggleDisplayTimestampsOptions (event: Event, playlistElement: VideoPlaylistElement) { |
131 | event.preventDefault() | 144 | event.preventDefault() |
132 | 145 | ||
133 | this.displayTimestampOptions = !this.displayTimestampOptions | 146 | this.displayTimestampOptions = !this.displayTimestampOptions |
@@ -137,17 +150,17 @@ export class VideoPlaylistElementMiniatureComponent { | |||
137 | startTimestampEnabled: false, | 150 | startTimestampEnabled: false, |
138 | stopTimestampEnabled: false, | 151 | stopTimestampEnabled: false, |
139 | startTimestamp: 0, | 152 | startTimestamp: 0, |
140 | stopTimestamp: video.duration | 153 | stopTimestamp: playlistElement.video.duration |
141 | } | 154 | } |
142 | 155 | ||
143 | if (video.playlistElement.startTimestamp) { | 156 | if (playlistElement.startTimestamp) { |
144 | this.timestampOptions.startTimestampEnabled = true | 157 | this.timestampOptions.startTimestampEnabled = true |
145 | this.timestampOptions.startTimestamp = video.playlistElement.startTimestamp | 158 | this.timestampOptions.startTimestamp = playlistElement.startTimestamp |
146 | } | 159 | } |
147 | 160 | ||
148 | if (video.playlistElement.stopTimestamp) { | 161 | if (playlistElement.stopTimestamp) { |
149 | this.timestampOptions.stopTimestampEnabled = true | 162 | this.timestampOptions.stopTimestampEnabled = true |
150 | this.timestampOptions.stopTimestamp = video.playlistElement.stopTimestamp | 163 | this.timestampOptions.stopTimestamp = playlistElement.stopTimestamp |
151 | } | 164 | } |
152 | } | 165 | } |
153 | 166 | ||
diff --git a/client/src/app/shared/video-playlist/video-playlist-element.model.ts b/client/src/app/shared/video-playlist/video-playlist-element.model.ts new file mode 100644 index 000000000..f1c46d1eb --- /dev/null +++ b/client/src/app/shared/video-playlist/video-playlist-element.model.ts | |||
@@ -0,0 +1,24 @@ | |||
1 | import { VideoPlaylistElement as ServerVideoPlaylistElement, VideoPlaylistElementType } from '../../../../../shared/models/videos' | ||
2 | import { Video } from '@app/shared/video/video.model' | ||
3 | |||
4 | export class VideoPlaylistElement implements ServerVideoPlaylistElement { | ||
5 | id: number | ||
6 | position: number | ||
7 | startTimestamp: number | ||
8 | stopTimestamp: number | ||
9 | |||
10 | type: VideoPlaylistElementType | ||
11 | |||
12 | video?: Video | ||
13 | |||
14 | constructor (hash: ServerVideoPlaylistElement, translations: {}) { | ||
15 | this.id = hash.id | ||
16 | this.position = hash.position | ||
17 | this.startTimestamp = hash.startTimestamp | ||
18 | this.stopTimestamp = hash.stopTimestamp | ||
19 | |||
20 | this.type = hash.type | ||
21 | |||
22 | if (hash.video) this.video = new Video(hash.video, translations) | ||
23 | } | ||
24 | } | ||
diff --git a/client/src/app/shared/video-playlist/video-playlist.service.ts b/client/src/app/shared/video-playlist/video-playlist.service.ts index da7437507..b93a19356 100644 --- a/client/src/app/shared/video-playlist/video-playlist.service.ts +++ b/client/src/app/shared/video-playlist/video-playlist.service.ts | |||
@@ -18,6 +18,9 @@ import { Account } from '@app/shared/account/account.model' | |||
18 | import { RestService } from '@app/shared/rest' | 18 | import { RestService } from '@app/shared/rest' |
19 | import { VideoExistInPlaylist } from '@shared/models/videos/playlist/video-exist-in-playlist.model' | 19 | import { VideoExistInPlaylist } from '@shared/models/videos/playlist/video-exist-in-playlist.model' |
20 | import { VideoPlaylistReorder } from '@shared/models/videos/playlist/video-playlist-reorder.model' | 20 | import { VideoPlaylistReorder } from '@shared/models/videos/playlist/video-playlist-reorder.model' |
21 | import { ComponentPagination } from '@app/shared/rest/component-pagination.model' | ||
22 | import { VideoPlaylistElement as ServerVideoPlaylistElement } from '@shared/models/videos/playlist/video-playlist-element.model' | ||
23 | import { VideoPlaylistElement } from '@app/shared/video-playlist/video-playlist-element.model' | ||
21 | 24 | ||
22 | @Injectable() | 25 | @Injectable() |
23 | export class VideoPlaylistService { | 26 | export class VideoPlaylistService { |
@@ -110,16 +113,16 @@ export class VideoPlaylistService { | |||
110 | ) | 113 | ) |
111 | } | 114 | } |
112 | 115 | ||
113 | updateVideoOfPlaylist (playlistId: number, videoId: number, body: VideoPlaylistElementUpdate) { | 116 | updateVideoOfPlaylist (playlistId: number, playlistElementId: number, body: VideoPlaylistElementUpdate) { |
114 | return this.authHttp.put(VideoPlaylistService.BASE_VIDEO_PLAYLIST_URL + playlistId + '/videos/' + videoId, body) | 117 | return this.authHttp.put(VideoPlaylistService.BASE_VIDEO_PLAYLIST_URL + playlistId + '/videos/' + playlistElementId, body) |
115 | .pipe( | 118 | .pipe( |
116 | map(this.restExtractor.extractDataBool), | 119 | map(this.restExtractor.extractDataBool), |
117 | catchError(err => this.restExtractor.handleError(err)) | 120 | catchError(err => this.restExtractor.handleError(err)) |
118 | ) | 121 | ) |
119 | } | 122 | } |
120 | 123 | ||
121 | removeVideoFromPlaylist (playlistId: number, videoId: number) { | 124 | removeVideoFromPlaylist (playlistId: number, playlistElementId: number) { |
122 | return this.authHttp.delete(VideoPlaylistService.BASE_VIDEO_PLAYLIST_URL + playlistId + '/videos/' + videoId) | 125 | return this.authHttp.delete(VideoPlaylistService.BASE_VIDEO_PLAYLIST_URL + playlistId + '/videos/' + playlistElementId) |
123 | .pipe( | 126 | .pipe( |
124 | map(this.restExtractor.extractDataBool), | 127 | map(this.restExtractor.extractDataBool), |
125 | catchError(err => this.restExtractor.handleError(err)) | 128 | catchError(err => this.restExtractor.handleError(err)) |
@@ -139,6 +142,24 @@ export class VideoPlaylistService { | |||
139 | ) | 142 | ) |
140 | } | 143 | } |
141 | 144 | ||
145 | getPlaylistVideos ( | ||
146 | videoPlaylistId: number | string, | ||
147 | componentPagination: ComponentPagination | ||
148 | ): Observable<ResultList<VideoPlaylistElement>> { | ||
149 | const path = VideoPlaylistService.BASE_VIDEO_PLAYLIST_URL + videoPlaylistId + '/videos' | ||
150 | const pagination = this.restService.componentPaginationToRestPagination(componentPagination) | ||
151 | |||
152 | let params = new HttpParams() | ||
153 | params = this.restService.addRestGetParams(params, pagination) | ||
154 | |||
155 | return this.authHttp | ||
156 | .get<ResultList<ServerVideoPlaylistElement>>(path, { params }) | ||
157 | .pipe( | ||
158 | switchMap(res => this.extractVideoPlaylistElements(res)), | ||
159 | catchError(err => this.restExtractor.handleError(err)) | ||
160 | ) | ||
161 | } | ||
162 | |||
142 | doesVideoExistInPlaylist (videoId: number) { | 163 | doesVideoExistInPlaylist (videoId: number) { |
143 | this.videoExistsInPlaylistSubject.next(videoId) | 164 | this.videoExistsInPlaylistSubject.next(videoId) |
144 | 165 | ||
@@ -167,6 +188,23 @@ export class VideoPlaylistService { | |||
167 | .pipe(map(translations => new VideoPlaylist(playlist, translations))) | 188 | .pipe(map(translations => new VideoPlaylist(playlist, translations))) |
168 | } | 189 | } |
169 | 190 | ||
191 | extractVideoPlaylistElements (result: ResultList<ServerVideoPlaylistElement>) { | ||
192 | return this.serverService.localeObservable | ||
193 | .pipe( | ||
194 | map(translations => { | ||
195 | const elementsJson = result.data | ||
196 | const total = result.total | ||
197 | const elements: VideoPlaylistElement[] = [] | ||
198 | |||
199 | for (const elementJson of elementsJson) { | ||
200 | elements.push(new VideoPlaylistElement(elementJson, translations)) | ||
201 | } | ||
202 | |||
203 | return { total, data: elements } | ||
204 | }) | ||
205 | ) | ||
206 | } | ||
207 | |||
170 | private doVideosExistInPlaylist (videoIds: number[]): Observable<VideoExistInPlaylist> { | 208 | private doVideosExistInPlaylist (videoIds: number[]): Observable<VideoExistInPlaylist> { |
171 | const url = VideoPlaylistService.MY_VIDEO_PLAYLIST_URL + 'videos-exist' | 209 | const url = VideoPlaylistService.MY_VIDEO_PLAYLIST_URL + 'videos-exist' |
172 | let params = new HttpParams() | 210 | let params = new HttpParams() |
diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts index 6f9de9241..fb98d5382 100644 --- a/client/src/app/shared/video/video.model.ts +++ b/client/src/app/shared/video/video.model.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { User } from '../' | 1 | import { User } from '../' |
2 | import { PlaylistElement, UserRight, Video as VideoServerModel, VideoPrivacy, VideoState } from '../../../../../shared' | 2 | import { UserRight, Video as VideoServerModel, VideoPrivacy, VideoState } from '../../../../../shared' |
3 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' | 3 | import { Avatar } from '../../../../../shared/models/avatars/avatar.model' |
4 | import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model' | 4 | import { VideoConstant } from '../../../../../shared/models/videos/video-constant.model' |
5 | import { durationToString, getAbsoluteAPIUrl } from '../misc/utils' | 5 | import { durationToString, getAbsoluteAPIUrl } from '../misc/utils' |
@@ -48,8 +48,6 @@ export class Video implements VideoServerModel { | |||
48 | blacklisted?: boolean | 48 | blacklisted?: boolean |
49 | blacklistedReason?: string | 49 | blacklistedReason?: string |
50 | 50 | ||
51 | playlistElement?: PlaylistElement | ||
52 | |||
53 | account: { | 51 | account: { |
54 | id: number | 52 | id: number |
55 | name: string | 53 | name: string |
@@ -126,8 +124,6 @@ export class Video implements VideoServerModel { | |||
126 | this.blacklistedReason = hash.blacklistedReason | 124 | this.blacklistedReason = hash.blacklistedReason |
127 | 125 | ||
128 | this.userHistory = hash.userHistory | 126 | this.userHistory = hash.userHistory |
129 | |||
130 | this.playlistElement = hash.playlistElement | ||
131 | } | 127 | } |
132 | 128 | ||
133 | isVideoNSFWForUser (user: User, serverConfig: ServerConfig) { | 129 | isVideoNSFWForUser (user: User, serverConfig: ServerConfig) { |
diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index 114b014ad..45366e3e3 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts | |||
@@ -31,7 +31,6 @@ import { ServerService } from '@app/core' | |||
31 | import { UserSubscriptionService } from '@app/shared/user-subscription/user-subscription.service' | 31 | import { UserSubscriptionService } from '@app/shared/user-subscription/user-subscription.service' |
32 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' | 32 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' |
33 | import { I18n } from '@ngx-translate/i18n-polyfill' | 33 | import { I18n } from '@ngx-translate/i18n-polyfill' |
34 | import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service' | ||
35 | 34 | ||
36 | export interface VideosProvider { | 35 | export interface VideosProvider { |
37 | getVideos (parameters: { | 36 | getVideos (parameters: { |
@@ -172,23 +171,6 @@ export class VideoService implements VideosProvider { | |||
172 | ) | 171 | ) |
173 | } | 172 | } |
174 | 173 | ||
175 | getPlaylistVideos ( | ||
176 | videoPlaylistId: number | string, | ||
177 | videoPagination: ComponentPagination | ||
178 | ): Observable<ResultList<Video>> { | ||
179 | const pagination = this.restService.componentPaginationToRestPagination(videoPagination) | ||
180 | |||
181 | let params = new HttpParams() | ||
182 | params = this.restService.addRestGetParams(params, pagination) | ||
183 | |||
184 | return this.authHttp | ||
185 | .get<ResultList<Video>>(VideoPlaylistService.BASE_VIDEO_PLAYLIST_URL + videoPlaylistId + '/videos', { params }) | ||
186 | .pipe( | ||
187 | switchMap(res => this.extractVideos(res)), | ||
188 | catchError(err => this.restExtractor.handleError(err)) | ||
189 | ) | ||
190 | } | ||
191 | |||
192 | getUserSubscriptionVideos (parameters: { | 174 | getUserSubscriptionVideos (parameters: { |
193 | videoPagination: ComponentPagination, | 175 | videoPagination: ComponentPagination, |
194 | sort: VideoSortField | 176 | sort: VideoSortField |