Updated 07212022-195702
astonishingly, I was able to download the ENTIRETY of my Netflix activity in JSON format on a gosh darned iPad thanks to a single Safari Extension with a userscript.
you should be excited if
1.) you'd enjoy "baking" me to death 2.) you'd like to do it for yourself! https://davidblue.wtf/netflix
astonishingly, I was able to download the ENTIRETY of my Netflix activity in JSON format *on a gosh darned iPad* thanks to a single Safari Extension with a userscript. https://t.co/9dp0F04WW7 pic.twitter.com/a39mSXIc1s
— ⓓⓐⓥⓘⓓ ⓑⓛⓤⓔ (@NeoYokel) July 22, 2022
I was able to download the entirety of my Netflix user activity in JSON format ridiculously quickly thanks to a script I found from GitHub User jsejcksn as well and the (brilliant, open source) Userscripts Safari Extension.
I'm assuming there's nothing revealing so I guess I'll publish it lol.
// ==UserScript==
// @name Netflix: Download activity
// @version 1.0.4
// @description Download your Netflix ratings or viewing activity as a JSON-formatted file
// @license MIT
// @author Jesse Jackson
// @match https://www.netflix.com/moviesyouveseen
// @match https://www.netflix.com/MoviesYouveSeen
// @match https://www.netflix.com/settings/rated/*
// @match https://www.netflix.com/settings/viewed/*
// @match https://www.netflix.com/viewingactivity
// @namespace https://github.com/jsejcksn
// @noframes
// @run-at document-idle
// ==/UserScript==
(() => {
'use strict';
const wait = ms => new Promise(res => setTimeout(res, ms));
const saveData = (obj, fileName) => {
const a = document.createElement('a');
document.body.appendChild(a);
a.style = 'display: none';
const json = JSON.stringify(obj);
const blob = new Blob([json], {type: 'application/json'});
const url = URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
a.remove();
URL.revokeObjectURL(url);
};
const init = async () => {
const halfSecond = 500;
const confirmationMessage = activity.type === activity.types.RATINGS ? 'Download ratings activity?' : 'Download viewing activity?';
// eslint-disable-next-line no-alert
if (confirm(confirmationMessage)) {
const button = document.querySelector('button.btn.btn-blue.btn-small');
while (!button.classList.contains('disabled')) {
button.click();
await wait(halfSecond);
scrollTo(0, document.body.scrollHeight);
}
const results = [];
const items = document.querySelectorAll('.retableRow');
for (const item of items) {
const data = {};
const idStartIndex = 30;
data.id = item.querySelector('.col.title > a').href.slice(idStartIndex);
data.title = item.querySelector('.col.title > a').textContent;
data.date = item.querySelector('.col.date').textContent.split('/').map((val, i) => {
const padLength = 2;
if (i !== padLength) return val.padStart(padLength, '0');
return `20${val}`;
}).sort((a, b) => {
const targetLength = 4;
if (a.length === targetLength) return -1;
else if (b.length === targetLength) return 1;
return 0;
}).join('-');
if (activity.type === activity.types.RATINGS) {
data.rating_stars = null;
data.rating_thumbs = null;
if (item.querySelector('.col.rating > .starbar')) {
const maxStars = 5;
let filled = 0;
let empty = 0;
const stars = item.querySelectorAll('.col.rating > .starbar > .icon-star');
for (const star of stars) {
if (star.classList.contains('personal')) filled += 1;
if (star.classList.contains('sb-placeholder')) empty += 1;
}
if (filled) data.rating_stars = filled;
else if (empty === maxStars) data.rating_stars = 0;
}
else if (item.querySelector('.col.rating > .thumbs-component')) {
const thumbs = item.querySelector('.col.rating > .thumbs-component');
if (thumbs.classList.contains('rated-up')) data.rating_thumbs = 'up';
if (thumbs.classList.contains('rated-down')) data.rating_thumbs = 'down';
}
}
results.push(data);
}
const date = new Date();
const dateFormatted = `${date.toISOString().split('.')[0].split('-').join('').split(':').join('')}Z`;
let profileName = 'Unknown';
try {
profileName = document.querySelector('img.activeProfile').getAttribute('alt');
}
catch {}
const data = {
profile: profileName,
results,
timestamp: date.toISOString(),
type: activity.type,
};
saveData(data, `netflix-activity-${activity.type}-${profileName.toLowerCase()}-${dateFormatted}.json`);
}
};
const activity = {
types: {
RATINGS: 'ratings',
VIEWS: 'views',
},
};
const reRatings = /^\/(?:settings\/rated|moviesyouveseen).*/u;
const reViews = /^\/(?:settings\/viewed|viewingactivity).*/u;
if (reRatings.test(location.pathname.toLowerCase())) {
activity.type = activity.types.RATINGS;
}
else if (reViews.test(location.pathname.toLowerCase())) {
activity.type = activity.types.VIEWS;
}
else throw new Error(`Activity type couldn't be determined by URL`);
init();
const selector = document.querySelector('.pageToggle');
selector.addEventListener('click', async () => {
const halfSecond = 500;
await wait(halfSecond);
if (selector.querySelector('.choice.icon.rating').nodeName === 'SPAN') {
activity.type = activity.types.RATINGS;
init();
}
else if (selector.querySelector('.choice.icon').nodeName === 'SPAN') {
activity.type = activity.types.VIEWS;
init();
}
});
})();
<a href="https://apps.apple.com/us/app/userscripts/id1463298887?itscg=30200&itsct=apps_box_appicon" style="width: 170px; height: 170px; border-radius: 22%; overflow: hidden; display: inline-block; vertical-align: middle;"><img src="https://is2-ssl.mzstatic.com/image/thumb/Purple122/v4/0a/74/d8/0a74d831-62c4-3255-94b8-64e69558b018/AppIcon-1x_U007emarketing-0-7-0-85-220.png/540x540bb.jpg&h=80cbebde5bc2bec0f9384eeeba09e0c0" alt="Userscripts" style="width: 170px; height: 170px; border-radius: 22%; overflow: hidden; display: inline-block; vertical-align: middle;"></a>