YouTube → Supabase Logger

Inserts each YouTube video ID, title, and channel name into your Supabase table exactly once

Version vom 08.05.2025. Aktuellste Version

// ==UserScript==
// @name        YouTube → Supabase Logger
// @description Inserts each YouTube video ID, title, and channel name into your Supabase table exactly once
// @match       https://www.youtube.com/
// @run-at      document-end
// @version 0.0.1.20250508195644
// @namespace https://greasyfork.org/users/1435046
// ==/UserScript==

(function() {
  'use strict';

  // Supabase configuration
  const SUPABASE_URL = 'https://haughsijawbsqwumuryg.supabase.co';
  const SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImhhdWdoc2lqYXdic3F3dW11cnlnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzM0ODE3MjYsImV4cCI6MjA0OTA1NzcyNn0.stESUMuJEs4CNBWGtxZr1XNp2XpnQeXmKkq3fNaVE-c';
  const TABLE       = 'youtube_recommended_videos_table';

  // Keep track of which IDs have already been sent
  const seen = new Set();

  // Extracts the “v” parameter from a YouTube URL
  function getVideoId(href) {
    try {
      const u = new URL(href);
      return u.searchParams.get('v');
    } catch {
      return null;
    }
  }

  // Posts a row with id, title, and channel name
  function insertVideoData(id, title, channel) {
    fetch(`${SUPABASE_URL}/rest/v1/${TABLE}`, {
      method: 'POST',
      headers: {
        'Content-Type':  'application/json',
        'apikey':        SUPABASE_KEY,
        'Authorization': `Bearer ${SUPABASE_KEY}`
      },
      body: JSON.stringify([{
        video_id_column:             id,
        video_title_column:          title,
        video_channel_name_column:   channel
      }])
    });
  }

  // Scan all video items on the page
  function logAndSend() {
    const ITEM_SELECTOR    = 'ytd-video-renderer, ytd-grid-video-renderer, ytd-compact-video-renderer';
    const TITLE_LINK_SEL   = 'a#video-title-link';
    const CHANNEL_SEL      = 'ytd-channel-name a';

    document.querySelectorAll(ITEM_SELECTOR).forEach(item => {
      const linkEl = item.querySelector(TITLE_LINK_SEL);
      if (!linkEl) return;

      const href    = linkEl.href;
      const id      = getVideoId(href);
      if (!id || seen.has(id)) return;

      seen.add(id);

      // Pull title from aria-label exactly as in your blocker script
      const title   = linkEl.getAttribute('aria-label').trim() || '‹no title›';

      // Pull channel name
      const chanEl  = item.querySelector(CHANNEL_SEL);
      const channel = chanEl ? chanEl.textContent.trim() : '‹no channel›';

      insertVideoData(id, title, channel);
    });
  }

  // Initial scan
  logAndSend();

  // Watch for new videos being loaded into the DOM
  new MutationObserver(logAndSend)
    .observe(document.body, { childList: true, subtree: true });

  // Also re-scan after YouTube navigation events
  window.addEventListener('yt-navigate-finish', logAndSend);
})();