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