返回首頁 

Netflix 备份工具

快速备份和迁移 Netflix 账号内容


Install this script?
  1. // ==UserScript==// @name Netflix 备份工具// @namespace NetflixBackupTools// @version 0.7// @description 快速备份和迁移 Netflix 账号内容// @author TGSAN// @include /https{0,1}\:\/\/www.netflix.com/browse(\/.*){0,1}/// @run-at document-end// @grant GM_unregisterMenuCommand// @grant GM_registerMenuCommand// @grant unsafeWindow// ==/UserScript==(function () {'use strict';let netflixApi = "https://www.netflix.com/api/shakti/mre";try {netflixApi = unsafeWindow.netflix.reactContext.models.playerModel.data.config.ui.initParams.apiUrl;} catch {console.log("获取 Netflix API 失败");// try {// netflixApi = "https://www.netflix.com/api/shakti/" + netflix.appContext.state.model.models.serverDefs.data.BUILD_IDENTIFIER// } catch {// console.log("获取 Netflix UI Version 失败");// }}function createToast() {let toast = document.createElement("div");toast.style.position = "fixed";toast.style.top = "20px";toast.style.left = "50%";toast.style.transform = "translateX(-50%)";toast.style.padding = "10px 20px";toast.style.backgroundColor = "rgba(250, 250, 250, 1.0)";toast.style.color = "rgba(32, 32, 32, 1.0)";toast.style.fontSize = "12px";toast.style.fontWeight = "600";toast.style.zIndex = "9999";toast.style.boxShadow = "0 0 10px rgba(0, 0, 0, 0.25)";toast.style.borderRadius = "30px";toast.style.opacity = "0.0";toast.style.transition = "opacity 0.5s";document.body.appendChild(toast);return toast;}function showToast(message, time = 1500) {let toast = createToast();toast.innerText = message;setTimeout(function () {toast.style.opacity = "1.0";setTimeout(function () {toast.style.opacity = "0.0";setTimeout(function () {document.body.removeChild(toast);}, 500);}, time);}, 1);}function dateFormat(dataObj, fmt) {var o = {"M+": dataObj.getMonth() + 1, //月份"d+": dataObj.getDate(), //日"h+": dataObj.getHours(), //小时"m+": dataObj.getMinutes(), //分"s+": dataObj.getSeconds(), //秒"q+": Math.floor((dataObj.getMonth() + 3) / 3), //季度"S": dataObj.getMilliseconds() //毫秒};if (/(y+)/.test(fmt)) {fmt = fmt.replace(RegExp.$1, (dataObj.getFullYear() + "").substr(4 - RegExp.$1.length));}for (var k in o) {if (new RegExp("(" + k + ")").test(fmt)) {fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));}}return fmt;}function backup() {let userAuthURL;try {userAuthURL = unsafeWindow.netflix.reactContext.models.userInfo.data.authURL;} catch {alert("无法获取 Netflix API 验证密钥");return;}let userName = "UnknownUser";try {userName = unsafeWindow.netflix.reactContext.models.userInfo.data.name;} catch {alert("无法获取用户名称,将使用默认名称备份");}let userGuid = "UnknownGUID";try {userGuid = unsafeWindow.netflix.reactContext.models.userInfo.data.userGuid;} catch { }let body = "path=" +encodeURIComponent(JSON.stringify(["mylist",{"from": 0,"to": 1000 // Netflix 最多支持一次吐 1333 个(0-1332)}])) +"&authURL=" +encodeURIComponent(userAuthURL);fetch(netflixApi + "/pathEvaluator",{"body": body,"method": "POST","mode": "cors","credentials": "include"}).then(function (res) {if (res.ok) {res.json().then(function (jsonRes) {let mylist = new Array();if (jsonRes.value != undefined && jsonRes.value.mylist != undefined) {let jsonResList = jsonRes.value.mylist;// jsonResList = [["", ""]];console.log(jsonResList);for (const [key, value] of Object.entries(jsonResList)) {if (value != undefined) {if (value.length > 1) {if (value[1] != undefined) {mylist.push(value[1]);}}}}}if (mylist.length > 0) {var enc = new TextEncoder();const link = document.createElement('a');link.href = URL.createObjectURL(new Blob([enc.encode(JSON.stringify(mylist))]));link.download = "netflix-mylist-" + userName + "-" + userGuid + "-" + dateFormat(new Date(), "yyyyMMddhhmmss") + ".json";link.click();window.URL.revokeObjectURL(link.href);alert("备份完成!(备份##有 " + mylist.length + " 个剧集)");} else {alert("播放列表为空(包括锁区内容),如果播放列表非空可尝试进入播放列表页面后再试");}}).catch(function (err) {alert("无法获取播放列表(无法解析接口返回的结果),备份失败\n" + err);})} else {alert("无法获取播放列表(接口访问失败),备份失败\nStatus: " + res.status);}}).catch(function (err) {alert("无法获取播放列表,备份失败\n" + err);});}function backupOld() {let mylistData;try {mylistData = unsafeWindow.netflix.falcorCache.mylist;} catch { }if (mylistData != undefined) {let userName = "UnknownUser";try {userName = unsafeWindow.netflix.reactContext.models.userInfo.data.name;} catch {alert("无法获取用户名称,将使用默认名称备份");}let userGuid = "UnknownGUID";try {userGuid = unsafeWindow.netflix.reactContext.models.userInfo.data.userGuid;} catch { }let mylist = new Array();for (const [key, value] of Object.entries(mylistData)) {if (value?.value && value.value[1] != undefined) {mylist.push(value.value[1]);}}// let mylist = Object.keys(netflix.falcorCache.videos);if (mylist.length > 0) {var enc = new TextEncoder();const link = document.createElement('a');link.href = URL.createObjectURL(new Blob([enc.encode(JSON.stringify(mylist))]));link.download = "netflix-mylist-" + userName + "-" + userGuid + "-" + dateFormat(new Date(), "yyyyMMddhhmmss") + ".json";link.click();window.URL.revokeObjectURL(link.href);alert("备份完成!(备份##有 " + mylist.length + " 个剧集)");} else {alert("播放列表为空(包括锁区内容),如果播放列表非空可尝试进入播放列表页面后再试");}} else {if (document.location.pathname != "/browse/my-list") {alert("无法获取播放列表,即将跳转到播放列表页面,跳转结束后请重新尝试备份");document.location = "/browse/my-list";} else {alert("无法获取播放列表,备份失败");}}}function restore() {let userAuthURL;try {userAuthURL = unsafeWindow.netflix.reactContext.models.userInfo.data.authURL;} catch {alert("无法获取 Netflix API 验证密钥");return;}const fileInput = document.createElement("input");fileInput.id = "file";fileInput.type = "file";fileInput.style.display = "none";fileInput.addEventListener('change', function () {if (this.files.length === 0) {return;}const reader = new FileReader();reader.onload = function () {let r###lt = reader.r###lt;try {let mylist = JSON.parse(r###lt);if (mylist.length < 1) {alert("备份文件为空");}try {let index = 0;let doFetch = function () {if (index < mylist.length) {showToast("正在还原备份(" + (index + 1) + "/" + mylist.length + ")");let body = {"operation": "add","videoId": mylist[index],// "trackId": 253896178,"authURL": userAuthURL};fetch(netflixApi + "/playlistop",{"body": JSON.stringify(body),"method": "POST","mode": "cors","credentials": "include"}).then(function () {index++;doFetch();}).catch(function () {alert("还原备份失败");});} else {alert("还原备份成功!(备份##有 " + mylist.length + " 个剧集)");}};doFetch();} catch {alert("还原备份失败");}} catch {alert("解析备份失败");}};reader.onerror = function () {alert("读取备份失败");};reader.readAsText(this.files[0]);});// document.body.appendChild(fileInput);fileInput.click();// document.body.removeChild(fileInput);}GM_registerMenuCommand("备份播放列表", backup);GM_registerMenuCommand("还原播放列表", restore);})();