From 152f4596c45d61cf0dbe40cf199db829e2079bcd Mon Sep 17 00:00:00 2001 From: PedroBailaAndrade Date: Thu, 11 Jan 2024 15:48:45 +0000 Subject: [PATCH] Add video to sitemap --- .github/workflows/website.yml | 6 ++ workspaces/cms-scripts/src/sitemap.ts | 91 ++++++++++++++++--- workspaces/cms-scripts/src/utils.ts | 34 +++++++ workspaces/cms-scripts/tsconfig.json | 2 +- .../src/components/VideoPlayer/constants.ts | 17 +++- 5 files changed, 131 insertions(+), 19 deletions(-) diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index fb371bf4b2..c319d666ca 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -45,6 +45,12 @@ jobs: - name: Update dynamic data run: yarn workspace @starknet-io/cms-scripts update-dynamic-data + env: + VITE_CF_STREAM_URL: ${{ secrets.CF_STREAM_URL }} + VITE_ED_VIDEO_ID_1: ${{ secrets.VITE_ED_VIDEO_ID_1 }} + VITE_ED_VIDEO_ID_2: ${{ secrets.VITE_ED_VIDEO_ID_2 }} + VITE_ED_VIDEO_ID_3: ${{ secrets.VITE_ED_VIDEO_ID_3 }} + VITE_ED_VIDEO_ID_4: ${{ secrets.VITE_ED_VIDEO_ID_4 }} - name: Inject slug/short variables uses: rlespinasse/github-slug-action@v4 diff --git a/workspaces/cms-scripts/src/sitemap.ts b/workspaces/cms-scripts/src/sitemap.ts index 3399720302..b0fe874d50 100644 --- a/workspaces/cms-scripts/src/sitemap.ts +++ b/workspaces/cms-scripts/src/sitemap.ts @@ -1,3 +1,5 @@ +/// + /** * Module dependencies. */ @@ -7,12 +9,14 @@ import path from 'path'; /** * Change directory to project root. - */ +*/ process.chdir(path.resolve(__dirname, '../../..')); import { locales } from '@starknet-io/cms-data/src/i18n/config'; import { getPosts } from './data'; +import { playlist } from '../../website/src/components/VideoPlayer/constants'; +import { escapeXml, formatDate } from './utils'; /** * Config constants. @@ -23,6 +27,18 @@ const changefreqByDepth = ['daily', 'weekly', 'monthly', 'yearly']; const priorityByDepth = [1, 0.8, 0.6, 0.4]; const customPages = ['announcements', 'events', 'jobs', 'posts', 'roadmap', 'tutorials']; +/** + * `Video` type. + */ + +type Video = { + thumbnail?: string; + title?: string; + description?: string; + publishedAt?: string; + url?: string; +} + /** * `SitemapUrl` type. */ @@ -32,6 +48,7 @@ type SitemapUrl = { lastmod?: string; priority: number; url: string; + videos?: Video[] }; /** @@ -74,11 +91,24 @@ const parsePageDirectory = async (dir: string, depth = 0) => { const { hidden_page, link } = JSON.parse(page); if (!hidden_page && link) { - sitemapUrls.push({ + let sitemapEntry: SitemapUrl = { url: link, changefreq: changefreqByDepth[depth], priority: priorityByDepth[depth] - }); + } + + if (link === '/en/learn/what-is-starknet') { + const videos: Video[] = playlist.map(chapter => ({ + thumbnail: `${domain}${chapter?.thumbnail}`, + title: chapter?.title, + description: chapter?.description, + url: `${import.meta.env.VITE_CF_STREAM_URL}/${chapter?.videoId}/watch` + })); + + sitemapEntry.videos = videos; + } + + sitemapUrls.push(sitemapEntry); } } catch { console.error('Error parsing page', file); @@ -95,7 +125,7 @@ const parsePosts = async () => { const { filenameMap } = await getPosts(); const categories: string[] = []; - filenameMap.forEach(({ locale, category: filenameCategory, slug, published_date }) => { + filenameMap.forEach(({ locale, category: filenameCategory, slug, published_date, post_type, video }) => { const category = filenameCategory === 'engineering' ? 'developers' : filenameCategory; if (!categories.includes(category)) { @@ -104,16 +134,27 @@ const parsePosts = async () => { sitemapUrls.push({ url: `/${locale}/posts/${category}`, changefreq: 'weekly', - priority: 0.8 + priority: 0.8 }); } - sitemapUrls.push({ + let sitemapEntry: SitemapUrl = { url: `/${locale}/posts/${category}/${slug}`, changefreq: 'monthly', priority: 0.6, - lastmod: published_date?.split('T')?.[0] - }); + lastmod: published_date?.split('T')?.[0], + ...(post_type === 'video' && video && { + videos: [{ + thumbnail: video?.data?.snippet?.thumbnails?.default?.url, + title: video?.data?.snippet?.title, + description: video?.data?.snippet?.description, + publishedAt: video?.data?.snippet?.publishedAt, + url: video?.url + }] + }) + } + + sitemapUrls.push(sitemapEntry); }); }; @@ -130,7 +171,14 @@ const parseTutorialsFile = async (locale: string, filename: string) => { sitemapUrls.push({ url: `/${locale}/tutorials/video/${tutorial.id}`, changefreq: 'monthly', - priority: 0.6 + priority: 0.6, + videos: [{ + thumbnail: `${domain}${tutorial?.image}`, + title: tutorial?.title, + description: tutorial?.description, + publishedAt: tutorial?.published_at, + url: tutorial?.url + }] }); } } catch { @@ -188,18 +236,33 @@ await parseDetails('roadmap'); await parseTutorials(); let sitemap = ` -`; - -sitemapUrls.forEach(({ url, changefreq, priority, lastmod }) => { +`; + +sitemapUrls.forEach(({ url, changefreq, priority, lastmod, videos }) => { sitemap += ` ${domain}${url} ${changefreq} ${priority}${lastmod ? ` - ${lastmod}` : ''} + ${lastmod}` : ''}`; + + videos?.forEach(video => { + if (video.thumbnail && video.title && video.description && video.url) { + sitemap += ` + + ${escapeXml(video.thumbnail)} + ${escapeXml(video.title)} + ${escapeXml(video.description)}${video.publishedAt ? ` + ${formatDate(video.publishedAt)}` : ''} + ${escapeXml(video.url)} + `; + } + }); + + sitemap += ` `; }); - sitemap += ` `; diff --git a/workspaces/cms-scripts/src/utils.ts b/workspaces/cms-scripts/src/utils.ts index 802ca753a2..a1cbec6fc7 100644 --- a/workspaces/cms-scripts/src/utils.ts +++ b/workspaces/cms-scripts/src/utils.ts @@ -1,5 +1,6 @@ import fs from "fs/promises"; import YAML from "yaml"; +import { format } from 'date-fns'; export async function json(filepath: string): Promise { return JSON.parse(await fs.readFile(filepath, { encoding: "utf8" })); @@ -46,3 +47,36 @@ export async function getFirst(...fns: Array<() => Promise>): Promise { cause, }); } + +/** + * Escape xml function. + */ + +export const escapeXml = (string: string) => { + return string.replace(/[<>&'"]/g, (char) => { + switch (char) { + case '<': return '<'; + case '>': return '>'; + case '&': return '&'; + case '\'': return '''; + case '"': return '"'; + default: return char; + } + }); +} + +/** + * Format date function. + */ + +export const formatDate = (date: string) => { + const newDate = new Date(date); + const offset = -newDate.getTimezoneOffset(); + const offsetSign = offset >= 0 ? '+' : '-'; + const offsetHours = String(Math.floor(Math.abs(offset) / 60)).padStart(2, '0'); + const offsetMinutes = String(Math.abs(offset) % 60).padStart(2, '0'); + + const formatedDate = format(newDate, "yyyy-MM-dd'T'HH:mm:ss") + `${offsetSign}${offsetHours}:${offsetMinutes}`; + + return formatedDate; +}; diff --git a/workspaces/cms-scripts/tsconfig.json b/workspaces/cms-scripts/tsconfig.json index 647092f9c1..6f58d031dc 100644 --- a/workspaces/cms-scripts/tsconfig.json +++ b/workspaces/cms-scripts/tsconfig.json @@ -4,5 +4,5 @@ "outDir": ".types" }, "include": ["src/**/*"], - "references": [{"path": "../cms-utils"}, {"path": "../cms-config"}, {"path": "../cms-data"}] + "references": [{"path": "../cms-utils"}, {"path": "../cms-config"}, {"path": "../cms-data"}, {"path": "../website"}] } diff --git a/workspaces/website/src/components/VideoPlayer/constants.ts b/workspaces/website/src/components/VideoPlayer/constants.ts index ad5d42c8af..fd1812ada9 100644 --- a/workspaces/website/src/components/VideoPlayer/constants.ts +++ b/workspaces/website/src/components/VideoPlayer/constants.ts @@ -6,6 +6,7 @@ export type Chapter = { title?: string; videoId: string; durationTime: string; + description: string; }; export const playlist: Chapter[] = [ @@ -15,7 +16,9 @@ export const playlist: Chapter[] = [ poster: "/assets/video/chapter1.png", thumbnail: "/assets/video/chapter1.png", videoId: import.meta.env.VITE_ED_VIDEO_ID_1, - durationTime: "02:42" + durationTime: "02:42", + title: "How Starknet Scales Ethereum", + description: "Discover how Ethereum tackles the scalability challenge while preserving security and decentralization. Dive into the world of blockchain and learn about the Starknet Validity Rollup—a game-changing solution for Ethereum's growth. Starknet's approach shifts transaction processing off the Ethereum Mainnet (off-chain), maintaining transaction summaries on-chain. Transactions are batched, processed efficiently, and summarized into a single on-chain transaction. Explore how STARK proofs ensure transaction integrity without costly re-execution." }, { id: "sequencer", @@ -23,7 +26,9 @@ export const playlist: Chapter[] = [ poster: "/assets/video/chapter2.png", thumbnail: "/assets/video/chapter2.png", videoId: import.meta.env.VITE_ED_VIDEO_ID_2, - durationTime: "02:24" + durationTime: "02:24", + title: "The Starknet Sequencer", + description: "Get acquainted with the Starknet Sequencer, the cornerstone of Starknet's architecture. Sequencers play a vital role in validating and executing transactions, shaping the future of blockchain technology. They efficiently group transactions, ensuring only successful ones proceed, significantly boosting transaction throughput compared to Ethereum nodes." }, { id: "prover", @@ -31,7 +36,9 @@ export const playlist: Chapter[] = [ poster: "/assets/video/chapter3.png", thumbnail: "/assets/video/chapter3.png", videoId: import.meta.env.VITE_ED_VIDEO_ID_3, - durationTime: "02:26" + durationTime: "02:26", + title: "The Starknet Prover", + description: "The Starknet Prover, a key player in ensuring the integrity of blockchain transactions. Dive into the world of mathematical validation as the Prover generates STARK proofs for block transactions. Blocks are processed efficiently in groups, with the Prover meticulously documenting each step in the Execution Trace and tracking changes in the system's state, known as the State Diff. Discover how the Prover's algorithm scrutinizes and safeguards data integrity, making any issues unmistakable. Random samples from the expanded dataset are selected to create a powerful STARK proof, validating thousands of transactions seamlessly." }, { id: "eth-settlement", @@ -39,7 +46,9 @@ export const playlist: Chapter[] = [ poster: "/assets/video/chapter4.png", thumbnail: "/assets/video/chapter4.png", videoId: import.meta.env.VITE_ED_VIDEO_ID_4, - durationTime: "03:28" + durationTime: "03:28", + title: "Secure Settlement on Ethereum", + description: "Explore the secure settlement process on Ethereum through the lens of STARK technology. Witness how the STARK proof and State Diff are seamlessly transmitted as a transaction to Ethereum, where the magic unfolds. Ethereum's Verifier and Starknet Core smart contracts take center stage. The Verifier meticulously dissects the proof and examines its samples, swiftly rejecting any hint of problematic data. Once the proof is validated, it journeys to the Starknet Core smart contract. The Core contract authenticates the proof, acknowledges the State Diff, and updates the Starknet state on the Ethereum blockchain. This updated state becomes part of an Ethereum block, undergoing validation and voting across the node network. Once it garners enough votes, it attains \"finalized\" status, becoming an immutable part of Ethereum's history." }, ];