Skip to content

Instantly share code, notes, and snippets.

@ttscoff
Last active October 18, 2025 16:37
Show Gist options
  • Save ttscoff/f048fbe18a59d9208d46f20cc829345a to your computer and use it in GitHub Desktop.
Save ttscoff/f048fbe18a59d9208d46f20cc829345a to your computer and use it in GitHub Desktop.
YouTube -> Markdown using BrettTerpstra.com/yt-md/api
#!/usr/bin/env ruby
# Simple CLI wrapper that queries https://brettterpstra.com/yt-md/api
# Usage: yt-md.rb [--title "Custom Title"] <YouTube URL or ID>
require 'optparse'
require 'net/http'
require 'uri'
require 'cgi'
options = {}
opt = OptionParser.new do |o|
o.banner = "Usage: #{File.basename($0)} [options] URL_or_ID"
o.on('-t', '--title TITLE', 'Override the scraped title') { |v| options[:title] = v }
o.on('--thumb SIZE', 'Thumbnail thumb: 0,1,2,3,hq,sd,max,maxres, or filename') { |v| options[:thumb] = v }
o.on('-h', '--help', 'Show this help') { puts o; exit }
o.on('-o', '--output TYPE', 'Output type: markdown (default), json, html') { |v| options[:output] = v }
# caption flag: true by default; provide --no-caption to omit caption
options[:caption] = true
o.on('--no-caption', 'Omit caption from HTML output') { options[:caption] = false }
end
begin
opt.parse!(ARGV)
rescue StandardError => e
$stderr.puts "Error parsing options: #{e.message}"
$stderr.puts opt
exit 1
end
arg = ARGV.shift
if arg.nil? || arg.strip.empty?
$stderr.puts opt
exit 1
end
base = 'https://brettterpstra.com/yt-md/api'
params = {}
# If it looks like a URL (contains a slash or youtube host), send as url; otherwise use id
if arg.include?('/') || arg =~ /youtu(?:\.be|be\.com)/i
params['url'] = arg
else
params['id'] = arg
end
params['title'] = options[:title] if options[:title]
params['caption'] = options.key?(:caption) ? (options[:caption] ? '1' : '0') : '1'
params['thumb'] = options[:thumb] if options[:thumb]
# Decide which output to request. Default to markdown when no --output is provided.
desired = (options[:output] || 'markdown').to_s.downcase
desired = 'markdown' if desired == 'md'
params['output'] = desired
query = URI.encode_www_form(params)
uri = URI.parse("#{base}?#{query}")
begin
res = Net::HTTP.get_response(uri)
if res.is_a?(Net::HTTPSuccess)
body = res.body
# If JSON requested, pretty-print; otherwise print the body directly (API returns the requested format)
if params['output'] == 'json'
begin
parsed = JSON.parse(body)
puts JSON.pretty_generate(parsed)
rescue StandardError
puts body
end
else
# markdown or html returned directly by the API; just print
puts body
end
exit 0
else
$stderr.puts "Request failed: #{res.code} #{res.message}"
$stderr.puts res.body unless res.body.nil? || res.body.empty?
exit 2
end
rescue StandardError => e
$stderr.puts "Network error: #{e.class}: #{e.message}"
exit 3
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment