Convert YouTube Link to Clickable MD (gets Title and Image - Handles Playlists and Live as Well) 6D7A

Convert YouTube Link to Clickable MD (gets Title and Image - Handles Playlists and Live as Well) 6D7A

content

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

:gem_stone:HANDS​:gem_stone: - YouTube

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 += `[![]( ${thumb} )](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[![]( ${thumb} )](https://youtu.be/${videoId})`;  
}  

// Insert  
let pos = editor.getSelectedLineRange()[0] + editor.getSelectedLineRange()[1];  
editor.setTextInRange(pos, 0, "\n" + output + "\n");  
editor.activate();  
  
// <!-- 202511291343 -->  

I think your post formatting is a little messed up and I don’t see any link to an action. I just see links to various videos and a block of code which is presumably from th action.

Is there an action for this (you posted it in the Actions category) or is it just a list of embedded examples in the topic and some script code?

I had a quick search in the directory but don’t see anything for this and there’s the odd “6D7A” at the end of the topic title (and in the code comments) that I thought might have been related to the action, but does not appear to be.