Netflix UHD

让 Netflix 在任何分辨率的显示器上播放 UHD 内容

当前为 2023-11-02 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Netflix UHD
  3. // @name:zh-CN Netflix UHD
  4. // @namespace http://tampermonkey.net/
  5. // @version 1.18
  6. // @description Play Netflix UHD content on any screen resolution
  7. // @description:zh-CN 让 Netflix 在任何分辨率的显示器上播放 UHD 内容
  8. // @author TGSAN
  9. // @match https://www.netflix.com/*
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=netflix.com
  11. // @run-at document-start
  12. // @grant unsafeWindow
  13. // @grant GM_registerMenuCommand
  14. // ==/UserScript==
  15.  
  16. (function () {
  17. // 'use strict';
  18.  
  19. const forceDolbyVision = false;
  20. const forceHEVC = false;
  21. const forceHDCP = false;
  22.  
  23. let useWindowCtx
  24.  
  25. if (self.unsafeWindow) {
  26. console.log("use unsafeWindow mode");
  27. useWindowCtx = self.unsafeWindow;
  28. } else {
  29. console.log("use window mode");
  30. useWindowCtx = self.window;
  31. }
  32.  
  33. // Hook
  34.  
  35. delete useWindowCtx.screen;
  36. useWindowCtx.__defineGetter__('screen', function () {
  37. let s = [];
  38. s.width = 7680;
  39. s.height = 4320;
  40. s.availWidth = 7680;
  41. s.availHeight = 4320;
  42. s.availLeft = 0;
  43. s.availTop = 0;
  44. s.colorDepth = 32;
  45. s.isExtended = false;
  46. s.pixelDepth = 32;
  47. return s;
  48. });
  49. delete useWindowCtx.devicePixelRatio;
  50. useWindowCtx.devicePixelRatio = 4;
  51.  
  52. if (useWindowCtx.MSMediaKeys) {
  53. useWindowCtx.MSMediaKeys.isTypeSupportedWithFeaturesOriginal = useWindowCtx.MSMediaKeys.isTypeSupportedWithFeatures;
  54. useWindowCtx.MSMediaKeys.isTypeSupportedWithFeatures = function (a, b) {
  55. const reg = /,display-res-[x|y]=\d+,display-res-[x|y]=\d+/
  56. b = b.replace(reg, "");
  57. if (forceDolbyVision == true && (b.indexOf("ext-profile=dvh") !== -1)) {
  58. a = a.replace("com.microsoft.playready.hardware", "com.microsoft.playready");
  59. }
  60. if (forceHEVC == true && b.indexOf("ext-profile=dvh") === -1 && (b.indexOf("hvc1") !== -1 || b.indexOf("hev1") !== -1)) {
  61. a = a.replace("com.microsoft.playready.hardware", "com.microsoft.playready");
  62. }
  63. if (forceHDCP == true && b.indexOf("hdcp=") !== -1) {
  64. a = a.replace("hdcp=1", "");
  65. a = a.replace("hdcp=2", "");
  66. }
  67. let r = this.isTypeSupportedWithFeaturesOriginal(a, b);
  68. // if (r !== '') {
  69. // console.log("Hook MSMediaKeys isTypeSupportedWithFeatures:", a, b, r !== '');
  70. // } else {
  71. // console.debug("Hook MSMediaKeys isTypeSupportedWithFeatures:", a, b, r !== '');
  72. // }
  73. return r;
  74. }
  75. }
  76.  
  77. if (useWindowCtx.WebKitMediaKeys) {
  78. useWindowCtx.WebKitMediaKeys.isTypeSupportedOriginal = useWindowCtx.WebKitMediaKeys.isTypeSupported;
  79. useWindowCtx.WebKitMediaKeys.isTypeSupported = function (keySystem, type) {
  80. let r = this.isTypeSupportedOriginal(keySystem, type);
  81. console.log("Hook WebKitMediaKeys", keySystem, type, r);
  82. return r;
  83. }
  84. }
  85.  
  86. // WIP: Firefox not support
  87. // if (useWindowCtx.MediaSource) {
  88. // useWindowCtx.MediaSource.isTypeSupportedOriginal = useWindowCtx.MediaSource.isTypeSupported;
  89. // useWindowCtx.MediaSource.isTypeSupported = function (mimeType) {
  90. // let r = this.isTypeSupportedOriginal(mimeType);
  91. // console.log("Hook MSE", mimeType, r);
  92. // return r;
  93. // }
  94. // }
  95.  
  96. if (useWindowCtx.MediaCapabilities.prototype) {
  97. useWindowCtx.MediaCapabilities.prototype.decodingInfoOriginal = useWindowCtx.MediaCapabilities.prototype.decodingInfo;
  98. useWindowCtx.MediaCapabilities.prototype.decodingInfo = function (mediaDecodingConfiguration) {
  99. let r = this.decodingInfoOriginal(mediaDecodingConfiguration);
  100. // console.log("MC", mediaDecodingConfiguration, r);
  101. let p = new Promise((res, rej) => {
  102. r.then(orir => {
  103. // console.log("orir", orir);
  104. orir.powerEfficient = orir.supported;
  105. orir.smooth = orir.supported;
  106. // console.log("orir edited", orir);
  107. res(orir);
  108. }).catch(ex => {
  109. rej(ex);
  110. });
  111. });
  112. return p;
  113. }
  114. }
  115.  
  116. // Ext
  117. let checkHDCPAsync = async function () {
  118. if (self.GM_registerMenuCommand && window.MSMediaKeys) {
  119. // HW
  120. let hwhdcp0 = window.MSMediaKeys.isTypeSupportedWithFeaturesOriginal("com.microsoft.playready.hardware", 'video/mp4; features="hdcp=0"') != '';
  121. let hwhdcp1 = window.MSMediaKeys.isTypeSupportedWithFeaturesOriginal("com.microsoft.playready.hardware", 'video/mp4; features="hdcp=1"') != '';
  122. let hwhdcp2 = window.MSMediaKeys.isTypeSupportedWithFeaturesOriginal("com.microsoft.playready.hardware", 'video/mp4; features="hdcp=2"') != '';
  123. let hwhdcp2hevc = window.MSMediaKeys.isTypeSupportedWithFeaturesOriginal("com.microsoft.playready.hardware", 'video/mp4; codecs="hev1,mp4a"; features="hdcp=2"') != '';
  124. let hwhdcp2hevc2160p = window.MSMediaKeys.isTypeSupportedWithFeaturesOriginal("com.microsoft.playready.hardware", 'video/mp4; codecs="hev1,mp4a"; features="decode-res-x=3840,decode-res-y=2160,decode-bpc=10,hdcp=2"') != '';
  125. // SW
  126. let swhdcp0 = window.MSMediaKeys.isTypeSupportedWithFeaturesOriginal("com.microsoft.playready.software", 'video/mp4; features="hdcp=0"') != '';
  127. let swhdcp1 = window.MSMediaKeys.isTypeSupportedWithFeaturesOriginal("com.microsoft.playready.software", 'video/mp4; features="hdcp=1"') != '';
  128. let swhdcp2 = window.MSMediaKeys.isTypeSupportedWithFeaturesOriginal("com.microsoft.playready.software", 'video/mp4; features="hdcp=2"') != '';
  129. let swhdcp2hevc = window.MSMediaKeys.isTypeSupportedWithFeaturesOriginal("com.microsoft.playready.software", 'video/mp4; codecs="hev1,mp4a"; features="hdcp=2"') != '';
  130. let swhdcp2hevc2160p = window.MSMediaKeys.isTypeSupportedWithFeaturesOriginal("com.microsoft.playready.software", 'video/mp4; codecs="hev1,mp4a"; features="decode-res-x=3840,decode-res-y=2160,decode-bpc=10,hdcp=2"') != '';
  131. let bool2Status = function (booltype) {
  132. return booltype ? "✓" : "✕";
  133. };
  134. GM_registerMenuCommand("PlayReady DRM Info (" + (hwhdcp2hevc2160p ? "UHD Ready" : "Restricted") + ")", function () {
  135. // DHCP0
  136. let content = "PlayReady DRM (without HDCP 2.2):\n";
  137. content += "Hardware: " + bool2Status(hwhdcp0) + " Software: " + bool2Status(swhdcp0) + "\n\n";
  138. // DHCP1
  139. content += "PlayReady DRM (HDCP 2.2):\n";
  140. content += "Hardware: " + bool2Status(hwhdcp1) + " Software: " + bool2Status(swhdcp1) + "\n\n";
  141. // DHCP2
  142. content += "PlayReady DRM (HDCP 2.2 Type 1):\n";
  143. content += "Hardware: " + bool2Status(hwhdcp2) + " Software: " + bool2Status(swhdcp2) + "\n\n";
  144. // DHCP2 + HEVC
  145. content += "PlayReady DRM (HDCP 2.2 Type 1) with HEVC:\n";
  146. content += "Hardware: " + bool2Status(hwhdcp2hevc) + " Software: " + bool2Status(swhdcp2hevc) + "\n\n";
  147. // DHCP2 + HEVC 2160P
  148. content += "PlayReady DRM (HDCP 2.2 Type 1) with HEVC UHD:\n";
  149. content += "Hardware: " + bool2Status(hwhdcp2hevc2160p) + " Software: " + bool2Status(swhdcp2hevc2160p) + "\n\n";
  150. // Display DRM Info
  151. alert(content);
  152. });
  153. }
  154. };
  155. checkHDCPAsync();
  156.  
  157. let switchPlayerInfo = function () {
  158. console.log("switch player info");
  159.  
  160. useWindowCtx.dispatchEvent(new KeyboardEvent('keydown', {
  161. keyCode: 68,
  162. ctrlKey: true,
  163. altKey: true,
  164. shiftKey: true,
  165. }));
  166. }
  167.  
  168. let switchStreamSelector = function () {
  169. console.log("switch player info");
  170.  
  171. useWindowCtx.dispatchEvent(new KeyboardEvent('keydown', {
  172. keyCode: 83,
  173. ctrlKey: true,
  174. altKey: true,
  175. shiftKey: true,
  176. }));
  177. }
  178.  
  179. GM_registerMenuCommand("Player Info", switchPlayerInfo);
  180. GM_registerMenuCommand("Stream Selector", switchStreamSelector);
  181. })();