const PurifyCSSPlugin = require('purifycss-webpack')
module.exports = function (options) {
- const isProd = options.env === 'production'
+ const isProd = options && options.env === 'production'
const configuration = {
entry: {
import { Video as VideoServerModel, VideoFile } from '../../../../../shared'
import { User } from '../../shared'
+import { VideoResolution } from '../../../../../shared/models/videos/video-resolution.enum'
export class Video implements VideoServerModel {
author: string
return (this.nsfw && (!user || user.displayNSFW === false))
}
- getDefaultMagnetUri () {
+ getAppropriateMagnetUri (actualDownloadSpeed = 0) {
if (this.files === undefined || this.files.length === 0) return ''
+ if (this.files.length === 1) return this.files[0].magnetUri
- // TODO: choose the original file
- 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) {
export * from './video-share.component'
export * from './video-report.component'
export * from './video-watch.component'
-export * from './webtorrent.service'
</div>
<div class="modal-body">
- <input #magnetUriInput (click)="magnetUriInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="video.getDefaultMagnetUri()" />
+ <div *ngFor="let file of video.files">
+ <label>{{ file.resolutionLabel }}</label>
+ <input #magnetUriInput (click)="magnetUriInput.select()" type="text" class="form-control input-sm readonly" readonly [value]="file.magnetUri" />
+ </div>
</div>
</div>
</div>
import { Subscription } from 'rxjs/Subscription'
import videojs from 'video.js'
+import '../../../assets/player/peertube-videojs-plugin'
+
import { MetaService } from '@ngx-meta/core'
import { NotificationsService } from 'angular2-notifications'
import { VideoReportComponent } from './video-report.component'
import { Video, VideoService } from '../shared'
import { WebTorrentService } from './webtorrent.service'
-import { UserVideoRateType, VideoRateType, UserVideoRate } from '../../../../../shared'
+import { UserVideoRateType, VideoRateType } from '../../../../../shared'
@Component({
selector: 'my-video-watch',
styleUrls: [ './video-watch.component.scss' ]
})
export class VideoWatchComponent implements OnInit, OnDestroy {
- private static LOADTIME_TOO_LONG = 20000
-
@ViewChild('videoMagnetModal') videoMagnetModal: VideoMagnetComponent
@ViewChild('videoShareModal') videoShareModal: VideoShareComponent
@ViewChild('videoReportModal') videoReportModal: VideoReportComponent
video: Video = null
videoNotFound = false
- private errorTimer: number
private paramsSub: Subscription
- private errorsSub: Subscription
- private torrentInfosInterval: number
constructor (
private elementRef: ElementRef,
- private ngZone: NgZone,
private route: ActivatedRoute,
private router: Router,
private videoService: VideoService,
private confirmService: ConfirmService,
private metaService: MetaService,
- private webTorrentService: WebTorrentService,
private authService: AuthService,
private notificationsService: NotificationsService
) {}
}
)
})
-
- this.playerElement = this.elementRef.nativeElement.querySelector('#video-container')
-
- const videojsOptions = {
- controls: true,
- autoplay: true
- }
-
- const self = this
- videojs(this.playerElement, videojsOptions, function () {
- self.player = this
- })
-
- this.errorsSub = this.webTorrentService.errors.subscribe(err => this.handleError(err))
}
ngOnDestroy () {
// Remove WebTorrent stuff
console.log('Removing video from webtorrent.')
- window.clearInterval(this.torrentInfosInterval)
- window.clearTimeout(this.errorTimer)
-
- if (this.video !== null && this.webTorrentService.has(this.video.getDefaultMagnetUri())) {
- this.webTorrentService.remove(this.video.getDefaultMagnetUri())
- }
// Remove player
videojs(this.playerElement).dispose()
// Unsubscribe subscriptions
this.paramsSub.unsubscribe()
- this.errorsSub.unsubscribe()
- }
-
- loadVideo () {
- // Reset the error
- this.error = false
- // We are loading the video
- this.loading = true
-
- console.log('Adding ' + this.video.getDefaultMagnetUri() + '.')
-
- // The callback might never return if there are network issues
- // So we create a timer to inform the user the load is abnormally long
- this.errorTimer = window.setTimeout(() => this.loadTooLong(), VideoWatchComponent.LOADTIME_TOO_LONG)
-
- const torrent = this.webTorrentService.add(this.video.getDefaultMagnetUri(), torrent => {
- // Clear the error timer
- window.clearTimeout(this.errorTimer)
- // Maybe the error was fired by the timer, so reset it
- this.error = false
-
- // We are not loading the video anymore
- this.loading = false
-
- console.log('Added ' + this.video.getDefaultMagnetUri() + '.')
- torrent.files[0].renderTo(this.playerElement, (err) => {
- if (err) {
- this.notificationsService.error('Error', 'Cannot append the file in the video element.')
- console.error(err)
- }
-
- // Hack to "simulate" src link in video.js >= 6
- // If no, we can't play the video after pausing it
- // https://github.com/videojs/video.js/blob/master/src/js/player.js#L1633
- (this.player as any).src = () => true
-
- this.player.play()
- })
-
- this.runInProgress(torrent)
- })
-
- torrent.on('error', err => this.handleError(err))
- torrent.on('warning', err => this.handleError(err))
}
setLike () {
return this.router.navigate([ '/videos/list' ])
}
+ this.playerElement = this.elementRef.nativeElement.querySelector('#video-container')
+
+ const videojsOptions = {
+ controls: true,
+ autoplay: true,
+ plugins: {
+ peertube: {
+ videoFiles: this.video.files,
+ playerElement: this.playerElement,
+ autoplay: true,
+ peerTubeLink: false
+ }
+ }
+ }
+
+ const self = this
+ videojs(this.playerElement, videojsOptions, function () {
+ self.player = this
+ this.on('customError', (event, data) => {
+ self.handleError(data.err)
+ })
+
+ this.on('torrentInfo', (event, data) => {
+ self.downloadSpeed = data.downloadSpeed
+ self.numPeers = data.numPeers
+ self.uploadSpeed = data.uploadSpeed
+ })
+ })
+
this.setOpenGraphTags()
- this.loadVideo()
this.checkUserRating()
}
)
this.video.dislikes += dislikesToIncrement
}
- private loadTooLong () {
- this.error = true
- console.error('The video load seems to be abnormally long.')
- }
-
private setOpenGraphTags () {
this.metaService.setTitle(this.video.name)
this.metaService.setTag('og:url', window.location.href)
this.metaService.setTag('url', window.location.href)
}
-
- private runInProgress (torrent: any) {
- // Refresh each second
- this.torrentInfosInterval = window.setInterval(() => {
- this.ngZone.run(() => {
- this.downloadSpeed = torrent.downloadSpeed
- this.numPeers = torrent.numPeers
- this.uploadSpeed = torrent.uploadSpeed
- })
- }, 1000)
- }
}
+++ /dev/null
-import { Injectable } from '@angular/core'
-import { Subject } from 'rxjs/Subject'
-
-import * as WebTorrent from 'webtorrent'
-
-@Injectable()
-export class WebTorrentService {
- errors = new Subject<string | Error>()
-
- private client: WebTorrent.Instance
-
- constructor () {
- this.client = new WebTorrent({ dht: false })
-
- this.client.on('error', err => this.errors.next(err))
- }
-
- add (magnetUri: string, callback: (torrent: WebTorrent.Torrent) => any) {
- return this.client.add(magnetUri, callback)
- }
-
- remove (magnetUri: string) {
- return this.client.remove(magnetUri)
- }
-
- has (magnetUri: string) {
- return this.client.get(magnetUri) !== null
- }
-}
VideoWatchComponent,
VideoMagnetComponent,
VideoReportComponent,
- VideoShareComponent,
- WebTorrentService
+ VideoShareComponent
} from './video-watch'
import { VideoService } from './shared'
import { SharedModule } from '../shared'
],
providers: [
- VideoService,
- WebTorrentService
+ VideoService
]
})
export class VideosModule { }
--- /dev/null
+// Big thanks to: https://github.com/kmoskwiak/videojs-resolution-switcher
+
+import videojs, { Player } from 'video.js'
+import * as WebTorrent from 'webtorrent'
+
+import { renderVideo } from './video-renderer'
+import { VideoFile } from '../../../../shared'
+
+// videojs typings don't have some method we need
+const videojsUntyped = videojs as any
+const webtorrent = new WebTorrent({ dht: false })
+
+const MenuItem = videojsUntyped.getComponent('MenuItem')
+const ResolutionMenuItem = videojsUntyped.extend(MenuItem, {
+ constructor: function (player: Player, options) {
+ options.selectable = true
+ MenuItem.call(this, player, options)
+
+ const currentResolution = this.player_.getCurrentResolution()
+ this.selected(this.options_.id === currentResolution)
+ },
+
+ handleClick: function (event) {
+ MenuItem.prototype.handleClick.call(this, event)
+ this.player_.updateResolution(this.options_.id)
+ }
+})
+MenuItem.registerComponent('ResolutionMenuItem', ResolutionMenuItem)
+
+const MenuButton = videojsUntyped.getComponent('MenuButton')
+const ResolutionMenuButton = videojsUntyped.extend(MenuButton, {
+ constructor: function (player, options) {
+ this.label = document.createElement('span')
+ options.label = 'Quality'
+
+ MenuButton.call(this, player, options)
+ this.el().setAttribute('aria-label', 'Quality')
+ this.controlText('Quality')
+
+ videojsUntyped.dom.addClass(this.label, 'vjs-resolution-button-label')
+ this.el().appendChild(this.label)
+
+ player.on('videoFileUpdate', videojs.bind(this, this.update))
+ },
+
+ createItems: function () {
+ const menuItems = []
+ for (const videoFile of this.player_.videoFiles) {
+ menuItems.push(new ResolutionMenuItem(
+ this.player_,
+ {
+ id: videoFile.resolution,
+ label: videoFile.resolutionLabel,
+ src: videoFile.magnetUri,
+ selected: videoFile.resolution === this.currentSelection
+ })
+ )
+ }
+
+ return menuItems
+ },
+
+ update: function () {
+ this.label.innerHTML = this.player_.getCurrentResolutionLabel()
+ return MenuButton.prototype.update.call(this)
+ },
+
+ buildCSSClass: function () {
+ return MenuButton.prototype.buildCSSClass.call(this) + ' vjs-resolution-button'
+ }
+})
+MenuButton.registerComponent('ResolutionMenuButton', ResolutionMenuButton)
+
+const Button = videojsUntyped.getComponent('Button')
+const PeertubeLinkButton = videojsUntyped.extend(Button, {
+ constructor: function (player) {
+ Button.apply(this, arguments)
+ this.player = player
+ },
+
+ createEl: function () {
+ const link = document.createElement('a')
+ link.href = window.location.href.replace('embed', 'watch')
+ link.innerHTML = 'PeerTube'
+ link.title = 'Go to the video page'
+ link.className = 'vjs-peertube-link'
+ link.target = '_blank'
+
+ return link
+ },
+
+ handleClick: function () {
+ this.player.pause()
+ }
+})
+Button.registerComponent('PeerTubeLinkButton', PeertubeLinkButton)
+
+type PeertubePluginOptions = {
+ videoFiles: VideoFile[]
+ playerElement: HTMLVideoElement
+ autoplay: boolean
+ peerTubeLink: boolean
+}
+const peertubePlugin = function (options: PeertubePluginOptions) {
+ const player = this
+ let currentVideoFile: VideoFile = undefined
+ const playerElement = options.playerElement
+ player.videoFiles = options.videoFiles
+
+ // Hack to "simulate" src link in video.js >= 6
+ // Without this, we can't play the video after pausing it
+ // https://github.com/videojs/video.js/blob/master/src/js/player.js#L1633
+ player.src = function () {
+ return true
+ }
+
+ player.getCurrentResolution = function () {
+ return currentVideoFile ? currentVideoFile.resolution : -1
+ }
+
+ player.getCurrentResolutionLabel = function () {
+ return currentVideoFile ? currentVideoFile.resolutionLabel : ''
+ }
+
+ player.updateVideoFile = function (videoFile: VideoFile, done: () => void) {
+ if (done === undefined) {
+ done = () => { /* empty */ }
+ }
+
+ // Pick the first one
+ if (videoFile === undefined) {
+ videoFile = player.videoFiles[0]
+ }
+
+ // Don't add the same video file once again
+ if (currentVideoFile !== undefined && currentVideoFile.magnetUri === videoFile.magnetUri) {
+ return
+ }
+
+ const previousVideoFile = currentVideoFile
+ currentVideoFile = videoFile
+
+ console.log('Adding ' + videoFile.magnetUri + '.')
+ player.torrent = webtorrent.add(videoFile.magnetUri, torrent => {
+ console.log('Added ' + videoFile.magnetUri + '.')
+
+ this.flushVideoFile(previousVideoFile)
+
+ const options = { autoplay: true, controls: true }
+ renderVideo(torrent.files[0], playerElement, options,(err, renderer) => {
+ if (err) return handleError(err)
+
+ this.renderer = renderer
+ player.play()
+
+ return done()
+ })
+ })
+
+ player.torrent.on('error', err => handleError(err))
+ player.torrent.on('warning', err => handleError(err))
+
+ player.trigger('videoFileUpdate')
+
+ return player
+ }
+
+ player.updateResolution = function (resolution) {
+ // Remember player state
+ const currentTime = player.currentTime()
+ const isPaused = player.paused()
+
+ // Hide bigPlayButton
+ if (!isPaused && this.player_.options_.bigPlayButton) {
+ this.player_.bigPlayButton.hide()
+ }
+
+ const newVideoFile = player.videoFiles.find(f => f.resolution === resolution)
+ player.updateVideoFile(newVideoFile, () => {
+ player.currentTime(currentTime)
+ player.handleTechSeeked_()
+ })
+ }
+
+ player.flushVideoFile = function (videoFile: VideoFile, destroyRenderer = true) {
+ if (videoFile !== undefined && webtorrent.get(videoFile.magnetUri)) {
+ if (destroyRenderer === true) this.renderer.destroy()
+ webtorrent.remove(videoFile.magnetUri)
+ }
+ }
+
+ player.ready(function () {
+ const controlBar = player.controlBar
+
+ const menuButton = new ResolutionMenuButton(player, options)
+ const fullscreenElement = controlBar.fullscreenToggle.el()
+ controlBar.resolutionSwitcher = controlBar.el().insertBefore(menuButton.el(), fullscreenElement)
+ controlBar.resolutionSwitcher.dispose = function () {
+ this.parentNode.removeChild(this)
+ }
+
+ player.dispose = function () {
+ // Don't need to destroy renderer, video player will be destroyed
+ player.flushVideoFile(currentVideoFile, false)
+ }
+
+ if (options.peerTubeLink === true) {
+ const peerTubeLinkButton = new PeertubeLinkButton(player)
+ controlBar.peerTubeLink = controlBar.el().insertBefore(peerTubeLinkButton.el(), fullscreenElement)
+
+ controlBar.peerTubeLink.dispose = function () {
+ this.parentNode.removeChild(this)
+ }
+ }
+
+ if (options.autoplay === true) {
+ player.updateVideoFile()
+ } else {
+ player.one('play', () => player.updateVideoFile())
+ }
+
+ setInterval(() => {
+ if (player.torrent !== undefined) {
+ player.trigger('torrentInfo', {
+ downloadSpeed: player.torrent.downloadSpeed,
+ numPeers: player.torrent.numPeers,
+ uploadSpeed: player.torrent.uploadSpeed
+ })
+ }
+ }, 1000)
+ })
+
+ function handleError (err: Error|string) {
+ return player.trigger('customError', { err })
+ }
+}
+
+videojsUntyped.registerPlugin('peertube', peertubePlugin)
--- /dev/null
+// Thanks: https://github.com/feross/render-media
+// TODO: use render-media once https://github.com/feross/render-media/issues/32 is fixed
+
+import { extname } from 'path'
+import * as MediaElementWrapper from 'mediasource'
+import * as videostream from 'videostream'
+
+const VIDEOSTREAM_EXTS = [
+ '.m4a',
+ '.m4v',
+ '.mp4'
+]
+
+type RenderMediaOptions = {
+ controls: boolean
+ autoplay: boolean
+}
+
+function renderVideo (
+ file,
+ elem: HTMLVideoElement,
+ opts: RenderMediaOptions,
+ callback: (err: Error, renderer: any) => void
+) {
+ validateFile(file)
+
+ return renderMedia(file, elem, opts, callback)
+}
+
+function renderMedia (file, elem: HTMLVideoElement, opts: RenderMediaOptions, callback: (err: Error, renderer: any) => void) {
+ const extension = extname(file.name).toLowerCase()
+ let preparedElem = undefined
+ let currentTime = 0
+ let renderer
+
+ if (VIDEOSTREAM_EXTS.indexOf(extension) >= 0) {
+ renderer = useVideostream()
+ } else {
+ renderer = useMediaSource()
+ }
+
+ function useVideostream () {
+ prepareElem()
+ preparedElem.addEventListener('error', fallbackToMediaSource)
+ preparedElem.addEventListener('loadstart', onLoadStart)
+ preparedElem.addEventListener('canplay', onCanPlay)
+ return videostream(file, preparedElem)
+ }
+
+ function useMediaSource () {
+ prepareElem()
+ preparedElem.addEventListener('error', callback)
+ preparedElem.addEventListener('loadstart', onLoadStart)
+ preparedElem.addEventListener('canplay', onCanPlay)
+
+ const wrapper = new MediaElementWrapper(preparedElem)
+ const writable = wrapper.createWriteStream(getCodec(file.name))
+ file.createReadStream().pipe(writable)
+
+ if (currentTime) preparedElem.currentTime = currentTime
+
+ return wrapper
+ }
+
+ function fallbackToMediaSource () {
+ preparedElem.removeEventListener('error', fallbackToMediaSource)
+ preparedElem.removeEventListener('canplay', onCanPlay)
+
+ useMediaSource()
+ }
+
+ function prepareElem () {
+ if (preparedElem === undefined) {
+ preparedElem = elem
+
+ preparedElem.addEventListener('progress', function () {
+ currentTime = elem.currentTime
+ })
+ }
+ }
+
+ function onLoadStart () {
+ preparedElem.removeEventListener('loadstart', onLoadStart)
+ if (opts.autoplay) preparedElem.play()
+ }
+
+ function onCanPlay () {
+ preparedElem.removeEventListener('canplay', onCanPlay)
+ callback(null, renderer)
+ }
+}
+
+function validateFile (file) {
+ if (file == null) {
+ throw new Error('file cannot be null or undefined')
+ }
+ if (typeof file.name !== 'string') {
+ throw new Error('missing or invalid file.name property')
+ }
+ if (typeof file.createReadStream !== 'function') {
+ throw new Error('missing or invalid file.createReadStream property')
+ }
+}
+
+function getCodec (name: string) {
+ const ext = extname(name).toLowerCase()
+ return {
+ '.m4a': 'audio/mp4; codecs="mp4a.40.5"',
+ '.m4v': 'video/mp4; codecs="avc1.640029, mp4a.40.5"',
+ '.mkv': 'video/webm; codecs="avc1.640029, mp4a.40.5"',
+ '.mp3': 'audio/mpeg',
+ '.mp4': 'video/mp4; codecs="avc1.640029, mp4a.40.5"',
+ '.webm': 'video/webm; codecs="vorbis, vp8"'
+ }[ext]
+}
+
+export {
+ renderVideo
+}
+// Thanks: https://github.com/kmoskwiak/videojs-resolution-switcher/pull/92/files
+.vjs-resolution-button-label {
+ font-size: 1em;
+ line-height: 3em;
+ position: absolute;
+ top: 0;
+ left: -1px;
+ width: 100%;
+ height: 100%;
+ text-align: center;
+ box-sizing: inherit;
+}
+
+.vjs-resolution-button {
+ outline: 0 !important;
+
+ .vjs-menu {
+ .vjs-menu-content {
+ width: 4em;
+ left: 50%; /* Center the menu, in it's parent */
+ margin-left: -2em; /* half of width, to center */
+ }
+
+ li {
+ text-transform: none;
+ font-size: 1em;
+ }
+ }
+}
+
// Thanks: https://github.com/zanechua/videojs-sublime-inspired-skin
// Video JS Sublime Skin
width: 6em;
position: absolute;
right: 0;
- margin-right: 30px;
+ margin-right: 65px;
}
.vjs-sublime-skin .vjs-volume-menu-button .vjs-menu-content,
line-height: 2.20;
transition: all .4s;
position: relative;
- right: 6px;
+ right: 8px;
+}
+
+.vjs-resolution-button-label {
+ left: -7px;
}
.vjs-peertube-link:hover {
// Fix volume panel because we added a new component (PeerTube link)
.vjs-volume-panel {
- margin-right: 90px !important;
+ margin-right: 130px !important;
}
import './embed.scss'
import videojs from 'video.js'
+import '../../assets/player/peertube-videojs-plugin'
import 'videojs-dock/dist/videojs-dock.es.js'
-import * as WebTorrent from 'webtorrent'
import { Video } from '../../../../shared'
-// videojs typings don't have some method we need
-const videojsUntyped = videojs as any
-
-function loadVideoInfos (videoId: string, callback: (err: Error, res?: Video) => void) {
+function loadVideoInfo (videoId: string, callback: (err: Error, res?: Video) => void) {
const xhttp = new XMLHttpRequest()
xhttp.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) {
xhttp.send()
}
-function loadVideoTorrent (magnetUri: string, player: videojs.Player) {
- console.log('Loading video ' + videoId)
- const client = new WebTorrent()
-
- console.log('Adding magnet ' + magnetUri)
- client.add(magnetUri, torrent => {
- const file = torrent.files[0]
-
- file.renderTo('video', err => {
- if (err) {
- console.error(err)
- return
- }
-
- // Hack to "simulate" src link in video.js >= 6
- // If no, we can't play the video after pausing it
- // https://github.com/videojs/video.js/blob/master/src/js/player.js#L1633
- (player as any).src = () => true
-
- player.play()
- })
- })
-}
-
const urlParts = window.location.href.split('/')
const videoId = urlParts[urlParts.length - 1]
-loadVideoInfos(videoId, (err, videoInfos) => {
+loadVideoInfo(videoId, (err, videoInfo) => {
if (err) {
console.error(err)
return
}
- let magnetUri = ''
- if (videoInfos.files !== undefined && videoInfos.files.length !== 0) {
- magnetUri = videoInfos.files[0].magnetUri
+ const videoElement = document.getElementById('video-container') as HTMLVideoElement
+ const previewUrl = window.location.origin + videoInfo.previewPath
+ videoElement.poster = previewUrl
+
+ const videojsOptions = {
+ controls: true,
+ autoplay: false,
+ plugins: {
+ peertube: {
+ videoFiles: videoInfo.files,
+ playerElement: videoElement,
+ autoplay: false,
+ peerTubeLink: true
+ }
+ }
}
-
- const videoContainer = document.getElementById('video-container') as HTMLVideoElement
- const previewUrl = window.location.origin + videoInfos.previewPath
- videoContainer.poster = previewUrl
-
- videojs('video-container', { controls: true, autoplay: false }, function () {
+ videojs('video-container', videojsOptions, function () {
const player = this
- const Button = videojsUntyped.getComponent('Button')
- const peertubeLinkButton = videojsUntyped.extend(Button, {
- constructor: function () {
- Button.apply(this, arguments)
- },
-
- createEl: function () {
- const link = document.createElement('a')
- link.href = window.location.href.replace('embed', 'watch')
- link.innerHTML = 'PeerTube'
- link.title = 'Go to the video page'
- link.className = 'vjs-peertube-link'
- link.target = '_blank'
-
- return link
- },
-
- handleClick: function () {
- player.pause()
- }
- })
- videojsUntyped.registerComponent('PeerTubeLinkButton', peertubeLinkButton)
-
- const controlBar = player.getChild('controlBar')
- const addedLink = controlBar.addChild('PeerTubeLinkButton', {})
- controlBar.el().insertBefore(addedLink.el(), controlBar.fullscreenToggle.el())
-
player.dock({
- title: videoInfos.name
+ title: videoInfo.name
})
-
- document.querySelector('.vjs-big-play-button').addEventListener('click', () => {
- loadVideoTorrent(magnetUri, player)
- }, false)
})
})
signup:
limit: 4
+
+transcoding:
+ enabled: false
admin:
email: 'admin3@example.com'
+
+transcoding:
+ enabled: false
admin:
email: 'admin4@example.com'
+
+transcoding:
+ enabled: false
admin:
email: 'admin5@example.com'
+
+transcoding:
+ enabled: false
admin:
email: 'admin6@example.com'
+
+transcoding:
+ enabled: false
signup:
enabled: true
+
+transcoding:
+ enabled: true
+ threads: 4
files: []
}
- this.VideoFiles.forEach(videoFile => {
- let resolutionLabel = VIDEO_FILE_RESOLUTIONS[videoFile.resolution]
- if (!resolutionLabel) resolutionLabel = 'Unknown'
-
- const videoFileJson = {
- resolution: videoFile.resolution,
- resolutionLabel,
- magnetUri: this.generateMagnetUri(videoFile),
- size: videoFile.size
- }
-
- json.files.push(videoFileJson)
- })
+ // Format and sort video files
+ json.files = this.VideoFiles
+ .map(videoFile => {
+ let resolutionLabel = VIDEO_FILE_RESOLUTIONS[videoFile.resolution]
+ if (!resolutionLabel) resolutionLabel = 'Unknown'
+
+ const videoFileJson = {
+ resolution: videoFile.resolution,
+ resolutionLabel,
+ magnetUri: this.generateMagnetUri(videoFile),
+ size: videoFile.size
+ }
+
+ return videoFileJson
+ })
+ .sort((a, b) => {
+ if (a.resolution < b.resolution) return 1
+ if (a.resolution === b.resolution) return 0
+ return -1
+ })
return json
}
const originalFile = video.files.find(f => f.resolution === 0)
expect(originalFile).not.to.be.undefined
expect(originalFile.resolutionLabel).to.equal('original')
- expect(originalFile.size).to.equal(711327)
+ expect(originalFile.size).to.be.above(700000).and.below(720000)
const file240p = video.files.find(f => f.resolution === 240)
expect(file240p).not.to.be.undefined
expect(file240p.resolutionLabel).to.equal('240p')
- expect(file240p.size).to.equal(139953)
+ expect(file240p.size).to.be.above(130000).and.below(150000)
const file360p = video.files.find(f => f.resolution === 360)
expect(file360p).not.to.be.undefined
expect(file360p.resolutionLabel).to.equal('360p')
- expect(file360p.size).to.equal(169926)
+ expect(file360p.size).to.be.above(160000).and.below(180000)
const file480p = video.files.find(f => f.resolution === 480)
expect(file480p).not.to.be.undefined
expect(file480p.resolutionLabel).to.equal('480p')
- expect(file480p.size).to.equal(206758)
+ expect(file480p.size).to.be.above(200000).and.below(220000)
const file720p = video.files.find(f => f.resolution === 720)
expect(file720p).not.to.be.undefined
expect(file720p.resolutionLabel).to.equal('720p')
- expect(file720p.size).to.equal(314913)
+ expect(file720p.size).to.be.above(310000).and.below(320000)
const test = await testVideoImage(server.url, 'video_short2.webm', video.thumbnailPath)
expect(test).to.equal(true)