Skip to content

Commit

Permalink
Merge pull request #2731 from owid/videos
Browse files Browse the repository at this point in the history
🎉 mp4 videos in articles
  • Loading branch information
ikesau authored Oct 11, 2023
2 parents aae01d0 + 6aaaa13 commit d867c3c
Show file tree
Hide file tree
Showing 11 changed files with 185 additions and 4 deletions.
10 changes: 10 additions & 0 deletions db/model/Gdoc/Gdoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,16 @@ export class Gdoc extends BaseEntity implements OwidGdocInterface {
)
}
)
.with({ type: "video" }, (video) => {
return [
Link.createFromUrl({
url: video.url,
source: this,
componentType: video.type,
text: spansToSimpleString(video.caption || []),
}),
]
})
.with(
{
// no urls directly on any of these components
Expand Down
13 changes: 13 additions & 0 deletions db/model/Gdoc/enrichedToRaw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
RawBlockResearchAndWritingLink,
RawBlockAlign,
RawBlockEntrySummary,
RawBlockVideo,
} from "@ourworldindata/utils"
import { spanToHtmlString } from "./gdocUtils.js"
import { match, P } from "ts-pattern"
Expand Down Expand Up @@ -148,6 +149,18 @@ export function enrichedBlockToRawBlock(
},
})
)
.with(
{ type: "video" },
(b): RawBlockVideo => ({
type: b.type,
value: {
url: b.url,
filename: b.filename,
caption: b.caption ? spansToHtmlText(b.caption) : undefined,
shouldLoop: String(b.shouldLoop),
},
})
)
.with(
{ type: "list" },
(b): RawBlockList => ({
Expand Down
8 changes: 8 additions & 0 deletions db/model/Gdoc/exampleEnrichedBlocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ export const enrichedBlockExamples: Record<
size: BlockImageSize.Wide,
parseErrors: [],
},
video: {
type: "video",
url: "https://ourworldindata.org/assets/videos/example.mp4",
filename: "https://ourworldindata.org/assets/images/example-poster.jpg",
caption: boldLinkExampleText,
shouldLoop: true,
parseErrors: [],
},
list: {
type: "list",
items: [enrichedBlockText],
Expand Down
13 changes: 13 additions & 0 deletions db/model/Gdoc/rawToArchie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
RawBlockHorizontalRule,
RawBlockHtml,
RawBlockImage,
RawBlockVideo,
RawBlockList,
RawBlockNumberedList,
RawBlockPosition,
Expand Down Expand Up @@ -164,6 +165,17 @@ function* rawBlockImageToArchieMLString(
yield "{}"
}

function* rawBlockVideoToArchieMLString(
block: RawBlockVideo
): Generator<string, void, undefined> {
yield "{.video}"
yield* propertyToArchieMLString("url", block.value)
yield* propertyToArchieMLString("filename", block.value)
yield* propertyToArchieMLString("shouldLoop", block.value)
yield* propertyToArchieMLString("caption", block.value)
yield "{}"
}

function* listToArchieMLString(
items: string[] | string,
blockName: string
Expand Down Expand Up @@ -566,6 +578,7 @@ export function* OwidRawGdocBlockToArchieMLStringGenerator(
.with({ type: "callout" }, rawBlockCalloutToArchieMLString)
.with({ type: "chart-story" }, rawBlockChartStoryToArchieMLString)
.with({ type: "image" }, rawBlockImageToArchieMLString)
.with({ type: "video" }, rawBlockVideoToArchieMLString)
.with({ type: "list" }, rawBlockListToArchieMLString)
.with({ type: "numbered-list" }, rawBlockNumberedListToArchieMLString)
.with({ type: "pull-quote" }, rawBlockPullQuoteToArchieMLString)
Expand Down
60 changes: 60 additions & 0 deletions db/model/Gdoc/rawToEnriched.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
EnrichedBlockHorizontalRule,
EnrichedBlockHtml,
EnrichedBlockImage,
EnrichedBlockVideo,
EnrichedBlockKeyInsights,
EnrichedBlockList,
EnrichedBlockNumberedList,
Expand Down Expand Up @@ -47,6 +48,7 @@ import {
RawBlockHeading,
RawBlockHtml,
RawBlockImage,
RawBlockVideo,
RawBlockKeyInsights,
RawBlockList,
RawBlockNumberedList,
Expand Down Expand Up @@ -125,6 +127,7 @@ export function parseRawBlocksToEnrichedBlocks(
.with({ type: "scroller" }, parseScroller)
.with({ type: "chart-story" }, parseChartStory)
.with({ type: "image" }, parseImage)
.with({ type: "video" }, parseVideo)
.with({ type: "list" }, parseList)
.with({ type: "numbered-list" }, parseNumberedList)
.with({ type: "pull-quote" }, parsePullQuote)
Expand Down Expand Up @@ -548,6 +551,63 @@ const parseImage = (image: RawBlockImage): EnrichedBlockImage => {
}
}

const parseVideo = (raw: RawBlockVideo): EnrichedBlockVideo => {
const createError = (
error: ParseError,
url: string = "",
filename: string = "",
shouldLoop: boolean = false,
caption?: Span[]
): EnrichedBlockVideo => ({
type: "video",
url,
filename,
shouldLoop,
caption,
parseErrors: [error],
})

const url = raw.value.url
if (!url) {
return createError({
message: "url property is missing or empty",
})
}

if (!url.endsWith(".mp4")) {
return createError({
message: "video must have a .mp4 file extension",
})
}

const filename = raw.value.filename
if (!filename) {
return createError({
message: "filename property is missing or empty",
})
}

const shouldLoop = raw.value.shouldLoop
if (!!shouldLoop && shouldLoop !== "true" && shouldLoop !== "false") {
return createError({
message: "If specified, shouldLoop property must be true or false",
})
}

const caption = raw.value.caption
? htmlToSpans(raw.value.caption)
: undefined

return {
type: "video",
url,
filename,
caption,
shouldLoop: shouldLoop === "true",
parseErrors: [],
}
}

const parseNumberedList = (
raw: RawBlockNumberedList
): EnrichedBlockNumberedList => {
Expand Down
1 change: 1 addition & 0 deletions packages/@ourworldindata/utils/src/Util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1610,6 +1610,7 @@ export function traverseEnrichedBlocks(
"horizontal-rule",
"html",
"image",
"video",
"missing-data",
"prominent-link",
"pull-quote",
Expand Down
2 changes: 2 additions & 0 deletions packages/@ourworldindata/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export {
type EnrichedBlockHorizontalRule,
type EnrichedBlockHtml,
type EnrichedBlockImage,
type EnrichedBlockVideo,
type EnrichedBlockKeyInsights,
type EnrichedBlockKeyInsightsSlide,
type EnrichedBlockList,
Expand Down Expand Up @@ -132,6 +133,7 @@ export {
type RawBlockHorizontalRule,
type RawBlockHtml,
type RawBlockImage,
type RawBlockVideo,
type RawBlockKeyInsights,
type RawBlockList,
type RawBlockMissingData,
Expand Down
20 changes: 20 additions & 0 deletions packages/@ourworldindata/utils/src/owidTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,24 @@ export type EnrichedBlockImage = {
size: BlockImageSize
} & EnrichedBlockWithParseErrors

export type RawBlockVideo = {
type: "video"
value: {
url?: string
caption?: string
shouldLoop?: string
filename?: string
}
}

export type EnrichedBlockVideo = {
type: "video"
url: string
shouldLoop: boolean
filename: string
caption?: Span[]
} & EnrichedBlockWithParseErrors

// TODO: This is what lists staring with * are converted to in archieToEnriched
// It might also be what is used inside recirc elements but there it's not a simple
// string IIRC - check this
Expand Down Expand Up @@ -1107,6 +1125,7 @@ export type OwidRawGdocBlock =
| RawBlockScroller
| RawBlockChartStory
| RawBlockImage
| RawBlockVideo
| RawBlockList
| RawBlockPullQuote
| RawBlockRecirc
Expand Down Expand Up @@ -1142,6 +1161,7 @@ export type OwidEnrichedGdocBlock =
| EnrichedBlockScroller
| EnrichedBlockChartStory
| EnrichedBlockImage
| EnrichedBlockVideo
| EnrichedBlockList
| EnrichedBlockPullQuote
| EnrichedBlockRecirc
Expand Down
19 changes: 15 additions & 4 deletions site/gdocs/ArticleBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { TopicPageIntro } from "./TopicPageIntro.js"
import { KeyInsights } from "./KeyInsights.js"
import { ResearchAndWriting } from "./ResearchAndWriting.js"
import { AllCharts } from "./AllCharts.js"
import Video from "./Video.js"

export type Container =
| "default"
Expand All @@ -57,15 +58,15 @@ const layouts: { [key in Container]: Layouts} = {
["aside-right"]: "col-start-11 span-cols-3 span-md-cols-10 col-md-start-3",
["chart-story"]: "col-start-4 span-cols-8 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2",
["chart"]: "col-start-4 span-cols-8 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2",
["explorer"]: "col-start-2 span-cols-12 span-md-cols-12 col-md-start-2",
["default"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2",
["divider"]: "col-start-2 span-cols-12",
["explorer"]: "col-start-2 span-cols-12 span-md-cols-12 col-md-start-2",
["gray-section"]: "span-cols-14 grid grid-cols-12-full-width",
["heading"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2",
["horizontal-rule"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2",
["html"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2",
["image--narrow"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 col-sm-start-2 span-sm-cols-12 ",
["image--wide"]: "col-start-4 span-cols-8 col-md-start-2 span-md-cols-12 col-sm-start-2 span-sm-cols-12 ",
["image--narrow"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 col-sm-start-2 span-sm-cols-12",
["image--wide"]: "col-start-4 span-cols-8 col-md-start-2 span-md-cols-12",
["image-caption"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2",
["key-insights"]: "col-start-2 span-cols-12",
["list"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2",
Expand All @@ -76,7 +77,6 @@ const layouts: { [key in Container]: Layouts} = {
["research-and-writing"]: "col-start-2 span-cols-12",
["scroller"]: "grid span-cols-12 col-start-2",
["sdg-grid"]: "grid col-start-2 span-cols-12 col-lg-start-3 span-lg-cols-10 span-sm-cols-12 col-sm-start-2",
["toc"]: "grid grid-cols-8 col-start-4 span-cols-8 grid-md-cols-10 col-md-start-3 span-md-cols-10 grid-sm-cols-12 span-sm-cols-12 col-sm-start-2",
["side-by-side"]: "grid span-cols-12 col-start-2",
["sticky-left-left-column"]: "grid grid-cols-7 span-cols-7 span-md-cols-10 grid-md-cols-10",
["sticky-left-right-column"]: "grid grid-cols-5 span-cols-5 span-md-cols-10 grid-md-cols-10",
Expand All @@ -85,7 +85,9 @@ const layouts: { [key in Container]: Layouts} = {
["sticky-right-right-column"]: "span-cols-7 grid-cols-7 span-md-cols-10 grid-md-cols-10 col-md-start-2 span-sm-cols-12 grid-sm-cols-12 col-sm-start-1",
["sticky-right"]: "grid span-cols-12 col-start-2",
["text"]: "col-start-5 span-cols-6 col-md-start-3 span-md-cols-10 span-sm-cols-12 col-sm-start-2",
["toc"]: "grid grid-cols-8 col-start-4 span-cols-8 grid-md-cols-10 col-md-start-3 span-md-cols-10 grid-sm-cols-12 span-sm-cols-12 col-sm-start-2",
["topic-page-intro"]: "grid col-start-2 span-cols-12",
["video"]: "col-start-4 span-cols-8 col-md-start-2 span-md-cols-12",
},
["datapage"]: {
["default"]: "col-start-2 span-cols-6 col-lg-start-2 span-lg-cols-7 col-md-start-2 span-md-cols-10 col-sm-start-1 span-sm-cols-12",
Expand Down Expand Up @@ -233,6 +235,15 @@ export default function ArticleBlock({
) : null}
</figure>
))
.with({ type: "video" }, (block) => (
<Video
className={getLayout("video", containerType)}
url={block.url}
shouldLoop={block.shouldLoop}
caption={block.caption}
filename={block.filename}
/>
))
.with({ type: "pull-quote" }, (block) => (
<PullQuote
className={getLayout("pull-quote", containerType)}
Expand Down
33 changes: 33 additions & 0 deletions site/gdocs/Video.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { useContext } from "react"
import cx from "classnames"
import { IMAGES_DIRECTORY, Span } from "@ourworldindata/utils"
import { renderSpans } from "./utils.js"
import { DocumentContext } from "./OwidGdoc.js"
import {
IMAGE_HOSTING_BUCKET_SUBFOLDER_PATH,
IMAGE_HOSTING_CDN_URL,
} from "../../settings/clientSettings.js"

interface VideoProps {
url: string
caption?: Span[]
className?: string
shouldLoop?: boolean
filename: string
}

export default function Video(props: VideoProps) {
const { url, caption, className, shouldLoop, filename } = props
const { isPreviewing } = useContext(DocumentContext)
const posterSrc = isPreviewing
? `${IMAGE_HOSTING_CDN_URL}/${IMAGE_HOSTING_BUCKET_SUBFOLDER_PATH}/${filename}`
: `${IMAGES_DIRECTORY}${filename}`
return (
<figure className={cx(className)}>
<video controls preload="none" loop={shouldLoop} poster={posterSrc}>
<source src={url} type="video/mp4" />
</video>
{caption ? <figcaption>{renderSpans(caption)}</figcaption> : null}
</figure>
)
}
10 changes: 10 additions & 0 deletions site/gdocs/centered-article.scss
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ h3.article-block__heading {
}
}

.article-block__video {
margin: 24px 0 32px 0;
a {
@include owid-link-90;
}
video {
width: 100%;
}
}

.article-block__sticky-left,
.article-block__sticky-right {
@include md-up {
Expand Down

0 comments on commit d867c3c

Please sign in to comment.