diff options
241 files changed, 2353 insertions, 1658 deletions
diff --git a/.eslintrc.json b/.eslintrc.json index fa6fb1b6f..042254c95 100644 --- a/.eslintrc.json +++ b/.eslintrc.json | |||
@@ -88,6 +88,7 @@ | |||
88 | "@typescript-eslint/no-namespace": "off", | 88 | "@typescript-eslint/no-namespace": "off", |
89 | "@typescript-eslint/no-empty-interface": "off", | 89 | "@typescript-eslint/no-empty-interface": "off", |
90 | "@typescript-eslint/no-extraneous-class": "off", | 90 | "@typescript-eslint/no-extraneous-class": "off", |
91 | "@typescript-eslint/no-use-before-define": "off", | ||
91 | // bugged but useful | 92 | // bugged but useful |
92 | "@typescript-eslint/restrict-plus-operands": "off" | 93 | "@typescript-eslint/restrict-plus-operands": "off" |
93 | }, | 94 | }, |
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d9f09833..a37ee604d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md | |||
@@ -67,7 +67,7 @@ | |||
67 | 67 | ||
68 | ### Features | 68 | ### Features |
69 | 69 | ||
70 | * :tada: Most robust uploads using a resumable upload endpoint [#3933](https://github.com/Chocobozzz/PeerTube/pull/3933) | 70 | * :tada: More robust uploads using a resumable upload endpoint [#3933](https://github.com/Chocobozzz/PeerTube/pull/3933) |
71 | * Accessibility/UI: | 71 | * Accessibility/UI: |
72 | * :tada: Redesign channel and account page | 72 | * :tada: Redesign channel and account page |
73 | * :tada: Increase video miniature size | 73 | * :tada: Increase video miniature size |
@@ -6,7 +6,7 @@ | |||
6 | 6 | ||
7 | <p align=center> | 7 | <p align=center> |
8 | <strong><a href="https://joinpeertube.org">Website</a></strong> | 8 | <strong><a href="https://joinpeertube.org">Website</a></strong> |
9 | | <strong><a href="https://instances.joinpeertube.org">Join an instance</a></strong> | 9 | | <strong><a href="https://joinpeertube.org/instances">Join an instance</a></strong> |
10 | | <strong><a href="#package-create-your-own-instance">Create an instance</a></strong> | 10 | | <strong><a href="#package-create-your-own-instance">Create an instance</a></strong> |
11 | | <strong><a href="#contact">Chat with us</a></strong> | 11 | | <strong><a href="#contact">Chat with us</a></strong> |
12 | | <strong><a href="https://framasoft.org/en/#soutenir">Donate</a></strong> | 12 | | <strong><a href="https://framasoft.org/en/#soutenir">Donate</a></strong> |
@@ -67,23 +67,24 @@ Introduction | |||
67 | 67 | ||
68 | PeerTube is a free, decentralized and federated video platform developed as an alternative to other platforms that centralize our data and attention, such as YouTube, Dailymotion or Vimeo. :clapper: | 68 | PeerTube is a free, decentralized and federated video platform developed as an alternative to other platforms that centralize our data and attention, such as YouTube, Dailymotion or Vimeo. :clapper: |
69 | 69 | ||
70 | But one organization hosting PeerTube alone may not have enough money to pay for bandwidth and video storage of its servers, | 70 | To learn more: |
71 | all servers of PeerTube are interoperable as a federated network, and non-PeerTube servers can be part of the larger Vidiverse | ||
72 | (federated video network) by talking our implementation of ActivityPub. | ||
73 | Video load is reduced thanks to P2P in the web browser using <a href="https://github.com/webtorrent/webtorrent">WebTorrent</a> or <a href="https://github.com/novage/p2p-media-loader">p2p-media-loader</a>. | ||
74 | |||
75 | To learn more, see: | ||
76 | * This [two-minute video](https://framatube.org/videos/watch/217eefeb-883d-45be-b7fc-a788ad8507d3) (hosted on PeerTube) explaining what PeerTube is and how it works | 71 | * This [two-minute video](https://framatube.org/videos/watch/217eefeb-883d-45be-b7fc-a788ad8507d3) (hosted on PeerTube) explaining what PeerTube is and how it works |
77 | * PeerTube's project homepage, [joinpeertube.org](https://joinpeertube.org) | 72 | * PeerTube's project homepage, [joinpeertube.org](https://joinpeertube.org) |
78 | * Demonstration instances: | 73 | * Demonstration instances: |
79 | * [peertube.cpy.re](https://peertube.cpy.re) | 74 | * [peertube.cpy.re](https://peertube.cpy.re) (stable) |
80 | * [peertube2.cpy.re](https://peertube2.cpy.re) | 75 | * [peertube2.cpy.re](https://peertube2.cpy.re) (Nightly) |
81 | * [peertube3.cpy.re](https://peertube3.cpy.re) | 76 | * [peertube3.cpy.re](https://peertube3.cpy.re) (RC) |
82 | * This [video](https://peertube.cpy.re/videos/watch/da2b08d4-a242-4170-b32a-4ec8cbdca701) demonstrating the communication between PeerTube and [Mastodon](https://github.com/tootsuite/mastodon) (a decentralized Twitter alternative) | 77 | * This [video](https://peertube.cpy.re/videos/watch/da2b08d4-a242-4170-b32a-4ec8cbdca701) demonstrating the communication between PeerTube and [Mastodon](https://github.com/tootsuite/mastodon) (a decentralized Twitter alternative) |
83 | 78 | ||
84 | :sparkles: Features | 79 | :sparkles: Features |
85 | ---------------------------------------------------------------- | 80 | ---------------------------------------------------------------- |
86 | 81 | ||
82 | <p align=center> | ||
83 | <strong><a href="https://joinpeertube.org/faq#what-are-the-peertube-features-for-viewers">All features for viewers</a></strong> | ||
84 | | <strong><a href="https://joinpeertube.org/faq#what-are-the-peertube-features-for-content-creators">All features for content creators</a></strong> | ||
85 | | <strong><a href="https://joinpeertube.org/faq#what-are-the-peertube-features-for-administrators">All features for administrators</a></strong> | ||
86 | </p> | ||
87 | |||
87 | <img src="https://lutim.cpy.re/AHbctLjn.png" align="left" height="300px"/> | 88 | <img src="https://lutim.cpy.re/AHbctLjn.png" align="left" height="300px"/> |
88 | <h3 align="left">Video streaming, even in live!</h3> | 89 | <h3 align="left">Video streaming, even in live!</h3> |
89 | <p align="left"> | 90 | <p align="left"> |
@@ -121,6 +122,8 @@ In addition to visitors using WebTorrent to share the load among them, instances | |||
121 | 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 <strike>incentivize</strike> alter creativity (more about that in our <a href="https://github.com/Chocobozzz/PeerTube/blob/develop/FAQ.md">FAQ</a>). | 122 | 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 <strike>incentivize</strike> alter creativity (more about that in our <a href="https://github.com/Chocobozzz/PeerTube/blob/develop/FAQ.md">FAQ</a>). |
122 | </p> | 123 | </p> |
123 | 124 | ||
125 | |||
126 | |||
124 | :raised_hands: Contributing | 127 | :raised_hands: Contributing |
125 | ---------------------------------------------------------------- | 128 | ---------------------------------------------------------------- |
126 | 129 | ||
diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts index 1a95980ae..6af224920 100644 --- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts +++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts | |||
@@ -5,8 +5,7 @@ import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' | |||
5 | import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core' | 5 | import { ComponentPagination, ConfirmService, hasMoreItems, Notifier } from '@app/core' |
6 | import { PluginService } from '@app/core/plugins/plugin.service' | 6 | import { PluginService } from '@app/core/plugins/plugin.service' |
7 | import { compareSemVer } from '@shared/core-utils/miscs/miscs' | 7 | import { compareSemVer } from '@shared/core-utils/miscs/miscs' |
8 | import { PeerTubePlugin } from '@shared/models/plugins/peertube-plugin.model' | 8 | import { PeerTubePlugin, PluginType } from '@shared/models' |
9 | import { PluginType } from '@shared/models/plugins/plugin.type' | ||
10 | 9 | ||
11 | @Component({ | 10 | @Component({ |
12 | selector: 'my-plugin-list-installed', | 11 | selector: 'my-plugin-list-installed', |
diff --git a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts index d2c179aba..0a6e57904 100644 --- a/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts +++ b/client/src/app/+admin/plugins/plugin-search/plugin-search.component.ts | |||
@@ -4,8 +4,7 @@ import { Component, OnInit } from '@angular/core' | |||
4 | import { ActivatedRoute, Router } from '@angular/router' | 4 | import { ActivatedRoute, Router } from '@angular/router' |
5 | import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' | 5 | import { PluginApiService } from '@app/+admin/plugins/shared/plugin-api.service' |
6 | import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, PluginService } from '@app/core' | 6 | import { ComponentPagination, ConfirmService, hasMoreItems, Notifier, PluginService } from '@app/core' |
7 | import { PeerTubePluginIndex } from '@shared/models/plugins/peertube-plugin-index.model' | 7 | import { PeerTubePluginIndex, PluginType } from '@shared/models' |
8 | import { PluginType } from '@shared/models/plugins/plugin.type' | ||
9 | 8 | ||
10 | @Component({ | 9 | @Component({ |
11 | selector: 'my-plugin-search', | 10 | selector: 'my-plugin-search', |
diff --git a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts index 34119f7ab..3d916dbce 100644 --- a/client/src/app/+videos/+video-edit/shared/video-edit.component.ts +++ b/client/src/app/+videos/+video-edit/shared/video-edit.component.ts | |||
@@ -21,8 +21,15 @@ import { | |||
21 | import { FormReactiveValidationMessages, FormValidatorService } from '@app/shared/shared-forms' | 21 | import { FormReactiveValidationMessages, FormValidatorService } from '@app/shared/shared-forms' |
22 | import { InstanceService } from '@app/shared/shared-instance' | 22 | import { InstanceService } from '@app/shared/shared-instance' |
23 | import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-main' | 23 | import { VideoCaptionEdit, VideoEdit, VideoService } from '@app/shared/shared-main' |
24 | import { LiveVideo, ServerConfig, VideoConstant, VideoDetails, VideoPrivacy } from '@shared/models' | 24 | import { |
25 | import { RegisterClientFormFieldOptions, RegisterClientVideoFieldOptions } from '@shared/models/plugins/register-client-form-field.model' | 25 | LiveVideo, |
26 | RegisterClientFormFieldOptions, | ||
27 | RegisterClientVideoFieldOptions, | ||
28 | ServerConfig, | ||
29 | VideoConstant, | ||
30 | VideoDetails, | ||
31 | VideoPrivacy | ||
32 | } from '@shared/models' | ||
26 | import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service' | 33 | import { I18nPrimengCalendarService } from './i18n-primeng-calendar.service' |
27 | import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component' | 34 | import { VideoCaptionAddModalComponent } from './video-caption-add-modal.component' |
28 | import { VideoEditType } from './video-edit.type' | 35 | import { VideoEditType } from './video-edit.type' |
diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts index 3aae24732..23bd5ef76 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts | |||
@@ -5,7 +5,7 @@ import { scrollToTop } from '@app/helpers' | |||
5 | import { FormValidatorService } from '@app/shared/shared-forms' | 5 | import { FormValidatorService } from '@app/shared/shared-forms' |
6 | import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' | 6 | import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main' |
7 | import { LoadingBarService } from '@ngx-loading-bar/core' | 7 | import { LoadingBarService } from '@ngx-loading-bar/core' |
8 | import { VideoPrivacy, VideoUpdate } from '@shared/models' | 8 | import { ServerErrorCode, VideoPrivacy, VideoUpdate } from '@shared/models' |
9 | import { hydrateFormFromVideo } from '../shared/video-edit-utils' | 9 | import { hydrateFormFromVideo } from '../shared/video-edit-utils' |
10 | import { VideoSend } from './video-send' | 10 | import { VideoSend } from './video-send' |
11 | 11 | ||
@@ -113,7 +113,13 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Af | |||
113 | this.loadingBar.useRef().complete() | 113 | this.loadingBar.useRef().complete() |
114 | this.isImportingVideo = false | 114 | this.isImportingVideo = false |
115 | this.firstStepError.emit() | 115 | this.firstStepError.emit() |
116 | this.notifier.error(err.message) | 116 | |
117 | let message = err.message | ||
118 | if (err.body?.code === ServerErrorCode.INCORRECT_FILES_IN_TORRENT) { | ||
119 | message = $localize`Torrents with only 1 file are supported.` | ||
120 | } | ||
121 | |||
122 | this.notifier.error(message) | ||
117 | } | 123 | } |
118 | ) | 124 | ) |
119 | } | 125 | } |
diff --git a/client/src/app/+videos/+video-edit/video-add.component.html b/client/src/app/+videos/+video-edit/video-add.component.html index dc8c2f21d..ac75d9ff8 100644 --- a/client/src/app/+videos/+video-edit/video-add.component.html +++ b/client/src/app/+videos/+video-edit/video-add.component.html | |||
@@ -20,8 +20,8 @@ | |||
20 | <ng-container *ngIf="secondStepType === 'upload'" i18n>Upload {{ videoName }}</ng-container> | 20 | <ng-container *ngIf="secondStepType === 'upload'" i18n>Upload {{ videoName }}</ng-container> |
21 | </div> | 21 | </div> |
22 | 22 | ||
23 | <div ngbNav #nav="ngbNav" class="nav-tabs video-add-nav" [ngClass]="{ 'hide-nav': secondStepType !== undefined }"> | 23 | <div ngbNav #nav="ngbNav" class="nav-tabs video-add-nav" [activeId]="activeNav" (activeIdChange)="onNavChange($event)" [ngClass]="{ 'hide-nav': !!secondStepType }"> |
24 | <ng-container ngbNavItem> | 24 | <ng-container ngbNavItem="upload"> |
25 | <a ngbNavLink> | 25 | <a ngbNavLink> |
26 | <span i18n>Upload a file</span> | 26 | <span i18n>Upload a file</span> |
27 | </a> | 27 | </a> |
@@ -31,7 +31,7 @@ | |||
31 | </ng-template> | 31 | </ng-template> |
32 | </ng-container> | 32 | </ng-container> |
33 | 33 | ||
34 | <ng-container ngbNavItem *ngIf="isVideoImportHttpEnabled()"> | 34 | <ng-container ngbNavItem="import-url" *ngIf="isVideoImportHttpEnabled()"> |
35 | <a ngbNavLink> | 35 | <a ngbNavLink> |
36 | <span i18n>Import with URL</span> | 36 | <span i18n>Import with URL</span> |
37 | </a> | 37 | </a> |
@@ -41,7 +41,7 @@ | |||
41 | </ng-template> | 41 | </ng-template> |
42 | </ng-container> | 42 | </ng-container> |
43 | 43 | ||
44 | <ng-container ngbNavItem *ngIf="isVideoImportTorrentEnabled()"> | 44 | <ng-container ngbNavItem="import-torrent" *ngIf="isVideoImportTorrentEnabled()"> |
45 | <a ngbNavLink> | 45 | <a ngbNavLink> |
46 | <span i18n>Import with torrent</span> | 46 | <span i18n>Import with torrent</span> |
47 | </a> | 47 | </a> |
@@ -51,7 +51,7 @@ | |||
51 | </ng-template> | 51 | </ng-template> |
52 | </ng-container> | 52 | </ng-container> |
53 | 53 | ||
54 | <ng-container ngbNavItem *ngIf="isVideoLiveEnabled()"> | 54 | <ng-container ngbNavItem="go-live" *ngIf="isVideoLiveEnabled()"> |
55 | <a ngbNavLink> | 55 | <a ngbNavLink> |
56 | <span i18n>Go live</span> | 56 | <span i18n>Go live</span> |
57 | </a> | 57 | </a> |
diff --git a/client/src/app/+videos/+video-edit/video-add.component.ts b/client/src/app/+videos/+video-edit/video-add.component.ts index 441d5a3db..d735c936c 100644 --- a/client/src/app/+videos/+video-edit/video-add.component.ts +++ b/client/src/app/+videos/+video-edit/video-add.component.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Component, HostListener, OnInit, ViewChild } from '@angular/core' | 1 | import { Component, HostListener, OnInit, ViewChild } from '@angular/core' |
2 | import { ActivatedRoute, Router } from '@angular/router' | ||
2 | import { AuthService, AuthUser, CanComponentDeactivate, ServerService } from '@app/core' | 3 | import { AuthService, AuthUser, CanComponentDeactivate, ServerService } from '@app/core' |
3 | import { ServerConfig } from '@shared/models' | 4 | import { ServerConfig } from '@shared/models' |
4 | import { VideoEditType } from './shared/video-edit.type' | 5 | import { VideoEditType } from './shared/video-edit.type' |
@@ -22,11 +23,16 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate { | |||
22 | 23 | ||
23 | secondStepType: VideoEditType | 24 | secondStepType: VideoEditType |
24 | videoName: string | 25 | videoName: string |
25 | serverConfig: ServerConfig | 26 | |
27 | activeNav: string | ||
28 | |||
29 | private serverConfig: ServerConfig | ||
26 | 30 | ||
27 | constructor ( | 31 | constructor ( |
28 | private auth: AuthService, | 32 | private auth: AuthService, |
29 | private serverService: ServerService | 33 | private serverService: ServerService, |
34 | private route: ActivatedRoute, | ||
35 | private router: Router | ||
30 | ) {} | 36 | ) {} |
31 | 37 | ||
32 | get userInformationLoaded () { | 38 | get userInformationLoaded () { |
@@ -42,6 +48,16 @@ export class VideoAddComponent implements OnInit, CanComponentDeactivate { | |||
42 | .subscribe(config => this.serverConfig = config) | 48 | .subscribe(config => this.serverConfig = config) |
43 | 49 | ||
44 | this.user = this.auth.getUser() | 50 | this.user = this.auth.getUser() |
51 | |||
52 | if (this.route.snapshot.fragment) { | ||
53 | this.onNavChange(this.route.snapshot.fragment) | ||
54 | } | ||
55 | } | ||
56 | |||
57 | onNavChange (newActiveNav: string) { | ||
58 | this.activeNav = newActiveNav | ||
59 | |||
60 | this.router.navigate([], { fragment: this.activeNav }) | ||
45 | } | 61 | } |
46 | 62 | ||
47 | onFirstStepDone (type: VideoEditType, videoName: string) { | 63 | onFirstStepDone (type: VideoEditType, videoName: string) { |
diff --git a/client/src/app/+videos/video-list/trending/video-trending-header.component.ts b/client/src/app/+videos/video-list/trending/video-trending-header.component.ts index 55040f3c9..bbb02a236 100644 --- a/client/src/app/+videos/video-list/trending/video-trending-header.component.ts +++ b/client/src/app/+videos/video-list/trending/video-trending-header.component.ts | |||
@@ -31,7 +31,8 @@ export class VideoTrendingHeaderComponent extends VideoListHeaderComponent imple | |||
31 | private route: ActivatedRoute, | 31 | private route: ActivatedRoute, |
32 | private router: Router, | 32 | private router: Router, |
33 | private auth: AuthService, | 33 | private auth: AuthService, |
34 | private serverService: ServerService | 34 | private serverService: ServerService, |
35 | private redirectService: RedirectService | ||
35 | ) { | 36 | ) { |
36 | super(data) | 37 | super(data) |
37 | 38 | ||
@@ -84,12 +85,7 @@ export class VideoTrendingHeaderComponent extends VideoListHeaderComponent imple | |||
84 | 85 | ||
85 | this.algorithmChangeSub = this.route.queryParams.subscribe( | 86 | this.algorithmChangeSub = this.route.queryParams.subscribe( |
86 | queryParams => { | 87 | queryParams => { |
87 | const algorithm = queryParams['alg'] | 88 | this.data.model = queryParams['alg'] || this.redirectService.getDefaultTrendingAlgorithm() |
88 | if (algorithm) { | ||
89 | this.data.model = algorithm | ||
90 | } else { | ||
91 | this.data.model = RedirectService.DEFAULT_TRENDING_ALGORITHM | ||
92 | } | ||
93 | } | 89 | } |
94 | ) | 90 | ) |
95 | } | 91 | } |
@@ -99,7 +95,7 @@ export class VideoTrendingHeaderComponent extends VideoListHeaderComponent imple | |||
99 | } | 95 | } |
100 | 96 | ||
101 | setSort () { | 97 | setSort () { |
102 | const alg = this.data.model !== RedirectService.DEFAULT_TRENDING_ALGORITHM | 98 | const alg = this.data.model !== this.redirectService.getDefaultTrendingAlgorithm() |
103 | ? this.data.model | 99 | ? this.data.model |
104 | : undefined | 100 | : undefined |
105 | 101 | ||
diff --git a/client/src/app/+videos/video-list/trending/video-trending.component.ts b/client/src/app/+videos/video-list/trending/video-trending.component.ts index e50d6ec3a..ebec672f3 100644 --- a/client/src/app/+videos/video-list/trending/video-trending.component.ts +++ b/client/src/app/+videos/video-list/trending/video-trending.component.ts | |||
@@ -35,11 +35,12 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit, | |||
35 | protected storageService: LocalStorageService, | 35 | protected storageService: LocalStorageService, |
36 | protected cfr: ComponentFactoryResolver, | 36 | protected cfr: ComponentFactoryResolver, |
37 | private videoService: VideoService, | 37 | private videoService: VideoService, |
38 | private redirectService: RedirectService, | ||
38 | private hooks: HooksService | 39 | private hooks: HooksService |
39 | ) { | 40 | ) { |
40 | super() | 41 | super() |
41 | 42 | ||
42 | this.defaultSort = this.parseAlgorithm(RedirectService.DEFAULT_TRENDING_ALGORITHM) | 43 | this.defaultSort = this.parseAlgorithm(this.redirectService.getDefaultTrendingAlgorithm()) |
43 | 44 | ||
44 | this.headerComponentInjector = this.getInjector() | 45 | this.headerComponentInjector = this.getInjector() |
45 | } | 46 | } |
@@ -106,7 +107,7 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit, | |||
106 | } | 107 | } |
107 | 108 | ||
108 | protected loadPageRouteParams (queryParams: Params) { | 109 | protected loadPageRouteParams (queryParams: Params) { |
109 | const algorithm = queryParams['alg'] || RedirectService.DEFAULT_TRENDING_ALGORITHM | 110 | const algorithm = queryParams['alg'] || this.redirectService.getDefaultTrendingAlgorithm() |
110 | 111 | ||
111 | this.sort = this.parseAlgorithm(algorithm) | 112 | this.sort = this.parseAlgorithm(algorithm) |
112 | } | 113 | } |
@@ -115,8 +116,10 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit, | |||
115 | switch (algorithm) { | 116 | switch (algorithm) { |
116 | case 'most-viewed': | 117 | case 'most-viewed': |
117 | return '-trending' | 118 | return '-trending' |
119 | |||
118 | case 'most-liked': | 120 | case 'most-liked': |
119 | return '-likes' | 121 | return '-likes' |
122 | |||
120 | default: | 123 | default: |
121 | return '-' + algorithm as VideoSortField | 124 | return '-' + algorithm as VideoSortField |
122 | } | 125 | } |
diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 66d871b4a..239e275a4 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts | |||
@@ -67,7 +67,7 @@ export class AppComponent implements OnInit, AfterViewInit { | |||
67 | } | 67 | } |
68 | 68 | ||
69 | goToDefaultRoute () { | 69 | goToDefaultRoute () { |
70 | return this.router.navigateByUrl(RedirectService.DEFAULT_ROUTE) | 70 | return this.router.navigateByUrl(this.redirectService.getDefaultRoute()) |
71 | } | 71 | } |
72 | 72 | ||
73 | ngOnInit () { | 73 | ngOnInit () { |
diff --git a/client/src/app/core/routing/redirect.service.ts b/client/src/app/core/routing/redirect.service.ts index 6d26fb504..cf690a4d0 100644 --- a/client/src/app/core/routing/redirect.service.ts +++ b/client/src/app/core/routing/redirect.service.ts | |||
@@ -6,14 +6,14 @@ import { ServerService } from '../server' | |||
6 | export class RedirectService { | 6 | export class RedirectService { |
7 | // Default route could change according to the instance configuration | 7 | // Default route could change according to the instance configuration |
8 | static INIT_DEFAULT_ROUTE = '/videos/trending' | 8 | static INIT_DEFAULT_ROUTE = '/videos/trending' |
9 | static DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE | ||
10 | static INIT_DEFAULT_TRENDING_ALGORITHM = 'most-viewed' | 9 | static INIT_DEFAULT_TRENDING_ALGORITHM = 'most-viewed' |
11 | static DEFAULT_TRENDING_ALGORITHM = RedirectService.INIT_DEFAULT_TRENDING_ALGORITHM | ||
12 | 10 | ||
13 | private previousUrl: string | 11 | private previousUrl: string |
14 | private currentUrl: string | 12 | private currentUrl: string |
15 | 13 | ||
16 | private redirectingToHomepage = false | 14 | private redirectingToHomepage = false |
15 | private defaultTrendingAlgorithm = RedirectService.INIT_DEFAULT_TRENDING_ALGORITHM | ||
16 | private defaultRoute = RedirectService.INIT_DEFAULT_ROUTE | ||
17 | 17 | ||
18 | constructor ( | 18 | constructor ( |
19 | private router: Router, | 19 | private router: Router, |
@@ -22,10 +22,10 @@ export class RedirectService { | |||
22 | // The config is first loaded from the cache so try to get the default route | 22 | // The config is first loaded from the cache so try to get the default route |
23 | const tmpConfig = this.serverService.getTmpConfig() | 23 | const tmpConfig = this.serverService.getTmpConfig() |
24 | if (tmpConfig?.instance?.defaultClientRoute) { | 24 | if (tmpConfig?.instance?.defaultClientRoute) { |
25 | RedirectService.DEFAULT_ROUTE = tmpConfig.instance.defaultClientRoute | 25 | this.defaultRoute = tmpConfig.instance.defaultClientRoute |
26 | } | 26 | } |
27 | if (tmpConfig?.trending?.videos?.algorithms?.default) { | 27 | if (tmpConfig?.trending?.videos?.algorithms?.default) { |
28 | RedirectService.DEFAULT_TRENDING_ALGORITHM = tmpConfig.trending.videos.algorithms.default | 28 | this.defaultTrendingAlgorithm = tmpConfig.trending.videos.algorithms.default |
29 | } | 29 | } |
30 | 30 | ||
31 | // Load default route | 31 | // Load default route |
@@ -34,13 +34,8 @@ export class RedirectService { | |||
34 | const defaultRouteConfig = config.instance.defaultClientRoute | 34 | const defaultRouteConfig = config.instance.defaultClientRoute |
35 | const defaultTrendingConfig = config.trending.videos.algorithms.default | 35 | const defaultTrendingConfig = config.trending.videos.algorithms.default |
36 | 36 | ||
37 | if (defaultRouteConfig) { | 37 | if (defaultRouteConfig) this.defaultRoute = defaultRouteConfig |
38 | RedirectService.DEFAULT_ROUTE = defaultRouteConfig | 38 | if (defaultTrendingConfig) this.defaultTrendingAlgorithm = defaultTrendingConfig |
39 | } | ||
40 | |||
41 | if (defaultTrendingConfig) { | ||
42 | RedirectService.DEFAULT_TRENDING_ALGORITHM = defaultTrendingConfig | ||
43 | } | ||
44 | }) | 39 | }) |
45 | 40 | ||
46 | // Track previous url | 41 | // Track previous url |
@@ -53,6 +48,14 @@ export class RedirectService { | |||
53 | }) | 48 | }) |
54 | } | 49 | } |
55 | 50 | ||
51 | getDefaultRoute () { | ||
52 | return this.defaultRoute | ||
53 | } | ||
54 | |||
55 | getDefaultTrendingAlgorithm () { | ||
56 | return this.defaultTrendingAlgorithm | ||
57 | } | ||
58 | |||
56 | redirectToPreviousRoute () { | 59 | redirectToPreviousRoute () { |
57 | const exceptions = [ | 60 | const exceptions = [ |
58 | '/verify-account', | 61 | '/verify-account', |
@@ -72,21 +75,21 @@ export class RedirectService { | |||
72 | 75 | ||
73 | this.redirectingToHomepage = true | 76 | this.redirectingToHomepage = true |
74 | 77 | ||
75 | console.log('Redirecting to %s...', RedirectService.DEFAULT_ROUTE) | 78 | console.log('Redirecting to %s...', this.defaultRoute) |
76 | 79 | ||
77 | this.router.navigateByUrl(RedirectService.DEFAULT_ROUTE, { skipLocationChange }) | 80 | this.router.navigateByUrl(this.defaultRoute, { skipLocationChange }) |
78 | .then(() => this.redirectingToHomepage = false) | 81 | .then(() => this.redirectingToHomepage = false) |
79 | .catch(() => { | 82 | .catch(() => { |
80 | this.redirectingToHomepage = false | 83 | this.redirectingToHomepage = false |
81 | 84 | ||
82 | console.error( | 85 | console.error( |
83 | 'Cannot navigate to %s, resetting default route to %s.', | 86 | 'Cannot navigate to %s, resetting default route to %s.', |
84 | RedirectService.DEFAULT_ROUTE, | 87 | this.defaultRoute, |
85 | RedirectService.INIT_DEFAULT_ROUTE | 88 | RedirectService.INIT_DEFAULT_ROUTE |
86 | ) | 89 | ) |
87 | 90 | ||
88 | RedirectService.DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE | 91 | this.defaultRoute = RedirectService.INIT_DEFAULT_ROUTE |
89 | return this.router.navigateByUrl(RedirectService.DEFAULT_ROUTE, { skipLocationChange }) | 92 | return this.router.navigateByUrl(this.defaultRoute, { skipLocationChange }) |
90 | }) | 93 | }) |
91 | 94 | ||
92 | } | 95 | } |
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index 906191ae1..e48786e18 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts | |||
@@ -3,7 +3,6 @@ import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators' | |||
3 | import { HttpClient } from '@angular/common/http' | 3 | import { HttpClient } from '@angular/common/http' |
4 | import { Inject, Injectable, LOCALE_ID } from '@angular/core' | 4 | import { Inject, Injectable, LOCALE_ID } from '@angular/core' |
5 | import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers' | 5 | import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers' |
6 | import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage' | ||
7 | import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n' | 6 | import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n' |
8 | import { SearchTargetType, ServerConfig, ServerStats, VideoConstant } from '@shared/models' | 7 | import { SearchTargetType, ServerConfig, ServerStats, VideoConstant } from '@shared/models' |
9 | import { environment } from '../../../environments/environment' | 8 | import { environment } from '../../../environments/environment' |
@@ -16,8 +15,6 @@ export class ServerService { | |||
16 | private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/' | 15 | private static BASE_LOCALE_URL = environment.apiUrl + '/client/locales/' |
17 | private static BASE_STATS_URL = environment.apiUrl + '/api/v1/server/stats' | 16 | private static BASE_STATS_URL = environment.apiUrl + '/api/v1/server/stats' |
18 | 17 | ||
19 | private static CONFIG_LOCAL_STORAGE_KEY = 'server-config' | ||
20 | |||
21 | configReloaded = new Subject<ServerConfig>() | 18 | configReloaded = new Subject<ServerConfig>() |
22 | 19 | ||
23 | private localeObservable: Observable<any> | 20 | private localeObservable: Observable<any> |
@@ -212,7 +209,6 @@ export class ServerService { | |||
212 | if (!this.configObservable) { | 209 | if (!this.configObservable) { |
213 | this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL) | 210 | this.configObservable = this.http.get<ServerConfig>(ServerService.BASE_CONFIG_URL) |
214 | .pipe( | 211 | .pipe( |
215 | tap(config => this.saveConfigLocally(config)), | ||
216 | tap(config => { | 212 | tap(config => { |
217 | this.config = config | 213 | this.config = config |
218 | this.configLoaded = true | 214 | this.configLoaded = true |
@@ -343,20 +339,15 @@ export class ServerService { | |||
343 | ) | 339 | ) |
344 | } | 340 | } |
345 | 341 | ||
346 | private saveConfigLocally (config: ServerConfig) { | ||
347 | peertubeLocalStorage.setItem(ServerService.CONFIG_LOCAL_STORAGE_KEY, JSON.stringify(config)) | ||
348 | } | ||
349 | |||
350 | private loadConfigLocally () { | 342 | private loadConfigLocally () { |
351 | const configString = peertubeLocalStorage.getItem(ServerService.CONFIG_LOCAL_STORAGE_KEY) | 343 | const configString = window['PeerTubeServerConfig'] |
352 | 344 | if (!configString) return | |
353 | if (configString) { | 345 | |
354 | try { | 346 | try { |
355 | const parsed = JSON.parse(configString) | 347 | const parsed = JSON.parse(configString) |
356 | Object.assign(this.config, parsed) | 348 | Object.assign(this.config, parsed) |
357 | } catch (err) { | 349 | } catch (err) { |
358 | console.error('Cannot parse config saved in local storage.', err) | 350 | console.error('Cannot parse config saved in from index.html.', err) |
359 | } | ||
360 | } | 351 | } |
361 | } | 352 | } |
362 | } | 353 | } |
diff --git a/client/src/app/core/theme/theme.service.ts b/client/src/app/core/theme/theme.service.ts index 4c4611d01..e7a5ae17a 100644 --- a/client/src/app/core/theme/theme.service.ts +++ b/client/src/app/core/theme/theme.service.ts | |||
@@ -82,7 +82,19 @@ export class ThemeService { | |||
82 | : this.userService.getAnonymousUser().theme | 82 | : this.userService.getAnonymousUser().theme |
83 | 83 | ||
84 | if (theme !== 'instance-default') return theme | 84 | if (theme !== 'instance-default') return theme |
85 | return this.serverConfig.theme.default | 85 | |
86 | const instanceTheme = this.serverConfig.theme.default | ||
87 | if (instanceTheme !== 'default') return instanceTheme | ||
88 | |||
89 | // Default to dark theme if available and wanted by the user | ||
90 | if ( | ||
91 | this.themes.find(t => t.name === 'dark') && | ||
92 | window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches | ||
93 | ) { | ||
94 | return 'dark' | ||
95 | } | ||
96 | |||
97 | return instanceTheme | ||
86 | } | 98 | } |
87 | 99 | ||
88 | private loadTheme (name: string) { | 100 | private loadTheme (name: string) { |
diff --git a/client/src/index.html b/client/src/index.html index 72c184dc1..28667cdd0 100644 --- a/client/src/index.html +++ b/client/src/index.html | |||
@@ -29,6 +29,7 @@ | |||
29 | <!-- description tag --> | 29 | <!-- description tag --> |
30 | <!-- custom css tag --> | 30 | <!-- custom css tag --> |
31 | <!-- meta tags --> | 31 | <!-- meta tags --> |
32 | <!-- server config --> | ||
32 | 33 | ||
33 | <!-- /!\ Do not remove it /!\ --> | 34 | <!-- /!\ Do not remove it /!\ --> |
34 | </head> | 35 | </head> |
diff --git a/client/src/root-helpers/plugins.ts b/client/src/root-helpers/plugins.ts index 5344c0468..8c1c858b7 100644 --- a/client/src/root-helpers/plugins.ts +++ b/client/src/root-helpers/plugins.ts | |||
@@ -1,14 +1,15 @@ | |||
1 | import { RegisterClientHelpers } from 'src/types/register-client-option.model' | 1 | import { RegisterClientHelpers } from 'src/types/register-client-option.model' |
2 | import { getHookType, internalRunHook } from '@shared/core-utils/plugins/hooks' | 2 | import { getHookType, internalRunHook } from '@shared/core-utils/plugins/hooks' |
3 | import { RegisterClientFormFieldOptions, RegisterClientVideoFieldOptions } from '@shared/models/plugins/register-client-form-field.model' | ||
4 | import { | 3 | import { |
5 | ClientHookName, | 4 | ClientHookName, |
6 | clientHookObject, | 5 | clientHookObject, |
7 | ClientScript, | 6 | ClientScript, |
8 | PluginType, | 7 | PluginType, |
8 | RegisterClientFormFieldOptions, | ||
9 | RegisterClientHookOptions, | 9 | RegisterClientHookOptions, |
10 | ServerConfigPlugin, | 10 | RegisterClientSettingsScript, |
11 | RegisterClientSettingsScript | 11 | RegisterClientVideoFieldOptions, |
12 | ServerConfigPlugin | ||
12 | } from '../../../shared/models' | 13 | } from '../../../shared/models' |
13 | import { ClientScript as ClientScriptModule } from '../types/client-script.model' | 14 | import { ClientScript as ClientScriptModule } from '../types/client-script.model' |
14 | import { importModule } from './utils' | 15 | import { importModule } from './utils' |
diff --git a/client/src/standalone/videos/embed.html b/client/src/standalone/videos/embed.html index 7d09bfb8f..e13a4dc24 100644 --- a/client/src/standalone/videos/embed.html +++ b/client/src/standalone/videos/embed.html | |||
@@ -1,14 +1,22 @@ | |||
1 | <!DOCTYPE html> | 1 | <!DOCTYPE html> |
2 | <html> | 2 | <html> |
3 | <head> | 3 | <head> |
4 | <!-- title tag --> | ||
5 | |||
6 | <meta charset="UTF-8"> | 4 | <meta charset="UTF-8"> |
7 | <meta name="viewport" content="width=device-width, initial-scale=1"> | 5 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
8 | <meta name="robots" content="noindex"> | 6 | <meta name="robots" content="noindex"> |
9 | <meta property="og:platform" content="PeerTube" /> | 7 | <meta property="og:platform" content="PeerTube" /> |
10 | 8 | ||
9 | |||
10 | <!-- /!\ The following comment is used by the server to prerender some tags /!\ --> | ||
11 | |||
12 | <!-- title tag --> | ||
13 | <!-- description tag --> | ||
11 | <!-- custom css tag --> | 14 | <!-- custom css tag --> |
15 | <!-- meta tags --> | ||
16 | <!-- server config --> | ||
17 | |||
18 | <!-- /!\ Do not remove it /!\ --> | ||
19 | |||
12 | <link rel="icon" type="image/png" href="/client/assets/images/favicon.png" /> | 20 | <link rel="icon" type="image/png" href="/client/assets/images/favicon.png" /> |
13 | </head> | 21 | </head> |
14 | 22 | ||
diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index 3a90fdc58..fc61d3730 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts | |||
@@ -1,28 +1,28 @@ | |||
1 | import './embed.scss' | 1 | import './embed.scss' |
2 | import videojs from 'video.js' | 2 | import videojs from 'video.js' |
3 | import { peertubeTranslate } from '../../../../shared/core-utils/i18n' | 3 | import { peertubeTranslate } from '../../../../shared/core-utils/i18n' |
4 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
4 | import { | 5 | import { |
6 | ClientHookName, | ||
7 | HTMLServerConfig, | ||
8 | PluginType, | ||
5 | ResultList, | 9 | ResultList, |
6 | ServerConfig, | ||
7 | UserRefreshToken, | 10 | UserRefreshToken, |
8 | VideoCaption, | 11 | VideoCaption, |
9 | VideoDetails, | 12 | VideoDetails, |
10 | VideoPlaylist, | 13 | VideoPlaylist, |
11 | VideoPlaylistElement, | 14 | VideoPlaylistElement, |
12 | VideoStreamingPlaylistType, | 15 | VideoStreamingPlaylistType |
13 | PluginType, | ||
14 | ClientHookName | ||
15 | } from '../../../../shared/models' | 16 | } from '../../../../shared/models' |
16 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
17 | import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager' | 17 | import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager' |
18 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' | 18 | import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings' |
19 | import { TranslationsManager } from '../../assets/player/translations-manager' | 19 | import { TranslationsManager } from '../../assets/player/translations-manager' |
20 | import { peertubeLocalStorage } from '../../root-helpers/peertube-web-storage' | ||
20 | import { Hooks, loadPlugin, runHook } from '../../root-helpers/plugins' | 21 | import { Hooks, loadPlugin, runHook } from '../../root-helpers/plugins' |
21 | import { Tokens } from '../../root-helpers/users' | 22 | import { Tokens } from '../../root-helpers/users' |
22 | import { peertubeLocalStorage } from '../../root-helpers/peertube-web-storage' | ||
23 | import { objectToUrlEncoded } from '../../root-helpers/utils' | 23 | import { objectToUrlEncoded } from '../../root-helpers/utils' |
24 | import { PeerTubeEmbedApi } from './embed-api' | ||
25 | import { RegisterClientHelpers } from '../../types/register-client-option.model' | 24 | import { RegisterClientHelpers } from '../../types/register-client-option.model' |
25 | import { PeerTubeEmbedApi } from './embed-api' | ||
26 | 26 | ||
27 | type Translations = { [ id: string ]: string } | 27 | type Translations = { [ id: string ]: string } |
28 | 28 | ||
@@ -56,8 +56,9 @@ export class PeerTubeEmbed { | |||
56 | CLIENT_SECRET: 'client_secret' | 56 | CLIENT_SECRET: 'client_secret' |
57 | } | 57 | } |
58 | 58 | ||
59 | config: HTMLServerConfig | ||
60 | |||
59 | private translationsPromise: Promise<{ [id: string]: string }> | 61 | private translationsPromise: Promise<{ [id: string]: string }> |
60 | private configPromise: Promise<ServerConfig> | ||
61 | private PeertubePlayerManagerModulePromise: Promise<any> | 62 | private PeertubePlayerManagerModulePromise: Promise<any> |
62 | 63 | ||
63 | private playlist: VideoPlaylist | 64 | private playlist: VideoPlaylist |
@@ -77,6 +78,12 @@ export class PeerTubeEmbed { | |||
77 | 78 | ||
78 | constructor (private videoWrapperId: string) { | 79 | constructor (private videoWrapperId: string) { |
79 | this.wrapperElement = document.getElementById(this.videoWrapperId) | 80 | this.wrapperElement = document.getElementById(this.videoWrapperId) |
81 | |||
82 | try { | ||
83 | this.config = JSON.parse(window['PeerTubeServerConfig']) | ||
84 | } catch (err) { | ||
85 | console.error('Cannot parse HTML config.', err) | ||
86 | } | ||
80 | } | 87 | } |
81 | 88 | ||
82 | getVideoUrl (id: string) { | 89 | getVideoUrl (id: string) { |
@@ -166,11 +173,6 @@ export class PeerTubeEmbed { | |||
166 | return this.refreshFetch(url.toString(), { headers: this.headers }) | 173 | return this.refreshFetch(url.toString(), { headers: this.headers }) |
167 | } | 174 | } |
168 | 175 | ||
169 | loadConfig (): Promise<ServerConfig> { | ||
170 | return this.refreshFetch('/api/v1/config') | ||
171 | .then(res => res.json()) | ||
172 | } | ||
173 | |||
174 | removeElement (element: HTMLElement) { | 176 | removeElement (element: HTMLElement) { |
175 | element.parentElement.removeChild(element) | 177 | element.parentElement.removeChild(element) |
176 | } | 178 | } |
@@ -466,6 +468,12 @@ export class PeerTubeEmbed { | |||
466 | this.playerElement.setAttribute('playsinline', 'true') | 468 | this.playerElement.setAttribute('playsinline', 'true') |
467 | this.wrapperElement.appendChild(this.playerElement) | 469 | this.wrapperElement.appendChild(this.playerElement) |
468 | 470 | ||
471 | // Issue when we parsed config from HTML, fallback to API | ||
472 | if (!this.config) { | ||
473 | this.config = await this.refreshFetch('/api/v1/config') | ||
474 | .then(res => res.json()) | ||
475 | } | ||
476 | |||
469 | const videoInfoPromise = videoResponse.json() | 477 | const videoInfoPromise = videoResponse.json() |
470 | .then((videoInfo: VideoDetails) => { | 478 | .then((videoInfo: VideoDetails) => { |
471 | if (!alreadyHadPlayer) this.loadPlaceholder(videoInfo) | 479 | if (!alreadyHadPlayer) this.loadPlaceholder(videoInfo) |
@@ -473,15 +481,14 @@ export class PeerTubeEmbed { | |||
473 | return videoInfo | 481 | return videoInfo |
474 | }) | 482 | }) |
475 | 483 | ||
476 | const [ videoInfoTmp, serverTranslations, captionsResponse, config, PeertubePlayerManagerModule ] = await Promise.all([ | 484 | const [ videoInfoTmp, serverTranslations, captionsResponse, PeertubePlayerManagerModule ] = await Promise.all([ |
477 | videoInfoPromise, | 485 | videoInfoPromise, |
478 | this.translationsPromise, | 486 | this.translationsPromise, |
479 | captionsPromise, | 487 | captionsPromise, |
480 | this.configPromise, | ||
481 | this.PeertubePlayerManagerModulePromise | 488 | this.PeertubePlayerManagerModulePromise |
482 | ]) | 489 | ]) |
483 | 490 | ||
484 | await this.ensurePluginsAreLoaded(config, serverTranslations) | 491 | await this.ensurePluginsAreLoaded(serverTranslations) |
485 | 492 | ||
486 | const videoInfo: VideoDetails = videoInfoTmp | 493 | const videoInfo: VideoDetails = videoInfoTmp |
487 | 494 | ||
@@ -576,7 +583,7 @@ export class PeerTubeEmbed { | |||
576 | 583 | ||
577 | this.buildCSS() | 584 | this.buildCSS() |
578 | 585 | ||
579 | await this.buildDock(videoInfo, config) | 586 | await this.buildDock(videoInfo) |
580 | 587 | ||
581 | this.initializeApi() | 588 | this.initializeApi() |
582 | 589 | ||
@@ -598,7 +605,6 @@ export class PeerTubeEmbed { | |||
598 | private async initCore () { | 605 | private async initCore () { |
599 | if (this.userTokens) this.setHeadersFromTokens() | 606 | if (this.userTokens) this.setHeadersFromTokens() |
600 | 607 | ||
601 | this.configPromise = this.loadConfig() | ||
602 | this.translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language) | 608 | this.translationsPromise = TranslationsManager.getServerTranslations(window.location.origin, navigator.language) |
603 | this.PeertubePlayerManagerModulePromise = import('../../assets/player/peertube-player-manager') | 609 | this.PeertubePlayerManagerModulePromise = import('../../assets/player/peertube-player-manager') |
604 | 610 | ||
@@ -653,7 +659,7 @@ export class PeerTubeEmbed { | |||
653 | } | 659 | } |
654 | } | 660 | } |
655 | 661 | ||
656 | private async buildDock (videoInfo: VideoDetails, config: ServerConfig) { | 662 | private async buildDock (videoInfo: VideoDetails) { |
657 | if (!this.controls) return | 663 | if (!this.controls) return |
658 | 664 | ||
659 | // On webtorrent fallback, player may have been disposed | 665 | // On webtorrent fallback, player may have been disposed |
@@ -661,7 +667,7 @@ export class PeerTubeEmbed { | |||
661 | 667 | ||
662 | const title = this.title ? videoInfo.name : undefined | 668 | const title = this.title ? videoInfo.name : undefined |
663 | 669 | ||
664 | const description = config.tracker.enabled && this.warningTitle | 670 | const description = this.config.tracker.enabled && this.warningTitle |
665 | ? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>' | 671 | ? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>' |
666 | : undefined | 672 | : undefined |
667 | 673 | ||
@@ -733,10 +739,10 @@ export class PeerTubeEmbed { | |||
733 | return window.location.pathname.split('/')[1] === 'video-playlists' | 739 | return window.location.pathname.split('/')[1] === 'video-playlists' |
734 | } | 740 | } |
735 | 741 | ||
736 | private async ensurePluginsAreLoaded (config: ServerConfig, translations?: { [ id: string ]: string }) { | 742 | private async ensurePluginsAreLoaded (translations?: { [ id: string ]: string }) { |
737 | if (config.plugin.registered.length === 0) return | 743 | if (this.config.plugin.registered.length === 0) return |
738 | 744 | ||
739 | for (const plugin of config.plugin.registered) { | 745 | for (const plugin of this.config.plugin.registered) { |
740 | for (const key of Object.keys(plugin.clientScripts)) { | 746 | for (const key of Object.keys(plugin.clientScripts)) { |
741 | const clientScript = plugin.clientScripts[key] | 747 | const clientScript = plugin.clientScripts[key] |
742 | 748 | ||
diff --git a/scripts/optimize-old-videos.ts b/scripts/optimize-old-videos.ts index 01d30244f..9692d76ba 100644 --- a/scripts/optimize-old-videos.ts +++ b/scripts/optimize-old-videos.ts | |||
@@ -5,7 +5,7 @@ import { VIDEO_TRANSCODING_FPS } from '../server/initializers/constants' | |||
5 | import { getDurationFromVideoFile, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../server/helpers/ffprobe-utils' | 5 | import { getDurationFromVideoFile, getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../server/helpers/ffprobe-utils' |
6 | import { getMaxBitrate } from '../shared/models/videos' | 6 | import { getMaxBitrate } from '../shared/models/videos' |
7 | import { VideoModel } from '../server/models/video/video' | 7 | import { VideoModel } from '../server/models/video/video' |
8 | import { optimizeOriginalVideofile } from '../server/lib/video-transcoding' | 8 | import { optimizeOriginalVideofile } from '../server/lib/transcoding/video-transcoding' |
9 | import { initDatabaseModels } from '../server/initializers/database' | 9 | import { initDatabaseModels } from '../server/initializers/database' |
10 | import { basename, dirname } from 'path' | 10 | import { basename, dirname } from 'path' |
11 | import { copy, move, remove } from 'fs-extra' | 11 | import { copy, move, remove } from 'fs-extra' |
diff --git a/scripts/print-transcode-command.ts b/scripts/print-transcode-command.ts index f6c96790e..00ac9ab6c 100644 --- a/scripts/print-transcode-command.ts +++ b/scripts/print-transcode-command.ts | |||
@@ -5,7 +5,7 @@ import * as program from 'commander' | |||
5 | import * as ffmpeg from 'fluent-ffmpeg' | 5 | import * as ffmpeg from 'fluent-ffmpeg' |
6 | import { buildx264VODCommand, runCommand, TranscodeOptions } from '@server/helpers/ffmpeg-utils' | 6 | import { buildx264VODCommand, runCommand, TranscodeOptions } from '@server/helpers/ffmpeg-utils' |
7 | import { exit } from 'process' | 7 | import { exit } from 'process' |
8 | import { VideoTranscodingProfilesManager } from '@server/lib/video-transcoding-profiles' | 8 | import { VideoTranscodingProfilesManager } from '@server/lib/transcoding/video-transcoding-profiles' |
9 | 9 | ||
10 | program | 10 | program |
11 | .arguments('<path>') | 11 | .arguments('<path>') |
diff --git a/scripts/prune-storage.ts b/scripts/prune-storage.ts index 32314b0b7..0f2d1320e 100755 --- a/scripts/prune-storage.ts +++ b/scripts/prune-storage.ts | |||
@@ -11,7 +11,7 @@ import { VideoRedundancyModel } from '../server/models/redundancy/video-redundan | |||
11 | import * as Bluebird from 'bluebird' | 11 | import * as Bluebird from 'bluebird' |
12 | import { getUUIDFromFilename } from '../server/helpers/utils' | 12 | import { getUUIDFromFilename } from '../server/helpers/utils' |
13 | import { ThumbnailModel } from '../server/models/video/thumbnail' | 13 | import { ThumbnailModel } from '../server/models/video/thumbnail' |
14 | import { ActorImageModel } from '../server/models/account/actor-image' | 14 | import { ActorImageModel } from '../server/models/actor/actor-image' |
15 | import { uniq, values } from 'lodash' | 15 | import { uniq, values } from 'lodash' |
16 | import { ThumbnailType } from '@shared/models' | 16 | import { ThumbnailType } from '@shared/models' |
17 | 17 | ||
diff --git a/scripts/reset-password.ts b/scripts/reset-password.ts index 7e7de6b8a..7c1a64a3f 100755 --- a/scripts/reset-password.ts +++ b/scripts/reset-password.ts | |||
@@ -3,7 +3,7 @@ registerTSPaths() | |||
3 | 3 | ||
4 | import * as program from 'commander' | 4 | import * as program from 'commander' |
5 | import { initDatabaseModels } from '../server/initializers/database' | 5 | import { initDatabaseModels } from '../server/initializers/database' |
6 | import { UserModel } from '../server/models/account/user' | 6 | import { UserModel } from '../server/models/user/user' |
7 | import { isUserPasswordValid } from '../server/helpers/custom-validators/users' | 7 | import { isUserPasswordValid } from '../server/helpers/custom-validators/users' |
8 | 8 | ||
9 | program | 9 | program |
diff --git a/scripts/update-host.ts b/scripts/update-host.ts index e497be4e2..592684225 100755 --- a/scripts/update-host.ts +++ b/scripts/update-host.ts | |||
@@ -2,9 +2,9 @@ import { registerTSPaths } from '../server/helpers/register-ts-paths' | |||
2 | registerTSPaths() | 2 | registerTSPaths() |
3 | 3 | ||
4 | import { WEBSERVER } from '../server/initializers/constants' | 4 | import { WEBSERVER } from '../server/initializers/constants' |
5 | import { ActorFollowModel } from '../server/models/activitypub/actor-follow' | 5 | import { ActorFollowModel } from '../server/models/actor/actor-follow' |
6 | import { VideoModel } from '../server/models/video/video' | 6 | import { VideoModel } from '../server/models/video/video' |
7 | import { ActorModel } from '../server/models/activitypub/actor' | 7 | import { ActorModel } from '../server/models/actor/actor' |
8 | import { | 8 | import { |
9 | getLocalAccountActivityPubUrl, | 9 | getLocalAccountActivityPubUrl, |
10 | getLocalVideoActivityPubUrl, | 10 | getLocalVideoActivityPubUrl, |
diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index 1b4acc234..1982e171d 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts | |||
@@ -30,7 +30,7 @@ import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } | |||
30 | import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' | 30 | import { videoPlaylistElementAPGetValidator, videoPlaylistsGetValidator } from '../../middlewares/validators/videos/video-playlists' |
31 | import { AccountModel } from '../../models/account/account' | 31 | import { AccountModel } from '../../models/account/account' |
32 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' | 32 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' |
33 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | 33 | import { ActorFollowModel } from '../../models/actor/actor-follow' |
34 | import { VideoModel } from '../../models/video/video' | 34 | import { VideoModel } from '../../models/video/video' |
35 | import { VideoCaptionModel } from '../../models/video/video-caption' | 35 | import { VideoCaptionModel } from '../../models/video/video-caption' |
36 | import { VideoCommentModel } from '../../models/video/video-comment' | 36 | import { VideoCommentModel } from '../../models/video/video-comment' |
diff --git a/server/controllers/activitypub/utils.ts b/server/controllers/activitypub/utils.ts index 599cf48ab..19bdd58eb 100644 --- a/server/controllers/activitypub/utils.ts +++ b/server/controllers/activitypub/utils.ts | |||
@@ -3,7 +3,6 @@ import * as express from 'express' | |||
3 | function activityPubResponse (data: any, res: express.Response) { | 3 | function activityPubResponse (data: any, res: express.Response) { |
4 | return res.type('application/activity+json; charset=utf-8') | 4 | return res.type('application/activity+json; charset=utf-8') |
5 | .json(data) | 5 | .json(data) |
6 | .end() | ||
7 | } | 6 | } |
8 | 7 | ||
9 | export { | 8 | export { |
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 2ddb73519..5ce7adc35 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts | |||
@@ -18,6 +18,7 @@ const configRouter = express.Router() | |||
18 | const auditLogger = auditLoggerFactory('config') | 18 | const auditLogger = auditLoggerFactory('config') |
19 | 19 | ||
20 | configRouter.get('/about', getAbout) | 20 | configRouter.get('/about', getAbout) |
21 | |||
21 | configRouter.get('/', | 22 | configRouter.get('/', |
22 | asyncMiddleware(getConfig) | 23 | asyncMiddleware(getConfig) |
23 | ) | 24 | ) |
@@ -27,12 +28,14 @@ configRouter.get('/custom', | |||
27 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), | 28 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), |
28 | getCustomConfig | 29 | getCustomConfig |
29 | ) | 30 | ) |
31 | |||
30 | configRouter.put('/custom', | 32 | configRouter.put('/custom', |
31 | authenticate, | 33 | authenticate, |
32 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), | 34 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), |
33 | customConfigUpdateValidator, | 35 | customConfigUpdateValidator, |
34 | asyncMiddleware(updateCustomConfig) | 36 | asyncMiddleware(updateCustomConfig) |
35 | ) | 37 | ) |
38 | |||
36 | configRouter.delete('/custom', | 39 | configRouter.delete('/custom', |
37 | authenticate, | 40 | authenticate, |
38 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), | 41 | ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), |
@@ -67,13 +70,13 @@ function getAbout (req: express.Request, res: express.Response) { | |||
67 | } | 70 | } |
68 | } | 71 | } |
69 | 72 | ||
70 | return res.json(about).end() | 73 | return res.json(about) |
71 | } | 74 | } |
72 | 75 | ||
73 | function getCustomConfig (req: express.Request, res: express.Response) { | 76 | function getCustomConfig (req: express.Request, res: express.Response) { |
74 | const data = customConfig() | 77 | const data = customConfig() |
75 | 78 | ||
76 | return res.json(data).end() | 79 | return res.json(data) |
77 | } | 80 | } |
78 | 81 | ||
79 | async function deleteCustomConfig (req: express.Request, res: express.Response) { | 82 | async function deleteCustomConfig (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/api/plugins.ts b/server/controllers/api/plugins.ts index a186de010..e18eed332 100644 --- a/server/controllers/api/plugins.ts +++ b/server/controllers/api/plugins.ts | |||
@@ -1,16 +1,18 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getFormattedObjects } from '../../helpers/utils' | 2 | import { logger } from '@server/helpers/logger' |
3 | import { getFormattedObjects } from '@server/helpers/utils' | ||
4 | import { listAvailablePluginsFromIndex } from '@server/lib/plugins/plugin-index' | ||
5 | import { PluginManager } from '@server/lib/plugins/plugin-manager' | ||
3 | import { | 6 | import { |
4 | asyncMiddleware, | 7 | asyncMiddleware, |
5 | authenticate, | 8 | authenticate, |
9 | availablePluginsSortValidator, | ||
6 | ensureUserHasRight, | 10 | ensureUserHasRight, |
7 | paginationValidator, | 11 | paginationValidator, |
12 | pluginsSortValidator, | ||
8 | setDefaultPagination, | 13 | setDefaultPagination, |
9 | setDefaultSort | 14 | setDefaultSort |
10 | } from '../../middlewares' | 15 | } from '@server/middlewares' |
11 | import { availablePluginsSortValidator, pluginsSortValidator } from '../../middlewares/validators' | ||
12 | import { PluginModel } from '../../models/server/plugin' | ||
13 | import { UserRight } from '../../../shared/models/users' | ||
14 | import { | 16 | import { |
15 | existingPluginValidator, | 17 | existingPluginValidator, |
16 | installOrUpdatePluginValidator, | 18 | installOrUpdatePluginValidator, |
@@ -18,16 +20,17 @@ import { | |||
18 | listPluginsValidator, | 20 | listPluginsValidator, |
19 | uninstallPluginValidator, | 21 | uninstallPluginValidator, |
20 | updatePluginSettingsValidator | 22 | updatePluginSettingsValidator |
21 | } from '../../middlewares/validators/plugins' | 23 | } from '@server/middlewares/validators/plugins' |
22 | import { PluginManager } from '../../lib/plugins/plugin-manager' | 24 | import { PluginModel } from '@server/models/server/plugin' |
23 | import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/install-plugin.model' | 25 | import { HttpStatusCode } from '@shared/core-utils' |
24 | import { ManagePlugin } from '../../../shared/models/plugins/manage-plugin.model' | 26 | import { |
25 | import { logger } from '../../helpers/logger' | 27 | InstallOrUpdatePlugin, |
26 | import { listAvailablePluginsFromIndex } from '../../lib/plugins/plugin-index' | 28 | ManagePlugin, |
27 | import { PeertubePluginIndexList } from '../../../shared/models/plugins/peertube-plugin-index-list.model' | 29 | PeertubePluginIndexList, |
28 | import { RegisteredServerSettings } from '../../../shared/models/plugins/register-server-setting.model' | 30 | PublicServerSetting, |
29 | import { PublicServerSetting } from '../../../shared/models/plugins/public-server.setting' | 31 | RegisteredServerSettings, |
30 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 32 | UserRight |
33 | } from '@shared/models' | ||
31 | 34 | ||
32 | const pluginRouter = express.Router() | 35 | const pluginRouter = express.Router() |
33 | 36 | ||
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index 80025bc5b..daeef22de 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts | |||
@@ -1,9 +1,15 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getServerActor } from '@server/models/application/application' | ||
3 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
2 | import { UserRight } from '../../../../shared/models/users' | 4 | import { UserRight } from '../../../../shared/models/users' |
3 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
4 | import { getFormattedObjects } from '../../../helpers/utils' | 6 | import { getFormattedObjects } from '../../../helpers/utils' |
5 | import { SERVER_ACTOR_NAME } from '../../../initializers/constants' | 7 | import { SERVER_ACTOR_NAME } from '../../../initializers/constants' |
8 | import { sequelizeTypescript } from '../../../initializers/database' | ||
9 | import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow' | ||
6 | import { sendAccept, sendReject, sendUndoFollow } from '../../../lib/activitypub/send' | 10 | import { sendAccept, sendReject, sendUndoFollow } from '../../../lib/activitypub/send' |
11 | import { JobQueue } from '../../../lib/job-queue' | ||
12 | import { removeRedundanciesOfServer } from '../../../lib/redundancy' | ||
7 | import { | 13 | import { |
8 | asyncMiddleware, | 14 | asyncMiddleware, |
9 | authenticate, | 15 | authenticate, |
@@ -19,16 +25,10 @@ import { | |||
19 | followingSortValidator, | 25 | followingSortValidator, |
20 | followValidator, | 26 | followValidator, |
21 | getFollowerValidator, | 27 | getFollowerValidator, |
22 | removeFollowingValidator, | 28 | listFollowsValidator, |
23 | listFollowsValidator | 29 | removeFollowingValidator |
24 | } from '../../../middlewares/validators' | 30 | } from '../../../middlewares/validators' |
25 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 31 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
26 | import { JobQueue } from '../../../lib/job-queue' | ||
27 | import { removeRedundanciesOfServer } from '../../../lib/redundancy' | ||
28 | import { sequelizeTypescript } from '../../../initializers/database' | ||
29 | import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow' | ||
30 | import { getServerActor } from '@server/models/application/application' | ||
31 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
32 | 32 | ||
33 | const serverFollowsRouter = express.Router() | 33 | const serverFollowsRouter = express.Router() |
34 | serverFollowsRouter.get('/following', | 34 | serverFollowsRouter.get('/following', |
diff --git a/server/controllers/api/server/server-blocklist.ts b/server/controllers/api/server/server-blocklist.ts index 6e341c0fb..a86bc7d19 100644 --- a/server/controllers/api/server/server-blocklist.ts +++ b/server/controllers/api/server/server-blocklist.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import 'multer' | 1 | import 'multer' |
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { logger } from '@server/helpers/logger' | 3 | import { logger } from '@server/helpers/logger' |
4 | import { UserNotificationModel } from '@server/models/account/user-notification' | 4 | import { UserNotificationModel } from '@server/models/user/user-notification' |
5 | import { getServerActor } from '@server/models/application/application' | 5 | import { getServerActor } from '@server/models/application/application' |
6 | import { UserRight } from '../../../../shared/models/users' | 6 | import { UserRight } from '../../../../shared/models/users' |
7 | import { getFormattedObjects } from '../../../helpers/utils' | 7 | import { getFormattedObjects } from '../../../helpers/utils' |
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index e2b1ea7cd..f384f0f28 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts | |||
@@ -45,7 +45,7 @@ import { | |||
45 | usersResetPasswordValidator, | 45 | usersResetPasswordValidator, |
46 | usersVerifyEmailValidator | 46 | usersVerifyEmailValidator |
47 | } from '../../../middlewares/validators' | 47 | } from '../../../middlewares/validators' |
48 | import { UserModel } from '../../../models/account/user' | 48 | import { UserModel } from '../../../models/user/user' |
49 | import { meRouter } from './me' | 49 | import { meRouter } from './me' |
50 | import { myAbusesRouter } from './my-abuses' | 50 | import { myAbusesRouter } from './my-abuses' |
51 | import { myBlocklistRouter } from './my-blocklist' | 51 | import { myBlocklistRouter } from './my-blocklist' |
@@ -323,14 +323,20 @@ async function updateUser (req: express.Request, res: express.Response) { | |||
323 | const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON()) | 323 | const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON()) |
324 | const roleChanged = body.role !== undefined && body.role !== userToUpdate.role | 324 | const roleChanged = body.role !== undefined && body.role !== userToUpdate.role |
325 | 325 | ||
326 | if (body.password !== undefined) userToUpdate.password = body.password | 326 | const keysToUpdate: (keyof UserUpdate)[] = [ |
327 | if (body.email !== undefined) userToUpdate.email = body.email | 327 | 'password', |
328 | if (body.emailVerified !== undefined) userToUpdate.emailVerified = body.emailVerified | 328 | 'email', |
329 | if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota | 329 | 'emailVerified', |
330 | if (body.videoQuotaDaily !== undefined) userToUpdate.videoQuotaDaily = body.videoQuotaDaily | 330 | 'videoQuota', |
331 | if (body.role !== undefined) userToUpdate.role = body.role | 331 | 'videoQuotaDaily', |
332 | if (body.adminFlags !== undefined) userToUpdate.adminFlags = body.adminFlags | 332 | 'role', |
333 | if (body.pluginAuth !== undefined) userToUpdate.pluginAuth = body.pluginAuth | 333 | 'adminFlags', |
334 | 'pluginAuth' | ||
335 | ] | ||
336 | |||
337 | for (const key of keysToUpdate) { | ||
338 | if (body[key] !== undefined) userToUpdate.set(key, body[key]) | ||
339 | } | ||
334 | 340 | ||
335 | const user = await userToUpdate.save() | 341 | const user = await userToUpdate.save() |
336 | 342 | ||
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 0763d1900..a609abaa6 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts | |||
@@ -28,9 +28,10 @@ import { deleteMeValidator, videoImportsSortValidator, videosSortValidator } fro | |||
28 | import { updateAvatarValidator } from '../../../middlewares/validators/actor-image' | 28 | import { updateAvatarValidator } from '../../../middlewares/validators/actor-image' |
29 | import { AccountModel } from '../../../models/account/account' | 29 | import { AccountModel } from '../../../models/account/account' |
30 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 30 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
31 | import { UserModel } from '../../../models/account/user' | 31 | import { UserModel } from '../../../models/user/user' |
32 | import { VideoModel } from '../../../models/video/video' | 32 | import { VideoModel } from '../../../models/video/video' |
33 | import { VideoImportModel } from '../../../models/video/video-import' | 33 | import { VideoImportModel } from '../../../models/video/video-import' |
34 | import { AttributesOnly } from '@shared/core-utils' | ||
34 | 35 | ||
35 | const auditLogger = auditLoggerFactory('users') | 36 | const auditLogger = auditLoggerFactory('users') |
36 | 37 | ||
@@ -191,17 +192,23 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
191 | 192 | ||
192 | const user = res.locals.oauth.token.user | 193 | const user = res.locals.oauth.token.user |
193 | 194 | ||
194 | if (body.password !== undefined) user.password = body.password | 195 | const keysToUpdate: (keyof UserUpdateMe & keyof AttributesOnly<UserModel>)[] = [ |
195 | if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy | 196 | 'password', |
196 | if (body.webTorrentEnabled !== undefined) user.webTorrentEnabled = body.webTorrentEnabled | 197 | 'nsfwPolicy', |
197 | if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo | 198 | 'webTorrentEnabled', |
198 | if (body.autoPlayNextVideo !== undefined) user.autoPlayNextVideo = body.autoPlayNextVideo | 199 | 'autoPlayVideo', |
199 | if (body.autoPlayNextVideoPlaylist !== undefined) user.autoPlayNextVideoPlaylist = body.autoPlayNextVideoPlaylist | 200 | 'autoPlayNextVideo', |
200 | if (body.videosHistoryEnabled !== undefined) user.videosHistoryEnabled = body.videosHistoryEnabled | 201 | 'autoPlayNextVideoPlaylist', |
201 | if (body.videoLanguages !== undefined) user.videoLanguages = body.videoLanguages | 202 | 'videosHistoryEnabled', |
202 | if (body.theme !== undefined) user.theme = body.theme | 203 | 'videoLanguages', |
203 | if (body.noInstanceConfigWarningModal !== undefined) user.noInstanceConfigWarningModal = body.noInstanceConfigWarningModal | 204 | 'theme', |
204 | if (body.noWelcomeModal !== undefined) user.noWelcomeModal = body.noWelcomeModal | 205 | 'noInstanceConfigWarningModal', |
206 | 'noWelcomeModal' | ||
207 | ] | ||
208 | |||
209 | for (const key of keysToUpdate) { | ||
210 | if (body[key] !== undefined) user.set(key, body[key]) | ||
211 | } | ||
205 | 212 | ||
206 | if (body.email !== undefined) { | 213 | if (body.email !== undefined) { |
207 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { | 214 | if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { |
@@ -215,15 +222,15 @@ async function updateMe (req: express.Request, res: express.Response) { | |||
215 | await sequelizeTypescript.transaction(async t => { | 222 | await sequelizeTypescript.transaction(async t => { |
216 | await user.save({ transaction: t }) | 223 | await user.save({ transaction: t }) |
217 | 224 | ||
218 | if (body.displayName !== undefined || body.description !== undefined) { | 225 | if (body.displayName === undefined && body.description === undefined) return |
219 | const userAccount = await AccountModel.load(user.Account.id, t) | ||
220 | 226 | ||
221 | if (body.displayName !== undefined) userAccount.name = body.displayName | 227 | const userAccount = await AccountModel.load(user.Account.id, t) |
222 | if (body.description !== undefined) userAccount.description = body.description | ||
223 | await userAccount.save({ transaction: t }) | ||
224 | 228 | ||
225 | await sendUpdateActor(userAccount, t) | 229 | if (body.displayName !== undefined) userAccount.name = body.displayName |
226 | } | 230 | if (body.description !== undefined) userAccount.description = body.description |
231 | await userAccount.save({ transaction: t }) | ||
232 | |||
233 | await sendUpdateActor(userAccount, t) | ||
227 | }) | 234 | }) |
228 | 235 | ||
229 | if (sendVerificationEmail === true) { | 236 | if (sendVerificationEmail === true) { |
diff --git a/server/controllers/api/users/my-blocklist.ts b/server/controllers/api/users/my-blocklist.ts index faaef3ac0..a1561b751 100644 --- a/server/controllers/api/users/my-blocklist.ts +++ b/server/controllers/api/users/my-blocklist.ts | |||
@@ -20,7 +20,7 @@ import { | |||
20 | import { AccountBlocklistModel } from '../../../models/account/account-blocklist' | 20 | import { AccountBlocklistModel } from '../../../models/account/account-blocklist' |
21 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' | 21 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' |
22 | import { ServerBlocklistModel } from '../../../models/server/server-blocklist' | 22 | import { ServerBlocklistModel } from '../../../models/server/server-blocklist' |
23 | import { UserNotificationModel } from '@server/models/account/user-notification' | 23 | import { UserNotificationModel } from '@server/models/user/user-notification' |
24 | import { logger } from '@server/helpers/logger' | 24 | import { logger } from '@server/helpers/logger' |
25 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 25 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
26 | 26 | ||
diff --git a/server/controllers/api/users/my-history.ts b/server/controllers/api/users/my-history.ts index 72c7da373..cff1697ab 100644 --- a/server/controllers/api/users/my-history.ts +++ b/server/controllers/api/users/my-history.ts | |||
@@ -9,7 +9,7 @@ import { | |||
9 | userHistoryRemoveValidator | 9 | userHistoryRemoveValidator |
10 | } from '../../../middlewares' | 10 | } from '../../../middlewares' |
11 | import { getFormattedObjects } from '../../../helpers/utils' | 11 | import { getFormattedObjects } from '../../../helpers/utils' |
12 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' | 12 | import { UserVideoHistoryModel } from '../../../models/user/user-video-history' |
13 | import { sequelizeTypescript } from '../../../initializers/database' | 13 | import { sequelizeTypescript } from '../../../initializers/database' |
14 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 14 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
15 | 15 | ||
diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts index 0a9101a46..2909770da 100644 --- a/server/controllers/api/users/my-notifications.ts +++ b/server/controllers/api/users/my-notifications.ts | |||
@@ -1,5 +1,9 @@ | |||
1 | import * as express from 'express' | ||
2 | import 'multer' | 1 | import 'multer' |
2 | import * as express from 'express' | ||
3 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
4 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
5 | import { UserNotificationSetting } from '../../../../shared/models/users' | ||
6 | import { getFormattedObjects } from '../../../helpers/utils' | ||
3 | import { | 7 | import { |
4 | asyncMiddleware, | 8 | asyncMiddleware, |
5 | asyncRetryTransactionMiddleware, | 9 | asyncRetryTransactionMiddleware, |
@@ -9,17 +13,13 @@ import { | |||
9 | setDefaultSort, | 13 | setDefaultSort, |
10 | userNotificationsSortValidator | 14 | userNotificationsSortValidator |
11 | } from '../../../middlewares' | 15 | } from '../../../middlewares' |
12 | import { getFormattedObjects } from '../../../helpers/utils' | ||
13 | import { UserNotificationModel } from '../../../models/account/user-notification' | ||
14 | import { meRouter } from './me' | ||
15 | import { | 16 | import { |
16 | listUserNotificationsValidator, | 17 | listUserNotificationsValidator, |
17 | markAsReadUserNotificationsValidator, | 18 | markAsReadUserNotificationsValidator, |
18 | updateNotificationSettingsValidator | 19 | updateNotificationSettingsValidator |
19 | } from '../../../middlewares/validators/user-notifications' | 20 | } from '../../../middlewares/validators/user-notifications' |
20 | import { UserNotificationSetting } from '../../../../shared/models/users' | 21 | import { UserNotificationSettingModel } from '../../../models/user/user-notification-setting' |
21 | import { UserNotificationSettingModel } from '../../../models/account/user-notification-setting' | 22 | import { meRouter } from './me' |
22 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
23 | 23 | ||
24 | const myNotificationsRouter = express.Router() | 24 | const myNotificationsRouter = express.Router() |
25 | 25 | ||
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts index 56b93276f..46a73d49e 100644 --- a/server/controllers/api/users/my-subscriptions.ts +++ b/server/controllers/api/users/my-subscriptions.ts | |||
@@ -27,7 +27,7 @@ import { | |||
27 | userSubscriptionsSortValidator, | 27 | userSubscriptionsSortValidator, |
28 | videosSortValidator | 28 | videosSortValidator |
29 | } from '../../../middlewares/validators' | 29 | } from '../../../middlewares/validators' |
30 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 30 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
31 | import { VideoModel } from '../../../models/video/video' | 31 | import { VideoModel } from '../../../models/video/video' |
32 | 32 | ||
33 | const mySubscriptionsRouter = express.Router() | 33 | const mySubscriptionsRouter = express.Router() |
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index a755d7e57..859d8b3c0 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts | |||
@@ -162,6 +162,7 @@ async function updateVideoChannelBanner (req: express.Request, res: express.Resp | |||
162 | 162 | ||
163 | return res.json({ banner: banner.toFormattedJSON() }) | 163 | return res.json({ banner: banner.toFormattedJSON() }) |
164 | } | 164 | } |
165 | |||
165 | async function updateVideoChannelAvatar (req: express.Request, res: express.Response) { | 166 | async function updateVideoChannelAvatar (req: express.Request, res: express.Response) { |
166 | const avatarPhysicalFile = req.files['avatarfile'][0] | 167 | const avatarPhysicalFile = req.files['avatarfile'][0] |
167 | const videoChannel = res.locals.videoChannel | 168 | const videoChannel = res.locals.videoChannel |
@@ -221,10 +222,6 @@ async function updateVideoChannel (req: express.Request, res: express.Response) | |||
221 | 222 | ||
222 | try { | 223 | try { |
223 | await sequelizeTypescript.transaction(async t => { | 224 | await sequelizeTypescript.transaction(async t => { |
224 | const sequelizeOptions = { | ||
225 | transaction: t | ||
226 | } | ||
227 | |||
228 | if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.name = videoChannelInfoToUpdate.displayName | 225 | if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.name = videoChannelInfoToUpdate.displayName |
229 | if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.description = videoChannelInfoToUpdate.description | 226 | if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.description = videoChannelInfoToUpdate.description |
230 | 227 | ||
@@ -238,7 +235,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response) | |||
238 | } | 235 | } |
239 | } | 236 | } |
240 | 237 | ||
241 | const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) as MChannelBannerAccountDefault | 238 | const videoChannelInstanceUpdated = await videoChannelInstance.save({ transaction: t }) as MChannelBannerAccountDefault |
242 | await sendUpdateActor(videoChannelInstanceUpdated, t) | 239 | await sendUpdateActor(videoChannelInstanceUpdated, t) |
243 | 240 | ||
244 | auditLogger.update( | 241 | auditLogger.update( |
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts index aab16533d..b8613699b 100644 --- a/server/controllers/api/video-playlist.ts +++ b/server/controllers/api/video-playlist.ts | |||
@@ -202,7 +202,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) { | |||
202 | id: videoPlaylistCreated.id, | 202 | id: videoPlaylistCreated.id, |
203 | uuid: videoPlaylistCreated.uuid | 203 | uuid: videoPlaylistCreated.uuid |
204 | } | 204 | } |
205 | }).end() | 205 | }) |
206 | } | 206 | } |
207 | 207 | ||
208 | async function updateVideoPlaylist (req: express.Request, res: express.Response) { | 208 | async function updateVideoPlaylist (req: express.Request, res: express.Response) { |
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts index f1f53d354..cfdf2773f 100644 --- a/server/controllers/api/videos/comment.ts +++ b/server/controllers/api/videos/comment.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 2 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
3 | import { ResultList, ThreadsResultList, UserRight } from '../../../../shared/models' | 3 | import { ResultList, ThreadsResultList, UserRight } from '../../../../shared/models' |
4 | import { VideoCommentCreate } from '../../../../shared/models/videos/video-comment.model' | 4 | import { VideoCommentCreate } from '../../../../shared/models/videos/comment/video-comment.model' |
5 | import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger' | 5 | import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger' |
6 | import { getFormattedObjects } from '../../../helpers/utils' | 6 | import { getFormattedObjects } from '../../../helpers/utils' |
7 | import { sequelizeTypescript } from '../../../initializers/database' | 7 | import { sequelizeTypescript } from '../../../initializers/database' |
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index 3b9b887e2..ee63c7b77 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts | |||
@@ -3,7 +3,9 @@ import { move, readFile } from 'fs-extra' | |||
3 | import * as magnetUtil from 'magnet-uri' | 3 | import * as magnetUtil from 'magnet-uri' |
4 | import * as parseTorrent from 'parse-torrent' | 4 | import * as parseTorrent from 'parse-torrent' |
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import { getEnabledResolutions } from '@server/lib/config' | ||
6 | import { setVideoTags } from '@server/lib/video' | 7 | import { setVideoTags } from '@server/lib/video' |
8 | import { FilteredModelAttributes } from '@server/types' | ||
7 | import { | 9 | import { |
8 | MChannelAccountDefault, | 10 | MChannelAccountDefault, |
9 | MThumbnail, | 11 | MThumbnail, |
@@ -14,17 +16,17 @@ import { | |||
14 | MVideoThumbnail, | 16 | MVideoThumbnail, |
15 | MVideoWithBlacklistLight | 17 | MVideoWithBlacklistLight |
16 | } from '@server/types/models' | 18 | } from '@server/types/models' |
17 | import { MVideoImport, MVideoImportFormattable } from '@server/types/models/video/video-import' | 19 | import { MVideoImportFormattable } from '@server/types/models/video/video-import' |
18 | import { VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared' | 20 | import { ServerErrorCode, VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared' |
19 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 21 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
20 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' | 22 | import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type' |
21 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' | 23 | import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' |
22 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' | 24 | import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' |
23 | import { isArray } from '../../../helpers/custom-validators/misc' | 25 | import { isArray } from '../../../helpers/custom-validators/misc' |
24 | import { createReqFiles } from '../../../helpers/express-utils' | 26 | import { cleanUpReqFiles, createReqFiles } from '../../../helpers/express-utils' |
25 | import { logger } from '../../../helpers/logger' | 27 | import { logger } from '../../../helpers/logger' |
26 | import { getSecureTorrentName } from '../../../helpers/utils' | 28 | import { getSecureTorrentName } from '../../../helpers/utils' |
27 | import { getYoutubeDLInfo, getYoutubeDLSubs, YoutubeDLInfo } from '../../../helpers/youtube-dl' | 29 | import { YoutubeDL, YoutubeDLInfo } from '../../../helpers/youtube-dl' |
28 | import { CONFIG } from '../../../initializers/config' | 30 | import { CONFIG } from '../../../initializers/config' |
29 | import { MIMETYPES } from '../../../initializers/constants' | 31 | import { MIMETYPES } from '../../../initializers/constants' |
30 | import { sequelizeTypescript } from '../../../initializers/database' | 32 | import { sequelizeTypescript } from '../../../initializers/database' |
@@ -81,22 +83,15 @@ async function addTorrentImport (req: express.Request, res: express.Response, to | |||
81 | let magnetUri: string | 83 | let magnetUri: string |
82 | 84 | ||
83 | if (torrentfile) { | 85 | if (torrentfile) { |
84 | torrentName = torrentfile.originalname | 86 | const result = await processTorrentOrAbortRequest(req, res, torrentfile) |
87 | if (!result) return | ||
85 | 88 | ||
86 | // Rename the torrent to a secured name | 89 | videoName = result.name |
87 | const newTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, getSecureTorrentName(torrentName)) | 90 | torrentName = result.torrentName |
88 | await move(torrentfile.path, newTorrentPath) | ||
89 | torrentfile.path = newTorrentPath | ||
90 | |||
91 | const buf = await readFile(torrentfile.path) | ||
92 | const parsedTorrent = parseTorrent(buf) | ||
93 | |||
94 | videoName = isArray(parsedTorrent.name) ? parsedTorrent.name[0] : parsedTorrent.name as string | ||
95 | } else { | 91 | } else { |
96 | magnetUri = body.magnetUri | 92 | const result = processMagnetURI(body) |
97 | 93 | magnetUri = result.magnetUri | |
98 | const parsed = magnetUtil.decode(magnetUri) | 94 | videoName = result.name |
99 | videoName = isArray(parsed.name) ? parsed.name[0] : parsed.name as string | ||
100 | } | 95 | } |
101 | 96 | ||
102 | const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) | 97 | const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) |
@@ -104,26 +99,26 @@ async function addTorrentImport (req: express.Request, res: express.Response, to | |||
104 | const thumbnailModel = await processThumbnail(req, video) | 99 | const thumbnailModel = await processThumbnail(req, video) |
105 | const previewModel = await processPreview(req, video) | 100 | const previewModel = await processPreview(req, video) |
106 | 101 | ||
107 | const tags = body.tags || undefined | ||
108 | const videoImportAttributes = { | ||
109 | magnetUri, | ||
110 | torrentName, | ||
111 | state: VideoImportState.PENDING, | ||
112 | userId: user.id | ||
113 | } | ||
114 | const videoImport = await insertIntoDB({ | 102 | const videoImport = await insertIntoDB({ |
115 | video, | 103 | video, |
116 | thumbnailModel, | 104 | thumbnailModel, |
117 | previewModel, | 105 | previewModel, |
118 | videoChannel: res.locals.videoChannel, | 106 | videoChannel: res.locals.videoChannel, |
119 | tags, | 107 | tags: body.tags || undefined, |
120 | videoImportAttributes, | 108 | user, |
121 | user | 109 | videoImportAttributes: { |
110 | magnetUri, | ||
111 | torrentName, | ||
112 | state: VideoImportState.PENDING, | ||
113 | userId: user.id | ||
114 | } | ||
122 | }) | 115 | }) |
123 | 116 | ||
124 | // Create job to import the video | 117 | // Create job to import the video |
125 | const payload = { | 118 | const payload = { |
126 | type: torrentfile ? 'torrent-file' as 'torrent-file' : 'magnet-uri' as 'magnet-uri', | 119 | type: torrentfile |
120 | ? 'torrent-file' as 'torrent-file' | ||
121 | : 'magnet-uri' as 'magnet-uri', | ||
127 | videoImportId: videoImport.id, | 122 | videoImportId: videoImport.id, |
128 | magnetUri | 123 | magnetUri |
129 | } | 124 | } |
@@ -139,10 +134,12 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
139 | const targetUrl = body.targetUrl | 134 | const targetUrl = body.targetUrl |
140 | const user = res.locals.oauth.token.User | 135 | const user = res.locals.oauth.token.User |
141 | 136 | ||
137 | const youtubeDL = new YoutubeDL(targetUrl, getEnabledResolutions('vod')) | ||
138 | |||
142 | // Get video infos | 139 | // Get video infos |
143 | let youtubeDLInfo: YoutubeDLInfo | 140 | let youtubeDLInfo: YoutubeDLInfo |
144 | try { | 141 | try { |
145 | youtubeDLInfo = await getYoutubeDLInfo(targetUrl) | 142 | youtubeDLInfo = await youtubeDL.getYoutubeDLInfo() |
146 | } catch (err) { | 143 | } catch (err) { |
147 | logger.info('Cannot fetch information from import for URL %s.', targetUrl, { err }) | 144 | logger.info('Cannot fetch information from import for URL %s.', targetUrl, { err }) |
148 | 145 | ||
@@ -170,45 +167,22 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response) | |||
170 | previewModel = await processPreviewFromUrl(youtubeDLInfo.thumbnailUrl, video) | 167 | previewModel = await processPreviewFromUrl(youtubeDLInfo.thumbnailUrl, video) |
171 | } | 168 | } |
172 | 169 | ||
173 | const tags = body.tags || youtubeDLInfo.tags | ||
174 | const videoImportAttributes = { | ||
175 | targetUrl, | ||
176 | state: VideoImportState.PENDING, | ||
177 | userId: user.id | ||
178 | } | ||
179 | const videoImport = await insertIntoDB({ | 170 | const videoImport = await insertIntoDB({ |
180 | video, | 171 | video, |
181 | thumbnailModel, | 172 | thumbnailModel, |
182 | previewModel, | 173 | previewModel, |
183 | videoChannel: res.locals.videoChannel, | 174 | videoChannel: res.locals.videoChannel, |
184 | tags, | 175 | tags: body.tags || youtubeDLInfo.tags, |
185 | videoImportAttributes, | 176 | user, |
186 | user | 177 | videoImportAttributes: { |
178 | targetUrl, | ||
179 | state: VideoImportState.PENDING, | ||
180 | userId: user.id | ||
181 | } | ||
187 | }) | 182 | }) |
188 | 183 | ||
189 | // Get video subtitles | 184 | // Get video subtitles |
190 | try { | 185 | await processYoutubeSubtitles(youtubeDL, targetUrl, video.id) |
191 | const subtitles = await getYoutubeDLSubs(targetUrl) | ||
192 | |||
193 | logger.info('Will create %s subtitles from youtube import %s.', subtitles.length, targetUrl) | ||
194 | |||
195 | for (const subtitle of subtitles) { | ||
196 | const videoCaption = new VideoCaptionModel({ | ||
197 | videoId: video.id, | ||
198 | language: subtitle.language, | ||
199 | filename: VideoCaptionModel.generateCaptionName(subtitle.language) | ||
200 | }) as MVideoCaption | ||
201 | |||
202 | // Move physical file | ||
203 | await moveAndProcessCaptionFile(subtitle, videoCaption) | ||
204 | |||
205 | await sequelizeTypescript.transaction(async t => { | ||
206 | await VideoCaptionModel.insertOrReplaceLanguage(videoCaption, t) | ||
207 | }) | ||
208 | } | ||
209 | } catch (err) { | ||
210 | logger.warn('Cannot get video subtitles.', { err }) | ||
211 | } | ||
212 | 186 | ||
213 | // Create job to import the video | 187 | // Create job to import the video |
214 | const payload = { | 188 | const payload = { |
@@ -240,7 +214,9 @@ function buildVideo (channelId: number, body: VideoImportCreate, importData: You | |||
240 | privacy: body.privacy || VideoPrivacy.PRIVATE, | 214 | privacy: body.privacy || VideoPrivacy.PRIVATE, |
241 | duration: 0, // duration will be set by the import job | 215 | duration: 0, // duration will be set by the import job |
242 | channelId: channelId, | 216 | channelId: channelId, |
243 | originallyPublishedAt: body.originallyPublishedAt || importData.originallyPublishedAt | 217 | originallyPublishedAt: body.originallyPublishedAt |
218 | ? new Date(body.originallyPublishedAt) | ||
219 | : importData.originallyPublishedAt | ||
244 | } | 220 | } |
245 | const video = new VideoModel(videoData) | 221 | const video = new VideoModel(videoData) |
246 | video.url = getLocalVideoActivityPubUrl(video) | 222 | video.url = getLocalVideoActivityPubUrl(video) |
@@ -304,7 +280,7 @@ async function insertIntoDB (parameters: { | |||
304 | previewModel: MThumbnail | 280 | previewModel: MThumbnail |
305 | videoChannel: MChannelAccountDefault | 281 | videoChannel: MChannelAccountDefault |
306 | tags: string[] | 282 | tags: string[] |
307 | videoImportAttributes: Partial<MVideoImport> | 283 | videoImportAttributes: FilteredModelAttributes<VideoImportModel> |
308 | user: MUser | 284 | user: MUser |
309 | }): Promise<MVideoImportFormattable> { | 285 | }): Promise<MVideoImportFormattable> { |
310 | const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters | 286 | const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters |
@@ -342,3 +318,71 @@ async function insertIntoDB (parameters: { | |||
342 | 318 | ||
343 | return videoImport | 319 | return videoImport |
344 | } | 320 | } |
321 | |||
322 | async function processTorrentOrAbortRequest (req: express.Request, res: express.Response, torrentfile: Express.Multer.File) { | ||
323 | const torrentName = torrentfile.originalname | ||
324 | |||
325 | // Rename the torrent to a secured name | ||
326 | const newTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, getSecureTorrentName(torrentName)) | ||
327 | await move(torrentfile.path, newTorrentPath, { overwrite: true }) | ||
328 | torrentfile.path = newTorrentPath | ||
329 | |||
330 | const buf = await readFile(torrentfile.path) | ||
331 | const parsedTorrent = parseTorrent(buf) as parseTorrent.Instance | ||
332 | |||
333 | if (parsedTorrent.files.length !== 1) { | ||
334 | cleanUpReqFiles(req) | ||
335 | |||
336 | res.status(HttpStatusCode.BAD_REQUEST_400) | ||
337 | .json({ | ||
338 | code: ServerErrorCode.INCORRECT_FILES_IN_TORRENT, | ||
339 | error: 'Torrents with only 1 file are supported.' | ||
340 | }) | ||
341 | |||
342 | return undefined | ||
343 | } | ||
344 | |||
345 | return { | ||
346 | name: extractNameFromArray(parsedTorrent.name), | ||
347 | torrentName | ||
348 | } | ||
349 | } | ||
350 | |||
351 | function processMagnetURI (body: VideoImportCreate) { | ||
352 | const magnetUri = body.magnetUri | ||
353 | const parsed = magnetUtil.decode(magnetUri) | ||
354 | |||
355 | return { | ||
356 | name: extractNameFromArray(parsed.name), | ||
357 | magnetUri | ||
358 | } | ||
359 | } | ||
360 | |||
361 | function extractNameFromArray (name: string | string[]) { | ||
362 | return isArray(name) ? name[0] : name | ||
363 | } | ||
364 | |||
365 | async function processYoutubeSubtitles (youtubeDL: YoutubeDL, targetUrl: string, videoId: number) { | ||
366 | try { | ||
367 | const subtitles = await youtubeDL.getYoutubeDLSubs() | ||
368 | |||
369 | logger.info('Will create %s subtitles from youtube import %s.', subtitles.length, targetUrl) | ||
370 | |||
371 | for (const subtitle of subtitles) { | ||
372 | const videoCaption = new VideoCaptionModel({ | ||
373 | videoId, | ||
374 | language: subtitle.language, | ||
375 | filename: VideoCaptionModel.generateCaptionName(subtitle.language) | ||
376 | }) as MVideoCaption | ||
377 | |||
378 | // Move physical file | ||
379 | await moveAndProcessCaptionFile(subtitle, videoCaption) | ||
380 | |||
381 | await sequelizeTypescript.transaction(async t => { | ||
382 | await VideoCaptionModel.insertOrReplaceLanguage(videoCaption, t) | ||
383 | }) | ||
384 | } | ||
385 | } catch (err) { | ||
386 | logger.warn('Cannot get video subtitles.', { err }) | ||
387 | } | ||
388 | } | ||
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index c32626d30..6483d2e8a 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts | |||
@@ -1,43 +1,20 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { move } from 'fs-extra' | ||
3 | import { extname } from 'path' | ||
4 | import toInt from 'validator/lib/toInt' | 2 | import toInt from 'validator/lib/toInt' |
5 | import { deleteResumableUploadMetaFile, getResumableUploadPath } from '@server/helpers/upload' | ||
6 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | ||
7 | import { changeVideoChannelShare } from '@server/lib/activitypub/share' | ||
8 | import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' | ||
9 | import { LiveManager } from '@server/lib/live-manager' | 3 | import { LiveManager } from '@server/lib/live-manager' |
10 | import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' | ||
11 | import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' | ||
12 | import { getServerActor } from '@server/models/application/application' | 4 | import { getServerActor } from '@server/models/application/application' |
13 | import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' | 5 | import { VideosCommonQuery } from '../../../../shared' |
14 | import { uploadx } from '@uploadx/core' | ||
15 | import { VideoCreate, VideosCommonQuery, VideoState, VideoUpdate } from '../../../../shared' | ||
16 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs' | 6 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs' |
17 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | 7 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' |
18 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils' | 8 | import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils' |
19 | import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils' | 9 | import { logger } from '../../../helpers/logger' |
20 | import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' | ||
21 | import { logger, loggerTagsFactory } from '../../../helpers/logger' | ||
22 | import { getFormattedObjects } from '../../../helpers/utils' | 10 | import { getFormattedObjects } from '../../../helpers/utils' |
23 | import { CONFIG } from '../../../initializers/config' | 11 | import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers/constants' |
24 | import { | ||
25 | DEFAULT_AUDIO_RESOLUTION, | ||
26 | MIMETYPES, | ||
27 | VIDEO_CATEGORIES, | ||
28 | VIDEO_LANGUAGES, | ||
29 | VIDEO_LICENCES, | ||
30 | VIDEO_PRIVACIES | ||
31 | } from '../../../initializers/constants' | ||
32 | import { sequelizeTypescript } from '../../../initializers/database' | 12 | import { sequelizeTypescript } from '../../../initializers/database' |
33 | import { sendView } from '../../../lib/activitypub/send/send-view' | 13 | import { sendView } from '../../../lib/activitypub/send/send-view' |
34 | import { federateVideoIfNeeded, fetchRemoteVideoDescription } from '../../../lib/activitypub/videos' | 14 | import { fetchRemoteVideoDescription } from '../../../lib/activitypub/videos' |
35 | import { JobQueue } from '../../../lib/job-queue' | 15 | import { JobQueue } from '../../../lib/job-queue' |
36 | import { Notifier } from '../../../lib/notifier' | ||
37 | import { Hooks } from '../../../lib/plugins/hooks' | 16 | import { Hooks } from '../../../lib/plugins/hooks' |
38 | import { Redis } from '../../../lib/redis' | 17 | import { Redis } from '../../../lib/redis' |
39 | import { generateVideoMiniature } from '../../../lib/thumbnail' | ||
40 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | ||
41 | import { | 18 | import { |
42 | asyncMiddleware, | 19 | asyncMiddleware, |
43 | asyncRetryTransactionMiddleware, | 20 | asyncRetryTransactionMiddleware, |
@@ -49,16 +26,11 @@ import { | |||
49 | setDefaultPagination, | 26 | setDefaultPagination, |
50 | setDefaultVideosSort, | 27 | setDefaultVideosSort, |
51 | videoFileMetadataGetValidator, | 28 | videoFileMetadataGetValidator, |
52 | videosAddLegacyValidator, | ||
53 | videosAddResumableInitValidator, | ||
54 | videosAddResumableValidator, | ||
55 | videosCustomGetValidator, | 29 | videosCustomGetValidator, |
56 | videosGetValidator, | 30 | videosGetValidator, |
57 | videosRemoveValidator, | 31 | videosRemoveValidator, |
58 | videosSortValidator, | 32 | videosSortValidator |
59 | videosUpdateValidator | ||
60 | } from '../../../middlewares' | 33 | } from '../../../middlewares' |
61 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | ||
62 | import { VideoModel } from '../../../models/video/video' | 34 | import { VideoModel } from '../../../models/video/video' |
63 | import { VideoFileModel } from '../../../models/video/video-file' | 35 | import { VideoFileModel } from '../../../models/video/video-file' |
64 | import { blacklistRouter } from './blacklist' | 36 | import { blacklistRouter } from './blacklist' |
@@ -68,40 +40,12 @@ import { videoImportsRouter } from './import' | |||
68 | import { liveRouter } from './live' | 40 | import { liveRouter } from './live' |
69 | import { ownershipVideoRouter } from './ownership' | 41 | import { ownershipVideoRouter } from './ownership' |
70 | import { rateVideoRouter } from './rate' | 42 | import { rateVideoRouter } from './rate' |
43 | import { updateRouter } from './update' | ||
44 | import { uploadRouter } from './upload' | ||
71 | import { watchingRouter } from './watching' | 45 | import { watchingRouter } from './watching' |
72 | 46 | ||
73 | const lTags = loggerTagsFactory('api', 'video') | ||
74 | const auditLogger = auditLoggerFactory('videos') | 47 | const auditLogger = auditLoggerFactory('videos') |
75 | const videosRouter = express.Router() | 48 | const videosRouter = express.Router() |
76 | const uploadxMiddleware = uploadx.upload({ directory: getResumableUploadPath() }) | ||
77 | |||
78 | const reqVideoFileAdd = createReqFiles( | ||
79 | [ 'videofile', 'thumbnailfile', 'previewfile' ], | ||
80 | Object.assign({}, MIMETYPES.VIDEO.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT), | ||
81 | { | ||
82 | videofile: CONFIG.STORAGE.TMP_DIR, | ||
83 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, | ||
84 | previewfile: CONFIG.STORAGE.TMP_DIR | ||
85 | } | ||
86 | ) | ||
87 | |||
88 | const reqVideoFileAddResumable = createReqFiles( | ||
89 | [ 'thumbnailfile', 'previewfile' ], | ||
90 | MIMETYPES.IMAGE.MIMETYPE_EXT, | ||
91 | { | ||
92 | thumbnailfile: getResumableUploadPath(), | ||
93 | previewfile: getResumableUploadPath() | ||
94 | } | ||
95 | ) | ||
96 | |||
97 | const reqVideoFileUpdate = createReqFiles( | ||
98 | [ 'thumbnailfile', 'previewfile' ], | ||
99 | MIMETYPES.IMAGE.MIMETYPE_EXT, | ||
100 | { | ||
101 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, | ||
102 | previewfile: CONFIG.STORAGE.TMP_DIR | ||
103 | } | ||
104 | ) | ||
105 | 49 | ||
106 | videosRouter.use('/', blacklistRouter) | 50 | videosRouter.use('/', blacklistRouter) |
107 | videosRouter.use('/', rateVideoRouter) | 51 | videosRouter.use('/', rateVideoRouter) |
@@ -111,6 +55,8 @@ videosRouter.use('/', videoImportsRouter) | |||
111 | videosRouter.use('/', ownershipVideoRouter) | 55 | videosRouter.use('/', ownershipVideoRouter) |
112 | videosRouter.use('/', watchingRouter) | 56 | videosRouter.use('/', watchingRouter) |
113 | videosRouter.use('/', liveRouter) | 57 | videosRouter.use('/', liveRouter) |
58 | videosRouter.use('/', uploadRouter) | ||
59 | videosRouter.use('/', updateRouter) | ||
114 | 60 | ||
115 | videosRouter.get('/categories', listVideoCategories) | 61 | videosRouter.get('/categories', listVideoCategories) |
116 | videosRouter.get('/licences', listVideoLicences) | 62 | videosRouter.get('/licences', listVideoLicences) |
@@ -127,39 +73,6 @@ videosRouter.get('/', | |||
127 | asyncMiddleware(listVideos) | 73 | asyncMiddleware(listVideos) |
128 | ) | 74 | ) |
129 | 75 | ||
130 | videosRouter.post('/upload', | ||
131 | authenticate, | ||
132 | reqVideoFileAdd, | ||
133 | asyncMiddleware(videosAddLegacyValidator), | ||
134 | asyncRetryTransactionMiddleware(addVideoLegacy) | ||
135 | ) | ||
136 | |||
137 | videosRouter.post('/upload-resumable', | ||
138 | authenticate, | ||
139 | reqVideoFileAddResumable, | ||
140 | asyncMiddleware(videosAddResumableInitValidator), | ||
141 | uploadxMiddleware | ||
142 | ) | ||
143 | |||
144 | videosRouter.delete('/upload-resumable', | ||
145 | authenticate, | ||
146 | uploadxMiddleware | ||
147 | ) | ||
148 | |||
149 | videosRouter.put('/upload-resumable', | ||
150 | authenticate, | ||
151 | uploadxMiddleware, // uploadx doesn't use call next() before the file upload completes | ||
152 | asyncMiddleware(videosAddResumableValidator), | ||
153 | asyncMiddleware(addVideoResumable) | ||
154 | ) | ||
155 | |||
156 | videosRouter.put('/:id', | ||
157 | authenticate, | ||
158 | reqVideoFileUpdate, | ||
159 | asyncMiddleware(videosUpdateValidator), | ||
160 | asyncRetryTransactionMiddleware(updateVideo) | ||
161 | ) | ||
162 | |||
163 | videosRouter.get('/:id/description', | 76 | videosRouter.get('/:id/description', |
164 | asyncMiddleware(videosGetValidator), | 77 | asyncMiddleware(videosGetValidator), |
165 | asyncMiddleware(getVideoDescription) | 78 | asyncMiddleware(getVideoDescription) |
@@ -209,279 +122,7 @@ function listVideoPrivacies (_req: express.Request, res: express.Response) { | |||
209 | res.json(VIDEO_PRIVACIES) | 122 | res.json(VIDEO_PRIVACIES) |
210 | } | 123 | } |
211 | 124 | ||
212 | async function addVideoLegacy (req: express.Request, res: express.Response) { | 125 | async function getVideo (_req: express.Request, res: express.Response) { |
213 | // Uploading the video could be long | ||
214 | // Set timeout to 10 minutes, as Express's default is 2 minutes | ||
215 | req.setTimeout(1000 * 60 * 10, () => { | ||
216 | logger.error('Upload video has timed out.') | ||
217 | return res.sendStatus(HttpStatusCode.REQUEST_TIMEOUT_408) | ||
218 | }) | ||
219 | |||
220 | const videoPhysicalFile = req.files['videofile'][0] | ||
221 | const videoInfo: VideoCreate = req.body | ||
222 | const files = req.files | ||
223 | |||
224 | return addVideo({ res, videoPhysicalFile, videoInfo, files }) | ||
225 | } | ||
226 | |||
227 | async function addVideoResumable (_req: express.Request, res: express.Response) { | ||
228 | const videoPhysicalFile = res.locals.videoFileResumable | ||
229 | const videoInfo = videoPhysicalFile.metadata | ||
230 | const files = { previewfile: videoInfo.previewfile } | ||
231 | |||
232 | // Don't need the meta file anymore | ||
233 | await deleteResumableUploadMetaFile(videoPhysicalFile.path) | ||
234 | |||
235 | return addVideo({ res, videoPhysicalFile, videoInfo, files }) | ||
236 | } | ||
237 | |||
238 | async function addVideo (options: { | ||
239 | res: express.Response | ||
240 | videoPhysicalFile: express.VideoUploadFile | ||
241 | videoInfo: VideoCreate | ||
242 | files: express.UploadFiles | ||
243 | }) { | ||
244 | const { res, videoPhysicalFile, videoInfo, files } = options | ||
245 | const videoChannel = res.locals.videoChannel | ||
246 | const user = res.locals.oauth.token.User | ||
247 | |||
248 | const videoData = buildLocalVideoFromReq(videoInfo, videoChannel.id) | ||
249 | |||
250 | videoData.state = CONFIG.TRANSCODING.ENABLED | ||
251 | ? VideoState.TO_TRANSCODE | ||
252 | : VideoState.PUBLISHED | ||
253 | |||
254 | videoData.duration = videoPhysicalFile.duration // duration was added by a previous middleware | ||
255 | |||
256 | const video = new VideoModel(videoData) as MVideoFullLight | ||
257 | video.VideoChannel = videoChannel | ||
258 | video.url = getLocalVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object | ||
259 | |||
260 | const videoFile = new VideoFileModel({ | ||
261 | extname: extname(videoPhysicalFile.filename), | ||
262 | size: videoPhysicalFile.size, | ||
263 | videoStreamingPlaylistId: null, | ||
264 | metadata: await getMetadataFromFile(videoPhysicalFile.path) | ||
265 | }) | ||
266 | |||
267 | if (videoFile.isAudio()) { | ||
268 | videoFile.resolution = DEFAULT_AUDIO_RESOLUTION | ||
269 | } else { | ||
270 | videoFile.fps = await getVideoFileFPS(videoPhysicalFile.path) | ||
271 | videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path)).videoFileResolution | ||
272 | } | ||
273 | |||
274 | videoFile.filename = generateVideoFilename(video, false, videoFile.resolution, videoFile.extname) | ||
275 | |||
276 | // Move physical file | ||
277 | const destination = getVideoFilePath(video, videoFile) | ||
278 | await move(videoPhysicalFile.path, destination) | ||
279 | // This is important in case if there is another attempt in the retry process | ||
280 | videoPhysicalFile.filename = getVideoFilePath(video, videoFile) | ||
281 | videoPhysicalFile.path = destination | ||
282 | |||
283 | const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ | ||
284 | video, | ||
285 | files, | ||
286 | fallback: type => generateVideoMiniature({ video, videoFile, type }) | ||
287 | }) | ||
288 | |||
289 | const { videoCreated } = await sequelizeTypescript.transaction(async t => { | ||
290 | const sequelizeOptions = { transaction: t } | ||
291 | |||
292 | const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight | ||
293 | |||
294 | await videoCreated.addAndSaveThumbnail(thumbnailModel, t) | ||
295 | await videoCreated.addAndSaveThumbnail(previewModel, t) | ||
296 | |||
297 | // Do not forget to add video channel information to the created video | ||
298 | videoCreated.VideoChannel = res.locals.videoChannel | ||
299 | |||
300 | videoFile.videoId = video.id | ||
301 | await videoFile.save(sequelizeOptions) | ||
302 | |||
303 | video.VideoFiles = [ videoFile ] | ||
304 | |||
305 | await setVideoTags({ video, tags: videoInfo.tags, transaction: t }) | ||
306 | |||
307 | // Schedule an update in the future? | ||
308 | if (videoInfo.scheduleUpdate) { | ||
309 | await ScheduleVideoUpdateModel.create({ | ||
310 | videoId: video.id, | ||
311 | updateAt: videoInfo.scheduleUpdate.updateAt, | ||
312 | privacy: videoInfo.scheduleUpdate.privacy || null | ||
313 | }, { transaction: t }) | ||
314 | } | ||
315 | |||
316 | // Channel has a new content, set as updated | ||
317 | await videoCreated.VideoChannel.setAsUpdated(t) | ||
318 | |||
319 | await autoBlacklistVideoIfNeeded({ | ||
320 | video, | ||
321 | user, | ||
322 | isRemote: false, | ||
323 | isNew: true, | ||
324 | transaction: t | ||
325 | }) | ||
326 | |||
327 | auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) | ||
328 | logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid, lTags(videoCreated.uuid)) | ||
329 | |||
330 | return { videoCreated } | ||
331 | }) | ||
332 | |||
333 | // Create the torrent file in async way because it could be long | ||
334 | createTorrentAndSetInfoHashAsync(video, videoFile) | ||
335 | .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err, ...lTags(video.uuid) })) | ||
336 | .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id)) | ||
337 | .then(refreshedVideo => { | ||
338 | if (!refreshedVideo) return | ||
339 | |||
340 | // Only federate and notify after the torrent creation | ||
341 | Notifier.Instance.notifyOnNewVideoIfNeeded(refreshedVideo) | ||
342 | |||
343 | return retryTransactionWrapper(() => { | ||
344 | return sequelizeTypescript.transaction(t => federateVideoIfNeeded(refreshedVideo, true, t)) | ||
345 | }) | ||
346 | }) | ||
347 | .catch(err => logger.error('Cannot federate or notify video creation %s', video.url, { err, ...lTags(video.uuid) })) | ||
348 | |||
349 | if (video.state === VideoState.TO_TRANSCODE) { | ||
350 | await addOptimizeOrMergeAudioJob(videoCreated, videoFile, user) | ||
351 | } | ||
352 | |||
353 | Hooks.runAction('action:api.video.uploaded', { video: videoCreated }) | ||
354 | |||
355 | return res.json({ | ||
356 | video: { | ||
357 | id: videoCreated.id, | ||
358 | uuid: videoCreated.uuid | ||
359 | } | ||
360 | }) | ||
361 | } | ||
362 | |||
363 | async function updateVideo (req: express.Request, res: express.Response) { | ||
364 | const videoInstance = res.locals.videoAll | ||
365 | const videoFieldsSave = videoInstance.toJSON() | ||
366 | const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) | ||
367 | const videoInfoToUpdate: VideoUpdate = req.body | ||
368 | |||
369 | const wasConfidentialVideo = videoInstance.isConfidential() | ||
370 | const hadPrivacyForFederation = videoInstance.hasPrivacyForFederation() | ||
371 | |||
372 | const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ | ||
373 | video: videoInstance, | ||
374 | files: req.files, | ||
375 | fallback: () => Promise.resolve(undefined), | ||
376 | automaticallyGenerated: false | ||
377 | }) | ||
378 | |||
379 | try { | ||
380 | const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => { | ||
381 | const sequelizeOptions = { transaction: t } | ||
382 | const oldVideoChannel = videoInstance.VideoChannel | ||
383 | |||
384 | if (videoInfoToUpdate.name !== undefined) videoInstance.name = videoInfoToUpdate.name | ||
385 | if (videoInfoToUpdate.category !== undefined) videoInstance.category = videoInfoToUpdate.category | ||
386 | if (videoInfoToUpdate.licence !== undefined) videoInstance.licence = videoInfoToUpdate.licence | ||
387 | if (videoInfoToUpdate.language !== undefined) videoInstance.language = videoInfoToUpdate.language | ||
388 | if (videoInfoToUpdate.nsfw !== undefined) videoInstance.nsfw = videoInfoToUpdate.nsfw | ||
389 | if (videoInfoToUpdate.waitTranscoding !== undefined) videoInstance.waitTranscoding = videoInfoToUpdate.waitTranscoding | ||
390 | if (videoInfoToUpdate.support !== undefined) videoInstance.support = videoInfoToUpdate.support | ||
391 | if (videoInfoToUpdate.description !== undefined) videoInstance.description = videoInfoToUpdate.description | ||
392 | if (videoInfoToUpdate.commentsEnabled !== undefined) videoInstance.commentsEnabled = videoInfoToUpdate.commentsEnabled | ||
393 | if (videoInfoToUpdate.downloadEnabled !== undefined) videoInstance.downloadEnabled = videoInfoToUpdate.downloadEnabled | ||
394 | |||
395 | if (videoInfoToUpdate.originallyPublishedAt !== undefined && videoInfoToUpdate.originallyPublishedAt !== null) { | ||
396 | videoInstance.originallyPublishedAt = new Date(videoInfoToUpdate.originallyPublishedAt) | ||
397 | } | ||
398 | |||
399 | let isNewVideo = false | ||
400 | if (videoInfoToUpdate.privacy !== undefined) { | ||
401 | isNewVideo = videoInstance.isNewVideo(videoInfoToUpdate.privacy) | ||
402 | |||
403 | const newPrivacy = parseInt(videoInfoToUpdate.privacy.toString(), 10) | ||
404 | videoInstance.setPrivacy(newPrivacy) | ||
405 | |||
406 | // Unfederate the video if the new privacy is not compatible with federation | ||
407 | if (hadPrivacyForFederation && !videoInstance.hasPrivacyForFederation()) { | ||
408 | await VideoModel.sendDelete(videoInstance, { transaction: t }) | ||
409 | } | ||
410 | } | ||
411 | |||
412 | const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) as MVideoFullLight | ||
413 | |||
414 | if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t) | ||
415 | if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, t) | ||
416 | |||
417 | // Video tags update? | ||
418 | if (videoInfoToUpdate.tags !== undefined) { | ||
419 | await setVideoTags({ | ||
420 | video: videoInstanceUpdated, | ||
421 | tags: videoInfoToUpdate.tags, | ||
422 | transaction: t | ||
423 | }) | ||
424 | } | ||
425 | |||
426 | // Video channel update? | ||
427 | if (res.locals.videoChannel && videoInstanceUpdated.channelId !== res.locals.videoChannel.id) { | ||
428 | await videoInstanceUpdated.$set('VideoChannel', res.locals.videoChannel, { transaction: t }) | ||
429 | videoInstanceUpdated.VideoChannel = res.locals.videoChannel | ||
430 | |||
431 | if (hadPrivacyForFederation === true) await changeVideoChannelShare(videoInstanceUpdated, oldVideoChannel, t) | ||
432 | } | ||
433 | |||
434 | // Schedule an update in the future? | ||
435 | if (videoInfoToUpdate.scheduleUpdate) { | ||
436 | await ScheduleVideoUpdateModel.upsert({ | ||
437 | videoId: videoInstanceUpdated.id, | ||
438 | updateAt: videoInfoToUpdate.scheduleUpdate.updateAt, | ||
439 | privacy: videoInfoToUpdate.scheduleUpdate.privacy || null | ||
440 | }, { transaction: t }) | ||
441 | } else if (videoInfoToUpdate.scheduleUpdate === null) { | ||
442 | await ScheduleVideoUpdateModel.deleteByVideoId(videoInstanceUpdated.id, t) | ||
443 | } | ||
444 | |||
445 | await autoBlacklistVideoIfNeeded({ | ||
446 | video: videoInstanceUpdated, | ||
447 | user: res.locals.oauth.token.User, | ||
448 | isRemote: false, | ||
449 | isNew: false, | ||
450 | transaction: t | ||
451 | }) | ||
452 | |||
453 | await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) | ||
454 | |||
455 | auditLogger.update( | ||
456 | getAuditIdFromRes(res), | ||
457 | new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), | ||
458 | oldVideoAuditView | ||
459 | ) | ||
460 | logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid, lTags(videoInstance.uuid)) | ||
461 | |||
462 | return videoInstanceUpdated | ||
463 | }) | ||
464 | |||
465 | if (wasConfidentialVideo) { | ||
466 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoInstanceUpdated) | ||
467 | } | ||
468 | |||
469 | Hooks.runAction('action:api.video.updated', { video: videoInstanceUpdated, body: req.body }) | ||
470 | } catch (err) { | ||
471 | // Force fields we want to update | ||
472 | // If the transaction is retried, sequelize will think the object has not changed | ||
473 | // So it will skip the SQL request, even if the last one was ROLLBACKed! | ||
474 | resetSequelizeInstance(videoInstance, videoFieldsSave) | ||
475 | |||
476 | throw err | ||
477 | } | ||
478 | |||
479 | return res.type('json') | ||
480 | .status(HttpStatusCode.NO_CONTENT_204) | ||
481 | .end() | ||
482 | } | ||
483 | |||
484 | async function getVideo (req: express.Request, res: express.Response) { | ||
485 | // We need more attributes | 126 | // We need more attributes |
486 | const userId: number = res.locals.oauth ? res.locals.oauth.token.User.id : null | 127 | const userId: number = res.locals.oauth ? res.locals.oauth.token.User.id : null |
487 | 128 | ||
@@ -543,13 +184,10 @@ async function viewVideo (req: express.Request, res: express.Response) { | |||
543 | 184 | ||
544 | async function getVideoDescription (req: express.Request, res: express.Response) { | 185 | async function getVideoDescription (req: express.Request, res: express.Response) { |
545 | const videoInstance = res.locals.videoAll | 186 | const videoInstance = res.locals.videoAll |
546 | let description = '' | ||
547 | 187 | ||
548 | if (videoInstance.isOwned()) { | 188 | const description = videoInstance.isOwned() |
549 | description = videoInstance.description | 189 | ? videoInstance.description |
550 | } else { | 190 | : await fetchRemoteVideoDescription(videoInstance) |
551 | description = await fetchRemoteVideoDescription(videoInstance) | ||
552 | } | ||
553 | 191 | ||
554 | return res.json({ description }) | 192 | return res.json({ description }) |
555 | } | 193 | } |
@@ -591,7 +229,7 @@ async function listVideos (req: express.Request, res: express.Response) { | |||
591 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 229 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
592 | } | 230 | } |
593 | 231 | ||
594 | async function removeVideo (req: express.Request, res: express.Response) { | 232 | async function removeVideo (_req: express.Request, res: express.Response) { |
595 | const videoInstance = res.locals.videoAll | 233 | const videoInstance = res.locals.videoAll |
596 | 234 | ||
597 | await sequelizeTypescript.transaction(async t => { | 235 | await sequelizeTypescript.transaction(async t => { |
@@ -607,17 +245,3 @@ async function removeVideo (req: express.Request, res: express.Response) { | |||
607 | .status(HttpStatusCode.NO_CONTENT_204) | 245 | .status(HttpStatusCode.NO_CONTENT_204) |
608 | .end() | 246 | .end() |
609 | } | 247 | } |
610 | |||
611 | async function createTorrentAndSetInfoHashAsync (video: MVideo, fileArg: MVideoFile) { | ||
612 | await createTorrentAndSetInfoHash(video, fileArg) | ||
613 | |||
614 | // Refresh videoFile because the createTorrentAndSetInfoHash could be long | ||
615 | const refreshedFile = await VideoFileModel.loadWithVideo(fileArg.id) | ||
616 | // File does not exist anymore, remove the generated torrent | ||
617 | if (!refreshedFile) return fileArg.removeTorrent() | ||
618 | |||
619 | refreshedFile.infoHash = fileArg.infoHash | ||
620 | refreshedFile.torrentFilename = fileArg.torrentFilename | ||
621 | |||
622 | return refreshedFile.save() | ||
623 | } | ||
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts index a85d7c30b..6102f28dc 100644 --- a/server/controllers/api/videos/ownership.ts +++ b/server/controllers/api/videos/ownership.ts | |||
@@ -99,7 +99,7 @@ async function listVideoOwnership (req: express.Request, res: express.Response) | |||
99 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 99 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
100 | } | 100 | } |
101 | 101 | ||
102 | async function acceptOwnership (req: express.Request, res: express.Response) { | 102 | function acceptOwnership (req: express.Request, res: express.Response) { |
103 | return sequelizeTypescript.transaction(async t => { | 103 | return sequelizeTypescript.transaction(async t => { |
104 | const videoChangeOwnership = res.locals.videoChangeOwnership | 104 | const videoChangeOwnership = res.locals.videoChangeOwnership |
105 | const channel = res.locals.videoChannel | 105 | const channel = res.locals.videoChannel |
@@ -126,7 +126,7 @@ async function acceptOwnership (req: express.Request, res: express.Response) { | |||
126 | }) | 126 | }) |
127 | } | 127 | } |
128 | 128 | ||
129 | async function refuseOwnership (req: express.Request, res: express.Response) { | 129 | function refuseOwnership (req: express.Request, res: express.Response) { |
130 | return sequelizeTypescript.transaction(async t => { | 130 | return sequelizeTypescript.transaction(async t => { |
131 | const videoChangeOwnership = res.locals.videoChangeOwnership | 131 | const videoChangeOwnership = res.locals.videoChangeOwnership |
132 | 132 | ||
diff --git a/server/controllers/api/videos/update.ts b/server/controllers/api/videos/update.ts new file mode 100644 index 000000000..2450abd0e --- /dev/null +++ b/server/controllers/api/videos/update.ts | |||
@@ -0,0 +1,191 @@ | |||
1 | import * as express from 'express' | ||
2 | import { Transaction } from 'sequelize/types' | ||
3 | import { changeVideoChannelShare } from '@server/lib/activitypub/share' | ||
4 | import { buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' | ||
5 | import { FilteredModelAttributes } from '@server/types' | ||
6 | import { MVideoFullLight } from '@server/types/models' | ||
7 | import { VideoUpdate } from '../../../../shared' | ||
8 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs' | ||
9 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | ||
10 | import { resetSequelizeInstance } from '../../../helpers/database-utils' | ||
11 | import { createReqFiles } from '../../../helpers/express-utils' | ||
12 | import { logger, loggerTagsFactory } from '../../../helpers/logger' | ||
13 | import { CONFIG } from '../../../initializers/config' | ||
14 | import { MIMETYPES } from '../../../initializers/constants' | ||
15 | import { sequelizeTypescript } from '../../../initializers/database' | ||
16 | import { federateVideoIfNeeded } from '../../../lib/activitypub/videos' | ||
17 | import { Notifier } from '../../../lib/notifier' | ||
18 | import { Hooks } from '../../../lib/plugins/hooks' | ||
19 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | ||
20 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videosUpdateValidator } from '../../../middlewares' | ||
21 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | ||
22 | import { VideoModel } from '../../../models/video/video' | ||
23 | |||
24 | const lTags = loggerTagsFactory('api', 'video') | ||
25 | const auditLogger = auditLoggerFactory('videos') | ||
26 | const updateRouter = express.Router() | ||
27 | |||
28 | const reqVideoFileUpdate = createReqFiles( | ||
29 | [ 'thumbnailfile', 'previewfile' ], | ||
30 | MIMETYPES.IMAGE.MIMETYPE_EXT, | ||
31 | { | ||
32 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, | ||
33 | previewfile: CONFIG.STORAGE.TMP_DIR | ||
34 | } | ||
35 | ) | ||
36 | |||
37 | updateRouter.put('/:id', | ||
38 | authenticate, | ||
39 | reqVideoFileUpdate, | ||
40 | asyncMiddleware(videosUpdateValidator), | ||
41 | asyncRetryTransactionMiddleware(updateVideo) | ||
42 | ) | ||
43 | |||
44 | // --------------------------------------------------------------------------- | ||
45 | |||
46 | export { | ||
47 | updateRouter | ||
48 | } | ||
49 | |||
50 | // --------------------------------------------------------------------------- | ||
51 | |||
52 | export async function updateVideo (req: express.Request, res: express.Response) { | ||
53 | const videoInstance = res.locals.videoAll | ||
54 | const videoFieldsSave = videoInstance.toJSON() | ||
55 | const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) | ||
56 | const videoInfoToUpdate: VideoUpdate = req.body | ||
57 | |||
58 | const wasConfidentialVideo = videoInstance.isConfidential() | ||
59 | const hadPrivacyForFederation = videoInstance.hasPrivacyForFederation() | ||
60 | |||
61 | const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ | ||
62 | video: videoInstance, | ||
63 | files: req.files, | ||
64 | fallback: () => Promise.resolve(undefined), | ||
65 | automaticallyGenerated: false | ||
66 | }) | ||
67 | |||
68 | try { | ||
69 | const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => { | ||
70 | const sequelizeOptions = { transaction: t } | ||
71 | const oldVideoChannel = videoInstance.VideoChannel | ||
72 | |||
73 | const keysToUpdate: (keyof VideoUpdate & FilteredModelAttributes<VideoModel>)[] = [ | ||
74 | 'name', | ||
75 | 'category', | ||
76 | 'licence', | ||
77 | 'language', | ||
78 | 'nsfw', | ||
79 | 'waitTranscoding', | ||
80 | 'support', | ||
81 | 'description', | ||
82 | 'commentsEnabled', | ||
83 | 'downloadEnabled' | ||
84 | ] | ||
85 | |||
86 | for (const key of keysToUpdate) { | ||
87 | if (videoInfoToUpdate[key] !== undefined) videoInstance.set(key, videoInfoToUpdate[key]) | ||
88 | } | ||
89 | |||
90 | if (videoInfoToUpdate.originallyPublishedAt !== undefined && videoInfoToUpdate.originallyPublishedAt !== null) { | ||
91 | videoInstance.originallyPublishedAt = new Date(videoInfoToUpdate.originallyPublishedAt) | ||
92 | } | ||
93 | |||
94 | // Privacy update? | ||
95 | let isNewVideo = false | ||
96 | if (videoInfoToUpdate.privacy !== undefined) { | ||
97 | isNewVideo = await updateVideoPrivacy({ videoInstance, videoInfoToUpdate, hadPrivacyForFederation, transaction: t }) | ||
98 | } | ||
99 | |||
100 | const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) as MVideoFullLight | ||
101 | |||
102 | // Thumbnail & preview updates? | ||
103 | if (thumbnailModel) await videoInstanceUpdated.addAndSaveThumbnail(thumbnailModel, t) | ||
104 | if (previewModel) await videoInstanceUpdated.addAndSaveThumbnail(previewModel, t) | ||
105 | |||
106 | // Video tags update? | ||
107 | if (videoInfoToUpdate.tags !== undefined) { | ||
108 | await setVideoTags({ video: videoInstanceUpdated, tags: videoInfoToUpdate.tags, transaction: t }) | ||
109 | } | ||
110 | |||
111 | // Video channel update? | ||
112 | if (res.locals.videoChannel && videoInstanceUpdated.channelId !== res.locals.videoChannel.id) { | ||
113 | await videoInstanceUpdated.$set('VideoChannel', res.locals.videoChannel, { transaction: t }) | ||
114 | videoInstanceUpdated.VideoChannel = res.locals.videoChannel | ||
115 | |||
116 | if (hadPrivacyForFederation === true) await changeVideoChannelShare(videoInstanceUpdated, oldVideoChannel, t) | ||
117 | } | ||
118 | |||
119 | // Schedule an update in the future? | ||
120 | await updateSchedule(videoInstanceUpdated, videoInfoToUpdate, t) | ||
121 | |||
122 | await autoBlacklistVideoIfNeeded({ | ||
123 | video: videoInstanceUpdated, | ||
124 | user: res.locals.oauth.token.User, | ||
125 | isRemote: false, | ||
126 | isNew: false, | ||
127 | transaction: t | ||
128 | }) | ||
129 | |||
130 | await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) | ||
131 | |||
132 | auditLogger.update( | ||
133 | getAuditIdFromRes(res), | ||
134 | new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), | ||
135 | oldVideoAuditView | ||
136 | ) | ||
137 | logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid, lTags(videoInstance.uuid)) | ||
138 | |||
139 | return videoInstanceUpdated | ||
140 | }) | ||
141 | |||
142 | if (wasConfidentialVideo) { | ||
143 | Notifier.Instance.notifyOnNewVideoIfNeeded(videoInstanceUpdated) | ||
144 | } | ||
145 | |||
146 | Hooks.runAction('action:api.video.updated', { video: videoInstanceUpdated, body: req.body }) | ||
147 | } catch (err) { | ||
148 | // Force fields we want to update | ||
149 | // If the transaction is retried, sequelize will think the object has not changed | ||
150 | // So it will skip the SQL request, even if the last one was ROLLBACKed! | ||
151 | resetSequelizeInstance(videoInstance, videoFieldsSave) | ||
152 | |||
153 | throw err | ||
154 | } | ||
155 | |||
156 | return res.type('json') | ||
157 | .status(HttpStatusCode.NO_CONTENT_204) | ||
158 | .end() | ||
159 | } | ||
160 | |||
161 | async function updateVideoPrivacy (options: { | ||
162 | videoInstance: MVideoFullLight | ||
163 | videoInfoToUpdate: VideoUpdate | ||
164 | hadPrivacyForFederation: boolean | ||
165 | transaction: Transaction | ||
166 | }) { | ||
167 | const { videoInstance, videoInfoToUpdate, hadPrivacyForFederation, transaction } = options | ||
168 | const isNewVideo = videoInstance.isNewVideo(videoInfoToUpdate.privacy) | ||
169 | |||
170 | const newPrivacy = parseInt(videoInfoToUpdate.privacy.toString(), 10) | ||
171 | videoInstance.setPrivacy(newPrivacy) | ||
172 | |||
173 | // Unfederate the video if the new privacy is not compatible with federation | ||
174 | if (hadPrivacyForFederation && !videoInstance.hasPrivacyForFederation()) { | ||
175 | await VideoModel.sendDelete(videoInstance, { transaction }) | ||
176 | } | ||
177 | |||
178 | return isNewVideo | ||
179 | } | ||
180 | |||
181 | function updateSchedule (videoInstance: MVideoFullLight, videoInfoToUpdate: VideoUpdate, transaction: Transaction) { | ||
182 | if (videoInfoToUpdate.scheduleUpdate) { | ||
183 | return ScheduleVideoUpdateModel.upsert({ | ||
184 | videoId: videoInstance.id, | ||
185 | updateAt: new Date(videoInfoToUpdate.scheduleUpdate.updateAt), | ||
186 | privacy: videoInfoToUpdate.scheduleUpdate.privacy || null | ||
187 | }, { transaction }) | ||
188 | } else if (videoInfoToUpdate.scheduleUpdate === null) { | ||
189 | return ScheduleVideoUpdateModel.deleteByVideoId(videoInstance.id, transaction) | ||
190 | } | ||
191 | } | ||
diff --git a/server/controllers/api/videos/upload.ts b/server/controllers/api/videos/upload.ts new file mode 100644 index 000000000..ebc17c760 --- /dev/null +++ b/server/controllers/api/videos/upload.ts | |||
@@ -0,0 +1,269 @@ | |||
1 | import * as express from 'express' | ||
2 | import { move } from 'fs-extra' | ||
3 | import { extname } from 'path' | ||
4 | import { deleteResumableUploadMetaFile, getResumableUploadPath } from '@server/helpers/upload' | ||
5 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | ||
6 | import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' | ||
7 | import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' | ||
8 | import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' | ||
9 | import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' | ||
10 | import { uploadx } from '@uploadx/core' | ||
11 | import { VideoCreate, VideoState } from '../../../../shared' | ||
12 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs' | ||
13 | import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' | ||
14 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | ||
15 | import { createReqFiles } from '../../../helpers/express-utils' | ||
16 | import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' | ||
17 | import { logger, loggerTagsFactory } from '../../../helpers/logger' | ||
18 | import { CONFIG } from '../../../initializers/config' | ||
19 | import { DEFAULT_AUDIO_RESOLUTION, MIMETYPES } from '../../../initializers/constants' | ||
20 | import { sequelizeTypescript } from '../../../initializers/database' | ||
21 | import { federateVideoIfNeeded } from '../../../lib/activitypub/videos' | ||
22 | import { Notifier } from '../../../lib/notifier' | ||
23 | import { Hooks } from '../../../lib/plugins/hooks' | ||
24 | import { generateVideoMiniature } from '../../../lib/thumbnail' | ||
25 | import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' | ||
26 | import { | ||
27 | asyncMiddleware, | ||
28 | asyncRetryTransactionMiddleware, | ||
29 | authenticate, | ||
30 | videosAddLegacyValidator, | ||
31 | videosAddResumableInitValidator, | ||
32 | videosAddResumableValidator | ||
33 | } from '../../../middlewares' | ||
34 | import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-update' | ||
35 | import { VideoModel } from '../../../models/video/video' | ||
36 | import { VideoFileModel } from '../../../models/video/video-file' | ||
37 | |||
38 | const lTags = loggerTagsFactory('api', 'video') | ||
39 | const auditLogger = auditLoggerFactory('videos') | ||
40 | const uploadRouter = express.Router() | ||
41 | const uploadxMiddleware = uploadx.upload({ directory: getResumableUploadPath() }) | ||
42 | |||
43 | const reqVideoFileAdd = createReqFiles( | ||
44 | [ 'videofile', 'thumbnailfile', 'previewfile' ], | ||
45 | Object.assign({}, MIMETYPES.VIDEO.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT), | ||
46 | { | ||
47 | videofile: CONFIG.STORAGE.TMP_DIR, | ||
48 | thumbnailfile: CONFIG.STORAGE.TMP_DIR, | ||
49 | previewfile: CONFIG.STORAGE.TMP_DIR | ||
50 | } | ||
51 | ) | ||
52 | |||
53 | const reqVideoFileAddResumable = createReqFiles( | ||
54 | [ 'thumbnailfile', 'previewfile' ], | ||
55 | MIMETYPES.IMAGE.MIMETYPE_EXT, | ||
56 | { | ||
57 | thumbnailfile: getResumableUploadPath(), | ||
58 | previewfile: getResumableUploadPath() | ||
59 | } | ||
60 | ) | ||
61 | |||
62 | uploadRouter.post('/upload', | ||
63 | authenticate, | ||
64 | reqVideoFileAdd, | ||
65 | asyncMiddleware(videosAddLegacyValidator), | ||
66 | asyncRetryTransactionMiddleware(addVideoLegacy) | ||
67 | ) | ||
68 | |||
69 | uploadRouter.post('/upload-resumable', | ||
70 | authenticate, | ||
71 | reqVideoFileAddResumable, | ||
72 | asyncMiddleware(videosAddResumableInitValidator), | ||
73 | uploadxMiddleware | ||
74 | ) | ||
75 | |||
76 | uploadRouter.delete('/upload-resumable', | ||
77 | authenticate, | ||
78 | uploadxMiddleware | ||
79 | ) | ||
80 | |||
81 | uploadRouter.put('/upload-resumable', | ||
82 | authenticate, | ||
83 | uploadxMiddleware, // uploadx doesn't use call next() before the file upload completes | ||
84 | asyncMiddleware(videosAddResumableValidator), | ||
85 | asyncMiddleware(addVideoResumable) | ||
86 | ) | ||
87 | |||
88 | // --------------------------------------------------------------------------- | ||
89 | |||
90 | export { | ||
91 | uploadRouter | ||
92 | } | ||
93 | |||
94 | // --------------------------------------------------------------------------- | ||
95 | |||
96 | export async function addVideoLegacy (req: express.Request, res: express.Response) { | ||
97 | // Uploading the video could be long | ||
98 | // Set timeout to 10 minutes, as Express's default is 2 minutes | ||
99 | req.setTimeout(1000 * 60 * 10, () => { | ||
100 | logger.error('Upload video has timed out.') | ||
101 | return res.sendStatus(HttpStatusCode.REQUEST_TIMEOUT_408) | ||
102 | }) | ||
103 | |||
104 | const videoPhysicalFile = req.files['videofile'][0] | ||
105 | const videoInfo: VideoCreate = req.body | ||
106 | const files = req.files | ||
107 | |||
108 | return addVideo({ res, videoPhysicalFile, videoInfo, files }) | ||
109 | } | ||
110 | |||
111 | export async function addVideoResumable (_req: express.Request, res: express.Response) { | ||
112 | const videoPhysicalFile = res.locals.videoFileResumable | ||
113 | const videoInfo = videoPhysicalFile.metadata | ||
114 | const files = { previewfile: videoInfo.previewfile } | ||
115 | |||
116 | // Don't need the meta file anymore | ||
117 | await deleteResumableUploadMetaFile(videoPhysicalFile.path) | ||
118 | |||
119 | return addVideo({ res, videoPhysicalFile, videoInfo, files }) | ||
120 | } | ||
121 | |||
122 | async function addVideo (options: { | ||
123 | res: express.Response | ||
124 | videoPhysicalFile: express.VideoUploadFile | ||
125 | videoInfo: VideoCreate | ||
126 | files: express.UploadFiles | ||
127 | }) { | ||
128 | const { res, videoPhysicalFile, videoInfo, files } = options | ||
129 | const videoChannel = res.locals.videoChannel | ||
130 | const user = res.locals.oauth.token.User | ||
131 | |||
132 | const videoData = buildLocalVideoFromReq(videoInfo, videoChannel.id) | ||
133 | |||
134 | videoData.state = CONFIG.TRANSCODING.ENABLED | ||
135 | ? VideoState.TO_TRANSCODE | ||
136 | : VideoState.PUBLISHED | ||
137 | |||
138 | videoData.duration = videoPhysicalFile.duration // duration was added by a previous middleware | ||
139 | |||
140 | const video = new VideoModel(videoData) as MVideoFullLight | ||
141 | video.VideoChannel = videoChannel | ||
142 | video.url = getLocalVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object | ||
143 | |||
144 | const videoFile = await buildNewFile(video, videoPhysicalFile) | ||
145 | |||
146 | // Move physical file | ||
147 | const destination = getVideoFilePath(video, videoFile) | ||
148 | await move(videoPhysicalFile.path, destination) | ||
149 | // This is important in case if there is another attempt in the retry process | ||
150 | videoPhysicalFile.filename = getVideoFilePath(video, videoFile) | ||
151 | videoPhysicalFile.path = destination | ||
152 | |||
153 | const [ thumbnailModel, previewModel ] = await buildVideoThumbnailsFromReq({ | ||
154 | video, | ||
155 | files, | ||
156 | fallback: type => generateVideoMiniature({ video, videoFile, type }) | ||
157 | }) | ||
158 | |||
159 | const { videoCreated } = await sequelizeTypescript.transaction(async t => { | ||
160 | const sequelizeOptions = { transaction: t } | ||
161 | |||
162 | const videoCreated = await video.save(sequelizeOptions) as MVideoFullLight | ||
163 | |||
164 | await videoCreated.addAndSaveThumbnail(thumbnailModel, t) | ||
165 | await videoCreated.addAndSaveThumbnail(previewModel, t) | ||
166 | |||
167 | // Do not forget to add video channel information to the created video | ||
168 | videoCreated.VideoChannel = res.locals.videoChannel | ||
169 | |||
170 | videoFile.videoId = video.id | ||
171 | await videoFile.save(sequelizeOptions) | ||
172 | |||
173 | video.VideoFiles = [ videoFile ] | ||
174 | |||
175 | await setVideoTags({ video, tags: videoInfo.tags, transaction: t }) | ||
176 | |||
177 | // Schedule an update in the future? | ||
178 | if (videoInfo.scheduleUpdate) { | ||
179 | await ScheduleVideoUpdateModel.create({ | ||
180 | videoId: video.id, | ||
181 | updateAt: new Date(videoInfo.scheduleUpdate.updateAt), | ||
182 | privacy: videoInfo.scheduleUpdate.privacy || null | ||
183 | }, sequelizeOptions) | ||
184 | } | ||
185 | |||
186 | // Channel has a new content, set as updated | ||
187 | await videoCreated.VideoChannel.setAsUpdated(t) | ||
188 | |||
189 | await autoBlacklistVideoIfNeeded({ | ||
190 | video, | ||
191 | user, | ||
192 | isRemote: false, | ||
193 | isNew: true, | ||
194 | transaction: t | ||
195 | }) | ||
196 | |||
197 | auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) | ||
198 | logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid, lTags(videoCreated.uuid)) | ||
199 | |||
200 | return { videoCreated } | ||
201 | }) | ||
202 | |||
203 | createTorrentFederate(video, videoFile) | ||
204 | |||
205 | if (video.state === VideoState.TO_TRANSCODE) { | ||
206 | await addOptimizeOrMergeAudioJob(videoCreated, videoFile, user) | ||
207 | } | ||
208 | |||
209 | Hooks.runAction('action:api.video.uploaded', { video: videoCreated }) | ||
210 | |||
211 | return res.json({ | ||
212 | video: { | ||
213 | id: videoCreated.id, | ||
214 | uuid: videoCreated.uuid | ||
215 | } | ||
216 | }) | ||
217 | } | ||
218 | |||
219 | async function buildNewFile (video: MVideo, videoPhysicalFile: express.VideoUploadFile) { | ||
220 | const videoFile = new VideoFileModel({ | ||
221 | extname: extname(videoPhysicalFile.filename), | ||
222 | size: videoPhysicalFile.size, | ||
223 | videoStreamingPlaylistId: null, | ||
224 | metadata: await getMetadataFromFile(videoPhysicalFile.path) | ||
225 | }) | ||
226 | |||
227 | if (videoFile.isAudio()) { | ||
228 | videoFile.resolution = DEFAULT_AUDIO_RESOLUTION | ||
229 | } else { | ||
230 | videoFile.fps = await getVideoFileFPS(videoPhysicalFile.path) | ||
231 | videoFile.resolution = (await getVideoFileResolution(videoPhysicalFile.path)).videoFileResolution | ||
232 | } | ||
233 | |||
234 | videoFile.filename = generateVideoFilename(video, false, videoFile.resolution, videoFile.extname) | ||
235 | |||
236 | return videoFile | ||
237 | } | ||
238 | |||
239 | async function createTorrentAndSetInfoHashAsync (video: MVideo, fileArg: MVideoFile) { | ||
240 | await createTorrentAndSetInfoHash(video, fileArg) | ||
241 | |||
242 | // Refresh videoFile because the createTorrentAndSetInfoHash could be long | ||
243 | const refreshedFile = await VideoFileModel.loadWithVideo(fileArg.id) | ||
244 | // File does not exist anymore, remove the generated torrent | ||
245 | if (!refreshedFile) return fileArg.removeTorrent() | ||
246 | |||
247 | refreshedFile.infoHash = fileArg.infoHash | ||
248 | refreshedFile.torrentFilename = fileArg.torrentFilename | ||
249 | |||
250 | return refreshedFile.save() | ||
251 | } | ||
252 | |||
253 | function createTorrentFederate (video: MVideoFullLight, videoFile: MVideoFile): void { | ||
254 | // Create the torrent file in async way because it could be long | ||
255 | createTorrentAndSetInfoHashAsync(video, videoFile) | ||
256 | .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err, ...lTags(video.uuid) })) | ||
257 | .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id)) | ||
258 | .then(refreshedVideo => { | ||
259 | if (!refreshedVideo) return | ||
260 | |||
261 | // Only federate and notify after the torrent creation | ||
262 | Notifier.Instance.notifyOnNewVideoIfNeeded(refreshedVideo) | ||
263 | |||
264 | return retryTransactionWrapper(() => { | ||
265 | return sequelizeTypescript.transaction(t => federateVideoIfNeeded(refreshedVideo, true, t)) | ||
266 | }) | ||
267 | }) | ||
268 | .catch(err => logger.error('Cannot federate or notify video creation %s', video.url, { err, ...lTags(video.uuid) })) | ||
269 | } | ||
diff --git a/server/controllers/api/videos/watching.ts b/server/controllers/api/videos/watching.ts index 627f12aa9..08190e583 100644 --- a/server/controllers/api/videos/watching.ts +++ b/server/controllers/api/videos/watching.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { UserWatchingVideo } from '../../../../shared' | 2 | import { UserWatchingVideo } from '../../../../shared' |
3 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoWatchingValidator } from '../../../middlewares' | 3 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoWatchingValidator } from '../../../middlewares' |
4 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' | 4 | import { UserVideoHistoryModel } from '../../../models/user/user-video-history' |
5 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 5 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
6 | 6 | ||
7 | const watchingRouter = express.Router() | 7 | const watchingRouter = express.Router() |
diff --git a/server/controllers/lazy-static.ts b/server/controllers/lazy-static.ts index 6f71fdb16..25d3b49b4 100644 --- a/server/controllers/lazy-static.ts +++ b/server/controllers/lazy-static.ts | |||
@@ -7,7 +7,7 @@ import { LAZY_STATIC_PATHS, STATIC_MAX_AGE } from '../initializers/constants' | |||
7 | import { actorImagePathUnsafeCache, pushActorImageProcessInQueue } from '../lib/actor-image' | 7 | import { actorImagePathUnsafeCache, pushActorImageProcessInQueue } from '../lib/actor-image' |
8 | import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache' | 8 | import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache' |
9 | import { asyncMiddleware } from '../middlewares' | 9 | import { asyncMiddleware } from '../middlewares' |
10 | import { ActorImageModel } from '../models/account/actor-image' | 10 | import { ActorImageModel } from '../models/actor/actor-image' |
11 | 11 | ||
12 | const lazyStaticRouter = express.Router() | 12 | const lazyStaticRouter = express.Router() |
13 | 13 | ||
diff --git a/server/controllers/services.ts b/server/controllers/services.ts index 189e1651b..8c0af9ff7 100644 --- a/server/controllers/services.ts +++ b/server/controllers/services.ts | |||
@@ -78,17 +78,18 @@ function buildOEmbed (options: { | |||
78 | const maxWidth = parseInt(req.query.maxwidth, 10) | 78 | const maxWidth = parseInt(req.query.maxwidth, 10) |
79 | 79 | ||
80 | const embedUrl = webserverUrl + embedPath | 80 | const embedUrl = webserverUrl + embedPath |
81 | let embedWidth = EMBED_SIZE.width | ||
82 | let embedHeight = EMBED_SIZE.height | ||
83 | const embedTitle = escapeHTML(title) | 81 | const embedTitle = escapeHTML(title) |
84 | 82 | ||
85 | let thumbnailUrl = previewPath | 83 | let thumbnailUrl = previewPath |
86 | ? webserverUrl + previewPath | 84 | ? webserverUrl + previewPath |
87 | : undefined | 85 | : undefined |
88 | 86 | ||
89 | if (maxHeight < embedHeight) embedHeight = maxHeight | 87 | let embedWidth = EMBED_SIZE.width |
90 | if (maxWidth < embedWidth) embedWidth = maxWidth | 88 | if (maxWidth < embedWidth) embedWidth = maxWidth |
91 | 89 | ||
90 | let embedHeight = EMBED_SIZE.height | ||
91 | if (maxHeight < embedHeight) embedHeight = maxHeight | ||
92 | |||
92 | // Our thumbnail is too big for the consumer | 93 | // Our thumbnail is too big for the consumer |
93 | if ( | 94 | if ( |
94 | (maxHeight !== undefined && maxHeight < previewSize.height) || | 95 | (maxHeight !== undefined && maxHeight < previewSize.height) || |
diff --git a/server/controllers/static.ts b/server/controllers/static.ts index 8d9003a3e..8a747ec52 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts | |||
@@ -2,9 +2,9 @@ import * as cors from 'cors' | |||
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import { join } from 'path' | 3 | import { join } from 'path' |
4 | import { serveIndexHTML } from '@server/lib/client-html' | 4 | import { serveIndexHTML } from '@server/lib/client-html' |
5 | import { getRegisteredPlugins, getRegisteredThemes } from '@server/lib/config' | 5 | import { getEnabledResolutions, getRegisteredPlugins, getRegisteredThemes } from '@server/lib/config' |
6 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | 6 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' |
7 | import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo' | 7 | import { HttpNodeinfoDiasporaSoftwareNsSchema20 } from '../../shared/models/nodeinfo/nodeinfo.model' |
8 | import { root } from '../helpers/core-utils' | 8 | import { root } from '../helpers/core-utils' |
9 | import { CONFIG, isEmailEnabled } from '../initializers/config' | 9 | import { CONFIG, isEmailEnabled } from '../initializers/config' |
10 | import { | 10 | import { |
@@ -18,10 +18,9 @@ import { | |||
18 | WEBSERVER | 18 | WEBSERVER |
19 | } from '../initializers/constants' | 19 | } from '../initializers/constants' |
20 | import { getThemeOrDefault } from '../lib/plugins/theme-utils' | 20 | import { getThemeOrDefault } from '../lib/plugins/theme-utils' |
21 | import { getEnabledResolutions } from '../lib/video-transcoding' | ||
22 | import { asyncMiddleware } from '../middlewares' | 21 | import { asyncMiddleware } from '../middlewares' |
23 | import { cacheRoute } from '../middlewares/cache' | 22 | import { cacheRoute } from '../middlewares/cache' |
24 | import { UserModel } from '../models/account/user' | 23 | import { UserModel } from '../models/user/user' |
25 | import { VideoModel } from '../models/video/video' | 24 | import { VideoModel } from '../models/video/video' |
26 | import { VideoCommentModel } from '../models/video/video-comment' | 25 | import { VideoCommentModel } from '../models/video/video-comment' |
27 | 26 | ||
diff --git a/server/helpers/actor.ts b/server/helpers/actor.ts index a60d3ed5d..5f742505b 100644 --- a/server/helpers/actor.ts +++ b/server/helpers/actor.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | 1 | ||
2 | import { ActorModel } from '../models/activitypub/actor' | 2 | import { ActorModel } from '../models/actor/actor' |
3 | import { MActorAccountChannelId, MActorFull } from '../types/models' | 3 | import { MActorAccountChannelId, MActorFull } from '../types/models' |
4 | 4 | ||
5 | type ActorFetchByUrlType = 'all' | 'association-ids' | 5 | type ActorFetchByUrlType = 'all' | 'association-ids' |
diff --git a/server/helpers/audit-logger.ts b/server/helpers/audit-logger.ts index 6aae5e821..884bd187d 100644 --- a/server/helpers/audit-logger.ts +++ b/server/helpers/audit-logger.ts | |||
@@ -7,7 +7,7 @@ import * as winston from 'winston' | |||
7 | import { AUDIT_LOG_FILENAME } from '@server/initializers/constants' | 7 | import { AUDIT_LOG_FILENAME } from '@server/initializers/constants' |
8 | import { AdminAbuse, User, VideoChannel, VideoDetails, VideoImport } from '../../shared' | 8 | import { AdminAbuse, User, VideoChannel, VideoDetails, VideoImport } from '../../shared' |
9 | import { CustomConfig } from '../../shared/models/server/custom-config.model' | 9 | import { CustomConfig } from '../../shared/models/server/custom-config.model' |
10 | import { VideoComment } from '../../shared/models/videos/video-comment.model' | 10 | import { VideoComment } from '../../shared/models/videos/comment/video-comment.model' |
11 | import { CONFIG } from '../initializers/config' | 11 | import { CONFIG } from '../initializers/config' |
12 | import { jsonLoggerFormat, labelFormatter } from './logger' | 12 | import { jsonLoggerFormat, labelFormatter } from './logger' |
13 | 13 | ||
diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts index fd3b45804..229e9f03c 100644 --- a/server/helpers/custom-validators/misc.ts +++ b/server/helpers/custom-validators/misc.ts | |||
@@ -14,7 +14,7 @@ function isSafePath (p: string) { | |||
14 | }) | 14 | }) |
15 | } | 15 | } |
16 | 16 | ||
17 | function isArray (value: any) { | 17 | function isArray (value: any): value is any[] { |
18 | return Array.isArray(value) | 18 | return Array.isArray(value) |
19 | } | 19 | } |
20 | 20 | ||
diff --git a/server/helpers/database-utils.ts b/server/helpers/database-utils.ts index f9cb33aca..7befa2c49 100644 --- a/server/helpers/database-utils.ts +++ b/server/helpers/database-utils.ts | |||
@@ -68,7 +68,7 @@ function transactionRetryer <T> (func: (err: any, data: T) => any) { | |||
68 | }) | 68 | }) |
69 | } | 69 | } |
70 | 70 | ||
71 | function updateInstanceWithAnother <T extends Model<T>> (instanceToUpdate: Model<T>, baseInstance: Model<T>) { | 71 | function updateInstanceWithAnother <M, T extends U, U extends Model<M>> (instanceToUpdate: T, baseInstance: U) { |
72 | const obj = baseInstance.toJSON() | 72 | const obj = baseInstance.toJSON() |
73 | 73 | ||
74 | for (const key of Object.keys(obj)) { | 74 | for (const key of Object.keys(obj)) { |
@@ -88,7 +88,7 @@ function afterCommitIfTransaction (t: Transaction, fn: Function) { | |||
88 | return fn() | 88 | return fn() |
89 | } | 89 | } |
90 | 90 | ||
91 | function deleteNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean } & Model<T>> ( | 91 | function deleteNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean } & Pick<Model, 'destroy'>> ( |
92 | fromDatabase: T[], | 92 | fromDatabase: T[], |
93 | newModels: T[], | 93 | newModels: T[], |
94 | t: Transaction | 94 | t: Transaction |
diff --git a/server/helpers/express-utils.ts b/server/helpers/express-utils.ts index ede22a3cc..010c6961a 100644 --- a/server/helpers/express-utils.ts +++ b/server/helpers/express-utils.ts | |||
@@ -1,13 +1,13 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import * as multer from 'multer' | 2 | import * as multer from 'multer' |
3 | import { extname } from 'path' | ||
4 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | ||
5 | import { CONFIG } from '../initializers/config' | ||
3 | import { REMOTE_SCHEME } from '../initializers/constants' | 6 | import { REMOTE_SCHEME } from '../initializers/constants' |
7 | import { isArray } from './custom-validators/misc' | ||
4 | import { logger } from './logger' | 8 | import { logger } from './logger' |
5 | import { deleteFileAndCatch, generateRandomString } from './utils' | 9 | import { deleteFileAndCatch, generateRandomString } from './utils' |
6 | import { extname } from 'path' | ||
7 | import { isArray } from './custom-validators/misc' | ||
8 | import { CONFIG } from '../initializers/config' | ||
9 | import { getExtFromMimetype } from './video' | 10 | import { getExtFromMimetype } from './video' |
10 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | ||
11 | 11 | ||
12 | function buildNSFWFilter (res?: express.Response, paramNSFW?: string) { | 12 | function buildNSFWFilter (res?: express.Response, paramNSFW?: string) { |
13 | if (paramNSFW === 'true') return true | 13 | if (paramNSFW === 'true') return true |
@@ -30,21 +30,21 @@ function buildNSFWFilter (res?: express.Response, paramNSFW?: string) { | |||
30 | return null | 30 | return null |
31 | } | 31 | } |
32 | 32 | ||
33 | function cleanUpReqFiles (req: { files: { [fieldname: string]: Express.Multer.File[] } | Express.Multer.File[] }) { | 33 | function cleanUpReqFiles ( |
34 | const files = req.files | 34 | req: { files: { [fieldname: string]: Express.Multer.File[] } | Express.Multer.File[] } |
35 | 35 | ) { | |
36 | if (!files) return | 36 | const filesObject = req.files |
37 | if (!filesObject) return | ||
37 | 38 | ||
38 | if (isArray(files)) { | 39 | if (isArray(filesObject)) { |
39 | (files as Express.Multer.File[]).forEach(f => deleteFileAndCatch(f.path)) | 40 | filesObject.forEach(f => deleteFileAndCatch(f.path)) |
40 | return | 41 | return |
41 | } | 42 | } |
42 | 43 | ||
43 | for (const key of Object.keys(files)) { | 44 | for (const key of Object.keys(filesObject)) { |
44 | const file = files[key] | 45 | const files = filesObject[key] |
45 | 46 | ||
46 | if (isArray(file)) file.forEach(f => deleteFileAndCatch(f.path)) | 47 | files.forEach(f => deleteFileAndCatch(f.path)) |
47 | else deleteFileAndCatch(file.path) | ||
48 | } | 48 | } |
49 | } | 49 | } |
50 | 50 | ||
diff --git a/server/helpers/ffprobe-utils.ts b/server/helpers/ffprobe-utils.ts index 40eaafd57..ef2aa3f89 100644 --- a/server/helpers/ffprobe-utils.ts +++ b/server/helpers/ffprobe-utils.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import * as ffmpeg from 'fluent-ffmpeg' | 1 | import * as ffmpeg from 'fluent-ffmpeg' |
2 | import { VideoFileMetadata } from '@shared/models/videos/video-file-metadata' | 2 | import { getMaxBitrate, VideoFileMetadata, VideoResolution } from '../../shared/models/videos' |
3 | import { getMaxBitrate, VideoResolution } from '../../shared/models/videos' | ||
4 | import { CONFIG } from '../initializers/config' | 3 | import { CONFIG } from '../initializers/config' |
5 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' | 4 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' |
6 | import { logger } from './logger' | 5 | import { logger } from './logger' |
diff --git a/server/helpers/middlewares/accounts.ts b/server/helpers/middlewares/accounts.ts index 13ae6cdf4..5addd3e1a 100644 --- a/server/helpers/middlewares/accounts.ts +++ b/server/helpers/middlewares/accounts.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Response } from 'express' | 1 | import { Response } from 'express' |
2 | import { UserModel } from '@server/models/account/user' | 2 | import { UserModel } from '@server/models/user/user' |
3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
4 | import { AccountModel } from '../../models/account/account' | 4 | import { AccountModel } from '../../models/account/account' |
5 | import { MAccountDefault } from '../../types/models' | 5 | import { MAccountDefault } from '../../types/models' |
diff --git a/server/helpers/signup.ts b/server/helpers/signup.ts index ed872539b..8fa81e601 100644 --- a/server/helpers/signup.ts +++ b/server/helpers/signup.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { UserModel } from '../models/account/user' | 1 | import { UserModel } from '../models/user/user' |
2 | import * as ipaddr from 'ipaddr.js' | 2 | import * as ipaddr from 'ipaddr.js' |
3 | import { CONFIG } from '../initializers/config' | 3 | import { CONFIG } from '../initializers/config' |
4 | 4 | ||
diff --git a/server/helpers/webfinger.ts b/server/helpers/webfinger.ts index da7e88077..33367f651 100644 --- a/server/helpers/webfinger.ts +++ b/server/helpers/webfinger.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import * as WebFinger from 'webfinger.js' | 1 | import * as WebFinger from 'webfinger.js' |
2 | import { WebFingerData } from '../../shared' | 2 | import { WebFingerData } from '../../shared' |
3 | import { ActorModel } from '../models/activitypub/actor' | ||
4 | import { isTestInstance } from './core-utils' | ||
5 | import { isActivityPubUrlValid } from './custom-validators/activitypub/misc' | ||
6 | import { WEBSERVER } from '../initializers/constants' | 3 | import { WEBSERVER } from '../initializers/constants' |
4 | import { ActorModel } from '../models/actor/actor' | ||
7 | import { MActorFull } from '../types/models' | 5 | import { MActorFull } from '../types/models' |
6 | import { isTestInstance } from './core-utils' | ||
7 | import { isActivityPubUrlValid } from './custom-validators/activitypub/misc' | ||
8 | 8 | ||
9 | const webfinger = new WebFinger({ | 9 | const webfinger = new WebFinger({ |
10 | webfist_fallback: false, | 10 | webfist_fallback: false, |
diff --git a/server/helpers/youtube-dl.ts b/server/helpers/youtube-dl.ts index fac3da6ba..d003ea3cf 100644 --- a/server/helpers/youtube-dl.ts +++ b/server/helpers/youtube-dl.ts | |||
@@ -6,7 +6,6 @@ import { CONFIG } from '@server/initializers/config' | |||
6 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | 6 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' |
7 | import { VideoResolution } from '../../shared/models/videos' | 7 | import { VideoResolution } from '../../shared/models/videos' |
8 | import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants' | 8 | import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants' |
9 | import { getEnabledResolutions } from '../lib/video-transcoding' | ||
10 | import { peertubeTruncate, pipelinePromise, root } from './core-utils' | 9 | import { peertubeTruncate, pipelinePromise, root } from './core-utils' |
11 | import { isVideoFileExtnameValid } from './custom-validators/videos' | 10 | import { isVideoFileExtnameValid } from './custom-validators/videos' |
12 | import { logger } from './logger' | 11 | import { logger } from './logger' |
@@ -35,361 +34,359 @@ const processOptions = { | |||
35 | maxBuffer: 1024 * 1024 * 10 // 10MB | 34 | maxBuffer: 1024 * 1024 * 10 // 10MB |
36 | } | 35 | } |
37 | 36 | ||
38 | function getYoutubeDLInfo (url: string, opts?: string[]): Promise<YoutubeDLInfo> { | 37 | class YoutubeDL { |
39 | return new Promise<YoutubeDLInfo>((res, rej) => { | ||
40 | let args = opts || [ '-j', '--flat-playlist' ] | ||
41 | 38 | ||
42 | if (CONFIG.IMPORT.VIDEOS.HTTP.FORCE_IPV4) { | 39 | constructor (private readonly url: string = '', private readonly enabledResolutions: number[] = []) { |
43 | args.push('--force-ipv4') | ||
44 | } | ||
45 | 40 | ||
46 | args = wrapWithProxyOptions(args) | 41 | } |
47 | args = [ '-f', getYoutubeDLVideoFormat() ].concat(args) | ||
48 | 42 | ||
49 | safeGetYoutubeDL() | 43 | getYoutubeDLInfo (opts?: string[]): Promise<YoutubeDLInfo> { |
50 | .then(youtubeDL => { | 44 | return new Promise<YoutubeDLInfo>((res, rej) => { |
51 | youtubeDL.getInfo(url, args, processOptions, (err, info) => { | 45 | let args = opts || [ '-j', '--flat-playlist' ] |
52 | if (err) return rej(err) | ||
53 | if (info.is_live === true) return rej(new Error('Cannot download a live streaming.')) | ||
54 | 46 | ||
55 | const obj = buildVideoInfo(normalizeObject(info)) | 47 | if (CONFIG.IMPORT.VIDEOS.HTTP.FORCE_IPV4) { |
56 | if (obj.name && obj.name.length < CONSTRAINTS_FIELDS.VIDEOS.NAME.min) obj.name += ' video' | 48 | args.push('--force-ipv4') |
49 | } | ||
57 | 50 | ||
58 | return res(obj) | 51 | args = this.wrapWithProxyOptions(args) |
59 | }) | 52 | args = [ '-f', this.getYoutubeDLVideoFormat() ].concat(args) |
60 | }) | ||
61 | .catch(err => rej(err)) | ||
62 | }) | ||
63 | } | ||
64 | 53 | ||
65 | function getYoutubeDLSubs (url: string, opts?: object): Promise<YoutubeDLSubs> { | 54 | YoutubeDL.safeGetYoutubeDL() |
66 | return new Promise<YoutubeDLSubs>((res, rej) => { | 55 | .then(youtubeDL => { |
67 | const cwd = CONFIG.STORAGE.TMP_DIR | 56 | youtubeDL.getInfo(this.url, args, processOptions, (err, info) => { |
68 | const options = opts || { all: true, format: 'vtt', cwd } | 57 | if (err) return rej(err) |
69 | 58 | if (info.is_live === true) return rej(new Error('Cannot download a live streaming.')) | |
70 | safeGetYoutubeDL() | ||
71 | .then(youtubeDL => { | ||
72 | youtubeDL.getSubs(url, options, (err, files) => { | ||
73 | if (err) return rej(err) | ||
74 | if (!files) return [] | ||
75 | |||
76 | logger.debug('Get subtitles from youtube dl.', { url, files }) | ||
77 | |||
78 | const subtitles = files.reduce((acc, filename) => { | ||
79 | const matched = filename.match(/\.([a-z]{2})(-[a-z]+)?\.(vtt|ttml)/i) | ||
80 | if (!matched || !matched[1]) return acc | ||
81 | |||
82 | return [ | ||
83 | ...acc, | ||
84 | { | ||
85 | language: matched[1], | ||
86 | path: join(cwd, filename), | ||
87 | filename | ||
88 | } | ||
89 | ] | ||
90 | }, []) | ||
91 | 59 | ||
92 | return res(subtitles) | 60 | const obj = this.buildVideoInfo(this.normalizeObject(info)) |
61 | if (obj.name && obj.name.length < CONSTRAINTS_FIELDS.VIDEOS.NAME.min) obj.name += ' video' | ||
62 | |||
63 | return res(obj) | ||
64 | }) | ||
93 | }) | 65 | }) |
94 | }) | 66 | .catch(err => rej(err)) |
95 | .catch(err => rej(err)) | 67 | }) |
96 | }) | 68 | } |
97 | } | ||
98 | 69 | ||
99 | function getYoutubeDLVideoFormat () { | 70 | getYoutubeDLSubs (opts?: object): Promise<YoutubeDLSubs> { |
100 | /** | 71 | return new Promise<YoutubeDLSubs>((res, rej) => { |
101 | * list of format selectors in order or preference | 72 | const cwd = CONFIG.STORAGE.TMP_DIR |
102 | * see https://github.com/ytdl-org/youtube-dl#format-selection | 73 | const options = opts || { all: true, format: 'vtt', cwd } |
103 | * | 74 | |
104 | * case #1 asks for a mp4 using h264 (avc1) and the exact resolution in the hope | 75 | YoutubeDL.safeGetYoutubeDL() |
105 | * of being able to do a "quick-transcode" | 76 | .then(youtubeDL => { |
106 | * case #2 is the first fallback. No "quick-transcode" means we can get anything else (like vp9) | 77 | youtubeDL.getSubs(this.url, options, (err, files) => { |
107 | * case #3 is the resolution-degraded equivalent of #1, and already a pretty safe fallback | 78 | if (err) return rej(err) |
108 | * | 79 | if (!files) return [] |
109 | * in any case we avoid AV1, see https://github.com/Chocobozzz/PeerTube/issues/3499 | 80 | |
110 | **/ | 81 | logger.debug('Get subtitles from youtube dl.', { url: this.url, files }) |
111 | const enabledResolutions = getEnabledResolutions('vod') | 82 | |
112 | const resolution = enabledResolutions.length === 0 | 83 | const subtitles = files.reduce((acc, filename) => { |
113 | ? VideoResolution.H_720P | 84 | const matched = filename.match(/\.([a-z]{2})(-[a-z]+)?\.(vtt|ttml)/i) |
114 | : Math.max(...enabledResolutions) | 85 | if (!matched || !matched[1]) return acc |
115 | 86 | ||
116 | return [ | 87 | return [ |
117 | `bestvideo[vcodec^=avc1][height=${resolution}]+bestaudio[ext=m4a]`, // case #1 | 88 | ...acc, |
118 | `bestvideo[vcodec!*=av01][vcodec!*=vp9.2][height=${resolution}]+bestaudio`, // case #2 | 89 | { |
119 | `bestvideo[vcodec^=avc1][height<=${resolution}]+bestaudio[ext=m4a]`, // case #3 | 90 | language: matched[1], |
120 | `bestvideo[vcodec!*=av01][vcodec!*=vp9.2]+bestaudio`, | 91 | path: join(cwd, filename), |
121 | 'best[vcodec!*=av01][vcodec!*=vp9.2]', // case fallback for known formats | 92 | filename |
122 | 'best' // Ultimate fallback | 93 | } |
123 | ].join('/') | 94 | ] |
124 | } | 95 | }, []) |
96 | |||
97 | return res(subtitles) | ||
98 | }) | ||
99 | }) | ||
100 | .catch(err => rej(err)) | ||
101 | }) | ||
102 | } | ||
125 | 103 | ||
126 | function downloadYoutubeDLVideo (url: string, fileExt: string, timeout: number) { | 104 | getYoutubeDLVideoFormat () { |
127 | // Leave empty the extension, youtube-dl will add it | 105 | /** |
128 | const pathWithoutExtension = generateVideoImportTmpPath(url, '') | 106 | * list of format selectors in order or preference |
107 | * see https://github.com/ytdl-org/youtube-dl#format-selection | ||
108 | * | ||
109 | * case #1 asks for a mp4 using h264 (avc1) and the exact resolution in the hope | ||
110 | * of being able to do a "quick-transcode" | ||
111 | * case #2 is the first fallback. No "quick-transcode" means we can get anything else (like vp9) | ||
112 | * case #3 is the resolution-degraded equivalent of #1, and already a pretty safe fallback | ||
113 | * | ||
114 | * in any case we avoid AV1, see https://github.com/Chocobozzz/PeerTube/issues/3499 | ||
115 | **/ | ||
116 | const resolution = this.enabledResolutions.length === 0 | ||
117 | ? VideoResolution.H_720P | ||
118 | : Math.max(...this.enabledResolutions) | ||
119 | |||
120 | return [ | ||
121 | `bestvideo[vcodec^=avc1][height=${resolution}]+bestaudio[ext=m4a]`, // case #1 | ||
122 | `bestvideo[vcodec!*=av01][vcodec!*=vp9.2][height=${resolution}]+bestaudio`, // case #2 | ||
123 | `bestvideo[vcodec^=avc1][height<=${resolution}]+bestaudio[ext=m4a]`, // case #3 | ||
124 | `bestvideo[vcodec!*=av01][vcodec!*=vp9.2]+bestaudio`, | ||
125 | 'best[vcodec!*=av01][vcodec!*=vp9.2]', // case fallback for known formats | ||
126 | 'best' // Ultimate fallback | ||
127 | ].join('/') | ||
128 | } | ||
129 | 129 | ||
130 | let timer | 130 | downloadYoutubeDLVideo (fileExt: string, timeout: number) { |
131 | // Leave empty the extension, youtube-dl will add it | ||
132 | const pathWithoutExtension = generateVideoImportTmpPath(this.url, '') | ||
131 | 133 | ||
132 | logger.info('Importing youtubeDL video %s to %s', url, pathWithoutExtension) | 134 | let timer |
133 | 135 | ||
134 | let options = [ '-f', getYoutubeDLVideoFormat(), '-o', pathWithoutExtension ] | 136 | logger.info('Importing youtubeDL video %s to %s', this.url, pathWithoutExtension) |
135 | options = wrapWithProxyOptions(options) | ||
136 | 137 | ||
137 | if (process.env.FFMPEG_PATH) { | 138 | let options = [ '-f', this.getYoutubeDLVideoFormat(), '-o', pathWithoutExtension ] |
138 | options = options.concat([ '--ffmpeg-location', process.env.FFMPEG_PATH ]) | 139 | options = this.wrapWithProxyOptions(options) |
139 | } | ||
140 | 140 | ||
141 | logger.debug('YoutubeDL options for %s.', url, { options }) | 141 | if (process.env.FFMPEG_PATH) { |
142 | options = options.concat([ '--ffmpeg-location', process.env.FFMPEG_PATH ]) | ||
143 | } | ||
142 | 144 | ||
143 | return new Promise<string>((res, rej) => { | 145 | logger.debug('YoutubeDL options for %s.', this.url, { options }) |
144 | safeGetYoutubeDL() | ||
145 | .then(youtubeDL => { | ||
146 | youtubeDL.exec(url, options, processOptions, async err => { | ||
147 | clearTimeout(timer) | ||
148 | 146 | ||
149 | try { | 147 | return new Promise<string>((res, rej) => { |
150 | // If youtube-dl did not guess an extension for our file, just use .mp4 as default | 148 | YoutubeDL.safeGetYoutubeDL() |
151 | if (await pathExists(pathWithoutExtension)) { | 149 | .then(youtubeDL => { |
152 | await move(pathWithoutExtension, pathWithoutExtension + '.mp4') | 150 | youtubeDL.exec(this.url, options, processOptions, async err => { |
153 | } | 151 | clearTimeout(timer) |
152 | |||
153 | try { | ||
154 | // If youtube-dl did not guess an extension for our file, just use .mp4 as default | ||
155 | if (await pathExists(pathWithoutExtension)) { | ||
156 | await move(pathWithoutExtension, pathWithoutExtension + '.mp4') | ||
157 | } | ||
154 | 158 | ||
155 | const path = await guessVideoPathWithExtension(pathWithoutExtension, fileExt) | 159 | const path = await this.guessVideoPathWithExtension(pathWithoutExtension, fileExt) |
156 | 160 | ||
157 | if (err) { | 161 | if (err) { |
158 | remove(path) | 162 | remove(path) |
159 | .catch(err => logger.error('Cannot delete path on YoutubeDL error.', { err })) | 163 | .catch(err => logger.error('Cannot delete path on YoutubeDL error.', { err })) |
160 | 164 | ||
165 | return rej(err) | ||
166 | } | ||
167 | |||
168 | return res(path) | ||
169 | } catch (err) { | ||
161 | return rej(err) | 170 | return rej(err) |
162 | } | 171 | } |
163 | 172 | }) | |
164 | return res(path) | 173 | |
165 | } catch (err) { | 174 | timer = setTimeout(() => { |
166 | return rej(err) | 175 | const err = new Error('YoutubeDL download timeout.') |
167 | } | 176 | |
177 | this.guessVideoPathWithExtension(pathWithoutExtension, fileExt) | ||
178 | .then(path => remove(path)) | ||
179 | .finally(() => rej(err)) | ||
180 | .catch(err => { | ||
181 | logger.error('Cannot remove file in youtubeDL timeout.', { err }) | ||
182 | return rej(err) | ||
183 | }) | ||
184 | }, timeout) | ||
168 | }) | 185 | }) |
186 | .catch(err => rej(err)) | ||
187 | }) | ||
188 | } | ||
169 | 189 | ||
170 | timer = setTimeout(() => { | 190 | buildOriginallyPublishedAt (obj: any) { |
171 | const err = new Error('YoutubeDL download timeout.') | 191 | let originallyPublishedAt: Date = null |
172 | 192 | ||
173 | guessVideoPathWithExtension(pathWithoutExtension, fileExt) | 193 | const uploadDateMatcher = /^(\d{4})(\d{2})(\d{2})$/.exec(obj.upload_date) |
174 | .then(path => remove(path)) | 194 | if (uploadDateMatcher) { |
175 | .finally(() => rej(err)) | 195 | originallyPublishedAt = new Date() |
176 | .catch(err => { | 196 | originallyPublishedAt.setHours(0, 0, 0, 0) |
177 | logger.error('Cannot remove file in youtubeDL timeout.', { err }) | ||
178 | return rej(err) | ||
179 | }) | ||
180 | }, timeout) | ||
181 | }) | ||
182 | .catch(err => rej(err)) | ||
183 | }) | ||
184 | } | ||
185 | |||
186 | // Thanks: https://github.com/przemyslawpluta/node-youtube-dl/blob/master/lib/downloader.js | ||
187 | // We rewrote it to avoid sync calls | ||
188 | async function updateYoutubeDLBinary () { | ||
189 | logger.info('Updating youtubeDL binary.') | ||
190 | 197 | ||
191 | const binDirectory = join(root(), 'node_modules', 'youtube-dl', 'bin') | 198 | const year = parseInt(uploadDateMatcher[1], 10) |
192 | const bin = join(binDirectory, 'youtube-dl') | 199 | // Month starts from 0 |
193 | const detailsPath = join(binDirectory, 'details') | 200 | const month = parseInt(uploadDateMatcher[2], 10) - 1 |
194 | const url = process.env.YOUTUBE_DL_DOWNLOAD_HOST || 'https://yt-dl.org/downloads/latest/youtube-dl' | 201 | const day = parseInt(uploadDateMatcher[3], 10) |
195 | 202 | ||
196 | await ensureDir(binDirectory) | 203 | originallyPublishedAt.setFullYear(year, month, day) |
204 | } | ||
197 | 205 | ||
198 | try { | 206 | return originallyPublishedAt |
199 | const result = await got(url, { followRedirect: false }) | 207 | } |
200 | 208 | ||
201 | if (result.statusCode !== HttpStatusCode.FOUND_302) { | 209 | private async guessVideoPathWithExtension (tmpPath: string, sourceExt: string) { |
202 | logger.error('youtube-dl update error: did not get redirect for the latest version link. Status %d', result.statusCode) | 210 | if (!isVideoFileExtnameValid(sourceExt)) { |
203 | return | 211 | throw new Error('Invalid video extension ' + sourceExt) |
204 | } | 212 | } |
205 | 213 | ||
206 | const newUrl = result.headers.location | 214 | const extensions = [ sourceExt, '.mp4', '.mkv', '.webm' ] |
207 | const newVersion = /yt-dl\.org\/downloads\/(\d{4}\.\d\d\.\d\d(\.\d)?)\/youtube-dl/.exec(newUrl)[1] | ||
208 | 215 | ||
209 | const downloadFileStream = got.stream(newUrl) | 216 | for (const extension of extensions) { |
210 | const writeStream = createWriteStream(bin, { mode: 493 }) | 217 | const path = tmpPath + extension |
211 | 218 | ||
212 | await pipelinePromise( | 219 | if (await pathExists(path)) return path |
213 | downloadFileStream, | 220 | } |
214 | writeStream | ||
215 | ) | ||
216 | |||
217 | const details = JSON.stringify({ version: newVersion, path: bin, exec: 'youtube-dl' }) | ||
218 | await writeFile(detailsPath, details, { encoding: 'utf8' }) | ||
219 | 221 | ||
220 | logger.info('youtube-dl updated to version %s.', newVersion) | 222 | throw new Error('Cannot guess path of ' + tmpPath) |
221 | } catch (err) { | ||
222 | logger.error('Cannot update youtube-dl.', { err }) | ||
223 | } | 223 | } |
224 | } | ||
225 | 224 | ||
226 | async function safeGetYoutubeDL () { | 225 | private normalizeObject (obj: any) { |
227 | let youtubeDL | 226 | const newObj: any = {} |
228 | 227 | ||
229 | try { | 228 | for (const key of Object.keys(obj)) { |
230 | youtubeDL = require('youtube-dl') | 229 | // Deprecated key |
231 | } catch (e) { | 230 | if (key === 'resolution') continue |
232 | // Download binary | ||
233 | await updateYoutubeDLBinary() | ||
234 | youtubeDL = require('youtube-dl') | ||
235 | } | ||
236 | 231 | ||
237 | return youtubeDL | 232 | const value = obj[key] |
238 | } | ||
239 | 233 | ||
240 | function buildOriginallyPublishedAt (obj: any) { | 234 | if (typeof value === 'string') { |
241 | let originallyPublishedAt: Date = null | 235 | newObj[key] = value.normalize() |
236 | } else { | ||
237 | newObj[key] = value | ||
238 | } | ||
239 | } | ||
242 | 240 | ||
243 | const uploadDateMatcher = /^(\d{4})(\d{2})(\d{2})$/.exec(obj.upload_date) | 241 | return newObj |
244 | if (uploadDateMatcher) { | 242 | } |
245 | originallyPublishedAt = new Date() | ||
246 | originallyPublishedAt.setHours(0, 0, 0, 0) | ||
247 | 243 | ||
248 | const year = parseInt(uploadDateMatcher[1], 10) | 244 | private buildVideoInfo (obj: any): YoutubeDLInfo { |
249 | // Month starts from 0 | 245 | return { |
250 | const month = parseInt(uploadDateMatcher[2], 10) - 1 | 246 | name: this.titleTruncation(obj.title), |
251 | const day = parseInt(uploadDateMatcher[3], 10) | 247 | description: this.descriptionTruncation(obj.description), |
248 | category: this.getCategory(obj.categories), | ||
249 | licence: this.getLicence(obj.license), | ||
250 | language: this.getLanguage(obj.language), | ||
251 | nsfw: this.isNSFW(obj), | ||
252 | tags: this.getTags(obj.tags), | ||
253 | thumbnailUrl: obj.thumbnail || undefined, | ||
254 | originallyPublishedAt: this.buildOriginallyPublishedAt(obj), | ||
255 | ext: obj.ext | ||
256 | } | ||
257 | } | ||
252 | 258 | ||
253 | originallyPublishedAt.setFullYear(year, month, day) | 259 | private titleTruncation (title: string) { |
260 | return peertubeTruncate(title, { | ||
261 | length: CONSTRAINTS_FIELDS.VIDEOS.NAME.max, | ||
262 | separator: /,? +/, | ||
263 | omission: ' […]' | ||
264 | }) | ||
254 | } | 265 | } |
255 | 266 | ||
256 | return originallyPublishedAt | 267 | private descriptionTruncation (description: string) { |
257 | } | 268 | if (!description || description.length < CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.min) return undefined |
258 | 269 | ||
259 | // --------------------------------------------------------------------------- | 270 | return peertubeTruncate(description, { |
271 | length: CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max, | ||
272 | separator: /,? +/, | ||
273 | omission: ' […]' | ||
274 | }) | ||
275 | } | ||
260 | 276 | ||
261 | export { | 277 | private isNSFW (info: any) { |
262 | updateYoutubeDLBinary, | 278 | return info.age_limit && info.age_limit >= 16 |
263 | getYoutubeDLVideoFormat, | 279 | } |
264 | downloadYoutubeDLVideo, | ||
265 | getYoutubeDLSubs, | ||
266 | getYoutubeDLInfo, | ||
267 | safeGetYoutubeDL, | ||
268 | buildOriginallyPublishedAt | ||
269 | } | ||
270 | 280 | ||
271 | // --------------------------------------------------------------------------- | 281 | private getTags (tags: any) { |
282 | if (Array.isArray(tags) === false) return [] | ||
272 | 283 | ||
273 | async function guessVideoPathWithExtension (tmpPath: string, sourceExt: string) { | 284 | return tags |
274 | if (!isVideoFileExtnameValid(sourceExt)) { | 285 | .filter(t => t.length < CONSTRAINTS_FIELDS.VIDEOS.TAG.max && t.length > CONSTRAINTS_FIELDS.VIDEOS.TAG.min) |
275 | throw new Error('Invalid video extension ' + sourceExt) | 286 | .map(t => t.normalize()) |
287 | .slice(0, 5) | ||
276 | } | 288 | } |
277 | 289 | ||
278 | const extensions = [ sourceExt, '.mp4', '.mkv', '.webm' ] | 290 | private getLicence (licence: string) { |
291 | if (!licence) return undefined | ||
279 | 292 | ||
280 | for (const extension of extensions) { | 293 | if (licence.includes('Creative Commons Attribution')) return 1 |
281 | const path = tmpPath + extension | ||
282 | 294 | ||
283 | if (await pathExists(path)) return path | 295 | for (const key of Object.keys(VIDEO_LICENCES)) { |
284 | } | 296 | const peertubeLicence = VIDEO_LICENCES[key] |
297 | if (peertubeLicence.toLowerCase() === licence.toLowerCase()) return parseInt(key, 10) | ||
298 | } | ||
285 | 299 | ||
286 | throw new Error('Cannot guess path of ' + tmpPath) | 300 | return undefined |
287 | } | 301 | } |
288 | 302 | ||
289 | function normalizeObject (obj: any) { | 303 | private getCategory (categories: string[]) { |
290 | const newObj: any = {} | 304 | if (!categories) return undefined |
291 | 305 | ||
292 | for (const key of Object.keys(obj)) { | 306 | const categoryString = categories[0] |
293 | // Deprecated key | 307 | if (!categoryString || typeof categoryString !== 'string') return undefined |
294 | if (key === 'resolution') continue | ||
295 | 308 | ||
296 | const value = obj[key] | 309 | if (categoryString === 'News & Politics') return 11 |
297 | 310 | ||
298 | if (typeof value === 'string') { | 311 | for (const key of Object.keys(VIDEO_CATEGORIES)) { |
299 | newObj[key] = value.normalize() | 312 | const category = VIDEO_CATEGORIES[key] |
300 | } else { | 313 | if (categoryString.toLowerCase() === category.toLowerCase()) return parseInt(key, 10) |
301 | newObj[key] = value | ||
302 | } | 314 | } |
303 | } | ||
304 | 315 | ||
305 | return newObj | 316 | return undefined |
306 | } | ||
307 | |||
308 | function buildVideoInfo (obj: any): YoutubeDLInfo { | ||
309 | return { | ||
310 | name: titleTruncation(obj.title), | ||
311 | description: descriptionTruncation(obj.description), | ||
312 | category: getCategory(obj.categories), | ||
313 | licence: getLicence(obj.license), | ||
314 | language: getLanguage(obj.language), | ||
315 | nsfw: isNSFW(obj), | ||
316 | tags: getTags(obj.tags), | ||
317 | thumbnailUrl: obj.thumbnail || undefined, | ||
318 | originallyPublishedAt: buildOriginallyPublishedAt(obj), | ||
319 | ext: obj.ext | ||
320 | } | 317 | } |
321 | } | ||
322 | 318 | ||
323 | function titleTruncation (title: string) { | 319 | private getLanguage (language: string) { |
324 | return peertubeTruncate(title, { | 320 | return VIDEO_LANGUAGES[language] ? language : undefined |
325 | length: CONSTRAINTS_FIELDS.VIDEOS.NAME.max, | 321 | } |
326 | separator: /,? +/, | ||
327 | omission: ' […]' | ||
328 | }) | ||
329 | } | ||
330 | 322 | ||
331 | function descriptionTruncation (description: string) { | 323 | private wrapWithProxyOptions (options: string[]) { |
332 | if (!description || description.length < CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.min) return undefined | 324 | if (CONFIG.IMPORT.VIDEOS.HTTP.PROXY.ENABLED) { |
325 | logger.debug('Using proxy for YoutubeDL') | ||
333 | 326 | ||
334 | return peertubeTruncate(description, { | 327 | return [ '--proxy', CONFIG.IMPORT.VIDEOS.HTTP.PROXY.URL ].concat(options) |
335 | length: CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max, | 328 | } |
336 | separator: /,? +/, | ||
337 | omission: ' […]' | ||
338 | }) | ||
339 | } | ||
340 | 329 | ||
341 | function isNSFW (info: any) { | 330 | return options |
342 | return info.age_limit && info.age_limit >= 16 | 331 | } |
343 | } | ||
344 | 332 | ||
345 | function getTags (tags: any) { | 333 | // Thanks: https://github.com/przemyslawpluta/node-youtube-dl/blob/master/lib/downloader.js |
346 | if (Array.isArray(tags) === false) return [] | 334 | // We rewrote it to avoid sync calls |
335 | static async updateYoutubeDLBinary () { | ||
336 | logger.info('Updating youtubeDL binary.') | ||
347 | 337 | ||
348 | return tags | 338 | const binDirectory = join(root(), 'node_modules', 'youtube-dl', 'bin') |
349 | .filter(t => t.length < CONSTRAINTS_FIELDS.VIDEOS.TAG.max && t.length > CONSTRAINTS_FIELDS.VIDEOS.TAG.min) | 339 | const bin = join(binDirectory, 'youtube-dl') |
350 | .map(t => t.normalize()) | 340 | const detailsPath = join(binDirectory, 'details') |
351 | .slice(0, 5) | 341 | const url = process.env.YOUTUBE_DL_DOWNLOAD_HOST || 'https://yt-dl.org/downloads/latest/youtube-dl' |
352 | } | ||
353 | 342 | ||
354 | function getLicence (licence: string) { | 343 | await ensureDir(binDirectory) |
355 | if (!licence) return undefined | ||
356 | 344 | ||
357 | if (licence.includes('Creative Commons Attribution')) return 1 | 345 | try { |
346 | const result = await got(url, { followRedirect: false }) | ||
358 | 347 | ||
359 | for (const key of Object.keys(VIDEO_LICENCES)) { | 348 | if (result.statusCode !== HttpStatusCode.FOUND_302) { |
360 | const peertubeLicence = VIDEO_LICENCES[key] | 349 | logger.error('youtube-dl update error: did not get redirect for the latest version link. Status %d', result.statusCode) |
361 | if (peertubeLicence.toLowerCase() === licence.toLowerCase()) return parseInt(key, 10) | 350 | return |
362 | } | 351 | } |
363 | 352 | ||
364 | return undefined | 353 | const newUrl = result.headers.location |
365 | } | 354 | const newVersion = /yt-dl\.org\/downloads\/(\d{4}\.\d\d\.\d\d(\.\d)?)\/youtube-dl/.exec(newUrl)[1] |
366 | 355 | ||
367 | function getCategory (categories: string[]) { | 356 | const downloadFileStream = got.stream(newUrl) |
368 | if (!categories) return undefined | 357 | const writeStream = createWriteStream(bin, { mode: 493 }) |
369 | 358 | ||
370 | const categoryString = categories[0] | 359 | await pipelinePromise( |
371 | if (!categoryString || typeof categoryString !== 'string') return undefined | 360 | downloadFileStream, |
361 | writeStream | ||
362 | ) | ||
372 | 363 | ||
373 | if (categoryString === 'News & Politics') return 11 | 364 | const details = JSON.stringify({ version: newVersion, path: bin, exec: 'youtube-dl' }) |
365 | await writeFile(detailsPath, details, { encoding: 'utf8' }) | ||
374 | 366 | ||
375 | for (const key of Object.keys(VIDEO_CATEGORIES)) { | 367 | logger.info('youtube-dl updated to version %s.', newVersion) |
376 | const category = VIDEO_CATEGORIES[key] | 368 | } catch (err) { |
377 | if (categoryString.toLowerCase() === category.toLowerCase()) return parseInt(key, 10) | 369 | logger.error('Cannot update youtube-dl.', { err }) |
370 | } | ||
378 | } | 371 | } |
379 | 372 | ||
380 | return undefined | 373 | static async safeGetYoutubeDL () { |
381 | } | 374 | let youtubeDL |
382 | |||
383 | function getLanguage (language: string) { | ||
384 | return VIDEO_LANGUAGES[language] ? language : undefined | ||
385 | } | ||
386 | 375 | ||
387 | function wrapWithProxyOptions (options: string[]) { | 376 | try { |
388 | if (CONFIG.IMPORT.VIDEOS.HTTP.PROXY.ENABLED) { | 377 | youtubeDL = require('youtube-dl') |
389 | logger.debug('Using proxy for YoutubeDL') | 378 | } catch (e) { |
379 | // Download binary | ||
380 | await this.updateYoutubeDLBinary() | ||
381 | youtubeDL = require('youtube-dl') | ||
382 | } | ||
390 | 383 | ||
391 | return [ '--proxy', CONFIG.IMPORT.VIDEOS.HTTP.PROXY.URL ].concat(options) | 384 | return youtubeDL |
392 | } | 385 | } |
386 | } | ||
387 | |||
388 | // --------------------------------------------------------------------------- | ||
393 | 389 | ||
394 | return options | 390 | export { |
391 | YoutubeDL | ||
395 | } | 392 | } |
diff --git a/server/initializers/checker-after-init.ts b/server/initializers/checker-after-init.ts index 5fd1af82f..d75c0af04 100644 --- a/server/initializers/checker-after-init.ts +++ b/server/initializers/checker-after-init.ts | |||
@@ -7,7 +7,7 @@ import { RecentlyAddedStrategy } from '../../shared/models/redundancy' | |||
7 | import { isProdInstance, isTestInstance, parseSemVersion } from '../helpers/core-utils' | 7 | import { isProdInstance, isTestInstance, parseSemVersion } from '../helpers/core-utils' |
8 | import { isArray } from '../helpers/custom-validators/misc' | 8 | import { isArray } from '../helpers/custom-validators/misc' |
9 | import { logger } from '../helpers/logger' | 9 | import { logger } from '../helpers/logger' |
10 | import { UserModel } from '../models/account/user' | 10 | import { UserModel } from '../models/user/user' |
11 | import { ApplicationModel, getServerActor } from '../models/application/application' | 11 | import { ApplicationModel, getServerActor } from '../models/application/application' |
12 | import { OAuthClientModel } from '../models/oauth/oauth-client' | 12 | import { OAuthClientModel } from '../models/oauth/oauth-client' |
13 | import { CONFIG, isEmailEnabled } from './config' | 13 | import { CONFIG, isEmailEnabled } from './config' |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 6f388420e..4cf7dcf0a 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -702,7 +702,8 @@ const CUSTOM_HTML_TAG_COMMENTS = { | |||
702 | TITLE: '<!-- title tag -->', | 702 | TITLE: '<!-- title tag -->', |
703 | DESCRIPTION: '<!-- description tag -->', | 703 | DESCRIPTION: '<!-- description tag -->', |
704 | CUSTOM_CSS: '<!-- custom css tag -->', | 704 | CUSTOM_CSS: '<!-- custom css tag -->', |
705 | META_TAGS: '<!-- meta tags -->' | 705 | META_TAGS: '<!-- meta tags -->', |
706 | SERVER_CONFIG: '<!-- server config -->' | ||
706 | } | 707 | } |
707 | 708 | ||
708 | // --------------------------------------------------------------------------- | 709 | // --------------------------------------------------------------------------- |
diff --git a/server/initializers/database.ts b/server/initializers/database.ts index edf12bc41..75a13ec8b 100644 --- a/server/initializers/database.ts +++ b/server/initializers/database.ts | |||
@@ -2,6 +2,9 @@ import { QueryTypes, Transaction } from 'sequelize' | |||
2 | import { Sequelize as SequelizeTypescript } from 'sequelize-typescript' | 2 | import { Sequelize as SequelizeTypescript } from 'sequelize-typescript' |
3 | import { TrackerModel } from '@server/models/server/tracker' | 3 | import { TrackerModel } from '@server/models/server/tracker' |
4 | import { VideoTrackerModel } from '@server/models/server/video-tracker' | 4 | import { VideoTrackerModel } from '@server/models/server/video-tracker' |
5 | import { UserModel } from '@server/models/user/user' | ||
6 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
7 | import { UserVideoHistoryModel } from '@server/models/user/user-video-history' | ||
5 | import { isTestInstance } from '../helpers/core-utils' | 8 | import { isTestInstance } from '../helpers/core-utils' |
6 | import { logger } from '../helpers/logger' | 9 | import { logger } from '../helpers/logger' |
7 | import { AbuseModel } from '../models/abuse/abuse' | 10 | import { AbuseModel } from '../models/abuse/abuse' |
@@ -11,13 +14,9 @@ import { VideoCommentAbuseModel } from '../models/abuse/video-comment-abuse' | |||
11 | import { AccountModel } from '../models/account/account' | 14 | import { AccountModel } from '../models/account/account' |
12 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | 15 | import { AccountBlocklistModel } from '../models/account/account-blocklist' |
13 | import { AccountVideoRateModel } from '../models/account/account-video-rate' | 16 | import { AccountVideoRateModel } from '../models/account/account-video-rate' |
14 | import { ActorImageModel } from '../models/account/actor-image' | 17 | import { ActorModel } from '../models/actor/actor' |
15 | import { UserModel } from '../models/account/user' | 18 | import { ActorFollowModel } from '../models/actor/actor-follow' |
16 | import { UserNotificationModel } from '../models/account/user-notification' | 19 | import { ActorImageModel } from '../models/actor/actor-image' |
17 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' | ||
18 | import { UserVideoHistoryModel } from '../models/account/user-video-history' | ||
19 | import { ActorModel } from '../models/activitypub/actor' | ||
20 | import { ActorFollowModel } from '../models/activitypub/actor-follow' | ||
21 | import { ApplicationModel } from '../models/application/application' | 20 | import { ApplicationModel } from '../models/application/application' |
22 | import { OAuthClientModel } from '../models/oauth/oauth-client' | 21 | import { OAuthClientModel } from '../models/oauth/oauth-client' |
23 | import { OAuthTokenModel } from '../models/oauth/oauth-token' | 22 | import { OAuthTokenModel } from '../models/oauth/oauth-token' |
@@ -25,6 +24,7 @@ import { VideoRedundancyModel } from '../models/redundancy/video-redundancy' | |||
25 | import { PluginModel } from '../models/server/plugin' | 24 | import { PluginModel } from '../models/server/plugin' |
26 | import { ServerModel } from '../models/server/server' | 25 | import { ServerModel } from '../models/server/server' |
27 | import { ServerBlocklistModel } from '../models/server/server-blocklist' | 26 | import { ServerBlocklistModel } from '../models/server/server-blocklist' |
27 | import { UserNotificationSettingModel } from '../models/user/user-notification-setting' | ||
28 | import { ScheduleVideoUpdateModel } from '../models/video/schedule-video-update' | 28 | import { ScheduleVideoUpdateModel } from '../models/video/schedule-video-update' |
29 | import { TagModel } from '../models/video/tag' | 29 | import { TagModel } from '../models/video/tag' |
30 | import { ThumbnailModel } from '../models/video/thumbnail' | 30 | import { ThumbnailModel } from '../models/video/thumbnail' |
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts index 8dcff64e2..676f88653 100644 --- a/server/initializers/installer.ts +++ b/server/initializers/installer.ts | |||
@@ -2,7 +2,7 @@ import * as passwordGenerator from 'password-generator' | |||
2 | import { UserRole } from '../../shared' | 2 | import { UserRole } from '../../shared' |
3 | import { logger } from '../helpers/logger' | 3 | import { logger } from '../helpers/logger' |
4 | import { createApplicationActor, createUserAccountAndChannelAndPlaylist } from '../lib/user' | 4 | import { createApplicationActor, createUserAccountAndChannelAndPlaylist } from '../lib/user' |
5 | import { UserModel } from '../models/account/user' | 5 | import { UserModel } from '../models/user/user' |
6 | import { ApplicationModel } from '../models/application/application' | 6 | import { ApplicationModel } from '../models/application/application' |
7 | import { OAuthClientModel } from '../models/oauth/oauth-client' | 7 | import { OAuthClientModel } from '../models/oauth/oauth-client' |
8 | import { applicationExist, clientsExist, usersExist } from './checker-after-init' | 8 | import { applicationExist, clientsExist, usersExist } from './checker-after-init' |
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index 5fe7381c9..1bcee7ef9 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -20,8 +20,8 @@ import { getUrlFromWebfinger } from '../../helpers/webfinger' | |||
20 | import { MIMETYPES, WEBSERVER } from '../../initializers/constants' | 20 | import { MIMETYPES, WEBSERVER } from '../../initializers/constants' |
21 | import { sequelizeTypescript } from '../../initializers/database' | 21 | import { sequelizeTypescript } from '../../initializers/database' |
22 | import { AccountModel } from '../../models/account/account' | 22 | import { AccountModel } from '../../models/account/account' |
23 | import { ActorImageModel } from '../../models/account/actor-image' | 23 | import { ActorModel } from '../../models/actor/actor' |
24 | import { ActorModel } from '../../models/activitypub/actor' | 24 | import { ActorImageModel } from '../../models/actor/actor-image' |
25 | import { ServerModel } from '../../models/server/server' | 25 | import { ServerModel } from '../../models/server/server' |
26 | import { VideoChannelModel } from '../../models/video/video-channel' | 26 | import { VideoChannelModel } from '../../models/video/video-channel' |
27 | import { | 27 | import { |
@@ -132,12 +132,11 @@ async function getOrCreateActorAndServerAndModel ( | |||
132 | return actorRefreshed | 132 | return actorRefreshed |
133 | } | 133 | } |
134 | 134 | ||
135 | function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string, uuid?: string) { | 135 | function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string) { |
136 | return new ActorModel({ | 136 | return new ActorModel({ |
137 | type, | 137 | type, |
138 | url, | 138 | url, |
139 | preferredUsername, | 139 | preferredUsername, |
140 | uuid, | ||
141 | publicKey: null, | 140 | publicKey: null, |
142 | privateKey: null, | 141 | privateKey: null, |
143 | followersCount: 0, | 142 | followersCount: 0, |
diff --git a/server/lib/activitypub/audience.ts b/server/lib/activitypub/audience.ts index 2986714d3..d0558f191 100644 --- a/server/lib/activitypub/audience.ts +++ b/server/lib/activitypub/audience.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience } from '../../../shared/models/activitypub' | 2 | import { ActivityAudience } from '../../../shared/models/activitypub' |
3 | import { ACTIVITY_PUB } from '../../initializers/constants' | 3 | import { ACTIVITY_PUB } from '../../initializers/constants' |
4 | import { ActorModel } from '../../models/activitypub/actor' | 4 | import { ActorModel } from '../../models/actor/actor' |
5 | import { VideoModel } from '../../models/video/video' | 5 | import { VideoModel } from '../../models/video/video' |
6 | import { VideoShareModel } from '../../models/video/video-share' | 6 | import { VideoShareModel } from '../../models/video/video-share' |
7 | import { MActorFollowersUrl, MActorLight, MActorUrl, MCommentOwner, MCommentOwnerVideo, MVideoId } from '../../types/models' | 7 | import { MActorFollowersUrl, MActorLight, MActorUrl, MCommentOwner, MCommentOwnerVideo, MVideoId } from '../../types/models' |
diff --git a/server/lib/activitypub/process/process-accept.ts b/server/lib/activitypub/process/process-accept.ts index 1799829f8..8ad470cf4 100644 --- a/server/lib/activitypub/process/process-accept.ts +++ b/server/lib/activitypub/process/process-accept.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import { ActivityAccept } from '../../../../shared/models/activitypub' | 1 | import { ActivityAccept } from '../../../../shared/models/activitypub' |
2 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 2 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
3 | import { addFetchOutboxJob } from '../actor' | ||
4 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 3 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
5 | import { MActorDefault, MActorSignature } from '../../../types/models' | 4 | import { MActorDefault, MActorSignature } from '../../../types/models' |
5 | import { addFetchOutboxJob } from '../actor' | ||
6 | 6 | ||
7 | async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) { | 7 | async function processAcceptActivity (options: APProcessorOptions<ActivityAccept>) { |
8 | const { byActor: targetActor, inboxActor } = options | 8 | const { byActor: targetActor, inboxActor } = options |
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index 88a968318..20214246c 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts | |||
@@ -2,7 +2,7 @@ import { ActivityDelete } from '../../../../shared/models/activitypub' | |||
2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { sequelizeTypescript } from '../../../initializers/database' | 4 | import { sequelizeTypescript } from '../../../initializers/database' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | 5 | import { ActorModel } from '../../../models/actor/actor' |
6 | import { VideoModel } from '../../../models/video/video' | 6 | import { VideoModel } from '../../../models/video/video' |
7 | import { VideoCommentModel } from '../../../models/video/video-comment' | 7 | import { VideoCommentModel } from '../../../models/video/video-comment' |
8 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | 8 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' |
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index 38d684512..9009c6469 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts | |||
@@ -1,17 +1,17 @@ | |||
1 | import { getServerActor } from '@server/models/application/application' | ||
1 | import { ActivityFollow } from '../../../../shared/models/activitypub' | 2 | import { ActivityFollow } from '../../../../shared/models/activitypub' |
3 | import { getAPId } from '../../../helpers/activitypub' | ||
2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
3 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
4 | import { sequelizeTypescript } from '../../../initializers/database' | ||
5 | import { ActorModel } from '../../../models/activitypub/actor' | ||
6 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
7 | import { sendAccept, sendReject } from '../send' | ||
8 | import { Notifier } from '../../notifier' | ||
9 | import { getAPId } from '../../../helpers/activitypub' | ||
10 | import { CONFIG } from '../../../initializers/config' | 6 | import { CONFIG } from '../../../initializers/config' |
7 | import { sequelizeTypescript } from '../../../initializers/database' | ||
8 | import { ActorModel } from '../../../models/actor/actor' | ||
9 | import { ActorFollowModel } from '../../../models/actor/actor-follow' | ||
11 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 10 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
12 | import { MActorFollowActors, MActorSignature } from '../../../types/models' | 11 | import { MActorFollowActors, MActorSignature } from '../../../types/models' |
12 | import { Notifier } from '../../notifier' | ||
13 | import { autoFollowBackIfNeeded } from '../follow' | 13 | import { autoFollowBackIfNeeded } from '../follow' |
14 | import { getServerActor } from '@server/models/application/application' | 14 | import { sendAccept, sendReject } from '../send' |
15 | 15 | ||
16 | async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { | 16 | async function processFollowActivity (options: APProcessorOptions<ActivityFollow>) { |
17 | const { activity, byActor } = options | 17 | const { activity, byActor } = options |
diff --git a/server/lib/activitypub/process/process-reject.ts b/server/lib/activitypub/process/process-reject.ts index 03b669fd9..7f7ab305f 100644 --- a/server/lib/activitypub/process/process-reject.ts +++ b/server/lib/activitypub/process/process-reject.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { ActivityReject } from '../../../../shared/models/activitypub/activity' | 1 | import { ActivityReject } from '../../../../shared/models/activitypub/activity' |
2 | import { sequelizeTypescript } from '../../../initializers/database' | 2 | import { sequelizeTypescript } from '../../../initializers/database' |
3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 3 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
4 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 4 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
5 | import { MActor } from '../../../types/models' | 5 | import { MActor } from '../../../types/models' |
6 | 6 | ||
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts index e520c2f0d..9f031b528 100644 --- a/server/lib/activitypub/process/process-undo.ts +++ b/server/lib/activitypub/process/process-undo.ts | |||
@@ -4,8 +4,8 @@ import { retryTransactionWrapper } from '../../../helpers/database-utils' | |||
4 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
5 | import { sequelizeTypescript } from '../../../initializers/database' | 5 | import { sequelizeTypescript } from '../../../initializers/database' |
6 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 6 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
7 | import { ActorModel } from '../../../models/activitypub/actor' | 7 | import { ActorModel } from '../../../models/actor/actor' |
8 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 8 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
9 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | 9 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' |
10 | import { VideoShareModel } from '../../../models/video/video-share' | 10 | import { VideoShareModel } from '../../../models/video/video-share' |
11 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 11 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index 6df9b93b2..6cd9d0fba 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -1,23 +1,23 @@ | |||
1 | import { isRedundancyAccepted } from '@server/lib/redundancy' | ||
2 | import { ActorImageType } from '@shared/models' | ||
1 | import { ActivityUpdate, CacheFileObject, VideoObject } from '../../../../shared/models/activitypub' | 3 | import { ActivityUpdate, CacheFileObject, VideoObject } from '../../../../shared/models/activitypub' |
2 | import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor' | 4 | import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor' |
5 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' | ||
6 | import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' | ||
7 | import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos' | ||
3 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils' | 8 | import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils' |
4 | import { logger } from '../../../helpers/logger' | 9 | import { logger } from '../../../helpers/logger' |
5 | import { sequelizeTypescript } from '../../../initializers/database' | 10 | import { sequelizeTypescript } from '../../../initializers/database' |
6 | import { AccountModel } from '../../../models/account/account' | 11 | import { AccountModel } from '../../../models/account/account' |
7 | import { ActorModel } from '../../../models/activitypub/actor' | 12 | import { ActorModel } from '../../../models/actor/actor' |
8 | import { VideoChannelModel } from '../../../models/video/video-channel' | 13 | import { VideoChannelModel } from '../../../models/video/video-channel' |
14 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | ||
15 | import { MAccountIdActor, MActorSignature } from '../../../types/models' | ||
9 | import { getImageInfoIfExists, updateActorImageInstance, updateActorInstance } from '../actor' | 16 | import { getImageInfoIfExists, updateActorImageInstance, updateActorInstance } from '../actor' |
10 | import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos' | ||
11 | import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos' | ||
12 | import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' | ||
13 | import { createOrUpdateCacheFile } from '../cache-file' | 17 | import { createOrUpdateCacheFile } from '../cache-file' |
14 | import { forwardVideoRelatedActivity } from '../send/utils' | ||
15 | import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object' | ||
16 | import { createOrUpdateVideoPlaylist } from '../playlist' | 18 | import { createOrUpdateVideoPlaylist } from '../playlist' |
17 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 19 | import { forwardVideoRelatedActivity } from '../send/utils' |
18 | import { MActorSignature, MAccountIdActor } from '../../../types/models' | 20 | import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos' |
19 | import { isRedundancyAccepted } from '@server/lib/redundancy' | ||
20 | import { ActorImageType } from '@shared/models' | ||
21 | 21 | ||
22 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { | 22 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { |
23 | const { activity, byActor } = options | 23 | const { activity, byActor } = options |
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts index e0acced18..d31f8c10b 100644 --- a/server/lib/activitypub/send/send-delete.ts +++ b/server/lib/activitypub/send/send-delete.ts | |||
@@ -2,7 +2,7 @@ import { Transaction } from 'sequelize' | |||
2 | import { getServerActor } from '@server/models/application/application' | 2 | import { getServerActor } from '@server/models/application/application' |
3 | import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' | 3 | import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' |
4 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | 5 | import { ActorModel } from '../../../models/actor/actor' |
6 | import { VideoCommentModel } from '../../../models/video/video-comment' | 6 | import { VideoCommentModel } from '../../../models/video/video-comment' |
7 | import { VideoShareModel } from '../../../models/video/video-share' | 7 | import { VideoShareModel } from '../../../models/video/video-share' |
8 | import { MActorUrl } from '../../../types/models' | 8 | import { MActorUrl } from '../../../types/models' |
diff --git a/server/lib/activitypub/send/send-view.ts b/server/lib/activitypub/send/send-view.ts index 9254dc7c5..153e94295 100644 --- a/server/lib/activitypub/send/send-view.ts +++ b/server/lib/activitypub/send/send-view.ts | |||
@@ -2,7 +2,7 @@ import { Transaction } from 'sequelize' | |||
2 | import { MActorAudience, MVideoImmutable, MVideoUrl } from '@server/types/models' | 2 | import { MActorAudience, MVideoImmutable, MVideoUrl } from '@server/types/models' |
3 | import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' | 3 | import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' |
4 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | 5 | import { ActorModel } from '../../../models/actor/actor' |
6 | import { audiencify, getAudience } from '../audience' | 6 | import { audiencify, getAudience } from '../audience' |
7 | import { getLocalVideoViewActivityPubUrl } from '../url' | 7 | import { getLocalVideoViewActivityPubUrl } from '../url' |
8 | import { sendVideoRelatedActivity } from './utils' | 8 | import { sendVideoRelatedActivity } from './utils' |
diff --git a/server/lib/activitypub/send/utils.ts b/server/lib/activitypub/send/utils.ts index 85a9f009d..db0e91b71 100644 --- a/server/lib/activitypub/send/utils.ts +++ b/server/lib/activitypub/send/utils.ts | |||
@@ -1,14 +1,14 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { getServerActor } from '@server/models/application/application' | ||
3 | import { ContextType } from '@shared/models/activitypub/context' | ||
2 | import { Activity, ActivityAudience } from '../../../../shared/models/activitypub' | 4 | import { Activity, ActivityAudience } from '../../../../shared/models/activitypub' |
5 | import { afterCommitIfTransaction } from '../../../helpers/database-utils' | ||
3 | import { logger } from '../../../helpers/logger' | 6 | import { logger } from '../../../helpers/logger' |
4 | import { ActorModel } from '../../../models/activitypub/actor' | 7 | import { ActorModel } from '../../../models/actor/actor' |
5 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 8 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
9 | import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../types/models' | ||
6 | import { JobQueue } from '../../job-queue' | 10 | import { JobQueue } from '../../job-queue' |
7 | import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' | 11 | import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' |
8 | import { afterCommitIfTransaction } from '../../../helpers/database-utils' | ||
9 | import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../types/models' | ||
10 | import { getServerActor } from '@server/models/application/application' | ||
11 | import { ContextType } from '@shared/models/activitypub/context' | ||
12 | 12 | ||
13 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { | 13 | async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { |
14 | byActor: MActorLight | 14 | byActor: MActorLight |
diff --git a/server/lib/auth/oauth-model.ts b/server/lib/auth/oauth-model.ts index b9c69eb2d..ae728d080 100644 --- a/server/lib/auth/oauth-model.ts +++ b/server/lib/auth/oauth-model.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { AccessDeniedError } from 'oauth2-server' | 2 | import { AccessDeniedError } from 'oauth2-server' |
3 | import { PluginManager } from '@server/lib/plugins/plugin-manager' | 3 | import { PluginManager } from '@server/lib/plugins/plugin-manager' |
4 | import { ActorModel } from '@server/models/activitypub/actor' | 4 | import { ActorModel } from '@server/models/actor/actor' |
5 | import { MOAuthClient } from '@server/types/models' | 5 | import { MOAuthClient } from '@server/types/models' |
6 | import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token' | 6 | import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token' |
7 | import { MUser } from '@server/types/models/user/user' | 7 | import { MUser } from '@server/types/models/user/user' |
@@ -9,7 +9,7 @@ import { UserAdminFlag } from '@shared/models/users/user-flag.model' | |||
9 | import { UserRole } from '@shared/models/users/user-role' | 9 | import { UserRole } from '@shared/models/users/user-role' |
10 | import { logger } from '../../helpers/logger' | 10 | import { logger } from '../../helpers/logger' |
11 | import { CONFIG } from '../../initializers/config' | 11 | import { CONFIG } from '../../initializers/config' |
12 | import { UserModel } from '../../models/account/user' | 12 | import { UserModel } from '../../models/user/user' |
13 | import { OAuthClientModel } from '../../models/oauth/oauth-client' | 13 | import { OAuthClientModel } from '../../models/oauth/oauth-client' |
14 | import { OAuthTokenModel } from '../../models/oauth/oauth-token' | 14 | import { OAuthTokenModel } from '../../models/oauth/oauth-token' |
15 | import { createUserAccountAndChannelAndPlaylist } from '../user' | 15 | import { createUserAccountAndChannelAndPlaylist } from '../user' |
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index 203bd3893..85fdc8754 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts | |||
@@ -2,12 +2,14 @@ import * as express from 'express' | |||
2 | import { readFile } from 'fs-extra' | 2 | import { readFile } from 'fs-extra' |
3 | import { join } from 'path' | 3 | import { join } from 'path' |
4 | import validator from 'validator' | 4 | import validator from 'validator' |
5 | import { escapeHTML } from '@shared/core-utils/renderer' | ||
6 | import { HTMLServerConfig } from '@shared/models' | ||
5 | import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n' | 7 | import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/core-utils/i18n/i18n' |
6 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | 8 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' |
7 | import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos' | 9 | import { VideoPlaylistPrivacy, VideoPrivacy } from '../../shared/models/videos' |
8 | import { isTestInstance, sha256 } from '../helpers/core-utils' | 10 | import { isTestInstance, sha256 } from '../helpers/core-utils' |
9 | import { escapeHTML } from '@shared/core-utils/renderer' | ||
10 | import { logger } from '../helpers/logger' | 11 | import { logger } from '../helpers/logger' |
12 | import { mdToPlainText } from '../helpers/markdown' | ||
11 | import { CONFIG } from '../initializers/config' | 13 | import { CONFIG } from '../initializers/config' |
12 | import { | 14 | import { |
13 | ACCEPT_HEADERS, | 15 | ACCEPT_HEADERS, |
@@ -24,7 +26,7 @@ import { VideoChannelModel } from '../models/video/video-channel' | |||
24 | import { getActivityStreamDuration } from '../models/video/video-format-utils' | 26 | import { getActivityStreamDuration } from '../models/video/video-format-utils' |
25 | import { VideoPlaylistModel } from '../models/video/video-playlist' | 27 | import { VideoPlaylistModel } from '../models/video/video-playlist' |
26 | import { MAccountActor, MChannelActor } from '../types/models' | 28 | import { MAccountActor, MChannelActor } from '../types/models' |
27 | import { mdToPlainText } from '../helpers/markdown' | 29 | import { getHTMLServerConfig } from './config' |
28 | 30 | ||
29 | type Tags = { | 31 | type Tags = { |
30 | ogType: string | 32 | ogType: string |
@@ -209,11 +211,14 @@ class ClientHtml { | |||
209 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] | 211 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] |
210 | 212 | ||
211 | const buffer = await readFile(path) | 213 | const buffer = await readFile(path) |
214 | const serverConfig = await getHTMLServerConfig() | ||
212 | 215 | ||
213 | let html = buffer.toString() | 216 | let html = buffer.toString() |
214 | html = await ClientHtml.addAsyncPluginCSS(html) | 217 | html = await ClientHtml.addAsyncPluginCSS(html) |
215 | html = ClientHtml.addCustomCSS(html) | 218 | html = ClientHtml.addCustomCSS(html) |
216 | html = ClientHtml.addTitleTag(html) | 219 | html = ClientHtml.addTitleTag(html) |
220 | html = ClientHtml.addDescriptionTag(html) | ||
221 | html = ClientHtml.addServerConfig(html, serverConfig) | ||
217 | 222 | ||
218 | ClientHtml.htmlCache[path] = html | 223 | ClientHtml.htmlCache[path] = html |
219 | 224 | ||
@@ -275,6 +280,7 @@ class ClientHtml { | |||
275 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] | 280 | if (!isTestInstance() && ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] |
276 | 281 | ||
277 | const buffer = await readFile(path) | 282 | const buffer = await readFile(path) |
283 | const serverConfig = await getHTMLServerConfig() | ||
278 | 284 | ||
279 | let html = buffer.toString() | 285 | let html = buffer.toString() |
280 | 286 | ||
@@ -283,6 +289,7 @@ class ClientHtml { | |||
283 | html = ClientHtml.addFaviconContentHash(html) | 289 | html = ClientHtml.addFaviconContentHash(html) |
284 | html = ClientHtml.addLogoContentHash(html) | 290 | html = ClientHtml.addLogoContentHash(html) |
285 | html = ClientHtml.addCustomCSS(html) | 291 | html = ClientHtml.addCustomCSS(html) |
292 | html = ClientHtml.addServerConfig(html, serverConfig) | ||
286 | html = await ClientHtml.addAsyncPluginCSS(html) | 293 | html = await ClientHtml.addAsyncPluginCSS(html) |
287 | 294 | ||
288 | ClientHtml.htmlCache[path] = html | 295 | ClientHtml.htmlCache[path] = html |
@@ -355,6 +362,13 @@ class ClientHtml { | |||
355 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) | 362 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) |
356 | } | 363 | } |
357 | 364 | ||
365 | private static addServerConfig (htmlStringPage: string, serverConfig: HTMLServerConfig) { | ||
366 | const serverConfigString = JSON.stringify(serverConfig) | ||
367 | const configScriptTag = `<script type="application/javascript">window.PeerTubeServerConfig = '${serverConfigString}'</script>` | ||
368 | |||
369 | return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.SERVER_CONFIG, configScriptTag) | ||
370 | } | ||
371 | |||
358 | private static async addAsyncPluginCSS (htmlStringPage: string) { | 372 | private static async addAsyncPluginCSS (htmlStringPage: string) { |
359 | const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH) | 373 | const globalCSSContent = await readFile(PLUGIN_GLOBAL_CSS_PATH) |
360 | if (globalCSSContent.byteLength === 0) return htmlStringPage | 374 | if (globalCSSContent.byteLength === 0) return htmlStringPage |
diff --git a/server/lib/config.ts b/server/lib/config.ts index b4c4c9299..18d49f05a 100644 --- a/server/lib/config.ts +++ b/server/lib/config.ts | |||
@@ -2,18 +2,13 @@ import { isSignupAllowed, isSignupAllowedForCurrentIP } from '@server/helpers/si | |||
2 | import { getServerCommit } from '@server/helpers/utils' | 2 | import { getServerCommit } from '@server/helpers/utils' |
3 | import { CONFIG, isEmailEnabled } from '@server/initializers/config' | 3 | import { CONFIG, isEmailEnabled } from '@server/initializers/config' |
4 | import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '@server/initializers/constants' | 4 | import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '@server/initializers/constants' |
5 | import { RegisteredExternalAuthConfig, RegisteredIdAndPassAuthConfig, ServerConfig } from '@shared/models' | 5 | import { HTMLServerConfig, RegisteredExternalAuthConfig, RegisteredIdAndPassAuthConfig, ServerConfig } from '@shared/models' |
6 | import { Hooks } from './plugins/hooks' | 6 | import { Hooks } from './plugins/hooks' |
7 | import { PluginManager } from './plugins/plugin-manager' | 7 | import { PluginManager } from './plugins/plugin-manager' |
8 | import { getThemeOrDefault } from './plugins/theme-utils' | 8 | import { getThemeOrDefault } from './plugins/theme-utils' |
9 | import { getEnabledResolutions } from './video-transcoding' | 9 | import { VideoTranscodingProfilesManager } from './transcoding/video-transcoding-profiles' |
10 | import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' | ||
11 | |||
12 | let serverCommit: string | ||
13 | 10 | ||
14 | async function getServerConfig (ip?: string): Promise<ServerConfig> { | 11 | async function getServerConfig (ip?: string): Promise<ServerConfig> { |
15 | if (serverCommit === undefined) serverCommit = await getServerCommit() | ||
16 | |||
17 | const { allowed } = await Hooks.wrapPromiseFun( | 12 | const { allowed } = await Hooks.wrapPromiseFun( |
18 | isSignupAllowed, | 13 | isSignupAllowed, |
19 | { | 14 | { |
@@ -23,6 +18,23 @@ async function getServerConfig (ip?: string): Promise<ServerConfig> { | |||
23 | ) | 18 | ) |
24 | 19 | ||
25 | const allowedForCurrentIP = isSignupAllowedForCurrentIP(ip) | 20 | const allowedForCurrentIP = isSignupAllowedForCurrentIP(ip) |
21 | |||
22 | const signup = { | ||
23 | allowed, | ||
24 | allowedForCurrentIP, | ||
25 | requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION | ||
26 | } | ||
27 | |||
28 | const htmlConfig = await getHTMLServerConfig() | ||
29 | |||
30 | return { ...htmlConfig, signup } | ||
31 | } | ||
32 | |||
33 | // Config injected in HTML | ||
34 | let serverCommit: string | ||
35 | async function getHTMLServerConfig (): Promise<HTMLServerConfig> { | ||
36 | if (serverCommit === undefined) serverCommit = await getServerCommit() | ||
37 | |||
26 | const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME) | 38 | const defaultTheme = getThemeOrDefault(CONFIG.THEME.DEFAULT, DEFAULT_THEME_NAME) |
27 | 39 | ||
28 | return { | 40 | return { |
@@ -66,11 +78,6 @@ async function getServerConfig (ip?: string): Promise<ServerConfig> { | |||
66 | }, | 78 | }, |
67 | serverVersion: PEERTUBE_VERSION, | 79 | serverVersion: PEERTUBE_VERSION, |
68 | serverCommit, | 80 | serverCommit, |
69 | signup: { | ||
70 | allowed, | ||
71 | allowedForCurrentIP, | ||
72 | requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION | ||
73 | }, | ||
74 | transcoding: { | 81 | transcoding: { |
75 | hls: { | 82 | hls: { |
76 | enabled: CONFIG.TRANSCODING.HLS.ENABLED | 83 | enabled: CONFIG.TRANSCODING.HLS.ENABLED |
@@ -208,12 +215,24 @@ function getRegisteredPlugins () { | |||
208 | })) | 215 | })) |
209 | } | 216 | } |
210 | 217 | ||
218 | function getEnabledResolutions (type: 'vod' | 'live') { | ||
219 | const transcoding = type === 'vod' | ||
220 | ? CONFIG.TRANSCODING | ||
221 | : CONFIG.LIVE.TRANSCODING | ||
222 | |||
223 | return Object.keys(transcoding.RESOLUTIONS) | ||
224 | .filter(key => transcoding.ENABLED && transcoding.RESOLUTIONS[key] === true) | ||
225 | .map(r => parseInt(r, 10)) | ||
226 | } | ||
227 | |||
211 | // --------------------------------------------------------------------------- | 228 | // --------------------------------------------------------------------------- |
212 | 229 | ||
213 | export { | 230 | export { |
214 | getServerConfig, | 231 | getServerConfig, |
215 | getRegisteredThemes, | 232 | getRegisteredThemes, |
216 | getRegisteredPlugins | 233 | getEnabledResolutions, |
234 | getRegisteredPlugins, | ||
235 | getHTMLServerConfig | ||
217 | } | 236 | } |
218 | 237 | ||
219 | // --------------------------------------------------------------------------- | 238 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/job-queue/handlers/activitypub-follow.ts b/server/lib/job-queue/handlers/activitypub-follow.ts index 82c95be80..ec8df8969 100644 --- a/server/lib/job-queue/handlers/activitypub-follow.ts +++ b/server/lib/job-queue/handlers/activitypub-follow.ts | |||
@@ -1,18 +1,18 @@ | |||
1 | import * as Bull from 'bull' | 1 | import * as Bull from 'bull' |
2 | import { logger } from '../../../helpers/logger' | 2 | import { getLocalActorFollowActivityPubUrl } from '@server/lib/activitypub/url' |
3 | import { REMOTE_SCHEME, WEBSERVER } from '../../../initializers/constants' | 3 | import { ActivitypubFollowPayload } from '@shared/models' |
4 | import { sendFollow } from '../../activitypub/send' | ||
5 | import { sanitizeHost } from '../../../helpers/core-utils' | 4 | import { sanitizeHost } from '../../../helpers/core-utils' |
6 | import { loadActorUrlOrGetFromWebfinger } from '../../../helpers/webfinger' | ||
7 | import { getOrCreateActorAndServerAndModel } from '../../activitypub/actor' | ||
8 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 5 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
9 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 6 | import { logger } from '../../../helpers/logger' |
10 | import { ActorModel } from '../../../models/activitypub/actor' | 7 | import { loadActorUrlOrGetFromWebfinger } from '../../../helpers/webfinger' |
11 | import { Notifier } from '../../notifier' | 8 | import { REMOTE_SCHEME, WEBSERVER } from '../../../initializers/constants' |
12 | import { sequelizeTypescript } from '../../../initializers/database' | 9 | import { sequelizeTypescript } from '../../../initializers/database' |
10 | import { ActorModel } from '../../../models/actor/actor' | ||
11 | import { ActorFollowModel } from '../../../models/actor/actor-follow' | ||
13 | import { MActor, MActorFollowActors, MActorFull } from '../../../types/models' | 12 | import { MActor, MActorFollowActors, MActorFull } from '../../../types/models' |
14 | import { ActivitypubFollowPayload } from '@shared/models' | 13 | import { getOrCreateActorAndServerAndModel } from '../../activitypub/actor' |
15 | import { getLocalActorFollowActivityPubUrl } from '@server/lib/activitypub/url' | 14 | import { sendFollow } from '../../activitypub/send' |
15 | import { Notifier } from '../../notifier' | ||
16 | 16 | ||
17 | async function processActivityPubFollow (job: Bull.Job) { | 17 | async function processActivityPubFollow (job: Bull.Job) { |
18 | const payload = job.data as ActivitypubFollowPayload | 18 | const payload = job.data as ActivitypubFollowPayload |
diff --git a/server/lib/job-queue/handlers/activitypub-refresher.ts b/server/lib/job-queue/handlers/activitypub-refresher.ts index 666e56868..c09b1bcc8 100644 --- a/server/lib/job-queue/handlers/activitypub-refresher.ts +++ b/server/lib/job-queue/handlers/activitypub-refresher.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import * as Bull from 'bull' | 1 | import * as Bull from 'bull' |
2 | import { refreshVideoPlaylistIfNeeded } from '@server/lib/activitypub/playlist' | ||
3 | import { RefreshPayload } from '@shared/models' | ||
2 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
3 | import { fetchVideoByUrl } from '../../../helpers/video' | 5 | import { fetchVideoByUrl } from '../../../helpers/video' |
6 | import { ActorModel } from '../../../models/actor/actor' | ||
7 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
4 | import { refreshActorIfNeeded } from '../../activitypub/actor' | 8 | import { refreshActorIfNeeded } from '../../activitypub/actor' |
5 | import { refreshVideoIfNeeded } from '../../activitypub/videos' | 9 | import { refreshVideoIfNeeded } from '../../activitypub/videos' |
6 | import { ActorModel } from '../../../models/activitypub/actor' | ||
7 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | ||
8 | import { RefreshPayload } from '@shared/models' | ||
9 | import { refreshVideoPlaylistIfNeeded } from '@server/lib/activitypub/playlist' | ||
10 | 10 | ||
11 | async function refreshAPObject (job: Bull.Job) { | 11 | async function refreshAPObject (job: Bull.Job) { |
12 | const payload = job.data as RefreshPayload | 12 | const payload = job.data as RefreshPayload |
diff --git a/server/lib/job-queue/handlers/actor-keys.ts b/server/lib/job-queue/handlers/actor-keys.ts index 125307843..3eef565d0 100644 --- a/server/lib/job-queue/handlers/actor-keys.ts +++ b/server/lib/job-queue/handlers/actor-keys.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import * as Bull from 'bull' | 1 | import * as Bull from 'bull' |
2 | import { generateAndSaveActorKeys } from '@server/lib/activitypub/actor' | 2 | import { generateAndSaveActorKeys } from '@server/lib/activitypub/actor' |
3 | import { ActorModel } from '@server/models/activitypub/actor' | 3 | import { ActorModel } from '@server/models/actor/actor' |
4 | import { ActorKeysPayload } from '@shared/models' | 4 | import { ActorKeysPayload } from '@shared/models' |
5 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
6 | 6 | ||
diff --git a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts index e8a91450d..37e7c1fad 100644 --- a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts +++ b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts | |||
@@ -1,10 +1,10 @@ | |||
1 | import { buildDigest } from '@server/helpers/peertube-crypto' | ||
2 | import { getServerActor } from '@server/models/application/application' | ||
3 | import { ContextType } from '@shared/models/activitypub/context' | ||
1 | import { buildSignedActivity } from '../../../../helpers/activitypub' | 4 | import { buildSignedActivity } from '../../../../helpers/activitypub' |
2 | import { ActorModel } from '../../../../models/activitypub/actor' | ||
3 | import { ACTIVITY_PUB, HTTP_SIGNATURE } from '../../../../initializers/constants' | 5 | import { ACTIVITY_PUB, HTTP_SIGNATURE } from '../../../../initializers/constants' |
6 | import { ActorModel } from '../../../../models/actor/actor' | ||
4 | import { MActor } from '../../../../types/models' | 7 | import { MActor } from '../../../../types/models' |
5 | import { getServerActor } from '@server/models/application/application' | ||
6 | import { buildDigest } from '@server/helpers/peertube-crypto' | ||
7 | import { ContextType } from '@shared/models/activitypub/context' | ||
8 | 8 | ||
9 | type Payload <T> = { body: T, contextType?: ContextType, signatureActorId?: number } | 9 | type Payload <T> = { body: T, contextType?: ContextType, signatureActorId?: number } |
10 | 10 | ||
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts index 71f2cafcd..8297a1571 100644 --- a/server/lib/job-queue/handlers/video-file-import.ts +++ b/server/lib/job-queue/handlers/video-file-import.ts | |||
@@ -3,7 +3,7 @@ import { copy, stat } from 'fs-extra' | |||
3 | import { extname } from 'path' | 3 | import { extname } from 'path' |
4 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | 4 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' |
5 | import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' | 5 | import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' |
6 | import { UserModel } from '@server/models/account/user' | 6 | import { UserModel } from '@server/models/user/user' |
7 | import { MVideoFullLight } from '@server/types/models' | 7 | import { MVideoFullLight } from '@server/types/models' |
8 | import { VideoFileImportPayload } from '@shared/models' | 8 | import { VideoFileImportPayload } from '@shared/models' |
9 | import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' | 9 | import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' |
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts index ed2c5eac0..3067ce214 100644 --- a/server/lib/job-queue/handlers/video-import.ts +++ b/server/lib/job-queue/handlers/video-import.ts | |||
@@ -23,7 +23,6 @@ import { getDurationFromVideoFile, getVideoFileFPS, getVideoFileResolution } fro | |||
23 | import { logger } from '../../../helpers/logger' | 23 | import { logger } from '../../../helpers/logger' |
24 | import { getSecureTorrentName } from '../../../helpers/utils' | 24 | import { getSecureTorrentName } from '../../../helpers/utils' |
25 | import { createTorrentAndSetInfoHash, downloadWebTorrentVideo } from '../../../helpers/webtorrent' | 25 | import { createTorrentAndSetInfoHash, downloadWebTorrentVideo } from '../../../helpers/webtorrent' |
26 | import { downloadYoutubeDLVideo } from '../../../helpers/youtube-dl' | ||
27 | import { CONFIG } from '../../../initializers/config' | 26 | import { CONFIG } from '../../../initializers/config' |
28 | import { VIDEO_IMPORT_TIMEOUT } from '../../../initializers/constants' | 27 | import { VIDEO_IMPORT_TIMEOUT } from '../../../initializers/constants' |
29 | import { sequelizeTypescript } from '../../../initializers/database' | 28 | import { sequelizeTypescript } from '../../../initializers/database' |
@@ -34,6 +33,8 @@ import { MThumbnail } from '../../../types/models/video/thumbnail' | |||
34 | import { federateVideoIfNeeded } from '../../activitypub/videos' | 33 | import { federateVideoIfNeeded } from '../../activitypub/videos' |
35 | import { Notifier } from '../../notifier' | 34 | import { Notifier } from '../../notifier' |
36 | import { generateVideoMiniature } from '../../thumbnail' | 35 | import { generateVideoMiniature } from '../../thumbnail' |
36 | import { YoutubeDL } from '@server/helpers/youtube-dl' | ||
37 | import { getEnabledResolutions } from '@server/lib/config' | ||
37 | 38 | ||
38 | async function processVideoImport (job: Bull.Job) { | 39 | async function processVideoImport (job: Bull.Job) { |
39 | const payload = job.data as VideoImportPayload | 40 | const payload = job.data as VideoImportPayload |
@@ -75,8 +76,10 @@ async function processYoutubeDLImport (job: Bull.Job, payload: VideoImportYoutub | |||
75 | videoImportId: videoImport.id | 76 | videoImportId: videoImport.id |
76 | } | 77 | } |
77 | 78 | ||
79 | const youtubeDL = new YoutubeDL(videoImport.targetUrl, getEnabledResolutions('vod')) | ||
80 | |||
78 | return processFile( | 81 | return processFile( |
79 | () => downloadYoutubeDLVideo(videoImport.targetUrl, payload.fileExt, VIDEO_IMPORT_TIMEOUT), | 82 | () => youtubeDL.downloadYoutubeDLVideo(payload.fileExt, VIDEO_IMPORT_TIMEOUT), |
80 | videoImport, | 83 | videoImport, |
81 | options | 84 | options |
82 | ) | 85 | ) |
diff --git a/server/lib/job-queue/handlers/video-live-ending.ts b/server/lib/job-queue/handlers/video-live-ending.ts index d57202ca5..517b90abc 100644 --- a/server/lib/job-queue/handlers/video-live-ending.ts +++ b/server/lib/job-queue/handlers/video-live-ending.ts | |||
@@ -5,9 +5,9 @@ import { ffprobePromise, getAudioStream, getDurationFromVideoFile, getVideoFileR | |||
5 | import { VIDEO_LIVE } from '@server/initializers/constants' | 5 | import { VIDEO_LIVE } from '@server/initializers/constants' |
6 | import { LiveManager } from '@server/lib/live-manager' | 6 | import { LiveManager } from '@server/lib/live-manager' |
7 | import { generateVideoMiniature } from '@server/lib/thumbnail' | 7 | import { generateVideoMiniature } from '@server/lib/thumbnail' |
8 | import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/video-transcoding' | ||
8 | import { publishAndFederateIfNeeded } from '@server/lib/video' | 9 | import { publishAndFederateIfNeeded } from '@server/lib/video' |
9 | import { getHLSDirectory } from '@server/lib/video-paths' | 10 | import { getHLSDirectory } from '@server/lib/video-paths' |
10 | import { generateHlsPlaylistResolutionFromTS } from '@server/lib/video-transcoding' | ||
11 | import { VideoModel } from '@server/models/video/video' | 11 | import { VideoModel } from '@server/models/video/video' |
12 | import { VideoFileModel } from '@server/models/video/video-file' | 12 | import { VideoFileModel } from '@server/models/video/video-file' |
13 | import { VideoLiveModel } from '@server/models/video/video-live' | 13 | import { VideoLiveModel } from '@server/models/video/video-live' |
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts index 010b95b05..8d659daa6 100644 --- a/server/lib/job-queue/handlers/video-transcoding.ts +++ b/server/lib/job-queue/handlers/video-transcoding.ts | |||
@@ -2,7 +2,7 @@ import * as Bull from 'bull' | |||
2 | import { TranscodeOptionsType } from '@server/helpers/ffmpeg-utils' | 2 | import { TranscodeOptionsType } from '@server/helpers/ffmpeg-utils' |
3 | import { getTranscodingJobPriority, publishAndFederateIfNeeded } from '@server/lib/video' | 3 | import { getTranscodingJobPriority, publishAndFederateIfNeeded } from '@server/lib/video' |
4 | import { getVideoFilePath } from '@server/lib/video-paths' | 4 | import { getVideoFilePath } from '@server/lib/video-paths' |
5 | import { UserModel } from '@server/models/account/user' | 5 | import { UserModel } from '@server/models/user/user' |
6 | import { MUser, MUserId, MVideoFullLight, MVideoUUID, MVideoWithFile } from '@server/types/models' | 6 | import { MUser, MUserId, MVideoFullLight, MVideoUUID, MVideoWithFile } from '@server/types/models' |
7 | import { | 7 | import { |
8 | HLSTranscodingPayload, | 8 | HLSTranscodingPayload, |
@@ -24,7 +24,7 @@ import { | |||
24 | mergeAudioVideofile, | 24 | mergeAudioVideofile, |
25 | optimizeOriginalVideofile, | 25 | optimizeOriginalVideofile, |
26 | transcodeNewWebTorrentResolution | 26 | transcodeNewWebTorrentResolution |
27 | } from '../../video-transcoding' | 27 | } from '../../transcoding/video-transcoding' |
28 | import { JobQueue } from '../job-queue' | 28 | import { JobQueue } from '../job-queue' |
29 | 29 | ||
30 | type HandlerFunction = (job: Bull.Job, payload: VideoTranscodingPayload, video: MVideoFullLight, user: MUser) => Promise<any> | 30 | type HandlerFunction = (job: Bull.Job, payload: VideoTranscodingPayload, video: MVideoFullLight, user: MUser) => Promise<any> |
diff --git a/server/lib/job-queue/handlers/video-views.ts b/server/lib/job-queue/handlers/video-views.ts index 897235ec0..86d0a271f 100644 --- a/server/lib/job-queue/handlers/video-views.ts +++ b/server/lib/job-queue/handlers/video-views.ts | |||
@@ -36,8 +36,8 @@ async function processVideosViews () { | |||
36 | } | 36 | } |
37 | 37 | ||
38 | await VideoViewModel.create({ | 38 | await VideoViewModel.create({ |
39 | startDate, | 39 | startDate: new Date(startDate), |
40 | endDate, | 40 | endDate: new Date(endDate), |
41 | views, | 41 | views, |
42 | videoId | 42 | videoId |
43 | }) | 43 | }) |
diff --git a/server/lib/live-manager.ts b/server/lib/live-manager.ts index 66b5d119b..8e7fd5511 100644 --- a/server/lib/live-manager.ts +++ b/server/lib/live-manager.ts | |||
@@ -11,7 +11,7 @@ import { computeResolutionsToTranscode, getVideoFileFPS, getVideoFileResolution | |||
11 | import { logger } from '@server/helpers/logger' | 11 | import { logger } from '@server/helpers/logger' |
12 | import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' | 12 | import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' |
13 | import { MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE, VIEW_LIFETIME, WEBSERVER } from '@server/initializers/constants' | 13 | import { MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE, VIEW_LIFETIME, WEBSERVER } from '@server/initializers/constants' |
14 | import { UserModel } from '@server/models/account/user' | 14 | import { UserModel } from '@server/models/user/user' |
15 | import { VideoModel } from '@server/models/video/video' | 15 | import { VideoModel } from '@server/models/video/video' |
16 | import { VideoFileModel } from '@server/models/video/video-file' | 16 | import { VideoFileModel } from '@server/models/video/video-file' |
17 | import { VideoLiveModel } from '@server/models/video/video-live' | 17 | import { VideoLiveModel } from '@server/models/video/video-live' |
@@ -23,9 +23,9 @@ import { buildSha256Segment } from './hls' | |||
23 | import { JobQueue } from './job-queue' | 23 | import { JobQueue } from './job-queue' |
24 | import { cleanupLive } from './job-queue/handlers/video-live-ending' | 24 | import { cleanupLive } from './job-queue/handlers/video-live-ending' |
25 | import { PeerTubeSocket } from './peertube-socket' | 25 | import { PeerTubeSocket } from './peertube-socket' |
26 | import { VideoTranscodingProfilesManager } from './transcoding/video-transcoding-profiles' | ||
26 | import { isAbleToUploadVideo } from './user' | 27 | import { isAbleToUploadVideo } from './user' |
27 | import { getHLSDirectory } from './video-paths' | 28 | import { getHLSDirectory } from './video-paths' |
28 | import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' | ||
29 | 29 | ||
30 | import memoizee = require('memoizee') | 30 | import memoizee = require('memoizee') |
31 | const NodeRtmpSession = require('node-media-server/node_rtmp_session') | 31 | const NodeRtmpSession = require('node-media-server/node_rtmp_session') |
diff --git a/server/lib/moderation.ts b/server/lib/moderation.ts index 925d64902..0cefe1648 100644 --- a/server/lib/moderation.ts +++ b/server/lib/moderation.ts | |||
@@ -23,9 +23,9 @@ import { ActivityCreate } from '../../shared/models/activitypub' | |||
23 | import { VideoObject } from '../../shared/models/activitypub/objects' | 23 | import { VideoObject } from '../../shared/models/activitypub/objects' |
24 | import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object' | 24 | import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object' |
25 | import { LiveVideoCreate, VideoCreate, VideoImportCreate } from '../../shared/models/videos' | 25 | import { LiveVideoCreate, VideoCreate, VideoImportCreate } from '../../shared/models/videos' |
26 | import { VideoCommentCreate } from '../../shared/models/videos/video-comment.model' | 26 | import { VideoCommentCreate } from '../../shared/models/videos/comment/video-comment.model' |
27 | import { UserModel } from '../models/account/user' | 27 | import { ActorModel } from '../models/actor/actor' |
28 | import { ActorModel } from '../models/activitypub/actor' | 28 | import { UserModel } from '../models/user/user' |
29 | import { VideoModel } from '../models/video/video' | 29 | import { VideoModel } from '../models/video/video' |
30 | import { VideoCommentModel } from '../models/video/video-comment' | 30 | import { VideoCommentModel } from '../models/video/video-comment' |
31 | import { sendAbuse } from './activitypub/send/send-flag' | 31 | import { sendAbuse } from './activitypub/send/send-flag' |
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts index da7f7cc05..1f9ff16df 100644 --- a/server/lib/notifier.ts +++ b/server/lib/notifier.ts | |||
@@ -17,8 +17,8 @@ import { VideoPrivacy, VideoState } from '../../shared/models/videos' | |||
17 | import { logger } from '../helpers/logger' | 17 | import { logger } from '../helpers/logger' |
18 | import { CONFIG } from '../initializers/config' | 18 | import { CONFIG } from '../initializers/config' |
19 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | 19 | import { AccountBlocklistModel } from '../models/account/account-blocklist' |
20 | import { UserModel } from '../models/account/user' | 20 | import { UserModel } from '../models/user/user' |
21 | import { UserNotificationModel } from '../models/account/user-notification' | 21 | import { UserNotificationModel } from '../models/user/user-notification' |
22 | import { MAbuseFull, MAbuseMessage, MAccountServer, MActorFollowFull, MApplication, MPlugin } from '../types/models' | 22 | import { MAbuseFull, MAbuseMessage, MAccountServer, MActorFollowFull, MApplication, MPlugin } from '../types/models' |
23 | import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../types/models/video' | 23 | import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../types/models/video' |
24 | import { isBlockedByServerOrAccount } from './blocklist' | 24 | import { isBlockedByServerOrAccount } from './blocklist' |
diff --git a/server/lib/plugins/hooks.ts b/server/lib/plugins/hooks.ts index aa92f03cc..5e97b52a0 100644 --- a/server/lib/plugins/hooks.ts +++ b/server/lib/plugins/hooks.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { ServerActionHookName, ServerFilterHookName } from '../../../shared/models/plugins/server-hook.model' | ||
2 | import { PluginManager } from './plugin-manager' | ||
3 | import { logger } from '../../helpers/logger' | ||
4 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { ServerActionHookName, ServerFilterHookName } from '../../../shared/models' | ||
3 | import { logger } from '../../helpers/logger' | ||
4 | import { PluginManager } from './plugin-manager' | ||
5 | 5 | ||
6 | type PromiseFunction <U, T> = (params: U) => Promise<T> | Bluebird<T> | 6 | type PromiseFunction <U, T> = (params: U) => Promise<T> | Bluebird<T> |
7 | type RawFunction <U, T> = (params: U) => T | 7 | type RawFunction <U, T> = (params: U) => T |
diff --git a/server/lib/plugins/plugin-helpers-builder.ts b/server/lib/plugins/plugin-helpers-builder.ts index f1bc24d8b..cb1cd4d9a 100644 --- a/server/lib/plugins/plugin-helpers-builder.ts +++ b/server/lib/plugins/plugin-helpers-builder.ts | |||
@@ -17,7 +17,7 @@ import { VideoBlacklistCreate } from '@shared/models' | |||
17 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../blocklist' | 17 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../blocklist' |
18 | import { getServerConfig } from '../config' | 18 | import { getServerConfig } from '../config' |
19 | import { blacklistVideo, unblacklistVideo } from '../video-blacklist' | 19 | import { blacklistVideo, unblacklistVideo } from '../video-blacklist' |
20 | import { UserModel } from '@server/models/account/user' | 20 | import { UserModel } from '@server/models/user/user' |
21 | 21 | ||
22 | function buildPluginHelpers (pluginModel: MPlugin, npmName: string): PeerTubeHelpers { | 22 | function buildPluginHelpers (pluginModel: MPlugin, npmName: string): PeerTubeHelpers { |
23 | const logger = buildPluginLogger(npmName) | 23 | const logger = buildPluginLogger(npmName) |
diff --git a/server/lib/plugins/plugin-index.ts b/server/lib/plugins/plugin-index.ts index 165bc91b3..119cee8e0 100644 --- a/server/lib/plugins/plugin-index.ts +++ b/server/lib/plugins/plugin-index.ts | |||
@@ -1,16 +1,16 @@ | |||
1 | import { sanitizeUrl } from '@server/helpers/core-utils' | 1 | import { sanitizeUrl } from '@server/helpers/core-utils' |
2 | import { ResultList } from '../../../shared/models' | 2 | import { logger } from '@server/helpers/logger' |
3 | import { PeertubePluginIndexList } from '../../../shared/models/plugins/peertube-plugin-index-list.model' | 3 | import { doJSONRequest } from '@server/helpers/requests' |
4 | import { PeerTubePluginIndex } from '../../../shared/models/plugins/peertube-plugin-index.model' | 4 | import { CONFIG } from '@server/initializers/config' |
5 | import { PEERTUBE_VERSION } from '@server/initializers/constants' | ||
6 | import { PluginModel } from '@server/models/server/plugin' | ||
5 | import { | 7 | import { |
8 | PeerTubePluginIndex, | ||
9 | PeertubePluginIndexList, | ||
6 | PeertubePluginLatestVersionRequest, | 10 | PeertubePluginLatestVersionRequest, |
7 | PeertubePluginLatestVersionResponse | 11 | PeertubePluginLatestVersionResponse, |
8 | } from '../../../shared/models/plugins/peertube-plugin-latest-version.model' | 12 | ResultList |
9 | import { logger } from '../../helpers/logger' | 13 | } from '@shared/models' |
10 | import { doJSONRequest } from '../../helpers/requests' | ||
11 | import { CONFIG } from '../../initializers/config' | ||
12 | import { PEERTUBE_VERSION } from '../../initializers/constants' | ||
13 | import { PluginModel } from '../../models/server/plugin' | ||
14 | import { PluginManager } from './plugin-manager' | 14 | import { PluginManager } from './plugin-manager' |
15 | 15 | ||
16 | async function listAvailablePluginsFromIndex (options: PeertubePluginIndexList) { | 16 | async function listAvailablePluginsFromIndex (options: PeertubePluginIndexList) { |
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts index ba9814383..6b9a255a4 100644 --- a/server/lib/plugins/plugin-manager.ts +++ b/server/lib/plugins/plugin-manager.ts | |||
@@ -4,16 +4,11 @@ import { createReadStream, createWriteStream } from 'fs' | |||
4 | import { ensureDir, outputFile, readJSON } from 'fs-extra' | 4 | import { ensureDir, outputFile, readJSON } from 'fs-extra' |
5 | import { basename, join } from 'path' | 5 | import { basename, join } from 'path' |
6 | import { MOAuthTokenUser, MUser } from '@server/types/models' | 6 | import { MOAuthTokenUser, MUser } from '@server/types/models' |
7 | import { RegisterServerHookOptions } from '@shared/models/plugins/register-server-hook.model' | 7 | import { getCompleteLocale } from '@shared/core-utils' |
8 | import { ClientScript, PluginPackageJson, PluginTranslation, PluginTranslationPaths, RegisterServerHookOptions } from '@shared/models' | ||
8 | import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks' | 9 | import { getHookType, internalRunHook } from '../../../shared/core-utils/plugins/hooks' |
9 | import { | ||
10 | ClientScript, | ||
11 | PluginPackageJson, | ||
12 | PluginTranslationPaths as PackagePluginTranslations | ||
13 | } from '../../../shared/models/plugins/plugin-package-json.model' | ||
14 | import { PluginTranslation } from '../../../shared/models/plugins/plugin-translation.model' | ||
15 | import { PluginType } from '../../../shared/models/plugins/plugin.type' | 10 | import { PluginType } from '../../../shared/models/plugins/plugin.type' |
16 | import { ServerHook, ServerHookName } from '../../../shared/models/plugins/server-hook.model' | 11 | import { ServerHook, ServerHookName } from '../../../shared/models/plugins/server/server-hook.model' |
17 | import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins' | 12 | import { isLibraryCodeValid, isPackageJSONValid } from '../../helpers/custom-validators/plugins' |
18 | import { logger } from '../../helpers/logger' | 13 | import { logger } from '../../helpers/logger' |
19 | import { CONFIG } from '../../initializers/config' | 14 | import { CONFIG } from '../../initializers/config' |
@@ -23,7 +18,6 @@ import { PluginLibrary, RegisterServerAuthExternalOptions, RegisterServerAuthPas | |||
23 | import { ClientHtml } from '../client-html' | 18 | import { ClientHtml } from '../client-html' |
24 | import { RegisterHelpers } from './register-helpers' | 19 | import { RegisterHelpers } from './register-helpers' |
25 | import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' | 20 | import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' |
26 | import { getCompleteLocale } from '@shared/core-utils' | ||
27 | 21 | ||
28 | export interface RegisteredPlugin { | 22 | export interface RegisteredPlugin { |
29 | npmName: string | 23 | npmName: string |
@@ -443,7 +437,7 @@ export class PluginManager implements ServerHook { | |||
443 | 437 | ||
444 | // ###################### Translations ###################### | 438 | // ###################### Translations ###################### |
445 | 439 | ||
446 | private async addTranslations (plugin: PluginModel, npmName: string, translationPaths: PackagePluginTranslations) { | 440 | private async addTranslations (plugin: PluginModel, npmName: string, translationPaths: PluginTranslationPaths) { |
447 | for (const locale of Object.keys(translationPaths)) { | 441 | for (const locale of Object.keys(translationPaths)) { |
448 | const path = translationPaths[locale] | 442 | const path = translationPaths[locale] |
449 | const json = await readJSON(join(this.getPluginPath(plugin.name, plugin.type), path)) | 443 | const json = await readJSON(join(this.getPluginPath(plugin.name, plugin.type), path)) |
diff --git a/server/lib/plugins/register-helpers.ts b/server/lib/plugins/register-helpers.ts index aa69ca2a2..f5b573370 100644 --- a/server/lib/plugins/register-helpers.ts +++ b/server/lib/plugins/register-helpers.ts | |||
@@ -26,10 +26,10 @@ import { | |||
26 | PluginVideoLicenceManager, | 26 | PluginVideoLicenceManager, |
27 | PluginVideoPrivacyManager, | 27 | PluginVideoPrivacyManager, |
28 | RegisterServerHookOptions, | 28 | RegisterServerHookOptions, |
29 | RegisterServerSettingOptions | 29 | RegisterServerSettingOptions, |
30 | serverHookObject | ||
30 | } from '@shared/models' | 31 | } from '@shared/models' |
31 | import { serverHookObject } from '@shared/models/plugins/server-hook.model' | 32 | import { VideoTranscodingProfilesManager } from '../transcoding/video-transcoding-profiles' |
32 | import { VideoTranscodingProfilesManager } from '../video-transcoding-profiles' | ||
33 | import { buildPluginHelpers } from './plugin-helpers-builder' | 33 | import { buildPluginHelpers } from './plugin-helpers-builder' |
34 | 34 | ||
35 | type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy' | 35 | type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy' |
diff --git a/server/lib/redundancy.ts b/server/lib/redundancy.ts index da620b607..2a9241249 100644 --- a/server/lib/redundancy.ts +++ b/server/lib/redundancy.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import { VideoRedundancyModel } from '../models/redundancy/video-redundancy' | ||
2 | import { sendUndoCacheFile } from './activitypub/send' | ||
3 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
4 | import { MActorSignature, MVideoRedundancyVideo } from '@server/types/models' | ||
5 | import { CONFIG } from '@server/initializers/config' | ||
6 | import { logger } from '@server/helpers/logger' | 2 | import { logger } from '@server/helpers/logger' |
7 | import { ActorFollowModel } from '@server/models/activitypub/actor-follow' | 3 | import { CONFIG } from '@server/initializers/config' |
8 | import { Activity } from '@shared/models' | 4 | import { ActorFollowModel } from '@server/models/actor/actor-follow' |
9 | import { getServerActor } from '@server/models/application/application' | 5 | import { getServerActor } from '@server/models/application/application' |
6 | import { MActorSignature, MVideoRedundancyVideo } from '@server/types/models' | ||
7 | import { Activity } from '@shared/models' | ||
8 | import { VideoRedundancyModel } from '../models/redundancy/video-redundancy' | ||
9 | import { sendUndoCacheFile } from './activitypub/send' | ||
10 | 10 | ||
11 | async function removeVideoRedundancy (videoRedundancy: MVideoRedundancyVideo, t?: Transaction) { | 11 | async function removeVideoRedundancy (videoRedundancy: MVideoRedundancyVideo, t?: Transaction) { |
12 | const serverActor = await getServerActor() | 12 | const serverActor = await getServerActor() |
diff --git a/server/lib/schedulers/actor-follow-scheduler.ts b/server/lib/schedulers/actor-follow-scheduler.ts index 598c0211f..1b80316e9 100644 --- a/server/lib/schedulers/actor-follow-scheduler.ts +++ b/server/lib/schedulers/actor-follow-scheduler.ts | |||
@@ -1,9 +1,9 @@ | |||
1 | import { isTestInstance } from '../../helpers/core-utils' | 1 | import { isTestInstance } from '../../helpers/core-utils' |
2 | import { logger } from '../../helpers/logger' | 2 | import { logger } from '../../helpers/logger' |
3 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | ||
4 | import { AbstractScheduler } from './abstract-scheduler' | ||
5 | import { ACTOR_FOLLOW_SCORE, SCHEDULER_INTERVALS_MS } from '../../initializers/constants' | 3 | import { ACTOR_FOLLOW_SCORE, SCHEDULER_INTERVALS_MS } from '../../initializers/constants' |
4 | import { ActorFollowModel } from '../../models/actor/actor-follow' | ||
6 | import { ActorFollowScoreCache } from '../files-cache' | 5 | import { ActorFollowScoreCache } from '../files-cache' |
6 | import { AbstractScheduler } from './abstract-scheduler' | ||
7 | 7 | ||
8 | export class ActorFollowScheduler extends AbstractScheduler { | 8 | export class ActorFollowScheduler extends AbstractScheduler { |
9 | 9 | ||
diff --git a/server/lib/schedulers/auto-follow-index-instances.ts b/server/lib/schedulers/auto-follow-index-instances.ts index 0b8cd1389..aaa5feed5 100644 --- a/server/lib/schedulers/auto-follow-index-instances.ts +++ b/server/lib/schedulers/auto-follow-index-instances.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { chunk } from 'lodash' | 1 | import { chunk } from 'lodash' |
2 | import { doJSONRequest } from '@server/helpers/requests' | 2 | import { doJSONRequest } from '@server/helpers/requests' |
3 | import { JobQueue } from '@server/lib/job-queue' | 3 | import { JobQueue } from '@server/lib/job-queue' |
4 | import { ActorFollowModel } from '@server/models/activitypub/actor-follow' | 4 | import { ActorFollowModel } from '@server/models/actor/actor-follow' |
5 | import { getServerActor } from '@server/models/application/application' | 5 | import { getServerActor } from '@server/models/application/application' |
6 | import { logger } from '../../helpers/logger' | 6 | import { logger } from '../../helpers/logger' |
7 | import { CONFIG } from '../../initializers/config' | 7 | import { CONFIG } from '../../initializers/config' |
diff --git a/server/lib/schedulers/remove-old-history-scheduler.ts b/server/lib/schedulers/remove-old-history-scheduler.ts index 17a42b2c4..225669ea2 100644 --- a/server/lib/schedulers/remove-old-history-scheduler.ts +++ b/server/lib/schedulers/remove-old-history-scheduler.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { logger } from '../../helpers/logger' | 1 | import { logger } from '../../helpers/logger' |
2 | import { AbstractScheduler } from './abstract-scheduler' | 2 | import { AbstractScheduler } from './abstract-scheduler' |
3 | import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants' | 3 | import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants' |
4 | import { UserVideoHistoryModel } from '../../models/account/user-video-history' | 4 | import { UserVideoHistoryModel } from '../../models/user/user-video-history' |
5 | import { CONFIG } from '../../initializers/config' | 5 | import { CONFIG } from '../../initializers/config' |
6 | 6 | ||
7 | export class RemoveOldHistoryScheduler extends AbstractScheduler { | 7 | export class RemoveOldHistoryScheduler extends AbstractScheduler { |
diff --git a/server/lib/schedulers/youtube-dl-update-scheduler.ts b/server/lib/schedulers/youtube-dl-update-scheduler.ts index aefe6aba4..898691c13 100644 --- a/server/lib/schedulers/youtube-dl-update-scheduler.ts +++ b/server/lib/schedulers/youtube-dl-update-scheduler.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { AbstractScheduler } from './abstract-scheduler' | 1 | import { YoutubeDL } from '@server/helpers/youtube-dl' |
2 | import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants' | 2 | import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants' |
3 | import { updateYoutubeDLBinary } from '../../helpers/youtube-dl' | 3 | import { AbstractScheduler } from './abstract-scheduler' |
4 | 4 | ||
5 | export class YoutubeDlUpdateScheduler extends AbstractScheduler { | 5 | export class YoutubeDlUpdateScheduler extends AbstractScheduler { |
6 | 6 | ||
@@ -13,7 +13,7 @@ export class YoutubeDlUpdateScheduler extends AbstractScheduler { | |||
13 | } | 13 | } |
14 | 14 | ||
15 | protected internalExecute () { | 15 | protected internalExecute () { |
16 | return updateYoutubeDLBinary() | 16 | return YoutubeDL.updateYoutubeDLBinary() |
17 | } | 17 | } |
18 | 18 | ||
19 | static get Instance () { | 19 | static get Instance () { |
diff --git a/server/lib/stat-manager.ts b/server/lib/stat-manager.ts index 09ba208bd..25ed21927 100644 --- a/server/lib/stat-manager.ts +++ b/server/lib/stat-manager.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { CONFIG } from '@server/initializers/config' | 1 | import { CONFIG } from '@server/initializers/config' |
2 | import { UserModel } from '@server/models/account/user' | 2 | import { UserModel } from '@server/models/user/user' |
3 | import { ActorFollowModel } from '@server/models/activitypub/actor-follow' | 3 | import { ActorFollowModel } from '@server/models/actor/actor-follow' |
4 | import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy' | 4 | import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy' |
5 | import { VideoModel } from '@server/models/video/video' | 5 | import { VideoModel } from '@server/models/video/video' |
6 | import { VideoChannelModel } from '@server/models/video/video-channel' | 6 | import { VideoChannelModel } from '@server/models/video/video-channel' |
diff --git a/server/lib/video-transcoding-profiles.ts b/server/lib/transcoding/video-transcoding-profiles.ts index 81f5e1962..c5ea72a5f 100644 --- a/server/lib/video-transcoding-profiles.ts +++ b/server/lib/transcoding/video-transcoding-profiles.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { logger } from '@server/helpers/logger' | 1 | import { logger } from '@server/helpers/logger' |
2 | import { AvailableEncoders, EncoderOptionsBuilder, getTargetBitrate, VideoResolution } from '../../shared/models/videos' | 2 | import { AvailableEncoders, EncoderOptionsBuilder, getTargetBitrate, VideoResolution } from '../../../shared/models/videos' |
3 | import { buildStreamSuffix, resetSupportedEncoders } from '../helpers/ffmpeg-utils' | 3 | import { buildStreamSuffix, resetSupportedEncoders } from '../../helpers/ffmpeg-utils' |
4 | import { | 4 | import { |
5 | canDoQuickAudioTranscode, | 5 | canDoQuickAudioTranscode, |
6 | ffprobePromise, | 6 | ffprobePromise, |
@@ -8,8 +8,8 @@ import { | |||
8 | getMaxAudioBitrate, | 8 | getMaxAudioBitrate, |
9 | getVideoFileBitrate, | 9 | getVideoFileBitrate, |
10 | getVideoStreamFromFile | 10 | getVideoStreamFromFile |
11 | } from '../helpers/ffprobe-utils' | 11 | } from '../../helpers/ffprobe-utils' |
12 | import { VIDEO_TRANSCODING_FPS } from '../initializers/constants' | 12 | import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants' |
13 | 13 | ||
14 | /** | 14 | /** |
15 | * | 15 | * |
diff --git a/server/lib/video-transcoding.ts b/server/lib/transcoding/video-transcoding.ts index c949dca2e..5df192575 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/transcoding/video-transcoding.ts | |||
@@ -3,17 +3,17 @@ import { copyFile, ensureDir, move, remove, stat } from 'fs-extra' | |||
3 | import { basename, extname as extnameUtil, join } from 'path' | 3 | import { basename, extname as extnameUtil, join } from 'path' |
4 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' | 4 | import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' |
5 | import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoFullLight } from '@server/types/models' | 5 | import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoFullLight } from '@server/types/models' |
6 | import { VideoResolution } from '../../shared/models/videos' | 6 | import { VideoResolution } from '../../../shared/models/videos' |
7 | import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' | 7 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' |
8 | import { transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils' | 8 | import { transcode, TranscodeOptions, TranscodeOptionsType } from '../../helpers/ffmpeg-utils' |
9 | import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../helpers/ffprobe-utils' | 9 | import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../../helpers/ffprobe-utils' |
10 | import { logger } from '../helpers/logger' | 10 | import { logger } from '../../helpers/logger' |
11 | import { CONFIG } from '../initializers/config' | 11 | import { CONFIG } from '../../initializers/config' |
12 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../initializers/constants' | 12 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../../initializers/constants' |
13 | import { VideoFileModel } from '../models/video/video-file' | 13 | import { VideoFileModel } from '../../models/video/video-file' |
14 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | 14 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' |
15 | import { updateMasterHLSPlaylist, updateSha256VODSegments } from './hls' | 15 | import { updateMasterHLSPlaylist, updateSha256VODSegments } from '../hls' |
16 | import { generateVideoFilename, generateVideoStreamingPlaylistName, getVideoFilePath } from './video-paths' | 16 | import { generateVideoFilename, generateVideoStreamingPlaylistName, getVideoFilePath } from '../video-paths' |
17 | import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' | 17 | import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' |
18 | 18 | ||
19 | /** | 19 | /** |
@@ -215,16 +215,6 @@ function generateHlsPlaylistResolution (options: { | |||
215 | }) | 215 | }) |
216 | } | 216 | } |
217 | 217 | ||
218 | function getEnabledResolutions (type: 'vod' | 'live') { | ||
219 | const transcoding = type === 'vod' | ||
220 | ? CONFIG.TRANSCODING | ||
221 | : CONFIG.LIVE.TRANSCODING | ||
222 | |||
223 | return Object.keys(transcoding.RESOLUTIONS) | ||
224 | .filter(key => transcoding.ENABLED && transcoding.RESOLUTIONS[key] === true) | ||
225 | .map(r => parseInt(r, 10)) | ||
226 | } | ||
227 | |||
228 | // --------------------------------------------------------------------------- | 218 | // --------------------------------------------------------------------------- |
229 | 219 | ||
230 | export { | 220 | export { |
@@ -232,8 +222,7 @@ export { | |||
232 | generateHlsPlaylistResolutionFromTS, | 222 | generateHlsPlaylistResolutionFromTS, |
233 | optimizeOriginalVideofile, | 223 | optimizeOriginalVideofile, |
234 | transcodeNewWebTorrentResolution, | 224 | transcodeNewWebTorrentResolution, |
235 | mergeAudioVideofile, | 225 | mergeAudioVideofile |
236 | getEnabledResolutions | ||
237 | } | 226 | } |
238 | 227 | ||
239 | // --------------------------------------------------------------------------- | 228 | // --------------------------------------------------------------------------- |
diff --git a/server/lib/user.ts b/server/lib/user.ts index 9b0a0a2f1..8a6fcebc7 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts | |||
@@ -1,14 +1,15 @@ | |||
1 | import { Transaction } from 'sequelize/types' | 1 | import { Transaction } from 'sequelize/types' |
2 | import { v4 as uuidv4 } from 'uuid' | 2 | import { v4 as uuidv4 } from 'uuid' |
3 | import { UserModel } from '@server/models/account/user' | 3 | import { UserModel } from '@server/models/user/user' |
4 | import { MActorDefault } from '@server/types/models/actor' | ||
4 | import { ActivityPubActorType } from '../../shared/models/activitypub' | 5 | import { ActivityPubActorType } from '../../shared/models/activitypub' |
5 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' | 6 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../shared/models/users' |
6 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants' | 7 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants' |
7 | import { sequelizeTypescript } from '../initializers/database' | 8 | import { sequelizeTypescript } from '../initializers/database' |
8 | import { AccountModel } from '../models/account/account' | 9 | import { AccountModel } from '../models/account/account' |
9 | import { UserNotificationSettingModel } from '../models/account/user-notification-setting' | 10 | import { ActorModel } from '../models/actor/actor' |
10 | import { ActorModel } from '../models/activitypub/actor' | 11 | import { UserNotificationSettingModel } from '../models/user/user-notification-setting' |
11 | import { MAccountDefault, MActorDefault, MChannelActor } from '../types/models' | 12 | import { MAccountDefault, MChannelActor } from '../types/models' |
12 | import { MUser, MUserDefault, MUserId } from '../types/models/user' | 13 | import { MUser, MUserDefault, MUserId } from '../types/models/user' |
13 | import { buildActorInstance, generateAndSaveActorKeys } from './activitypub/actor' | 14 | import { buildActorInstance, generateAndSaveActorKeys } from './activitypub/actor' |
14 | import { getLocalAccountActivityPubUrl } from './activitypub/url' | 15 | import { getLocalAccountActivityPubUrl } from './activitypub/url' |
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index 0476cb2d5..d57e832fe 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { v4 as uuidv4 } from 'uuid' | ||
3 | import { VideoChannelCreate } from '../../shared/models' | 2 | import { VideoChannelCreate } from '../../shared/models' |
4 | import { VideoModel } from '../models/video/video' | 3 | import { VideoModel } from '../models/video/video' |
5 | import { VideoChannelModel } from '../models/video/video-channel' | 4 | import { VideoChannelModel } from '../models/video/video-channel' |
@@ -9,9 +8,8 @@ import { getLocalVideoChannelActivityPubUrl } from './activitypub/url' | |||
9 | import { federateVideoIfNeeded } from './activitypub/videos' | 8 | import { federateVideoIfNeeded } from './activitypub/videos' |
10 | 9 | ||
11 | async function createLocalVideoChannel (videoChannelInfo: VideoChannelCreate, account: MAccountId, t: Sequelize.Transaction) { | 10 | async function createLocalVideoChannel (videoChannelInfo: VideoChannelCreate, account: MAccountId, t: Sequelize.Transaction) { |
12 | const uuid = uuidv4() | ||
13 | const url = getLocalVideoChannelActivityPubUrl(videoChannelInfo.name) | 11 | const url = getLocalVideoChannelActivityPubUrl(videoChannelInfo.name) |
14 | const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid) | 12 | const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name) |
15 | 13 | ||
16 | const actorInstanceCreated = await actorInstance.save({ transaction: t }) | 14 | const actorInstanceCreated = await actorInstance.save({ transaction: t }) |
17 | 15 | ||
diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts index 736ebb2f8..51a9c747e 100644 --- a/server/lib/video-comment.ts +++ b/server/lib/video-comment.ts | |||
@@ -3,7 +3,7 @@ import * as Sequelize from 'sequelize' | |||
3 | import { logger } from '@server/helpers/logger' | 3 | import { logger } from '@server/helpers/logger' |
4 | import { sequelizeTypescript } from '@server/initializers/database' | 4 | import { sequelizeTypescript } from '@server/initializers/database' |
5 | import { ResultList } from '../../shared/models' | 5 | import { ResultList } from '../../shared/models' |
6 | import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model' | 6 | import { VideoCommentThreadTree } from '../../shared/models/videos/comment/video-comment.model' |
7 | import { VideoCommentModel } from '../models/video/video-comment' | 7 | import { VideoCommentModel } from '../models/video/video-comment' |
8 | import { MAccountDefault, MComment, MCommentOwnerVideo, MCommentOwnerVideoReply, MVideoFullLight } from '../types/models' | 8 | import { MAccountDefault, MComment, MCommentOwnerVideo, MCommentOwnerVideoReply, MVideoFullLight } from '../types/models' |
9 | import { sendCreateVideoComment, sendDeleteVideoComment } from './activitypub/send' | 9 | import { sendCreateVideoComment, sendDeleteVideoComment } from './activitypub/send' |
diff --git a/server/lib/video.ts b/server/lib/video.ts index 21e4b7ff2..d26cf85cd 100644 --- a/server/lib/video.ts +++ b/server/lib/video.ts | |||
@@ -28,6 +28,8 @@ function buildLocalVideoFromReq (videoInfo: VideoCreate, channelId: number): Fil | |||
28 | privacy: videoInfo.privacy || VideoPrivacy.PRIVATE, | 28 | privacy: videoInfo.privacy || VideoPrivacy.PRIVATE, |
29 | channelId: channelId, | 29 | channelId: channelId, |
30 | originallyPublishedAt: videoInfo.originallyPublishedAt | 30 | originallyPublishedAt: videoInfo.originallyPublishedAt |
31 | ? new Date(videoInfo.originallyPublishedAt) | ||
32 | : null | ||
31 | } | 33 | } |
32 | } | 34 | } |
33 | 35 | ||
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts index bb849dc72..1d18de8cd 100644 --- a/server/middlewares/validators/follows.ts +++ b/server/middlewares/validators/follows.ts | |||
@@ -1,18 +1,18 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { isFollowStateValid } from '@server/helpers/custom-validators/follows' | ||
4 | import { getServerActor } from '@server/models/application/application' | ||
5 | import { MActorFollowActorsDefault } from '@server/types/models' | ||
6 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
3 | import { isTestInstance } from '../../helpers/core-utils' | 7 | import { isTestInstance } from '../../helpers/core-utils' |
8 | import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' | ||
4 | import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' | 9 | import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' |
5 | import { logger } from '../../helpers/logger' | 10 | import { logger } from '../../helpers/logger' |
11 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' | ||
6 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' | 12 | import { SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' |
7 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | 13 | import { ActorModel } from '../../models/actor/actor' |
14 | import { ActorFollowModel } from '../../models/actor/actor-follow' | ||
8 | import { areValidationErrors } from './utils' | 15 | import { areValidationErrors } from './utils' |
9 | import { ActorModel } from '../../models/activitypub/actor' | ||
10 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' | ||
11 | import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' | ||
12 | import { MActorFollowActorsDefault } from '@server/types/models' | ||
13 | import { isFollowStateValid } from '@server/helpers/custom-validators/follows' | ||
14 | import { getServerActor } from '@server/models/application/application' | ||
15 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
16 | 16 | ||
17 | const listFollowsValidator = [ | 17 | const listFollowsValidator = [ |
18 | query('state') | 18 | query('state') |
diff --git a/server/middlewares/validators/plugins.ts b/server/middlewares/validators/plugins.ts index ab87fe720..2c47ec5bb 100644 --- a/server/middlewares/validators/plugins.ts +++ b/server/middlewares/validators/plugins.ts | |||
@@ -1,15 +1,15 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query, ValidationChain } from 'express-validator' | 2 | import { body, param, query, ValidationChain } from 'express-validator' |
3 | import { logger } from '../../helpers/logger' | 3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
4 | import { areValidationErrors } from './utils' | 4 | import { PluginType } from '../../../shared/models/plugins/plugin.type' |
5 | import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/server/api/install-plugin.model' | ||
6 | import { exists, isBooleanValid, isSafePath, toBooleanOrNull, toIntOrNull } from '../../helpers/custom-validators/misc' | ||
5 | import { isNpmPluginNameValid, isPluginNameValid, isPluginTypeValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins' | 7 | import { isNpmPluginNameValid, isPluginNameValid, isPluginTypeValid, isPluginVersionValid } from '../../helpers/custom-validators/plugins' |
8 | import { logger } from '../../helpers/logger' | ||
9 | import { CONFIG } from '../../initializers/config' | ||
6 | import { PluginManager } from '../../lib/plugins/plugin-manager' | 10 | import { PluginManager } from '../../lib/plugins/plugin-manager' |
7 | import { isBooleanValid, isSafePath, toBooleanOrNull, exists, toIntOrNull } from '../../helpers/custom-validators/misc' | ||
8 | import { PluginModel } from '../../models/server/plugin' | 11 | import { PluginModel } from '../../models/server/plugin' |
9 | import { InstallOrUpdatePlugin } from '../../../shared/models/plugins/install-plugin.model' | 12 | import { areValidationErrors } from './utils' |
10 | import { PluginType } from '../../../shared/models/plugins/plugin.type' | ||
11 | import { CONFIG } from '../../initializers/config' | ||
12 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
13 | 13 | ||
14 | const getPluginValidator = (pluginType: PluginType, withVersion = true) => { | 14 | const getPluginValidator = (pluginType: PluginType, withVersion = true) => { |
15 | const validators: (ValidationChain | express.Handler)[] = [ | 15 | const validators: (ValidationChain | express.Handler)[] = [ |
diff --git a/server/middlewares/validators/user-subscriptions.ts b/server/middlewares/validators/user-subscriptions.ts index 0d0c8ccbf..1823892b6 100644 --- a/server/middlewares/validators/user-subscriptions.ts +++ b/server/middlewares/validators/user-subscriptions.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param, query } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { logger } from '../../helpers/logger' | 3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
4 | import { areValidationErrors } from './utils' | ||
5 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' | ||
6 | import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' | 4 | import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' |
7 | import { toArray } from '../../helpers/custom-validators/misc' | 5 | import { toArray } from '../../helpers/custom-validators/misc' |
6 | import { logger } from '../../helpers/logger' | ||
8 | import { WEBSERVER } from '../../initializers/constants' | 7 | import { WEBSERVER } from '../../initializers/constants' |
9 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 8 | import { ActorFollowModel } from '../../models/actor/actor-follow' |
9 | import { areValidationErrors } from './utils' | ||
10 | 10 | ||
11 | const userSubscriptionListValidator = [ | 11 | const userSubscriptionListValidator = [ |
12 | query('search').optional().not().isEmpty().withMessage('Should have a valid search'), | 12 | query('search').optional().not().isEmpty().withMessage('Should have a valid search'), |
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index 37119e279..548d5df4d 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts | |||
@@ -34,8 +34,8 @@ import { doesVideoExist } from '../../helpers/middlewares' | |||
34 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup' | 34 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '../../helpers/signup' |
35 | import { isThemeRegistered } from '../../lib/plugins/theme-utils' | 35 | import { isThemeRegistered } from '../../lib/plugins/theme-utils' |
36 | import { Redis } from '../../lib/redis' | 36 | import { Redis } from '../../lib/redis' |
37 | import { UserModel } from '../../models/account/user' | 37 | import { UserModel } from '../../models/user/user' |
38 | import { ActorModel } from '../../models/activitypub/actor' | 38 | import { ActorModel } from '../../models/actor/actor' |
39 | import { areValidationErrors } from './utils' | 39 | import { areValidationErrors } from './utils' |
40 | 40 | ||
41 | const usersListValidator = [ | 41 | const usersListValidator = [ |
diff --git a/server/middlewares/validators/videos/video-channels.ts b/server/middlewares/validators/videos/video-channels.ts index 2463d281c..e881f0d3e 100644 --- a/server/middlewares/validators/videos/video-channels.ts +++ b/server/middlewares/validators/videos/video-channels.ts | |||
@@ -3,6 +3,7 @@ import { body, param, query } from 'express-validator' | |||
3 | import { VIDEO_CHANNELS } from '@server/initializers/constants' | 3 | import { VIDEO_CHANNELS } from '@server/initializers/constants' |
4 | import { MChannelAccountDefault, MUser } from '@server/types/models' | 4 | import { MChannelAccountDefault, MUser } from '@server/types/models' |
5 | import { UserRight } from '../../../../shared' | 5 | import { UserRight } from '../../../../shared' |
6 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
6 | import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' | 7 | import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor' |
7 | import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' | 8 | import { isBooleanValid, toBooleanOrNull } from '../../../helpers/custom-validators/misc' |
8 | import { | 9 | import { |
@@ -12,10 +13,9 @@ import { | |||
12 | } from '../../../helpers/custom-validators/video-channels' | 13 | } from '../../../helpers/custom-validators/video-channels' |
13 | import { logger } from '../../../helpers/logger' | 14 | import { logger } from '../../../helpers/logger' |
14 | import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares' | 15 | import { doesLocalVideoChannelNameExist, doesVideoChannelNameWithHostExist } from '../../../helpers/middlewares' |
15 | import { ActorModel } from '../../../models/activitypub/actor' | 16 | import { ActorModel } from '../../../models/actor/actor' |
16 | import { VideoChannelModel } from '../../../models/video/video-channel' | 17 | import { VideoChannelModel } from '../../../models/video/video-channel' |
17 | import { areValidationErrors } from '../utils' | 18 | import { areValidationErrors } from '../utils' |
18 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
19 | 19 | ||
20 | const videoChannelsAddValidator = [ | 20 | const videoChannelsAddValidator = [ |
21 | body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), | 21 | body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'), |
diff --git a/server/middlewares/validators/videos/video-imports.ts b/server/middlewares/validators/videos/video-imports.ts index c53af3861..d0643ff26 100644 --- a/server/middlewares/validators/videos/video-imports.ts +++ b/server/middlewares/validators/videos/video-imports.ts | |||
@@ -47,14 +47,12 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | |||
47 | cleanUpReqFiles(req) | 47 | cleanUpReqFiles(req) |
48 | return res.status(HttpStatusCode.CONFLICT_409) | 48 | return res.status(HttpStatusCode.CONFLICT_409) |
49 | .json({ error: 'HTTP import is not enabled on this instance.' }) | 49 | .json({ error: 'HTTP import is not enabled on this instance.' }) |
50 | .end() | ||
51 | } | 50 | } |
52 | 51 | ||
53 | if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) { | 52 | if (CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED !== true && (req.body.magnetUri || torrentFile)) { |
54 | cleanUpReqFiles(req) | 53 | cleanUpReqFiles(req) |
55 | return res.status(HttpStatusCode.CONFLICT_409) | 54 | return res.status(HttpStatusCode.CONFLICT_409) |
56 | .json({ error: 'Torrent/magnet URI import is not enabled on this instance.' }) | 55 | .json({ error: 'Torrent/magnet URI import is not enabled on this instance.' }) |
57 | .end() | ||
58 | } | 56 | } |
59 | 57 | ||
60 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) | 58 | if (!await doesVideoChannelOfAccountExist(req.body.channelId, user, res)) return cleanUpReqFiles(req) |
@@ -65,7 +63,6 @@ const videoImportAddValidator = getCommonVideoEditAttributes().concat([ | |||
65 | 63 | ||
66 | return res.status(HttpStatusCode.BAD_REQUEST_400) | 64 | return res.status(HttpStatusCode.BAD_REQUEST_400) |
67 | .json({ error: 'Should have a magnetUri or a targetUrl or a torrent file.' }) | 65 | .json({ error: 'Should have a magnetUri or a targetUrl or a torrent file.' }) |
68 | .end() | ||
69 | } | 66 | } |
70 | 67 | ||
71 | if (!await isImportAccepted(req, res)) return cleanUpReqFiles(req) | 68 | if (!await isImportAccepted(req, res)) return cleanUpReqFiles(req) |
diff --git a/server/middlewares/validators/videos/videos.ts b/server/middlewares/validators/videos/videos.ts index d26bcd4a6..3219e10d4 100644 --- a/server/middlewares/validators/videos/videos.ts +++ b/server/middlewares/validators/videos/videos.ts | |||
@@ -7,7 +7,7 @@ import { ExpressPromiseHandler } from '@server/types/express' | |||
7 | import { MUserAccountId, MVideoWithRights } from '@server/types/models' | 7 | import { MUserAccountId, MVideoWithRights } from '@server/types/models' |
8 | import { ServerErrorCode, UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared' | 8 | import { ServerErrorCode, UserRight, VideoChangeOwnershipStatus, VideoPrivacy } from '../../../../shared' |
9 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | 9 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
10 | import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/video-change-ownership-accept.model' | 10 | import { VideoChangeOwnershipAccept } from '../../../../shared/models/videos/change-ownership/video-change-ownership-accept.model' |
11 | import { | 11 | import { |
12 | exists, | 12 | exists, |
13 | isBooleanValid, | 13 | isBooleanValid, |
diff --git a/server/middlewares/validators/webfinger.ts b/server/middlewares/validators/webfinger.ts index a71422ed8..c2dfccc96 100644 --- a/server/middlewares/validators/webfinger.ts +++ b/server/middlewares/validators/webfinger.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { query } from 'express-validator' | 2 | import { query } from 'express-validator' |
3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
3 | import { isWebfingerLocalResourceValid } from '../../helpers/custom-validators/webfinger' | 4 | import { isWebfingerLocalResourceValid } from '../../helpers/custom-validators/webfinger' |
5 | import { getHostWithPort } from '../../helpers/express-utils' | ||
4 | import { logger } from '../../helpers/logger' | 6 | import { logger } from '../../helpers/logger' |
5 | import { ActorModel } from '../../models/activitypub/actor' | 7 | import { ActorModel } from '../../models/actor/actor' |
6 | import { areValidationErrors } from './utils' | 8 | import { areValidationErrors } from './utils' |
7 | import { getHostWithPort } from '../../helpers/express-utils' | ||
8 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
9 | 9 | ||
10 | const webfingerValidator = [ | 10 | const webfingerValidator = [ |
11 | query('resource').custom(isWebfingerLocalResourceValid).withMessage('Should have a valid webfinger resource'), | 11 | query('resource').custom(isWebfingerLocalResourceValid).withMessage('Should have a valid webfinger resource'), |
diff --git a/server/models/abuse/abuse-message.ts b/server/models/abuse/abuse-message.ts index 7e51b3e07..2c5987e96 100644 --- a/server/models/abuse/abuse-message.ts +++ b/server/models/abuse/abuse-message.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses' | 2 | import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses' |
3 | import { MAbuseMessage, MAbuseMessageFormattable } from '@server/types/models' | 3 | import { MAbuseMessage, MAbuseMessageFormattable } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { AbuseMessage } from '@shared/models' | 5 | import { AbuseMessage } from '@shared/models' |
5 | import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account' | 6 | import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account' |
6 | import { getSort, throwIfNotValid } from '../utils' | 7 | import { getSort, throwIfNotValid } from '../utils' |
@@ -17,7 +18,7 @@ import { AbuseModel } from './abuse' | |||
17 | } | 18 | } |
18 | ] | 19 | ] |
19 | }) | 20 | }) |
20 | export class AbuseMessageModel extends Model { | 21 | export class AbuseMessageModel extends Model<Partial<AttributesOnly<AbuseMessageModel>>> { |
21 | 22 | ||
22 | @AllowNull(false) | 23 | @AllowNull(false) |
23 | @Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message')) | 24 | @Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message')) |
diff --git a/server/models/abuse/abuse.ts b/server/models/abuse/abuse.ts index 262f364f1..3518f5c02 100644 --- a/server/models/abuse/abuse.ts +++ b/server/models/abuse/abuse.ts | |||
@@ -16,7 +16,7 @@ import { | |||
16 | UpdatedAt | 16 | UpdatedAt |
17 | } from 'sequelize-typescript' | 17 | } from 'sequelize-typescript' |
18 | import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses' | 18 | import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses' |
19 | import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse' | 19 | import { abusePredefinedReasonsMap, AttributesOnly } from '@shared/core-utils' |
20 | import { | 20 | import { |
21 | AbuseFilter, | 21 | AbuseFilter, |
22 | AbuseObject, | 22 | AbuseObject, |
@@ -187,7 +187,7 @@ export enum ScopeNames { | |||
187 | } | 187 | } |
188 | ] | 188 | ] |
189 | }) | 189 | }) |
190 | export class AbuseModel extends Model { | 190 | export class AbuseModel extends Model<Partial<AttributesOnly<AbuseModel>>> { |
191 | 191 | ||
192 | @AllowNull(false) | 192 | @AllowNull(false) |
193 | @Default(null) | 193 | @Default(null) |
diff --git a/server/models/abuse/video-abuse.ts b/server/models/abuse/video-abuse.ts index 90aa0695e..95bff50d0 100644 --- a/server/models/abuse/video-abuse.ts +++ b/server/models/abuse/video-abuse.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { VideoDetails } from '@shared/models' | 3 | import { VideoDetails } from '@shared/models' |
3 | import { VideoModel } from '../video/video' | 4 | import { VideoModel } from '../video/video' |
4 | import { AbuseModel } from './abuse' | 5 | import { AbuseModel } from './abuse' |
@@ -14,7 +15,7 @@ import { AbuseModel } from './abuse' | |||
14 | } | 15 | } |
15 | ] | 16 | ] |
16 | }) | 17 | }) |
17 | export class VideoAbuseModel extends Model { | 18 | export class VideoAbuseModel extends Model<Partial<AttributesOnly<VideoAbuseModel>>> { |
18 | 19 | ||
19 | @CreatedAt | 20 | @CreatedAt |
20 | createdAt: Date | 21 | createdAt: Date |
diff --git a/server/models/abuse/video-comment-abuse.ts b/server/models/abuse/video-comment-abuse.ts index d3fce76a5..32cb2ca64 100644 --- a/server/models/abuse/video-comment-abuse.ts +++ b/server/models/abuse/video-comment-abuse.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { VideoCommentModel } from '../video/video-comment' | 3 | import { VideoCommentModel } from '../video/video-comment' |
3 | import { AbuseModel } from './abuse' | 4 | import { AbuseModel } from './abuse' |
4 | 5 | ||
@@ -13,7 +14,7 @@ import { AbuseModel } from './abuse' | |||
13 | } | 14 | } |
14 | ] | 15 | ] |
15 | }) | 16 | }) |
16 | export class VideoCommentAbuseModel extends Model { | 17 | export class VideoCommentAbuseModel extends Model<Partial<AttributesOnly<VideoCommentAbuseModel>>> { |
17 | 18 | ||
18 | @CreatedAt | 19 | @CreatedAt |
19 | createdAt: Date | 20 | createdAt: Date |
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts index fe9168ab8..b2375b006 100644 --- a/server/models/account/account-blocklist.ts +++ b/server/models/account/account-blocklist.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import { Op } from 'sequelize' | 1 | import { Op } from 'sequelize' |
2 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/types/models' | 3 | import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { AccountBlock } from '../../../shared/models' | 5 | import { AccountBlock } from '../../../shared/models' |
5 | import { ActorModel } from '../activitypub/actor' | 6 | import { ActorModel } from '../actor/actor' |
6 | import { ServerModel } from '../server/server' | 7 | import { ServerModel } from '../server/server' |
7 | import { getSort, searchAttribute } from '../utils' | 8 | import { getSort, searchAttribute } from '../utils' |
8 | import { AccountModel } from './account' | 9 | import { AccountModel } from './account' |
@@ -40,7 +41,7 @@ enum ScopeNames { | |||
40 | } | 41 | } |
41 | ] | 42 | ] |
42 | }) | 43 | }) |
43 | export class AccountBlocklistModel extends Model { | 44 | export class AccountBlocklistModel extends Model<Partial<AttributesOnly<AccountBlocklistModel>>> { |
44 | 45 | ||
45 | @CreatedAt | 46 | @CreatedAt |
46 | createdAt: Date | 47 | createdAt: Date |
diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts index 801f76bba..ee6dbc6da 100644 --- a/server/models/account/account-video-rate.ts +++ b/server/models/account/account-video-rate.ts | |||
@@ -7,11 +7,12 @@ import { | |||
7 | MAccountVideoRateAccountVideo, | 7 | MAccountVideoRateAccountVideo, |
8 | MAccountVideoRateFormattable | 8 | MAccountVideoRateFormattable |
9 | } from '@server/types/models/video/video-rate' | 9 | } from '@server/types/models/video/video-rate' |
10 | import { AttributesOnly } from '@shared/core-utils' | ||
10 | import { AccountVideoRate } from '../../../shared' | 11 | import { AccountVideoRate } from '../../../shared' |
11 | import { VideoRateType } from '../../../shared/models/videos' | 12 | import { VideoRateType } from '../../../shared/models/videos' |
12 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 13 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
13 | import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants' | 14 | import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants' |
14 | import { ActorModel } from '../activitypub/actor' | 15 | import { ActorModel } from '../actor/actor' |
15 | import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' | 16 | import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils' |
16 | import { VideoModel } from '../video/video' | 17 | import { VideoModel } from '../video/video' |
17 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' | 18 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' |
@@ -42,7 +43,7 @@ import { AccountModel } from './account' | |||
42 | } | 43 | } |
43 | ] | 44 | ] |
44 | }) | 45 | }) |
45 | export class AccountVideoRateModel extends Model { | 46 | export class AccountVideoRateModel extends Model<Partial<AttributesOnly<AccountVideoRateModel>>> { |
46 | 47 | ||
47 | @AllowNull(false) | 48 | @AllowNull(false) |
48 | @Column(DataType.ENUM(...values(VIDEO_RATE_TYPES))) | 49 | @Column(DataType.ENUM(...values(VIDEO_RATE_TYPES))) |
diff --git a/server/models/account/account.ts b/server/models/account/account.ts index d33353af7..665ecd595 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts | |||
@@ -17,10 +17,11 @@ import { | |||
17 | UpdatedAt | 17 | UpdatedAt |
18 | } from 'sequelize-typescript' | 18 | } from 'sequelize-typescript' |
19 | import { ModelCache } from '@server/models/model-cache' | 19 | import { ModelCache } from '@server/models/model-cache' |
20 | import { AttributesOnly } from '@shared/core-utils' | ||
20 | import { Account, AccountSummary } from '../../../shared/models/actors' | 21 | import { Account, AccountSummary } from '../../../shared/models/actors' |
21 | import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts' | 22 | import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts' |
22 | import { CONSTRAINTS_FIELDS, SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' | 23 | import { CONSTRAINTS_FIELDS, SERVER_ACTOR_NAME, WEBSERVER } from '../../initializers/constants' |
23 | import { sendDeleteActor } from '../../lib/activitypub/send' | 24 | import { sendDeleteActor } from '../../lib/activitypub/send/send-delete' |
24 | import { | 25 | import { |
25 | MAccount, | 26 | MAccount, |
26 | MAccountActor, | 27 | MAccountActor, |
@@ -30,19 +31,19 @@ import { | |||
30 | MAccountSummaryFormattable, | 31 | MAccountSummaryFormattable, |
31 | MChannelActor | 32 | MChannelActor |
32 | } from '../../types/models' | 33 | } from '../../types/models' |
33 | import { ActorModel } from '../activitypub/actor' | 34 | import { ActorModel } from '../actor/actor' |
34 | import { ActorFollowModel } from '../activitypub/actor-follow' | 35 | import { ActorFollowModel } from '../actor/actor-follow' |
36 | import { ActorImageModel } from '../actor/actor-image' | ||
35 | import { ApplicationModel } from '../application/application' | 37 | import { ApplicationModel } from '../application/application' |
36 | import { ActorImageModel } from './actor-image' | ||
37 | import { ServerModel } from '../server/server' | 38 | import { ServerModel } from '../server/server' |
38 | import { ServerBlocklistModel } from '../server/server-blocklist' | 39 | import { ServerBlocklistModel } from '../server/server-blocklist' |
40 | import { UserModel } from '../user/user' | ||
39 | import { getSort, throwIfNotValid } from '../utils' | 41 | import { getSort, throwIfNotValid } from '../utils' |
40 | import { VideoModel } from '../video/video' | 42 | import { VideoModel } from '../video/video' |
41 | import { VideoChannelModel } from '../video/video-channel' | 43 | import { VideoChannelModel } from '../video/video-channel' |
42 | import { VideoCommentModel } from '../video/video-comment' | 44 | import { VideoCommentModel } from '../video/video-comment' |
43 | import { VideoPlaylistModel } from '../video/video-playlist' | 45 | import { VideoPlaylistModel } from '../video/video-playlist' |
44 | import { AccountBlocklistModel } from './account-blocklist' | 46 | import { AccountBlocklistModel } from './account-blocklist' |
45 | import { UserModel } from './user' | ||
46 | 47 | ||
47 | export enum ScopeNames { | 48 | export enum ScopeNames { |
48 | SUMMARY = 'SUMMARY' | 49 | SUMMARY = 'SUMMARY' |
@@ -141,7 +142,7 @@ export type SummaryOptions = { | |||
141 | } | 142 | } |
142 | ] | 143 | ] |
143 | }) | 144 | }) |
144 | export class AccountModel extends Model { | 145 | export class AccountModel extends Model<Partial<AttributesOnly<AccountModel>>> { |
145 | 146 | ||
146 | @AllowNull(false) | 147 | @AllowNull(false) |
147 | @Column | 148 | @Column |
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/actor/actor-follow.ts index 4c5f37620..3a09e51d6 100644 --- a/server/models/activitypub/actor-follow.ts +++ b/server/models/actor/actor-follow.ts | |||
@@ -28,6 +28,7 @@ import { | |||
28 | MActorFollowFormattable, | 28 | MActorFollowFormattable, |
29 | MActorFollowSubscriptions | 29 | MActorFollowSubscriptions |
30 | } from '@server/types/models' | 30 | } from '@server/types/models' |
31 | import { AttributesOnly } from '@shared/core-utils' | ||
31 | import { ActivityPubActorType } from '@shared/models' | 32 | import { ActivityPubActorType } from '@shared/models' |
32 | import { FollowState } from '../../../shared/models/actors' | 33 | import { FollowState } from '../../../shared/models/actors' |
33 | import { ActorFollow } from '../../../shared/models/actors/follow.model' | 34 | import { ActorFollow } from '../../../shared/models/actors/follow.model' |
@@ -61,7 +62,7 @@ import { ActorModel, unusedActorAttributesForAPI } from './actor' | |||
61 | } | 62 | } |
62 | ] | 63 | ] |
63 | }) | 64 | }) |
64 | export class ActorFollowModel extends Model { | 65 | export class ActorFollowModel extends Model<Partial<AttributesOnly<ActorFollowModel>>> { |
65 | 66 | ||
66 | @AllowNull(false) | 67 | @AllowNull(false) |
67 | @Column(DataType.ENUM(...values(FOLLOW_STATES))) | 68 | @Column(DataType.ENUM(...values(FOLLOW_STATES))) |
@@ -619,7 +620,7 @@ export class ActorFollowModel extends Model { | |||
619 | if (serverIds.length === 0) return | 620 | if (serverIds.length === 0) return |
620 | 621 | ||
621 | const me = await getServerActor() | 622 | const me = await getServerActor() |
622 | const serverIdsString = createSafeIn(ActorFollowModel, serverIds) | 623 | const serverIdsString = createSafeIn(ActorFollowModel.sequelize, serverIds) |
623 | 624 | ||
624 | const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` + | 625 | const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` + |
625 | 'WHERE id IN (' + | 626 | 'WHERE id IN (' + |
diff --git a/server/models/account/actor-image.ts b/server/models/actor/actor-image.ts index ae05b4969..a35f9edb0 100644 --- a/server/models/account/actor-image.ts +++ b/server/models/actor/actor-image.ts | |||
@@ -2,6 +2,7 @@ import { remove } from 'fs-extra' | |||
2 | import { join } from 'path' | 2 | import { join } from 'path' |
3 | import { AfterDestroy, AllowNull, Column, CreatedAt, Default, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 3 | import { AfterDestroy, AllowNull, Column, CreatedAt, Default, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
4 | import { MActorImageFormattable } from '@server/types/models' | 4 | import { MActorImageFormattable } from '@server/types/models' |
5 | import { AttributesOnly } from '@shared/core-utils' | ||
5 | import { ActorImageType } from '@shared/models' | 6 | import { ActorImageType } from '@shared/models' |
6 | import { ActorImage } from '../../../shared/models/actors/actor-image.model' | 7 | import { ActorImage } from '../../../shared/models/actors/actor-image.model' |
7 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 8 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
@@ -19,7 +20,7 @@ import { throwIfNotValid } from '../utils' | |||
19 | } | 20 | } |
20 | ] | 21 | ] |
21 | }) | 22 | }) |
22 | export class ActorImageModel extends Model { | 23 | export class ActorImageModel extends Model<Partial<AttributesOnly<ActorImageModel>>> { |
23 | 24 | ||
24 | @AllowNull(false) | 25 | @AllowNull(false) |
25 | @Column | 26 | @Column |
diff --git a/server/models/activitypub/actor.ts b/server/models/actor/actor.ts index 1af9efac2..65c53f8f8 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/actor/actor.ts | |||
@@ -18,6 +18,7 @@ import { | |||
18 | UpdatedAt | 18 | UpdatedAt |
19 | } from 'sequelize-typescript' | 19 | } from 'sequelize-typescript' |
20 | import { ModelCache } from '@server/models/model-cache' | 20 | import { ModelCache } from '@server/models/model-cache' |
21 | import { AttributesOnly } from '@shared/core-utils' | ||
21 | import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub' | 22 | import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub' |
22 | import { ActorImage } from '../../../shared/models/actors/actor-image.model' | 23 | import { ActorImage } from '../../../shared/models/actors/actor-image.model' |
23 | import { activityPubContextify } from '../../helpers/activitypub' | 24 | import { activityPubContextify } from '../../helpers/activitypub' |
@@ -51,12 +52,12 @@ import { | |||
51 | MActorWithInboxes | 52 | MActorWithInboxes |
52 | } from '../../types/models' | 53 | } from '../../types/models' |
53 | import { AccountModel } from '../account/account' | 54 | import { AccountModel } from '../account/account' |
54 | import { ActorImageModel } from '../account/actor-image' | ||
55 | import { ServerModel } from '../server/server' | 55 | import { ServerModel } from '../server/server' |
56 | import { isOutdated, throwIfNotValid } from '../utils' | 56 | import { isOutdated, throwIfNotValid } from '../utils' |
57 | import { VideoModel } from '../video/video' | 57 | import { VideoModel } from '../video/video' |
58 | import { VideoChannelModel } from '../video/video-channel' | 58 | import { VideoChannelModel } from '../video/video-channel' |
59 | import { ActorFollowModel } from './actor-follow' | 59 | import { ActorFollowModel } from './actor-follow' |
60 | import { ActorImageModel } from './actor-image' | ||
60 | 61 | ||
61 | enum ScopeNames { | 62 | enum ScopeNames { |
62 | FULL = 'FULL' | 63 | FULL = 'FULL' |
@@ -159,7 +160,7 @@ export const unusedActorAttributesForAPI = [ | |||
159 | } | 160 | } |
160 | ] | 161 | ] |
161 | }) | 162 | }) |
162 | export class ActorModel extends Model { | 163 | export class ActorModel extends Model<Partial<AttributesOnly<ActorModel>>> { |
163 | 164 | ||
164 | @AllowNull(false) | 165 | @AllowNull(false) |
165 | @Column(DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES))) | 166 | @Column(DataType.ENUM(...values(ACTIVITY_PUB_ACTOR_TYPES))) |
diff --git a/server/models/application/application.ts b/server/models/application/application.ts index 21f8b1cbc..5531d134a 100644 --- a/server/models/application/application.ts +++ b/server/models/application/application.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import * as memoizee from 'memoizee' | ||
1 | import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript' | 2 | import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript' |
3 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { AccountModel } from '../account/account' | 4 | import { AccountModel } from '../account/account' |
3 | import * as memoizee from 'memoizee' | ||
4 | 5 | ||
5 | export const getServerActor = memoizee(async function () { | 6 | export const getServerActor = memoizee(async function () { |
6 | const application = await ApplicationModel.load() | 7 | const application = await ApplicationModel.load() |
@@ -24,7 +25,7 @@ export const getServerActor = memoizee(async function () { | |||
24 | tableName: 'application', | 25 | tableName: 'application', |
25 | timestamps: false | 26 | timestamps: false |
26 | }) | 27 | }) |
27 | export class ApplicationModel extends Model { | 28 | export class ApplicationModel extends Model<Partial<AttributesOnly<ApplicationModel>>> { |
28 | 29 | ||
29 | @AllowNull(false) | 30 | @AllowNull(false) |
30 | @Default(0) | 31 | @Default(0) |
diff --git a/server/models/oauth/oauth-client.ts b/server/models/oauth/oauth-client.ts index 8dbc1c2f5..890954bdb 100644 --- a/server/models/oauth/oauth-client.ts +++ b/server/models/oauth/oauth-client.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { AllowNull, Column, CreatedAt, DataType, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, Column, CreatedAt, DataType, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { OAuthTokenModel } from './oauth-token' | 3 | import { OAuthTokenModel } from './oauth-token' |
3 | 4 | ||
4 | @Table({ | 5 | @Table({ |
@@ -14,7 +15,7 @@ import { OAuthTokenModel } from './oauth-token' | |||
14 | } | 15 | } |
15 | ] | 16 | ] |
16 | }) | 17 | }) |
17 | export class OAuthClientModel extends Model { | 18 | export class OAuthClientModel extends Model<Partial<AttributesOnly<OAuthClientModel>>> { |
18 | 19 | ||
19 | @AllowNull(false) | 20 | @AllowNull(false) |
20 | @Column | 21 | @Column |
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts index 27e643aa7..af4b0ec42 100644 --- a/server/models/oauth/oauth-token.ts +++ b/server/models/oauth/oauth-token.ts | |||
@@ -15,10 +15,11 @@ import { | |||
15 | import { TokensCache } from '@server/lib/auth/tokens-cache' | 15 | import { TokensCache } from '@server/lib/auth/tokens-cache' |
16 | import { MUserAccountId } from '@server/types/models' | 16 | import { MUserAccountId } from '@server/types/models' |
17 | import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token' | 17 | import { MOAuthTokenUser } from '@server/types/models/oauth/oauth-token' |
18 | import { AttributesOnly } from '@shared/core-utils' | ||
18 | import { logger } from '../../helpers/logger' | 19 | import { logger } from '../../helpers/logger' |
19 | import { AccountModel } from '../account/account' | 20 | import { AccountModel } from '../account/account' |
20 | import { UserModel } from '../account/user' | 21 | import { ActorModel } from '../actor/actor' |
21 | import { ActorModel } from '../activitypub/actor' | 22 | import { UserModel } from '../user/user' |
22 | import { OAuthClientModel } from './oauth-client' | 23 | import { OAuthClientModel } from './oauth-client' |
23 | 24 | ||
24 | export type OAuthTokenInfo = { | 25 | export type OAuthTokenInfo = { |
@@ -78,7 +79,7 @@ enum ScopeNames { | |||
78 | } | 79 | } |
79 | ] | 80 | ] |
80 | }) | 81 | }) |
81 | export class OAuthTokenModel extends Model { | 82 | export class OAuthTokenModel extends Model<Partial<AttributesOnly<OAuthTokenModel>>> { |
82 | 83 | ||
83 | @AllowNull(false) | 84 | @AllowNull(false) |
84 | @Column | 85 | @Column |
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts index 53ebadeaf..ef780c2a4 100644 --- a/server/models/redundancy/video-redundancy.ts +++ b/server/models/redundancy/video-redundancy.ts | |||
@@ -16,6 +16,7 @@ import { | |||
16 | } from 'sequelize-typescript' | 16 | } from 'sequelize-typescript' |
17 | import { getServerActor } from '@server/models/application/application' | 17 | import { getServerActor } from '@server/models/application/application' |
18 | import { MActor, MVideoForRedundancyAPI, MVideoRedundancy, MVideoRedundancyAP, MVideoRedundancyVideo } from '@server/types/models' | 18 | import { MActor, MVideoForRedundancyAPI, MVideoRedundancy, MVideoRedundancyAP, MVideoRedundancyVideo } from '@server/types/models' |
19 | import { AttributesOnly } from '@shared/core-utils' | ||
19 | import { VideoRedundanciesTarget } from '@shared/models/redundancy/video-redundancies-filters.model' | 20 | import { VideoRedundanciesTarget } from '@shared/models/redundancy/video-redundancies-filters.model' |
20 | import { | 21 | import { |
21 | FileRedundancyInformation, | 22 | FileRedundancyInformation, |
@@ -29,7 +30,7 @@ import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validato | |||
29 | import { logger } from '../../helpers/logger' | 30 | import { logger } from '../../helpers/logger' |
30 | import { CONFIG } from '../../initializers/config' | 31 | import { CONFIG } from '../../initializers/config' |
31 | import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants' | 32 | import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants' |
32 | import { ActorModel } from '../activitypub/actor' | 33 | import { ActorModel } from '../actor/actor' |
33 | import { ServerModel } from '../server/server' | 34 | import { ServerModel } from '../server/server' |
34 | import { getSort, getVideoSort, parseAggregateResult, throwIfNotValid } from '../utils' | 35 | import { getSort, getVideoSort, parseAggregateResult, throwIfNotValid } from '../utils' |
35 | import { ScheduleVideoUpdateModel } from '../video/schedule-video-update' | 36 | import { ScheduleVideoUpdateModel } from '../video/schedule-video-update' |
@@ -84,7 +85,7 @@ export enum ScopeNames { | |||
84 | } | 85 | } |
85 | ] | 86 | ] |
86 | }) | 87 | }) |
87 | export class VideoRedundancyModel extends Model { | 88 | export class VideoRedundancyModel extends Model<Partial<AttributesOnly<VideoRedundancyModel>>> { |
88 | 89 | ||
89 | @CreatedAt | 90 | @CreatedAt |
90 | createdAt: Date | 91 | createdAt: Date |
diff --git a/server/models/server/plugin.ts b/server/models/server/plugin.ts index 82387af6a..8bb9649da 100644 --- a/server/models/server/plugin.ts +++ b/server/models/server/plugin.ts | |||
@@ -1,9 +1,8 @@ | |||
1 | import { FindAndCountOptions, json, QueryTypes } from 'sequelize' | 1 | import { FindAndCountOptions, json, QueryTypes } from 'sequelize' |
2 | import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, Column, CreatedAt, DataType, DefaultScope, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MPlugin, MPluginFormattable } from '@server/types/models' | 3 | import { MPlugin, MPluginFormattable } from '@server/types/models' |
4 | import { PeerTubePlugin } from '../../../shared/models/plugins/peertube-plugin.model' | 4 | import { AttributesOnly } from '@shared/core-utils' |
5 | import { PluginType } from '../../../shared/models/plugins/plugin.type' | 5 | import { PeerTubePlugin, PluginType, RegisterServerSettingOptions } from '../../../shared/models' |
6 | import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model' | ||
7 | import { | 6 | import { |
8 | isPluginDescriptionValid, | 7 | isPluginDescriptionValid, |
9 | isPluginHomepage, | 8 | isPluginHomepage, |
@@ -28,7 +27,7 @@ import { getSort, throwIfNotValid } from '../utils' | |||
28 | } | 27 | } |
29 | ] | 28 | ] |
30 | }) | 29 | }) |
31 | export class PluginModel extends Model { | 30 | export class PluginModel extends Model<Partial<AttributesOnly<PluginModel>>> { |
32 | 31 | ||
33 | @AllowNull(false) | 32 | @AllowNull(false) |
34 | @Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name')) | 33 | @Is('PluginName', value => throwIfNotValid(value, isPluginNameValid, 'name')) |
diff --git a/server/models/server/server-blocklist.ts b/server/models/server/server-blocklist.ts index 4dc236537..b3579d589 100644 --- a/server/models/server/server-blocklist.ts +++ b/server/models/server/server-blocklist.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { Op } from 'sequelize' | 1 | import { Op } from 'sequelize' |
2 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MServerBlocklist, MServerBlocklistAccountServer, MServerBlocklistFormattable } from '@server/types/models' | 3 | import { MServerBlocklist, MServerBlocklistAccountServer, MServerBlocklistFormattable } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { ServerBlock } from '@shared/models' | 5 | import { ServerBlock } from '@shared/models' |
5 | import { AccountModel } from '../account/account' | 6 | import { AccountModel } from '../account/account' |
6 | import { getSort, searchAttribute } from '../utils' | 7 | import { getSort, searchAttribute } from '../utils' |
@@ -42,7 +43,7 @@ enum ScopeNames { | |||
42 | } | 43 | } |
43 | ] | 44 | ] |
44 | }) | 45 | }) |
45 | export class ServerBlocklistModel extends Model { | 46 | export class ServerBlocklistModel extends Model<Partial<AttributesOnly<ServerBlocklistModel>>> { |
46 | 47 | ||
47 | @CreatedAt | 48 | @CreatedAt |
48 | createdAt: Date | 49 | createdAt: Date |
diff --git a/server/models/server/server.ts b/server/models/server/server.ts index 0e58beeaf..25d9924fb 100644 --- a/server/models/server/server.ts +++ b/server/models/server/server.ts | |||
@@ -1,7 +1,8 @@ | |||
1 | import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, Column, CreatedAt, Default, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { MServer, MServerFormattable } from '@server/types/models/server' | 2 | import { MServer, MServerFormattable } from '@server/types/models/server' |
3 | import { AttributesOnly } from '@shared/core-utils' | ||
3 | import { isHostValid } from '../../helpers/custom-validators/servers' | 4 | import { isHostValid } from '../../helpers/custom-validators/servers' |
4 | import { ActorModel } from '../activitypub/actor' | 5 | import { ActorModel } from '../actor/actor' |
5 | import { throwIfNotValid } from '../utils' | 6 | import { throwIfNotValid } from '../utils' |
6 | import { ServerBlocklistModel } from './server-blocklist' | 7 | import { ServerBlocklistModel } from './server-blocklist' |
7 | 8 | ||
@@ -14,7 +15,7 @@ import { ServerBlocklistModel } from './server-blocklist' | |||
14 | } | 15 | } |
15 | ] | 16 | ] |
16 | }) | 17 | }) |
17 | export class ServerModel extends Model { | 18 | export class ServerModel extends Model<Partial<AttributesOnly<ServerModel>>> { |
18 | 19 | ||
19 | @AllowNull(false) | 20 | @AllowNull(false) |
20 | @Is('Host', value => throwIfNotValid(value, isHostValid, 'valid host')) | 21 | @Is('Host', value => throwIfNotValid(value, isHostValid, 'valid host')) |
diff --git a/server/models/server/tracker.ts b/server/models/server/tracker.ts index 97520f92d..c09fdd64b 100644 --- a/server/models/server/tracker.ts +++ b/server/models/server/tracker.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { AllowNull, BelongsToMany, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsToMany, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { Transaction } from 'sequelize/types' | 2 | import { Transaction } from 'sequelize/types' |
3 | import { MTracker } from '@server/types/models/server/tracker' | 3 | import { MTracker } from '@server/types/models/server/tracker' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { VideoModel } from '../video/video' | 5 | import { VideoModel } from '../video/video' |
5 | import { VideoTrackerModel } from './video-tracker' | 6 | import { VideoTrackerModel } from './video-tracker' |
6 | 7 | ||
@@ -13,7 +14,7 @@ import { VideoTrackerModel } from './video-tracker' | |||
13 | } | 14 | } |
14 | ] | 15 | ] |
15 | }) | 16 | }) |
16 | export class TrackerModel extends Model { | 17 | export class TrackerModel extends Model<Partial<AttributesOnly<TrackerModel>>> { |
17 | 18 | ||
18 | @AllowNull(false) | 19 | @AllowNull(false) |
19 | @Column | 20 | @Column |
diff --git a/server/models/server/video-tracker.ts b/server/models/server/video-tracker.ts index 367bf0117..c49fbd1c6 100644 --- a/server/models/server/video-tracker.ts +++ b/server/models/server/video-tracker.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { VideoModel } from '../video/video' | 3 | import { VideoModel } from '../video/video' |
3 | import { TrackerModel } from './tracker' | 4 | import { TrackerModel } from './tracker' |
4 | 5 | ||
@@ -13,7 +14,7 @@ import { TrackerModel } from './tracker' | |||
13 | } | 14 | } |
14 | ] | 15 | ] |
15 | }) | 16 | }) |
16 | export class VideoTrackerModel extends Model { | 17 | export class VideoTrackerModel extends Model<Partial<AttributesOnly<VideoTrackerModel>>> { |
17 | @CreatedAt | 18 | @CreatedAt |
18 | createdAt: Date | 19 | createdAt: Date |
19 | 20 | ||
diff --git a/server/models/account/user-notification-setting.ts b/server/models/user/user-notification-setting.ts index 138051528..bee7d7851 100644 --- a/server/models/account/user-notification-setting.ts +++ b/server/models/user/user-notification-setting.ts | |||
@@ -14,6 +14,7 @@ import { | |||
14 | } from 'sequelize-typescript' | 14 | } from 'sequelize-typescript' |
15 | import { TokensCache } from '@server/lib/auth/tokens-cache' | 15 | import { TokensCache } from '@server/lib/auth/tokens-cache' |
16 | import { MNotificationSettingFormattable } from '@server/types/models' | 16 | import { MNotificationSettingFormattable } from '@server/types/models' |
17 | import { AttributesOnly } from '@shared/core-utils' | ||
17 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model' | 18 | import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model' |
18 | import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' | 19 | import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' |
19 | import { throwIfNotValid } from '../utils' | 20 | import { throwIfNotValid } from '../utils' |
@@ -28,7 +29,7 @@ import { UserModel } from './user' | |||
28 | } | 29 | } |
29 | ] | 30 | ] |
30 | }) | 31 | }) |
31 | export class UserNotificationSettingModel extends Model { | 32 | export class UserNotificationSettingModel extends Model<Partial<AttributesOnly<UserNotificationSettingModel>>> { |
32 | 33 | ||
33 | @AllowNull(false) | 34 | @AllowNull(false) |
34 | @Default(null) | 35 | @Default(null) |
diff --git a/server/models/account/user-notification.ts b/server/models/user/user-notification.ts index 805095002..a7f84e9ca 100644 --- a/server/models/account/user-notification.ts +++ b/server/models/user/user-notification.ts | |||
@@ -1,14 +1,17 @@ | |||
1 | import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize' | 1 | import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize' |
2 | import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user' | 3 | import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { UserNotification, UserNotificationType } from '../../../shared' | 5 | import { UserNotification, UserNotificationType } from '../../../shared' |
5 | import { isBooleanValid } from '../../helpers/custom-validators/misc' | 6 | import { isBooleanValid } from '../../helpers/custom-validators/misc' |
6 | import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications' | 7 | import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications' |
7 | import { AbuseModel } from '../abuse/abuse' | 8 | import { AbuseModel } from '../abuse/abuse' |
8 | import { VideoAbuseModel } from '../abuse/video-abuse' | 9 | import { VideoAbuseModel } from '../abuse/video-abuse' |
9 | import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' | 10 | import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' |
10 | import { ActorModel } from '../activitypub/actor' | 11 | import { AccountModel } from '../account/account' |
11 | import { ActorFollowModel } from '../activitypub/actor-follow' | 12 | import { ActorModel } from '../actor/actor' |
13 | import { ActorFollowModel } from '../actor/actor-follow' | ||
14 | import { ActorImageModel } from '../actor/actor-image' | ||
12 | import { ApplicationModel } from '../application/application' | 15 | import { ApplicationModel } from '../application/application' |
13 | import { PluginModel } from '../server/plugin' | 16 | import { PluginModel } from '../server/plugin' |
14 | import { ServerModel } from '../server/server' | 17 | import { ServerModel } from '../server/server' |
@@ -18,8 +21,6 @@ import { VideoBlacklistModel } from '../video/video-blacklist' | |||
18 | import { VideoChannelModel } from '../video/video-channel' | 21 | import { VideoChannelModel } from '../video/video-channel' |
19 | import { VideoCommentModel } from '../video/video-comment' | 22 | import { VideoCommentModel } from '../video/video-comment' |
20 | import { VideoImportModel } from '../video/video-import' | 23 | import { VideoImportModel } from '../video/video-import' |
21 | import { AccountModel } from './account' | ||
22 | import { ActorImageModel } from './actor-image' | ||
23 | import { UserModel } from './user' | 24 | import { UserModel } from './user' |
24 | 25 | ||
25 | enum ScopeNames { | 26 | enum ScopeNames { |
@@ -286,7 +287,7 @@ function buildAccountInclude (required: boolean, withActor = false) { | |||
286 | } | 287 | } |
287 | ] as (ModelIndexesOptions & { where?: WhereOptions })[] | 288 | ] as (ModelIndexesOptions & { where?: WhereOptions })[] |
288 | }) | 289 | }) |
289 | export class UserNotificationModel extends Model { | 290 | export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNotificationModel>>> { |
290 | 291 | ||
291 | @AllowNull(false) | 292 | @AllowNull(false) |
292 | @Default(null) | 293 | @Default(null) |
diff --git a/server/models/account/user-video-history.ts b/server/models/user/user-video-history.ts index 6be1d65ea..e3dc4a062 100644 --- a/server/models/account/user-video-history.ts +++ b/server/models/user/user-video-history.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import { DestroyOptions, Op, Transaction } from 'sequelize' | ||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MUserAccountId, MUserId } from '@server/types/models' | ||
4 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { VideoModel } from '../video/video' | 5 | import { VideoModel } from '../video/video' |
3 | import { UserModel } from './user' | 6 | import { UserModel } from './user' |
4 | import { DestroyOptions, Op, Transaction } from 'sequelize' | ||
5 | import { MUserAccountId, MUserId } from '@server/types/models' | ||
6 | 7 | ||
7 | @Table({ | 8 | @Table({ |
8 | tableName: 'userVideoHistory', | 9 | tableName: 'userVideoHistory', |
@@ -19,7 +20,7 @@ import { MUserAccountId, MUserId } from '@server/types/models' | |||
19 | } | 20 | } |
20 | ] | 21 | ] |
21 | }) | 22 | }) |
22 | export class UserVideoHistoryModel extends Model { | 23 | export class UserVideoHistoryModel extends Model<Partial<AttributesOnly<UserVideoHistoryModel>>> { |
23 | @CreatedAt | 24 | @CreatedAt |
24 | createdAt: Date | 25 | createdAt: Date |
25 | 26 | ||
diff --git a/server/models/account/user.ts b/server/models/user/user.ts index 513455773..20696b1f4 100644 --- a/server/models/account/user.ts +++ b/server/models/user/user.ts | |||
@@ -31,6 +31,7 @@ import { | |||
31 | MUserWithNotificationSetting, | 31 | MUserWithNotificationSetting, |
32 | MVideoWithRights | 32 | MVideoWithRights |
33 | } from '@server/types/models' | 33 | } from '@server/types/models' |
34 | import { AttributesOnly } from '@shared/core-utils' | ||
34 | import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users' | 35 | import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users' |
35 | import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models' | 36 | import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models' |
36 | import { User, UserRole } from '../../../shared/models/users' | 37 | import { User, UserRole } from '../../../shared/models/users' |
@@ -60,8 +61,10 @@ import { | |||
60 | import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' | 61 | import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' |
61 | import { DEFAULT_USER_THEME_NAME, NSFW_POLICY_TYPES } from '../../initializers/constants' | 62 | import { DEFAULT_USER_THEME_NAME, NSFW_POLICY_TYPES } from '../../initializers/constants' |
62 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' | 63 | import { getThemeOrDefault } from '../../lib/plugins/theme-utils' |
63 | import { ActorModel } from '../activitypub/actor' | 64 | import { AccountModel } from '../account/account' |
64 | import { ActorFollowModel } from '../activitypub/actor-follow' | 65 | import { ActorModel } from '../actor/actor' |
66 | import { ActorFollowModel } from '../actor/actor-follow' | ||
67 | import { ActorImageModel } from '../actor/actor-image' | ||
65 | import { OAuthTokenModel } from '../oauth/oauth-token' | 68 | import { OAuthTokenModel } from '../oauth/oauth-token' |
66 | import { getSort, throwIfNotValid } from '../utils' | 69 | import { getSort, throwIfNotValid } from '../utils' |
67 | import { VideoModel } from '../video/video' | 70 | import { VideoModel } from '../video/video' |
@@ -69,9 +72,7 @@ import { VideoChannelModel } from '../video/video-channel' | |||
69 | import { VideoImportModel } from '../video/video-import' | 72 | import { VideoImportModel } from '../video/video-import' |
70 | import { VideoLiveModel } from '../video/video-live' | 73 | import { VideoLiveModel } from '../video/video-live' |
71 | import { VideoPlaylistModel } from '../video/video-playlist' | 74 | import { VideoPlaylistModel } from '../video/video-playlist' |
72 | import { AccountModel } from './account' | ||
73 | import { UserNotificationSettingModel } from './user-notification-setting' | 75 | import { UserNotificationSettingModel } from './user-notification-setting' |
74 | import { ActorImageModel } from './actor-image' | ||
75 | 76 | ||
76 | enum ScopeNames { | 77 | enum ScopeNames { |
77 | FOR_ME_API = 'FOR_ME_API', | 78 | FOR_ME_API = 'FOR_ME_API', |
@@ -233,7 +234,7 @@ enum ScopeNames { | |||
233 | } | 234 | } |
234 | ] | 235 | ] |
235 | }) | 236 | }) |
236 | export class UserModel extends Model { | 237 | export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> { |
237 | 238 | ||
238 | @AllowNull(true) | 239 | @AllowNull(true) |
239 | @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true)) | 240 | @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true)) |
diff --git a/server/models/utils.ts b/server/models/utils.ts index ec51c66bf..e27625bc8 100644 --- a/server/models/utils.ts +++ b/server/models/utils.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import { literal, Op, OrderItem } from 'sequelize' | 1 | import { literal, Op, OrderItem, Sequelize } from 'sequelize' |
2 | import { Model, Sequelize } from 'sequelize-typescript' | ||
3 | import { Col } from 'sequelize/types/lib/utils' | 2 | import { Col } from 'sequelize/types/lib/utils' |
4 | import validator from 'validator' | 3 | import validator from 'validator' |
5 | 4 | ||
@@ -195,11 +194,11 @@ function parseAggregateResult (result: any) { | |||
195 | return total | 194 | return total |
196 | } | 195 | } |
197 | 196 | ||
198 | const createSafeIn = (model: typeof Model, stringArr: (string | number)[]) => { | 197 | function createSafeIn (sequelize: Sequelize, stringArr: (string | number)[]) { |
199 | return stringArr.map(t => { | 198 | return stringArr.map(t => { |
200 | return t === null | 199 | return t === null |
201 | ? null | 200 | ? null |
202 | : model.sequelize.escape('' + t) | 201 | : sequelize.escape('' + t) |
203 | }).join(', ') | 202 | }).join(', ') |
204 | } | 203 | } |
205 | 204 | ||
diff --git a/server/models/video/schedule-video-update.ts b/server/models/video/schedule-video-update.ts index 22b08e91a..b0952c431 100644 --- a/server/models/video/schedule-video-update.ts +++ b/server/models/video/schedule-video-update.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | ||
2 | import { ScopeNames as VideoScopeNames, VideoModel } from './video' | ||
3 | import { VideoPrivacy } from '../../../shared/models/videos' | ||
4 | import { Op, Transaction } from 'sequelize' | 1 | import { Op, Transaction } from 'sequelize' |
2 | import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | ||
5 | import { MScheduleVideoUpdateFormattable, MScheduleVideoUpdateVideoAll } from '@server/types/models' | 3 | import { MScheduleVideoUpdateFormattable, MScheduleVideoUpdateVideoAll } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
5 | import { VideoPrivacy } from '../../../shared/models/videos' | ||
6 | import { ScopeNames as VideoScopeNames, VideoModel } from './video' | ||
6 | 7 | ||
7 | @Table({ | 8 | @Table({ |
8 | tableName: 'scheduleVideoUpdate', | 9 | tableName: 'scheduleVideoUpdate', |
@@ -16,7 +17,7 @@ import { MScheduleVideoUpdateFormattable, MScheduleVideoUpdateVideoAll } from '@ | |||
16 | } | 17 | } |
17 | ] | 18 | ] |
18 | }) | 19 | }) |
19 | export class ScheduleVideoUpdateModel extends Model { | 20 | export class ScheduleVideoUpdateModel extends Model<Partial<AttributesOnly<ScheduleVideoUpdateModel>>> { |
20 | 21 | ||
21 | @AllowNull(false) | 22 | @AllowNull(false) |
22 | @Default(null) | 23 | @Default(null) |
diff --git a/server/models/video/tag.ts b/server/models/video/tag.ts index d04205703..c1eebe27f 100644 --- a/server/models/video/tag.ts +++ b/server/models/video/tag.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { col, fn, QueryTypes, Transaction } from 'sequelize' | 1 | import { col, fn, QueryTypes, Transaction } from 'sequelize' |
2 | import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MTag } from '@server/types/models' | 3 | import { MTag } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { VideoPrivacy, VideoState } from '../../../shared/models/videos' | 5 | import { VideoPrivacy, VideoState } from '../../../shared/models/videos' |
5 | import { isVideoTagValid } from '../../helpers/custom-validators/videos' | 6 | import { isVideoTagValid } from '../../helpers/custom-validators/videos' |
6 | import { throwIfNotValid } from '../utils' | 7 | import { throwIfNotValid } from '../utils' |
@@ -21,7 +22,7 @@ import { VideoTagModel } from './video-tag' | |||
21 | } | 22 | } |
22 | ] | 23 | ] |
23 | }) | 24 | }) |
24 | export class TagModel extends Model { | 25 | export class TagModel extends Model<Partial<AttributesOnly<TagModel>>> { |
25 | 26 | ||
26 | @AllowNull(false) | 27 | @AllowNull(false) |
27 | @Is('VideoTag', value => throwIfNotValid(value, isVideoTagValid, 'tag')) | 28 | @Is('VideoTag', value => throwIfNotValid(value, isVideoTagValid, 'tag')) |
diff --git a/server/models/video/thumbnail.ts b/server/models/video/thumbnail.ts index f1187c8d6..3388478d9 100644 --- a/server/models/video/thumbnail.ts +++ b/server/models/video/thumbnail.ts | |||
@@ -17,6 +17,7 @@ import { | |||
17 | } from 'sequelize-typescript' | 17 | } from 'sequelize-typescript' |
18 | import { afterCommitIfTransaction } from '@server/helpers/database-utils' | 18 | import { afterCommitIfTransaction } from '@server/helpers/database-utils' |
19 | import { MThumbnail, MThumbnailVideo, MVideo } from '@server/types/models' | 19 | import { MThumbnail, MThumbnailVideo, MVideo } from '@server/types/models' |
20 | import { AttributesOnly } from '@shared/core-utils' | ||
20 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' | 21 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' |
21 | import { logger } from '../../helpers/logger' | 22 | import { logger } from '../../helpers/logger' |
22 | import { CONFIG } from '../../initializers/config' | 23 | import { CONFIG } from '../../initializers/config' |
@@ -40,7 +41,7 @@ import { VideoPlaylistModel } from './video-playlist' | |||
40 | } | 41 | } |
41 | ] | 42 | ] |
42 | }) | 43 | }) |
43 | export class ThumbnailModel extends Model { | 44 | export class ThumbnailModel extends Model<Partial<AttributesOnly<ThumbnailModel>>> { |
44 | 45 | ||
45 | @AllowNull(false) | 46 | @AllowNull(false) |
46 | @Column | 47 | @Column |
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts index aa18896da..98f4ec9c5 100644 --- a/server/models/video/video-blacklist.ts +++ b/server/models/video/video-blacklist.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { FindOptions } from 'sequelize' | 1 | import { FindOptions } from 'sequelize' |
2 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/types/models' | 3 | import { MVideoBlacklist, MVideoBlacklistFormattable } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' | 5 | import { VideoBlacklist, VideoBlacklistType } from '../../../shared/models/videos' |
5 | import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' | 6 | import { isVideoBlacklistReasonValid, isVideoBlacklistTypeValid } from '../../helpers/custom-validators/video-blacklist' |
6 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' | 7 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' |
@@ -18,7 +19,7 @@ import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel | |||
18 | } | 19 | } |
19 | ] | 20 | ] |
20 | }) | 21 | }) |
21 | export class VideoBlacklistModel extends Model { | 22 | export class VideoBlacklistModel extends Model<Partial<AttributesOnly<VideoBlacklistModel>>> { |
22 | 23 | ||
23 | @AllowNull(true) | 24 | @AllowNull(true) |
24 | @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true)) | 25 | @Is('VideoBlacklistReason', value => throwIfNotValid(value, isVideoBlacklistReasonValid, 'reason', true)) |
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts index bfdec73e9..d2c742b66 100644 --- a/server/models/video/video-caption.ts +++ b/server/models/video/video-caption.ts | |||
@@ -17,6 +17,7 @@ import { | |||
17 | } from 'sequelize-typescript' | 17 | } from 'sequelize-typescript' |
18 | import { v4 as uuidv4 } from 'uuid' | 18 | import { v4 as uuidv4 } from 'uuid' |
19 | import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models' | 19 | import { MVideo, MVideoCaption, MVideoCaptionFormattable, MVideoCaptionVideo } from '@server/types/models' |
20 | import { AttributesOnly } from '@shared/core-utils' | ||
20 | import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' | 21 | import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model' |
21 | import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' | 22 | import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions' |
22 | import { logger } from '../../helpers/logger' | 23 | import { logger } from '../../helpers/logger' |
@@ -57,7 +58,7 @@ export enum ScopeNames { | |||
57 | } | 58 | } |
58 | ] | 59 | ] |
59 | }) | 60 | }) |
60 | export class VideoCaptionModel extends Model { | 61 | export class VideoCaptionModel extends Model<Partial<AttributesOnly<VideoCaptionModel>>> { |
61 | @CreatedAt | 62 | @CreatedAt |
62 | createdAt: Date | 63 | createdAt: Date |
63 | 64 | ||
diff --git a/server/models/video/video-change-ownership.ts b/server/models/video/video-change-ownership.ts index 298e8bfe2..7d20a954d 100644 --- a/server/models/video/video-change-ownership.ts +++ b/server/models/video/video-change-ownership.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { MVideoChangeOwnershipFormattable, MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership' | 2 | import { MVideoChangeOwnershipFormattable, MVideoChangeOwnershipFull } from '@server/types/models/video/video-change-ownership' |
3 | import { AttributesOnly } from '@shared/core-utils' | ||
3 | import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos' | 4 | import { VideoChangeOwnership, VideoChangeOwnershipStatus } from '../../../shared/models/videos' |
4 | import { AccountModel } from '../account/account' | 5 | import { AccountModel } from '../account/account' |
5 | import { getSort } from '../utils' | 6 | import { getSort } from '../utils' |
@@ -53,7 +54,7 @@ enum ScopeNames { | |||
53 | ] | 54 | ] |
54 | } | 55 | } |
55 | })) | 56 | })) |
56 | export class VideoChangeOwnershipModel extends Model { | 57 | export class VideoChangeOwnershipModel extends Model<Partial<AttributesOnly<VideoChangeOwnershipModel>>> { |
57 | @CreatedAt | 58 | @CreatedAt |
58 | createdAt: Date | 59 | createdAt: Date |
59 | 60 | ||
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 081b21f2d..8c4357009 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -19,6 +19,7 @@ import { | |||
19 | } from 'sequelize-typescript' | 19 | } from 'sequelize-typescript' |
20 | import { setAsUpdated } from '@server/helpers/database-utils' | 20 | import { setAsUpdated } from '@server/helpers/database-utils' |
21 | import { MAccountActor } from '@server/types/models' | 21 | import { MAccountActor } from '@server/types/models' |
22 | import { AttributesOnly } from '@shared/core-utils' | ||
22 | import { ActivityPubActor } from '../../../shared/models/activitypub' | 23 | import { ActivityPubActor } from '../../../shared/models/activitypub' |
23 | import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' | 24 | import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' |
24 | import { | 25 | import { |
@@ -36,9 +37,9 @@ import { | |||
36 | MChannelSummaryFormattable | 37 | MChannelSummaryFormattable |
37 | } from '../../types/models/video' | 38 | } from '../../types/models/video' |
38 | import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' | 39 | import { AccountModel, ScopeNames as AccountModelScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' |
39 | import { ActorImageModel } from '../account/actor-image' | 40 | import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor' |
40 | import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' | 41 | import { ActorFollowModel } from '../actor/actor-follow' |
41 | import { ActorFollowModel } from '../activitypub/actor-follow' | 42 | import { ActorImageModel } from '../actor/actor-image' |
42 | import { ServerModel } from '../server/server' | 43 | import { ServerModel } from '../server/server' |
43 | import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' | 44 | import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' |
44 | import { VideoModel } from './video' | 45 | import { VideoModel } from './video' |
@@ -246,7 +247,7 @@ export type SummaryOptions = { | |||
246 | } | 247 | } |
247 | ] | 248 | ] |
248 | }) | 249 | }) |
249 | export class VideoChannelModel extends Model { | 250 | export class VideoChannelModel extends Model<Partial<AttributesOnly<VideoChannelModel>>> { |
250 | 251 | ||
251 | @AllowNull(false) | 252 | @AllowNull(false) |
252 | @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name')) | 253 | @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name')) |
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index 151c2bc81..bdf5d86bc 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts | |||
@@ -16,10 +16,11 @@ import { | |||
16 | } from 'sequelize-typescript' | 16 | } from 'sequelize-typescript' |
17 | import { getServerActor } from '@server/models/application/application' | 17 | import { getServerActor } from '@server/models/application/application' |
18 | import { MAccount, MAccountId, MUserAccountId } from '@server/types/models' | 18 | import { MAccount, MAccountId, MUserAccountId } from '@server/types/models' |
19 | import { AttributesOnly } from '@shared/core-utils' | ||
19 | import { VideoPrivacy } from '@shared/models' | 20 | import { VideoPrivacy } from '@shared/models' |
20 | import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects' | 21 | import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects' |
21 | import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' | 22 | import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' |
22 | import { VideoComment, VideoCommentAdmin } from '../../../shared/models/videos/video-comment.model' | 23 | import { VideoComment, VideoCommentAdmin } from '../../../shared/models/videos/comment/video-comment.model' |
23 | import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' | 24 | import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' |
24 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 25 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
25 | import { regexpCapture } from '../../helpers/regexp' | 26 | import { regexpCapture } from '../../helpers/regexp' |
@@ -39,7 +40,7 @@ import { | |||
39 | } from '../../types/models/video' | 40 | } from '../../types/models/video' |
40 | import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' | 41 | import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' |
41 | import { AccountModel } from '../account/account' | 42 | import { AccountModel } from '../account/account' |
42 | import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' | 43 | import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor' |
43 | import { | 44 | import { |
44 | buildBlockedAccountSQL, | 45 | buildBlockedAccountSQL, |
45 | buildBlockedAccountSQLOptimized, | 46 | buildBlockedAccountSQLOptimized, |
@@ -173,7 +174,7 @@ export enum ScopeNames { | |||
173 | } | 174 | } |
174 | ] | 175 | ] |
175 | }) | 176 | }) |
176 | export class VideoCommentModel extends Model { | 177 | export class VideoCommentModel extends Model<Partial<AttributesOnly<VideoCommentModel>>> { |
177 | @CreatedAt | 178 | @CreatedAt |
178 | createdAt: Date | 179 | createdAt: Date |
179 | 180 | ||
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts index 1ad796104..e3fa2f3d2 100644 --- a/server/models/video/video-file.ts +++ b/server/models/video/video-file.ts | |||
@@ -25,6 +25,7 @@ import { logger } from '@server/helpers/logger' | |||
25 | import { extractVideo } from '@server/helpers/video' | 25 | import { extractVideo } from '@server/helpers/video' |
26 | import { getTorrentFilePath } from '@server/lib/video-paths' | 26 | import { getTorrentFilePath } from '@server/lib/video-paths' |
27 | import { MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models' | 27 | import { MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models' |
28 | import { AttributesOnly } from '@shared/core-utils' | ||
28 | import { | 29 | import { |
29 | isVideoFileExtnameValid, | 30 | isVideoFileExtnameValid, |
30 | isVideoFileInfoHashValid, | 31 | isVideoFileInfoHashValid, |
@@ -149,7 +150,7 @@ export enum ScopeNames { | |||
149 | } | 150 | } |
150 | ] | 151 | ] |
151 | }) | 152 | }) |
152 | export class VideoFileModel extends Model { | 153 | export class VideoFileModel extends Model<Partial<AttributesOnly<VideoFileModel>>> { |
153 | @CreatedAt | 154 | @CreatedAt |
154 | createdAt: Date | 155 | createdAt: Date |
155 | 156 | ||
diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts index 8324166cc..5c73fb07c 100644 --- a/server/models/video/video-import.ts +++ b/server/models/video/video-import.ts | |||
@@ -13,15 +13,16 @@ import { | |||
13 | Table, | 13 | Table, |
14 | UpdatedAt | 14 | UpdatedAt |
15 | } from 'sequelize-typescript' | 15 | } from 'sequelize-typescript' |
16 | import { afterCommitIfTransaction } from '@server/helpers/database-utils' | ||
16 | import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import' | 17 | import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import' |
18 | import { AttributesOnly } from '@shared/core-utils' | ||
17 | import { VideoImport, VideoImportState } from '../../../shared' | 19 | import { VideoImport, VideoImportState } from '../../../shared' |
18 | import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports' | 20 | import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports' |
19 | import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' | 21 | import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' |
20 | import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers/constants' | 22 | import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers/constants' |
21 | import { UserModel } from '../account/user' | 23 | import { UserModel } from '../user/user' |
22 | import { getSort, throwIfNotValid } from '../utils' | 24 | import { getSort, throwIfNotValid } from '../utils' |
23 | import { ScopeNames as VideoModelScopeNames, VideoModel } from './video' | 25 | import { ScopeNames as VideoModelScopeNames, VideoModel } from './video' |
24 | import { afterCommitIfTransaction } from '@server/helpers/database-utils' | ||
25 | 26 | ||
26 | @DefaultScope(() => ({ | 27 | @DefaultScope(() => ({ |
27 | include: [ | 28 | include: [ |
@@ -52,7 +53,7 @@ import { afterCommitIfTransaction } from '@server/helpers/database-utils' | |||
52 | } | 53 | } |
53 | ] | 54 | ] |
54 | }) | 55 | }) |
55 | export class VideoImportModel extends Model { | 56 | export class VideoImportModel extends Model<Partial<AttributesOnly<VideoImportModel>>> { |
56 | @CreatedAt | 57 | @CreatedAt |
57 | createdAt: Date | 58 | createdAt: Date |
58 | 59 | ||
diff --git a/server/models/video/video-live.ts b/server/models/video/video-live.ts index cb4a9b896..014491d50 100644 --- a/server/models/video/video-live.ts +++ b/server/models/video/video-live.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, DefaultScope, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, DefaultScope, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { WEBSERVER } from '@server/initializers/constants' | 2 | import { WEBSERVER } from '@server/initializers/constants' |
3 | import { MVideoLive, MVideoLiveVideo } from '@server/types/models' | 3 | import { MVideoLive, MVideoLiveVideo } from '@server/types/models' |
4 | import { AttributesOnly } from '@shared/core-utils' | ||
4 | import { LiveVideo, VideoState } from '@shared/models' | 5 | import { LiveVideo, VideoState } from '@shared/models' |
5 | import { VideoModel } from './video' | 6 | import { VideoModel } from './video' |
6 | import { VideoBlacklistModel } from './video-blacklist' | 7 | import { VideoBlacklistModel } from './video-blacklist' |
@@ -28,7 +29,7 @@ import { VideoBlacklistModel } from './video-blacklist' | |||
28 | } | 29 | } |
29 | ] | 30 | ] |
30 | }) | 31 | }) |
31 | export class VideoLiveModel extends Model { | 32 | export class VideoLiveModel extends Model<Partial<AttributesOnly<VideoLiveModel>>> { |
32 | 33 | ||
33 | @AllowNull(true) | 34 | @AllowNull(true) |
34 | @Column(DataType.STRING) | 35 | @Column(DataType.STRING) |
diff --git a/server/models/video/video-playlist-element.ts b/server/models/video/video-playlist-element.ts index d2d7e2740..e6906cb19 100644 --- a/server/models/video/video-playlist-element.ts +++ b/server/models/video/video-playlist-element.ts | |||
@@ -32,6 +32,7 @@ import { AccountModel } from '../account/account' | |||
32 | import { getSort, throwIfNotValid } from '../utils' | 32 | import { getSort, throwIfNotValid } from '../utils' |
33 | import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video' | 33 | import { ForAPIOptions, ScopeNames as VideoScopeNames, VideoModel } from './video' |
34 | import { VideoPlaylistModel } from './video-playlist' | 34 | import { VideoPlaylistModel } from './video-playlist' |
35 | import { AttributesOnly } from '@shared/core-utils' | ||
35 | 36 | ||
36 | @Table({ | 37 | @Table({ |
37 | tableName: 'videoPlaylistElement', | 38 | tableName: 'videoPlaylistElement', |
@@ -48,7 +49,7 @@ import { VideoPlaylistModel } from './video-playlist' | |||
48 | } | 49 | } |
49 | ] | 50 | ] |
50 | }) | 51 | }) |
51 | export class VideoPlaylistElementModel extends Model { | 52 | export class VideoPlaylistElementModel extends Model<Partial<AttributesOnly<VideoPlaylistElementModel>>> { |
52 | @CreatedAt | 53 | @CreatedAt |
53 | createdAt: Date | 54 | createdAt: Date |
54 | 55 | ||
@@ -274,7 +275,8 @@ export class VideoPlaylistElementModel extends Model { | |||
274 | validate: false // We use a literal to update the position | 275 | validate: false // We use a literal to update the position |
275 | } | 276 | } |
276 | 277 | ||
277 | return VideoPlaylistElementModel.update({ position: Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`) }, query) | 278 | const positionQuery = Sequelize.literal(`${newPosition} + "position" - ${firstPosition}`) |
279 | return VideoPlaylistElementModel.update({ position: positionQuery as any }, query) | ||
278 | } | 280 | } |
279 | 281 | ||
280 | static increasePositionOf ( | 282 | static increasePositionOf ( |
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index efe5be36d..c293287d3 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts | |||
@@ -19,6 +19,7 @@ import { | |||
19 | } from 'sequelize-typescript' | 19 | } from 'sequelize-typescript' |
20 | import { v4 as uuidv4 } from 'uuid' | 20 | import { v4 as uuidv4 } from 'uuid' |
21 | import { MAccountId, MChannelId } from '@server/types/models' | 21 | import { MAccountId, MChannelId } from '@server/types/models' |
22 | import { AttributesOnly } from '@shared/core-utils' | ||
22 | import { ActivityIconObject } from '../../../shared/models/activitypub/objects' | 23 | import { ActivityIconObject } from '../../../shared/models/activitypub/objects' |
23 | import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' | 24 | import { PlaylistObject } from '../../../shared/models/activitypub/objects/playlist-object' |
24 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' | 25 | import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model' |
@@ -50,11 +51,11 @@ import { | |||
50 | MVideoPlaylistIdWithElements | 51 | MVideoPlaylistIdWithElements |
51 | } from '../../types/models/video/video-playlist' | 52 | } from '../../types/models/video/video-playlist' |
52 | import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account' | 53 | import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account' |
54 | import { ActorModel } from '../actor/actor' | ||
53 | import { buildServerIdsFollowedBy, buildWhereIdOrUUID, getPlaylistSort, isOutdated, throwIfNotValid } from '../utils' | 55 | import { buildServerIdsFollowedBy, buildWhereIdOrUUID, getPlaylistSort, isOutdated, throwIfNotValid } from '../utils' |
54 | import { ThumbnailModel } from './thumbnail' | 56 | import { ThumbnailModel } from './thumbnail' |
55 | import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel' | 57 | import { ScopeNames as VideoChannelScopeNames, VideoChannelModel } from './video-channel' |
56 | import { VideoPlaylistElementModel } from './video-playlist-element' | 58 | import { VideoPlaylistElementModel } from './video-playlist-element' |
57 | import { ActorModel } from '../activitypub/actor' | ||
58 | 59 | ||
59 | enum ScopeNames { | 60 | enum ScopeNames { |
60 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', | 61 | AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', |
@@ -221,7 +222,7 @@ type AvailableForListOptions = { | |||
221 | } | 222 | } |
222 | ] | 223 | ] |
223 | }) | 224 | }) |
224 | export class VideoPlaylistModel extends Model { | 225 | export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlaylistModel>>> { |
225 | @CreatedAt | 226 | @CreatedAt |
226 | createdAt: Date | 227 | createdAt: Date |
227 | 228 | ||
diff --git a/server/models/video/video-query-builder.ts b/server/models/video/video-query-builder.ts index 155afe64b..2aa5e65c8 100644 --- a/server/models/video/video-query-builder.ts +++ b/server/models/video/video-query-builder.ts | |||
@@ -1,9 +1,9 @@ | |||
1 | import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models' | 1 | import { Sequelize } from 'sequelize/types' |
2 | import { buildDirectionAndField, createSafeIn } from '@server/models/utils' | ||
3 | import { Model } from 'sequelize-typescript' | ||
4 | import { MUserAccountId, MUserId } from '@server/types/models' | ||
5 | import validator from 'validator' | 2 | import validator from 'validator' |
6 | import { exists } from '@server/helpers/custom-validators/misc' | 3 | import { exists } from '@server/helpers/custom-validators/misc' |
4 | import { buildDirectionAndField, createSafeIn } from '@server/models/utils' | ||
5 | import { MUserAccountId, MUserId } from '@server/types/models' | ||
6 | import { VideoFilter, VideoPrivacy, VideoState } from '@shared/models' | ||
7 | 7 | ||
8 | export type BuildVideosQueryOptions = { | 8 | export type BuildVideosQueryOptions = { |
9 | attributes?: string[] | 9 | attributes?: string[] |
@@ -55,7 +55,7 @@ export type BuildVideosQueryOptions = { | |||
55 | having?: string | 55 | having?: string |
56 | } | 56 | } |
57 | 57 | ||
58 | function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) { | 58 | function buildListQuery (sequelize: Sequelize, options: BuildVideosQueryOptions) { |
59 | const and: string[] = [] | 59 | const and: string[] = [] |
60 | const joins: string[] = [] | 60 | const joins: string[] = [] |
61 | const replacements: any = {} | 61 | const replacements: any = {} |
@@ -77,7 +77,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) | |||
77 | const blockerIds = [ options.serverAccountId ] | 77 | const blockerIds = [ options.serverAccountId ] |
78 | if (options.user) blockerIds.push(options.user.Account.id) | 78 | if (options.user) blockerIds.push(options.user.Account.id) |
79 | 79 | ||
80 | const inClause = createSafeIn(model, blockerIds) | 80 | const inClause = createSafeIn(sequelize, blockerIds) |
81 | 81 | ||
82 | and.push( | 82 | and.push( |
83 | 'NOT EXISTS (' + | 83 | 'NOT EXISTS (' + |
@@ -179,7 +179,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) | |||
179 | 'EXISTS (' + | 179 | 'EXISTS (' + |
180 | ' SELECT 1 FROM "videoTag" ' + | 180 | ' SELECT 1 FROM "videoTag" ' + |
181 | ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + | 181 | ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + |
182 | ' WHERE lower("tag"."name") IN (' + createSafeIn(model, tagsOneOfLower) + ') ' + | 182 | ' WHERE lower("tag"."name") IN (' + createSafeIn(sequelize, tagsOneOfLower) + ') ' + |
183 | ' AND "video"."id" = "videoTag"."videoId"' + | 183 | ' AND "video"."id" = "videoTag"."videoId"' + |
184 | ')' | 184 | ')' |
185 | ) | 185 | ) |
@@ -192,7 +192,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) | |||
192 | 'EXISTS (' + | 192 | 'EXISTS (' + |
193 | ' SELECT 1 FROM "videoTag" ' + | 193 | ' SELECT 1 FROM "videoTag" ' + |
194 | ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + | 194 | ' INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + |
195 | ' WHERE lower("tag"."name") IN (' + createSafeIn(model, tagsAllOfLower) + ') ' + | 195 | ' WHERE lower("tag"."name") IN (' + createSafeIn(sequelize, tagsAllOfLower) + ') ' + |
196 | ' AND "video"."id" = "videoTag"."videoId" ' + | 196 | ' AND "video"."id" = "videoTag"."videoId" ' + |
197 | ' GROUP BY "videoTag"."videoId" HAVING COUNT(*) = ' + tagsAllOfLower.length + | 197 | ' GROUP BY "videoTag"."videoId" HAVING COUNT(*) = ' + tagsAllOfLower.length + |
198 | ')' | 198 | ')' |
@@ -232,7 +232,7 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) | |||
232 | languagesQueryParts.push( | 232 | languagesQueryParts.push( |
233 | 'EXISTS (' + | 233 | 'EXISTS (' + |
234 | ' SELECT 1 FROM "videoCaption" WHERE "videoCaption"."language" ' + | 234 | ' SELECT 1 FROM "videoCaption" WHERE "videoCaption"."language" ' + |
235 | ' IN (' + createSafeIn(model, languages) + ') AND ' + | 235 | ' IN (' + createSafeIn(sequelize, languages) + ') AND ' + |
236 | ' "videoCaption"."videoId" = "video"."id"' + | 236 | ' "videoCaption"."videoId" = "video"."id"' + |
237 | ')' | 237 | ')' |
238 | ) | 238 | ) |
@@ -345,8 +345,8 @@ function buildListQuery (model: typeof Model, options: BuildVideosQueryOptions) | |||
345 | } | 345 | } |
346 | 346 | ||
347 | if (options.search) { | 347 | if (options.search) { |
348 | const escapedSearch = model.sequelize.escape(options.search) | 348 | const escapedSearch = sequelize.escape(options.search) |
349 | const escapedLikeSearch = model.sequelize.escape('%' + options.search + '%') | 349 | const escapedLikeSearch = sequelize.escape('%' + options.search + '%') |
350 | 350 | ||
351 | cte.push( | 351 | cte.push( |
352 | '"trigramSearch" AS (' + | 352 | '"trigramSearch" AS (' + |
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts index 5059c1fa6..505c305e2 100644 --- a/server/models/video/video-share.ts +++ b/server/models/video/video-share.ts | |||
@@ -1,10 +1,11 @@ | |||
1 | import { literal, Op, QueryTypes, Transaction } from 'sequelize' | 1 | import { literal, Op, QueryTypes, Transaction } from 'sequelize' |
2 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { AttributesOnly } from '@shared/core-utils' | ||
3 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 4 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
4 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' | 5 | import { CONSTRAINTS_FIELDS } from '../../initializers/constants' |
5 | import { MActorDefault } from '../../types/models' | 6 | import { MActorDefault } from '../../types/models' |
6 | import { MVideoShareActor, MVideoShareFull } from '../../types/models/video' | 7 | import { MVideoShareActor, MVideoShareFull } from '../../types/models/video' |
7 | import { ActorModel } from '../activitypub/actor' | 8 | import { ActorModel } from '../actor/actor' |
8 | import { buildLocalActorIdsIn, throwIfNotValid } from '../utils' | 9 | import { buildLocalActorIdsIn, throwIfNotValid } from '../utils' |
9 | import { VideoModel } from './video' | 10 | import { VideoModel } from './video' |
10 | 11 | ||
@@ -50,7 +51,7 @@ enum ScopeNames { | |||
50 | } | 51 | } |
51 | ] | 52 | ] |
52 | }) | 53 | }) |
53 | export class VideoShareModel extends Model { | 54 | export class VideoShareModel extends Model<Partial<AttributesOnly<VideoShareModel>>> { |
54 | 55 | ||
55 | @AllowNull(false) | 56 | @AllowNull(false) |
56 | @Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) | 57 | @Is('VideoShareUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) |
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts index c9375b433..d627e8c9d 100644 --- a/server/models/video/video-streaming-playlist.ts +++ b/server/models/video/video-streaming-playlist.ts | |||
@@ -13,6 +13,7 @@ import { CONSTRAINTS_FIELDS, MEMOIZE_LENGTH, MEMOIZE_TTL, P2P_MEDIA_LOADER_PEER_ | |||
13 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | 13 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' |
14 | import { throwIfNotValid } from '../utils' | 14 | import { throwIfNotValid } from '../utils' |
15 | import { VideoModel } from './video' | 15 | import { VideoModel } from './video' |
16 | import { AttributesOnly } from '@shared/core-utils' | ||
16 | 17 | ||
17 | @Table({ | 18 | @Table({ |
18 | tableName: 'videoStreamingPlaylist', | 19 | tableName: 'videoStreamingPlaylist', |
@@ -30,7 +31,7 @@ import { VideoModel } from './video' | |||
30 | } | 31 | } |
31 | ] | 32 | ] |
32 | }) | 33 | }) |
33 | export class VideoStreamingPlaylistModel extends Model { | 34 | export class VideoStreamingPlaylistModel extends Model<Partial<AttributesOnly<VideoStreamingPlaylistModel>>> { |
34 | @CreatedAt | 35 | @CreatedAt |
35 | createdAt: Date | 36 | createdAt: Date |
36 | 37 | ||
diff --git a/server/models/video/video-tag.ts b/server/models/video/video-tag.ts index 5052b8c4d..1285d375b 100644 --- a/server/models/video/video-tag.ts +++ b/server/models/video/video-tag.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { TagModel } from './tag' | 3 | import { TagModel } from './tag' |
3 | import { VideoModel } from './video' | 4 | import { VideoModel } from './video' |
4 | 5 | ||
@@ -13,7 +14,7 @@ import { VideoModel } from './video' | |||
13 | } | 14 | } |
14 | ] | 15 | ] |
15 | }) | 16 | }) |
16 | export class VideoTagModel extends Model { | 17 | export class VideoTagModel extends Model<Partial<AttributesOnly<VideoTagModel>>> { |
17 | @CreatedAt | 18 | @CreatedAt |
18 | createdAt: Date | 19 | createdAt: Date |
19 | 20 | ||
diff --git a/server/models/video/video-view.ts b/server/models/video/video-view.ts index 992cf258a..dfc6296ce 100644 --- a/server/models/video/video-view.ts +++ b/server/models/video/video-view.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table } from 'sequelize-typescript' | 2 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table } from 'sequelize-typescript' |
3 | import { AttributesOnly } from '@shared/core-utils' | ||
2 | import { VideoModel } from './video' | 4 | import { VideoModel } from './video' |
3 | import * as Sequelize from 'sequelize' | ||
4 | 5 | ||
5 | @Table({ | 6 | @Table({ |
6 | tableName: 'videoView', | 7 | tableName: 'videoView', |
@@ -14,7 +15,7 @@ import * as Sequelize from 'sequelize' | |||
14 | } | 15 | } |
15 | ] | 16 | ] |
16 | }) | 17 | }) |
17 | export class VideoViewModel extends Model { | 18 | export class VideoViewModel extends Model<Partial<AttributesOnly<VideoViewModel>>> { |
18 | @CreatedAt | 19 | @CreatedAt |
19 | createdAt: Date | 20 | createdAt: Date |
20 | 21 | ||
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 8c316e00c..749ef7197 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -31,6 +31,7 @@ import { LiveManager } from '@server/lib/live-manager' | |||
31 | import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths' | 31 | import { getHLSDirectory, getVideoFilePath } from '@server/lib/video-paths' |
32 | import { getServerActor } from '@server/models/application/application' | 32 | import { getServerActor } from '@server/models/application/application' |
33 | import { ModelCache } from '@server/models/model-cache' | 33 | import { ModelCache } from '@server/models/model-cache' |
34 | import { AttributesOnly } from '@shared/core-utils' | ||
34 | import { VideoFile } from '@shared/models/videos/video-file.model' | 35 | import { VideoFile } from '@shared/models/videos/video-file.model' |
35 | import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared' | 36 | import { ResultList, UserRight, VideoPrivacy, VideoState } from '../../../shared' |
36 | import { VideoObject } from '../../../shared/models/activitypub/objects' | 37 | import { VideoObject } from '../../../shared/models/activitypub/objects' |
@@ -100,14 +101,14 @@ import { MVideoFile, MVideoFileStreamingPlaylistVideo } from '../../types/models | |||
100 | import { VideoAbuseModel } from '../abuse/video-abuse' | 101 | import { VideoAbuseModel } from '../abuse/video-abuse' |
101 | import { AccountModel } from '../account/account' | 102 | import { AccountModel } from '../account/account' |
102 | import { AccountVideoRateModel } from '../account/account-video-rate' | 103 | import { AccountVideoRateModel } from '../account/account-video-rate' |
103 | import { ActorImageModel } from '../account/actor-image' | 104 | import { ActorModel } from '../actor/actor' |
104 | import { UserModel } from '../account/user' | 105 | import { ActorImageModel } from '../actor/actor-image' |
105 | import { UserVideoHistoryModel } from '../account/user-video-history' | ||
106 | import { ActorModel } from '../activitypub/actor' | ||
107 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' | 106 | import { VideoRedundancyModel } from '../redundancy/video-redundancy' |
108 | import { ServerModel } from '../server/server' | 107 | import { ServerModel } from '../server/server' |
109 | import { TrackerModel } from '../server/tracker' | 108 | import { TrackerModel } from '../server/tracker' |
110 | import { VideoTrackerModel } from '../server/video-tracker' | 109 | import { VideoTrackerModel } from '../server/video-tracker' |
110 | import { UserModel } from '../user/user' | ||
111 | import { UserVideoHistoryModel } from '../user/user-video-history' | ||
111 | import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils' | 112 | import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils' |
112 | import { ScheduleVideoUpdateModel } from './schedule-video-update' | 113 | import { ScheduleVideoUpdateModel } from './schedule-video-update' |
113 | import { TagModel } from './tag' | 114 | import { TagModel } from './tag' |
@@ -489,7 +490,7 @@ export type AvailableForListIDsOptions = { | |||
489 | } | 490 | } |
490 | ] | 491 | ] |
491 | }) | 492 | }) |
492 | export class VideoModel extends Model { | 493 | export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> { |
493 | 494 | ||
494 | @AllowNull(false) | 495 | @AllowNull(false) |
495 | @Default(DataType.UUIDV4) | 496 | @Default(DataType.UUIDV4) |
@@ -1617,7 +1618,7 @@ export class VideoModel extends Model { | |||
1617 | includeLocalVideos: true | 1618 | includeLocalVideos: true |
1618 | } | 1619 | } |
1619 | 1620 | ||
1620 | const { query, replacements } = buildListQuery(VideoModel, queryOptions) | 1621 | const { query, replacements } = buildListQuery(VideoModel.sequelize, queryOptions) |
1621 | 1622 | ||
1622 | return this.sequelize.query<any>(query, { replacements, type: QueryTypes.SELECT }) | 1623 | return this.sequelize.query<any>(query, { replacements, type: QueryTypes.SELECT }) |
1623 | .then(rows => rows.map(r => r[field])) | 1624 | .then(rows => rows.map(r => r[field])) |
@@ -1645,7 +1646,7 @@ export class VideoModel extends Model { | |||
1645 | if (countVideos !== true) return Promise.resolve(undefined) | 1646 | if (countVideos !== true) return Promise.resolve(undefined) |
1646 | 1647 | ||
1647 | const countOptions = Object.assign({}, options, { isCount: true }) | 1648 | const countOptions = Object.assign({}, options, { isCount: true }) |
1648 | const { query: queryCount, replacements: replacementsCount } = buildListQuery(VideoModel, countOptions) | 1649 | const { query: queryCount, replacements: replacementsCount } = buildListQuery(VideoModel.sequelize, countOptions) |
1649 | 1650 | ||
1650 | return VideoModel.sequelize.query<any>(queryCount, { replacements: replacementsCount, type: QueryTypes.SELECT }) | 1651 | return VideoModel.sequelize.query<any>(queryCount, { replacements: replacementsCount, type: QueryTypes.SELECT }) |
1651 | .then(rows => rows.length !== 0 ? rows[0].total : 0) | 1652 | .then(rows => rows.length !== 0 ? rows[0].total : 0) |
@@ -1654,7 +1655,7 @@ export class VideoModel extends Model { | |||
1654 | function getModels () { | 1655 | function getModels () { |
1655 | if (options.count === 0) return Promise.resolve([]) | 1656 | if (options.count === 0) return Promise.resolve([]) |
1656 | 1657 | ||
1657 | const { query, replacements, order } = buildListQuery(VideoModel, options) | 1658 | const { query, replacements, order } = buildListQuery(VideoModel.sequelize, options) |
1658 | const queryModels = wrapForAPIResults(query, replacements, options, order) | 1659 | const queryModels = wrapForAPIResults(query, replacements, options, order) |
1659 | 1660 | ||
1660 | return VideoModel.sequelize.query<any>(queryModels, { replacements, type: QueryTypes.SELECT, nest: true }) | 1661 | return VideoModel.sequelize.query<any>(queryModels, { replacements, type: QueryTypes.SELECT, nest: true }) |
diff --git a/server/tests/api/check-params/plugins.ts b/server/tests/api/check-params/plugins.ts index 6e540bcbb..a833fe6ff 100644 --- a/server/tests/api/check-params/plugins.ts +++ b/server/tests/api/check-params/plugins.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | 4 | import { HttpStatusCode } from '@shared/core-utils' | |
5 | import { | 5 | import { |
6 | checkBadCountPagination, | 6 | checkBadCountPagination, |
7 | checkBadSortPagination, | 7 | checkBadSortPagination, |
@@ -11,14 +11,14 @@ import { | |||
11 | flushAndRunServer, | 11 | flushAndRunServer, |
12 | immutableAssign, | 12 | immutableAssign, |
13 | installPlugin, | 13 | installPlugin, |
14 | makeGetRequest, makePostBodyRequest, makePutBodyRequest, | 14 | makeGetRequest, |
15 | makePostBodyRequest, | ||
16 | makePutBodyRequest, | ||
15 | ServerInfo, | 17 | ServerInfo, |
16 | setAccessTokensToServers, | 18 | setAccessTokensToServers, |
17 | userLogin | 19 | userLogin |
18 | } from '../../../../shared/extra-utils' | 20 | } from '@shared/extra-utils' |
19 | import { PluginType } from '../../../../shared/models/plugins/plugin.type' | 21 | import { PeerTubePlugin, PluginType } from '@shared/models' |
20 | import { PeerTubePlugin } from '../../../../shared/models/plugins/peertube-plugin.model' | ||
21 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
22 | 22 | ||
23 | describe('Test server plugins API validators', function () { | 23 | describe('Test server plugins API validators', function () { |
24 | let server: ServerInfo | 24 | let server: ServerInfo |
diff --git a/server/tests/api/moderation/blocklist.ts b/server/tests/api/moderation/blocklist.ts index e8202aff1..b767d38c7 100644 --- a/server/tests/api/moderation/blocklist.ts +++ b/server/tests/api/moderation/blocklist.ts | |||
@@ -1,46 +1,50 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | 3 | import 'mocha' |
5 | import { AccountBlock, ServerBlock, Video, UserNotification, UserNotificationType } from '../../../../shared/index' | 4 | import * as chai from 'chai' |
6 | import { | 5 | import { |
6 | addAccountToAccountBlocklist, | ||
7 | addAccountToServerBlocklist, | ||
8 | addServerToAccountBlocklist, | ||
9 | addServerToServerBlocklist, | ||
10 | addVideoCommentReply, | ||
11 | addVideoCommentThread, | ||
7 | cleanupTests, | 12 | cleanupTests, |
8 | createUser, | 13 | createUser, |
9 | deleteVideoComment, | 14 | deleteVideoComment, |
10 | doubleFollow, | 15 | doubleFollow, |
16 | findCommentId, | ||
11 | flushAndRunMultipleServers, | 17 | flushAndRunMultipleServers, |
12 | ServerInfo, | ||
13 | uploadVideo, | ||
14 | userLogin, | ||
15 | follow, | 18 | follow, |
16 | unfollow | ||
17 | } from '../../../../shared/extra-utils/index' | ||
18 | import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login' | ||
19 | import { getVideosList, getVideosListWithToken } from '../../../../shared/extra-utils/videos/videos' | ||
20 | import { | ||
21 | addVideoCommentReply, | ||
22 | addVideoCommentThread, | ||
23 | getVideoCommentThreads, | ||
24 | getVideoThreadComments, | ||
25 | findCommentId | ||
26 | } from '../../../../shared/extra-utils/videos/video-comments' | ||
27 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | ||
28 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | ||
29 | import { | ||
30 | addAccountToAccountBlocklist, | ||
31 | addAccountToServerBlocklist, | ||
32 | addServerToAccountBlocklist, | ||
33 | addServerToServerBlocklist, | ||
34 | getAccountBlocklistByAccount, | 19 | getAccountBlocklistByAccount, |
35 | getAccountBlocklistByServer, | 20 | getAccountBlocklistByServer, |
36 | getServerBlocklistByAccount, | 21 | getServerBlocklistByAccount, |
37 | getServerBlocklistByServer, | 22 | getServerBlocklistByServer, |
23 | getUserNotifications, | ||
24 | getVideoCommentThreads, | ||
25 | getVideosList, | ||
26 | getVideosListWithToken, | ||
27 | getVideoThreadComments, | ||
38 | removeAccountFromAccountBlocklist, | 28 | removeAccountFromAccountBlocklist, |
39 | removeAccountFromServerBlocklist, | 29 | removeAccountFromServerBlocklist, |
40 | removeServerFromAccountBlocklist, | 30 | removeServerFromAccountBlocklist, |
41 | removeServerFromServerBlocklist | 31 | removeServerFromServerBlocklist, |
42 | } from '../../../../shared/extra-utils/users/blocklist' | 32 | ServerInfo, |
43 | import { getUserNotifications } from '../../../../shared/extra-utils/users/user-notifications' | 33 | setAccessTokensToServers, |
34 | unfollow, | ||
35 | uploadVideo, | ||
36 | userLogin, | ||
37 | waitJobs | ||
38 | } from '@shared/extra-utils' | ||
39 | import { | ||
40 | AccountBlock, | ||
41 | ServerBlock, | ||
42 | UserNotification, | ||
43 | UserNotificationType, | ||
44 | Video, | ||
45 | VideoComment, | ||
46 | VideoCommentThreadTree | ||
47 | } from '@shared/models' | ||
44 | 48 | ||
45 | const expect = chai.expect | 49 | const expect = chai.expect |
46 | 50 | ||
diff --git a/server/tests/api/notifications/comments-notifications.ts b/server/tests/api/notifications/comments-notifications.ts index 5e4ab0d6c..d2badf237 100644 --- a/server/tests/api/notifications/comments-notifications.ts +++ b/server/tests/api/notifications/comments-notifications.ts | |||
@@ -2,20 +2,25 @@ | |||
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { cleanupTests, getVideoCommentThreads, getVideoThreadComments, updateMyUser } from '../../../../shared/extra-utils' | ||
6 | import { ServerInfo, uploadVideo } from '../../../../shared/extra-utils/index' | ||
7 | import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email' | ||
8 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | ||
9 | import { addAccountToAccountBlocklist, removeAccountFromAccountBlocklist } from '../../../../shared/extra-utils/users/blocklist' | ||
10 | import { | 5 | import { |
6 | addAccountToAccountBlocklist, | ||
7 | addVideoCommentReply, | ||
8 | addVideoCommentThread, | ||
11 | checkCommentMention, | 9 | checkCommentMention, |
12 | CheckerBaseParams, | 10 | CheckerBaseParams, |
13 | checkNewCommentOnMyVideo, | 11 | checkNewCommentOnMyVideo, |
14 | prepareNotificationsTest | 12 | cleanupTests, |
15 | } from '../../../../shared/extra-utils/users/user-notifications' | 13 | getVideoCommentThreads, |
16 | import { addVideoCommentReply, addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments' | 14 | getVideoThreadComments, |
17 | import { UserNotification } from '../../../../shared/models/users' | 15 | MockSmtpServer, |
18 | import { VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | 16 | prepareNotificationsTest, |
17 | removeAccountFromAccountBlocklist, | ||
18 | ServerInfo, | ||
19 | updateMyUser, | ||
20 | uploadVideo, | ||
21 | waitJobs | ||
22 | } from '@shared/extra-utils' | ||
23 | import { UserNotification, VideoCommentThreadTree } from '@shared/models' | ||
19 | 24 | ||
20 | const expect = chai.expect | 25 | const expect = chai.expect |
21 | 26 | ||
diff --git a/server/tests/api/server/bulk.ts b/server/tests/api/server/bulk.ts index 51ba0e7af..80fa7fce6 100644 --- a/server/tests/api/server/bulk.ts +++ b/server/tests/api/server/bulk.ts | |||
@@ -2,12 +2,14 @@ | |||
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { VideoComment } from '@shared/models/videos/video-comment.model' | 5 | import { Video, VideoComment } from '@shared/models' |
6 | import { | 6 | import { |
7 | addVideoCommentReply, | ||
7 | addVideoCommentThread, | 8 | addVideoCommentThread, |
8 | bulkRemoveCommentsOf, | 9 | bulkRemoveCommentsOf, |
9 | cleanupTests, | 10 | cleanupTests, |
10 | createUser, | 11 | createUser, |
12 | doubleFollow, | ||
11 | flushAndRunMultipleServers, | 13 | flushAndRunMultipleServers, |
12 | getVideoCommentThreads, | 14 | getVideoCommentThreads, |
13 | getVideosList, | 15 | getVideosList, |
@@ -15,11 +17,8 @@ import { | |||
15 | setAccessTokensToServers, | 17 | setAccessTokensToServers, |
16 | uploadVideo, | 18 | uploadVideo, |
17 | userLogin, | 19 | userLogin, |
18 | waitJobs, | 20 | waitJobs |
19 | addVideoCommentReply | ||
20 | } from '../../../../shared/extra-utils/index' | 21 | } from '../../../../shared/extra-utils/index' |
21 | import { doubleFollow } from '../../../../shared/extra-utils/server/follows' | ||
22 | import { Video } from '@shared/models' | ||
23 | 22 | ||
24 | const expect = chai.expect | 23 | const expect = chai.expect |
25 | 24 | ||
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts index eb9ab10eb..e1c062020 100644 --- a/server/tests/api/server/follows.ts +++ b/server/tests/api/server/follows.ts | |||
@@ -1,37 +1,35 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | 3 | import 'mocha' |
5 | import { Video, VideoPrivacy } from '../../../../shared/models/videos' | 4 | import * as chai from 'chai' |
6 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | ||
7 | import { cleanupTests, completeVideoCheck, deleteVideoComment } from '../../../../shared/extra-utils' | ||
8 | import { | 5 | import { |
6 | addVideoCommentReply, | ||
7 | addVideoCommentThread, | ||
8 | cleanupTests, | ||
9 | completeVideoCheck, | ||
10 | createUser, | ||
11 | createVideoCaption, | ||
12 | dateIsValid, | ||
13 | deleteVideoComment, | ||
14 | expectAccountFollows, | ||
9 | flushAndRunMultipleServers, | 15 | flushAndRunMultipleServers, |
10 | getVideosList, | ||
11 | ServerInfo, | ||
12 | setAccessTokensToServers, | ||
13 | uploadVideo | ||
14 | } from '../../../../shared/extra-utils/index' | ||
15 | import { dateIsValid } from '../../../../shared/extra-utils/miscs/miscs' | ||
16 | import { | ||
17 | follow, | 16 | follow, |
18 | getFollowersListPaginationAndSort, | 17 | getFollowersListPaginationAndSort, |
19 | getFollowingListPaginationAndSort, | 18 | getFollowingListPaginationAndSort, |
20 | unfollow | ||
21 | } from '../../../../shared/extra-utils/server/follows' | ||
22 | import { expectAccountFollows } from '../../../../shared/extra-utils/users/accounts' | ||
23 | import { userLogin } from '../../../../shared/extra-utils/users/login' | ||
24 | import { createUser } from '../../../../shared/extra-utils/users/users' | ||
25 | import { | ||
26 | addVideoCommentReply, | ||
27 | addVideoCommentThread, | ||
28 | getVideoCommentThreads, | 19 | getVideoCommentThreads, |
29 | getVideoThreadComments | 20 | getVideosList, |
30 | } from '../../../../shared/extra-utils/videos/video-comments' | 21 | getVideoThreadComments, |
31 | import { rateVideo } from '../../../../shared/extra-utils/videos/videos' | 22 | listVideoCaptions, |
32 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 23 | rateVideo, |
33 | import { createVideoCaption, listVideoCaptions, testCaptionFile } from '../../../../shared/extra-utils/videos/video-captions' | 24 | ServerInfo, |
34 | import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model' | 25 | setAccessTokensToServers, |
26 | testCaptionFile, | ||
27 | unfollow, | ||
28 | uploadVideo, | ||
29 | userLogin, | ||
30 | waitJobs | ||
31 | } from '@shared/extra-utils' | ||
32 | import { Video, VideoCaption, VideoComment, VideoCommentThreadTree, VideoPrivacy } from '@shared/models' | ||
35 | 33 | ||
36 | const expect = chai.expect | 34 | const expect = chai.expect |
37 | 35 | ||
diff --git a/server/tests/api/server/handle-down.ts b/server/tests/api/server/handle-down.ts index 817c79f6e..fe4a0e100 100644 --- a/server/tests/api/server/handle-down.ts +++ b/server/tests/api/server/handle-down.ts | |||
@@ -4,7 +4,7 @@ import * as chai from 'chai' | |||
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { JobState, Video } from '../../../../shared/models' | 5 | import { JobState, Video } from '../../../../shared/models' |
6 | import { VideoPrivacy } from '../../../../shared/models/videos' | 6 | import { VideoPrivacy } from '../../../../shared/models/videos' |
7 | import { VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | 7 | import { VideoCommentThreadTree } from '../../../../shared/models/videos/comment/video-comment.model' |
8 | 8 | ||
9 | import { | 9 | import { |
10 | cleanupTests, | 10 | cleanupTests, |
@@ -346,10 +346,12 @@ describe('Test handle downs', function () { | |||
346 | // Wait video expiration | 346 | // Wait video expiration |
347 | await wait(11000) | 347 | await wait(11000) |
348 | 348 | ||
349 | for (let i = 0; i < 3; i++) { | 349 | for (let i = 0; i < 5; i++) { |
350 | await getVideo(servers[1].url, videoIdsServer1[i]) | 350 | try { |
351 | await waitJobs([ servers[1] ]) | 351 | await getVideo(servers[1].url, videoIdsServer1[i]) |
352 | await wait(1500) | 352 | await waitJobs([ servers[1] ]) |
353 | await wait(1500) | ||
354 | } catch {} | ||
353 | } | 355 | } |
354 | 356 | ||
355 | for (const id of videoIdsServer1) { | 357 | for (const id of videoIdsServer1) { |
diff --git a/server/tests/api/server/plugins.ts b/server/tests/api/server/plugins.ts index 1c6eabe6d..3c09acc9a 100644 --- a/server/tests/api/server/plugins.ts +++ b/server/tests/api/server/plugins.ts | |||
@@ -28,14 +28,8 @@ import { | |||
28 | updatePluginSettings, | 28 | updatePluginSettings, |
29 | wait, | 29 | wait, |
30 | waitUntilLog | 30 | waitUntilLog |
31 | } from '../../../../shared/extra-utils' | 31 | } from '@shared/extra-utils' |
32 | import { PeerTubePluginIndex } from '../../../../shared/models/plugins/peertube-plugin-index.model' | 32 | import { PeerTubePlugin, PeerTubePluginIndex, PluginPackageJson, PluginType, PublicServerSetting, ServerConfig, User } from '@shared/models' |
33 | import { PeerTubePlugin } from '../../../../shared/models/plugins/peertube-plugin.model' | ||
34 | import { PluginPackageJson } from '../../../../shared/models/plugins/plugin-package-json.model' | ||
35 | import { PluginType } from '../../../../shared/models/plugins/plugin.type' | ||
36 | import { PublicServerSetting } from '../../../../shared/models/plugins/public-server.setting' | ||
37 | import { ServerConfig } from '../../../../shared/models/server' | ||
38 | import { User } from '../../../../shared/models/users' | ||
39 | 33 | ||
40 | const expect = chai.expect | 34 | const expect = chai.expect |
41 | 35 | ||
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts index 41cd814e0..6aa996038 100644 --- a/server/tests/api/videos/multiple-servers.ts +++ b/server/tests/api/videos/multiple-servers.ts | |||
@@ -1,11 +1,10 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | ||
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import * as request from 'supertest' | 6 | import * as request from 'supertest' |
7 | import { VideoPrivacy } from '../../../../shared/models/videos' | 7 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' |
8 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | ||
9 | import { | 8 | import { |
10 | addVideoChannel, | 9 | addVideoChannel, |
11 | checkTmpIsEmpty, | 10 | checkTmpIsEmpty, |
@@ -32,16 +31,16 @@ import { | |||
32 | wait, | 31 | wait, |
33 | webtorrentAdd | 32 | webtorrentAdd |
34 | } from '../../../../shared/extra-utils' | 33 | } from '../../../../shared/extra-utils' |
34 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | ||
35 | import { | 35 | import { |
36 | addVideoCommentReply, | 36 | addVideoCommentReply, |
37 | addVideoCommentThread, | 37 | addVideoCommentThread, |
38 | deleteVideoComment, | 38 | deleteVideoComment, |
39 | findCommentId, | ||
39 | getVideoCommentThreads, | 40 | getVideoCommentThreads, |
40 | getVideoThreadComments, | 41 | getVideoThreadComments |
41 | findCommentId | ||
42 | } from '../../../../shared/extra-utils/videos/video-comments' | 42 | } from '../../../../shared/extra-utils/videos/video-comments' |
43 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 43 | import { VideoComment, VideoCommentThreadTree, VideoPrivacy } from '../../../../shared/models/videos' |
44 | import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' | ||
45 | 44 | ||
46 | const expect = chai.expect | 45 | const expect = chai.expect |
47 | 46 | ||
diff --git a/server/tests/api/videos/video-comments.ts b/server/tests/api/videos/video-comments.ts index 615e0ea45..a5ff3a39d 100644 --- a/server/tests/api/videos/video-comments.ts +++ b/server/tests/api/videos/video-comments.ts | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | 5 | import { VideoComment, VideoCommentAdmin, VideoCommentThreadTree } from '@shared/models' | |
6 | import { cleanupTests, testImage } from '../../../../shared/extra-utils' | 6 | import { cleanupTests, testImage } from '../../../../shared/extra-utils' |
7 | import { | 7 | import { |
8 | createUser, | 8 | createUser, |
@@ -22,7 +22,6 @@ import { | |||
22 | getVideoCommentThreads, | 22 | getVideoCommentThreads, |
23 | getVideoThreadComments | 23 | getVideoThreadComments |
24 | } from '../../../../shared/extra-utils/videos/video-comments' | 24 | } from '../../../../shared/extra-utils/videos/video-comments' |
25 | import { VideoComment, VideoCommentAdmin, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | ||
26 | 25 | ||
27 | const expect = chai.expect | 26 | const expect = chai.expect |
28 | 27 | ||
diff --git a/server/tests/client.ts b/server/tests/client.ts index 3c99bcd1f..a385edd26 100644 --- a/server/tests/client.ts +++ b/server/tests/client.ts | |||
@@ -3,7 +3,7 @@ | |||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import * as request from 'supertest' | 5 | import * as request from 'supertest' |
6 | import { Account, VideoPlaylistPrivacy } from '@shared/models' | 6 | import { Account, HTMLServerConfig, ServerConfig, VideoPlaylistPrivacy } from '@shared/models' |
7 | import { | 7 | import { |
8 | addVideoInPlaylist, | 8 | addVideoInPlaylist, |
9 | cleanupTests, | 9 | cleanupTests, |
@@ -11,6 +11,7 @@ import { | |||
11 | doubleFollow, | 11 | doubleFollow, |
12 | flushAndRunMultipleServers, | 12 | flushAndRunMultipleServers, |
13 | getAccount, | 13 | getAccount, |
14 | getConfig, | ||
14 | getCustomConfig, | 15 | getCustomConfig, |
15 | getVideosList, | 16 | getVideosList, |
16 | makeHTMLRequest, | 17 | makeHTMLRequest, |
@@ -25,13 +26,17 @@ import { | |||
25 | waitJobs | 26 | waitJobs |
26 | } from '../../shared/extra-utils' | 27 | } from '../../shared/extra-utils' |
27 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | 28 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' |
29 | import { omit } from 'lodash' | ||
28 | 30 | ||
29 | const expect = chai.expect | 31 | const expect = chai.expect |
30 | 32 | ||
31 | function checkIndexTags (html: string, title: string, description: string, css: string) { | 33 | function checkIndexTags (html: string, title: string, description: string, css: string, config: ServerConfig) { |
32 | expect(html).to.contain('<title>' + title + '</title>') | 34 | expect(html).to.contain('<title>' + title + '</title>') |
33 | expect(html).to.contain('<meta name="description" content="' + description + '" />') | 35 | expect(html).to.contain('<meta name="description" content="' + description + '" />') |
34 | expect(html).to.contain('<style class="custom-css-style">' + css + '</style>') | 36 | expect(html).to.contain('<style class="custom-css-style">' + css + '</style>') |
37 | |||
38 | const htmlConfig: HTMLServerConfig = omit(config, 'signup') | ||
39 | expect(html).to.contain(`<script type="application/javascript">window.PeerTubeServerConfig = '${JSON.stringify(htmlConfig)}'</script>`) | ||
35 | } | 40 | } |
36 | 41 | ||
37 | describe('Test a client controllers', function () { | 42 | describe('Test a client controllers', function () { |
@@ -296,10 +301,11 @@ describe('Test a client controllers', function () { | |||
296 | describe('Index HTML', function () { | 301 | describe('Index HTML', function () { |
297 | 302 | ||
298 | it('Should have valid index html tags (title, description...)', async function () { | 303 | it('Should have valid index html tags (title, description...)', async function () { |
304 | const resConfig = await getConfig(servers[0].url) | ||
299 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') | 305 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') |
300 | 306 | ||
301 | const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.' | 307 | const description = 'PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.' |
302 | checkIndexTags(res.text, 'PeerTube', description, '') | 308 | checkIndexTags(res.text, 'PeerTube', description, '', resConfig.body) |
303 | }) | 309 | }) |
304 | 310 | ||
305 | it('Should update the customized configuration and have the correct index html tags', async function () { | 311 | it('Should update the customized configuration and have the correct index html tags', async function () { |
@@ -318,15 +324,17 @@ describe('Test a client controllers', function () { | |||
318 | } | 324 | } |
319 | }) | 325 | }) |
320 | 326 | ||
327 | const resConfig = await getConfig(servers[0].url) | ||
321 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') | 328 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') |
322 | 329 | ||
323 | checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }') | 330 | checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) |
324 | }) | 331 | }) |
325 | 332 | ||
326 | it('Should have valid index html updated tags (title, description...)', async function () { | 333 | it('Should have valid index html updated tags (title, description...)', async function () { |
334 | const resConfig = await getConfig(servers[0].url) | ||
327 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') | 335 | const res = await makeHTMLRequest(servers[0].url, '/videos/trending') |
328 | 336 | ||
329 | checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }') | 337 | checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) |
330 | }) | 338 | }) |
331 | 339 | ||
332 | it('Should use the original video URL for the canonical tag', async function () { | 340 | it('Should use the original video URL for the canonical tag', async function () { |
@@ -350,6 +358,16 @@ describe('Test a client controllers', function () { | |||
350 | }) | 358 | }) |
351 | }) | 359 | }) |
352 | 360 | ||
361 | describe('Embed HTML', function () { | ||
362 | |||
363 | it('Should have the correct embed html tags', async function () { | ||
364 | const resConfig = await getConfig(servers[0].url) | ||
365 | const res = await makeHTMLRequest(servers[0].url, servers[0].video.embedPath) | ||
366 | |||
367 | checkIndexTags(res.text, 'PeerTube updated', 'my short description', 'body { background-color: red; }', resConfig.body) | ||
368 | }) | ||
369 | }) | ||
370 | |||
353 | after(async function () { | 371 | after(async function () { |
354 | await cleanupTests(servers) | 372 | await cleanupTests(servers) |
355 | }) | 373 | }) |
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts index 7d4f7abb4..1d6bb6cf4 100644 --- a/server/tests/plugins/filter-hooks.ts +++ b/server/tests/plugins/filter-hooks.ts | |||
@@ -38,6 +38,7 @@ import { | |||
38 | import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers' | 38 | import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers' |
39 | import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports' | 39 | import { getGoodVideoUrl, getMyVideoImports, importVideo } from '../../../shared/extra-utils/videos/video-imports' |
40 | import { | 40 | import { |
41 | VideoCommentThreadTree, | ||
41 | VideoDetails, | 42 | VideoDetails, |
42 | VideoImport, | 43 | VideoImport, |
43 | VideoImportState, | 44 | VideoImportState, |
@@ -45,7 +46,6 @@ import { | |||
45 | VideoPlaylistPrivacy, | 46 | VideoPlaylistPrivacy, |
46 | VideoPrivacy | 47 | VideoPrivacy |
47 | } from '../../../shared/models/videos' | 48 | } from '../../../shared/models/videos' |
48 | import { VideoCommentThreadTree } from '../../../shared/models/videos/video-comment.model' | ||
49 | 49 | ||
50 | const expect = chai.expect | 50 | const expect = chai.expect |
51 | 51 | ||
diff --git a/server/tools/peertube-import-videos.ts b/server/tools/peertube-import-videos.ts index 915995031..b3f57a8f9 100644 --- a/server/tools/peertube-import-videos.ts +++ b/server/tools/peertube-import-videos.ts | |||
@@ -11,9 +11,9 @@ import { promisify } from 'util' | |||
11 | import { advancedVideosSearch, getClient, getVideoCategories, login, uploadVideo } from '../../shared/extra-utils/index' | 11 | import { advancedVideosSearch, getClient, getVideoCategories, login, uploadVideo } from '../../shared/extra-utils/index' |
12 | import { sha256 } from '../helpers/core-utils' | 12 | import { sha256 } from '../helpers/core-utils' |
13 | import { doRequestAndSaveToFile } from '../helpers/requests' | 13 | import { doRequestAndSaveToFile } from '../helpers/requests' |
14 | import { buildOriginallyPublishedAt, getYoutubeDLVideoFormat, safeGetYoutubeDL } from '../helpers/youtube-dl' | ||
15 | import { CONSTRAINTS_FIELDS } from '../initializers/constants' | 14 | import { CONSTRAINTS_FIELDS } from '../initializers/constants' |
16 | import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getLogger, getServerCredentials } from './cli' | 15 | import { buildCommonVideoOptions, buildVideoAttributesFromCommander, getLogger, getServerCredentials } from './cli' |
16 | import { YoutubeDL } from '@server/helpers/youtube-dl' | ||
17 | 17 | ||
18 | type UserInfo = { | 18 | type UserInfo = { |
19 | username: string | 19 | username: string |
@@ -74,9 +74,9 @@ async function run (url: string, user: UserInfo) { | |||
74 | user.password = await promptPassword() | 74 | user.password = await promptPassword() |
75 | } | 75 | } |
76 | 76 | ||
77 | const youtubeDL = await safeGetYoutubeDL() | 77 | const youtubeDLBinary = await YoutubeDL.safeGetYoutubeDL() |
78 | 78 | ||
79 | let info = await getYoutubeDLInfo(youtubeDL, options.targetUrl, command.args) | 79 | let info = await getYoutubeDLInfo(youtubeDLBinary, options.targetUrl, command.args) |
80 | 80 | ||
81 | if (!Array.isArray(info)) info = [ info ] | 81 | if (!Array.isArray(info)) info = [ info ] |
82 | 82 | ||
@@ -86,7 +86,7 @@ async function run (url: string, user: UserInfo) { | |||
86 | if (uploadsObject) { | 86 | if (uploadsObject) { |
87 | console.log('Fixing URL to %s.', uploadsObject.url) | 87 | console.log('Fixing URL to %s.', uploadsObject.url) |
88 | 88 | ||
89 | info = await getYoutubeDLInfo(youtubeDL, uploadsObject.url, command.args) | 89 | info = await getYoutubeDLInfo(youtubeDLBinary, uploadsObject.url, command.args) |
90 | } | 90 | } |
91 | 91 | ||
92 | let infoArray: any[] | 92 | let infoArray: any[] |
@@ -130,13 +130,14 @@ async function processVideo (parameters: { | |||
130 | youtubeInfo: any | 130 | youtubeInfo: any |
131 | }) { | 131 | }) { |
132 | const { youtubeInfo, cwd, url, user } = parameters | 132 | const { youtubeInfo, cwd, url, user } = parameters |
133 | const youtubeDL = new YoutubeDL('', []) | ||
133 | 134 | ||
134 | log.debug('Fetching object.', youtubeInfo) | 135 | log.debug('Fetching object.', youtubeInfo) |
135 | 136 | ||
136 | const videoInfo = await fetchObject(youtubeInfo) | 137 | const videoInfo = await fetchObject(youtubeInfo) |
137 | log.debug('Fetched object.', videoInfo) | 138 | log.debug('Fetched object.', videoInfo) |
138 | 139 | ||
139 | const originallyPublishedAt = buildOriginallyPublishedAt(videoInfo) | 140 | const originallyPublishedAt = youtubeDL.buildOriginallyPublishedAt(videoInfo) |
140 | if (options.since && originallyPublishedAt && originallyPublishedAt.getTime() < options.since.getTime()) { | 141 | if (options.since && originallyPublishedAt && originallyPublishedAt.getTime() < options.since.getTime()) { |
141 | log.info('Video "%s" has been published before "%s", don\'t upload it.\n', | 142 | log.info('Video "%s" has been published before "%s", don\'t upload it.\n', |
142 | videoInfo.title, formatDate(options.since)) | 143 | videoInfo.title, formatDate(options.since)) |
@@ -161,13 +162,14 @@ async function processVideo (parameters: { | |||
161 | 162 | ||
162 | log.info('Downloading video "%s"...', videoInfo.title) | 163 | log.info('Downloading video "%s"...', videoInfo.title) |
163 | 164 | ||
164 | const youtubeDLOptions = [ '-f', getYoutubeDLVideoFormat(), ...command.args, '-o', path ] | 165 | const youtubeDLOptions = [ '-f', youtubeDL.getYoutubeDLVideoFormat(), ...command.args, '-o', path ] |
165 | try { | 166 | try { |
166 | const youtubeDL = await safeGetYoutubeDL() | 167 | const youtubeDLBinary = await YoutubeDL.safeGetYoutubeDL() |
167 | const youtubeDLExec = promisify(youtubeDL.exec).bind(youtubeDL) | 168 | const youtubeDLExec = promisify(youtubeDLBinary.exec).bind(youtubeDLBinary) |
168 | const output = await youtubeDLExec(videoInfo.url, youtubeDLOptions, processOptions) | 169 | const output = await youtubeDLExec(videoInfo.url, youtubeDLOptions, processOptions) |
169 | log.info(output.join('\n')) | 170 | log.info(output.join('\n')) |
170 | await uploadVideoOnPeerTube({ | 171 | await uploadVideoOnPeerTube({ |
172 | youtubeDL, | ||
171 | cwd, | 173 | cwd, |
172 | url, | 174 | url, |
173 | user, | 175 | user, |
@@ -180,13 +182,14 @@ async function processVideo (parameters: { | |||
180 | } | 182 | } |
181 | 183 | ||
182 | async function uploadVideoOnPeerTube (parameters: { | 184 | async function uploadVideoOnPeerTube (parameters: { |
185 | youtubeDL: YoutubeDL | ||
183 | videoInfo: any | 186 | videoInfo: any |
184 | videoPath: string | 187 | videoPath: string |
185 | cwd: string | 188 | cwd: string |
186 | url: string | 189 | url: string |
187 | user: { username: string, password: string } | 190 | user: { username: string, password: string } |
188 | }) { | 191 | }) { |
189 | const { videoInfo, videoPath, cwd, url, user } = parameters | 192 | const { youtubeDL, videoInfo, videoPath, cwd, url, user } = parameters |
190 | 193 | ||
191 | const category = await getCategory(videoInfo.categories, url) | 194 | const category = await getCategory(videoInfo.categories, url) |
192 | const licence = getLicence(videoInfo.license) | 195 | const licence = getLicence(videoInfo.license) |
@@ -205,7 +208,7 @@ async function uploadVideoOnPeerTube (parameters: { | |||
205 | await doRequestAndSaveToFile(videoInfo.thumbnail, thumbnailfile) | 208 | await doRequestAndSaveToFile(videoInfo.thumbnail, thumbnailfile) |
206 | } | 209 | } |
207 | 210 | ||
208 | const originallyPublishedAt = buildOriginallyPublishedAt(videoInfo) | 211 | const originallyPublishedAt = youtubeDL.buildOriginallyPublishedAt(videoInfo) |
209 | 212 | ||
210 | const defaultAttributes = { | 213 | const defaultAttributes = { |
211 | name: truncate(videoInfo.title, { | 214 | name: truncate(videoInfo.title, { |
@@ -304,7 +307,7 @@ function fetchObject (info: any) { | |||
304 | const url = buildUrl(info) | 307 | const url = buildUrl(info) |
305 | 308 | ||
306 | return new Promise<any>(async (res, rej) => { | 309 | return new Promise<any>(async (res, rej) => { |
307 | const youtubeDL = await safeGetYoutubeDL() | 310 | const youtubeDL = await YoutubeDL.safeGetYoutubeDL() |
308 | youtubeDL.getInfo(url, undefined, processOptions, (err, videoInfo) => { | 311 | youtubeDL.getInfo(url, undefined, processOptions, (err, videoInfo) => { |
309 | if (err) return rej(err) | 312 | if (err) return rej(err) |
310 | 313 | ||
diff --git a/server/tools/peertube-plugins.ts b/server/tools/peertube-plugins.ts index c8a576844..cb591377b 100644 --- a/server/tools/peertube-plugins.ts +++ b/server/tools/peertube-plugins.ts | |||
@@ -4,10 +4,9 @@ import { registerTSPaths } from '../helpers/register-ts-paths' | |||
4 | registerTSPaths() | 4 | registerTSPaths() |
5 | 5 | ||
6 | import * as program from 'commander' | 6 | import * as program from 'commander' |
7 | import { PluginType } from '../../shared/models/plugins/plugin.type' | ||
8 | import { installPlugin, listPlugins, uninstallPlugin, updatePlugin } from '../../shared/extra-utils/server/plugins' | 7 | import { installPlugin, listPlugins, uninstallPlugin, updatePlugin } from '../../shared/extra-utils/server/plugins' |
9 | import { getAdminTokenOrDie, getServerCredentials } from './cli' | 8 | import { getAdminTokenOrDie, getServerCredentials } from './cli' |
10 | import { PeerTubePlugin } from '../../shared/models/plugins/peertube-plugin.model' | 9 | import { PeerTubePlugin, PluginType } from '../../shared/models' |
11 | import { isAbsolute } from 'path' | 10 | import { isAbsolute } from 'path' |
12 | import * as CliTable3 from 'cli-table3' | 11 | import * as CliTable3 from 'cli-table3' |
13 | import commander = require('commander') | 12 | import commander = require('commander') |
diff --git a/server/types/models/moderation/abuse-message.ts b/server/types/models/abuse/abuse-message.ts index 565eca706..565eca706 100644 --- a/server/types/models/moderation/abuse-message.ts +++ b/server/types/models/abuse/abuse-message.ts | |||
diff --git a/server/types/models/moderation/abuse.ts b/server/types/models/abuse/abuse.ts index 6fd83684c..6fd83684c 100644 --- a/server/types/models/moderation/abuse.ts +++ b/server/types/models/abuse/abuse.ts | |||
diff --git a/server/types/models/moderation/index.ts b/server/types/models/abuse/index.ts index 1ed91b249..1ed91b249 100644 --- a/server/types/models/moderation/index.ts +++ b/server/types/models/abuse/index.ts | |||
diff --git a/server/types/models/account/account.ts b/server/types/models/account/account.ts index 9513acad8..984841291 100644 --- a/server/types/models/account/account.ts +++ b/server/types/models/account/account.ts | |||
@@ -1,7 +1,5 @@ | |||
1 | import { FunctionProperties, PickWith } from '@shared/core-utils' | 1 | import { FunctionProperties, PickWith } from '@shared/core-utils' |
2 | import { AccountModel } from '../../../models/account/account' | 2 | import { AccountModel } from '../../../models/account/account' |
3 | import { MChannelDefault } from '../video/video-channels' | ||
4 | import { MAccountBlocklistId } from './account-blocklist' | ||
5 | import { | 3 | import { |
6 | MActor, | 4 | MActor, |
7 | MActorAPAccount, | 5 | MActorAPAccount, |
@@ -15,7 +13,9 @@ import { | |||
15 | MActorSummary, | 13 | MActorSummary, |
16 | MActorSummaryFormattable, | 14 | MActorSummaryFormattable, |
17 | MActorUrl | 15 | MActorUrl |
18 | } from './actor' | 16 | } from '../actor' |
17 | import { MChannelDefault } from '../video/video-channels' | ||
18 | import { MAccountBlocklistId } from './account-blocklist' | ||
19 | 19 | ||
20 | type Use<K extends keyof AccountModel, M> = PickWith<AccountModel, K, M> | 20 | type Use<K extends keyof AccountModel, M> = PickWith<AccountModel, K, M> |
21 | 21 | ||
diff --git a/server/types/models/account/index.ts b/server/types/models/account/index.ts index e3fc00f94..dab2eea7e 100644 --- a/server/types/models/account/index.ts +++ b/server/types/models/account/index.ts | |||
@@ -1,5 +1,2 @@ | |||
1 | export * from './account' | 1 | export * from './account' |
2 | export * from './account-blocklist' | 2 | export * from './account-blocklist' |
3 | export * from './actor-follow' | ||
4 | export * from './actor-image' | ||
5 | export * from './actor' | ||
diff --git a/server/types/models/account/actor-follow.ts b/server/types/models/actor/actor-follow.ts index 8e19c6140..98a6ca8a5 100644 --- a/server/types/models/account/actor-follow.ts +++ b/server/types/models/actor/actor-follow.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { PickWith } from '@shared/core-utils' | 1 | import { PickWith } from '@shared/core-utils' |
2 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 2 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
3 | import { | 3 | import { |
4 | MActor, | 4 | MActor, |
5 | MActorChannelAccountActor, | 5 | MActorChannelAccountActor, |
diff --git a/server/types/models/account/actor-image.ts b/server/types/models/actor/actor-image.ts index e59f8b141..89adb01ae 100644 --- a/server/types/models/account/actor-image.ts +++ b/server/types/models/actor/actor-image.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { ActorImageModel } from '../../../models/account/actor-image' | ||
2 | import { FunctionProperties } from '@shared/core-utils' | 1 | import { FunctionProperties } from '@shared/core-utils' |
2 | import { ActorImageModel } from '../../../models/actor/actor-image' | ||
3 | 3 | ||
4 | export type MActorImage = ActorImageModel | 4 | export type MActorImage = ActorImageModel |
5 | 5 | ||
diff --git a/server/types/models/account/actor.ts b/server/types/models/actor/actor.ts index 0b620872e..b3a70cbce 100644 --- a/server/types/models/account/actor.ts +++ b/server/types/models/actor/actor.ts | |||
@@ -1,9 +1,8 @@ | |||
1 | |||
2 | import { FunctionProperties, PickWith, PickWithOpt } from '@shared/core-utils' | 1 | import { FunctionProperties, PickWith, PickWithOpt } from '@shared/core-utils' |
3 | import { ActorModel } from '../../../models/activitypub/actor' | 2 | import { ActorModel } from '../../../models/actor/actor' |
3 | import { MAccount, MAccountDefault, MAccountId, MAccountIdActor } from '../account' | ||
4 | import { MServer, MServerHost, MServerHostBlocks, MServerRedundancyAllowed } from '../server' | 4 | import { MServer, MServerHost, MServerHostBlocks, MServerRedundancyAllowed } from '../server' |
5 | import { MChannel, MChannelAccountActor, MChannelAccountDefault, MChannelId, MChannelIdActor } from '../video' | 5 | import { MChannel, MChannelAccountActor, MChannelAccountDefault, MChannelId, MChannelIdActor } from '../video' |
6 | import { MAccount, MAccountDefault, MAccountId, MAccountIdActor } from './account' | ||
7 | import { MActorImage, MActorImageFormattable } from './actor-image' | 6 | import { MActorImage, MActorImageFormattable } from './actor-image' |
8 | 7 | ||
9 | type Use<K extends keyof ActorModel, M> = PickWith<ActorModel, K, M> | 8 | type Use<K extends keyof ActorModel, M> = PickWith<ActorModel, K, M> |
diff --git a/server/types/models/actor/index.ts b/server/types/models/actor/index.ts new file mode 100644 index 000000000..b27815255 --- /dev/null +++ b/server/types/models/actor/index.ts | |||
@@ -0,0 +1,3 @@ | |||
1 | export * from './actor-follow' | ||
2 | export * from './actor-image' | ||
3 | export * from './actor' | ||
diff --git a/server/types/models/index.ts b/server/types/models/index.ts index b4fdb1ff3..704cb9844 100644 --- a/server/types/models/index.ts +++ b/server/types/models/index.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | export * from './abuse' | ||
1 | export * from './account' | 2 | export * from './account' |
3 | export * from './actor' | ||
2 | export * from './application' | 4 | export * from './application' |
3 | export * from './moderation' | ||
4 | export * from './oauth' | 5 | export * from './oauth' |
5 | export * from './server' | 6 | export * from './server' |
6 | export * from './user' | 7 | export * from './user' |
diff --git a/server/types/models/user/user-notification-setting.ts b/server/types/models/user/user-notification-setting.ts index c674add1b..d1db645e7 100644 --- a/server/types/models/user/user-notification-setting.ts +++ b/server/types/models/user/user-notification-setting.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { UserNotificationSettingModel } from '@server/models/account/user-notification-setting' | 1 | import { UserNotificationSettingModel } from '@server/models/user/user-notification-setting' |
2 | 2 | ||
3 | export type MNotificationSetting = Omit<UserNotificationSettingModel, 'User'> | 3 | export type MNotificationSetting = Omit<UserNotificationSettingModel, 'User'> |
4 | 4 | ||
diff --git a/server/types/models/user/user-notification.ts b/server/types/models/user/user-notification.ts index 7ebb0485d..918614dd1 100644 --- a/server/types/models/user/user-notification.ts +++ b/server/types/models/user/user-notification.ts | |||
@@ -2,13 +2,13 @@ import { VideoAbuseModel } from '@server/models/abuse/video-abuse' | |||
2 | import { VideoCommentAbuseModel } from '@server/models/abuse/video-comment-abuse' | 2 | import { VideoCommentAbuseModel } from '@server/models/abuse/video-comment-abuse' |
3 | import { ApplicationModel } from '@server/models/application/application' | 3 | import { ApplicationModel } from '@server/models/application/application' |
4 | import { PluginModel } from '@server/models/server/plugin' | 4 | import { PluginModel } from '@server/models/server/plugin' |
5 | import { UserNotificationModel } from '@server/models/user/user-notification' | ||
5 | import { PickWith, PickWithOpt } from '@shared/core-utils' | 6 | import { PickWith, PickWithOpt } from '@shared/core-utils' |
6 | import { AbuseModel } from '../../../models/abuse/abuse' | 7 | import { AbuseModel } from '../../../models/abuse/abuse' |
7 | import { AccountModel } from '../../../models/account/account' | 8 | import { AccountModel } from '../../../models/account/account' |
8 | import { ActorImageModel } from '../../../models/account/actor-image' | 9 | import { ActorModel } from '../../../models/actor/actor' |
9 | import { UserNotificationModel } from '../../../models/account/user-notification' | 10 | import { ActorFollowModel } from '../../../models/actor/actor-follow' |
10 | import { ActorModel } from '../../../models/activitypub/actor' | 11 | import { ActorImageModel } from '../../../models/actor/actor-image' |
11 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
12 | import { ServerModel } from '../../../models/server/server' | 12 | import { ServerModel } from '../../../models/server/server' |
13 | import { VideoModel } from '../../../models/video/video' | 13 | import { VideoModel } from '../../../models/video/video' |
14 | import { VideoBlacklistModel } from '../../../models/video/video-blacklist' | 14 | import { VideoBlacklistModel } from '../../../models/video/video-blacklist' |
diff --git a/server/types/models/user/user-video-history.ts b/server/types/models/user/user-video-history.ts index 62673ab1b..34e2930e7 100644 --- a/server/types/models/user/user-video-history.ts +++ b/server/types/models/user/user-video-history.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' | 1 | import { UserVideoHistoryModel } from '../../../models/user/user-video-history' |
2 | 2 | ||
3 | export type MUserVideoHistory = Omit<UserVideoHistoryModel, 'Video' | 'User'> | 3 | export type MUserVideoHistory = Omit<UserVideoHistoryModel, 'Video' | 'User'> |
4 | 4 | ||
diff --git a/server/types/models/user/user.ts b/server/types/models/user/user.ts index fa7de9c52..f79220e11 100644 --- a/server/types/models/user/user.ts +++ b/server/types/models/user/user.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { AccountModel } from '@server/models/account/account' | 1 | import { AccountModel } from '@server/models/account/account' |
2 | import { UserModel } from '@server/models/user/user' | ||
2 | import { MVideoPlaylist } from '@server/types/models' | 3 | import { MVideoPlaylist } from '@server/types/models' |
3 | import { PickWith, PickWithOpt } from '@shared/core-utils' | 4 | import { PickWith, PickWithOpt } from '@shared/core-utils' |
4 | import { UserModel } from '../../../models/account/user' | ||
5 | import { | 5 | import { |
6 | MAccount, | 6 | MAccount, |
7 | MAccountDefault, | 7 | MAccountDefault, |
diff --git a/server/types/models/video/video-channels.ts b/server/types/models/video/video-channels.ts index f577807ca..c147567d9 100644 --- a/server/types/models/video/video-channels.ts +++ b/server/types/models/video/video-channels.ts | |||
@@ -9,7 +9,9 @@ import { | |||
9 | MAccountSummaryBlocks, | 9 | MAccountSummaryBlocks, |
10 | MAccountSummaryFormattable, | 10 | MAccountSummaryFormattable, |
11 | MAccountUrl, | 11 | MAccountUrl, |
12 | MAccountUserId, | 12 | MAccountUserId |
13 | } from '../account' | ||
14 | import { | ||
13 | MActor, | 15 | MActor, |
14 | MActorAccountChannelId, | 16 | MActorAccountChannelId, |
15 | MActorAPChannel, | 17 | MActorAPChannel, |
@@ -23,7 +25,7 @@ import { | |||
23 | MActorSummary, | 25 | MActorSummary, |
24 | MActorSummaryFormattable, | 26 | MActorSummaryFormattable, |
25 | MActorUrl | 27 | MActorUrl |
26 | } from '../account' | 28 | } from '../actor' |
27 | import { MVideo } from './video' | 29 | import { MVideo } from './video' |
28 | 30 | ||
29 | type Use<K extends keyof VideoChannelModel, M> = PickWith<VideoChannelModel, K, M> | 31 | type Use<K extends keyof VideoChannelModel, M> = PickWith<VideoChannelModel, K, M> |
diff --git a/server/types/models/video/video-share.ts b/server/types/models/video/video-share.ts index b7a783bb6..78f44e58c 100644 --- a/server/types/models/video/video-share.ts +++ b/server/types/models/video/video-share.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { VideoShareModel } from '../../../models/video/video-share' | ||
2 | import { PickWith } from '@shared/core-utils' | 1 | import { PickWith } from '@shared/core-utils' |
3 | import { MActorDefault } from '../account' | 2 | import { VideoShareModel } from '../../../models/video/video-share' |
3 | import { MActorDefault } from '../actor' | ||
4 | import { MVideo } from './video' | 4 | import { MVideo } from './video' |
5 | 5 | ||
6 | type Use<K extends keyof VideoShareModel, M> = PickWith<VideoShareModel, K, M> | 6 | type Use<K extends keyof VideoShareModel, M> = PickWith<VideoShareModel, K, M> |
diff --git a/server/types/plugins/register-server-option.model.ts b/server/types/plugins/register-server-option.model.ts index 2432b7ac4..8774bcd8c 100644 --- a/server/types/plugins/register-server-option.model.ts +++ b/server/types/plugins/register-server-option.model.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Router, Response } from 'express' | 1 | import { Response, Router } from 'express' |
2 | import { Logger } from 'winston' | 2 | import { Logger } from 'winston' |
3 | import { ActorModel } from '@server/models/activitypub/actor' | 3 | import { ActorModel } from '@server/models/actor/actor' |
4 | import { | 4 | import { |
5 | PluginPlaylistPrivacyManager, | 5 | PluginPlaylistPrivacyManager, |
6 | PluginSettingsManager, | 6 | PluginSettingsManager, |
diff --git a/server/types/sequelize.ts b/server/types/sequelize.ts index 9cd83612d..535113d01 100644 --- a/server/types/sequelize.ts +++ b/server/types/sequelize.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Model } from 'sequelize-typescript' | 1 | import { AttributesOnly } from '@shared/core-utils' |
2 | import { Model } from 'sequelize' | ||
2 | 3 | ||
3 | // Thanks to sequelize-typescript: https://github.com/RobinBuschmann/sequelize-typescript | 4 | // Thanks to sequelize-typescript: https://github.com/RobinBuschmann/sequelize-typescript |
4 | 5 | ||
@@ -9,7 +10,7 @@ export type Omit<T, K extends keyof T> = { [P in Diff<keyof T, K>]: T[P] } | |||
9 | 10 | ||
10 | export type RecursivePartial<T> = { [P in keyof T]?: RecursivePartial<T[P]> } | 11 | export type RecursivePartial<T> = { [P in keyof T]?: RecursivePartial<T[P]> } |
11 | 12 | ||
12 | export type FilteredModelAttributes<T extends Model<T>> = RecursivePartial<Omit<T, keyof Model<any>>> & { | 13 | export type FilteredModelAttributes<T extends Model<any>> = Partial<AttributesOnly<T>> & { |
13 | id?: number | any | 14 | id?: number | any |
14 | createdAt?: Date | any | 15 | createdAt?: Date | any |
15 | updatedAt?: Date | any | 16 | updatedAt?: Date | any |
diff --git a/shared/core-utils/miscs/types.ts b/shared/core-utils/miscs/types.ts index bb64dc830..bd2a97b98 100644 --- a/shared/core-utils/miscs/types.ts +++ b/shared/core-utils/miscs/types.ts | |||
@@ -6,6 +6,10 @@ export type FunctionPropertyNames<T> = { | |||
6 | 6 | ||
7 | export type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>> | 7 | export type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>> |
8 | 8 | ||
9 | export type AttributesOnly<T> = { | ||
10 | [K in keyof T]: T[K] extends Function ? never : T[K] | ||
11 | } | ||
12 | |||
9 | export type PickWith<T, KT extends keyof T, V> = { | 13 | export type PickWith<T, KT extends keyof T, V> = { |
10 | [P in KT]: T[P] extends V ? V : never | 14 | [P in KT]: T[P] extends V ? V : never |
11 | } | 15 | } |
diff --git a/shared/extra-utils/index.ts b/shared/extra-utils/index.ts index 898a92d43..720db19cb 100644 --- a/shared/extra-utils/index.ts +++ b/shared/extra-utils/index.ts | |||
@@ -1,15 +1,24 @@ | |||
1 | export * from './bulk/bulk' | 1 | export * from './bulk/bulk' |
2 | |||
2 | export * from './cli/cli' | 3 | export * from './cli/cli' |
4 | |||
3 | export * from './feeds/feeds' | 5 | export * from './feeds/feeds' |
6 | |||
4 | export * from './mock-servers/mock-instances-index' | 7 | export * from './mock-servers/mock-instances-index' |
5 | export * from './miscs/miscs' | 8 | |
9 | export * from './miscs/email' | ||
6 | export * from './miscs/sql' | 10 | export * from './miscs/sql' |
11 | export * from './miscs/miscs' | ||
7 | export * from './miscs/stubs' | 12 | export * from './miscs/stubs' |
13 | |||
8 | export * from './moderation/abuses' | 14 | export * from './moderation/abuses' |
9 | export * from './plugins/mock-blocklist' | 15 | export * from './plugins/mock-blocklist' |
16 | |||
10 | export * from './requests/check-api-params' | 17 | export * from './requests/check-api-params' |
11 | export * from './requests/requests' | 18 | export * from './requests/requests' |
19 | |||
12 | export * from './search/videos' | 20 | export * from './search/videos' |
21 | |||
13 | export * from './server/activitypub' | 22 | export * from './server/activitypub' |
14 | export * from './server/clients' | 23 | export * from './server/clients' |
15 | export * from './server/config' | 24 | export * from './server/config' |
@@ -18,9 +27,14 @@ export * from './server/follows' | |||
18 | export * from './server/jobs' | 27 | export * from './server/jobs' |
19 | export * from './server/plugins' | 28 | export * from './server/plugins' |
20 | export * from './server/servers' | 29 | export * from './server/servers' |
30 | |||
21 | export * from './users/accounts' | 31 | export * from './users/accounts' |
32 | export * from './users/blocklist' | ||
22 | export * from './users/login' | 33 | export * from './users/login' |
34 | export * from './users/user-notifications' | ||
35 | export * from './users/user-subscriptions' | ||
23 | export * from './users/users' | 36 | export * from './users/users' |
37 | |||
24 | export * from './videos/live' | 38 | export * from './videos/live' |
25 | export * from './videos/services' | 39 | export * from './videos/services' |
26 | export * from './videos/video-blacklist' | 40 | export * from './videos/video-blacklist' |
diff --git a/shared/extra-utils/server/plugins.ts b/shared/extra-utils/server/plugins.ts index 864954ee7..d53e5b382 100644 --- a/shared/extra-utils/server/plugins.ts +++ b/shared/extra-utils/server/plugins.ts | |||
@@ -4,12 +4,12 @@ import { expect } from 'chai' | |||
4 | import { readJSON, writeJSON } from 'fs-extra' | 4 | import { readJSON, writeJSON } from 'fs-extra' |
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import { RegisteredServerSettings } from '@shared/models' | 6 | import { RegisteredServerSettings } from '@shared/models' |
7 | import { PeertubePluginIndexList } from '../../models/plugins/peertube-plugin-index-list.model' | 7 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
8 | import { PeertubePluginIndexList } from '../../models/plugins/plugin-index/peertube-plugin-index-list.model' | ||
8 | import { PluginType } from '../../models/plugins/plugin.type' | 9 | import { PluginType } from '../../models/plugins/plugin.type' |
9 | import { buildServerDirectory, root } from '../miscs/miscs' | 10 | import { buildServerDirectory, root } from '../miscs/miscs' |
10 | import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests' | 11 | import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests' |
11 | import { ServerInfo } from './servers' | 12 | import { ServerInfo } from './servers' |
12 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
13 | 13 | ||
14 | function listPlugins (parameters: { | 14 | function listPlugins (parameters: { |
15 | url: string | 15 | url: string |
diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts index 479f08e12..d04757470 100644 --- a/shared/extra-utils/server/servers.ts +++ b/shared/extra-utils/server/servers.ts | |||
@@ -45,9 +45,12 @@ interface ServerInfo { | |||
45 | uuid: string | 45 | uuid: string |
46 | name?: string | 46 | name?: string |
47 | url?: string | 47 | url?: string |
48 | |||
48 | account?: { | 49 | account?: { |
49 | name: string | 50 | name: string |
50 | } | 51 | } |
52 | |||
53 | embedPath?: string | ||
51 | } | 54 | } |
52 | 55 | ||
53 | remoteVideo?: { | 56 | remoteVideo?: { |
diff --git a/shared/models/nodeinfo/index.ts b/shared/models/nodeinfo/index.ts new file mode 100644 index 000000000..faa64302a --- /dev/null +++ b/shared/models/nodeinfo/index.ts | |||
@@ -0,0 +1 @@ | |||
export * from './nodeinfo.model' | |||
diff --git a/shared/models/nodeinfo/index.d.ts b/shared/models/nodeinfo/nodeinfo.model.ts index 336cb66d2..336cb66d2 100644 --- a/shared/models/nodeinfo/index.d.ts +++ b/shared/models/nodeinfo/nodeinfo.model.ts | |||
diff --git a/shared/models/overviews/index.ts b/shared/models/overviews/index.ts index 376609efa..468507c6b 100644 --- a/shared/models/overviews/index.ts +++ b/shared/models/overviews/index.ts | |||
@@ -1 +1 @@ | |||
export * from './videos-overview' | export * from './videos-overview.model' | ||
diff --git a/shared/models/overviews/videos-overview.ts b/shared/models/overviews/videos-overview.model.ts index 0f3cb4a52..0f3cb4a52 100644 --- a/shared/models/overviews/videos-overview.ts +++ b/shared/models/overviews/videos-overview.model.ts | |||
diff --git a/shared/models/plugins/client-hook.model.ts b/shared/models/plugins/client/client-hook.model.ts index 620651051..620651051 100644 --- a/shared/models/plugins/client-hook.model.ts +++ b/shared/models/plugins/client/client-hook.model.ts | |||
diff --git a/shared/models/plugins/client/index.ts b/shared/models/plugins/client/index.ts new file mode 100644 index 000000000..6dfc6351f --- /dev/null +++ b/shared/models/plugins/client/index.ts | |||
@@ -0,0 +1,6 @@ | |||
1 | export * from './client-hook.model' | ||
2 | export * from './plugin-client-scope.type' | ||
3 | export * from './plugin-element-placeholder.type' | ||
4 | export * from './register-client-form-field.model' | ||
5 | export * from './register-client-hook.model' | ||
6 | export * from './register-client-settings-script.model' | ||
diff --git a/shared/models/plugins/plugin-client-scope.type.ts b/shared/models/plugins/client/plugin-client-scope.type.ts index 8cc234ff2..8cc234ff2 100644 --- a/shared/models/plugins/plugin-client-scope.type.ts +++ b/shared/models/plugins/client/plugin-client-scope.type.ts | |||
diff --git a/shared/models/plugins/plugin-element-placeholder.type.ts b/shared/models/plugins/client/plugin-element-placeholder.type.ts index 129099c62..129099c62 100644 --- a/shared/models/plugins/plugin-element-placeholder.type.ts +++ b/shared/models/plugins/client/plugin-element-placeholder.type.ts | |||
diff --git a/shared/models/plugins/register-client-form-field.model.ts b/shared/models/plugins/client/register-client-form-field.model.ts index 2df071337..2df071337 100644 --- a/shared/models/plugins/register-client-form-field.model.ts +++ b/shared/models/plugins/client/register-client-form-field.model.ts | |||
diff --git a/shared/models/plugins/register-client-hook.model.ts b/shared/models/plugins/client/register-client-hook.model.ts index 81047b21d..81047b21d 100644 --- a/shared/models/plugins/register-client-hook.model.ts +++ b/shared/models/plugins/client/register-client-hook.model.ts | |||
diff --git a/shared/models/plugins/register-client-settings-script.model.ts b/shared/models/plugins/client/register-client-settings-script.model.ts index ac16af366..481ceef96 100644 --- a/shared/models/plugins/register-client-settings-script.model.ts +++ b/shared/models/plugins/client/register-client-settings-script.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { RegisterServerSettingOptions } from "./register-server-setting.model" | 1 | import { RegisterServerSettingOptions } from '../server' |
2 | 2 | ||
3 | export interface RegisterClientSettingsScript { | 3 | export interface RegisterClientSettingsScript { |
4 | isSettingHidden (options: { | 4 | isSettingHidden (options: { |
diff --git a/shared/models/plugins/index.ts b/shared/models/plugins/index.ts index 03b27f907..cbbe4916e 100644 --- a/shared/models/plugins/index.ts +++ b/shared/models/plugins/index.ts | |||
@@ -1,28 +1,6 @@ | |||
1 | export * from './client-hook.model' | 1 | export * from './client' |
2 | export * from './plugin-index' | ||
3 | export * from './server' | ||
2 | export * from './hook-type.enum' | 4 | export * from './hook-type.enum' |
3 | export * from './install-plugin.model' | ||
4 | export * from './manage-plugin.model' | ||
5 | export * from './peertube-plugin-index-list.model' | ||
6 | export * from './peertube-plugin-index.model' | ||
7 | export * from './peertube-plugin-latest-version.model' | ||
8 | export * from './peertube-plugin.model' | ||
9 | export * from './plugin-client-scope.type' | ||
10 | export * from './plugin-element-placeholder.type' | ||
11 | export * from './plugin-package-json.model' | 5 | export * from './plugin-package-json.model' |
12 | export * from './plugin-playlist-privacy-manager.model' | ||
13 | export * from './plugin-settings-manager.model' | ||
14 | export * from './plugin-storage-manager.model' | ||
15 | export * from './plugin-transcoding-manager.model' | ||
16 | export * from './plugin-translation.model' | ||
17 | export * from './plugin-video-category-manager.model' | ||
18 | export * from './plugin-video-language-manager.model' | ||
19 | export * from './plugin-video-licence-manager.model' | ||
20 | export * from './plugin-video-privacy-manager.model' | ||
21 | export * from './plugin.type' | 6 | export * from './plugin.type' |
22 | export * from './public-server.setting' | ||
23 | export * from './register-client-hook.model' | ||
24 | export * from './register-client-settings-script.model' | ||
25 | export * from './register-client-form-field.model' | ||
26 | export * from './register-server-hook.model' | ||
27 | export * from './register-server-setting.model' | ||
28 | export * from './server-hook.model' | ||
diff --git a/shared/models/plugins/plugin-index/index.ts b/shared/models/plugins/plugin-index/index.ts new file mode 100644 index 000000000..913846638 --- /dev/null +++ b/shared/models/plugins/plugin-index/index.ts | |||
@@ -0,0 +1,3 @@ | |||
1 | export * from './peertube-plugin-index-list.model' | ||
2 | export * from './peertube-plugin-index.model' | ||
3 | export * from './peertube-plugin-latest-version.model' | ||
diff --git a/shared/models/plugins/peertube-plugin-index-list.model.ts b/shared/models/plugins/plugin-index/peertube-plugin-index-list.model.ts index 817bac31e..ecb46482e 100644 --- a/shared/models/plugins/peertube-plugin-index-list.model.ts +++ b/shared/models/plugins/plugin-index/peertube-plugin-index-list.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { PluginType } from './plugin.type' | 1 | import { PluginType } from '../plugin.type' |
2 | 2 | ||
3 | export interface PeertubePluginIndexList { | 3 | export interface PeertubePluginIndexList { |
4 | start: number | 4 | start: number |
diff --git a/shared/models/plugins/peertube-plugin-index.model.ts b/shared/models/plugins/plugin-index/peertube-plugin-index.model.ts index e91c8b4dc..e91c8b4dc 100644 --- a/shared/models/plugins/peertube-plugin-index.model.ts +++ b/shared/models/plugins/plugin-index/peertube-plugin-index.model.ts | |||
diff --git a/shared/models/plugins/peertube-plugin-latest-version.model.ts b/shared/models/plugins/plugin-index/peertube-plugin-latest-version.model.ts index 811a64429..811a64429 100644 --- a/shared/models/plugins/peertube-plugin-latest-version.model.ts +++ b/shared/models/plugins/plugin-index/peertube-plugin-latest-version.model.ts | |||
diff --git a/shared/models/plugins/plugin-package-json.model.ts b/shared/models/plugins/plugin-package-json.model.ts index c26e9ae5b..b2f92af80 100644 --- a/shared/models/plugins/plugin-package-json.model.ts +++ b/shared/models/plugins/plugin-package-json.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { PluginClientScope } from './plugin-client-scope.type' | 1 | import { PluginClientScope } from './client/plugin-client-scope.type' |
2 | 2 | ||
3 | export type PluginTranslationPaths = { | 3 | export type PluginTranslationPaths = { |
4 | [ locale: string ]: string | 4 | [ locale: string ]: string |
diff --git a/shared/models/plugins/server/api/index.ts b/shared/models/plugins/server/api/index.ts new file mode 100644 index 000000000..eb59a03f0 --- /dev/null +++ b/shared/models/plugins/server/api/index.ts | |||
@@ -0,0 +1,3 @@ | |||
1 | export * from './install-plugin.model' | ||
2 | export * from './manage-plugin.model' | ||
3 | export * from './peertube-plugin.model' | ||
diff --git a/shared/models/plugins/install-plugin.model.ts b/shared/models/plugins/server/api/install-plugin.model.ts index 5a268ebe1..5a268ebe1 100644 --- a/shared/models/plugins/install-plugin.model.ts +++ b/shared/models/plugins/server/api/install-plugin.model.ts | |||
diff --git a/shared/models/plugins/manage-plugin.model.ts b/shared/models/plugins/server/api/manage-plugin.model.ts index 612b3056c..612b3056c 100644 --- a/shared/models/plugins/manage-plugin.model.ts +++ b/shared/models/plugins/server/api/manage-plugin.model.ts | |||
diff --git a/shared/models/plugins/peertube-plugin.model.ts b/shared/models/plugins/server/api/peertube-plugin.model.ts index 2b0bb8cfa..54c383f57 100644 --- a/shared/models/plugins/peertube-plugin.model.ts +++ b/shared/models/plugins/server/api/peertube-plugin.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { PluginType } from './plugin.type' | 1 | import { PluginType } from '../../plugin.type' |
2 | 2 | ||
3 | export interface PeerTubePlugin { | 3 | export interface PeerTubePlugin { |
4 | name: string | 4 | name: string |
diff --git a/shared/models/plugins/server/index.ts b/shared/models/plugins/server/index.ts new file mode 100644 index 000000000..d3ff49d3b --- /dev/null +++ b/shared/models/plugins/server/index.ts | |||
@@ -0,0 +1,6 @@ | |||
1 | export * from './api' | ||
2 | export * from './managers' | ||
3 | export * from './settings' | ||
4 | export * from './plugin-translation.model' | ||
5 | export * from './register-server-hook.model' | ||
6 | export * from './server-hook.model' | ||
diff --git a/shared/models/plugins/server/managers/index.ts b/shared/models/plugins/server/managers/index.ts new file mode 100644 index 000000000..49365a854 --- /dev/null +++ b/shared/models/plugins/server/managers/index.ts | |||
@@ -0,0 +1,9 @@ | |||
1 | |||
2 | export * from './plugin-playlist-privacy-manager.model' | ||
3 | export * from './plugin-settings-manager.model' | ||
4 | export * from './plugin-storage-manager.model' | ||
5 | export * from './plugin-transcoding-manager.model' | ||
6 | export * from './plugin-video-category-manager.model' | ||
7 | export * from './plugin-video-language-manager.model' | ||
8 | export * from './plugin-video-licence-manager.model' | ||
9 | export * from './plugin-video-privacy-manager.model' | ||
diff --git a/shared/models/plugins/plugin-playlist-privacy-manager.model.ts b/shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts index d1823ef4e..4703c0a8b 100644 --- a/shared/models/plugins/plugin-playlist-privacy-manager.model.ts +++ b/shared/models/plugins/server/managers/plugin-playlist-privacy-manager.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { VideoPlaylistPrivacy } from '../videos/playlist/video-playlist-privacy.model' | 1 | import { VideoPlaylistPrivacy } from '../../../videos/playlist/video-playlist-privacy.model' |
2 | 2 | ||
3 | export interface PluginPlaylistPrivacyManager { | 3 | export interface PluginPlaylistPrivacyManager { |
4 | // PUBLIC = 1, | 4 | // PUBLIC = 1, |
diff --git a/shared/models/plugins/plugin-settings-manager.model.ts b/shared/models/plugins/server/managers/plugin-settings-manager.model.ts index 3c28c0565..3c28c0565 100644 --- a/shared/models/plugins/plugin-settings-manager.model.ts +++ b/shared/models/plugins/server/managers/plugin-settings-manager.model.ts | |||
diff --git a/shared/models/plugins/plugin-storage-manager.model.ts b/shared/models/plugins/server/managers/plugin-storage-manager.model.ts index 51567044a..51567044a 100644 --- a/shared/models/plugins/plugin-storage-manager.model.ts +++ b/shared/models/plugins/server/managers/plugin-storage-manager.model.ts | |||
diff --git a/shared/models/plugins/plugin-transcoding-manager.model.ts b/shared/models/plugins/server/managers/plugin-transcoding-manager.model.ts index 8babccd4e..a0422a460 100644 --- a/shared/models/plugins/plugin-transcoding-manager.model.ts +++ b/shared/models/plugins/server/managers/plugin-transcoding-manager.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { EncoderOptionsBuilder } from '../videos/video-transcoding.model' | 1 | import { EncoderOptionsBuilder } from '../../../videos/video-transcoding.model' |
2 | 2 | ||
3 | export interface PluginTranscodingManager { | 3 | export interface PluginTranscodingManager { |
4 | addLiveProfile (encoder: string, profile: string, builder: EncoderOptionsBuilder): boolean | 4 | addLiveProfile (encoder: string, profile: string, builder: EncoderOptionsBuilder): boolean |
diff --git a/shared/models/plugins/plugin-video-category-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-category-manager.model.ts index 201bfa979..201bfa979 100644 --- a/shared/models/plugins/plugin-video-category-manager.model.ts +++ b/shared/models/plugins/server/managers/plugin-video-category-manager.model.ts | |||
diff --git a/shared/models/plugins/plugin-video-language-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-language-manager.model.ts index 3fd577a79..3fd577a79 100644 --- a/shared/models/plugins/plugin-video-language-manager.model.ts +++ b/shared/models/plugins/server/managers/plugin-video-language-manager.model.ts | |||
diff --git a/shared/models/plugins/plugin-video-licence-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts index 82a634d3a..82a634d3a 100644 --- a/shared/models/plugins/plugin-video-licence-manager.model.ts +++ b/shared/models/plugins/server/managers/plugin-video-licence-manager.model.ts | |||
diff --git a/shared/models/plugins/plugin-video-privacy-manager.model.ts b/shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts index 3ada99608..7717115e3 100644 --- a/shared/models/plugins/plugin-video-privacy-manager.model.ts +++ b/shared/models/plugins/server/managers/plugin-video-privacy-manager.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { VideoPrivacy } from '../videos/video-privacy.enum' | 1 | import { VideoPrivacy } from '../../../videos/video-privacy.enum' |
2 | 2 | ||
3 | export interface PluginVideoPrivacyManager { | 3 | export interface PluginVideoPrivacyManager { |
4 | // PUBLIC = 1 | 4 | // PUBLIC = 1 |
diff --git a/shared/models/plugins/plugin-translation.model.ts b/shared/models/plugins/server/plugin-translation.model.ts index a2dd8e560..a2dd8e560 100644 --- a/shared/models/plugins/plugin-translation.model.ts +++ b/shared/models/plugins/server/plugin-translation.model.ts | |||
diff --git a/shared/models/plugins/register-server-hook.model.ts b/shared/models/plugins/server/register-server-hook.model.ts index 746fdc329..746fdc329 100644 --- a/shared/models/plugins/register-server-hook.model.ts +++ b/shared/models/plugins/server/register-server-hook.model.ts | |||
diff --git a/shared/models/plugins/server-hook.model.ts b/shared/models/plugins/server/server-hook.model.ts index 88277af5a..88277af5a 100644 --- a/shared/models/plugins/server-hook.model.ts +++ b/shared/models/plugins/server/server-hook.model.ts | |||
diff --git a/shared/models/plugins/server/settings/index.ts b/shared/models/plugins/server/settings/index.ts new file mode 100644 index 000000000..b456de019 --- /dev/null +++ b/shared/models/plugins/server/settings/index.ts | |||
@@ -0,0 +1,2 @@ | |||
1 | export * from './public-server.setting' | ||
2 | export * from './register-server-setting.model' | ||
diff --git a/shared/models/plugins/public-server.setting.ts b/shared/models/plugins/server/settings/public-server.setting.ts index 9802c4d7d..9802c4d7d 100644 --- a/shared/models/plugins/public-server.setting.ts +++ b/shared/models/plugins/server/settings/public-server.setting.ts | |||
diff --git a/shared/models/plugins/register-server-setting.model.ts b/shared/models/plugins/server/settings/register-server-setting.model.ts index 9f45c3c37..d9a798cac 100644 --- a/shared/models/plugins/register-server-setting.model.ts +++ b/shared/models/plugins/server/settings/register-server-setting.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { RegisterClientFormFieldOptions } from './register-client-form-field.model' | 1 | import { RegisterClientFormFieldOptions } from '../../client' |
2 | 2 | ||
3 | export type RegisterServerSettingOptions = RegisterClientFormFieldOptions & { | 3 | export type RegisterServerSettingOptions = RegisterClientFormFieldOptions & { |
4 | // If the setting is not private, anyone can view its value (client code included) | 4 | // If the setting is not private, anyone can view its value (client code included) |
diff --git a/shared/models/redundancy/index.ts b/shared/models/redundancy/index.ts index 649cc489f..641a5d625 100644 --- a/shared/models/redundancy/index.ts +++ b/shared/models/redundancy/index.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | export * from './videos-redundancy-strategy.model' | ||
2 | export * from './video-redundancies-filters.model' | 1 | export * from './video-redundancies-filters.model' |
2 | export * from './video-redundancy-config-filter.type' | ||
3 | export * from './video-redundancy.model' | 3 | export * from './video-redundancy.model' |
4 | export * from './videos-redundancy-strategy.model' | ||
diff --git a/shared/models/server/server-config.model.ts b/shared/models/server/server-config.model.ts index 85d84af44..2c5026b30 100644 --- a/shared/models/server/server-config.model.ts +++ b/shared/models/server/server-config.model.ts | |||
@@ -215,3 +215,5 @@ export interface ServerConfig { | |||
215 | dismissable: boolean | 215 | dismissable: boolean |
216 | } | 216 | } |
217 | } | 217 | } |
218 | |||
219 | export type HTMLServerConfig = Omit<ServerConfig, 'signup'> | ||
diff --git a/shared/models/server/server-error-code.enum.ts b/shared/models/server/server-error-code.enum.ts index c02b0e6c7..d17d958be 100644 --- a/shared/models/server/server-error-code.enum.ts +++ b/shared/models/server/server-error-code.enum.ts | |||
@@ -2,4 +2,5 @@ export const enum ServerErrorCode { | |||
2 | DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS = 1, | 2 | DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS = 1, |
3 | MAX_INSTANCE_LIVES_LIMIT_REACHED = 2, | 3 | MAX_INSTANCE_LIVES_LIMIT_REACHED = 2, |
4 | MAX_USER_LIVES_LIMIT_REACHED = 3, | 4 | MAX_USER_LIVES_LIMIT_REACHED = 3, |
5 | INCORRECT_FILES_IN_TORRENT = 4 | ||
5 | } | 6 | } |
diff --git a/shared/models/videos/change-ownership/index.ts b/shared/models/videos/change-ownership/index.ts new file mode 100644 index 000000000..a942fb2cd --- /dev/null +++ b/shared/models/videos/change-ownership/index.ts | |||
@@ -0,0 +1,3 @@ | |||
1 | export * from './video-change-ownership-accept.model' | ||
2 | export * from './video-change-ownership-create.model' | ||
3 | export * from './video-change-ownership.model' | ||
diff --git a/shared/models/videos/video-change-ownership-accept.model.ts b/shared/models/videos/change-ownership/video-change-ownership-accept.model.ts index f27247633..f27247633 100644 --- a/shared/models/videos/video-change-ownership-accept.model.ts +++ b/shared/models/videos/change-ownership/video-change-ownership-accept.model.ts | |||
diff --git a/shared/models/videos/video-change-ownership-create.model.ts b/shared/models/videos/change-ownership/video-change-ownership-create.model.ts index 40fcca285..40fcca285 100644 --- a/shared/models/videos/video-change-ownership-create.model.ts +++ b/shared/models/videos/change-ownership/video-change-ownership-create.model.ts | |||
diff --git a/shared/models/videos/video-change-ownership.model.ts b/shared/models/videos/change-ownership/video-change-ownership.model.ts index 669c7f3e7..3d31cad0a 100644 --- a/shared/models/videos/video-change-ownership.model.ts +++ b/shared/models/videos/change-ownership/video-change-ownership.model.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Account } from '../actors' | 1 | import { Account } from '../../actors' |
2 | import { Video } from './video.model' | 2 | import { Video } from '../video.model' |
3 | 3 | ||
4 | export interface VideoChangeOwnership { | 4 | export interface VideoChangeOwnership { |
5 | id: number | 5 | id: number |
diff --git a/shared/models/videos/comment/index.ts b/shared/models/videos/comment/index.ts new file mode 100644 index 000000000..7b9261a36 --- /dev/null +++ b/shared/models/videos/comment/index.ts | |||
@@ -0,0 +1 @@ | |||
export * from './video-comment.model' | |||
diff --git a/shared/models/videos/video-comment.model.ts b/shared/models/videos/comment/video-comment.model.ts index 9730a3f76..79c0e4c0a 100644 --- a/shared/models/videos/video-comment.model.ts +++ b/shared/models/videos/comment/video-comment.model.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { Account } from '../actors' | 1 | import { Account } from '../../actors' |
2 | 2 | ||
3 | export interface VideoComment { | 3 | export interface VideoComment { |
4 | id: number | 4 | id: number |
diff --git a/shared/models/videos/index.ts b/shared/models/videos/index.ts index fac3e0b2f..64f2c9df6 100644 --- a/shared/models/videos/index.ts +++ b/shared/models/videos/index.ts | |||
@@ -1,6 +1,8 @@ | |||
1 | export * from './blacklist' | 1 | export * from './blacklist' |
2 | export * from './caption' | 2 | export * from './caption' |
3 | export * from './change-ownership' | ||
3 | export * from './channel' | 4 | export * from './channel' |
5 | export * from './comment' | ||
4 | export * from './live' | 6 | export * from './live' |
5 | export * from './import' | 7 | export * from './import' |
6 | export * from './playlist' | 8 | export * from './playlist' |
@@ -10,17 +12,11 @@ export * from './nsfw-policy.type' | |||
10 | 12 | ||
11 | export * from './thumbnail.type' | 13 | export * from './thumbnail.type' |
12 | 14 | ||
13 | export * from './video-change-ownership-accept.model' | ||
14 | export * from './video-change-ownership-create.model' | ||
15 | export * from './video-change-ownership.model' | ||
16 | |||
17 | export * from './video-comment.model' | ||
18 | export * from './video-constant.model' | 15 | export * from './video-constant.model' |
19 | export * from './video-create.model' | 16 | export * from './video-create.model' |
20 | export * from './video-file-metadata' | ||
21 | export * from './video-file.model' | ||
22 | 17 | ||
23 | export * from './live/live-video.model' | 18 | export * from './video-file-metadata.model' |
19 | export * from './video-file.model' | ||
24 | 20 | ||
25 | export * from './video-privacy.enum' | 21 | export * from './video-privacy.enum' |
26 | export * from './video-query.type' | 22 | export * from './video-query.type' |
diff --git a/shared/models/videos/video-file-metadata.ts b/shared/models/videos/video-file-metadata.model.ts index 8f527c0a7..8f527c0a7 100644 --- a/shared/models/videos/video-file-metadata.ts +++ b/shared/models/videos/video-file-metadata.model.ts | |||
diff --git a/shared/models/videos/video-file.model.ts b/shared/models/videos/video-file.model.ts index 1e830b19c..28fce0aaf 100644 --- a/shared/models/videos/video-file.model.ts +++ b/shared/models/videos/video-file.model.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { VideoConstant } from './video-constant.model' | 1 | import { VideoConstant } from './video-constant.model' |
2 | import { VideoFileMetadata } from './video-file-metadata' | 2 | import { VideoFileMetadata } from './video-file-metadata.model' |
3 | import { VideoResolution } from './video-resolution.enum' | 3 | import { VideoResolution } from './video-resolution.enum' |
4 | 4 | ||
5 | export interface VideoFile { | 5 | export interface VideoFile { |
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 0e0d2ab5f..fc62f95bb 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml | |||
@@ -4,12 +4,12 @@ info: | |||
4 | version: 3.2.0-rc.1 | 4 | version: 3.2.0-rc.1 |
5 | contact: | 5 | contact: |
6 | name: PeerTube Community | 6 | name: PeerTube Community |
7 | url: 'https://joinpeertube.org' | 7 | url: https://joinpeertube.org |
8 | license: | 8 | license: |
9 | name: AGPLv3.0 | 9 | name: AGPLv3.0 |
10 | url: 'https://github.com/Chocobozzz/PeerTube/blob/master/LICENSE' | 10 | url: https://github.com/Chocobozzz/PeerTube/blob/master/LICENSE |
11 | x-logo: | 11 | x-logo: |
12 | url: 'https://joinpeertube.org/img/brand.png' | 12 | url: https://joinpeertube.org/img/brand.png |
13 | altText: PeerTube Project Homepage | 13 | altText: PeerTube Project Homepage |
14 | description: | | 14 | description: | |
15 | The PeerTube API is built on HTTP(S) and is RESTful. You can use your favorite | 15 | The PeerTube API is built on HTTP(S) and is RESTful. You can use your favorite |
@@ -27,8 +27,8 @@ info: | |||
27 | # Authentication | 27 | # Authentication |
28 | 28 | ||
29 | When you sign up for an account on a PeerTube instance, you are given the possibility | 29 | When you sign up for an account on a PeerTube instance, you are given the possibility |
30 | to generate sessions on it, and authenticate there using a session token. Only __one | 30 | to generate sessions on it, and authenticate there using an access token. Only __one |
31 | session token can currently be used at a time__. | 31 | access token can currently be used at a time__. |
32 | 32 | ||
33 | ## Roles | 33 | ## Roles |
34 | 34 | ||
@@ -38,41 +38,60 @@ info: | |||
38 | # Errors | 38 | # Errors |
39 | 39 | ||
40 | The API uses standard HTTP status codes to indicate the success or failure | 40 | The API uses standard HTTP status codes to indicate the success or failure |
41 | of the API call. The body of the response will be JSON in the following | 41 | of the API call. |
42 | formats. | ||
43 | 42 | ||
44 | ``` | 43 | ``` |
44 | HTTP 1.1 404 Not Found | ||
45 | Content-Type: application/json | ||
46 | |||
45 | { | 47 | { |
46 | "error": "Account not found" // error debug message | 48 | "errorCode": 1 |
49 | "error": "Account not found" | ||
47 | } | 50 | } |
48 | ``` | 51 | ``` |
49 | 52 | ||
50 | Some errors benefit from a more detailed message: | 53 | We provide error codes for [a growing number of cases](https://github.com/Chocobozzz/PeerTube/blob/develop/shared/models/server/server-error-code.enum.ts), |
54 | but it is still optional. | ||
55 | |||
56 | ### Validation errors | ||
57 | |||
58 | Each parameter is evaluated on its own against a set of rules before the route validator | ||
59 | proceeds with potential testing involving parameter combinations. Errors coming from Validation | ||
60 | errors appear earlier and benefit from a more detailed error type: | ||
61 | |||
51 | ``` | 62 | ``` |
63 | HTTP 1.1 400 Bad Request | ||
64 | Content-Type: application/json | ||
65 | |||
52 | { | 66 | { |
53 | "errors": { | 67 | "errors": { |
54 | "id": { // where 'id' is the name of the parameter concerned by the error. | 68 | "id": { // |
55 | "value": "a117eb-c6a9-4756-bb09-2a956239f", // value that triggered the error. | 69 | "value": "a117eb-c6a9-4756-bb09-2a956239f", |
56 | "msg": "Should have an valid id", // error debug message | 70 | "msg": "Should have a valid id", |
57 | "param": "id", | 71 | "param": "id", |
58 | "location": "params" // 'params', 'body', 'header', 'query' or 'cookies' | 72 | "location": "params" |
59 | } | 73 | } |
60 | } | 74 | } |
61 | } | 75 | } |
62 | ``` | 76 | ``` |
63 | 77 | ||
78 | Where `id` is the name of the field concerned by the error, within the route definition. | ||
79 | `errors.<field>.location` can be either 'params', 'body', 'header', 'query' or 'cookies', and | ||
80 | `errors.<field>.value` reports the value that didn't pass validation whose `errors.<field>.msg` | ||
81 | is about. | ||
82 | |||
64 | # Rate limits | 83 | # Rate limits |
65 | 84 | ||
66 | We are rate-limiting all endpoints of PeerTube's API. Custom values can be set by administrators: | 85 | We are rate-limiting all endpoints of PeerTube's API. Custom values can be set by administrators: |
67 | 86 | ||
68 | | Endpoint | Calls | Time frame | | 87 | | Endpoint (prefix: `/api/v1`) | Calls | Time frame | |
69 | |-------------------------|------------------|---------------------------| | 88 | |------------------------------|---------------|--------------| |
70 | | `/*` | 50 | 10 seconds | | 89 | | `/*` | 50 | 10 seconds | |
71 | | `POST /users/token` | 15 | 5 minutes | | 90 | | `POST /users/token` | 15 | 5 minutes | |
72 | | `POST /users/register` | 2¹ | 5 minutes | | 91 | | `POST /users/register` | 2<sup>*</sup> | 5 minutes | |
73 | | `POST /users/ask-send-verify-email` | 3 | 5 minutes | | 92 | | `POST /users/ask-send-verify-email` | 3 | 5 minutes | |
74 | 93 | ||
75 | Depending on the endpoint, ¹failed requests are not taken into account. A service | 94 | Depending on the endpoint, <sup>*</sup>failed requests are not taken into account. A service |
76 | limit is announced by a `429 Too Many Requests` status code. | 95 | limit is announced by a `429 Too Many Requests` status code. |
77 | 96 | ||
78 | You can get details about the current state of your rate limit by reading the | 97 | You can get details about the current state of your rate limit by reading the |
@@ -80,13 +99,37 @@ info: | |||
80 | 99 | ||
81 | | Header | Description | | 100 | | Header | Description | |
82 | |-------------------------|------------------------------------------------------------| | 101 | |-------------------------|------------------------------------------------------------| |
83 | | X-RateLimit-Limit | Number of max requests allowed in the current time period | | 102 | | `X-RateLimit-Limit` | Number of max requests allowed in the current time period | |
84 | | X-RateLimit-Remaining | Number of remaining requests in the current time period | | 103 | | `X-RateLimit-Remaining` | Number of remaining requests in the current time period | |
85 | | X-RateLimit-Reset | Timestamp of end of current time period as UNIX timestamp | | 104 | | `X-RateLimit-Reset` | Timestamp of end of current time period as UNIX timestamp | |
86 | | Retry-After | Seconds to delay after the first `429` is received | | 105 | | `Retry-After` | Seconds to delay after the first `429` is received | |
106 | |||
107 | # CORS | ||
108 | |||
109 | This API features [Cross-Origin Resource Sharing (CORS)](https://fetch.spec.whatwg.org/), | ||
110 | allowing cross-domain communication from the browser for some routes: | ||
111 | |||
112 | | Endpoint | | ||
113 | |------------------------- ---| | ||
114 | | `/api/*` | | ||
115 | | `/download/*` | | ||
116 | | `/lazy-static/*` | | ||
117 | | `/live/segments-sha256/*` | | ||
118 | | `/.well-known/webfinger` | | ||
119 | |||
120 | In addition, all routes serving ActivityPub are CORS-enabled for all origins. | ||
87 | externalDocs: | 121 | externalDocs: |
88 | url: https://docs.joinpeertube.org/api-rest-reference.html | 122 | url: https://docs.joinpeertube.org/api-rest-reference.html |
89 | tags: | 123 | tags: |
124 | - name: Register | ||
125 | description: | | ||
126 | As a visitor, you can use this API to open an account (if registrations are open on | ||
127 | that PeerTube instance). As an admin, you should use the dedicated [User creation | ||
128 | API](#operation/createUser) instead. | ||
129 | - name: Session | ||
130 | x-displayName: Login/Logout | ||
131 | description: | | ||
132 | Sessions deal with access tokens over time. Only __one session token can currently be used at a time__. | ||
90 | - name: Accounts | 133 | - name: Accounts |
91 | description: > | 134 | description: > |
92 | Accounts encompass remote accounts discovered across the federation, | 135 | Accounts encompass remote accounts discovered across the federation, |
@@ -210,6 +253,10 @@ tags: | |||
210 | 253 | ||
211 | For importing videos as your own, refer to [video imports](#operation/importVideo). | 254 | For importing videos as your own, refer to [video imports](#operation/importVideo). |
212 | x-tagGroups: | 255 | x-tagGroups: |
256 | - name: Auth | ||
257 | tags: | ||
258 | - Register | ||
259 | - Session | ||
213 | - name: Accounts | 260 | - name: Accounts |
214 | tags: | 261 | tags: |
215 | - Accounts | 262 | - Accounts |
@@ -458,66 +505,105 @@ paths: | |||
458 | maxItems: 100 | 505 | maxItems: 100 |
459 | items: | 506 | items: |
460 | $ref: '#/components/schemas/Job' | 507 | $ref: '#/components/schemas/Job' |
461 | '/server/following/{host}': | 508 | |
509 | /server/followers: | ||
510 | get: | ||
511 | tags: | ||
512 | - Instance Follows | ||
513 | summary: List instances following the server | ||
514 | parameters: | ||
515 | - $ref: '#/components/parameters/followState' | ||
516 | - $ref: '#/components/parameters/actorType' | ||
517 | - $ref: '#/components/parameters/start' | ||
518 | - $ref: '#/components/parameters/count' | ||
519 | - $ref: '#/components/parameters/sort' | ||
520 | responses: | ||
521 | '200': | ||
522 | description: successful operation | ||
523 | content: | ||
524 | application/json: | ||
525 | schema: | ||
526 | type: object | ||
527 | properties: | ||
528 | total: | ||
529 | type: integer | ||
530 | example: 1 | ||
531 | data: | ||
532 | type: array | ||
533 | items: | ||
534 | $ref: '#/components/schemas/Follow' | ||
535 | '/server/followers/{nameWithHost}': | ||
462 | delete: | 536 | delete: |
537 | summary: Remove or reject a follower to your server | ||
463 | security: | 538 | security: |
464 | - OAuth2: | 539 | - OAuth2: |
465 | - admin | 540 | - admin |
466 | tags: | 541 | tags: |
467 | - Instance Follows | 542 | - Instance Follows |
468 | summary: Unfollow a server | ||
469 | parameters: | 543 | parameters: |
470 | - name: host | 544 | - name: nameWithHost |
471 | in: path | 545 | in: path |
472 | required: true | 546 | required: true |
473 | description: 'The host to unfollow ' | 547 | description: The remote actor handle to remove from your followers |
474 | schema: | 548 | schema: |
475 | type: string | 549 | type: string |
476 | format: hostname | 550 | format: email |
477 | responses: | 551 | responses: |
478 | '201': | 552 | '204': |
479 | description: successful operation | 553 | description: successful operation |
480 | /server/followers: | 554 | '404': |
481 | get: | 555 | description: follower not found |
556 | '/server/followers/{nameWithHost}/reject': | ||
557 | post: | ||
558 | summary: Reject a pending follower to your server | ||
559 | security: | ||
560 | - OAuth2: | ||
561 | - admin | ||
482 | tags: | 562 | tags: |
483 | - Instance Follows | 563 | - Instance Follows |
484 | summary: List instance followers | ||
485 | parameters: | 564 | parameters: |
486 | - $ref: '#/components/parameters/start' | 565 | - name: nameWithHost |
487 | - $ref: '#/components/parameters/count' | 566 | in: path |
488 | - $ref: '#/components/parameters/sort' | 567 | required: true |
568 | description: The remote actor handle to remove from your followers | ||
569 | schema: | ||
570 | type: string | ||
571 | format: email | ||
489 | responses: | 572 | responses: |
490 | '200': | 573 | '204': |
491 | description: successful operation | 574 | description: successful operation |
492 | content: | 575 | '404': |
493 | application/json: | 576 | description: follower not found |
494 | schema: | 577 | '/server/followers/{nameWithHost}/accept': |
495 | type: array | 578 | post: |
496 | items: | 579 | summary: Accept a pending follower to your server |
497 | $ref: '#/components/schemas/Follow' | 580 | security: |
581 | - OAuth2: | ||
582 | - admin | ||
583 | tags: | ||
584 | - Instance Follows | ||
585 | parameters: | ||
586 | - name: nameWithHost | ||
587 | in: path | ||
588 | required: true | ||
589 | description: The remote actor handle to remove from your followers | ||
590 | schema: | ||
591 | type: string | ||
592 | format: email | ||
593 | responses: | ||
594 | '204': | ||
595 | description: successful operation | ||
596 | '404': | ||
597 | description: follower not found | ||
598 | |||
498 | /server/following: | 599 | /server/following: |
499 | get: | 600 | get: |
500 | tags: | 601 | tags: |
501 | - Instance Follows | 602 | - Instance Follows |
502 | summary: List instances followed by the server | 603 | summary: List instances followed by the server |
503 | parameters: | 604 | parameters: |
504 | - name: state | 605 | - $ref: '#/components/parameters/followState' |
505 | in: query | 606 | - $ref: '#/components/parameters/actorType' |
506 | schema: | ||
507 | type: string | ||
508 | enum: | ||
509 | - pending | ||
510 | - accepted | ||
511 | - name: actorType | ||
512 | in: query | ||
513 | schema: | ||
514 | type: string | ||
515 | enum: | ||
516 | - Person | ||
517 | - Application | ||
518 | - Group | ||
519 | - Service | ||
520 | - Organization | ||
521 | - $ref: '#/components/parameters/start' | 607 | - $ref: '#/components/parameters/start' |
522 | - $ref: '#/components/parameters/count' | 608 | - $ref: '#/components/parameters/count' |
523 | - $ref: '#/components/parameters/sort' | 609 | - $ref: '#/components/parameters/sort' |
@@ -527,16 +613,22 @@ paths: | |||
527 | content: | 613 | content: |
528 | application/json: | 614 | application/json: |
529 | schema: | 615 | schema: |
530 | type: array | 616 | type: object |
531 | items: | 617 | properties: |
532 | $ref: '#/components/schemas/Follow' | 618 | total: |
619 | type: integer | ||
620 | example: 1 | ||
621 | data: | ||
622 | type: array | ||
623 | items: | ||
624 | $ref: '#/components/schemas/Follow' | ||
533 | post: | 625 | post: |
534 | security: | 626 | security: |
535 | - OAuth2: | 627 | - OAuth2: |
536 | - admin | 628 | - admin |
537 | tags: | 629 | tags: |
538 | - Instance Follows | 630 | - Instance Follows |
539 | summary: Follow a server | 631 | summary: Follow a list of servers |
540 | responses: | 632 | responses: |
541 | '204': | 633 | '204': |
542 | description: successful operation | 634 | description: successful operation |
@@ -554,9 +646,32 @@ paths: | |||
554 | type: string | 646 | type: string |
555 | format: hostname | 647 | format: hostname |
556 | uniqueItems: true | 648 | uniqueItems: true |
649 | '/server/following/{host}': | ||
650 | delete: | ||
651 | summary: Unfollow a server | ||
652 | security: | ||
653 | - OAuth2: | ||
654 | - admin | ||
655 | tags: | ||
656 | - Instance Follows | ||
657 | parameters: | ||
658 | - name: host | ||
659 | in: path | ||
660 | required: true | ||
661 | description: The host to unfollow | ||
662 | schema: | ||
663 | type: string | ||
664 | format: hostname | ||
665 | responses: | ||
666 | '204': | ||
667 | description: successful operation | ||
668 | '404': | ||
669 | description: host not found | ||
670 | |||
557 | /users: | 671 | /users: |
558 | post: | 672 | post: |
559 | summary: Create a user | 673 | summary: Create a user |
674 | operationId: createUser | ||
560 | security: | 675 | security: |
561 | - OAuth2: | 676 | - OAuth2: |
562 | - admin | 677 | - admin |
@@ -673,11 +788,92 @@ paths: | |||
673 | schema: | 788 | schema: |
674 | $ref: '#/components/schemas/UpdateUser' | 789 | $ref: '#/components/schemas/UpdateUser' |
675 | required: true | 790 | required: true |
791 | |||
792 | /oauth-clients/local: | ||
793 | get: | ||
794 | summary: Login prerequisite | ||
795 | description: You need to retrieve a client id and secret before [logging in](#operation/getOauthToken). | ||
796 | operationId: getOAuthClient | ||
797 | tags: | ||
798 | - Session | ||
799 | responses: | ||
800 | '200': | ||
801 | description: successful operation | ||
802 | content: | ||
803 | application/json: | ||
804 | schema: | ||
805 | $ref: '#/components/schemas/OAuthClient' | ||
806 | links: | ||
807 | UseOAuthClientToLogin: | ||
808 | operationId: getOAuthToken | ||
809 | parameters: | ||
810 | client_id: '$response.body#/client_id' | ||
811 | client_secret: '$response.body#/client_secret' | ||
812 | /users/token: | ||
813 | post: | ||
814 | summary: Login | ||
815 | operationId: getOAuthToken | ||
816 | description: With your [client id and secret](#operation/getOAuthClient), you can retrieve an access and refresh tokens. | ||
817 | tags: | ||
818 | - Session | ||
819 | requestBody: | ||
820 | content: | ||
821 | application/x-www-form-urlencoded: | ||
822 | schema: | ||
823 | oneOf: | ||
824 | - $ref: '#/components/schemas/OAuthToken-password' | ||
825 | - $ref: '#/components/schemas/OAuthToken-refresh_token' | ||
826 | discriminator: | ||
827 | propertyName: grant_type | ||
828 | mapping: | ||
829 | password: '#/components/schemas/OAuthToken-password' | ||
830 | refresh_token: '#/components/schemas/OAuthToken-refresh_token' | ||
831 | responses: | ||
832 | '200': | ||
833 | description: successful operation | ||
834 | content: | ||
835 | application/json: | ||
836 | schema: | ||
837 | type: object | ||
838 | properties: | ||
839 | token_type: | ||
840 | type: string | ||
841 | example: Bearer | ||
842 | access_token: | ||
843 | type: string | ||
844 | example: 90286a0bdf0f7315d9d3fe8dabf9e1d2be9c97d0 | ||
845 | description: valid for 1 day | ||
846 | refresh_token: | ||
847 | type: string | ||
848 | example: 2e0d675df9fc96d2e4ec8a3ebbbf45eca9137bb7 | ||
849 | description: valid for 2 weeks | ||
850 | expires_in: | ||
851 | type: integer | ||
852 | minimum: 0 | ||
853 | example: 14399 | ||
854 | refresh_token_expires_in: | ||
855 | type: integer | ||
856 | minimum: 0 | ||
857 | example: 1209600 | ||
858 | /users/revoke-token: | ||
859 | post: | ||
860 | summary: Logout | ||
861 | description: Revokes your access token and its associated refresh token, destroying your current session. | ||
862 | operationId: revokeOAuthToken | ||
863 | tags: | ||
864 | - Session | ||
865 | security: | ||
866 | - OAuth2: [] | ||
867 | responses: | ||
868 | '200': | ||
869 | description: successful operation | ||
870 | |||
676 | /users/register: | 871 | /users/register: |
677 | post: | 872 | post: |
678 | summary: Register a user | 873 | summary: Register a user |
679 | tags: | 874 | tags: |
680 | - Users | 875 | - Users |
876 | - Register | ||
681 | responses: | 877 | responses: |
682 | '204': | 878 | '204': |
683 | description: successful operation | 879 | description: successful operation |
@@ -687,6 +883,47 @@ paths: | |||
687 | schema: | 883 | schema: |
688 | $ref: '#/components/schemas/RegisterUser' | 884 | $ref: '#/components/schemas/RegisterUser' |
689 | required: true | 885 | required: true |
886 | /users/{id}/verify-email: | ||
887 | post: | ||
888 | summary: Verify a user | ||
889 | description: | | ||
890 | Following a user registration, the new user will receive an email asking to click a link | ||
891 | containing a secret. | ||
892 | tags: | ||
893 | - Users | ||
894 | - Register | ||
895 | parameters: | ||
896 | - $ref: '#/components/parameters/id' | ||
897 | requestBody: | ||
898 | content: | ||
899 | application/json: | ||
900 | schema: | ||
901 | type: object | ||
902 | properties: | ||
903 | verificationString: | ||
904 | type: string | ||
905 | format: url | ||
906 | isPendingEmail: | ||
907 | type: boolean | ||
908 | required: | ||
909 | - verificationString | ||
910 | responses: | ||
911 | '204': | ||
912 | description: successful operation | ||
913 | '403': | ||
914 | description: invalid verification string | ||
915 | '404': | ||
916 | description: user not found | ||
917 | /users/ask-send-verify-email: | ||
918 | post: | ||
919 | summary: Resend user verification link | ||
920 | tags: | ||
921 | - Users | ||
922 | - Register | ||
923 | responses: | ||
924 | '204': | ||
925 | description: successful operation | ||
926 | |||
690 | /users/me: | 927 | /users/me: |
691 | get: | 928 | get: |
692 | summary: Get my user information | 929 | summary: Get my user information |
@@ -1317,7 +1554,7 @@ paths: | |||
1317 | type: string | 1554 | type: string |
1318 | support: | 1555 | support: |
1319 | description: A text tell the audience how to support the video creator | 1556 | description: A text tell the audience how to support the video creator |
1320 | example: Please support my work on <insert crowdfunding plateform>! <3 | 1557 | example: Please support our work on https://soutenir.framasoft.org/en/ <3 |
1321 | type: string | 1558 | type: string |
1322 | nsfw: | 1559 | nsfw: |
1323 | description: Whether or not this video contains sensitive content | 1560 | description: Whether or not this video contains sensitive content |
@@ -1672,74 +1909,7 @@ paths: | |||
1672 | content: | 1909 | content: |
1673 | multipart/form-data: | 1910 | multipart/form-data: |
1674 | schema: | 1911 | schema: |
1675 | type: object | 1912 | $ref: '#/components/schemas/VideoCreateImport' |
1676 | properties: | ||
1677 | torrentfile: | ||
1678 | description: Torrent File | ||
1679 | type: string | ||
1680 | format: binary | ||
1681 | targetUrl: | ||
1682 | $ref: '#/components/schemas/VideoImport/properties/targetUrl' | ||
1683 | magnetUri: | ||
1684 | $ref: '#/components/schemas/VideoImport/properties/magnetUri' | ||
1685 | channelId: | ||
1686 | description: Channel id that will contain this video | ||
1687 | allOf: | ||
1688 | - $ref: '#/components/schemas/VideoChannel/properties/id' | ||
1689 | thumbnailfile: | ||
1690 | description: Video thumbnail file | ||
1691 | type: string | ||
1692 | format: binary | ||
1693 | previewfile: | ||
1694 | description: Video preview file | ||
1695 | type: string | ||
1696 | format: binary | ||
1697 | privacy: | ||
1698 | $ref: '#/components/schemas/VideoPrivacySet' | ||
1699 | category: | ||
1700 | $ref: '#/components/schemas/VideoCategorySet' | ||
1701 | licence: | ||
1702 | $ref: '#/components/schemas/VideoLicenceSet' | ||
1703 | language: | ||
1704 | $ref: '#/components/schemas/VideoLanguageSet' | ||
1705 | description: | ||
1706 | description: Video description | ||
1707 | type: string | ||
1708 | waitTranscoding: | ||
1709 | description: Whether or not we wait transcoding before publish the video | ||
1710 | type: boolean | ||
1711 | support: | ||
1712 | description: A text tell the audience how to support the video creator | ||
1713 | example: Please support my work on <insert crowdfunding plateform>! <3 | ||
1714 | type: string | ||
1715 | nsfw: | ||
1716 | description: Whether or not this video contains sensitive content | ||
1717 | type: boolean | ||
1718 | name: | ||
1719 | description: Video name | ||
1720 | type: string | ||
1721 | minLength: 3 | ||
1722 | maxLength: 120 | ||
1723 | tags: | ||
1724 | description: Video tags (maximum 5 tags each between 2 and 30 characters) | ||
1725 | type: array | ||
1726 | minItems: 1 | ||
1727 | maxItems: 5 | ||
1728 | items: | ||
1729 | type: string | ||
1730 | minLength: 2 | ||
1731 | maxLength: 30 | ||
1732 | commentsEnabled: | ||
1733 | description: Enable or disable comments for this video | ||
1734 | type: boolean | ||
1735 | downloadEnabled: | ||
1736 | description: Enable or disable downloading for this video | ||
1737 | type: boolean | ||
1738 | scheduleUpdate: | ||
1739 | $ref: '#/components/schemas/VideoScheduledUpdate' | ||
1740 | required: | ||
1741 | - channelId | ||
1742 | - name | ||
1743 | encoding: | 1913 | encoding: |
1744 | torrentfile: | 1914 | torrentfile: |
1745 | contentType: application/x-bittorrent | 1915 | contentType: application/x-bittorrent |
@@ -1814,7 +1984,7 @@ paths: | |||
1814 | type: string | 1984 | type: string |
1815 | support: | 1985 | support: |
1816 | description: A text tell the audience how to support the creator | 1986 | description: A text tell the audience how to support the creator |
1817 | example: Please support my work on <insert crowdfunding plateform>! <3 | 1987 | example: Please support our work on https://soutenir.framasoft.org/en/ <3 |
1818 | type: string | 1988 | type: string |
1819 | nsfw: | 1989 | nsfw: |
1820 | description: Whether or not this live video/replay contains sensitive content | 1990 | description: Whether or not this live video/replay contains sensitive content |
@@ -2042,10 +2212,12 @@ paths: | |||
2042 | - $ref: '#/components/schemas/Video/properties/id' | 2212 | - $ref: '#/components/schemas/Video/properties/id' |
2043 | startAt: | 2213 | startAt: |
2044 | type: integer | 2214 | type: integer |
2215 | format: seconds | ||
2045 | description: Timestamp in the video that marks the beginning of the report | 2216 | description: Timestamp in the video that marks the beginning of the report |
2046 | minimum: 0 | 2217 | minimum: 0 |
2047 | endAt: | 2218 | endAt: |
2048 | type: integer | 2219 | type: integer |
2220 | format: seconds | ||
2049 | description: Timestamp in the video that marks the ending of the report | 2221 | description: Timestamp in the video that marks the ending of the report |
2050 | minimum: 0 | 2222 | minimum: 0 |
2051 | comment: | 2223 | comment: |
@@ -2064,8 +2236,18 @@ paths: | |||
2064 | required: | 2236 | required: |
2065 | - reason | 2237 | - reason |
2066 | responses: | 2238 | responses: |
2067 | '204': | 2239 | '200': |
2068 | description: successful operation | 2240 | description: successful operation |
2241 | content: | ||
2242 | application/json: | ||
2243 | schema: | ||
2244 | type: object | ||
2245 | properties: | ||
2246 | abuse: | ||
2247 | type: object | ||
2248 | properties: | ||
2249 | id: | ||
2250 | $ref: '#/components/schemas/id' | ||
2069 | '400': | 2251 | '400': |
2070 | description: incorrect request parameters | 2252 | description: incorrect request parameters |
2071 | '/abuses/{abuseId}': | 2253 | '/abuses/{abuseId}': |
@@ -2127,9 +2309,15 @@ paths: | |||
2127 | content: | 2309 | content: |
2128 | application/json: | 2310 | application/json: |
2129 | schema: | 2311 | schema: |
2130 | type: array | 2312 | type: object |
2131 | items: | 2313 | properties: |
2132 | $ref: '#/components/schemas/AbuseMessage' | 2314 | total: |
2315 | type: integer | ||
2316 | example: 1 | ||
2317 | data: | ||
2318 | type: array | ||
2319 | items: | ||
2320 | $ref: '#/components/schemas/AbuseMessage' | ||
2133 | 2321 | ||
2134 | post: | 2322 | post: |
2135 | summary: Add message to an abuse | 2323 | summary: Add message to an abuse |
@@ -2316,6 +2504,7 @@ paths: | |||
2316 | /video-channels: | 2504 | /video-channels: |
2317 | get: | 2505 | get: |
2318 | summary: List video channels | 2506 | summary: List video channels |
2507 | operationId: getVideoChannels | ||
2319 | tags: | 2508 | tags: |
2320 | - Video Channels | 2509 | - Video Channels |
2321 | parameters: | 2510 | parameters: |
@@ -2331,6 +2520,7 @@ paths: | |||
2331 | $ref: '#/components/schemas/VideoChannelList' | 2520 | $ref: '#/components/schemas/VideoChannelList' |
2332 | post: | 2521 | post: |
2333 | summary: Create a video channel | 2522 | summary: Create a video channel |
2523 | operationId: createVideoChannel | ||
2334 | security: | 2524 | security: |
2335 | - OAuth2: [] | 2525 | - OAuth2: [] |
2336 | tags: | 2526 | tags: |
@@ -2338,6 +2528,16 @@ paths: | |||
2338 | responses: | 2528 | responses: |
2339 | '204': | 2529 | '204': |
2340 | description: successful operation | 2530 | description: successful operation |
2531 | content: | ||
2532 | application/json: | ||
2533 | schema: | ||
2534 | type: object | ||
2535 | properties: | ||
2536 | videoChannel: | ||
2537 | type: object | ||
2538 | properties: | ||
2539 | id: | ||
2540 | $ref: '#/components/schemas/VideoChannel/properties/id' | ||
2341 | requestBody: | 2541 | requestBody: |
2342 | content: | 2542 | content: |
2343 | application/json: | 2543 | application/json: |
@@ -2346,6 +2546,7 @@ paths: | |||
2346 | '/video-channels/{channelHandle}': | 2546 | '/video-channels/{channelHandle}': |
2347 | get: | 2547 | get: |
2348 | summary: Get a video channel | 2548 | summary: Get a video channel |
2549 | operationId: getVideoChannel | ||
2349 | tags: | 2550 | tags: |
2350 | - Video Channels | 2551 | - Video Channels |
2351 | parameters: | 2552 | parameters: |
@@ -2617,13 +2818,13 @@ paths: | |||
2617 | thumbnailfile: | 2818 | thumbnailfile: |
2618 | contentType: image/jpeg | 2819 | contentType: image/jpeg |
2619 | 2820 | ||
2620 | /video-playlists/{id}: | 2821 | /video-playlists/{playlistId}: |
2621 | get: | 2822 | get: |
2622 | summary: Get a video playlist | 2823 | summary: Get a video playlist |
2623 | tags: | 2824 | tags: |
2624 | - Video Playlists | 2825 | - Video Playlists |
2625 | parameters: | 2826 | parameters: |
2626 | - $ref: '#/components/parameters/idOrUUID' | 2827 | - $ref: '#/components/parameters/playlistId' |
2627 | responses: | 2828 | responses: |
2628 | '200': | 2829 | '200': |
2629 | description: successful operation | 2830 | description: successful operation |
@@ -2642,7 +2843,7 @@ paths: | |||
2642 | '204': | 2843 | '204': |
2643 | description: successful operation | 2844 | description: successful operation |
2644 | parameters: | 2845 | parameters: |
2645 | - $ref: '#/components/parameters/idOrUUID' | 2846 | - $ref: '#/components/parameters/playlistId' |
2646 | requestBody: | 2847 | requestBody: |
2647 | content: | 2848 | content: |
2648 | multipart/form-data: | 2849 | multipart/form-data: |
@@ -2677,19 +2878,19 @@ paths: | |||
2677 | tags: | 2878 | tags: |
2678 | - Video Playlists | 2879 | - Video Playlists |
2679 | parameters: | 2880 | parameters: |
2680 | - $ref: '#/components/parameters/idOrUUID' | 2881 | - $ref: '#/components/parameters/playlistId' |
2681 | responses: | 2882 | responses: |
2682 | '204': | 2883 | '204': |
2683 | description: successful operation | 2884 | description: successful operation |
2684 | 2885 | ||
2685 | /video-playlists/{id}/videos: | 2886 | /video-playlists/{playlistId}/videos: |
2686 | get: | 2887 | get: |
2687 | summary: 'List videos of a playlist' | 2888 | summary: 'List videos of a playlist' |
2688 | tags: | 2889 | tags: |
2689 | - Videos | 2890 | - Videos |
2690 | - Video Playlists | 2891 | - Video Playlists |
2691 | parameters: | 2892 | parameters: |
2692 | - $ref: '#/components/parameters/idOrUUID' | 2893 | - $ref: '#/components/parameters/playlistId' |
2693 | responses: | 2894 | responses: |
2694 | '200': | 2895 | '200': |
2695 | description: successful operation | 2896 | description: successful operation |
@@ -2698,14 +2899,14 @@ paths: | |||
2698 | schema: | 2899 | schema: |
2699 | $ref: '#/components/schemas/VideoListResponse' | 2900 | $ref: '#/components/schemas/VideoListResponse' |
2700 | post: | 2901 | post: |
2701 | summary: 'Add a video in a playlist' | 2902 | summary: Add a video in a playlist |
2702 | security: | 2903 | security: |
2703 | - OAuth2: [] | 2904 | - OAuth2: [] |
2704 | tags: | 2905 | tags: |
2705 | - Videos | 2906 | - Videos |
2706 | - Video Playlists | 2907 | - Video Playlists |
2707 | parameters: | 2908 | parameters: |
2708 | - $ref: '#/components/parameters/idOrUUID' | 2909 | - $ref: '#/components/parameters/playlistId' |
2709 | responses: | 2910 | responses: |
2710 | '200': | 2911 | '200': |
2711 | description: successful operation | 2912 | description: successful operation |
@@ -2719,6 +2920,7 @@ paths: | |||
2719 | properties: | 2920 | properties: |
2720 | id: | 2921 | id: |
2721 | type: integer | 2922 | type: integer |
2923 | example: 2 | ||
2722 | requestBody: | 2924 | requestBody: |
2723 | content: | 2925 | content: |
2724 | application/json: | 2926 | application/json: |
@@ -2726,19 +2928,22 @@ paths: | |||
2726 | type: object | 2928 | type: object |
2727 | properties: | 2929 | properties: |
2728 | videoId: | 2930 | videoId: |
2729 | allOf: | 2931 | oneOf: |
2932 | - $ref: '#/components/schemas/Video/properties/uuid' | ||
2730 | - $ref: '#/components/schemas/Video/properties/id' | 2933 | - $ref: '#/components/schemas/Video/properties/id' |
2731 | description: Video to add in the playlist | 2934 | description: Video to add in the playlist |
2732 | startTimestamp: | 2935 | startTimestamp: |
2733 | type: integer | 2936 | type: integer |
2734 | description: Start the video at this specific timestamp (in seconds) | 2937 | format: seconds |
2938 | description: Start the video at this specific timestamp | ||
2735 | stopTimestamp: | 2939 | stopTimestamp: |
2736 | type: integer | 2940 | type: integer |
2737 | description: Stop the video at this specific timestamp (in seconds) | 2941 | format: seconds |
2942 | description: Stop the video at this specific timestamp | ||
2738 | required: | 2943 | required: |
2739 | - videoId | 2944 | - videoId |
2740 | 2945 | ||
2741 | /video-playlists/{id}/videos/reorder: | 2946 | /video-playlists/{playlistId}/videos/reorder: |
2742 | post: | 2947 | post: |
2743 | summary: 'Reorder a playlist' | 2948 | summary: 'Reorder a playlist' |
2744 | security: | 2949 | security: |
@@ -2746,7 +2951,7 @@ paths: | |||
2746 | tags: | 2951 | tags: |
2747 | - Video Playlists | 2952 | - Video Playlists |
2748 | parameters: | 2953 | parameters: |
2749 | - $ref: '#/components/parameters/idOrUUID' | 2954 | - $ref: '#/components/parameters/playlistId' |
2750 | responses: | 2955 | responses: |
2751 | '204': | 2956 | '204': |
2752 | description: successful operation | 2957 | description: successful operation |
@@ -2772,15 +2977,15 @@ paths: | |||
2772 | - startPosition | 2977 | - startPosition |
2773 | - insertAfterPosition | 2978 | - insertAfterPosition |
2774 | 2979 | ||
2775 | /video-playlists/{id}/videos/{playlistElementId}: | 2980 | /video-playlists/{playlistId}/videos/{playlistElementId}: |
2776 | put: | 2981 | put: |
2777 | summary: 'Update a playlist element' | 2982 | summary: Update a playlist element |
2778 | security: | 2983 | security: |
2779 | - OAuth2: [] | 2984 | - OAuth2: [] |
2780 | tags: | 2985 | tags: |
2781 | - Video Playlists | 2986 | - Video Playlists |
2782 | parameters: | 2987 | parameters: |
2783 | - $ref: '#/components/parameters/idOrUUID' | 2988 | - $ref: '#/components/parameters/playlistId' |
2784 | - $ref: '#/components/parameters/playlistElementId' | 2989 | - $ref: '#/components/parameters/playlistElementId' |
2785 | responses: | 2990 | responses: |
2786 | '204': | 2991 | '204': |
@@ -2793,18 +2998,20 @@ paths: | |||
2793 | properties: | 2998 | properties: |
2794 | startTimestamp: | 2999 | startTimestamp: |
2795 | type: integer | 3000 | type: integer |
2796 | description: 'Start the video at this specific timestamp (in seconds)' | 3001 | format: seconds |
3002 | description: Start the video at this specific timestamp | ||
2797 | stopTimestamp: | 3003 | stopTimestamp: |
2798 | type: integer | 3004 | type: integer |
2799 | description: 'Stop the video at this specific timestamp (in seconds)' | 3005 | format: seconds |
3006 | description: Stop the video at this specific timestamp | ||
2800 | delete: | 3007 | delete: |
2801 | summary: 'Delete an element from a playlist' | 3008 | summary: Delete an element from a playlist |
2802 | security: | 3009 | security: |
2803 | - OAuth2: [] | 3010 | - OAuth2: [] |
2804 | tags: | 3011 | tags: |
2805 | - Video Playlists | 3012 | - Video Playlists |
2806 | parameters: | 3013 | parameters: |
2807 | - $ref: '#/components/parameters/idOrUUID' | 3014 | - $ref: '#/components/parameters/playlistId' |
2808 | - $ref: '#/components/parameters/playlistElementId' | 3015 | - $ref: '#/components/parameters/playlistElementId' |
2809 | responses: | 3016 | responses: |
2810 | '204': | 3017 | '204': |
@@ -2812,7 +3019,7 @@ paths: | |||
2812 | 3019 | ||
2813 | '/users/me/video-playlists/videos-exist': | 3020 | '/users/me/video-playlists/videos-exist': |
2814 | get: | 3021 | get: |
2815 | summary: 'Check video exists in my playlists' | 3022 | summary: Check video exists in my playlists |
2816 | security: | 3023 | security: |
2817 | - OAuth2: [] | 3024 | - OAuth2: [] |
2818 | tags: | 3025 | tags: |
@@ -2845,8 +3052,10 @@ paths: | |||
2845 | type: integer | 3052 | type: integer |
2846 | startTimestamp: | 3053 | startTimestamp: |
2847 | type: integer | 3054 | type: integer |
3055 | format: seconds | ||
2848 | stopTimestamp: | 3056 | stopTimestamp: |
2849 | type: integer | 3057 | type: integer |
3058 | format: seconds | ||
2850 | 3059 | ||
2851 | '/accounts/{name}/video-channels': | 3060 | '/accounts/{name}/video-channels': |
2852 | get: | 3061 | get: |
@@ -2942,8 +3151,10 @@ paths: | |||
2942 | type: object | 3151 | type: object |
2943 | properties: | 3152 | properties: |
2944 | text: | 3153 | text: |
2945 | type: string | 3154 | allOf: |
2946 | description: 'Text comment' | 3155 | - $ref: '#/components/schemas/VideoComment/properties/text' |
3156 | format: markdown | ||
3157 | maxLength: 10000 | ||
2947 | required: | 3158 | required: |
2948 | - text | 3159 | - text |
2949 | 3160 | ||
@@ -2988,7 +3199,10 @@ paths: | |||
2988 | type: object | 3199 | type: object |
2989 | properties: | 3200 | properties: |
2990 | text: | 3201 | text: |
2991 | $ref: '#/components/schemas/VideoComment/properties/text' | 3202 | allOf: |
3203 | - $ref: '#/components/schemas/VideoComment/properties/text' | ||
3204 | format: markdown | ||
3205 | maxLength: 10000 | ||
2992 | required: | 3206 | required: |
2993 | - text | 3207 | - text |
2994 | 3208 | ||
@@ -3019,6 +3233,19 @@ paths: | |||
3019 | - Video Rates | 3233 | - Video Rates |
3020 | parameters: | 3234 | parameters: |
3021 | - $ref: '#/components/parameters/idOrUUID' | 3235 | - $ref: '#/components/parameters/idOrUUID' |
3236 | requestBody: | ||
3237 | content: | ||
3238 | application/json: | ||
3239 | schema: | ||
3240 | type: object | ||
3241 | properties: | ||
3242 | rating: | ||
3243 | type: string | ||
3244 | enum: | ||
3245 | - like | ||
3246 | - dislike | ||
3247 | required: | ||
3248 | - rating | ||
3022 | responses: | 3249 | responses: |
3023 | '204': | 3250 | '204': |
3024 | description: successful operation | 3251 | description: successful operation |
@@ -3130,7 +3357,8 @@ paths: | |||
3130 | $ref: '#/components/schemas/VideoChannelList' | 3357 | $ref: '#/components/schemas/VideoChannelList' |
3131 | '500': | 3358 | '500': |
3132 | description: search index unavailable | 3359 | description: search index unavailable |
3133 | /blocklist/accounts: | 3360 | |
3361 | /server/blocklist/accounts: | ||
3134 | get: | 3362 | get: |
3135 | tags: | 3363 | tags: |
3136 | - Account Blocks | 3364 | - Account Blocks |
@@ -3169,7 +3397,7 @@ paths: | |||
3169 | description: successful operation | 3397 | description: successful operation |
3170 | '409': | 3398 | '409': |
3171 | description: self-blocking forbidden | 3399 | description: self-blocking forbidden |
3172 | '/blocklist/accounts/{accountName}': | 3400 | '/server/blocklist/accounts/{accountName}': |
3173 | delete: | 3401 | delete: |
3174 | tags: | 3402 | tags: |
3175 | - Account Blocks | 3403 | - Account Blocks |
@@ -3189,7 +3417,8 @@ paths: | |||
3189 | description: successful operation | 3417 | description: successful operation |
3190 | '404': | 3418 | '404': |
3191 | description: account or account block does not exist | 3419 | description: account or account block does not exist |
3192 | /blocklist/servers: | 3420 | |
3421 | /server/blocklist/servers: | ||
3193 | get: | 3422 | get: |
3194 | tags: | 3423 | tags: |
3195 | - Server Blocks | 3424 | - Server Blocks |
@@ -3224,11 +3453,11 @@ paths: | |||
3224 | required: | 3453 | required: |
3225 | - host | 3454 | - host |
3226 | responses: | 3455 | responses: |
3227 | '200': | 3456 | '204': |
3228 | description: successful operation | 3457 | description: successful operation |
3229 | '409': | 3458 | '409': |
3230 | description: self-blocking forbidden | 3459 | description: self-blocking forbidden |
3231 | '/blocklist/servers/{host}': | 3460 | '/server/blocklist/servers/{host}': |
3232 | delete: | 3461 | delete: |
3233 | tags: | 3462 | tags: |
3234 | - Server Blocks | 3463 | - Server Blocks |
@@ -3245,11 +3474,12 @@ paths: | |||
3245 | type: string | 3474 | type: string |
3246 | format: hostname | 3475 | format: hostname |
3247 | responses: | 3476 | responses: |
3248 | '201': | 3477 | '204': |
3249 | description: successful operation | 3478 | description: successful operation |
3250 | '404': | 3479 | '404': |
3251 | description: account block does not exist | 3480 | description: account block does not exist |
3252 | /redundancy/{host}: | 3481 | |
3482 | /server/redundancy/{host}: | ||
3253 | put: | 3483 | put: |
3254 | tags: | 3484 | tags: |
3255 | - Instance Redundancy | 3485 | - Instance Redundancy |
@@ -3281,7 +3511,7 @@ paths: | |||
3281 | description: successful operation | 3511 | description: successful operation |
3282 | '404': | 3512 | '404': |
3283 | description: server is not already known | 3513 | description: server is not already known |
3284 | /redundancy/videos: | 3514 | /server/redundancy/videos: |
3285 | get: | 3515 | get: |
3286 | tags: | 3516 | tags: |
3287 | - Video Mirroring | 3517 | - Video Mirroring |
@@ -3337,7 +3567,7 @@ paths: | |||
3337 | description: video does not exist | 3567 | description: video does not exist |
3338 | '409': | 3568 | '409': |
3339 | description: video is already mirrored | 3569 | description: video is already mirrored |
3340 | /redundancy/videos/{redundancyId}: | 3570 | /server/redundancy/videos/{redundancyId}: |
3341 | delete: | 3571 | delete: |
3342 | tags: | 3572 | tags: |
3343 | - Video Mirroring | 3573 | - Video Mirroring |
@@ -3357,6 +3587,7 @@ paths: | |||
3357 | description: successful operation | 3587 | description: successful operation |
3358 | '404': | 3588 | '404': |
3359 | description: video redundancy not found | 3589 | description: video redundancy not found |
3590 | |||
3360 | '/feeds/video-comments.{format}': | 3591 | '/feeds/video-comments.{format}': |
3361 | get: | 3592 | get: |
3362 | tags: | 3593 | tags: |
@@ -4019,6 +4250,13 @@ components: | |||
4019 | oneOf: | 4250 | oneOf: |
4020 | - $ref: '#/components/schemas/id' | 4251 | - $ref: '#/components/schemas/id' |
4021 | - $ref: '#/components/schemas/UUIDv4' | 4252 | - $ref: '#/components/schemas/UUIDv4' |
4253 | playlistId: | ||
4254 | name: playlistId | ||
4255 | in: path | ||
4256 | required: true | ||
4257 | description: Playlist id | ||
4258 | schema: | ||
4259 | $ref: '#/components/schemas/VideoPlaylist/properties/id' | ||
4022 | playlistElementId: | 4260 | playlistElementId: |
4023 | name: playlistElementId | 4261 | name: playlistElementId |
4024 | in: path | 4262 | in: path |
@@ -4223,22 +4461,42 @@ components: | |||
4223 | - activitypub-refresher | 4461 | - activitypub-refresher |
4224 | - video-redundancy | 4462 | - video-redundancy |
4225 | - video-live-ending | 4463 | - video-live-ending |
4464 | followState: | ||
4465 | name: state | ||
4466 | in: query | ||
4467 | schema: | ||
4468 | type: string | ||
4469 | enum: | ||
4470 | - pending | ||
4471 | - accepted | ||
4472 | actorType: | ||
4473 | name: actorType | ||
4474 | in: query | ||
4475 | schema: | ||
4476 | type: string | ||
4477 | enum: | ||
4478 | - Person | ||
4479 | - Application | ||
4480 | - Group | ||
4481 | - Service | ||
4482 | - Organization | ||
4226 | securitySchemes: | 4483 | securitySchemes: |
4227 | OAuth2: | 4484 | OAuth2: |
4228 | description: | | 4485 | description: | |
4229 | Authenticating via OAuth requires the following steps: | 4486 | Authenticating via OAuth requires the following steps: |
4230 | - Have an activated account | 4487 | - Have an activated account |
4231 | - [Generate](https://docs.joinpeertube.org/api-rest-getting-started) a | 4488 | - [Generate] an access token for that account at `/api/v1/users/token`. |
4232 | Bearer Token for that account at `/api/v1/users/token` | 4489 | - Make requests with the *Authorization: Bearer <token\>* header |
4233 | - Make authenticated requests, putting *Authorization: Bearer <token\>* | ||
4234 | - Profit, depending on the role assigned to the account | 4490 | - Profit, depending on the role assigned to the account |
4235 | 4491 | ||
4236 | Note that the __access token is valid for 1 day__ and, and is given | 4492 | Note that the __access token is valid for 1 day__ and is given |
4237 | along with a __refresh token valid for 2 weeks__. | 4493 | along with a __refresh token valid for 2 weeks__. |
4494 | |||
4495 | [Generate]: https://docs.joinpeertube.org/api-rest-getting-started | ||
4238 | type: oauth2 | 4496 | type: oauth2 |
4239 | flows: | 4497 | flows: |
4240 | password: | 4498 | password: |
4241 | tokenUrl: 'https://peertube.example.com/api/v1/users/token' | 4499 | tokenUrl: /api/v1/users/token |
4242 | scopes: | 4500 | scopes: |
4243 | admin: Admin scope | 4501 | admin: Admin scope |
4244 | moderator: Moderator scope | 4502 | moderator: Moderator scope |
@@ -4258,20 +4516,21 @@ components: | |||
4258 | maxLength: 36 | 4516 | maxLength: 36 |
4259 | username: | 4517 | username: |
4260 | type: string | 4518 | type: string |
4261 | description: The username of the user | 4519 | description: immutable name of the user, used to find or mention its actor |
4262 | example: chocobozzz | 4520 | example: chocobozzz |
4263 | pattern: '/^[a-z0-9._]{1,50}$/' | 4521 | pattern: '/^[a-z0-9._]+$/' |
4264 | minLength: 1 | 4522 | minLength: 1 |
4265 | maxLength: 50 | 4523 | maxLength: 50 |
4266 | usernameChannel: | 4524 | usernameChannel: |
4267 | type: string | 4525 | type: string |
4268 | description: The username for the default channel | 4526 | description: immutable name of the channel, used to interact with its actor |
4269 | example: The Capybara Channel | 4527 | example: framasoft_videos |
4270 | pattern: '/^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\-_.:]+$/' | 4528 | pattern: '/^[a-zA-Z0-9\\-_.:]+$/' |
4529 | minLength: 1 | ||
4530 | maxLength: 50 | ||
4271 | password: | 4531 | password: |
4272 | type: string | 4532 | type: string |
4273 | format: password | 4533 | format: password |
4274 | description: The password of the user | ||
4275 | minLength: 6 | 4534 | minLength: 6 |
4276 | maxLength: 255 | 4535 | maxLength: 255 |
4277 | 4536 | ||
@@ -4483,8 +4742,10 @@ components: | |||
4483 | type: integer | 4742 | type: integer |
4484 | startTimestamp: | 4743 | startTimestamp: |
4485 | type: integer | 4744 | type: integer |
4745 | format: seconds | ||
4486 | stopTimestamp: | 4746 | stopTimestamp: |
4487 | type: integer | 4747 | type: integer |
4748 | format: seconds | ||
4488 | video: | 4749 | video: |
4489 | nullable: true | 4750 | nullable: true |
4490 | allOf: | 4751 | allOf: |
@@ -4633,6 +4894,7 @@ components: | |||
4633 | duration: | 4894 | duration: |
4634 | type: integer | 4895 | type: integer |
4635 | example: 1419 | 4896 | example: 1419 |
4897 | format: seconds | ||
4636 | description: duration of the video in seconds | 4898 | description: duration of the video in seconds |
4637 | isLocal: | 4899 | isLocal: |
4638 | type: boolean | 4900 | type: boolean |
@@ -4701,7 +4963,7 @@ components: | |||
4701 | support: | 4963 | support: |
4702 | type: string | 4964 | type: string |
4703 | description: A text tell the audience how to support the video creator | 4965 | description: A text tell the audience how to support the video creator |
4704 | example: Please support my work on <insert crowdfunding plateform>! <3 | 4966 | example: Please support our work on https://soutenir.framasoft.org/en/ <3 |
4705 | minLength: 3 | 4967 | minLength: 3 |
4706 | maxLength: 1000 | 4968 | maxLength: 1000 |
4707 | channel: | 4969 | channel: |
@@ -4806,10 +5068,33 @@ components: | |||
4806 | label: | 5068 | label: |
4807 | type: string | 5069 | type: string |
4808 | example: Pending | 5070 | example: Pending |
5071 | VideoCreateImport: | ||
5072 | allOf: | ||
5073 | - type: object | ||
5074 | additionalProperties: false | ||
5075 | oneOf: | ||
5076 | - properties: | ||
5077 | targetUrl: | ||
5078 | $ref: '#/components/schemas/VideoImport/properties/targetUrl' | ||
5079 | required: [targetUrl] | ||
5080 | - properties: | ||
5081 | magnetUri: | ||
5082 | $ref: '#/components/schemas/VideoImport/properties/magnetUri' | ||
5083 | required: [magnetUri] | ||
5084 | - properties: | ||
5085 | torrentfile: | ||
5086 | $ref: '#/components/schemas/VideoImport/properties/torrentfile' | ||
5087 | required: [torrentfile] | ||
5088 | - $ref: '#/components/schemas/VideoUploadRequestCommon' | ||
5089 | required: | ||
5090 | - channelId | ||
5091 | - name | ||
4809 | VideoImport: | 5092 | VideoImport: |
4810 | properties: | 5093 | properties: |
4811 | id: | 5094 | id: |
4812 | $ref: '#/components/schemas/id' | 5095 | readOnly: true |
5096 | allOf: | ||
5097 | - $ref: '#/components/schemas/id' | ||
4813 | targetUrl: | 5098 | targetUrl: |
4814 | type: string | 5099 | type: string |
4815 | format: url | 5100 | format: url |
@@ -4821,19 +5106,31 @@ components: | |||
4821 | description: magnet URI allowing to resolve the import's source video | 5106 | description: magnet URI allowing to resolve the import's source video |
4822 | example: magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.torrent&xt=urn:btih:38b4747ff788b30bf61f59d1965cd38f9e48e01f&dn=What+is+PeerTube%3F&tr=wss%3A%2F%2Fframatube.org%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4 | 5107 | example: magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.torrent&xt=urn:btih:38b4747ff788b30bf61f59d1965cd38f9e48e01f&dn=What+is+PeerTube%3F&tr=wss%3A%2F%2Fframatube.org%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F9c9de5e8-0a1e-484a-b099-e80766180a6d-240.mp4 |
4823 | pattern: /magnet:\?xt=urn:[a-z0-9]+:[a-z0-9]{32}/i | 5108 | pattern: /magnet:\?xt=urn:[a-z0-9]+:[a-z0-9]{32}/i |
5109 | torrentfile: | ||
5110 | writeOnly: true | ||
5111 | type: string | ||
5112 | format: binary | ||
5113 | description: Torrent file containing only the video file | ||
4824 | torrentName: | 5114 | torrentName: |
5115 | readOnly: true | ||
4825 | type: string | 5116 | type: string |
4826 | state: | 5117 | state: |
4827 | $ref: '#/components/schemas/VideoImportStateConstant' | 5118 | readOnly: true |
5119 | allOf: | ||
5120 | - $ref: '#/components/schemas/VideoImportStateConstant' | ||
4828 | error: | 5121 | error: |
5122 | readOnly: true | ||
4829 | type: string | 5123 | type: string |
4830 | createdAt: | 5124 | createdAt: |
5125 | readOnly: true | ||
4831 | type: string | 5126 | type: string |
4832 | format: date-time | 5127 | format: date-time |
4833 | updatedAt: | 5128 | updatedAt: |
5129 | readOnly: true | ||
4834 | type: string | 5130 | type: string |
4835 | format: date-time | 5131 | format: date-time |
4836 | video: | 5132 | video: |
5133 | readOnly: true | ||
4837 | nullable: true | 5134 | nullable: true |
4838 | allOf: | 5135 | allOf: |
4839 | - $ref: '#/components/schemas/Video' | 5136 | - $ref: '#/components/schemas/Video' |
@@ -4963,13 +5260,16 @@ components: | |||
4963 | format: url | 5260 | format: url |
4964 | text: | 5261 | text: |
4965 | type: string | 5262 | type: string |
4966 | description: Text of the comment in Markdown | 5263 | format: html |
5264 | description: Text of the comment | ||
4967 | minLength: 1 | 5265 | minLength: 1 |
4968 | maxLength: 10000 | 5266 | example: This video is wonderful! |
4969 | threadId: | 5267 | threadId: |
4970 | type: integer | ||
4971 | inReplyToCommentId: | ||
4972 | $ref: '#/components/schemas/id' | 5268 | $ref: '#/components/schemas/id' |
5269 | inReplyToCommentId: | ||
5270 | nullable: true | ||
5271 | allOf: | ||
5272 | - $ref: '#/components/schemas/id' | ||
4973 | videoId: | 5273 | videoId: |
4974 | $ref: '#/components/schemas/Video/properties/id' | 5274 | $ref: '#/components/schemas/Video/properties/id' |
4975 | createdAt: | 5275 | createdAt: |
@@ -4978,6 +5278,14 @@ components: | |||
4978 | updatedAt: | 5278 | updatedAt: |
4979 | type: string | 5279 | type: string |
4980 | format: date-time | 5280 | format: date-time |
5281 | deletedAt: | ||
5282 | nullable: true | ||
5283 | type: string | ||
5284 | format: date-time | ||
5285 | default: null | ||
5286 | isDeleted: | ||
5287 | type: boolean | ||
5288 | default: false | ||
4981 | totalRepliesFromVideoAuthor: | 5289 | totalRepliesFromVideoAuthor: |
4982 | type: integer | 5290 | type: integer |
4983 | minimum: 0 | 5291 | minimum: 0 |
@@ -5035,7 +5343,7 @@ components: | |||
5035 | type: string | 5343 | type: string |
5036 | format: url | 5344 | format: url |
5037 | name: | 5345 | name: |
5038 | description: immutable name of the actor | 5346 | description: immutable name of the actor, used to find or mention it |
5039 | allOf: | 5347 | allOf: |
5040 | - $ref: '#/components/schemas/username' | 5348 | - $ref: '#/components/schemas/username' |
5041 | host: | 5349 | host: |
@@ -5071,7 +5379,9 @@ components: | |||
5071 | - $ref: '#/components/schemas/User/properties/id' | 5379 | - $ref: '#/components/schemas/User/properties/id' |
5072 | displayName: | 5380 | displayName: |
5073 | type: string | 5381 | type: string |
5074 | description: name displayed on the account's profile | 5382 | description: editable name of the account, displayed in its representations |
5383 | minLength: 3 | ||
5384 | maxLength: 120 | ||
5075 | description: | 5385 | description: |
5076 | type: string | 5386 | type: string |
5077 | description: text or bio displayed on the account's profile | 5387 | description: text or bio displayed on the account's profile |
@@ -5079,6 +5389,7 @@ components: | |||
5079 | properties: | 5389 | properties: |
5080 | currentTime: | 5390 | currentTime: |
5081 | type: integer | 5391 | type: integer |
5392 | format: seconds | ||
5082 | description: timestamp within the video, in seconds | 5393 | description: timestamp within the video, in seconds |
5083 | example: 5 | 5394 | example: 5 |
5084 | ServerConfig: | 5395 | ServerConfig: |
@@ -5593,7 +5904,7 @@ components: | |||
5593 | type: boolean | 5904 | type: boolean |
5594 | support: | 5905 | support: |
5595 | description: A text tell the audience how to support the video creator | 5906 | description: A text tell the audience how to support the video creator |
5596 | example: Please support my work on <insert crowdfunding plateform>! <3 | 5907 | example: Please support our work on https://soutenir.framasoft.org/en/ <3 |
5597 | type: string | 5908 | type: string |
5598 | nsfw: | 5909 | nsfw: |
5599 | description: Whether or not this video contains sensitive content | 5910 | description: Whether or not this video contains sensitive content |
@@ -5822,9 +6133,9 @@ components: | |||
5822 | UpdateUser: | 6133 | UpdateUser: |
5823 | properties: | 6134 | properties: |
5824 | email: | 6135 | email: |
5825 | type: string | ||
5826 | format: email | ||
5827 | description: The updated email of the user | 6136 | description: The updated email of the user |
6137 | allOf: | ||
6138 | - $ref: '#/components/schemas/User/properties/email' | ||
5828 | emailVerified: | 6139 | emailVerified: |
5829 | type: boolean | 6140 | type: boolean |
5830 | description: Set the email as verified | 6141 | description: Set the email as verified |
@@ -5844,28 +6155,54 @@ components: | |||
5844 | adminFlags: | 6155 | adminFlags: |
5845 | $ref: '#/components/schemas/UserAdminFlags' | 6156 | $ref: '#/components/schemas/UserAdminFlags' |
5846 | UpdateMe: | 6157 | UpdateMe: |
6158 | # see shared/models/users/user-update-me.model.ts: | ||
5847 | properties: | 6159 | properties: |
5848 | password: | 6160 | password: |
5849 | $ref: '#/components/schemas/password' | 6161 | $ref: '#/components/schemas/password' |
6162 | currentPassword: | ||
6163 | $ref: '#/components/schemas/password' | ||
5850 | email: | 6164 | email: |
6165 | description: new email used for login and service communications | ||
6166 | allOf: | ||
6167 | - $ref: '#/components/schemas/User/properties/email' | ||
6168 | displayName: | ||
5851 | type: string | 6169 | type: string |
5852 | format: email | 6170 | description: new name of the user in its representations |
5853 | description: Your new email | 6171 | minLength: 3 |
6172 | maxLength: 120 | ||
5854 | displayNSFW: | 6173 | displayNSFW: |
5855 | type: string | 6174 | type: string |
5856 | description: Your new displayNSFW | 6175 | description: new NSFW display policy |
5857 | enum: | 6176 | enum: |
5858 | - 'true' | 6177 | - 'true' |
5859 | - 'false' | 6178 | - 'false' |
5860 | - both | 6179 | - both |
6180 | webTorrentEnabled: | ||
6181 | type: boolean | ||
6182 | description: whether to enable P2P in the player or not | ||
5861 | autoPlayVideo: | 6183 | autoPlayVideo: |
5862 | type: boolean | 6184 | type: boolean |
5863 | description: Your new autoPlayVideo | 6185 | description: new preference regarding playing videos automatically |
5864 | required: | 6186 | autoPlayNextVideo: |
5865 | - password | 6187 | type: boolean |
5866 | 6188 | description: new preference regarding playing following videos automatically | |
5867 | - displayNSFW | 6189 | autoPlayNextVideoPlaylist: |
5868 | - autoPlayVideo | 6190 | type: boolean |
6191 | description: new preference regarding playing following playlist videos automatically | ||
6192 | videosHistoryEnabled: | ||
6193 | type: boolean | ||
6194 | description: whether to keep track of watched history or not | ||
6195 | videoLanguages: | ||
6196 | type: array | ||
6197 | items: | ||
6198 | type: string | ||
6199 | description: list of languages to filter videos down to | ||
6200 | theme: | ||
6201 | type: string | ||
6202 | noInstanceConfigWarningModal: | ||
6203 | type: boolean | ||
6204 | noWelcomeModal: | ||
6205 | type: boolean | ||
5869 | GetMeVideoRating: | 6206 | GetMeVideoRating: |
5870 | properties: | 6207 | properties: |
5871 | id: | 6208 | id: |
@@ -5897,38 +6234,94 @@ components: | |||
5897 | RegisterUser: | 6234 | RegisterUser: |
5898 | properties: | 6235 | properties: |
5899 | username: | 6236 | username: |
5900 | $ref: '#/components/schemas/username' | 6237 | description: immutable name of the user, used to find or mention its actor |
6238 | allOf: | ||
6239 | - $ref: '#/components/schemas/username' | ||
5901 | password: | 6240 | password: |
5902 | $ref: '#/components/schemas/password' | 6241 | $ref: '#/components/schemas/password' |
5903 | email: | 6242 | email: |
5904 | type: string | 6243 | type: string |
5905 | format: email | 6244 | format: email |
5906 | description: The email of the user | 6245 | description: email of the user, used for login or service communications |
5907 | displayName: | 6246 | displayName: |
5908 | type: string | 6247 | type: string |
5909 | description: The user display name | 6248 | description: editable name of the user, displayed in its representations |
5910 | minLength: 1 | 6249 | minLength: 1 |
5911 | maxLength: 120 | 6250 | maxLength: 120 |
5912 | channel: | 6251 | channel: |
5913 | type: object | 6252 | type: object |
6253 | description: channel base information used to create the first channel of the user | ||
5914 | properties: | 6254 | properties: |
5915 | name: | 6255 | name: |
5916 | $ref: '#/components/schemas/usernameChannel' | 6256 | $ref: '#/components/schemas/usernameChannel' |
5917 | displayName: | 6257 | displayName: |
5918 | type: string | 6258 | $ref: '#/components/schemas/VideoChannel/properties/displayName' |
5919 | description: The display name for the default channel | ||
5920 | minLength: 1 | ||
5921 | maxLength: 120 | ||
5922 | required: | 6259 | required: |
5923 | - username | 6260 | - username |
5924 | - password | 6261 | - password |
5925 | 6262 | ||
5926 | 6263 | ||
6264 | OAuthClient: | ||
6265 | properties: | ||
6266 | client_id: | ||
6267 | type: string | ||
6268 | pattern: /^[a-z0-9]$/ | ||
6269 | maxLength: 32 | ||
6270 | minLength: 32 | ||
6271 | example: v1ikx5hnfop4mdpnci8nsqh93c45rldf | ||
6272 | client_secret: | ||
6273 | type: string | ||
6274 | pattern: /^[a-zA-Z0-9]$/ | ||
6275 | maxLength: 32 | ||
6276 | minLength: 32 | ||
6277 | example: AjWiOapPltI6EnsWQwlFarRtLh4u8tDt | ||
6278 | OAuthToken-password: | ||
6279 | allOf: | ||
6280 | - $ref: '#/components/schemas/OAuthClient' | ||
6281 | - type: object | ||
6282 | properties: | ||
6283 | grant_type: | ||
6284 | type: string | ||
6285 | enum: | ||
6286 | - password | ||
6287 | - refresh_token | ||
6288 | default: password | ||
6289 | username: | ||
6290 | $ref: '#/components/schemas/User/properties/username' | ||
6291 | password: | ||
6292 | $ref: '#/components/schemas/password' | ||
6293 | required: | ||
6294 | - client_id | ||
6295 | - client_secret | ||
6296 | - grant_type | ||
6297 | - username | ||
6298 | - password | ||
6299 | OAuthToken-refresh_token: | ||
6300 | allOf: | ||
6301 | - $ref: '#/components/schemas/OAuthClient' | ||
6302 | - type: object | ||
6303 | properties: | ||
6304 | grant_type: | ||
6305 | type: string | ||
6306 | enum: | ||
6307 | - password | ||
6308 | - refresh_token | ||
6309 | default: password | ||
6310 | refresh_token: | ||
6311 | type: string | ||
6312 | example: 2e0d675df9fc96d2e4ec8a3ebbbf45eca9137bb7 | ||
6313 | required: | ||
6314 | - client_id | ||
6315 | - client_secret | ||
6316 | - grant_type | ||
6317 | - refresh_token | ||
6318 | |||
5927 | VideoChannel: | 6319 | VideoChannel: |
5928 | properties: | 6320 | properties: |
5929 | # GET/POST/PUT properties | 6321 | # GET/POST/PUT properties |
5930 | displayName: | 6322 | displayName: |
5931 | type: string | 6323 | type: string |
6324 | description: editable name of the channel, displayed in its representations | ||
5932 | example: Videos of Framasoft | 6325 | example: Videos of Framasoft |
5933 | minLength: 1 | 6326 | minLength: 1 |
5934 | maxLength: 120 | 6327 | maxLength: 120 |
@@ -5940,7 +6333,7 @@ components: | |||
5940 | support: | 6333 | support: |
5941 | type: string | 6334 | type: string |
5942 | description: text shown by default on all videos of this channel, to tell the audience how to support it | 6335 | description: text shown by default on all videos of this channel, to tell the audience how to support it |
5943 | example: Please support my work on <insert crowdfunding plateform>! <3 | 6336 | example: Please support our work on https://soutenir.framasoft.org/en/ <3 |
5944 | minLength: 3 | 6337 | minLength: 3 |
5945 | maxLength: 1000 | 6338 | maxLength: 1000 |
5946 | # GET-only properties | 6339 | # GET-only properties |