Greasy Fork is available in English.

YouTube不重看

删除首页看过一定进度的视频,只会记录最大进度,仅对以新窗口打开的页面有效,ctrl+i可导入记录

  1. // ==UserScript==
  2. // @name YouTube不重看
  3. // @namespace 蒋晓楠
  4. // @version 20240331
  5. // @description 删除首页看过一定进度的视频,只会记录最大进度,仅对以新窗口打开的页面有效,ctrl+i可导入记录
  6. // @author 蒋晓楠
  7. // @license MIT
  8. // @match https://www.youtube.com/
  9. // @match https://www.youtube.com/watch?v=*
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  11. // @grant GM_setValue
  12. // @grant GM_getValue
  13. // @grant GM_deleteValue
  14. // @grant GM_listValues
  15. // @grant GM_registerMenuCommand
  16. // @grant GM_addElement
  17. // ==/UserScript==
  18. //清理视频记录延时,单位秒
  19. const ClearVideoSavedPercentDelay = 30;
  20. //清理视频记录几率,0到100的整数
  21. const ClearVideoSavedPercentRate = 2;
  22. //移除视频间隔,单位秒
  23. const RemoveVideoInterval = 1;
  24. //以下不懂的不要修改
  25. //修改配置
  26. function SetConfig(Key, Value) {
  27. GM_setValue("Config:" + Key, Value);
  28. }
  29.  
  30. //获取配置
  31. function GetConfig(Key, Default) {
  32. return GM_getValue("Config:" + Key, Default);
  33. }
  34.  
  35. //删除配置
  36. function RemoveConfig(Key) {
  37. GM_deleteValue("Config:" + Key);
  38. }
  39.  
  40. //获取当前时间
  41. function GetNowTime() {
  42. return parseInt((new Date().getTime() / 86400000).toString());
  43. }
  44.  
  45. //获取视频记录进度
  46. function GetVideoSavedPercent(Identifier) {
  47. let Video = GM_getValue("Data", {})[Identifier];
  48. return Video === undefined ? 0 : Video.Percent;
  49. }
  50.  
  51. //设置视频记录进度
  52. function SetVideoSavedPercent(Identifier, Percent) {
  53. let AllVideos = GM_getValue("Data", {});
  54. AllVideos[Identifier] = {Percent: Percent, Expire: GetNewExpire()};
  55. GM_setValue("Data", AllVideos);
  56. }
  57.  
  58. //清理视频记录进度
  59. function ClearVideoSavedPercent() {
  60. let NowDay = GetNowTime();
  61. let AllVideos = GM_getValue("Data", {});
  62. for (const Key in AllVideos) {
  63. if (AllVideos[Key].Expire < NowDay) {
  64. delete AllVideos[Key];
  65. }
  66. }
  67. console.log(AllVideos);
  68. GM_setValue("Data", AllVideos);
  69. console.log("已清理过期的播放记录");
  70. }
  71.  
  72. //获取新的过期时间
  73. function GetNewExpire() {
  74. return GetNowTime() + GetConfig("Duration", 180);
  75. }
  76.  
  77. //获取视频标识符
  78. function GetVideoIdentifier(Url) {
  79. return Object.fromEntries((new URL(Url)).searchParams).v;
  80. }
  81.  
  82. function InitHomePage() {
  83. let RemovePercent = GetConfig("RemovePercent", 90);
  84. setInterval(() => {
  85. document.querySelectorAll("ytd-rich-item-renderer:not(.JXNProcessed)").forEach((Item) => {
  86. Item.classList.add("JXNProcessed");
  87. let Link = Item.__shady_native_querySelector("#video-title-link")
  88. if (Link !== null) {
  89. let Percent = GetVideoSavedPercent(GetVideoIdentifier(Link.href));
  90. if (Percent >= RemovePercent) {
  91. console.log("删除[" + Item.__shady_native_querySelector("#video-title").__shady_native_textContent + "]进度" + Percent);
  92. //用隐藏代替删除,因为删除了还会重新在原位置出现
  93. Item.style.display = "none";
  94. }
  95. }
  96. });
  97. }, RemoveVideoInterval * 1000);
  98. }
  99.  
  100.  
  101. function InitVideoPage() {
  102. let Identifier = GetVideoIdentifier(location.href);
  103. let OldPercent = GetVideoSavedPercent(Identifier);
  104. let VideoElement = document.querySelector(".video-stream.html5-main-video");
  105. //等待直到视频元素正确加载
  106. let CheckInterval = setInterval(function () {
  107. if (!isNaN(VideoElement.duration)) {
  108. clearInterval(CheckInterval);
  109. let Duration = VideoElement.duration;
  110. console.log("原进度", OldPercent, "总时间", Duration);
  111. VideoElement.ontimeupdate = () => {
  112. let NowPercent = parseInt((VideoElement.currentTime / Duration * 100).toFixed());
  113. if (NowPercent > OldPercent) {
  114. console.log("新进度", NowPercent);
  115. SetVideoSavedPercent(Identifier, NowPercent);
  116. OldPercent = NowPercent;
  117. }
  118. }
  119. VideoElement.ended = () => {
  120. if (OldPercent !== 100) {
  121. SetVideoSavedPercent(Identifier, 100);
  122. }
  123. }
  124. }
  125. }, 1000);
  126. }
  127.  
  128. function InitImport() {
  129. let ImportFile = GM_addElement("input", {
  130. type: "file",
  131. hidden: true,
  132. accept: "application/json"
  133. });
  134. ImportFile.onchange = () => {
  135. if (ImportFile.files.length > 0) {
  136. let JsonList = ImportFile.files[0];
  137. let Reader = new FileReader();
  138. Reader.onload = (Result) => {
  139. try {
  140. GM_setValue("Data", JSON.parse(Result.target.result));
  141. alert("导入完成");
  142. } catch (e) {
  143. alert("读取的文件格式不正确");
  144. }
  145. };
  146. Reader.readAsText(JsonList);
  147. }
  148. };
  149. window.onkeydown = (e) => {
  150. if (e.ctrlKey && e.key === "i") {
  151. if (confirm("导入记录将会覆盖当前数据,是否继续?")) {
  152. ImportFile.click();
  153. }
  154. }
  155. };
  156. }
  157.  
  158. function ShowInfo() {
  159. setTimeout(() => {
  160. console.log("当前时间", GetNowTime());
  161. console.log("所有记录", GM_getValue("Data", {}));
  162. }, 1000);
  163. }
  164.  
  165. function Run() {
  166. GM_registerMenuCommand("设置移除百分比", () => {
  167. let Interval = prompt("达到这个的才会删除", GetConfig("RemovePercent", 90));
  168. console.log(Interval)
  169. if (Interval !== null) {
  170. SetConfig("RemovePercent", parseInt(Interval));
  171. }
  172. });
  173. GM_registerMenuCommand("设置记录进度有效期", () => {
  174. let Duration = prompt("设置记录视频播放进度有效期,单位天,超过这个时间的记录会被删除", "180");
  175. console.log(Duration)
  176. if (Duration !== null) {
  177. SetConfig("Duration", parseInt(Duration));
  178. }
  179. });
  180. GM_registerMenuCommand("导出记录", () => {
  181. let ExportJson = document.createElement("a");
  182. ExportJson.download = "Youtube不重看.json";
  183. ExportJson.href = URL.createObjectURL(new Blob([JSON.stringify(GM_getValue("Data", {}))]));
  184. ExportJson.click();
  185. });
  186. InitImport();
  187. setTimeout(() => {
  188. if (1 + Math.round(Math.random() * 99) <= ClearVideoSavedPercentRate) {
  189. ClearVideoSavedPercent();
  190. }
  191. }, ClearVideoSavedPercentDelay * 1000);
  192. ShowInfo();
  193. location.pathname === "/watch" ? InitVideoPage() : InitHomePage();
  194. }
  195.  
  196. Run();