Convert YouTube Link to Clickable MD (gets Title and Image - Handles Playlists and Live as Well) 6D7A
TITLE ERROR 401:
https://youtu.be/vC9WqJvHqDM
*
YouTube Video ERROR 401 (private/age-restricted)
https://youtu.be/vC9WqJvHqDM

LIVE:
https://www.youtube.com/live/M4USXckfWLU?si=sLrFaoZ-AhSTqftE
Thursday LIVE: Rep. McCormick Indicted, Christian Killings, Texas Redistricting, Nvidia and AI
https://youtu.be/M4USXckfWLU

PLAYLIST:
https://m.youtube.com/watch?v=rNv8K8AYGi8&list=PLKPmuubwTfas9jfxMT80k3WQ0FkN4Wzsl&index=3&pp=gAQBiAQB8AUB
Hotboii - Imma Be Fine (Official Video)
https://youtu.be/jy8yYRI1bW0

Post Malone - I Had Some Help (feat. Morgan Wallen) (Official Video)
https://youtu.be/4QIZE708gJ4

etc…
// Youtube to MD - 6D7A
function extractYouTubeId(url) {
let match = url.match(/(?:youtu\.be\/|youtube\.com\/(?:watch\?v=|live\/|embed\/))([a-zA-Z0-9_-]{11})/);
return match ? match[1] : null;
}
function extractPlaylistId(url) {
let match = url.match(/[?&]list=([a-zA-Z0-9_-]+)/);
return match ? match[1] : null;
}
function extractVideoIdsFromRss(rssText) {
let ids = [];
let matches = rssText.match(/\/watch\?v=([a-zA-Z0-9_-]{11})/g);
if (matches) {
matches.forEach(match => {
let id = match.match(/[a-zA-Z0-9_-]{11}$/)[0];
if (ids.indexOf(id) === -1) ids.push(id);
});
}
return ids;
}
function formatTitle(title, errorMessage) {
if (errorMessage) return `__${title}${errorMessage}__`;
let parts = title.split(" - ");
if (parts.length >= 2) {
let first = parts[0].trim();
let rest = parts.slice(1).join(" - ").trim();
return `_${first}_ - __${rest}__`;
}
return `__${title}__`;
}
function getOEmbedTitle(videoId, http) {
let oembedUrl = `https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoId}`;
let response = http.request({url: oembedUrl, method: "GET", timeout: 10, encoding: "utf-8"});
let title = "YouTube Video";
let errorMessage = "";
if (response.success) {
let data = JSON.parse(response.responseText);
title = data.title || title;
} else {
errorMessage = " ERROR " + (response.statusCode || "Unknown");
if (response.statusCode === 401) errorMessage += " (private/age-restricted)";
else if (response.statusCode === 404) errorMessage += " (not found)";
}
return { title, errorMessage };
}
// === MAIN ===
let input = editor.getSelectedText().trim();
if (!input) { let r = editor.getSelectedLineRange(); input = editor.getTextInRange(r[0], r[1]).trim(); }
if (!input) input = app.getClipboard().trim();
if (!input) { alert("No link found"); context.cancel(); }
let playlistId = extractPlaylistId(input);
let videoId = extractYouTubeId(input);
let isPlaylist = !!playlistId;
if (!isPlaylist && !videoId) { alert("Invalid YouTube URL"); context.cancel(); }
let http = HTTP.create();
let output = "";
if (isPlaylist) {
let playlistUrl = `https://www.youtube.com/playlist?list=${playlistId}`;
// Get playlist title & channel from the actual page
let page = http.request({url: playlistUrl, method: "GET", timeout: 12, encoding: "utf-8"});
let playlistTitle = "Unknown Playlist";
let channelName = "";
if (page.success) {
let html = page.responseText;
let t = html.match(/<title>([^<]+)\s*-\s*YouTube<\/title>/i);
if (t) playlistTitle = t[1].trim();
// More reliable channel extraction (2025 page structure)
let c1 = html.match(/"ownerText":\{"runs":\[\{"text":"([^"]+)"/);
let c2 = html.match(/channel-name["'][^>]*>([^<]+)</i);
channelName = (c1 ? c1[1] : (c2 ? c2[1] : "")).trim();
}
// Final header exactly as you want
output += `[__${playlistTitle}__](${playlistUrl})${channelName ? ` by _${channelName}_` : ""} \n\n`;
// Fetch videos via RSS
let rss = http.request({url: `https://www.youtube.com/feeds/videos.xml?playlist_id=${playlistId}`, method: "GET", timeout: 10});
if (!rss.success) {
alert("Could not load playlist videos (private or deleted)");
context.cancel();
}
let ids = extractVideoIdsFromRss(rss.responseText);
if (ids.length === 0) { alert("No videos found in this playlist"); context.cancel(); }
for (let id of ids) {
let { title, errorMessage } = getOEmbedTitle(id, http);
let formatted = formatTitle(title, errorMessage);
let thumb = `https://i3.ytimg.com/vi/${id}/mqdefault.jpg`;
output += `${formatted} \n`;
output += `<https://youtu.be/${id}> \n`;
output += `[](https://youtu.be/${id}) \n\n`;
}
output = output.trim();
} else {
// Single video
let { title, errorMessage } = getOEmbedTitle(videoId, http);
let formatted = formatTitle(title, errorMessage);
let thumb = `https://i3.ytimg.com/vi/${videoId}/mqdefault.jpg`;
output = `${formatted} \n<https://youtu.be/${videoId}> \n[](https://youtu.be/${videoId})`;
}
// Insert
let pos = editor.getSelectedLineRange()[0] + editor.getSelectedLineRange()[1];
editor.setTextInRange(pos, 0, "\n" + output + "\n");
editor.activate();
// <!-- 202511291343 -->