Prefer web videos in favour of webtorrent
This commit is contained in:
parent
c3030e944a
commit
784e2ad5c3
|
@ -116,7 +116,7 @@ Be it as a user or an instance administrator, you can decide what your experienc
|
||||||
|
|
||||||
<h3 align="right">Communities that help each other</h3>
|
<h3 align="right">Communities that help each other</h3>
|
||||||
<p align="right">
|
<p align="right">
|
||||||
In addition to visitors using WebTorrent to share the load among them, instances can help each other by caching one another's videos. This way even small instances have a way to show content to a wider audience, as they will be shouldered by friend instances (more about that in our <a href="https://docs.joinpeertube.org/contribute/architecture#redundancy-between-instances">redundancy guide</a>).
|
In addition to visitors using P2P with WebRTC to share the load among them, instances can help each other by caching one another's videos. This way even small instances have a way to show content to a wider audience, as they will be shouldered by friend instances (more about that in our <a href="https://docs.joinpeertube.org/contribute/architecture#redundancy-between-instances">redundancy guide</a>).
|
||||||
</p>
|
</p>
|
||||||
<p align="right">
|
<p align="right">
|
||||||
Content creators can get help from their viewers in the simplest way possible: a support button showing a message linking to their donation accounts or really anything else. No more pay-per-view and advertisements that hurt visitors and alter creativity (more about that in our <a href="https://github.com/Chocobozzz/PeerTube/blob/develop/FAQ.md">FAQ</a>).
|
Content creators can get help from their viewers in the simplest way possible: a support button showing a message linking to their donation accounts or really anything else. No more pay-per-view and advertisements that hurt visitors and alter creativity (more about that in our <a href="https://github.com/Chocobozzz/PeerTube/blob/develop/FAQ.md">FAQ</a>).
|
||||||
|
|
|
@ -31,8 +31,8 @@ describe('Private videos all workflow', () => {
|
||||||
return loginPage.loginOnPeerTube2()
|
return loginPage.loginOnPeerTube2()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should play an internal webtorrent video', async () => {
|
it('Should play an internal web video video', async () => {
|
||||||
await go(FIXTURE_URLS.INTERNAL_WEBTORRENT_VIDEO)
|
await go(FIXTURE_URLS.INTERNAL_WEB_VIDEO)
|
||||||
|
|
||||||
await videoWatchPage.waitWatchVideoName(internalVideoName)
|
await videoWatchPage.waitWatchVideoName(internalVideoName)
|
||||||
await checkCorrectlyPlay(playerPage)
|
await checkCorrectlyPlay(playerPage)
|
||||||
|
@ -52,8 +52,8 @@ describe('Private videos all workflow', () => {
|
||||||
await checkCorrectlyPlay(playerPage)
|
await checkCorrectlyPlay(playerPage)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should play an internal WebTorrent video in embed', async () => {
|
it('Should play an internal Web Video in embed', async () => {
|
||||||
await go(FIXTURE_URLS.INTERNAL_EMBED_WEBTORRENT_VIDEO)
|
await go(FIXTURE_URLS.INTERNAL_EMBED_WEB_VIDEO)
|
||||||
|
|
||||||
await videoWatchPage.waitEmbedForDisplayed()
|
await videoWatchPage.waitEmbedForDisplayed()
|
||||||
await checkCorrectlyPlay(playerPage)
|
await checkCorrectlyPlay(playerPage)
|
||||||
|
|
|
@ -89,7 +89,7 @@ describe('Videos all workflow', () => {
|
||||||
let videoNameToExcept = videoName
|
let videoNameToExcept = videoName
|
||||||
|
|
||||||
if (isMobileDevice() || isSafari()) {
|
if (isMobileDevice() || isSafari()) {
|
||||||
await go(FIXTURE_URLS.WEBTORRENT_VIDEO)
|
await go(FIXTURE_URLS.WEB_VIDEO)
|
||||||
videoNameToExcept = 'E2E tests'
|
videoNameToExcept = 'E2E tests'
|
||||||
} else {
|
} else {
|
||||||
await videoListPage.clickOnVideo(videoName)
|
await videoListPage.clickOnVideo(videoName)
|
||||||
|
@ -176,7 +176,7 @@ describe('Videos all workflow', () => {
|
||||||
await videoWatchPage.waitUntilVideoName(video2Name, 40 * 1000)
|
await videoWatchPage.waitUntilVideoName(video2Name, 40 * 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should watch the webtorrent playlist in the embed', async () => {
|
it('Should watch the WEB VIDEO playlist in the embed', async () => {
|
||||||
if (isUploadUnsupported()) return
|
if (isUploadUnsupported()) return
|
||||||
|
|
||||||
const accessToken = await browser.execute(`return window.localStorage.getItem('access_token');`)
|
const accessToken = await browser.execute(`return window.localStorage.getItem('access_token');`)
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
const FIXTURE_URLS = {
|
const FIXTURE_URLS = {
|
||||||
INTERNAL_WEBTORRENT_VIDEO: 'https://peertube2.cpy.re/w/pwfz7NizSdPD4mJcbbmNwa?mode=webtorrent&start=0',
|
INTERNAL_WEB_VIDEO: 'https://peertube2.cpy.re/w/pwfz7NizSdPD4mJcbbmNwa?mode=web-video&start=0',
|
||||||
INTERNAL_HLS_VIDEO: 'https://peertube2.cpy.re/w/pwfz7NizSdPD4mJcbbmNwa?start=0',
|
INTERNAL_HLS_VIDEO: 'https://peertube2.cpy.re/w/pwfz7NizSdPD4mJcbbmNwa?start=0',
|
||||||
|
|
||||||
INTERNAL_EMBED_WEBTORRENT_VIDEO: 'https://peertube2.cpy.re/videos/embed/pwfz7NizSdPD4mJcbbmNwa?mode=webtorrent&start=0',
|
INTERNAL_EMBED_WEB_VIDEO: 'https://peertube2.cpy.re/videos/embed/pwfz7NizSdPD4mJcbbmNwa?mode=web-video&start=0',
|
||||||
INTERNAL_EMBED_HLS_VIDEO: 'https://peertube2.cpy.re/videos/embed/pwfz7NizSdPD4mJcbbmNwa?start=0',
|
INTERNAL_EMBED_HLS_VIDEO: 'https://peertube2.cpy.re/videos/embed/pwfz7NizSdPD4mJcbbmNwa?start=0',
|
||||||
|
|
||||||
INTERNAL_HLS_ONLY_VIDEO: 'https://peertube2.cpy.re/w/tKQmHcqdYZRdCszLUiWM3V?start=0',
|
INTERNAL_HLS_ONLY_VIDEO: 'https://peertube2.cpy.re/w/tKQmHcqdYZRdCszLUiWM3V?start=0',
|
||||||
INTERNAL_EMBED_HLS_ONLY_VIDEO: 'https://peertube2.cpy.re/videos/embed/tKQmHcqdYZRdCszLUiWM3V?start=0',
|
INTERNAL_EMBED_HLS_ONLY_VIDEO: 'https://peertube2.cpy.re/videos/embed/tKQmHcqdYZRdCszLUiWM3V?start=0',
|
||||||
|
|
||||||
WEBTORRENT_VIDEO: 'https://peertube2.cpy.re/w/122d093a-1ede-43bd-bd34-59d2931ffc5e',
|
WEB_VIDEO: 'https://peertube2.cpy.re/w/122d093a-1ede-43bd-bd34-59d2931ffc5e',
|
||||||
|
|
||||||
HLS_EMBED: 'https://peertube2.cpy.re/videos/embed/969bf103-7818-43b5-94a0-de159e13de50',
|
HLS_EMBED: 'https://peertube2.cpy.re/videos/embed/969bf103-7818-43b5-94a0-de159e13de50',
|
||||||
HLS_PLAYLIST_EMBED: 'https://peertube2.cpy.re/video-playlists/embed/73804a40-da9a-40c2-b1eb-2c6d9eec8f0a',
|
HLS_PLAYLIST_EMBED: 'https://peertube2.cpy.re/video-playlists/embed/73804a40-da9a-40c2-b1eb-2c6d9eec8f0a',
|
||||||
|
|
|
@ -24,19 +24,19 @@ module.exports = {
|
||||||
specFileRetries: 0,
|
specFileRetries: 0,
|
||||||
|
|
||||||
capabilities: [
|
capabilities: [
|
||||||
{
|
// {
|
||||||
'browserName': 'chrome',
|
// 'browserName': 'chrome',
|
||||||
'acceptInsecureCerts': true,
|
// 'acceptInsecureCerts': true,
|
||||||
'goog:chromeOptions': {
|
// 'goog:chromeOptions': {
|
||||||
args: [ '--disable-gpu', windowSizeArg ],
|
// args: [ '--disable-gpu', windowSizeArg ],
|
||||||
prefs
|
// prefs
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
'browserName': 'firefox',
|
'browserName': 'firefox',
|
||||||
'moz:firefoxOptions': {
|
'moz:firefoxOptions': {
|
||||||
binary: '/usr/bin/firefox-developer-edition',
|
binary: '/usr/bin/firefox-developer-edition',
|
||||||
args: [ '--headless', windowSizeArg ],
|
// args: [ '--headless', windowSizeArg ],
|
||||||
|
|
||||||
prefs
|
prefs
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@
|
||||||
<div class="form-group" [ngClass]="getTranscodingDisabledClass()">
|
<div class="form-group" [ngClass]="getTranscodingDisabledClass()">
|
||||||
<my-peertube-checkbox
|
<my-peertube-checkbox
|
||||||
inputName="transcodingWebTorrentEnabled" formControlName="enabled"
|
inputName="transcodingWebTorrentEnabled" formControlName="enabled"
|
||||||
i18n-labelText labelText="WebTorrent enabled"
|
i18n-labelText labelText="Web Videos enabled"
|
||||||
>
|
>
|
||||||
<ng-template ptTemplate="help">
|
<ng-template ptTemplate="help">
|
||||||
<ng-container>
|
<ng-container>
|
||||||
|
@ -93,14 +93,14 @@
|
||||||
<ng-container i18n>
|
<ng-container i18n>
|
||||||
<strong>Requires ffmpeg >= 4.1</strong>
|
<strong>Requires ffmpeg >= 4.1</strong>
|
||||||
|
|
||||||
<p>Generate HLS playlists and fragmented MP4 files resulting in a better playback than with plain WebTorrent:</p>
|
<p>Generate HLS playlists and fragmented MP4 files resulting in a better playback than with Web Videos:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Resolution change is smoother</li>
|
<li>Resolution change is smoother</li>
|
||||||
<li>Faster playback especially with long videos</li>
|
<li>Faster playback especially with long videos</li>
|
||||||
<li>More stable playback (less bugs/infinite loading)</li>
|
<li>More stable playback (less bugs/infinite loading)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>If you also enabled WebTorrent support, it will multiply videos storage by 2</p>
|
<p>If you also enabled Web Videos support, it will multiply videos storage by 2</p>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</my-peertube-checkbox>
|
</my-peertube-checkbox>
|
||||||
|
|
|
@ -59,12 +59,12 @@ export class VideoAdminService {
|
||||||
title: $localize`Video files`,
|
title: $localize`Video files`,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
value: 'webtorrent:true isLocal:true',
|
value: 'webVideos:true isLocal:true',
|
||||||
label: $localize`With WebTorrent`
|
label: $localize`With Web Videos`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'webtorrent:false isLocal:true',
|
value: 'webVideos:false isLocal:true',
|
||||||
label: $localize`Without WebTorrent`
|
label: $localize`Without Web Videos`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'hls:true isLocal:true',
|
value: 'hls:true isLocal:true',
|
||||||
|
@ -126,8 +126,8 @@ export class VideoAdminService {
|
||||||
prefix: 'hls:',
|
prefix: 'hls:',
|
||||||
isBoolean: true
|
isBoolean: true
|
||||||
},
|
},
|
||||||
hasWebtorrentFiles: {
|
hasWebVideoFiles: {
|
||||||
prefix: 'webtorrent:',
|
prefix: 'webVideos:',
|
||||||
isBoolean: true
|
isBoolean: true
|
||||||
},
|
},
|
||||||
isLive: {
|
isLive: {
|
||||||
|
|
|
@ -83,8 +83,8 @@
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<span *ngIf="isHLS(video)" class="pt-badge badge-blue">HLS</span>
|
<span *ngIf="hasHLS(video)" class="pt-badge badge-blue">HLS</span>
|
||||||
<span *ngIf="isWebTorrent(video)" class="pt-badge badge-blue">WebTorrent ({{ video.files.length }})</span>
|
<span *ngIf="hasWebVideos(video)" class="pt-badge badge-blue">Web Videos ({{ video.files.length }})</span>
|
||||||
<span i18n *ngIf="video.isLive" class="pt-badge badge-blue">Live</span>
|
<span i18n *ngIf="video.isLive" class="pt-badge badge-blue">Live</span>
|
||||||
<span i18n *ngIf="hasObjectStorage(video)" class="pt-badge badge-purple">Object storage</span>
|
<span i18n *ngIf="hasObjectStorage(video)" class="pt-badge badge-purple">Object storage</span>
|
||||||
|
|
||||||
|
@ -102,8 +102,8 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="video-info expand-cell" myAutoColspan>
|
<td class="video-info expand-cell" myAutoColspan>
|
||||||
<div>
|
<div>
|
||||||
<div *ngIf="isWebTorrent(video)">
|
<div *ngIf="hasWebVideos(video)">
|
||||||
WebTorrent:
|
Web Videos:
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li *ngFor="let file of video.files">
|
<li *ngFor="let file of video.files">
|
||||||
|
@ -112,13 +112,13 @@
|
||||||
<my-global-icon
|
<my-global-icon
|
||||||
*ngIf="canRemoveOneFile(video)"
|
*ngIf="canRemoveOneFile(video)"
|
||||||
i18n-ngbTooltip ngbTooltip="Delete this file" iconName="delete" role="button"
|
i18n-ngbTooltip ngbTooltip="Delete this file" iconName="delete" role="button"
|
||||||
(click)="removeVideoFile(video, file, 'webtorrent')"
|
(click)="removeVideoFile(video, file, 'web-videos')"
|
||||||
></my-global-icon>
|
></my-global-icon>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="isHLS(video)">
|
<div *ngIf="hasHLS(video)">
|
||||||
HLS:
|
HLS:
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -99,8 +99,8 @@ export class VideoListComponent extends RestTable <Video> implements OnInit {
|
||||||
iconName: 'cog'
|
iconName: 'cog'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: $localize`Run WebTorrent transcoding`,
|
label: $localize`Run Web Video transcoding`,
|
||||||
handler: videos => this.runTranscoding(videos, 'webtorrent'),
|
handler: videos => this.runTranscoding(videos, 'web-video'),
|
||||||
isDisplayed: videos => videos.every(v => v.canRunTranscoding(this.authUser)),
|
isDisplayed: videos => videos.every(v => v.canRunTranscoding(this.authUser)),
|
||||||
iconName: 'cog'
|
iconName: 'cog'
|
||||||
},
|
},
|
||||||
|
@ -111,8 +111,8 @@ export class VideoListComponent extends RestTable <Video> implements OnInit {
|
||||||
iconName: 'delete'
|
iconName: 'delete'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: $localize`Delete WebTorrent files`,
|
label: $localize`Delete Web Video files`,
|
||||||
handler: videos => this.removeVideoFiles(videos, 'webtorrent'),
|
handler: videos => this.removeVideoFiles(videos, 'web-videos'),
|
||||||
isDisplayed: videos => videos.every(v => v.canRemoveFiles(this.authUser)),
|
isDisplayed: videos => videos.every(v => v.canRemoveFiles(this.authUser)),
|
||||||
iconName: 'delete'
|
iconName: 'delete'
|
||||||
}
|
}
|
||||||
|
@ -150,14 +150,14 @@ export class VideoListComponent extends RestTable <Video> implements OnInit {
|
||||||
return video.state.id === VideoState.TO_IMPORT
|
return video.state.id === VideoState.TO_IMPORT
|
||||||
}
|
}
|
||||||
|
|
||||||
isHLS (video: Video) {
|
hasHLS (video: Video) {
|
||||||
const p = video.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
|
const p = video.streamingPlaylists.find(p => p.type === VideoStreamingPlaylistType.HLS)
|
||||||
if (!p) return false
|
if (!p) return false
|
||||||
|
|
||||||
return p.files.length !== 0
|
return p.files.length !== 0
|
||||||
}
|
}
|
||||||
|
|
||||||
isWebTorrent (video: Video) {
|
hasWebVideos (video: Video) {
|
||||||
return video.files.length !== 0
|
return video.files.length !== 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,14 +176,14 @@ export class VideoListComponent extends RestTable <Video> implements OnInit {
|
||||||
getFilesSize (video: Video) {
|
getFilesSize (video: Video) {
|
||||||
let files = video.files
|
let files = video.files
|
||||||
|
|
||||||
if (this.isHLS(video)) {
|
if (this.hasHLS(video)) {
|
||||||
files = files.concat(video.streamingPlaylists[0].files)
|
files = files.concat(video.streamingPlaylists[0].files)
|
||||||
}
|
}
|
||||||
|
|
||||||
return files.reduce((p, f) => p += f.size, 0)
|
return files.reduce((p, f) => p += f.size, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeVideoFile (video: Video, file: VideoFile, type: 'hls' | 'webtorrent') {
|
async removeVideoFile (video: Video, file: VideoFile, type: 'hls' | 'web-videos') {
|
||||||
const message = $localize`Are you sure you want to delete this ${file.resolution.label} file?`
|
const message = $localize`Are you sure you want to delete this ${file.resolution.label} file?`
|
||||||
const res = await this.confirmService.confirm(message, $localize`Delete file`)
|
const res = await this.confirmService.confirm(message, $localize`Delete file`)
|
||||||
if (res === false) return
|
if (res === false) return
|
||||||
|
@ -262,7 +262,7 @@ export class VideoListComponent extends RestTable <Video> implements OnInit {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async removeVideoFiles (videos: Video[], type: 'hls' | 'webtorrent') {
|
private async removeVideoFiles (videos: Video[], type: 'hls' | 'web-videos') {
|
||||||
let message: string
|
let message: string
|
||||||
|
|
||||||
if (type === 'hls') {
|
if (type === 'hls') {
|
||||||
|
@ -274,7 +274,7 @@ export class VideoListComponent extends RestTable <Video> implements OnInit {
|
||||||
} else {
|
} else {
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
message = formatICU(
|
message = formatICU(
|
||||||
$localize`Are you sure you want to delete WebTorrent files of {count, plural, =1 {1 video} other {{count} videos}}?`,
|
$localize`Are you sure you want to delete Web Video files of {count, plural, =1 {1 video} other {{count} videos}}?`,
|
||||||
{ count: videos.length }
|
{ count: videos.length }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,7 @@ export class VideoListComponent extends RestTable <Video> implements OnInit {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private runTranscoding (videos: Video[], type: 'hls' | 'webtorrent') {
|
private runTranscoding (videos: Video[], type: 'hls' | 'web-video') {
|
||||||
this.videoService.runTranscoding(videos.map(v => v.id), type)
|
this.videoService.runTranscoding(videos.map(v => v.id), type)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
|
|
|
@ -30,8 +30,6 @@ export class User implements UserServerModel {
|
||||||
autoPlayNextVideoPlaylist: boolean
|
autoPlayNextVideoPlaylist: boolean
|
||||||
|
|
||||||
p2pEnabled: boolean
|
p2pEnabled: boolean
|
||||||
// FIXME: deprecated in 4.1
|
|
||||||
webTorrentEnabled: never
|
|
||||||
|
|
||||||
videosHistoryEnabled: boolean
|
videosHistoryEnabled: boolean
|
||||||
videoLanguages: string[]
|
videoLanguages: string[]
|
||||||
|
|
|
@ -252,7 +252,7 @@ export class Video implements VideoServerModel {
|
||||||
user && user.hasRight(UserRight.MANAGE_VIDEO_FILES) &&
|
user && user.hasRight(UserRight.MANAGE_VIDEO_FILES) &&
|
||||||
this.state.id !== VideoState.TO_TRANSCODE &&
|
this.state.id !== VideoState.TO_TRANSCODE &&
|
||||||
this.hasHLS() &&
|
this.hasHLS() &&
|
||||||
this.hasWebTorrent()
|
this.hasWebVideos()
|
||||||
}
|
}
|
||||||
|
|
||||||
canRunTranscoding (user: AuthUser) {
|
canRunTranscoding (user: AuthUser) {
|
||||||
|
@ -265,7 +265,7 @@ export class Video implements VideoServerModel {
|
||||||
return this.streamingPlaylists?.some(p => p.type === VideoStreamingPlaylistType.HLS)
|
return this.streamingPlaylists?.some(p => p.type === VideoStreamingPlaylistType.HLS)
|
||||||
}
|
}
|
||||||
|
|
||||||
hasWebTorrent () {
|
hasWebVideos () {
|
||||||
return this.files && this.files.length !== 0
|
return this.files && this.files.length !== 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -311,7 +311,7 @@ export class VideoService {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeVideoFiles (videoIds: (number | string)[], type: 'hls' | 'webtorrent') {
|
removeVideoFiles (videoIds: (number | string)[], type: 'hls' | 'web-videos') {
|
||||||
return from(videoIds)
|
return from(videoIds)
|
||||||
.pipe(
|
.pipe(
|
||||||
concatMap(id => this.authHttp.delete(VideoService.BASE_VIDEO_URL + '/' + id + '/' + type)),
|
concatMap(id => this.authHttp.delete(VideoService.BASE_VIDEO_URL + '/' + id + '/' + type)),
|
||||||
|
@ -320,12 +320,12 @@ export class VideoService {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFile (videoId: number | string, fileId: number, type: 'hls' | 'webtorrent') {
|
removeFile (videoId: number | string, fileId: number, type: 'hls' | 'web-videos') {
|
||||||
return this.authHttp.delete(VideoService.BASE_VIDEO_URL + '/' + videoId + '/' + type + '/' + fileId)
|
return this.authHttp.delete(VideoService.BASE_VIDEO_URL + '/' + videoId + '/' + type + '/' + fileId)
|
||||||
.pipe(catchError(err => this.restExtractor.handleError(err)))
|
.pipe(catchError(err => this.restExtractor.handleError(err)))
|
||||||
}
|
}
|
||||||
|
|
||||||
runTranscoding (videoIds: (number | string)[], type: 'hls' | 'webtorrent') {
|
runTranscoding (videoIds: (number | string)[], type: 'hls' | 'web-video') {
|
||||||
const body: VideoTranscodingCreate = { transcodingType: type }
|
const body: VideoTranscodingCreate = { transcodingType: type }
|
||||||
|
|
||||||
return from(videoIds)
|
return from(videoIds)
|
||||||
|
|
|
@ -273,7 +273,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeVideoFiles (video: Video, type: 'hls' | 'webtorrent') {
|
async removeVideoFiles (video: Video, type: 'hls' | 'web-videos') {
|
||||||
const confirmMessage = $localize`Do you really want to remove "${this.video.name}" files?`
|
const confirmMessage = $localize`Do you really want to remove "${this.video.name}" files?`
|
||||||
|
|
||||||
const res = await this.confirmService.confirm(confirmMessage, $localize`Remove "${this.video.name}" files`)
|
const res = await this.confirmService.confirm(confirmMessage, $localize`Remove "${this.video.name}" files`)
|
||||||
|
@ -290,7 +290,7 @@ export class VideoActionsDropdownComponent implements OnChanges {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
runTranscoding (video: Video, type: 'hls' | 'webtorrent') {
|
runTranscoding (video: Video, type: 'hls' | 'web-video') {
|
||||||
this.videoService.runTranscoding([ video.id ], type)
|
this.videoService.runTranscoding([ video.id ], type)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
|
@ -394,8 +394,8 @@ export class VideoActionsDropdownComponent implements OnChanges {
|
||||||
iconName: 'cog'
|
iconName: 'cog'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: $localize`Run WebTorrent transcoding`,
|
label: $localize`Run Web Video transcoding`,
|
||||||
handler: ({ video }) => this.runTranscoding(video, 'webtorrent'),
|
handler: ({ video }) => this.runTranscoding(video, 'web-video'),
|
||||||
isDisplayed: () => this.displayOptions.transcoding && this.canRunTranscoding(),
|
isDisplayed: () => this.displayOptions.transcoding && this.canRunTranscoding(),
|
||||||
iconName: 'cog'
|
iconName: 'cog'
|
||||||
},
|
},
|
||||||
|
@ -406,8 +406,8 @@ export class VideoActionsDropdownComponent implements OnChanges {
|
||||||
iconName: 'delete'
|
iconName: 'delete'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: $localize`Delete WebTorrent files`,
|
label: $localize`Delete Web Video files`,
|
||||||
handler: ({ video }) => this.removeVideoFiles(video, 'webtorrent'),
|
handler: ({ video }) => this.removeVideoFiles(video, 'web-videos'),
|
||||||
isDisplayed: () => this.displayOptions.removeFiles && this.canRemoveVideoFiles(),
|
isDisplayed: () => this.displayOptions.removeFiles && this.canRemoveVideoFiles(),
|
||||||
iconName: 'delete'
|
iconName: 'delete'
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,7 +201,7 @@ object_storage:
|
||||||
# Useful when you want to use a CDN/external proxy
|
# Useful when you want to use a CDN/external proxy
|
||||||
base_url: '' # Example: 'https://mirror.example.com'
|
base_url: '' # Example: 'https://mirror.example.com'
|
||||||
|
|
||||||
# Same settings but for webtorrent videos
|
# Same settings but for web videos
|
||||||
videos:
|
videos:
|
||||||
bucket_name: 'videos'
|
bucket_name: 'videos'
|
||||||
prefix: ''
|
prefix: ''
|
||||||
|
@ -480,18 +480,18 @@ transcoding:
|
||||||
# Transcode and keep original resolution, even if it's above your maximum enabled resolution
|
# Transcode and keep original resolution, even if it's above your maximum enabled resolution
|
||||||
always_transcode_original_resolution: true
|
always_transcode_original_resolution: true
|
||||||
|
|
||||||
# Generate videos in a WebTorrent format (what we do since the first PeerTube release)
|
# Generate videos in a web compatible format
|
||||||
# If you also enabled the hls format, it will multiply videos storage by 2
|
# If you also enabled the hls format, it will multiply videos storage by 2
|
||||||
# If disabled, breaks federation with PeerTube instances < 2.1
|
# If disabled, breaks federation with PeerTube instances < 2.1
|
||||||
webtorrent:
|
webtorrent:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
# /!\ Requires ffmpeg >= 4.1
|
# /!\ Requires ffmpeg >= 4.1
|
||||||
# Generate HLS playlists and fragmented MP4 files. Better playback than with WebTorrent:
|
# Generate HLS playlists and fragmented MP4 files. Better playback than with Web Videos:
|
||||||
# * Resolution change is smoother
|
# * Resolution change is smoother
|
||||||
# * Faster playback in particular with long videos
|
# * Faster playback in particular with long videos
|
||||||
# * More stable playback (less bugs/infinite loading)
|
# * More stable playback (less bugs/infinite loading)
|
||||||
# If you also enabled the webtorrent format, it will multiply videos storage by 2
|
# If you also enabled the web videos format, it will multiply videos storage by 2
|
||||||
hls:
|
hls:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
|
|
|
@ -199,7 +199,7 @@ object_storage:
|
||||||
# Useful when you want to use a CDN/external proxy
|
# Useful when you want to use a CDN/external proxy
|
||||||
base_url: '' # Example: 'https://mirror.example.com'
|
base_url: '' # Example: 'https://mirror.example.com'
|
||||||
|
|
||||||
# Same settings but for webtorrent videos
|
# Same settings but for web videos
|
||||||
videos:
|
videos:
|
||||||
bucket_name: 'videos'
|
bucket_name: 'videos'
|
||||||
prefix: ''
|
prefix: ''
|
||||||
|
@ -490,18 +490,18 @@ transcoding:
|
||||||
# Transcode and keep original resolution, even if it's above your maximum enabled resolution
|
# Transcode and keep original resolution, even if it's above your maximum enabled resolution
|
||||||
always_transcode_original_resolution: true
|
always_transcode_original_resolution: true
|
||||||
|
|
||||||
# Generate videos in a WebTorrent format (what we do since the first PeerTube release)
|
# Generate videos in a web compatible format
|
||||||
# If you also enabled the hls format, it will multiply videos storage by 2
|
# If you also enabled the hls format, it will multiply videos storage by 2
|
||||||
# If disabled, breaks federation with PeerTube instances < 2.1
|
# If disabled, breaks federation with PeerTube instances < 2.1
|
||||||
webtorrent:
|
webtorrent:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
# /!\ Requires ffmpeg >= 4.1
|
# /!\ Requires ffmpeg >= 4.1
|
||||||
# Generate HLS playlists and fragmented MP4 files. Better playback than with WebTorrent:
|
# Generate HLS playlists and fragmented MP4 files. Better playback than with Web Videos:
|
||||||
# * Resolution change is smoother
|
# * Resolution change is smoother
|
||||||
# * Faster playback in particular with long videos
|
# * Faster playback in particular with long videos
|
||||||
# * More stable playback (less bugs/infinite loading)
|
# * More stable playback (less bugs/infinite loading)
|
||||||
# If you also enabled the webtorrent format, it will multiply videos storage by 2
|
# If you also enabled the web videos format, it will multiply videos storage by 2
|
||||||
hls:
|
hls:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
|
@ -599,7 +599,6 @@ video_studio:
|
||||||
# If enabled, users can create transcoding tasks as they wish
|
# If enabled, users can create transcoding tasks as they wish
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
|
|
||||||
# Enable remote runners to transcode studio tasks
|
# Enable remote runners to transcode studio tasks
|
||||||
# If enabled, your instance won't transcode the videos itself
|
# If enabled, your instance won't transcode the videos itself
|
||||||
# At least 1 remote runner must be configured to transcode your videos
|
# At least 1 remote runner must be configured to transcode your videos
|
||||||
|
|
|
@ -37,8 +37,8 @@ async function run () {
|
||||||
console.log('Detecting files to remove, it could take a while...')
|
console.log('Detecting files to remove, it could take a while...')
|
||||||
|
|
||||||
toDelete = toDelete.concat(
|
toDelete = toDelete.concat(
|
||||||
await pruneDirectory(DIRECTORIES.VIDEOS.PUBLIC, doesWebTorrentFileExist()),
|
await pruneDirectory(DIRECTORIES.VIDEOS.PUBLIC, doesWebVideoFileExist()),
|
||||||
await pruneDirectory(DIRECTORIES.VIDEOS.PRIVATE, doesWebTorrentFileExist()),
|
await pruneDirectory(DIRECTORIES.VIDEOS.PRIVATE, doesWebVideoFileExist()),
|
||||||
|
|
||||||
await pruneDirectory(DIRECTORIES.HLS_STREAMING_PLAYLIST.PRIVATE, doesHLSPlaylistExist()),
|
await pruneDirectory(DIRECTORIES.HLS_STREAMING_PLAYLIST.PRIVATE, doesHLSPlaylistExist()),
|
||||||
await pruneDirectory(DIRECTORIES.HLS_STREAMING_PLAYLIST.PUBLIC, doesHLSPlaylistExist()),
|
await pruneDirectory(DIRECTORIES.HLS_STREAMING_PLAYLIST.PUBLIC, doesHLSPlaylistExist()),
|
||||||
|
@ -93,12 +93,12 @@ async function pruneDirectory (directory: string, existFun: ExistFun) {
|
||||||
return toDelete
|
return toDelete
|
||||||
}
|
}
|
||||||
|
|
||||||
function doesWebTorrentFileExist () {
|
function doesWebVideoFileExist () {
|
||||||
return (filePath: string) => {
|
return (filePath: string) => {
|
||||||
// Don't delete private directory
|
// Don't delete private directory
|
||||||
if (filePath === DIRECTORIES.VIDEOS.PRIVATE) return true
|
if (filePath === DIRECTORIES.VIDEOS.PRIVATE) return true
|
||||||
|
|
||||||
return VideoFileModel.doesOwnedWebTorrentVideoFileExist(basename(filePath))
|
return VideoFileModel.doesOwnedWebVideoFileExist(basename(filePath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
||||||
import { proxifyHLS, proxifyWebTorrentFile } from '@server/lib/object-storage'
|
import { proxifyHLS, proxifyWebVideoFile } from '@server/lib/object-storage'
|
||||||
import { VideoPathManager } from '@server/lib/video-path-manager'
|
import { VideoPathManager } from '@server/lib/video-path-manager'
|
||||||
import { getStudioTaskFilePath } from '@server/lib/video-studio'
|
import { getStudioTaskFilePath } from '@server/lib/video-studio'
|
||||||
import { apiRateLimiter, asyncMiddleware } from '@server/middlewares'
|
import { apiRateLimiter, asyncMiddleware } from '@server/middlewares'
|
||||||
|
@ -70,7 +70,7 @@ async function getMaxQualityVideoFile (req: express.Request, res: express.Respon
|
||||||
}
|
}
|
||||||
|
|
||||||
// Web video
|
// Web video
|
||||||
return proxifyWebTorrentFile({
|
return proxifyWebVideoFile({
|
||||||
req,
|
req,
|
||||||
res,
|
res,
|
||||||
filename: file.filename
|
filename: file.filename
|
||||||
|
|
|
@ -213,19 +213,14 @@ async function updateMe (req: express.Request, res: express.Response) {
|
||||||
'noInstanceConfigWarningModal',
|
'noInstanceConfigWarningModal',
|
||||||
'noAccountSetupWarningModal',
|
'noAccountSetupWarningModal',
|
||||||
'noWelcomeModal',
|
'noWelcomeModal',
|
||||||
'emailPublic'
|
'emailPublic',
|
||||||
|
'p2pEnabled'
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const key of keysToUpdate) {
|
for (const key of keysToUpdate) {
|
||||||
if (body[key] !== undefined) user.set(key, body[key])
|
if (body[key] !== undefined) user.set(key, body[key])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body.p2pEnabled !== undefined) {
|
|
||||||
user.set('p2pEnabled', body.p2pEnabled)
|
|
||||||
} else if (body.webTorrentEnabled !== undefined) { // FIXME: deprecated in 4.1
|
|
||||||
user.set('p2pEnabled', body.webTorrentEnabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (body.email !== undefined) {
|
if (body.email !== undefined) {
|
||||||
if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
|
if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
|
||||||
user.pendingEmail = body.email
|
user.pendingEmail = body.email
|
||||||
|
|
|
@ -2,7 +2,8 @@ import express from 'express'
|
||||||
import toInt from 'validator/lib/toInt'
|
import toInt from 'validator/lib/toInt'
|
||||||
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
||||||
import { federateVideoIfNeeded } from '@server/lib/activitypub/videos'
|
import { federateVideoIfNeeded } from '@server/lib/activitypub/videos'
|
||||||
import { removeAllWebTorrentFiles, removeHLSFile, removeHLSPlaylist, removeWebTorrentFile } from '@server/lib/video-file'
|
import { updatePlaylistAfterFileChange } from '@server/lib/hls'
|
||||||
|
import { removeAllWebVideoFiles, removeHLSFile, removeHLSPlaylist, removeWebVideoFile } from '@server/lib/video-file'
|
||||||
import { VideoFileModel } from '@server/models/video/video-file'
|
import { VideoFileModel } from '@server/models/video/video-file'
|
||||||
import { HttpStatusCode, UserRight } from '@shared/models'
|
import { HttpStatusCode, UserRight } from '@shared/models'
|
||||||
import {
|
import {
|
||||||
|
@ -12,11 +13,10 @@ import {
|
||||||
videoFileMetadataGetValidator,
|
videoFileMetadataGetValidator,
|
||||||
videoFilesDeleteHLSFileValidator,
|
videoFilesDeleteHLSFileValidator,
|
||||||
videoFilesDeleteHLSValidator,
|
videoFilesDeleteHLSValidator,
|
||||||
videoFilesDeleteWebTorrentFileValidator,
|
videoFilesDeleteWebVideoFileValidator,
|
||||||
videoFilesDeleteWebTorrentValidator,
|
videoFilesDeleteWebVideoValidator,
|
||||||
videosGetValidator
|
videosGetValidator
|
||||||
} from '../../../middlewares'
|
} from '../../../middlewares'
|
||||||
import { updatePlaylistAfterFileChange } from '@server/lib/hls'
|
|
||||||
|
|
||||||
const lTags = loggerTagsFactory('api', 'video')
|
const lTags = loggerTagsFactory('api', 'video')
|
||||||
const filesRouter = express.Router()
|
const filesRouter = express.Router()
|
||||||
|
@ -40,17 +40,19 @@ filesRouter.delete('/:id/hls/:videoFileId',
|
||||||
asyncMiddleware(removeHLSFileController)
|
asyncMiddleware(removeHLSFileController)
|
||||||
)
|
)
|
||||||
|
|
||||||
filesRouter.delete('/:id/webtorrent',
|
filesRouter.delete(
|
||||||
|
[ '/:id/webtorrent', '/:id/web-videos' ], // TODO: remove webtorrent in V7
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES),
|
ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES),
|
||||||
asyncMiddleware(videoFilesDeleteWebTorrentValidator),
|
asyncMiddleware(videoFilesDeleteWebVideoValidator),
|
||||||
asyncMiddleware(removeAllWebTorrentFilesController)
|
asyncMiddleware(removeAllWebVideoFilesController)
|
||||||
)
|
)
|
||||||
filesRouter.delete('/:id/webtorrent/:videoFileId',
|
filesRouter.delete(
|
||||||
|
[ '/:id/webtorrent/:videoFileId', '/:id/web-videos/:videoFileId' ], // TODO: remove webtorrent in V7
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES),
|
ensureUserHasRight(UserRight.MANAGE_VIDEO_FILES),
|
||||||
asyncMiddleware(videoFilesDeleteWebTorrentFileValidator),
|
asyncMiddleware(videoFilesDeleteWebVideoFileValidator),
|
||||||
asyncMiddleware(removeWebTorrentFileController)
|
asyncMiddleware(removeWebVideoFileController)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -96,24 +98,24 @@ async function removeHLSFileController (req: express.Request, res: express.Respo
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function removeAllWebTorrentFilesController (req: express.Request, res: express.Response) {
|
async function removeAllWebVideoFilesController (req: express.Request, res: express.Response) {
|
||||||
const video = res.locals.videoAll
|
const video = res.locals.videoAll
|
||||||
|
|
||||||
logger.info('Deleting WebTorrent files of %s.', video.url, lTags(video.uuid))
|
logger.info('Deleting Web Video files of %s.', video.url, lTags(video.uuid))
|
||||||
|
|
||||||
await removeAllWebTorrentFiles(video)
|
await removeAllWebVideoFiles(video)
|
||||||
await federateVideoIfNeeded(video, false, undefined)
|
await federateVideoIfNeeded(video, false, undefined)
|
||||||
|
|
||||||
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeWebTorrentFileController (req: express.Request, res: express.Response) {
|
async function removeWebVideoFileController (req: express.Request, res: express.Response) {
|
||||||
const video = res.locals.videoAll
|
const video = res.locals.videoAll
|
||||||
|
|
||||||
const videoFileId = +req.params.videoFileId
|
const videoFileId = +req.params.videoFileId
|
||||||
logger.info('Deleting WebTorrent file %d of %s.', videoFileId, video.url, lTags(video.uuid))
|
logger.info('Deleting Web Video file %d of %s.', videoFileId, video.url, lTags(video.uuid))
|
||||||
|
|
||||||
await removeWebTorrentFile(video, videoFileId)
|
await removeWebVideoFile(video, videoFileId)
|
||||||
await federateVideoIfNeeded(video, false, undefined)
|
await federateVideoIfNeeded(video, false, undefined)
|
||||||
|
|
||||||
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import cors from 'cors'
|
import cors from 'cors'
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { OBJECT_STORAGE_PROXY_PATHS } from '@server/initializers/constants'
|
import { OBJECT_STORAGE_PROXY_PATHS } from '@server/initializers/constants'
|
||||||
import { proxifyHLS, proxifyWebTorrentFile } from '@server/lib/object-storage'
|
import { proxifyHLS, proxifyWebVideoFile } from '@server/lib/object-storage'
|
||||||
import {
|
import {
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
ensureCanAccessPrivateVideoHLSFiles,
|
ensureCanAccessPrivateVideoHLSFiles,
|
||||||
ensureCanAccessVideoPrivateWebTorrentFiles,
|
ensureCanAccessVideoPrivateWebVideoFiles,
|
||||||
ensurePrivateObjectStorageProxyIsEnabled,
|
ensurePrivateObjectStorageProxyIsEnabled,
|
||||||
optionalAuthenticate
|
optionalAuthenticate
|
||||||
} from '@server/middlewares'
|
} from '@server/middlewares'
|
||||||
|
@ -18,8 +18,8 @@ objectStorageProxyRouter.use(cors())
|
||||||
objectStorageProxyRouter.get(OBJECT_STORAGE_PROXY_PATHS.PRIVATE_WEBSEED + ':filename',
|
objectStorageProxyRouter.get(OBJECT_STORAGE_PROXY_PATHS.PRIVATE_WEBSEED + ':filename',
|
||||||
ensurePrivateObjectStorageProxyIsEnabled,
|
ensurePrivateObjectStorageProxyIsEnabled,
|
||||||
optionalAuthenticate,
|
optionalAuthenticate,
|
||||||
asyncMiddleware(ensureCanAccessVideoPrivateWebTorrentFiles),
|
asyncMiddleware(ensureCanAccessVideoPrivateWebVideoFiles),
|
||||||
asyncMiddleware(proxifyWebTorrentController)
|
asyncMiddleware(proxifyWebVideoController)
|
||||||
)
|
)
|
||||||
|
|
||||||
objectStorageProxyRouter.get(OBJECT_STORAGE_PROXY_PATHS.STREAMING_PLAYLISTS.PRIVATE_HLS + ':videoUUID/:filename',
|
objectStorageProxyRouter.get(OBJECT_STORAGE_PROXY_PATHS.STREAMING_PLAYLISTS.PRIVATE_HLS + ':videoUUID/:filename',
|
||||||
|
@ -35,10 +35,10 @@ export {
|
||||||
objectStorageProxyRouter
|
objectStorageProxyRouter
|
||||||
}
|
}
|
||||||
|
|
||||||
function proxifyWebTorrentController (req: express.Request, res: express.Response) {
|
function proxifyWebVideoController (req: express.Request, res: express.Response) {
|
||||||
const filename = req.params.filename
|
const filename = req.params.filename
|
||||||
|
|
||||||
return proxifyWebTorrentFile({ req, res, filename })
|
return proxifyWebVideoFile({ req, res, filename })
|
||||||
}
|
}
|
||||||
|
|
||||||
function proxifyHLSController (req: express.Request, res: express.Response) {
|
function proxifyHLSController (req: express.Request, res: express.Response) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { injectQueryToPlaylistUrls } from '@server/lib/hls'
|
||||||
import {
|
import {
|
||||||
asyncMiddleware,
|
asyncMiddleware,
|
||||||
ensureCanAccessPrivateVideoHLSFiles,
|
ensureCanAccessPrivateVideoHLSFiles,
|
||||||
ensureCanAccessVideoPrivateWebTorrentFiles,
|
ensureCanAccessVideoPrivateWebVideoFiles,
|
||||||
handleStaticError,
|
handleStaticError,
|
||||||
optionalAuthenticate
|
optionalAuthenticate
|
||||||
} from '@server/middlewares'
|
} from '@server/middlewares'
|
||||||
|
@ -21,16 +21,16 @@ const staticRouter = express.Router()
|
||||||
staticRouter.use(cors())
|
staticRouter.use(cors())
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// WebTorrent/Classic videos
|
// Web videos/Classic videos
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const privateWebTorrentStaticMiddlewares = CONFIG.STATIC_FILES.PRIVATE_FILES_REQUIRE_AUTH === true
|
const privateWebVideoStaticMiddlewares = CONFIG.STATIC_FILES.PRIVATE_FILES_REQUIRE_AUTH === true
|
||||||
? [ optionalAuthenticate, asyncMiddleware(ensureCanAccessVideoPrivateWebTorrentFiles) ]
|
? [ optionalAuthenticate, asyncMiddleware(ensureCanAccessVideoPrivateWebVideoFiles) ]
|
||||||
: []
|
: []
|
||||||
|
|
||||||
staticRouter.use(
|
staticRouter.use(
|
||||||
STATIC_PATHS.PRIVATE_WEBSEED,
|
STATIC_PATHS.PRIVATE_WEBSEED,
|
||||||
...privateWebTorrentStaticMiddlewares,
|
...privateWebVideoStaticMiddlewares,
|
||||||
express.static(DIRECTORIES.VIDEOS.PRIVATE, { fallthrough: false }),
|
express.static(DIRECTORIES.VIDEOS.PRIVATE, { fallthrough: false }),
|
||||||
handleStaticError
|
handleStaticError
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
function isValidPlayerMode (value: any) {
|
function isValidPlayerMode (value: any) {
|
||||||
return value === 'webtorrent' || value === 'p2p-media-loader'
|
// TODO: remove webtorrent in v7
|
||||||
|
return value === 'webtorrent' || value === 'web-video' || value === 'p2p-media-loader'
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { exists } from './misc'
|
||||||
|
|
||||||
function isValidCreateTranscodingType (value: any) {
|
function isValidCreateTranscodingType (value: any) {
|
||||||
return exists(value) &&
|
return exists(value) &&
|
||||||
(value === 'hls' || value === 'webtorrent')
|
(value === 'hls' || value === 'webtorrent' || value === 'web-video') // TODO: remove webtorrent in v7
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -23,7 +23,8 @@ function pickCommonVideoQuery (query: VideosCommonQueryAfterSanitize) {
|
||||||
'include',
|
'include',
|
||||||
'skipCount',
|
'skipCount',
|
||||||
'hasHLSFiles',
|
'hasHLSFiles',
|
||||||
'hasWebtorrentFiles',
|
'hasWebtorrentFiles', // TODO: Remove in v7
|
||||||
|
'hasWebVideoFiles',
|
||||||
'search',
|
'search',
|
||||||
'excludeAlreadyWatched'
|
'excludeAlreadyWatched'
|
||||||
])
|
])
|
||||||
|
|
|
@ -199,7 +199,7 @@ function checkStorageConfig () {
|
||||||
function checkTranscodingConfig () {
|
function checkTranscodingConfig () {
|
||||||
if (CONFIG.TRANSCODING.ENABLED) {
|
if (CONFIG.TRANSCODING.ENABLED) {
|
||||||
if (CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false && CONFIG.TRANSCODING.HLS.ENABLED === false) {
|
if (CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false && CONFIG.TRANSCODING.HLS.ENABLED === false) {
|
||||||
throw new Error('You need to enable at least WebTorrent transcoding or HLS transcoding.')
|
throw new Error('You need to enable at least Web Video transcoding or HLS transcoding.')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONFIG.TRANSCODING.CONCURRENCY <= 0) {
|
if (CONFIG.TRANSCODING.CONCURRENCY <= 0) {
|
||||||
|
|
|
@ -126,7 +126,7 @@ export abstract class APVideoAbstractBuilder {
|
||||||
video.VideoLive = videoLive
|
video.VideoLive = videoLive
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async setWebTorrentFiles (video: MVideoFullLight, t: Transaction) {
|
protected async setWebVideoFiles (video: MVideoFullLight, t: Transaction) {
|
||||||
const videoFileAttributes = getFileAttributesFromUrl(video, this.videoObject.url)
|
const videoFileAttributes = getFileAttributesFromUrl(video, this.videoObject.url)
|
||||||
const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a))
|
const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a))
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ export class APVideoCreator extends APVideoAbstractBuilder {
|
||||||
|
|
||||||
await this.setThumbnail(videoCreated, t)
|
await this.setThumbnail(videoCreated, t)
|
||||||
await this.setPreview(videoCreated, t)
|
await this.setPreview(videoCreated, t)
|
||||||
await this.setWebTorrentFiles(videoCreated, t)
|
await this.setWebVideoFiles(videoCreated, t)
|
||||||
await this.setStreamingPlaylists(videoCreated, t)
|
await this.setStreamingPlaylists(videoCreated, t)
|
||||||
await this.setTags(videoCreated, t)
|
await this.setTags(videoCreated, t)
|
||||||
await this.setTrackers(videoCreated, t)
|
await this.setTrackers(videoCreated, t)
|
||||||
|
|
|
@ -50,7 +50,7 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
|
||||||
if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel)
|
if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel)
|
||||||
|
|
||||||
await runInReadCommittedTransaction(async t => {
|
await runInReadCommittedTransaction(async t => {
|
||||||
await this.setWebTorrentFiles(videoUpdated, t)
|
await this.setWebVideoFiles(videoUpdated, t)
|
||||||
await this.setStreamingPlaylists(videoUpdated, t)
|
await this.setStreamingPlaylists(videoUpdated, t)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { join } from 'path'
|
||||||
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
import { logger, loggerTagsFactory } from '@server/helpers/logger'
|
||||||
import { updateTorrentMetadata } from '@server/helpers/webtorrent'
|
import { updateTorrentMetadata } from '@server/helpers/webtorrent'
|
||||||
import { P2P_MEDIA_LOADER_PEER_VERSION } from '@server/initializers/constants'
|
import { P2P_MEDIA_LOADER_PEER_VERSION } from '@server/initializers/constants'
|
||||||
import { storeHLSFileFromFilename, storeWebTorrentFile } from '@server/lib/object-storage'
|
import { storeHLSFileFromFilename, storeWebVideoFile } from '@server/lib/object-storage'
|
||||||
import { getHLSDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths'
|
import { getHLSDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths'
|
||||||
import { VideoPathManager } from '@server/lib/video-path-manager'
|
import { VideoPathManager } from '@server/lib/video-path-manager'
|
||||||
import { moveToFailedMoveToObjectStorageState, moveToNextState } from '@server/lib/video-state'
|
import { moveToFailedMoveToObjectStorageState, moveToNextState } from '@server/lib/video-state'
|
||||||
|
@ -33,9 +33,9 @@ export async function processMoveToObjectStorage (job: Job) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (video.VideoFiles) {
|
if (video.VideoFiles) {
|
||||||
logger.debug('Moving %d webtorrent files for video %s.', video.VideoFiles.length, video.uuid, lTags)
|
logger.debug('Moving %d web video files for video %s.', video.VideoFiles.length, video.uuid, lTags)
|
||||||
|
|
||||||
await moveWebTorrentFiles(video)
|
await moveWebVideoFiles(video)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video.VideoStreamingPlaylists) {
|
if (video.VideoStreamingPlaylists) {
|
||||||
|
@ -75,11 +75,11 @@ export async function onMoveToObjectStorageFailure (job: Job, err: any) {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function moveWebTorrentFiles (video: MVideoWithAllFiles) {
|
async function moveWebVideoFiles (video: MVideoWithAllFiles) {
|
||||||
for (const file of video.VideoFiles) {
|
for (const file of video.VideoFiles) {
|
||||||
if (file.storage !== VideoStorage.FILE_SYSTEM) continue
|
if (file.storage !== VideoStorage.FILE_SYSTEM) continue
|
||||||
|
|
||||||
const fileUrl = await storeWebTorrentFile(video, file)
|
const fileUrl = await storeWebVideoFile(video, file)
|
||||||
|
|
||||||
const oldPath = VideoPathManager.Instance.getFSVideoFileOutputPath(video, file)
|
const oldPath = VideoPathManager.Instance.getFSVideoFileOutputPath(video, file)
|
||||||
await onFileMoved({ videoOrPlaylist: video, file, fileUrl, oldPath })
|
await onFileMoved({ videoOrPlaylist: video, file, fileUrl, oldPath })
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { copy, stat } from 'fs-extra'
|
||||||
import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
|
import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
|
||||||
import { CONFIG } from '@server/initializers/config'
|
import { CONFIG } from '@server/initializers/config'
|
||||||
import { federateVideoIfNeeded } from '@server/lib/activitypub/videos'
|
import { federateVideoIfNeeded } from '@server/lib/activitypub/videos'
|
||||||
import { generateWebTorrentVideoFilename } from '@server/lib/paths'
|
import { generateWebVideoFilename } from '@server/lib/paths'
|
||||||
import { buildMoveToObjectStorageJob } from '@server/lib/video'
|
import { buildMoveToObjectStorageJob } from '@server/lib/video'
|
||||||
import { VideoPathManager } from '@server/lib/video-path-manager'
|
import { VideoPathManager } from '@server/lib/video-path-manager'
|
||||||
import { VideoModel } from '@server/models/video/video'
|
import { VideoModel } from '@server/models/video/video'
|
||||||
|
@ -56,7 +56,7 @@ async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) {
|
||||||
|
|
||||||
if (currentVideoFile) {
|
if (currentVideoFile) {
|
||||||
// Remove old file and old torrent
|
// Remove old file and old torrent
|
||||||
await video.removeWebTorrentFile(currentVideoFile)
|
await video.removeWebVideoFile(currentVideoFile)
|
||||||
// Remove the old video file from the array
|
// Remove the old video file from the array
|
||||||
video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile)
|
video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile)
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) {
|
||||||
const newVideoFile = new VideoFileModel({
|
const newVideoFile = new VideoFileModel({
|
||||||
resolution,
|
resolution,
|
||||||
extname: fileExt,
|
extname: fileExt,
|
||||||
filename: generateWebTorrentVideoFilename(resolution, fileExt),
|
filename: generateWebVideoFilename(resolution, fileExt),
|
||||||
storage: VideoStorage.FILE_SYSTEM,
|
storage: VideoStorage.FILE_SYSTEM,
|
||||||
size,
|
size,
|
||||||
fps,
|
fps,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { retryTransactionWrapper } from '@server/helpers/database-utils'
|
||||||
import { YoutubeDLWrapper } from '@server/helpers/youtube-dl'
|
import { YoutubeDLWrapper } from '@server/helpers/youtube-dl'
|
||||||
import { CONFIG } from '@server/initializers/config'
|
import { CONFIG } from '@server/initializers/config'
|
||||||
import { isPostImportVideoAccepted } from '@server/lib/moderation'
|
import { isPostImportVideoAccepted } from '@server/lib/moderation'
|
||||||
import { generateWebTorrentVideoFilename } from '@server/lib/paths'
|
import { generateWebVideoFilename } from '@server/lib/paths'
|
||||||
import { Hooks } from '@server/lib/plugins/hooks'
|
import { Hooks } from '@server/lib/plugins/hooks'
|
||||||
import { ServerConfigManager } from '@server/lib/server-config-manager'
|
import { ServerConfigManager } from '@server/lib/server-config-manager'
|
||||||
import { createOptimizeOrMergeAudioJobs } from '@server/lib/transcoding/create-transcoding-job'
|
import { createOptimizeOrMergeAudioJobs } from '@server/lib/transcoding/create-transcoding-job'
|
||||||
|
@ -148,7 +148,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: MVid
|
||||||
extname: fileExt,
|
extname: fileExt,
|
||||||
resolution,
|
resolution,
|
||||||
size: stats.size,
|
size: stats.size,
|
||||||
filename: generateWebTorrentVideoFilename(resolution, fileExt),
|
filename: generateWebVideoFilename(resolution, fileExt),
|
||||||
fps,
|
fps,
|
||||||
videoId: videoImport.videoId
|
videoId: videoImport.videoId
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Job } from 'bullmq'
|
import { Job } from 'bullmq'
|
||||||
import { onTranscodingEnded } from '@server/lib/transcoding/ended-transcoding'
|
import { onTranscodingEnded } from '@server/lib/transcoding/ended-transcoding'
|
||||||
import { generateHlsPlaylistResolution } from '@server/lib/transcoding/hls-transcoding'
|
import { generateHlsPlaylistResolution } from '@server/lib/transcoding/hls-transcoding'
|
||||||
import { mergeAudioVideofile, optimizeOriginalVideofile, transcodeNewWebTorrentResolution } from '@server/lib/transcoding/web-transcoding'
|
import { mergeAudioVideofile, optimizeOriginalVideofile, transcodeNewWebVideoResolution } from '@server/lib/transcoding/web-transcoding'
|
||||||
import { removeAllWebTorrentFiles } from '@server/lib/video-file'
|
import { removeAllWebVideoFiles } from '@server/lib/video-file'
|
||||||
import { VideoPathManager } from '@server/lib/video-path-manager'
|
import { VideoPathManager } from '@server/lib/video-path-manager'
|
||||||
import { moveToFailedTranscodingState } from '@server/lib/video-state'
|
import { moveToFailedTranscodingState } from '@server/lib/video-state'
|
||||||
import { UserModel } from '@server/models/user/user'
|
import { UserModel } from '@server/models/user/user'
|
||||||
|
@ -11,7 +11,7 @@ import { MUser, MUserId, MVideoFullLight } from '@server/types/models'
|
||||||
import {
|
import {
|
||||||
HLSTranscodingPayload,
|
HLSTranscodingPayload,
|
||||||
MergeAudioTranscodingPayload,
|
MergeAudioTranscodingPayload,
|
||||||
NewWebTorrentResolutionTranscodingPayload,
|
NewWebVideoResolutionTranscodingPayload,
|
||||||
OptimizeTranscodingPayload,
|
OptimizeTranscodingPayload,
|
||||||
VideoTranscodingPayload
|
VideoTranscodingPayload
|
||||||
} from '@shared/models'
|
} from '@shared/models'
|
||||||
|
@ -22,9 +22,9 @@ type HandlerFunction = (job: Job, payload: VideoTranscodingPayload, video: MVide
|
||||||
|
|
||||||
const handlers: { [ id in VideoTranscodingPayload['type'] ]: HandlerFunction } = {
|
const handlers: { [ id in VideoTranscodingPayload['type'] ]: HandlerFunction } = {
|
||||||
'new-resolution-to-hls': handleHLSJob,
|
'new-resolution-to-hls': handleHLSJob,
|
||||||
'new-resolution-to-webtorrent': handleNewWebTorrentResolutionJob,
|
'new-resolution-to-web-video': handleNewWebVideoResolutionJob,
|
||||||
'merge-audio-to-webtorrent': handleWebTorrentMergeAudioJob,
|
'merge-audio-to-web-video': handleWebVideoMergeAudioJob,
|
||||||
'optimize-to-webtorrent': handleWebTorrentOptimizeJob
|
'optimize-to-web-video': handleWebVideoOptimizeJob
|
||||||
}
|
}
|
||||||
|
|
||||||
const lTags = loggerTagsFactory('transcoding')
|
const lTags = loggerTagsFactory('transcoding')
|
||||||
|
@ -74,7 +74,7 @@ export {
|
||||||
// Job handlers
|
// Job handlers
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function handleWebTorrentMergeAudioJob (job: Job, payload: MergeAudioTranscodingPayload, video: MVideoFullLight, user: MUserId) {
|
async function handleWebVideoMergeAudioJob (job: Job, payload: MergeAudioTranscodingPayload, video: MVideoFullLight, user: MUserId) {
|
||||||
logger.info('Handling merge audio transcoding job for %s.', video.uuid, lTags(video.uuid), { payload })
|
logger.info('Handling merge audio transcoding job for %s.', video.uuid, lTags(video.uuid), { payload })
|
||||||
|
|
||||||
await mergeAudioVideofile({ video, resolution: payload.resolution, fps: payload.fps, job })
|
await mergeAudioVideofile({ video, resolution: payload.resolution, fps: payload.fps, job })
|
||||||
|
@ -84,7 +84,7 @@ async function handleWebTorrentMergeAudioJob (job: Job, payload: MergeAudioTrans
|
||||||
await onTranscodingEnded({ isNewVideo: payload.isNewVideo, moveVideoToNextState: !payload.hasChildren, video })
|
await onTranscodingEnded({ isNewVideo: payload.isNewVideo, moveVideoToNextState: !payload.hasChildren, video })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleWebTorrentOptimizeJob (job: Job, payload: OptimizeTranscodingPayload, video: MVideoFullLight, user: MUserId) {
|
async function handleWebVideoOptimizeJob (job: Job, payload: OptimizeTranscodingPayload, video: MVideoFullLight, user: MUserId) {
|
||||||
logger.info('Handling optimize transcoding job for %s.', video.uuid, lTags(video.uuid), { payload })
|
logger.info('Handling optimize transcoding job for %s.', video.uuid, lTags(video.uuid), { payload })
|
||||||
|
|
||||||
await optimizeOriginalVideofile({ video, inputVideoFile: video.getMaxQualityFile(), quickTranscode: payload.quickTranscode, job })
|
await optimizeOriginalVideofile({ video, inputVideoFile: video.getMaxQualityFile(), quickTranscode: payload.quickTranscode, job })
|
||||||
|
@ -96,12 +96,12 @@ async function handleWebTorrentOptimizeJob (job: Job, payload: OptimizeTranscodi
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function handleNewWebTorrentResolutionJob (job: Job, payload: NewWebTorrentResolutionTranscodingPayload, video: MVideoFullLight) {
|
async function handleNewWebVideoResolutionJob (job: Job, payload: NewWebVideoResolutionTranscodingPayload, video: MVideoFullLight) {
|
||||||
logger.info('Handling WebTorrent transcoding job for %s.', video.uuid, lTags(video.uuid), { payload })
|
logger.info('Handling Web Video transcoding job for %s.', video.uuid, lTags(video.uuid), { payload })
|
||||||
|
|
||||||
await transcodeNewWebTorrentResolution({ video, resolution: payload.resolution, fps: payload.fps, job })
|
await transcodeNewWebVideoResolution({ video, resolution: payload.resolution, fps: payload.fps, job })
|
||||||
|
|
||||||
logger.info('WebTorrent transcoding job for %s ended.', video.uuid, lTags(video.uuid), { payload })
|
logger.info('Web Video transcoding job for %s ended.', video.uuid, lTags(video.uuid), { payload })
|
||||||
|
|
||||||
await onTranscodingEnded({ isNewVideo: payload.isNewVideo, moveVideoToNextState: true, video })
|
await onTranscodingEnded({ isNewVideo: payload.isNewVideo, moveVideoToNextState: true, video })
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ async function handleHLSJob (job: Job, payload: HLSTranscodingPayload, videoArg:
|
||||||
video = await VideoModel.loadFull(videoArg.uuid)
|
video = await VideoModel.loadFull(videoArg.uuid)
|
||||||
|
|
||||||
const videoFileInput = payload.copyCodecs
|
const videoFileInput = payload.copyCodecs
|
||||||
? video.getWebTorrentFile(payload.resolution)
|
? video.getWebVideoFile(payload.resolution)
|
||||||
: video.getMaxQualityFile()
|
: video.getMaxQualityFile()
|
||||||
|
|
||||||
const videoOrStreamingPlaylist = videoFileInput.getVideoOrStreamingPlaylist()
|
const videoOrStreamingPlaylist = videoFileInput.getVideoOrStreamingPlaylist()
|
||||||
|
@ -140,10 +140,10 @@ async function handleHLSJob (job: Job, payload: HLSTranscodingPayload, videoArg:
|
||||||
|
|
||||||
logger.info('HLS transcoding job for %s ended.', video.uuid, lTags(video.uuid), { payload })
|
logger.info('HLS transcoding job for %s ended.', video.uuid, lTags(video.uuid), { payload })
|
||||||
|
|
||||||
if (payload.deleteWebTorrentFiles === true) {
|
if (payload.deleteWebVideoFiles === true) {
|
||||||
logger.info('Removing WebTorrent files of %s now we have a HLS version of it.', video.uuid, lTags(video.uuid))
|
logger.info('Removing Web Video files of %s now we have a HLS version of it.', video.uuid, lTags(video.uuid))
|
||||||
|
|
||||||
await removeAllWebTorrentFiles(video)
|
await removeAllWebVideoFiles(video)
|
||||||
}
|
}
|
||||||
|
|
||||||
await onTranscodingEnded({ isNewVideo: payload.isNewVideo, moveVideoToNextState: true, video })
|
await onTranscodingEnded({ isNewVideo: payload.isNewVideo, moveVideoToNextState: true, video })
|
||||||
|
|
|
@ -9,12 +9,12 @@ function generateHLSObjectBaseStorageKey (playlist: MStreamingPlaylistVideo) {
|
||||||
return join(playlist.getStringType(), playlist.Video.uuid)
|
return join(playlist.getStringType(), playlist.Video.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateWebTorrentObjectStorageKey (filename: string) {
|
function generateWebVideoObjectStorageKey (filename: string) {
|
||||||
return filename
|
return filename
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
generateHLSObjectStorageKey,
|
generateHLSObjectStorageKey,
|
||||||
generateHLSObjectBaseStorageKey,
|
generateHLSObjectBaseStorageKey,
|
||||||
generateWebTorrentObjectStorageKey
|
generateWebVideoObjectStorageKey
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@ import { GetObjectCommand } from '@aws-sdk/client-s3'
|
||||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
|
||||||
import { CONFIG } from '@server/initializers/config'
|
import { CONFIG } from '@server/initializers/config'
|
||||||
import { MStreamingPlaylistVideo, MVideoFile } from '@server/types/models'
|
import { MStreamingPlaylistVideo, MVideoFile } from '@server/types/models'
|
||||||
import { generateHLSObjectStorageKey, generateWebTorrentObjectStorageKey } from './keys'
|
import { generateHLSObjectStorageKey, generateWebVideoObjectStorageKey } from './keys'
|
||||||
import { buildKey, getClient } from './shared'
|
import { buildKey, getClient } from './shared'
|
||||||
import { getHLSPublicFileUrl, getWebTorrentPublicFileUrl } from './urls'
|
import { getHLSPublicFileUrl, getWebVideoPublicFileUrl } from './urls'
|
||||||
|
|
||||||
export async function generateWebVideoPresignedUrl (options: {
|
export async function generateWebVideoPresignedUrl (options: {
|
||||||
file: MVideoFile
|
file: MVideoFile
|
||||||
|
@ -12,7 +12,7 @@ export async function generateWebVideoPresignedUrl (options: {
|
||||||
}) {
|
}) {
|
||||||
const { file, downloadFilename } = options
|
const { file, downloadFilename } = options
|
||||||
|
|
||||||
const key = generateWebTorrentObjectStorageKey(file.filename)
|
const key = generateWebVideoObjectStorageKey(file.filename)
|
||||||
|
|
||||||
const command = new GetObjectCommand({
|
const command = new GetObjectCommand({
|
||||||
Bucket: CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME,
|
Bucket: CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME,
|
||||||
|
@ -22,7 +22,7 @@ export async function generateWebVideoPresignedUrl (options: {
|
||||||
|
|
||||||
const url = await getSignedUrl(getClient(), command, { expiresIn: 3600 * 24 })
|
const url = await getSignedUrl(getClient(), command, { expiresIn: 3600 * 24 })
|
||||||
|
|
||||||
return getWebTorrentPublicFileUrl(url)
|
return getWebVideoPublicFileUrl(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function generateHLSFilePresignedUrl (options: {
|
export async function generateHLSFilePresignedUrl (options: {
|
||||||
|
|
|
@ -7,19 +7,19 @@ import { StreamReplacer } from '@server/helpers/stream-replacer'
|
||||||
import { MStreamingPlaylist, MVideo } from '@server/types/models'
|
import { MStreamingPlaylist, MVideo } from '@server/types/models'
|
||||||
import { HttpStatusCode } from '@shared/models'
|
import { HttpStatusCode } from '@shared/models'
|
||||||
import { injectQueryToPlaylistUrls } from '../hls'
|
import { injectQueryToPlaylistUrls } from '../hls'
|
||||||
import { getHLSFileReadStream, getWebTorrentFileReadStream } from './videos'
|
import { getHLSFileReadStream, getWebVideoFileReadStream } from './videos'
|
||||||
|
|
||||||
export async function proxifyWebTorrentFile (options: {
|
export async function proxifyWebVideoFile (options: {
|
||||||
req: express.Request
|
req: express.Request
|
||||||
res: express.Response
|
res: express.Response
|
||||||
filename: string
|
filename: string
|
||||||
}) {
|
}) {
|
||||||
const { req, res, filename } = options
|
const { req, res, filename } = options
|
||||||
|
|
||||||
logger.debug('Proxifying WebTorrent file %s from object storage.', filename)
|
logger.debug('Proxifying Web Video file %s from object storage.', filename)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { response: s3Response, stream } = await getWebTorrentFileReadStream({
|
const { response: s3Response, stream } = await getWebVideoFileReadStream({
|
||||||
filename,
|
filename,
|
||||||
rangeHeader: req.header('range')
|
rangeHeader: req.header('range')
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,7 +9,7 @@ function getInternalUrl (config: BucketInfo, keyWithoutPrefix: string) {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function getWebTorrentPublicFileUrl (fileUrl: string) {
|
function getWebVideoPublicFileUrl (fileUrl: string) {
|
||||||
const baseUrl = CONFIG.OBJECT_STORAGE.VIDEOS.BASE_URL
|
const baseUrl = CONFIG.OBJECT_STORAGE.VIDEOS.BASE_URL
|
||||||
if (!baseUrl) return fileUrl
|
if (!baseUrl) return fileUrl
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ function getHLSPrivateFileUrl (video: MVideoUUID, filename: string) {
|
||||||
return WEBSERVER.URL + OBJECT_STORAGE_PROXY_PATHS.STREAMING_PLAYLISTS.PRIVATE_HLS + video.uuid + `/${filename}`
|
return WEBSERVER.URL + OBJECT_STORAGE_PROXY_PATHS.STREAMING_PLAYLISTS.PRIVATE_HLS + video.uuid + `/${filename}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWebTorrentPrivateFileUrl (filename: string) {
|
function getWebVideoPrivateFileUrl (filename: string) {
|
||||||
return WEBSERVER.URL + OBJECT_STORAGE_PROXY_PATHS.PRIVATE_WEBSEED + filename
|
return WEBSERVER.URL + OBJECT_STORAGE_PROXY_PATHS.PRIVATE_WEBSEED + filename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,11 +38,11 @@ function getWebTorrentPrivateFileUrl (filename: string) {
|
||||||
export {
|
export {
|
||||||
getInternalUrl,
|
getInternalUrl,
|
||||||
|
|
||||||
getWebTorrentPublicFileUrl,
|
getWebVideoPublicFileUrl,
|
||||||
getHLSPublicFileUrl,
|
getHLSPublicFileUrl,
|
||||||
|
|
||||||
getHLSPrivateFileUrl,
|
getHLSPrivateFileUrl,
|
||||||
getWebTorrentPrivateFileUrl,
|
getWebVideoPrivateFileUrl,
|
||||||
|
|
||||||
replaceByBaseUrl
|
replaceByBaseUrl
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { CONFIG } from '@server/initializers/config'
|
||||||
import { MStreamingPlaylistVideo, MVideo, MVideoFile } from '@server/types/models'
|
import { MStreamingPlaylistVideo, MVideo, MVideoFile } from '@server/types/models'
|
||||||
import { getHLSDirectory } from '../paths'
|
import { getHLSDirectory } from '../paths'
|
||||||
import { VideoPathManager } from '../video-path-manager'
|
import { VideoPathManager } from '../video-path-manager'
|
||||||
import { generateHLSObjectBaseStorageKey, generateHLSObjectStorageKey, generateWebTorrentObjectStorageKey } from './keys'
|
import { generateHLSObjectBaseStorageKey, generateHLSObjectStorageKey, generateWebVideoObjectStorageKey } from './keys'
|
||||||
import {
|
import {
|
||||||
createObjectReadStream,
|
createObjectReadStream,
|
||||||
listKeysOfPrefix,
|
listKeysOfPrefix,
|
||||||
|
@ -55,10 +55,10 @@ function storeHLSFileFromContent (playlist: MStreamingPlaylistVideo, path: strin
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function storeWebTorrentFile (video: MVideo, file: MVideoFile) {
|
function storeWebVideoFile (video: MVideo, file: MVideoFile) {
|
||||||
return storeObject({
|
return storeObject({
|
||||||
inputPath: VideoPathManager.Instance.getFSVideoFileOutputPath(video, file),
|
inputPath: VideoPathManager.Instance.getFSVideoFileOutputPath(video, file),
|
||||||
objectStorageKey: generateWebTorrentObjectStorageKey(file.filename),
|
objectStorageKey: generateWebVideoObjectStorageKey(file.filename),
|
||||||
bucketInfo: CONFIG.OBJECT_STORAGE.VIDEOS,
|
bucketInfo: CONFIG.OBJECT_STORAGE.VIDEOS,
|
||||||
isPrivate: video.hasPrivateStaticPath()
|
isPrivate: video.hasPrivateStaticPath()
|
||||||
})
|
})
|
||||||
|
@ -66,9 +66,9 @@ function storeWebTorrentFile (video: MVideo, file: MVideoFile) {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function updateWebTorrentFileACL (video: MVideo, file: MVideoFile) {
|
async function updateWebVideoFileACL (video: MVideo, file: MVideoFile) {
|
||||||
await updateObjectACL({
|
await updateObjectACL({
|
||||||
objectStorageKey: generateWebTorrentObjectStorageKey(file.filename),
|
objectStorageKey: generateWebVideoObjectStorageKey(file.filename),
|
||||||
bucketInfo: CONFIG.OBJECT_STORAGE.VIDEOS,
|
bucketInfo: CONFIG.OBJECT_STORAGE.VIDEOS,
|
||||||
isPrivate: video.hasPrivateStaticPath()
|
isPrivate: video.hasPrivateStaticPath()
|
||||||
})
|
})
|
||||||
|
@ -102,8 +102,8 @@ function removeHLSFileObjectStorageByFullKey (key: string) {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function removeWebTorrentObjectStorage (videoFile: MVideoFile) {
|
function removeWebVideoObjectStorage (videoFile: MVideoFile) {
|
||||||
return removeObject(generateWebTorrentObjectStorageKey(videoFile.filename), CONFIG.OBJECT_STORAGE.VIDEOS)
|
return removeObject(generateWebVideoObjectStorageKey(videoFile.filename), CONFIG.OBJECT_STORAGE.VIDEOS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -122,10 +122,10 @@ async function makeHLSFileAvailable (playlist: MStreamingPlaylistVideo, filename
|
||||||
return destination
|
return destination
|
||||||
}
|
}
|
||||||
|
|
||||||
async function makeWebTorrentFileAvailable (filename: string, destination: string) {
|
async function makeWebVideoFileAvailable (filename: string, destination: string) {
|
||||||
const key = generateWebTorrentObjectStorageKey(filename)
|
const key = generateWebVideoObjectStorageKey(filename)
|
||||||
|
|
||||||
logger.info('Fetching WebTorrent file %s from object storage to %s.', key, destination, lTags())
|
logger.info('Fetching Web Video file %s from object storage to %s.', key, destination, lTags())
|
||||||
|
|
||||||
await makeAvailable({
|
await makeAvailable({
|
||||||
key,
|
key,
|
||||||
|
@ -138,13 +138,13 @@ async function makeWebTorrentFileAvailable (filename: string, destination: strin
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function getWebTorrentFileReadStream (options: {
|
function getWebVideoFileReadStream (options: {
|
||||||
filename: string
|
filename: string
|
||||||
rangeHeader: string
|
rangeHeader: string
|
||||||
}) {
|
}) {
|
||||||
const { filename, rangeHeader } = options
|
const { filename, rangeHeader } = options
|
||||||
|
|
||||||
const key = generateWebTorrentObjectStorageKey(filename)
|
const key = generateWebVideoObjectStorageKey(filename)
|
||||||
|
|
||||||
return createObjectReadStream({
|
return createObjectReadStream({
|
||||||
key,
|
key,
|
||||||
|
@ -174,12 +174,12 @@ function getHLSFileReadStream (options: {
|
||||||
export {
|
export {
|
||||||
listHLSFileKeysOf,
|
listHLSFileKeysOf,
|
||||||
|
|
||||||
storeWebTorrentFile,
|
storeWebVideoFile,
|
||||||
storeHLSFileFromFilename,
|
storeHLSFileFromFilename,
|
||||||
storeHLSFileFromPath,
|
storeHLSFileFromPath,
|
||||||
storeHLSFileFromContent,
|
storeHLSFileFromContent,
|
||||||
|
|
||||||
updateWebTorrentFileACL,
|
updateWebVideoFileACL,
|
||||||
updateHLSFilesACL,
|
updateHLSFilesACL,
|
||||||
|
|
||||||
removeHLSObjectStorage,
|
removeHLSObjectStorage,
|
||||||
|
@ -187,11 +187,11 @@ export {
|
||||||
removeHLSFileObjectStorageByPath,
|
removeHLSFileObjectStorageByPath,
|
||||||
removeHLSFileObjectStorageByFullKey,
|
removeHLSFileObjectStorageByFullKey,
|
||||||
|
|
||||||
removeWebTorrentObjectStorage,
|
removeWebVideoObjectStorage,
|
||||||
|
|
||||||
makeWebTorrentFileAvailable,
|
makeWebVideoFileAvailable,
|
||||||
makeHLSFileAvailable,
|
makeHLSFileAvailable,
|
||||||
|
|
||||||
getWebTorrentFileReadStream,
|
getWebVideoFileReadStream,
|
||||||
getHLSFileReadStream
|
getHLSFileReadStream
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { isVideoInPrivateDirectory } from './video-privacy'
|
||||||
|
|
||||||
// ################## Video file name ##################
|
// ################## Video file name ##################
|
||||||
|
|
||||||
function generateWebTorrentVideoFilename (resolution: number, extname: string) {
|
function generateWebVideoFilename (resolution: number, extname: string) {
|
||||||
return buildUUID() + '-' + resolution + extname
|
return buildUUID() + '-' + resolution + extname
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ function getFSTorrentFilePath (videoFile: MVideoFile) {
|
||||||
|
|
||||||
export {
|
export {
|
||||||
generateHLSVideoFilename,
|
generateHLSVideoFilename,
|
||||||
generateWebTorrentVideoFilename,
|
generateWebVideoFilename,
|
||||||
|
|
||||||
generateTorrentFileName,
|
generateTorrentFileName,
|
||||||
getFSTorrentFilePath,
|
getFSTorrentFilePath,
|
||||||
|
|
|
@ -104,7 +104,7 @@ function buildVideosHelpers () {
|
||||||
const video = await VideoModel.loadFull(id)
|
const video = await VideoModel.loadFull(id)
|
||||||
if (!video) return undefined
|
if (!video) return undefined
|
||||||
|
|
||||||
const webtorrentVideoFiles = (video.VideoFiles || []).map(f => ({
|
const webVideoFiles = (video.VideoFiles || []).map(f => ({
|
||||||
path: f.storage === VideoStorage.FILE_SYSTEM
|
path: f.storage === VideoStorage.FILE_SYSTEM
|
||||||
? VideoPathManager.Instance.getFSVideoFileOutputPath(video, f)
|
? VideoPathManager.Instance.getFSVideoFileOutputPath(video, f)
|
||||||
: null,
|
: null,
|
||||||
|
@ -138,8 +138,12 @@ function buildVideosHelpers () {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
webtorrent: {
|
webtorrent: { // TODO: remove in v7
|
||||||
videoFiles: webtorrentVideoFiles
|
videoFiles: webVideoFiles
|
||||||
|
},
|
||||||
|
|
||||||
|
webVideo: {
|
||||||
|
videoFiles: webVideoFiles
|
||||||
},
|
},
|
||||||
|
|
||||||
hls: {
|
hls: {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { move } from 'fs-extra'
|
||||||
import { dirname, join } from 'path'
|
import { dirname, join } from 'path'
|
||||||
import { logger, LoggerTagsFn } from '@server/helpers/logger'
|
import { logger, LoggerTagsFn } from '@server/helpers/logger'
|
||||||
import { onTranscodingEnded } from '@server/lib/transcoding/ended-transcoding'
|
import { onTranscodingEnded } from '@server/lib/transcoding/ended-transcoding'
|
||||||
import { onWebTorrentVideoFileTranscoding } from '@server/lib/transcoding/web-transcoding'
|
import { onWebVideoFileTranscoding } from '@server/lib/transcoding/web-transcoding'
|
||||||
import { buildNewFile } from '@server/lib/video-file'
|
import { buildNewFile } from '@server/lib/video-file'
|
||||||
import { VideoModel } from '@server/models/video/video'
|
import { VideoModel } from '@server/models/video/video'
|
||||||
import { MVideoFullLight } from '@server/types/models'
|
import { MVideoFullLight } from '@server/types/models'
|
||||||
|
@ -22,7 +22,7 @@ export async function onVODWebVideoOrAudioMergeTranscodingJob (options: {
|
||||||
const newVideoFilePath = join(dirname(videoFilePath), videoFile.filename)
|
const newVideoFilePath = join(dirname(videoFilePath), videoFile.filename)
|
||||||
await move(videoFilePath, newVideoFilePath)
|
await move(videoFilePath, newVideoFilePath)
|
||||||
|
|
||||||
await onWebTorrentVideoFileTranscoding({
|
await onWebVideoFileTranscoding({
|
||||||
video,
|
video,
|
||||||
videoFile,
|
videoFile,
|
||||||
videoOutputPath: newVideoFilePath
|
videoOutputPath: newVideoFilePath
|
||||||
|
|
|
@ -83,7 +83,7 @@ export class VODAudioMergeTranscodingJobHandler extends AbstractVODTranscodingJo
|
||||||
|
|
||||||
// We can remove the old audio file
|
// We can remove the old audio file
|
||||||
const oldAudioFile = video.VideoFiles[0]
|
const oldAudioFile = video.VideoFiles[0]
|
||||||
await video.removeWebTorrentFile(oldAudioFile)
|
await video.removeWebVideoFile(oldAudioFile)
|
||||||
await oldAudioFile.destroy()
|
await oldAudioFile.destroy()
|
||||||
video.VideoFiles = []
|
video.VideoFiles = []
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { renameVideoFileInPlaylist } from '@server/lib/hls'
|
||||||
import { getHlsResolutionPlaylistFilename } from '@server/lib/paths'
|
import { getHlsResolutionPlaylistFilename } from '@server/lib/paths'
|
||||||
import { onTranscodingEnded } from '@server/lib/transcoding/ended-transcoding'
|
import { onTranscodingEnded } from '@server/lib/transcoding/ended-transcoding'
|
||||||
import { onHLSVideoFileTranscoding } from '@server/lib/transcoding/hls-transcoding'
|
import { onHLSVideoFileTranscoding } from '@server/lib/transcoding/hls-transcoding'
|
||||||
import { buildNewFile, removeAllWebTorrentFiles } from '@server/lib/video-file'
|
import { buildNewFile, removeAllWebVideoFiles } from '@server/lib/video-file'
|
||||||
import { VideoJobInfoModel } from '@server/models/video/video-job-info'
|
import { VideoJobInfoModel } from '@server/models/video/video-job-info'
|
||||||
import { MVideo } from '@server/types/models'
|
import { MVideo } from '@server/types/models'
|
||||||
import { MRunnerJob } from '@server/types/models/runners'
|
import { MRunnerJob } from '@server/types/models/runners'
|
||||||
|
@ -106,7 +106,7 @@ export class VODHLSTranscodingJobHandler extends AbstractVODTranscodingJobHandle
|
||||||
if (privatePayload.deleteWebVideoFiles === true) {
|
if (privatePayload.deleteWebVideoFiles === true) {
|
||||||
logger.info('Removing web video files of %s now we have a HLS version of it.', video.uuid, this.lTags(video.uuid))
|
logger.info('Removing web video files of %s now we have a HLS version of it.', video.uuid, this.lTags(video.uuid))
|
||||||
|
|
||||||
await removeAllWebTorrentFiles(video)
|
await removeAllWebVideoFiles(video)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Runner VOD HLS job %s for %s ended.', runnerJob.uuid, video.uuid, this.lTags(runnerJob.uuid, video.uuid))
|
logger.info('Runner VOD HLS job %s for %s ended.', runnerJob.uuid, video.uuid, this.lTags(runnerJob.uuid, video.uuid))
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { getLocalVideoCacheFileActivityPubUrl, getLocalVideoCacheStreamingPlayli
|
||||||
import { getOrCreateAPVideo } from '../activitypub/videos'
|
import { getOrCreateAPVideo } from '../activitypub/videos'
|
||||||
import { downloadPlaylistSegments } from '../hls'
|
import { downloadPlaylistSegments } from '../hls'
|
||||||
import { removeVideoRedundancy } from '../redundancy'
|
import { removeVideoRedundancy } from '../redundancy'
|
||||||
import { generateHLSRedundancyUrl, generateWebTorrentRedundancyUrl } from '../video-urls'
|
import { generateHLSRedundancyUrl, generateWebVideoRedundancyUrl } from '../video-urls'
|
||||||
import { AbstractScheduler } from './abstract-scheduler'
|
import { AbstractScheduler } from './abstract-scheduler'
|
||||||
|
|
||||||
const lTags = loggerTagsFactory('redundancy')
|
const lTags = loggerTagsFactory('redundancy')
|
||||||
|
@ -244,7 +244,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
|
||||||
const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({
|
const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({
|
||||||
expiresOn,
|
expiresOn,
|
||||||
url: getLocalVideoCacheFileActivityPubUrl(file),
|
url: getLocalVideoCacheFileActivityPubUrl(file),
|
||||||
fileUrl: generateWebTorrentRedundancyUrl(file),
|
fileUrl: generateWebVideoRedundancyUrl(file),
|
||||||
strategy,
|
strategy,
|
||||||
videoFileId: file.id,
|
videoFileId: file.id,
|
||||||
actorId: serverActor.id
|
actorId: serverActor.id
|
||||||
|
|
|
@ -15,7 +15,7 @@ export function createOptimizeOrMergeAudioJobs (options: {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export function createTranscodingJobs (options: {
|
export function createTranscodingJobs (options: {
|
||||||
transcodingType: 'hls' | 'webtorrent'
|
transcodingType: 'hls' | 'webtorrent' | 'web-video' // TODO: remove webtorrent in v7
|
||||||
video: MVideoFullLight
|
video: MVideoFullLight
|
||||||
resolutions: number[]
|
resolutions: number[]
|
||||||
isNewVideo: boolean
|
isNewVideo: boolean
|
||||||
|
|
|
@ -12,7 +12,7 @@ export abstract class AbstractJobBuilder {
|
||||||
}): Promise<any>
|
}): Promise<any>
|
||||||
|
|
||||||
abstract createTranscodingJobs (options: {
|
abstract createTranscodingJobs (options: {
|
||||||
transcodingType: 'hls' | 'webtorrent'
|
transcodingType: 'hls' | 'webtorrent' | 'web-video' // TODO: remove webtorrent in v7
|
||||||
video: MVideoFullLight
|
video: MVideoFullLight
|
||||||
resolutions: number[]
|
resolutions: number[]
|
||||||
isNewVideo: boolean
|
isNewVideo: boolean
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { ffprobePromise, getVideoStreamDimensionsInfo, getVideoStreamFPS, hasAud
|
||||||
import {
|
import {
|
||||||
HLSTranscodingPayload,
|
HLSTranscodingPayload,
|
||||||
MergeAudioTranscodingPayload,
|
MergeAudioTranscodingPayload,
|
||||||
NewWebTorrentResolutionTranscodingPayload,
|
NewWebVideoResolutionTranscodingPayload,
|
||||||
OptimizeTranscodingPayload,
|
OptimizeTranscodingPayload,
|
||||||
VideoTranscodingPayload
|
VideoTranscodingPayload
|
||||||
} from '@shared/models'
|
} from '@shared/models'
|
||||||
|
@ -33,7 +33,7 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder {
|
||||||
const { video, videoFile, isNewVideo, user, videoFileAlreadyLocked } = options
|
const { video, videoFile, isNewVideo, user, videoFileAlreadyLocked } = options
|
||||||
|
|
||||||
let mergeOrOptimizePayload: MergeAudioTranscodingPayload | OptimizeTranscodingPayload
|
let mergeOrOptimizePayload: MergeAudioTranscodingPayload | OptimizeTranscodingPayload
|
||||||
let nextTranscodingSequentialJobPayloads: (NewWebTorrentResolutionTranscodingPayload | HLSTranscodingPayload)[][] = []
|
let nextTranscodingSequentialJobPayloads: (NewWebVideoResolutionTranscodingPayload | HLSTranscodingPayload)[][] = []
|
||||||
|
|
||||||
const mutexReleaser = videoFileAlreadyLocked
|
const mutexReleaser = videoFileAlreadyLocked
|
||||||
? () => {}
|
? () => {}
|
||||||
|
@ -60,7 +60,7 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder {
|
||||||
if (CONFIG.TRANSCODING.HLS.ENABLED === true) {
|
if (CONFIG.TRANSCODING.HLS.ENABLED === true) {
|
||||||
nextTranscodingSequentialJobPayloads.push([
|
nextTranscodingSequentialJobPayloads.push([
|
||||||
this.buildHLSJobPayload({
|
this.buildHLSJobPayload({
|
||||||
deleteWebTorrentFiles: CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false,
|
deleteWebVideoFiles: CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false,
|
||||||
|
|
||||||
// We had some issues with a web video quick transcoded while producing a HLS version of it
|
// We had some issues with a web video quick transcoded while producing a HLS version of it
|
||||||
copyCodecs: !quickTranscode,
|
copyCodecs: !quickTranscode,
|
||||||
|
@ -116,7 +116,7 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async createTranscodingJobs (options: {
|
async createTranscodingJobs (options: {
|
||||||
transcodingType: 'hls' | 'webtorrent'
|
transcodingType: 'hls' | 'webtorrent' | 'web-video' // TODO: remove webtorrent in v7
|
||||||
video: MVideoFullLight
|
video: MVideoFullLight
|
||||||
resolutions: number[]
|
resolutions: number[]
|
||||||
isNewVideo: boolean
|
isNewVideo: boolean
|
||||||
|
@ -138,8 +138,8 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder {
|
||||||
return this.buildHLSJobPayload({ videoUUID: video.uuid, resolution, fps, isNewVideo })
|
return this.buildHLSJobPayload({ videoUUID: video.uuid, resolution, fps, isNewVideo })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transcodingType === 'webtorrent') {
|
if (transcodingType === 'webtorrent' || transcodingType === 'web-video') {
|
||||||
return this.buildWebTorrentJobPayload({ videoUUID: video.uuid, resolution, fps, isNewVideo })
|
return this.buildWebVideoJobPayload({ videoUUID: video.uuid, resolution, fps, isNewVideo })
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Unknown transcoding type')
|
throw new Error('Unknown transcoding type')
|
||||||
|
@ -149,7 +149,7 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder {
|
||||||
|
|
||||||
const parent = transcodingType === 'hls'
|
const parent = transcodingType === 'hls'
|
||||||
? this.buildHLSJobPayload({ videoUUID: video.uuid, resolution: maxResolution, fps, isNewVideo })
|
? this.buildHLSJobPayload({ videoUUID: video.uuid, resolution: maxResolution, fps, isNewVideo })
|
||||||
: this.buildWebTorrentJobPayload({ videoUUID: video.uuid, resolution: maxResolution, fps, isNewVideo })
|
: this.buildWebVideoJobPayload({ videoUUID: video.uuid, resolution: maxResolution, fps, isNewVideo })
|
||||||
|
|
||||||
// Process the last resolution after the other ones to prevent concurrency issue
|
// Process the last resolution after the other ones to prevent concurrency issue
|
||||||
// Because low resolutions use the biggest one as ffmpeg input
|
// Because low resolutions use the biggest one as ffmpeg input
|
||||||
|
@ -160,8 +160,8 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder {
|
||||||
|
|
||||||
private async createTranscodingJobsWithChildren (options: {
|
private async createTranscodingJobsWithChildren (options: {
|
||||||
videoUUID: string
|
videoUUID: string
|
||||||
parent: (HLSTranscodingPayload | NewWebTorrentResolutionTranscodingPayload)
|
parent: (HLSTranscodingPayload | NewWebVideoResolutionTranscodingPayload)
|
||||||
children: (HLSTranscodingPayload | NewWebTorrentResolutionTranscodingPayload)[]
|
children: (HLSTranscodingPayload | NewWebVideoResolutionTranscodingPayload)[]
|
||||||
user: MUserId | null
|
user: MUserId | null
|
||||||
}) {
|
}) {
|
||||||
const { videoUUID, parent, children, user } = options
|
const { videoUUID, parent, children, user } = options
|
||||||
|
@ -203,14 +203,14 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder {
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
|
|
||||||
const sequentialPayloads: (NewWebTorrentResolutionTranscodingPayload | HLSTranscodingPayload)[][] = []
|
const sequentialPayloads: (NewWebVideoResolutionTranscodingPayload | HLSTranscodingPayload)[][] = []
|
||||||
|
|
||||||
for (const resolution of resolutionsEnabled) {
|
for (const resolution of resolutionsEnabled) {
|
||||||
const fps = computeOutputFPS({ inputFPS: inputVideoFPS, resolution })
|
const fps = computeOutputFPS({ inputFPS: inputVideoFPS, resolution })
|
||||||
|
|
||||||
if (CONFIG.TRANSCODING.WEBTORRENT.ENABLED) {
|
if (CONFIG.TRANSCODING.WEBTORRENT.ENABLED) {
|
||||||
const payloads: (NewWebTorrentResolutionTranscodingPayload | HLSTranscodingPayload)[] = [
|
const payloads: (NewWebVideoResolutionTranscodingPayload | HLSTranscodingPayload)[] = [
|
||||||
this.buildWebTorrentJobPayload({
|
this.buildWebVideoJobPayload({
|
||||||
videoUUID: video.uuid,
|
videoUUID: video.uuid,
|
||||||
resolution,
|
resolution,
|
||||||
fps,
|
fps,
|
||||||
|
@ -253,10 +253,10 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder {
|
||||||
resolution: number
|
resolution: number
|
||||||
fps: number
|
fps: number
|
||||||
isNewVideo: boolean
|
isNewVideo: boolean
|
||||||
deleteWebTorrentFiles?: boolean // default false
|
deleteWebVideoFiles?: boolean // default false
|
||||||
copyCodecs?: boolean // default false
|
copyCodecs?: boolean // default false
|
||||||
}): HLSTranscodingPayload {
|
}): HLSTranscodingPayload {
|
||||||
const { videoUUID, resolution, fps, isNewVideo, deleteWebTorrentFiles = false, copyCodecs = false } = options
|
const { videoUUID, resolution, fps, isNewVideo, deleteWebVideoFiles = false, copyCodecs = false } = options
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'new-resolution-to-hls',
|
type: 'new-resolution-to-hls',
|
||||||
|
@ -265,20 +265,20 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder {
|
||||||
fps,
|
fps,
|
||||||
copyCodecs,
|
copyCodecs,
|
||||||
isNewVideo,
|
isNewVideo,
|
||||||
deleteWebTorrentFiles
|
deleteWebVideoFiles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildWebTorrentJobPayload (options: {
|
private buildWebVideoJobPayload (options: {
|
||||||
videoUUID: string
|
videoUUID: string
|
||||||
resolution: number
|
resolution: number
|
||||||
fps: number
|
fps: number
|
||||||
isNewVideo: boolean
|
isNewVideo: boolean
|
||||||
}): NewWebTorrentResolutionTranscodingPayload {
|
}): NewWebVideoResolutionTranscodingPayload {
|
||||||
const { videoUUID, resolution, fps, isNewVideo } = options
|
const { videoUUID, resolution, fps, isNewVideo } = options
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'new-resolution-to-webtorrent',
|
type: 'new-resolution-to-web-video',
|
||||||
videoUUID,
|
videoUUID,
|
||||||
isNewVideo,
|
isNewVideo,
|
||||||
resolution,
|
resolution,
|
||||||
|
@ -294,7 +294,7 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder {
|
||||||
const { videoUUID, isNewVideo, hasChildren } = options
|
const { videoUUID, isNewVideo, hasChildren } = options
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'merge-audio-to-webtorrent',
|
type: 'merge-audio-to-web-video',
|
||||||
resolution: DEFAULT_AUDIO_RESOLUTION,
|
resolution: DEFAULT_AUDIO_RESOLUTION,
|
||||||
fps: VIDEO_TRANSCODING_FPS.AUDIO_MERGE,
|
fps: VIDEO_TRANSCODING_FPS.AUDIO_MERGE,
|
||||||
videoUUID,
|
videoUUID,
|
||||||
|
@ -312,7 +312,7 @@ export class TranscodingJobQueueBuilder extends AbstractJobBuilder {
|
||||||
const { videoUUID, quickTranscode, isNewVideo, hasChildren } = options
|
const { videoUUID, quickTranscode, isNewVideo, hasChildren } = options
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'optimize-to-webtorrent',
|
type: 'optimize-to-web-video',
|
||||||
videoUUID,
|
videoUUID,
|
||||||
isNewVideo,
|
isNewVideo,
|
||||||
hasChildren,
|
hasChildren,
|
||||||
|
|
|
@ -89,7 +89,7 @@ export class TranscodingRunnerJobBuilder extends AbstractJobBuilder {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async createTranscodingJobs (options: {
|
async createTranscodingJobs (options: {
|
||||||
transcodingType: 'hls' | 'webtorrent'
|
transcodingType: 'hls' | 'webtorrent' | 'web-video' // TODO: remove webtorrent in v7
|
||||||
video: MVideoFullLight
|
video: MVideoFullLight
|
||||||
resolutions: number[]
|
resolutions: number[]
|
||||||
isNewVideo: boolean
|
isNewVideo: boolean
|
||||||
|
@ -130,7 +130,7 @@ export class TranscodingRunnerJobBuilder extends AbstractJobBuilder {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transcodingType === 'webtorrent') {
|
if (transcodingType === 'webtorrent' || transcodingType === 'web-video') {
|
||||||
await new VODWebVideoTranscodingJobHandler().create({
|
await new VODWebVideoTranscodingJobHandler().create({
|
||||||
video,
|
video,
|
||||||
resolution,
|
resolution,
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { VideoResolution, VideoStorage } from '@shared/models'
|
||||||
import { CONFIG } from '../../initializers/config'
|
import { CONFIG } from '../../initializers/config'
|
||||||
import { VideoFileModel } from '../../models/video/video-file'
|
import { VideoFileModel } from '../../models/video/video-file'
|
||||||
import { JobQueue } from '../job-queue'
|
import { JobQueue } from '../job-queue'
|
||||||
import { generateWebTorrentVideoFilename } from '../paths'
|
import { generateWebVideoFilename } from '../paths'
|
||||||
import { buildFileMetadata } from '../video-file'
|
import { buildFileMetadata } from '../video-file'
|
||||||
import { VideoPathManager } from '../video-path-manager'
|
import { VideoPathManager } from '../video-path-manager'
|
||||||
import { buildFFmpegVOD } from './shared'
|
import { buildFFmpegVOD } from './shared'
|
||||||
|
@ -63,10 +63,10 @@ export async function optimizeOriginalVideofile (options: {
|
||||||
// Important to do this before getVideoFilename() to take in account the new filename
|
// Important to do this before getVideoFilename() to take in account the new filename
|
||||||
inputVideoFile.resolution = resolution
|
inputVideoFile.resolution = resolution
|
||||||
inputVideoFile.extname = newExtname
|
inputVideoFile.extname = newExtname
|
||||||
inputVideoFile.filename = generateWebTorrentVideoFilename(resolution, newExtname)
|
inputVideoFile.filename = generateWebVideoFilename(resolution, newExtname)
|
||||||
inputVideoFile.storage = VideoStorage.FILE_SYSTEM
|
inputVideoFile.storage = VideoStorage.FILE_SYSTEM
|
||||||
|
|
||||||
const { videoFile } = await onWebTorrentVideoFileTranscoding({
|
const { videoFile } = await onWebVideoFileTranscoding({
|
||||||
video,
|
video,
|
||||||
videoFile: inputVideoFile,
|
videoFile: inputVideoFile,
|
||||||
videoOutputPath
|
videoOutputPath
|
||||||
|
@ -83,8 +83,8 @@ export async function optimizeOriginalVideofile (options: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transcode the original video file to a lower resolution compatible with WebTorrent
|
// Transcode the original video file to a lower resolution compatible with web browsers
|
||||||
export async function transcodeNewWebTorrentResolution (options: {
|
export async function transcodeNewWebVideoResolution (options: {
|
||||||
video: MVideoFullLight
|
video: MVideoFullLight
|
||||||
resolution: VideoResolution
|
resolution: VideoResolution
|
||||||
fps: number
|
fps: number
|
||||||
|
@ -105,7 +105,7 @@ export async function transcodeNewWebTorrentResolution (options: {
|
||||||
const newVideoFile = new VideoFileModel({
|
const newVideoFile = new VideoFileModel({
|
||||||
resolution,
|
resolution,
|
||||||
extname: newExtname,
|
extname: newExtname,
|
||||||
filename: generateWebTorrentVideoFilename(resolution, newExtname),
|
filename: generateWebVideoFilename(resolution, newExtname),
|
||||||
size: 0,
|
size: 0,
|
||||||
videoId: video.id
|
videoId: video.id
|
||||||
})
|
})
|
||||||
|
@ -126,7 +126,7 @@ export async function transcodeNewWebTorrentResolution (options: {
|
||||||
|
|
||||||
await buildFFmpegVOD(job).transcode(transcodeOptions)
|
await buildFFmpegVOD(job).transcode(transcodeOptions)
|
||||||
|
|
||||||
return onWebTorrentVideoFileTranscoding({ video, videoFile: newVideoFile, videoOutputPath })
|
return onWebVideoFileTranscoding({ video, videoFile: newVideoFile, videoOutputPath })
|
||||||
})
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -189,14 +189,14 @@ export async function mergeAudioVideofile (options: {
|
||||||
// Important to do this before getVideoFilename() to take in account the new file extension
|
// Important to do this before getVideoFilename() to take in account the new file extension
|
||||||
inputVideoFile.extname = newExtname
|
inputVideoFile.extname = newExtname
|
||||||
inputVideoFile.resolution = resolution
|
inputVideoFile.resolution = resolution
|
||||||
inputVideoFile.filename = generateWebTorrentVideoFilename(inputVideoFile.resolution, newExtname)
|
inputVideoFile.filename = generateWebVideoFilename(inputVideoFile.resolution, newExtname)
|
||||||
|
|
||||||
// ffmpeg generated a new video file, so update the video duration
|
// ffmpeg generated a new video file, so update the video duration
|
||||||
// See https://trac.ffmpeg.org/ticket/5456
|
// See https://trac.ffmpeg.org/ticket/5456
|
||||||
video.duration = await getVideoStreamDuration(videoOutputPath)
|
video.duration = await getVideoStreamDuration(videoOutputPath)
|
||||||
await video.save()
|
await video.save()
|
||||||
|
|
||||||
return onWebTorrentVideoFileTranscoding({
|
return onWebVideoFileTranscoding({
|
||||||
video,
|
video,
|
||||||
videoFile: inputVideoFile,
|
videoFile: inputVideoFile,
|
||||||
videoOutputPath,
|
videoOutputPath,
|
||||||
|
@ -210,7 +210,7 @@ export async function mergeAudioVideofile (options: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function onWebTorrentVideoFileTranscoding (options: {
|
export async function onWebVideoFileTranscoding (options: {
|
||||||
video: MVideoFullLight
|
video: MVideoFullLight
|
||||||
videoFile: MVideoFile
|
videoFile: MVideoFile
|
||||||
videoOutputPath: string
|
videoOutputPath: string
|
||||||
|
@ -239,8 +239,8 @@ export async function onWebTorrentVideoFileTranscoding (options: {
|
||||||
|
|
||||||
await createTorrentAndSetInfoHash(video, videoFile)
|
await createTorrentAndSetInfoHash(video, videoFile)
|
||||||
|
|
||||||
const oldFile = await VideoFileModel.loadWebTorrentFile({ videoId: video.id, fps: videoFile.fps, resolution: videoFile.resolution })
|
const oldFile = await VideoFileModel.loadWebVideoFile({ videoId: video.id, fps: videoFile.fps, resolution: videoFile.resolution })
|
||||||
if (oldFile) await video.removeWebTorrentFile(oldFile)
|
if (oldFile) await video.removeWebVideoFile(oldFile)
|
||||||
|
|
||||||
await VideoFileModel.customUpsert(videoFile, 'video', undefined)
|
await VideoFileModel.customUpsert(videoFile, 'video', undefined)
|
||||||
video.VideoFiles = await video.$get('VideoFiles')
|
video.VideoFiles = await video.$get('VideoFiles')
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { getFileSize } from '@shared/extra-utils'
|
||||||
import { ffprobePromise, getVideoStreamDimensionsInfo, getVideoStreamFPS, isAudioFile } from '@shared/ffmpeg'
|
import { ffprobePromise, getVideoStreamDimensionsInfo, getVideoStreamFPS, isAudioFile } from '@shared/ffmpeg'
|
||||||
import { VideoFileMetadata, VideoResolution } from '@shared/models'
|
import { VideoFileMetadata, VideoResolution } from '@shared/models'
|
||||||
import { lTags } from './object-storage/shared'
|
import { lTags } from './object-storage/shared'
|
||||||
import { generateHLSVideoFilename, generateWebTorrentVideoFilename } from './paths'
|
import { generateHLSVideoFilename, generateWebVideoFilename } from './paths'
|
||||||
import { VideoPathManager } from './video-path-manager'
|
import { VideoPathManager } from './video-path-manager'
|
||||||
|
|
||||||
async function buildNewFile (options: {
|
async function buildNewFile (options: {
|
||||||
|
@ -33,7 +33,7 @@ async function buildNewFile (options: {
|
||||||
}
|
}
|
||||||
|
|
||||||
videoFile.filename = mode === 'web-video'
|
videoFile.filename = mode === 'web-video'
|
||||||
? generateWebTorrentVideoFilename(videoFile.resolution, videoFile.extname)
|
? generateWebVideoFilename(videoFile.resolution, videoFile.extname)
|
||||||
: generateHLSVideoFilename(videoFile.resolution)
|
: generateHLSVideoFilename(videoFile.resolution)
|
||||||
|
|
||||||
return videoFile
|
return videoFile
|
||||||
|
@ -85,12 +85,12 @@ async function removeHLSFile (video: MVideoWithAllFiles, fileToDeleteId: number)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function removeAllWebTorrentFiles (video: MVideoWithAllFiles) {
|
async function removeAllWebVideoFiles (video: MVideoWithAllFiles) {
|
||||||
const videoFileMutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid)
|
const videoFileMutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const file of video.VideoFiles) {
|
for (const file of video.VideoFiles) {
|
||||||
await video.removeWebTorrentFile(file)
|
await video.removeWebVideoFile(file)
|
||||||
await file.destroy()
|
await file.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,17 +102,17 @@ async function removeAllWebTorrentFiles (video: MVideoWithAllFiles) {
|
||||||
return video
|
return video
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeWebTorrentFile (video: MVideoWithAllFiles, fileToDeleteId: number) {
|
async function removeWebVideoFile (video: MVideoWithAllFiles, fileToDeleteId: number) {
|
||||||
const files = video.VideoFiles
|
const files = video.VideoFiles
|
||||||
|
|
||||||
if (files.length === 1) {
|
if (files.length === 1) {
|
||||||
return removeAllWebTorrentFiles(video)
|
return removeAllWebVideoFiles(video)
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoFileMutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid)
|
const videoFileMutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid)
|
||||||
try {
|
try {
|
||||||
const toDelete = files.find(f => f.id === fileToDeleteId)
|
const toDelete = files.find(f => f.id === fileToDeleteId)
|
||||||
await video.removeWebTorrentFile(toDelete)
|
await video.removeWebVideoFile(toDelete)
|
||||||
await toDelete.destroy()
|
await toDelete.destroy()
|
||||||
|
|
||||||
video.VideoFiles = files.filter(f => f.id !== toDelete.id)
|
video.VideoFiles = files.filter(f => f.id !== toDelete.id)
|
||||||
|
@ -138,8 +138,8 @@ export {
|
||||||
|
|
||||||
removeHLSPlaylist,
|
removeHLSPlaylist,
|
||||||
removeHLSFile,
|
removeHLSFile,
|
||||||
removeAllWebTorrentFiles,
|
removeAllWebVideoFiles,
|
||||||
removeWebTorrentFile,
|
removeWebVideoFile,
|
||||||
|
|
||||||
buildFileMetadata
|
buildFileMetadata
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { DIRECTORIES } from '@server/initializers/constants'
|
||||||
import { MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '@server/types/models'
|
import { MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '@server/types/models'
|
||||||
import { buildUUID } from '@shared/extra-utils'
|
import { buildUUID } from '@shared/extra-utils'
|
||||||
import { VideoStorage } from '@shared/models'
|
import { VideoStorage } from '@shared/models'
|
||||||
import { makeHLSFileAvailable, makeWebTorrentFileAvailable } from './object-storage'
|
import { makeHLSFileAvailable, makeWebVideoFileAvailable } from './object-storage'
|
||||||
import { getHLSDirectory, getHLSRedundancyDirectory, getHlsResolutionPlaylistFilename } from './paths'
|
import { getHLSDirectory, getHLSRedundancyDirectory, getHlsResolutionPlaylistFilename } from './paths'
|
||||||
import { isVideoInPrivateDirectory } from './video-privacy'
|
import { isVideoInPrivateDirectory } from './video-privacy'
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ class VideoPathManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.makeAvailableFactory(
|
return this.makeAvailableFactory(
|
||||||
() => makeWebTorrentFileAvailable(videoFile.filename, destination),
|
() => makeWebVideoFileAvailable(videoFile.filename, destination),
|
||||||
true,
|
true,
|
||||||
cb
|
cb
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { logger } from '@server/helpers/logger'
|
||||||
import { DIRECTORIES } from '@server/initializers/constants'
|
import { DIRECTORIES } from '@server/initializers/constants'
|
||||||
import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
|
import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
|
||||||
import { VideoPrivacy, VideoStorage } from '@shared/models'
|
import { VideoPrivacy, VideoStorage } from '@shared/models'
|
||||||
import { updateHLSFilesACL, updateWebTorrentFileACL } from './object-storage'
|
import { updateHLSFilesACL, updateWebVideoFileACL } from './object-storage'
|
||||||
|
|
||||||
const validPrivacySet = new Set([
|
const validPrivacySet = new Set([
|
||||||
VideoPrivacy.PRIVATE,
|
VideoPrivacy.PRIVATE,
|
||||||
|
@ -67,9 +67,9 @@ async function moveFiles (options: {
|
||||||
|
|
||||||
for (const file of video.VideoFiles) {
|
for (const file of video.VideoFiles) {
|
||||||
if (file.storage === VideoStorage.FILE_SYSTEM) {
|
if (file.storage === VideoStorage.FILE_SYSTEM) {
|
||||||
await moveWebTorrentFileOnFS(type, video, file)
|
await moveWebVideoFileOnFS(type, video, file)
|
||||||
} else {
|
} else {
|
||||||
await updateWebTorrentFileACL(video, file)
|
await updateWebVideoFileACL(video, file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,22 +84,22 @@ async function moveFiles (options: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function moveWebTorrentFileOnFS (type: MoveType, video: MVideo, file: MVideoFile) {
|
async function moveWebVideoFileOnFS (type: MoveType, video: MVideo, file: MVideoFile) {
|
||||||
const directories = getWebTorrentDirectories(type)
|
const directories = getWebVideoDirectories(type)
|
||||||
|
|
||||||
const source = join(directories.old, file.filename)
|
const source = join(directories.old, file.filename)
|
||||||
const destination = join(directories.new, file.filename)
|
const destination = join(directories.new, file.filename)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info('Moving WebTorrent files of %s after privacy change (%s -> %s).', video.uuid, source, destination)
|
logger.info('Moving web video files of %s after privacy change (%s -> %s).', video.uuid, source, destination)
|
||||||
|
|
||||||
await move(source, destination)
|
await move(source, destination)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Cannot move webtorrent file %s to %s after privacy change', source, destination, { err })
|
logger.error('Cannot move web video file %s to %s after privacy change', source, destination, { err })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWebTorrentDirectories (moveType: MoveType) {
|
function getWebVideoDirectories (moveType: MoveType) {
|
||||||
if (moveType === 'private-to-public') {
|
if (moveType === 'private-to-public') {
|
||||||
return { old: DIRECTORIES.VIDEOS.PRIVATE, new: DIRECTORIES.VIDEOS.PUBLIC }
|
return { old: DIRECTORIES.VIDEOS.PRIVATE, new: DIRECTORIES.VIDEOS.PUBLIC }
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { JobQueue } from './job-queue'
|
||||||
import { VideoStudioTranscodingJobHandler } from './runners'
|
import { VideoStudioTranscodingJobHandler } from './runners'
|
||||||
import { createOptimizeOrMergeAudioJobs } from './transcoding/create-transcoding-job'
|
import { createOptimizeOrMergeAudioJobs } from './transcoding/create-transcoding-job'
|
||||||
import { getTranscodingJobPriority } from './transcoding/transcoding-priority'
|
import { getTranscodingJobPriority } from './transcoding/transcoding-priority'
|
||||||
import { buildNewFile, removeHLSPlaylist, removeWebTorrentFile } from './video-file'
|
import { buildNewFile, removeHLSPlaylist, removeWebVideoFile } from './video-file'
|
||||||
import { VideoPathManager } from './video-path-manager'
|
import { VideoPathManager } from './video-path-manager'
|
||||||
|
|
||||||
const lTags = loggerTagsFactory('video-studio')
|
const lTags = loggerTagsFactory('video-studio')
|
||||||
|
@ -119,12 +119,12 @@ export async function onVideoStudioEnded (options: {
|
||||||
// Private
|
// Private
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function removeAllFiles (video: MVideoWithAllFiles, webTorrentFileException: MVideoFile) {
|
async function removeAllFiles (video: MVideoWithAllFiles, webVideoFileException: MVideoFile) {
|
||||||
await removeHLSPlaylist(video)
|
await removeHLSPlaylist(video)
|
||||||
|
|
||||||
for (const file of video.VideoFiles) {
|
for (const file of video.VideoFiles) {
|
||||||
if (file.id === webTorrentFileException.id) continue
|
if (file.id === webVideoFileException.id) continue
|
||||||
|
|
||||||
await removeWebTorrentFile(video, file.id)
|
await removeWebVideoFile(video, file.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ function generateHLSRedundancyUrl (video: MVideo, playlist: MStreamingPlaylist)
|
||||||
return WEBSERVER.URL + STATIC_PATHS.REDUNDANCY + playlist.getStringType() + '/' + video.uuid
|
return WEBSERVER.URL + STATIC_PATHS.REDUNDANCY + playlist.getStringType() + '/' + video.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateWebTorrentRedundancyUrl (file: MVideoFile) {
|
function generateWebVideoRedundancyUrl (file: MVideoFile) {
|
||||||
return WEBSERVER.URL + STATIC_PATHS.REDUNDANCY + file.filename
|
return WEBSERVER.URL + STATIC_PATHS.REDUNDANCY + file.filename
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,6 @@ function getLocalVideoFileMetadataUrl (video: MVideoUUID, videoFile: MVideoFile)
|
||||||
export {
|
export {
|
||||||
getLocalVideoFileMetadataUrl,
|
getLocalVideoFileMetadataUrl,
|
||||||
|
|
||||||
generateWebTorrentRedundancyUrl,
|
generateWebVideoRedundancyUrl,
|
||||||
generateHLSRedundancyUrl
|
generateHLSRedundancyUrl
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ const staticFileTokenBypass = new LRUCache<string, LRUValue>({
|
||||||
ttl: LRU_CACHE.STATIC_VIDEO_FILES_RIGHTS_CHECK.TTL
|
ttl: LRU_CACHE.STATIC_VIDEO_FILES_RIGHTS_CHECK.TTL
|
||||||
})
|
})
|
||||||
|
|
||||||
const ensureCanAccessVideoPrivateWebTorrentFiles = [
|
const ensureCanAccessVideoPrivateWebVideoFiles = [
|
||||||
query('videoFileToken').optional().custom(exists),
|
query('videoFileToken').optional().custom(exists),
|
||||||
|
|
||||||
isValidVideoPasswordHeader(),
|
isValidVideoPasswordHeader(),
|
||||||
|
@ -48,7 +48,7 @@ const ensureCanAccessVideoPrivateWebTorrentFiles = [
|
||||||
return res.sendStatus(HttpStatusCode.FORBIDDEN_403)
|
return res.sendStatus(HttpStatusCode.FORBIDDEN_403)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await isWebTorrentAllowed(req, res)
|
const result = await isWebVideoAllowed(req, res)
|
||||||
|
|
||||||
staticFileTokenBypass.set(cacheKey, result)
|
staticFileTokenBypass.set(cacheKey, result)
|
||||||
|
|
||||||
|
@ -122,13 +122,13 @@ const ensureCanAccessPrivateVideoHLSFiles = [
|
||||||
]
|
]
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ensureCanAccessVideoPrivateWebTorrentFiles,
|
ensureCanAccessVideoPrivateWebVideoFiles,
|
||||||
ensureCanAccessPrivateVideoHLSFiles
|
ensureCanAccessPrivateVideoHLSFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function isWebTorrentAllowed (req: express.Request, res: express.Response) {
|
async function isWebVideoAllowed (req: express.Request, res: express.Response) {
|
||||||
const filename = basename(req.path)
|
const filename = basename(req.path)
|
||||||
|
|
||||||
const file = await VideoFileModel.loadWithVideoByFilename(filename)
|
const file = await VideoFileModel.loadWithVideoByFilename(filename)
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { MVideo } from '@server/types/models'
|
||||||
import { HttpStatusCode } from '@shared/models'
|
import { HttpStatusCode } from '@shared/models'
|
||||||
import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared'
|
import { areValidationErrors, doesVideoExist, isValidVideoIdParam } from '../shared'
|
||||||
|
|
||||||
const videoFilesDeleteWebTorrentValidator = [
|
const videoFilesDeleteWebVideoValidator = [
|
||||||
isValidVideoIdParam('id'),
|
isValidVideoIdParam('id'),
|
||||||
|
|
||||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
|
@ -16,17 +16,17 @@ const videoFilesDeleteWebTorrentValidator = [
|
||||||
|
|
||||||
if (!checkLocalVideo(video, res)) return
|
if (!checkLocalVideo(video, res)) return
|
||||||
|
|
||||||
if (!video.hasWebTorrentFiles()) {
|
if (!video.hasWebVideoFiles()) {
|
||||||
return res.fail({
|
return res.fail({
|
||||||
status: HttpStatusCode.BAD_REQUEST_400,
|
status: HttpStatusCode.BAD_REQUEST_400,
|
||||||
message: 'This video does not have WebTorrent files'
|
message: 'This video does not have Web Video files'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!video.getHLSPlaylist()) {
|
if (!video.getHLSPlaylist()) {
|
||||||
return res.fail({
|
return res.fail({
|
||||||
status: HttpStatusCode.BAD_REQUEST_400,
|
status: HttpStatusCode.BAD_REQUEST_400,
|
||||||
message: 'Cannot delete WebTorrent files since this video does not have HLS playlist'
|
message: 'Cannot delete Web Video files since this video does not have HLS playlist'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ const videoFilesDeleteWebTorrentValidator = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const videoFilesDeleteWebTorrentFileValidator = [
|
const videoFilesDeleteWebVideoFileValidator = [
|
||||||
isValidVideoIdParam('id'),
|
isValidVideoIdParam('id'),
|
||||||
|
|
||||||
param('videoFileId')
|
param('videoFileId')
|
||||||
|
@ -52,14 +52,14 @@ const videoFilesDeleteWebTorrentFileValidator = [
|
||||||
if (!files.find(f => f.id === +req.params.videoFileId)) {
|
if (!files.find(f => f.id === +req.params.videoFileId)) {
|
||||||
return res.fail({
|
return res.fail({
|
||||||
status: HttpStatusCode.NOT_FOUND_404,
|
status: HttpStatusCode.NOT_FOUND_404,
|
||||||
message: 'This video does not have this WebTorrent file id'
|
message: 'This video does not have this Web Video file id'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (files.length === 1 && !video.getHLSPlaylist()) {
|
if (files.length === 1 && !video.getHLSPlaylist()) {
|
||||||
return res.fail({
|
return res.fail({
|
||||||
status: HttpStatusCode.BAD_REQUEST_400,
|
status: HttpStatusCode.BAD_REQUEST_400,
|
||||||
message: 'Cannot delete WebTorrent files since this video does not have HLS playlist'
|
message: 'Cannot delete Web Video files since this video does not have HLS playlist'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,10 +87,10 @@ const videoFilesDeleteHLSValidator = [
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!video.hasWebTorrentFiles()) {
|
if (!video.hasWebVideoFiles()) {
|
||||||
return res.fail({
|
return res.fail({
|
||||||
status: HttpStatusCode.BAD_REQUEST_400,
|
status: HttpStatusCode.BAD_REQUEST_400,
|
||||||
message: 'Cannot delete HLS playlist since this video does not have WebTorrent files'
|
message: 'Cannot delete HLS playlist since this video does not have Web Video files'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,10 +128,10 @@ const videoFilesDeleteHLSFileValidator = [
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last file to delete
|
// Last file to delete
|
||||||
if (hlsFiles.length === 1 && !video.hasWebTorrentFiles()) {
|
if (hlsFiles.length === 1 && !video.hasWebVideoFiles()) {
|
||||||
return res.fail({
|
return res.fail({
|
||||||
status: HttpStatusCode.BAD_REQUEST_400,
|
status: HttpStatusCode.BAD_REQUEST_400,
|
||||||
message: 'Cannot delete last HLS playlist file since this video does not have WebTorrent files'
|
message: 'Cannot delete last HLS playlist file since this video does not have Web Video files'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,8 +140,8 @@ const videoFilesDeleteHLSFileValidator = [
|
||||||
]
|
]
|
||||||
|
|
||||||
export {
|
export {
|
||||||
videoFilesDeleteWebTorrentValidator,
|
videoFilesDeleteWebVideoValidator,
|
||||||
videoFilesDeleteWebTorrentFileValidator,
|
videoFilesDeleteWebVideoFileValidator,
|
||||||
|
|
||||||
videoFilesDeleteHLSValidator,
|
videoFilesDeleteHLSValidator,
|
||||||
videoFilesDeleteHLSFileValidator
|
videoFilesDeleteHLSFileValidator
|
||||||
|
|
|
@ -506,10 +506,14 @@ const commonVideosFiltersValidator = [
|
||||||
.optional()
|
.optional()
|
||||||
.customSanitizer(toBooleanOrNull)
|
.customSanitizer(toBooleanOrNull)
|
||||||
.custom(isBooleanValid).withMessage('Should have a valid hasHLSFiles boolean'),
|
.custom(isBooleanValid).withMessage('Should have a valid hasHLSFiles boolean'),
|
||||||
query('hasWebtorrentFiles')
|
query('hasWebtorrentFiles') // TODO: remove in v7
|
||||||
.optional()
|
.optional()
|
||||||
.customSanitizer(toBooleanOrNull)
|
.customSanitizer(toBooleanOrNull)
|
||||||
.custom(isBooleanValid).withMessage('Should have a valid hasWebtorrentFiles boolean'),
|
.custom(isBooleanValid).withMessage('Should have a valid hasWebtorrentFiles boolean'),
|
||||||
|
query('hasWebVideoFiles')
|
||||||
|
.optional()
|
||||||
|
.customSanitizer(toBooleanOrNull)
|
||||||
|
.custom(isBooleanValid).withMessage('Should have a valid hasWebVideoFiles boolean'),
|
||||||
query('skipCount')
|
query('skipCount')
|
||||||
.optional()
|
.optional()
|
||||||
.customSanitizer(toBooleanOrNull)
|
.customSanitizer(toBooleanOrNull)
|
||||||
|
|
|
@ -162,7 +162,7 @@ export class VideoRedundancyModel extends Model<Partial<AttributesOnly<VideoRedu
|
||||||
const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}`
|
const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}`
|
||||||
logger.info('Removing duplicated video file %s.', logIdentifier)
|
logger.info('Removing duplicated video file %s.', logIdentifier)
|
||||||
|
|
||||||
videoFile.Video.removeWebTorrentFile(videoFile, true)
|
videoFile.Video.removeWebVideoFile(videoFile, true)
|
||||||
.catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err }))
|
.catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -786,7 +786,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
|
||||||
'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' +
|
'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' +
|
||||||
`WHERE "account"."userId" = ${options.whereUserId} ${andWhere}`
|
`WHERE "account"."userId" = ${options.whereUserId} ${andWhere}`
|
||||||
|
|
||||||
const webtorrentFiles = 'SELECT "videoFile"."size" AS "size", "video"."id" AS "videoId" FROM "videoFile" ' +
|
const webVideoFiles = 'SELECT "videoFile"."size" AS "size", "video"."id" AS "videoId" FROM "videoFile" ' +
|
||||||
'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" AND "video"."isLive" IS FALSE ' +
|
'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" AND "video"."isLive" IS FALSE ' +
|
||||||
videoChannelJoin
|
videoChannelJoin
|
||||||
|
|
||||||
|
@ -797,7 +797,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
|
||||||
|
|
||||||
return 'SELECT COALESCE(SUM("size"), 0) AS "total" ' +
|
return 'SELECT COALESCE(SUM("size"), 0) AS "total" ' +
|
||||||
'FROM (' +
|
'FROM (' +
|
||||||
`SELECT MAX("t1"."size") AS "size" FROM (${webtorrentFiles} UNION ${hlsFiles}) t1 ` +
|
`SELECT MAX("t1"."size") AS "size" FROM (${webVideoFiles} UNION ${hlsFiles}) t1 ` +
|
||||||
'GROUP BY "t1"."videoId"' +
|
'GROUP BY "t1"."videoId"' +
|
||||||
') t2'
|
') t2'
|
||||||
}
|
}
|
||||||
|
@ -890,8 +890,6 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
|
||||||
|
|
||||||
nsfwPolicy: this.nsfwPolicy,
|
nsfwPolicy: this.nsfwPolicy,
|
||||||
|
|
||||||
// FIXME: deprecated in 4.1
|
|
||||||
webTorrentEnabled: this.p2pEnabled,
|
|
||||||
p2pEnabled: this.p2pEnabled,
|
p2pEnabled: this.p2pEnabled,
|
||||||
|
|
||||||
videosHistoryEnabled: this.videosHistoryEnabled,
|
videosHistoryEnabled: this.videosHistoryEnabled,
|
||||||
|
|
|
@ -111,7 +111,7 @@ export class AbstractVideoQueryBuilder extends AbstractRunQuery {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected includeWebtorrentFiles () {
|
protected includeWebVideoFiles () {
|
||||||
this.addJoin('LEFT JOIN "videoFile" AS "VideoFiles" ON "VideoFiles"."videoId" = "video"."id"')
|
this.addJoin('LEFT JOIN "videoFile" AS "VideoFiles" ON "VideoFiles"."videoId" = "video"."id"')
|
||||||
|
|
||||||
this.attributes = {
|
this.attributes = {
|
||||||
|
@ -263,7 +263,7 @@ export class AbstractVideoQueryBuilder extends AbstractRunQuery {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected includeWebTorrentRedundancies () {
|
protected includeWebVideoRedundancies () {
|
||||||
this.addJoin(
|
this.addJoin(
|
||||||
'LEFT OUTER JOIN "videoRedundancy" AS "VideoFiles->RedundancyVideos" ON ' +
|
'LEFT OUTER JOIN "videoRedundancy" AS "VideoFiles->RedundancyVideos" ON ' +
|
||||||
'"VideoFiles"."id" = "VideoFiles->RedundancyVideos"."videoFileId"'
|
'"VideoFiles"."id" = "VideoFiles->RedundancyVideos"."videoFileId"'
|
||||||
|
|
|
@ -14,7 +14,7 @@ export type FileQueryOptions = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Fetch files (webtorrent and streaming playlist) according to a video
|
* Fetch files (web videos and streaming playlist) according to a video
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ export class VideoFileQueryBuilder extends AbstractVideoQueryBuilder {
|
||||||
super(sequelize, 'get')
|
super(sequelize, 'get')
|
||||||
}
|
}
|
||||||
|
|
||||||
queryWebTorrentVideos (options: FileQueryOptions) {
|
queryWebVideos (options: FileQueryOptions) {
|
||||||
this.buildWebtorrentFilesQuery(options)
|
this.buildWebVideoFilesQuery(options)
|
||||||
|
|
||||||
return this.runQuery(options)
|
return this.runQuery(options)
|
||||||
}
|
}
|
||||||
|
@ -37,15 +37,15 @@ export class VideoFileQueryBuilder extends AbstractVideoQueryBuilder {
|
||||||
return this.runQuery(options)
|
return this.runQuery(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildWebtorrentFilesQuery (options: FileQueryOptions) {
|
private buildWebVideoFilesQuery (options: FileQueryOptions) {
|
||||||
this.attributes = {
|
this.attributes = {
|
||||||
'"video"."id"': ''
|
'"video"."id"': ''
|
||||||
}
|
}
|
||||||
|
|
||||||
this.includeWebtorrentFiles()
|
this.includeWebVideoFiles()
|
||||||
|
|
||||||
if (options.includeRedundancy) {
|
if (options.includeRedundancy) {
|
||||||
this.includeWebTorrentRedundancies()
|
this.includeWebVideoRedundancies()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.whereId(options)
|
this.whereId(options)
|
||||||
|
|
|
@ -60,10 +60,10 @@ export class VideoModelBuilder {
|
||||||
buildVideosFromRows (options: {
|
buildVideosFromRows (options: {
|
||||||
rows: SQLRow[]
|
rows: SQLRow[]
|
||||||
include?: VideoInclude
|
include?: VideoInclude
|
||||||
rowsWebTorrentFiles?: SQLRow[]
|
rowsWebVideoFiles?: SQLRow[]
|
||||||
rowsStreamingPlaylist?: SQLRow[]
|
rowsStreamingPlaylist?: SQLRow[]
|
||||||
}) {
|
}) {
|
||||||
const { rows, rowsWebTorrentFiles, rowsStreamingPlaylist, include } = options
|
const { rows, rowsWebVideoFiles, rowsStreamingPlaylist, include } = options
|
||||||
|
|
||||||
this.reinit()
|
this.reinit()
|
||||||
|
|
||||||
|
@ -85,8 +85,8 @@ export class VideoModelBuilder {
|
||||||
this.addActorAvatar(row, 'VideoChannel.Account.Actor', accountActor)
|
this.addActorAvatar(row, 'VideoChannel.Account.Actor', accountActor)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rowsWebTorrentFiles) {
|
if (!rowsWebVideoFiles) {
|
||||||
this.addWebTorrentFile(row, videoModel)
|
this.addWebVideoFile(row, videoModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rowsStreamingPlaylist) {
|
if (!rowsStreamingPlaylist) {
|
||||||
|
@ -112,7 +112,7 @@ export class VideoModelBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.grabSeparateWebTorrentFiles(rowsWebTorrentFiles)
|
this.grabSeparateWebVideoFiles(rowsWebVideoFiles)
|
||||||
this.grabSeparateStreamingPlaylistFiles(rowsStreamingPlaylist)
|
this.grabSeparateStreamingPlaylistFiles(rowsStreamingPlaylist)
|
||||||
|
|
||||||
return this.videos
|
return this.videos
|
||||||
|
@ -140,15 +140,15 @@ export class VideoModelBuilder {
|
||||||
this.videos = []
|
this.videos = []
|
||||||
}
|
}
|
||||||
|
|
||||||
private grabSeparateWebTorrentFiles (rowsWebTorrentFiles?: SQLRow[]) {
|
private grabSeparateWebVideoFiles (rowsWebVideoFiles?: SQLRow[]) {
|
||||||
if (!rowsWebTorrentFiles) return
|
if (!rowsWebVideoFiles) return
|
||||||
|
|
||||||
for (const row of rowsWebTorrentFiles) {
|
for (const row of rowsWebVideoFiles) {
|
||||||
const id = row['VideoFiles.id']
|
const id = row['VideoFiles.id']
|
||||||
if (!id) continue
|
if (!id) continue
|
||||||
|
|
||||||
const videoModel = this.videosMemo[row.id]
|
const videoModel = this.videosMemo[row.id]
|
||||||
this.addWebTorrentFile(row, videoModel)
|
this.addWebVideoFile(row, videoModel)
|
||||||
this.addRedundancy(row, 'VideoFiles', this.videoFileMemo[id])
|
this.addRedundancy(row, 'VideoFiles', this.videoFileMemo[id])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,7 +258,7 @@ export class VideoModelBuilder {
|
||||||
this.thumbnailsDone.add(id)
|
this.thumbnailsDone.add(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
private addWebTorrentFile (row: SQLRow, videoModel: VideoModel) {
|
private addWebVideoFile (row: SQLRow, videoModel: VideoModel) {
|
||||||
const id = row['VideoFiles.id']
|
const id = row['VideoFiles.id']
|
||||||
if (!id || this.videoFileMemo[id]) return
|
if (!id || this.videoFileMemo[id]) return
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ export type BuildVideoGetQueryOptions = {
|
||||||
|
|
||||||
export class VideoModelGetQueryBuilder {
|
export class VideoModelGetQueryBuilder {
|
||||||
videoQueryBuilder: VideosModelGetQuerySubBuilder
|
videoQueryBuilder: VideosModelGetQuerySubBuilder
|
||||||
webtorrentFilesQueryBuilder: VideoFileQueryBuilder
|
webVideoFilesQueryBuilder: VideoFileQueryBuilder
|
||||||
streamingPlaylistFilesQueryBuilder: VideoFileQueryBuilder
|
streamingPlaylistFilesQueryBuilder: VideoFileQueryBuilder
|
||||||
|
|
||||||
private readonly videoModelBuilder: VideoModelBuilder
|
private readonly videoModelBuilder: VideoModelBuilder
|
||||||
|
@ -44,7 +44,7 @@ export class VideoModelGetQueryBuilder {
|
||||||
|
|
||||||
constructor (protected readonly sequelize: Sequelize) {
|
constructor (protected readonly sequelize: Sequelize) {
|
||||||
this.videoQueryBuilder = new VideosModelGetQuerySubBuilder(sequelize)
|
this.videoQueryBuilder = new VideosModelGetQuerySubBuilder(sequelize)
|
||||||
this.webtorrentFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
|
this.webVideoFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
|
||||||
this.streamingPlaylistFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
|
this.streamingPlaylistFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
|
||||||
|
|
||||||
this.videoModelBuilder = new VideoModelBuilder('get', new VideoTableAttributes('get'))
|
this.videoModelBuilder = new VideoModelBuilder('get', new VideoTableAttributes('get'))
|
||||||
|
@ -57,11 +57,11 @@ export class VideoModelGetQueryBuilder {
|
||||||
includeRedundancy: this.shouldIncludeRedundancies(options)
|
includeRedundancy: this.shouldIncludeRedundancies(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
const [ videoRows, webtorrentFilesRows, streamingPlaylistFilesRows ] = await Promise.all([
|
const [ videoRows, webVideoFilesRows, streamingPlaylistFilesRows ] = await Promise.all([
|
||||||
this.videoQueryBuilder.queryVideos(options),
|
this.videoQueryBuilder.queryVideos(options),
|
||||||
|
|
||||||
VideoModelGetQueryBuilder.videoFilesInclude.has(options.type)
|
VideoModelGetQueryBuilder.videoFilesInclude.has(options.type)
|
||||||
? this.webtorrentFilesQueryBuilder.queryWebTorrentVideos(fileQueryOptions)
|
? this.webVideoFilesQueryBuilder.queryWebVideos(fileQueryOptions)
|
||||||
: Promise.resolve(undefined),
|
: Promise.resolve(undefined),
|
||||||
|
|
||||||
VideoModelGetQueryBuilder.videoFilesInclude.has(options.type)
|
VideoModelGetQueryBuilder.videoFilesInclude.has(options.type)
|
||||||
|
@ -71,7 +71,7 @@ export class VideoModelGetQueryBuilder {
|
||||||
|
|
||||||
const videos = this.videoModelBuilder.buildVideosFromRows({
|
const videos = this.videoModelBuilder.buildVideosFromRows({
|
||||||
rows: videoRows,
|
rows: videoRows,
|
||||||
rowsWebTorrentFiles: webtorrentFilesRows,
|
rowsWebVideoFiles: webVideoFilesRows,
|
||||||
rowsStreamingPlaylist: streamingPlaylistFilesRows
|
rowsStreamingPlaylist: streamingPlaylistFilesRows
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ export class VideoModelGetQueryBuilder {
|
||||||
export class VideosModelGetQuerySubBuilder extends AbstractVideoQueryBuilder {
|
export class VideosModelGetQuerySubBuilder extends AbstractVideoQueryBuilder {
|
||||||
protected attributes: { [key: string]: string }
|
protected attributes: { [key: string]: string }
|
||||||
|
|
||||||
protected webtorrentFilesQuery: string
|
protected webVideoFilesQuery: string
|
||||||
protected streamingPlaylistFilesQuery: string
|
protected streamingPlaylistFilesQuery: string
|
||||||
|
|
||||||
private static readonly trackersInclude = new Set<GetType>([ 'api' ])
|
private static readonly trackersInclude = new Set<GetType>([ 'api' ])
|
||||||
|
|
|
@ -48,7 +48,9 @@ export type BuildVideosListQueryOptions = {
|
||||||
|
|
||||||
hasFiles?: boolean
|
hasFiles?: boolean
|
||||||
hasHLSFiles?: boolean
|
hasHLSFiles?: boolean
|
||||||
hasWebtorrentFiles?: boolean
|
|
||||||
|
hasWebVideoFiles?: boolean
|
||||||
|
hasWebtorrentFiles?: boolean // TODO: Remove in v7
|
||||||
|
|
||||||
accountId?: number
|
accountId?: number
|
||||||
videoChannelId?: number
|
videoChannelId?: number
|
||||||
|
@ -175,7 +177,9 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exists(options.hasWebtorrentFiles)) {
|
if (exists(options.hasWebtorrentFiles)) {
|
||||||
this.whereWebTorrentFileExists(options.hasWebtorrentFiles)
|
this.whereWebVideoFileExists(options.hasWebtorrentFiles)
|
||||||
|
} else if (exists(options.hasWebVideoFiles)) {
|
||||||
|
this.whereWebVideoFileExists(options.hasWebVideoFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exists(options.hasHLSFiles)) {
|
if (exists(options.hasHLSFiles)) {
|
||||||
|
@ -400,18 +404,18 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
private whereFileExists () {
|
private whereFileExists () {
|
||||||
this.and.push(`(${this.buildWebTorrentFileExistsQuery(true)} OR ${this.buildHLSFileExistsQuery(true)})`)
|
this.and.push(`(${this.buildWebVideoFileExistsQuery(true)} OR ${this.buildHLSFileExistsQuery(true)})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
private whereWebTorrentFileExists (exists: boolean) {
|
private whereWebVideoFileExists (exists: boolean) {
|
||||||
this.and.push(this.buildWebTorrentFileExistsQuery(exists))
|
this.and.push(this.buildWebVideoFileExistsQuery(exists))
|
||||||
}
|
}
|
||||||
|
|
||||||
private whereHLSFileExists (exists: boolean) {
|
private whereHLSFileExists (exists: boolean) {
|
||||||
this.and.push(this.buildHLSFileExistsQuery(exists))
|
this.and.push(this.buildHLSFileExistsQuery(exists))
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildWebTorrentFileExistsQuery (exists: boolean) {
|
private buildWebVideoFileExistsQuery (exists: boolean) {
|
||||||
const prefix = exists ? '' : 'NOT '
|
const prefix = exists ? '' : 'NOT '
|
||||||
|
|
||||||
return prefix + 'EXISTS (SELECT 1 FROM "videoFile" WHERE "videoFile"."videoId" = "video"."id")'
|
return prefix + 'EXISTS (SELECT 1 FROM "videoFile" WHERE "videoFile"."videoId" = "video"."id")'
|
||||||
|
|
|
@ -18,7 +18,7 @@ export class VideosModelListQueryBuilder extends AbstractVideoQueryBuilder {
|
||||||
private innerQuery: string
|
private innerQuery: string
|
||||||
private innerSort: string
|
private innerSort: string
|
||||||
|
|
||||||
webtorrentFilesQueryBuilder: VideoFileQueryBuilder
|
webVideoFilesQueryBuilder: VideoFileQueryBuilder
|
||||||
streamingPlaylistFilesQueryBuilder: VideoFileQueryBuilder
|
streamingPlaylistFilesQueryBuilder: VideoFileQueryBuilder
|
||||||
|
|
||||||
private readonly videoModelBuilder: VideoModelBuilder
|
private readonly videoModelBuilder: VideoModelBuilder
|
||||||
|
@ -27,7 +27,7 @@ export class VideosModelListQueryBuilder extends AbstractVideoQueryBuilder {
|
||||||
super(sequelize, 'list')
|
super(sequelize, 'list')
|
||||||
|
|
||||||
this.videoModelBuilder = new VideoModelBuilder(this.mode, this.tables)
|
this.videoModelBuilder = new VideoModelBuilder(this.mode, this.tables)
|
||||||
this.webtorrentFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
|
this.webVideoFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
|
||||||
this.streamingPlaylistFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
|
this.streamingPlaylistFilesQueryBuilder = new VideoFileQueryBuilder(sequelize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,12 +48,12 @@ export class VideosModelListQueryBuilder extends AbstractVideoQueryBuilder {
|
||||||
includeRedundancy: false
|
includeRedundancy: false
|
||||||
}
|
}
|
||||||
|
|
||||||
const [ rowsWebTorrentFiles, rowsStreamingPlaylist ] = await Promise.all([
|
const [ rowsWebVideoFiles, rowsStreamingPlaylist ] = await Promise.all([
|
||||||
this.webtorrentFilesQueryBuilder.queryWebTorrentVideos(fileQueryOptions),
|
this.webVideoFilesQueryBuilder.queryWebVideos(fileQueryOptions),
|
||||||
this.streamingPlaylistFilesQueryBuilder.queryStreamingPlaylistVideos(fileQueryOptions)
|
this.streamingPlaylistFilesQueryBuilder.queryStreamingPlaylistVideos(fileQueryOptions)
|
||||||
])
|
])
|
||||||
|
|
||||||
return this.videoModelBuilder.buildVideosFromRows({ rows, include: options.include, rowsStreamingPlaylist, rowsWebTorrentFiles })
|
return this.videoModelBuilder.buildVideosFromRows({ rows, include: options.include, rowsStreamingPlaylist, rowsWebVideoFiles })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ enum ScopeNames {
|
||||||
{
|
{
|
||||||
model: VideoModel.scope([
|
model: VideoModel.scope([
|
||||||
VideoScopeNames.WITH_THUMBNAILS,
|
VideoScopeNames.WITH_THUMBNAILS,
|
||||||
VideoScopeNames.WITH_WEBTORRENT_FILES,
|
VideoScopeNames.WITH_WEB_VIDEO_FILES,
|
||||||
VideoScopeNames.WITH_STREAMING_PLAYLISTS,
|
VideoScopeNames.WITH_STREAMING_PLAYLISTS,
|
||||||
VideoScopeNames.WITH_ACCOUNT_DETAILS
|
VideoScopeNames.WITH_ACCOUNT_DETAILS
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -26,8 +26,8 @@ import { buildRemoteVideoBaseUrl } from '@server/lib/activitypub/url'
|
||||||
import {
|
import {
|
||||||
getHLSPrivateFileUrl,
|
getHLSPrivateFileUrl,
|
||||||
getHLSPublicFileUrl,
|
getHLSPublicFileUrl,
|
||||||
getWebTorrentPrivateFileUrl,
|
getWebVideoPrivateFileUrl,
|
||||||
getWebTorrentPublicFileUrl
|
getWebVideoPublicFileUrl
|
||||||
} from '@server/lib/object-storage'
|
} from '@server/lib/object-storage'
|
||||||
import { getFSTorrentFilePath } from '@server/lib/paths'
|
import { getFSTorrentFilePath } from '@server/lib/paths'
|
||||||
import { isVideoInPrivateDirectory } from '@server/lib/video-privacy'
|
import { isVideoInPrivateDirectory } from '@server/lib/video-privacy'
|
||||||
|
@ -276,15 +276,15 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
|
||||||
|
|
||||||
static async doesOwnedTorrentFileExist (filename: string) {
|
static async doesOwnedTorrentFileExist (filename: string) {
|
||||||
const query = 'SELECT 1 FROM "videoFile" ' +
|
const query = 'SELECT 1 FROM "videoFile" ' +
|
||||||
'LEFT JOIN "video" "webtorrent" ON "webtorrent"."id" = "videoFile"."videoId" AND "webtorrent"."remote" IS FALSE ' +
|
'LEFT JOIN "video" "webvideo" ON "webvideo"."id" = "videoFile"."videoId" AND "webvideo"."remote" IS FALSE ' +
|
||||||
'LEFT JOIN "videoStreamingPlaylist" ON "videoStreamingPlaylist"."id" = "videoFile"."videoStreamingPlaylistId" ' +
|
'LEFT JOIN "videoStreamingPlaylist" ON "videoStreamingPlaylist"."id" = "videoFile"."videoStreamingPlaylistId" ' +
|
||||||
'LEFT JOIN "video" "hlsVideo" ON "hlsVideo"."id" = "videoStreamingPlaylist"."videoId" AND "hlsVideo"."remote" IS FALSE ' +
|
'LEFT JOIN "video" "hlsVideo" ON "hlsVideo"."id" = "videoStreamingPlaylist"."videoId" AND "hlsVideo"."remote" IS FALSE ' +
|
||||||
'WHERE "torrentFilename" = $filename AND ("hlsVideo"."id" IS NOT NULL OR "webtorrent"."id" IS NOT NULL) LIMIT 1'
|
'WHERE "torrentFilename" = $filename AND ("hlsVideo"."id" IS NOT NULL OR "webvideo"."id" IS NOT NULL) LIMIT 1'
|
||||||
|
|
||||||
return doesExist(this.sequelize, query, { filename })
|
return doesExist(this.sequelize, query, { filename })
|
||||||
}
|
}
|
||||||
|
|
||||||
static async doesOwnedWebTorrentVideoFileExist (filename: string) {
|
static async doesOwnedWebVideoFileExist (filename: string) {
|
||||||
const query = 'SELECT 1 FROM "videoFile" INNER JOIN "video" ON "video"."id" = "videoFile"."videoId" AND "video"."remote" IS FALSE ' +
|
const query = 'SELECT 1 FROM "videoFile" INNER JOIN "video" ON "video"."id" = "videoFile"."videoId" AND "video"."remote" IS FALSE ' +
|
||||||
`WHERE "filename" = $filename AND "storage" = ${VideoStorage.FILE_SYSTEM} LIMIT 1`
|
`WHERE "filename" = $filename AND "storage" = ${VideoStorage.FILE_SYSTEM} LIMIT 1`
|
||||||
|
|
||||||
|
@ -378,7 +378,7 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
static getStats () {
|
static getStats () {
|
||||||
const webtorrentFilesQuery: FindOptions = {
|
const webVideoFilesQuery: FindOptions = {
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
attributes: [],
|
attributes: [],
|
||||||
|
@ -412,10 +412,10 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
VideoFileModel.aggregate('size', 'SUM', webtorrentFilesQuery),
|
VideoFileModel.aggregate('size', 'SUM', webVideoFilesQuery),
|
||||||
VideoFileModel.aggregate('size', 'SUM', hlsFilesQuery)
|
VideoFileModel.aggregate('size', 'SUM', hlsFilesQuery)
|
||||||
]).then(([ webtorrentResult, hlsResult ]) => ({
|
]).then(([ webVideoResult, hlsResult ]) => ({
|
||||||
totalLocalVideoFilesSize: parseAggregateResult(webtorrentResult) + parseAggregateResult(hlsResult)
|
totalLocalVideoFilesSize: parseAggregateResult(webVideoResult) + parseAggregateResult(hlsResult)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +433,7 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
|
||||||
|
|
||||||
const element = mode === 'streaming-playlist'
|
const element = mode === 'streaming-playlist'
|
||||||
? await VideoFileModel.loadHLSFile({ ...baseFind, playlistId: videoFile.videoStreamingPlaylistId })
|
? await VideoFileModel.loadHLSFile({ ...baseFind, playlistId: videoFile.videoStreamingPlaylistId })
|
||||||
: await VideoFileModel.loadWebTorrentFile({ ...baseFind, videoId: videoFile.videoId })
|
: await VideoFileModel.loadWebVideoFile({ ...baseFind, videoId: videoFile.videoId })
|
||||||
|
|
||||||
if (!element) return videoFile.save({ transaction })
|
if (!element) return videoFile.save({ transaction })
|
||||||
|
|
||||||
|
@ -444,7 +444,7 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
|
||||||
return element.save({ transaction })
|
return element.save({ transaction })
|
||||||
}
|
}
|
||||||
|
|
||||||
static async loadWebTorrentFile (options: {
|
static async loadWebVideoFile (options: {
|
||||||
videoId: number
|
videoId: number
|
||||||
fps: number
|
fps: number
|
||||||
resolution: number
|
resolution: number
|
||||||
|
@ -523,7 +523,7 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
|
||||||
return getHLSPrivateFileUrl(video, this.filename)
|
return getHLSPrivateFileUrl(video, this.filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
return getWebTorrentPrivateFileUrl(this.filename)
|
return getWebVideoPrivateFileUrl(this.filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPublicObjectStorageUrl () {
|
private getPublicObjectStorageUrl () {
|
||||||
|
@ -531,7 +531,7 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
|
||||||
return getHLSPublicFileUrl(this.fileUrl)
|
return getHLSPublicFileUrl(this.fileUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
return getWebTorrentPublicFileUrl(this.fileUrl)
|
return getWebVideoPublicFileUrl(this.fileUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -553,10 +553,10 @@ export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>
|
||||||
getFileStaticPath (video: MVideo) {
|
getFileStaticPath (video: MVideo) {
|
||||||
if (this.isHLS()) return this.getHLSFileStaticPath(video)
|
if (this.isHLS()) return this.getHLSFileStaticPath(video)
|
||||||
|
|
||||||
return this.getWebTorrentFileStaticPath(video)
|
return this.getWebVideoFileStaticPath(video)
|
||||||
}
|
}
|
||||||
|
|
||||||
private getWebTorrentFileStaticPath (video: MVideo) {
|
private getWebVideoFileStaticPath (video: MVideo) {
|
||||||
if (isVideoInPrivateDirectory(video.privacy)) {
|
if (isVideoInPrivateDirectory(video.privacy)) {
|
||||||
return join(STATIC_PATHS.PRIVATE_WEBSEED, this.filename)
|
return join(STATIC_PATHS.PRIVATE_WEBSEED, this.filename)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import {
|
||||||
import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
|
import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
|
||||||
import { InternalEventEmitter } from '@server/lib/internal-event-emitter'
|
import { InternalEventEmitter } from '@server/lib/internal-event-emitter'
|
||||||
import { LiveManager } from '@server/lib/live/live-manager'
|
import { LiveManager } from '@server/lib/live/live-manager'
|
||||||
import { removeHLSFileObjectStorageByFilename, removeHLSObjectStorage, removeWebTorrentObjectStorage } from '@server/lib/object-storage'
|
import { removeHLSFileObjectStorageByFilename, removeHLSObjectStorage, removeWebVideoObjectStorage } from '@server/lib/object-storage'
|
||||||
import { tracer } from '@server/lib/opentelemetry/tracing'
|
import { tracer } from '@server/lib/opentelemetry/tracing'
|
||||||
import { getHLSDirectory, getHLSRedundancyDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths'
|
import { getHLSDirectory, getHLSRedundancyDirectory, getHlsResolutionPlaylistFilename } from '@server/lib/paths'
|
||||||
import { Hooks } from '@server/lib/plugins/hooks'
|
import { Hooks } from '@server/lib/plugins/hooks'
|
||||||
|
@ -151,7 +151,7 @@ export enum ScopeNames {
|
||||||
FOR_API = 'FOR_API',
|
FOR_API = 'FOR_API',
|
||||||
WITH_ACCOUNT_DETAILS = 'WITH_ACCOUNT_DETAILS',
|
WITH_ACCOUNT_DETAILS = 'WITH_ACCOUNT_DETAILS',
|
||||||
WITH_TAGS = 'WITH_TAGS',
|
WITH_TAGS = 'WITH_TAGS',
|
||||||
WITH_WEBTORRENT_FILES = 'WITH_WEBTORRENT_FILES',
|
WITH_WEB_VIDEO_FILES = 'WITH_WEB_VIDEO_FILES',
|
||||||
WITH_SCHEDULED_UPDATE = 'WITH_SCHEDULED_UPDATE',
|
WITH_SCHEDULED_UPDATE = 'WITH_SCHEDULED_UPDATE',
|
||||||
WITH_BLACKLISTED = 'WITH_BLACKLISTED',
|
WITH_BLACKLISTED = 'WITH_BLACKLISTED',
|
||||||
WITH_STREAMING_PLAYLISTS = 'WITH_STREAMING_PLAYLISTS',
|
WITH_STREAMING_PLAYLISTS = 'WITH_STREAMING_PLAYLISTS',
|
||||||
|
@ -290,7 +290,7 @@ export type ForAPIOptions = {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
[ScopeNames.WITH_WEBTORRENT_FILES]: (withRedundancies = false) => {
|
[ScopeNames.WITH_WEB_VIDEO_FILES]: (withRedundancies = false) => {
|
||||||
let subInclude: any[] = []
|
let subInclude: any[] = []
|
||||||
|
|
||||||
if (withRedundancies === true) {
|
if (withRedundancies === true) {
|
||||||
|
@ -813,7 +813,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
||||||
|
|
||||||
// Remove physical files and torrents
|
// Remove physical files and torrents
|
||||||
instance.VideoFiles.forEach(file => {
|
instance.VideoFiles.forEach(file => {
|
||||||
tasks.push(instance.removeWebTorrentFile(file))
|
tasks.push(instance.removeWebVideoFile(file))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Remove playlists file
|
// Remove playlists file
|
||||||
|
@ -1107,7 +1107,10 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
||||||
include?: VideoInclude
|
include?: VideoInclude
|
||||||
|
|
||||||
hasFiles?: boolean // default false
|
hasFiles?: boolean // default false
|
||||||
hasWebtorrentFiles?: boolean
|
|
||||||
|
hasWebtorrentFiles?: boolean // TODO: remove in v7
|
||||||
|
hasWebVideoFiles?: boolean
|
||||||
|
|
||||||
hasHLSFiles?: boolean
|
hasHLSFiles?: boolean
|
||||||
|
|
||||||
categoryOneOf?: number[]
|
categoryOneOf?: number[]
|
||||||
|
@ -1172,6 +1175,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
||||||
'historyOfUser',
|
'historyOfUser',
|
||||||
'hasHLSFiles',
|
'hasHLSFiles',
|
||||||
'hasWebtorrentFiles',
|
'hasWebtorrentFiles',
|
||||||
|
'hasWebVideoFiles',
|
||||||
'search',
|
'search',
|
||||||
'excludeAlreadyWatched'
|
'excludeAlreadyWatched'
|
||||||
]),
|
]),
|
||||||
|
@ -1205,7 +1209,9 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
||||||
|
|
||||||
user?: MUserAccountId
|
user?: MUserAccountId
|
||||||
|
|
||||||
hasWebtorrentFiles?: boolean
|
hasWebtorrentFiles?: boolean // TODO: remove in v7
|
||||||
|
hasWebVideoFiles?: boolean
|
||||||
|
|
||||||
hasHLSFiles?: boolean
|
hasHLSFiles?: boolean
|
||||||
|
|
||||||
search?: string
|
search?: string
|
||||||
|
@ -1252,6 +1258,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
||||||
'durationMax',
|
'durationMax',
|
||||||
'hasHLSFiles',
|
'hasHLSFiles',
|
||||||
'hasWebtorrentFiles',
|
'hasWebtorrentFiles',
|
||||||
|
'hasWebVideoFiles',
|
||||||
'uuids',
|
'uuids',
|
||||||
'search',
|
'search',
|
||||||
'displayOnlyForFollower',
|
'displayOnlyForFollower',
|
||||||
|
@ -1676,7 +1683,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
||||||
return this.getQualityFileBy(minBy)
|
return this.getQualityFileBy(minBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
getWebTorrentFile<T extends MVideoWithFile> (this: T, resolution: number): MVideoFileVideo {
|
getWebVideoFile<T extends MVideoWithFile> (this: T, resolution: number): MVideoFileVideo {
|
||||||
if (Array.isArray(this.VideoFiles) === false) return undefined
|
if (Array.isArray(this.VideoFiles) === false) return undefined
|
||||||
|
|
||||||
const file = this.VideoFiles.find(f => f.resolution === resolution)
|
const file = this.VideoFiles.find(f => f.resolution === resolution)
|
||||||
|
@ -1685,7 +1692,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
||||||
return Object.assign(file, { Video: this })
|
return Object.assign(file, { Video: this })
|
||||||
}
|
}
|
||||||
|
|
||||||
hasWebTorrentFiles () {
|
hasWebVideoFiles () {
|
||||||
return Array.isArray(this.VideoFiles) === true && this.VideoFiles.length !== 0
|
return Array.isArray(this.VideoFiles) === true && this.VideoFiles.length !== 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1884,7 +1891,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
||||||
.concat(toAdd)
|
.concat(toAdd)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeWebTorrentFile (videoFile: MVideoFile, isRedundancy = false) {
|
removeWebVideoFile (videoFile: MVideoFile, isRedundancy = false) {
|
||||||
const filePath = isRedundancy
|
const filePath = isRedundancy
|
||||||
? VideoPathManager.Instance.getFSRedundancyVideoFilePath(this, videoFile)
|
? VideoPathManager.Instance.getFSRedundancyVideoFilePath(this, videoFile)
|
||||||
: VideoPathManager.Instance.getFSVideoFileOutputPath(this, videoFile)
|
: VideoPathManager.Instance.getFSVideoFileOutputPath(this, videoFile)
|
||||||
|
@ -1893,7 +1900,7 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
|
||||||
if (!isRedundancy) promises.push(videoFile.removeTorrent())
|
if (!isRedundancy) promises.push(videoFile.removeTorrent())
|
||||||
|
|
||||||
if (videoFile.storage === VideoStorage.OBJECT_STORAGE) {
|
if (videoFile.storage === VideoStorage.OBJECT_STORAGE) {
|
||||||
promises.push(removeWebTorrentObjectStorage(videoFile))
|
promises.push(removeWebVideoObjectStorage(videoFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
|
|
|
@ -345,7 +345,7 @@ describe('Test config API validators', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should fail with a disabled webtorrent & hls transcoding', async function () {
|
it('Should fail with a disabled web videos & hls transcoding', async function () {
|
||||||
const newUpdateParams = {
|
const newUpdateParams = {
|
||||||
...updateParams,
|
...updateParams,
|
||||||
|
|
||||||
|
|
|
@ -49,21 +49,21 @@ describe('Test transcoding API validators', function () {
|
||||||
|
|
||||||
it('Should not run transcoding of a unknown video', async function () {
|
it('Should not run transcoding of a unknown video', async function () {
|
||||||
await servers[0].videos.runTranscoding({ videoId: 404, transcodingType: 'hls', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
await servers[0].videos.runTranscoding({ videoId: 404, transcodingType: 'hls', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
||||||
await servers[0].videos.runTranscoding({ videoId: 404, transcodingType: 'webtorrent', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
await servers[0].videos.runTranscoding({ videoId: 404, transcodingType: 'web-video', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not run transcoding of a remote video', async function () {
|
it('Should not run transcoding of a remote video', async function () {
|
||||||
const expectedStatus = HttpStatusCode.BAD_REQUEST_400
|
const expectedStatus = HttpStatusCode.BAD_REQUEST_400
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({ videoId: remoteId, transcodingType: 'hls', expectedStatus })
|
await servers[0].videos.runTranscoding({ videoId: remoteId, transcodingType: 'hls', expectedStatus })
|
||||||
await servers[0].videos.runTranscoding({ videoId: remoteId, transcodingType: 'webtorrent', expectedStatus })
|
await servers[0].videos.runTranscoding({ videoId: remoteId, transcodingType: 'web-video', expectedStatus })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not run transcoding by a non admin user', async function () {
|
it('Should not run transcoding by a non admin user', async function () {
|
||||||
const expectedStatus = HttpStatusCode.FORBIDDEN_403
|
const expectedStatus = HttpStatusCode.FORBIDDEN_403
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls', token: userToken, expectedStatus })
|
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls', token: userToken, expectedStatus })
|
||||||
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'webtorrent', token: moderatorToken, expectedStatus })
|
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video', token: moderatorToken, expectedStatus })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not run transcoding without transcoding type', async function () {
|
it('Should not run transcoding without transcoding type', async function () {
|
||||||
|
@ -82,7 +82,7 @@ describe('Test transcoding API validators', function () {
|
||||||
await servers[0].config.disableTranscoding()
|
await servers[0].config.disableTranscoding()
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls', expectedStatus })
|
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls', expectedStatus })
|
||||||
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'webtorrent', expectedStatus })
|
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video', expectedStatus })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should run transcoding', async function () {
|
it('Should run transcoding', async function () {
|
||||||
|
@ -93,15 +93,15 @@ describe('Test transcoding API validators', function () {
|
||||||
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls' })
|
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls' })
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'webtorrent' })
|
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video' })
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not run transcoding on a video that is already being transcoded', async function () {
|
it('Should not run transcoding on a video that is already being transcoded', async function () {
|
||||||
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'webtorrent' })
|
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video' })
|
||||||
|
|
||||||
const expectedStatus = HttpStatusCode.CONFLICT_409
|
const expectedStatus = HttpStatusCode.CONFLICT_409
|
||||||
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'webtorrent', expectedStatus })
|
await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video', expectedStatus })
|
||||||
})
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
|
|
|
@ -60,7 +60,7 @@ describe('Test videos files', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Deleting files', function () {
|
describe('Deleting files', function () {
|
||||||
let webtorrentId: string
|
let webVideoId: string
|
||||||
let hlsId: string
|
let hlsId: string
|
||||||
let remoteId: string
|
let remoteId: string
|
||||||
|
|
||||||
|
@ -68,10 +68,10 @@ describe('Test videos files', function () {
|
||||||
let validId2: string
|
let validId2: string
|
||||||
|
|
||||||
let hlsFileId: number
|
let hlsFileId: number
|
||||||
let webtorrentFileId: number
|
let webVideoFileId: number
|
||||||
|
|
||||||
let remoteHLSFileId: number
|
let remoteHLSFileId: number
|
||||||
let remoteWebtorrentFileId: number
|
let remoteWebVideoFileId: number
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
this.timeout(300_000)
|
this.timeout(300_000)
|
||||||
|
@ -83,7 +83,7 @@ describe('Test videos files', function () {
|
||||||
const video = await servers[1].videos.get({ id: uuid })
|
const video = await servers[1].videos.get({ id: uuid })
|
||||||
remoteId = video.uuid
|
remoteId = video.uuid
|
||||||
remoteHLSFileId = video.streamingPlaylists[0].files[0].id
|
remoteHLSFileId = video.streamingPlaylists[0].files[0].id
|
||||||
remoteWebtorrentFileId = video.files[0].id
|
remoteWebVideoFileId = video.files[0].id
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -96,7 +96,7 @@ describe('Test videos files', function () {
|
||||||
const video = await servers[0].videos.get({ id: uuid })
|
const video = await servers[0].videos.get({ id: uuid })
|
||||||
validId1 = video.uuid
|
validId1 = video.uuid
|
||||||
hlsFileId = video.streamingPlaylists[0].files[0].id
|
hlsFileId = video.streamingPlaylists[0].files[0].id
|
||||||
webtorrentFileId = video.files[0].id
|
webVideoFileId = video.files[0].id
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -117,8 +117,8 @@ describe('Test videos files', function () {
|
||||||
|
|
||||||
{
|
{
|
||||||
await servers[0].config.enableTranscoding(false, true)
|
await servers[0].config.enableTranscoding(false, true)
|
||||||
const { uuid } = await servers[0].videos.quickUpload({ name: 'webtorrent' })
|
const { uuid } = await servers[0].videos.quickUpload({ name: 'web-video' })
|
||||||
webtorrentId = uuid
|
webVideoId = uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
@ -128,27 +128,27 @@ describe('Test videos files', function () {
|
||||||
const expectedStatus = HttpStatusCode.NOT_FOUND_404
|
const expectedStatus = HttpStatusCode.NOT_FOUND_404
|
||||||
|
|
||||||
await servers[0].videos.removeHLSPlaylist({ videoId: 404, expectedStatus })
|
await servers[0].videos.removeHLSPlaylist({ videoId: 404, expectedStatus })
|
||||||
await servers[0].videos.removeAllWebTorrentFiles({ videoId: 404, expectedStatus })
|
await servers[0].videos.removeAllWebVideoFiles({ videoId: 404, expectedStatus })
|
||||||
|
|
||||||
await servers[0].videos.removeHLSFile({ videoId: 404, fileId: hlsFileId, expectedStatus })
|
await servers[0].videos.removeHLSFile({ videoId: 404, fileId: hlsFileId, expectedStatus })
|
||||||
await servers[0].videos.removeWebTorrentFile({ videoId: 404, fileId: webtorrentFileId, expectedStatus })
|
await servers[0].videos.removeWebVideoFile({ videoId: 404, fileId: webVideoFileId, expectedStatus })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not delete unknown files', async function () {
|
it('Should not delete unknown files', async function () {
|
||||||
const expectedStatus = HttpStatusCode.NOT_FOUND_404
|
const expectedStatus = HttpStatusCode.NOT_FOUND_404
|
||||||
|
|
||||||
await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: webtorrentFileId, expectedStatus })
|
await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: webVideoFileId, expectedStatus })
|
||||||
await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: hlsFileId, expectedStatus })
|
await servers[0].videos.removeWebVideoFile({ videoId: validId1, fileId: hlsFileId, expectedStatus })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not delete files of a remote video', async function () {
|
it('Should not delete files of a remote video', async function () {
|
||||||
const expectedStatus = HttpStatusCode.BAD_REQUEST_400
|
const expectedStatus = HttpStatusCode.BAD_REQUEST_400
|
||||||
|
|
||||||
await servers[0].videos.removeHLSPlaylist({ videoId: remoteId, expectedStatus })
|
await servers[0].videos.removeHLSPlaylist({ videoId: remoteId, expectedStatus })
|
||||||
await servers[0].videos.removeAllWebTorrentFiles({ videoId: remoteId, expectedStatus })
|
await servers[0].videos.removeAllWebVideoFiles({ videoId: remoteId, expectedStatus })
|
||||||
|
|
||||||
await servers[0].videos.removeHLSFile({ videoId: remoteId, fileId: remoteHLSFileId, expectedStatus })
|
await servers[0].videos.removeHLSFile({ videoId: remoteId, fileId: remoteHLSFileId, expectedStatus })
|
||||||
await servers[0].videos.removeWebTorrentFile({ videoId: remoteId, fileId: remoteWebtorrentFileId, expectedStatus })
|
await servers[0].videos.removeWebVideoFile({ videoId: remoteId, fileId: remoteWebVideoFileId, expectedStatus })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not delete files by a non admin user', async function () {
|
it('Should not delete files by a non admin user', async function () {
|
||||||
|
@ -157,35 +157,35 @@ describe('Test videos files', function () {
|
||||||
await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: userToken, expectedStatus })
|
await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: userToken, expectedStatus })
|
||||||
await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: moderatorToken, expectedStatus })
|
await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: moderatorToken, expectedStatus })
|
||||||
|
|
||||||
await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId1, token: userToken, expectedStatus })
|
await servers[0].videos.removeAllWebVideoFiles({ videoId: validId1, token: userToken, expectedStatus })
|
||||||
await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId1, token: moderatorToken, expectedStatus })
|
await servers[0].videos.removeAllWebVideoFiles({ videoId: validId1, token: moderatorToken, expectedStatus })
|
||||||
|
|
||||||
await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId, token: userToken, expectedStatus })
|
await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId, token: userToken, expectedStatus })
|
||||||
await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId, token: moderatorToken, expectedStatus })
|
await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId, token: moderatorToken, expectedStatus })
|
||||||
|
|
||||||
await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId, token: userToken, expectedStatus })
|
await servers[0].videos.removeWebVideoFile({ videoId: validId1, fileId: webVideoFileId, token: userToken, expectedStatus })
|
||||||
await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId, token: moderatorToken, expectedStatus })
|
await servers[0].videos.removeWebVideoFile({ videoId: validId1, fileId: webVideoFileId, token: moderatorToken, expectedStatus })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not delete files if the files are not available', async function () {
|
it('Should not delete files if the files are not available', async function () {
|
||||||
await servers[0].videos.removeHLSPlaylist({ videoId: hlsId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
await servers[0].videos.removeHLSPlaylist({ videoId: hlsId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
||||||
await servers[0].videos.removeAllWebTorrentFiles({ videoId: webtorrentId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
await servers[0].videos.removeAllWebVideoFiles({ videoId: webVideoId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
||||||
|
|
||||||
await servers[0].videos.removeHLSFile({ videoId: hlsId, fileId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
await servers[0].videos.removeHLSFile({ videoId: hlsId, fileId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
||||||
await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentId, fileId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
await servers[0].videos.removeWebVideoFile({ videoId: webVideoId, fileId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should not delete files if no both versions are available', async function () {
|
it('Should not delete files if no both versions are available', async function () {
|
||||||
await servers[0].videos.removeHLSPlaylist({ videoId: hlsId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
await servers[0].videos.removeHLSPlaylist({ videoId: hlsId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
||||||
await servers[0].videos.removeAllWebTorrentFiles({ videoId: webtorrentId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
await servers[0].videos.removeAllWebVideoFiles({ videoId: webVideoId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should delete files if both versions are available', async function () {
|
it('Should delete files if both versions are available', async function () {
|
||||||
await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId })
|
await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId })
|
||||||
await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId })
|
await servers[0].videos.removeWebVideoFile({ videoId: validId1, fileId: webVideoFileId })
|
||||||
|
|
||||||
await servers[0].videos.removeHLSPlaylist({ videoId: validId1 })
|
await servers[0].videos.removeHLSPlaylist({ videoId: validId1 })
|
||||||
await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId2 })
|
await servers[0].videos.removeAllWebVideoFiles({ videoId: validId2 })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,7 @@ describe('Object storage for video static file privacy', function () {
|
||||||
const video = await server.videos.getWithToken({ id: videoId })
|
const video = await server.videos.getWithToken({ id: videoId })
|
||||||
|
|
||||||
return {
|
return {
|
||||||
webTorrentFile: video.files[0].fileUrl,
|
webVideoFile: video.files[0].fileUrl,
|
||||||
hlsFile: getHLS(video).files[0].fileUrl
|
hlsFile: getHLS(video).files[0].fileUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,10 +175,10 @@ describe('Object storage for video static file privacy', function () {
|
||||||
it('Should not get files without appropriate OAuth token', async function () {
|
it('Should not get files without appropriate OAuth token', async function () {
|
||||||
this.timeout(60000)
|
this.timeout(60000)
|
||||||
|
|
||||||
const { webTorrentFile, hlsFile } = await getSampleFileUrls(privateVideoUUID)
|
const { webVideoFile, hlsFile } = await getSampleFileUrls(privateVideoUUID)
|
||||||
|
|
||||||
await makeRawRequest({ url: webTorrentFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
await makeRawRequest({ url: webVideoFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
||||||
await makeRawRequest({ url: webTorrentFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
|
await makeRawRequest({ url: webVideoFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
|
||||||
|
|
||||||
await makeRawRequest({ url: hlsFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
await makeRawRequest({ url: hlsFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
||||||
await makeRawRequest({ url: hlsFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
|
await makeRawRequest({ url: hlsFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
|
||||||
|
@ -187,18 +187,18 @@ describe('Object storage for video static file privacy', function () {
|
||||||
it('Should not get files without appropriate password or appropriate OAuth token', async function () {
|
it('Should not get files without appropriate password or appropriate OAuth token', async function () {
|
||||||
this.timeout(60000)
|
this.timeout(60000)
|
||||||
|
|
||||||
const { webTorrentFile, hlsFile } = await getSampleFileUrls(passwordProtectedVideoUUID)
|
const { webVideoFile, hlsFile } = await getSampleFileUrls(passwordProtectedVideoUUID)
|
||||||
|
|
||||||
await makeRawRequest({ url: webTorrentFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
await makeRawRequest({ url: webVideoFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
||||||
await makeRawRequest({
|
await makeRawRequest({
|
||||||
url: webTorrentFile,
|
url: webVideoFile,
|
||||||
token: null,
|
token: null,
|
||||||
headers: incorrectPasswordHeader,
|
headers: incorrectPasswordHeader,
|
||||||
expectedStatus: HttpStatusCode.FORBIDDEN_403
|
expectedStatus: HttpStatusCode.FORBIDDEN_403
|
||||||
})
|
})
|
||||||
await makeRawRequest({ url: webTorrentFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
|
await makeRawRequest({ url: webVideoFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
|
||||||
await makeRawRequest({
|
await makeRawRequest({
|
||||||
url: webTorrentFile,
|
url: webVideoFile,
|
||||||
token: null,
|
token: null,
|
||||||
headers: correctPasswordHeader,
|
headers: correctPasswordHeader,
|
||||||
expectedStatus: HttpStatusCode.OK_200
|
expectedStatus: HttpStatusCode.OK_200
|
||||||
|
@ -239,9 +239,9 @@ describe('Object storage for video static file privacy', function () {
|
||||||
const badVideoFileToken = await server.videoToken.getVideoFileToken({ token: userToken, videoId: userPrivateVideoUUID })
|
const badVideoFileToken = await server.videoToken.getVideoFileToken({ token: userToken, videoId: userPrivateVideoUUID })
|
||||||
const goodVideoFileToken = await server.videoToken.getVideoFileToken({ videoId: privateVideoUUID })
|
const goodVideoFileToken = await server.videoToken.getVideoFileToken({ videoId: privateVideoUUID })
|
||||||
|
|
||||||
const { webTorrentFile, hlsFile } = await getSampleFileUrls(privateVideoUUID)
|
const { webVideoFile, hlsFile } = await getSampleFileUrls(privateVideoUUID)
|
||||||
|
|
||||||
for (const url of [ webTorrentFile, hlsFile ]) {
|
for (const url of [ webVideoFile, hlsFile ]) {
|
||||||
await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
||||||
await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
||||||
await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
|
await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
|
||||||
|
@ -261,9 +261,9 @@ describe('Object storage for video static file privacy', function () {
|
||||||
videoPassword: correctPassword
|
videoPassword: correctPassword
|
||||||
})
|
})
|
||||||
|
|
||||||
const { webTorrentFile, hlsFile } = await getSampleFileUrls(passwordProtectedVideoUUID)
|
const { webVideoFile, hlsFile } = await getSampleFileUrls(passwordProtectedVideoUUID)
|
||||||
|
|
||||||
for (const url of [ hlsFile, webTorrentFile ]) {
|
for (const url of [ hlsFile, webVideoFile ]) {
|
||||||
await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
||||||
await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
|
||||||
await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
|
await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
|
||||||
|
@ -534,11 +534,11 @@ describe('Object storage for video static file privacy', function () {
|
||||||
|
|
||||||
it('Should not be able to access object storage proxy', async function () {
|
it('Should not be able to access object storage proxy', async function () {
|
||||||
const privateVideo = await server.videos.getWithToken({ id: videoUUID })
|
const privateVideo = await server.videos.getWithToken({ id: videoUUID })
|
||||||
const webtorrentFilename = extractFilenameFromUrl(privateVideo.files[0].fileUrl)
|
const webVideoFilename = extractFilenameFromUrl(privateVideo.files[0].fileUrl)
|
||||||
const hlsFilename = extractFilenameFromUrl(getHLS(privateVideo).files[0].fileUrl)
|
const hlsFilename = extractFilenameFromUrl(getHLS(privateVideo).files[0].fileUrl)
|
||||||
|
|
||||||
await makeRawRequest({
|
await makeRawRequest({
|
||||||
url: server.url + '/object-storage-proxy/webseed/private/' + webtorrentFilename,
|
url: server.url + '/object-storage-proxy/webseed/private/' + webVideoFilename,
|
||||||
token: server.accessToken,
|
token: server.accessToken,
|
||||||
expectedStatus: HttpStatusCode.BAD_REQUEST_400
|
expectedStatus: HttpStatusCode.BAD_REQUEST_400
|
||||||
})
|
})
|
||||||
|
|
|
@ -41,8 +41,8 @@ async function checkFiles (options: {
|
||||||
playlistBucket: string
|
playlistBucket: string
|
||||||
playlistPrefix?: string
|
playlistPrefix?: string
|
||||||
|
|
||||||
webtorrentBucket: string
|
webVideoBucket: string
|
||||||
webtorrentPrefix?: string
|
webVideoPrefix?: string
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
server,
|
server,
|
||||||
|
@ -50,20 +50,20 @@ async function checkFiles (options: {
|
||||||
originSQLCommand,
|
originSQLCommand,
|
||||||
video,
|
video,
|
||||||
playlistBucket,
|
playlistBucket,
|
||||||
webtorrentBucket,
|
webVideoBucket,
|
||||||
baseMockUrl,
|
baseMockUrl,
|
||||||
playlistPrefix,
|
playlistPrefix,
|
||||||
webtorrentPrefix
|
webVideoPrefix
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
let allFiles = video.files
|
let allFiles = video.files
|
||||||
|
|
||||||
for (const file of video.files) {
|
for (const file of video.files) {
|
||||||
const baseUrl = baseMockUrl
|
const baseUrl = baseMockUrl
|
||||||
? `${baseMockUrl}/${webtorrentBucket}/`
|
? `${baseMockUrl}/${webVideoBucket}/`
|
||||||
: `http://${webtorrentBucket}.${ObjectStorageCommand.getMockEndpointHost()}/`
|
: `http://${webVideoBucket}.${ObjectStorageCommand.getMockEndpointHost()}/`
|
||||||
|
|
||||||
const prefix = webtorrentPrefix || ''
|
const prefix = webVideoPrefix || ''
|
||||||
const start = baseUrl + prefix
|
const start = baseUrl + prefix
|
||||||
|
|
||||||
expectStartWith(file.fileUrl, start)
|
expectStartWith(file.fileUrl, start)
|
||||||
|
@ -134,8 +134,8 @@ function runTestSuite (options: {
|
||||||
playlistBucket: string
|
playlistBucket: string
|
||||||
playlistPrefix?: string
|
playlistPrefix?: string
|
||||||
|
|
||||||
webtorrentBucket: string
|
webVideoBucket: string
|
||||||
webtorrentPrefix?: string
|
webVideoPrefix?: string
|
||||||
|
|
||||||
useMockBaseUrl?: boolean
|
useMockBaseUrl?: boolean
|
||||||
}) {
|
}) {
|
||||||
|
@ -161,7 +161,7 @@ function runTestSuite (options: {
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
await objectStorage.createMockBucket(options.playlistBucket)
|
await objectStorage.createMockBucket(options.playlistBucket)
|
||||||
await objectStorage.createMockBucket(options.webtorrentBucket)
|
await objectStorage.createMockBucket(options.webVideoBucket)
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
object_storage: {
|
object_storage: {
|
||||||
|
@ -182,10 +182,10 @@ function runTestSuite (options: {
|
||||||
},
|
},
|
||||||
|
|
||||||
videos: {
|
videos: {
|
||||||
bucket_name: options.webtorrentBucket,
|
bucket_name: options.webVideoBucket,
|
||||||
prefix: options.webtorrentPrefix,
|
prefix: options.webVideoPrefix,
|
||||||
base_url: baseMockUrl
|
base_url: baseMockUrl
|
||||||
? `${baseMockUrl}/${options.webtorrentBucket}`
|
? `${baseMockUrl}/${options.webVideoBucket}`
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -386,27 +386,27 @@ describe('Object storage for videos', function () {
|
||||||
describe('Test simple object storage', function () {
|
describe('Test simple object storage', function () {
|
||||||
runTestSuite({
|
runTestSuite({
|
||||||
playlistBucket: objectStorage.getMockBucketName('streaming-playlists'),
|
playlistBucket: objectStorage.getMockBucketName('streaming-playlists'),
|
||||||
webtorrentBucket: objectStorage.getMockBucketName('videos')
|
webVideoBucket: objectStorage.getMockBucketName('videos')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Test object storage with prefix', function () {
|
describe('Test object storage with prefix', function () {
|
||||||
runTestSuite({
|
runTestSuite({
|
||||||
playlistBucket: objectStorage.getMockBucketName('mybucket'),
|
playlistBucket: objectStorage.getMockBucketName('mybucket'),
|
||||||
webtorrentBucket: objectStorage.getMockBucketName('mybucket'),
|
webVideoBucket: objectStorage.getMockBucketName('mybucket'),
|
||||||
|
|
||||||
playlistPrefix: 'streaming-playlists_',
|
playlistPrefix: 'streaming-playlists_',
|
||||||
webtorrentPrefix: 'webtorrent_'
|
webVideoPrefix: 'webvideo_'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Test object storage with prefix and base URL', function () {
|
describe('Test object storage with prefix and base URL', function () {
|
||||||
runTestSuite({
|
runTestSuite({
|
||||||
playlistBucket: objectStorage.getMockBucketName('mybucket'),
|
playlistBucket: objectStorage.getMockBucketName('mybucket'),
|
||||||
webtorrentBucket: objectStorage.getMockBucketName('mybucket'),
|
webVideoBucket: objectStorage.getMockBucketName('mybucket'),
|
||||||
|
|
||||||
playlistPrefix: 'streaming-playlists/',
|
playlistPrefix: 'streaming-playlists/',
|
||||||
webtorrentPrefix: 'webtorrent/',
|
webVideoPrefix: 'webvideo/',
|
||||||
|
|
||||||
useMockBaseUrl: true
|
useMockBaseUrl: true
|
||||||
})
|
})
|
||||||
|
@ -431,7 +431,7 @@ describe('Object storage for videos', function () {
|
||||||
runTestSuite({
|
runTestSuite({
|
||||||
maxUploadPart,
|
maxUploadPart,
|
||||||
playlistBucket: objectStorage.getMockBucketName('streaming-playlists'),
|
playlistBucket: objectStorage.getMockBucketName('streaming-playlists'),
|
||||||
webtorrentBucket: objectStorage.getMockBucketName('videos'),
|
webVideoBucket: objectStorage.getMockBucketName('videos'),
|
||||||
fixture
|
fixture
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -43,7 +43,7 @@ async function checkMagnetWebseeds (file: VideoFile, baseWebseeds: string[], ser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createServers (strategy: VideoRedundancyStrategy | null, additionalParams: any = {}, withWebtorrent = true) {
|
async function createServers (strategy: VideoRedundancyStrategy | null, additionalParams: any = {}, withWebVideo = true) {
|
||||||
const strategies: any[] = []
|
const strategies: any[] = []
|
||||||
|
|
||||||
if (strategy !== null) {
|
if (strategy !== null) {
|
||||||
|
@ -61,7 +61,7 @@ async function createServers (strategy: VideoRedundancyStrategy | null, addition
|
||||||
const config = {
|
const config = {
|
||||||
transcoding: {
|
transcoding: {
|
||||||
webtorrent: {
|
webtorrent: {
|
||||||
enabled: withWebtorrent
|
enabled: withWebVideo
|
||||||
},
|
},
|
||||||
hls: {
|
hls: {
|
||||||
enabled: true
|
enabled: true
|
||||||
|
@ -100,7 +100,7 @@ async function createServers (strategy: VideoRedundancyStrategy | null, addition
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureSameFilenames (videoUUID: string) {
|
async function ensureSameFilenames (videoUUID: string) {
|
||||||
let webtorrentFilenames: string[]
|
let webVideoFilenames: string[]
|
||||||
let hlsFilenames: string[]
|
let hlsFilenames: string[]
|
||||||
|
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
|
@ -108,17 +108,17 @@ async function ensureSameFilenames (videoUUID: string) {
|
||||||
|
|
||||||
// Ensure we use the same filenames that the origin
|
// Ensure we use the same filenames that the origin
|
||||||
|
|
||||||
const localWebtorrentFilenames = video.files.map(f => basename(f.fileUrl)).sort()
|
const localWebVideoFilenames = video.files.map(f => basename(f.fileUrl)).sort()
|
||||||
const localHLSFilenames = video.streamingPlaylists[0].files.map(f => basename(f.fileUrl)).sort()
|
const localHLSFilenames = video.streamingPlaylists[0].files.map(f => basename(f.fileUrl)).sort()
|
||||||
|
|
||||||
if (webtorrentFilenames) expect(webtorrentFilenames).to.deep.equal(localWebtorrentFilenames)
|
if (webVideoFilenames) expect(webVideoFilenames).to.deep.equal(localWebVideoFilenames)
|
||||||
else webtorrentFilenames = localWebtorrentFilenames
|
else webVideoFilenames = localWebVideoFilenames
|
||||||
|
|
||||||
if (hlsFilenames) expect(hlsFilenames).to.deep.equal(localHLSFilenames)
|
if (hlsFilenames) expect(hlsFilenames).to.deep.equal(localHLSFilenames)
|
||||||
else hlsFilenames = localHLSFilenames
|
else hlsFilenames = localHLSFilenames
|
||||||
}
|
}
|
||||||
|
|
||||||
return { webtorrentFilenames, hlsFilenames }
|
return { webVideoFilenames, hlsFilenames }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function check1WebSeed (videoUUID?: string) {
|
async function check1WebSeed (videoUUID?: string) {
|
||||||
|
@ -156,7 +156,7 @@ async function check2Webseeds (videoUUID?: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { webtorrentFilenames } = await ensureSameFilenames(videoUUID)
|
const { webVideoFilenames } = await ensureSameFilenames(videoUUID)
|
||||||
|
|
||||||
const directories = [
|
const directories = [
|
||||||
servers[0].getDirectoryPath('redundancy'),
|
servers[0].getDirectoryPath('redundancy'),
|
||||||
|
@ -168,7 +168,7 @@ async function check2Webseeds (videoUUID?: string) {
|
||||||
expect(files).to.have.length.at.least(4)
|
expect(files).to.have.length.at.least(4)
|
||||||
|
|
||||||
// Ensure we files exist on disk
|
// Ensure we files exist on disk
|
||||||
expect(files.find(f => webtorrentFilenames.includes(f))).to.exist
|
expect(files.find(f => webVideoFilenames.includes(f))).to.exist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
describe('Test audio only video transcoding', function () {
|
describe('Test audio only video transcoding', function () {
|
||||||
let servers: PeerTubeServer[] = []
|
let servers: PeerTubeServer[] = []
|
||||||
let videoUUID: string
|
let videoUUID: string
|
||||||
let webtorrentAudioFileUrl: string
|
let webVideoAudioFileUrl: string
|
||||||
let fragmentedAudioFileUrl: string
|
let fragmentedAudioFileUrl: string
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
|
@ -71,7 +71,7 @@ describe('Test audio only video transcoding', function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server.serverNumber === 1) {
|
if (server.serverNumber === 1) {
|
||||||
webtorrentAudioFileUrl = video.files[2].fileUrl
|
webVideoAudioFileUrl = video.files[2].fileUrl
|
||||||
fragmentedAudioFileUrl = video.streamingPlaylists[0].files[2].fileUrl
|
fragmentedAudioFileUrl = video.streamingPlaylists[0].files[2].fileUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ describe('Test audio only video transcoding', function () {
|
||||||
|
|
||||||
it('0p transcoded video should not have video', async function () {
|
it('0p transcoded video should not have video', async function () {
|
||||||
const paths = [
|
const paths = [
|
||||||
servers[0].servers.buildWebTorrentFilePath(webtorrentAudioFileUrl),
|
servers[0].servers.buildWebVideoFilePath(webVideoAudioFileUrl),
|
||||||
servers[0].servers.buildFragmentedFilePath(videoUUID, fragmentedAudioFileUrl)
|
servers[0].servers.buildFragmentedFilePath(videoUUID, fragmentedAudioFileUrl)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -96,12 +96,12 @@ function runTests (enableObjectStorage: boolean) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should generate WebTorrent', async function () {
|
it('Should generate Web Video', async function () {
|
||||||
this.timeout(60000)
|
this.timeout(60000)
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({
|
await servers[0].videos.runTranscoding({
|
||||||
videoId: videoUUID,
|
videoId: videoUUID,
|
||||||
transcodingType: 'webtorrent'
|
transcodingType: 'web-video'
|
||||||
})
|
})
|
||||||
|
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
@ -117,13 +117,13 @@ function runTests (enableObjectStorage: boolean) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should generate WebTorrent from HLS only video', async function () {
|
it('Should generate Web Video from HLS only video', async function () {
|
||||||
this.timeout(60000)
|
this.timeout(60000)
|
||||||
|
|
||||||
await servers[0].videos.removeAllWebTorrentFiles({ videoId: videoUUID })
|
await servers[0].videos.removeAllWebVideoFiles({ videoId: videoUUID })
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'webtorrent' })
|
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
|
@ -137,13 +137,13 @@ function runTests (enableObjectStorage: boolean) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should only generate WebTorrent', async function () {
|
it('Should only generate Web Video', async function () {
|
||||||
this.timeout(60000)
|
this.timeout(60000)
|
||||||
|
|
||||||
await servers[0].videos.removeHLSPlaylist({ videoId: videoUUID })
|
await servers[0].videos.removeHLSPlaylist({ videoId: videoUUID })
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'webtorrent' })
|
await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
|
|
|
@ -111,7 +111,7 @@ describe('Test HLS videos', function () {
|
||||||
await doubleFollow(servers[0], servers[1])
|
await doubleFollow(servers[0], servers[1])
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('With WebTorrent & HLS enabled', function () {
|
describe('With Web Video & HLS enabled', function () {
|
||||||
runTestSuite(false)
|
runTestSuite(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -251,7 +251,7 @@ describe('Test video transcoding', function () {
|
||||||
expect(videoDetails.files).to.have.lengthOf(5)
|
expect(videoDetails.files).to.have.lengthOf(5)
|
||||||
|
|
||||||
const file = videoDetails.files.find(f => f.resolution.id === 240)
|
const file = videoDetails.files.find(f => f.resolution.id === 240)
|
||||||
const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
|
const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
|
||||||
const probe = await getAudioStream(path)
|
const probe = await getAudioStream(path)
|
||||||
|
|
||||||
if (probe.audioStream) {
|
if (probe.audioStream) {
|
||||||
|
@ -281,7 +281,7 @@ describe('Test video transcoding', function () {
|
||||||
const videoDetails = await server.videos.get({ id: video.id })
|
const videoDetails = await server.videos.get({ id: video.id })
|
||||||
|
|
||||||
const file = videoDetails.files.find(f => f.resolution.id === 240)
|
const file = videoDetails.files.find(f => f.resolution.id === 240)
|
||||||
const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
|
const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
|
||||||
|
|
||||||
expect(await hasAudioStream(path)).to.be.false
|
expect(await hasAudioStream(path)).to.be.false
|
||||||
}
|
}
|
||||||
|
@ -310,7 +310,7 @@ describe('Test video transcoding', function () {
|
||||||
const fixtureVideoProbe = await getAudioStream(fixturePath)
|
const fixtureVideoProbe = await getAudioStream(fixturePath)
|
||||||
|
|
||||||
const file = videoDetails.files.find(f => f.resolution.id === 240)
|
const file = videoDetails.files.find(f => f.resolution.id === 240)
|
||||||
const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
|
const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
|
||||||
|
|
||||||
const videoProbe = await getAudioStream(path)
|
const videoProbe = await getAudioStream(path)
|
||||||
|
|
||||||
|
@ -472,14 +472,14 @@ describe('Test video transcoding', function () {
|
||||||
|
|
||||||
for (const resolution of [ 144, 240, 360, 480 ]) {
|
for (const resolution of [ 144, 240, 360, 480 ]) {
|
||||||
const file = videoDetails.files.find(f => f.resolution.id === resolution)
|
const file = videoDetails.files.find(f => f.resolution.id === resolution)
|
||||||
const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
|
const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
|
||||||
const fps = await getVideoStreamFPS(path)
|
const fps = await getVideoStreamFPS(path)
|
||||||
|
|
||||||
expect(fps).to.be.below(31)
|
expect(fps).to.be.below(31)
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = videoDetails.files.find(f => f.resolution.id === 720)
|
const file = videoDetails.files.find(f => f.resolution.id === 720)
|
||||||
const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
|
const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
|
||||||
const fps = await getVideoStreamFPS(path)
|
const fps = await getVideoStreamFPS(path)
|
||||||
|
|
||||||
expect(fps).to.be.above(58).and.below(62)
|
expect(fps).to.be.above(58).and.below(62)
|
||||||
|
@ -516,14 +516,14 @@ describe('Test video transcoding', function () {
|
||||||
|
|
||||||
{
|
{
|
||||||
const file = video.files.find(f => f.resolution.id === 240)
|
const file = video.files.find(f => f.resolution.id === 240)
|
||||||
const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
|
const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
|
||||||
const fps = await getVideoStreamFPS(path)
|
const fps = await getVideoStreamFPS(path)
|
||||||
expect(fps).to.be.equal(25)
|
expect(fps).to.be.equal(25)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const file = video.files.find(f => f.resolution.id === 720)
|
const file = video.files.find(f => f.resolution.id === 720)
|
||||||
const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
|
const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
|
||||||
const fps = await getVideoStreamFPS(path)
|
const fps = await getVideoStreamFPS(path)
|
||||||
expect(fps).to.be.equal(59)
|
expect(fps).to.be.equal(59)
|
||||||
}
|
}
|
||||||
|
@ -556,7 +556,7 @@ describe('Test video transcoding', function () {
|
||||||
|
|
||||||
for (const resolution of [ 240, 360, 480, 720, 1080 ]) {
|
for (const resolution of [ 240, 360, 480, 720, 1080 ]) {
|
||||||
const file = video.files.find(f => f.resolution.id === resolution)
|
const file = video.files.find(f => f.resolution.id === resolution)
|
||||||
const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
|
const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
|
||||||
|
|
||||||
const bitrate = await getVideoStreamBitrate(path)
|
const bitrate = await getVideoStreamBitrate(path)
|
||||||
const fps = await getVideoStreamFPS(path)
|
const fps = await getVideoStreamFPS(path)
|
||||||
|
@ -607,7 +607,7 @@ describe('Test video transcoding', function () {
|
||||||
for (const r of resolutions) {
|
for (const r of resolutions) {
|
||||||
const file = video.files.find(f => f.resolution.id === r)
|
const file = video.files.find(f => f.resolution.id === r)
|
||||||
|
|
||||||
const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
|
const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
|
||||||
const bitrate = await getVideoStreamBitrate(path)
|
const bitrate = await getVideoStreamBitrate(path)
|
||||||
|
|
||||||
const inputBitrate = 60_000
|
const inputBitrate = 60_000
|
||||||
|
@ -631,7 +631,7 @@ describe('Test video transcoding', function () {
|
||||||
{
|
{
|
||||||
const video = await servers[1].videos.get({ id: videoUUID })
|
const video = await servers[1].videos.get({ id: videoUUID })
|
||||||
const file = video.files.find(f => f.resolution.id === 240)
|
const file = video.files.find(f => f.resolution.id === 240)
|
||||||
const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
|
const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
|
||||||
|
|
||||||
const probe = await ffprobePromise(path)
|
const probe = await ffprobePromise(path)
|
||||||
const metadata = new VideoFileMetadata(probe)
|
const metadata = new VideoFileMetadata(probe)
|
||||||
|
@ -704,14 +704,14 @@ describe('Test video transcoding', function () {
|
||||||
expect(transcodingJobs).to.have.lengthOf(16)
|
expect(transcodingJobs).to.have.lengthOf(16)
|
||||||
|
|
||||||
const hlsJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-hls')
|
const hlsJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-hls')
|
||||||
const webtorrentJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-webtorrent')
|
const webVideoJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-web-video')
|
||||||
const optimizeJobs = transcodingJobs.filter(j => j.data.type === 'optimize-to-webtorrent')
|
const optimizeJobs = transcodingJobs.filter(j => j.data.type === 'optimize-to-web-video')
|
||||||
|
|
||||||
expect(hlsJobs).to.have.lengthOf(8)
|
expect(hlsJobs).to.have.lengthOf(8)
|
||||||
expect(webtorrentJobs).to.have.lengthOf(7)
|
expect(webVideoJobs).to.have.lengthOf(7)
|
||||||
expect(optimizeJobs).to.have.lengthOf(1)
|
expect(optimizeJobs).to.have.lengthOf(1)
|
||||||
|
|
||||||
for (const j of optimizeJobs.concat(hlsJobs.concat(webtorrentJobs))) {
|
for (const j of optimizeJobs.concat(hlsJobs.concat(webVideoJobs))) {
|
||||||
expect(j.priority).to.be.greaterThan(100)
|
expect(j.priority).to.be.greaterThan(100)
|
||||||
expect(j.priority).to.be.lessThan(150)
|
expect(j.priority).to.be.lessThan(150)
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ describe('Test update video privacy while transcoding', function () {
|
||||||
await doubleFollow(servers[0], servers[1])
|
await doubleFollow(servers[0], servers[1])
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('With WebTorrent & HLS enabled', function () {
|
describe('With Web Video & HLS enabled', function () {
|
||||||
runTestSuite(false)
|
runTestSuite(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -273,7 +273,7 @@ describe('Test video studio', function () {
|
||||||
describe('HLS only studio edition', function () {
|
describe('HLS only studio edition', function () {
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
// Disable webtorrent
|
// Disable Web Videos
|
||||||
await servers[0].config.updateExistingSubConfig({
|
await servers[0].config.updateExistingSubConfig({
|
||||||
newConfig: {
|
newConfig: {
|
||||||
transcoding: {
|
transcoding: {
|
||||||
|
@ -354,8 +354,8 @@ describe('Test video studio', function () {
|
||||||
expect(oldFileUrls).to.not.include(f.fileUrl)
|
expect(oldFileUrls).to.not.include(f.fileUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const webtorrentFile of video.files) {
|
for (const webVideoFile of video.files) {
|
||||||
expectStartWith(webtorrentFile.fileUrl, objectStorage.getMockWebVideosBaseUrl())
|
expectStartWith(webVideoFile.fileUrl, objectStorage.getMockWebVideosBaseUrl())
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const hlsFile of video.streamingPlaylists[0].files) {
|
for (const hlsFile of video.streamingPlaylists[0].files) {
|
||||||
|
|
|
@ -184,7 +184,7 @@ describe('Test user videos', function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should disable webtorrent, enable HLS, and update my quota', async function () {
|
it('Should disable web videos, enable HLS, and update my quota', async function () {
|
||||||
this.timeout(160000)
|
this.timeout(160000)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -229,25 +229,13 @@ describe('Test users', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should be able to change the p2p attribute', async function () {
|
it('Should be able to change the p2p attribute', async function () {
|
||||||
{
|
await server.users.updateMe({
|
||||||
await server.users.updateMe({
|
token: userToken,
|
||||||
token: userToken,
|
p2pEnabled: true
|
||||||
webTorrentEnabled: false
|
})
|
||||||
})
|
|
||||||
|
|
||||||
const user = await server.users.getMyInfo({ token: userToken })
|
const user = await server.users.getMyInfo({ token: userToken })
|
||||||
expect(user.p2pEnabled).to.be.false
|
expect(user.p2pEnabled).to.be.true
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
await server.users.updateMe({
|
|
||||||
token: userToken,
|
|
||||||
p2pEnabled: true
|
|
||||||
})
|
|
||||||
|
|
||||||
const user = await server.users.getMyInfo({ token: userToken })
|
|
||||||
expect(user.p2pEnabled).to.be.true
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should be able to change the email attribute', async function () {
|
it('Should be able to change the email attribute', async function () {
|
||||||
|
|
|
@ -48,10 +48,10 @@ describe('Test videos files', function () {
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should delete webtorrent files', async function () {
|
it('Should delete web video files', async function () {
|
||||||
this.timeout(30_000)
|
this.timeout(30_000)
|
||||||
|
|
||||||
await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId1 })
|
await servers[0].videos.removeAllWebVideoFiles({ videoId: validId1 })
|
||||||
|
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
@ -80,15 +80,15 @@ describe('Test videos files', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('When deleting a specific file', function () {
|
describe('When deleting a specific file', function () {
|
||||||
let webtorrentId: string
|
let webVideoId: string
|
||||||
let hlsId: string
|
let hlsId: string
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
this.timeout(120_000)
|
this.timeout(120_000)
|
||||||
|
|
||||||
{
|
{
|
||||||
const { uuid } = await servers[0].videos.quickUpload({ name: 'webtorrent' })
|
const { uuid } = await servers[0].videos.quickUpload({ name: 'web-video' })
|
||||||
webtorrentId = uuid
|
webVideoId = uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -99,38 +99,38 @@ describe('Test videos files', function () {
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Shoulde delete a webtorrent file', async function () {
|
it('Shoulde delete a web video file', async function () {
|
||||||
this.timeout(30_000)
|
this.timeout(30_000)
|
||||||
|
|
||||||
const video = await servers[0].videos.get({ id: webtorrentId })
|
const video = await servers[0].videos.get({ id: webVideoId })
|
||||||
const files = video.files
|
const files = video.files
|
||||||
|
|
||||||
await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentId, fileId: files[0].id })
|
await servers[0].videos.removeWebVideoFile({ videoId: webVideoId, fileId: files[0].id })
|
||||||
|
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
const video = await server.videos.get({ id: webtorrentId })
|
const video = await server.videos.get({ id: webVideoId })
|
||||||
|
|
||||||
expect(video.files).to.have.lengthOf(files.length - 1)
|
expect(video.files).to.have.lengthOf(files.length - 1)
|
||||||
expect(video.files.find(f => f.id === files[0].id)).to.not.exist
|
expect(video.files.find(f => f.id === files[0].id)).to.not.exist
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should delete all webtorrent files', async function () {
|
it('Should delete all web video files', async function () {
|
||||||
this.timeout(30_000)
|
this.timeout(30_000)
|
||||||
|
|
||||||
const video = await servers[0].videos.get({ id: webtorrentId })
|
const video = await servers[0].videos.get({ id: webVideoId })
|
||||||
const files = video.files
|
const files = video.files
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentId, fileId: file.id })
|
await servers[0].videos.removeWebVideoFile({ videoId: webVideoId, fileId: file.id })
|
||||||
}
|
}
|
||||||
|
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
const video = await server.videos.get({ id: webtorrentId })
|
const video = await server.videos.get({ id: webVideoId })
|
||||||
|
|
||||||
expect(video.files).to.have.lengthOf(0)
|
expect(video.files).to.have.lengthOf(0)
|
||||||
}
|
}
|
||||||
|
@ -182,16 +182,16 @@ describe('Test videos files', function () {
|
||||||
it('Should not delete last file of a video', async function () {
|
it('Should not delete last file of a video', async function () {
|
||||||
this.timeout(60_000)
|
this.timeout(60_000)
|
||||||
|
|
||||||
const webtorrentOnly = await servers[0].videos.get({ id: hlsId })
|
const webVideoOnly = await servers[0].videos.get({ id: hlsId })
|
||||||
const hlsOnly = await servers[0].videos.get({ id: webtorrentId })
|
const hlsOnly = await servers[0].videos.get({ id: webVideoId })
|
||||||
|
|
||||||
for (let i = 0; i < 4; i++) {
|
for (let i = 0; i < 4; i++) {
|
||||||
await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentOnly.id, fileId: webtorrentOnly.files[i].id })
|
await servers[0].videos.removeWebVideoFile({ videoId: webVideoOnly.id, fileId: webVideoOnly.files[i].id })
|
||||||
await servers[0].videos.removeHLSFile({ videoId: hlsOnly.id, fileId: hlsOnly.streamingPlaylists[0].files[i].id })
|
await servers[0].videos.removeHLSFile({ videoId: hlsOnly.id, fileId: hlsOnly.streamingPlaylists[0].files[i].id })
|
||||||
}
|
}
|
||||||
|
|
||||||
const expectedStatus = HttpStatusCode.BAD_REQUEST_400
|
const expectedStatus = HttpStatusCode.BAD_REQUEST_400
|
||||||
await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentOnly.id, fileId: webtorrentOnly.files[4].id, expectedStatus })
|
await servers[0].videos.removeWebVideoFile({ videoId: webVideoOnly.id, fileId: webVideoOnly.files[4].id, expectedStatus })
|
||||||
await servers[0].videos.removeHLSFile({ videoId: hlsOnly.id, fileId: hlsOnly.streamingPlaylists[0].files[4].id, expectedStatus })
|
await servers[0].videos.removeHLSFile({ videoId: hlsOnly.id, fileId: hlsOnly.streamingPlaylists[0].files[4].id, expectedStatus })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -154,7 +154,7 @@ describe('Test videos filter', function () {
|
||||||
server: PeerTubeServer
|
server: PeerTubeServer
|
||||||
path: string
|
path: string
|
||||||
isLocal?: boolean
|
isLocal?: boolean
|
||||||
hasWebtorrentFiles?: boolean
|
hasWebVideoFiles?: boolean
|
||||||
hasHLSFiles?: boolean
|
hasHLSFiles?: boolean
|
||||||
include?: VideoInclude
|
include?: VideoInclude
|
||||||
privacyOneOf?: VideoPrivacy[]
|
privacyOneOf?: VideoPrivacy[]
|
||||||
|
@ -174,7 +174,7 @@ describe('Test videos filter', function () {
|
||||||
'include',
|
'include',
|
||||||
'category',
|
'category',
|
||||||
'tagsAllOf',
|
'tagsAllOf',
|
||||||
'hasWebtorrentFiles',
|
'hasWebVideoFiles',
|
||||||
'hasHLSFiles',
|
'hasHLSFiles',
|
||||||
'privacyOneOf',
|
'privacyOneOf',
|
||||||
'excludeAlreadyWatched'
|
'excludeAlreadyWatched'
|
||||||
|
@ -463,14 +463,14 @@ describe('Test videos filter', function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should filter by HLS or WebTorrent files', async function () {
|
it('Should filter by HLS or Web Video files', async function () {
|
||||||
this.timeout(360000)
|
this.timeout(360000)
|
||||||
|
|
||||||
const finderFactory = (name: string) => (videos: Video[]) => videos.some(v => v.name === name)
|
const finderFactory = (name: string) => (videos: Video[]) => videos.some(v => v.name === name)
|
||||||
|
|
||||||
await servers[0].config.enableTranscoding(true, false)
|
await servers[0].config.enableTranscoding(true, false)
|
||||||
await servers[0].videos.upload({ attributes: { name: 'webtorrent video' } })
|
await servers[0].videos.upload({ attributes: { name: 'web video video' } })
|
||||||
const hasWebtorrent = finderFactory('webtorrent video')
|
const hasWebVideo = finderFactory('web video video')
|
||||||
|
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
@ -481,24 +481,24 @@ describe('Test videos filter', function () {
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
await servers[0].config.enableTranscoding(true, true)
|
await servers[0].config.enableTranscoding(true, true)
|
||||||
await servers[0].videos.upload({ attributes: { name: 'hls and webtorrent video' } })
|
await servers[0].videos.upload({ attributes: { name: 'hls and web video video' } })
|
||||||
const hasBoth = finderFactory('hls and webtorrent video')
|
const hasBoth = finderFactory('hls and web video video')
|
||||||
|
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
|
|
||||||
for (const path of paths) {
|
for (const path of paths) {
|
||||||
{
|
{
|
||||||
const videos = await listVideos({ server: servers[0], path, hasWebtorrentFiles: true })
|
const videos = await listVideos({ server: servers[0], path, hasWebVideoFiles: true })
|
||||||
|
|
||||||
expect(hasWebtorrent(videos)).to.be.true
|
expect(hasWebVideo(videos)).to.be.true
|
||||||
expect(hasHLS(videos)).to.be.false
|
expect(hasHLS(videos)).to.be.false
|
||||||
expect(hasBoth(videos)).to.be.true
|
expect(hasBoth(videos)).to.be.true
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const videos = await listVideos({ server: servers[0], path, hasWebtorrentFiles: false })
|
const videos = await listVideos({ server: servers[0], path, hasWebVideoFiles: false })
|
||||||
|
|
||||||
expect(hasWebtorrent(videos)).to.be.false
|
expect(hasWebVideo(videos)).to.be.false
|
||||||
expect(hasHLS(videos)).to.be.true
|
expect(hasHLS(videos)).to.be.true
|
||||||
expect(hasBoth(videos)).to.be.false
|
expect(hasBoth(videos)).to.be.false
|
||||||
}
|
}
|
||||||
|
@ -506,7 +506,7 @@ describe('Test videos filter', function () {
|
||||||
{
|
{
|
||||||
const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true })
|
const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true })
|
||||||
|
|
||||||
expect(hasWebtorrent(videos)).to.be.false
|
expect(hasWebVideo(videos)).to.be.false
|
||||||
expect(hasHLS(videos)).to.be.true
|
expect(hasHLS(videos)).to.be.true
|
||||||
expect(hasBoth(videos)).to.be.true
|
expect(hasBoth(videos)).to.be.true
|
||||||
}
|
}
|
||||||
|
@ -514,23 +514,23 @@ describe('Test videos filter', function () {
|
||||||
{
|
{
|
||||||
const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false })
|
const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false })
|
||||||
|
|
||||||
expect(hasWebtorrent(videos)).to.be.true
|
expect(hasWebVideo(videos)).to.be.true
|
||||||
expect(hasHLS(videos)).to.be.false
|
expect(hasHLS(videos)).to.be.false
|
||||||
expect(hasBoth(videos)).to.be.false
|
expect(hasBoth(videos)).to.be.false
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false, hasWebtorrentFiles: false })
|
const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false, hasWebVideoFiles: false })
|
||||||
|
|
||||||
expect(hasWebtorrent(videos)).to.be.false
|
expect(hasWebVideo(videos)).to.be.false
|
||||||
expect(hasHLS(videos)).to.be.false
|
expect(hasHLS(videos)).to.be.false
|
||||||
expect(hasBoth(videos)).to.be.false
|
expect(hasBoth(videos)).to.be.false
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true, hasWebtorrentFiles: true })
|
const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true, hasWebVideoFiles: true })
|
||||||
|
|
||||||
expect(hasWebtorrent(videos)).to.be.false
|
expect(hasWebVideo(videos)).to.be.false
|
||||||
expect(hasHLS(videos)).to.be.false
|
expect(hasHLS(videos)).to.be.false
|
||||||
expect(hasBoth(videos)).to.be.true
|
expect(hasBoth(videos)).to.be.true
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,8 @@ describe('Test studio transcoding in peertube-runner program', function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (objectStorage) {
|
if (objectStorage) {
|
||||||
for (const webtorrentFile of video.files) {
|
for (const webVideoFile of video.files) {
|
||||||
expectStartWith(webtorrentFile.fileUrl, objectStorage.getMockWebVideosBaseUrl())
|
expectStartWith(webVideoFile.fileUrl, objectStorage.getMockWebVideosBaseUrl())
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const hlsFile of video.streamingPlaylists[0].files) {
|
for (const hlsFile of video.streamingPlaylists[0].files) {
|
||||||
|
|
|
@ -24,13 +24,13 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
let peertubeRunner: PeerTubeRunnerProcess
|
let peertubeRunner: PeerTubeRunnerProcess
|
||||||
|
|
||||||
function runSuite (options: {
|
function runSuite (options: {
|
||||||
webtorrentEnabled: boolean
|
webVideoEnabled: boolean
|
||||||
hlsEnabled: boolean
|
hlsEnabled: boolean
|
||||||
objectStorage?: ObjectStorageCommand
|
objectStorage?: ObjectStorageCommand
|
||||||
}) {
|
}) {
|
||||||
const { webtorrentEnabled, hlsEnabled, objectStorage } = options
|
const { webVideoEnabled, hlsEnabled, objectStorage } = options
|
||||||
|
|
||||||
const objectStorageBaseUrlWebTorrent = objectStorage
|
const objectStorageBaseUrlWebVideo = objectStorage
|
||||||
? objectStorage.getMockWebVideosBaseUrl()
|
? objectStorage.getMockWebVideosBaseUrl()
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
|
@ -46,13 +46,13 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
await waitJobs(servers, { runnerJobs: true })
|
await waitJobs(servers, { runnerJobs: true })
|
||||||
|
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
if (webtorrentEnabled) {
|
if (webVideoEnabled) {
|
||||||
await completeWebVideoFilesCheck({
|
await completeWebVideoFilesCheck({
|
||||||
server,
|
server,
|
||||||
originServer: servers[0],
|
originServer: servers[0],
|
||||||
fixture: 'video_short.mp4',
|
fixture: 'video_short.mp4',
|
||||||
videoUUID: uuid,
|
videoUUID: uuid,
|
||||||
objectStorageBaseUrl: objectStorageBaseUrlWebTorrent,
|
objectStorageBaseUrl: objectStorageBaseUrlWebVideo,
|
||||||
files: [
|
files: [
|
||||||
{ resolution: 0 },
|
{ resolution: 0 },
|
||||||
{ resolution: 144 },
|
{ resolution: 144 },
|
||||||
|
@ -66,7 +66,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
|
|
||||||
if (hlsEnabled) {
|
if (hlsEnabled) {
|
||||||
await completeCheckHlsPlaylist({
|
await completeCheckHlsPlaylist({
|
||||||
hlsOnly: !webtorrentEnabled,
|
hlsOnly: !webVideoEnabled,
|
||||||
servers,
|
servers,
|
||||||
videoUUID: uuid,
|
videoUUID: uuid,
|
||||||
objectStorageBaseUrl: objectStorageBaseUrlHLS,
|
objectStorageBaseUrl: objectStorageBaseUrlHLS,
|
||||||
|
@ -84,13 +84,13 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
await waitJobs(servers, { runnerJobs: true })
|
await waitJobs(servers, { runnerJobs: true })
|
||||||
|
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
if (webtorrentEnabled) {
|
if (webVideoEnabled) {
|
||||||
await completeWebVideoFilesCheck({
|
await completeWebVideoFilesCheck({
|
||||||
server,
|
server,
|
||||||
originServer: servers[0],
|
originServer: servers[0],
|
||||||
fixture: 'video_short.webm',
|
fixture: 'video_short.webm',
|
||||||
videoUUID: uuid,
|
videoUUID: uuid,
|
||||||
objectStorageBaseUrl: objectStorageBaseUrlWebTorrent,
|
objectStorageBaseUrl: objectStorageBaseUrlWebVideo,
|
||||||
files: [
|
files: [
|
||||||
{ resolution: 0 },
|
{ resolution: 0 },
|
||||||
{ resolution: 144 },
|
{ resolution: 144 },
|
||||||
|
@ -104,7 +104,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
|
|
||||||
if (hlsEnabled) {
|
if (hlsEnabled) {
|
||||||
await completeCheckHlsPlaylist({
|
await completeCheckHlsPlaylist({
|
||||||
hlsOnly: !webtorrentEnabled,
|
hlsOnly: !webVideoEnabled,
|
||||||
servers,
|
servers,
|
||||||
videoUUID: uuid,
|
videoUUID: uuid,
|
||||||
objectStorageBaseUrl: objectStorageBaseUrlHLS,
|
objectStorageBaseUrl: objectStorageBaseUrlHLS,
|
||||||
|
@ -123,13 +123,13 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
await waitJobs(servers, { runnerJobs: true })
|
await waitJobs(servers, { runnerJobs: true })
|
||||||
|
|
||||||
for (const server of servers) {
|
for (const server of servers) {
|
||||||
if (webtorrentEnabled) {
|
if (webVideoEnabled) {
|
||||||
await completeWebVideoFilesCheck({
|
await completeWebVideoFilesCheck({
|
||||||
server,
|
server,
|
||||||
originServer: servers[0],
|
originServer: servers[0],
|
||||||
fixture: 'sample.ogg',
|
fixture: 'sample.ogg',
|
||||||
videoUUID: uuid,
|
videoUUID: uuid,
|
||||||
objectStorageBaseUrl: objectStorageBaseUrlWebTorrent,
|
objectStorageBaseUrl: objectStorageBaseUrlWebVideo,
|
||||||
files: [
|
files: [
|
||||||
{ resolution: 0 },
|
{ resolution: 0 },
|
||||||
{ resolution: 144 },
|
{ resolution: 144 },
|
||||||
|
@ -142,7 +142,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
|
|
||||||
if (hlsEnabled) {
|
if (hlsEnabled) {
|
||||||
await completeCheckHlsPlaylist({
|
await completeCheckHlsPlaylist({
|
||||||
hlsOnly: !webtorrentEnabled,
|
hlsOnly: !webVideoEnabled,
|
||||||
servers,
|
servers,
|
||||||
videoUUID: uuid,
|
videoUUID: uuid,
|
||||||
objectStorageBaseUrl: objectStorageBaseUrlHLS,
|
objectStorageBaseUrl: objectStorageBaseUrlHLS,
|
||||||
|
@ -159,13 +159,13 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
|
|
||||||
await waitJobs(servers, { runnerJobs: true })
|
await waitJobs(servers, { runnerJobs: true })
|
||||||
|
|
||||||
if (webtorrentEnabled) {
|
if (webVideoEnabled) {
|
||||||
await completeWebVideoFilesCheck({
|
await completeWebVideoFilesCheck({
|
||||||
server: servers[0],
|
server: servers[0],
|
||||||
originServer: servers[0],
|
originServer: servers[0],
|
||||||
fixture: 'video_short.mp4',
|
fixture: 'video_short.mp4',
|
||||||
videoUUID: uuid,
|
videoUUID: uuid,
|
||||||
objectStorageBaseUrl: objectStorageBaseUrlWebTorrent,
|
objectStorageBaseUrl: objectStorageBaseUrlWebVideo,
|
||||||
files: [
|
files: [
|
||||||
{ resolution: 0 },
|
{ resolution: 0 },
|
||||||
{ resolution: 144 },
|
{ resolution: 144 },
|
||||||
|
@ -179,7 +179,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
|
|
||||||
if (hlsEnabled) {
|
if (hlsEnabled) {
|
||||||
await completeCheckHlsPlaylist({
|
await completeCheckHlsPlaylist({
|
||||||
hlsOnly: !webtorrentEnabled,
|
hlsOnly: !webVideoEnabled,
|
||||||
servers: [ servers[0] ],
|
servers: [ servers[0] ],
|
||||||
videoUUID: uuid,
|
videoUUID: uuid,
|
||||||
objectStorageBaseUrl: objectStorageBaseUrlHLS,
|
objectStorageBaseUrl: objectStorageBaseUrlHLS,
|
||||||
|
@ -203,7 +203,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
|
|
||||||
await servers[0].config.enableTranscoding(true, true, true)
|
await servers[0].config.enableTranscoding(true, true, true)
|
||||||
|
|
||||||
await servers[0].videos.runTranscoding({ transcodingType: 'webtorrent', videoId: uuid })
|
await servers[0].videos.runTranscoding({ transcodingType: 'web-video', videoId: uuid })
|
||||||
await waitJobs(servers, { runnerJobs: true })
|
await waitJobs(servers, { runnerJobs: true })
|
||||||
|
|
||||||
await completeWebVideoFilesCheck({
|
await completeWebVideoFilesCheck({
|
||||||
|
@ -211,7 +211,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
originServer: servers[0],
|
originServer: servers[0],
|
||||||
fixture: 'video_short.mp4',
|
fixture: 'video_short.mp4',
|
||||||
videoUUID: uuid,
|
videoUUID: uuid,
|
||||||
objectStorageBaseUrl: objectStorageBaseUrlWebTorrent,
|
objectStorageBaseUrl: objectStorageBaseUrlWebVideo,
|
||||||
files: [
|
files: [
|
||||||
{ resolution: 0 },
|
{ resolution: 0 },
|
||||||
{ resolution: 144 },
|
{ resolution: 144 },
|
||||||
|
@ -262,7 +262,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
await servers[0].config.enableTranscoding(true, false, true)
|
await servers[0].config.enableTranscoding(true, false, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
runSuite({ webtorrentEnabled: true, hlsEnabled: false })
|
runSuite({ webVideoEnabled: true, hlsEnabled: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('HLS videos only enabled', function () {
|
describe('HLS videos only enabled', function () {
|
||||||
|
@ -271,7 +271,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
await servers[0].config.enableTranscoding(false, true, true)
|
await servers[0].config.enableTranscoding(false, true, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
runSuite({ webtorrentEnabled: false, hlsEnabled: true })
|
runSuite({ webVideoEnabled: false, hlsEnabled: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Web video & HLS enabled', function () {
|
describe('Web video & HLS enabled', function () {
|
||||||
|
@ -280,7 +280,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
await servers[0].config.enableTranscoding(true, true, true)
|
await servers[0].config.enableTranscoding(true, true, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
runSuite({ webtorrentEnabled: true, hlsEnabled: true })
|
runSuite({ webVideoEnabled: true, hlsEnabled: true })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
await servers[0].config.enableTranscoding(true, false, true)
|
await servers[0].config.enableTranscoding(true, false, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
runSuite({ webtorrentEnabled: true, hlsEnabled: false, objectStorage })
|
runSuite({ webVideoEnabled: true, hlsEnabled: false, objectStorage })
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('HLS videos only enabled', function () {
|
describe('HLS videos only enabled', function () {
|
||||||
|
@ -315,7 +315,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
await servers[0].config.enableTranscoding(false, true, true)
|
await servers[0].config.enableTranscoding(false, true, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
runSuite({ webtorrentEnabled: false, hlsEnabled: true, objectStorage })
|
runSuite({ webVideoEnabled: false, hlsEnabled: true, objectStorage })
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Web video & HLS enabled', function () {
|
describe('Web video & HLS enabled', function () {
|
||||||
|
@ -324,7 +324,7 @@ describe('Test VOD transcoding in peertube-runner program', function () {
|
||||||
await servers[0].config.enableTranscoding(true, true, true)
|
await servers[0].config.enableTranscoding(true, true, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
runSuite({ webtorrentEnabled: true, hlsEnabled: true, objectStorage })
|
runSuite({ webVideoEnabled: true, hlsEnabled: true, objectStorage })
|
||||||
})
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
|
|
|
@ -302,11 +302,11 @@ describe('Test plugin helpers', function () {
|
||||||
|
|
||||||
// Video files check
|
// Video files check
|
||||||
{
|
{
|
||||||
expect(body.webtorrent.videoFiles).to.be.an('array')
|
expect(body.webVideo.videoFiles).to.be.an('array')
|
||||||
expect(body.hls.videoFiles).to.be.an('array')
|
expect(body.hls.videoFiles).to.be.an('array')
|
||||||
|
|
||||||
for (const resolution of [ 144, 240, 360, 480, 720 ]) {
|
for (const resolution of [ 144, 240, 360, 480, 720 ]) {
|
||||||
for (const files of [ body.webtorrent.videoFiles, body.hls.videoFiles ]) {
|
for (const files of [ body.webVideo.videoFiles, body.hls.videoFiles ]) {
|
||||||
const file = files.find(f => f.resolution === resolution)
|
const file = files.find(f => f.resolution === resolution)
|
||||||
expect(file).to.exist
|
expect(file).to.exist
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ describe('Test plugin helpers', function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
videoPath = body.webtorrent.videoFiles[0].path
|
videoPath = body.webVideo.videoFiles[0].path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thumbnails check
|
// Thumbnails check
|
||||||
|
|
|
@ -247,7 +247,7 @@ describe('Test transcoding plugins', function () {
|
||||||
|
|
||||||
const video = await server.videos.get({ id: videoUUID })
|
const video = await server.videos.get({ id: videoUUID })
|
||||||
|
|
||||||
const path = server.servers.buildWebTorrentFilePath(video.files[0].fileUrl)
|
const path = server.servers.buildWebVideoFilePath(video.files[0].fileUrl)
|
||||||
const audioProbe = await getAudioStream(path)
|
const audioProbe = await getAudioStream(path)
|
||||||
expect(audioProbe.audioStream.codec_name).to.equal('opus')
|
expect(audioProbe.audioStream.codec_name).to.equal('opus')
|
||||||
|
|
||||||
|
|
|
@ -215,22 +215,22 @@ async function checkVideoFilesWereRemoved (options: {
|
||||||
}) {
|
}) {
|
||||||
const { video, server, captions = [], onlyVideoFiles = false } = options
|
const { video, server, captions = [], onlyVideoFiles = false } = options
|
||||||
|
|
||||||
const webtorrentFiles = video.files || []
|
const webVideoFiles = video.files || []
|
||||||
const hlsFiles = video.streamingPlaylists[0]?.files || []
|
const hlsFiles = video.streamingPlaylists[0]?.files || []
|
||||||
|
|
||||||
const thumbnailName = basename(video.thumbnailPath)
|
const thumbnailName = basename(video.thumbnailPath)
|
||||||
const previewName = basename(video.previewPath)
|
const previewName = basename(video.previewPath)
|
||||||
|
|
||||||
const torrentNames = webtorrentFiles.concat(hlsFiles).map(f => basename(f.torrentUrl))
|
const torrentNames = webVideoFiles.concat(hlsFiles).map(f => basename(f.torrentUrl))
|
||||||
|
|
||||||
const captionNames = captions.map(c => basename(c.captionPath))
|
const captionNames = captions.map(c => basename(c.captionPath))
|
||||||
|
|
||||||
const webtorrentFilenames = webtorrentFiles.map(f => basename(f.fileUrl))
|
const webVideoFilenames = webVideoFiles.map(f => basename(f.fileUrl))
|
||||||
const hlsFilenames = hlsFiles.map(f => basename(f.fileUrl))
|
const hlsFilenames = hlsFiles.map(f => basename(f.fileUrl))
|
||||||
|
|
||||||
let directories: { [ directory: string ]: string[] } = {
|
let directories: { [ directory: string ]: string[] } = {
|
||||||
videos: webtorrentFilenames,
|
videos: webVideoFilenames,
|
||||||
redundancy: webtorrentFilenames,
|
redundancy: webVideoFilenames,
|
||||||
[join('playlists', 'hls')]: hlsFilenames,
|
[join('playlists', 'hls')]: hlsFilenames,
|
||||||
[join('redundancy', 'hls')]: hlsFilenames
|
[join('redundancy', 'hls')]: hlsFilenames
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,19 +65,19 @@ async function listRedundanciesCLI (target: VideoRedundanciesTarget) {
|
||||||
}) as any
|
}) as any
|
||||||
|
|
||||||
for (const redundancy of data) {
|
for (const redundancy of data) {
|
||||||
const webtorrentFiles = redundancy.redundancies.files
|
const webVideoFiles = redundancy.redundancies.files
|
||||||
const streamingPlaylists = redundancy.redundancies.streamingPlaylists
|
const streamingPlaylists = redundancy.redundancies.streamingPlaylists
|
||||||
|
|
||||||
let totalSize = ''
|
let totalSize = ''
|
||||||
if (target === 'remote-videos') {
|
if (target === 'remote-videos') {
|
||||||
const tmp = webtorrentFiles.concat(streamingPlaylists)
|
const tmp = webVideoFiles.concat(streamingPlaylists)
|
||||||
.reduce((a, b) => a + b.size, 0)
|
.reduce((a, b) => a + b.size, 0)
|
||||||
|
|
||||||
totalSize = bytes(tmp)
|
totalSize = bytes(tmp)
|
||||||
}
|
}
|
||||||
|
|
||||||
const instances = uniqify(
|
const instances = uniqify(
|
||||||
webtorrentFiles.concat(streamingPlaylists)
|
webVideoFiles.concat(streamingPlaylists)
|
||||||
.map(r => r.fileUrl)
|
.map(r => r.fileUrl)
|
||||||
.map(u => new URL(u).host)
|
.map(u => new URL(u).host)
|
||||||
)
|
)
|
||||||
|
@ -86,7 +86,7 @@ async function listRedundanciesCLI (target: VideoRedundanciesTarget) {
|
||||||
redundancy.id.toString(),
|
redundancy.id.toString(),
|
||||||
redundancy.name,
|
redundancy.name,
|
||||||
redundancy.url,
|
redundancy.url,
|
||||||
webtorrentFiles.length,
|
webVideoFiles.length,
|
||||||
streamingPlaylists.length,
|
streamingPlaylists.length,
|
||||||
instances.join('\n'),
|
instances.join('\n'),
|
||||||
totalSize
|
totalSize
|
||||||
|
|
|
@ -38,6 +38,6 @@ export function isStreamingPlaylistFile (file: any): file is MVideoFileStreaming
|
||||||
return !!file.videoStreamingPlaylistId
|
return !!file.videoStreamingPlaylistId
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isWebtorrentFile (file: any): file is MVideoFileVideo {
|
export function isWebVideoFile (file: any): file is MVideoFileVideo {
|
||||||
return !!file.videoId
|
return !!file.videoId
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,17 @@ export type PeerTubeHelpers = {
|
||||||
ffprobe: (path: string) => Promise<any>
|
ffprobe: (path: string) => Promise<any>
|
||||||
|
|
||||||
getFiles: (id: number | string) => Promise<{
|
getFiles: (id: number | string) => Promise<{
|
||||||
webtorrent: {
|
webtorrent: { // TODO: remove in v7
|
||||||
|
videoFiles: {
|
||||||
|
path: string // Could be null if using remote storage
|
||||||
|
url: string
|
||||||
|
resolution: number
|
||||||
|
size: number
|
||||||
|
fps: number
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
webVideo: {
|
||||||
videoFiles: {
|
videoFiles: {
|
||||||
path: string // Could be null if using remote storage
|
path: string // Could be null if using remote storage
|
||||||
url: string
|
url: string
|
||||||
|
|
|
@ -30,7 +30,9 @@ export interface VideosCommonQuery {
|
||||||
tagsAllOf?: string[]
|
tagsAllOf?: string[]
|
||||||
|
|
||||||
hasHLSFiles?: boolean
|
hasHLSFiles?: boolean
|
||||||
hasWebtorrentFiles?: boolean
|
|
||||||
|
hasWebtorrentFiles?: boolean // TODO: remove in v7
|
||||||
|
hasWebVideoFiles?: boolean
|
||||||
|
|
||||||
skipCount?: boolean
|
skipCount?: boolean
|
||||||
|
|
||||||
|
|
|
@ -148,17 +148,17 @@ export interface HLSTranscodingPayload extends BaseTranscodingPayload {
|
||||||
fps: number
|
fps: number
|
||||||
copyCodecs: boolean
|
copyCodecs: boolean
|
||||||
|
|
||||||
deleteWebTorrentFiles: boolean
|
deleteWebVideoFiles: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NewWebTorrentResolutionTranscodingPayload extends BaseTranscodingPayload {
|
export interface NewWebVideoResolutionTranscodingPayload extends BaseTranscodingPayload {
|
||||||
type: 'new-resolution-to-webtorrent'
|
type: 'new-resolution-to-web-video'
|
||||||
resolution: VideoResolution
|
resolution: VideoResolution
|
||||||
fps: number
|
fps: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MergeAudioTranscodingPayload extends BaseTranscodingPayload {
|
export interface MergeAudioTranscodingPayload extends BaseTranscodingPayload {
|
||||||
type: 'merge-audio-to-webtorrent'
|
type: 'merge-audio-to-web-video'
|
||||||
|
|
||||||
resolution: VideoResolution
|
resolution: VideoResolution
|
||||||
fps: number
|
fps: number
|
||||||
|
@ -167,7 +167,7 @@ export interface MergeAudioTranscodingPayload extends BaseTranscodingPayload {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OptimizeTranscodingPayload extends BaseTranscodingPayload {
|
export interface OptimizeTranscodingPayload extends BaseTranscodingPayload {
|
||||||
type: 'optimize-to-webtorrent'
|
type: 'optimize-to-web-video'
|
||||||
|
|
||||||
quickTranscode: boolean
|
quickTranscode: boolean
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ export interface OptimizeTranscodingPayload extends BaseTranscodingPayload {
|
||||||
|
|
||||||
export type VideoTranscodingPayload =
|
export type VideoTranscodingPayload =
|
||||||
HLSTranscodingPayload
|
HLSTranscodingPayload
|
||||||
| NewWebTorrentResolutionTranscodingPayload
|
| NewWebVideoResolutionTranscodingPayload
|
||||||
| OptimizeTranscodingPayload
|
| OptimizeTranscodingPayload
|
||||||
| MergeAudioTranscodingPayload
|
| MergeAudioTranscodingPayload
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,6 @@ export interface UserUpdateMe {
|
||||||
description?: string
|
description?: string
|
||||||
nsfwPolicy?: NSFWPolicyType
|
nsfwPolicy?: NSFWPolicyType
|
||||||
|
|
||||||
// FIXME: deprecated in favour of p2pEnabled in 4.1
|
|
||||||
webTorrentEnabled?: boolean
|
|
||||||
p2pEnabled?: boolean
|
p2pEnabled?: boolean
|
||||||
|
|
||||||
autoPlayVideo?: boolean
|
autoPlayVideo?: boolean
|
||||||
|
|
|
@ -22,8 +22,6 @@ export interface User {
|
||||||
autoPlayNextVideo: boolean
|
autoPlayNextVideo: boolean
|
||||||
autoPlayNextVideoPlaylist: boolean
|
autoPlayNextVideoPlaylist: boolean
|
||||||
|
|
||||||
// @deprecated in favour of p2pEnabled
|
|
||||||
webTorrentEnabled: boolean
|
|
||||||
p2pEnabled: boolean
|
p2pEnabled: boolean
|
||||||
|
|
||||||
videosHistoryEnabled: boolean
|
videosHistoryEnabled: boolean
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export interface VideoTranscodingCreate {
|
export interface VideoTranscodingCreate {
|
||||||
transcodingType: 'hls' | 'webtorrent'
|
transcodingType: 'hls' | 'webtorrent' | 'web-video' // TODO: remove webtorrent in v7
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ export class ConfigCommand extends AbstractCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: convert args to object
|
// TODO: convert args to object
|
||||||
enableTranscoding (webtorrent = true, hls = true, with0p = false) {
|
enableTranscoding (webVideo = true, hls = true, with0p = false) {
|
||||||
return this.updateExistingSubConfig({
|
return this.updateExistingSubConfig({
|
||||||
newConfig: {
|
newConfig: {
|
||||||
transcoding: {
|
transcoding: {
|
||||||
|
@ -143,7 +143,7 @@ export class ConfigCommand extends AbstractCommand {
|
||||||
resolutions: ConfigCommand.getCustomConfigResolutions(true, with0p),
|
resolutions: ConfigCommand.getCustomConfigResolutions(true, with0p),
|
||||||
|
|
||||||
webtorrent: {
|
webtorrent: {
|
||||||
enabled: webtorrent
|
enabled: webVideo
|
||||||
},
|
},
|
||||||
hls: {
|
hls: {
|
||||||
enabled: hls
|
enabled: hls
|
||||||
|
@ -154,7 +154,7 @@ export class ConfigCommand extends AbstractCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: convert args to object
|
// TODO: convert args to object
|
||||||
enableMinimumTranscoding (webtorrent = true, hls = true) {
|
enableMinimumTranscoding (webVideo = true, hls = true) {
|
||||||
return this.updateExistingSubConfig({
|
return this.updateExistingSubConfig({
|
||||||
newConfig: {
|
newConfig: {
|
||||||
transcoding: {
|
transcoding: {
|
||||||
|
@ -170,7 +170,7 @@ export class ConfigCommand extends AbstractCommand {
|
||||||
},
|
},
|
||||||
|
|
||||||
webtorrent: {
|
webtorrent: {
|
||||||
enabled: webtorrent
|
enabled: webVideo
|
||||||
},
|
},
|
||||||
hls: {
|
hls: {
|
||||||
enabled: hls
|
enabled: hls
|
||||||
|
|
|
@ -77,7 +77,7 @@ export class ServersCommand extends AbstractCommand {
|
||||||
return join(root(), 'test' + this.server.internalServerNumber, directory)
|
return join(root(), 'test' + this.server.internalServerNumber, directory)
|
||||||
}
|
}
|
||||||
|
|
||||||
buildWebTorrentFilePath (fileUrl: string) {
|
buildWebVideoFilePath (fileUrl: string) {
|
||||||
return this.buildDirectory(join('videos', basename(fileUrl)))
|
return this.buildDirectory(join('videos', basename(fileUrl)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -686,10 +686,10 @@ export class VideosCommand extends AbstractCommand {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAllWebTorrentFiles (options: OverrideCommandOptions & {
|
removeAllWebVideoFiles (options: OverrideCommandOptions & {
|
||||||
videoId: number | string
|
videoId: number | string
|
||||||
}) {
|
}) {
|
||||||
const path = '/api/v1/videos/' + options.videoId + '/webtorrent'
|
const path = '/api/v1/videos/' + options.videoId + '/web-videos'
|
||||||
|
|
||||||
return this.deleteRequest({
|
return this.deleteRequest({
|
||||||
...options,
|
...options,
|
||||||
|
@ -700,11 +700,11 @@ export class VideosCommand extends AbstractCommand {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
removeWebTorrentFile (options: OverrideCommandOptions & {
|
removeWebVideoFile (options: OverrideCommandOptions & {
|
||||||
videoId: number | string
|
videoId: number | string
|
||||||
fileId: number
|
fileId: number
|
||||||
}) {
|
}) {
|
||||||
const path = '/api/v1/videos/' + options.videoId + '/webtorrent/' + options.fileId
|
const path = '/api/v1/videos/' + options.videoId + '/web-videos/' + options.fileId
|
||||||
|
|
||||||
return this.deleteRequest({
|
return this.deleteRequest({
|
||||||
...options,
|
...options,
|
||||||
|
@ -717,7 +717,7 @@ export class VideosCommand extends AbstractCommand {
|
||||||
|
|
||||||
runTranscoding (options: OverrideCommandOptions & {
|
runTranscoding (options: OverrideCommandOptions & {
|
||||||
videoId: number | string
|
videoId: number | string
|
||||||
transcodingType: 'hls' | 'webtorrent'
|
transcodingType: 'hls' | 'webtorrent' | 'web-video'
|
||||||
}) {
|
}) {
|
||||||
const path = '/api/v1/videos/' + options.videoId + '/transcoding'
|
const path = '/api/v1/videos/' + options.videoId + '/transcoding'
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,7 @@ Value must be a valid color (`red` or `rgba(100, 100, 100, 0.5)`).
|
||||||
|
|
||||||
Force a specific player engine.
|
Force a specific player engine.
|
||||||
|
|
||||||
Value must be a valid mode (`webtorrent` or `p2p-media-loader`).
|
Value must be a valid mode (`web-video` or `p2p-media-loader`).
|
||||||
|
|
||||||
### api
|
### api
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue