ALIST 外调mpv播放器进行在线播放视频

xc
xc
2025-05-05 / 0 评论 / 5 阅读 / 正在检测是否收录...

概述:

最近对mpv播放器很感兴趣,alist网盘程序虽然下面继承了多个第三方播放器,但是没Mpv,原因就是mpv播放器默认不会将mpv://协议注册到系统中。

正文:

那我们就自己动手吧,大概原理也很简单,让系统知道mpv://协议应该调用mpv播放器进行播放,
另外就是在alist网页中注入相关按钮和生成mpv://协议。

我们先完成系统注册,网上都会提到使用 mpv-handler ,但我试了多次,最终也没成功,我很奇怪,它通过注册表启动mpv-handler.exe,然后这个应用查找本地的config文件,在config文件中,我们要修改本地的mpv播放器路径。

流程

多次尝试,最后总结的成功流程:
先系统注册表,告诉系统mpv://协议,就去启动一个mpv-handler.bat或mpv-handler.exe的文件,因为此时的URL真实地址前面多了一个mpv://,MPV播放器正常来说,还是不识别的,当然,有的系统会被识别,为了统一标准,提高运行成功率,我们需要mpv-handler.bat或mpv-handler.exe将mpv://删除,只保留后面的视频地址给Mpv播放器。

注册mpv-handler.bat或mpv-handler.exe

创建注册表脚本
新建一个文本文件,粘贴以下内容,然后另存为 mpv_protocol.reg(确保保存类型选"所有文件"):

生成mpv-handler.bat

@echo off
setlocal

:: 去掉前缀并解码
set "raw_url=%~1"
set "raw_url=%raw_url:mpv://=%"

:: 确保 URL 是有效的(修复 https// 到 https://)
set "raw_url=%raw_url:https//=https://%"

:: 打印 URL 和路径以调试
echo Incoming URL: %~1
echo Decoded URL: %raw_url%
echo MPV Path: "D:\mpv\mpv\mpv.exe"

:: 启动 mpv
start "" "D:\mpv\mpv\mpv.exe" "%raw_url%"

:: 检查是否成功启动
if %errorlevel% neq 0 (
    echo Failed to start MPV! Error code: %errorlevel%
) else (
    echo MPV started successfully!
)

生成mpv-handler.exe
首先要安装好python
其次安装模块

pip install pyinstaller
pip install pyinstaller

系统有多个python记得前面加

python -m

mpv-handler.py

( 请根据需要修改D:\mpv\mpv\mpv.exe)

import sys
import subprocess

def fix_url(url):
    """ 修复 URL 格式,例如将 https// 修正为 https:// """
    url = url.replace('mpv://', '')  # 去掉前缀
    url = url.replace('https//', 'https://')  # 修正协议部分
    return url

if len(sys.argv) > 1:
    raw_url = sys.argv[1]
    fixed_url = fix_url(raw_url)  # 修复 URL
    mpv_path = r"D:\mpv\mpv\mpv.exe"
    
    # 启动 MPV 播放器并传入修复后的 URL
    subprocess.Popen([mpv_path, fixed_url])

编译

pyinstaller --onefile mpv-handler.py

出错就强制编译

C:\Users\Admin\AppData\Local\Programs\Python\Python312\Scripts\pyinstaller.exe --onefile mpv-handler.py

新生成的exe文件在:dist\mpv-handler.exe

修改alist页面

大概alist管理页面,在全局的自定义中,添加以下代码。
ALIST管理页面

<script>
(function() {
    'use strict';

    // --- Configuration ---
    const MPV_PROTOCOL = 'mpv://';
    const BUTTON_CONTAINER_SELECTOR = 'div.hope-flex[class*="igXrpAn-css"]'; // More flexible selector
    const EXISTING_BUTTON_SELECTOR = 'a.hope-anchor[href*="://"]';
    const MPV_BUTTON_ID = 'alist-external-mpv-button';
    // *** IMPORTANT: Make sure this path is correct OR use Base64 ***
    const MPV_ICON_SRC = 'data:image/png;base64,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'; // Or 'data:image/png;base64,YOUR_BASE64_STRING'
    const DEBUG_MODE = true; // Set to true for more console logs

    // --- Logging Function ---
    function log(...args) {
        if (DEBUG_MODE) {
            console.log('[AList MPV Button]', ...args);
        }
    }

    log("Script Initializing...");

    // --- Helper Functions ---
    function extractRawUrl(href) {
        if (!href) return null;
        log("Attempting to extract raw URL from:", href);
        const specificSchemes = [
            { prefix: 'iina://weblink?url=', isBase64: false },
            { prefix: 'potplayer://', isBase64: false },
            { prefix: 'vlc://', isBase64: false },
            { prefix: 'nplayer-', isBase64: false },
            { prefix: 'omniplayer://weblink?url=', isBase64: false },
            { prefix: 'figplayer://weblink?url=', isBase64: false },
            { prefix: 'infuse://x-callback-url/play?url=', isBase64: false },
            { prefix: 'filebox://play?url=', isBase64: false },
            { prefix: 'intent:', isBase64: false },
            { prefix: 'iplay://play/any?type=url&url=', isBase64: true }
        ];

        for (const scheme of specificSchemes) {
            if (href.startsWith(scheme.prefix)) {
                let urlPart = href.substring(scheme.prefix.length);

                 // Handle intent URLs
                 if (scheme.prefix === 'intent:') {
                    const intentEndIndex = urlPart.indexOf('#Intent');
                    if (intentEndIndex !== -1) {
                        urlPart = urlPart.substring(0, intentEndIndex);
                    }
                 }

                // --- 修改开始: 先移除后缀,然后解码 ---
                urlPart = urlPart.replace(/:0$/, ''); // 移除潜在的 :0 后缀

                if (scheme.isBase64) {
                    try {
                        urlPart = atob(urlPart);
                        log("Decoded Base64 URL:", urlPart);
                    } catch (e) {
                        console.error("Error decoding Base64 URL:", e, urlPart);
                        return null;
                    }
                } else {
                    // 对非 Base64 的 URL 进行 URL 解码
                    try {
                        urlPart = decodeURIComponent(urlPart);
                        log("URL-decoded part:", urlPart);
                    } catch (e) {
                         console.error("Error URL-decoding part:", e, urlPart);
                         // 如果解码失败(比如 URL 本身有问题),也认为无效
                         return null;
                    }
                }
                // --- 修改结束 ---

                // 现在检查解码后的 URL
                if (urlPart.startsWith('http://') || urlPart.startsWith('https://')) {
                     log("Extracted URL:", urlPart);
                     return urlPart;
                } else {
                     log("Processed part doesn't look like a valid URL:", urlPart);
                }
            }
        }

        // Fallback (也需要解码)
        const genericMatch = href.match(/(https?:\/\/.*)/);
        if (genericMatch && genericMatch[1]) {
            try {
                const fallbackUrl = decodeURIComponent(genericMatch[1].replace(/:0$/, ''));
                 log("Extracted URL using fallback (decoded):", fallbackUrl);
                 if (fallbackUrl.startsWith('http://') || fallbackUrl.startsWith('https://')) {
                      return fallbackUrl;
                 }
            } catch (e) {
                 console.error("Error URL-decoding fallback URL:", e, genericMatch[1]);
            }
        }

        console.warn("Could not extract raw URL from:", href);
        return null;
    }

    function createMpvButton(rawUrl) {
        if (!rawUrl) return null;
        log("Creating MPV button for URL:", rawUrl);
        const mpvUrl = MPV_PROTOCOL + rawUrl;

        const link = document.createElement('a');
        link.id = MPV_BUTTON_ID;
        // Use specific classes found in the example, be careful if they change
        link.className = 'hope-anchor hope-c-iHuheP hope-c-PJLV hope-c-PJLV-idrWMwW-css';
        link.href = mpvUrl;
        link.target = "_blank";
        link.style.marginLeft = '4px'; // Add a little space

        const img = document.createElement('img');
        // Use specific classes found in the example
        img.className = 'hope-image hope-c-PJLV hope-c-PJLV-idGrHCo-css';
        img.src = MPV_ICON_SRC;
        img.alt = "MPV";
        img.title = "使用 MPV 播放";
        // Add basic error handling for the image
        img.onerror = () => {
             console.error(`Failed to load MPV icon: ${MPV_ICON_SRC}`);
             img.alt = "MPV (icon error)"; // Show text if icon fails
             img.style.width = '20px'; // Give it some size even without icon
             img.style.height = '20px';
             img.style.border = '1px solid red'; // Make it visible
         };

        link.appendChild(img);
        return link;
    }

    function addMpvButtonToContainer(container) {
        if (!container) {
            log("addMpvButtonToContainer called with null container");
            return;
        }
        if (container.querySelector(`#${MPV_BUTTON_ID}`)) {
            log("MPV button already exists in container:", container);
            return; // Already added
        }
        log("Attempting to add MPV button to container:", container);

        const existingButton = container.querySelector(EXISTING_BUTTON_SELECTOR);
        if (!existingButton || !existingButton.href) {
            log("No existing button with href found in container.");
            return;
        }

        const rawUrl = extractRawUrl(existingButton.href);
        if (!rawUrl) {
            log("Failed to extract raw URL from existing button:", existingButton.href);
            return;
        }

        const mpvButton = createMpvButton(rawUrl);
        if (mpvButton) {
            // Append the button
            container.appendChild(mpvButton);
            log("MPV button added successfully.");
        } else {
             log("Failed to create MPV button element.");
        }
    }

    // --- MutationObserver Logic ---
    const observerCallback = (mutationsList, observer) => {
        log("MutationObserver detected changes...");
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList') {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        // Check if the added node itself is the container
                        if (node.matches && node.matches(BUTTON_CONTAINER_SELECTOR)) {
                            log("Container node added:", node);
                            addMpvButtonToContainer(node);
                        }
                        // Check if the added node contains the container
                        else if (node.querySelectorAll) {
                            const containers = node.querySelectorAll(BUTTON_CONTAINER_SELECTOR);
                            if (containers.length > 0) {
                                log("Container node found within added node:", node);
                                containers.forEach(addMpvButtonToContainer);
                            }
                        }
                    }
                });
                 // Also check removed nodes in case the container is re-rendered quickly
                 // This might be overkill, but could help in some frameworks
                 mutation.removedNodes.forEach(node => {
                      if (node.nodeType === Node.ELEMENT_NODE && node.matches && node.matches(BUTTON_CONTAINER_SELECTOR)) {
                          log("Container node removed, will re-check on next addition.");
                      }
                 });
            }
            // Optional: Observe attribute changes if the container exists but buttons are added later
            // else if (mutation.type === 'attributes' && mutation.target.matches(BUTTON_CONTAINER_SELECTOR)) {
            //     log("Attributes changed on container, re-checking:", mutation.target);
            //     addMpvButtonToContainer(mutation.target);
            // }
        }
    };

    // Start observing
    const observer = new MutationObserver(observerCallback);
    // Observe body, but consider observing a more specific, stable parent if possible
    observer.observe(document.body, { childList: true, subtree: true });
    log("Observer started.");

    // --- Initial Check ---
    log("Performing initial check for existing containers...");
    document.querySelectorAll(BUTTON_CONTAINER_SELECTOR).forEach(container => {
        log("Found existing container on load:", container);
        addMpvButtonToContainer(container);
    });
    log("Initial check complete.");

})();
</script>

总结:

mpv-handler.bat和mpv-handler.exe二选一就可以了,在win系统中exe文件要比bat文件更可靠一些,但bat文件修改起来更方便一些,按需选择,最后记得更新mpv_protocol.reg的位置信息。

我已经打包好了这些文件,确保你的mpv.exe和我一致的话(D:\mpv\mpv\mpv.exe),拿来直接用就可以了。

0

评论 (0)

取消
使用 Typecho 建站,并搭配 joe 主题(有修改)
蜀ICP备2022005623号-2 川公网安备 51012202001212号
本站已运行 00000000
Copyright © 1970 ~ Xcshare All rights reserved.