Skip to content

Commit

Permalink
Add open-graph and twitter-card to video-playlists watch view
Browse files Browse the repository at this point in the history
  • Loading branch information
kimsible committed Jul 22, 2020
1 parent ad4aa14 commit c4dea59
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 2 deletions.
7 changes: 7 additions & 0 deletions server/controllers/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ 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('/accounts/:nameWithHost', asyncMiddleware(generateAccountHtmlPage))
clientsRouter.use('/video-channels/:nameWithHost', asyncMiddleware(generateVideoChannelHtmlPage))
Expand Down Expand Up @@ -134,6 +135,12 @@ async function generateWatchHtmlPage (req: express.Request, res: express.Respons
return sendHTML(html, res)
}

async function generateWatchPlaylistHtmlPage(req: express.Request, res: express.Response) {
const html = await ClientHtml.getWatchPlaylistHTMLPage(req.params.id + '', req, res)

return sendHTML(html, res)
}

async function generateAccountHtmlPage (req: express.Request, res: express.Response) {
const html = await ClientHtml.getAccountHTMLPage(req.params.nameWithHost, req, res)

Expand Down
81 changes: 79 additions & 2 deletions server/lib/client-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import { AVATARS_SIZE, CUSTOM_HTML_TAG_COMMENTS, EMBED_SIZE, PLUGIN_GLOBAL_CSS_P
import { join } from 'path'
import { escapeHTML, sha256 } from '../helpers/core-utils'
import { VideoModel } from '../models/video/video'
import { VideoPlaylistModel } from '../models/video/video-playlist'
import validator from 'validator'
import { VideoPrivacy } from '../../shared/models/videos'
import { VideoPrivacy, VideoPlaylistPrivacy } from '../../shared/models/videos'
import { readFile } from 'fs-extra'
import { getActivityStreamDuration } from '../models/video/video-format-utils'
import { AccountModel } from '../models/account/account'
import { VideoChannelModel } from '../models/video/video-channel'
import * as Bluebird from 'bluebird'
import { CONFIG } from '../initializers/config'
import { logger } from '../helpers/logger'
import { MAccountActor, MChannelActor, MVideo } from '../types/models'
import { MAccountActor, MChannelActor, MVideo, MVideoPlaylist } from '../types/models'

export class ClientHtml {

Expand Down Expand Up @@ -61,6 +62,31 @@ export class ClientHtml {
return customHtml
}

static async getWatchPlaylistHTMLPage(videoPlaylistId: string, req: express.Request, res: express.Response) {
// Let Angular application handle errors
if (!validator.isInt(videoPlaylistId) && !validator.isUUID(videoPlaylistId, 4)) {
res.status(404)
return ClientHtml.getIndexHTML(req, res)
}

const [html, videoPlaylist] = await Promise.all([
ClientHtml.getIndexHTML(req, res),
VideoPlaylistModel.loadWithAccountAndChannel(videoPlaylistId, null)
])

// Let Angular application handle errors
if (!videoPlaylist || videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) {
res.status(404)
return html
}

let customHtml = ClientHtml.addTitleTag(html, escapeHTML(videoPlaylist.name))
customHtml = ClientHtml.addDescriptionTag(customHtml, escapeHTML(videoPlaylist.description))
customHtml = ClientHtml.addVideoPlaylistOpenGraphAndMetaTags(customHtml, videoPlaylist)

return customHtml
}

static async getAccountHTMLPage (nameWithHost: string, req: express.Request, res: express.Response) {
return this.getAccountOrChannelHTMLPage(() => AccountModel.loadByNameWithHost(nameWithHost), req, res)
}
Expand Down Expand Up @@ -262,6 +288,57 @@ export class ClientHtml {
return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString)
}

private static addVideoPlaylistOpenGraphAndMetaTags(htmlStringPage: string, videoPlaylist: MVideoPlaylist) {
const videoPlaylistThumbnailUrl = videoPlaylist.getThumbnailUrl()
const videoPlaylistUrl = videoPlaylist.getWatchUrl()

const videoPlaylistNameEscaped = escapeHTML(videoPlaylist.name)
const videoPlaylistDescriptionEscaped = escapeHTML(videoPlaylist.description)

const openGraphMetaTags = {
'og:title': videoPlaylistNameEscaped,
'og:image': videoPlaylistThumbnailUrl,
'og:url': videoPlaylistUrl,
'og:description': videoPlaylistDescriptionEscaped,

'name': videoPlaylistNameEscaped,
'description': videoPlaylistDescriptionEscaped,
'image': videoPlaylistThumbnailUrl,

'twitter:card': 'summary_large_image',
'twitter:site': CONFIG.SERVICES.TWITTER.USERNAME,
'twitter:title': videoPlaylistNameEscaped,
'twitter:description': videoPlaylistDescriptionEscaped,
'twitter:image': videoPlaylistThumbnailUrl
}

const schemaTags = {
'@context': 'http://schema.org',
'@type': 'ItemList',
'name': videoPlaylistNameEscaped,
'description': videoPlaylistDescriptionEscaped,
'image': videoPlaylistThumbnailUrl,
'numberOfItems': videoPlaylist.get('videosLength')
}

let tagsString = ''

// Opengraph
Object.keys(openGraphMetaTags).forEach(tagName => {
const tagValue = openGraphMetaTags[tagName]

tagsString += `<meta property="${tagName}" content="${tagValue}" />`
})

// Schema.org
tagsString += `<script type="application/ld+json">${JSON.stringify(schemaTags)}</script>`

// SEO, use origin videoPlaylist url so Google does not index remote videos
tagsString += `<link rel="canonical" href="${videoPlaylistUrl}" />`

return this.addOpenGraphAndOEmbedTags(htmlStringPage, tagsString)
}

private static addAccountOrChannelOpenGraphAndMetaTags (htmlStringPage: string, entity: MAccountActor | MChannelActor) {
const entityDisplayNameEscaped = escapeHTML(entity.getDisplayName())
const entityDescriptionEscaped = escapeHTML(entity.description)
Expand Down
4 changes: 4 additions & 0 deletions server/models/video/video-playlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,10 @@ export class VideoPlaylistModel extends Model<VideoPlaylistModel> {
return join(STATIC_PATHS.THUMBNAILS, this.Thumbnail.filename)
}

getWatchUrl () {
return WEBSERVER.URL + '/videos/watch/playlist/' + this.uuid
}

setAsRefreshed () {
this.changed('updatedAt', true)

Expand Down

0 comments on commit c4dea59

Please sign in to comment.