From a1eda903a497857017495f37a1fd3593ba7ab23c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 28 May 2021 11:36:33 +0200 Subject: [PATCH] Support '/w/' and '/w/p/' for watch page And use them as default in client --- client/e2e/src/po/my-account.ts | 2 +- client/e2e/src/po/video-upload.po.ts | 2 +- client/e2e/src/po/video-watch.po.ts | 6 +- client/e2e/src/videos.e2e-spec.ts | 2 +- .../edit-basic-configuration.component.html | 2 +- .../my-video-imports.component.ts | 2 +- .../page-not-found.component.ts | 2 - .../remote-interaction.component.ts | 2 +- .../app/+search/video-lazy-load.resolver.ts | 2 +- .../video-go-live.component.ts | 2 +- .../video-upload.component.ts | 2 +- .../+video-edit/video-update.component.html | 2 +- .../+video-edit/video-update.component.ts | 2 +- .../comment/video-comment.component.html | 4 +- .../video-watch-routing.module.ts | 2 +- .../+video-watch/video-watch.component.ts | 2 +- .../src/app/+videos/videos-routing.module.ts | 25 --- client/src/app/app-routing.module.ts | 65 ++++-- .../users/user-notification.model.ts | 2 +- .../shared/shared-main/video/video.model.ts | 2 +- .../video-share.component.ts | 4 +- .../video-thumbnail.component.ts | 2 +- .../video-comment.model.ts | 2 +- .../video-miniature.component.ts | 2 +- ...eo-playlist-element-miniature.component.ts | 2 +- .../video-playlist-miniature.component.ts | 2 +- client/src/assets/player/utils.ts | 4 +- server/controllers/bots.ts | 2 +- server/controllers/client.ts | 4 +- server/controllers/feeds.ts | 2 +- server/middlewares/validators/oembed.ts | 24 ++- server/models/video/video-playlist.ts | 2 +- server/models/video/video.ts | 2 +- server/tests/api/server/services.ts | 104 +++++----- server/tests/client.ts | 196 ++++++++++-------- server/tools/peertube-watch.ts | 2 +- 36 files changed, 273 insertions(+), 215 deletions(-) diff --git a/client/e2e/src/po/my-account.ts b/client/e2e/src/po/my-account.ts index 61d42214d..9866953e9 100644 --- a/client/e2e/src/po/my-account.ts +++ b/client/e2e/src/po/my-account.ts @@ -61,7 +61,7 @@ export class MyAccountPage { async goOnAssociatedPlaylistEmbed () { let url = await browser.getCurrentUrl() - url = url.replace('/videos/watch/playlist/', '/video-playlists/embed/') + url = url.replace('/w/p/', '/video-playlists/embed/') url = url.replace(':3333', ':9001') return browser.get(url) diff --git a/client/e2e/src/po/video-upload.po.ts b/client/e2e/src/po/video-upload.po.ts index ad2acee7f..a248912ed 100644 --- a/client/e2e/src/po/video-upload.po.ts +++ b/client/e2e/src/po/video-upload.po.ts @@ -41,7 +41,7 @@ export class VideoUploadPage { await this.getSecondStepSubmitButton().click() - return browser.wait(browser.ExpectedConditions.urlContains('/watch/')) + return browser.wait(browser.ExpectedConditions.urlContains('/w/')) } private getSecondStepSubmitButton () { diff --git a/client/e2e/src/po/video-watch.po.ts b/client/e2e/src/po/video-watch.po.ts index b41fe0882..6d91d241e 100644 --- a/client/e2e/src/po/video-watch.po.ts +++ b/client/e2e/src/po/video-watch.po.ts @@ -43,7 +43,7 @@ export class VideoWatchPage { async goOnAssociatedEmbed () { let url = await browser.getCurrentUrl() - url = url.replace('/watch/', '/embed/') + url = url.replace('/w/', '/embed/') url = url.replace(':3333', ':9001') return browser.get(url) @@ -65,7 +65,7 @@ export class VideoWatchPage { await browser.wait(browser.ExpectedConditions.elementToBeClickable(video)) await video.click() - await browser.wait(browser.ExpectedConditions.urlContains('/watch/')) + await browser.wait(browser.ExpectedConditions.urlContains('/w/')) } async clickOnFirstVideo () { @@ -78,7 +78,7 @@ export class VideoWatchPage { const textToReturn = videoName.getText() await video.click() - await browser.wait(browser.ExpectedConditions.urlContains('/watch/')) + await browser.wait(browser.ExpectedConditions.urlContains('/w/')) return textToReturn } diff --git a/client/e2e/src/videos.e2e-spec.ts b/client/e2e/src/videos.e2e-spec.ts index bcc810297..fc816d1bf 100644 --- a/client/e2e/src/videos.e2e-spec.ts +++ b/client/e2e/src/videos.e2e-spec.ts @@ -85,7 +85,7 @@ describe('Videos workflow', () => { let videoNameToExcept = videoName if (await isMobileDevice() || await isSafari()) { - await browser.get('https://peertube2.cpy.re/videos/watch/122d093a-1ede-43bd-bd34-59d2931ffc5e') + await browser.get('https://peertube2.cpy.re/w/122d093a-1ede-43bd-bd34-59d2931ffc5e') videoNameToExcept = 'E2E tests' } else { await videoWatchPage.clickOnVideo(videoName) diff --git a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html index 451e6a34a..03997ea40 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.html @@ -489,7 +489,7 @@ If your instance is explicitly allowed by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.
If the instance is not, we use an image link card that will redirect to your PeerTube instance.

- Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on + Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/w/blabla) on https://cards-dev.twitter.com/validator to see if you instance is allowed.
diff --git a/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts b/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts index 359535526..bb9d70524 100644 --- a/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts +++ b/client/src/app/+my-library/my-video-imports/my-video-imports.component.ts @@ -55,7 +55,7 @@ export class MyVideoImportsComponent extends RestTable implements OnInit { } getVideoUrl (video: { uuid: string }) { - return '/videos/watch/' + video.uuid + return '/w/' + video.uuid } getEditVideoUrl (video: { uuid: string }) { diff --git a/client/src/app/+page-not-found/page-not-found.component.ts b/client/src/app/+page-not-found/page-not-found.component.ts index 695568898..639e5db78 100644 --- a/client/src/app/+page-not-found/page-not-found.component.ts +++ b/client/src/app/+page-not-found/page-not-found.component.ts @@ -2,8 +2,6 @@ import { Component, OnInit } from '@angular/core' import { Title } from '@angular/platform-browser' import { Router } from '@angular/router' import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' - - @Component({ selector: 'my-page-not-found', templateUrl: './page-not-found.component.html', diff --git a/client/src/app/+remote-interaction/remote-interaction.component.ts b/client/src/app/+remote-interaction/remote-interaction.component.ts index 3ebe62f49..6ddf5b58d 100644 --- a/client/src/app/+remote-interaction/remote-interaction.component.ts +++ b/client/src/app/+remote-interaction/remote-interaction.component.ts @@ -39,7 +39,7 @@ export class RemoteInteractionComponent implements OnInit { if (videoResult.data.length !== 0) { const video = videoResult.data[0] - redirectUrl = '/videos/watch/' + video.uuid + redirectUrl = '/w/' + video.uuid } else if (channelResult.data.length !== 0) { const channel = new VideoChannel(channelResult.data[0]) diff --git a/client/src/app/+search/video-lazy-load.resolver.ts b/client/src/app/+search/video-lazy-load.resolver.ts index d4fe6ed79..e43e0089b 100644 --- a/client/src/app/+search/video-lazy-load.resolver.ts +++ b/client/src/app/+search/video-lazy-load.resolver.ts @@ -28,7 +28,7 @@ export class VideoLazyLoadResolver implements Resolve { const video = result.data[0] - return this.router.navigateByUrl('/videos/watch/' + video.uuid) + return this.router.navigateByUrl('/w/' + video.uuid) }) ) } diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts index 8e035b6bb..727bbc32f 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts @@ -127,7 +127,7 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView () => { this.notifier.success($localize`Live published.`) - this.router.navigate(['/videos/watch', video.uuid]) + this.router.navigate(['/w', video.uuid]) }, err => { diff --git a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts index bca1b6eb6..e20f08879 100644 --- a/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts +++ b/client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts @@ -244,7 +244,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy this.isUploadingVideo = false this.notifier.success($localize`Video published.`) - this.router.navigate([ '/videos/watch', video.uuid ]) + this.router.navigate([ '/w', video.uuid ]) }, err => { diff --git a/client/src/app/+videos/+video-edit/video-update.component.html b/client/src/app/+videos/+video-edit/video-update.component.html index 3ce3e623e..9629081e3 100644 --- a/client/src/app/+videos/+video-edit/video-update.component.html +++ b/client/src/app/+videos/+video-edit/video-update.component.html @@ -1,7 +1,7 @@
diff --git a/client/src/app/+videos/+video-edit/video-update.component.ts b/client/src/app/+videos/+video-edit/video-update.component.ts index 2973c6840..574669a23 100644 --- a/client/src/app/+videos/+video-edit/video-update.component.ts +++ b/client/src/app/+videos/+video-edit/video-update.component.ts @@ -156,7 +156,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { this.isUpdatingVideo = false this.loadingBar.useRef().complete() this.notifier.success($localize`Video updated.`) - this.router.navigate([ '/videos/watch', this.video.uuid ]) + this.router.navigate([ '/w', this.video.uuid ]) }, err => { diff --git a/client/src/app/+videos/+video-watch/comment/video-comment.component.html b/client/src/app/+videos/+video-watch/comment/video-comment.component.html index fc0d66ffd..06548edc8 100644 --- a/client/src/app/+videos/+video-watch/comment/video-comment.component.html +++ b/client/src/app/+videos/+video-watch/comment/video-comment.component.html @@ -20,7 +20,7 @@
- + {{ comment.createdAt | myFromNow }} @@ -45,7 +45,7 @@ diff --git a/client/src/app/+videos/+video-watch/video-watch-routing.module.ts b/client/src/app/+videos/+video-watch/video-watch-routing.module.ts index cb77685c0..657fd10f8 100644 --- a/client/src/app/+videos/+video-watch/video-watch-routing.module.ts +++ b/client/src/app/+videos/+video-watch/video-watch-routing.module.ts @@ -4,7 +4,7 @@ import { VideoWatchComponent } from './video-watch.component' const videoWatchRoutes: Routes = [ { - path: 'playlist/:playlistId', + path: 'p/:playlistId', component: VideoWatchComponent }, { diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts index 88c5cef52..0acd44524 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts @@ -690,7 +690,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { if (this.playlist) { this.zone.run(() => this.videoWatchPlaylist.navigateToNextPlaylistVideo()) } else if (this.nextVideoUuid) { - this.router.navigate([ '/videos/watch', this.nextVideoUuid ]) + this.router.navigate([ '/w', this.nextVideoUuid ]) } } diff --git a/client/src/app/+videos/videos-routing.module.ts b/client/src/app/+videos/videos-routing.module.ts index f9f476b18..926dfaab0 100644 --- a/client/src/app/+videos/videos-routing.module.ts +++ b/client/src/app/+videos/videos-routing.module.ts @@ -74,31 +74,6 @@ const videosRoutes: Routes = [ key: 'local-videos-list' } } - }, - { - path: 'upload', - loadChildren: () => import('@app/+videos/+video-edit/video-add.module').then(m => m.VideoAddModule), - data: { - meta: { - title: $localize`Upload a video` - } - } - }, - { - path: 'update/:uuid', - loadChildren: () => import('@app/+videos/+video-edit/video-update.module').then(m => m.VideoUpdateModule), - data: { - meta: { - title: $localize`Edit a video` - } - } - }, - { - path: 'watch', - loadChildren: () => import('@app/+videos/+video-watch/video-watch.module').then(m => m.VideoWatchModule), - data: { - preload: 3000 - } } ] } diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 444b6f134..e35f540be 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -36,16 +36,27 @@ const routes: Routes = [ loadChildren: () => import('./+signup/+verify-account/verify-account.module').then(m => m.VerifyAccountModule), canActivateChild: [ MetaGuard ] }, + + { + path: 'accounts', + redirectTo: 'a' + }, { path: 'a', loadChildren: () => import('./+accounts/accounts.module').then(m => m.AccountsModule), canActivateChild: [ MetaGuard ] }, + + { + path: 'video-channels', + redirectTo: 'c' + }, { path: 'c', loadChildren: () => import('./+video-channels/video-channels.module').then(m => m.VideoChannelsModule), canActivateChild: [ MetaGuard ] }, + { path: 'about', loadChildren: () => import('./+about/about.module').then(m => m.AboutModule), @@ -71,31 +82,60 @@ const routes: Routes = [ loadChildren: () => import('./+search/search.module').then(m => m.SearchModule), canActivateChild: [ MetaGuard ] }, + { - path: 'videos', - loadChildren: () => import('./+videos/videos.module').then(m => m.VideosModule), - canActivateChild: [ MetaGuard ] + path: 'videos/upload', + loadChildren: () => import('@app/+videos/+video-edit/video-add.module').then(m => m.VideoAddModule), + data: { + meta: { + title: $localize`Upload a video` + } + } }, { - path: 'remote-interaction', - loadChildren: () => import('./+remote-interaction/remote-interaction.module').then(m => m.RemoteInteractionModule), + path: 'videos/update/:uuid', + loadChildren: () => import('@app/+videos/+video-edit/video-update.module').then(m => m.VideoUpdateModule), + data: { + meta: { + title: $localize`Edit a video` + } + } + }, + + { + path: 'videos/watch/playlist', + redirectTo: 'w/p' + }, + { + path: 'videos/watch', + redirectTo: 'w' + }, + { + path: 'w', + loadChildren: () => import('@app/+videos/+video-watch/video-watch.module').then(m => m.VideoWatchModule), + data: { + preload: 3000 + } + }, + { + path: 'videos', + loadChildren: () => import('./+videos/videos.module').then(m => m.VideosModule), canActivateChild: [ MetaGuard ] }, { path: 'video-playlists/watch', redirectTo: 'videos/watch/playlist' }, + { - path: 'accounts', - redirectTo: 'a' - }, - { - path: 'video-channels', - redirectTo: 'c' + path: 'remote-interaction', + loadChildren: () => import('./+remote-interaction/remote-interaction.module').then(m => m.RemoteInteractionModule), + canActivateChild: [ MetaGuard ] }, + + // Matches /@:actorName { matcher: (url): UrlMatchResult => { - // Matches /@:actorName const regex = new RegExp(`^@(${USER_USERNAME_REGEX_CHARACTERS}+)$`) if (url.length !== 1) return null @@ -113,6 +153,7 @@ const routes: Routes = [ canActivate: [ ActorRedirectGuard ], component: EmptyComponent }, + { path: '', component: EmptyComponent // Avoid 404, app component will redirect dynamically diff --git a/client/src/app/shared/shared-main/users/user-notification.model.ts b/client/src/app/shared/shared-main/users/user-notification.model.ts index 002a01583..c80bc13b0 100644 --- a/client/src/app/shared/shared-main/users/user-notification.model.ts +++ b/client/src/app/shared/shared-main/users/user-notification.model.ts @@ -238,7 +238,7 @@ export class UserNotification implements UserNotificationServer { } private buildVideoUrl (video: { uuid: string }) { - return '/videos/watch/' + video.uuid + return '/w/' + video.uuid } private buildAccountUrl (account: { name: string, host: string }) { diff --git a/client/src/app/shared/shared-main/video/video.model.ts b/client/src/app/shared/shared-main/video/video.model.ts index 526d10e32..e7f739bfe 100644 --- a/client/src/app/shared/shared-main/video/video.model.ts +++ b/client/src/app/shared/shared-main/video/video.model.ts @@ -88,7 +88,7 @@ export class Video implements VideoServerModel { pluginData?: any static buildClientUrl (videoUUID: string) { - return '/videos/watch/' + videoUUID + return '/w/' + videoUUID } constructor (hash: VideoServerModel, translations = {}) { diff --git a/client/src/app/shared/shared-share-modal/video-share.component.ts b/client/src/app/shared/shared-share-modal/video-share.component.ts index e8760bfcc..2a73e6166 100644 --- a/client/src/app/shared/shared-share-modal/video-share.component.ts +++ b/client/src/app/shared/shared-share-modal/video-share.component.ts @@ -98,14 +98,14 @@ export class VideoShareComponent { getVideoUrl () { let baseUrl = this.customizations.originUrl ? this.video.originInstanceUrl : window.location.origin - baseUrl += '/videos/watch/' + this.video.uuid + baseUrl += '/w/' + this.video.uuid const options = this.getVideoOptions(baseUrl) return buildVideoLink(options) } getPlaylistUrl () { - const base = window.location.origin + '/videos/watch/playlist/' + this.playlist.uuid + const base = window.location.origin + '/w/p/' + this.playlist.uuid if (!this.includeVideoInPlaylist) return base diff --git a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.ts b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.ts index bdede17a3..d5583c29f 100644 --- a/client/src/app/shared/shared-thumbnail/video-thumbnail.component.ts +++ b/client/src/app/shared/shared-thumbnail/video-thumbnail.component.ts @@ -57,7 +57,7 @@ export class VideoThumbnailComponent { getVideoRouterLink () { if (this.videoRouterLink) return this.videoRouterLink - return [ '/videos/watch', this.video.uuid ] + return [ '/w', this.video.uuid ] } onWatchLaterClick (event: Event) { diff --git a/client/src/app/shared/shared-video-comment/video-comment.model.ts b/client/src/app/shared/shared-video-comment/video-comment.model.ts index 1a2fe03db..94d6c5fa8 100644 --- a/client/src/app/shared/shared-video-comment/video-comment.model.ts +++ b/client/src/app/shared/shared-video-comment/video-comment.model.ts @@ -85,7 +85,7 @@ export class VideoCommentAdmin implements VideoCommentAdminServerModel { id: hash.video.id, uuid: hash.video.uuid, name: hash.video.name, - localUrl: '/videos/watch/' + hash.video.uuid + localUrl: '/w/' + hash.video.uuid } this.localUrl = this.video.localUrl + ';threadId=' + this.threadId diff --git a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts index b58c118be..aac55a6e9 100644 --- a/client/src/app/shared/shared-video-miniature/video-miniature.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-miniature.component.ts @@ -125,7 +125,7 @@ export class VideoMiniatureComponent implements OnInit { buildVideoLink () { if (this.videoLinkType === 'internal' || !this.video.url) { - this.videoRouterLink = [ '/videos/watch', this.video.uuid ] + this.videoRouterLink = [ '/w', this.video.uuid ] return } diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts index 7c083ae26..86c281a1e 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts +++ b/client/src/app/shared/shared-video-playlist/video-playlist-element-miniature.component.ts @@ -71,7 +71,7 @@ export class VideoPlaylistElementMiniatureComponent implements OnInit { buildRouterLink () { if (!this.playlist) return null - return [ '/videos/watch/playlist', this.playlist.uuid ] + return [ '/w/p', this.playlist.uuid ] } buildRouterQuery () { diff --git a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts index 6b0b1056f..9bbec6038 100644 --- a/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts +++ b/client/src/app/shared/shared-video-playlist/video-playlist-miniature.component.ts @@ -18,6 +18,6 @@ export class VideoPlaylistMiniatureComponent { if (this.toManage) return [ '/my-library/video-playlists', this.playlist.uuid ] if (this.playlist.videosLength === 0) return null - return [ '/videos/watch/playlist', this.playlist.uuid ] + return [ '/w/p', this.playlist.uuid ] } } diff --git a/client/src/assets/player/utils.ts b/client/src/assets/player/utils.ts index 1243526d2..2bb70d1fa 100644 --- a/client/src/assets/player/utils.ts +++ b/client/src/assets/player/utils.ts @@ -65,7 +65,7 @@ function buildVideoLink (options: { const url = baseUrl ? baseUrl - : window.location.origin + window.location.pathname.replace('/embed/', '/watch/') + : window.location.origin + window.location.pathname.replace('/embed/', '/w/') const params = generateParams(window.location.search) @@ -101,7 +101,7 @@ function buildPlaylistLink (options: { const url = baseUrl ? baseUrl - : window.location.origin + window.location.pathname.replace('/video-playlists/embed/', '/videos/watch/playlist/') + : window.location.origin + window.location.pathname.replace('/video-playlists/embed/', '/w/p/') const params = generateParams(window.location.search) diff --git a/server/controllers/bots.ts b/server/controllers/bots.ts index 8d1fa72f3..9e92063d4 100644 --- a/server/controllers/bots.ts +++ b/server/controllers/bots.ts @@ -75,7 +75,7 @@ async function getSitemapLocalVideoUrls () { }) return data.map(v => ({ - url: WEBSERVER.URL + '/videos/watch/' + v.uuid, + url: WEBSERVER.URL + '/w/' + v.uuid, video: [ { title: v.name, diff --git a/server/controllers/client.ts b/server/controllers/client.ts index 35e5af9d1..fcccc48e0 100644 --- a/server/controllers/client.ts +++ b/server/controllers/client.ts @@ -19,8 +19,8 @@ const testEmbedPath = join(distPath, 'standalone', 'videos', 'test-embed.html') // Special route that add OpenGraph and oEmbed tags // Do not use a template engine for a so little thing -clientsRouter.use('/videos/watch/playlist/:id', asyncMiddleware(generateWatchPlaylistHtmlPage)) -clientsRouter.use('/videos/watch/:id', asyncMiddleware(generateWatchHtmlPage)) +clientsRouter.use([ '/w/p/:id', '/videos/watch/playlist/:id' ], asyncMiddleware(generateWatchPlaylistHtmlPage)) +clientsRouter.use([ '/w/:id', '/videos/watch/:id' ], asyncMiddleware(generateWatchHtmlPage)) clientsRouter.use([ '/accounts/:nameWithHost', '/a/:nameWithHost' ], asyncMiddleware(generateAccountHtmlPage)) clientsRouter.use([ '/video-channels/:nameWithHost', '/c/:nameWithHost' ], asyncMiddleware(generateVideoChannelHtmlPage)) clientsRouter.use('/@:nameWithHost', asyncMiddleware(generateActorHtmlPage)) diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts index f0717bbbc..865f5c2a1 100644 --- a/server/controllers/feeds.ts +++ b/server/controllers/feeds.ts @@ -293,7 +293,7 @@ function addVideosToFeed (feed, videos: VideoModel[]) { feed.addItem({ title: video.name, id: video.url, - link: WEBSERVER.URL + '/videos/watch/' + video.uuid, + link: WEBSERVER.URL + '/w/' + video.uuid, description: video.getTruncatedDescription(), content: video.description, author: [ diff --git a/server/middlewares/validators/oembed.ts b/server/middlewares/validators/oembed.ts index 2a7dc257b..165eda6d5 100644 --- a/server/middlewares/validators/oembed.ts +++ b/server/middlewares/validators/oembed.ts @@ -4,15 +4,29 @@ import { join } from 'path' import { fetchVideo } from '@server/helpers/video' import { VideoPlaylistModel } from '@server/models/video/video-playlist' import { VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' +import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' import { isTestInstance } from '../../helpers/core-utils' import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' import { logger } from '../../helpers/logger' import { WEBSERVER } from '../../initializers/constants' import { areValidationErrors } from './utils' -import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' -const startVideoPlaylistsURL = WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, 'videos', 'watch', 'playlist') + '/' -const startVideosURL = WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, 'videos', 'watch') + '/' +const playlistPaths = [ + join('videos', 'watch', 'playlist'), + join('w', 'p') +] + +const videoPaths = [ + join('videos', 'watch'), + 'w' +] + +function buildUrls (paths: string[]) { + return paths.map(p => WEBSERVER.SCHEME + '://' + join(WEBSERVER.HOST, p) + '/') +} + +const startPlaylistURLs = buildUrls(playlistPaths) +const startVideoURLs = buildUrls(videoPaths) const watchRegex = /([^/]+)$/ const isURLOptions = { @@ -43,8 +57,8 @@ const oembedValidator = [ const url = req.query.url as string - const isPlaylist = url.startsWith(startVideoPlaylistsURL) - const isVideo = isPlaylist ? false : url.startsWith(startVideosURL) + const isPlaylist = startPlaylistURLs.some(u => url.startsWith(u)) + const isVideo = isPlaylist ? false : startVideoURLs.some(u => url.startsWith(u)) const startIsOk = isVideo || isPlaylist diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts index c293287d3..98cea1b64 100644 --- a/server/models/video/video-playlist.ts +++ b/server/models/video/video-playlist.ts @@ -496,7 +496,7 @@ export class VideoPlaylistModel extends Model>> { } getWatchStaticPath () { - return '/videos/watch/' + this.uuid + return '/w/' + this.uuid } getEmbedStaticPath () { diff --git a/server/tests/api/server/services.ts b/server/tests/api/server/services.ts index f0fa91674..ea64e4040 100644 --- a/server/tests/api/server/services.ts +++ b/server/tests/api/server/services.ts @@ -67,61 +67,67 @@ describe('Test services', function () { }) it('Should have a valid oEmbed video response', async function () { - const oembedUrl = 'http://localhost:' + server.port + '/videos/watch/' + video.uuid - - const res = await getOEmbed(server.url, oembedUrl) - const expectedHtml = '' - const expectedThumbnailUrl = 'http://localhost:' + server.port + video.previewPath - - expect(res.body.html).to.equal(expectedHtml) - expect(res.body.title).to.equal(video.name) - expect(res.body.author_name).to.equal(server.videoChannel.displayName) - expect(res.body.width).to.equal(560) - expect(res.body.height).to.equal(315) - expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl) - expect(res.body.thumbnail_width).to.equal(850) - expect(res.body.thumbnail_height).to.equal(480) + for (const basePath of [ '/videos/watch/', '/w/' ]) { + const oembedUrl = 'http://localhost:' + server.port + basePath + video.uuid + + const res = await getOEmbed(server.url, oembedUrl) + const expectedHtml = '' + const expectedThumbnailUrl = 'http://localhost:' + server.port + video.previewPath + + expect(res.body.html).to.equal(expectedHtml) + expect(res.body.title).to.equal(video.name) + expect(res.body.author_name).to.equal(server.videoChannel.displayName) + expect(res.body.width).to.equal(560) + expect(res.body.height).to.equal(315) + expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl) + expect(res.body.thumbnail_width).to.equal(850) + expect(res.body.thumbnail_height).to.equal(480) + } }) it('Should have a valid playlist oEmbed response', async function () { - const oembedUrl = 'http://localhost:' + server.port + '/videos/watch/playlist/' + playlistUUID - - const res = await getOEmbed(server.url, oembedUrl) - const expectedHtml = '' - - expect(res.body.html).to.equal(expectedHtml) - expect(res.body.title).to.equal('The Life and Times of Scrooge McDuck') - expect(res.body.author_name).to.equal(server.videoChannel.displayName) - expect(res.body.width).to.equal(560) - expect(res.body.height).to.equal(315) - expect(res.body.thumbnail_url).exist - expect(res.body.thumbnail_width).to.equal(280) - expect(res.body.thumbnail_height).to.equal(157) + for (const basePath of [ '/videos/watch/playlist/', '/w/p/' ]) { + const oembedUrl = 'http://localhost:' + server.port + basePath + playlistUUID + + const res = await getOEmbed(server.url, oembedUrl) + const expectedHtml = '' + + expect(res.body.html).to.equal(expectedHtml) + expect(res.body.title).to.equal('The Life and Times of Scrooge McDuck') + expect(res.body.author_name).to.equal(server.videoChannel.displayName) + expect(res.body.width).to.equal(560) + expect(res.body.height).to.equal(315) + expect(res.body.thumbnail_url).exist + expect(res.body.thumbnail_width).to.equal(280) + expect(res.body.thumbnail_height).to.equal(157) + } }) it('Should have a valid oEmbed response with small max height query', async function () { - const oembedUrl = 'http://localhost:' + server.port + '/videos/watch/' + video.uuid - const format = 'json' - const maxHeight = 50 - const maxWidth = 50 - - const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth) - const expectedHtml = '' - - expect(res.body.html).to.equal(expectedHtml) - expect(res.body.title).to.equal(video.name) - expect(res.body.author_name).to.equal(server.videoChannel.displayName) - expect(res.body.height).to.equal(50) - expect(res.body.width).to.equal(50) - expect(res.body).to.not.have.property('thumbnail_url') - expect(res.body).to.not.have.property('thumbnail_width') - expect(res.body).to.not.have.property('thumbnail_height') + for (const basePath of [ '/videos/watch/', '/w/' ]) { + const oembedUrl = 'http://localhost:' + server.port + basePath + video.uuid + const format = 'json' + const maxHeight = 50 + const maxWidth = 50 + + const res = await getOEmbed(server.url, oembedUrl, format, maxHeight, maxWidth) + const expectedHtml = '' + + expect(res.body.html).to.equal(expectedHtml) + expect(res.body.title).to.equal(video.name) + expect(res.body.author_name).to.equal(server.videoChannel.displayName) + expect(res.body.height).to.equal(50) + expect(res.body.width).to.equal(50) + expect(res.body).to.not.have.property('thumbnail_url') + expect(res.body).to.not.have.property('thumbnail_width') + expect(res.body).to.not.have.property('thumbnail_height') + } }) after(async function () { diff --git a/server/tests/client.ts b/server/tests/client.ts index f33e5c1da..253a95624 100644 --- a/server/tests/client.ts +++ b/server/tests/client.ts @@ -54,6 +54,9 @@ describe('Test a client controllers', function () { const channelDescription = 'my super channel description' + const watchVideoBasePaths = [ '/videos/watch/', '/w/' ] + const watchPlaylistBasePaths = [ '/videos/watch/playlist/', '/w/p/' ] + before(async function () { this.timeout(120000) @@ -111,35 +114,40 @@ describe('Test a client controllers', function () { }) describe('oEmbed', function () { + it('Should have valid oEmbed discovery tags for videos', async function () { - const path = '/videos/watch/' + servers[0].video.uuid - const res = await request(servers[0].url) - .get(path) - .set('Accept', 'text/html') - .expect(HttpStatusCode.OK_200) + for (const basePath of watchVideoBasePaths) { + const path = basePath + servers[0].video.uuid + const res = await request(servers[0].url) + .get(path) + .set('Accept', 'text/html') + .expect(HttpStatusCode.OK_200) - const port = servers[0].port + const port = servers[0].port - const expectedLink = '` + const expectedLink = '` - expect(res.text).to.contain(expectedLink) + expect(res.text).to.contain(expectedLink) + } }) it('Should have valid oEmbed discovery tags for a playlist', async function () { - const res = await request(servers[0].url) - .get('/videos/watch/playlist/' + playlistUUID) - .set('Accept', 'text/html') - .expect(HttpStatusCode.OK_200) + for (const basePath of watchPlaylistBasePaths) { + const res = await request(servers[0].url) + .get(basePath + playlistUUID) + .set('Accept', 'text/html') + .expect(HttpStatusCode.OK_200) - const port = servers[0].port + const port = servers[0].port - const expectedLink = '` + const expectedLink = '` - expect(res.text).to.contain(expectedLink) + expect(res.text).to.contain(expectedLink) + } }) }) @@ -165,6 +173,26 @@ describe('Test a client controllers', function () { expect(text).to.contain(``) } + async function watchVideoPageTest (path: string) { + const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) + const text = res.text + + expect(text).to.contain(``) + expect(text).to.contain(``) + expect(text).to.contain('') + expect(text).to.contain(``) + } + + async function watchPlaylistPageTest (path: string) { + const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) + const text = res.text + + expect(text).to.contain(``) + expect(text).to.contain(``) + expect(text).to.contain('') + expect(text).to.contain(``) + } + it('Should have valid Open Graph tags on the account page', async function () { await accountPageTest('/accounts/' + servers[0].user.username) await accountPageTest('/a/' + servers[0].user.username) @@ -177,40 +205,16 @@ describe('Test a client controllers', function () { await channelPageTest('/@' + servers[0].videoChannel.name) }) - it('Should have valid Open Graph tags on the watch page with video id', async function () { - const res = await request(servers[0].url) - .get('/videos/watch/' + servers[0].video.id) - .set('Accept', 'text/html') - .expect(HttpStatusCode.OK_200) - - expect(res.text).to.contain(``) - expect(res.text).to.contain(``) - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - }) - - it('Should have valid Open Graph tags on the watch page with video uuid', async function () { - const res = await request(servers[0].url) - .get('/videos/watch/' + servers[0].video.uuid) - .set('Accept', 'text/html') - .expect(HttpStatusCode.OK_200) - - expect(res.text).to.contain(``) - expect(res.text).to.contain(``) - expect(res.text).to.contain('') - expect(res.text).to.contain(``) + it('Should have valid Open Graph tags on the watch page', async function () { + await watchVideoPageTest('/videos/watch/' + servers[0].video.id) + await watchVideoPageTest('/videos/watch/' + servers[0].video.uuid) + await watchVideoPageTest('/w/' + servers[0].video.uuid) + await watchVideoPageTest('/w/' + servers[0].video.id) }) it('Should have valid Open Graph tags on the watch playlist page', async function () { - const res = await request(servers[0].url) - .get('/videos/watch/playlist/' + playlistUUID) - .set('Accept', 'text/html') - .expect(HttpStatusCode.OK_200) - - expect(res.text).to.contain(``) - expect(res.text).to.contain(``) - expect(res.text).to.contain('') - expect(res.text).to.contain(``) + await watchPlaylistPageTest('/videos/watch/playlist/' + playlistUUID) + await watchPlaylistPageTest('/w/p/' + playlistUUID) }) }) @@ -238,28 +242,36 @@ describe('Test a client controllers', function () { expect(text).to.contain(``) } - it('Should have valid twitter card on the watch video page', async function () { - const res = await request(servers[0].url) - .get('/videos/watch/' + servers[0].video.uuid) - .set('Accept', 'text/html') - .expect(HttpStatusCode.OK_200) + async function watchVideoPageTest (path: string) { + const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) + const text = res.text - expect(res.text).to.contain('') - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - expect(res.text).to.contain(``) + expect(text).to.contain('') + expect(text).to.contain('') + expect(text).to.contain(``) + expect(text).to.contain(``) + } + + async function watchPlaylistPageTest (path: string) { + const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) + const text = res.text + + expect(text).to.contain('') + expect(text).to.contain('') + expect(text).to.contain(``) + expect(text).to.contain(``) + } + + it('Should have valid twitter card on the watch video page', async function () { + await watchVideoPageTest('/videos/watch/' + servers[0].video.id) + await watchVideoPageTest('/videos/watch/' + servers[0].video.uuid) + await watchVideoPageTest('/w/' + servers[0].video.uuid) + await watchVideoPageTest('/w/' + servers[0].video.id) }) it('Should have valid twitter card on the watch playlist page', async function () { - const res = await request(servers[0].url) - .get('/videos/watch/playlist/' + playlistUUID) - .set('Accept', 'text/html') - .expect(HttpStatusCode.OK_200) - - expect(res.text).to.contain('') - expect(res.text).to.contain('') - expect(res.text).to.contain(``) - expect(res.text).to.contain(``) + await watchPlaylistPageTest('/videos/watch/playlist/' + playlistUUID) + await watchPlaylistPageTest('/w/p/' + playlistUUID) }) it('Should have valid twitter card on the account page', async function () { @@ -304,24 +316,32 @@ describe('Test a client controllers', function () { expect(text).to.contain('') } - it('Should have valid twitter card on the watch video page', async function () { - const res = await request(servers[0].url) - .get('/videos/watch/' + servers[0].video.uuid) - .set('Accept', 'text/html') - .expect(HttpStatusCode.OK_200) + async function watchVideoPageTest (path: string) { + const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) + const text = res.text - expect(res.text).to.contain('') - expect(res.text).to.contain('') + expect(text).to.contain('') + expect(text).to.contain('') + } + + async function watchPlaylistPageTest (path: string) { + const res = await makeGetRequest({ url: servers[0].url, path, accept: 'text/html', statusCodeExpected: HttpStatusCode.OK_200 }) + const text = res.text + + expect(text).to.contain('') + expect(text).to.contain('') + } + + it('Should have valid twitter card on the watch video page', async function () { + await watchVideoPageTest('/videos/watch/' + servers[0].video.id) + await watchVideoPageTest('/videos/watch/' + servers[0].video.uuid) + await watchVideoPageTest('/w/' + servers[0].video.uuid) + await watchVideoPageTest('/w/' + servers[0].video.id) }) it('Should have valid twitter card on the watch playlist page', async function () { - const res = await request(servers[0].url) - .get('/videos/watch/playlist/' + playlistUUID) - .set('Accept', 'text/html') - .expect(HttpStatusCode.OK_200) - - expect(res.text).to.contain('') - expect(res.text).to.contain('') + await watchPlaylistPageTest('/videos/watch/playlist/' + playlistUUID) + await watchPlaylistPageTest('/w/p/' + playlistUUID) }) it('Should have valid twitter card on the account page', async function () { @@ -378,8 +398,10 @@ describe('Test a client controllers', function () { }) it('Should use the original video URL for the canonical tag', async function () { - const res = await makeHTMLRequest(servers[1].url, '/videos/watch/' + servers[0].video.uuid) - expect(res.text).to.contain(``) + for (const basePath of watchVideoBasePaths) { + const res = await makeHTMLRequest(servers[1].url, basePath + servers[0].video.uuid) + expect(res.text).to.contain(``) + } }) it('Should use the original account URL for the canonical tag', async function () { @@ -403,8 +425,10 @@ describe('Test a client controllers', function () { }) it('Should use the original playlist URL for the canonical tag', async function () { - const res = await makeHTMLRequest(servers[1].url, '/videos/watch/playlist/' + playlistUUID) - expect(res.text).to.contain(``) + for (const basePath of watchPlaylistBasePaths) { + const res = await makeHTMLRequest(servers[1].url, basePath + playlistUUID) + expect(res.text).to.contain(``) + } }) }) diff --git a/server/tools/peertube-watch.ts b/server/tools/peertube-watch.ts index 6d9cfa3b7..3ca3e242a 100644 --- a/server/tools/peertube-watch.ts +++ b/server/tools/peertube-watch.ts @@ -30,7 +30,7 @@ function run (url: string, options: program.OptionValues) { const cmd = 'node ' + join(__dirname, 'node_modules', 'webtorrent-hybrid', 'bin', 'cmd.js') const args = ` --${options.gui} ` + - url.replace('videos/watch', 'download/torrents') + + url.replace(/(\/videos\/watch\/)|\/w\//, '/download/torrents/') + `-${options.resolution}.torrent` try { -- 2.41.0