Last active
December 17, 2025 12:45
-
-
Save alana314/6bdc9af962fd8620a4fb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //Now with less jquery | |
| //1) go to your my-list page, and scroll to the bottom to make sure it's all loaded: | |
| //http://www.netflix.com/browse/my-list | |
| //2) Next, paste this in your developer tools console and hit enter: | |
| [...document.querySelectorAll('.slider [aria-label]')].map(ele => ele.getAttribute('aria-label')) | |
| //or use this to copy the list to your clipboard: | |
| copy([...document.querySelectorAll('.slider [aria-label]')].map(ele => ele.getAttribute('aria-label'))) |
New, Support extracting title, URL to watch and Image + description,
Best things is Auto Scroll,
Future proof for class change, time in config,
Option to skip description to make it fast.
Step 1: Go to Netflix - My List - https://www.netflix.com/browse/my-list
Step 2: Press Control + Shift + J on Windows/Linux or Command + Option + J on Mac
Step 3: Copy Paste Below script
(async () => {
'use strict';
/* ================= CONFIG ================= */
const CONFIG = {
collectDescription: true, // true = slow, false = FAST
scrollIntervalMs: 3000,
noIncreaseTimeoutMs: 30000,
modalOpenTimeoutMs: 8000,
modalCloseTimeoutMs: 8000,
};
/* ================= SELECTORS ================= */
const ROW_SELECTOR =
'.galleryLockups .rowContainer.rowContainer_title_card';
const ITEM_SELECTOR =
'.galleryLockups .rowContainer.rowContainer_title_card .slider-item .ptrack-content';
const MODAL_SELECTOR =
'.focus-trap-wrapper.previewModal--wrapper.detail-modal';
const MODAL_DESCRIPTION_SELECTOR =
'.previewModal--detailsMetadata-left p div';
/* ================= UTILS ================= */
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const nowMs = () => Date.now();
const waitFor = async (fn, timeoutMs, stepMs = 200) => {
let waited = 0;
while (waited < timeoutMs) {
const res = fn();
if (res) return res;
await sleep(stepMs);
waited += stepMs;
}
return null;
};
const scrollToBottom = () => {
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth'
});
};
const getRowCount = () =>
document.querySelectorAll(ROW_SELECTOR).length;
const closeModalViaHistory = async () => {
// Netflix modal is route-based
window.history.back();
// wait until modal is really gone
await waitFor(
() => !document.querySelector(MODAL_SELECTOR),
CONFIG.modalCloseTimeoutMs
);
// animation buffer
await sleep(300);
};
const downloadJson = (data) => {
const fileName = `netflix_my_list_${nowMs()
}.json`;
const blob = new Blob([JSON.stringify(data,
null,
2)
],
{
type: 'application/json',
});
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = fileName;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(a.href);
};
/* ================= STEP 1: AUTO SCROLL ================= */
console.log('[Netflix Export
] Loading all items…');
let lastCount = 0;
let lastIncreaseAt = nowMs();
while (true) {
scrollToBottom();
await sleep(CONFIG.scrollIntervalMs);
const count = getRowCount();
if (count > lastCount) {
lastCount = count;
lastIncreaseAt = nowMs();
console.log(`[Scroll
] Rows: ${count
}`);
} else if (nowMs() - lastIncreaseAt >= CONFIG.noIncreaseTimeoutMs) {
break;
}
}
/* ================= STEP 2: COLLECT DATA ================= */
console.log('[Netflix Export
] Collecting titles…');
const nodes = Array.from(document.querySelectorAll(ITEM_SELECTOR));
const results = [];
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i
];
const link = node.querySelector('a');
const img = node.querySelector('.boxart-container img');
if (!link) continue;
const entry = {
index: i + 1,
href: 'https: //www.netflix.com/' + link.getAttribute('href'),
title: link.getAttribute('aria-label'),
image: img?.getAttribute('src') || null,
description: null,
};
if (CONFIG.collectDescription) {
try {
link.scrollIntoView({ block: 'center'
});
await sleep(400);
link.click();
const modal = await waitFor(
() => document.querySelector(MODAL_SELECTOR),
CONFIG.modalOpenTimeoutMs
);
if (modal) {
const descEl = await waitFor(
() => modal.querySelector(MODAL_DESCRIPTION_SELECTOR),
CONFIG.modalOpenTimeoutMs
);
if (descEl) {
entry.description = descEl.innerText.trim();
}
await closeModalViaHistory();
}
} catch (e) {
console.warn(`[${entry.title
}
] description failed`, e);
await closeModalViaHistory();
}
}
results.push(entry);
console.log(`[Collected
] ${entry.title
}`);
}
/* ================= EXPORT ================= */
downloadJson(results);
console.log('[Netflix Export
] DONE 🎉');
})();
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I suggest to take a look to https://github.com/origamiofficial/docker-netflix-migrate for exporting and importing netflix ratings