GMedia for Hummus

Adds HTML5 media support + embeds YouTube and other media sources! (mini fix)

// ==UserScript==
// @name         GMedia for Hummus
// @namespace    http://hummus.sys42.net/
// @version      1.2.0
// @description  Adds HTML5 media support + embeds YouTube and other media sources! (mini fix)
// @author       gn0mesort & natsu (original), upgraded by Pinkie Pie
// @license      MIT
// @match        https://hummus.sys42.net/*
// @match        https://hmus.sys42.net/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    function convertMedia() {
        const targets = document.querySelectorAll('.attachment-inner a, .markup>a, .attachment-inner img');
        const scroller = document.querySelector('.scroller.messages');
        const scroll = scroller ? scroller.scrollHeight - scroller.scrollTop === scroller.clientHeight : false;
        let dataFound = false;

        targets.forEach(target => {
            const handled = target.getAttribute('handled.gnomesort.media');
            const hrefAttr = target.href || target.getAttribute('href');
            if (!handled && hrefAttr) {
                const href = hrefAttr.replace(/^(https?)/g, 'https');
                const ext = href.split('.').pop().toLowerCase();
                const fileName = href.split('/').pop();

                let data;
                if (href.includes("youtube.com/watch") || href.includes("youtu.be/")) {
                    // 🎬 YouTube Embedding
                    const videoId = href.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]+)/);
                    if (videoId && videoId[1]) {
                        data = document.createElement('iframe');
                        data.src = `https://www.youtube.com/embed/${videoId[1]}`;
                        data.width = "560";
                        data.height = "315";
                        data.frameBorder = "0";
                        data.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture";
                        data.allowFullscreen = true;
                    }
                } else if (['mp4', 'webm', 'mov', 'mkv'].includes(ext)) {
                    // 🎥 Video Embedding
                    data = document.createElement('video');
                    data.src = encodeURI(href);
                    data.type = `video/${ext}`;
                    data.controls = true;
                    data.style = "max-width: 100%; height: auto;";
                } else if (['mp3', 'mpeg', 'ogg', 'wav', 'flac', 'aac'].includes(ext)) {
                    // 🎵 Audio Embedding
                    data = document.createElement('audio');
                    data.src = encodeURI(href);
                    data.type = `audio/${ext}`;
                    data.controls = true;
                } else if (href.includes("soundcloud.com")) {
                    // 🎧 SoundCloud Embedding
                    data = document.createElement('iframe');
                    data.src = `https://w.soundcloud.com/player/?url=${encodeURI(href)}`;
                    data.width = "100%";
                    data.height = "166";
                    data.frameBorder = "0";
                    data.allow = "autoplay";
                }

                if (data) {
                    dataFound = true;
                    console.log(`Media Found! type is ${ext} & href is ${href}`);

                    // Create metadata display with filename and loop control
                    const loopControl = 'Loop: <input type="checkbox" name="loop" style="vertical-align: middle" onchange="this.closest(\'div\').querySelector(\'video, audio\').loop = this.checked">';
                    const metaDataElement = document.createElement('div');
                    metaDataElement.className = "metadata gnomesort-metadata";
                    metaDataElement.style = "font-size: 11px; color: gray";
                    metaDataElement.innerHTML = `<a href="${href}" style="font-size: 11px; display: inline" handled.gnomesort.media="true">${fileName}</a> - ${ext} - ${loopControl}`;

                    const container = document.createElement('div');
                    container.appendChild(data);
                    container.appendChild(metaDataElement);
                    target.replaceWith(container);

                    target.setAttribute('handled.gnomesort.media', true);
                }
            }
        });

        if (scroll && dataFound) {
            scroller.scrollTop = scroller.scrollHeight;
            console.log('Scrolling to most recent!');
        }
    }

    // Setup observers to handle dynamically loaded content
    const observer = new MutationObserver(() => {
        convertMedia();
        observer.disconnect();
        observer.observe(document.body, { childList: true, subtree: true });
    });

    observer.observe(document.body, { childList: true, subtree: true });

    // Initial run for already loaded content
    convertMedia();
})();