Add generation for video pages.
This commit is contained in:
		
							parent
							
								
									235064a0ae
								
							
						
					
					
						commit
						eb21f1b777
					
				| 
						 | 
				
			
			@ -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(())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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