// ==UserScript== // @name Letterboxd Friend Ratings Analyzer // @version 3.8 // @description Show ratings by your friends in a histogram, similar to the one Letterboxd shows for all user ratings. // @match https://letterboxd.com/film/* // @run-at document-end // @namespace http://tampermonkey.net/ // @downloadURL https://update.greasyfork.icu/scripts/509173/Letterboxd%20Friend%20Ratings%20Analyzer.user.js // @updateURL https://update.greasyfork.icu/scripts/509173/Letterboxd%20Friend%20Ratings%20Analyzer.meta.js // ==/UserScript== const username = "YOUR_USERNAME_HERE"; const film = location.pathname.split('/').at(-2); const ratingString = (count) => count === 1 ? "rating" : "ratings"; const fetchRatings = (user, film) => fetch(`/${user}/friends/film/${film}/ratings/rated/.5-5/`) .then(response => response.text()) .then(html => Array.from(new DOMParser().parseFromString(html, 'text/html').querySelectorAll(".film-rating-group")) .flatMap(section => { const [, score] = section.querySelector("h2 > .rating")?.className.match(/rated-large-(\d+)/) || []; const reviewCount = section.querySelectorAll("ul.avatar-list > li").length; return score ? Array(reviewCount).fill(parseFloat(score) / 2) : []; }) ); const constructHistogram = (ratings, user, film) => { const bins = Array(10).fill(0); ratings.forEach(rating => bins[Math.floor((rating - 0.5) * 2)]++); const maxCount = Math.max(...bins); const totalRatings = ratings.length; const averageRating = (ratings.reduce((sum, rating) => sum + rating, 0) / totalRatings).toFixed(1); // SVGs copied from the current Letterboxd HTML structure const startSvg = ``; const endSvg = ``; const friendsRatingsLink = `/${user}/friends/film/${film}/rated/.5-5/`; const rows = bins.map((count, index) => { const ratingVal = (index + 1) * 0.5; const starCount = Math.floor(ratingVal); const isHalf = ratingVal % 1 !== 0; // Generate label (half-★, ★, ★½, etc.) let label = ''; if (ratingVal === 0.5) label = 'half-★'; else { label = '★'.repeat(starCount); if (isHalf) label += '½'; } // Generate URL segment (½, 1, 1½, etc.) let urlRating; if (ratingVal === 0.5) urlRating = '%C2%BD'; else if (isHalf) urlRating = `${starCount}%C2%BD`; else urlRating = ratingVal.toString(); const ratingLink = `/${user}/friends/film/${film}/rated/${urlRating}/`; const percentage = totalRatings > 0 ? ((count / totalRatings) * 100).toFixed(0) : 0; const value = maxCount > 0 ? (count / maxCount) : 0; // Use standard 'title' attribute for simple HTML tooltip const titleText = `${count.toLocaleString()} ${label} ${ratingString(count)} (${percentage}%)`; const srOnlyText = `${count.toLocaleString()} (${percentage}%)`; return `