Skip to content

Instantly share code, notes, and snippets.

@thiagomgd
Last active April 30, 2024 22:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thiagomgd/ddc36493dc934490fe3f4949e827e4bb to your computer and use it in GitHub Desktop.
Save thiagomgd/ddc36493dc934490fe3f4949e827e4bb to your computer and use it in GitHub Desktop.
Better TMDB
// ==UserScript==
// @name Better TMDB
// @namespace http://tampermonkey.net/
// @match https://www.themoviedb.org/*
// @grant GM_xmlhttpRequest
// @grant GM_getResourceText
// @grant GM_getValue
// @grant GM_setValue
// @version 1.11.8
// @author Geekosaur
// @description 2024-05-22, 09:25:21 a.m.
// ==/UserScript==
const config = GM_getValue("config", {});
const AUTH_BEARER = config["AUTH_BEARER"];
const API_KEY = config["API_KEY"];
const ACCOUNT = config["ACCOUNT"];
const ACTORS = config["ACTORS"];
const CAST_AUTO_LOAD_MORE_DETAILS = true;
const HIGHLIGHT = "#dcefff";
const HIGHLIGHT_RATED = "#efffdc";
const HIGHLIGHT_WATCHLIST = "#dcefff";
const CREW_ROLES = [
"acting",
"actor",
"actress",
"director",
"screenplay",
"story",
"music",
"novel",
"comic book",
"producer",
"original music composer",
"animation director",
"writer",
"original story",
"songs",
];
const MS_PER_DAY = 1000 * 60 * 60 * 24;
let CURRENT_FILTER = "";
let CURRENT_EXT_LIST_PAGE = undefined;
let EXT_LIST_TOTAL_PAGES = undefined;
let LOADING_MY_LISTS = false;
let WATCH_OPTIONS_MEDIA = null;
const LISTS_IGNORE_STREAM = [
8271845, 8271848, 8271832, 8271847, 8292656, 8293351, 8293350, 8293173,
8293168, 8271831, 8271844, 8292658, 8293354, 8293353, 8293352, 8293174,
8287234, 8293166, 8292657,
];
const LONG_MOVIES_LIST = 8298359;
const SHORT_MOVIES_LIST = 8298358;
const LONG_DURATION = 115;
const SHORT_DURATION = 82;
const HEADERS = {
accept: "application/json",
Authorization: `Bearer ${AUTH_BEARER}`,
};
function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName("head")[0];
if (!head) {
return;
}
style = document.createElement("style");
style.type = "text/css";
style.innerHTML = css;
head.appendChild(style);
}
function addObserver(selector, selector2, fn) {
const targetNode = document.querySelector(selector);
const config = { attributes: true, childList: true, subtree: true };
const callback = (mutationList, observer) => {
for (const mutation of mutationList) {
if (mutation.type === "childList") {
// console.debug("A child node has been added or removed.");
// console.debug(mutation);
const newLinks = document.querySelectorAll(selector2);
if (newLinks.length > 0) {
// console.debug("CHANGED!!");
observer.disconnect();
setTimeout(fn, 1500);
}
}
}
};
const observer = new MutationObserver(callback);
observer.observe(targetNode, config);
}
/*
HELPER FUNCTIONS
*/
function toHoursAndMinutes(totalMinutes) {
const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60;
return `${hours}:${minutes.toString().padStart(2, "0")}`;
}
function ListPageShowAll() {
console.debug("SHOW ALL");
const allMedia = document.querySelectorAll("li.item.visited");
allMedia.forEach((el) => {
el.style.display = "list-item";
});
CURRENT_FILTER = "";
}
function ListPageUnratedOnly() {
const allMedia = document.querySelectorAll("li.item.visited");
allMedia.forEach((el) => {
if (el.classList.contains("rated")) {
el.style.display = "none";
} else {
el.style.display = "list-item";
}
});
CURRENT_FILTER = "unrated";
}
function ListPageRatedOnly() {
const allMedia = document.querySelectorAll("li.item.visited");
allMedia.forEach((el) => {
if (el.classList.contains("rated")) {
el.style.display = "list-item";
} else {
el.style.display = "none";
}
});
CURRENT_FILTER = "rated";
}
function ListPageAdultOnly() {
const allMedia = document.querySelectorAll("ol.list_items li.visited");
allMedia.forEach((el) => {
if (el.classList.contains("adult_false")) {
el.style.display = "none";
} else {
el.style.display = "grid";
}
});
CURRENT_FILTER = "adult";
}
function ListPageNotAdultOnly() {
const allMedia = document.querySelectorAll("ol.list_items li.visited");
allMedia.forEach((el) => {
if (el.classList.contains("adult_true")) {
el.style.display = "none";
} else {
el.style.display = "grid";
}
});
CURRENT_FILTER = "adult";
}
function ListPageForeignOnly() {
const languageFilter = prompt("Language - empty for all foreign");
const classFilter = languageFilter
? `${languageFilter}-language`
: "foreign-language";
const allMedia = document.querySelectorAll("ol.list_items li.visited");
allMedia.forEach((el) => {
if (el.classList.contains(classFilter)) {
el.style.display = "grid";
} else {
el.style.display = "none";
}
});
CURRENT_FILTER = "rated";
}
function ListPageStreamableOnly() {
const allMedia = document.querySelectorAll("ol.list_items li.visited");
allMedia.forEach((el) => {
if (el.classList.contains("streamable")) {
el.style.display = "grid";
} else {
el.style.display = "none";
}
});
CURRENT_FILTER = "streamable";
}
/*
POST FUNCTIONS
*/
function delete_from_tmdb_list(list_id, media_type, media_id) {
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "DELETE",
headers: { ...HEADERS, "content-type": "application/json" },
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/4/list/${list_id}/items`,
data: JSON.stringify({
items: [{ media_type: media_type, media_id: media_id }],
}),
onload: (resp) => {
// console.debug("!!!!!", resp);
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
resolve("error");
return;
}
// console.debug('data!!!!: ', json);
resolve(json);
return;
},
});
});
}
async function remove_from_list(list_id, media_key) {
const [media_type, media_id] = media_key.split("-");
const confirmation = confirm(`Delete this?`);
if (!confirmation) {
return;
}
const resp = await delete_from_tmdb_list(list_id, media_type, media_id);
// console.debug(resp);
if (resp.success) {
alert("DONE");
}
}
function comment_tmdb_list(list_id, media_type, media_id, comment) {
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "PUT",
headers: { ...HEADERS, "content-type": "application/json" },
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/4/list/${list_id}/items`,
data: JSON.stringify({
items: [
{ media_type: media_type, media_id: media_id, comment: comment },
],
}),
onload: (resp) => {
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
resolve("error");
return;
}
// console.debug('data!!!!: ', json);
resolve(json);
return;
},
});
});
}
async function add_comment_list(list_id, media_key) {
console.debug("add_comment_list");
const [media_type, media_id] = media_key.split("-");
const comment = prompt(`Comment`);
if (!comment) {
return;
}
const resp = await comment_tmdb_list(list_id, media_type, media_id, comment);
if (resp.success) {
alert("DONE");
}
}
function add_to_tmdb_list(items, list_id) {
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "POST",
headers: { ...HEADERS, "content-type": "application/json" },
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/4/list/${list_id}/items`,
data: JSON.stringify({
items: items,
}),
onload: (resp) => {
console.debug("!!!!!", resp);
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
resolve("error");
return;
}
// console.debug('data!!!!: ', json);
resolve(json);
return;
},
});
});
}
async function addMediaToList(media_type, media_id, list_id) {
const resp = await add_to_tmdb_list(
[{ media_type: media_type, media_id: media_id }],
list_id
);
if (resp.success) {
alert("DONE");
}
}
/*
FETCH FUNCTIONS
*/
function fetch_ext_lists(movie_id, page) {
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "GET",
headers: HEADERS,
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/3/movie/${movie_id}/lists?language=en-US&page=${page}`,
onload: (resp) => {
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
resolve("error");
return;
}
console.debug("response", json);
EXT_LIST_TOTAL_PAGES = json.total_pages;
CURRENT_EXT_LIST_PAGE = page;
// if (json.total_pages > page) {
// CURRENT_EXT_LIST_NEXT_PAGE = page + 1;
// } else {
// CURRENT_EXT_LIST_NEXT_PAGE = 0;
// }
resolve(json.results);
return;
},
});
});
}
async function loadExtLists(movie_id, page) {
console.debug("loadExtLists", movie_id, page);
// if (!CURRENT_EXT_LIST_NEXT_PAGE) {
// return;
// }
const lists = await fetch_ext_lists(movie_id, page);
const links = lists
.map((list) => {
return `<li><a href="https://www.themoviedb.org/list/${list.id}">${list.name}</a></li>`;
})
.join(" ");
const el_listsUl = document.getElementById("extListsUl");
el_listsUl.innerHTML = links;
// loadExtListsButtonDiv
let buttonsHtml = "";
if (page > 1) {
buttonsHtml = `<button id="loadPrevExtListPage" class="myButton">prev</button>`;
}
if (page < EXT_LIST_TOTAL_PAGES) {
buttonsHtml = buttonsHtml.concat(
`<button id="loadNextExtListPage" class="myButton">next</button>`
);
}
console.debug(buttonsHtml);
const _el_button_div = document.getElementById("loadExtListsButtonDiv");
console.debug(_el_button_div);
_el_button_div.innerHTML = buttonsHtml;
const _el_prev_page = document.getElementById("loadPrevExtListPage");
const _el_next_page = document.getElementById("loadNextExtListPage");
_el_prev_page &&
_el_prev_page.addEventListener(
"click",
() => {
loadExtLists(movie_id, page - 1);
},
false
);
_el_next_page &&
_el_next_page.addEventListener(
"click",
() => {
loadExtLists(movie_id, page + 1);
},
false
);
const _el_header = document.getElementById("externalListsH4");
_el_header.textContent = `External Lists (${page}/${EXT_LIST_TOTAL_PAGES})`;
}
function fetch_person_movie_credits(person_id) {
console.debug("fetching credits");
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "GET",
headers: HEADERS,
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/3/person/${person_id}/movie_credits?language=en-US`,
onload: (resp) => {
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
resolve("error");
return;
}
resolve(json.cast);
return;
},
});
});
}
function fetch_movie_credits(movie_id) {
console.debug("fetching movie credits");
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "GET",
headers: HEADERS,
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/3/movie/${movie_id}/credits?language=en-US`,
onload: (resp) => {
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
resolve("error");
return;
}
resolve(json);
return;
},
});
});
}
function fetch_movie_details_credits(movie_id) {
console.debug("fetching movie details + credits");
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "GET",
headers: HEADERS,
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/3/movie/${movie_id}?language=en-US&append_to_response=credits,keywords`,
onload: (resp) => {
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
resolve("error");
return;
}
resolve(json);
return;
},
});
});
}
async function loadPersonPosters(actor_id) {
const movies_list = await fetch_person_movie_credits(actor_id);
const avg_global_rating =
movies_list.reduce((total, item) => total + item.vote_average, 0) /
movies_list.filter((item) => item.vote_average).length;
const movies = {};
movies_list.forEach((el, index) => (movies[`/movie/${el.id}`] = el));
const _el_sidebar = document.querySelector("section.left_column");
let el_rating = document.createElement("p");
el_rating.textContent = "Global Rating: " + avg_global_rating.toFixed(2);
_el_sidebar.firstChild.appendChild(el_rating);
const _el_role_links = document.querySelectorAll("td.role > a.tooltip");
for (const roleLink of _el_role_links) {
const movieData = movies[roleLink.pathname];
// console.debug(roleLink.pathname, movieData);
if (!movieData) continue;
if (movieData.original_language !== "en") {
roleLink.textContent = `[${movieData.original_language}] ${roleLink.textContent}`;
}
if (movieData.adult) {
roleLink.textContent = `[A] ${roleLink.textContent}`;
}
roleLink.closest("td").style.verticalAlign = "top";
const _el_tr = roleLink.closest("tr");
const el_data_td = document.createElement("td");
el_data_td.innerHTML = `<td><span class="rating">Rating: ${movieData.vote_average}</span></br><span class="popularity">Pop.: ${movieData.popularity}</span></td>`;
el_data_td.style.verticalAlign = "top";
el_data_td.style.float = "right";
if (movieData.vote_average < avg_global_rating) {
el_data_td.firstChild.classList.add("less_avg");
}
_el_tr.appendChild(el_data_td);
const el_image_td = document.createElement("td");
el_image_td.style.width = "115px";
el_image_td.innerHTML = `<td><img style="max-height:150px;float: right" src="https://image.tmdb.org/t/p/original${movieData.poster_path}"/></td>`;
_el_tr.appendChild(el_image_td);
}
}
function fetch_tmdb_lists_page(page) {
// TODO: add pagination
// https://api.themoviedb.org/4/account/{account_object_id}/lists
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "GET",
headers: HEADERS,
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/4/account/${ACCOUNT}/lists?api_key=${API_KEY}&page=${page}`,
onload: (resp) => {
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
resolve("error");
return;
}
resolve(json);
return;
},
});
});
}
async function fetch_tmdb_lists_list() {
const lists = [];
let page = 1,
pages = 1;
do {
const data = await fetch_tmdb_lists_page(page);
pages = data.total_pages;
page = page + 1;
lists.push(
...data.results.map((itm) => {
return {
id: itm.id,
name: itm.name,
};
})
);
} while (page <= pages);
return lists;
}
function fetch_list_items_page(list_id, page) {
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "GET",
headers: HEADERS,
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/4/list/${list_id}?api_key=${API_KEY}&page=${page}`,
onload: (resp) => {
// console.debug("!!!!!", resp);
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
resolve("error");
return;
}
// console.debug('data!!!!: ', json);
resolve(json);
return;
},
});
});
}
function fetch_ratings_page(media_type, page) {
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "GET",
headers: HEADERS,
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/4/account/${ACCOUNT}/${media_type}/rated?api_key=${API_KEY}&page=${page}`,
onload: (resp) => {
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
resolve("error");
return;
}
// console.debug('data!!!!: ', json);
resolve(json);
return;
},
});
});
}
function fetch_watchlist_page(media_type, page) {
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "GET",
headers: HEADERS,
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/4/account/${ACCOUNT}/${media_type}/watchlist?api_key=${API_KEY}&page=${page}`,
onload: (resp) => {
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
resolve("error");
return;
}
// console.debug('data!!!!: ', json);
resolve(json);
return;
},
});
});
}
async function fetch_tmdb_list_items(list_id) {
// TODO: add pagination
// https://api.themoviedb.org/4/account/{account_object_id}/lists
// console.debug('loading list items');
const list_items = [];
let page = 1,
pages = 1;
do {
const data = await fetch_list_items_page(list_id, page);
pages = data.total_pages;
page = page + 1;
list_items.push(
...data.results.map((itm) => {
return {
id: itm.id,
media_type: itm.media_type,
// language: itm.original_language,
// year: itm.release_date ? itm.release_date.substring(0, 4) : null,
// runtime: itm.runtime,
};
})
);
} while (page <= pages);
return list_items;
}
function fetchMovieStreamOptions(movie_id) {
// console.debug("fetching stream options");
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: "GET",
headers: HEADERS,
responseType: "json",
synchronous: false,
url: `https://api.themoviedb.org/3/movie/${movie_id}/watch/providers`,
onload: (resp) => {
let json = JSON.parse(resp.responseText);
if (json && json.Error) {
console.debug("Error: " + json.Error);
console.debug("error on movie id", movie_id);
resolve({});
return;
}
resolve(json.results);
return;
},
});
});
}
function getWatchOptionsMap() {
if (!WATCH_OPTIONS_MEDIA) {
WATCH_OPTIONS_MEDIA = GM_getValue("media_watch_options", {});
}
return WATCH_OPTIONS_MEDIA;
}
function saveWatchOptionsMap() {
if (WATCH_OPTIONS_MEDIA) {
GM_setValue("media_watch_options", WATCH_OPTIONS_MEDIA);
}
}
async function getMediaStreamOptions(media_key) {
const [media_type, itemId] = media_key.split("-");
if (media_type !== "movie") {
return;
}
const optionsMap = getWatchOptionsMap();
if (
optionsMap[media_key] &&
(Date.now() - optionsMap[media_key].syncdate) / MS_PER_DAY
) {
return optionsMap[media_key].options;
}
const results = await fetchMovieStreamOptions(itemId);
if (!results) {
return "N";
}
const result = results["CA"];
if (!result) {
return "N";
}
let options = "";
if (result["ads"]) {
options += "F";
}
if (result["flatrate"]) {
options += "S";
}
if (result["rent"]) {
options += "R";
}
WATCH_OPTIONS_MEDIA[media_key] = { options, syncdate: Date.now() };
return options || "N";
}
async function load_tmdb_lists() {
// get lists from local, or remote
// if (LOADING_MY_LISTS) {
// }
let lists = GM_getValue("user_lists", undefined);
const last_sync = GM_getValue("user_lists_sync", Date.now());
const dateDiff = (Date.now() - last_sync) / MS_PER_DAY;
if (lists && dateDiff <= 1) {
return lists;
}
const list_array = await fetch_tmdb_lists_list();
lists = {};
const media_details = GM_getValue("media_details", {});
for (let list of list_array) {
const { id: listId, name: listName, ...listData } = list;
const items = await fetch_tmdb_list_items(listId);
for (const item of items) {
const { id: itemId, media_type } = item;
if (media_type !== "movie" || media_details["movie-" + itemId]) continue;
const details = await fetch_movie_details_credits(itemId);
media_details[`${media_type}-${itemId}`] = {
adult: details.adult,
language: details.original_language,
year: details.release_date
? details.release_date.substring(0, 4)
: null,
runtime: details.runtime,
};
}
lists[listId] = {
id: listId,
name: listName,
items: items.map((item) => `${item.media_type}-${item.id}`),
};
}
GM_setValue("user_lists", lists);
GM_setValue("user_lists_sync", Date.now());
GM_setValue("media_details", media_details);
return lists;
}
async function fetch_tmdb_ratings(media_type) {
console.debug("loading ratings", media_type);
const rating_items = [];
let page = 1,
pages = 1;
do {
const data = await fetch_ratings_page(media_type, page);
pages = data.total_pages;
page = page + 1;
rating_items.push(...data.results);
} while (page <= pages);
return rating_items;
}
async function fetch_tmdb_watchlist(media_type) {
console.debug("loading watchlist", media_type);
const watchlist_items = [];
let page = 1,
pages = 1;
do {
const data = await fetch_watchlist_page(media_type, page);
pages = data.total_pages;
page = page + 1;
watchlist_items.push(...data.results);
} while (page <= pages);
return watchlist_items;
}
function map_ratings_dict(media_type, ratings, media_ratings) {
for (const item of ratings) {
const rating = item.account_rating.value;
const key = `${media_type}-${item.id}`;
if (media_ratings[key]) {
console.error("DUPLICATE RATING", item);
} else {
media_ratings[key] = rating;
}
}
return media_ratings;
}
function map_watchlist_dict(media_type, watchlist, media_watchlist) {
for (const item of watchlist) {
const key = `${media_type}-${item.id}`;
if (media_watchlist[key]) {
console.error("DUPLICATE RATING", item);
} else {
media_watchlist[key] = true;
}
}
return media_watchlist;
}
async function load_tmdb_ratings() {
let ratings = GM_getValue("user_ratings", undefined);
const last_sync = GM_getValue("user_ratings_sync", Date.now());
const dateDiff = (Date.now() - last_sync) / MS_PER_DAY;
if (ratings && dateDiff <= 1) {
return ratings;
}
const movie_ratings = await fetch_tmdb_ratings("movie");
const tv_ratings = await fetch_tmdb_ratings("tv");
console.debug(
"fetched ratings. Movies:",
movie_ratings.length,
"TV",
tv_ratings.length,
"Total",
movie_ratings.length + tv_ratings.length
);
console.debug(movie_ratings);
const media_ratings = {};
map_ratings_dict("movie", movie_ratings, media_ratings);
map_ratings_dict("tv", tv_ratings, media_ratings);
GM_setValue("user_ratings", media_ratings);
GM_setValue("user_ratings_sync", Date.now());
return media_ratings;
}
async function load_tmdb_watchlist() {
let watchlist = GM_getValue("user_watchlist", undefined);
const last_sync = GM_getValue("user_watchlist_sync", Date.now());
const dateDiff = (Date.now() - last_sync) / MS_PER_DAY;
if (watchlist && dateDiff <= 1) {
return watchlist;
}
const movie_watchlist = await fetch_tmdb_watchlist("movie");
const tv_watchlist = await fetch_tmdb_watchlist("tv");
// console.debug("fetched ratings. Movies:", movie_ratings.length, "TV", tv_ratings.length, "Total", movie_ratings.length + tv_ratings.length)
const media_watchlist = {};
map_watchlist_dict("movie", movie_watchlist, media_watchlist);
map_watchlist_dict("tv", tv_watchlist, media_watchlist);
GM_setValue("user_watchlist", media_watchlist);
GM_setValue("user_watchlist_sync", Date.now());
return media_watchlist;
}
function map_lists_to_dict(lists) {
const media = {};
for (const list of Object.values(lists)) {
const list_key = `${list.id}|${list.name}`;
for (const item of list.items || []) {
if (media[item]) {
media[item].push(list_key);
} else {
media[item] = [list_key];
}
}
}
return media;
}
async function getMediaToListsMap() {
const lists = await load_tmdb_lists();
return map_lists_to_dict(lists);
}
async function getListItems(listId) {
const lists = await load_tmdb_lists();
return lists[listId]?.items || [];
}
async function getMediaDetails() {
return GM_getValue("media_details", {});
}
async function getListItemsAndMedia(listId) {
const lists = await load_tmdb_lists();
if (lists[listId]) {
return [lists[listId].items, await getMediaDetails()];
}
console.debug("loading external list items");
const media_details = {};
const items = await fetch_tmdb_list_items(listId);
console.debug(items);
// TODO: GET DETAILS WITH RUNTIME
for (const item of items) {
const { id: itemId, media_type, ...details } = item;
media_details[`${media_type}-${itemId}`] = details;
}
return [items.map((item) => `${item.media_type}-${item.id}`), media_details];
}
/*
IMPROVE PAGE FUNCTIONS
*/
async function improvePersonPage() {
const media_to_list = await getMediaToListsMap();
const media_ratings = await load_tmdb_ratings();
const media_watchlist = await load_tmdb_watchlist();
let total_rating = 0;
let total_rated = 0;
let total_listed = 0;
let total_watchlist = 0;
const tmdb_id = parseInt(
window.location.href
.split("https://www.themoviedb.org/person/")[1]
.split("-")[0]
);
// FLAG MEDIA ON MY LISTS
const movieLinks = document.querySelectorAll("a.tooltip");
for (const movie of movieLinks) {
const key = movie.pathname
.split("/")
.slice(1)
.join("-")
.split("-")
.slice(0, 2)
.join("-");
if (media_to_list[key]) {
total_listed += 1;
const TMDB_lists = [];
media_to_list[key].forEach((item) => {
TMDB_lists.push(item.split("|")[1]);
});
movie.textContent = `${movie.textContent} [${TMDB_lists.join(", ")}]`;
movie.parentElement.parentElement.style.backgroundColor = HIGHLIGHT;
}
console.debug(key);
if (media_ratings[key]) {
total_rated += 1;
total_rating += media_ratings[key];
const _el_check =
movie.parentElement.previousElementSibling.querySelector(
".circle-empty"
);
_el_check.classList.remove("circle-empty");
_el_check.classList.add("circle-check");
} else if (media_watchlist[key]) {
total_watchlist += 1;
movie.parentElement.parentElement.style.backgroundColor = HIGHLIGHT;
const _el_check =
movie.parentElement.previousElementSibling.querySelector(
".circle-empty"
);
_el_check.classList.remove("circle-empty");
_el_check.classList.add("bookmark");
}
}
// console.debug("TOTAL RATED", total_rated, 'AVERAGE', total_rating/total_rated);
let el_section_header = document.createElement("section");
if (!CAST_AUTO_LOAD_MORE_DETAILS) {
// add option to load images
const el_imgs_btn = document.createElement("button");
el_imgs_btn.setAttribute("id", "loadPostersBtn");
el_imgs_btn.textContent = "Load Posters";
el_section_header.appendChild(el_imgs_btn);
}
// ADD IF ACTOR IS ON MY LIST
if (ACTORS.includes(tmdb_id)) {
let el_following = document.createElement("p");
el_following.innerText = `Following`;
el_section_header.appendChild(el_following);
}
let el_watchlist = document.createElement("p");
el_watchlist.textContent =
"Total Watchlist: " + total_watchlist + "/" + movieLinks.length;
let el_listed = document.createElement("p");
el_listed.textContent =
"Total Listed: " + total_listed + "/" + movieLinks.length;
let el_rated = document.createElement("p");
el_rated.textContent =
"Total Rated: " + total_rated + "/" + movieLinks.length;
let el_rating = document.createElement("p");
el_rating.textContent =
"Avg Rating: " +
(total_rating ? Number(total_rating / total_rated).toFixed(2) : 0);
let el_space = document.createElement("p");
el_section_header.appendChild(el_watchlist);
el_section_header.appendChild(el_listed);
el_section_header.appendChild(el_rated);
el_section_header.appendChild(el_rating);
el_section_header.appendChild(el_space);
const _el_sidebar = document.querySelector("section.left_column");
_el_sidebar.insertBefore(el_section_header, _el_sidebar.firstChild);
if (!CAST_AUTO_LOAD_MORE_DETAILS) {
document.getElementById("loadPostersBtn").addEventListener(
"click",
() => {
loadPersonPosters(tmdb_id);
},
false
);
} else {
loadPersonPosters(tmdb_id);
}
}
function getRuntimeClass(runtime) {
if (runtime >= 105) return "longruntime";
if (runtime <= 82) return "shortruntime";
return "regruntime";
}
async function addRatedToListPage(list_items, media_ratings) {
const _el_totalratings = document.getElementById("totalRatings");
if (!_el_totalratings) {
let total_rated = 0;
for (const key of list_items) {
if (media_ratings[key]) {
total_rated += 1;
}
}
// const _el_info = document.querySelector("ul.list_info");
// let el_ratings = document.createElement("li");
// el_ratings.innerHTML = `<span><em>${total_rated}</em></span><br/> rated items`;
// el_ratings.setAttribute("id", "totalRatings");
// // TODO: fix to insert as 2nd item
// _el_info.firstChild.after(el_ratings);
}
}
async function addMediaDetailsToListPage(
media_ratings,
media_details,
watchlist,
listId
) {
const mediaLinks = document.querySelectorAll(
"ol.list_items li:not(.visited) a.picture"
);
// console.debug("addMediaDetailsToListPage", mediaLinks);
const shouldGetWatchOptions = !LISTS_IGNORE_STREAM.includes(listId);
for (const media of mediaLinks) {
// const el_newInfoStrip = document.createElement("div");
// el_newInfoStrip.classList.add("info_strip");
// el_newInfoStrip.id = "newInfoStrip";
// // console.debug(media.textContent);
const key = media.pathname
.split("/")
.slice(1)
.join("-")
.split("-")
.slice(0, 2)
.join("-");
media.closest("li").classList.add("visited");
// if (media_ratings[key]) {
// media.closest("li").classList.add("rated");
// }
// console.debug("media key", key);
// if (!media_details[key]) {
// continue;
// }
const _el_li = media.closest("li");
const _el_wrapper = _el_li.querySelector("div.p-4"); //.querySelector("div.info_wrapper");
// console.debug(_el_wrapper);
const el_info = document.createElement("span");
el_info.classList.add("text-sm");
el_info.classList.add("block");
el_info.classList.add("font-light");
el_info.classList.add("border-t");
el_info.classList.add("pt-2");
el_info.classList.add("list-itm-extra-info");
const details = media_details[key] || {};
media.closest("li").classList.add(`adult_${details.adult}`);
if (details.year) {
const _el_text_link = _el_wrapper.querySelector("a");
_el_text_link.textContent = `${_el_text_link.textContent} (${details.year})`;
}
if (details.language) {
const el_language = document.createElement("span");
el_language.classList.add("language");
el_language.textContent = details.language.toUpperCase();
el_info.appendChild(el_language);
media.closest("li").classList.add(`${details.language}-language`);
if (details.language !== "en") {
media.closest("li").classList.add("foreign-language");
}
}
if (details.language && details.runtime) {
const el_separator = document.createElement("span");
el_separator.textContent = " | ";
el_info.appendChild(el_separator);
}
if (details.runtime) {
const el_runtime = document.createElement("span");
const runtime = toHoursAndMinutes(details.runtime);
const runtime_class = getRuntimeClass(details.runtime);
el_runtime.classList.add(runtime_class);
el_runtime.textContent = runtime;
el_info.appendChild(el_runtime);
}
const el_separator_rt = document.createElement("span");
el_separator_rt.textContent = " | ";
el_info.appendChild(el_separator_rt);
// console.debug("checkrated", key, media_ratings[key]);
if (media_ratings[key]) {
media.closest("li").classList.add("rated");
media.closest("li").style.backgroundColor = HIGHLIGHT_RATED;
// const el_rating = document.createElement("span");
// el_rating.classList.add("rating");
// el_rating.textContent = media_ratings[key];
// el_info.appendChild(el_rating);
} else {
const el_rating = document.createElement("span");
el_rating.classList.add("no_rating");
el_rating.textContent = "*";
el_info.appendChild(el_rating);
}
// if (shouldGetWatchOptions && !media_ratings[key]) {
if (shouldGetWatchOptions) {
const watchOptions = await getMediaStreamOptions(key);
if (watchOptions) {
media.closest("li").classList.add("streamable");
const el_separator_stream = document.createElement("span");
el_separator_stream.textContent = " | ";
el_info.appendChild(el_separator_stream);
const el_stream = document.createElement("span");
el_stream.classList.add("no_rating");
el_stream.textContent = watchOptions;
el_info.appendChild(el_stream);
}
}
if (watchlist[key]) {
media.closest("li").classList.add("watchlist");
media.closest("li").style.backgroundColor = HIGHLIGHT_WATCHLIST;
// const el_watchlist = document.createElement("span");
// el_watchlist.classList.add("in_watchlist");
// el_watchlist.textContent = "W";
// const el_separator_rt = document.createElement("span");
// el_separator_rt.textContent = " | ";
// el_info.appendChild(el_separator_rt);
// el_info.appendChild(el_watchlist);
}
_el_wrapper.children[0].after(el_info); //appendChild(el_info);
// ADD DELETE AND COMMENT OPTIONS
const _el_title_div = _el_wrapper.querySelector("div");
_el_title_div.innerHTML += "</br>";
// console.debug(_el_wrapper);
// console.debug(_el_title_div);
const _el_comment_button = document.createElement("button");
_el_comment_button.onclick = () => {
add_comment_list(listId, key);
};
_el_comment_button.textContent = "C";
_el_comment_button.classList.add("defaultStyle");
_el_title_div.appendChild(_el_comment_button);
// _el_title_div.innerHTML += ' - ';
const _el_remove_button = document.createElement("button");
_el_remove_button.onclick = () => {
remove_from_list(listId, key);
};
_el_remove_button.textContent = "X";
_el_remove_button.classList.add("defaultStyle");
_el_remove_button.style.float = "right";
_el_title_div.appendChild(_el_remove_button);
}
if (shouldGetWatchOptions) {
saveWatchOptionsMap();
}
addObserver(
"ol.list_items",
"ol.list_items li:not(.visited) a.picture",
improveListPage
);
}
function addToListPageOptions() {
// filter menu
const _el_filters = document.getElementById("dropdown_list_show_me")
.children[0];
// console.debug(_el_filters);
if (document.getElementById("filterForeignOnly")) {
return;
}
const el_adult_only = document.createElement("a");
el_adult_only.id = "filterAdultOnly";
el_adult_only.classList =
"hover:bg-gray-200 rounded-md text-black-600 block text-nowrap whitespace-nowrap px-4 py-2 text-sm lg:text-md";
el_adult_only.textContent = "Adult";
// el_adult_only.href = '#';
_el_filters.appendChild(el_adult_only);
const el_not_adult_only = document.createElement("a");
el_not_adult_only.id = "filterNotAdultOnly";
el_not_adult_only.classList =
"hover:bg-gray-200 rounded-md text-black-600 block text-nowrap whitespace-nowrap px-4 py-2 text-sm lg:text-md";
el_not_adult_only.textContent = "Not Adult";
_el_filters.appendChild(el_not_adult_only);
const el_foreign_only = document.createElement("a");
el_foreign_only.id = "filterForeignOnly";
el_foreign_only.classList =
"hover:bg-gray-200 rounded-md text-black-600 block text-nowrap whitespace-nowrap px-4 py-2 text-sm lg:text-md";
el_foreign_only.textContent = "Language";
// el_foreign_only.href = '#';
_el_filters.appendChild(el_foreign_only);
const el_streamable_only = document.createElement("a");
el_streamable_only.id = "filterStreamableOnly";
el_streamable_only.classList =
"hover:bg-gray-200 rounded-md text-black-600 block text-nowrap whitespace-nowrap px-4 py-2 text-sm lg:text-md";
el_streamable_only.textContent = "Streamable";
// el_foreign_only.href = '#';
_el_filters.appendChild(el_streamable_only);
// document
// .getElementById("filterShowAll")
// .addEventListener("click", ListPageShowAll, false);
document
.getElementById("filterAdultOnly")
.addEventListener("click", ListPageAdultOnly, false);
document
.getElementById("filterNotAdultOnly")
.addEventListener("click", ListPageNotAdultOnly, false);
document
.getElementById("filterForeignOnly")
.addEventListener("click", ListPageForeignOnly, false);
document
.getElementById("filterStreamableOnly")
.addEventListener("click", ListPageStreamableOnly, false);
const el_summaryButton = document.createElement("button");
el_summaryButton.onclick = () => listReport();
el_summaryButton.textContent = "summary";
el_summaryButton.classList.add(
"flex",
"items-center",
"text-md",
"text-white/50",
"font-semibold"
);
const el_longShortButton = document.createElement("button");
el_summaryButton.onclick = () => addToShortAndLongList();
// el_summaryButton.onclick = () => exportRatingsLetterboxd();
el_summaryButton.textContent = "long/short lists";
el_summaryButton.classList.add(
"flex",
"items-center",
"text-md",
"text-white/50",
"font-semibold"
);
// addToShortAndLongList
const _el_buttons_div = document.getElementById("share").closest("div");
_el_buttons_div.appendChild(el_summaryButton);
_el_buttons_div.appendChild(el_longShortButton);
}
function addToListPageItemsOptions() {}
async function improveListPage() {
if (window.location.pathname.endsWith("/edit")) {
return;
}
// TODO - create another function for list view
if (!window.location.search.includes("view=grid")) {
console.debug("NOT GRID");
return;
}
console.debug("improvelistpage");
const media_ratings = await load_tmdb_ratings();
const media_watchlist = await load_tmdb_watchlist();
const tmdb_id = parseInt(
window.location.href
.split("https://www.themoviedb.org/list/")[1]
.split("-")[0]
);
const [list_items, media_details] = await getListItemsAndMedia(tmdb_id);
// TODO: fix function for new layout
// await addRatedToListPage(list_items, media_ratings);
addToListPageOptions();
addToListPageItemsOptions();
await addMediaDetailsToListPage(
media_ratings,
media_details,
media_watchlist,
tmdb_id
);
}
async function improveWatchListPage() {
console.debug("improveWatchListPage");
const media_to_list = await getMediaToListsMap();
const mediaLinks = document.querySelectorAll(
"div.title a:not(.visited):has(h2)"
);
for (const media of mediaLinks) {
// console.debug(media.textContent);
const key = media.pathname.split("/").slice(1).join("-");
media.classList.add("visited");
if (media_to_list[key]) {
const TMDB_lists = [];
media_to_list[key].forEach((item) => {
TMDB_lists.push(item.split("|")[1]);
});
media.querySelector("h2").textContent = `${
media.textContent
} [${TMDB_lists.join(", ")}]`;
}
}
const _el_load_more = document.querySelector("a.load_more");
if (_el_load_more) {
_el_load_more.addEventListener("click", () => {
setTimeout(improveWatchListPage, 1000);
});
}
addObserver(
"div.items_wrapper",
"div.items_wrapper div.title a:not(.visited):has(h2)",
improveWatchListPage
);
}
async function improveSearchPage() {
// console.debug('improve search page');
const media_to_list = await getMediaToListsMap();
// console.debug('>> loaded lists');
const media_ratings = await load_tmdb_ratings();
// console.debug('>> loaded ratings');
const mediaLinks = document.querySelectorAll("a.result:has(h2)");
// console.debug('>> links', mediaLinks);
for (const media of mediaLinks) {
const key = media.pathname.split("/").slice(1).join("-");
if (media_to_list[key]) {
const TMDB_lists = [];
media_to_list[key].forEach((item) => {
TMDB_lists.push(item.split("|")[1]);
});
media.querySelector("h2").textContent = `${
media.textContent
} [${TMDB_lists.join(", ")}]`;
media.closest("div.details").style.backgroundColor = HIGHLIGHT;
// media.parentElement.parentElement.style.backgroundColor = 'aliceblue';
}
if (media_ratings[key]) {
media.querySelector(
"h2"
).textContent = `${media.textContent} ${media_ratings[key]}*`;
// media.parentElement.parentElement.style.backgroundColor = 'aliceblue';
}
}
}
async function improveMediaPage() {
console.debug("improve media page");
const media_to_list = await getMediaToListsMap();
// console.debug(media_to_list);
let content = 0;
let TMDB_id = 0;
let TMDB_lists = [];
if (window.location.href.includes("https://www.themoviedb.org/movie/")) {
content = "movie";
TMDB_id = parseInt(
window.location.href
.split("https://www.themoviedb.org/movie/")[1]
.split("-")[0]
);
} else if (window.location.href.includes("https://www.themoviedb.org/tv/")) {
content = "tv";
TMDB_id = parseInt(
window.location.href
.split("https://www.themoviedb.org/tv/")[1]
.split("-")[0]
);
} else {
return;
}
// (media_to_list[`${content}-${TMDB_id}`] || []).forEach((item) => {
// TMDB_lists.push(item.split("|")[1]);
// });
let el_lists_header = document.createElement("h3");
el_lists_header.innerText = `Lists:`; // ${TMDB_lists.join(", ")}`;
el_lists_header.classList.add("tagline");
(media_to_list[`${content}-${TMDB_id}`] || []).forEach((item) => {
// TMDB_lists.push(item.split("|")[1]);
const el_list_link = document.createElement("a");
el_list_link.textContent = item.split("|")[1];
el_list_link.href = `https://www.themoviedb.org/list/${item.split("|")[0]}`;
el_lists_header.appendChild(el_list_link);
const el_space = document.createElement("span");
el_space.innerHTML = "<span>&nbsp;</span>";
el_lists_header.appendChild(el_space);
const el_list_button = document.createElement("button");
el_list_button.textContent = "x";
el_list_button.onclick = () => {
remove_from_list(item.split("|")[0], `${content}-${TMDB_id}`);
};
el_lists_header.appendChild(el_list_button);
const el_divider = document.createElement("span");
el_divider.innerHTML = "<span>, &nbsp;</span>";
el_lists_header.appendChild(el_divider);
});
let _el_header_info = document.getElementsByClassName("header_info")[0];
if (_el_header_info) {
_el_header_info.insertBefore(el_lists_header, _el_header_info.firstChild);
}
if (TMDB_lists.length) {
let _el_list_icon = document.getElementsByClassName("thumbnails-list")[0];
_el_list_icon.classList.remove("thumbnails-list");
_el_list_icon.classList.add("plus");
}
// console.debug(`${content}-${TMDB_id}`,media_to_list[`${content}-${TMDB_id}`]);
// external lists feature
if (content === "movie") {
const _el_sidebar = document.querySelector("div.column.no_bottom_pad");
const el_lists_div = document.createElement("section");
el_lists_div.setAttribute("id", "listsSection");
el_lists_div.innerHTML = `<section>
<h4 id="externalListsH4">External Lists</h4>
<div id="loadExtListsButtonDiv">
<button id="loadExtLists" class="myButton">Load Lists</button>
</div>
<ul id="extListsUl"></ul>
</section>
`;
// _el_sidebar.appendChild(el_lists_div);
_el_sidebar.insertBefore(el_lists_div, _el_sidebar.children[1]);
document.getElementById("loadExtLists").addEventListener(
"click",
() => {
loadExtLists(TMDB_id, 1);
},
false
);
}
const _el_sidebar = document.querySelector("div.column.no_bottom_pad");
const el_add_to_list_div = document.createElement("section");
el_add_to_list_div.setAttribute("id", "addToListSection");
const el_select_list = document.createElement("select");
el_select_list.id = "addToListSelect";
el_select_list.classList.add("defaultStyle");
const myLists = await load_tmdb_lists();
const orderedLists = Object.entries(myLists)
.map((itm) => {
return { id: itm[0], name: itm[1].name };
})
.sort((a, b) => (a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1));
for (const list of orderedLists) {
var option = document.createElement("option");
option.text = list.name;
option.value = list.id;
el_select_list.add(option);
// el_select_list.add({value:1, text:'name'});
}
el_add_to_list_div.appendChild(el_select_list);
const el_add_to_list_btn = document.createElement("button");
el_add_to_list_btn.textContent = "Add To List";
el_add_to_list_btn.id = "addToListBtn";
el_add_to_list_btn.classList.add("myButton");
el_add_to_list_div.appendChild(el_add_to_list_btn);
// _el_sidebar.appendChild(el_lists_div);
_el_sidebar.insertBefore(el_add_to_list_div, _el_sidebar.children[1]);
document.getElementById("addToListBtn").addEventListener(
"click",
() => {
addMediaToList(
content,
TMDB_id,
document.getElementById("addToListSelect").value
);
},
false
);
}
async function improveKeywordPage() {
console.debug("improveKeywordtPage");
const media_to_list = await getMediaToListsMap();
const media_ratings = await load_tmdb_ratings();
const media_watchlist = await load_tmdb_watchlist();
const mediaLinks = document.querySelectorAll(
"div.items_wrapper div.card:not(.visited) div.details a"
);
// console.debug(mediaLinks);
for (const media of mediaLinks) {
// // console.debug(media.textContent);
const key = media.pathname.split("/").slice(1).join("-");
media.closest("div.card").classList.add("visited");
if (media_to_list[key]) {
const TMDB_lists = [];
media_to_list[key].forEach((item) => {
TMDB_lists.push(item.split("|")[1]);
});
media.querySelector("h2").textContent = `${
media.textContent
} [${TMDB_lists.join(", ")}]`;
if (media_ratings[key]) {
media.closest("div.card").style.backgroundColor = HIGHLIGHT_RATED;
} else if (media_watchlist[key]) {
media.closest("div.card").style.backgroundColor = HIGHLIGHT_WATCHLIST;
}
}
}
addObserver(
"div.items_wrapper",
"div.items_wrapper div.card:not(.visited)",
improveKeywordPage
);
}
async function improveRatingsPage() {
console.debug("improveRatingsPage");
const _el_title_group = document.querySelector("div.title_group");
const el_title = document.createElement("h3");
const el_button = document.createElement("button");
el_button.textContent = "Month Report";
el_button.onclick = () => {
monthlyReport();
};
el_title.appendChild(el_button);
_el_title_group.appendChild(el_title);
}
/*
MAIN
*/
async function main() {
// add global styles
addGlobalStyle(".less_avg { color: red }");
// addGlobalStyle(".language { color: white }");
addGlobalStyle(".shortruntime { color: green }");
// addGlobalStyle(".regruntime { color: black }");
addGlobalStyle(".longruntime { color: red }");
addGlobalStyle("span.list-itm-extra-info { white-space: pre }");
addGlobalStyle("button.myButton { all: revert }");
addGlobalStyle(".defaultStyle { all: revert }");
if (window.location.href.startsWith("https://www.themoviedb.org/movie/")) {
improveMediaPage();
} else if (
window.location.href.startsWith("https://www.themoviedb.org/tv/")
) {
improveMediaPage();
} else if (
window.location.href.startsWith("https://www.themoviedb.org/person/")
) {
improvePersonPage();
} else if (
window.location.href.startsWith("https://www.themoviedb.org/search?")
) {
improveSearchPage();
} else if (
window.location.href.startsWith("https://www.themoviedb.org/list/")
) {
improveListPage();
} else if (
window.location.href.startsWith(
"https://www.themoviedb.org/u/falconsensei/watchlist"
)
) {
improveWatchListPage();
} else if (
window.location.href.startsWith("https://www.themoviedb.org/keyword")
) {
improveKeywordPage();
} else if (
window.location.href.startsWith("https://www.themoviedb.org/u/") &&
window.location.href.includes("ratings")
) {
improveRatingsPage();
}
}
main();
/*
HELPER FUNCTIONS FOR REPORTS
*/
function getMdSummaries(mediaMap) {
const orderedCredits = {};
let mdCredits = "";
let mdLanguages = "";
let mdDecades = "";
let mdGenres = "";
let mdKeywords = "";
for (const job in mediaMap.credits) {
orderedCredits[job] = Object.values(mediaMap.credits[job])
.filter((item) => item.total > 1 || job == "actress")
.sort((a, b) =>
a.total != b.total ? b.total - a.total : a.name < b.name ? -1 : 1
);
if (orderedCredits[job].length == 0) {
delete orderedCredits[job];
continue;
}
mdCredits += ` \n${job}`;
for (const person of orderedCredits[job]) {
mdCredits += `\n- [${person.name}](https://www.themoviedb.org/person/${person.id}) - ${person.total}`;
}
mdCredits += "\n";
}
const orderedLanguages = Object.entries(mediaMap.languages)
.sort((a, b) => (b[1] != a[1] ? b[1] - a[1] : a[0] < b[0] ? -1 : 1))
.map(([key, value]) => {
return { lang: key, total: value };
});
const orderedDecades = Object.entries(mediaMap.decades)
.sort((a, b) => (b[1] != a[1] ? b[1] - a[1] : a[0] < b[0] ? -1 : 1))
.map(([key, value]) => {
return { decade: key, total: value };
});
const orderedKeywords = Object.entries(mediaMap.keywords)
.sort((a, b) => (b[1] != a[1] ? b[1] - a[1] : a[0] < b[0] ? -1 : 1))
.filter(([key, value]) => value > 1)
.map(([key, value]) => {
return { keyword: key, total: value };
});
const orderedGenres = Object.entries(mediaMap.genres)
.sort((a, b) => (b[1] != a[1] ? b[1] - a[1] : a[0] < b[0] ? -1 : 1))
.map(([key, value]) => {
return { genre: key, total: value };
});
for (const language of orderedLanguages) {
mdLanguages += `\n- ${language.lang}: ${language.total}`;
}
for (const decade of orderedDecades) {
mdDecades += `\n- ${decade.decade}: ${decade.total}`;
}
for (const keyword of orderedKeywords) {
mdKeywords += `\n- ${keyword.keyword}: ${keyword.total}`;
}
for (const genre of orderedGenres) {
mdGenres += `\n- ${genre.genre}: ${genre.total}`;
}
return {
// orderedCredits,
mdCredits,
mdLanguages,
mdDecades,
mdGenres,
mdKeywords,
avg_rating:
mediaMap.rating.reduce((a, b) => a + b, 0) / mediaMap.rating.length,
avg_global_rating:
mediaMap.global_rating.reduce((a, b) => a + b, 0) /
mediaMap.global_rating.length,
};
}
/*
MONTHLY REPORT
*/
async function monthlyReport() {
let month = parseInt(prompt("Month"));
if (isNaN(month) || month < 1 || month > 12) {
alert("error", month);
}
month = month - 1;
const year = new Date().getFullYear();
const rated_items = [];
const movieData = {};
const mainData = await fetch_ratings_page("movie", 1);
const pages = mainData.total_pages;
let page = pages;
do {
const data = await fetch_ratings_page("movie", page);
page = page - 1;
// console.debug(data);
for (const item of data.results) {
const rating_date = new Date(item["account_rating"]["created_at"]);
// console.debug(rating_date);
if (
rating_date.getFullYear() === year &&
rating_date.getMonth() === month
) {
rated_items.push(`${item["id"]}=${item["title"]}`);
movieData[item["id"]] = item;
}
// API DOES NOT RETURN SORTED RATINGS;
// else if (rating_date.getMonth() < month) {
// console.debug("break", rating_date.getMonth(), month, item);
// page = 0;
// break;
// }
}
// rating_items.push(...data.results);
} while (page > 0);
// console.debug(rated_items);
alert(rated_items.join("|"));
const toUse = prompt("Items to consider");
const movieIds = toUse.split("|").map((item) => item.split("=")[0]);
// console.debug("movieids", movieIds);
const allMovies = {
languages: {},
decades: {},
rating: [],
global_rating: [],
credits: {},
keywords: {},
genres: {},
mdMovies: "",
};
const ratings = await load_tmdb_ratings();
for (const id of movieIds) {
const itm = await fetch_movie_details_credits(id);
const all_credits = itm["credits"];
// console.debug(id, all_credits, itm);
const cast = all_credits.cast.concat(all_credits.crew);
const decade = itm.release_date
? itm.release_date.substring(0, 3) + "0s"
: "0";
const originalTitle =
itm.title === itm.original_title ? "" : `(${itm.original_title})`;
const movieListItem = `\n- ${itm.title} (${
itm.release_date ? itm.release_date.substring(0, 4) : "?"
}) ${originalTitle} [link](https://www.themoviedb.org/movie/${itm.id})`;
allMovies.mdMovies += movieListItem;
allMovies.decades[decade] = (allMovies.decades[decade] || 0) + 1;
allMovies.languages[itm.original_language] =
(allMovies.languages[itm.original_language] || 0) + 1;
ratings["movie-" + id] && allMovies.rating.push(ratings["movie-" + id]);
allMovies.global_rating.push(itm.vote_average);
for (const genre of itm.genres) {
allMovies.genres[genre.name] = (allMovies.genres[genre.name] || 0) + 1;
}
for (const keyword of itm.keywords.keywords) {
allMovies.keywords[keyword.name] =
(allMovies.keywords[keyword.name] || 0) + 1;
}
for (const person of cast) {
const tempJob = (
person.job ||
(person.character ? "acting" : `unknown-${person.department}`)
).toLowerCase();
const job =
tempJob === "acting"
? person.gender == 1
? "actress"
: "actor"
: tempJob;
// console.debug(job, !CREW_ROLES.includes(job))
if (!CREW_ROLES.includes(job) && !job.startsWith("unknown-")) {
continue;
}
const creditDetails = {
id: person.id,
total: 0,
profile_path: person.profile_path,
gender: person.gender,
known_for_department: person.known_for_department,
department: person.department,
name: person.name,
original_name: person.original_name,
};
if (!allMovies.credits[job]) {
allMovies.credits[job] = {};
}
if (!allMovies.credits[job][person.id]) {
allMovies.credits[job][person.id] = { ...creditDetails };
}
allMovies.credits[job][person.id].total += 1;
}
}
console.debug(allMovies.credits);
const allMovieSummaries = getMdSummaries(allMovies);
const allData = {
// rated: {
avg_rating: allMovies.avg_rating,
avg_global_rating: allMovies.avg_global_rating,
mdMovies: allMovies.mdMovies,
...allMovieSummaries,
// },
};
console.debug(allData);
}
/*
LIST REPORT
*/
async function listReport() {
const listId = parseInt(
window.location.href
.split("https://www.themoviedb.org/list/")[1]
.split("-")[0]
);
// const [list_items, media_details] = await getListItemsAndMedia(tmdb_id);
const lists = await load_tmdb_lists();
const ratings = await load_tmdb_ratings();
const movieIds = lists[listId].items
.filter((itm) => itm.split("-")[0] === "movie")
.map((itm) => itm.split("-")[1]);
// lists[listId].items
// const movieData = {};
const allMovies = {
languages: {},
decades: {},
rating: [],
global_rating: [],
credits: {},
keywords: {},
genres: {},
mdMovies: "",
};
const ratedMovies = {
languages: {},
decades: {},
rating: [],
global_rating: [],
credits: {},
keywords: {},
genres: {},
mdMovies: "",
};
for (const id of movieIds) {
const itm = await fetch_movie_details_credits(id);
const all_credits = itm["credits"];
const cast = all_credits.cast.concat(all_credits.crew);
const decade = itm.release_date
? itm.release_date.substring(0, 3) + "0s"
: "0";
const originalTitle =
itm.title === itm.original_title ? "" : `(${itm.original_title})`;
const movieListItem = `\n- ${itm.title} (${
itm.release_date ? itm.release_date.substring(0, 4) : "?"
}) ${originalTitle} [link](https://www.themoviedb.org/movie/${itm.id})`;
allMovies.mdMovies += movieListItem;
allMovies.decades[decade] = (allMovies.decades[decade] || 0) + 1;
allMovies.languages[itm.original_language] =
(allMovies.languages[itm.original_language] || 0) + 1;
ratings["movie-" + id] && allMovies.rating.push(ratings["movie-" + id]);
allMovies.global_rating.push(itm.vote_average);
if (ratings["movie-" + id]) {
ratedMovies.mdMovies += movieListItem;
ratedMovies.decades[decade] = (ratedMovies.decades[decade] || 0) + 1;
ratedMovies.languages[itm.original_language] =
(ratedMovies.languages[itm.original_language] || 0) + 1;
ratings["movie-" + id] && ratedMovies.rating.push(ratings["movie-" + id]);
ratedMovies.global_rating.push(itm.vote_average);
}
for (const genre of itm.genres) {
allMovies.genres[genre.name] = (allMovies.genres[genre.name] || 0) + 1;
if (ratings["movie-" + id]) {
ratedMovies.genres[genre.name] =
(ratedMovies.genres[genre.name] || 0) + 1;
}
}
for (const keyword of itm.keywords.keywords) {
allMovies.keywords[keyword.name] =
(allMovies.keywords[keyword.name] || 0) + 1;
if (ratings["movie-" + id]) {
ratedMovies.keywords[keyword.name] =
(ratedMovies.keywords[keyword.name] || 0) + 1;
}
}
for (const person of cast) {
const tempJob = (
person.job ||
(person.character ? "acting" : `unknown-${person.department}`)
).toLowerCase();
const job =
tempJob === "acting"
? person.gender == 1
? "actress"
: "actor"
: tempJob;
// console.debug(job, !CREW_ROLES.includes(job))
if (!CREW_ROLES.includes(job) && !job.startsWith("unknown-")) {
continue;
}
const creditDetails = {
id: person.id,
total: 0,
profile_path: person.profile_path,
gender: person.gender,
known_for_department: person.known_for_department,
department: person.department,
name: person.name,
original_name: person.original_name,
};
if (!allMovies.credits[job]) {
allMovies.credits[job] = {};
}
if (!allMovies.credits[job][person.id]) {
allMovies.credits[job][person.id] = { ...creditDetails };
}
allMovies.credits[job][person.id].total += 1;
if (ratings["movie-" + id]) {
if (!ratedMovies.credits[job]) {
ratedMovies.credits[job] = {};
}
if (!ratedMovies.credits[job][person.id]) {
ratedMovies.credits[job][person.id] = { ...creditDetails };
}
ratedMovies.credits[job][person.id].total += 1;
}
}
}
const allMovieSummaries = getMdSummaries(allMovies);
const ratedMovieSummaries = getMdSummaries(ratedMovies);
const allData = {
rated: {
avg_rating: ratedMovies.avg_rating,
avg_global_rating: ratedMovies.avg_global_rating,
mdMovies: ratedMovies.mdMovies,
...ratedMovieSummaries,
},
// all: {
// ...allMovies,
// ...allMovieSummaries,
// },
};
// console.debug(credits);
console.debug(allData);
}
/*
OTHER FUNCTIONS
*/
async function addToShortAndLongList() {
// LONG_MOVIES_LIST, SHORT_MOVIES_LIST
const lists = await load_tmdb_lists();
// all stored media details are from my lists, so can use that directly;
const mediaDetails = await getMediaDetails();
const moviesToSkip = [];
// .concat is not working??
moviesToSkip.push(
...lists[LONG_MOVIES_LIST].items
.filter((item) => item.startsWith("movie-"))
.map((item) => item.split("-")[1])
);
moviesToSkip.push(
...lists[SHORT_MOVIES_LIST].items
.filter((item) => item.startsWith("movie-"))
.map((item) => item.split("-")[1])
);
// console.debug(moviesToSkip);
// console.debug(mediaDetails);
const addToLong = [];
const addToShort = [];
for (const [key, item] of Object.entries(mediaDetails)) {
// console.debug(key, item);
const [type, id] = key.split("-");
// console.debug("details:", key, type, id, item);
if (type !== "movie" || moviesToSkip.includes(id)) {
// console.debug("skipping", type, id);
continue;
}
// console.debug(item);
if (item.runtime <= SHORT_DURATION) {
// console.debug("add to short", item);
addToShort.push({ media_type: "movie", media_id: id });
} else if (item.runtime >= LONG_DURATION) {
// console.debug("add to long", item);
addToLong.push({ media_type: "movie", media_id: id });
}
}
console.debug("long", addToLong);
console.debug("short", addToShort);
addToShort.length && (await add_to_tmdb_list(addToShort, SHORT_MOVIES_LIST));
addToLong.length && (await add_to_tmdb_list(addToLong, LONG_MOVIES_LIST));
}
async function exportRatingsLetterboxd() {
const ratings = await load_tmdb_ratings();
let exportedRatings = "tmdbID,Rating10\n";
for (const [key, value] of Object.entries(ratings)) {
// console.debug(key, item);
const [type, id] = key.split("-");
if (type !== "movie") {
continue;
}
exportedRatings += `${id},${value}\n`;
}
navigator.clipboard.writeText(exportedRatings);
alert(exportedRatings);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment