Adapt client with video channels
This commit is contained in:
parent
f5028693a8
commit
404b54e14f
|
@ -1,5 +1,23 @@
|
||||||
import { User as UserServerModel, UserRole } from '../../../../../shared'
|
import {
|
||||||
|
User as UserServerModel,
|
||||||
|
UserRole,
|
||||||
|
VideoChannel
|
||||||
|
} from '../../../../../shared'
|
||||||
|
|
||||||
|
export type UserConstructorHash = {
|
||||||
|
id: number,
|
||||||
|
username: string,
|
||||||
|
email: string,
|
||||||
|
role: UserRole,
|
||||||
|
videoQuota?: number,
|
||||||
|
displayNSFW?: boolean,
|
||||||
|
createdAt?: Date,
|
||||||
|
author?: {
|
||||||
|
id: number
|
||||||
|
uuid: string
|
||||||
|
},
|
||||||
|
videoChannels?: VideoChannel[]
|
||||||
|
}
|
||||||
export class User implements UserServerModel {
|
export class User implements UserServerModel {
|
||||||
id: number
|
id: number
|
||||||
username: string
|
username: string
|
||||||
|
@ -7,21 +25,23 @@ export class User implements UserServerModel {
|
||||||
role: UserRole
|
role: UserRole
|
||||||
displayNSFW: boolean
|
displayNSFW: boolean
|
||||||
videoQuota: number
|
videoQuota: number
|
||||||
|
author: {
|
||||||
|
id: number
|
||||||
|
uuid: string
|
||||||
|
}
|
||||||
|
videoChannels: VideoChannel[]
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
|
||||||
constructor (hash: {
|
constructor (hash: UserConstructorHash) {
|
||||||
id: number,
|
|
||||||
username: string,
|
|
||||||
email: string,
|
|
||||||
role: UserRole,
|
|
||||||
videoQuota?: number,
|
|
||||||
displayNSFW?: boolean,
|
|
||||||
createdAt?: Date
|
|
||||||
}) {
|
|
||||||
this.id = hash.id
|
this.id = hash.id
|
||||||
this.username = hash.username
|
this.username = hash.username
|
||||||
this.email = hash.email
|
this.email = hash.email
|
||||||
this.role = hash.role
|
this.role = hash.role
|
||||||
|
this.author = hash.author
|
||||||
|
|
||||||
|
if (hash.videoChannels !== undefined) {
|
||||||
|
this.videoChannels = hash.videoChannels
|
||||||
|
}
|
||||||
|
|
||||||
if (hash.videoQuota !== undefined) {
|
if (hash.videoQuota !== undefined) {
|
||||||
this.videoQuota = hash.videoQuota
|
this.videoQuota = hash.videoQuota
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
VIDEO_DESCRIPTION,
|
VIDEO_DESCRIPTION,
|
||||||
VIDEO_TAGS
|
VIDEO_TAGS
|
||||||
} from '../../shared'
|
} from '../../shared'
|
||||||
import { Video, VideoService } from '../shared'
|
import { VideoEdit, VideoService } from '../shared'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-videos-update',
|
selector: 'my-videos-update',
|
||||||
|
@ -27,7 +27,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
|
||||||
videoCategories = []
|
videoCategories = []
|
||||||
videoLicences = []
|
videoLicences = []
|
||||||
videoLanguages = []
|
videoLanguages = []
|
||||||
video: Video
|
video: VideoEdit
|
||||||
|
|
||||||
tagValidators = VIDEO_TAGS.VALIDATORS
|
tagValidators = VIDEO_TAGS.VALIDATORS
|
||||||
tagValidatorsMessages = VIDEO_TAGS.MESSAGES
|
tagValidatorsMessages = VIDEO_TAGS.MESSAGES
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Component, Input, ViewChild } from '@angular/core'
|
||||||
|
|
||||||
import { ModalDirective } from 'ngx-bootstrap/modal'
|
import { ModalDirective } from 'ngx-bootstrap/modal'
|
||||||
|
|
||||||
import { Video } from '../shared'
|
import { VideoDetails } from '../shared'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-download',
|
selector: 'my-video-download',
|
||||||
|
@ -10,7 +10,7 @@ import { Video } from '../shared'
|
||||||
styles: [ '.resolution-block { margin-top: 20px; }' ]
|
styles: [ '.resolution-block { margin-top: 20px; }' ]
|
||||||
})
|
})
|
||||||
export class VideoDownloadComponent {
|
export class VideoDownloadComponent {
|
||||||
@Input() video: Video = null
|
@Input() video: VideoDetails = null
|
||||||
|
|
||||||
@ViewChild('modal') modal: ModalDirective
|
@ViewChild('modal') modal: ModalDirective
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,14 @@ import { ModalDirective } from 'ngx-bootstrap/modal'
|
||||||
import { NotificationsService } from 'angular2-notifications'
|
import { NotificationsService } from 'angular2-notifications'
|
||||||
|
|
||||||
import { FormReactive, VideoAbuseService, VIDEO_ABUSE_REASON } from '../../shared'
|
import { FormReactive, VideoAbuseService, VIDEO_ABUSE_REASON } from '../../shared'
|
||||||
import { Video, VideoService } from '../shared'
|
import { VideoDetails, VideoService } from '../shared'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-report',
|
selector: 'my-video-report',
|
||||||
templateUrl: './video-report.component.html'
|
templateUrl: './video-report.component.html'
|
||||||
})
|
})
|
||||||
export class VideoReportComponent extends FormReactive implements OnInit {
|
export class VideoReportComponent extends FormReactive implements OnInit {
|
||||||
@Input() video: Video = null
|
@Input() video: VideoDetails = null
|
||||||
|
|
||||||
@ViewChild('modal') modal: ModalDirective
|
@ViewChild('modal') modal: ModalDirective
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,14 @@ import { Component, Input, ViewChild } from '@angular/core'
|
||||||
|
|
||||||
import { ModalDirective } from 'ngx-bootstrap/modal'
|
import { ModalDirective } from 'ngx-bootstrap/modal'
|
||||||
|
|
||||||
import { Video } from '../shared'
|
import { VideoDetails } from '../shared'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-share',
|
selector: 'my-video-share',
|
||||||
templateUrl: './video-share.component.html'
|
templateUrl: './video-share.component.html'
|
||||||
})
|
})
|
||||||
export class VideoShareComponent {
|
export class VideoShareComponent {
|
||||||
@Input() video: Video = null
|
@Input() video: VideoDetails = null
|
||||||
|
|
||||||
@ViewChild('modal') modal: ModalDirective
|
@ViewChild('modal') modal: ModalDirective
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { AuthService, ConfirmService } from '../../core'
|
||||||
import { VideoDownloadComponent } from './video-download.component'
|
import { VideoDownloadComponent } from './video-download.component'
|
||||||
import { VideoShareComponent } from './video-share.component'
|
import { VideoShareComponent } from './video-share.component'
|
||||||
import { VideoReportComponent } from './video-report.component'
|
import { VideoReportComponent } from './video-report.component'
|
||||||
import { Video, VideoService } from '../shared'
|
import { VideoDetails, VideoService } from '../shared'
|
||||||
import { VideoBlacklistService } from '../../shared'
|
import { VideoBlacklistService } from '../../shared'
|
||||||
import { UserVideoRateType, VideoRateType } from '../../../../../shared'
|
import { UserVideoRateType, VideoRateType } from '../../../../../shared'
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
playerElement: HTMLMediaElement
|
playerElement: HTMLMediaElement
|
||||||
uploadSpeed: number
|
uploadSpeed: number
|
||||||
userRating: UserVideoRateType = null
|
userRating: UserVideoRateType = null
|
||||||
video: Video = null
|
video: VideoDetails = null
|
||||||
videoPlayerLoaded = false
|
videoPlayerLoaded = false
|
||||||
videoNotFound = false
|
videoNotFound = false
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private onVideoFetched (video: Video) {
|
private onVideoFetched (video: VideoDetails) {
|
||||||
this.video = video
|
this.video = video
|
||||||
|
|
||||||
let observable
|
let observable
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
export * from './sort-field.type'
|
export * from './sort-field.type'
|
||||||
export * from './video.model'
|
export * from './video.model'
|
||||||
|
export * from './video-details.model'
|
||||||
|
export * from './video-edit.model'
|
||||||
export * from './video.service'
|
export * from './video.service'
|
||||||
export * from './video-pagination.model'
|
export * from './video-pagination.model'
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { Video } from './video.model'
|
||||||
|
import {
|
||||||
|
VideoDetails as VideoDetailsServerModel,
|
||||||
|
VideoFile,
|
||||||
|
VideoChannel,
|
||||||
|
VideoResolution
|
||||||
|
} from '../../../../../shared'
|
||||||
|
|
||||||
|
export class VideoDetails extends Video implements VideoDetailsServerModel {
|
||||||
|
author: 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
|
||||||
|
podHost: string
|
||||||
|
tags: string[]
|
||||||
|
thumbnailPath: string
|
||||||
|
thumbnailUrl: string
|
||||||
|
previewPath: string
|
||||||
|
previewUrl: string
|
||||||
|
embedPath: string
|
||||||
|
embedUrl: string
|
||||||
|
views: number
|
||||||
|
likes: number
|
||||||
|
dislikes: number
|
||||||
|
nsfw: boolean
|
||||||
|
files: VideoFile[]
|
||||||
|
channel: VideoChannel
|
||||||
|
|
||||||
|
constructor (hash: VideoDetailsServerModel) {
|
||||||
|
super(hash)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return user && this.isLocal === true && (this.author === user.username || user.isAdmin() === true)
|
||||||
|
}
|
||||||
|
|
||||||
|
isBlackistableBy (user) {
|
||||||
|
return user && user.isAdmin() === true && this.isLocal === false
|
||||||
|
}
|
||||||
|
|
||||||
|
isUpdatableBy (user) {
|
||||||
|
return user && this.isLocal === true && user.username === this.author
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
export class VideoEdit {
|
||||||
|
category: number
|
||||||
|
licence: number
|
||||||
|
language: number
|
||||||
|
description: string
|
||||||
|
name: string
|
||||||
|
tags: string[]
|
||||||
|
nsfw: boolean
|
||||||
|
channel: number
|
||||||
|
uuid?: string
|
||||||
|
id?: number
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import { Video as VideoServerModel, VideoFile } from '../../../../../shared'
|
import { Video as VideoServerModel } from '../../../../../shared'
|
||||||
import { User } from '../../shared'
|
import { User } from '../../shared'
|
||||||
import { VideoResolution } from '../../../../../shared/models/videos/video-resolution.enum'
|
|
||||||
|
|
||||||
export class Video implements VideoServerModel {
|
export class Video implements VideoServerModel {
|
||||||
author: string
|
author: string
|
||||||
|
@ -32,7 +31,6 @@ export class Video implements VideoServerModel {
|
||||||
likes: number
|
likes: number
|
||||||
dislikes: number
|
dislikes: number
|
||||||
nsfw: boolean
|
nsfw: boolean
|
||||||
files: VideoFile[]
|
|
||||||
|
|
||||||
private static createByString (author: string, podHost: string) {
|
private static createByString (author: string, podHost: string) {
|
||||||
return author + '@' + podHost
|
return author + '@' + podHost
|
||||||
|
@ -47,32 +45,7 @@ export class Video implements VideoServerModel {
|
||||||
return minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString()
|
return minutesPadding + minutes.toString() + ':' + secondsPadding + seconds.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor (hash: {
|
constructor (hash: VideoServerModel) {
|
||||||
author: string,
|
|
||||||
createdAt: Date | string,
|
|
||||||
categoryLabel: string,
|
|
||||||
category: number,
|
|
||||||
licenceLabel: string,
|
|
||||||
licence: number,
|
|
||||||
languageLabel: string
|
|
||||||
language: number
|
|
||||||
description: string,
|
|
||||||
duration: number
|
|
||||||
id: number,
|
|
||||||
uuid: string,
|
|
||||||
isLocal: boolean,
|
|
||||||
name: string,
|
|
||||||
podHost: string,
|
|
||||||
tags: string[],
|
|
||||||
thumbnailPath: string,
|
|
||||||
previewPath: string,
|
|
||||||
embedPath: string,
|
|
||||||
views: number,
|
|
||||||
likes: number,
|
|
||||||
dislikes: number,
|
|
||||||
nsfw: boolean,
|
|
||||||
files: VideoFile[]
|
|
||||||
}) {
|
|
||||||
let absoluteAPIUrl = API_URL
|
let absoluteAPIUrl = API_URL
|
||||||
if (!absoluteAPIUrl) {
|
if (!absoluteAPIUrl) {
|
||||||
// The API is on the same domain
|
// The API is on the same domain
|
||||||
|
@ -106,69 +79,12 @@ export class Video implements VideoServerModel {
|
||||||
this.likes = hash.likes
|
this.likes = hash.likes
|
||||||
this.dislikes = hash.dislikes
|
this.dislikes = hash.dislikes
|
||||||
this.nsfw = hash.nsfw
|
this.nsfw = hash.nsfw
|
||||||
this.files = hash.files
|
|
||||||
|
|
||||||
this.by = Video.createByString(hash.author, hash.podHost)
|
this.by = Video.createByString(hash.author, hash.podHost)
|
||||||
}
|
}
|
||||||
|
|
||||||
isRemovableBy (user) {
|
|
||||||
return user && this.isLocal === true && (this.author === user.username || user.isAdmin() === true)
|
|
||||||
}
|
|
||||||
|
|
||||||
isBlackistableBy (user) {
|
|
||||||
return user && user.isAdmin() === true && this.isLocal === false
|
|
||||||
}
|
|
||||||
|
|
||||||
isUpdatableBy (user) {
|
|
||||||
return user && this.isLocal === true && user.username === this.author
|
|
||||||
}
|
|
||||||
|
|
||||||
isVideoNSFWForUser (user: User) {
|
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...
|
// 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))
|
return (this.nsfw && (!user || user.displayNSFW === false))
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
patch (values: Object) {
|
|
||||||
Object.keys(values).forEach((key) => {
|
|
||||||
this[key] = values[key]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON () {
|
|
||||||
return {
|
|
||||||
author: this.author,
|
|
||||||
createdAt: this.createdAt,
|
|
||||||
category: this.category,
|
|
||||||
licence: this.licence,
|
|
||||||
language: this.language,
|
|
||||||
description: this.description,
|
|
||||||
duration: this.duration,
|
|
||||||
id: this.id,
|
|
||||||
isLocal: this.isLocal,
|
|
||||||
name: this.name,
|
|
||||||
podHost: this.podHost,
|
|
||||||
tags: this.tags,
|
|
||||||
thumbnailPath: this.thumbnailPath,
|
|
||||||
views: this.views,
|
|
||||||
likes: this.likes,
|
|
||||||
dislikes: this.dislikes,
|
|
||||||
nsfw: this.nsfw,
|
|
||||||
files: this.files
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import {
|
||||||
UserService
|
UserService
|
||||||
} from '../../shared'
|
} from '../../shared'
|
||||||
import { Video } from './video.model'
|
import { Video } from './video.model'
|
||||||
|
import { VideoDetails } from './video-details.model'
|
||||||
|
import { VideoEdit } from './video-edit.model'
|
||||||
import { VideoPagination } from './video-pagination.model'
|
import { VideoPagination } from './video-pagination.model'
|
||||||
import {
|
import {
|
||||||
UserVideoRate,
|
UserVideoRate,
|
||||||
|
@ -20,6 +22,7 @@ import {
|
||||||
VideoAbuseCreate,
|
VideoAbuseCreate,
|
||||||
UserVideoRateUpdate,
|
UserVideoRateUpdate,
|
||||||
Video as VideoServerModel,
|
Video as VideoServerModel,
|
||||||
|
VideoDetails as VideoDetailsServerModel,
|
||||||
ResultList
|
ResultList
|
||||||
} from '../../../../../shared'
|
} from '../../../../../shared'
|
||||||
|
|
||||||
|
@ -34,12 +37,12 @@ export class VideoService {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
getVideo (uuid: string) {
|
getVideo (uuid: string) {
|
||||||
return this.authHttp.get<VideoServerModel>(VideoService.BASE_VIDEO_URL + uuid)
|
return this.authHttp.get<VideoDetailsServerModel>(VideoService.BASE_VIDEO_URL + uuid)
|
||||||
.map(videoHash => new Video(videoHash))
|
.map(videoHash => new VideoDetails(videoHash))
|
||||||
.catch((res) => this.restExtractor.handleError(res))
|
.catch((res) => this.restExtractor.handleError(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
updateVideo (video: Video) {
|
updateVideo (video: VideoEdit) {
|
||||||
const language = video.language ? video.language : null
|
const language = video.language ? video.language : null
|
||||||
|
|
||||||
const body: VideoUpdate = {
|
const body: VideoUpdate = {
|
||||||
|
|
|
@ -3,9 +3,9 @@ import './embed.scss'
|
||||||
import videojs from 'video.js'
|
import videojs from 'video.js'
|
||||||
import '../../assets/player/peertube-videojs-plugin'
|
import '../../assets/player/peertube-videojs-plugin'
|
||||||
import 'videojs-dock/dist/videojs-dock.es.js'
|
import 'videojs-dock/dist/videojs-dock.es.js'
|
||||||
import { Video } from '../../../../shared'
|
import { VideoDetails } from '../../../../shared'
|
||||||
|
|
||||||
function loadVideoInfo (videoId: string, callback: (err: Error, res?: Video) => void) {
|
function loadVideoInfo (videoId: string, callback: (err: Error, res?: VideoDetails) => void) {
|
||||||
const xhttp = new XMLHttpRequest()
|
const xhttp = new XMLHttpRequest()
|
||||||
xhttp.onreadystatechange = function () {
|
xhttp.onreadystatechange = function () {
|
||||||
if (this.readyState === 4 && this.status === 200) {
|
if (this.readyState === 4 && this.status === 200) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import * as Promise from 'bluebird'
|
|
||||||
|
|
||||||
import { pseudoRandomBytesPromise } from './core-utils'
|
import { pseudoRandomBytesPromise } from './core-utils'
|
||||||
import { CONFIG, database as db } from '../initializers'
|
import { CONFIG, database as db } from '../initializers'
|
||||||
|
|
Loading…
Reference in New Issue