Make episode HTML into file tasks for incremental builds
Each episode page rebuilds only when its transcript changes.
The pages task now aggregates episode HTML and generates the index.

Assisted-by: Claude Opus 4.6 via pi
change wllylpookvvvsutqwmolvrtrsoqqvpzv
commit a1fc78d244ab449101a98accdde3955cea9d7ce0
author Alpha Chen <alpha@kejadlen.dev>
date
parent unylnmwy
diff --git a/Rakefile b/Rakefile
index ebeb754..19536ae 100644
--- a/Rakefile
+++ b/Rakefile
@@ -81,6 +81,21 @@ EPISODE_TASKS.each do |et|
   file et.text_path => [et.transcript_path, CONFIG.text_dir.to_s] do
     CONFIG.transcriber.render(et.transcript_path, et.text_path)
   end
+
+  file et.html_path => [et.text_path, CONFIG.pages_dir.to_s] do
+    require "cgi"
+    require "erb"
+
+    template = ERB.new(File.read(File.expand_path("lib/pages/episode.html.erb", __dir__)))
+    ep = {
+      number: et.number,
+      title: et.episode.title.sub(/^Episode #{et.number}:\s+/, ""),
+      slug: et.slug,
+      audio_url: et.episode.audio_url,
+      text: File.read(et.text_path)
+    }
+    File.write(et.html_path, template.result_with_hash(ep:))
+  end
 end
 
 # --- Tasks ---
@@ -123,35 +138,25 @@ task :retranscribe, [:number] do |_t, args|
   Rake::Task[et.text_path].invoke
 end
 
+directory CONFIG.pages_dir.to_s
+
+EPISODE_HTML_TASKS = EPISODE_TASKS.select { |et| Pathname(et.text_path).exist? }
+
 desc "Generate HTML transcript pages"
-task :pages do
+task pages: EPISODE_HTML_TASKS.map(&:html_path) do
   require "cgi"
   require "erb"
 
-  templates_dir = File.expand_path("lib/pages", __dir__)
-  index_template = ERB.new(File.read("#{templates_dir}/index.html.erb"))
-  episode_template = ERB.new(File.read("#{templates_dir}/episode.html.erb"))
-
-  transcripts = EPISODE_TASKS
-    .select { |et| Pathname(et.text_path).exist? }
-    .map do |et|
-      {
-        number: et.number,
-        title: et.episode.title.sub(/^Episode #{et.number}:\s+/, ""),
-        slug: et.slug,
-        audio_url: et.episode.audio_url,
-        text: File.read(et.text_path)
-      }
-    end
-
-  CONFIG.pages_dir.mkpath
-
-  transcripts.each do |ep|
-    html = episode_template.result_with_hash(ep:)
-    (CONFIG.pages_dir / "#{ep[:slug]}.html").write(html)
+  template = ERB.new(File.read(File.expand_path("lib/pages/index.html.erb", __dir__)))
+  transcripts = EPISODE_HTML_TASKS.map do |et|
+    {
+      number: et.number,
+      title: et.episode.title.sub(/^Episode #{et.number}:\s+/, ""),
+      slug: et.slug,
+    }
   end
 
-  html = index_template.result_with_hash(transcripts:)
+  html = template.result_with_hash(transcripts:)
   (CONFIG.pages_dir / "index.html").write(html)
 
   puts "Generated #{transcripts.length} episode pages + index."
@@ -162,7 +167,16 @@ end
 
 desc "Index pages for search with Pagefind"
 task pagefind: :pages do
+  stamp = CONFIG.pages_dir / ".pagefind-stamp"
+  html_files = FileList["#{CONFIG.pages_dir}/*.html"]
+
+  if stamp.exist?
+    stamp_mtime = stamp.mtime
+    next if html_files.all? { |f| File.mtime(f) <= stamp_mtime }
+  end
+
   sh "uv", "run", "--with", "pagefind[bin]", "python3", "-m", "pagefind", "--site", CONFIG.pages_dir.to_s
+  FileUtils.touch(stamp.to_s)
 end
 
 desc "Serve the generated pages locally"
diff --git a/lib/episode_task.rb b/lib/episode_task.rb
index a547c2c..3c9891a 100644
--- a/lib/episode_task.rb
+++ b/lib/episode_task.rb
@@ -5,5 +5,6 @@ module CookingIssues
     def audio_path = (config.audio_dir / "#{slug}.mp3").to_s
     def transcript_path = (config.transcriber_cache_dir / "#{slug}.json").to_s
     def text_path = (config.text_dir / "#{slug}.txt").to_s
+    def html_path = (config.pages_dir / "#{slug}.html").to_s
   end
 end