aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared
diff options
context:
space:
mode:
Diffstat (limited to 'shared')
-rw-r--r--shared/models/activitypub/activity.ts31
-rw-r--r--shared/models/activitypub/activitypub-ordered-collection.ts5
-rw-r--r--shared/models/activitypub/objects/cache-file-object.ts4
-rw-r--r--shared/models/activitypub/objects/common-objects.ts60
-rw-r--r--shared/models/activitypub/objects/object.model.ts1
-rw-r--r--shared/models/activitypub/objects/video-torrent-object.ts3
-rw-r--r--shared/models/actors/actor.model.ts2
-rw-r--r--shared/models/server/custom-config.model.ts3
-rw-r--r--shared/models/server/server-config.model.ts14
-rw-r--r--shared/models/server/server-stats.model.ts1
-rw-r--r--shared/models/users/user-notification.model.ts31
-rw-r--r--shared/models/users/user-update.model.ts1
-rw-r--r--shared/models/videos/blacklist/video-blacklist-create.model.ts1
-rw-r--r--shared/models/videos/blacklist/video-blacklist.model.ts1
-rw-r--r--shared/models/videos/video-create.model.ts1
-rw-r--r--shared/models/videos/video-streaming-playlist.model.ts12
-rw-r--r--shared/models/videos/video-streaming-playlist.type.ts3
-rw-r--r--shared/models/videos/video-update.model.ts1
-rw-r--r--shared/models/videos/video.model.ts10
-rw-r--r--shared/utils/index.ts2
-rw-r--r--shared/utils/requests/requests.ts17
-rw-r--r--shared/utils/server/config.ts3
-rw-r--r--shared/utils/server/servers.ts15
-rw-r--r--shared/utils/users/user-notifications.ts3
-rw-r--r--shared/utils/users/users.ts2
-rw-r--r--shared/utils/videos/video-blacklist.ts11
-rw-r--r--shared/utils/videos/video-playlists.ts51
-rw-r--r--shared/utils/videos/videos.ts19
28 files changed, 248 insertions, 60 deletions
diff --git a/shared/models/activitypub/activity.ts b/shared/models/activitypub/activity.ts
index 44cb99efb..89994f665 100644
--- a/shared/models/activitypub/activity.ts
+++ b/shared/models/activitypub/activity.ts
@@ -5,12 +5,14 @@ import { DislikeObject } from './objects/dislike-object'
5import { VideoAbuseObject } from './objects/video-abuse-object' 5import { VideoAbuseObject } from './objects/video-abuse-object'
6import { VideoCommentObject } from './objects/video-comment-object' 6import { VideoCommentObject } from './objects/video-comment-object'
7import { ViewObject } from './objects/view-object' 7import { ViewObject } from './objects/view-object'
8import { APObject } from './objects/object.model'
8 9
9export type Activity = ActivityCreate | ActivityUpdate | 10export type Activity = ActivityCreate | ActivityUpdate |
10 ActivityDelete | ActivityFollow | ActivityAccept | ActivityAnnounce | 11 ActivityDelete | ActivityFollow | ActivityAccept | ActivityAnnounce |
11 ActivityUndo | ActivityLike | ActivityReject 12 ActivityUndo | ActivityLike | ActivityReject | ActivityView | ActivityDislike | ActivityFlag
12 13
13export type ActivityType = 'Create' | 'Update' | 'Delete' | 'Follow' | 'Accept' | 'Announce' | 'Undo' | 'Like' | 'Reject' 14export type ActivityType = 'Create' | 'Update' | 'Delete' | 'Follow' | 'Accept' | 'Announce' | 'Undo' | 'Like' | 'Reject' |
15 'View' | 'Dislike' | 'Flag'
14 16
15export interface ActivityAudience { 17export interface ActivityAudience {
16 to: string[] 18 to: string[]
@@ -59,15 +61,34 @@ export interface ActivityReject extends BaseActivity {
59 61
60export interface ActivityAnnounce extends BaseActivity { 62export interface ActivityAnnounce extends BaseActivity {
61 type: 'Announce' 63 type: 'Announce'
62 object: string | { id: string } 64 object: APObject
63} 65}
64 66
65export interface ActivityUndo extends BaseActivity { 67export interface ActivityUndo extends BaseActivity {
66 type: 'Undo', 68 type: 'Undo',
67 object: ActivityFollow | ActivityLike | ActivityCreate | ActivityAnnounce 69 object: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce
68} 70}
69 71
70export interface ActivityLike extends BaseActivity { 72export interface ActivityLike extends BaseActivity {
71 type: 'Like', 73 type: 'Like',
72 object: string 74 object: APObject
75}
76
77export interface ActivityView extends BaseActivity {
78 type: 'View',
79 actor: string
80 object: APObject
81}
82
83export interface ActivityDislike extends BaseActivity {
84 id: string
85 type: 'Dislike'
86 actor: string
87 object: APObject
88}
89
90export interface ActivityFlag extends BaseActivity {
91 type: 'Flag',
92 content: string,
93 object: APObject
73} 94}
diff --git a/shared/models/activitypub/activitypub-ordered-collection.ts b/shared/models/activitypub/activitypub-ordered-collection.ts
index dfec0bb76..3de0890bb 100644
--- a/shared/models/activitypub/activitypub-ordered-collection.ts
+++ b/shared/models/activitypub/activitypub-ordered-collection.ts
@@ -2,6 +2,9 @@ export interface ActivityPubOrderedCollection<T> {
2 '@context': string[] 2 '@context': string[]
3 type: 'OrderedCollection' | 'OrderedCollectionPage' 3 type: 'OrderedCollection' | 'OrderedCollectionPage'
4 totalItems: number 4 totalItems: number
5 partOf?: string
6 orderedItems: T[] 5 orderedItems: T[]
6
7 partOf?: string
8 next?: string
9 first?: string
7} 10}
diff --git a/shared/models/activitypub/objects/cache-file-object.ts b/shared/models/activitypub/objects/cache-file-object.ts
index 0a5125f5b..4b0a3a724 100644
--- a/shared/models/activitypub/objects/cache-file-object.ts
+++ b/shared/models/activitypub/objects/cache-file-object.ts
@@ -1,9 +1,9 @@
1import { ActivityVideoUrlObject } from './common-objects' 1import { ActivityVideoUrlObject, ActivityPlaylistUrlObject } from './common-objects'
2 2
3export interface CacheFileObject { 3export interface CacheFileObject {
4 id: string 4 id: string
5 type: 'CacheFile', 5 type: 'CacheFile',
6 object: string 6 object: string
7 expires: string 7 expires: string
8 url: ActivityVideoUrlObject 8 url: ActivityVideoUrlObject | ActivityPlaylistUrlObject
9} 9}
diff --git a/shared/models/activitypub/objects/common-objects.ts b/shared/models/activitypub/objects/common-objects.ts
index 118a4f43d..8c89810d6 100644
--- a/shared/models/activitypub/objects/common-objects.ts
+++ b/shared/models/activitypub/objects/common-objects.ts
@@ -28,25 +28,47 @@ export type ActivityVideoUrlObject = {
28 fps: number 28 fps: number
29} 29}
30 30
31export type ActivityUrlObject = 31export type ActivityPlaylistSegmentHashesObject = {
32 ActivityVideoUrlObject 32 type: 'Link'
33 | 33 name: 'sha256'
34 { 34 // TODO: remove mimeType (backward compatibility, introduced in v1.1.0)
35 type: 'Link' 35 mimeType?: 'application/json'
36 // TODO: remove mimeType (backward compatibility, introduced in v1.1.0) 36 mediaType: 'application/json'
37 mimeType?: 'application/x-bittorrent' | 'application/x-bittorrent;x-scheme-handler/magnet' 37 href: string
38 mediaType: 'application/x-bittorrent' | 'application/x-bittorrent;x-scheme-handler/magnet' 38}
39 href: string 39
40 height: number 40export type ActivityPlaylistInfohashesObject = {
41 } 41 type: 'Infohash'
42 | 42 name: string
43 { 43}
44 type: 'Link' 44
45 // TODO: remove mimeType (backward compatibility, introduced in v1.1.0) 45export type ActivityPlaylistUrlObject = {
46 mimeType?: 'text/html' 46 type: 'Link'
47 mediaType: 'text/html' 47 // TODO: remove mimeType (backward compatibility, introduced in v1.1.0)
48 href: string 48 mimeType?: 'application/x-mpegURL'
49 } 49 mediaType: 'application/x-mpegURL'
50 href: string
51 tag?: (ActivityPlaylistSegmentHashesObject | ActivityPlaylistInfohashesObject)[]
52}
53
54export type ActivityBitTorrentUrlObject = {
55 type: 'Link'
56 // TODO: remove mimeType (backward compatibility, introduced in v1.1.0)
57 mimeType?: 'application/x-bittorrent' | 'application/x-bittorrent;x-scheme-handler/magnet'
58 mediaType: 'application/x-bittorrent' | 'application/x-bittorrent;x-scheme-handler/magnet'
59 href: string
60 height: number
61}
62
63export type ActivityHtmlUrlObject = {
64 type: 'Link'
65 // TODO: remove mimeType (backward compatibility, introduced in v1.1.0)
66 mimeType?: 'text/html'
67 mediaType: 'text/html'
68 href: string
69}
70
71export type ActivityUrlObject = ActivityVideoUrlObject | ActivityPlaylistUrlObject | ActivityBitTorrentUrlObject | ActivityHtmlUrlObject
50 72
51export interface ActivityPubAttributedTo { 73export interface ActivityPubAttributedTo {
52 type: 'Group' | 'Person' 74 type: 'Group' | 'Person'
diff --git a/shared/models/activitypub/objects/object.model.ts b/shared/models/activitypub/objects/object.model.ts
new file mode 100644
index 000000000..3fd33800a
--- /dev/null
+++ b/shared/models/activitypub/objects/object.model.ts
@@ -0,0 +1 @@
export type APObject = string | { id: string }
diff --git a/shared/models/activitypub/objects/video-torrent-object.ts b/shared/models/activitypub/objects/video-torrent-object.ts
index df07507b4..239822bc4 100644
--- a/shared/models/activitypub/objects/video-torrent-object.ts
+++ b/shared/models/activitypub/objects/video-torrent-object.ts
@@ -20,7 +20,8 @@ export interface VideoTorrentObject {
20 subtitleLanguage: ActivityIdentifierObject[] 20 subtitleLanguage: ActivityIdentifierObject[]
21 views: number 21 views: number
22 sensitive: boolean 22 sensitive: boolean
23 commentsEnabled: boolean 23 commentsEnabled: boolean,
24 downloadEnabled: boolean,
24 waitTranscoding: boolean 25 waitTranscoding: boolean
25 state: VideoState 26 state: VideoState
26 published: string 27 published: string
diff --git a/shared/models/actors/actor.model.ts b/shared/models/actors/actor.model.ts
index 6b3b1b47c..a3953874d 100644
--- a/shared/models/actors/actor.model.ts
+++ b/shared/models/actors/actor.model.ts
@@ -10,5 +10,5 @@ export interface Actor {
10 followersCount: number 10 followersCount: number
11 createdAt: Date | string 11 createdAt: Date | string
12 updatedAt: Date | string 12 updatedAt: Date | string
13 avatar: Avatar 13 avatar?: Avatar
14} 14}
diff --git a/shared/models/server/custom-config.model.ts b/shared/models/server/custom-config.model.ts
index 7a3eaa33f..b42ff90c6 100644
--- a/shared/models/server/custom-config.model.ts
+++ b/shared/models/server/custom-config.model.ts
@@ -61,6 +61,9 @@ export interface CustomConfig {
61 '720p': boolean 61 '720p': boolean
62 '1080p': boolean 62 '1080p': boolean
63 } 63 }
64 hls: {
65 enabled: boolean
66 }
64 } 67 }
65 68
66 import: { 69 import: {
diff --git a/shared/models/server/server-config.model.ts b/shared/models/server/server-config.model.ts
index 7031009d9..baafed31f 100644
--- a/shared/models/server/server-config.model.ts
+++ b/shared/models/server/server-config.model.ts
@@ -25,11 +25,15 @@ export interface ServerConfig {
25 25
26 signup: { 26 signup: {
27 allowed: boolean, 27 allowed: boolean,
28 allowedForCurrentIP: boolean, 28 allowedForCurrentIP: boolean
29 requiresEmailVerification: boolean 29 requiresEmailVerification: boolean
30 } 30 }
31 31
32 transcoding: { 32 transcoding: {
33 hls: {
34 enabled: boolean
35 }
36
33 enabledResolutions: number[] 37 enabledResolutions: number[]
34 } 38 }
35 39
@@ -48,7 +52,7 @@ export interface ServerConfig {
48 file: { 52 file: {
49 size: { 53 size: {
50 max: number 54 max: number
51 }, 55 }
52 extensions: string[] 56 extensions: string[]
53 } 57 }
54 } 58 }
@@ -78,4 +82,10 @@ export interface ServerConfig {
78 videoQuota: number 82 videoQuota: number
79 videoQuotaDaily: number 83 videoQuotaDaily: number
80 } 84 }
85
86 trending: {
87 videos: {
88 intervalDays: number
89 }
90 }
81} 91}
diff --git a/shared/models/server/server-stats.model.ts b/shared/models/server/server-stats.model.ts
index a6bd2d4d3..74f3de5d3 100644
--- a/shared/models/server/server-stats.model.ts
+++ b/shared/models/server/server-stats.model.ts
@@ -5,6 +5,7 @@ export interface ServerStats {
5 totalLocalVideos: number 5 totalLocalVideos: number
6 totalLocalVideoViews: number 6 totalLocalVideoViews: number
7 totalLocalVideoComments: number 7 totalLocalVideoComments: number
8 totalLocalVideoFilesSize: number
8 9
9 totalVideos: number 10 totalVideos: number
10 totalVideoComments: number 11 totalVideoComments: number
diff --git a/shared/models/users/user-notification.model.ts b/shared/models/users/user-notification.model.ts
index f41b6f534..186b62612 100644
--- a/shared/models/users/user-notification.model.ts
+++ b/shared/models/users/user-notification.model.ts
@@ -22,16 +22,23 @@ export interface VideoInfo {
22 name: string 22 name: string
23} 23}
24 24
25export interface ActorInfo {
26 id: number
27 displayName: string
28 name: string
29 host: string
30 avatar?: {
31 path: string
32 }
33}
34
25export interface UserNotification { 35export interface UserNotification {
26 id: number 36 id: number
27 type: UserNotificationType 37 type: UserNotificationType
28 read: boolean 38 read: boolean
29 39
30 video?: VideoInfo & { 40 video?: VideoInfo & {
31 channel: { 41 channel: ActorInfo
32 id: number
33 displayName: string
34 }
35 } 42 }
36 43
37 videoImport?: { 44 videoImport?: {
@@ -45,10 +52,7 @@ export interface UserNotification {
45 comment?: { 52 comment?: {
46 id: number 53 id: number
47 threadId: number 54 threadId: number
48 account: { 55 account: ActorInfo
49 id: number
50 displayName: string
51 }
52 video: VideoInfo 56 video: VideoInfo
53 } 57 }
54 58
@@ -62,18 +66,11 @@ export interface UserNotification {
62 video: VideoInfo 66 video: VideoInfo
63 } 67 }
64 68
65 account?: { 69 account?: ActorInfo
66 id: number
67 displayName: string
68 name: string
69 }
70 70
71 actorFollow?: { 71 actorFollow?: {
72 id: number 72 id: number
73 follower: { 73 follower: ActorInfo
74 name: string
75 displayName: string
76 }
77 following: { 74 following: {
78 type: 'account' | 'channel' 75 type: 'account' | 'channel'
79 name: string 76 name: string
diff --git a/shared/models/users/user-update.model.ts b/shared/models/users/user-update.model.ts
index abde51321..cd215bab3 100644
--- a/shared/models/users/user-update.model.ts
+++ b/shared/models/users/user-update.model.ts
@@ -1,6 +1,7 @@
1import { UserRole } from './user-role' 1import { UserRole } from './user-role'
2 2
3export interface UserUpdate { 3export interface UserUpdate {
4 password?: string
4 email?: string 5 email?: string
5 emailVerified?: boolean 6 emailVerified?: boolean
6 videoQuota?: number 7 videoQuota?: number
diff --git a/shared/models/videos/blacklist/video-blacklist-create.model.ts b/shared/models/videos/blacklist/video-blacklist-create.model.ts
index 89c69cb56..6e7d36421 100644
--- a/shared/models/videos/blacklist/video-blacklist-create.model.ts
+++ b/shared/models/videos/blacklist/video-blacklist-create.model.ts
@@ -1,3 +1,4 @@
1export interface VideoBlacklistCreate { 1export interface VideoBlacklistCreate {
2 reason?: string 2 reason?: string
3 unfederate?: boolean
3} 4}
diff --git a/shared/models/videos/blacklist/video-blacklist.model.ts b/shared/models/videos/blacklist/video-blacklist.model.ts
index ef4e5e3a2..4bd976190 100644
--- a/shared/models/videos/blacklist/video-blacklist.model.ts
+++ b/shared/models/videos/blacklist/video-blacklist.model.ts
@@ -2,6 +2,7 @@ export interface VideoBlacklist {
2 id: number 2 id: number
3 createdAt: Date 3 createdAt: Date
4 updatedAt: Date 4 updatedAt: Date
5 unfederated: boolean
5 reason?: string 6 reason?: string
6 7
7 video: { 8 video: {
diff --git a/shared/models/videos/video-create.model.ts b/shared/models/videos/video-create.model.ts
index 392bd1025..53631bf79 100644
--- a/shared/models/videos/video-create.model.ts
+++ b/shared/models/videos/video-create.model.ts
@@ -13,6 +13,7 @@ export interface VideoCreate {
13 name: string 13 name: string
14 tags?: string[] 14 tags?: string[]
15 commentsEnabled?: boolean 15 commentsEnabled?: boolean
16 downloadEnabled?: boolean
16 privacy: VideoPrivacy 17 privacy: VideoPrivacy
17 scheduleUpdate?: VideoScheduleUpdate 18 scheduleUpdate?: VideoScheduleUpdate
18 originallyPublishedAt: Date | string 19 originallyPublishedAt: Date | string
diff --git a/shared/models/videos/video-streaming-playlist.model.ts b/shared/models/videos/video-streaming-playlist.model.ts
new file mode 100644
index 000000000..17f8fe865
--- /dev/null
+++ b/shared/models/videos/video-streaming-playlist.model.ts
@@ -0,0 +1,12 @@
1import { VideoStreamingPlaylistType } from './video-streaming-playlist.type'
2
3export class VideoStreamingPlaylist {
4 id: number
5 type: VideoStreamingPlaylistType
6 playlistUrl: string
7 segmentsSha256Url: string
8
9 redundancies: {
10 baseUrl: string
11 }[]
12}
diff --git a/shared/models/videos/video-streaming-playlist.type.ts b/shared/models/videos/video-streaming-playlist.type.ts
new file mode 100644
index 000000000..3b403f295
--- /dev/null
+++ b/shared/models/videos/video-streaming-playlist.type.ts
@@ -0,0 +1,3 @@
1export enum VideoStreamingPlaylistType {
2 HLS = 1
3}
diff --git a/shared/models/videos/video-update.model.ts b/shared/models/videos/video-update.model.ts
index 62e02e079..4ef904156 100644
--- a/shared/models/videos/video-update.model.ts
+++ b/shared/models/videos/video-update.model.ts
@@ -11,6 +11,7 @@ export interface VideoUpdate {
11 privacy?: VideoPrivacy 11 privacy?: VideoPrivacy
12 tags?: string[] 12 tags?: string[]
13 commentsEnabled?: boolean 13 commentsEnabled?: boolean
14 downloadEnabled?: boolean
14 nsfw?: boolean 15 nsfw?: boolean
15 waitTranscoding?: boolean 16 waitTranscoding?: boolean
16 channelId?: number 17 channelId?: number
diff --git a/shared/models/videos/video.model.ts b/shared/models/videos/video.model.ts
index 2373ceb18..df800461c 100644
--- a/shared/models/videos/video.model.ts
+++ b/shared/models/videos/video.model.ts
@@ -5,6 +5,7 @@ import { VideoChannel } from './channel/video-channel.model'
5import { VideoPrivacy } from './video-privacy.enum' 5import { VideoPrivacy } from './video-privacy.enum'
6import { VideoScheduleUpdate } from './video-schedule-update.model' 6import { VideoScheduleUpdate } from './video-schedule-update.model'
7import { VideoConstant } from './video-constant.model' 7import { VideoConstant } from './video-constant.model'
8import { VideoStreamingPlaylist } from './video-streaming-playlist.model'
8 9
9export interface VideoFile { 10export interface VideoFile {
10 magnetUri: string 11 magnetUri: string
@@ -24,7 +25,7 @@ export interface VideoChannelAttribute {
24 displayName: string 25 displayName: string
25 url: string 26 url: string
26 host: string 27 host: string
27 avatar: Avatar 28 avatar?: Avatar
28} 29}
29 30
30export interface AccountAttribute { 31export interface AccountAttribute {
@@ -34,7 +35,7 @@ export interface AccountAttribute {
34 displayName: string 35 displayName: string
35 url: string 36 url: string
36 host: string 37 host: string
37 avatar: Avatar 38 avatar?: Avatar
38} 39}
39 40
40export interface Video { 41export interface Video {
@@ -83,8 +84,13 @@ export interface VideoDetails extends Video {
83 files: VideoFile[] 84 files: VideoFile[]
84 account: Account 85 account: Account
85 commentsEnabled: boolean 86 commentsEnabled: boolean
87 downloadEnabled: boolean
86 88
87 // Not optional in details (unlike in Video) 89 // Not optional in details (unlike in Video)
88 waitTranscoding: boolean 90 waitTranscoding: boolean
89 state: VideoConstant<VideoState> 91 state: VideoConstant<VideoState>
92
93 trackerUrls: string[]
94
95 streamingPlaylists: VideoStreamingPlaylist[]
90} 96}
diff --git a/shared/utils/index.ts b/shared/utils/index.ts
index e08bbfd2a..156901372 100644
--- a/shared/utils/index.ts
+++ b/shared/utils/index.ts
@@ -17,6 +17,8 @@ export * from './users/users'
17export * from './videos/video-abuses' 17export * from './videos/video-abuses'
18export * from './videos/video-blacklist' 18export * from './videos/video-blacklist'
19export * from './videos/video-channels' 19export * from './videos/video-channels'
20export * from './videos/video-comments'
21export * from './videos/video-playlists'
20export * from './videos/videos' 22export * from './videos/videos'
21export * from './videos/video-change-ownership' 23export * from './videos/video-change-ownership'
22export * from './feeds/feeds' 24export * from './feeds/feeds'
diff --git a/shared/utils/requests/requests.ts b/shared/utils/requests/requests.ts
index 77e9f6164..6b59e24fc 100644
--- a/shared/utils/requests/requests.ts
+++ b/shared/utils/requests/requests.ts
@@ -1,24 +1,32 @@
1import * as request from 'supertest' 1import * as request from 'supertest'
2import { buildAbsoluteFixturePath, root } from '../miscs/miscs' 2import { buildAbsoluteFixturePath, root } from '../miscs/miscs'
3import { isAbsolute, join } from 'path' 3import { isAbsolute, join } from 'path'
4import { parse } from 'url'
5
6function makeRawRequest (url: string, statusCodeExpected?: number, range?: string) {
7 const { host, protocol, pathname } = parse(url)
8
9 return makeGetRequest({ url: `${protocol}//${host}`, path: pathname, statusCodeExpected, range })
10}
4 11
5function makeGetRequest (options: { 12function makeGetRequest (options: {
6 url: string, 13 url: string,
7 path: string, 14 path?: string,
8 query?: any, 15 query?: any,
9 token?: string, 16 token?: string,
10 statusCodeExpected?: number, 17 statusCodeExpected?: number,
11 contentType?: string 18 contentType?: string,
19 range?: string
12}) { 20}) {
13 if (!options.statusCodeExpected) options.statusCodeExpected = 400 21 if (!options.statusCodeExpected) options.statusCodeExpected = 400
14 if (options.contentType === undefined) options.contentType = 'application/json' 22 if (options.contentType === undefined) options.contentType = 'application/json'
15 23
16 const req = request(options.url) 24 const req = request(options.url).get(options.path)
17 .get(options.path)
18 25
19 if (options.contentType) req.set('Accept', options.contentType) 26 if (options.contentType) req.set('Accept', options.contentType)
20 if (options.token) req.set('Authorization', 'Bearer ' + options.token) 27 if (options.token) req.set('Authorization', 'Bearer ' + options.token)
21 if (options.query) req.query(options.query) 28 if (options.query) req.query(options.query)
29 if (options.range) req.set('Range', options.range)
22 30
23 return req.expect(options.statusCodeExpected) 31 return req.expect(options.statusCodeExpected)
24} 32}
@@ -164,5 +172,6 @@ export {
164 makePostBodyRequest, 172 makePostBodyRequest,
165 makePutBodyRequest, 173 makePutBodyRequest,
166 makeDeleteRequest, 174 makeDeleteRequest,
175 makeRawRequest,
167 updateAvatarRequest 176 updateAvatarRequest
168} 177}
diff --git a/shared/utils/server/config.ts b/shared/utils/server/config.ts
index 0c5512bab..29c24cff9 100644
--- a/shared/utils/server/config.ts
+++ b/shared/utils/server/config.ts
@@ -97,6 +97,9 @@ function updateCustomSubConfig (url: string, token: string, newConfig: any) {
97 '480p': true, 97 '480p': true,
98 '720p': false, 98 '720p': false,
99 '1080p': false 99 '1080p': false
100 },
101 hls: {
102 enabled: false
100 } 103 }
101 }, 104 },
102 import: { 105 import: {
diff --git a/shared/utils/server/servers.ts b/shared/utils/server/servers.ts
index 568385a41..bde7dd5c2 100644
--- a/shared/utils/server/servers.ts
+++ b/shared/utils/server/servers.ts
@@ -145,8 +145,16 @@ function runServer (serverNumber: number, configOverride?: Object, args = []) {
145 if (dontContinue === true) return 145 if (dontContinue === true) return
146 146
147 server.app.stdout.removeListener('data', onStdout) 147 server.app.stdout.removeListener('data', onStdout)
148
149 process.on('exit', () => {
150 try {
151 process.kill(server.app.pid)
152 } catch { /* empty */ }
153 })
154
148 res(server) 155 res(server)
149 }) 156 })
157
150 }) 158 })
151} 159}
152 160
@@ -158,9 +166,13 @@ async function reRunServer (server: ServerInfo, configOverride?: any) {
158} 166}
159 167
160async function checkTmpIsEmpty (server: ServerInfo) { 168async function checkTmpIsEmpty (server: ServerInfo) {
169 return checkDirectoryIsEmpty(server, 'tmp')
170}
171
172async function checkDirectoryIsEmpty (server: ServerInfo, directory: string) {
161 const testDirectory = 'test' + server.serverNumber 173 const testDirectory = 'test' + server.serverNumber
162 174
163 const directoryPath = join(root(), testDirectory, 'tmp') 175 const directoryPath = join(root(), testDirectory, directory)
164 176
165 const directoryExists = existsSync(directoryPath) 177 const directoryExists = existsSync(directoryPath)
166 expect(directoryExists).to.be.true 178 expect(directoryExists).to.be.true
@@ -191,6 +203,7 @@ async function waitUntilLog (server: ServerInfo, str: string, count = 1) {
191// --------------------------------------------------------------------------- 203// ---------------------------------------------------------------------------
192 204
193export { 205export {
206 checkDirectoryIsEmpty,
194 checkTmpIsEmpty, 207 checkTmpIsEmpty,
195 ServerInfo, 208 ServerInfo,
196 flushAndRunMultipleServers, 209 flushAndRunMultipleServers,
diff --git a/shared/utils/users/user-notifications.ts b/shared/utils/users/user-notifications.ts
index bcbe29fc7..c8ed7df30 100644
--- a/shared/utils/users/user-notifications.ts
+++ b/shared/utils/users/user-notifications.ts
@@ -146,6 +146,7 @@ function checkVideo (video: any, videoName?: string, videoUUID?: string) {
146function checkActor (actor: any) { 146function checkActor (actor: any) {
147 expect(actor.displayName).to.be.a('string') 147 expect(actor.displayName).to.be.a('string')
148 expect(actor.displayName).to.not.be.empty 148 expect(actor.displayName).to.not.be.empty
149 expect(actor.host).to.not.be.undefined
149} 150}
150 151
151function checkComment (comment: any, commentId: number, threadId: number) { 152function checkComment (comment: any, commentId: number, threadId: number) {
@@ -273,8 +274,8 @@ async function checkNewActorFollow (
273 checkActor(notification.actorFollow.follower) 274 checkActor(notification.actorFollow.follower)
274 expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName) 275 expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName)
275 expect(notification.actorFollow.follower.name).to.equal(followerName) 276 expect(notification.actorFollow.follower.name).to.equal(followerName)
277 expect(notification.actorFollow.follower.host).to.not.be.undefined
276 278
277 checkActor(notification.actorFollow.following)
278 expect(notification.actorFollow.following.displayName).to.equal(followingDisplayName) 279 expect(notification.actorFollow.following.displayName).to.equal(followingDisplayName)
279 expect(notification.actorFollow.following.type).to.equal(followType) 280 expect(notification.actorFollow.following.type).to.equal(followType)
280 } else { 281 } else {
diff --git a/shared/utils/users/users.ts b/shared/utils/users/users.ts
index 61a7e3757..7191b263e 100644
--- a/shared/utils/users/users.ts
+++ b/shared/utils/users/users.ts
@@ -213,11 +213,13 @@ function updateUser (options: {
213 emailVerified?: boolean, 213 emailVerified?: boolean,
214 videoQuota?: number, 214 videoQuota?: number,
215 videoQuotaDaily?: number, 215 videoQuotaDaily?: number,
216 password?: string,
216 role?: UserRole 217 role?: UserRole
217}) { 218}) {
218 const path = '/api/v1/users/' + options.userId 219 const path = '/api/v1/users/' + options.userId
219 220
220 const toSend = {} 221 const toSend = {}
222 if (options.password !== undefined && options.password !== null) toSend['password'] = options.password
221 if (options.email !== undefined && options.email !== null) toSend['email'] = options.email 223 if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
222 if (options.emailVerified !== undefined && options.emailVerified !== null) toSend['emailVerified'] = options.emailVerified 224 if (options.emailVerified !== undefined && options.emailVerified !== null) toSend['emailVerified'] = options.emailVerified
223 if (options.videoQuota !== undefined && options.videoQuota !== null) toSend['videoQuota'] = options.videoQuota 225 if (options.videoQuota !== undefined && options.videoQuota !== null) toSend['videoQuota'] = options.videoQuota
diff --git a/shared/utils/videos/video-blacklist.ts b/shared/utils/videos/video-blacklist.ts
index 2c176fde0..f2ae0ed26 100644
--- a/shared/utils/videos/video-blacklist.ts
+++ b/shared/utils/videos/video-blacklist.ts
@@ -1,11 +1,18 @@
1import * as request from 'supertest' 1import * as request from 'supertest'
2 2
3function addVideoToBlacklist (url: string, token: string, videoId: number | string, reason?: string, specialStatus = 204) { 3function addVideoToBlacklist (
4 url: string,
5 token: string,
6 videoId: number | string,
7 reason?: string,
8 unfederate?: boolean,
9 specialStatus = 204
10) {
4 const path = '/api/v1/videos/' + videoId + '/blacklist' 11 const path = '/api/v1/videos/' + videoId + '/blacklist'
5 12
6 return request(url) 13 return request(url)
7 .post(path) 14 .post(path)
8 .send({ reason }) 15 .send({ reason, unfederate })
9 .set('Accept', 'application/json') 16 .set('Accept', 'application/json')
10 .set('Authorization', 'Bearer ' + token) 17 .set('Authorization', 'Bearer ' + token)
11 .expect(specialStatus) 18 .expect(specialStatus)
diff --git a/shared/utils/videos/video-playlists.ts b/shared/utils/videos/video-playlists.ts
new file mode 100644
index 000000000..eb25011cb
--- /dev/null
+++ b/shared/utils/videos/video-playlists.ts
@@ -0,0 +1,51 @@
1import { makeRawRequest } from '../requests/requests'
2import { sha256 } from '../../../server/helpers/core-utils'
3import { VideoStreamingPlaylist } from '../../models/videos/video-streaming-playlist.model'
4import { expect } from 'chai'
5
6function getPlaylist (url: string, statusCodeExpected = 200) {
7 return makeRawRequest(url, statusCodeExpected)
8}
9
10function getSegment (url: string, statusCodeExpected = 200, range?: string) {
11 return makeRawRequest(url, statusCodeExpected, range)
12}
13
14function getSegmentSha256 (url: string, statusCodeExpected = 200) {
15 return makeRawRequest(url, statusCodeExpected)
16}
17
18async function checkSegmentHash (
19 baseUrlPlaylist: string,
20 baseUrlSegment: string,
21 videoUUID: string,
22 resolution: number,
23 hlsPlaylist: VideoStreamingPlaylist
24) {
25 const res = await getPlaylist(`${baseUrlPlaylist}/${videoUUID}/${resolution}.m3u8`)
26 const playlist = res.text
27
28 const videoName = `${videoUUID}-${resolution}-fragmented.mp4`
29
30 const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist)
31
32 const length = parseInt(matches[1], 10)
33 const offset = parseInt(matches[2], 10)
34 const range = `${offset}-${offset + length - 1}`
35
36 const res2 = await getSegment(`${baseUrlSegment}/${videoUUID}/${videoName}`, 206, `bytes=${range}`)
37
38 const resSha = await getSegmentSha256(hlsPlaylist.segmentsSha256Url)
39
40 const sha256Server = resSha.body[ videoName ][range]
41 expect(sha256(res2.body)).to.equal(sha256Server)
42}
43
44// ---------------------------------------------------------------------------
45
46export {
47 getPlaylist,
48 getSegment,
49 getSegmentSha256,
50 checkSegmentHash
51}
diff --git a/shared/utils/videos/videos.ts b/shared/utils/videos/videos.ts
index 0cf6e7c4f..39c808d1f 100644
--- a/shared/utils/videos/videos.ts
+++ b/shared/utils/videos/videos.ts
@@ -28,6 +28,7 @@ type VideoAttributes = {
28 language?: string 28 language?: string
29 nsfw?: boolean 29 nsfw?: boolean
30 commentsEnabled?: boolean 30 commentsEnabled?: boolean
31 downloadEnabled?: boolean
31 waitTranscoding?: boolean 32 waitTranscoding?: boolean
32 description?: string 33 description?: string
33 tags?: string[] 34 tags?: string[]
@@ -271,7 +272,16 @@ function removeVideo (url: string, token: string, id: number | string, expectedS
271async function checkVideoFilesWereRemoved ( 272async function checkVideoFilesWereRemoved (
272 videoUUID: string, 273 videoUUID: string,
273 serverNumber: number, 274 serverNumber: number,
274 directories = [ 'redundancy', 'videos', 'thumbnails', 'torrents', 'previews', 'captions' ] 275 directories = [
276 'redundancy',
277 'videos',
278 'thumbnails',
279 'torrents',
280 'previews',
281 'captions',
282 join('playlists', 'hls'),
283 join('redundancy', 'hls')
284 ]
275) { 285) {
276 const testDirectory = 'test' + serverNumber 286 const testDirectory = 'test' + serverNumber
277 287
@@ -279,7 +289,7 @@ async function checkVideoFilesWereRemoved (
279 const directoryPath = join(root(), testDirectory, directory) 289 const directoryPath = join(root(), testDirectory, directory)
280 290
281 const directoryExists = existsSync(directoryPath) 291 const directoryExists = existsSync(directoryPath)
282 expect(directoryExists).to.be.true 292 if (!directoryExists) continue
283 293
284 const files = await readdir(directoryPath) 294 const files = await readdir(directoryPath)
285 for (const file of files) { 295 for (const file of files) {
@@ -311,6 +321,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
311 tags: [ 'tag' ], 321 tags: [ 'tag' ],
312 privacy: VideoPrivacy.PUBLIC, 322 privacy: VideoPrivacy.PUBLIC,
313 commentsEnabled: true, 323 commentsEnabled: true,
324 downloadEnabled: true,
314 fixture: 'video_short.webm' 325 fixture: 'video_short.webm'
315 }, videoAttributesArg) 326 }, videoAttributesArg)
316 327
@@ -321,6 +332,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
321 .field('name', attributes.name) 332 .field('name', attributes.name)
322 .field('nsfw', JSON.stringify(attributes.nsfw)) 333 .field('nsfw', JSON.stringify(attributes.nsfw))
323 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled)) 334 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
335 .field('downloadEnabled', JSON.stringify(attributes.downloadEnabled))
324 .field('waitTranscoding', JSON.stringify(attributes.waitTranscoding)) 336 .field('waitTranscoding', JSON.stringify(attributes.waitTranscoding))
325 .field('privacy', attributes.privacy.toString()) 337 .field('privacy', attributes.privacy.toString())
326 .field('channelId', attributes.channelId) 338 .field('channelId', attributes.channelId)
@@ -371,6 +383,7 @@ function updateVideo (url: string, accessToken: string, id: number | string, att
371 if (attributes.language) body['language'] = attributes.language 383 if (attributes.language) body['language'] = attributes.language
372 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw) 384 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
373 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled) 385 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
386 if (attributes.downloadEnabled !== undefined) body['downloadEnabled'] = JSON.stringify(attributes.downloadEnabled)
374 if (attributes.description) body['description'] = attributes.description 387 if (attributes.description) body['description'] = attributes.description
375 if (attributes.tags) body['tags'] = attributes.tags 388 if (attributes.tags) body['tags'] = attributes.tags
376 if (attributes.privacy) body['privacy'] = attributes.privacy 389 if (attributes.privacy) body['privacy'] = attributes.privacy
@@ -436,6 +449,7 @@ async function completeVideoCheck (
436 language: string 449 language: string
437 nsfw: boolean 450 nsfw: boolean
438 commentsEnabled: boolean 451 commentsEnabled: boolean
452 downloadEnabled: boolean
439 description: string 453 description: string
440 publishedAt?: string 454 publishedAt?: string
441 support: string 455 support: string
@@ -510,6 +524,7 @@ async function completeVideoCheck (
510 expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true 524 expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
511 expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true 525 expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
512 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled) 526 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
527 expect(videoDetails.downloadEnabled).to.equal(attributes.downloadEnabled)
513 528
514 for (const attributeFile of attributes.files) { 529 for (const attributeFile of attributes.files) {
515 const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution) 530 const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)