diff --git a/Extension/Background.js b/Extension/Background.js index 2e60081..12c3549 100644 --- a/Extension/Background.js +++ b/Extension/Background.js @@ -24,7 +24,8 @@ const validComboSets = [ // valid combinations of tags origin, image hoster, ["TU", "tumblr", "tumblr"], ["VA", "img.booru.org", "vidyart"], ["MW", "medicalwhiskey","medicalwhiskey"], - ["DB", "danbooru", "danbooru"] + ["DB", "danbooru", "danbooru"], + ["IG", "fbcdn", "instagram"] ]; var invokeSaveAs = true, diff --git a/Extension/Parsers/IG.js b/Extension/Parsers/IG.js new file mode 100644 index 0000000..c39c122 --- /dev/null +++ b/Extension/Parsers/IG.js @@ -0,0 +1,107 @@ +"use strict"; +var tagsOrigin = "IG"; +var windowDisplacement = 0; + +//For somewhat more robust anchoring instead of pre-calculated 28 one can use +//const IG_ID_DISPLACEMENT = document.URL.lastIndexOf('/p/')+3; +const IG_ID_DISPLACEMENT = 28; + +const presentingArticle = 'article[role*="presentation"]'; +const selectableArticle = 'article[tabindex="-1"]'; +const scalableImage = 'img[sizes]'; + +const styleTargets = String.raw`${presentingArticle} div span a[href*="explore/tags"]`; + +function getAuthorHandle() { + var profileHandle = safeQuery(String.raw`${presentingArticle} div span a`).innerText; + return profileHandle.replace(/[ ,\\/:?<>\t\n\v\f\r]/g, '-'); +}; + +function getAuthorName() { + return ""; +}; + +function getPictureName() { + return ""; +}; + +function getTags() { + var tagString = " "; + for (let tag of safeQueryA('a[href*="explore/tags"]')) { + tagString += tag.innerText.replace(/[#]/g, '') + " "; + }; + return tagString.replace(/[,\\/:?<>\t\n\v\f\r]/g, '_'); +}; + +function getPictureID() { + if (document.URL.indexOf('/p/') === -1) return; + let pic_ID = document.URL.substring(IG_ID_DISPLACEMENT).replace(/[\W]/g, ''); //Instagram IDs contain A-z 0-9 and underscore + return (pic_ID)?"instagram_"+pic_ID:""; +} + +// ! Instagram-specific Image|Video unfvckery + +const target = String.raw `${presentingArticle} div ${scalableImage}, ${presentingArticle} div video + ${selectableArticle} div ${scalableImage}, ${selectableArticle} div video`; + +const carousel = String.raw `${presentingArticle} ul li ${scalableImage}, ${presentingArticle} ul li video + ${selectableArticle} div ${scalableImage}, ${selectableArticle} div video`; + +let individualPostPage = true; + +function findParentElementOfType(element, type) { + if (element.localName === "body") return null; + if (element.parentElement.localName === type) return element.parentElement; + return findParentElementOfType(element.parentElement, type); +} + +function cleanSlide(content) { + let overlayContainer; + switch (content.localName) { + case "img": + content.sizes = "4096px"; // Promoting the image to maximum quality, 4096 is arbitary here, scrset maximum currently is 1080 + overlayContainer = content.parentElement.parentElement; // Contains transparent overlay preventing right-clicking on image + if (overlayContainer.childNodes[1]) overlayContainer.childNodes[1].remove(); + break; + + case "video": + overlayContainer = content.parentElement.parentElement.parentElement.parentElement; // Contains the play button and its background + if (overlayContainer.childNodes[1]) { + overlayContainer.childNodes[1].style.top="40%"; + overlayContainer.childNodes[1].style.bottom="40%"; + overlayContainer.childNodes[1].style.left="40%"; + overlayContainer.childNodes[1].style.right="40%"; + }; + if (overlayContainer.childNodes[2]) { + overlayContainer.childNodes[2].style.top="40%"; + overlayContainer.childNodes[2].style.bottom="40%"; + overlayContainer.childNodes[2].style.left="40%"; + overlayContainer.childNodes[2].style.right="40%"; + }; + break; + default: + break; + } +} + +function periodicCleaner() { + // ! Since the scope is so narrow with only one element satisfying img[sizes*="px"] prepended by article[role*="presentation"] or article[tabindex="-1"], + // ! the following two lines (individual post page detection) are overkill but I will leave them just in case IG breaks something + + if (individualPostPage) individualPostPage = (document.URL.indexOf("instagram.com/p/") !== -1); // Transition true→false is possible, false→true is not + let content = individualPostPage ? document.querySelector(target) : document.body.lastElementChild.querySelector(target); // first picture or video in queue + + if (content) { + if (document.querySelector(carousel)) { + let carouselContainer = findParentElementOfType(content, 'ul'); + for (let slide of carouselContainer.children) if (slide.querySelector('div img, div video')) cleanSlide(slide.querySelector('div img, div video')); + } else { + cleanSlide(content); + } + setTimeout(periodicCleaner, 100); + } else { + setTimeout(periodicCleaner, 500); + } +} + +setTimeout(periodicCleaner, 200); \ No newline at end of file diff --git a/Extension/manifest.json b/Extension/manifest.json index accb739..3d0734b 100644 --- a/Extension/manifest.json +++ b/Extension/manifest.json @@ -7,7 +7,7 @@ "48": "SIR_48x48.png", "16": "SIR_16x16.png" }, - "version": "1.5.1", + "version": "1.6.0", "manifest_version": 2, "permissions": ["downloads", "contextMenus"], "content_scripts": [ @@ -60,6 +60,11 @@ "matches": ["*://medicalwhiskey.com/?p=*"], "run_at": "document_end", "js": ["Parsers/MW.js", "Content.js"] + }, + { + "matches": ["*://www.instagram.com/*"], + "run_at": "document_end", + "js": ["Parsers/IG.js", "Content.js"] } ], "background": { diff --git a/package.json b/package.json index a868828..3baac6a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sir_image_renamer", - "version": "1.5.1", + "version": "1.6.0", "description": "SIR is an Image Renamer.\nProvides meaningful image names when saving on Pixiv, Deviantart, Artstation, etc.", "scripts": { "test": "web-ext lint --source-dir=./Extension/", diff --git a/test.js b/test.js index 3728df7..f4c05a2 100644 --- a/test.js +++ b/test.js @@ -9,7 +9,8 @@ var Links_to_test = [ "https://www.pixiv.net/artworks/79196939", // Special case: thumbnail detection "https://blurryken.tumblr.com/post/185528821532/do-you-want-to-share-a-bubble-tea-with-me", // NO IDS KNOWN - ID tracking not implemented "https://twitter.com/RGVaerialphotos/status/1280334509863579648", // NO IDS KNOWN - ID tracking not implemented Special case: image must be ORIG - "https://vidyart.booru.org/index.php?page=post&s=view&id=375444" // Special cases: >6 artists; very long tags string + "https://vidyart.booru.org/index.php?page=post&s=view&id=375444", // Special cases: >6 artists; very long tags string + "https://www.instagram.com/p/CFBa7UUM7ue/" // ID is alphanumeric, Special case: video ]; for (let link of Links_to_test) { window.open(link,"_blank"); };