First.
This commit is contained in:
commit
97e82ae1de
5 changed files with 155 additions and 0 deletions
19
README.md
Normal file
19
README.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# NotSmug
|
||||
|
||||
NotSmug is a Chromium-compatible browser extension that allows you to download
|
||||
or view paywalled images from the SmugMug platform en masse. I made this so I
|
||||
could access church photos.
|
||||
|
||||
## How to use/install
|
||||
|
||||
1. Clone/download this repository
|
||||
2. Go to chrome://extensions
|
||||
3. Load unpacked (you might need to enable some developer mode)
|
||||
4. Select the folder containing NotSmug
|
||||
5. Go to a SmugMug gallery that is paywalled
|
||||
6. Click on the extension, you can choose to open all images in a new tab
|
||||
or download all images.
|
||||
|
||||
## LICENSE
|
||||
|
||||
Public Domain CC0, as all things should be.
|
48
content.js
Normal file
48
content.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
console.log("NotSmug loaded...");
|
||||
|
||||
|
||||
/*********************************************
|
||||
* Description - Get highest quality version of an image
|
||||
* Author - Vilyaem
|
||||
* *******************************************/
|
||||
function Upgrade(url) {
|
||||
return url.replace(/(-[A-Z]\d+)?\.jpg/, '-X5.jpg');
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* Description - Get all image URLs possible
|
||||
* Author - Vilyaem
|
||||
* *******************************************/
|
||||
function ExtractImages() {
|
||||
const allUrls = new Set();
|
||||
|
||||
/*extract image urls everywhere, they start with photos.smugmug.com*/
|
||||
document.querySelectorAll('*').forEach(el => {
|
||||
/*attributes*/
|
||||
['src', 'href', 'data-src'].forEach(attr => {
|
||||
const val = el.getAttribute?.(attr);
|
||||
if (val && val.includes('photos.smugmug.com') && val.endsWith('.jpg')) {
|
||||
allUrls.add(Upgrade(val));
|
||||
}
|
||||
});
|
||||
|
||||
const style = el.getAttribute?.('style') || '';
|
||||
const matches = [...style.matchAll(/url\(["']?(https:\/\/photos\.smugmug\.com\/.*?\.jpg)["']?\)/g)];
|
||||
matches.forEach(match => allUrls.add(Upgrade(match[1])));
|
||||
});
|
||||
|
||||
/*scripts and styles*/
|
||||
document.querySelectorAll('script, style').forEach(node => {
|
||||
const text = node.textContent;
|
||||
const matches = [...text.matchAll(/https:\/\/photos\.smugmug\.com\/.*?\.jpg/g)];
|
||||
matches.forEach(match => allUrls.add(Upgrade(match[0])));
|
||||
});
|
||||
|
||||
return Array.from(allUrls);
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request.action === "GetImageURLs") {
|
||||
sendResponse({ urls: ExtractImages() });
|
||||
}
|
||||
});
|
18
manifest.json
Normal file
18
manifest.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"manifest_version": 3,
|
||||
"name": "NotSmug",
|
||||
"author": "Vilyaem",
|
||||
"version": "1.0",
|
||||
"description": "Allows you to download download-restricted SmugMug images.",
|
||||
"permissions": ["scripting", "tabs", "downloads"],
|
||||
"action": {
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["*://*.smugmug.com/*"],
|
||||
"js": ["content.js"],
|
||||
"run_at": "document_idle"
|
||||
}
|
||||
]
|
||||
}
|
26
popup.html
Normal file
26
popup.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
min-width: 220px;
|
||||
padding: 10px;
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
padding: 10px;
|
||||
font-size: 15px;
|
||||
cursor: pointer;
|
||||
background-color: cornflower;
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button id="open">Open SmugMug Images in individual tabs</button>
|
||||
<button id="download">Download all SmugMug images</button>
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
44
popup.js
Normal file
44
popup.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*********************************************
|
||||
* Description - Get all images in the current tab
|
||||
* Author - Vilyaem
|
||||
* *******************************************/
|
||||
async function GetImagesFromTab() {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
|
||||
if (!tab || !tab.id || !tab.url.startsWith("http")) {
|
||||
alert("Not a valid tab.");
|
||||
return [];
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
chrome.tabs.sendMessage(tab.id, { action: "GetImageURLs" }, (response) => {
|
||||
if (chrome.runtime.lastError || !response) {
|
||||
alert("Content script not available. Try refreshing the page.");
|
||||
return resolve([]);
|
||||
}
|
||||
resolve(response.urls || []);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* Description - Open all images in a new tab
|
||||
* Author - Vilyaem
|
||||
* *******************************************/
|
||||
document.getElementById("open").addEventListener("click", async () => {
|
||||
const urls = await GetImagesFromTab();
|
||||
if (urls.length === 0) return alert("No image URLs found.");
|
||||
urls.forEach(url => chrome.tabs.create({ url }));
|
||||
});
|
||||
|
||||
/*********************************************
|
||||
* Description - Download all images
|
||||
* Author - Vilyaem
|
||||
* *******************************************/
|
||||
document.getElementById("download").addEventListener("click", async () => {
|
||||
const urls = await GetImagesFromTab();
|
||||
if (urls.length === 0) return alert("No image URLs found.");
|
||||
urls.forEach(url => {
|
||||
chrome.downloads.download({ url });
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue