Show Subtitle/Audio Names for Plex

Add the subtitle, video, audio track titles and media version info to the Plex Web app.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name     Show Subtitle/Audio Names for Plex 
// @version  2.5
// @grant    none
// @include  https://app.plex.tv/*
// @description Add the subtitle, video, audio track titles and media version info to the Plex Web app.
// @license  MIT; https://spdx.org/licenses/MIT.html#licenseText
// @namespace https://greasyfork.org/users/456605
// ==/UserScript==

function main () {
    function getMediaTitle(media) {
      	let parts = media.Part;
        for (let k = 0; k < parts.length; k++) {
            if (!parts[k].hasOwnProperty("Stream")) continue;
            let streams = parts[k].Stream;
            for (let l = 0; l < streams.length; l++) {
                if (streams[l].streamType == 1 && streams[l].hasOwnProperty("displayTitle")) {
                    const format = streams[l].displayTitle.match(".* \\((.*)\\)$");
                    if (format != null) return format[1] + " " + media.container;
                    else return media.container;
                }
            }
        }
        return "Unknown";
    }
  
    function intercept(url, responseText) {
        if (url.indexOf("/library/metadata/") == -1 && url.indexOf("/status/sessions") == -1) return responseText;
        let response = JSON.parse(responseText);
        if (!response.hasOwnProperty("MediaContainer") ||
            !response.MediaContainer.hasOwnProperty("Metadata")) return responseText;
        const meta = response.MediaContainer.Metadata;
        for (let i = 0; i < meta.length; i++) {
            if (!meta[i].hasOwnProperty("Media")) continue;
            let medias = meta[i].Media;
            for (let j = 0; j < medias.length; j++) {
                if (!medias[j].hasOwnProperty("Part")) continue;
                if (!medias[j].hasOwnProperty("title")) medias[j].title = getMediaTitle(medias[j]);
                let parts = medias[j].Part;
                for (let k = 0; k < parts.length; k++) {
                    if (!parts[k].hasOwnProperty("Stream")) continue;
                    let streams = parts[k].Stream;
                    for (let l = 0; l < streams.length; l++) {
                        if (!streams[l].hasOwnProperty("displayTitle") || !streams[l].hasOwnProperty("title")) continue;
                        streams[l].displayTitle = streams[l].displayTitle + " (" + streams[l].title + ")"; 
                    }
                }
            }
        }
        return JSON.stringify(response);
    }

    // From https://stackoverflow.com/questions/26447335/
    (function() {
        // create XMLHttpRequest proxy object
        var oldXMLHttpRequest = XMLHttpRequest;

        // define constructor for my proxy object
        XMLHttpRequest = function() {
            var actual = new oldXMLHttpRequest();
            var self = this;

            this.onreadystatechange = null;

            // this is the actual handler on the real XMLHttpRequest object
            actual.onreadystatechange = function() {
                if (this.readyState == 4 && (actual.responseType == '' || actual.responseType == 'text')) {
                    try {
                        self._responseText = intercept(actual.responseURL, actual.responseText);
                    } catch (err) {
                        console.error(err);
                    }
                }
                if (self.onreadystatechange) {
                    return self.onreadystatechange();
                }
            };

            // add all proxy getters/setters
            ["responseText"].forEach(function(item) {
                Object.defineProperty(self, item, {
                    get: function() {
                        if (self.hasOwnProperty("_" + item)) {
                            return self["_" + item];
                        } else {
                            return actual[item];
                        }
                    },
                    set: function(val) {actual[item] = val;}
                });
            });

            // add all proxy getters/setters
            ["status", "statusText", "responseType", "response", "readyState", "responseXML",
            "upload", "ontimeout, timeout", "withCredentials", "onload", "onerror",
            "onprogress", "responseURL"].forEach(function(item) {
                Object.defineProperty(self, item, {
                    get: function() {return actual[item];},
                    set: function(val) {actual[item] = val;}
                });
            });

            // add all pure proxy pass-through methods
            ["addEventListener", "send", "open", "abort", "getAllResponseHeaders",
             "getResponseHeader", "overrideMimeType", "setRequestHeader"].forEach(function(item) {
                Object.defineProperty(self, item, {
                    value: function() {return actual[item].apply(actual, arguments);}
                });
            });
        }
    })();
}

// From https://stackoverflow.com/questions/2303147/
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ main +')();'));
(document.body || document.head || document.documentElement).appendChild(script);