GMedia for Hummus

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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();
})();