aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/client-html.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/client-html.ts')
-rw-r--r--server/lib/client-html.ts80
1 files changed, 60 insertions, 20 deletions
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index b2c376e20..516827a05 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -1,7 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as Bluebird from 'bluebird'
3import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/models/i18n/i18n' 2import { buildFileLocale, getDefaultLocale, is18nLocale, POSSIBLE_LOCALES } from '../../shared/models/i18n/i18n'
4import { CONFIG, CUSTOM_HTML_TAG_COMMENTS, EMBED_SIZE } from '../initializers' 3import { CUSTOM_HTML_TAG_COMMENTS, EMBED_SIZE, WEBSERVER } from '../initializers/constants'
5import { join } from 'path' 4import { join } from 'path'
6import { escapeHTML } from '../helpers/core-utils' 5import { escapeHTML } from '../helpers/core-utils'
7import { VideoModel } from '../models/video/video' 6import { VideoModel } from '../models/video/video'
@@ -9,10 +8,14 @@ import * as validator from 'validator'
9import { VideoPrivacy } from '../../shared/models/videos' 8import { VideoPrivacy } from '../../shared/models/videos'
10import { readFile } from 'fs-extra' 9import { readFile } from 'fs-extra'
11import { getActivityStreamDuration } from '../models/video/video-format-utils' 10import { getActivityStreamDuration } from '../models/video/video-format-utils'
11import { AccountModel } from '../models/account/account'
12import { VideoChannelModel } from '../models/video/video-channel'
13import * as Bluebird from 'bluebird'
14import { CONFIG } from '../initializers/config'
12 15
13export class ClientHtml { 16export class ClientHtml {
14 17
15 private static htmlCache: { [path: string]: string } = {} 18 private static htmlCache: { [ path: string ]: string } = {}
16 19
17 static invalidCache () { 20 static invalidCache () {
18 ClientHtml.htmlCache = {} 21 ClientHtml.htmlCache = {}
@@ -28,18 +31,14 @@ export class ClientHtml {
28 } 31 }
29 32
30 static async getWatchHTMLPage (videoId: string, req: express.Request, res: express.Response) { 33 static async getWatchHTMLPage (videoId: string, req: express.Request, res: express.Response) {
31 let videoPromise: Bluebird<VideoModel>
32
33 // Let Angular application handle errors 34 // Let Angular application handle errors
34 if (validator.isInt(videoId) || validator.isUUID(videoId, 4)) { 35 if (!validator.isInt(videoId) && !validator.isUUID(videoId, 4)) {
35 videoPromise = VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
36 } else {
37 return ClientHtml.getIndexHTML(req, res) 36 return ClientHtml.getIndexHTML(req, res)
38 } 37 }
39 38
40 const [ html, video ] = await Promise.all([ 39 const [ html, video ] = await Promise.all([
41 ClientHtml.getIndexHTML(req, res), 40 ClientHtml.getIndexHTML(req, res),
42 videoPromise 41 VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
43 ]) 42 ])
44 43
45 // Let Angular application handle errors 44 // Let Angular application handle errors
@@ -49,14 +48,44 @@ export class ClientHtml {
49 48
50 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(video.name)) 49 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(video.name))
51 customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(video.description)) 50 customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(video.description))
52 customHtml = ClientHtml.addOpenGraphAndOEmbedTags(customHtml, video) 51 customHtml = ClientHtml.addVideoOpenGraphAndOEmbedTags(customHtml, video)
52
53 return customHtml
54 }
55
56 static async getAccountHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) {
57 return this.getAccountOrChannelHTMLPage(() => AccountModel.loadByNameWithHost(nameWithHost), req, res)
58 }
59
60 static async getVideoChannelHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) {
61 return this.getAccountOrChannelHTMLPage(() => VideoChannelModel.loadByNameWithHostAndPopulateAccount(nameWithHost), req, res)
62 }
63
64 private static async getAccountOrChannelHTMLPage (
65 loader: () => Bluebird<AccountModel | VideoChannelModel>,
66 req: express.Request,
67 res: express.Response
68 ) {
69 const [ html, entity ] = await Promise.all([
70 ClientHtml.getIndexHTML(req, res),
71 loader()
72 ])
73
74 // Let Angular application handle errors
75 if (!entity) {
76 return ClientHtml.getIndexHTML(req, res)
77 }
78
79 let customHtml = ClientHtml.addTitleTag(html, escapeHTML(entity.getDisplayName()))
80 customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(entity.description))
81 customHtml = ClientHtml.addAccountOrChannelMetaTags(customHtml, entity)
53 82
54 return customHtml 83 return customHtml
55 } 84 }
56 85
57 private static async getIndexHTML (req: express.Request, res: express.Response, paramLang?: string) { 86 private static async getIndexHTML (req: express.Request, res: express.Response, paramLang?: string) {
58 const path = ClientHtml.getIndexPath(req, res, paramLang) 87 const path = ClientHtml.getIndexPath(req, res, paramLang)
59 if (ClientHtml.htmlCache[path]) return ClientHtml.htmlCache[path] 88 if (ClientHtml.htmlCache[ path ]) return ClientHtml.htmlCache[ path ]
60 89
61 const buffer = await readFile(path) 90 const buffer = await readFile(path)
62 91
@@ -64,7 +93,7 @@ export class ClientHtml {
64 93
65 html = ClientHtml.addCustomCSS(html) 94 html = ClientHtml.addCustomCSS(html)
66 95
67 ClientHtml.htmlCache[path] = html 96 ClientHtml.htmlCache[ path ] = html
68 97
69 return html 98 return html
70 } 99 }
@@ -78,7 +107,7 @@ export class ClientHtml {
78 107
79 // Save locale in cookies 108 // Save locale in cookies
80 res.cookie('clientLanguage', lang, { 109 res.cookie('clientLanguage', lang, {
81 secure: CONFIG.WEBSERVER.SCHEME === 'https', 110 secure: WEBSERVER.SCHEME === 'https',
82 sameSite: true, 111 sameSite: true,
83 maxAge: 1000 * 3600 * 24 * 90 // 3 months 112 maxAge: 1000 * 3600 * 24 * 90 // 3 months
84 }) 113 })
@@ -114,13 +143,13 @@ export class ClientHtml {
114 return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag) 143 return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.CUSTOM_CSS, styleTag)
115 } 144 }
116 145
117 private static addOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) { 146 private static addVideoOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) {
118 const previewUrl = CONFIG.WEBSERVER.URL + video.getPreviewStaticPath() 147 const previewUrl = WEBSERVER.URL + video.getPreviewStaticPath()
119 const videoUrl = CONFIG.WEBSERVER.URL + video.getWatchStaticPath() 148 const videoUrl = WEBSERVER.URL + video.getWatchStaticPath()
120 149
121 const videoNameEscaped = escapeHTML(video.name) 150 const videoNameEscaped = escapeHTML(video.name)
122 const videoDescriptionEscaped = escapeHTML(video.description) 151 const videoDescriptionEscaped = escapeHTML(video.description)
123 const embedUrl = CONFIG.WEBSERVER.URL + video.getEmbedStaticPath() 152 const embedUrl = WEBSERVER.URL + video.getEmbedStaticPath()
124 153
125 const openGraphMetaTags = { 154 const openGraphMetaTags = {
126 'og:type': 'video', 155 'og:type': 'video',
@@ -152,7 +181,7 @@ export class ClientHtml {
152 const oembedLinkTags = [ 181 const oembedLinkTags = [
153 { 182 {
154 type: 'application/json+oembed', 183 type: 'application/json+oembed',
155 href: CONFIG.WEBSERVER.URL + '/services/oembed?url=' + encodeURIComponent(videoUrl), 184 href: WEBSERVER.URL + '/services/oembed?url=' + encodeURIComponent(videoUrl),
156 title: videoNameEscaped 185 title: videoNameEscaped
157 } 186 }
158 ] 187 ]
@@ -174,7 +203,7 @@ export class ClientHtml {
174 203
175 // Opengraph 204 // Opengraph
176 Object.keys(openGraphMetaTags).forEach(tagName => { 205 Object.keys(openGraphMetaTags).forEach(tagName => {
177 const tagValue = openGraphMetaTags[tagName] 206 const tagValue = openGraphMetaTags[ tagName ]
178 207
179 tagsString += `<meta property="${tagName}" content="${tagValue}" />` 208 tagsString += `<meta property="${tagName}" content="${tagValue}" />`
180 }) 209 })
@@ -190,6 +219,17 @@ export class ClientHtml {
190 // SEO, use origin video url so Google does not index remote videos 219 // SEO, use origin video url so Google does not index remote videos
191 tagsString += `<link rel="canonical" href="${video.url}" />` 220 tagsString += `<link rel="canonical" href="${video.url}" />`
192 221
193 return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.OPENGRAPH_AND_OEMBED, tagsString) 222 return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString)
223 }
224
225 private static addAccountOrChannelMetaTags (htmlStringPage: string, entity: AccountModel | VideoChannelModel) {
226 // SEO, use origin account or channel URL
227 const metaTags = `<link rel="canonical" href="${entity.Actor.url}" />`
228
229 return this.addOpenGraphAndOEmbedTags(htmlStringPage, metaTags)
230 }
231
232 private static addOpenGraphAndOEmbedTags (htmlStringPage: string, metaTags: string) {
233 return htmlStringPage.replace(CUSTOM_HTML_TAG_COMMENTS.META_TAGS, metaTags)
194 } 234 }
195} 235}