Compare commits
5 Commits
f2e4ef64ee
...
cf28b65d16
Author | SHA1 | Date |
---|---|---|
Bauke | cf28b65d16 | |
Bauke | 2e6fddfac8 | |
Bauke | eb21f1b777 | |
Bauke | 235064a0ae | |
Bauke | 9581974eda |
|
@ -1,119 +1,7 @@
|
||||||
# Logs
|
.netlify/
|
||||||
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
|
|
||||||
node_modules/
|
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
|
Cargo.lock
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
|
||||||
**/*.rs.bk
|
|
||||||
|
|
||||||
# Build directory
|
|
||||||
public/
|
|
||||||
|
|
|
@ -16,8 +16,9 @@ path = "source/main.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
askama = "0.11.1"
|
askama = "0.11.1"
|
||||||
color-eyre = "0.6.2"
|
color-eyre = "0.6.2"
|
||||||
|
comrak = "0.15.0"
|
||||||
rsass = "0.23.4"
|
rsass = "0.23.4"
|
||||||
serde_json = "1.0.91"
|
toml-frontmatter = "0.1.0"
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"@bauke/eslint-config": "^0.1.2",
|
"@bauke/eslint-config": "^0.1.2",
|
||||||
"@bauke/prettier-config": "^0.1.2",
|
"@bauke/prettier-config": "^0.1.2",
|
||||||
"@bauke/stylelint-config": "^0.1.2",
|
"@bauke/stylelint-config": "^0.1.2",
|
||||||
|
"netlify-cli": "^12.5.0",
|
||||||
"stylelint": "^14.16.1",
|
"stylelint": "^14.16.1",
|
||||||
"xo": "^0.53.1"
|
"xo": "^0.53.1"
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,6 +8,7 @@ use color_eyre::{install, Result};
|
||||||
mod copy;
|
mod copy;
|
||||||
mod scss;
|
mod scss;
|
||||||
mod templates;
|
mod templates;
|
||||||
|
mod video;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
install()?;
|
install()?;
|
||||||
|
@ -20,6 +21,7 @@ fn main() -> Result<()> {
|
||||||
templates::Index::write(&public_dir)?;
|
templates::Index::write(&public_dir)?;
|
||||||
scss::Scss::write(&public_dir, &source_dir)?;
|
scss::Scss::write(&public_dir, &source_dir)?;
|
||||||
copy::Copy::write(&build_dir, &public_dir, &source_dir)?;
|
copy::Copy::write(&build_dir, &public_dir, &source_dir)?;
|
||||||
|
video::write_all(&public_dir)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ impl Scss {
|
||||||
create_dir_all(&css_dir)?;
|
create_dir_all(&css_dir)?;
|
||||||
|
|
||||||
let scss_dir = source_dir.join("scss");
|
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 {
|
let format = Format {
|
||||||
precision: 5,
|
precision: 5,
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 %}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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(())
|
||||||
|
}
|
Loading…
Reference in New Issue