Last active
October 18, 2025 16:37
-
-
Save ttscoff/f048fbe18a59d9208d46f20cc829345a to your computer and use it in GitHub Desktop.
YouTube -> Markdown using BrettTerpstra.com/yt-md/api
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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