diff options
author | Chocobozzz <me@florianbigard.com> | 2020-09-17 09:20:52 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-11-09 15:33:04 +0100 |
commit | c6c0fa6cd8fe8f752463d8982c3dbcd448739c4e (patch) | |
tree | 79304b0152b0a38d33b26e65d4acdad0da4032a7 /server/initializers | |
parent | 110d463fece85e87a26aca48a6048ae0017a27b3 (diff) | |
download | PeerTube-c6c0fa6cd8fe8f752463d8982c3dbcd448739c4e.tar.gz PeerTube-c6c0fa6cd8fe8f752463d8982c3dbcd448739c4e.tar.zst PeerTube-c6c0fa6cd8fe8f752463d8982c3dbcd448739c4e.zip |
Live streaming implementation first step
Diffstat (limited to 'server/initializers')
-rw-r--r-- | server/initializers/config.ts | 21 | ||||
-rw-r--r-- | server/initializers/constants.ts | 50 | ||||
-rw-r--r-- | server/initializers/database.ts | 10 | ||||
-rw-r--r-- | server/initializers/migrations/0535-video-live.ts | 39 | ||||
-rw-r--r-- | server/initializers/migrations/0540-video-file-infohash.ts | 26 |
5 files changed, 127 insertions, 19 deletions
diff --git a/server/initializers/config.ts b/server/initializers/config.ts index b40e525a5..7a8200ed9 100644 --- a/server/initializers/config.ts +++ b/server/initializers/config.ts | |||
@@ -198,6 +198,27 @@ const CONFIG = { | |||
198 | get ENABLED () { return config.get<boolean>('transcoding.webtorrent.enabled') } | 198 | get ENABLED () { return config.get<boolean>('transcoding.webtorrent.enabled') } |
199 | } | 199 | } |
200 | }, | 200 | }, |
201 | LIVE: { | ||
202 | get ENABLED () { return config.get<boolean>('live.enabled') }, | ||
203 | |||
204 | RTMP: { | ||
205 | get PORT () { return config.get<number>('live.rtmp.port') } | ||
206 | }, | ||
207 | |||
208 | TRANSCODING: { | ||
209 | get ENABLED () { return config.get<boolean>('live.transcoding.enabled') }, | ||
210 | get THREADS () { return config.get<number>('live.transcoding.threads') }, | ||
211 | |||
212 | RESOLUTIONS: { | ||
213 | get '240p' () { return config.get<boolean>('live.transcoding.resolutions.240p') }, | ||
214 | get '360p' () { return config.get<boolean>('live.transcoding.resolutions.360p') }, | ||
215 | get '480p' () { return config.get<boolean>('live.transcoding.resolutions.480p') }, | ||
216 | get '720p' () { return config.get<boolean>('live.transcoding.resolutions.720p') }, | ||
217 | get '1080p' () { return config.get<boolean>('live.transcoding.resolutions.1080p') }, | ||
218 | get '2160p' () { return config.get<boolean>('live.transcoding.resolutions.2160p') } | ||
219 | } | ||
220 | } | ||
221 | }, | ||
201 | IMPORT: { | 222 | IMPORT: { |
202 | VIDEOS: { | 223 | VIDEOS: { |
203 | HTTP: { | 224 | HTTP: { |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 171e9e9c2..606eeba2d 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -23,7 +23,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' | |||
23 | 23 | ||
24 | // --------------------------------------------------------------------------- | 24 | // --------------------------------------------------------------------------- |
25 | 25 | ||
26 | const LAST_MIGRATION_VERSION = 530 | 26 | const LAST_MIGRATION_VERSION = 540 |
27 | 27 | ||
28 | // --------------------------------------------------------------------------- | 28 | // --------------------------------------------------------------------------- |
29 | 29 | ||
@@ -50,7 +50,8 @@ const WEBSERVER = { | |||
50 | SCHEME: '', | 50 | SCHEME: '', |
51 | WS: '', | 51 | WS: '', |
52 | HOSTNAME: '', | 52 | HOSTNAME: '', |
53 | PORT: 0 | 53 | PORT: 0, |
54 | RTMP_URL: '' | ||
54 | } | 55 | } |
55 | 56 | ||
56 | // Sortable columns per schema | 57 | // Sortable columns per schema |
@@ -264,7 +265,7 @@ const CONSTRAINTS_FIELDS = { | |||
264 | VIEWS: { min: 0 }, | 265 | VIEWS: { min: 0 }, |
265 | LIKES: { min: 0 }, | 266 | LIKES: { min: 0 }, |
266 | DISLIKES: { min: 0 }, | 267 | DISLIKES: { min: 0 }, |
267 | FILE_SIZE: { min: 10 }, | 268 | FILE_SIZE: { min: -1 }, |
268 | URL: { min: 3, max: 2000 } // Length | 269 | URL: { min: 3, max: 2000 } // Length |
269 | }, | 270 | }, |
270 | VIDEO_PLAYLISTS: { | 271 | VIDEO_PLAYLISTS: { |
@@ -370,39 +371,41 @@ const VIDEO_LICENCES = { | |||
370 | 371 | ||
371 | const VIDEO_LANGUAGES: { [id: string]: string } = {} | 372 | const VIDEO_LANGUAGES: { [id: string]: string } = {} |
372 | 373 | ||
373 | const VIDEO_PRIVACIES = { | 374 | const VIDEO_PRIVACIES: { [ id in VideoPrivacy ]: string } = { |
374 | [VideoPrivacy.PUBLIC]: 'Public', | 375 | [VideoPrivacy.PUBLIC]: 'Public', |
375 | [VideoPrivacy.UNLISTED]: 'Unlisted', | 376 | [VideoPrivacy.UNLISTED]: 'Unlisted', |
376 | [VideoPrivacy.PRIVATE]: 'Private', | 377 | [VideoPrivacy.PRIVATE]: 'Private', |
377 | [VideoPrivacy.INTERNAL]: 'Internal' | 378 | [VideoPrivacy.INTERNAL]: 'Internal' |
378 | } | 379 | } |
379 | 380 | ||
380 | const VIDEO_STATES = { | 381 | const VIDEO_STATES: { [ id in VideoState ]: string } = { |
381 | [VideoState.PUBLISHED]: 'Published', | 382 | [VideoState.PUBLISHED]: 'Published', |
382 | [VideoState.TO_TRANSCODE]: 'To transcode', | 383 | [VideoState.TO_TRANSCODE]: 'To transcode', |
383 | [VideoState.TO_IMPORT]: 'To import' | 384 | [VideoState.TO_IMPORT]: 'To import', |
385 | [VideoState.WAITING_FOR_LIVE]: 'Waiting for livestream', | ||
386 | [VideoState.LIVE_ENDED]: 'Livestream ended' | ||
384 | } | 387 | } |
385 | 388 | ||
386 | const VIDEO_IMPORT_STATES = { | 389 | const VIDEO_IMPORT_STATES: { [ id in VideoImportState ]: string } = { |
387 | [VideoImportState.FAILED]: 'Failed', | 390 | [VideoImportState.FAILED]: 'Failed', |
388 | [VideoImportState.PENDING]: 'Pending', | 391 | [VideoImportState.PENDING]: 'Pending', |
389 | [VideoImportState.SUCCESS]: 'Success', | 392 | [VideoImportState.SUCCESS]: 'Success', |
390 | [VideoImportState.REJECTED]: 'Rejected' | 393 | [VideoImportState.REJECTED]: 'Rejected' |
391 | } | 394 | } |
392 | 395 | ||
393 | const ABUSE_STATES = { | 396 | const ABUSE_STATES: { [ id in AbuseState ]: string } = { |
394 | [AbuseState.PENDING]: 'Pending', | 397 | [AbuseState.PENDING]: 'Pending', |
395 | [AbuseState.REJECTED]: 'Rejected', | 398 | [AbuseState.REJECTED]: 'Rejected', |
396 | [AbuseState.ACCEPTED]: 'Accepted' | 399 | [AbuseState.ACCEPTED]: 'Accepted' |
397 | } | 400 | } |
398 | 401 | ||
399 | const VIDEO_PLAYLIST_PRIVACIES = { | 402 | const VIDEO_PLAYLIST_PRIVACIES: { [ id in VideoPlaylistPrivacy ]: string } = { |
400 | [VideoPlaylistPrivacy.PUBLIC]: 'Public', | 403 | [VideoPlaylistPrivacy.PUBLIC]: 'Public', |
401 | [VideoPlaylistPrivacy.UNLISTED]: 'Unlisted', | 404 | [VideoPlaylistPrivacy.UNLISTED]: 'Unlisted', |
402 | [VideoPlaylistPrivacy.PRIVATE]: 'Private' | 405 | [VideoPlaylistPrivacy.PRIVATE]: 'Private' |
403 | } | 406 | } |
404 | 407 | ||
405 | const VIDEO_PLAYLIST_TYPES = { | 408 | const VIDEO_PLAYLIST_TYPES: { [ id in VideoPlaylistType ]: string } = { |
406 | [VideoPlaylistType.REGULAR]: 'Regular', | 409 | [VideoPlaylistType.REGULAR]: 'Regular', |
407 | [VideoPlaylistType.WATCH_LATER]: 'Watch later' | 410 | [VideoPlaylistType.WATCH_LATER]: 'Watch later' |
408 | } | 411 | } |
@@ -600,6 +603,17 @@ const LRU_CACHE = { | |||
600 | const HLS_STREAMING_PLAYLIST_DIRECTORY = join(CONFIG.STORAGE.STREAMING_PLAYLISTS_DIR, 'hls') | 603 | const HLS_STREAMING_PLAYLIST_DIRECTORY = join(CONFIG.STORAGE.STREAMING_PLAYLISTS_DIR, 'hls') |
601 | const HLS_REDUNDANCY_DIRECTORY = join(CONFIG.STORAGE.REDUNDANCY_DIR, 'hls') | 604 | const HLS_REDUNDANCY_DIRECTORY = join(CONFIG.STORAGE.REDUNDANCY_DIR, 'hls') |
602 | 605 | ||
606 | const VIDEO_LIVE = { | ||
607 | EXTENSION: '.ts', | ||
608 | RTMP: { | ||
609 | CHUNK_SIZE: 60000, | ||
610 | GOP_CACHE: true, | ||
611 | PING: 60, | ||
612 | PING_TIMEOUT: 30, | ||
613 | BASE_PATH: 'live' | ||
614 | } | ||
615 | } | ||
616 | |||
603 | const MEMOIZE_TTL = { | 617 | const MEMOIZE_TTL = { |
604 | OVERVIEWS_SAMPLE: 1000 * 3600 * 4, // 4 hours | 618 | OVERVIEWS_SAMPLE: 1000 * 3600 * 4, // 4 hours |
605 | INFO_HASH_EXISTS: 1000 * 3600 * 12 // 12 hours | 619 | INFO_HASH_EXISTS: 1000 * 3600 * 12 // 12 hours |
@@ -622,7 +636,8 @@ const REDUNDANCY = { | |||
622 | const ACCEPT_HEADERS = [ 'html', 'application/json' ].concat(ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS) | 636 | const ACCEPT_HEADERS = [ 'html', 'application/json' ].concat(ACTIVITY_PUB.POTENTIAL_ACCEPT_HEADERS) |
623 | 637 | ||
624 | const ASSETS_PATH = { | 638 | const ASSETS_PATH = { |
625 | DEFAULT_AUDIO_BACKGROUND: join(root(), 'dist', 'server', 'assets', 'default-audio-background.jpg') | 639 | DEFAULT_AUDIO_BACKGROUND: join(root(), 'dist', 'server', 'assets', 'default-audio-background.jpg'), |
640 | DEFAULT_LIVE_BACKGROUND: join(root(), 'dist', 'server', 'assets', 'default-live-background.jpg') | ||
626 | } | 641 | } |
627 | 642 | ||
628 | // --------------------------------------------------------------------------- | 643 | // --------------------------------------------------------------------------- |
@@ -688,9 +703,9 @@ if (isTestInstance() === true) { | |||
688 | STATIC_MAX_AGE.SERVER = '0' | 703 | STATIC_MAX_AGE.SERVER = '0' |
689 | 704 | ||
690 | ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE = 2 | 705 | ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE = 2 |
691 | ACTIVITY_PUB.ACTOR_REFRESH_INTERVAL = 10 * 1000 // 10 seconds | 706 | ACTIVITY_PUB.ACTOR_REFRESH_INTERVAL = 100 * 10000 // 10 seconds |
692 | ACTIVITY_PUB.VIDEO_REFRESH_INTERVAL = 10 * 1000 // 10 seconds | 707 | ACTIVITY_PUB.VIDEO_REFRESH_INTERVAL = 100 * 10000 // 10 seconds |
693 | ACTIVITY_PUB.VIDEO_PLAYLIST_REFRESH_INTERVAL = 10 * 1000 // 10 seconds | 708 | ACTIVITY_PUB.VIDEO_PLAYLIST_REFRESH_INTERVAL = 100 * 10000 // 10 seconds |
694 | 709 | ||
695 | CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max = 100 * 1024 // 100KB | 710 | CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max = 100 * 1024 // 100KB |
696 | 711 | ||
@@ -737,6 +752,7 @@ const FILES_CONTENT_HASH = { | |||
737 | export { | 752 | export { |
738 | WEBSERVER, | 753 | WEBSERVER, |
739 | API_VERSION, | 754 | API_VERSION, |
755 | VIDEO_LIVE, | ||
740 | PEERTUBE_VERSION, | 756 | PEERTUBE_VERSION, |
741 | LAZY_STATIC_PATHS, | 757 | LAZY_STATIC_PATHS, |
742 | SEARCH_INDEX, | 758 | SEARCH_INDEX, |
@@ -892,10 +908,14 @@ function buildVideoMimetypeExt () { | |||
892 | function updateWebserverUrls () { | 908 | function updateWebserverUrls () { |
893 | WEBSERVER.URL = sanitizeUrl(CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT) | 909 | WEBSERVER.URL = sanitizeUrl(CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT) |
894 | WEBSERVER.HOST = sanitizeHost(CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT, REMOTE_SCHEME.HTTP) | 910 | WEBSERVER.HOST = sanitizeHost(CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT, REMOTE_SCHEME.HTTP) |
895 | WEBSERVER.SCHEME = CONFIG.WEBSERVER.SCHEME | ||
896 | WEBSERVER.WS = CONFIG.WEBSERVER.WS | 911 | WEBSERVER.WS = CONFIG.WEBSERVER.WS |
912 | |||
913 | WEBSERVER.SCHEME = CONFIG.WEBSERVER.SCHEME | ||
897 | WEBSERVER.HOSTNAME = CONFIG.WEBSERVER.HOSTNAME | 914 | WEBSERVER.HOSTNAME = CONFIG.WEBSERVER.HOSTNAME |
898 | WEBSERVER.PORT = CONFIG.WEBSERVER.PORT | 915 | WEBSERVER.PORT = CONFIG.WEBSERVER.PORT |
916 | WEBSERVER.PORT = CONFIG.WEBSERVER.PORT | ||
917 | |||
918 | WEBSERVER.RTMP_URL = 'rtmp://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.LIVE.RTMP.PORT + '/' + VIDEO_LIVE.RTMP.BASE_PATH | ||
899 | } | 919 | } |
900 | 920 | ||
901 | function updateWebserverConfig () { | 921 | function updateWebserverConfig () { |
diff --git a/server/initializers/database.ts b/server/initializers/database.ts index a20cdacc3..128ed5b75 100644 --- a/server/initializers/database.ts +++ b/server/initializers/database.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import { QueryTypes, Transaction } from 'sequelize' | 1 | import { QueryTypes, Transaction } from 'sequelize' |
2 | import { Sequelize as SequelizeTypescript } from 'sequelize-typescript' | 2 | import { Sequelize as SequelizeTypescript } from 'sequelize-typescript' |
3 | import { AbuseModel } from '@server/models/abuse/abuse' | ||
4 | import { AbuseMessageModel } from '@server/models/abuse/abuse-message' | ||
5 | import { VideoAbuseModel } from '@server/models/abuse/video-abuse' | ||
6 | import { VideoCommentAbuseModel } from '@server/models/abuse/video-comment-abuse' | ||
7 | import { isTestInstance } from '../helpers/core-utils' | 3 | import { isTestInstance } from '../helpers/core-utils' |
8 | import { logger } from '../helpers/logger' | 4 | import { logger } from '../helpers/logger' |
5 | import { AbuseModel } from '../models/abuse/abuse' | ||
6 | import { AbuseMessageModel } from '../models/abuse/abuse-message' | ||
7 | import { VideoAbuseModel } from '../models/abuse/video-abuse' | ||
8 | import { VideoCommentAbuseModel } from '../models/abuse/video-comment-abuse' | ||
9 | import { AccountModel } from '../models/account/account' | 9 | import { AccountModel } from '../models/account/account' |
10 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | 10 | import { AccountBlocklistModel } from '../models/account/account-blocklist' |
11 | import { AccountVideoRateModel } from '../models/account/account-video-rate' | 11 | import { AccountVideoRateModel } from '../models/account/account-video-rate' |
@@ -34,6 +34,7 @@ import { VideoChannelModel } from '../models/video/video-channel' | |||
34 | import { VideoCommentModel } from '../models/video/video-comment' | 34 | import { VideoCommentModel } from '../models/video/video-comment' |
35 | import { VideoFileModel } from '../models/video/video-file' | 35 | import { VideoFileModel } from '../models/video/video-file' |
36 | import { VideoImportModel } from '../models/video/video-import' | 36 | import { VideoImportModel } from '../models/video/video-import' |
37 | import { VideoLiveModel } from '../models/video/video-live' | ||
37 | import { VideoPlaylistModel } from '../models/video/video-playlist' | 38 | import { VideoPlaylistModel } from '../models/video/video-playlist' |
38 | import { VideoPlaylistElementModel } from '../models/video/video-playlist-element' | 39 | import { VideoPlaylistElementModel } from '../models/video/video-playlist-element' |
39 | import { VideoShareModel } from '../models/video/video-share' | 40 | import { VideoShareModel } from '../models/video/video-share' |
@@ -118,6 +119,7 @@ async function initDatabaseModels (silent: boolean) { | |||
118 | VideoViewModel, | 119 | VideoViewModel, |
119 | VideoRedundancyModel, | 120 | VideoRedundancyModel, |
120 | UserVideoHistoryModel, | 121 | UserVideoHistoryModel, |
122 | VideoLiveModel, | ||
121 | AccountBlocklistModel, | 123 | AccountBlocklistModel, |
122 | ServerBlocklistModel, | 124 | ServerBlocklistModel, |
123 | UserNotificationModel, | 125 | UserNotificationModel, |
diff --git a/server/initializers/migrations/0535-video-live.ts b/server/initializers/migrations/0535-video-live.ts new file mode 100644 index 000000000..35523efc4 --- /dev/null +++ b/server/initializers/migrations/0535-video-live.ts | |||
@@ -0,0 +1,39 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction | ||
5 | queryInterface: Sequelize.QueryInterface | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | }): Promise<void> { | ||
8 | { | ||
9 | const query = ` | ||
10 | CREATE TABLE IF NOT EXISTS "videoLive" ( | ||
11 | "id" SERIAL , | ||
12 | "streamKey" VARCHAR(255) NOT NULL, | ||
13 | "videoId" INTEGER NOT NULL REFERENCES "video" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | ||
14 | "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
15 | "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
16 | PRIMARY KEY ("id") | ||
17 | ); | ||
18 | ` | ||
19 | |||
20 | await utils.sequelize.query(query) | ||
21 | } | ||
22 | |||
23 | { | ||
24 | await utils.queryInterface.addColumn('video', 'isLive', { | ||
25 | type: Sequelize.BOOLEAN, | ||
26 | defaultValue: false, | ||
27 | allowNull: false | ||
28 | }) | ||
29 | } | ||
30 | } | ||
31 | |||
32 | function down (options) { | ||
33 | throw new Error('Not implemented.') | ||
34 | } | ||
35 | |||
36 | export { | ||
37 | up, | ||
38 | down | ||
39 | } | ||
diff --git a/server/initializers/migrations/0540-video-file-infohash.ts b/server/initializers/migrations/0540-video-file-infohash.ts new file mode 100644 index 000000000..550178dab --- /dev/null +++ b/server/initializers/migrations/0540-video-file-infohash.ts | |||
@@ -0,0 +1,26 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction | ||
5 | queryInterface: Sequelize.QueryInterface | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | }): Promise<void> { | ||
8 | { | ||
9 | const data = { | ||
10 | type: Sequelize.STRING, | ||
11 | defaultValue: null, | ||
12 | allowNull: true | ||
13 | } | ||
14 | |||
15 | await utils.queryInterface.changeColumn('videoFile', 'infoHash', data) | ||
16 | } | ||
17 | } | ||
18 | |||
19 | function down (options) { | ||
20 | throw new Error('Not implemented.') | ||
21 | } | ||
22 | |||
23 | export { | ||
24 | up, | ||
25 | down | ||
26 | } | ||