| 1 |
import { getById, resetLog, writeError, writeInfo, writeDebug, a, span, div, addClass } from "/common.module.js" |
| 2 |
|
| 3 |
let netflix_file_input = undefined; |
| 4 |
|
| 5 |
export function onLoad() { |
| 6 |
|
| 7 |
const form = getById("form"); |
| 8 |
netflix_file_input = getById("netflix-file"); |
| 9 |
|
| 10 |
form.addEventListener("submit", submitForm); |
| 11 |
} |
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
function submitForm(event) { |
| 16 |
event.preventDefault(); |
| 17 |
event.stopPropagation(); |
| 18 |
|
| 19 |
resetLog(); |
| 20 |
const files = netflix_file_input.files; |
| 21 |
if (files.length !== 1) { |
| 22 |
writeError("First select your viewing history file."); |
| 23 |
return; |
| 24 |
} |
| 25 |
|
| 26 |
const netflix_file = files[0]; |
| 27 |
|
| 28 |
writeInfo("Checking metadata..."); |
| 29 |
|
| 30 |
if (!netflix_file.type.match('text/csv')) { |
| 31 |
let detected_filetype = ""; |
| 32 |
if (netflix_file.type) { |
| 33 |
detected_filetype = ", detected filetype: " + netflix_file.type; |
| 34 |
} |
| 35 |
writeError("Select a CSV file" + detected_filetype); |
| 36 |
return; |
| 37 |
} |
| 38 |
|
| 39 |
handleFile(netflix_file); |
| 40 |
} |
| 41 |
|
| 42 |
|
| 43 |
function handleFile(file) { |
| 44 |
writeInfo("Reading file..."); |
| 45 |
file.text() |
| 46 |
.then(convertText); |
| 47 |
} |
| 48 |
|
| 49 |
const viewingHistoryPattern = /"(.*)","(\d{1,2})\/(\d{1,2})\/(\d+)"/; |
| 50 |
|
| 51 |
|
| 52 |
function convertText(data) { |
| 53 |
writeInfo("Checking header..."); |
| 54 |
const [header, ...lines] = data.split(/\r?\n|\r|\n/g); |
| 55 |
if (header !== "Title,Date") { |
| 56 |
writeError("Invalid Netflix viewing history file, expected header \"Title,Date\""); |
| 57 |
return; |
| 58 |
} |
| 59 |
writeInfo("History count: " + lines.length); |
| 60 |
let letterboxd_output = "Title,WatchedDate,Rewatch\n"; |
| 61 |
let watched_movies = new Set(); |
| 62 |
|
| 63 |
for (let line of lines) { |
| 64 |
if (!line) continue; |
| 65 |
if (line.includes(": Season") |
| 66 |
|| line.includes(": Limited Series:") |
| 67 |
|| line.includes(": Part") |
| 68 |
|| line.includes(": Chapter ") |
| 69 |
|| line.includes(": Volume ") |
| 70 |
|| line.includes(": Series ") |
| 71 |
|| line.includes(": Book ")) { |
| 72 |
writeDebug("Skipping show episode: " + line); |
| 73 |
continue; |
| 74 |
} |
| 75 |
if (line.startsWith(": ")) { |
| 76 |
writeDebug("Skipping empty title entry") |
| 77 |
} |
| 78 |
const [_, title, day, month, year] = viewingHistoryPattern.exec(line); |
| 79 |
if (title && day && month && year) { |
| 80 |
const rewatch = watched_movies.has(title); |
| 81 |
letterboxd_output += `"${title}",${year}-${month}-${day},${rewatch}\n` |
| 82 |
if (rewatch) { |
| 83 |
writeDebug("Rewatch of: " + title); |
| 84 |
} |
| 85 |
watched_movies.add(title); |
| 86 |
} |
| 87 |
else { |
| 88 |
writeError("could not parse line: " + line); |
| 89 |
} |
| 90 |
} |
| 91 |
|
| 92 |
let letterboxd_import = new Blob([letterboxd_output], { type: 'text/csv' }); |
| 93 |
|
| 94 |
const filename = "netflix-letterboxd-import.csv" |
| 95 |
let download_button = a(window.URL.createObjectURL(letterboxd_import), filename) |
| 96 |
let download_text = span("Download import file ==> ") |
| 97 |
download_button.download = filename; |
| 98 |
log.appendChild(div(addClass(download_text, "info"), download_button)); |
| 99 |
} |