From 202f6b6c9dcc9b0aec4b0c1b15e455c22a7952a7 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 1 Dec 2017 18:56:26 +0100 Subject: Begin videos of an account --- client/src/app/account/account-routing.module.ts | 19 +-- .../account-videos/account-videos.component.html | 9 ++ .../account-videos/account-videos.component.scss | 0 .../account-videos/account-videos.component.ts | 35 ++++ client/src/app/account/account.module.ts | 4 +- client/src/app/shared/shared.module.ts | 12 +- .../src/app/shared/video/abstract-video-list.html | 19 +++ .../src/app/shared/video/abstract-video-list.scss | 0 client/src/app/shared/video/abstract-video-list.ts | 121 ++++++++++++++ client/src/app/shared/video/sort-field.type.ts | 5 + client/src/app/shared/video/video-details.model.ts | 84 ++++++++++ client/src/app/shared/video/video-edit.model.ts | 50 ++++++ .../src/app/shared/video/video-pagination.model.ts | 5 + .../shared/video/video-thumbnail.component.html | 10 ++ .../shared/video/video-thumbnail.component.scss | 28 ++++ .../app/shared/video/video-thumbnail.component.ts | 12 ++ client/src/app/shared/video/video.model.ts | 90 +++++++++++ client/src/app/shared/video/video.service.ts | 170 ++++++++++++++++++++ .../videos/+video-edit/shared/video-edit.module.ts | 3 +- .../app/videos/+video-edit/video-add.component.ts | 22 ++- .../videos/+video-edit/video-update.component.ts | 19 ++- .../+video-watch/video-download.component.ts | 4 +- .../videos/+video-watch/video-report.component.ts | 8 +- .../videos/+video-watch/video-share.component.ts | 4 +- .../videos/+video-watch/video-watch.component.ts | 4 +- .../app/videos/+video-watch/video-watch.module.ts | 5 +- client/src/app/videos/shared/index.ts | 6 - client/src/app/videos/shared/sort-field.type.ts | 5 - .../src/app/videos/shared/video-details.model.ts | 84 ---------- client/src/app/videos/shared/video-edit.model.ts | 50 ------ .../app/videos/shared/video-pagination.model.ts | 5 - client/src/app/videos/shared/video.model.ts | 90 ----------- client/src/app/videos/shared/video.service.ts | 176 --------------------- .../video-list/shared/abstract-video-list.html | 19 --- .../video-list/shared/abstract-video-list.scss | 0 .../video-list/shared/abstract-video-list.ts | 121 -------------- client/src/app/videos/video-list/shared/index.ts | 1 - .../shared/video-miniature.component.html | 11 +- .../shared/video-miniature.component.scss | 29 ---- .../video-list/shared/video-miniature.component.ts | 4 +- .../video-list/video-recently-added.component.ts | 8 +- .../videos/video-list/video-trending.component.ts | 8 +- client/src/app/videos/videos.module.ts | 9 +- 43 files changed, 704 insertions(+), 664 deletions(-) create mode 100644 client/src/app/account/account-videos/account-videos.component.html create mode 100644 client/src/app/account/account-videos/account-videos.component.scss create mode 100644 client/src/app/account/account-videos/account-videos.component.ts create mode 100644 client/src/app/shared/video/abstract-video-list.html create mode 100644 client/src/app/shared/video/abstract-video-list.scss create mode 100644 client/src/app/shared/video/abstract-video-list.ts create mode 100644 client/src/app/shared/video/sort-field.type.ts create mode 100644 client/src/app/shared/video/video-details.model.ts create mode 100644 client/src/app/shared/video/video-edit.model.ts create mode 100644 client/src/app/shared/video/video-pagination.model.ts create mode 100644 client/src/app/shared/video/video-thumbnail.component.html create mode 100644 client/src/app/shared/video/video-thumbnail.component.scss create mode 100644 client/src/app/shared/video/video-thumbnail.component.ts create mode 100644 client/src/app/shared/video/video.model.ts create mode 100644 client/src/app/shared/video/video.service.ts delete mode 100644 client/src/app/videos/shared/sort-field.type.ts delete mode 100644 client/src/app/videos/shared/video-details.model.ts delete mode 100644 client/src/app/videos/shared/video-edit.model.ts delete mode 100644 client/src/app/videos/shared/video-pagination.model.ts delete mode 100644 client/src/app/videos/shared/video.model.ts delete mode 100644 client/src/app/videos/shared/video.service.ts delete mode 100644 client/src/app/videos/video-list/shared/abstract-video-list.html delete mode 100644 client/src/app/videos/video-list/shared/abstract-video-list.scss delete mode 100644 client/src/app/videos/video-list/shared/abstract-video-list.ts (limited to 'client/src/app') diff --git a/client/src/app/account/account-routing.module.ts b/client/src/app/account/account-routing.module.ts index 2e9de1cfb..070b9b5c5 100644 --- a/client/src/app/account/account-routing.module.ts +++ b/client/src/app/account/account-routing.module.ts @@ -6,6 +6,7 @@ import { MetaGuard } from '@ngx-meta/core' import { LoginGuard } from '../core' import { AccountComponent } from './account.component' import { AccountSettingsComponent } from './account-settings/account-settings.component' +import { AccountVideosComponent } from './account-videos/account-videos.component' const accountRoutes: Routes = [ { @@ -22,15 +23,15 @@ const accountRoutes: Routes = [ } } }, - // { - // path: 'videos', - // component: AccountVideosComponent, - // data: { - // meta: { - // title: 'Account videos' - // } - // } - // } + { + path: 'videos', + component: AccountVideosComponent, + data: { + meta: { + title: 'Account videos' + } + } + } ] } ] diff --git a/client/src/app/account/account-videos/account-videos.component.html b/client/src/app/account/account-videos/account-videos.component.html new file mode 100644 index 000000000..6c8ac4508 --- /dev/null +++ b/client/src/app/account/account-videos/account-videos.component.html @@ -0,0 +1,9 @@ +
+
+ +
+
diff --git a/client/src/app/account/account-videos/account-videos.component.scss b/client/src/app/account/account-videos/account-videos.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/app/account/account-videos/account-videos.component.ts b/client/src/app/account/account-videos/account-videos.component.ts new file mode 100644 index 000000000..ff945825d --- /dev/null +++ b/client/src/app/account/account-videos/account-videos.component.ts @@ -0,0 +1,35 @@ +import { Component, OnDestroy, OnInit } from '@angular/core' +import { AbstractVideoList } from '../../shared/video/abstract-video-list' +import { ActivatedRoute } from '@angular/router' +import { Router } from '@angular/router' +import { NotificationsService } from 'angular2-notifications' +import { VideoService } from '../../shared/video/video.service' + +@Component({ + selector: 'my-account-videos', + templateUrl: './account-videos.component.html', + styleUrls: [ './account-videos.component.scss' ] +}) +export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { + titlePage = 'My videos' + currentRoute = '/account/videos' + + constructor (protected router: Router, + protected route: ActivatedRoute, + protected notificationsService: NotificationsService, + private videoService: VideoService) { + super() + } + + ngOnInit () { + super.ngOnInit() + } + + ngOnDestroy () { + super.ngOnDestroy() + } + + getVideosObservable () { + return this.videoService.getMyVideos(this.pagination, this.sort) + } +} diff --git a/client/src/app/account/account.module.ts b/client/src/app/account/account.module.ts index ff444ddeb..020199e23 100644 --- a/client/src/app/account/account.module.ts +++ b/client/src/app/account/account.module.ts @@ -6,6 +6,7 @@ import { AccountDetailsComponent } from './account-settings/account-details/acco import { AccountSettingsComponent } from './account-settings/account-settings.component' import { AccountComponent } from './account.component' import { AccountService } from './account.service' +import { AccountVideosComponent } from './account-videos/account-videos.component' @NgModule({ imports: [ @@ -17,7 +18,8 @@ import { AccountService } from './account.service' AccountComponent, AccountSettingsComponent, AccountChangePasswordComponent, - AccountDetailsComponent + AccountDetailsComponent, + AccountVideosComponent ], exports: [ diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 7618748e9..e76f7636a 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -20,6 +20,9 @@ import { SearchComponent, SearchService } from './search' import { UserService } from './users' import { VideoAbuseService } from './video-abuse' import { VideoBlacklistService } from './video-blacklist' +import { VideoThumbnailComponent } from './video/video-thumbnail.component' +import { VideoService } from './video/video.service' +import { InfiniteScrollModule } from 'ngx-infinite-scroll' @NgModule({ imports: [ @@ -34,7 +37,8 @@ import { VideoBlacklistService } from './video-blacklist' ProgressbarModule.forRoot(), DataTableModule, - PrimeSharedModule + PrimeSharedModule, + InfiniteScrollModule ], declarations: [ @@ -42,6 +46,7 @@ import { VideoBlacklistService } from './video-blacklist' KeysPipe, SearchComponent, LoaderComponent, + VideoThumbnailComponent, NumberFormatterPipe, FromNowPipe ], @@ -58,11 +63,13 @@ import { VideoBlacklistService } from './video-blacklist' ProgressbarModule, DataTableModule, PrimeSharedModule, + InfiniteScrollModule, BytesPipe, KeysPipe, SearchComponent, LoaderComponent, + VideoThumbnailComponent, NumberFormatterPipe, FromNowPipe @@ -75,7 +82,8 @@ import { VideoBlacklistService } from './video-blacklist' SearchService, VideoAbuseService, VideoBlacklistService, - UserService + UserService, + VideoService ] }) export class SharedModule { } diff --git a/client/src/app/shared/video/abstract-video-list.html b/client/src/app/shared/video/abstract-video-list.html new file mode 100644 index 000000000..bd4f6b1f8 --- /dev/null +++ b/client/src/app/shared/video/abstract-video-list.html @@ -0,0 +1,19 @@ +
+
+ {{ titlePage }} +
+ +
+ + +
+
diff --git a/client/src/app/shared/video/abstract-video-list.scss b/client/src/app/shared/video/abstract-video-list.scss new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts new file mode 100644 index 000000000..cf717cf4c --- /dev/null +++ b/client/src/app/shared/video/abstract-video-list.ts @@ -0,0 +1,121 @@ +import { OnDestroy, OnInit } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { NotificationsService } from 'angular2-notifications' +import { Observable } from 'rxjs/Observable' +import { Subscription } from 'rxjs/Subscription' +import { SortField } from './sort-field.type' +import { VideoPagination } from './video-pagination.model' +import { Video } from './video.model' + +export abstract class AbstractVideoList implements OnInit, OnDestroy { + pagination: VideoPagination = { + currentPage: 1, + itemsPerPage: 25, + totalItems: null + } + sort: SortField = '-createdAt' + videos: Video[] = [] + + protected notificationsService: NotificationsService + protected router: Router + protected route: ActivatedRoute + protected subActivatedRoute: Subscription + + protected abstract currentRoute: string + + abstract titlePage: string + private loadedPages: { [ id: number ]: boolean } = {} + + abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}> + + ngOnInit () { + // Subscribe to route changes + const routeParams = this.route.snapshot.params + this.loadRouteParams(routeParams) + this.loadMoreVideos('after') + } + + ngOnDestroy () { + if (this.subActivatedRoute) { + this.subActivatedRoute.unsubscribe() + } + } + + onNearOfTop () { + if (this.pagination.currentPage > 1) { + this.previousPage() + } + } + + onNearOfBottom () { + if (this.hasMoreVideos()) { + this.nextPage() + } + } + + loadMoreVideos (where: 'before' | 'after') { + if (this.loadedPages[this.pagination.currentPage] === true) return + + const observable = this.getVideosObservable() + + observable.subscribe( + ({ videos, totalVideos }) => { + this.loadedPages[this.pagination.currentPage] = true + this.pagination.totalItems = totalVideos + + if (where === 'before') { + this.videos = videos.concat(this.videos) + } else { + this.videos = this.videos.concat(videos) + } + }, + error => this.notificationsService.error('Error', error.text) + ) + } + + protected hasMoreVideos () { + if (!this.pagination.totalItems) return true + + const maxPage = this.pagination.totalItems / this.pagination.itemsPerPage + return maxPage > this.pagination.currentPage + } + + protected previousPage () { + this.pagination.currentPage-- + + this.setNewRouteParams() + this.loadMoreVideos('before') + } + + protected nextPage () { + this.pagination.currentPage++ + + this.setNewRouteParams() + this.loadMoreVideos('after') + } + + protected buildRouteParams () { + // There is always a sort and a current page + const params = { + sort: this.sort, + page: this.pagination.currentPage + } + + return params + } + + protected loadRouteParams (routeParams: { [ key: string ]: any }) { + this.sort = routeParams['sort'] as SortField || '-createdAt' + + if (routeParams['page'] !== undefined) { + this.pagination.currentPage = parseInt(routeParams['page'], 10) + } else { + this.pagination.currentPage = 1 + } + } + + protected setNewRouteParams () { + const routeParams = this.buildRouteParams() + this.router.navigate([ this.currentRoute, routeParams ]) + } +} diff --git a/client/src/app/shared/video/sort-field.type.ts b/client/src/app/shared/video/sort-field.type.ts new file mode 100644 index 000000000..776f360f8 --- /dev/null +++ b/client/src/app/shared/video/sort-field.type.ts @@ -0,0 +1,5 @@ +export type SortField = 'name' | '-name' + | 'duration' | '-duration' + | 'createdAt' | '-createdAt' + | 'views' | '-views' + | 'likes' | '-likes' diff --git a/client/src/app/shared/video/video-details.model.ts b/client/src/app/shared/video/video-details.model.ts new file mode 100644 index 000000000..93c380b73 --- /dev/null +++ b/client/src/app/shared/video/video-details.model.ts @@ -0,0 +1,84 @@ +import { Video } from '../../shared/video/video.model' +import { AuthUser } from '../../core' +import { + VideoDetails as VideoDetailsServerModel, + VideoFile, + VideoChannel, + VideoResolution, + UserRight, + VideoPrivacy +} from '../../../../../shared' + +export class VideoDetails extends Video implements VideoDetailsServerModel { + account: string + by: string + createdAt: Date + updatedAt: Date + categoryLabel: string + category: number + licenceLabel: string + licence: number + languageLabel: string + language: number + description: string + duration: number + durationLabel: string + id: number + uuid: string + isLocal: boolean + name: string + serverHost: string + tags: string[] + thumbnailPath: string + thumbnailUrl: string + previewPath: string + previewUrl: string + embedPath: string + embedUrl: string + views: number + likes: number + dislikes: number + nsfw: boolean + descriptionPath: string + files: VideoFile[] + channel: VideoChannel + privacy: VideoPrivacy + privacyLabel: string + + constructor (hash: VideoDetailsServerModel) { + super(hash) + + this.privacy = hash.privacy + this.privacyLabel = hash.privacyLabel + this.descriptionPath = hash.descriptionPath + this.files = hash.files + this.channel = hash.channel + } + + getAppropriateMagnetUri (actualDownloadSpeed = 0) { + if (this.files === undefined || this.files.length === 0) return '' + if (this.files.length === 1) return this.files[0].magnetUri + + // Find first video that is good for our download speed (remember they are sorted) + let betterResolutionFile = this.files.find(f => actualDownloadSpeed > (f.size / this.duration)) + + // If the download speed is too bad, return the lowest resolution we have + if (betterResolutionFile === undefined) { + betterResolutionFile = this.files.find(f => f.resolution === VideoResolution.H_240P) + } + + return betterResolutionFile.magnetUri + } + + isRemovableBy (user: AuthUser) { + return user && this.isLocal === true && (this.account === user.username || user.hasRight(UserRight.REMOVE_ANY_VIDEO)) + } + + isBlackistableBy (user: AuthUser) { + return user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true && this.isLocal === false + } + + isUpdatableBy (user: AuthUser) { + return user && this.isLocal === true && user.username === this.account + } +} diff --git a/client/src/app/shared/video/video-edit.model.ts b/client/src/app/shared/video/video-edit.model.ts new file mode 100644 index 000000000..88d23a59f --- /dev/null +++ b/client/src/app/shared/video/video-edit.model.ts @@ -0,0 +1,50 @@ +import { VideoDetails } from './video-details.model' +import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' + +export class VideoEdit { + category: number + licence: number + language: number + description: string + name: string + tags: string[] + nsfw: boolean + channel: number + privacy: VideoPrivacy + uuid?: string + id?: number + + constructor (videoDetails: VideoDetails) { + this.id = videoDetails.id + this.uuid = videoDetails.uuid + this.category = videoDetails.category + this.licence = videoDetails.licence + this.language = videoDetails.language + this.description = videoDetails.description + this.name = videoDetails.name + this.tags = videoDetails.tags + this.nsfw = videoDetails.nsfw + this.channel = videoDetails.channel.id + this.privacy = videoDetails.privacy + } + + patch (values: Object) { + Object.keys(values).forEach((key) => { + this[key] = values[key] + }) + } + + toJSON () { + return { + category: this.category, + licence: this.licence, + language: this.language, + description: this.description, + name: this.name, + tags: this.tags, + nsfw: this.nsfw, + channel: this.channel, + privacy: this.privacy + } + } +} diff --git a/client/src/app/shared/video/video-pagination.model.ts b/client/src/app/shared/video/video-pagination.model.ts new file mode 100644 index 000000000..9e71769cb --- /dev/null +++ b/client/src/app/shared/video/video-pagination.model.ts @@ -0,0 +1,5 @@ +export interface VideoPagination { + currentPage: number + itemsPerPage: number + totalItems: number +} diff --git a/client/src/app/shared/video/video-thumbnail.component.html b/client/src/app/shared/video/video-thumbnail.component.html new file mode 100644 index 000000000..5c698e8f6 --- /dev/null +++ b/client/src/app/shared/video/video-thumbnail.component.html @@ -0,0 +1,10 @@ + +video thumbnail + +
+ {{ video.durationLabel }} +
+
diff --git a/client/src/app/shared/video/video-thumbnail.component.scss b/client/src/app/shared/video/video-thumbnail.component.scss new file mode 100644 index 000000000..ab4f9bcb1 --- /dev/null +++ b/client/src/app/shared/video/video-thumbnail.component.scss @@ -0,0 +1,28 @@ +.video-thumbnail { + display: inline-block; + position: relative; + border-radius: 4px; + overflow: hidden; + + &:hover { + text-decoration: none !important; + } + + img.blur-filter { + filter: blur(5px); + transform : scale(1.03); + } + + .video-thumbnail-overlay { + position: absolute; + right: 5px; + bottom: 5px; + display: inline-block; + background-color: rgba(0, 0, 0, 0.7); + color: #fff; + font-size: 12px; + font-weight: $font-bold; + border-radius: 3px; + padding: 0 5px; + } +} diff --git a/client/src/app/shared/video/video-thumbnail.component.ts b/client/src/app/shared/video/video-thumbnail.component.ts new file mode 100644 index 000000000..e543e9903 --- /dev/null +++ b/client/src/app/shared/video/video-thumbnail.component.ts @@ -0,0 +1,12 @@ +import { Component, Input } from '@angular/core' +import { Video } from './video.model' + +@Component({ + selector: 'my-video-thumbnail', + styleUrls: [ './video-thumbnail.component.scss' ], + templateUrl: './video-thumbnail.component.html' +}) +export class VideoThumbnailComponent { + @Input() video: Video + @Input() nsfw = false +} diff --git a/client/src/app/shared/video/video.model.ts b/client/src/app/shared/video/video.model.ts new file mode 100644 index 000000000..6929c8755 --- /dev/null +++ b/client/src/app/shared/video/video.model.ts @@ -0,0 +1,90 @@ +import { Video as VideoServerModel } from '../../../../../shared' +import { User } from '../' + +export class Video implements VideoServerModel { + account: string + by: string + createdAt: Date + updatedAt: Date + categoryLabel: string + category: number + licenceLabel: string + licence: number + languageLabel: string + language: number + description: string + duration: number + durationLabel: string + id: number + uuid: string + isLocal: boolean + name: string + serverHost: string + tags: string[] + thumbnailPath: string + thumbnailUrl: string + previewPath: string + previewUrl: string + embedPath: string + embedUrl: string + views: number + likes: number + dislikes: number + nsfw: boolean + + private static createByString (account: string, serverHost: string) { + return account + '@' + serverHost + } + + private static createDurationString (duration: number) { + const minutes = Math.floor(duration / 60) + const seconds = duration % 60 + const minutesPadding = minutes >= 10 ? '' : '0' + const secondsPadding = seconds >= 10 ? '' : '0' + + return minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString() + } + + constructor (hash: VideoServerModel) { + let absoluteAPIUrl = API_URL + if (!absoluteAPIUrl) { + // The API is on the same domain + absoluteAPIUrl = window.location.origin + } + + this.account = hash.account + this.createdAt = new Date(hash.createdAt.toString()) + this.categoryLabel = hash.categoryLabel + this.category = hash.category + this.licenceLabel = hash.licenceLabel + this.licence = hash.licence + this.languageLabel = hash.languageLabel + this.language = hash.language + this.description = hash.description + this.duration = hash.duration + this.durationLabel = Video.createDurationString(hash.duration) + this.id = hash.id + this.uuid = hash.uuid + this.isLocal = hash.isLocal + this.name = hash.name + this.serverHost = hash.serverHost + this.tags = hash.tags + this.thumbnailPath = hash.thumbnailPath + this.thumbnailUrl = absoluteAPIUrl + hash.thumbnailPath + this.previewPath = hash.previewPath + this.previewUrl = absoluteAPIUrl + hash.previewPath + this.embedPath = hash.embedPath + this.embedUrl = absoluteAPIUrl + hash.embedPath + this.views = hash.views + this.likes = hash.likes + this.dislikes = hash.dislikes + this.nsfw = hash.nsfw + + this.by = Video.createByString(hash.account, hash.serverHost) + } + + 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)) + } +} diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts new file mode 100644 index 000000000..b2a26417c --- /dev/null +++ b/client/src/app/shared/video/video.service.ts @@ -0,0 +1,170 @@ +import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' +import { Injectable } from '@angular/core' +import 'rxjs/add/operator/catch' +import 'rxjs/add/operator/map' +import { Observable } from 'rxjs/Observable' +import { Video as VideoServerModel, VideoDetails as VideoDetailsServerModel } from '../../../../../shared' +import { ResultList } from '../../../../../shared/models/result-list.model' +import { UserVideoRateUpdate } from '../../../../../shared/models/videos/user-video-rate-update.model' +import { UserVideoRate } from '../../../../../shared/models/videos/user-video-rate.model' +import { VideoRateType } from '../../../../../shared/models/videos/video-rate.type' +import { VideoUpdate } from '../../../../../shared/models/videos/video-update.model' +import { RestExtractor } from '../rest/rest-extractor.service' +import { RestService } from '../rest/rest.service' +import { Search } from '../search/search.model' +import { UserService } from '../users/user.service' +import { SortField } from './sort-field.type' +import { VideoDetails } from './video-details.model' +import { VideoEdit } from './video-edit.model' +import { VideoPagination } from './video-pagination.model' +import { Video } from './video.model' + +@Injectable() +export class VideoService { + private static BASE_VIDEO_URL = API_URL + '/api/v1/videos/' + + constructor ( + private authHttp: HttpClient, + private restExtractor: RestExtractor, + private restService: RestService + ) {} + + getVideo (uuid: string): Observable { + return this.authHttp.get(VideoService.BASE_VIDEO_URL + uuid) + .map(videoHash => new VideoDetails(videoHash)) + .catch((res) => this.restExtractor.handleError(res)) + } + + viewVideo (uuid: string): Observable { + return this.authHttp.post(VideoService.BASE_VIDEO_URL + uuid + '/views', {}) + .map(this.restExtractor.extractDataBool) + .catch(this.restExtractor.handleError) + } + + updateVideo (video: VideoEdit) { + const language = video.language ? video.language : null + + const body: VideoUpdate = { + name: video.name, + category: video.category, + licence: video.licence, + language, + description: video.description, + privacy: video.privacy, + tags: video.tags, + nsfw: video.nsfw + } + + return this.authHttp.put(VideoService.BASE_VIDEO_URL + video.id, body) + .map(this.restExtractor.extractDataBool) + .catch(this.restExtractor.handleError) + } + + uploadVideo (video: FormData) { + const req = new HttpRequest('POST', VideoService.BASE_VIDEO_URL + 'upload', video, { reportProgress: true }) + + return this.authHttp + .request(req) + .catch(this.restExtractor.handleError) + } + + getMyVideos (videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { + const pagination = this.videoPaginationToRestPagination(videoPagination) + + let params = new HttpParams() + params = this.restService.addRestGetParams(params, pagination, sort) + + return this.authHttp.get(UserService.BASE_USERS_URL + '/me/videos', { params }) + .map(this.extractVideos) + .catch((res) => this.restExtractor.handleError(res)) + } + + getVideos (videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { + const pagination = this.videoPaginationToRestPagination(videoPagination) + + let params = new HttpParams() + params = this.restService.addRestGetParams(params, pagination, sort) + + return this.authHttp + .get(VideoService.BASE_VIDEO_URL, { params }) + .map(this.extractVideos) + .catch((res) => this.restExtractor.handleError(res)) + } + + searchVideos (search: Search, videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { + const url = VideoService.BASE_VIDEO_URL + 'search/' + encodeURIComponent(search.value) + + const pagination = this.videoPaginationToRestPagination(videoPagination) + + let params = new HttpParams() + params = this.restService.addRestGetParams(params, pagination, sort) + + if (search.field) params.set('field', search.field) + + return this.authHttp + .get>(url, { params }) + .map(this.extractVideos) + .catch((res) => this.restExtractor.handleError(res)) + } + + removeVideo (id: number) { + return this.authHttp + .delete(VideoService.BASE_VIDEO_URL + id) + .map(this.restExtractor.extractDataBool) + .catch((res) => this.restExtractor.handleError(res)) + } + + loadCompleteDescription (descriptionPath: string) { + return this.authHttp + .get(API_URL + descriptionPath) + .map(res => res['description']) + .catch((res) => this.restExtractor.handleError(res)) + } + + setVideoLike (id: number) { + return this.setVideoRate(id, 'like') + } + + setVideoDislike (id: number) { + return this.setVideoRate(id, 'dislike') + } + + getUserVideoRating (id: number): Observable { + const url = UserService.BASE_USERS_URL + 'me/videos/' + id + '/rating' + + return this.authHttp + .get(url) + .catch(res => this.restExtractor.handleError(res)) + } + + private videoPaginationToRestPagination (videoPagination: VideoPagination) { + const start: number = (videoPagination.currentPage - 1) * videoPagination.itemsPerPage + const count: number = videoPagination.itemsPerPage + + return { start, count } + } + + private setVideoRate (id: number, rateType: VideoRateType) { + const url = VideoService.BASE_VIDEO_URL + id + '/rate' + const body: UserVideoRateUpdate = { + rating: rateType + } + + return this.authHttp + .put(url, body) + .map(this.restExtractor.extractDataBool) + .catch(res => this.restExtractor.handleError(res)) + } + + private extractVideos (result: ResultList) { + const videosJson = result.data + const totalVideos = result.total + const videos = [] + + for (const videoJson of videosJson) { + videos.push(new Video(videoJson)) + } + + return { videos, totalVideos } + } +} diff --git a/client/src/app/videos/+video-edit/shared/video-edit.module.ts b/client/src/app/videos/+video-edit/shared/video-edit.module.ts index c64cea920..cdab694f9 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.module.ts +++ b/client/src/app/videos/+video-edit/shared/video-edit.module.ts @@ -3,7 +3,7 @@ import { NgModule } from '@angular/core' import { TagInputModule } from 'ngx-chips' import { TabsModule } from 'ngx-bootstrap/tabs' -import { VideoService, MarkdownService, VideoDescriptionComponent } from '../../shared' +import { MarkdownService, VideoDescriptionComponent } from '../../shared' import { SharedModule } from '../../../shared' @NgModule({ @@ -26,7 +26,6 @@ import { SharedModule } from '../../../shared' ], providers: [ - VideoService, MarkdownService ] }) 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 76bfbb515..989addbd7 100644 --- a/client/src/app/videos/+video-edit/video-add.component.ts +++ b/client/src/app/videos/+video-edit/video-add.component.ts @@ -1,25 +1,23 @@ +import { HttpEventType, HttpResponse } from '@angular/common/http' import { Component, OnInit, ViewChild } from '@angular/core' import { FormBuilder, FormGroup } from '@angular/forms' import { Router } from '@angular/router' - import { NotificationsService } from 'angular2-notifications' - +import { VideoService } from 'app/shared/video/video.service' +import { VideoCreate } from '../../../../../shared' +import { AuthService, ServerService } from '../../core' import { FormReactive, - VIDEO_NAME, VIDEO_CATEGORY, - VIDEO_LICENCE, - VIDEO_LANGUAGE, - VIDEO_DESCRIPTION, - VIDEO_TAGS, VIDEO_CHANNEL, + VIDEO_DESCRIPTION, VIDEO_FILE, - VIDEO_PRIVACY + VIDEO_LANGUAGE, + VIDEO_LICENCE, + VIDEO_NAME, + VIDEO_PRIVACY, + VIDEO_TAGS } from '../../shared' -import { AuthService, ServerService } from '../../core' -import { VideoService } from '../shared' -import { VideoCreate } from '../../../../../shared' -import { HttpEventType, HttpResponse } from '@angular/common/http' @Component({ selector: 'my-videos-add', diff --git a/client/src/app/videos/+video-edit/video-update.component.ts b/client/src/app/videos/+video-edit/video-update.component.ts index 0e966cb50..017781866 100644 --- a/client/src/app/videos/+video-edit/video-update.component.ts +++ b/client/src/app/videos/+video-edit/video-update.component.ts @@ -1,23 +1,22 @@ import { Component, OnInit } from '@angular/core' import { FormBuilder, FormGroup } from '@angular/forms' import { ActivatedRoute, Router } from '@angular/router' -import 'rxjs/add/observable/forkJoin' - import { NotificationsService } from 'angular2-notifications' - +import 'rxjs/add/observable/forkJoin' +import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' import { ServerService } from '../../core' import { FormReactive, - VIDEO_NAME, VIDEO_CATEGORY, - VIDEO_LICENCE, - VIDEO_LANGUAGE, VIDEO_DESCRIPTION, - VIDEO_TAGS, - VIDEO_PRIVACY + VIDEO_LANGUAGE, + VIDEO_LICENCE, + VIDEO_NAME, + VIDEO_PRIVACY, + VIDEO_TAGS } from '../../shared' -import { VideoEdit, VideoService } from '../shared' -import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' +import { VideoService } from '../../shared/video/video.service' +import { VideoEdit } from '../../shared/video/video-edit.model' @Component({ selector: 'my-videos-update', diff --git a/client/src/app/videos/+video-watch/video-download.component.ts b/client/src/app/videos/+video-watch/video-download.component.ts index c32f8d586..010a246cd 100644 --- a/client/src/app/videos/+video-watch/video-download.component.ts +++ b/client/src/app/videos/+video-watch/video-download.component.ts @@ -1,8 +1,6 @@ import { Component, Input, ViewChild } from '@angular/core' - import { ModalDirective } from 'ngx-bootstrap/modal' - -import { VideoDetails } from '../shared' +import { VideoDetails } from '../../shared/video/video-details.model' @Component({ selector: 'my-video-download', diff --git a/client/src/app/videos/+video-watch/video-report.component.ts b/client/src/app/videos/+video-watch/video-report.component.ts index fc9b5a9d4..b94e4144e 100644 --- a/client/src/app/videos/+video-watch/video-report.component.ts +++ b/client/src/app/videos/+video-watch/video-report.component.ts @@ -1,11 +1,9 @@ import { Component, Input, OnInit, ViewChild } from '@angular/core' import { FormBuilder, FormGroup } from '@angular/forms' - -import { ModalDirective } from 'ngx-bootstrap/modal' import { NotificationsService } from 'angular2-notifications' - -import { FormReactive, VideoAbuseService, VIDEO_ABUSE_REASON } from '../../shared' -import { VideoDetails, VideoService } from '../shared' +import { ModalDirective } from 'ngx-bootstrap/modal' +import { FormReactive, VIDEO_ABUSE_REASON, VideoAbuseService } from '../../shared' +import { VideoDetails } from '../../shared/video/video-details.model' @Component({ selector: 'my-video-report', diff --git a/client/src/app/videos/+video-watch/video-share.component.ts b/client/src/app/videos/+video-watch/video-share.component.ts index aeef65ecf..4df9adf29 100644 --- a/client/src/app/videos/+video-watch/video-share.component.ts +++ b/client/src/app/videos/+video-watch/video-share.component.ts @@ -1,8 +1,6 @@ import { Component, Input, ViewChild } from '@angular/core' - import { ModalDirective } from 'ngx-bootstrap/modal' - -import { VideoDetails } from '../shared' +import { VideoDetails } from '../../shared/video/video-details.model' @Component({ selector: 'my-video-share', diff --git a/client/src/app/videos/+video-watch/video-watch.component.ts b/client/src/app/videos/+video-watch/video-watch.component.ts index b26f3092f..eac676be8 100644 --- a/client/src/app/videos/+video-watch/video-watch.component.ts +++ b/client/src/app/videos/+video-watch/video-watch.component.ts @@ -2,6 +2,7 @@ import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/co import { ActivatedRoute, Router } from '@angular/router' import { MetaService } from '@ngx-meta/core' import { NotificationsService } from 'angular2-notifications' +import { VideoService } from 'app/shared/video/video.service' import { Observable } from 'rxjs/Observable' import { Subscription } from 'rxjs/Subscription' import videojs from 'video.js' @@ -9,10 +10,11 @@ import { UserVideoRateType, VideoRateType } from '../../../../../shared' import '../../../assets/player/peertube-videojs-plugin' import { AuthService, ConfirmService } from '../../core' import { VideoBlacklistService } from '../../shared' -import { MarkdownService, VideoDetails, VideoService } from '../shared' +import { MarkdownService } from '../shared' import { VideoDownloadComponent } from './video-download.component' import { VideoReportComponent } from './video-report.component' import { VideoShareComponent } from './video-share.component' +import { VideoDetails } from '../../shared/video/video-details.model' @Component({ selector: 'my-video-watch', diff --git a/client/src/app/videos/+video-watch/video-watch.module.ts b/client/src/app/videos/+video-watch/video-watch.module.ts index 1b983200d..0b1dd5c15 100644 --- a/client/src/app/videos/+video-watch/video-watch.module.ts +++ b/client/src/app/videos/+video-watch/video-watch.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core' import { VideoWatchRoutingModule } from './video-watch-routing.module' -import { VideoService, MarkdownService } from '../shared' +import { MarkdownService } from '../shared' import { SharedModule } from '../../shared' import { VideoWatchComponent } from './video-watch.component' @@ -28,8 +28,7 @@ import { VideoDownloadComponent } from './video-download.component' ], providers: [ - MarkdownService, - VideoService + MarkdownService ] }) export class VideoWatchModule { } diff --git a/client/src/app/videos/shared/index.ts b/client/src/app/videos/shared/index.ts index 3f1458088..3c72ed895 100644 --- a/client/src/app/videos/shared/index.ts +++ b/client/src/app/videos/shared/index.ts @@ -1,8 +1,2 @@ -export * from './sort-field.type' export * from './markdown.service' -export * from './video.model' -export * from './video-details.model' -export * from './video-edit.model' -export * from './video.service' export * from './video-description.component' -export * from './video-pagination.model' diff --git a/client/src/app/videos/shared/sort-field.type.ts b/client/src/app/videos/shared/sort-field.type.ts deleted file mode 100644 index 776f360f8..000000000 --- a/client/src/app/videos/shared/sort-field.type.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type SortField = 'name' | '-name' - | 'duration' | '-duration' - | 'createdAt' | '-createdAt' - | 'views' | '-views' - | 'likes' | '-likes' diff --git a/client/src/app/videos/shared/video-details.model.ts b/client/src/app/videos/shared/video-details.model.ts deleted file mode 100644 index 64cb4f847..000000000 --- a/client/src/app/videos/shared/video-details.model.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Video } from './video.model' -import { AuthUser } from '../../core' -import { - VideoDetails as VideoDetailsServerModel, - VideoFile, - VideoChannel, - VideoResolution, - UserRight, - VideoPrivacy -} from '../../../../../shared' - -export class VideoDetails extends Video implements VideoDetailsServerModel { - account: string - by: string - createdAt: Date - updatedAt: Date - categoryLabel: string - category: number - licenceLabel: string - licence: number - languageLabel: string - language: number - description: string - duration: number - durationLabel: string - id: number - uuid: string - isLocal: boolean - name: string - serverHost: string - tags: string[] - thumbnailPath: string - thumbnailUrl: string - previewPath: string - previewUrl: string - embedPath: string - embedUrl: string - views: number - likes: number - dislikes: number - nsfw: boolean - descriptionPath: string - files: VideoFile[] - channel: VideoChannel - privacy: VideoPrivacy - privacyLabel: string - - constructor (hash: VideoDetailsServerModel) { - super(hash) - - this.privacy = hash.privacy - this.privacyLabel = hash.privacyLabel - this.descriptionPath = hash.descriptionPath - this.files = hash.files - this.channel = hash.channel - } - - getAppropriateMagnetUri (actualDownloadSpeed = 0) { - if (this.files === undefined || this.files.length === 0) return '' - if (this.files.length === 1) return this.files[0].magnetUri - - // Find first video that is good for our download speed (remember they are sorted) - let betterResolutionFile = this.files.find(f => actualDownloadSpeed > (f.size / this.duration)) - - // If the download speed is too bad, return the lowest resolution we have - if (betterResolutionFile === undefined) { - betterResolutionFile = this.files.find(f => f.resolution === VideoResolution.H_240P) - } - - return betterResolutionFile.magnetUri - } - - isRemovableBy (user: AuthUser) { - return user && this.isLocal === true && (this.account === user.username || user.hasRight(UserRight.REMOVE_ANY_VIDEO)) - } - - isBlackistableBy (user: AuthUser) { - return user && user.hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) === true && this.isLocal === false - } - - isUpdatableBy (user: AuthUser) { - return user && this.isLocal === true && user.username === this.account - } -} diff --git a/client/src/app/videos/shared/video-edit.model.ts b/client/src/app/videos/shared/video-edit.model.ts deleted file mode 100644 index 88d23a59f..000000000 --- a/client/src/app/videos/shared/video-edit.model.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { VideoDetails } from './video-details.model' -import { VideoPrivacy } from '../../../../../shared/models/videos/video-privacy.enum' - -export class VideoEdit { - category: number - licence: number - language: number - description: string - name: string - tags: string[] - nsfw: boolean - channel: number - privacy: VideoPrivacy - uuid?: string - id?: number - - constructor (videoDetails: VideoDetails) { - this.id = videoDetails.id - this.uuid = videoDetails.uuid - this.category = videoDetails.category - this.licence = videoDetails.licence - this.language = videoDetails.language - this.description = videoDetails.description - this.name = videoDetails.name - this.tags = videoDetails.tags - this.nsfw = videoDetails.nsfw - this.channel = videoDetails.channel.id - this.privacy = videoDetails.privacy - } - - patch (values: Object) { - Object.keys(values).forEach((key) => { - this[key] = values[key] - }) - } - - toJSON () { - return { - category: this.category, - licence: this.licence, - language: this.language, - description: this.description, - name: this.name, - tags: this.tags, - nsfw: this.nsfw, - channel: this.channel, - privacy: this.privacy - } - } -} diff --git a/client/src/app/videos/shared/video-pagination.model.ts b/client/src/app/videos/shared/video-pagination.model.ts deleted file mode 100644 index 9e71769cb..000000000 --- a/client/src/app/videos/shared/video-pagination.model.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface VideoPagination { - currentPage: number - itemsPerPage: number - totalItems: number -} diff --git a/client/src/app/videos/shared/video.model.ts b/client/src/app/videos/shared/video.model.ts deleted file mode 100644 index 0dd41d71b..000000000 --- a/client/src/app/videos/shared/video.model.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Video as VideoServerModel } from '../../../../../shared' -import { User } from '../../shared' - -export class Video implements VideoServerModel { - account: string - by: string - createdAt: Date - updatedAt: Date - categoryLabel: string - category: number - licenceLabel: string - licence: number - languageLabel: string - language: number - description: string - duration: number - durationLabel: string - id: number - uuid: string - isLocal: boolean - name: string - serverHost: string - tags: string[] - thumbnailPath: string - thumbnailUrl: string - previewPath: string - previewUrl: string - embedPath: string - embedUrl: string - views: number - likes: number - dislikes: number - nsfw: boolean - - private static createByString (account: string, serverHost: string) { - return account + '@' + serverHost - } - - private static createDurationString (duration: number) { - const minutes = Math.floor(duration / 60) - const seconds = duration % 60 - const minutesPadding = minutes >= 10 ? '' : '0' - const secondsPadding = seconds >= 10 ? '' : '0' - - return minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString() - } - - constructor (hash: VideoServerModel) { - let absoluteAPIUrl = API_URL - if (!absoluteAPIUrl) { - // The API is on the same domain - absoluteAPIUrl = window.location.origin - } - - this.account = hash.account - this.createdAt = new Date(hash.createdAt.toString()) - this.categoryLabel = hash.categoryLabel - this.category = hash.category - this.licenceLabel = hash.licenceLabel - this.licence = hash.licence - this.languageLabel = hash.languageLabel - this.language = hash.language - this.description = hash.description - this.duration = hash.duration - this.durationLabel = Video.createDurationString(hash.duration) - this.id = hash.id - this.uuid = hash.uuid - this.isLocal = hash.isLocal - this.name = hash.name - this.serverHost = hash.serverHost - this.tags = hash.tags - this.thumbnailPath = hash.thumbnailPath - this.thumbnailUrl = absoluteAPIUrl + hash.thumbnailPath - this.previewPath = hash.previewPath - this.previewUrl = absoluteAPIUrl + hash.previewPath - this.embedPath = hash.embedPath - this.embedUrl = absoluteAPIUrl + hash.embedPath - this.views = hash.views - this.likes = hash.likes - this.dislikes = hash.dislikes - this.nsfw = hash.nsfw - - this.by = Video.createByString(hash.account, hash.serverHost) - } - - 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)) - } -} diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts deleted file mode 100644 index 5d25a26d4..000000000 --- a/client/src/app/videos/shared/video.service.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { Injectable } from '@angular/core' -import { Observable } from 'rxjs/Observable' -import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http' -import 'rxjs/add/operator/catch' -import 'rxjs/add/operator/map' - -import { SortField } from './sort-field.type' -import { - RestExtractor, - RestService, - UserService, - Search -} from '../../shared' -import { Video } from './video.model' -import { VideoDetails } from './video-details.model' -import { VideoEdit } from './video-edit.model' -import { VideoPagination } from './video-pagination.model' -import { - UserVideoRate, - VideoRateType, - VideoUpdate, - UserVideoRateUpdate, - Video as VideoServerModel, - VideoDetails as VideoDetailsServerModel, - ResultList -} from '../../../../../shared' - -@Injectable() -export class VideoService { - private static BASE_VIDEO_URL = API_URL + '/api/v1/videos/' - - constructor ( - private authHttp: HttpClient, - private restExtractor: RestExtractor, - private restService: RestService - ) {} - - getVideo (uuid: string): Observable { - return this.authHttp.get(VideoService.BASE_VIDEO_URL + uuid) - .map(videoHash => new VideoDetails(videoHash)) - .catch((res) => this.restExtractor.handleError(res)) - } - - viewVideo (uuid: string): Observable { - return this.authHttp.post(VideoService.BASE_VIDEO_URL + uuid + '/views', {}) - .map(this.restExtractor.extractDataBool) - .catch(this.restExtractor.handleError) - } - - updateVideo (video: VideoEdit) { - const language = video.language ? video.language : null - - const body: VideoUpdate = { - name: video.name, - category: video.category, - licence: video.licence, - language, - description: video.description, - privacy: video.privacy, - tags: video.tags, - nsfw: video.nsfw - } - - return this.authHttp.put(VideoService.BASE_VIDEO_URL + video.id, body) - .map(this.restExtractor.extractDataBool) - .catch(this.restExtractor.handleError) - } - - uploadVideo (video: FormData) { - const req = new HttpRequest('POST', VideoService.BASE_VIDEO_URL + 'upload', video, { reportProgress: true }) - - return this.authHttp - .request(req) - .catch(this.restExtractor.handleError) - } - - getMyVideos (videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { - const pagination = this.videoPaginationToRestPagination(videoPagination) - - let params = new HttpParams() - params = this.restService.addRestGetParams(params, pagination, sort) - - return this.authHttp.get(UserService.BASE_USERS_URL + '/me/videos', { params }) - .map(this.extractVideos) - .catch((res) => this.restExtractor.handleError(res)) - } - - getVideos (videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { - const pagination = this.videoPaginationToRestPagination(videoPagination) - - let params = new HttpParams() - params = this.restService.addRestGetParams(params, pagination, sort) - - return this.authHttp - .get(VideoService.BASE_VIDEO_URL, { params }) - .map(this.extractVideos) - .catch((res) => this.restExtractor.handleError(res)) - } - - searchVideos (search: Search, videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { - const url = VideoService.BASE_VIDEO_URL + 'search/' + encodeURIComponent(search.value) - - const pagination = this.videoPaginationToRestPagination(videoPagination) - - let params = new HttpParams() - params = this.restService.addRestGetParams(params, pagination, sort) - - if (search.field) params.set('field', search.field) - - return this.authHttp - .get>(url, { params }) - .map(this.extractVideos) - .catch((res) => this.restExtractor.handleError(res)) - } - - removeVideo (id: number) { - return this.authHttp - .delete(VideoService.BASE_VIDEO_URL + id) - .map(this.restExtractor.extractDataBool) - .catch((res) => this.restExtractor.handleError(res)) - } - - loadCompleteDescription (descriptionPath: string) { - return this.authHttp - .get(API_URL + descriptionPath) - .map(res => res['description']) - .catch((res) => this.restExtractor.handleError(res)) - } - - setVideoLike (id: number) { - return this.setVideoRate(id, 'like') - } - - setVideoDislike (id: number) { - return this.setVideoRate(id, 'dislike') - } - - getUserVideoRating (id: number): Observable { - const url = UserService.BASE_USERS_URL + 'me/videos/' + id + '/rating' - - return this.authHttp - .get(url) - .catch(res => this.restExtractor.handleError(res)) - } - - private videoPaginationToRestPagination (videoPagination: VideoPagination) { - const start: number = (videoPagination.currentPage - 1) * videoPagination.itemsPerPage - const count: number = videoPagination.itemsPerPage - - return { start, count } - } - - private setVideoRate (id: number, rateType: VideoRateType) { - const url = VideoService.BASE_VIDEO_URL + id + '/rate' - const body: UserVideoRateUpdate = { - rating: rateType - } - - return this.authHttp - .put(url, body) - .map(this.restExtractor.extractDataBool) - .catch(res => this.restExtractor.handleError(res)) - } - - private extractVideos (result: ResultList) { - const videosJson = result.data - const totalVideos = result.total - const videos = [] - - for (const videoJson of videosJson) { - videos.push(new Video(videoJson)) - } - - return { videos, totalVideos } - } -} diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.html b/client/src/app/videos/video-list/shared/abstract-video-list.html deleted file mode 100644 index bd4f6b1f8..000000000 --- a/client/src/app/videos/video-list/shared/abstract-video-list.html +++ /dev/null @@ -1,19 +0,0 @@ -
-
- {{ titlePage }} -
- -
- - -
-
diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.scss b/client/src/app/videos/video-list/shared/abstract-video-list.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/client/src/app/videos/video-list/shared/abstract-video-list.ts b/client/src/app/videos/video-list/shared/abstract-video-list.ts deleted file mode 100644 index a684ffef4..000000000 --- a/client/src/app/videos/video-list/shared/abstract-video-list.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { OnDestroy, OnInit } from '@angular/core' -import { ActivatedRoute, Router } from '@angular/router' - -import { NotificationsService } from 'angular2-notifications' -import { Observable } from 'rxjs/Observable' -import { Subscription } from 'rxjs/Subscription' - -import { SortField, Video, VideoPagination } from '../../shared' - -export abstract class AbstractVideoList implements OnInit, OnDestroy { - pagination: VideoPagination = { - currentPage: 1, - itemsPerPage: 25, - totalItems: null - } - sort: SortField = '-createdAt' - videos: Video[] = [] - - protected notificationsService: NotificationsService - protected router: Router - protected route: ActivatedRoute - protected subActivatedRoute: Subscription - - protected abstract currentRoute: string - - abstract titlePage: string - private loadedPages: { [ id: number ]: boolean } = {} - - abstract getVideosObservable (): Observable<{ videos: Video[], totalVideos: number}> - - ngOnInit () { - // Subscribe to route changes - const routeParams = this.route.snapshot.params - this.loadRouteParams(routeParams) - this.loadMoreVideos('after') - } - - ngOnDestroy () { - if (this.subActivatedRoute) { - this.subActivatedRoute.unsubscribe() - } - } - - onNearOfTop () { - if (this.pagination.currentPage > 1) { - this.previousPage() - } - } - - onNearOfBottom () { - if (this.hasMoreVideos()) { - this.nextPage() - } - } - - loadMoreVideos (where: 'before' | 'after') { - if (this.loadedPages[this.pagination.currentPage] === true) return - - const observable = this.getVideosObservable() - - observable.subscribe( - ({ videos, totalVideos }) => { - this.loadedPages[this.pagination.currentPage] = true - this.pagination.totalItems = totalVideos - - if (where === 'before') { - this.videos = videos.concat(this.videos) - } else { - this.videos = this.videos.concat(videos) - } - }, - error => this.notificationsService.error('Error', error.text) - ) - } - - protected hasMoreVideos () { - if (!this.pagination.totalItems) return true - - const maxPage = this.pagination.totalItems/this.pagination.itemsPerPage - return maxPage > this.pagination.currentPage - } - - protected previousPage () { - this.pagination.currentPage-- - - this.setNewRouteParams() - this.loadMoreVideos('before') - } - - protected nextPage () { - this.pagination.currentPage++ - - this.setNewRouteParams() - this.loadMoreVideos('after') - } - - protected buildRouteParams () { - // There is always a sort and a current page - const params = { - sort: this.sort, - page: this.pagination.currentPage - } - - return params - } - - protected loadRouteParams (routeParams: { [ key: string ]: any }) { - this.sort = routeParams['sort'] as SortField || '-createdAt' - - if (routeParams['page'] !== undefined) { - this.pagination.currentPage = parseInt(routeParams['page'], 10) - } else { - this.pagination.currentPage = 1 - } - } - - protected setNewRouteParams () { - const routeParams = this.buildRouteParams() - this.router.navigate([ this.currentRoute, routeParams ]) - } -} diff --git a/client/src/app/videos/video-list/shared/index.ts b/client/src/app/videos/video-list/shared/index.ts index 170ca4832..2778f2d9e 100644 --- a/client/src/app/videos/video-list/shared/index.ts +++ b/client/src/app/videos/video-list/shared/index.ts @@ -1,2 +1 @@ -export * from './abstract-video-list' export * from './video-miniature.component' diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.html b/client/src/app/videos/video-list/shared/video-miniature.component.html index aea85b6c6..f2756ca3d 100644 --- a/client/src/app/videos/video-list/shared/video-miniature.component.html +++ b/client/src/app/videos/video-list/shared/video-miniature.component.html @@ -1,14 +1,5 @@
- - video thumbnail - -
- {{ video.durationLabel }} -
-
+
diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.scss b/client/src/app/videos/video-list/shared/video-miniature.component.scss index ed15864d9..658d7af9d 100644 --- a/client/src/app/videos/video-list/shared/video-miniature.component.scss +++ b/client/src/app/videos/video-list/shared/video-miniature.component.scss @@ -5,35 +5,6 @@ height: 175px; vertical-align: top; - .video-miniature-thumbnail { - display: inline-block; - position: relative; - border-radius: 4px; - overflow: hidden; - - &:hover { - text-decoration: none !important; - } - - img.blur-filter { - filter: blur(5px); - transform : scale(1.03); - } - - .video-miniature-thumbnail-overlay { - position: absolute; - right: 5px; - bottom: 5px; - display: inline-block; - background-color: rgba(0, 0, 0, 0.7); - color: #fff; - font-size: 12px; - font-weight: $font-bold; - border-radius: 3px; - padding: 0 5px; - } - } - .video-miniature-information { width: 200px; margin-top: 2px; diff --git a/client/src/app/videos/video-list/shared/video-miniature.component.ts b/client/src/app/videos/video-list/shared/video-miniature.component.ts index e5a87907b..e8fc8e911 100644 --- a/client/src/app/videos/video-list/shared/video-miniature.component.ts +++ b/client/src/app/videos/video-list/shared/video-miniature.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core' - -import { SortField, Video } from '../../shared' import { User } from '../../../shared' +import { SortField } from '../../../shared/video/sort-field.type' +import { Video } from '../../../shared/video/video.model' @Component({ selector: 'my-video-miniature', diff --git a/client/src/app/videos/video-list/video-recently-added.component.ts b/client/src/app/videos/video-list/video-recently-added.component.ts index 9bf69bd78..d48804414 100644 --- a/client/src/app/videos/video-list/video-recently-added.component.ts +++ b/client/src/app/videos/video-list/video-recently-added.component.ts @@ -1,13 +1,13 @@ import { Component, OnDestroy, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' -import { VideoService } from '../shared' -import { AbstractVideoList } from './shared' +import { VideoService } from '../../shared/video/video.service' +import { AbstractVideoList } from '../../shared/video/abstract-video-list' @Component({ selector: 'my-videos-recently-added', - styleUrls: [ './shared/abstract-video-list.scss' ], - templateUrl: './shared/abstract-video-list.html' + styleUrls: [ '../../shared/video/abstract-video-list.scss' ], + templateUrl: '../../shared/video/abstract-video-list.html' }) export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { titlePage = 'Recently added' diff --git a/client/src/app/videos/video-list/video-trending.component.ts b/client/src/app/videos/video-list/video-trending.component.ts index a1df68711..9108289c9 100644 --- a/client/src/app/videos/video-list/video-trending.component.ts +++ b/client/src/app/videos/video-list/video-trending.component.ts @@ -1,13 +1,13 @@ import { Component, OnDestroy, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' -import { VideoService } from '../shared' -import { AbstractVideoList } from './shared' +import { VideoService } from '../../shared/video/video.service' +import { AbstractVideoList } from 'app/shared/video/abstract-video-list' @Component({ selector: 'my-videos-trending', - styleUrls: [ './shared/abstract-video-list.scss' ], - templateUrl: './shared/abstract-video-list.html' + styleUrls: [ '../../shared/video/abstract-video-list.scss' ], + templateUrl: '../../shared/video/abstract-video-list.html' }) export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { titlePage = 'Trending' diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts index 1d6194158..6d846fd3b 100644 --- a/client/src/app/videos/videos.module.ts +++ b/client/src/app/videos/videos.module.ts @@ -1,7 +1,5 @@ import { NgModule } from '@angular/core' -import { InfiniteScrollModule } from 'ngx-infinite-scroll' import { SharedModule } from '../shared' -import { VideoService } from './shared' import { VideoMiniatureComponent } from './video-list' import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' import { VideoTrendingComponent } from './video-list/video-trending.component' @@ -11,8 +9,7 @@ import { VideosComponent } from './videos.component' @NgModule({ imports: [ VideosRoutingModule, - SharedModule, - InfiniteScrollModule + SharedModule ], declarations: [ @@ -27,8 +24,6 @@ import { VideosComponent } from './videos.component' VideosComponent ], - providers: [ - VideoService - ] + providers: [] }) export class VideosModule { } -- cgit v1.2.3