DEV Community

Joe Holmes
Joe Holmes

Posted on

Templater Treats for Obsidian October

I wrote two small Templater scripts for Obsidian October*, both with their own Python micro-APIs. One transcribes YouTube videos and one fetches the day's quote from DailyZen. Neither are anywhere as complicated as the plugins people are submitting, but it feels good to participate all the same.

*Obsidian is a note-taking app that has lots of novel ways to link your notes together and visualize them. It's totally free, so if it sounds intriguing to you you should try it.

Daily Zen Quote in Your Journal

First comes the API, hosted on Replit and written in Python and Flask. I find Beautiful Soup, the Python library for web scraping, to be easier to work with than the web scrapers popular for JavaScript.

import requests
from bs4 import BeautifulSoup
from markdownify import markdownify as markdownify
from flask import Flask
from flask_cors import CORS
import re
import prettierfier

app = Flask("app")

def output():
  URL = ""
  page = requests.get(URL)
  soup = BeautifulSoup(page.content, "html.parser")
  html = f"{soup('blockquote')[0]}{soup('cite')[0]}"
  pretty_html = prettierfier.prettify_html(html)
  markdown = markdownify(pretty_html, convert=['blockquote', 'cite'])
  quote = re.sub(">", "", markdown)
  return f">  {quote.lstrip().rstrip()}""", port=8080)
Enter fullscreen mode Exit fullscreen mode

I pay a couple bucks a month to have the Replit hacker plan, which keeps certain servers "always on" without having to configure anything.

Since nothing needs to be passed into this one, it's pretty simple.

async function dailyzen() {
  const result = await fetch("");
  const reader = result.body.getReader();
  const { value } = await;
  const quote = new TextDecoder().decode(value);
  return quote;
module.exports = dailyzen;
Enter fullscreen mode Exit fullscreen mode

Replit sends stuff over in ReadableStream, so you usually need to import it into your file in bite-size chunks. The quotes are so short that they come in entirely on the first chunk.

Then in Templater just call it with tp.user.dailyzen(). Voila.


Fetch YouTube Transcripts in Your Notes

This one's a little trickier.
First, a Templater system prompt gets a YouTube URL. Then the URL is verified, we make sure it's actually a link to a video, and the ID of the video is sent to the server.

Behind the scenes, Replit sends a big ReadableStream back with the transcript in chunks. Those chunks are gathered up until the stream completes, then they're concatenated.

async function transcript(str) {
  let url = new URL(str);
  let videoID;
  //test and make sure it's a youtube URL
  if ( == "" && url.pathname == "/watch") {
    videoID =;
    //send videoID as POST request to replit
    const res = await fetch("", {
      method: "post",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      body: JSON.stringify({
        ID: videoID,
    //replit returns a readablestream
    const reader = res.body.getReader();
    let result = "";
    let transcriptOutput = "";
    await processText({ done, value }) {
      if (done) {
        transcriptOutput = result;
        return transcriptOutput;
      let stringValue = new TextDecoder("utf-8").decode(value);
      result += stringValue;
    return transcriptOutput;
  } else {
    return "I can't recognize this as a YouTube link, sorry!";
  //get the markdown transcript back and return it
module.exports = transcript;
Enter fullscreen mode Exit fullscreen mode

Behind the scenes, here's Replit getting us the transcript. There's a pip package available that makes it really easy:

from flask import Flask
from flask import request
from flask_cors import CORS
from youtube_transcript_api import YouTubeTranscriptApi

app = Flask("app")

@app.route("/", methods=["POST"])
def output():
  ytID = request.json.get("ID")
  transcriptData = YouTubeTranscriptApi.get_transcript(ytID)
  transcript = ""
  for dict in transcriptData:
    transcript += dict["text"] + " \n"
  return transcript"", port=8080)
Enter fullscreen mode Exit fullscreen mode

Notice how there's a line break at the end of every string returned by the transcript package.

There are several ways to do this, but I found it easiest to prompt the user for a URL within the Templater markdown file itself.

<% await tp.user.transcript(tp.system.prompt("YouTube URL Here"))%>.
youtube transcript

Try them out!

If you want to use my servers, help yourself! Just code the JavaScript portions of this tutorial. Of course, they may disappear at any time--these were some just for fun projects.

Top comments (0)