Netflix Mark Watched

Mark Netflix shows as watched.

  1. // ==UserScript==
  2. // @name Netflix Mark Watched
  3. // @namespace watched_netflix
  4. // @version 2.1
  5. // @description Mark Netflix shows as watched.
  6. // @include https://www.netflix.com/*
  7. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
  8. // @grant GM_registerMenuCommand
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @grant GM_listValues
  12. // @grant GM_deleteValue
  13. // @license MIT
  14. // ==/UserScript==
  15.  
  16. // Function to migrate data from localStorage (old) to GM storage (new)
  17. function migrateWatchedData() {
  18. var migrated = false;
  19. // Iterate through all localStorage keys
  20. for (var i = 0; i < localStorage.length; i++) {
  21. var key = localStorage.key(i);
  22. if (key.startsWith("watched_")) {
  23. var value = localStorage.getItem(key);
  24. // Store the value using GM_setValue
  25. GM_setValue(key, value);
  26. console.log("Migrated:", key, value);
  27. migrated = true;
  28. }
  29. }
  30.  
  31. if (migrated) {
  32. // Delete migrated entries from localStorage
  33. for (var i = 0; i < localStorage.length; i++) {
  34. var key = localStorage.key(i);
  35. if (key.startsWith("watched_")) {
  36. localStorage.removeItem(key);
  37. console.log("Deleted from localStorage:", key);
  38. }
  39. }
  40. alert("Migration of watched data to new GM storage completed.");
  41. } else {
  42. console.log("No watched shows data found in localStorage to migrate.");
  43. }
  44. }
  45.  
  46. // Function to add the watched button
  47. function addWatchedButton(element) {
  48. var id = JSON.parse(decodeURI(element.data('ui-tracking-context'))).video_id;
  49. var watched = GM_getValue("watched_" + id, 'false');
  50.  
  51. if (watched === 'true') {
  52. element.closest('.title-card-container').addClass('g_watched');
  53. }
  54.  
  55. var watchedEye = $('<div class="watched_eye">&#128065;</div>');
  56.  
  57. watchedEye.click(function () {
  58. var cardContainer = $(this).closest('.title-card-container');
  59. var isWatched = cardContainer.hasClass('g_watched');
  60.  
  61. if (isWatched) {
  62. cardContainer.removeClass('g_watched');
  63. markAsUnwatched(id);
  64. } else {
  65. cardContainer.addClass('g_watched');
  66. markAsWatched(id);
  67. }
  68. });
  69.  
  70. element.closest('.title-card-container').append(watchedEye);
  71. }
  72.  
  73. // Function to mark a show as watched
  74. function markAsWatched(videoId) {
  75. GM_setValue("watched_" + videoId, "true");
  76. }
  77.  
  78. // Function to mark a show as unwatched
  79. function markAsUnwatched(videoId) {
  80. GM_setValue("watched_" + videoId, "false");
  81. }
  82.  
  83. // Backup data related to watched shows to a JSON file
  84. function backupWatchedShows() {
  85. var watchedData = {};
  86. GM_listValues().forEach(function (key) {
  87. if (key.startsWith("watched_")) {
  88. watchedData[key] = GM_getValue(key);
  89. }
  90. });
  91.  
  92. var data = JSON.stringify(watchedData);
  93. var blob = new Blob([data], { type: "application/json" });
  94. var url = URL.createObjectURL(blob);
  95.  
  96. var a = document.createElement("a");
  97. a.href = url;
  98. a.download = "watchedShowsBackup.json";
  99. a.style.display = "none";
  100. document.body.appendChild(a);
  101. a.click();
  102.  
  103. document.body.removeChild(a);
  104. URL.revokeObjectURL(url);
  105. }
  106.  
  107. // Restore data related to watched shows from a JSON file
  108. function restoreWatchedShows() {
  109. var input = document.createElement("input");
  110. input.type = "file";
  111. input.accept = ".json";
  112.  
  113. input.click();
  114.  
  115. input.addEventListener("change", function () {
  116. var file = input.files[0];
  117. var reader = new FileReader();
  118.  
  119. reader.onload = function () {
  120. try {
  121. var data = JSON.parse(reader.result);
  122. var existingKeys = GM_listValues().filter(key => key.startsWith("watched_"));
  123.  
  124. var action = prompt("Choose an action: 'overwrite' or 'merge'. Type 'overwrite' to replace existing data or 'merge' to combine with existing data.", "overwrite");
  125. if (action === "overwrite") {
  126. for (var key in data) {
  127. GM_setValue(key, data[key]);
  128. }
  129. alert("Watched data has been overwritten successfully.");
  130. } else if (action === "merge") {
  131. for (var key in data) {
  132. if (!existingKeys.includes(key)) {
  133. GM_setValue(key, data[key]);
  134. }
  135. }
  136. alert("Watched data has been merged successfully.");
  137. } else {
  138. alert("Invalid action specified. No changes were made.");
  139. }
  140. } catch (e) {
  141. alert("Error restoring data: " + e);
  142. }
  143. };
  144.  
  145. if (file) {
  146. reader.readAsText(file);
  147. }
  148. });
  149. }
  150.  
  151. $(document).ready(function () {
  152. // Run migration on script start
  153. migrateWatchedData();
  154.  
  155. // Initial execution for the elements present on the page load
  156. $('[data-ui-tracking-context]').each(function () {
  157. addWatchedButton($(this));
  158. });
  159.  
  160. // Add a MutationObserver to detect when new elements are added
  161. var observer = new MutationObserver(function (mutations) {
  162. mutations.forEach(function (mutation) {
  163. if (mutation.addedNodes) {
  164. $(mutation.addedNodes).find('[data-ui-tracking-context]').each(function () {
  165. addWatchedButton($(this));
  166. });
  167. }
  168. });
  169. });
  170.  
  171. // Observe changes in the DOM, including dynamically loaded content
  172. observer.observe(document.body, { childList: true, subtree: true });
  173.  
  174. GM_registerMenuCommand("Backup Watched", backupWatchedShows);
  175. GM_registerMenuCommand("Restore Watched", restoreWatchedShows);
  176.  
  177. $('head').append(`
  178. <style>
  179. .watched_eye {
  180. font-size: 57px;
  181. padding: 10px;
  182. position: absolute;
  183. bottom: -40px;
  184. left: 0;
  185. background: gray;
  186. border-radius: 6px;
  187. height: 30px;
  188. line-height: 30px;
  189. }
  190. .watched_eye:hover {
  191. opacity: 0.3;
  192. cursor: pointer;
  193. }
  194. .title-card-container.g_watched {
  195. opacity: 0.3;
  196. }
  197. </style>
  198. `);
  199. });

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址