[READ-ONLY] Mirror of https://github.com/probablykasper/thumbnail-grabber. Chrome extension for grabbing thumbnails/covers
browser-extension chrome chrome-extension chrome-extention extension
0

Configure Feed

Select the types of activity you want to include in your feed.

Code restructuring/cleanup

Kasper (Feb 20, 2021, 5:13 AM +0100) 388b35bc a4a4caea

+87 -62
+4 -4
.eslintrc.js
··· 12 12 }, 13 13 ignorePatterns: ['**/node_modules'], 14 14 rules: { 15 - indent: ['error', 2], 16 - quotes: ['error', 'single', { 'avoidEscape': true }], 15 + indent: ['error', 2, { SwitchCase: 1 }], 16 + quotes: ['error', 'single', { avoidEscape: true }], 17 17 'comma-dangle': ['error', 'always-multiline'], 18 - // semi: ['error', 'never'], 18 + semi: ['error', 'always'], 19 19 // 'no-unused-vars': [ 20 20 // 'error', 21 21 // { args: 'none' }, ··· 30 30 // objectsInArrays: true, 31 31 }], 32 32 }, 33 - } 33 + };
+80 -55
src/content-script.js
··· 55 55 notificationElement.parentElement.removeChild(notificationElement); 56 56 } 57 57 }, 500); 58 - }) 59 - }) 58 + }); 59 + }); 60 60 61 61 var icon = chrome.runtime.getURL('icon48.png'); 62 62 notificationElement.querySelector('img').src = icon; ··· 79 79 return async function() { 80 80 await xhr(url, 'GET', 'blob'); 81 81 return url; 82 - } 82 + }; 83 83 }); 84 84 let error = ''; 85 85 for (const urlPromise of urlPromises) { ··· 91 91 continue; 92 92 } 93 93 } 94 - throw `No thumbnail found (${error})` 94 + throw `No thumbnail found (${error})`; 95 95 } 96 96 97 97 function googleUserContentUrl(urlObj) { ··· 101 101 // the original size of the image. 102 102 // Info about `googleusercontent.com` urls: 103 103 // https://sites.google.com/site/picasaresources/Home/Picasa-FAQ/google-photos-1/how-to/how-to-get-a-direct-link-to-an-image 104 - var imgPathname = urlObj.pathname.split('=')[0]+'=s0' 105 - return urlObj.origin + imgPathname + urlObj.search + urlObj.hash 104 + var imgPathname = urlObj.pathname.split('=')[0]+'=s0'; 105 + return urlObj.origin + imgPathname + urlObj.search + urlObj.hash; 106 106 } 107 107 108 108 var img = thumbnailGrabber.querySelector('img'); 109 - var imgUrl, filename; 110 - async function setup(newUrl) { 109 + var imageUrl; 110 + 111 + let lastImageUrl; 112 + async function getImageUrl(newUrl) { 113 + let url; 111 114 const site = urlUtil.getSite(newUrl); 112 115 if (site == 'soundcloud') { 113 - filename = 'Cover'; 114 116 if (newUrl == location.href) { 115 117 // would be easier to grab the <meta og:image> element, but that does 116 118 // not update when we navigate to new pages ··· 120 122 if (bgImgUrl.endsWith('"') && bgImgUrl.endsWith('"')) { 121 123 bgImgUrl = bgImgUrl.slice(1, -1); 122 124 } 123 - imgUrl = bgImgUrl; 125 + url = bgImgUrl; 124 126 } else { 125 127 const oembedUrl = 'https://soundcloud.com/oembed?format=json&url='+newUrl; 126 128 const oembed = await xhr(oembedUrl, 'POST', 'json'); 127 - imgUrl = oembed.thumbnail_url; 129 + url = oembed.thumbnail_url; 128 130 } 129 131 } else if (site == 'youtube music') { 130 - if (newUrl != location.href) throw 'For YouTube Music, you need to be at the URL' 131 - filename = 'Cover'; 132 + if (newUrl != location.href) throw 'For YouTube Music, you need to be at the URL'; 132 133 const coverImg = document.querySelector('.ytmusic-player-bar.image'); 133 134 const iurl = new URL(coverImg.src); 134 135 if (iurl.hostname == 'i.ytimg.com') { 135 136 if (!iurl.pathname.startsWith('/vi/')) { 136 - throw "i.ytimg.com url doesn't start with /vi/" 137 + throw "i.ytimg.com url doesn't start with /vi/"; 137 138 } 138 139 const id = iurl.pathname.substr(4, 11); 139 - imgUrl = await getYouTubeThumbnail(id); 140 + url = await getYouTubeThumbnail(id); 140 141 } else { 141 - imgUrl = googleUserContentUrl(iurl) 142 + url = googleUserContentUrl(iurl); 142 143 } 143 144 } else if (site == 'youtube music playlist') { 144 - if (newUrl != location.href) throw 'For YouTube Music, you need to be at the URL' 145 - filename = 'Cover'; 145 + if (newUrl != location.href) throw 'For YouTube Music, you need to be at the URL'; 146 146 const coverImg = document.querySelector('#img'); 147 147 const iurl = new URL(coverImg.src); 148 148 if (iurl.hostname == 'i.ytimg.com') { 149 149 if (iurl.pathname.startsWith('/vi/')) { 150 150 const id = iurl.pathname.substr(4, 11); 151 - imgUrl = await getYouTubeThumbnail(id); 151 + url = await getYouTubeThumbnail(id); 152 152 } 153 153 } else { 154 - imgUrl = googleUserContentUrl(iurl) 154 + url = googleUserContentUrl(iurl); 155 155 } 156 156 } else if (site == 'youtube') { 157 - filename = 'Thumbnail'; 158 157 if (newUrl == location.href) { 159 158 // youtube updates the schemaObject when we navigate to new pages 160 159 const schemaObject = JSON.parse(document.getElementById('scriptTag').text); 161 - imgUrl = schemaObject.thumbnailUrl[0]; 160 + url = schemaObject.thumbnailUrl[0]; 162 161 } else { 163 162 const id = new URL(newUrl).searchParams.get('v'); 164 - imgUrl = await getYouTubeThumbnail(id); 163 + url = await getYouTubeThumbnail(id); 165 164 } 166 165 } else if (site == 'spotify') { 167 - filename = 'Cover'; 168 166 if (newUrl == location.href) { 169 167 const coverEl = document.querySelector('._4c838ef3d2b6da1a61669046bbfae3d1-scss'); 170 168 if (coverEl.srcset) { ··· 172 170 const srcset = coverEl.srcset.split(','); 173 171 // The last src in srcset is the highest res 174 172 const srcItem = srcset[srcset.length-1].trim(); 175 - const srcUrl = srcItem.split(' ')[0] 176 - imgUrl = srcUrl 173 + const srcUrl = srcItem.split(' ')[0]; 174 + url = srcUrl; 177 175 } else { 178 176 // For /playlist/ urls 179 - imgUrl = coverEl.src 177 + url = coverEl.src; 180 178 } 181 179 } else { 182 - const id = new URL(newUrl).searchParams.get('v'); 183 - imgUrl = await getYouTubeThumbnail(id); 180 + const oembedUrl = 'https://open.spotify.com/oembed?format=json&url='+newUrl; 181 + const oembed = await xhr(oembedUrl, 'POST', 'json'); 182 + url = oembed.thumbnail_url; 184 183 } 185 184 } else { 186 185 throw 'Not supported on this URL'; 187 186 } 188 - const fileExt = imgUrl.split('.').pop(-1); 187 + lastImageUrl = url; 188 + return url; 189 + } 190 + 191 + function getFilename(newUrl, imageUrl) { 192 + const site = urlUtil.getSite(newUrl); 193 + let filename = 'Thumbnail'; 194 + switch (site) { 195 + case 'soundcloud': 196 + case 'youtube music': 197 + case 'youtube music playlist': 198 + case 'spotify': 199 + filename = 'Cover'; 200 + } 201 + if (site == 'soundcloud') filename = 'Cover'; 202 + else if (site == 'youtube music') filename = 'Cover'; 203 + else if (site == 'youtube music playlist') filename = 'Cover'; 204 + else if (site == 'spotify') filename = 'Cover'; 205 + const fileExt = imageUrl.split('.').pop(-1); 189 206 if (fileExt == 'jpg' || fileExt == 'png') { 190 207 filename = filename+'.'+fileExt; 191 208 } else { 192 209 filename = filename+'.jpg'; 193 210 } 194 - img.src = imgUrl; 211 + return filename; 195 212 } 196 213 197 - const copyEventListener = function(e) { 198 - e.preventDefault(); 199 - copy(imgUrl).then(() => { 200 - close(); 201 - }).catch((error) => { 202 - console.error('Error copying thumbnail: '+error); 203 - notify('Error copying thumbnail: '+error); 204 - close(); 205 - }); 206 - } 207 - const keydownEventListener = function(e) { 214 + function keydownEventListener(e) { 208 215 if ( 209 216 e.key == 'Escape' && 210 217 e.metaKey == false && ··· 216 223 close(); 217 224 } 218 225 } 226 + function copyEventListener(e) { 227 + e.preventDefault(); 228 + copy(lastImageUrl).then(() => { 229 + close(); 230 + }).catch((error) => { 231 + console.error('Error copying thumbnail: '+error); 232 + notify('Error copying thumbnail: '+error); 233 + close(); 234 + }); 235 + } 219 236 async function open() { 220 237 document.addEventListener('keydown', keydownEventListener); 221 238 document.addEventListener('copy', copyEventListener); ··· 231 248 } 232 249 close(); 233 250 251 + async function setup(newUrl) { 252 + imageUrl = await getImageUrl(newUrl); 253 + img.src = imageUrl; 254 + return imageUrl; 255 + } 256 + 234 257 chrome.runtime.onMessage.addListener(async function(msg, sender, sendResponse) { 235 258 if (msg.type == 'existance-check') { 236 259 sendResponse({ injected: true }); ··· 238 261 notify(msg.notificationMsg); 239 262 } else if (msg.type == 'open') { 240 263 try { 241 - await setup(msg.externalUrl || location.href); 242 - open(); 264 + const imageUrl = await setup(msg.externalUrl || location.href); 265 + open(imageUrl); 243 266 } catch(error) { 244 267 notify('Error opening thumbnail: '+error); 245 268 console.error('Error opening thumbnail: '+error); 246 269 } 247 270 } else if (msg.type == 'download') { 248 271 try { 249 - await setup(msg.externalUrl || location.href); 250 - await download(imgUrl, filename); 272 + const imageUrl = await setup(msg.externalUrl || location.href); 273 + const filename = getFilename(imageUrl); 274 + await download(imageUrl, filename); 251 275 } catch(error) { 252 - notify('Error downloading thumbnail: '+error) 253 - console.error('Error downloading thumbnail: '+error) 276 + notify('Error downloading thumbnail: '+error); 277 + console.error('Error downloading thumbnail: '+error); 254 278 } 255 279 if (thumbnailGrabber.style.display != 'none') close(); 256 280 } else if (msg.type == 'copy') { 257 281 try { 258 - await setup(msg.externalUrl || location.href); 259 - await copy(imgUrl); 282 + const imageUrl = await setup(msg.externalUrl || location.href); 283 + await copy(imageUrl); 260 284 notify('Copied'); 261 285 } catch(error) { 262 - notify('Error copying thumbnail: '+error) 263 - console.error('Error copying thumbnail: '+error) 286 + notify('Error copying thumbnail: '+error); 287 + console.error('Error copying thumbnail: '+error); 264 288 } 265 289 if (thumbnailGrabber.style.display != 'none') close(); 266 290 } 267 - }) 291 + }); 268 292 269 293 thumbnailGrabber.addEventListener('click', async function(e) { 270 294 if (e.target == this) { 271 295 close(); 272 296 } else if (e.target.textContent == 'DOWNLOAD') { 273 297 try { 274 - await download(imgUrl, filename); 298 + const filename = getFilename(lastImageUrl); 299 + await download(lastImageUrl, filename); 275 300 } catch(err) { 276 301 console.error('Error downloading thumbnail: '+err); 277 302 notify('Error downloading thumbnail: '+err); ··· 279 304 close(); 280 305 } else if (e.target.textContent == 'COPY') { 281 306 try { 282 - await copy(imgUrl); 307 + await copy(lastImageUrl); 283 308 notify('Copied'); 284 309 } catch(err) { 285 310 console.error('Error copying thumbnail: '+err);
+1 -1
src/modules/url-util.js
··· 64 64 } 65 65 }, 66 66 matchPatterns, 67 - } 67 + };
+2 -2
src/options.js
··· 6 6 if (!keep) { 7 7 status.style.animation = 'fade-out 1s forwards'; 8 8 } 9 - }) 9 + }); 10 10 } 11 11 12 12 function save() { ··· 41 41 document.getElementById('cx-download').checked = items.cxDownload; 42 42 document.getElementById('cx-copy').checked = items.cxCopy; 43 43 } 44 - }) 44 + }); 45 45 } 46 46 47 47 document.addEventListener('DOMContentLoaded', restoreOptions);