TMDB load unloadable images

Force TMDB to load unloaded images

  1. // ==UserScript==
  2. // @name TMDB load unloadable images
  3. // @namespace https://github.com/Tetrax-10
  4. // @description Force TMDB to load unloaded images
  5. // @icon https://www.google.com/s2/favicons?sz=64&domain=themoviedb.org
  6. // @license MIT
  7. // @version 1.4
  8. // @match *://*.themoviedb.org/*
  9. // @run-at document-idle
  10. // ==/UserScript==
  11.  
  12. ;(() => {
  13. function changeSrc(img) {
  14. if (img.src.includes("media.themoviedb.org") && [".jpg", ".png", ".webp"].some((e) => img.src.endsWith(e)) && !img.naturalHeight) {
  15. // Get the resolution and id of the image
  16. const resolution = img.src.match(/\/t\/p\/([^\/]+)\/[^\/]+$/)?.[1] ?? ""
  17. const id = img.src.split("/").pop()
  18.  
  19. img.src = `https://image.tmdb.org/t/p/${resolution}/${id}`
  20. img.removeAttribute("srcset")
  21. }
  22. }
  23.  
  24. // Function to handle intersection events
  25. function handleIntersection(entries, observer) {
  26. entries.forEach((entry) => {
  27. if (entry.isIntersecting) {
  28. // Check if the element is visible in the viewport
  29. const imgElement = entry.target
  30. changeSrc(imgElement) // Change the image source if it's unloaded
  31. observer.unobserve(imgElement) // Stop observing the current image after it's logged
  32. }
  33. })
  34. }
  35.  
  36. async function waitForElement(selector, timeout = null, nthElement = 1) {
  37. // wait till document body loads
  38. while (!document.body) {
  39. await new Promise((resolve) => setTimeout(resolve, 10))
  40. }
  41.  
  42. nthElement -= 1
  43.  
  44. return new Promise((resolve) => {
  45. if (document.querySelectorAll(selector)?.[nthElement]) {
  46. return resolve(document.querySelectorAll(selector)?.[nthElement])
  47. }
  48.  
  49. const observer = new MutationObserver(async () => {
  50. if (document.querySelectorAll(selector)?.[nthElement]) {
  51. resolve(document.querySelectorAll(selector)?.[nthElement])
  52. observer.disconnect()
  53. } else {
  54. if (timeout) {
  55. async function timeOver() {
  56. return new Promise((resolve) => {
  57. setTimeout(() => {
  58. observer.disconnect()
  59. resolve(undefined)
  60. }, timeout)
  61. })
  62. }
  63. resolve(await timeOver())
  64. }
  65. }
  66. })
  67.  
  68. observer.observe(document.body, {
  69. childList: true,
  70. subtree: true,
  71. })
  72. })
  73. }
  74.  
  75. function changeDivSrc(div) {
  76. // Get the background image URL from the div
  77. const bgImageUrl = getComputedStyle(div).backgroundImage
  78.  
  79. // Extract the URL from the `url('...')` syntax
  80. const matches = bgImageUrl.match(/url\(['"]?(.*?)['"]?\)/)
  81. if (matches && matches[1]) {
  82. const url = matches[1]
  83.  
  84. // Create a new Image object to check if it loads successfully
  85. const img = new Image()
  86. img.onerror = () => {
  87. if (url.includes("media.themoviedb.org") && [".jpg", ".png", ".webp"].some((e) => url.endsWith(e))) {
  88. // Get the resolution and id of the image
  89. const resolution = url.match(/\/t\/p\/([^\/]+)\/[^\/]+$/)?.[1] ?? ""
  90. const id = url.split("/").pop()
  91.  
  92. div.style.backgroundImage = bgImageUrl.replace(
  93. /https?:\/\/[^\s]+?\.(jpg|png|webp)/,
  94. `https://image.tmdb.org/t/p/${resolution}/${id}`
  95. )
  96. }
  97. }
  98.  
  99. // Set the src to the extracted URL
  100. img.src = url
  101. } else {
  102. console.log("No valid background image URL found.")
  103. }
  104. }
  105.  
  106. // Create a new IntersectionObserver instance
  107. const observer = new IntersectionObserver(handleIntersection, {
  108. root: null, // Observe the viewport
  109. rootMargin: "0px", // No margin around the viewport
  110. threshold: 0.1, // Trigger when at least 10% of the image is visible
  111. })
  112.  
  113. // Select all img elements and observe them
  114. document.querySelectorAll("img").forEach((img) => {
  115. observer.observe(img)
  116. })
  117.  
  118. // Create a MutationObserver to listen for new img elements
  119. const mutationObserver = new MutationObserver((mutationsList) => {
  120. mutationsList.forEach((mutation) => {
  121. // Check if nodes are added
  122. if (mutation.type === "childList") {
  123. mutation.addedNodes.forEach((node) => {
  124. if (node.tagName === "IMG") {
  125. console.log("New img element added:", node)
  126. observer.observe(node) // Observe the new img
  127. }
  128. // Check for img elements inside added elements
  129. if (node.nodeType === Node.ELEMENT_NODE) {
  130. node.querySelectorAll("img").forEach((img) => {
  131. console.log("New img element added:", node)
  132. observer.observe(img)
  133. })
  134. }
  135. })
  136. }
  137. })
  138. })
  139.  
  140. // Start observing the body for changes
  141. mutationObserver.observe(document.body, {
  142. childList: true, // Listen for direct children changes
  143. subtree: true, // Include all descendants
  144. })
  145.  
  146. // Change non img elements
  147. const nonImgEle = [".header"]
  148.  
  149. for (const ele of nonImgEle) {
  150. waitForElement(ele, 10000).then((ele) => {
  151. changeDivSrc(ele)
  152. })
  153. }
  154. })()