├── .github └── ISSUE_TEMPLATE │ ├── report-a-bug-.md │ └── request-a-feature-.md ├── AdjustImagePreviews.css ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── UncompressedImages.plugin.js └── UncompressedImagesConsoleVer.js /.github/ISSUE_TEMPLATE/report-a-bug-.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report a bug. 3 | about: Create a report to help us improve 4 | title: "[BUG] - " 5 | labels: bug 6 | assignees: Knewest 7 | 8 | --- 9 | 10 | **Describe the bug:** 11 | A clear and concise description of what the bug is. 12 | 13 | **How to reproduce:** 14 | Steps to reproduce the behaviour: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behaviour:** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots:** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Version of Discord:** 27 | Regular Discord, Canary, PTB? 28 | Version too, if you know how to check. 29 | 30 | **Version of Plugin:** 31 | Confirm the version of the plugin. 32 | 33 | **Additional context:** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/request-a-feature-.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Request a feature. 3 | about: Suggest an idea for this project 4 | title: "[FEATURE RQST] - " 5 | labels: enhancement, question 6 | assignees: Knewest 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /AdjustImagePreviews.css: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | [Knewest](https://github.com/Knewest) 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Uncompressed Discord Images Fix: 2 |

Discord's solution to previewing images is awful so by changing 'media.discordapp.net' 3 | links to 'cdn.discordapp.com' links, we will no longer have blurry images (especially with JPEG, WebP and other lossy formats). 4 |

5 | Another feature of this plugin is that it makes animated PNGs uploaded to the Discord server playable within the client.
6 | Discord has broken APNG support completely when they addressed the 'aCropalypse' vulnerbility.

7 | 8 | This is compatible with BetterDiscord as a plugin and/or can be executed in the console of the client/browser.
9 |
Download from GitHub 10 | Download the latest version 11 | Use the console version 12 | 13 | 14 |
15 | 16 | ## Comparisons: 17 | 18 | |**Image format:**|**Before fix:**|**After fix:**| 19 | |:---:|:---:|:---:| 20 | |**PNG**|![PNGComparisonLQ1](https://cdn.discordapp.com/attachments/753561208073879642/1101597065953431652/wVUDsCCrsD_LQ_PNG.webp)|![PNGComparisonHQ1](https://cdn.discordapp.com/attachments/753561208073879642/1101597074463674449/wVUDsCCrsD_HQ_PNG.webp)| 21 | |**JPEG**|![JPGComparisonLQ1](https://user-images.githubusercontent.com/94736474/224312885-cb26264e-e2d0-404f-ae50-395bf81a54f1.png)|![JPGComparisonHQ1](https://user-images.githubusercontent.com/94736474/224315183-e5e5dc8b-16f7-4072-8fae-45a4ed904a21.png)| 22 | |**JPEG**|![JPGComparisonLQ2](https://user-images.githubusercontent.com/94736474/224315861-7ce2defa-ecaa-47be-8a14-a678aa71cc03.png)|![JPGComparisonHQ2](https://user-images.githubusercontent.com/94736474/224315883-0b9c87fa-7144-4916-ba07-67a0f5dc4c80.png)| 23 | |**WebP**|![WebPComparisonLQ1](https://user-images.githubusercontent.com/94736474/224316202-2410e3c6-8b3d-4784-aea8-7dee2ea36edd.png)|![WebPComparisonHQ1](https://user-images.githubusercontent.com/94736474/224316220-cb74424b-1ee3-4de7-85c4-444fa6703327.png)| 24 | |**WebP**|![WebPComparisonLQ2](https://user-images.githubusercontent.com/94736474/224316543-be26756c-320c-4212-b911-e6caba186644.png)|![WebPComparisonHQ2](https://user-images.githubusercontent.com/94736474/224316584-d61ca0af-5f3e-480b-a357-ffce329267b8.png)| 25 | |**WebP**|![WebPComparisonLQ3](https://user-images.githubusercontent.com/94736474/224316809-f7af7946-d7b7-42ce-a408-8789c9b87a1d.png)|![WebPComparisonHQ3](https://user-images.githubusercontent.com/94736474/224316830-f02e485f-8330-435e-b953-cf527fc4f17c.png)| 26 | 27 | To see the best comparison, open a before/after in a new tab and flip between the two (they are lined up for you). 28 | 29 |
30 | 31 | ### Final Comments: 32 | Something to note with these comparisons is that unless open the image in a browser, you will *never* see the original, uncompressed image; it is *always* a lightly or heavily compressed (depends on the format) version of the original. 33 | 34 | This means that if you click on the image, even though the quality is a bit better, it is still compressed and not what the person sent, this is most prevelant with JPEG and WebP-- hence the examples provided. 35 | 36 | ---------------------------------------------------- 37 | 38 | ### Help: 39 | Need help? Join the [support server](https://discord.gg/NqqqzajfK4) (NqqqzajfK4). 40 | 41 | ---------------------------------------------------- 42 | 43 | ### Simplifying BetterDiscord: 44 | For a guide on making BetterDiscord easier to install and use, check out this link: https://gist.github.com/Knewest/ee59d3960e18e6d813c9221b54b36ab1
45 | If you find this challenging, please follow the regular installation procedure: https://betterdiscord.app/ 46 | -------------------------------------------------------------------------------- /UncompressedImages.plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Uncompressed Images 3 | * @author Knew 4 | * @description Discord's solution to previewing images is awful so by changing 'media.discordapp.net' links to 'cdn.discordapp.com' links, we will no longer have blurry images (especially with JPEG 1, WebP, and other lossy formats). 5 | * @version 3.32 6 | * @authorId 332116671294734336 7 | * @authorLink https://github.com/Knewest 8 | * @invite NqqqzajfK4 9 | * @website https://twitter.com/KnewestLSEP 10 | * @source https://github.com/Knewest/Uncompressed-Discord-Images 11 | * @updateUrl https://raw.githubusercontent.com/Knewest/Uncompressed-Discord-Images/main/UncompressedImages.plugin.js 12 | * @changelog {banner} https://betterdiscord.app/resources/thumbnails/1284.png 13 | * @changelog {blurb} Missed or want to know previous changelogs? Find them [here](https://github.com/Knewest/Uncompressed-Discord-Images/releases). 14 | * @changelog {fixed.item} Fixed the image zoom with 'ImageUtilities' by DevilBro not reliably uncompressing the image. 15 | * @changelog {fixed.item} Fixed up old code. 16 | * @changelog {footer} Need help? Join my the [support server (NqqqzajfK4)](https://discord.gg/NqqqzajfK4). 17 | */ 18 | 19 | function debounce(func, wait) { 20 | let timeout; 21 | return function(...args) { 22 | const context = this; 23 | clearTimeout(timeout); 24 | timeout = setTimeout(() => func.apply(context, args), wait); 25 | }; 26 | } 27 | 28 | module.exports = class UncompressedImages { 29 | constructor() { 30 | this.observer = null; 31 | this.resizeListener = null; 32 | this.animationFrame = null; 33 | } 34 | 35 | start() { 36 | 37 | const config = { 38 | attributes: true, 39 | childList: true, 40 | subtree: true, 41 | attributeFilter: ['src'], 42 | }; 43 | 44 | const localObserver = new MutationObserver(callback); 45 | // ----------------- // 46 | function updateGridLayoutClass() { 47 | const twoByTwoGridElements = document.querySelectorAll('.twoByTwoGrid_f4758a'); 48 | twoByTwoGridElements.forEach((element) => { 49 | element.classList.remove('twoByTwoGrid_f4758a'); 50 | element.classList.add('threeByThreeGrid_f4758a'); 51 | element.style.gridTemplateColumns = 'repeat(2, 1fr)'; 52 | 53 | addClassToChildren(element, 'oneByTwoSoloItem_f4758a'); 54 | }); 55 | 56 | const threeByThreeGridElements = document.querySelectorAll('.threeByThreeGrid_f4758a'); 57 | threeByThreeGridElements.forEach((element) => { 58 | if (!element.classList.contains('original-threeByThreeGrid')) { 59 | addClassToChildren(element, 'oneByTwoSoloItem_f4758a'); 60 | } 61 | }); 62 | } 63 | 64 | function addClassToChildren(parentElement, className) { 65 | const childElements = parentElement.children; 66 | for (let i = 0; i < childElements.length; i++) { 67 | childElements[i].classList.add(className); 68 | } 69 | } 70 | 71 | function adjustHeightBasedOnNearestVerticalResolution() { 72 | let debounceTimer; 73 | const debounceDelay = 2000; 74 | 75 | const resizeObserver = new ResizeObserver((entries) => { 76 | const reversedEntries = Array.from(entries).reverse(); 77 | let delay = 0; 78 | 79 | reversedEntries.forEach((entry) => { 80 | setTimeout(() => { 81 | const element = entry.target; 82 | const nearestGridItem = element.closest( 83 | '.oneByTwoGridItem_f4758a, ' + 84 | '.oneByTwoGrid_f4758a.oneByTwoLayoutThreeGrid_f4758a .oneByTwoSoloItem_f4758a, ' + 85 | '.twoByOneGridItem_f4758a, ' + 86 | '.oneByOneGrid_f4758a.oneByOneGridMosaic_f4758a, ' + 87 | '.threeByThreeGrid_f4758a .oneByTwoSoloItem_f4758a, ' + 88 | '.oneByTwoGrid_f4758a .oneByTwoGridItem_f4758a' 89 | ); 90 | 91 | if (nearestGridItem) { 92 | const renderedHeight = nearestGridItem.getBoundingClientRect().height; 93 | if (renderedHeight >= 10) { 94 | element.style.height = `${renderedHeight}px`; 95 | } 96 | } 97 | }, delay); 98 | delay += 25; 99 | }); 100 | 101 | clearTimeout(debounceTimer); 102 | debounceTimer = setTimeout(() => { 103 | centerImageUponWindowResize(); 104 | }, debounceDelay); 105 | }); 106 | 107 | const elementsToObserve = document.querySelectorAll('.clickableWrapper_af017a, .loadingOverlay_af017a'); 108 | elementsToObserve.forEach((element) => { 109 | resizeObserver.observe(element); 110 | 111 | setTimeout(() => { 112 | resizeObserver.unobserve(element); 113 | }, 2000); 114 | }); 115 | } 116 | // ----------------- // 117 | // ----------------- // 118 | function centerImageBecauseRegularCSSWillNot() { 119 | const updateImagePositions = document.querySelectorAll( 120 | '.imageContainer__0f481 .lazyImg_f4758a.processed-image.processed-grid-layout:not(.uncompressedImagesCentered)' 121 | ); 122 | const imagesArray = Array.from(updateImagePositions).reverse(); 123 | let delay = 0; 124 | 125 | imagesArray.forEach((image) => { 126 | setTimeout(() => { 127 | const container = image.closest( 128 | '.oneByTwoGridItem_f4758a, ' + 129 | '.oneByTwoGrid_f4758a.oneByTwoLayoutThreeGrid_f4758a .oneByTwoSoloItem_f4758a, ' + 130 | '.oneByTwoSoloItem_f4758a, ' + 131 | '.twoByOneGridItem_f4758a, ' + 132 | '.oneByOneGrid_f4758a.oneByOneGridMosaic_f4758a, ' + 133 | '.oneByTwoGrid_f4758a .oneByTwoGridItem_f4758a' 134 | ); 135 | 136 | if (container && image) { 137 | if (container.matches('.threeByThreeGrid_f4758a .oneByTwoSoloItem_f4758a')) { 138 | container.style.maxHeight = '175px'; 139 | image.classList.add('uncompressedImagesCentered'); 140 | } 141 | 142 | const containerHeight = container.clientHeight; 143 | const originalImageHeight = image.clientHeight; 144 | const scaleFactor = Math.max(1, containerHeight / originalImageHeight); 145 | 146 | image.style.transform = `scale(${scaleFactor})`; 147 | image.style.transformOrigin = 'top'; 148 | image.offsetHeight; // Forcing the reflow - Knew 149 | 150 | const scaledImageHeight = scaleFactor === 1 ? originalImageHeight : originalImageHeight * scaleFactor; 151 | const translateY = (containerHeight - scaledImageHeight) / 2; 152 | 153 | image.style.transform += ` translateY(${translateY}px)`; 154 | image.classList.add('uncompressedImagesCentered'); 155 | } 156 | }, delay); 157 | delay += 100; 158 | }); 159 | 160 | setTimeout(adjustHeightBasedOnNearestVerticalResolution, 10); 161 | setTimeout(centerImageUponWindowResize, 2000); 162 | setTimeout(adjustHeightBasedOnNearestVerticalResolution, 4500); 163 | } 164 | 165 | function centerImageUponWindowResize() { // Reminder: The function above is NOT the same. - Knew 166 | const updateImagePositions = document.querySelectorAll( 167 | '.imageContainer__0f481 .lazyImg_f4758a.processed-image.processed-grid-layout' 168 | ); 169 | const imagesArray = Array.from(updateImagePositions).reverse(); 170 | let delay = 0; 171 | 172 | imagesArray.forEach((image) => { 173 | setTimeout(() => { 174 | const container = image.closest( 175 | '.oneByTwoGridItem_f4758a, ' + 176 | '.oneByTwoGrid_f4758a.oneByTwoLayoutThreeGrid_f4758a .oneByTwoSoloItem_f4758a, ' + 177 | '.oneByTwoSoloItem_f4758a, ' + 178 | '.twoByOneGridItem_f4758a, ' + 179 | '.oneByOneGrid_f4758a.oneByOneGridMosaic_f4758a, ' + 180 | '.oneByTwoGrid_f4758a .oneByTwoGridItem_f4758a' 181 | ); 182 | 183 | if (container && image) { 184 | if (container.matches('.threeByThreeGrid_f4758a .oneByTwoSoloItem_f4758a')) { 185 | container.style.maxHeight = '175px'; 186 | } 187 | 188 | const containerHeight = container.clientHeight; 189 | const originalImageHeight = image.clientHeight; 190 | const scaleFactor = Math.max(1, containerHeight / originalImageHeight); 191 | 192 | image.style.transform = `scale(${scaleFactor})`; 193 | image.style.transformOrigin = 'top'; 194 | image.offsetHeight; // Forcing the reflow - Knew 195 | 196 | const scaledImageHeight = scaleFactor === 1 ? originalImageHeight : originalImageHeight * scaleFactor; 197 | const translateY = (containerHeight - scaledImageHeight) / 2; 198 | 199 | image.style.transform += ` translateY(${translateY}px)`; 200 | image.classList.add('uncompressedImagesCentered'); 201 | } 202 | }, delay); 203 | delay += 100; 204 | }); 205 | } 206 | 207 | this.resizeListener = window.addEventListener( 208 | 'resize', 209 | debounce(centerImageUponWindowResize, 75) 210 | ); 211 | // ----------------- // 212 | // ----------------- // 213 | function enhanceAvatarQuality() { 214 | const avatarURLs = document.querySelectorAll( 215 | 'img.avatar_c19a55[src^="https://cdn.discordapp.com/avatars"]:not(.processed-avatar), img.avatar__44b0c[src^="https://cdn.discordapp.com/avatars"]:not(.processed-avatar)' 216 | ); 217 | avatarURLs.forEach((image) => { 218 | let newSrc = image.src 219 | .replace(/\.webp\?size=\d+/, '.png?size=4096'); 220 | image.src = newSrc; 221 | image.classList.add('processed-avatar'); 222 | }); 223 | } 224 | 225 | function enhanceIconQuality() { // 'Enhance' is so corny. I should probably change this to "fetch", but whatever for now. - Knew 226 | const iconURLs = document.querySelectorAll( 227 | 'img.icon__6e9f8[src^="https://cdn.discordapp.com/icons/"]:not(.processed-icon)' 228 | ); 229 | iconURLs.forEach((image) => { 230 | let newSrc = image.src 231 | .replace(/\.webp\?size=\d+/, '.png?size=4096'); 232 | image.src = newSrc; 233 | image.classList.add('processed-icon'); 234 | }); 235 | } 236 | 237 | function enhanceActivityIconQuality() { 238 | const iconURLs = document.querySelectorAll( 239 | 'img.contentImage__42bf5[src^="https://cdn.discordapp.com/app-icons/"]:not(.processed-activity-icon)' 240 | ); 241 | iconURLs.forEach((image) => { 242 | let newSrc = image.src 243 | .replace(/\.webp\?size=\d+&keep_aspect_ratio=false/, '.png?size=4096&keep_aspect_ratio=true') 244 | image.src = newSrc; 245 | image.classList.add('processed-activity-icon'); 246 | }); 247 | } 248 | 249 | function imagesExternalLinks() { 250 | const imgElements = document.querySelectorAll('img'); 251 | imgElements.forEach(img => { 252 | const externalLink = /^(https:\/\/images-ext-\d+\.discordapp\.net\/external\/[^\/]+\/https\/[^?]+)\?.+$/; 253 | const match = img.src.match(externalLink); 254 | if (match) { 255 | img.src = match[1] + '?'; 256 | img.classList.add('processed-external-link'); 257 | } 258 | }); 259 | } 260 | // ----------------- // 261 | // ----------------- // 262 | const SELECTOR_IMG_SRC = ` 263 | .zoomLens_ac0584 img[src^="https://media.discordapp.net/attachments"]:not(.processed-image), 264 | .layerContainer_da8173 img[src^="https://media.discordapp.net/attachments"]:not(.processed-image), 265 | .imageContainer__0f481 img[src^="https://media.discordapp.net/attachments"]:not(.processed-image), 266 | .vc-imgzoom-lens img[src^="https://media.discordapp.net/attachments"]:not(.processed-image) 267 | `; 268 | 269 | function convertMediaToCDN() { 270 | const mediaURLs = Array.from(document.querySelectorAll(SELECTOR_IMG_SRC)).reverse(); 271 | for (const image of mediaURLs) { 272 | if ( 273 | !image.classList.contains('gif__2dc39') && 274 | !image.closest('.video_f316dd') && 275 | !image.classList.contains('processed-image') 276 | ) { 277 | image.src = image.src.replace( 278 | 'https://media.discordapp.net/attachments', 279 | 'https://cdn.discordapp.com/attachments' 280 | ); 281 | image.classList.add('processed-image'); 282 | } 283 | } 284 | } 285 | 286 | function monitorZoomLensInjection() { 287 | const observer = new MutationObserver((mutations) => { 288 | for (const mutation of mutations) { 289 | for (const node of mutation.addedNodes) { 290 | if ( 291 | node instanceof HTMLElement && 292 | node.classList.contains('zoomLens_ac0584') 293 | ) { 294 | convertMediaToCDN(); 295 | } 296 | } 297 | } 298 | }); 299 | 300 | observer.observe(document.body, { 301 | childList: true, 302 | subtree: true, 303 | }); 304 | } 305 | // ----------------- // 306 | // ----------------- // 307 | function replaceURLs() { 308 | const messages = document.querySelectorAll('.container_b7e1cb'); 309 | 310 | messages.forEach((message) => { 311 | const images = message.querySelectorAll('.imageDetails_ac0584'); 312 | 313 | if (images.length === 1) { 314 | const image = images[0]; 315 | image.style.display = 'inline-table'; 316 | image.style.transform = 'translateX(5px) translateY(0)'; 317 | image.style.lineHeight = 'unset'; 318 | 319 | const parent = image.closest( 320 | '.imageContent__0f481.embedWrapper_b7e1cb.itemContentContainer_f4758a.mosaicItemContent__6c706' 321 | ); 322 | 323 | if (parent) { 324 | parent.appendChild(image); 325 | } 326 | } else if (images.length > 1) { 327 | images.forEach((image) => { 328 | image.style.display = 'none'; 329 | }); 330 | } 331 | }); 332 | 333 | const SINGLE_IMAGE_SELECTOR = '.container_b7e1cb .lazyImg_f4758a.processed-image.processed-single-layout'; 334 | const SINGLE_WRAPPER_SELECTOR = '.imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-single-layout'; 335 | 336 | const GRID_IMAGE_SELECTOR = '.container_b7e1cb .lazyImg_f4758a.processed-image.processed-grid-layout'; 337 | const GRID_WRAPPER_SELECTOR = '.imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-grid-layout'; 338 | 339 | // Handle single image layout (landscape): 340 | const imagesSingle = document.querySelectorAll(SINGLE_IMAGE_SELECTOR); 341 | imagesSingle.forEach((image) => { 342 | image.addEventListener('load', () => { 343 | const classElement = image.closest(SINGLE_WRAPPER_SELECTOR); 344 | if (classElement && image.naturalWidth > image.naturalHeight) { 345 | classElement.classList.add('auto-width-single'); 346 | } 347 | }); 348 | }); 349 | 350 | // Handle grid image layout (portrait): 351 | const imagesGrid = document.querySelectorAll(GRID_IMAGE_SELECTOR); 352 | imagesGrid.forEach((image) => { 353 | image.addEventListener('load', () => { 354 | const classElement = image.closest(GRID_WRAPPER_SELECTOR); 355 | if (classElement && image.naturalHeight > image.naturalWidth) { 356 | classElement.classList.add('auto-width-grid'); 357 | } 358 | }); 359 | }); 360 | } 361 | // ----------------- // 362 | // ----------------- // 363 | function processImageSrc() { 364 | convertMediaToCDN(); 365 | replaceURLs(); 366 | checkForGridLayout(); 367 | updateGridLayoutClass(); 368 | centerImageBecauseRegularCSSWillNot(); 369 | } 370 | 371 | function callback(mutationsList, observer) { 372 | const CLASS_LAZY_IMG = 'lazyImg_f4758a'; 373 | const SELECTOR_IMG_SRC = `.${CLASS_LAZY_IMG}`; 374 | 375 | for (const mutation of mutationsList) { 376 | if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { 377 | const addedImages = Array.from(mutation.addedNodes).flatMap((node) => 378 | node.querySelectorAll 379 | ? Array.from(node.querySelectorAll(SELECTOR_IMG_SRC)) 380 | : [] 381 | ); 382 | 383 | addedImages.forEach((image) => { 384 | if (!image.src.includes('.gif')) { 385 | setImmediate(processImageSrc); 386 | } 387 | }); 388 | } else if (mutation.type === 'attributes' && mutation.attributeName === 'src') { 389 | if (!mutation.target.src.includes('.gif')) { 390 | processImageSrc(); 391 | enhanceAvatarQuality(); 392 | enhanceIconQuality(); 393 | enhanceActivityIconQuality(); 394 | imagesExternalLinks(); 395 | } 396 | } 397 | } 398 | } 399 | // ----------------- // 400 | // ----------------- // 401 | function checkForGridLayout() { 402 | const CLASS_CONTAINER = 'container_b7e1cb'; 403 | const CLASS_IMAGE_WRAPPER = 'imageWrapper_af017a'; 404 | const CLASS_IMAGE_CONTAINER = 'imageContainer__0f481'; 405 | const CLASS_LAZY_IMG = 'lazyImg_f4758a'; 406 | const CLASS_LAZY_IMG_CONTAINER = 'lazyImgContainer_f4758a'; 407 | const CLASS_IMAGE_CONTENT = 'imageContent__0f481'; 408 | 409 | const messages = document.querySelectorAll(`.${CLASS_CONTAINER}`); 410 | messages.forEach((message) => { 411 | const allRelatedElements = message.querySelectorAll([ 412 | `.${CLASS_LAZY_IMG}`, 413 | `.${CLASS_IMAGE_CONTAINER}`, 414 | `.${CLASS_LAZY_IMG_CONTAINER}`, 415 | `.${CLASS_IMAGE_WRAPPER}`, 416 | `.${CLASS_IMAGE_CONTENT}`, 417 | ].join(', ')); 418 | 419 | const imageElements = message.querySelectorAll(`.${CLASS_LAZY_IMG}`); 420 | const CLASS_PROCESSED_SINGLE = 'processed-single-layout'; 421 | const CLASS_PROCESSED_GRID = 'processed-grid-layout'; 422 | 423 | if (imageElements.length > 1) { 424 | allRelatedElements.forEach((element) => { 425 | element.classList.remove(CLASS_PROCESSED_SINGLE); 426 | element.classList.add(CLASS_PROCESSED_GRID); 427 | }); 428 | } else if (imageElements.length === 1) { 429 | allRelatedElements.forEach((element) => { 430 | element.classList.remove(CLASS_PROCESSED_GRID); 431 | element.classList.add(CLASS_PROCESSED_SINGLE); 432 | }); 433 | } 434 | }); 435 | } 436 | // ----------------- // 437 | // ----------------- // 438 | function createUncompressedImagesCSSStyle() { 439 | const style = document.createElement('style'); 440 | style.textContent = ` 441 | 442 | .pointerCover_d125d2 { 443 | z-index: -9999 !important; 444 | } 445 | 446 | .pointerCover__5c3cc { 447 | z-index: -9999 !important; 448 | } 449 | 450 | .altText__0f481 { 451 | margin: .25rem 0 -0.15rem !important; 452 | line-height: 17px !important; 453 | } 454 | 455 | .mediaAttachmentsContainer__242e2 { 456 | 457 | } 458 | 459 | .auto-width-single { 460 | width: auto !important; 461 | height: auto !important; 462 | max-width: 550px !important; 463 | } 464 | 465 | .auto-width-single img { 466 | max-height: 350px !important; 467 | } 468 | 469 | .auto-width-grid { 470 | height: auto !important; 471 | max-width: 550px !important; 472 | } 473 | 474 | .auto-width-grid img { 475 | 476 | } 477 | 478 | .imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-single-layout { 479 | 480 | } 481 | 482 | .carouselModal_d2b9a1.zoomedCarouselModalRoot_f74404.root__49fc1.fullscreenOnMobile__49fc1 { 483 | display: flex !important; 484 | justify-content: center !important; 485 | align-items: center !important; 486 | } 487 | 488 | .imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-grid-layout { 489 | display: -webkit-box !important; 490 | } 491 | 492 | .imageContent__0f481.embedWrapper_b7e1cb.itemContentContainer_f4758a.mosaicItemContent__6c706.processed-single-layout { 493 | height: auto !important; 494 | width: auto !important; 495 | max-width: 550px !important; 496 | } 497 | 498 | .imageWrapper_af017a.embedWrapper_b7e1cb.lazyImg_f4758a.mosaicItemContent__6c706.processed-single-layout { 499 | 500 | } 501 | 502 | .imageDetailsAdded_ac0584 .imageWrapper_af017a { 503 | height: 100% !important; 504 | } 505 | 506 | .imageDetails_ac0584 { 507 | margin: 0.15rem 0 0rem !important; 508 | } 509 | 510 | .lazyImg_f4758a.processed-image.processed-grid-layout { 511 | aspect-ratio: unset !important; 512 | display: grid !important; 513 | object-fit: cover !important; 514 | } 515 | 516 | .lazyImg_f4758a.processed-image.processed-single-layout { 517 | 518 | } 519 | 520 | .imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-grid-layout { 521 | max-width: 100% !important; 522 | } 523 | 524 | .imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-single-layout { 525 | height: 100% !important; 526 | } 527 | 528 | .cursorPointer_B3uwDA { 529 | transform: translateY(2px) !important; 530 | } 531 | 532 | .spoilerContent__54ab5.spoilerContainer__54ab5 { 533 | background-color: rgba(255, 255, 255, 0); 534 | } 535 | 536 | .loadingOverlay_af017a { 537 | aspect-ratio: unset !important; 538 | } 539 | 540 | .threeByThreeGrid_f4758a .lazyImgContainer_f4758a, .threeByThreeGrid_f4758a .lazyImg_f4758a { 541 | aspect-ratio: unset !important; 542 | } 543 | 544 | .lazyImg_f4758a.processed-image.processed-grid-layout { 545 | min-height: auto !important; 546 | } 547 | 548 | .oneByTwoGrid_f4758a .itemContentContainer_f4758a, .oneByTwoGrid_f4758a .lazyImg_f4758a { 549 | height: unset !important; 550 | } 551 | `; 552 | document.head.appendChild(style); 553 | return style; 554 | } 555 | 556 | function modifyImageUtilitiesCSSRule() { 557 | var styleElement = document.getElementById("ImageUtilitiesCSS"); 558 | 559 | if (styleElement) { 560 | var cssText = styleElement.textContent; 561 | 562 | var oldRule = ".imageDetailsAdded_ac0584 .imageWrapper_af017a {border-radius: 8px !important;height: calc(100% - 1rem - 16px) !important;max-height: unset !important;margin-left: unset !important;}"; 563 | var newRule = ".imageDetailsAdded_ac0584 .imageWrapper_af017a {border-radius: 8px !important;height: calc(100% - 1rem - 16px);max-height: unset !important;margin-left: unset !important;}"; 564 | 565 | cssText = cssText.replace(oldRule, newRule); 566 | 567 | styleElement.textContent = cssText; 568 | } else { 569 | // console.error("Uncompressed Images Error: Style element with ID 'ImageUtilitiesCSS' not found."); 570 | } 571 | } 572 | // ----------------- // 573 | // ----------------- // 574 | function runMutation() { 575 | convertMediaToCDN(); 576 | monitorZoomLensInjection(); 577 | replaceURLs(); 578 | enhanceAvatarQuality(); 579 | enhanceIconQuality(); 580 | enhanceActivityIconQuality(); 581 | imagesExternalLinks(); 582 | setTimeout(modifyImageUtilitiesCSSRule, 4000); 583 | localObserver.observe(document, config); 584 | } 585 | 586 | runMutation(); 587 | 588 | if (!this.UncompressedImagesCSSStyle) { 589 | this.UncompressedImagesCSSStyle = createUncompressedImagesCSSStyle(); 590 | } 591 | 592 | this.mutationObserver = localObserver; 593 | 594 | /** 595 | Main code ends here, don't forget. 596 | That "}" is attached to the "start () {" function. 597 | */ 598 | // ----------------- // 599 | } stop() { 600 | if (this.mutationObserver) { 601 | this.mutationObserver.disconnect(); 602 | this.mutationObserver = null; 603 | } 604 | 605 | if (this.UncompressedImagesCSSStyle) { 606 | this.UncompressedImagesCSSStyle.remove(); 607 | this.UncompressedImagesCSSStyle = null; 608 | } 609 | 610 | if (this.resizeListener) { 611 | window.removeEventListener('resize', this.resizeListener); 612 | this.resizeListener = null; 613 | } 614 | 615 | if (this.animationFrame) { 616 | cancelAnimationFrame(this.animationFrame); 617 | this.animationFrame = null; 618 | } 619 | 620 | function removeClassFromChildren(parentElement, className) { 621 | const childElements = parentElement.children; 622 | for (let i = 0; i < childElements.length; i++) { 623 | childElements[i].classList.remove(className); 624 | } 625 | } 626 | 627 | (function modifyImageUtilitiesCSSRuleDisable() { 628 | var styleElement = document.getElementById("ImageUtilitiesCSS"); 629 | 630 | if (styleElement) { 631 | var cssText = styleElement.textContent; 632 | 633 | var newRule = ".imageDetailsAdded_ac0584 .imageWrapper_af017a {border-radius: 8px !important;height: calc(100% - 1rem - 16px) !important;max-height: unset !important;margin-left: unset !important;}"; 634 | var oldRule = ".imageDetailsAdded_ac0584 .imageWrapper_af017a {border-radius: 8px !important;height: calc(100% - 1rem - 16px);max-height: unset !important;margin-left: unset !important;}"; 635 | 636 | cssText = cssText.replace(oldRule, newRule); 637 | 638 | styleElement.textContent = cssText; 639 | } 640 | })(); 641 | 642 | const threeByThreeGridElements = document.querySelectorAll('.threeByThreeGrid_f4758a'); 643 | threeByThreeGridElements.forEach(element => { 644 | element.classList.remove('threeByThreeGrid_f4758a'); 645 | element.classList.add('twoByTwoGrid_f4758a'); 646 | element.style.gridTemplateColumns = ""; 647 | 648 | removeClassFromChildren(element, 'oneByTwoSoloItem_f4758a'); 649 | }); 650 | 651 | const elementsWithAdjustedHeight = document.querySelectorAll('.clickableWrapper_af017a, .loadingOverlay_af017a'); 652 | elementsWithAdjustedHeight.forEach(element => { 653 | element.style.height = "100%"; 654 | }); 655 | 656 | const centeredImages = document.querySelectorAll('.imageContainer__0f481 .lazyImg_f4758a.processed-image.processed-grid-layout.uncompressedImagesCentered'); 657 | centeredImages.forEach(image => { 658 | image.style.transform = ""; 659 | image.classList.remove('uncompressedImagesCentered'); 660 | }); 661 | 662 | const revertClassesAndStyles = (selector, className, srcRegex, srcReplacement, appendQuery) => { 663 | const elements = document.querySelectorAll(selector); 664 | elements.forEach((element) => { 665 | if (element) { 666 | element.classList.remove(className); 667 | if (srcRegex && srcReplacement) { 668 | element.src = element.src.replace(srcRegex, srcReplacement); 669 | } 670 | if (appendQuery) { 671 | if (element.src.includes('?')) { 672 | element.src += '&' + appendQuery; 673 | } else { 674 | element.src += '?' + appendQuery; 675 | } 676 | } 677 | } 678 | }); 679 | }; 680 | 681 | revertClassesAndStyles('.auto-width-single', 'auto-width-single'); 682 | revertClassesAndStyles('.auto-width-grid', 'auto-width-grid'); 683 | revertClassesAndStyles('.max-width-adjusted', 'max-width-adjusted'); 684 | revertClassesAndStyles('.processed-avatar', 'processed-avatar', /\?quality=lossless/, ''); 685 | revertClassesAndStyles('.processed-icon', 'processed-icon', /\?quality=lossless/, ''); 686 | revertClassesAndStyles('.processed-image', 'processed-image', /https:\/\/cdn\.discordapp\.com\/attachments/, 'https:\/\/media.discordapp.net\/attachments'); 687 | revertClassesAndStyles('.processed-single-layout', 'processed-single-layout'); 688 | revertClassesAndStyles('.processed-grid-layout', 'processed-grid-layout'); 689 | revertClassesAndStyles('.processed-external-link', 'processed-external-link', null, null, 'format=webp'); 690 | 691 | const removeLoadEventListener = (selector) => { 692 | const images = document.querySelectorAll(selector); 693 | images.forEach((image) => { 694 | if (typeof handleImageLoad === 'function') { 695 | image.removeEventListener('load', handleImageLoad); 696 | } 697 | }); 698 | }; 699 | 700 | removeLoadEventListener('.container_b7e1cb .lazyImg_f4758a.processed-image.processed-single-layout'); 701 | removeLoadEventListener('.container_b7e1cb .lazyImg_f4758a.processed-image.processed-grid-layout'); 702 | 703 | const imageDetails = document.querySelectorAll('.messageListItem__5126c .imageDetails_ac0584'); 704 | imageDetails.forEach((image) => { 705 | if (image) { 706 | image.style.removeProperty('display'); 707 | image.style.removeProperty('transform'); 708 | image.style.lineHeight = '16px'; 709 | image.style.display = ''; 710 | } 711 | }); 712 | 713 | const imageContainers = document.querySelectorAll('.imageDetails_ac0584'); 714 | imageContainers.forEach((element) => { 715 | if (element) { 716 | const commonParent = element.closest('.imageContent__0f481.embedWrapper_b7e1cb.itemContentContainer_f4758a.mosaicItemContent__6c706'); 717 | const targetParent = commonParent ? commonParent.querySelector('.imageContainer__0f481 div') : null; 718 | if (targetParent) { 719 | targetParent.appendChild(element); 720 | } 721 | } 722 | }); 723 | 724 | if (typeof timeoutId !== 'undefined') { 725 | clearTimeout(timeoutId); 726 | } 727 | } 728 | }; 729 | 730 | /** 731 | * Version 3.32 of 'Uncompressed Images'. 732 | * Copyright (Boost Software License 1.0) 2023-2025 Knew 733 | * Link to plugin: https://github.com/Knewest/Uncompressed-Discord-Images 734 | * Support server: https://discord.gg/NqqqzajfK4 735 | */ 736 | 737 | /** IGNORE THIS 738 | * @changelog {banner} https://cdn.discordapp.com/attachments/753561208073879642/1134847376541106176/output_animation8.webp 739 | * @changelog {blurb} Missed or want to know previous changelogs? Find them [here](https://github.com/Knewest/embed-more-images/releases). 740 | * @changelog {fixed.item} 741 | * @changelog {added.title} What I changed 742 | * @changelog {added.item} 743 | * @changelog {footer} Need help? Join my the [support server (NqqqzajfK4)](https://discord.gg/NqqqzajfK4). 744 | */ 745 | -------------------------------------------------------------------------------- /UncompressedImagesConsoleVer.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Uncompressed Images 3 | // @namespace Uncompressed Images 4 | // @match https://www.discord.com/* 5 | // @grant none 6 | // @version 3.31 7 | // @author @Knewest on Github 8 | // @source https://github.com/Knewest/uncompressed-discord-images 9 | // ==/UserScript== 10 | 11 | function debounce(func, wait) { 12 | let timeout; 13 | return function(...args) { 14 | const context = this; 15 | clearTimeout(timeout); 16 | timeout = setTimeout(() => func.apply(context, args), wait); 17 | }; 18 | } 19 | 20 | const config = { 21 | attributes: true, 22 | childList: true, 23 | subtree: true, 24 | attributeFilter: ['src'], 25 | }; 26 | 27 | const localObserver = new MutationObserver(callback); 28 | 29 | function updateGridLayoutClass() { 30 | const twoByTwoGridElements = document.querySelectorAll('.twoByTwoGrid_f4758a'); 31 | twoByTwoGridElements.forEach((element) => { 32 | element.classList.remove('twoByTwoGrid_f4758a'); 33 | element.classList.add('threeByThreeGrid_f4758a'); 34 | element.style.gridTemplateColumns = 'repeat(2, 1fr)'; 35 | 36 | addClassToChildren(element, 'oneByTwoSoloItem_f4758a'); 37 | }); 38 | 39 | const threeByThreeGridElements = document.querySelectorAll('.threeByThreeGrid_f4758a'); 40 | threeByThreeGridElements.forEach((element) => { 41 | if (!element.classList.contains('original-threeByThreeGrid')) { 42 | addClassToChildren(element, 'oneByTwoSoloItem_f4758a'); 43 | } 44 | }); 45 | } 46 | 47 | function addClassToChildren(parentElement, className) { 48 | const childElements = parentElement.children; 49 | for (let i = 0; i < childElements.length; i++) { 50 | childElements[i].classList.add(className); 51 | } 52 | } 53 | 54 | function adjustHeightBasedOnNearestVerticalResolution() { 55 | let debounceTimer; 56 | const debounceDelay = 2000; 57 | 58 | const resizeObserver = new ResizeObserver((entries) => { 59 | const reversedEntries = Array.from(entries).reverse(); 60 | let delay = 0; 61 | 62 | reversedEntries.forEach((entry) => { 63 | setTimeout(() => { 64 | const element = entry.target; 65 | const nearestGridItem = element.closest( 66 | '.oneByTwoGridItem_f4758a, ' + 67 | '.oneByTwoGrid_f4758a.oneByTwoLayoutThreeGrid_f4758a .oneByTwoSoloItem_f4758a, ' + 68 | '.twoByOneGridItem_f4758a, ' + 69 | '.oneByOneGrid_f4758a.oneByOneGridMosaic_f4758a, ' + 70 | '.threeByThreeGrid_f4758a .oneByTwoSoloItem_f4758a, ' + 71 | '.oneByTwoGrid_f4758a .oneByTwoGridItem_f4758a' 72 | ); 73 | 74 | if (nearestGridItem) { 75 | const renderedHeight = nearestGridItem.getBoundingClientRect().height; 76 | if (renderedHeight >= 10) { 77 | element.style.height = `${renderedHeight}px`; 78 | } 79 | } 80 | }, delay); 81 | delay += 25; 82 | }); 83 | 84 | clearTimeout(debounceTimer); 85 | debounceTimer = setTimeout(() => { 86 | centerImageUponWindowResize(); 87 | }, debounceDelay); 88 | }); 89 | 90 | const elementsToObserve = document.querySelectorAll('.clickableWrapper_af017a, .loadingOverlay_af017a'); 91 | elementsToObserve.forEach((element) => { 92 | resizeObserver.observe(element); 93 | 94 | setTimeout(() => { 95 | resizeObserver.unobserve(element); 96 | }, 2000); 97 | }); 98 | } 99 | 100 | function centerImageBecauseRegularCSSWillNot() { 101 | const updateImagePositions = document.querySelectorAll( 102 | '.imageContainer__0f481 .lazyImg_f4758a.processed-image.processed-grid-layout:not(.uncompressedImagesCentered)' 103 | ); 104 | const imagesArray = Array.from(updateImagePositions).reverse(); 105 | let delay = 0; 106 | 107 | imagesArray.forEach((image) => { 108 | setTimeout(() => { 109 | const container = image.closest( 110 | '.oneByTwoGridItem_f4758a, ' + 111 | '.oneByTwoGrid_f4758a.oneByTwoLayoutThreeGrid_f4758a .oneByTwoSoloItem_f4758a, ' + 112 | '.oneByTwoSoloItem_f4758a, ' + 113 | '.twoByOneGridItem_f4758a, ' + 114 | '.oneByOneGrid_f4758a.oneByOneGridMosaic_f4758a, ' + 115 | '.oneByTwoGrid_f4758a .oneByTwoGridItem_f4758a' 116 | ); 117 | 118 | if (container && image) { 119 | if (container.matches('.threeByThreeGrid_f4758a .oneByTwoSoloItem_f4758a')) { 120 | container.style.maxHeight = '175px'; 121 | image.classList.add('uncompressedImagesCentered'); 122 | } 123 | 124 | const containerHeight = container.clientHeight; 125 | const originalImageHeight = image.clientHeight; 126 | const scaleFactor = Math.max(1, containerHeight / originalImageHeight); 127 | 128 | image.style.transform = `scale(${scaleFactor})`; 129 | image.style.transformOrigin = 'top'; 130 | image.offsetHeight; // Forcing the reflow - Knew 131 | 132 | const scaledImageHeight = scaleFactor === 1 ? originalImageHeight : originalImageHeight * scaleFactor; 133 | const translateY = (containerHeight - scaledImageHeight) / 2; 134 | 135 | image.style.transform += ` translateY(${translateY}px)`; 136 | image.classList.add('uncompressedImagesCentered'); 137 | } 138 | }, delay); 139 | delay += 100; 140 | }); 141 | 142 | setTimeout(adjustHeightBasedOnNearestVerticalResolution, 10); 143 | setTimeout(centerImageUponWindowResize, 2000); 144 | setTimeout(adjustHeightBasedOnNearestVerticalResolution, 4500); 145 | } 146 | 147 | function centerImageUponWindowResize() { 148 | const updateImagePositions = document.querySelectorAll( 149 | '.imageContainer__0f481 .lazyImg_f4758a.processed-image.processed-grid-layout' 150 | ); 151 | const imagesArray = Array.from(updateImagePositions).reverse(); 152 | let delay = 0; 153 | 154 | imagesArray.forEach((image) => { 155 | setTimeout(() => { 156 | const container = image.closest( 157 | '.oneByTwoGridItem_f4758a, ' + 158 | '.oneByTwoGrid_f4758a.oneByTwoLayoutThreeGrid_f4758a .oneByTwoSoloItem_f4758a, ' + 159 | '.oneByTwoSoloItem_f4758a, ' + 160 | '.twoByOneGridItem_f4758a, ' + 161 | '.oneByOneGrid_f4758a.oneByOneGridMosaic_f4758a, ' + 162 | '.oneByTwoGrid_f4758a .oneByTwoGridItem_f4758a' 163 | ); 164 | 165 | if (container && image) { 166 | if (container.matches('.threeByThreeGrid_f4758a .oneByTwoSoloItem_f4758a')) { 167 | container.style.maxHeight = '175px'; 168 | } 169 | 170 | const containerHeight = container.clientHeight; 171 | const originalImageHeight = image.clientHeight; 172 | const scaleFactor = Math.max(1, containerHeight / originalImageHeight); 173 | 174 | image.style.transform = `scale(${scaleFactor})`; 175 | image.style.transformOrigin = 'top'; 176 | image.offsetHeight; // force reflow 177 | 178 | const scaledImageHeight = scaleFactor === 1 ? originalImageHeight : originalImageHeight * scaleFactor; 179 | const translateY = (containerHeight - scaledImageHeight) / 2; 180 | 181 | image.style.transform += ` translateY(${translateY}px)`; 182 | image.classList.add('uncompressedImagesCentered'); 183 | } 184 | }, delay); 185 | delay += 100; 186 | }); 187 | } 188 | 189 | this.resizeListener = window.addEventListener( 190 | 'resize', 191 | debounce(centerImageUponWindowResize, 75) 192 | ); 193 | // ----------------- // 194 | // ----------------- // 195 | function enhanceAvatarQuality() { 196 | const avatarURLs = document.querySelectorAll( 197 | 'img.avatar_c19a55[src^="https://cdn.discordapp.com/avatars"]:not(.processed-avatar), img.avatar__44b0c[src^="https://cdn.discordapp.com/avatars"]:not(.processed-avatar)' 198 | ); 199 | avatarURLs.forEach((image) => { 200 | let newSrc = image.src 201 | .replace(/\.webp\?size=\d+/, '.png?size=4096'); 202 | image.src = newSrc; 203 | image.classList.add('processed-avatar'); 204 | }); 205 | } 206 | 207 | function enhanceIconQuality() { // 'Enhance' is so corny. I should probably change this to "fetch", but whatever for now. - Knew 208 | const iconURLs = document.querySelectorAll( 209 | 'img.icon__6e9f8[src^="https://cdn.discordapp.com/icons/"]:not(.processed-icon)' 210 | ); 211 | iconURLs.forEach((image) => { 212 | let newSrc = image.src 213 | .replace(/\.webp\?size=\d+/, '.png?size=4096'); 214 | image.src = newSrc; 215 | image.classList.add('processed-icon'); 216 | }); 217 | } 218 | 219 | function enhanceActivityIconQuality() { 220 | const iconURLs = document.querySelectorAll( 221 | 'img.contentImage__42bf5[src^="https://cdn.discordapp.com/app-icons/"]:not(.processed-activity-icon)' 222 | ); 223 | iconURLs.forEach((image) => { 224 | let newSrc = image.src 225 | .replace(/\.webp\?size=\d+&keep_aspect_ratio=false/, '.png?size=4096&keep_aspect_ratio=true') 226 | image.src = newSrc; 227 | image.classList.add('processed-activity-icon'); 228 | }); 229 | } 230 | 231 | function imagesExternalLinks() { 232 | const imgElements = document.querySelectorAll('img'); 233 | imgElements.forEach(img => { 234 | const externalLink = /^(https:\/\/images-ext-\d+\.discordapp\.net\/external\/[^\/]+\/https\/[^?]+)\?.+$/; 235 | const match = img.src.match(externalLink); 236 | if (match) { 237 | img.src = match[1] + '?'; 238 | img.classList.add('processed-external-link'); 239 | } 240 | }); 241 | } 242 | // ----------------- // 243 | // ----------------- // 244 | const SELECTOR_IMG_SRC = '.zoomLens_ac0584 img[src^="https://media.discordapp.net/attachments"]:not(.processed-image), .layerContainer_da8173 img[src^="https://media.discordapp.net/attachments"]:not(.processed-image), .imageContainer__0f481 img[src^="https://media.discordapp.net/attachments"]:not(.processed-image), .vc-imgzoom-lens img[src^="https://media.discordapp.net/attachments"]:not(.processed-image)'; 245 | 246 | function convertMediaToCDN() { 247 | const mediaURLs = Array.from(document.querySelectorAll(SELECTOR_IMG_SRC)).reverse(); 248 | mediaURLs.forEach((image) => { 249 | if (!image.classList.contains('gif__2dc39') && !image.nextElementSibling?.classList.contains('video_f316dd')) { 250 | image.src = image.src.replace( 251 | 'https://media.discordapp.net/attachments', 252 | 'https://cdn.discordapp.com/attachments' 253 | ); 254 | image.classList.add('processed-image'); 255 | } 256 | }); 257 | } 258 | // ----------------- // 259 | // ----------------- // 260 | function replaceURLs() { 261 | const messages = document.querySelectorAll('.container_b7e1cb'); 262 | 263 | messages.forEach((message) => { 264 | const images = message.querySelectorAll('.imageDetails_ac0584'); 265 | 266 | if (images.length === 1) { 267 | const image = images[0]; 268 | image.style.display = 'inline-table'; 269 | image.style.transform = 'translateX(5px) translateY(0)'; 270 | image.style.lineHeight = 'unset'; 271 | 272 | const parent = image.closest( 273 | '.imageContent__0f481.embedWrapper_b7e1cb.itemContentContainer_f4758a.mosaicItemContent__6c706' 274 | ); 275 | 276 | if (parent) { 277 | parent.appendChild(image); 278 | } 279 | } else if (images.length > 1) { 280 | images.forEach((image) => { 281 | image.style.display = 'none'; 282 | }); 283 | } 284 | }); 285 | 286 | const SINGLE_IMAGE_SELECTOR = '.container_b7e1cb .lazyImg_f4758a.processed-image.processed-single-layout'; 287 | const SINGLE_WRAPPER_SELECTOR = '.imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-single-layout'; 288 | 289 | const GRID_IMAGE_SELECTOR = '.container_b7e1cb .lazyImg_f4758a.processed-image.processed-grid-layout'; 290 | const GRID_WRAPPER_SELECTOR = '.imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-grid-layout'; 291 | 292 | // Handle single image layout (landscape): 293 | const imagesSingle = document.querySelectorAll(SINGLE_IMAGE_SELECTOR); 294 | imagesSingle.forEach((image) => { 295 | image.addEventListener('load', () => { 296 | const classElement = image.closest(SINGLE_WRAPPER_SELECTOR); 297 | if (classElement && image.naturalWidth > image.naturalHeight) { 298 | classElement.classList.add('auto-width-single'); 299 | } 300 | }); 301 | }); 302 | 303 | // Handle grid image layout (portrait): 304 | const imagesGrid = document.querySelectorAll(GRID_IMAGE_SELECTOR); 305 | imagesGrid.forEach((image) => { 306 | image.addEventListener('load', () => { 307 | const classElement = image.closest(GRID_WRAPPER_SELECTOR); 308 | if (classElement && image.naturalHeight > image.naturalWidth) { 309 | classElement.classList.add('auto-width-grid'); 310 | } 311 | }); 312 | }); 313 | } 314 | // ----------------- // 315 | // ----------------- // 316 | function processImageSrc() { 317 | convertMediaToCDN(); 318 | replaceURLs(); 319 | checkForGridLayout(); 320 | updateGridLayoutClass(); 321 | centerImageBecauseRegularCSSWillNot(); 322 | } 323 | 324 | function callback(mutationsList, observer) { 325 | const CLASS_LAZY_IMG = 'lazyImg_f4758a'; 326 | const SELECTOR_IMG_SRC = `.${CLASS_LAZY_IMG}`; 327 | 328 | for (const mutation of mutationsList) { 329 | if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { 330 | const addedImages = Array.from(mutation.addedNodes).flatMap((node) => 331 | node.querySelectorAll 332 | ? Array.from(node.querySelectorAll(SELECTOR_IMG_SRC)) 333 | : [] 334 | ); 335 | 336 | addedImages.forEach((image) => { 337 | if (!image.src.includes('.gif')) { 338 | setImmediate(processImageSrc); 339 | } 340 | }); 341 | } else if (mutation.type === 'attributes' && mutation.attributeName === 'src') { 342 | if (!mutation.target.src.includes('.gif')) { 343 | processImageSrc(); 344 | enhanceAvatarQuality(); 345 | enhanceIconQuality(); 346 | enhanceActivityIconQuality(); 347 | imagesExternalLinks(); 348 | } 349 | } 350 | } 351 | } 352 | // ----------------- // 353 | // ----------------- // 354 | function checkForGridLayout() { 355 | const CLASS_CONTAINER = 'container_b7e1cb'; 356 | const CLASS_IMAGE_WRAPPER = 'imageWrapper_af017a'; 357 | const CLASS_IMAGE_CONTAINER = 'imageContainer__0f481'; 358 | const CLASS_LAZY_IMG = 'lazyImg_f4758a'; 359 | const CLASS_LAZY_IMG_CONTAINER = 'lazyImgContainer_f4758a'; 360 | const CLASS_IMAGE_CONTENT = 'imageContent__0f481'; 361 | 362 | const CLASS_PROCESSED_SINGLE = 'processed-single-layout'; 363 | const CLASS_PROCESSED_GRID = 'processed-grid-layout'; 364 | 365 | const messages = document.querySelectorAll(`.${CLASS_CONTAINER}`); 366 | messages.forEach((message) => { 367 | const allRelatedElements = message.querySelectorAll([ 368 | `.${CLASS_LAZY_IMG}`, 369 | `.${CLASS_IMAGE_CONTAINER}`, 370 | `.${CLASS_LAZY_IMG_CONTAINER}`, 371 | `.${CLASS_IMAGE_WRAPPER}`, 372 | `.${CLASS_IMAGE_CONTENT}`, 373 | ].join(', ')); 374 | 375 | const imageElements = message.querySelectorAll(`.${CLASS_LAZY_IMG}`); 376 | 377 | if (imageElements.length > 1) { 378 | allRelatedElements.forEach((element) => { 379 | element.classList.remove(CLASS_PROCESSED_SINGLE); 380 | element.classList.add(CLASS_PROCESSED_GRID); 381 | }); 382 | } else if (imageElements.length === 1) { 383 | allRelatedElements.forEach((element) => { 384 | element.classList.remove(CLASS_PROCESSED_GRID); 385 | element.classList.add(CLASS_PROCESSED_SINGLE); 386 | }); 387 | } 388 | }); 389 | } 390 | // ----------------- // 391 | function createUncompressedImagesCSSStyle() { 392 | const style = document.createElement('style'); 393 | style.textContent = ` 394 | 395 | .pointerCover_d125d2 { 396 | z-index: -9999 !important; 397 | } 398 | 399 | .pointerCover__5c3cc { 400 | z-index: -9999 !important; 401 | } 402 | 403 | .altText__0f481 { 404 | margin: .25rem 0 -0.15rem !important; 405 | line-height: 17px !important; 406 | } 407 | 408 | .mediaAttachmentsContainer__242e2 { 409 | 410 | } 411 | 412 | .auto-width-single { 413 | width: auto !important; 414 | height: auto !important; 415 | max-width: 550px !important; 416 | } 417 | 418 | .auto-width-single img { 419 | max-height: 350px !important; 420 | } 421 | 422 | .auto-width-grid { 423 | height: auto !important; 424 | max-width: 550px !important; 425 | } 426 | 427 | .auto-width-grid img { 428 | 429 | } 430 | 431 | .imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-single-layout { 432 | 433 | } 434 | 435 | .carouselModal_d2b9a1.zoomedCarouselModalRoot_f74404.root__49fc1.fullscreenOnMobile__49fc1 { 436 | display: flex !important; 437 | justify-content: center !important; 438 | align-items: center !important; 439 | } 440 | 441 | .imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-grid-layout { 442 | display: -webkit-box !important; 443 | } 444 | 445 | .imageContent__0f481.embedWrapper_b7e1cb.itemContentContainer_f4758a.mosaicItemContent__6c706.processed-single-layout { 446 | height: auto !important; 447 | width: auto !important; 448 | max-width: 550px !important; 449 | } 450 | 451 | .imageWrapper_af017a.embedWrapper_b7e1cb.lazyImg_f4758a.mosaicItemContent__6c706.processed-single-layout { 452 | 453 | } 454 | 455 | .imageDetailsAdded_ac0584 .imageWrapper_af017a { 456 | height: 100% !important; 457 | } 458 | 459 | .imageDetails_ac0584 { 460 | margin: 0.15rem 0 0rem !important; 461 | } 462 | 463 | .lazyImg_f4758a.processed-image.processed-grid-layout { 464 | aspect-ratio: unset !important; 465 | display: grid !important; 466 | object-fit: cover !important; 467 | } 468 | 469 | .lazyImg_f4758a.processed-image.processed-single-layout { 470 | 471 | } 472 | 473 | .imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-grid-layout { 474 | max-width: 100% !important; 475 | } 476 | 477 | .imageWrapper_af017a.imageZoom_af017a.clickable_af017a.lazyImgContainer_f4758a.processed-single-layout { 478 | height: 100% !important; 479 | } 480 | 481 | .cursorPointer_B3uwDA { 482 | transform: translateY(2px) !important; 483 | } 484 | 485 | .spoilerContent__54ab5.spoilerContainer__54ab5 { 486 | background-color: rgba(255, 255, 255, 0); 487 | } 488 | 489 | .loadingOverlay_af017a { 490 | aspect-ratio: unset !important; 491 | } 492 | 493 | .threeByThreeGrid_f4758a .lazyImgContainer_f4758a, .threeByThreeGrid_f4758a .lazyImg_f4758a { 494 | aspect-ratio: unset !important; 495 | } 496 | 497 | .lazyImg_f4758a.processed-image.processed-grid-layout { 498 | min-height: auto !important; 499 | } 500 | 501 | .oneByTwoGrid_f4758a .itemContentContainer_f4758a, .oneByTwoGrid_f4758a .lazyImg_f4758a { 502 | height: unset !important; 503 | } 504 | `; 505 | document.head.appendChild(style); 506 | return style; 507 | } 508 | 509 | function modifyImageUtilitiesCSSRule() { 510 | var styleElement = document.getElementById("ImageUtilitiesCSS"); 511 | 512 | if (styleElement) { 513 | var cssText = styleElement.textContent; 514 | 515 | var oldRule = ".imageDetailsAdded_ac0584 .imageWrapper_af017a {border-radius: 8px !important;height: calc(100% - 1rem - 16px) !important;max-height: unset !important;margin-left: unset !important;}"; 516 | var newRule = ".imageDetailsAdded_ac0584 .imageWrapper_af017a {border-radius: 8px !important;height: calc(100% - 1rem - 16px);max-height: unset !important;margin-left: unset !important;}"; 517 | 518 | cssText = cssText.replace(oldRule, newRule); 519 | 520 | styleElement.textContent = cssText; 521 | } else { 522 | // console.error("Uncompressed Images Error: Style element with ID 'ImageUtilitiesCSS' not found."); 523 | } 524 | } 525 | // ----------------- // 526 | // ----------------- // 527 | function runMutation() { 528 | convertMediaToCDN(); 529 | replaceURLs(); 530 | enhanceAvatarQuality(); 531 | enhanceIconQuality(); 532 | enhanceActivityIconQuality(); 533 | imagesExternalLinks(); 534 | setTimeout(modifyImageUtilitiesCSSRule, 4000); 535 | localObserver.observe(document, config); 536 | } 537 | 538 | runMutation(); 539 | 540 | if (!this.UncompressedImagesCSSStyle) { 541 | this.UncompressedImagesCSSStyle = createUncompressedImagesCSSStyle(); 542 | } 543 | 544 | this.mutationObserver = localObserver; 545 | /** 546 | * Copyright (Boost Software License 1.0) 2023-2025 Knew 547 | * Support server: https://discord.gg/NqqqzajfK4 548 | */ 549 | --------------------------------------------------------------------------------