Displays IMDB ratings next to movie titles on AMC Theatres website
Size
7.4 KB
Version
1.1.2
Created
Dec 30, 2025
Updated
about 1 month ago
1// ==UserScript==
2// @name AMC Movies IMDB Rating Display
3// @description Displays IMDB ratings next to movie titles on AMC Theatres website
4// @version 1.1.2
5// @match https://*.amctheatres.com/*
6// @icon https://www.amctheatres.com/favicon.ico
7// @grant GM.xmlhttpRequest
8// @grant GM.getValue
9// @grant GM.setValue
10// @connect www.omdbapi.com
11// ==/UserScript==
12(function() {
13 'use strict';
14
15 // OMDb API key (free tier - 1000 requests per day)
16 const OMDB_API_KEY = '4e3ee3be'; // Public demo key
17 const OMDB_API_URL = 'https://www.omdbapi.com/';
18
19 // Cache to store ratings and avoid duplicate API calls
20 const ratingsCache = {};
21
22 console.log('AMC IMDB Rating Extension loaded');
23
24 // Function to fetch IMDB rating from OMDb API
25 async function fetchIMDBRating(movieTitle) {
26 // Check memory cache first
27 if (ratingsCache[movieTitle]) {
28 console.log(`Using memory cache for: ${movieTitle}`);
29 return ratingsCache[movieTitle];
30 }
31
32 // Check persistent storage cache
33 const cacheKey = `imdb_rating_${movieTitle}`;
34 const cachedRating = await GM.getValue(cacheKey, null);
35
36 if (cachedRating !== null) {
37 console.log(`Using stored cache for: ${movieTitle}`);
38 const rating = JSON.parse(cachedRating);
39 ratingsCache[movieTitle] = rating;
40 return rating;
41 }
42
43 return new Promise((resolve) => {
44 console.log(`Fetching IMDB rating for: ${movieTitle}`);
45
46 GM.xmlhttpRequest({
47 method: 'GET',
48 url: `${OMDB_API_URL}?apikey=${OMDB_API_KEY}&t=${encodeURIComponent(movieTitle)}&type=movie`,
49 onload: function(response) {
50 try {
51 console.log(`Response status for ${movieTitle}:`, response.status);
52 console.log(`Response text for ${movieTitle}:`, response.responseText);
53
54 const data = JSON.parse(response.responseText);
55 console.log(`Data for ${movieTitle}:`, data);
56
57 if (data.Response === 'True' && data.imdbRating && data.imdbRating !== 'N/A') {
58 const rating = {
59 imdbRating: data.imdbRating,
60 imdbVotes: data.imdbVotes
61 };
62 ratingsCache[movieTitle] = rating;
63 // Store in persistent cache
64 GM.setValue(cacheKey, JSON.stringify(rating));
65 console.log(`Found rating for ${movieTitle}: ${rating.imdbRating}`);
66 resolve(rating);
67 } else {
68 console.log(`No rating found for: ${movieTitle}`);
69 ratingsCache[movieTitle] = null;
70 // Store null result to avoid repeated failed lookups
71 GM.setValue(cacheKey, JSON.stringify(null));
72 resolve(null);
73 }
74 } catch (error) {
75 console.error(`Error parsing response for ${movieTitle}:`, error);
76 resolve(null);
77 }
78 },
79 onerror: function(error) {
80 console.error(`Network error for ${movieTitle}:`, error);
81 resolve(null);
82 },
83 ontimeout: function() {
84 console.error(`Timeout for ${movieTitle}`);
85 resolve(null);
86 }
87 });
88 });
89 }
90
91 // Function to create and insert rating badge
92 function createRatingBadge(rating, votes) {
93 const badge = document.createElement('span');
94 badge.className = 'imdb-rating-badge';
95 badge.style.cssText = `
96 display: inline-flex;
97 align-items: center;
98 gap: 4px;
99 background: #f5c518;
100 color: #000;
101 padding: 4px 8px;
102 border-radius: 4px;
103 font-weight: bold;
104 font-size: 14px;
105 margin-left: 8px;
106 vertical-align: middle;
107 `;
108
109 // Create star icon
110 const star = document.createElement('span');
111 star.textContent = '⭐';
112 star.style.fontSize = '12px';
113
114 // Create rating text
115 const ratingText = document.createElement('span');
116 ratingText.textContent = rating;
117
118 badge.appendChild(star);
119 badge.appendChild(ratingText);
120
121 // Add tooltip with vote count
122 if (votes) {
123 badge.title = `IMDB Rating: ${rating}/10 (${votes} votes)`;
124 } else {
125 badge.title = `IMDB Rating: ${rating}/10`;
126 }
127
128 return badge;
129 }
130
131 // Function to add ratings to movie titles
132 async function addRatingsToMovies() {
133 console.log('Searching for movie titles...');
134
135 // Find all movie title links
136 const movieLinks = document.querySelectorAll('a.headline.text-500[aria-label*="details"]');
137 console.log(`Found ${movieLinks.length} movie titles`);
138
139 for (const link of movieLinks) {
140 // Skip if already processed
141 if (link.dataset.imdbProcessed === 'true') {
142 continue;
143 }
144
145 const movieTitle = link.textContent.trim();
146 console.log(`Processing: ${movieTitle}`);
147
148 // Mark as processed
149 link.dataset.imdbProcessed = 'true';
150
151 // Fetch rating
152 const ratingData = await fetchIMDBRating(movieTitle);
153
154 if (ratingData && ratingData.imdbRating) {
155 // Create and insert rating badge
156 const badge = createRatingBadge(ratingData.imdbRating, ratingData.imdbVotes);
157 link.appendChild(badge);
158 console.log(`Added rating badge for: ${movieTitle}`);
159 }
160
161 // Small delay to avoid overwhelming the API
162 await new Promise(resolve => setTimeout(resolve, 300));
163 }
164
165 console.log('Finished processing all movies');
166 }
167
168 // Debounce function to avoid excessive calls
169 function debounce(func, wait) {
170 let timeout;
171 return function executedFunction(...args) {
172 const later = () => {
173 clearTimeout(timeout);
174 func(...args);
175 };
176 clearTimeout(timeout);
177 timeout = setTimeout(later, wait);
178 };
179 }
180
181 // Initialize when page is ready
182 function init() {
183 console.log('Initializing AMC IMDB Rating Extension...');
184
185 // Wait for page to be fully loaded
186 if (document.readyState === 'loading') {
187 document.addEventListener('DOMContentLoaded', () => {
188 setTimeout(addRatingsToMovies, 1000);
189 });
190 } else {
191 setTimeout(addRatingsToMovies, 1000);
192 }
193
194 // Watch for dynamic content changes (infinite scroll, filters, etc.)
195 const debouncedAddRatings = debounce(addRatingsToMovies, 1000);
196 const observer = new MutationObserver(debouncedAddRatings);
197
198 observer.observe(document.body, {
199 childList: true,
200 subtree: true
201 });
202
203 console.log('Observer set up for dynamic content');
204 }
205
206 // Start the extension
207 init();
208})();