1
Fork 0

Compare commits

..

5 Commits

Author SHA1 Message Date
Bauke cf28b65d16
Add netlify-cli dependency. 2023-01-07 16:35:04 +01:00
Bauke 2e6fddfac8
Simplify gitignore. 2023-01-07 16:34:50 +01:00
Bauke eb21f1b777
Add generation for video pages. 2023-01-07 14:58:05 +01:00
Bauke 235064a0ae
Add comrak dependency. 2023-01-07 13:37:00 +01:00
Bauke 9581974eda
Replace serde_json with toml-frontmatter. 2023-01-07 13:09:16 +01:00
9 changed files with 224 additions and 117 deletions

118
.gitignore vendored
View File

@ -1,119 +1,7 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
.netlify/
node_modules/
jspm_packages/
public/
target/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
pnpm-lock.yaml
# These are backup files generated by rustfmt
**/*.rs.bk
# Build directory
public/

View File

@ -16,8 +16,9 @@ path = "source/main.rs"
[dependencies]
askama = "0.11.1"
color-eyre = "0.6.2"
comrak = "0.15.0"
rsass = "0.23.4"
serde_json = "1.0.91"
toml-frontmatter = "0.1.0"
[dependencies.serde]
features = ["derive"]

View File

@ -12,6 +12,7 @@
"@bauke/eslint-config": "^0.1.2",
"@bauke/prettier-config": "^0.1.2",
"@bauke/stylelint-config": "^0.1.2",
"netlify-cli": "^12.5.0",
"stylelint": "^14.16.1",
"xo": "^0.53.1"
},

View File

@ -8,6 +8,7 @@ use color_eyre::{install, Result};
mod copy;
mod scss;
mod templates;
mod video;
fn main() -> Result<()> {
install()?;
@ -20,6 +21,7 @@ fn main() -> Result<()> {
templates::Index::write(&public_dir)?;
scss::Scss::write(&public_dir, &source_dir)?;
copy::Copy::write(&build_dir, &public_dir, &source_dir)?;
video::write_all(&public_dir)?;
Ok(())
}

View File

@ -17,7 +17,7 @@ impl Scss {
create_dir_all(&css_dir)?;
let scss_dir = source_dir.join("scss");
let scss_filenames = vec!["index", "modern-normalize"];
let scss_filenames = vec!["index", "modern-normalize", "video"];
let format = Format {
precision: 5,

63
source/scss/video.scss Normal file
View File

@ -0,0 +1,63 @@
@use "mixins";
@use "reset";
body {
// Colors.
--foreground: #f1ebff;
--background: #1f003e;
--anchor: #89c3ff;
// Spacing constants.
--spacing-small: 4px;
--spacing-medium: 8px;
--spacing-large: 16px;
background-color: var(--background);
color: var(--foreground);
font-size: 1rem;
}
a {
color: var(--anchor);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
ul {
margin-left: 1rem;
}
iframe,
h2 {
margin-top: var(--spacing-large);
}
iframe {
aspect-ratio: 16 / 9;
border: 2px dashed;
display: block;
width: 100%;
margin-bottom: var(--spacing-large);
max-width: 1280px;
padding: var(--spacing-medium);
}
.page-header {
border-bottom: 2px dashed;
padding: var(--spacing-large);
h1 {
@include mixins.responsive-container;
}
}
.page-main {
@include mixins.responsive-container;
}
.timestamp-link {
font-family: monospace;
}

View File

@ -0,0 +1,64 @@
{% extends "base.html" %}
{% block head %}
<link rel="stylesheet" href="/css/video.css">
{% endblock %}
{% block body %}
<header class="page-header">
<h1>{{ page_title }}</h1>
</header>
<main class="page-main">
{{ rendered_markdown|safe }}
{% if let Some(speedrun) = speedrun %}
<h2>Speedrun</h2>
<ul>
<li>
<a href="{{ speedrun.entry }}" target="_blank">Entry</a>
</li>
<li>
<a href="{{ speedrun.leaderboard }}" target="_blank">Leaderboard</a>
</li>
</ul>
{% if let Some(mods) = speedrun.mods %}
<h2>Mods</h2>
<ul>
{% for (name, id) in mods %}
<li>
<a href="https://drg.mod.io/{{ id }}" target="_blank">{{ name }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
{% if let Some(chapters) = speedrun.chapters %}
<h2>Chapters</h2>
<ul>
{% for (timestamp, text) in chapters %}
<li>
<p>
<a
class="timestamp-link"
href="https://youtu.be/{{ video_id }}?t={{ timestamp|timestamp_to_seconds }}"
target="_blank"
>{{ timestamp }}</a>
{{ text }}
</p>
</li>
{% endfor %}
</ul>
{% endif %}
<iframe
src="https://www.youtube-nocookie.com/embed/{{ video_id }}"
title="Embedded YouTube video player"
frameborder="0"
allowfullscreen>
</iframe>
{% endif %}
</main>
{% endblock %}

19
source/video/filters.rs Normal file
View File

@ -0,0 +1,19 @@
/*!
Filters for Askama templates.
*/
/**
Turn a timestamp with format `mm:ss` into its total seconds.
## Examples
- `00:30` -> 30 seconds
- `01:00` -> 60 seconds
- `01:30` -> 90 seconds
*/
pub fn timestamp_to_seconds(timestamp: &str) -> askama::Result<i32> {
let mut split = timestamp.split(":");
let minutes = split.next().map(str::parse::<i32>).unwrap().unwrap();
let seconds = split.next().map(str::parse::<i32>).unwrap().unwrap();
Ok(minutes * 60 + seconds)
}

69
source/video/mod.rs Normal file
View File

@ -0,0 +1,69 @@
use std::{fs, path::Path};
use {askama::Template, color_eyre::Result, serde::Deserialize};
mod filters;
#[derive(Debug, Template)]
#[template(path = "video.html")]
pub struct VideoTemplate {
pub page_title: String,
pub rendered_markdown: String,
pub speedrun: Option<SpeedrunData>,
pub video_id: String,
}
#[derive(Debug, Deserialize)]
struct VideoData {
pub id: String,
pub page_title: String,
pub speedrun: Option<SpeedrunData>,
}
#[derive(Debug, Deserialize)]
pub struct SpeedrunData {
pub chapters: Option<Vec<(String, String)>>,
pub entry: String,
pub leaderboard: String,
pub mods: Option<Vec<(String, String)>>,
}
pub fn write_all(public_dir: &Path) -> Result<()> {
let video_datas = {
let mut data = vec![];
for dir in ["2022"] {
for file in fs::read_dir(format!("source/video/{dir}"))? {
let file_path = file?.path();
if file_path.extension().unwrap() != "md" {
continue;
}
let file_contents = fs::read_to_string(file_path)?;
let (video_data, markdown) =
toml_frontmatter::parse::<VideoData>(&file_contents).unwrap();
data.push((video_data, markdown.to_string()));
}
}
data
};
for (video_data, markdown) in video_datas {
let video_dir = public_dir.join("v").join(&video_data.id);
fs::create_dir_all(&video_dir)?;
let template = VideoTemplate {
page_title: video_data.page_title,
rendered_markdown: comrak::markdown_to_html(
&markdown,
&Default::default(),
),
speedrun: video_data.speedrun,
video_id: video_data.id,
};
fs::write(video_dir.join("index.html"), template.render()?)?;
}
Ok(())
}