Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🎉 mp4 videos in articles #2731

Merged
merged 2 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.mp4",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: .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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was updating/adding the explorer, image--wide and toc layouts intentional in this PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, just alphabetised and removed some redundancies

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
Loading