Show Subtitle/Audio Names for Plex

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

  1. // ==UserScript==
  2. // @name Show Subtitle/Audio Names for Plex
  3. // @version 2.5
  4. // @grant none
  5. // @include https://app.plex.tv/*
  6. // @description Add the subtitle, video, audio track titles and media version info to the Plex Web app.
  7. // @license MIT; https://spdx.org/licenses/MIT.html#licenseText
  8. // @namespace https://greasyfork.org/users/456605
  9. // ==/UserScript==
  10.  
  11. function main () {
  12. function getMediaTitle(media) {
  13. let parts = media.Part;
  14. for (let k = 0; k < parts.length; k++) {
  15. if (!parts[k].hasOwnProperty("Stream")) continue;
  16. let streams = parts[k].Stream;
  17. for (let l = 0; l < streams.length; l++) {
  18. if (streams[l].streamType == 1 && streams[l].hasOwnProperty("displayTitle")) {
  19. const format = streams[l].displayTitle.match(".* \\((.*)\\)$");
  20. if (format != null) return format[1] + " " + media.container;
  21. else return media.container;
  22. }
  23. }
  24. }
  25. return "Unknown";
  26. }
  27. function intercept(url, responseText) {
  28. if (url.indexOf("/library/metadata/") == -1 && url.indexOf("/status/sessions") == -1) return responseText;
  29. let response = JSON.parse(responseText);
  30. if (!response.hasOwnProperty("MediaContainer") ||
  31. !response.MediaContainer.hasOwnProperty("Metadata")) return responseText;
  32. const meta = response.MediaContainer.Metadata;
  33. for (let i = 0; i < meta.length; i++) {
  34. if (!meta[i].hasOwnProperty("Media")) continue;
  35. let medias = meta[i].Media;
  36. for (let j = 0; j < medias.length; j++) {
  37. if (!medias[j].hasOwnProperty("Part")) continue;
  38. if (!medias[j].hasOwnProperty("title")) medias[j].title = getMediaTitle(medias[j]);
  39. let parts = medias[j].Part;
  40. for (let k = 0; k < parts.length; k++) {
  41. if (!parts[k].hasOwnProperty("Stream")) continue;
  42. let streams = parts[k].Stream;
  43. for (let l = 0; l < streams.length; l++) {
  44. if (!streams[l].hasOwnProperty("displayTitle") || !streams[l].hasOwnProperty("title")) continue;
  45. streams[l].displayTitle = streams[l].displayTitle + " (" + streams[l].title + ")";
  46. }
  47. }
  48. }
  49. }
  50. return JSON.stringify(response);
  51. }
  52.  
  53. // From https://stackoverflow.com/questions/26447335/
  54. (function() {
  55. // create XMLHttpRequest proxy object
  56. var oldXMLHttpRequest = XMLHttpRequest;
  57.  
  58. // define constructor for my proxy object
  59. XMLHttpRequest = function() {
  60. var actual = new oldXMLHttpRequest();
  61. var self = this;
  62.  
  63. this.onreadystatechange = null;
  64.  
  65. // this is the actual handler on the real XMLHttpRequest object
  66. actual.onreadystatechange = function() {
  67. if (this.readyState == 4 && (actual.responseType == '' || actual.responseType == 'text')) {
  68. try {
  69. self._responseText = intercept(actual.responseURL, actual.responseText);
  70. } catch (err) {
  71. console.error(err);
  72. }
  73. }
  74. if (self.onreadystatechange) {
  75. return self.onreadystatechange();
  76. }
  77. };
  78.  
  79. // add all proxy getters/setters
  80. ["responseText"].forEach(function(item) {
  81. Object.defineProperty(self, item, {
  82. get: function() {
  83. if (self.hasOwnProperty("_" + item)) {
  84. return self["_" + item];
  85. } else {
  86. return actual[item];
  87. }
  88. },
  89. set: function(val) {actual[item] = val;}
  90. });
  91. });
  92.  
  93. // add all proxy getters/setters
  94. ["status", "statusText", "responseType", "response", "readyState", "responseXML",
  95. "upload", "ontimeout, timeout", "withCredentials", "onload", "onerror",
  96. "onprogress", "responseURL"].forEach(function(item) {
  97. Object.defineProperty(self, item, {
  98. get: function() {return actual[item];},
  99. set: function(val) {actual[item] = val;}
  100. });
  101. });
  102.  
  103. // add all pure proxy pass-through methods
  104. ["addEventListener", "send", "open", "abort", "getAllResponseHeaders",
  105. "getResponseHeader", "overrideMimeType", "setRequestHeader"].forEach(function(item) {
  106. Object.defineProperty(self, item, {
  107. value: function() {return actual[item].apply(actual, arguments);}
  108. });
  109. });
  110. }
  111. })();
  112. }
  113.  
  114. // From https://stackoverflow.com/questions/2303147/
  115. var script = document.createElement('script');
  116. script.appendChild(document.createTextNode('('+ main +')();'));
  117. (document.body || document.head || document.documentElement).appendChild(script);