diff --git a/README.md b/README.md index 98b4518..f7314ae 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# [instantgram] v2024.06.11 Summer Feeling :sunglasses: -![GitHub release](https://img.shields.io/badge/release-v2024.06.11-green) +# [instantgram] v2024.10.07 Beginning of Fall 🍂 +![GitHub release](https://img.shields.io/badge/release-v2024.10.07-green) ![badge](https://img.shields.io/badge/for-instagram-yellow.svg?style=flat-square) [![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](http://standardjs.com/) @@ -20,25 +20,23 @@ With this version we support all modern browsers that have ECMAScript 2015 (es6) | Browser | Compatible? | | -------------------- | -------------------| | Google Chrome | :white_check_mark: | -| Mozilla Firefox* | :white_check_mark: | +| Mozilla Firefox | :white_check_mark: | | Edge on chromium >=80 | :white_check_mark: | -| Edge** | :warning: | +| Edge Legacy* | :warning: | | Internet Explorer 11 | :x: | -*_ Supported again on [instantgram] version >= 10.0.0 -**_ apparently Edge doesn't allow you to drag a button to the bookmark bar +*_ apparently Edge Legacy doesn't allow you to drag a button to the bookmark bar ## Roadmap -- ~~a way of notify updates in [instantgram]~~ :heavy_check_mark: in v2.0.0 -- ~~make a gif explaining the [instantgram]~~ :heavy_check_mark: -- ~~video :smirk_cat:~~ :heavy_check_mark: in v2.2.0 +- ? ## Contributing Read [CONTRIBUTING.md](CONTRIBUTING.md) for more information. :heart: ## Changelog +- v2024.10.07- [instangram] merged with [instantgram-light] due to the new build system, as we are now under the 65KB limit. - v2024.06.11 - [instangram] Fixed github pages bug, fixed current slider number often not correct detected... - v2024.06.06 - [instangram] Replaced the old, inefficient webpack build system with Rollup. Also, switched from the Babel minimizer to SWC. Removed the Bookmarkletify dependency and updated Metalsmith to the latest version. Completed an efficient rewrite of all modules to reduce the overall size. Redesigned the UI into tabs to clarify some settings and added some new settings. Fixed some videos are treated as images. And many more cosmetic fixes... - v2023.06.01 - [instangram] Complete rewrite of instantgram changed the way this software detects images/videos.\ @@ -54,59 +52,6 @@ Fixed some other bugs. New versioning. Fixed stories video detection. #23 Fixed wrong order capturing. #24 -- v12.1.0 - [instangram] Support for the latest backend version of instagram. -- v12.0.0 - [instangram] Rewrite of instantgram to support last backend of insta. \ -Profile page bulk downloader dropped because insta deleted all api around them. -Maybe there is a possibility in the future but unfortunately not now. -- v11.1.0 - [instangram] Added default faster video detection, rewrite fallback blob video detection, fixed most bugs. -- v11.0.0 - [instangram] Now compatible with new Instagram layout/design. \ -Bulk profile page downloader disabled because of instagram changed APIs, searching in future releases for a hot fix. -- v10.1.3 - [instangram] Fix saving videos on some post types. -- v10.1.2 - [instangram] Fix issue #17 can't save most types of videos anymore. -- v10.1.1 - [instangram] Fix profile page downloading doesn't work anymore. -- v10.1.0 - [instangram] New image detection algorithm. Image with highest percent in feed and visible in viewport will be grabed by the scanner. -- v10.0.6 - [instangram] Fix #15 Instantgram often grabs avatars/profile pics -- v10.0.5 - [instangram] Fix carousel not working -- v10.0.4 - [instangram] Fix reels not working. -- v10.0.3 - [instangram] Fix profile page downloading/compression calculation. -- v10.0.2 - [instangram] Complete rewrite of the Project to support Firefox again. \ -By rewriting, we were also able to increase the speed noticeably. \ -Fixed bug on profile page modal showed twice. -Fixed bug on some pages where nothing happens. -- v10.0.1 - [instangram] Complete rewrite of the Project to support Firefox again. \ -By rewriting, we were also able to increase the speed noticeably. \ -Fixed bug on some pages where nothing happens. -- v10.0.0 - [instangram] Complete rewrite of the Project to support Firefox again. \ -By rewriting, we were also able to increase the speed noticeably. -- v9.0.0 - [instangram] Now you can download a whole user profile. \ -But with limits it only works in Chrome, Opera, Safari. Firefox currently are not supported. -- v8.1.1 - [instangram] Fix video are not downloadable in feed in due of commented out variable. -- v8.1.0 - [instangram] Re-designed video downloader to handle encrypted videos. -- v8.0.2 - [instangram] Fixed updater that had displayed an old version. -- v8.0.1 - [instangram] Added spanish language. -- v8.0.0 - [instangram] Migrate to typescript which has fixed many bugs. -- v7.1.2 - [instangram] Fix update dialog styling. -- v7.1.1 - [instangram] Fix video download on some situations. -- v7.1.0 - [instangram] Re-design update dialog. -- v7.0.1 - [instangram] Fix update handler. -- v7.0.0 - [instangram] Fix get highest image on every post or story. -- v6.0.0 - [instangram] Biggest Update ever! \ -Added support for blob videos, means all videos can be downloaded again. \ -Supports the new and old story layout. \ -Replaced native browser dialog with own for nicer display. \ -The removed update checker in version 4.0 is available again. \ -This version should now work properly and display all errors in the console correctly. -- v5.2.0 - [instangram] Refactor Languages. -- v5.1.1 - [instangram] Fix set language: undefined in dev console. -- v5.1.0 - [instangram] Instagram changed their stories design to a new one. And this update adress this new design and make it compatible again. -- v5.0.0 - [instangram] We have completely rewritten instantgram. \ -With this version we support all modern browsers that have ECMAScript 2015 (es6) support. -- v4.0.0 - [instangram] now working again with full support of all media types also recognizes canvas images.\ -Also it has now support for multiple images videos in any site feed or post with modal. -- v2.4.0 - [instangram] now supports Stories. -- v2.3.0 - [instangram] now supports localization, both app and website. Initially it has en-US and pt-BR. You can help us translate [instantgram] for your language! Cool? Read [contributing](CONTRIBUTING.md) for more information. -- v2.2.0 - [instantgram] now supports video too! :movie_camera: -- After v2.0.0, [instantgram] has your data saved in `localStorage` and can be accessed entering `localStorage.getItem('instantgram')` in console inside instagram.com. If you can't access this item, you may be using a version before v2.0.0. [1]:http://thinkbig-company.github.io/instantgram \ No newline at end of file diff --git a/dist/main.js b/dist/main.js index 23b1c95..ea2bacb 100644 --- a/dist/main.js +++ b/dist/main.js @@ -1,21 +1,34 @@ -!function(e){"use strict";(t=a||(a={})).Ad="AD",t.Image="IMAGE",t.Video="VIDEO",t.Carousel="CAROUSEL",t.UNDEFINED="UNDEFINED";var t,a,i={langs:{"en-US":{"helpers.localize_defaultlang":"[instantgram] set language: ${LANG_DEFAULT} \n For more information about available languages please check http://thinkbig-company.github.io/instantgram","index@alert_onlyWorks":"Works only on instagram.com","index@profilepage_downloader_disabled":"Sorry the ProfilePage downloader is currently disabled because instagram changed their system.\n\nMaybe in the future there will be a solution to fix the problem.","index@alert_dontFound":"Did you open any Instagram post? Like for example
https://www.instagram.com/p/CIGrv1VMBkS/
","index#program#modal_settings@title":"Settings","index#program#modal_settings@settings_attention":"Attention: You must open this app again for your changes to be applied!","index#program#modal_settings@settings_general_title_1":"Enable monetized posts","index#program#modal_settings@settings_general_description_1":"Ad blockers must be deactivated","index#program#modal_settings@settings_general_title_2":"Open download in new tab","index#program#modal_settings@settings_general_description_2":"The download will be opened in a new tab","index#program#modal_settings@settings_general_title_3":"Slideshow on/off","index#program#modal_settings@settings_general_description_3":"Enable or disable the automatic slideshow","index#program#modal_settings@settings_general_title_4":"Change file name format for downloads","index#program#modal_settings@settings_general_description_4":"Change the file name format for the downloads here
The default format is
{Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}","index#program#modal_settings@settings_stories_title_1":"Mute stories","index#program#modal_settings@settings_stories_description_1":"Stories are muted when opened","index#program#modal_settings@settings_stories_title_2":"Pause stories when opening","index#program#modal_settings@settings_stories_description_2":"Stories are paused when opened","index#program#modal_settings@settings_stories_title_3":"Display stories individually","index#program#modal_settings@settings_stories_description_3":"Stories are displayed individually when opened","index@download":"Download","index@save":"Save","index@saved":"Saved","index@close":"Close","modules.update@consoleWarnOutdatedInfo":"[instantgram] is outdated. Please check http://thinkbig-company.github.io/instantgram for available updates.","modules.update@consoleWarnOutdatedInfoVersions":"[instantgram] Installed version: ${data.version} | New update: ${data.onlineVersion}","modules.update@determineIfGetUpdateIsNecessary_contacting":"[instantgram] is looking for available updates...","modules.update@determineIfGetUpdateIsNecessary_updated":"[instantgram] updated your current version.","modules.update@determineIfGetUpdateIsNecessary_@update_available":"There is a new update available","modules.update@determineIfGetUpdateIsNecessary_@load_update":"Get update",ad:"Sponsored"},"de-DE":{"helpers.localize_defaultlang":"Ausgew\xe4hlte Sprache: ${LANG_DEFAULT} \n Weitere Informationen zu den unterst\xfctzten Sprachen findest du auf http://thinkbig-company.github.io/instantgram","index@alert_onlyWorks":"Funktioniert nur auf instagram.com","index@profilepage_downloader_disabled":"Leider ist der ProfilePage-Downloader derzeit deaktiviert, da Instagram sein System ge\xe4ndert hat.\n\nVielleicht gibt es in Zukunft eine L\xf6sung f\xfcr dieses Problem.","index@alert_dontFound":"Hast du einen Instagram Post ge\xf6ffnet? Zum Beispiel
https://www.instagram.com/p/CIGrv1VMBkS/
","index#program#modal_settings@title":"Einstellungen","index#program#modal_settings@settings_attention":"Achtung: Sie m\xfcssen diese App erneut \xf6ffnen, damit ihre \xc4nderungen \xfcbernommen werden!","index#program#modal_settings@settings_general_title_1":"Monetarisierte Beitr\xe4ge aktivieren","index#program#modal_settings@settings_general_description_1":"Werbeblocker m\xfcssen deaktiviert sein","index#program#modal_settings@settings_general_title_2":"Download in neuem Tab \xf6ffnen","index#program#modal_settings@settings_general_description_2":"Der Download wird in einem neuen Tab ge\xf6ffnet","index#program#modal_settings@settings_general_title_3":"Slideshow ein/ausschalten","index#program#modal_settings@settings_general_description_3":"Aktivieren oder deaktivieren Sie die automatische Slideshow","index#program#modal_settings@settings_general_title_4":"Dateinamenformat f\xfcr Downloads \xe4ndern","index#program#modal_settings@settings_general_description_4":"\xc4ndere hier das Dateinamenformat f\xfcr die Downloads
Das Standardformat ist
{Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}","index#program#modal_settings@settings_stories_title_1":"Stories stummschalten","index#program#modal_settings@settings_stories_description_1":"Beim \xf6ffnen werden Stories stummgeschaltet","index#program#modal_settings@settings_stories_title_2":"Stories beim \xf6ffnen pausieren","index#program#modal_settings@settings_stories_description_2":"Beim \xf6ffnen werden Stories pausiert","index#program#modal_settings@settings_stories_title_3":"Stories einzeln anzeigen","index#program#modal_settings@settings_stories_description_3":"Beim \xf6ffnen werden Stories einzeln angezeigt","index@download":"Download","index@save":"Speichern","index@saved":"Gespeichert","index@close":"Schlie\xdfen","modules.update@consoleWarnOutdatedInfo":"[instantgram] ist veraltet. Bitte besuche die Seite http://thinkbig-company.github.io/instantgram f\xfcr ein Update.","modules.update@consoleWarnOutdatedInfoVersions":"[instantgram] Installierte Version: ${data.version} | Neue Version: ${data.onlineVersion}","modules.update@determineIfGetUpdateIsNecessary_contacting":"[instantgram] sucht nach neuen verf\xfcgbaren Updates...","modules.update@determineIfGetUpdateIsNecessary_updated":"[instantgram] wurde aktualisiert.","modules.update@determineIfGetUpdateIsNecessary_@update_available":"Es ist ein neues Update verf\xfcgbar","modules.update@determineIfGetUpdateIsNecessary_@load_update":"Update laden",ad:"Anzeige"},"es-AR":{"helpers.localize_defaultlang":"[instantgram] establecer idioma: ${LANG_DEFAULT} \n Para m\xe1s informaci\xf3n sobre los idiomas disponibles, consulte http://thinkbig-company.github.io/instantgram","index@alert_onlyWorks":"S\xf3lo funciona en instagram.com","index@profilepage_downloader_disabled":"Lo siento, la descarga de ProfilePage est\xe1 deshabilitada actualmente porque Instagram cambi\xf3 su sistema.\n\nTal vez en el futuro haya una soluci\xf3n para solucionar el problema.","index@alert_dontFound":"\xbfHas abierto alg\xfan post de Instagram? Como por ejemplo
https://www.instagram.com/p/CIGrv1VMBkS/
","index#program#modal_settings@title":"Ajustes","index#program#modal_settings@settings_attention":"Atenci\xf3n: \xa1Debes abrir esta aplicaci\xf3n nuevamente para que se apliquen los cambios!","index#program#modal_settings@settings_general_title_1":"Habilitar publicaciones monetizadas","index#program#modal_settings@settings_general_description_1":"Los bloqueadores de publicidad deben estar desactivados","index#program#modal_settings@settings_general_title_2":"Abrir descarga en una nueva pesta\xf1a","index#program#modal_settings@settings_general_description_2":"La descarga se abrir\xe1 en una nueva pesta\xf1a","index#program#modal_settings@settings_general_title_3":"Slideshow on/off","index#program#modal_settings@settings_general_description_3":"Activar/desactivar presentaci\xf3n de diapositivas","index#program#modal_settings@settings_general_title_4":"Cambiar el formato del nombre del archivo para descargas","index#program#modal_settings@settings_general_description_4":"Cambie el formato del nombre de archivo para las descargas aqu\xed
El formato predeterminado es
{Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}","index#program#modal_settings@settings_stories_title_1":"Historias mudas","index#program#modal_settings@settings_stories_description_1":"Las historias se silencian cuando se abren","index#program#modal_settings@settings_stories_title_2":"Pausar historias al abrir","index#program#modal_settings@settings_stories_description_2":"Las historias se pausan cuando se abren.","index#program#modal_settings@settings_stories_title_3":"Mostrar historias individualmente","index#program#modal_settings@settings_stories_description_3":"Las historias se muestran individualmente cuando se abren","index@download":"Descargar","index@save":"Ahorrar","index@saved":"Salvado","index@close":"Cerca","modules.update@consoleWarnOutdatedInfo":"[instantgram] es obsoleto. Consulte en http://thinkbig-company.github.io/instantgram las actualizaciones disponibles.","modules.update@consoleWarnOutdatedInfoVersions":"[instantgram] Versi\xf3n instalada: ${data.version} | Nueva actualizaci\xf3n: ${data.onlineVersion}","modules.update@determineIfGetUpdateIsNecessary_contacting":"[instantgram] est\xe1 buscando actualizaciones disponibles...","modules.update@determineIfGetUpdateIsNecessary_updated":"[instantgram] ha actualizado su versi\xf3n actual.","modules.update@determineIfGetUpdateIsNecessary_@update_available":"Hay una nueva actualizaci\xf3n disponible","modules.update@determineIfGetUpdateIsNecessary_@load_update":"Obtener informaci\xf3n",ad:"Publicidad"},"pt-BR":{"helpers.localize_defaultlang":"[instantgram] idioma configurado: ${LANG_DEFAULT} \npara mais informa\xe7\xf5es sobre os idiomas suportados, acesse http://thinkbig-company.github.io/instantgram","index@alert_onlyWorks":"[instantgram] somente funciona no instagram.com","index@profilepage_downloader_disabled":"Lamentamos que o programa de download ProfilePage esteja atualmente desativado porque o programa mudou seu sistema.\n\nTalvez, no futuro, haja uma solu\xe7\xe3o para resolver o problema.","index@alert_dontFound":"ops, voc\xea est\xe1 em algum post do instagram? ex:
https://www.instagram.com/p/CIGrv1VMBkS/
","index#program#modal_settings@title":"Configura\xe7\xf5es","index#program#modal_settings@settings_attention":"Aten\xe7\xe3o: Voc\xea deve abrir este aplicativo novamente para que suas altera\xe7\xf5es sejam aplicadas!","index#program#modal_settings@settings_general_title_1":"Habilitar postagens monetizadas","index#program#modal_settings@settings_general_description_1":"Os bloqueadores de an\xfancios devem ser desativados","index#program#modal_settings@settings_general_title_2":"Abrir o download em uma nova guia","index#program#modal_settings@settings_general_description_2":"O download ser\xe1 aberto em uma nova guia","index#program#modal_settings@settings_general_title_3":"Ativa\xe7\xe3o/desativa\xe7\xe3o da apresenta\xe7\xe3o de slides","index#program#modal_settings@settings_general_description_3":"Ativar ou desativar a apresenta\xe7\xe3o autom\xe1tica de slides","index#program#modal_settings@settings_general_title_4":"Alterar o formato do nome do arquivo para downloads","index#program#modal_settings@settings_general_description_4":"Altere o formato do nome do arquivo para os downloads aqui
O formato padr\xe3o \xe9
{Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}","index#program#modal_settings@settings_stories_title_1":"Hist\xf3rias mudas","index#program#modal_settings@settings_stories_description_1":"As hist\xf3rias s\xe3o silenciadas quando abertas","index#program#modal_settings@settings_stories_title_2":"Pausar hist\xf3rias ao abrir","index#program#modal_settings@settings_stories_description_2":"As hist\xf3rias s\xe3o pausadas quando abertas","index#program#modal_settings@settings_stories_title_3":"Exibir hist\xf3rias individualmente","index#program#modal_settings@settings_stories_description_3":"As hist\xf3rias s\xe3o exibidas individualmente quando abertas","index@download":"Download","index@save":"Salvar","index@saved":"Salvo em","index@close":"Fechar","modules.update@consoleWarnOutdatedInfo":"[instantgram] est\xe1 desatualizado. Acesse http://thinkbig-company.github.io/instantgram para atualizar","modules.update@consoleWarnOutdatedInfoVersions":"[instantgram] vers\xe3o local: ${data.version} | nova vers\xe3o: ${data.onlineVersion}","modules.update@determineIfGetUpdateIsNecessary_contacting":"[instantgram] est\xe1 procurando atualiza\xe7\xf5es...","modules.update@determineIfGetUpdateIsNecessary_updated":"[instantgram] informa\xe7\xf5es locais atualizadas","modules.update@determineIfGetUpdateIsNecessary_@update_available":"H\xe1 uma nova atualiza\xe7\xe3o dispon\xedvel","modules.update@determineIfGetUpdateIsNecessary_@load_update":"Carga de actualiza\xe7\xe3o",ad:"Patrocinado"}}};let n=navigator.language;-1!==n.indexOf("-")&&(n=n.split("-")[0]),-1!==n.indexOf("_")&&(n=n.split("_")[0]);let r={de:"de-DE",en:"en-US",es:"es-AR",pt:"pt-BR"}[n];function o(e,t=r){try{if(Object.prototype.hasOwnProperty.call(i.langs,t)||(t="en-US"),i.langs[t][e])return i.langs[t][e];return""}catch(a){return console.error(`[${$.NAME}]LOC error:`,a),`ops, an error ocurred in localization system. Enter in https://github.com/ThinkBIG-Company/${$.NAME}/issues/new and open an issue with this code: "LOC_dont_found_str_neither_default:[${t}->${e}]" - for more information open the console`}}console.info(o("helpers.localize_defaultlang").replace("${LANG_DEFAULT}",r));let s=new Map;function l(e){let t=window.location.pathname,a=t.split("/"),i={"/reel/":()=>a[2],"/reels/":()=>a[2],"/stories/":()=>a[3]};for(let e in i)if(t.startsWith(e))return i[e]();let n=/^\/p\/([^/]+)\//;for(let t of Array.from(e.querySelectorAll("a[href]"))){let e=t.getAttribute("href").match(n);if(e)return e[1]}return null}async function d(e){let t=window.location.href.match(/www.instagram.com\/stories\/[^/]+\/(\d+)/);if(t)return t[1];if(!s.has(e)){let t=`https://www.instagram.com/p/${e}/`,a=await fetch(t),i=(await a.text()).match(/instagram:\/\/media\?id=(\d+)|["' ]media_id["' ]:["' ](\d+)["' ]/);if(!i){let e=await fetch(t+"?__a=1&__d=dis");if(!(i=(await e.text()).match(/"pk":(\d+)/)))return null}let n=null;for(let e=0;e script");for(let a=0;awindow.location.pathname.startsWith(e);if(i===a&&(s("/p/")||s("/reels/")))o.items&&o.items[0]&&o.items[0].user&&(i=o.items[0].user.username,n=_("https://www.instagram.com",window.location.pathname,i));else{let e=o.reels_media&&o.reels_media[0]&&o.reels_media[0].user&&s("/stories/"),t=o.items&&o.items[0]&&o.items[0].user;(e||t)&&(i=e?o.reels_media[0].user.username:o.items[0].user.username,n=_("https://www.instagram.com",window.location.pathname,i))}return await g(e,o,i,n,t)}async function g(e,t,i,n,r){var s;let l,d="",m={showAds:"true"===localStorage.getItem(`${r.STORAGE_NAME}_settings_general_1`),openInNewTab:"true"===localStorage.getItem(`${r.STORAGE_NAME}_settings_general_2`),autoSlideshow:"true"===localStorage.getItem(`${r.STORAGE_NAME}_settings_general_3`),formattedFilenameInput:localStorage.getItem(`${r.STORAGE_NAME}_settings_general_4`)||"{Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}",mutedStories:"true"===localStorage.getItem(`${r.STORAGE_NAME}_settings_stories_1`),noMultiStories:"true"===localStorage.getItem(`${r.STORAGE_NAME}_settings_stories_3`)},c=(e,t)=>{var n,s,l;let c,g;if(e&&e.width&&e.height&&e.url)c=e.url,g="profile_pic.jpg";else{let{formattedFilename:a,url:n}=function(e,t,a,i){let n=new Date(1e3*e.taken_at),r=function(e,t){for(let a in t){let i=RegExp(`{${a}}`,"g");e=e.replace(i,t[a])}return e=(e=e.replace(/\s+/g,"-")).replace(/[^\w-.]/g,"")}(a,{Minute:n.getMinutes().toString().padStart(2,"0"),Hour:n.getHours().toString().padStart(2,"0"),Day:n.getDate().toString().padStart(2,"0"),Month:(n.getMonth()+1).toString().padStart(2,"0"),Year:n.getFullYear().toString(),Username:t}),{extension:o,url:s}=e.items?"video_versions"in e?{extension:"mp4",url:e.items[0].video_versions[0].url}:{extension:"jpg",url:e.items[0].image_versions2.candidates[0].url}:"video_versions"in e?{extension:"mp4",url:e.video_versions[0].url}:{extension:"jpg",url:e.image_versions2.candidates[0].url};return{formattedFilename:r+"_"+Number(i+1)+"."+o,url:s}}(e,i,m.formattedFilenameInput,t);c=n,g=a}let p=(n=void 0!==e.carousel_media?a.Carousel:void 0!==e.video_dash_manifest||void 0!==e.video_duration||void 0!==e.video_versions?a.Video:a.Image,s=c,l=m.mutedStories,n===a.Video?``:``),u=`https://instantgram.1337.pictures/download.php?data=${btoa(c)}:${btoa(g)}`,h=m.openInNewTab?c:u;d+=``},g=0;m.noMultiStories&&t.reels_media&&t.reels_media.length>0&&t.reels_media[0].items.length>0?(g=f(e),c(t.reels_media[0].items[g],g)):(l=0,t.reels_media&&t.reels_media.length>0&&t.reels_media[0].items?(t.reels_media[0].items.forEach(c),l=t.reels_media[0].items.length):t.items&&t.items.length>0&&t.items[0].carousel_media?(t.items[0].carousel_media.forEach(c),l=t.items[0].carousel_media.length):t.items&&t.items.length>0?(c(t.items[0],0),l=1):t&&t.user.hd_profile_pic_url_info.url&&(c(t.user.hd_profile_pic_url_info,0),l=1),g=l);let p=(s=d,`
${s}
`),u=g>0?f(e):0;return{found:!0,mediaType:a.UNDEFINED,mediaInfo:t,modalBody:p,selectedSliderIndex:u,userName:i,userLink:n}}function p(e){if(!e||!e.getBoundingClientRect)return 0;let{top:t,bottom:a}=e.getBoundingClientRect(),i=window.scrollY||document.documentElement.scrollTop,n=i+window.innerHeight,r=t+window.scrollY,o=a+window.scrollY;return i>o||n2?t[2]:null}async function h(e,t,a){return t?window.location.pathname.startsWith("/stories/highlights/")?await m({type:"getReelsMediaFromFeed",articleNode:e,id:null}):window.location.pathname.startsWith("/stories/")?await m({type:"getReelsMediaFromFeed",articleNode:e,id:a}):await m({type:"getMediaFromInfo",articleNode:e}):await m({type:"getReelsMediaFromFeed",articleNode:e,id:a})}function f(e){let t;if(!e)return 0;for(let a of["._aamj._acvz._acnc._acng",".x1ned7t2.x78zum5","section header div","section > div header > div","section > div > div > div > div > div > div > div > div","div > div > div > div > div > div > div > div"])if(t=e.querySelector(a))break;if(!t)return 0;let a=t?Array.from(t.children):[];for(let e=0;e1)return e}else for(let a of Array.from(t)){let t=a.style.width,i=a.style.transform;if(t&&"100%"!==t||i&&""!==i.trim())return e}}return 0}async function b(e,t){try{let a=await fetch(e,{method:"GET",headers:{Accept:"*/*","X-IG-App-ID":t},credentials:"include",mode:"cors"});if(200!==a.status)return console.log(`Fetch API failed with status code: ${a.status}`),null;return await a.json()}catch(e){return console.log(`Error fetching data: ${e} -${e.stack}`),null}}function _(e,t,a){return t.startsWith("/p/")||t.startsWith("/stories/")?`${e}/${a}/`:t.startsWith("/reels/")?`${e}/${a}/reels/`:null}class x{constructor(e){if(this.modal=null,this.imageURL=e.imageURL||"",this.heading=e.heading||[""],this.headingStyle=e.headingStyle||"",this.body=e.body||[""],this.bodyStyle=e.bodyStyle||"",this.buttonList=e.buttonList||[],this.callback=e.callback||null,null==document.getElementById($.NAME+"-modal")){let e=document.createElement("style");e.id=$.NAME+"-modal",e.innerHTML='.instantgram-modal-footer button,.instantgram-modal-header h5{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important;font-size:16px!important}.instantgram-modal-overlay{display:none!important;opacity:0!important;transition:.1s!important;position:fixed!important;top:0!important;left:0!important;right:0!important;bottom:0!important;z-index:1000!important;background:rgba(0,0,0,.65)!important;justify-content:center!important;align-items:center!important}.instantgram-modal{transition:width .1s ease-in-out!important;display:inline-block!important;width:400px!important;padding:1.6px!important;z-index:1001!important}.instantgram-modal select{margin-left:.8px!important;border:1px solid #dbdbdb!important;border-radius:3px!important;color:#262626!important;outline:0!important;padding:3px!important;text-align:center!important}@media (min-width:736px){.instantgram-modal{width:500px!important}}.instantgram-modal-content{position:relative;display:flex;flex-direction:column;width:100%!important;pointer-events:auto!important;background-clip:padding-box!important;outline:0!important}.instantgram-modal-header{color:#fff!important;background-color:#fd1d1d!important;background-image:linear-gradient(45deg,#405de6,#5851db,#833ab4,#c13584,#e1306c,#fd1d1d)!important;border-top-left-radius:12px!important;border-top-right-radius:12px!important;padding:0 16px!important;text-align:center}.instantgram-modal-header h5:nth-child(2){margin-top:-15px!important;margin-bottom:20px!important}.instantgram-modal-body{background:#fff!important;color:#000!important;position:relative!important;-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.instantgram-modal-body>img{background:#000;object-fit:scale-down}.instantgram-modal-footer{background-color:#fff!important;border-top:1px solid #dbdbdb!important;border-left:0!important;border-right:0!important;border-bottom-left-radius:12px!important;border-bottom-right-radius:12px!important;line-height:1.5!important;min-height:48px!important;padding:4px 8px!important;user-select:none!important;display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important;-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:center!important}.instantgram-modal-footer button{width:100%!important;min-height:39px!important;background-color:transparent!important;border:0!important;outline:0!important;cursor:pointer!important}.instantgram-modal-footer button.active{color:#0095e2!important}.instantgram-modal-show{opacity:1!important}.instantgram-modal-visible{display:flex!important}#instantgram-bulk-download-indicator{text-align:center!important}.instantgram-modal-db{color:#fff!important;background:linear-gradient(45deg,#405de6,#5851db,#833ab4,#c13584,#e1306c,#fd1d1d)!important;display:block;padding:.8rem;width:100%;border:none;cursor:pointer}.instantgram-modal-db:focus{outline:0;background:linear-gradient(45deg,rgba(64,93,230,.5),rgba(88,81,219,.5),rgba(131,58,180,.5),rgba(193,53,132,.5),rgba(225,48,108,.5),rgba(253,29,29,.5))!important}.instantgram-modal-header h5{color:#fff!important;margin:revert!important;display:flex;justify-content:center;align-items:center}.header-text-left{margin-right:auto}.header-text-right{margin-left:auto}.header-text-middle{margin:0}.header-text-middle a{color:#fff!important}.header-text-right:last-child{margin-right:30px}.instantgram-settings{cursor:pointer;display:inline-block;color:rgba(255,255,255,.7);background-color:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.2);border-radius:.3rem;transition:color .2s,background-color .2s,border-color .2s;width:40px;height:40px;top:16px;right:16px;position:absolute}.instantgram-settings:hover{color:rgba(255,255,255,.8);text-decoration:none;background-color:rgba(255,255,255,.2);border-color:rgba(255,255,255,.3)}.instantgram-modal-body input{color:#000!important}',document.head.appendChild(e)}}get element(){return this.modal}createModal(){let e=document.createElement("div");e.classList.add($.NAME+"-modal-overlay");let t=document.createElement("div");t.classList.add($.NAME+"-modal"),e.appendChild(t);let a=document.createElement("div");a.classList.add($.NAME+"-modal-content"),t.appendChild(a);let i=document.createElement("div");i.classList.add($.NAME+"-modal-header"),this.headingStyle.length>0&&i.setAttribute("style",this.headingStyle),a.appendChild(i),this.heading.forEach(e=>{if("string"!=typeof e||/<\/?[a-z][\s\S]*>/i.test(e)){if(/<\/?[a-z][\s\S]*>/i.test(e)){let t;let a=document.createElement("div"),n=document.createDocumentFragment();for(a.innerHTML=e;null!==(t=a.firstChild);)n.appendChild(t);i.appendChild(n)}else i.appendChild(e)}else{let t=document.createElement("h5");t.innerHTML=e,i.appendChild(t)}});let n=document.createElement("div");if(n.classList.add($.NAME+"-modal-body"),this.bodyStyle.length>0&&n.setAttribute("style",this.bodyStyle),a.appendChild(n),this.imageURL.length>0){let e=document.createElement("div");a.appendChild(e);let t=document.createElement("img");t.setAttribute("height","76px"),t.setAttribute("width","76px"),t.style.margin="auto",t.style.paddingBottom="20px",t.setAttribute("src",this.imageURL),e.appendChild(t)}if(this.body.forEach(e=>{if("string"!=typeof e||/<\/?[a-z][\s\S]*>/i.test(e)){if(/<\/?[a-z][\s\S]*>/i.test(e)){let t;let a=document.createElement("div"),i=document.createDocumentFragment();for(a.innerHTML=e;null!==(t=a.firstChild);)i.appendChild(t);n.appendChild(i)}else n.appendChild(e)}else{let t=document.createElement("p");t.innerText=e,n.appendChild(t)}}),this.buttonList.length>0){let e=document.createElement("div");e.classList.add($.NAME+"-modal-footer"),a.appendChild(e),this.buttonList.forEach(t=>{let a=document.createElement("button");a.classList.add($.NAME+"-modal-button"),a.innerText=t.text,t.active&&a.classList.add("active"),a.onclick=()=>{t&&t.callback&&t.callback(),this.close.bind(this)()},e.appendChild(a)})}else a.style.paddingBottom="4px;";return e}async open(){this.modal&&await this.close(),this.modal=this.createModal(),document.body.appendChild(this.modal),this.modal.classList.add($.NAME+"-modal-visible"),setTimeout(()=>{this.modal.classList.add($.NAME+"-modal-show")}),this.callback&&this.callback(this,this.modal)}async close(){this.modal&&(this.modal.classList.remove($.NAME+"-modal-show"),await new Promise(function(e){setTimeout(e,100)}),this.modal.classList.remove($.NAME+"-modal-visible"),this.modal.parentNode.removeChild(this.modal),this.modal=null)}async refresh(){this.modal&&(this.modal.parentNode.removeChild(this.modal),this.modal=null),await this.open(),this.callback&&this.callback(this,this.modal.querySelector("."+$.NAME+"-modal-body"))}}class v{getName(){return"FeedScanner"}collectMediaElementsInfo(e){return Array.from(e).map((e,t)=>({i1:t,mediaEl:e,elemVisiblePercentage:p(e)||0}))}async execute(e){try{let t=document.getElementsByTagName("article");if(0===t.length)return{found:!1,errorMessage:"No target found."};let a=this.collectMediaElementsInfo(t).reduce((e,t)=>e.elemVisiblePercentage>t.elemVisiblePercentage?e:t),i=t[a.i1];if(!i||i.getBoundingClientRect().height<40)return{found:!1,errorMessage:"Article not found or too small, likely an ad"};return await c(i,e)}catch(t){return console.error(`[${e.NAME}] ${e.VERSION}`,this.getName()+"()",t),{found:!1,errorMessage:t.message,error:t}}}}class y{getName(){return"PostAndReelScanner"}getArticleElement(){return document.querySelector("div[role='dialog'] article")||document.querySelector("section main > div > :first-child > :first-child")}async execute(e){try{let t=this.getArticleElement();if(!t)return{found:!1,errorMessage:"No target found."};return await c(t,e)}catch(t){return console.error(`[${e.NAME}] ${e.VERSION}`,this.getName()+"()",t),{found:!1,errorMessage:t.message,error:t}}}}class w{getName(){return"ProfileScanner"}async handleProfilePage(e){let t=u(window.location.href);if(!t)return{found:!1,errorMessage:"Invalid username extracted from URL"};try{let a=await m({type:"getUserInfoFromWebProfile",userName:t});if(!a.data.user.id)return{found:!1,errorMessage:"No userID found in userInfo"};let i=a.data.user.id,n=await m({type:"getUserFromInfo",userId:i});if(n&&n.user.hd_profile_pic_url_info.url)return await g(null,n,t,window.location.href,e);return{found:!1,errorMessage:"Incomplete userDetails received"}}catch(e){return{found:!1,userName:t,errorMessage:e.message,error:e}}}async execute(e){if(!e.regexProfilePath.test(window.location.pathname))return{found:!1,errorMessage:"Path does not match profile path regex, exiting."};try{let t=await this.handleProfilePage(e);if(!t)return{found:!1,errorMessage:"No result from handleProfilePage, returning null."};return t}catch(t){return console.error(`[${e.NAME}] ${e.VERSION}`,this.getName()+"()",t),{found:!1,errorMessage:t.message,error:t}}}}class S{getName(){return"ReelsScanner"}getRelevantArticles(){return Array.from(document.querySelectorAll("section > main > div > div")).filter(e=>e.children.length>0)}findMostRelevantArticle(e){if(0===e.length)return null;let t=e.map((e,t)=>({index:t,article:e,visibility:p(e)})).reduce((e,t)=>e.visibility>t.visibility?e:t,{index:-1,visibility:0});return t.visibility>0?e[t.index]:null}async execute(e){try{let t=this.getRelevantArticles(),a=this.findMostRelevantArticle(t);if(!a)return{found:!1,errorMessage:"No target found."};return await c(a,e)}catch(t){return console.error(`[${e.NAME}] ${e.VERSION}`,this.getName()+"()",t),{found:!1,errorMessage:t.message,error:t}}}}class k{getName(){return"StoriesScanner"}pausePlayCurrentStory(e){let t=Array.from(e.querySelectorAll("path")).find(e=>"M15 1c-3.3 0-6 1.3-6 3v40c0 1.7 2.7 3 6 3s6-1.3 6-3V4c0-1.7-2.7-3-6-3zm18 0c-3.3 0-6 1.3-6 3v40c0 1.7 2.7 3 6 3s6-1.3 6-3V4c0-1.7-2.7-3-6-3z"===e.getAttribute("d"));if(t){let e=t.closest('div[role="button"]');e?.click()}}findVisibleStory(e){let t=Array.from(e.querySelectorAll("section"));return t.find(e=>p(e))?.querySelector("div > div")}findFirstVisibleDiv(e){return Array.from(e.getElementsByTagName("div")).find(e=>e.offsetWidth>0&&e.offsetHeight>0)}shouldPauseStory(e){return"true"===localStorage.getItem(e.STORAGE_NAME+"_settings_stories_2")}async handleHighlights(e,t){let a=this.findVisibleStory(e);return a?(this.shouldPauseStory(t)&&this.pausePlayCurrentStory(a),c(a,t)):null}async handleFeedStories(e,t){let a=this.findFirstVisibleDiv(e);return a?(this.shouldPauseStory(t)&&this.pausePlayCurrentStory(a),c(a,t)):null}async execute(e){try{let t=document.querySelector("body > div:nth-child(5)");if(!t)return{found:!1,errorMessage:"No target found."};let a=window.location.pathname;if(a.startsWith("/stories/highlights/"))return await this.handleHighlights(t,e);if(a.startsWith("/stories/"))return await this.handleFeedStories(t,e);return{found:!1,errorMessage:"No target found."}}catch(t){return console.error(`[${e.NAME}] ${e.VERSION}`,this.getName()+"()",t),{found:!1,errorMessage:t.message,error:t}}}}class E{getName(){return"MediaScanner"}initModalSettingsListeners(e,t){let a=document.querySelectorAll("div.nav-tabs > button.nav-link"),i=document.querySelectorAll(".tab-pane"),n=e=>{e.preventDefault(),a.forEach(e=>e.classList.remove("active")),i.forEach(e=>e.classList.remove("show","active"));let t=e.currentTarget;t.classList.add("active");let n=t.getAttribute("data-target");if(n){let e=document.querySelector(n);e&&e.classList.add("show","active")}};a.forEach(e=>{e.addEventListener("click",n)}),Array.from(e.querySelectorAll('label.slideon input[type="checkbox"]')).forEach(e=>{let a=`${t.STORAGE_NAME}_${e.id.replace(/-/g,"_")}`;e.checked="true"===localStorage.getItem(a),e.addEventListener("change",()=>{localStorage.setItem(a,String(e.checked))})});let r=e.querySelector("#settings-general-4");if(r){let a=`${t.STORAGE_NAME}_settings_general_4`;r.value=localStorage.getItem(a)||"{Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}";let i=e.querySelector("#settings-general-btn-4");i.addEventListener("click",e=>{e.preventDefault(),localStorage.setItem(a,r.value),this.updateInputButtonStyle(i,"index@saved","btn-primary","btn-success"),setTimeout(()=>{this.updateInputButtonStyle(i,"index@save","btn-success","btn-primary")},1e3)})}}initializeStyles(e){this.removeStyleTagsWithIDs([e.NAME+"-cssGeneral",e.NAME+"-cssSlideOn",e.NAME+"-cssCarouselSlider"]),this.appendStyles(e.NAME+"-cssGeneral",".btn,label{display:inline-block}.btn,.form-control{padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5}.alert,p{margin-bottom:1rem}@keyframes horizontal-shaking{0%,100%{transform:translateX(0)}25%,75%{transform:translateX(5px)}50%{transform:translateX(-5px)}}.btn{color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control,.form-group{display:block;width:100%}.btn:hover{color:#212529;text-decoration:none}.btn:not(:disabled):not(.disabled){cursor:pointer}.btn-primary{color:#fff!important;background-color:#007bff!important;border-color:#007bff!important}.btn-primary:hover{color:#fff!important;background-color:#0069d9!important;border-color:#0062cc!important}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-success{color:#fff!important;background-color:#28a745!important;border-color:#28a745!important}.mt-1,.my-1{margin-top:.25rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mt-3,.my-3{margin-top:1rem!important}.w94{width:94%!important}.ml-15,.mr-15{margin-left:15px!important}label{margin-bottom:.5rem}.form-control{height:calc(1.5em + .75rem + 2px);color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.alert{position:relative;padding:.75rem 1.25rem;border:1px solid transparent;border-radius:.25rem}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.pr-0{padding-right:0!important}.col,.col-12,.col-auto{max-width:100%}.instantgram-modal-body *{box-sizing:border-box}b,strong{font-weight:bolder}button,input{overflow:visible}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.col-12{-ms-flex:0 0 100%;flex:0 0 100%}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid #eef0f3}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-item.active{z-index:2;color:#fff;background-color:#1b68ff;border-color:#1b68ff}.list-group-item.disabled,.list-group-item:disabled{color:#6d7174;pointer-events:none;background-color:#fff}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.mb-0,.my-0{margin-bottom:0!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-auto,.mx-auto{margin-left:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mt-4,.my-4{margin-top:1.5rem!important}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.text-muted{color:#6c757d!important}p{margin-top:0}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.tab-content>.tab-pane{display:none}.fade:not(.show){opacity:0}.fade{transition:opacity .15s linear}.tab-content>.active{display:block}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-tabs{border-bottom:1px solid #dee2e6;margin-bottom:-1px}.nav-tabs .nav-link{cursor:pointer;color:#000;margin-bottom:-1px;background-color:transparent;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:#e9ecef #e9ecef #dee2e6}.nav-link{display:block;padding:.5rem 1rem}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}"),this.appendStyles(e.NAME+"-cssSlideOn",'.slideon{position:relative;display:inline-block;width:42px;height:24px;vertical-align:middle}.slideon>input,input.slideon{display:none}.slideon-slider{position:absolute;cursor:pointer;border-radius:34px;top:0;left:0;right:0;bottom:0;background-color:#ccc;-webkit-transition:.4s;transition:.4s}.slideon-slider:before{position:absolute;content:"";height:22px;width:22px;left:1px;bottom:1px;border-radius:50%;background-color:#fff;-webkit-transition:.4s;transition:.4s;-webkit-box-shadow:0 0 3px 0 rgba(0,0,0,.45);-moz-box-shadow:0 0 3px 0 rgba(0,0,0,.45);box-shadow:0 0 3px 0 rgb(0,0,0,.45)}.slideon input:checked~.slideon-slider{background-color:#007bff}.slideon input:checked~.slideon-slider:before{-webkit-transform:translateX(18px);-ms-transform:translateX(18px);transform:translateX(18px)}.slideon input:disabled~.slideon-slider{opacity:.5}'),this.appendStyles(e.NAME+"-cssCarouselSlider",".slider-container{width:500px;overflow:hidden;position:relative}.slider{height:100%;display:flex;transition:left .5s;position:inherit}.slide{position:relative;background:#000}.slide img, .slide video{width:501px;height:300px;object-fit:contain}.slider-controls{display:flex;justify-content:center;margin-top:10px;margin-bottom:10px;flex-wrap:wrap;gap:5px;}.slider-controls button{cursor: pointer;width:25px;height:25px;padding:0;margin:0 5px;border-radius:50%;background:#ddd;border:0}.slider-controls button.active{background:#999}")}appendStyles(e,t){let a=document.createElement("style");a.id=e,a.innerHTML=t,document.body.appendChild(a)}displayModal(e,t,a,i,n){new x({heading:[t],body:[e.modalBody],bodyStyle:a,buttonList:i,callback:n}).open()}async handleURLPatterns(e){for(let t of[{regex:e.regexStoriesURI,scanner:k},{regex:e.regexProfilePath,scanner:w},{regex:e.regexRootPath,scanner:v},{regex:e.regexPostPath,scanner:y},{regex:e.regexReelURI,scanner:y},{regex:e.regexReelsURI,scanner:S}])if(t.regex.test(window.location.pathname))try{let a=new t.scanner,i=await a.execute(e);i.found&&(i.foundByModule=a.getName(),this.displayModal(i,`
- @${i.userName} - - -
`,"padding:0!important;text-align:center",[{active:!0,text:o("index@close")}],(t,a)=>{if(a.querySelector(".slider")){let t;let n=a.querySelector(".slider"),r=a.querySelectorAll(".slide"),o=a.querySelector(".slider-controls"),s=i.selectedSliderIndex;r.forEach((e,t)=>{let a=document.createElement("button");a.innerHTML=String(t+1),a.dataset.index=String(t),a.classList.toggle("active",1===r.length),r.length>1&&a.addEventListener("click",()=>{s=t,l(!0)}),o.appendChild(a)});let l=a=>{null!==document.fullscreenElement||(g(!1),n.style.transform=`translateX(${-r[0].clientWidth*s}px)`,[...o.children].forEach((e,t)=>{e.classList.toggle("active",t===s)}),a&&clearTimeout(t),"true"===localStorage.getItem(`${e.STORAGE_NAME}_settings_general_3`)&&d())},d=()=>{let e=r[s].querySelector("video");e?e.play()&&(e.onended=m):c()},m=()=>{s=(s+1)%r.length,l(!1)},c=()=>{t=setTimeout(m,5e3)},g=e=>{r.forEach(e=>{let t=e.querySelector("video");t&&t.pause()})};r.length>1&&l(!1);let p=()=>{null!==document.fullscreenElement&&clearTimeout(t)};document.addEventListener("fullscreenchange",p)}a.querySelector(`.${e.NAME}-settings`).addEventListener("click",()=>{let t=(e,t="",a={},i="")=>{let n=document.createElement(e);return t&&(n.className=t),Object.keys(a).forEach(e=>n.setAttribute(e,a[e])),i&&(n.innerHTML=i),n},a=(e,a,i,n)=>{let r=t("div","list-group-item"),o=t("div","row align-items-center"),s=t("div","col pr-0");s.appendChild(t("strong","mb-0",{},e)),a&&s.appendChild(t("p","text-muted mb-0",{},a));let l=t("div","col-auto"),d=t("label","slideon"),m=t("input","",{type:"checkbox",id:`settings-${i}`}),c=t("span","slideon-slider");if(d.appendChild(m),d.appendChild(c),l.appendChild(d),n){let n=t("div","form-group",{style:"display: flex;flex-direction: column;align-items: flex-start;"},`${e} -

${a}

- - - `);o.appendChild(n)}else o.appendChild(s),o.appendChild(l);return r.appendChild(o),r},i=t("div","container"),n=t("div","row justify-content-center"),r=t("div","col-12 col-lg-10 col-xl-8 mx-auto"),s=t("div","my-4"),l=t("nav"),d=t("div","nav nav-tabs",{id:"nav-tab",role:"tablist"});d.appendChild(t("button","nav-link active",{id:"nav-general-tab","data-toggle":"tab","data-target":"#nav-general",type:"button",role:"tab","aria-controls":"nav-general","aria-selected":"true"},"General")),d.appendChild(t("button","nav-link",{id:"nav-stories-tab","data-toggle":"tab","data-target":"#nav-stories",type:"button",role:"tab","aria-controls":"nav-stories","aria-selected":"false"},"Stories"));let m=t("div","tab-content",{id:"nav-tabContent"}),c=t("div","tab-pane fade active show",{id:"nav-general",role:"tabpanel","aria-labelledby":"nav-general-tab"}),g=t("div","tab-pane fade",{id:"nav-stories",role:"tabpanel","aria-labelledby":"nav-stories-tab"});[{title:"index#program#modal_settings@settings_general_title_1",description:"index#program#modal_settings@settings_general_description_1",settingsName:"general-1"},{title:"index#program#modal_settings@settings_general_title_2",description:"index#program#modal_settings@settings_general_description_2",settingsName:"general-2"},{title:"index#program#modal_settings@settings_general_title_3",description:"index#program#modal_settings@settings_general_description_3",settingsName:"general-3"},{title:"index#program#modal_settings@settings_general_title_4",description:"index#program#modal_settings@settings_general_description_4",settingsName:"general-4",isLargeInput:!0},{title:"index#program#modal_settings@settings_stories_title_1",description:"index#program#modal_settings@settings_stories_description_1",settingsName:"stories-1"},{title:"index#program#modal_settings@settings_stories_title_2",description:"index#program#modal_settings@settings_stories_description_2",settingsName:"stories-2"},{title:"index#program#modal_settings@settings_stories_title_3",description:"index#program#modal_settings@settings_stories_description_3",settingsName:"stories-3"}].forEach(e=>{(e.title.includes("general")?c:g).appendChild(a(o(e.title),o(e.description),e.settingsName,e.isLargeInput))}),m.appendChild(c),m.appendChild(g),l.appendChild(d),s.appendChild(l),s.appendChild(m),s.appendChild(t("div","alert alert-warning mt-3",{},o("index#program#modal_settings@settings_attention"))),r.appendChild(s),n.appendChild(r),i.appendChild(n),new x({heading:[`
- [${e.NAME}] - ${o("index#program#modal_settings@title")} - v${e.VERSION} -
`],body:[i],bodyStyle:null,buttonList:[{active:!0,text:o("index@close")}],callback:(t,a)=>{this.initModalSettingsListeners(a,e)}}).open()})}))}catch(a){let e=new t.scanner;console.error(`Error executing scanner ${e.getName()}:`,a)}}isModalOpen(e){return!!document.querySelector("div."+e.NAME+"-modal-overlay."+e.NAME+"-modal-visible."+e.NAME+"-modal-show")}removeStyleTagsWithIDs(e){e.forEach(e=>{let t=document.getElementById(e);t&&t.remove()})}shakeModal(e){let t=document.querySelector("."+e);t&&(t.style.animation="horizontal-shaking 0.25s linear infinite",setTimeout(()=>t.style.animation=null,1e3))}updateInputButtonStyle(e,t,a,i){e.textContent=o(t),e.classList.contains(i)?(e.classList.remove(i),e.classList.add(a)):(e.classList.remove(a),e.classList.add(i))}async execute(e){try{if(this.isModalOpen(e)){this.shakeModal(`${e.NAME}-modal`);return}this.initializeStyles(e),await this.handleURLPatterns(e)}catch(t){console.error(`${this.getName()}()`,`[${e.NAME}] ${e.VERSION}`,t)}}}class A{constructor(e){this.program=e,this.storageKey=`${e.STORAGE_NAME}`}async update(e){if(this.isUpdateNecessary(e)){let t=await this.fetchChangelog();t&&this.processChangelog(e,t)}}async fetchChangelog(){try{let e=await fetch("https://www.instagram.com/graphql/query/?query_hash=003056d32c2554def87228bc3fd9668a&variables={%22id%22:45039295328,%22first%22:100}"),[t,a]=(await e.json()).data.user.edge_owner_to_timeline_media.edges[0].node.edge_media_to_caption.edges[0].node.text.split("::");return{date:t,textBody:a}}catch(e){return console.error("Failed to fetch changelog: ",e),null}}processChangelog(e,{date:t,textBody:a}){let i=this.generateHtmlListFromText(a);this.storeVersionInfo(e,t),console.info(o("modules.update@update_successful")),new Date(t)>new Date(e)&&(this.showUpdateModal(e,t,i),this.informOutdatedVersionInDevConsole())}generateHtmlListFromText(e){return e.split(/[.!?]/).filter(e=>""!==e.trim()).reduce((e,t)=>e+`
  • ${t.trim()}
  • `,"
      ")+"
    "}storeVersionInfo(e,t){let a=new Date;a.setHours(a.getHours()+6);let i={version:e,onlineVersion:t,lastVerification:Date.now(),dateExpiration:a.getTime()};window.localStorage.setItem(this.storageKey,JSON.stringify(i))}isUpdateNecessary(e){let t=JSON.parse(window.localStorage.getItem(this.storageKey)||"{}"),a=new Date(e),i=new Date(t.onlineVersion),n=Date.now()>t.dateExpiration;return i>a||n||!t}showUpdateModal(e,t,a){new x({heading:[`
    [${this.program.NAME}]v${e}
    `],body:[`
    Update available v${t}
    ${a}
    `],buttonList:[{active:!0,text:"Ok"}]}).open()}informOutdatedVersionInDevConsole(){let e=JSON.parse(window.localStorage.getItem(this.storageKey)||"{}");console.warn(o("modules.update@consoleWarnOutdatedInfo")),console.warn(o("modules.update@consoleWarnOutdatedInfoVersions").replace("${data.version}",e.version).replace("${data.onlineVersion}",e.onlineVersion))}}console.clear();let N="instantgram",M="2024.06.11",I=N.toLowerCase().replace(/-/g,"_"),$={NAME:N,STORAGE_NAME:I,VERSION:M,browser:function(){let e=navigator.userAgent,t,a=e.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i)||[];return/trident/i.test(a[1])?{name:"IE",version:(t=/\brv[ :]+(\d+)/g.exec(e)||[])[1]||""}:"Chrome"===a[1]&&null!=(t=e.match(/\b(OPR|Edge)\/(\d+)/))?{name:t[1].replace("OPR","Opera"),version:t[2]}:(a=a[2]?[a[1],a[2]]:[navigator.appName,navigator.appVersion,"-?"],null!=(t=e.match(/version\/(\d+)/i))&&a.splice(1,1,t[1]),{name:a[0],version:a[1]})}(),hostname:window.location.hostname,path:window.location.pathname,regexHostname:/^instagram\.com$/,regexRootPath:/^\/+$/,regexProfilePath:/^\/(\w[-\w.]+)\/?$/,regexPostPath:/^\/p\//,regexReelURI:/reel\/(.*)+/,regexReelsURI:/reels\/(.*)+/,regexStoriesURI:/\/stories\/(\w+)|\/highlights\/(\d+)\//,foundByModule:null};(async()=>{if(!$.hostname.includes("instagram.com")){new x({heading:[`
    [${$.NAME}]v${$.VERSION}
    `],body:[o("index@alert_onlyWorks")],bodyStyle:"text-align:center",buttonList:[{active:!0,text:"Ok"}]}).open();return}new E().execute($);{let e=new A($);await e.update(M)}})(),e.program=$}({}); +!function(e){"use strict";let t="background:#f6e2d8!important;background:linear-gradient(45deg,#f6e2d8 0%,#68c2e8 100%)!important;",i=`.slideon{position:relative;display:inline-block;width:42px;height:24px;vertical-align:middle}.slideon>input,input.slideon{display:none}.slideon-slider{position:absolute;cursor:pointer;border-radius:34px;top:0;left:0;right:0;bottom:0;background-color:#ccc;-webkit-transition:.4s;transition:.4s}.slideon-slider:before{position:absolute;content:"";height:22px;width:22px;left:1px;bottom:1px;border-radius:50%;background-color:#fff;-webkit-transition:.4s;transition:.4s;-webkit-box-shadow:0 0 3px 0 rgba(0,0,0,.45);-moz-box-shadow:0 0 3px 0 rgba(0,0,0,.45);box-shadow:0 0 3px 0 rgb(0,0,0,.45)}.slideon input:checked~.slideon-slider{${t}}.slideon input:checked~.slideon-slider:before{-webkit-transform:translateX(18px);-ms-transform:translateX(18px);transform:translateX(18px)}.slideon input:disabled~.slideon-slider{opacity:.5}`,a=`.instantgram-btn,label{display:inline-block}.instantgram-btn{padding:.25rem 1.9rem .4rem 1.9rem;font-size:1rem;font-weight:400;line-height:1.5}.alert,p{margin-bottom:1rem}@keyframes horizontal-shaking{0%,100%{transform:translateX(0)}25%,75%{transform:translateX(5px)}50%{transform:translateX(-5px)}}.instantgram-btn{color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control,.form-group{display:block;width:100%}.instantgram-btn:hover{color:#212529;text-decoration:none}.instantgram-btn:not(:disabled):not(.disabled){cursor:pointer}.instantgram-btn-primary{color:#fff!important;${t}border:4px!important}.instantgram-btn-primary:hover{color:#fff!important;background-color:#0069d9!important;border-color:#0062cc!important}.instantgram-btn-primary:not(:disabled):not(.disabled).active,.instantgram-btn-primary:not(:disabled):not(.disabled):active,.show>.instantgram-btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.instantgram-btn-primary:not(:disabled):not(.disabled).active:focus,.instantgram-btn-primary:not(:disabled):not(.disabled):active:focus,.show>.instantgram-btn-primary.dropdown-toggle:focus{box-shadow: 0 0 0 .2rem rgba(246, 226, 216, 0.5)}.instantgram-btn-success{color:#fff!important;background-color:#28a745!important;border-color:#28a745!important}.mt-1,.my-1{margin-top:.25rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mt-3,.my-3{margin-top:1rem!important}.w94{width:94%!important}.ml-15,.mr-15{margin-left:15px!important}label{margin-bottom:.5rem}.form-control{padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;height:calc(1.5em + .75rem + 2px);color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.alert{position:relative;padding:.75rem 1.25rem;border:1px solid transparent;border-radius:.25rem}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.pr-0{padding-right:0!important}.col,.col-12,.col-auto{max-width:100%}.instantgram-modal-body *{box-sizing:border-box}b,strong{font-weight:bolder}button,input{overflow:visible}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.col-12{-ms-flex:0 0 100%;flex:0 0 100%}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid #eef0f3}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-item.active{z-index:2;color:#fff;background-color:#1b68ff;border-color:#1b68ff}.list-group-item.disabled,.list-group-item:disabled{color:#6d7174;pointer-events:none;background-color:#fff}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.mb-0,.my-0{margin-bottom:0!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-auto,.mx-auto{margin-left:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mt-4,.my-4{margin-top:1.5rem!important}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.text-muted{color:#6c757d!important}p{margin-top:0}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.tab-content>.tab-pane{display:none}.fade:not(.show){opacity:0}.fade{transition:opacity .15s linear}.tab-content>.active{display:block}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-tabs{border-bottom:1px solid #dee2e6;margin-bottom:-1px}.nav-tabs .nav-link{cursor:pointer;color:#000;margin-bottom:-1px;background-color:transparent;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:#e9ecef #e9ecef #dee2e6}.nav-link{display:block;padding:.5rem 1rem}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff;border-bottom: aliceblue 6px solid;}`,n=`.instantgram-modal-footer button,.instantgram-modal-header h5{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important;font-size:16px!important}.instantgram-modal-overlay{display:none!important;opacity:0!important;transition:.1s!important;position:fixed!important;top:0!important;left:0!important;right:0!important;bottom:0!important;z-index:1000!important;background:rgba(0,0,0,.65)!important;justify-content:center!important;align-items:center!important}.instantgram-modal{transition:width .1s ease-in-out!important;display:inline-block!important;width:400px!important;padding:1.6px!important;z-index:1001!important}.instantgram-modal select{margin-left:.8px!important;border:1px solid #dbdbdb!important;border-radius:3px!important;color:#262626!important;outline:0!important;padding:3px!important;text-align:center!important}@media (min-width:736px){.instantgram-modal{width:500px!important}}.instantgram-modal-content{position:relative;display:flex;flex-direction:column;width:100%!important;pointer-events:auto!important;background-clip:padding-box!important;outline:0!important}.instantgram-modal-header{${t}}.instantgram-modal-header{color:#fff!important;border-top-left-radius:12px!important;border-top-right-radius:12px!important;padding:0 16px!important;text-align:center}.instantgram-modal-header h5:nth-child(2){margin-top:-15px!important;margin-bottom:20px!important}.instantgram-modal-body{background:#fff!important;color:#000!important;position:relative!important;-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.instantgram-modal-body>img{background:#000;object-fit:scale-down}.instantgram-modal-footer{background-color:#fff!important;border-top:1px solid #dbdbdb!important;border-left:0!important;border-right:0!important;border-bottom-left-radius:12px!important;border-bottom-right-radius:12px!important;line-height:1.5!important;min-height:48px!important;padding:4px 8px!important;user-select:none!important;display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important;-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:center!important}.instantgram-modal-footer button{width:100%!important;min-height:39px!important;background-color:transparent!important;border:0!important;outline:0!important;cursor:pointer!important}.instantgram-modal-footer button.active{color:#0095e2!important}.instantgram-modal-show{opacity:1!important}.instantgram-modal-visible{display:flex!important}#instantgram-bulk-download-indicator{text-align:center!important}.instantgram-modal-db{color:#fff!important;${t}display:block;padding:.8rem;width:100%;border:none;cursor:pointer}.instantgram-modal-db:focus{outline:0;${t}}.instantgram-modal-header h5{color:#fff!important;margin:revert!important;display:flex;justify-content:space-between;align-items:center;height:15px;}.header-text-left{flex:1;text-align:left;}.header-text-right{flex:1;text-align:right;}.header-text-middle{flex:1;text-align:center;}.header-text-middle a{color:#fff!important}.instantgram-settings{cursor:pointer;display:inline-block;color:rgba(255,255,255,.7);background-color:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.2);border-radius:.3rem;transition:color .2s,background-color .2s,border-color .2s;width:40px;height:40px;vertical-align:middle;}.instantgram-settings:hover{color:rgba(255,255,255,.8);text-decoration:none;background-color:rgba(255,255,255,.2);border-color:rgba(255,255,255,.3)}.instantgram-modal-body input{color:#000!important}`,r='[instantgram]';(o=s||(s={})).Ad="AD",o.Image="IMAGE",o.Video="VIDEO",o.Carousel="CAROUSEL",o.UNDEFINED="UNDEFINED";var o,s,l={langs:{"en-US":{"helpers.localizeDefaultLang":"[instantgram] set language: ${LANG_DEFAULT} \n For more information about available languages please check http://thinkbig-company.github.io/instantgram",alertWorksOnlyOn:"Works only on instagram.com",alertNotFound:"Did you open any Instagram post? Like for example
    https://www.instagram.com/p/CIGrv1VMBkS/
    ",modalSettingsTitle:"Settings",modalSettingsAttention:"Attention: You must open this app again for your changes to be applied!",modalSettingsGeneral:"General",modalSettingsGenTitle1:"Enable monetized posts",modalSettingsGenDesc1:"Ad blockers must be deactivated",modalSettingsGenTitle2:"Open download in new tab",modalSettingsGenDesc2:"The download will be opened in a new tab",modalSettingsGenTitle3:"Slideshow on/off",modalSettingsGenDesc3:"Enable or disable the automatic slideshow",modalSettingsGenTitle4:"Change file name format for downloads",modalSettingsGenDesc4:"Change the file name format for the downloads here
    The default format is
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}",modalSettingsStoriesTitle1:"Mute stories",modalSettingsStoriesDesc1:"Stories are muted when opened",modalSettingsStoriesTitle2:"Pause stories when opening",modalSettingsStoriesDesc2:"Stories are paused when opened",modalSettingsStoriesTitle3:"Display stories individually",modalSettingsStoriesDesc3:"Stories are displayed individually when opened",download:"Download",save:"Save",saved:"Saved",close:"Close",consoleWarnOutdatedInfo:"[instantgram] is outdated. Please check http://thinkbig-company.github.io/instantgram for available updates.",consoleWarnOutdatedVersions:"[instantgram] Installed version: ${data.version} | New update: ${data.onlineVersion}",ad:"Sponsored"},"de-DE":{"helpers.localizeDefaultLang":"Ausgew\xe4hlte Sprache: ${LANG_DEFAULT} \n Weitere Informationen zu den unterst\xfctzten Sprachen findest du auf http://thinkbig-company.github.io/instantgram",alertWorksOnlyOn:"Funktioniert nur auf instagram.com",alertNotFound:"Hast du einen Instagram Post ge\xf6ffnet? Zum Beispiel
    https://www.instagram.com/p/CIGrv1VMBkS/
    ",modalSettingsTitle:"Einstellungen",modalSettingsAttention:"Achtung: Sie m\xfcssen diese App erneut \xf6ffnen, damit ihre \xc4nderungen \xfcbernommen werden!",modalSettingsGeneral:"Allgemein",modalSettingsGenTitle1:"Monetarisierte Beitr\xe4ge aktivieren",modalSettingsGenDesc1:"Werbeblocker m\xfcssen deaktiviert sein",modalSettingsGenTitle2:"Download in neuem Tab \xf6ffnen",modalSettingsGenDesc2:"Der Download wird in einem neuen Tab ge\xf6ffnet",modalSettingsGenTitle3:"Slideshow ein/ausschalten",modalSettingsGenDesc3:"Aktivieren oder deaktivieren Sie die automatische Slideshow",modalSettingsGenTitle4:"Dateinamenformat f\xfcr Downloads \xe4ndern",modalSettingsGenDesc4:"\xc4ndere hier das Dateinamenformat f\xfcr die Downloads
    Das Standardformat ist
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}",modalSettingsStoriesTitle1:"Stories stummschalten",modalSettingsStoriesDesc1:"Beim \xf6ffnen werden Stories stummgeschaltet",modalSettingsStoriesTitle2:"Stories beim \xf6ffnen pausieren",modalSettingsStoriesDesc2:"Beim \xf6ffnen werden Stories pausiert",modalSettingsStoriesTitle3:"Stories einzeln anzeigen",modalSettingsStoriesDesc3:"Beim \xf6ffnen werden Stories einzeln angezeigt",download:"Download",save:"Speichern",saved:"Gespeichert",close:"Schlie\xdfen",consoleWarnOutdatedInfo:"[instantgram] ist veraltet. Bitte besuche die Seite http://thinkbig-company.github.io/instantgram f\xfcr ein Update.",consoleWarnOutdatedVersions:"[instantgram] Installierte Version: ${data.version} | Neue Version: ${data.onlineVersion}",ad:"Anzeige"},"es-AR":{"helpers.localizeDefaultLang":"[instantgram] establecer idioma: ${LANG_DEFAULT} \n Para m\xe1s informaci\xf3n sobre los idiomas disponibles, consulte http://thinkbig-company.github.io/instantgram",alertWorksOnlyOn:"S\xf3lo funciona en instagram.com",alertNotFound:"\xbfHas abierto alg\xfan post de Instagram? Como por ejemplo
    https://www.instagram.com/p/CIGrv1VMBkS/
    ",modalSettingsTitle:"Ajustes",modalSettingsAttention:"Atenci\xf3n: \xa1Debes abrir esta aplicaci\xf3n nuevamente para que se apliquen los cambios!",modalSettingsGeneral:"General",modalSettingsGenTitle1:"Habilitar publicaciones monetizadas",modalSettingsGenDesc1:"Los bloqueadores de publicidad deben estar desactivados",modalSettingsGenTitle2:"Abrir descarga en una nueva pesta\xf1a",modalSettingsGenDesc2:"La descarga se abrir\xe1 en una nueva pesta\xf1a",modalSettingsGenTitle3:"Slideshow on/off",modalSettingsGenDesc3:"Activar/desactivar presentaci\xf3n de diapositivas",modalSettingsGenTitle4:"Cambiar el formato del nombre del archivo para descargas",modalSettingsGenDesc4:"Cambie el formato del nombre de archivo para las descargas aqu\xed
    El formato predeterminado es
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}",modalSettingsStoriesTitle1:"Historias mudas",modalSettingsStoriesDesc1:"Las historias se silencian cuando se abren",modalSettingsStoriesTitle2:"Pausar historias al abrir",modalSettingsStoriesDesc2:"Las historias se pausan cuando se abren.",modalSettingsStoriesTitle3:"Mostrar historias individualmente",modalSettingsStoriesDesc3:"Las historias se muestran individualmente cuando se abren",download:"Descargar",save:"Ahorrar",saved:"Salvado",close:"Cerca",consoleWarnOutdatedInfo:"[instantgram] es obsoleto. Consulte en http://thinkbig-company.github.io/instantgram las actualizaciones disponibles.",consoleWarnOutdatedVersions:"[instantgram] Versi\xf3n instalada: ${data.version} | Nueva actualizaci\xf3n: ${data.onlineVersion}",ad:"Publicidad"},"pt-BR":{"helpers.localizeDefaultLang":"[instantgram] idioma configurado: ${LANG_DEFAULT} \npara mais informa\xe7\xf5es sobre os idiomas suportados, acesse http://thinkbig-company.github.io/instantgram",alertWorksOnlyOn:"[instantgram] somente funciona no instagram.com",alertNotFound:"ops, voc\xea est\xe1 em algum post do instagram? ex:
    https://www.instagram.com/p/CIGrv1VMBkS/
    ",modalSettingsTitle:"Configura\xe7\xf5es",modalSettingsAttention:"Aten\xe7\xe3o: Voc\xea deve abrir este aplicativo novamente para que suas altera\xe7\xf5es sejam aplicadas!",modalSettingsGeneral:"Geral",modalSettingsGenTitle1:"Habilitar postagens monetizadas",modalSettingsGenDesc1:"Os bloqueadores de an\xfancios devem ser desativados",modalSettingsGenTitle2:"Abrir o download em uma nova guia",modalSettingsGenDesc2:"O download ser\xe1 aberto em uma nova guia",modalSettingsGenTitle3:"Ativa\xe7\xe3o/desativa\xe7\xe3o da apresenta\xe7\xe3o de slides",modalSettingsGenDesc3:"Ativar ou desativar a apresenta\xe7\xe3o autom\xe1tica de slides",modalSettingsGenTitle4:"Alterar o formato do nome do arquivo para downloads",modalSettingsGenDesc4:"Altere o formato do nome do arquivo para os downloads aqui
    O formato padr\xe3o \xe9
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}",modalSettingsStoriesTitle1:"Hist\xf3rias mudas",modalSettingsStoriesDesc1:"As hist\xf3rias s\xe3o silenciadas quando abertas",modalSettingsStoriesTitle2:"Pausar hist\xf3rias ao abrir",modalSettingsStoriesDesc2:"As hist\xf3rias s\xe3o pausadas quando abertas",modalSettingsStoriesTitle3:"Exibir hist\xf3rias individualmente",modalSettingsStoriesDesc3:"As hist\xf3rias s\xe3o exibidas individualmente quando abertas",download:"Download",save:"Salvar",saved:"Salvo em",close:"Fechar",consoleWarnOutdatedInfo:"[instantgram] est\xe1 desatualizado. Acesse http://thinkbig-company.github.io/instantgram para atualizar",consoleWarnOutdatedVersions:"[instantgram] vers\xe3o local: ${data.version} | nova vers\xe3o: ${data.onlineVersion}",ad:"Patrocinado"}}};let d=navigator.language;-1!==d.indexOf("-")&&(d=d.split("-")[0]),-1!==d.indexOf("_")&&(d=d.split("_")[0]);let m={de:"de-DE",en:"en-US",es:"es-AR",pt:"pt-BR"}[d];function c(e,t=m){try{if(Object.prototype.hasOwnProperty.call(l.langs,t)||(t="en-US"),l.langs[t][e])return l.langs[t][e];return""}catch(i){return console.error(`[${et.NAME}]LOC error:`,i),`ops, an error ocurred in localization system. Enter in https://github.com/ThinkBIG-Company/${et.NAME}/issues/new and open an issue with this code: "LOC_dont_found_str_neither_default:[${t}->${e}]" + for more information open the console`}}console.info(c("helpers.localizeDefaultLang").replace("${LANG_DEFAULT}",m));let g=new Map,u=()=>{let e=/"X-IG-App-ID":"([\d]+)"/,t=Array.from(document.querySelectorAll("body > script")).map(t=>t.textContent?.match(e)).find(Boolean);return t?t[1]:null},p=e=>{let t=window.location.pathname,i=t.split("/"),a={"/reel/":()=>i[2],"/reels/":()=>i[2],"/stories/":()=>i[3]};for(let e in a)if(t.startsWith(e))return a[e]();let n=/^\/p\/([^/]+)\//;return Array.from(e.querySelectorAll("a[href]")).map(e=>e.getAttribute("href").match(n)).find(e=>e)?.[1]||null};async function h(e){let t=window.location.href.match(/www.instagram.com\/stories\/[^/]+\/(\d+)/);if(t)return t[1];if(!g.has(e)){let t=`https://www.instagram.com/p/${e}/`,i=await fetch(t),a=(await i.text()).match(/instagram:\/\/media\?id=(\d+)|["' ]media_id["' ]:["' ](\d+)["' ]/);if(!a){let e=await fetch(t+"?__a=1&__d=dis");if(!(a=(await e.text()).match(/"pk":(\d+)/)))return null}let n=null;for(let e=0;e{let{type:t,articleNode:i,id:a,userName:n,userId:r}=e,o=u();if(!o)return console.log("AppID not found"),null;let s=await ({getReelsMediaFromFeed:async()=>{let e=a||await h(p(i));return e?`https://i.instagram.com/api/v1/feed/reels_media/?reel_ids=${a?"":"highlight%3A"}${e}`:null},getMediaFromInfo:async()=>{let e=await h(p(i));return e?`https://i.instagram.com/api/v1/media/${e}/info/`:null},getUserFromInfo:()=>`https://i.instagram.com/api/v1/users/${r}/info/`,getUserInfoFromWebProfile:()=>`https://i.instagram.com/api/v1/users/web_profile_info/?username=${n}`})[t]?.();return s?F(s,o):null},b=async(e,t)=>{let i=e=>window.location.pathname.startsWith(e),a=w(window.location.href),n=p(e),r=i("/stories/")?(await f({type:"getUserInfoFromWebProfile",userName:a}))?.data?.user?.id??null:null,o="",l=await E(e,n,r);if(a===n&&(i("/p/")||i("/reels/")))a=l.items?.[0]?.user?.username;else{let e=l.reels_media?.[0]?.user?.username,t=l.items?.[0]?.user?.username;a=e||t||a}let d=L("https://www.instagram.com",window.location.pathname,a);if(t.settings.showAds&&T(e,i("/stories/"))){if(!(e.querySelector("video[playsinline]")||e.querySelector("img[draggable]")))return{found:!1};let i=document.body,n=i.querySelectorAll("video")||[],r=Array.from(R(i).querySelectorAll('img[draggable="false"]')||[]).find(e=>V(e)&&!H(e)),m="",g=null;if(n.length){let e=P(q(n[0]).reverse().find(e=>"SECTION"===e.nodeName));m=e?.return?.memoizedProps?.post?.videoUrl||B(n[0]),g=s.Video}else r?.src&&!r.src.startsWith("data:")&&(m=r.src,g=s.Image);if(m){let{formattedFilename:e,url:i}=x(m,a,t.settings.formattedFilenameInput,0),n=k(g,i,t.settings.storiesMuted),r=`https://instantgram.1337.pictures/download.php?data=${btoa(i)}:${btoa(e)}`,s=t.settings.openInNewTab?i:r;return o+=` + `,{found:!0,mediaType:_(),mediaInfo:l,modalBody:D(o),selectedSliderIndex:0,userName:a,userLink:d}}return{found:!1}}if(t.settings.noMultiStories&&l.reels_media?.[0]?.items.length>0){let i=N(e);o=y(o,l.reels_media[0].items[i],i,a,t)}else M(l,(e,i,n)=>{o=y(o,e,i,a,t)});let m=D(o),g=N(e);return{found:!0,mediaType:_(),mediaInfo:l,modalBody:m,selectedSliderIndex:g,userName:a,userLink:d}};async function v(e,t,i,a,n){let r="",o=0;if(n.settings.noMultiStories&&t.reels_media?.[0]?.items.length>0){let a=N(e);r=y(r,t.reels_media[0].items[a],a,i,n),o=1}else M(t,(e,t,a)=>{r=y(r,e,t,i,n)});let s=D(r),l=o>0?N(e):0;return{found:!0,mediaType:_(),mediaInfo:t,modalBody:s,selectedSliderIndex:l,userName:i,userLink:a}}let y=(e,t,i,a,n)=>{let{formattedFilename:r,url:o}=x(t,a,n.settings.formattedFilenameInput,i),s=k($(t),o,n.settings.storiesMuted),l=`https://instantgram.1337.pictures/download.php?data=${btoa(o)}:${btoa(r)}`,d=n.settings.openInNewTab?o:l;return e+` + `},S=e=>{if(!e?.getBoundingClientRect)return 0;let{top:t,bottom:i}=e.getBoundingClientRect(),a=window.scrollY||document.documentElement.scrollTop,n=a+window.innerHeight,r=t+window.scrollY,o=i+window.scrollY;return a>o||n{if(e&&"object"==typeof e&&e.height&&e.width&&e.url)return{formattedFilename:`${t}.jpg`,url:e.url};if("string"==typeof e){let n=new Date,r=I(i,{Minute:n.getMinutes().toString().padStart(2,"0"),Hour:n.getHours().toString().padStart(2,"0"),Day:n.getDate().toString().padStart(2,"0"),Month:(n.getMonth()+1).toString().padStart(2,"0"),Year:n.getFullYear().toString(),Username:t});return{formattedFilename:`${r}_${a+1}.txt`,url:e}}if(e&&"object"==typeof e){let n=new Date(1e3*e.taken_at),r=I(i,{Minute:n.getMinutes().toString().padStart(2,"0"),Hour:n.getHours().toString().padStart(2,"0"),Day:n.getDate().toString().padStart(2,"0"),Month:(n.getMonth()+1).toString().padStart(2,"0"),Year:n.getFullYear().toString(),Username:t}),{extension:o,url:s}=A(e);return{formattedFilename:`${r}_${a+1}.${o}`,url:s}}throw Error("Unsupported media type")},w=e=>{let t=e.match(/https:\/\/www\.instagram\.com\/(stories\/|reels\/|p\/)?([^/?]+)/);return t?t[2]:null},A=e=>e.items?"video_versions"in e&&e.items[0]?.video_versions?.[0]?.url?{extension:"mp4",url:e.items[0].video_versions[0].url}:e.items[0]?.image_versions2?.candidates?.[0]?.url?{extension:"jpg",url:e.items[0].image_versions2.candidates[0].url}:(console.error("Error: No valid video or image URL found in item.items[0]"),null):"video_versions"in e&&e.video_versions?.[0]?.url?{extension:"mp4",url:e.video_versions[0].url}:e.image_versions2?.candidates?.[0]?.url?{extension:"jpg",url:e.image_versions2.candidates[0].url}:(console.error("Error: No valid video or image URL found"),null),k=(e,t,i)=>e===s.Video?``:``,E=async(e,t,i)=>t?window.location.pathname.startsWith("/stories/highlights/")?await f({type:"getReelsMediaFromFeed",articleNode:e,id:null}):window.location.pathname.startsWith("/stories/")?await f({type:"getReelsMediaFromFeed",articleNode:e,id:i}):await f({type:"getMediaFromInfo",articleNode:e}):await f({type:"getReelsMediaFromFeed",articleNode:e,id:i}),M=(e,t)=>{let i=0;e.reels_media?.[0]?.items?(i=e.reels_media[0].items.length,e.reels_media[0].items.forEach((e,a)=>{t(e,a,i)})):e.items?.[0]?.carousel_media?(i=e.items[0].carousel_media.length,e.items[0].carousel_media.forEach((e,a)=>{t(e,a,i)})):e.items?.[0]?(i=1,t(e.items[0],0,i)):e.user?.hd_profile_pic_url_info?.url&&(i=1,t(e.user.hd_profile_pic_url_info,0,i))},N=(e,t)=>{let i;if(!e)return 0;for(var a of["._acvz._acnc._acng","section header div",".x1ned7t2.x78zum5","section > div header > div","section > div > div > div > div > div > div > div > div","div > div > div > div > div > div > div > div"])if(i=e.querySelector(a))break;if(!i)return 0;let n=i?Array.from(i.children):[];for(let e=0;e1)return e}else{if(t[0]?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.children[1]?.querySelector("span"))return Array.from(t[0]?.parentNode?.parentNode?.children??[]).findIndex(e=>e.children.length>0),n.length,0;for(let i of Array.from(t)){let t=i.style.width,a=i.style.transform;if(t&&"100%"!==t||a&&""!==a.trim())return e}}}return 0},$=e=>e.carousel_media?s.Carousel:e.video_dash_manifest||e.video_duration||e.video_versions?s.Video:s.Image,_=()=>s.UNDEFINED,F=async(e,t)=>{try{let i=await fetch(e,{method:"GET",headers:{Accept:"*/*","X-IG-App-ID":t},credentials:"include",mode:"cors"});if(200!==i.status)return console.info(`Fetch API failed with status code: ${i.status}`),null;return await i.json()}catch(e){return console.info(`Error fetching data: ${e} +${e.stack}`),null}},C=e=>new Promise(t=>setTimeout(t,e)),I=(e,t)=>{for(let i in t){let a=RegExp(`{${i}}`,"g");e=e.replace(a,t[i])}return e.replace(/\s+/g,"-").replace(/[^\w-.]/g,"")},D=e=>`
    ${e}
    `,L=(e,t,i)=>t.startsWith("/p/")||t.startsWith("/stories/")?`${e}/${i}/`:t.startsWith("/reels/")?`${e}/${i}/reels/`:`${e}/${i}`,T=(e,t)=>{if(t){let t=(()=>{try{return e.children[0]?.children[0]?.children[0]?.children[0]?.children[0]?.children[1]?.children[0]?.children[0]?.children[1]?.children[1]?.children[0]?.textContent}catch{return}})();return!!t&&Object.values(l.langs).some(e=>e.ad===t)}return!!Array.from(e.querySelectorAll("path")).find(e=>"M21 17.502a.997.997 0 0 1-.707-.293L12 8.913l-8.293 8.296a1 1 0 1 1-1.414-1.414l9-9.004a1.03 1.03 0 0 1 1.414 0l9 9.004A1 1 0 0 1 21 17.502Z"===e.getAttribute("d"))},G=e=>{let t=e=>{if(!e)return null;let t=e[Object.keys(e).find(e=>e.includes("Instance")||e.includes("Fiber"))],i=t?.memoizedProps?.hidden===!0||t?.return?.memoizedProps?.hidden===!0;return"DIV"!==e.tagName||i?null:e},i=e.firstElementChild;for(;i;){let e=t(i);if(e)return e;i=i.nextElementSibling}return null},O=e=>{if(!e)return null;let t=e.querySelectorAll("div > div > div");return 0===t.length?null:Array.from(t).reduce((e,t)=>{let i=parseFloat(getComputedStyle(e).width);return parseFloat(getComputedStyle(t).width)>i?t:e},t[0])},R=e=>{let t=[...e?.querySelectorAll("section")||[]];return t[t.length-1]},q=e=>{let t=[];for(t.push(e);e.parentNode;)t.unshift(e.parentNode),e=e.parentNode;return t},P=e=>{let t=Object.keys(e).find(e=>e.includes("Instance")||e.includes("Fiber"));return t?e[t]:null},z=(e,t)=>"hd"===e.quality&&"hd"!==t.quality?-1:"hd"!==e.quality&&"hd"===t.quality?1:t.bandwidth-e.bandwidth,U=e=>({quality:e.getAttribute("FBQualityClass"),bandwidth:+e.getAttribute("bandwidth"),baseUrl:e.querySelector("BaseURL")?.textContent?.trim()||null}),B=e=>{let t=e.src&&!e.src.startsWith("blob:")?e.src:null;if(t)return t;let i=P(e)?.return?.return?.memoizedProps?.manifest;if(!i)return null;let a=new DOMParser().parseFromString(i,"text/xml");return Array.from(a.querySelectorAll('Representation[mimeType="video/mp4"]')).map(U).filter(e=>e.baseUrl).sort(z)[0]?.baseUrl||null},V=e=>{let{top:t,right:i,bottom:a,left:n}=e.getBoundingClientRect();return a>0&&i>0&&n{let t=e.parentElement;return"user-avatar"===e.getAttribute("data-testid")||e.width<48||["span","a"].includes(t?.localName)||q(e).some(e=>"HEADER"===e.nodeName)};class W{constructor(e){if(this.modal=null,this.imageURL=e.imageURL||"",this.heading=e.heading||[""],this.headingStyle=e.headingStyle||"",this.body=e.body||[""],this.bodyStyle=e.bodyStyle||"",this.buttonList=e.buttonList||[],this.callback=e.callback||null,null==document.getElementById(et.NAME+"-modal")){let e=document.createElement("style");e.id=et.NAME+"-modal",e.innerHTML=n,document.head.appendChild(e)}}get element(){return this.modal}createModal(){let e=document.createElement("div");e.classList.add(et.NAME+"-modal-overlay");let t=document.createElement("div");t.classList.add(et.NAME+"-modal"),e.appendChild(t);let i=document.createElement("div");i.classList.add(et.NAME+"-modal-content"),t.appendChild(i);let a=document.createElement("div");a.classList.add(et.NAME+"-modal-header"),this.headingStyle.length>0&&a.setAttribute("style",this.headingStyle),i.appendChild(a),this.heading.forEach(e=>{if("string"!=typeof e||/<\/?[a-z][\s\S]*>/i.test(e)){if(/<\/?[a-z][\s\S]*>/i.test(e)){let t;let i=document.createElement("div"),n=document.createDocumentFragment();for(i.innerHTML=e;null!==(t=i.firstChild);)n.appendChild(t);a.appendChild(n)}else a.appendChild(e)}else{let t=document.createElement("h5");t.innerHTML=e,a.appendChild(t)}});let n=document.createElement("div");if(n.classList.add(et.NAME+"-modal-body"),this.bodyStyle.length>0&&n.setAttribute("style",this.bodyStyle),i.appendChild(n),this.imageURL.length>0){let e=document.createElement("div");i.appendChild(e);let t=document.createElement("img");t.setAttribute("height","76px"),t.setAttribute("width","76px"),t.style.margin="auto",t.style.paddingBottom="20px",t.setAttribute("src",this.imageURL),e.appendChild(t)}if(this.body.forEach(e=>{if("string"!=typeof e||/<\/?[a-z][\s\S]*>/i.test(e)){if(/<\/?[a-z][\s\S]*>/i.test(e)){let t;let i=document.createElement("div"),a=document.createDocumentFragment();for(i.innerHTML=e;null!==(t=i.firstChild);)a.appendChild(t);n.appendChild(a)}else n.appendChild(e)}else{let t=document.createElement("div");t.innerText=e,n.appendChild(t)}}),this.buttonList.length>0){let e=document.createElement("div");e.classList.add(et.NAME+"-modal-footer"),i.appendChild(e),this.buttonList.forEach(t=>{let i=document.createElement("button");i.classList.add(et.NAME+"-modal-button"),i.innerText=t.text,t.active&&i.classList.add("active"),i.onclick=()=>{t&&t.callback&&t.callback(),this.close.bind(this)()},e.appendChild(i)})}else i.style.paddingBottom="4px;";return e}async open(){this.modal&&await this.close(),this.modal=this.createModal(),document.body.appendChild(this.modal),this.modal.classList.add(et.NAME+"-modal-visible"),setTimeout(()=>{this.modal.classList.add(et.NAME+"-modal-show")}),this.callback&&this.callback(this,this.modal)}async close(){this.modal&&(this.modal.classList.remove(et.NAME+"-modal-show"),await C(100),this.modal.classList.remove(et.NAME+"-modal-visible"),this.modal.parentNode.removeChild(this.modal),this.modal=null)}async refresh(){this.modal&&(this.modal.parentNode.removeChild(this.modal),this.modal=null),await this.open(),this.callback&&this.callback(this,this.modal.querySelector("."+et.NAME+"-modal-body"))}}class j{getName(){return"FeedScanner"}collectMediaElementsInfo(e){return Array.from(e).map((e,t)=>({i1:t,mediaEl:e,elemVisiblePercentage:S(e)||0}))}async execute(e){try{let t=document.getElementsByTagName("article");if(0===t.length)return{found:!1,errorMessage:"No target found."};let i=this.collectMediaElementsInfo(t).reduce((e,t)=>e.elemVisiblePercentage>t.elemVisiblePercentage?e:t),a=t[i.i1];if(!a||a.getBoundingClientRect().height<40)return{found:!1,errorMessage:"Article not found or too small, likely an ad"};return await b(a,e)}catch(t){return console.error(`[${e.NAME}] ${e.VERSION}`,this.getName()+"()",t),{found:!1,errorMessage:t.message,error:t}}}}class Y{getName(){return"PostAndReelScanner"}getArticleElement(){return document.querySelector("div[role='dialog'] article")||document.querySelector("section main > div > :first-child > :first-child")}async execute(e){try{let t=this.getArticleElement();if(!t)return{found:!1,errorMessage:"No target found."};return await b(t,e)}catch(t){return console.error(`[${e.NAME}] ${e.VERSION}`,this.getName()+"()",t),{found:!1,errorMessage:t.message,error:t}}}}class X{getName(){return"ProfileScanner"}async handleProfilePage(e){let t=w(window.location.href);if(!t)return{found:!1,errorMessage:"Invalid username extracted from URL"};try{let i=await f({type:"getUserInfoFromWebProfile",userName:t});if(!i.data.user.id)return{found:!1,errorMessage:"No userID found in userInfo"};let a=i.data.user.id,n=await f({type:"getUserFromInfo",userId:a});if(n&&n.user?.hd_profile_pic_url_info?.url)return await v(null,n,t,window.location.href,e);return{found:!1,errorMessage:"Incomplete userDetails received"}}catch(e){return{found:!1,userName:t,errorMessage:e.message,error:e}}}async execute(e){if(!e.regexProfilePath.test(window.location.pathname))return{found:!1,errorMessage:"Path does not match profile path regex, exiting."};try{let t=await this.handleProfilePage(e);if(!t)return{found:!1,errorMessage:"No result from handleProfilePage, returning null."};return t}catch(t){return console.error(`[${e.NAME}] ${e.VERSION}`,this.getName()+"()",t),{found:!1,errorMessage:t.message,error:t}}}}class Z{getName(){return"ReelsScanner"}getRelevantArticles(){return Array.from(document.querySelectorAll("section > main > div > div")).filter(e=>e.children.length>0)}findMostRelevantArticle(e){if(0===e.length)return null;let t=e.map((e,t)=>({index:t,article:e,visibility:S(e)})).reduce((e,t)=>e.visibility>t.visibility?e:t,{index:-1,visibility:0});return t.visibility>0?e[t.index]:null}async execute(e){try{let t=this.getRelevantArticles(),i=this.findMostRelevantArticle(t);if(!i)return{found:!1,errorMessage:"No target found."};return await b(i,e)}catch(t){return console.error(`[${e.NAME}] ${e.VERSION}`,this.getName()+"()",t),{found:!1,errorMessage:t.message,error:t}}}}class Q{getName(){return"StoriesScanner"}pausePlayCurrentStory(e){let t=Array.from(e.querySelectorAll("path")).find(e=>"M15 1c-3.3 0-6 1.3-6 3v40c0 1.7 2.7 3 6 3s6-1.3 6-3V4c0-1.7-2.7-3-6-3zm18 0c-3.3 0-6 1.3-6 3v40c0 1.7 2.7 3 6 3s6-1.3 6-3V4c0-1.7-2.7-3-6-3z"===e.getAttribute("d"));if(t){let e=t.closest('div[role="button"]');e?.click()}}shouldPauseStory(e){return"true"===localStorage.getItem(e.STORAGE_NAME+"_settings_stories_2")}findCurrentStory(e){let t=Array.from(e.querySelectorAll("section")),i=null;for(let e of t){let t=O(e);if(t){i=t;break}}return i}async handleHighlightsStories(e,t){let i=this.findCurrentStory(e);return i?(this.shouldPauseStory(t)&&this.pausePlayCurrentStory(i),b(i,t)):null}async handleFeedStories(e,t){let i=G(e.querySelector("div > div > div"));return(i=this.findCurrentStory(i))?(this.shouldPauseStory(t)&&this.pausePlayCurrentStory(i),b(i,t)):null}async execute(e){try{let t=document.querySelector('[id^="mount_"]');if(!t)return{found:!1,errorMessage:"No target found."};let i=window.location.pathname;if(i.startsWith("/stories/highlights/"))return await this.handleHighlightsStories(t,e);if(i.startsWith("/stories/"))return await this.handleFeedStories(t,e);return{found:!1,errorMessage:"No target found."}}catch(t){return console.error(`[${e.NAME}] ${e.VERSION}`,this.getName()+"()",t),{found:!1,errorMessage:t.message,error:t}}}}class J{getName(){return"MediaScanner"}constructor(){this.svgSettings=null;let e="http://www.w3.org/2000/svg";this.svgSettings=document.createElementNS(e,"svg"),this.svgSettings.setAttribute("style","margin-left: auto; margin-right:auto; display:block;"),this.svgSettings.setAttribute("aria-label","Optionen"),this.svgSettings.setAttribute("class","x1lliihq x1n2onr6"),this.svgSettings.setAttribute("color","rgb(255, 255, 255)"),this.svgSettings.setAttribute("fill","rgb(255, 255, 255)"),this.svgSettings.setAttribute("height","24"),this.svgSettings.setAttribute("role","img"),this.svgSettings.setAttribute("viewBox","0 0 24 24"),this.svgSettings.setAttribute("width","24");let t=document.createElementNS(e,"title");t.textContent="Optionen",this.svgSettings.appendChild(t);let i=document.createElementNS(e,"circle");i.setAttribute("cx","12"),i.setAttribute("cy","12"),i.setAttribute("fill","none"),i.setAttribute("r","8.635"),i.setAttribute("stroke","currentColor"),i.setAttribute("stroke-linecap","round"),i.setAttribute("stroke-linejoin","round"),i.setAttribute("stroke-width","2"),this.svgSettings.appendChild(i);let a=document.createElementNS(e,"path");a.setAttribute("d","M14.232 3.656a1.269 1.269 0 0 1-.796-.66L12.93 2h-1.86l-.505.996a1.269 1.269 0 0 1-.796.66m-.001 16.688a1.269 1.269 0 0 1 .796.66l.505.996h1.862l.505-.996a1.269 1.269 0 0 1 .796-.66M3.656 9.768a1.269 1.269 0 0 1-.66.796L2 11.07v1.862l.996.505a1.269 1.269 0 0 1 .66.796m16.688-.001a1.269 1.269 0 0 1 .66-.796L22 12.93v-1.86l-.996-.505a1.269 1.269 0 0 1-.66-.796M7.678 4.522a1.269 1.269 0 0 1-1.03.096l-1.06-.348L4.27 5.587l.348 1.062a1.269 1.269 0 0 1-.096 1.03m11.8 11.799a1.269 1.269 0 0 1 1.03-.096l1.06.348 1.318-1.317-.348-1.062a1.269 1.269 0 0 1 .096-1.03m-14.956.001a1.269 1.269 0 0 1 .096 1.03l-.348 1.06 1.317 1.318 1.062-.348a1.269 1.269 0 0 1 1.03.096m11.799-11.8a1.269 1.269 0 0 1-.096-1.03l.348-1.06-1.317-1.318-1.062.348a1.269 1.269 0 0 1-1.03-.096"),a.setAttribute("fill","none"),a.setAttribute("stroke","currentColor"),a.setAttribute("stroke-linejoin","round"),a.setAttribute("stroke-width","2"),this.svgSettings.appendChild(a)}initModalSettingsListeners(e,t){let i=document.querySelectorAll("div.nav-tabs > button.nav-link"),a=document.querySelectorAll(".tab-pane"),n=e=>{e.preventDefault(),i.forEach(e=>e.classList.remove("active")),a.forEach(e=>e.classList.remove("show","active"));let t=e.currentTarget;t.classList.add("active");let n=t.getAttribute("data-target");if(n){let e=document.querySelector(n);e&&e.classList.add("show","active")}};i.forEach(e=>{e.addEventListener("click",n)}),Array.from(e.querySelectorAll('label.slideon input[type="checkbox"]')).forEach(e=>{let i=`${t.STORAGE_NAME}_${e.id.replace(/-/g,"_")}`;e.checked="true"===localStorage.getItem(i),e.addEventListener("change",()=>{localStorage.setItem(i,String(e.checked))})});let r=e.querySelector("#settings-general-4");if(r){let i=`${t.STORAGE_NAME}_settings_general_4`;r.value=localStorage.getItem(i)||"{Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}";let a=e.querySelector("#settings-general-btn-4");a.addEventListener("click",e=>{e.preventDefault(),localStorage.setItem(i,r.value),this.updateInputButtonStyle(a,"saved",`${t.NAME}-primary`,`${t.NAME}-success`),setTimeout(()=>{this.updateInputButtonStyle(a,"save",`${t.NAME}-success`,`${t.NAME}-primary`)},1e3)})}}initializeStyles(e){this.removeStyleTagsWithIDs([e.NAME+"-cssGeneral",e.NAME+"-cssSlideOn",e.NAME+"-cssCarouselSlider"]),this.appendStyles(e.NAME+"-cssGeneral",a),this.appendStyles(e.NAME+"-cssSlideOn",i),this.appendStyles(e.NAME+"-cssCarouselSlider",".slider-container{width:500px;overflow:hidden;position:relative}.slider{height:100%;display:flex;transition:left .5s;position:inherit}.slide{position:relative;background:#000}.slide img, .slide video{width:501px;height:300px;object-fit:contain}.slider-controls{display:flex;justify-content:center;margin-top:10px;margin-bottom:10px;flex-wrap:wrap;gap:5px;}.slider-controls button{cursor: pointer;width:25px;height:25px;padding:0;margin:0 5px;border-radius:50%;background:#ddd;border:0}.slider-controls button.active{background:#999}")}appendStyles(e,t){let i=document.createElement("style");i.id=e,i.innerHTML=t,document.body.appendChild(i)}displayModal(e,t,i,a,n){new W({heading:[t],body:[e.modalBody],bodyStyle:i,buttonList:a,callback:n}).open()}handleSettingsButtonClick(e){let t=(e,t="",i={},a="")=>{let n=document.createElement(e);return t&&(n.className=t),Object.keys(i).forEach(e=>n.setAttribute(e,i[e])),a&&(n.innerHTML=a),n},i=(i,a,n,r)=>{let o=t("div","list-group-item"),s=t("div","row align-items-center"),l=t("div","col pr-0");l.appendChild(t("strong","mb-0",{},i)),a&&l.appendChild(t("p","text-muted mb-0",{},a));let d=t("div","col-auto"),m=t("label","slideon"),g=t("input","",{type:"checkbox",id:`settings-${n}`}),u=t("span","slideon-slider");if(m.appendChild(g),m.appendChild(u),d.appendChild(m),r){let r=t("div","form-group",{style:"display: flex; flex-direction: column; align-items: flex-start;"},`${i} +

    ${a}

    + + `);s.appendChild(r)}else s.appendChild(l),s.appendChild(d);return o.appendChild(s),o},a=t("div","container"),n=t("div","row justify-content-center"),o=t("div","col-12 col-lg-10 col-xl-8 mx-auto"),s=t("div","my-4"),l=t("nav"),d=t("div","nav nav-tabs",{id:"nav-tab",role:"tablist"});d.appendChild(t("button","nav-link active",{id:"nav-general-tab","data-toggle":"tab","data-target":"#nav-general",type:"button",role:"tab","aria-controls":"nav-general","aria-selected":"true"},`${c("modalSettingsGeneral")}`)),d.appendChild(t("button","nav-link",{id:"nav-stories-tab","data-toggle":"tab","data-target":"#nav-stories",type:"button",role:"tab","aria-controls":"nav-stories","aria-selected":"false"},"Stories"));let m=t("div","tab-content",{id:"nav-tabContent"}),g=t("div","tab-pane fade active show",{id:"nav-general",role:"tabpanel","aria-labelledby":"nav-general-tab"}),u=t("div","tab-pane fade",{id:"nav-stories",role:"tabpanel","aria-labelledby":"nav-stories-tab"});[{title:"modalSettingsGenTitle1",description:"modalSettingsGenDesc1",settingsName:"general-1"},{title:"modalSettingsGenTitle2",description:"modalSettingsGenDesc2",settingsName:"general-2"},{title:"modalSettingsGenTitle3",description:"modalSettingsGenDesc3",settingsName:"general-3"},{title:"modalSettingsGenTitle4",description:"modalSettingsGenDesc4",settingsName:"general-4",isLargeInput:!0},{title:"modalSettingsStoriesTitle1",description:"modalSettingsStoriesDesc1",settingsName:"stories-1"},{title:"modalSettingsStoriesTitle2",description:"modalSettingsStoriesDesc2",settingsName:"stories-2"},{title:"modalSettingsStoriesTitle3",description:"modalSettingsStoriesDesc3",settingsName:"stories-3"}].forEach(e=>{(e.title.includes("Gen")?g:u).appendChild(i(c(e.title),c(e.description),e.settingsName,e.isLargeInput))}),m.appendChild(g),m.appendChild(u),l.appendChild(d),s.appendChild(l),s.appendChild(m),s.appendChild(t("div","alert alert-warning mt-3",{},c("modalSettingsAttention"))),o.appendChild(s),n.appendChild(o),a.appendChild(n),new W({heading:[`
    + ${r} + ${c("modalSettingsTitle")} + v${e.VERSION} +
    `],body:[a],bodyStyle:null,buttonList:[{active:!0,text:c("close")}],callback:(t,i)=>{this.initModalSettingsListeners(i,e)}}).open()}async handleURLPatterns(e){if(!e.hostname.includes("instagram.com")){new W({heading:[`
    + ${r} + v${e.VERSION} +
    `],body:[c("alertWorksOnlyOn")],bodyStyle:"text-align:center;padding:20px",buttonList:[{active:!0,text:"Ok"}],callback:(t,i)=>{i.querySelector(`.${e.NAME}-settings`).addEventListener("click",()=>{this.handleSettingsButtonClick(e)})}}).open();return}for(let t of[{regex:e.regexStoriesURI,scanner:Q},{regex:e.regexProfilePath,scanner:X},{regex:e.regexRootPath,scanner:j},{regex:e.regexPostPath,scanner:Y},{regex:e.regexReelURI,scanner:Y},{regex:e.regexReelsURI,scanner:Z}])if(t.regex.test(window.location.pathname))try{let i=new t.scanner,a=await i.execute(e);a.found?(a.foundByModule=i.getName(),this.displayModal(a,`
    + ${r} + @${a.userName} + +
    `,"padding:0!important;text-align:center",[{active:!0,text:c("close")}],(t,i)=>{if(i.querySelector(".slider")){let t;let n=i.querySelector(".slider"),r=i.querySelectorAll(".slide"),o=i.querySelector(".slider-controls"),s=a.selectedSliderIndex;r.forEach((e,t)=>{let i=document.createElement("button");i.innerHTML=String(t+1),i.dataset.index=String(t),i.classList.toggle("active",1===r.length),r.length>1&&i.addEventListener("click",()=>{s=t,l(!0)}),o.appendChild(i)});let l=i=>{null!==document.fullscreenElement||(g(!1),n.style.transform=`translateX(${-r[0].clientWidth*s}px)`,[...o.children].forEach((e,t)=>{e.classList.toggle("active",t===s)}),i&&clearTimeout(t),"true"===localStorage.getItem(`${e.STORAGE_NAME}_settings_general_3`)&&d())},d=()=>{let e=r[s].querySelector("video");e?e.play()&&(e.onended=m):c()},m=()=>{s=(s+1)%r.length,l(!1)},c=()=>{t=setTimeout(m,5e3)},g=e=>{r.forEach(e=>{let t=e.querySelector("video");t&&t.pause()})};r.length>1&&l(!1);let u=()=>{null!==document.fullscreenElement&&clearTimeout(t)};document.addEventListener("fullscreenchange",u)}i.querySelector(`.${e.NAME}-settings`).addEventListener("click",()=>{this.handleSettingsButtonClick(e)})})):new W({heading:[`
    + ${r} + v${e.VERSION} +
    `],body:[c("alertNotFound")],bodyStyle:"text-align:center;padding:20px",buttonList:[{active:!0,text:"Ok"}],callback:(t,i)=>{i.querySelector(`.${e.NAME}-settings`).addEventListener("click",()=>{this.handleSettingsButtonClick(e)})}}).open()}catch(i){let e=new t.scanner;console.error(`Error executing scanner ${e.getName()}:`,i)}}isModalOpen(e){return!!document.querySelector("div."+e.NAME+"-modal-overlay."+e.NAME+"-modal-visible."+e.NAME+"-modal-show")}removeStyleTagsWithIDs(e){e.forEach(e=>{let t=document.getElementById(e);t&&t.remove()})}shakeModal(e){let t=document.querySelector("."+e);t&&(t.style.animation="horizontal-shaking 0.25s linear infinite",setTimeout(()=>t.style.animation=null,1e3))}updateInputButtonStyle(e,t,i,a){e.textContent=c(t),e.classList.contains(a)?(e.classList.remove(a),e.classList.add(i)):(e.classList.remove(i),e.classList.add(a))}async execute(e){try{if(this.isModalOpen(e)){this.shakeModal(`${e.NAME}-modal`);return}this.initializeStyles(e),await this.handleURLPatterns(e)}catch(t){console.error(`${this.getName()}()`,`[${e.NAME}] ${e.VERSION}`,t)}}}console.clear();let K="instantgram",ee=K.toLowerCase().replace(/-/g,"_"),et={NAME:K,STORAGE_NAME:ee,DEVELOPMENT:!1,VERSION:"2024.10.07",browser:(()=>{let e;let t=navigator.userAgent,i=t.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i)||[];if(/trident/i.test(i[1]))return{name:"IE",version:(e=/\brv[ :]+(\d+)/g.exec(t)||[])[1]||""};if("Chrome"===i[1]&&(e=t.match(/\b(OPR|Edge)\/(\d+)/)))return{name:e[1].replace("OPR","Opera"),version:e[2]};if(i.length>1){let e=t.match(/version\/(\d+)/i);return e&&(i[2]=e[1]),{name:i[1],version:i[2]}}return{name:navigator.appName,version:navigator.appVersion}})(),hostname:window.location.hostname,path:window.location.pathname,regexHostname:/^instagram\.com$/,regexRootPath:/^\/+$/,regexProfilePath:/^\/(\w[-\w.]+)\/?$/,regexPostPath:/^\/p\//,regexReelURI:/reel\/(.*)+/,regexReelsURI:/reels\/(.*)+/,regexStoriesURI:/\/stories\/(\w+)|\/highlights\/(\d+)\//,foundByModule:null,settings:{showAds:"true"===localStorage.getItem(`${ee}_settings_general_1`),openInNewTab:"true"===localStorage.getItem(`${ee}_settings_general_2`),autoSlideshow:"true"===localStorage.getItem(`${ee}_settings_general_3`),formattedFilenameInput:localStorage.getItem(`${ee}_settings_general_4`)||"{Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}",storiesMuted:"true"===localStorage.getItem(`${ee}_settings_stories_1`),noMultiStories:"true"===localStorage.getItem(`${ee}_settings_stories_3`)}};(async()=>{new J().execute(et)})(),e.program=et}({}); diff --git a/eslint.config.mjs b/eslint.config.mjs index 8399860..ded7ac7 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,16 +1,36 @@ import globals from "globals"; import pluginJs from "@eslint/js"; -import tseslint from "typescript-eslint"; +import tsPlugin from "@typescript-eslint/eslint-plugin"; +import tsParser from "@typescript-eslint/parser"; export default [ { + files: ["**/*.ts", "**/*.tsx"], // Apply this config to TypeScript files languageOptions: { globals: { ...globals.browser, ...globals.node }, + parser: tsParser, + parserOptions: { + ecmaVersion: 2020, + sourceType: "module", + project: "./tsconfig.json", + }, + }, + plugins: { + "@typescript-eslint": tsPlugin, }, rules: { - "no-useless-escape": "off", // Disabling the rule that's causing incorrect warnings - } + "no-useless-escape": "off", // Disabling the rule causing incorrect warnings + "@typescript-eslint/no-explicit-any": "off", // Disabling the no-explicit-any rule + }, + }, + { + files: ["**/*.js"], // Apply this config to JavaScript files + languageOptions: { + globals: { ...globals.browser, ...globals.node }, + }, + rules: { + // Additional rules for JavaScript files, if needed + }, }, - pluginJs.configs.recommended, - ...tseslint.configs.recommended, -]; \ No newline at end of file + pluginJs.configs.recommended, // Apply the recommended config for JavaScript +]; diff --git a/index.html b/index.html index 123b604..e343bcb 100644 --- a/index.html +++ b/index.html @@ -95,7 +95,7 @@

    Instantgram

    ☁️ A bookmarklet for downloading images from Instagram.

    - [instantgram 2024.06.11]
    + [instantgram 2024.10.07]
    drag this button to the bookmark bar of your browser. @@ -107,7 +107,7 @@

    [instantgram]

    - version + version badge

    [instantgram] is a bookmarklet for downloading single images from Instagram. Tiny, simple, without any further extensions or downloads. Just drag the [instantgram] button to the bookmark bar of your browser, open any Instagram post and click on the bookmarklet. Just works :-)

    diff --git a/lang/de-de/index.html b/lang/de-de/index.html index 97aad14..c172f39 100644 --- a/lang/de-de/index.html +++ b/lang/de-de/index.html @@ -95,7 +95,7 @@

    Instantgram

    ☁️ Ein Bookmarklet, um Bilder auf Instagram herunterzuladen

    - [instantgram 2024.06.11]
    + [instantgram 2024.10.07]
    Ziehe diesen Button in die Favoritenleiste deines Browsers. @@ -107,7 +107,7 @@

    [instantgram]

    - version + version badge

    [instantgram] ist ein Bookmarklet, um einzelne Instagram Bilder herunterzuladen. Klein, simpel, ohne Abhängigkeiten zu anderen Erweiterungen oder Downloads. Ziehe einfach den [instantgram] button in die Favoritenleiste deines Browsers, öffne einen einzelnen Instagram Post und klicke auf das Bookmarklet. So einfach :-)

    diff --git a/lang/en-us/index.html b/lang/en-us/index.html index 123b604..e343bcb 100644 --- a/lang/en-us/index.html +++ b/lang/en-us/index.html @@ -95,7 +95,7 @@

    Instantgram

    ☁️ A bookmarklet for downloading images from Instagram.

    - [instantgram 2024.06.11]
    + [instantgram 2024.10.07]
    drag this button to the bookmark bar of your browser. @@ -107,7 +107,7 @@

    [instantgram]

    - version + version badge

    [instantgram] is a bookmarklet for downloading single images from Instagram. Tiny, simple, without any further extensions or downloads. Just drag the [instantgram] button to the bookmark bar of your browser, open any Instagram post and click on the bookmarklet. Just works :-)

    diff --git a/lang/es-ar/index.html b/lang/es-ar/index.html index 826bb77..c5f1561 100644 --- a/lang/es-ar/index.html +++ b/lang/es-ar/index.html @@ -95,7 +95,7 @@

    Instantgram

    ☁️ Un bookmarklet para descargar imágenes de Instagram.

    - [instantgram 2024.06.11]
    + [instantgram 2024.10.07]
    Arrastre este botón a la barra de favoritos de su navegador. @@ -107,7 +107,7 @@

    [instantgram]

    - version + version badge

    [instantgram] es un bookmarklet para descargar imágenes individuales de Instagram. Minúsculo, sencillo, sin más extensiones ni descargas. Sólo tienes que arrastrar el botón [instantgram] a la barra de marcadores de tu navegador, abrir cualquier publicación de Instagram y hacer clic en el bookmarklet. Simplemente funciona :-)

    diff --git a/lang/pt-br/index.html b/lang/pt-br/index.html index 7bef299..0db76dc 100644 --- a/lang/pt-br/index.html +++ b/lang/pt-br/index.html @@ -95,7 +95,7 @@

    Instantgram

    ☁️ Um bookmarklet para download de fotos do Instagram

    - [instantgram 2024.06.11]
    + [instantgram 2024.10.07]
    arraste o botão acima para a barra de favoritos do navegador @@ -107,7 +107,7 @@

    [instantgram]

    - version + version badge

    [instantgram] é um bookmarklet com a função de baixar imagens do Instagram. Pequeno, simples, sem necessidade de extensões ou downloads. Só é necessário arrastar o link do [instantgram] para a barra de favoritos do seu navegador, ir até o instagram.com (versão web), abrir um post e clicar no bookmarklet. Simples e funcional.

    diff --git a/license.txt b/license.txt index 0aaa071..d8c390f 100644 --- a/license.txt +++ b/license.txt @@ -1,9 +1,9 @@ instantgram -https://github.com/theus/instantgram +https://github.com/ThinkBIG-Company/instantgram The MIT License (MIT) -Copyright (c) 2016 Matheus Falcão +Copyright (c) 2024 Sascha Heim Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/package.json b/package.json index 098b4e4..0eb5cbc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "instantgram", - "version": "2024.06.11", + "version": "2024.10.07", "description": "A bookmarklet for download photos in Instagram", "homepage": "https://thinkbig-company.github.io/instantgram/", "repository": { @@ -26,31 +26,34 @@ "start": "npm run dev" }, "devDependencies": { - "@eslint/js": "^9.2.0", + "@eslint/js": "^9.9.1", "@metalsmith/layouts": "^2.7.0", "@metalsmith/markdown": "^1.10.0", "@metalsmith/permalinks": "^3.0.1", - "@rollup/plugin-replace": "^5.0.5", + "@rollup/plugin-replace": "^5.0.7", "@rollup/plugin-typescript": "^11.1.6", - "@swc/core": "^1.5.7", - "@types/node": "^20.12.12", + "@swc/core": "^1.7.23", + "@types/node": "^22.5.2", "concurrently": "^8.2.2", - "cssnano": "^7.0.1", - "eslint": "^8.57.0", - "globals": "^15.2.0", + "cssnano": "^7.0.5", + "globals": "^15.9.0", "handlebars": "^4.7.8", "jstransformer-handlebars": "^1.2.0", "metalsmith": "^2.6.3", "metalsmith-define": "^2.1.3", "metalsmith-discover-partials": "^0.1.2", - "nodemon": "^3.1.0", - "rollup": "^4.17.2", + "nodemon": "^3.1.4", + "rollup": "^4.21.2", "rollup-plugin-analyzer": "^4.0.0", "rollup-plugin-postcss": "^4.0.2", + "rollup-plugin-sourcemaps": "^0.6.3", "rollup-plugin-swc3": "^0.11.2", "signale": "^1.4.0", - "typescript": "^5.4.5", - "typescript-eslint": "^7.8.0", - "uglify-js": "^3.17.4" + "typescript": "^5.5.4", + "typescript-eslint": "^8.4.0", + "uglify-js": "^3.19.3" + }, + "dependencies": { + "util": "^0.12.5" } } \ No newline at end of file diff --git a/rollup.config.js b/rollup.config.js index 7add46c..ad52e3d 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -5,36 +5,34 @@ const postcss = require('rollup-plugin-postcss'); const cssnano = require('cssnano'); const { swc } = require('rollup-plugin-swc3'); -// Import the package.json file -const pkg = require('./package.json'); - -const development = process.env.ROLLUP_WATCH +const development = process.env.ROLLUP_WATCH === 'true'; module.exports = { - input: 'src/index.ts', // Dein Eingabe-TypeScript-Datei + input: 'src/index.ts', output: { - file: 'dist/main.js', // Ausgabe-Datei - format: 'iife', // or 'umd', 'cjs', etc. - name: 'Instantgram', // Provide a global variable name for the IIFE bundle - sourcemap: false // Enable sourcemap generation + file: 'dist/main.js', + format: 'iife', + name: 'Instantgram', + sourcemap: false, }, plugins: [ replace({ - 'process.env.DEV': development ? true : false, - 'process.env.VERSION': JSON.stringify(pkg.version), - preventAssignment: true // Important to prevent errors with newer versions of the plugin + 'process.env.DEV': JSON.stringify(development), + 'process.env.VERSION': JSON.stringify(require('./package.json').version), + preventAssignment: true, + }), + typescript({ + tsconfig: './tsconfig.json', + sourceMap: false, }), - // Configure the TypeScript plugin to generate sourcemaps - typescript({ sourceMap: false }), - // Configure the SWC plugin to generate sourcemaps swc({ jsc: { parser: { syntax: 'typescript', - tsx: false + tsx: false, }, transform: {}, - target: 'esnext' + target: 'esnext', }, sourceMaps: false, minify: true @@ -42,8 +40,8 @@ module.exports = { postcss({ plugins: [ cssnano({ - preset: 'default', // Use default preset for minification - }) + preset: 'default', + }), ], minimize: true, // Minimize the CSS output inject: false, // Optional: if you want to extract the CSS to a separate file @@ -51,13 +49,8 @@ module.exports = { analyze({ summaryOnly: true }) ], onwarn: (warning, warn) => { - // Ignore circular dependency warnings if (warning.code === 'CIRCULAR_DEPENDENCY') return; - - // Ignore specific sourcemap warning if (warning.code === 'PLUGIN_WARNING' && warning.plugin === 'typescript' && /sourcemap/.test(warning.message)) return; - - // Use default for everything else warn(warning); - } -}; + }, +}; \ No newline at end of file diff --git a/src/App.ts b/src/App.ts index 0b866f5..f4fe18d 100644 --- a/src/App.ts +++ b/src/App.ts @@ -1,13 +1,11 @@ export type Program = { NAME: string STORAGE_NAME: string + DEVELOPMENT: boolean VERSION: string - browser: { name: string, version: string } - hostname: string path: string - regexHostname: RegExp regexRootPath: RegExp regexProfilePath: RegExp @@ -15,6 +13,13 @@ export type Program = { regexReelURI: RegExp regexReelsURI: RegExp regexStoriesURI: RegExp - foundByModule: string | null | undefined + settings: { + showAds: boolean, + openInNewTab: boolean, + autoSlideshow: boolean, + formattedFilenameInput: string, + storiesMuted: boolean, + noMultiStories: boolean + } } \ No newline at end of file diff --git a/src/_langs/layouts/partials/button.hbs b/src/_langs/layouts/partials/button.hbs index 3753682..713eaf9 100644 --- a/src/_langs/layouts/partials/button.hbs +++ b/src/_langs/layouts/partials/button.hbs @@ -1 +1 @@ -[instantgram 2024.06.11] \ No newline at end of file +[instantgram 2024.10.07] \ No newline at end of file diff --git a/src/components/Alert.ts b/src/components/Alert.ts deleted file mode 100644 index 59802a7..0000000 --- a/src/components/Alert.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { program } from ".." -import { sleep } from "../helpers/utils" - -export type AlertType = "default" | "warn" | "error" - -export abstract class Alert { - - private static wrapper: HTMLDivElement = Alert.addBaseToPage() - - public static add(text: string, type: AlertType = "default", timeout: number = 5000): void { - const element = document.getElementById(program.NAME + "-alert") - if (element == null) { - const style = document.createElement("style") - style.id = program.NAME + "-alert" - style.innerHTML = `.${program.NAME}-alert-wrapper{position:fixed;bottom:4rem;left:calc(50% - 225px);width:calc(80vw - 1rem);z-index:2000;max-width:550px}.${program.NAME}-alert{opacity:0;transition:opacity .2s;position:relative;margin-top:20px;padding:15px 29px 15px 15px;animation:fade-in .3s;border-radius:5px}.${program.NAME}-alert.fade-in{opacity:1}.${program.NAME}-alert.fade-out{opacity:0}.${program.NAME}-alert-close{position:absolute;content:url("data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" stroke="white" fill="white" width="18" height="18" viewBox="0 0 18 18"%3E%3Cpath stroke="white" d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z"/%3E%3C/svg%3E%0A");cursor:pointer;right:5px;top:5px}.${program.NAME}-alert-default{background-color:#0095f6;color:#fff}.${program.NAME}-alert-warn{background-color:#f69500;color:#fff}.${program.NAME}-alert-error{background-color:#f65a00;color:#fff}` - document.head.appendChild(style) - } - - const message = document.createElement("div") - message.classList.add(program.NAME + "-alert", program.NAME + "-alert-" + type) - - const closeIcon = document.createElement("i") - closeIcon.onclick = () => Alert.remove(message) - closeIcon.classList.add(program.NAME + "-alert-close") - message.appendChild(closeIcon) - - const textElement = document.createElement("div") - textElement.innerHTML = text - message.appendChild(textElement) - - Alert.wrapper.appendChild(message) - Alert.animateIn(message) - - sleep(timeout).then(() => Alert.remove(message)) - } - - private static animateIn(element: HTMLDivElement): void { - element.animate( - [{ opacity: "0" }, { opacity: "1" }], - { duration: 300, fill: "forwards" } - ) - } - - private static addBaseToPage(): HTMLDivElement { - document.querySelectorAll("." + program.NAME + "-alert-wrapper").forEach(e => e.remove()) - - const wrapper = document.createElement("div") - wrapper.classList.add(program.NAME + "-alert-wrapper") - document.body.appendChild(wrapper) - - return wrapper - } - - private static remove(element: HTMLDivElement): void { - const animation = element.animate( - [{ opacity: "1" }, { opacity: "0" }], - { duration: 300, fill: "forwards" } - ) - animation.finished.then(() => { - element.remove() - this.wrapper.remove() - }) - } -} \ No newline at end of file diff --git a/src/components/Interconnect.ts b/src/components/Interconnect.ts index bd0bfea..0ec7f2c 100644 --- a/src/components/Interconnect.ts +++ b/src/components/Interconnect.ts @@ -1,4 +1,6 @@ -export const cssGeneral = `.btn,label{display:inline-block}.btn,.form-control{padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5}.alert,p{margin-bottom:1rem}@keyframes horizontal-shaking{0%,100%{transform:translateX(0)}25%,75%{transform:translateX(5px)}50%{transform:translateX(-5px)}}.btn{color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control,.form-group{display:block;width:100%}.btn:hover{color:#212529;text-decoration:none}.btn:not(:disabled):not(.disabled){cursor:pointer}.btn-primary{color:#fff!important;background-color:#007bff!important;border-color:#007bff!important}.btn-primary:hover{color:#fff!important;background-color:#0069d9!important;border-color:#0062cc!important}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-success{color:#fff!important;background-color:#28a745!important;border-color:#28a745!important}.mt-1,.my-1{margin-top:.25rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mt-3,.my-3{margin-top:1rem!important}.w94{width:94%!important}.ml-15,.mr-15{margin-left:15px!important}label{margin-bottom:.5rem}.form-control{height:calc(1.5em + .75rem + 2px);color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.alert{position:relative;padding:.75rem 1.25rem;border:1px solid transparent;border-radius:.25rem}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.pr-0{padding-right:0!important}.col,.col-12,.col-auto{max-width:100%}.instantgram-modal-body *{box-sizing:border-box}b,strong{font-weight:bolder}button,input{overflow:visible}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.col-12{-ms-flex:0 0 100%;flex:0 0 100%}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid #eef0f3}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-item.active{z-index:2;color:#fff;background-color:#1b68ff;border-color:#1b68ff}.list-group-item.disabled,.list-group-item:disabled{color:#6d7174;pointer-events:none;background-color:#fff}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.mb-0,.my-0{margin-bottom:0!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-auto,.mx-auto{margin-left:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mt-4,.my-4{margin-top:1.5rem!important}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.text-muted{color:#6c757d!important}p{margin-top:0}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.tab-content>.tab-pane{display:none}.fade:not(.show){opacity:0}.fade{transition:opacity .15s linear}.tab-content>.active{display:block}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-tabs{border-bottom:1px solid #dee2e6;margin-bottom:-1px}.nav-tabs .nav-link{cursor:pointer;color:#000;margin-bottom:-1px;background-color:transparent;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:#e9ecef #e9ecef #dee2e6}.nav-link{display:block;padding:.5rem 1rem}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}` +export const cssLinearGradient = `background:#f6e2d8!important;background:linear-gradient(45deg,#f6e2d8 0%,#68c2e8 100%)!important;` export const cssCarouselSlider = `.slider-container{width:500px;overflow:hidden;position:relative}.slider{height:100%;display:flex;transition:left .5s;position:inherit}.slide{position:relative;background:#000}.slide img, .slide video{width:501px;height:300px;object-fit:contain}.slider-controls{display:flex;justify-content:center;margin-top:10px;margin-bottom:10px;flex-wrap:wrap;gap:5px;}.slider-controls button{cursor: pointer;width:25px;height:25px;padding:0;margin:0 5px;border-radius:50%;background:#ddd;border:0}.slider-controls button.active{background:#999}` -export const cssSlideOn = `.slideon{position:relative;display:inline-block;width:42px;height:24px;vertical-align:middle}.slideon>input,input.slideon{display:none}.slideon-slider{position:absolute;cursor:pointer;border-radius:34px;top:0;left:0;right:0;bottom:0;background-color:#ccc;-webkit-transition:.4s;transition:.4s}.slideon-slider:before{position:absolute;content:"";height:22px;width:22px;left:1px;bottom:1px;border-radius:50%;background-color:#fff;-webkit-transition:.4s;transition:.4s;-webkit-box-shadow:0 0 3px 0 rgba(0,0,0,.45);-moz-box-shadow:0 0 3px 0 rgba(0,0,0,.45);box-shadow:0 0 3px 0 rgb(0,0,0,.45)}.slideon input:checked~.slideon-slider{background-color:#007bff}.slideon input:checked~.slideon-slider:before{-webkit-transform:translateX(18px);-ms-transform:translateX(18px);transform:translateX(18px)}.slideon input:disabled~.slideon-slider{opacity:.5}` -export const cssModal = `.instantgram-modal-footer button,.instantgram-modal-header h5{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important;font-size:16px!important}.instantgram-modal-overlay{display:none!important;opacity:0!important;transition:.1s!important;position:fixed!important;top:0!important;left:0!important;right:0!important;bottom:0!important;z-index:1000!important;background:rgba(0,0,0,.65)!important;justify-content:center!important;align-items:center!important}.instantgram-modal{transition:width .1s ease-in-out!important;display:inline-block!important;width:400px!important;padding:1.6px!important;z-index:1001!important}.instantgram-modal select{margin-left:.8px!important;border:1px solid #dbdbdb!important;border-radius:3px!important;color:#262626!important;outline:0!important;padding:3px!important;text-align:center!important}@media (min-width:736px){.instantgram-modal{width:500px!important}}.instantgram-modal-content{position:relative;display:flex;flex-direction:column;width:100%!important;pointer-events:auto!important;background-clip:padding-box!important;outline:0!important}.instantgram-modal-header{color:#fff!important;background-color:#fd1d1d!important;background-image:linear-gradient(45deg,#405de6,#5851db,#833ab4,#c13584,#e1306c,#fd1d1d)!important;border-top-left-radius:12px!important;border-top-right-radius:12px!important;padding:0 16px!important;text-align:center}.instantgram-modal-header h5:nth-child(2){margin-top:-15px!important;margin-bottom:20px!important}.instantgram-modal-body{background:#fff!important;color:#000!important;position:relative!important;-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.instantgram-modal-body>img{background:#000;object-fit:scale-down}.instantgram-modal-footer{background-color:#fff!important;border-top:1px solid #dbdbdb!important;border-left:0!important;border-right:0!important;border-bottom-left-radius:12px!important;border-bottom-right-radius:12px!important;line-height:1.5!important;min-height:48px!important;padding:4px 8px!important;user-select:none!important;display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important;-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:center!important}.instantgram-modal-footer button{width:100%!important;min-height:39px!important;background-color:transparent!important;border:0!important;outline:0!important;cursor:pointer!important}.instantgram-modal-footer button.active{color:#0095e2!important}.instantgram-modal-show{opacity:1!important}.instantgram-modal-visible{display:flex!important}#instantgram-bulk-download-indicator{text-align:center!important}.instantgram-modal-db{color:#fff!important;background:linear-gradient(45deg,#405de6,#5851db,#833ab4,#c13584,#e1306c,#fd1d1d)!important;display:block;padding:.8rem;width:100%;border:none;cursor:pointer}.instantgram-modal-db:focus{outline:0;background:linear-gradient(45deg,rgba(64,93,230,.5),rgba(88,81,219,.5),rgba(131,58,180,.5),rgba(193,53,132,.5),rgba(225,48,108,.5),rgba(253,29,29,.5))!important}.instantgram-modal-header h5{color:#fff!important;margin:revert!important;display:flex;justify-content:center;align-items:center}.header-text-left{margin-right:auto}.header-text-right{margin-left:auto}.header-text-middle{margin:0}.header-text-middle a{color:#fff!important}.header-text-right:last-child{margin-right:30px}.instantgram-settings{cursor:pointer;display:inline-block;color:rgba(255,255,255,.7);background-color:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.2);border-radius:.3rem;transition:color .2s,background-color .2s,border-color .2s;width:40px;height:40px;top:16px;right:16px;position:absolute}.instantgram-settings:hover{color:rgba(255,255,255,.8);text-decoration:none;background-color:rgba(255,255,255,.2);border-color:rgba(255,255,255,.3)}.instantgram-modal-body input{color:#000!important}` \ No newline at end of file +export const cssSlideOn = `.slideon{position:relative;display:inline-block;width:42px;height:24px;vertical-align:middle}.slideon>input,input.slideon{display:none}.slideon-slider{position:absolute;cursor:pointer;border-radius:34px;top:0;left:0;right:0;bottom:0;background-color:#ccc;-webkit-transition:.4s;transition:.4s}.slideon-slider:before{position:absolute;content:"";height:22px;width:22px;left:1px;bottom:1px;border-radius:50%;background-color:#fff;-webkit-transition:.4s;transition:.4s;-webkit-box-shadow:0 0 3px 0 rgba(0,0,0,.45);-moz-box-shadow:0 0 3px 0 rgba(0,0,0,.45);box-shadow:0 0 3px 0 rgb(0,0,0,.45)}.slideon input:checked~.slideon-slider{${cssLinearGradient}}.slideon input:checked~.slideon-slider:before{-webkit-transform:translateX(18px);-ms-transform:translateX(18px);transform:translateX(18px)}.slideon input:disabled~.slideon-slider{opacity:.5}` +export const cssGeneral = `.instantgram-btn,label{display:inline-block}.instantgram-btn{padding:.25rem 1.9rem .4rem 1.9rem;font-size:1rem;font-weight:400;line-height:1.5}.alert,p{margin-bottom:1rem}@keyframes horizontal-shaking{0%,100%{transform:translateX(0)}25%,75%{transform:translateX(5px)}50%{transform:translateX(-5px)}}.instantgram-btn{color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control,.form-group{display:block;width:100%}.instantgram-btn:hover{color:#212529;text-decoration:none}.instantgram-btn:not(:disabled):not(.disabled){cursor:pointer}.instantgram-btn-primary{color:#fff!important;${cssLinearGradient}border:4px!important}.instantgram-btn-primary:hover{color:#fff!important;background-color:#0069d9!important;border-color:#0062cc!important}.instantgram-btn-primary:not(:disabled):not(.disabled).active,.instantgram-btn-primary:not(:disabled):not(.disabled):active,.show>.instantgram-btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.instantgram-btn-primary:not(:disabled):not(.disabled).active:focus,.instantgram-btn-primary:not(:disabled):not(.disabled):active:focus,.show>.instantgram-btn-primary.dropdown-toggle:focus{box-shadow: 0 0 0 .2rem rgba(246, 226, 216, 0.5)}.instantgram-btn-success{color:#fff!important;background-color:#28a745!important;border-color:#28a745!important}.mt-1,.my-1{margin-top:.25rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mt-3,.my-3{margin-top:1rem!important}.w94{width:94%!important}.ml-15,.mr-15{margin-left:15px!important}label{margin-bottom:.5rem}.form-control{padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;height:calc(1.5em + .75rem + 2px);color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.alert{position:relative;padding:.75rem 1.25rem;border:1px solid transparent;border-radius:.25rem}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.pr-0{padding-right:0!important}.col,.col-12,.col-auto{max-width:100%}.instantgram-modal-body *{box-sizing:border-box}b,strong{font-weight:bolder}button,input{overflow:visible}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;min-width:0}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto}.col-12{-ms-flex:0 0 100%;flex:0 0 100%}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid #eef0f3}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-item.active{z-index:2;color:#fff;background-color:#1b68ff;border-color:#1b68ff}.list-group-item.disabled,.list-group-item:disabled{color:#6d7174;pointer-events:none;background-color:#fff}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.mb-0,.my-0{margin-bottom:0!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-auto,.mx-auto{margin-left:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mt-4,.my-4{margin-top:1.5rem!important}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.text-muted{color:#6c757d!important}p{margin-top:0}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.tab-content>.tab-pane{display:none}.fade:not(.show){opacity:0}.fade{transition:opacity .15s linear}.tab-content>.active{display:block}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-tabs{border-bottom:1px solid #dee2e6;margin-bottom:-1px}.nav-tabs .nav-link{cursor:pointer;color:#000;margin-bottom:-1px;background-color:transparent;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:#e9ecef #e9ecef #dee2e6}.nav-link{display:block;padding:.5rem 1rem}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff;border-bottom: aliceblue 6px solid;}` +export const cssModal = `.instantgram-modal-footer button,.instantgram-modal-header h5{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important;font-size:16px!important}.instantgram-modal-overlay{display:none!important;opacity:0!important;transition:.1s!important;position:fixed!important;top:0!important;left:0!important;right:0!important;bottom:0!important;z-index:1000!important;background:rgba(0,0,0,.65)!important;justify-content:center!important;align-items:center!important}.instantgram-modal{transition:width .1s ease-in-out!important;display:inline-block!important;width:400px!important;padding:1.6px!important;z-index:1001!important}.instantgram-modal select{margin-left:.8px!important;border:1px solid #dbdbdb!important;border-radius:3px!important;color:#262626!important;outline:0!important;padding:3px!important;text-align:center!important}@media (min-width:736px){.instantgram-modal{width:500px!important}}.instantgram-modal-content{position:relative;display:flex;flex-direction:column;width:100%!important;pointer-events:auto!important;background-clip:padding-box!important;outline:0!important}.instantgram-modal-header{${cssLinearGradient}}.instantgram-modal-header{color:#fff!important;border-top-left-radius:12px!important;border-top-right-radius:12px!important;padding:0 16px!important;text-align:center}.instantgram-modal-header h5:nth-child(2){margin-top:-15px!important;margin-bottom:20px!important}.instantgram-modal-body{background:#fff!important;color:#000!important;position:relative!important;-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.instantgram-modal-body>img{background:#000;object-fit:scale-down}.instantgram-modal-footer{background-color:#fff!important;border-top:1px solid #dbdbdb!important;border-left:0!important;border-right:0!important;border-bottom-left-radius:12px!important;border-bottom-right-radius:12px!important;line-height:1.5!important;min-height:48px!important;padding:4px 8px!important;user-select:none!important;display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important;-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:center!important}.instantgram-modal-footer button{width:100%!important;min-height:39px!important;background-color:transparent!important;border:0!important;outline:0!important;cursor:pointer!important}.instantgram-modal-footer button.active{color:#0095e2!important}.instantgram-modal-show{opacity:1!important}.instantgram-modal-visible{display:flex!important}#instantgram-bulk-download-indicator{text-align:center!important}.instantgram-modal-db{color:#fff!important;${cssLinearGradient}display:block;padding:.8rem;width:100%;border:none;cursor:pointer}.instantgram-modal-db:focus{outline:0;${cssLinearGradient}}.instantgram-modal-header h5{color:#fff!important;margin:revert!important;display:flex;justify-content:space-between;align-items:center;height:15px;}.header-text-left{flex:1;text-align:left;}.header-text-right{flex:1;text-align:right;}.header-text-middle{flex:1;text-align:center;}.header-text-middle a{color:#fff!important}.instantgram-settings{cursor:pointer;display:inline-block;color:rgba(255,255,255,.7);background-color:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.2);border-radius:.3rem;transition:color .2s,background-color .2s,border-color .2s;width:40px;height:40px;vertical-align:middle;}.instantgram-settings:hover{color:rgba(255,255,255,.8);text-decoration:none;background-color:rgba(255,255,255,.2);border-color:rgba(255,255,255,.3)}.instantgram-modal-body input{color:#000!important}` +export const logo = `[instantgram]` \ No newline at end of file diff --git a/src/components/Modal.ts b/src/components/Modal.ts index f219b22..d27f3a2 100644 --- a/src/components/Modal.ts +++ b/src/components/Modal.ts @@ -1,3 +1,4 @@ +/* eslint-disable */ import { program } from ".." import { cssModal } from "./Interconnect" import { sleep } from "../helpers/utils" @@ -19,7 +20,10 @@ export interface ModalOptions { body?: (HTMLElement | string)[] bodyStyle?: string buttonList?: ModalButton[] - callback?(arg0: Modal, arg1: HTMLElement): void + callback?( + arg0: Modal, + arg1: HTMLElement + ): void } export class Modal { @@ -117,7 +121,7 @@ export class Modal { this.body.forEach(content => { if (typeof content === "string" && !/<\/?[a-z][\s\S]*>/i.test(content)) { - const modalText = document.createElement("p") + const modalText = document.createElement("div") modalText.innerText = content modalBody.appendChild(modalText) } else { diff --git a/src/helpers/localize.ts b/src/helpers/localize.ts index a68a088..26fc2e2 100644 --- a/src/helpers/localize.ts +++ b/src/helpers/localize.ts @@ -43,6 +43,6 @@ function localize(str: string, lang: string = LANG_DEFAULT): string { } } -console.info(localize("helpers.localize_defaultlang").replace("${LANG_DEFAULT}", LANG_DEFAULT)) +console.info(localize("helpers.localizeDefaultLang").replace("${LANG_DEFAULT}", LANG_DEFAULT)) export default localize \ No newline at end of file diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index 32c8101..552e453 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -1,44 +1,36 @@ import { Program } from "../App" import { MediaScanResult } from "../model/MediaScanResult" import { MediaType } from "../model/MediaType" +import localization from "../localization" import localize from "./localize" const mediaIdCache: Map = new Map() -export function findAppId() { - const appIdPattern = /"X-IG-App-ID":"([\d]+)"/ - const bodyScripts: NodeListOf = document.querySelectorAll("body > script") - for (let i = 0; i < bodyScripts.length; ++i) { - const match = bodyScripts[i].text.match(appIdPattern) - if (match) return match[1] - } - return null +export const findAppId = (): string | null => { + const appIdPattern = /"X-IG-App-ID":"([\d]+)"/; + const scripts = Array.from(document.querySelectorAll("body > script")) as HTMLScriptElement[] + + const script = scripts + .map(s => s.textContent?.match(appIdPattern)) + .find(Boolean) + + return script ? script[1] : null } -export function findPostId(articleNode: HTMLElement) { +export const findPostId = (articleNode: HTMLElement) => { const pathname = window.location.pathname const segments = pathname.split('/') - // Handling known path prefixes with a mapping object const prefixHandlers = { '/reel/': () => segments[2], '/reels/': () => segments[2], - '/stories/': () => segments[3] + '/stories/': () => segments[3], } - // Check if the current pathname starts with any known prefix for (const prefix in prefixHandlers) { - if (pathname.startsWith(prefix)) { - return prefixHandlers[prefix]() - } + if (pathname.startsWith(prefix)) return prefixHandlers[prefix](); } - // Default case for non-prefix URLs const postIdPattern = /^\/p\/([^/]+)\// - const aNodes: NodeListOf = articleNode.querySelectorAll("a[href]") - const aNodeArray = Array.from(aNodes) - for (const aNode of aNodeArray) { - const link = aNode.getAttribute("href") - const match = link.match(postIdPattern) - if (match) return match[1] - } - return null + return Array.from(articleNode.querySelectorAll("a[href]")) + .map(a => a.getAttribute("href").match(postIdPattern)) + .find(match => match)?.[1] || null } export async function findMediaId(postId: string) { const match = window.location.href.match(/www.instagram.com\/stories\/[^/]+\/(\d+)/) @@ -71,317 +63,347 @@ export async function findMediaId(postId: string) { } return mediaIdCache.get(postId) } -export async function fetchDataFromApi(config) { +export const fetchDataFromApi = async (config) => { const { type, articleNode, id, userName, userId } = config const appId = findAppId() if (!appId) { - console.log(`${type}() Cannot find appId`) + console.log('AppID not found') return null } - let url - let mediaId - switch (type) { - case 'getReelsMediaFromFeed': - mediaId = id || await findMediaId(findPostId(articleNode)) - if (!mediaId) { - console.log(`${type}() Cannot find media id`) - return null - } - url = `https://i.instagram.com/api/v1/feed/reels_media/?reel_ids=${id ? '' : 'highlight%3A'}${mediaId}` - break - case 'getMediaFromInfo': - mediaId = await findMediaId(findPostId(articleNode)) - if (!mediaId) { - console.log(`${type}() Cannot find media id`) - return null - } - url = `https://i.instagram.com/api/v1/media/${mediaId}/info/` - break - case 'getUserFromInfo': - url = `https://i.instagram.com/api/v1/users/${userId}/info/` - break - case 'getUserInfoFromWebProfile': - url = `https://i.instagram.com/api/v1/users/web_profile_info/?username=${userName}` - break - default: - console.log('Unsupported type of API request') - return null + + const urlMap = { + 'getReelsMediaFromFeed': async () => { + const mediaId = id || await findMediaId(findPostId(articleNode)) + if (!mediaId) return null + return `https://i.instagram.com/api/v1/feed/reels_media/?reel_ids=${id ? '' : 'highlight%3A'}${mediaId}` + }, + 'getMediaFromInfo': async () => { + const mediaId = await findMediaId(findPostId(articleNode)) + if (!mediaId) return null + return `https://i.instagram.com/api/v1/media/${mediaId}/info/` + }, + 'getUserFromInfo': () => `https://i.instagram.com/api/v1/users/${userId}/info/`, + 'getUserInfoFromWebProfile': () => `https://i.instagram.com/api/v1/users/web_profile_info/?username=${userName}`, } + const url = await urlMap[type]?.() + if (!url) return null return secureFetch(url, appId) } -export async function generateModalBody(el: HTMLElement, program: Program) { - const postId = findPostId(el) +export const generateModalBody = async (el: HTMLElement, program: Program) => { + const isPathMatch = (path: string) => window.location.pathname.startsWith(path) let userName = getIGUsername(window.location.href) - let userLink = null - const userId = window.location.pathname.startsWith("/stories/") ? (await fetchDataFromApi({ type: 'getUserInfoFromWebProfile', userName: userName })).data.user.id : null - /* eslint-disable @typescript-eslint/no-explicit-any */ - const mediaInfo: any | null = await getMediaInfo(el, postId, userId) - if (!mediaInfo) return null - const isPathMatch = path => window.location.pathname.startsWith(path) + const postId = findPostId(el) + const userId = isPathMatch("/stories/") + ? (await fetchDataFromApi({ type: 'getUserInfoFromWebProfile', userName }))?.data?.user?.id ?? null + : null + let modalBody = "" + const mediaInfo = await getMediaInfo(el, postId, userId) if (userName === postId && (isPathMatch("/p/") || isPathMatch("/reels/"))) { - if (mediaInfo.items && mediaInfo.items[0] && mediaInfo.items[0].user) { - userName = mediaInfo.items[0].user.username - userLink = resolveUserLink('https://www.instagram.com', window.location.pathname, userName) + userName = mediaInfo.items?.[0]?.user?.username + } else { + const userFromReels = mediaInfo.reels_media?.[0]?.user?.username + const userFromItems = mediaInfo.items?.[0]?.user?.username + userName = userFromReels || userFromItems || userName + } + const userLink = resolveUserLink('https://www.instagram.com', window.location.pathname, userName) + if (program.settings.showAds && findAD(el, isPathMatch("/stories/"))) { + const targetNode = el.querySelector("video[playsinline]") || el.querySelector('img[draggable]') + if (!targetNode) return { found: false } + const body = document.body + const videos = body.querySelectorAll("video") || [] + const storyWrapper = getStoryWrapper(body) + const images = storyWrapper.querySelectorAll('img[draggable="false"]') || [] + const filteredImage = Array.from(images).find(img => isElementInViewport(img) && !isProfileImage(img)) as HTMLImageElement | undefined + let mediaUrl = "" + let mediaType = null + if (videos.length) { + const section = getAllNodeParent(videos[0]).reverse().find(el => el.nodeName === "SECTION") + const instance = getReactInstanceFromElement(section) + mediaUrl = instance?.return?.memoizedProps?.post?.videoUrl || getOriginalVideo(videos[0]) + mediaType = MediaType.Video + } else if (filteredImage?.src && !filteredImage.src.startsWith("data:")) { + mediaUrl = filteredImage.src + mediaType = MediaType.Image } + if (mediaUrl) { + const { formattedFilename, url } = getFormattedFilenameAndUrl(mediaUrl, userName, program.settings.formattedFilenameInput, 0) + const mediaElement = getMediaElement(mediaType, url, program.settings.storiesMuted) + const encodedUrl = `https://instantgram.1337.pictures/download.php?data=${btoa(url)}:${btoa(formattedFilename)}` + const downloadUrl = program.settings.openInNewTab ? url : encodedUrl + modalBody += ` +
    + ${mediaElement} + ${localize("download")} + +
    ` + return { + found: true, + mediaType: resolveOverallMediaType(), + mediaInfo, + modalBody: wrapInSliderContainer(modalBody), + selectedSliderIndex: 0, + userName, + userLink, + } + } + return { found: false } + } + if (program.settings.noMultiStories && mediaInfo.reels_media?.[0]?.items.length > 0) { + const sIndex = resolveCurrentStoryIndex(el, 'noMultiStories') + modalBody = addMediaToBody(modalBody, mediaInfo.reels_media[0].items[sIndex], sIndex, userName, program) } else { - // Check if reels_media and its elements are defined - const userFromReels = mediaInfo.reels_media && mediaInfo.reels_media[0] && mediaInfo.reels_media[0].user && isPathMatch("/stories/") - // Check if items and its elements are defined - const userFromItems = mediaInfo.items && mediaInfo.items[0] && mediaInfo.items[0].user + processMediaInfo(mediaInfo, (media, index, _count) => { + modalBody = addMediaToBody(modalBody, media, index, userName, program) + }) + } - if (userFromReels || userFromItems) { - userName = userFromReels ? mediaInfo.reels_media[0].user.username : mediaInfo.items[0].user.username - userLink = resolveUserLink('https://www.instagram.com', window.location.pathname, userName) - } + const sliderHtml = wrapInSliderContainer(modalBody) + const selectedSliderIndex = resolveCurrentStoryIndex(el, 'selectedSliderIndex') + return { + found: true, + mediaType: resolveOverallMediaType(), + mediaInfo, + modalBody: sliderHtml, + selectedSliderIndex, + userName, + userLink, } - // if (mediaInfo && mediaInfo.user && mediaInfo.user.username) { - // userName = mediaInfo.user.username - // } else { - // const mediaInfoText = JSON.stringify(mediaInfo) - // const usernameMatch = mediaInfoText.match(/"username":"(.*?)"/) - // userName = usernameMatch ? usernameMatch[1] : null - // } - return await generateModalBodyHelper(el, mediaInfo, userName, userLink, program) } export async function generateModalBodyHelper(el: HTMLElement, mediaInfo, userName: string, userLink: string, program: Program): Promise { let modalBody = "" - const settings = { - showAds: localStorage.getItem(`${program.STORAGE_NAME}_settings_general_1`) === "true", - openInNewTab: localStorage.getItem(`${program.STORAGE_NAME}_settings_general_2`) === "true", - autoSlideshow: localStorage.getItem(`${program.STORAGE_NAME}_settings_general_3`) === "true", - formattedFilenameInput: localStorage.getItem(`${program.STORAGE_NAME}_settings_general_4`) || "{Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}", - mutedStories: localStorage.getItem(`${program.STORAGE_NAME}_settings_stories_1`) === "true", - noMultiStories: localStorage.getItem(`${program.STORAGE_NAME}_settings_stories_3`) === "true" - } - - const addMediaToBody = (media: { width: any; height: any; url: string }, index: number) => { - let URL: string - let FORMATTED_FILENAME: string - if (media && media.width && media.height && media.url) { - URL = media.url - FORMATTED_FILENAME = "profile_pic.jpg" - } else { - const { formattedFilename, url } = getFormattedFilenameAndUrl(media, userName, settings.formattedFilenameInput, index) - URL = url - FORMATTED_FILENAME = formattedFilename - } - - const mediaType = resolveElementMediaType(media) - const mediaElement = getMediaElement(mediaType, URL, settings.mutedStories) - - const encodedUrl = `https://instantgram.1337.pictures/download.php?data=${btoa(URL)}:${btoa(FORMATTED_FILENAME)}` - const downloadUrl = settings.openInNewTab ? URL : encodedUrl - - modalBody += `` - } - let itemCount = 0 - if (settings.noMultiStories && mediaInfo.reels_media && mediaInfo.reels_media.length > 0 && mediaInfo.reels_media[0].items.length > 0) { - itemCount = resolveCurrentSliderIndex(el) - addMediaToBody(mediaInfo.reels_media[0].items[itemCount], itemCount) + if (program.settings.noMultiStories && mediaInfo.reels_media?.[0]?.items.length > 0) { + const itemIndex = resolveCurrentStoryIndex(el, 'noMultiStories') + modalBody = addMediaToBody(modalBody, mediaInfo.reels_media[0].items[itemIndex], itemIndex, userName, program) + itemCount = 1 // Da nur ein Element verarbeitet wird } else { - itemCount = processMediaInfo(mediaInfo, addMediaToBody) + processMediaInfo(mediaInfo, (media, index, _count) => { + modalBody = addMediaToBody(modalBody, media, index, userName, program) + }) } - const sliderHtml = wrapInSliderContainer(modalBody) - const selectedSliderIndex = itemCount > 0 ? resolveCurrentSliderIndex(el) : 0 - + const selectedSliderIndex = itemCount > 0 ? resolveCurrentStoryIndex(el, 'selectedSliderIndex') : 0 return { found: true, mediaType: resolveOverallMediaType(), mediaInfo, modalBody: sliderHtml, selectedSliderIndex, userName, userLink: userLink } } -export function getBrowserInfo() { - const ua = navigator.userAgent - let tem, - M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [] - if (/trident/i.test(M[1])) { - tem = /\brv[ :]+(\d+)/g.exec(ua) || [] - return { name: 'IE', version: (tem[1] || '') } - } - if (M[1] === 'Chrome') { - tem = ua.match(/\b(OPR|Edge)\/(\d+)/) - if (tem != null) return { name: tem[1].replace('OPR', 'Opera'), version: tem[2] } - } - M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'] - if ((tem = ua.match(/version\/(\d+)/i)) != null) M.splice(1, 1, tem[1]) - return { name: M[0], version: M[1] } +export const addMediaToBody = (modalBody: string, media: any, index: number, userName: string, program: Program): string => { + const { formattedFilename, url } = getFormattedFilenameAndUrl(media, userName, program.settings.formattedFilenameInput, index) + const mediaElement = getMediaElement(resolveElementMediaType(media), url, program.settings.storiesMuted) + + const encodedUrl = `https://instantgram.1337.pictures/download.php?data=${btoa(url)}:${btoa(formattedFilename)}` + const downloadUrl = program.settings.openInNewTab ? url : encodedUrl + + return modalBody + ` +
    + ${mediaElement} + ${localize("download")} + +
    ` } -export function getCurrentStory(el: HTMLElement) { - // Handle the case where the element might not be found - if (!el) { - return 0 +export const getBrowserInfo = () => { + const ua = navigator.userAgent + const match = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; + let temp + if (/trident/i.test(match[1])) { + temp = /\brv[ :]+(\d+)/g.exec(ua) || [] + return { name: 'IE', version: temp[1] || '' } } - // Initialize variables to keep track of the tallest element and its height - let tallestElement: HTMLElement | null = null - let maxHeight = 0 - // Iterate over each child element of the parent - el.childNodes.forEach(node => { - // Ensure the node is an HTMLElement to access offsetHeight - if (node instanceof HTMLElement) { - const height = node.offsetHeight // Includes padding but not margins - - // Update tallestElement if the current child's height is greater than maxHeight - if (height > maxHeight) { - maxHeight = height - tallestElement = node - } - } - }) - // Return the tallest element found or null if there are no HTMLElement children - return tallestElement + if (match[1] === 'Chrome') { + temp = ua.match(/\b(OPR|Edge)\/(\d+)/) + if (temp) return { name: temp[1].replace('OPR', 'Opera'), version: temp[2] } + } + if (match.length > 1) { + const versionMatch = ua.match(/version\/(\d+)/i) + if (versionMatch) match[2] = versionMatch[1] + return { name: match[1], version: match[2] } + } + return { name: navigator.appName, version: navigator.appVersion } } -export function getElementInViewPercentage(el: HTMLElement): number { - if (!el || !el.getBoundingClientRect) return 0 +export const getElementInViewPercentage = (el: HTMLElement): number => { + if (!el?.getBoundingClientRect) return 0; - const { top, bottom } = el.getBoundingClientRect() - const viewportTop = window.scrollY || document.documentElement.scrollTop - const viewportBottom = viewportTop + window.innerHeight - const elementTop = top + window.scrollY - const elementBottom = bottom + window.scrollY + const { top, bottom } = el.getBoundingClientRect(); + const viewportTop = window.scrollY || document.documentElement.scrollTop; + const viewportBottom = viewportTop + window.innerHeight; + const elementTop = top + window.scrollY; + const elementBottom = bottom + window.scrollY; - if (viewportTop > elementBottom || viewportBottom < elementTop) return 0 + if (viewportTop > elementBottom || viewportBottom < elementTop) return 0; - const visibleTop = Math.max(viewportTop, elementTop) - const visibleBottom = Math.min(viewportBottom, elementBottom) - const visibleHeight = visibleBottom - visibleTop - const elementHeight = bottom - top + const visibleHeight = Math.min(viewportBottom, elementBottom) - Math.max(viewportTop, elementTop); + const elementHeight = bottom - top; - return Math.round((visibleHeight / elementHeight) * 100) + return Math.round((visibleHeight / elementHeight) * 100); } -export function getFormattedFilenameAndUrl(media, userName: string, template: string, index: number) { - const date = new Date(media.taken_at * 1000) - const placeholders = { - Minute: date.getMinutes().toString().padStart(2, "0"), - Hour: date.getHours().toString().padStart(2, "0"), - Day: date.getDate().toString().padStart(2, "0"), - Month: (date.getMonth() + 1).toString().padStart(2, "0"), - Year: date.getFullYear().toString(), - Username: userName - } - const filename = userFilenameFormatter(template, placeholders) - const { extension, url } = getImgOrVideoUrl(media) - return { formattedFilename: filename + "_" + Number(index + 1) + "." + extension, url: url } +export const getFormattedFilenameAndUrl = (media, userName: string, template: string, index: number) => { + let placeholders = {} + + if (media && typeof media === "object" && media.height && media.width && media.url) { + return { formattedFilename: `${userName}.jpg`, url: media.url } + } + + if (typeof media === "string") { + const date = new Date() + placeholders = { + Minute: date.getMinutes().toString().padStart(2, "0"), + Hour: date.getHours().toString().padStart(2, "0"), + Day: date.getDate().toString().padStart(2, "0"), + Month: (date.getMonth() + 1).toString().padStart(2, "0"), + Year: date.getFullYear().toString(), + Username: userName + } + const filename = userFilenameFormatter(template, placeholders) + return { formattedFilename: `${filename}_${index + 1}.txt`, url: media } + } else if (media && typeof media === "object") { + const date = new Date(media.taken_at * 1000) + placeholders = { + Minute: date.getMinutes().toString().padStart(2, "0"), + Hour: date.getHours().toString().padStart(2, "0"), + Day: date.getDate().toString().padStart(2, "0"), + Month: (date.getMonth() + 1).toString().padStart(2, "0"), + Year: date.getFullYear().toString(), + Username: userName + } + const filename = userFilenameFormatter(template, placeholders) + const { extension, url } = getImgOrVideoUrl(media) + return { formattedFilename: `${filename}_${index + 1}.${extension}`, url: url } + } else { + throw new Error("Unsupported media type") + } } -export function getIGUsername(url: string): string { +export const getIGUsername = (url: string): string => { const regex = /https:\/\/www\.instagram\.com\/(stories\/|reels\/|p\/)?([^/?]+)/ const match = url.match(regex) - if (match && match.length > 2) { - return match[2] // The username is in the second capturing group - } - return null // Return null if no username is found + return match ? match[2] : null } -export function getImgOrVideoUrl(item: Record) { +export const getImgOrVideoUrl = (item: Record) => { if (item.items) { - if ("video_versions" in item) { - return { extension: "mp4", url: item.items[0].video_versions[0].url } + if ("video_versions" in item && item.items[0]?.video_versions?.[0]?.url) { + return { extension: "mp4", url: item.items[0].video_versions[0].url }; + } else if (item.items[0]?.image_versions2?.candidates?.[0]?.url) { + return { extension: "jpg", url: item.items[0].image_versions2.candidates[0].url }; } else { - return { extension: "jpg", url: item.items[0].image_versions2.candidates[0].url } + console.error('Error: No valid video or image URL found in item.items[0]'); + return null; // Or handle the error appropriately } } - if ("video_versions" in item) { - return { extension: "mp4", url: item.video_versions[0].url } + if ("video_versions" in item && item.video_versions?.[0]?.url) { + return { extension: "mp4", url: item.video_versions[0].url }; + } else if (item.image_versions2?.candidates?.[0]?.url) { + return { extension: "jpg", url: item.image_versions2.candidates[0].url }; } else { - return { extension: "jpg", url: item.image_versions2.candidates[0].url } + console.error('Error: No valid video or image URL found'); + return null; // Or handle the error appropriately } -} -export function getMediaElement(mediaType, url, mutedStories: boolean) { +}; +export const getMediaElement = (mediaType, url, storiesMuted: boolean) => { return mediaType === MediaType.Video - ? `` - : `` + ? `` + : ``; } -export async function getMediaInfo(el: HTMLElement, postId: string, userId: string): Promise { +export const getMediaInfo = async (el: HTMLElement, postId: string, userId: string): Promise => { if (!postId) { - return await fetchDataFromApi({ type: 'getReelsMediaFromFeed', articleNode: el, id: userId }) + return await fetchDataFromApi({ type: 'getReelsMediaFromFeed', articleNode: el, id: userId }); } if (window.location.pathname.startsWith("/stories/highlights/")) { - return await fetchDataFromApi({ type: 'getReelsMediaFromFeed', articleNode: el, id: null }) + return await fetchDataFromApi({ type: 'getReelsMediaFromFeed', articleNode: el, id: null }); } else if (window.location.pathname.startsWith("/stories/")) { - return await fetchDataFromApi({ type: 'getReelsMediaFromFeed', articleNode: el, id: userId }) + return await fetchDataFromApi({ type: 'getReelsMediaFromFeed', articleNode: el, id: userId }); } else { - return await fetchDataFromApi({ type: 'getMediaFromInfo', articleNode: el }) + return await fetchDataFromApi({ type: 'getMediaFromInfo', articleNode: el }); } } -export function processMediaInfo(mediaInfo, callback) { - let count = 0 - if (mediaInfo.reels_media && mediaInfo.reels_media.length > 0 && mediaInfo.reels_media[0].items) { - mediaInfo.reels_media[0].items.forEach(callback) - count = mediaInfo.reels_media[0].items.length - } else if (mediaInfo.items && mediaInfo.items.length > 0 && mediaInfo.items[0].carousel_media) { - mediaInfo.items[0].carousel_media.forEach(callback) - count = mediaInfo.items[0].carousel_media.length - } else if (mediaInfo.items && mediaInfo.items.length > 0) { - callback(mediaInfo.items[0], 0) - count = 1 - } else { - if (mediaInfo && mediaInfo.user.hd_profile_pic_url_info.url) { - callback(mediaInfo.user.hd_profile_pic_url_info, 0) - count = 1 - } +export const processMediaInfo = (mediaInfo: any, callback: any) => { + let count = 0; + + if (mediaInfo.reels_media?.[0]?.items) { + count = mediaInfo.reels_media[0].items.length; + mediaInfo.reels_media[0].items.forEach((media, index) => { + callback(media, index, count); // Pass the count to the callback + }); + } else if (mediaInfo.items?.[0]?.carousel_media) { + count = mediaInfo.items[0].carousel_media.length; + mediaInfo.items[0].carousel_media.forEach((media, index) => { + callback(media, index, count); // Pass the count to the callback + }); + } else if (mediaInfo.items?.[0]) { + count = 1; + callback(mediaInfo.items[0], 0, count); // Single item case + } else if (mediaInfo.user?.hd_profile_pic_url_info?.url) { + count = 1; + callback(mediaInfo.user.hd_profile_pic_url_info, 0, count); // Profile picture case } - return count -} -export function removeStyleTagsWithIDs(idsToRemove) { - const styleTags = document.querySelectorAll("style[id]") - styleTags.forEach(styleTag => { +}; +export const removeStyleTagsWithIDs = (idsToRemove: string[]) => { + document.querySelectorAll("style[id]").forEach(styleTag => { if (idsToRemove.includes(styleTag.id)) { - styleTag.parentNode.removeChild(styleTag) + styleTag.remove(); } - }) + }); } -export function resolveCurrentSliderIndex(el: HTMLElement): number { +export const resolveCurrentStoryIndex = (el: HTMLElement, _step: string): number => { // If the element is null, return -1 immediately if (!el) { return 0 } // Define an array of possible selectors for the slides root const selectors = [ - "._aamj._acvz._acnc._acng", // this is for feed posts + "._acvz._acnc._acng", // this is for feed posts + "section header div", // this is for stories on the feed ".x1ned7t2.x78zum5", // this is for stories/highlights in profile - "section header div", // this is for stories in feed "section > div header > div", // forgotten, dont know "section > div > div > div > div > div > div > div > div", // forgotten, dont know "div > div > div > div > div > div > div > div" // this is for the following: feed posts, profile modal and direct posts, ] - // Attempt to find the root element using each selector let slidesRoot: { children: Iterable | ArrayLike } - for (const selector of selectors) { + for (var selector of selectors) { slidesRoot = el.querySelector(selector) if (slidesRoot) { break } } - if (!slidesRoot) { return 0 // Return 0 if no root element is found } - // Collect all child elements of the root, if any const slidesChildren: HTMLElement[] = slidesRoot ? Array.from(slidesRoot.children) as HTMLElement[] : [] - // Find the index of the first child with nested elements for (let i = 0; i < slidesChildren.length; i++) { // Get all div elements inside each child - const allDivs = slidesChildren[i].querySelectorAll("div") - - // Check if allDivs is empty + const allDivs = slidesChildren[i].querySelectorAll('div') + //Check if allDivs is empty if (allDivs.length === 0) { - // Count classes directly on slidesChildren[i] - const classListLength = slidesChildren[i].classList.length // if length is greater than 1 it should have an active state - if (classListLength > 1) { - return i // Return the index of the child + if (selector == "._acvz._acnc._acng") { + // Count classes directly on slidesChildren[i] + const classListLength = slidesChildren[i].classList.length // if length is greater than 1 it have an active state + if (classListLength > 1) { + return i // Return the index of the child + } + } else { + continue } } else { - for (const div of Array.from(allDivs)) { - const widthStyle = div.style.width - const transformStyle = div.style.transform - - // Check for width not 100% or any transform property present - if ((widthStyle && widthStyle !== "100%") || (transformStyle && transformStyle.trim() !== "")) { - return i // Return the index of the selected slider + // Check for expired stories + const spanElement = allDivs[0]?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode?.children[1]?.querySelector('span') + if (spanElement) { + const targetIndex = Array.from(allDivs[0]?.parentNode?.parentNode?.children ?? []).findIndex(child => child.children.length > 0) + return (slidesChildren.length - targetIndex) > 0 ? 0 : 0 + } else { + for (const div of Array.from(allDivs)) { + const widthStyle = div.style.width + const transformStyle = div.style.transform + // Check for width not 100% or any transform property present + if ((widthStyle && widthStyle !== "100%") || (transformStyle && transformStyle.trim() !== "")) { + return i // Return the index of the selected slider + } } } } } - return 0 // Return 0 if no selected slider is found } // export function resolveExpiredStories(el) { @@ -393,20 +415,15 @@ export function resolveCurrentSliderIndex(el: HTMLElement): number { // } // return -1 // Return -1 if no such div is found // } -export function resolveElementMediaType(mediaArray: { width?: any; height?: any; url?: string; carousel_media?: any; video_dash_manifest?: any; video_duration?: any; video_versions?: any }) { - if (mediaArray.carousel_media !== undefined) { - return MediaType.Carousel - } else if (mediaArray.video_dash_manifest !== undefined || mediaArray.video_duration !== undefined || mediaArray.video_versions !== undefined) { - return MediaType.Video - } else { - return MediaType.Image - } +export const resolveElementMediaType = (mediaArray: { width?: any; height?: any; url?: string; carousel_media?: any; video_dash_manifest?: any; video_duration?: any; video_versions?: any }) => { + if (mediaArray.carousel_media) return MediaType.Carousel + if (mediaArray.video_dash_manifest || mediaArray.video_duration || mediaArray.video_versions) return MediaType.Video + return MediaType.Image } -export function resolveOverallMediaType() { - // Logic to determine overall media type from mediaInfo - return MediaType.UNDEFINED // Example placeholder +export const resolveOverallMediaType = () => { + return MediaType.UNDEFINED } -export async function secureFetch(url, appId) { +export const secureFetch = async (url, appId) => { try { const response = await fetch(url, { method: 'GET', @@ -415,41 +432,126 @@ export async function secureFetch(url, appId) { mode: 'cors', }) if (response.status !== 200) { - console.log(`Fetch API failed with status code: ${response.status}`) + console.info(`Fetch API failed with status code: ${response.status}`) return null } return await response.json() } catch (e) { - console.log(`Error fetching data: ${e}\n${e.stack}`) + console.info(`Error fetching data: ${e}\n${e.stack}`) return null } } -export function sleep(ms: number): Promise { - return new Promise(function (resolve) { setTimeout(resolve, ms) }) -} -export function userFilenameFormatter(filename: string, placeholders: Record): string { - // Replace placeholders with corresponding values +export const sleep = (ms: number): Promise => new Promise(resolve => setTimeout(resolve, ms)); +export const userFilenameFormatter = (filename: string, placeholders: Record): string => { for (const placeholder in placeholders) { const regex = new RegExp(`{${placeholder}}`, "g") filename = filename.replace(regex, placeholders[placeholder]) } - - // Replace spaces with dashes (-) - filename = filename.replace(/\s+/g, "-") - - // Remove any special characters except dashes, underscores, and dots - filename = filename.replace(/[^\w-.]/g, "") - - return filename + return filename.replace(/\s+/g, "-").replace(/[^\w-.]/g, "") } -export function wrapInSliderContainer(modalBody) { - return `
    ${modalBody}
    ` -} -export function resolveUserLink(rootUrl, path, userName) { +export const wrapInSliderContainer = (modalBody) => `
    ${modalBody}
    `; +export const resolveUserLink = (rootUrl, path, userName) => { if (path.startsWith("/p/") || path.startsWith("/stories/")) { return `${rootUrl}/${userName}/` } else if (path.startsWith("/reels/")) { return `${rootUrl}/${userName}/reels/` + } else { + return `${rootUrl}/${userName}` + } +} +export const findAD = (el: HTMLElement, isStory?: boolean): boolean => { + const isADPathPresent = (): boolean => { + return Boolean(Array.from(el.querySelectorAll("path")) + .find(p => p.getAttribute("d") === "M21 17.502a.997.997 0 0 1-.707-.293L12 8.913l-8.293 8.296a1 1 0 1 1-1.414-1.414l9-9.004a1.03 1.03 0 0 1 1.414 0l9 9.004A1 1 0 0 1 21 17.502Z")); + } + const getAdText = (): string | undefined => { + try { + return el.children[0]?.children[0]?.children[0]?.children[0]?.children[0]?.children[1]?.children[0]?.children[0]?.children[1]?.children[1]?.children[0]?.textContent; + } catch { + return undefined + } + } + if (isStory) { + const adText = getAdText() + if (adText) { + return Object.values(localization.langs).some(locale => locale.ad === adText) + } + return false + } + return isADPathPresent() +} +export function getReactFiberKey(element) { + return Object.keys(element).find(key => key.includes('Instance') || key.includes('Fiber')) +} +export const traverseReactDOMAndFindHidden = (root) => { + const traverse = (node) => { + if (!node) return null + const fiberNode = node[getReactFiberKey(node)] + const isHidden = fiberNode?.memoizedProps?.hidden === true || fiberNode?.return?.memoizedProps?.hidden === true + return node.tagName === 'DIV' && !isHidden ? node : null + } + let child = root.firstElementChild + while (child) { + const result = traverse(child) + if (result) return result + child = child.nextElementSibling } return null -} \ No newline at end of file +} +export const getElementWithHighestWidth = (el: HTMLElement): HTMLElement | null => { + if (!el) return null + const divs = el.querySelectorAll('div > div > div') + if (divs.length === 0) return null + return Array.from(divs).reduce((maxDiv, currentDiv) => { + const maxWidth = parseFloat(getComputedStyle(maxDiv).width) + const currentWidth = parseFloat(getComputedStyle(currentDiv).width) + return currentWidth > maxWidth ? currentDiv : maxDiv + }, divs[0]) +} +export const getStoryWrapper = (el) => { + const sections = [...el?.querySelectorAll("section") || []] + return sections[sections.length - 1] +} +export const getAllNodeParent = (el) => { + const parents = [] + for (parents.push(el); el.parentNode;) { + parents.unshift(el.parentNode) + el = el.parentNode + } + return parents +} +export const getReactInstanceFromElement = (element) => { + const key = Object.keys(element).find((key) => key.includes("Instance") || key.includes("Fiber")) + return key ? element[key] : null +} +export const compareMpegRepresentation = (a, b) => + a.quality === "hd" && b.quality !== "hd" ? -1 : + a.quality !== "hd" && b.quality === "hd" ? 1 : + b.bandwidth - a.bandwidth +export const toMpegRepresentation = (el) => ({ + quality: el.getAttribute("FBQualityClass"), + bandwidth: +el.getAttribute("bandwidth"), + baseUrl: el.querySelector("BaseURL")?.textContent?.trim() || null, +}) +export const getOriginalVideo = (el) => { + const src = el.src && !el.src.startsWith("blob:") ? el.src : null + if (src) return src + const manifest = getReactInstanceFromElement(el)?.return?.return?.memoizedProps?.manifest + if (!manifest) return null + const doc = new DOMParser().parseFromString(manifest, "text/xml") + return Array.from(doc.querySelectorAll('Representation[mimeType="video/mp4"]')) + .map(toMpegRepresentation) + .filter(rep => rep.baseUrl) + .sort(compareMpegRepresentation)[0]?.baseUrl || null +} +export const isElementInViewport = (el) => { + const { top, right, bottom, left } = el.getBoundingClientRect() + return bottom > 0 && right > 0 && left < window.innerWidth && top < window.innerHeight +} +export const isProfileImage = (el) => { + const parent = el.parentElement + return el.getAttribute("data-testid") === "user-avatar" || + el.width < 48 || + ["span", "a"].includes(parent?.localName) || + getAllNodeParent(el).some(node => node.nodeName === "HEADER") +} diff --git a/src/index.ts b/src/index.ts index e1cba5a..4f89564 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,18 +1,16 @@ import { Program } from "./App" import { MediaScanner } from "./modules/MediaScanner" -import { Modal } from "./components/Modal" import { getBrowserInfo } from "./helpers/utils" import VersionUpdater from "./modules/Update" -import localize from "./helpers/localize" - console.clear() - const APP_NAME = "instantgram" +const DEVELOPMENT = process.env.DEV as unknown as boolean ?? false const VERSION = process.env.VERSION as string const STORAGE_NAME = APP_NAME.toLowerCase().replace(/-/g, "_") export const program: Program = { NAME: APP_NAME, STORAGE_NAME: STORAGE_NAME, + DEVELOPMENT: DEVELOPMENT, VERSION: VERSION, browser: getBrowserInfo(), hostname: window.location.hostname, @@ -24,10 +22,17 @@ export const program: Program = { regexReelURI: /reel\/(.*)+/, regexReelsURI: /reels\/(.*)+/, regexStoriesURI: /\/stories\/(\w+)|\/highlights\/(\d+)\//, - foundByModule: null + foundByModule: null, + settings: { + showAds: localStorage.getItem(`${STORAGE_NAME}_settings_general_1`) === "true", + openInNewTab: localStorage.getItem(`${STORAGE_NAME}_settings_general_2`) === "true", + autoSlideshow: localStorage.getItem(`${STORAGE_NAME}_settings_general_3`) === "true", + formattedFilenameInput: localStorage.getItem(`${STORAGE_NAME}_settings_general_4`) || "{Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}", + storiesMuted: localStorage.getItem(`${STORAGE_NAME}_settings_stories_1`) === "true", + noMultiStories: localStorage.getItem(`${STORAGE_NAME}_settings_stories_3`) === "true" + } } - -if (process.env.DEV) { +if (DEVELOPMENT) { console.info(["Developer Mode Caution!", program]) if (program.browser) { console.info(["Browser Name", program.browser.name]) @@ -35,22 +40,12 @@ if (process.env.DEV) { console.info(["Browser OS", navigator.platform]) } } - const runApp = async () => { - if (!program.hostname.includes("instagram.com")) { - new Modal({ - heading: [`
    [${program.NAME}]v${program.VERSION}
    `], - body: [localize("index@alert_onlyWorks")], - bodyStyle: "text-align:center", - buttonList: [{ active: true, text: "Ok" }] - }).open() - return - } const scanner = new MediaScanner() scanner.execute(program) - if (!process.env.DEV) { - const updater = new VersionUpdater(program) - await updater.update(VERSION) - } + // if (!DEVELOPMENT) { + // const updater = new VersionUpdater(program) + // await updater.check(VERSION) + // } } runApp() \ No newline at end of file diff --git a/src/localization.ts b/src/localization.ts index ac721b1..0405aa5 100644 --- a/src/localization.ts +++ b/src/localization.ts @@ -1,163 +1,119 @@ export default { "langs": { "en-US": { - "helpers.localize_defaultlang": "[instantgram] set language: ${LANG_DEFAULT} \n For more information about available languages please check http://thinkbig-company.github.io/instantgram", - - "index@alert_onlyWorks": "Works only on instagram.com", - "index@profilepage_downloader_disabled": "Sorry the ProfilePage downloader is currently disabled because instagram changed their system.\n\nMaybe in the future there will be a solution to fix the problem.", - - "index@alert_dontFound": "Did you open any Instagram post? Like for example
    ", - - "index#program#modal_settings@title": "Settings", - "index#program#modal_settings@settings_attention": "Attention: You must open this app again for your changes to be applied!", - "index#program#modal_settings@settings_general_title_1": "Enable monetized posts", - "index#program#modal_settings@settings_general_description_1": "Ad blockers must be deactivated", - "index#program#modal_settings@settings_general_title_2": "Open download in new tab", - "index#program#modal_settings@settings_general_description_2": "The download will be opened in a new tab", - "index#program#modal_settings@settings_general_title_3": "Slideshow on/off", - "index#program#modal_settings@settings_general_description_3": "Enable or disable the automatic slideshow", - "index#program#modal_settings@settings_general_title_4": "Change file name format for downloads", - "index#program#modal_settings@settings_general_description_4": "Change the file name format for the downloads here
    The default format is
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}", - - "index#program#modal_settings@settings_stories_title_1": "Mute stories", - "index#program#modal_settings@settings_stories_description_1": "Stories are muted when opened", - "index#program#modal_settings@settings_stories_title_2": "Pause stories when opening", - "index#program#modal_settings@settings_stories_description_2": "Stories are paused when opened", - "index#program#modal_settings@settings_stories_title_3": "Display stories individually", - "index#program#modal_settings@settings_stories_description_3": "Stories are displayed individually when opened", - - "index@download": "Download", - "index@save": "Save", - "index@saved": "Saved", - "index@close": "Close", - - "modules.update@consoleWarnOutdatedInfo": "[instantgram] is outdated. Please check http://thinkbig-company.github.io/instantgram for available updates.", - "modules.update@consoleWarnOutdatedInfoVersions": "[instantgram] Installed version: ${data.version} | New update: ${data.onlineVersion}", - "modules.update@determineIfGetUpdateIsNecessary_contacting": "[instantgram] is looking for available updates...", - "modules.update@determineIfGetUpdateIsNecessary_updated": "[instantgram] updated your current version.", - "modules.update@determineIfGetUpdateIsNecessary_@update_available": "There is a new update available", - "modules.update@determineIfGetUpdateIsNecessary_@load_update": "Get update", - + "helpers.localizeDefaultLang": "[instantgram] set language: ${LANG_DEFAULT} \n For more information about available languages please check http://thinkbig-company.github.io/instantgram", + "alertWorksOnlyOn": "Works only on instagram.com", + "alertNotFound": "Did you open any Instagram post? Like for example
    ", + "modalSettingsTitle": "Settings", + "modalSettingsAttention": "Attention: You must open this app again for your changes to be applied!", + "modalSettingsGeneral": "General", + "modalSettingsGenTitle1": "Enable monetized posts", + "modalSettingsGenDesc1": "Ad blockers must be deactivated", + "modalSettingsGenTitle2": "Open download in new tab", + "modalSettingsGenDesc2": "The download will be opened in a new tab", + "modalSettingsGenTitle3": "Slideshow on/off", + "modalSettingsGenDesc3": "Enable or disable the automatic slideshow", + "modalSettingsGenTitle4": "Change file name format for downloads", + "modalSettingsGenDesc4": "Change the file name format for the downloads here
    The default format is
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}", + "modalSettingsStoriesTitle1": "Mute stories", + "modalSettingsStoriesDesc1": "Stories are muted when opened", + "modalSettingsStoriesTitle2": "Pause stories when opening", + "modalSettingsStoriesDesc2": "Stories are paused when opened", + "modalSettingsStoriesTitle3": "Display stories individually", + "modalSettingsStoriesDesc3": "Stories are displayed individually when opened", + "download": "Download", + "save": "Save", + "saved": "Saved", + "close": "Close", + "consoleWarnOutdatedInfo": "[instantgram] is outdated. Please check http://thinkbig-company.github.io/instantgram for available updates.", + "consoleWarnOutdatedVersions": "[instantgram] Installed version: ${data.version} | New update: ${data.onlineVersion}", "ad": "Sponsored" }, "de-DE": { - "helpers.localize_defaultlang": "Ausgewählte Sprache: ${LANG_DEFAULT} \n Weitere Informationen zu den unterstützten Sprachen findest du auf http://thinkbig-company.github.io/instantgram", - - "index@alert_onlyWorks": "Funktioniert nur auf instagram.com", - "index@profilepage_downloader_disabled": "Leider ist der ProfilePage-Downloader derzeit deaktiviert, da Instagram sein System geändert hat.\n\nVielleicht gibt es in Zukunft eine Lösung für dieses Problem.", - - "index@alert_dontFound": "Hast du einen Instagram Post geöffnet? Zum Beispiel
    ", - - "index#program#modal_settings@title": "Einstellungen", - "index#program#modal_settings@settings_attention": "Achtung: Sie müssen diese App erneut öffnen, damit ihre Änderungen übernommen werden!", - "index#program#modal_settings@settings_general_title_1": "Monetarisierte Beiträge aktivieren", - "index#program#modal_settings@settings_general_description_1": "Werbeblocker müssen deaktiviert sein", - "index#program#modal_settings@settings_general_title_2": "Download in neuem Tab öffnen", - "index#program#modal_settings@settings_general_description_2": "Der Download wird in einem neuen Tab geöffnet", - "index#program#modal_settings@settings_general_title_3": "Slideshow ein/ausschalten", - "index#program#modal_settings@settings_general_description_3": "Aktivieren oder deaktivieren Sie die automatische Slideshow", - "index#program#modal_settings@settings_general_title_4": "Dateinamenformat für Downloads ändern", - "index#program#modal_settings@settings_general_description_4": "Ändere hier das Dateinamenformat für die Downloads
    Das Standardformat ist
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}", - - "index#program#modal_settings@settings_stories_title_1": "Stories stummschalten", - "index#program#modal_settings@settings_stories_description_1": "Beim öffnen werden Stories stummgeschaltet", - "index#program#modal_settings@settings_stories_title_2": "Stories beim öffnen pausieren", - "index#program#modal_settings@settings_stories_description_2": "Beim öffnen werden Stories pausiert", - "index#program#modal_settings@settings_stories_title_3": "Stories einzeln anzeigen", - "index#program#modal_settings@settings_stories_description_3": "Beim öffnen werden Stories einzeln angezeigt", - - "index@download": "Download", - "index@save": "Speichern", - "index@saved": "Gespeichert", - "index@close": "Schließen", - - "modules.update@consoleWarnOutdatedInfo": "[instantgram] ist veraltet. Bitte besuche die Seite http://thinkbig-company.github.io/instantgram für ein Update.", - "modules.update@consoleWarnOutdatedInfoVersions": "[instantgram] Installierte Version: ${data.version} | Neue Version: ${data.onlineVersion}", - "modules.update@determineIfGetUpdateIsNecessary_contacting": "[instantgram] sucht nach neuen verfügbaren Updates...", - "modules.update@determineIfGetUpdateIsNecessary_updated": "[instantgram] wurde aktualisiert.", - "modules.update@determineIfGetUpdateIsNecessary_@update_available": "Es ist ein neues Update verfügbar", - "modules.update@determineIfGetUpdateIsNecessary_@load_update": "Update laden", - + "helpers.localizeDefaultLang": "Ausgewählte Sprache: ${LANG_DEFAULT} \n Weitere Informationen zu den unterstützten Sprachen findest du auf http://thinkbig-company.github.io/instantgram", + "alertWorksOnlyOn": "Funktioniert nur auf instagram.com", + "alertNotFound": "Hast du einen Instagram Post geöffnet? Zum Beispiel
    ", + "modalSettingsTitle": "Einstellungen", + "modalSettingsAttention": "Achtung: Sie müssen diese App erneut öffnen, damit ihre Änderungen übernommen werden!", + "modalSettingsGeneral": "Allgemein", + "modalSettingsGenTitle1": "Monetarisierte Beiträge aktivieren", + "modalSettingsGenDesc1": "Werbeblocker müssen deaktiviert sein", + "modalSettingsGenTitle2": "Download in neuem Tab öffnen", + "modalSettingsGenDesc2": "Der Download wird in einem neuen Tab geöffnet", + "modalSettingsGenTitle3": "Slideshow ein/ausschalten", + "modalSettingsGenDesc3": "Aktivieren oder deaktivieren Sie die automatische Slideshow", + "modalSettingsGenTitle4": "Dateinamenformat für Downloads ändern", + "modalSettingsGenDesc4": "Ändere hier das Dateinamenformat für die Downloads
    Das Standardformat ist
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}", + "modalSettingsStoriesTitle1": "Stories stummschalten", + "modalSettingsStoriesDesc1": "Beim öffnen werden Stories stummgeschaltet", + "modalSettingsStoriesTitle2": "Stories beim öffnen pausieren", + "modalSettingsStoriesDesc2": "Beim öffnen werden Stories pausiert", + "modalSettingsStoriesTitle3": "Stories einzeln anzeigen", + "modalSettingsStoriesDesc3": "Beim öffnen werden Stories einzeln angezeigt", + "download": "Download", + "save": "Speichern", + "saved": "Gespeichert", + "close": "Schließen", + "consoleWarnOutdatedInfo": "[instantgram] ist veraltet. Bitte besuche die Seite http://thinkbig-company.github.io/instantgram für ein Update.", + "consoleWarnOutdatedVersions": "[instantgram] Installierte Version: ${data.version} | Neue Version: ${data.onlineVersion}", "ad": "Anzeige" }, "es-AR": { - "helpers.localize_defaultlang": "[instantgram] establecer idioma: ${LANG_DEFAULT} \n Para más información sobre los idiomas disponibles, consulte http://thinkbig-company.github.io/instantgram", - - "index@alert_onlyWorks": "Sólo funciona en instagram.com", - "index@profilepage_downloader_disabled": "Lo siento, la descarga de ProfilePage está deshabilitada actualmente porque Instagram cambió su sistema.\n\nTal vez en el futuro haya una solución para solucionar el problema.", - - "index@alert_dontFound": "¿Has abierto algún post de Instagram? Como por ejemplo
    ", - - "index#program#modal_settings@title": "Ajustes", - "index#program#modal_settings@settings_attention": "Atención: ¡Debes abrir esta aplicación nuevamente para que se apliquen los cambios!", - "index#program#modal_settings@settings_general_title_1": "Habilitar publicaciones monetizadas", - "index#program#modal_settings@settings_general_description_1": "Los bloqueadores de publicidad deben estar desactivados", - "index#program#modal_settings@settings_general_title_2": "Abrir descarga en una nueva pestaña", - "index#program#modal_settings@settings_general_description_2": "La descarga se abrirá en una nueva pestaña", - "index#program#modal_settings@settings_general_title_3": "Slideshow on/off", - "index#program#modal_settings@settings_general_description_3": "Activar/desactivar presentación de diapositivas", - "index#program#modal_settings@settings_general_title_4": "Cambiar el formato del nombre del archivo para descargas", - "index#program#modal_settings@settings_general_description_4": "Cambie el formato del nombre de archivo para las descargas aquí
    El formato predeterminado es
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}", - - "index#program#modal_settings@settings_stories_title_1": "Historias mudas", - "index#program#modal_settings@settings_stories_description_1": "Las historias se silencian cuando se abren", - "index#program#modal_settings@settings_stories_title_2": "Pausar historias al abrir", - "index#program#modal_settings@settings_stories_description_2": "Las historias se pausan cuando se abren.", - "index#program#modal_settings@settings_stories_title_3": "Mostrar historias individualmente", - "index#program#modal_settings@settings_stories_description_3": "Las historias se muestran individualmente cuando se abren", - - "index@download": "Descargar", - "index@save": "Ahorrar", - "index@saved": "Salvado", - "index@close": "Cerca", - - "modules.update@consoleWarnOutdatedInfo": "[instantgram] es obsoleto. Consulte en http://thinkbig-company.github.io/instantgram las actualizaciones disponibles.", - "modules.update@consoleWarnOutdatedInfoVersions": "[instantgram] Versión instalada: ${data.version} | Nueva actualización: ${data.onlineVersion}", - "modules.update@determineIfGetUpdateIsNecessary_contacting": "[instantgram] está buscando actualizaciones disponibles...", - "modules.update@determineIfGetUpdateIsNecessary_updated": "[instantgram] ha actualizado su versión actual.", - "modules.update@determineIfGetUpdateIsNecessary_@update_available": "Hay una nueva actualización disponible", - "modules.update@determineIfGetUpdateIsNecessary_@load_update": "Obtener información", - + "helpers.localizeDefaultLang": "[instantgram] establecer idioma: ${LANG_DEFAULT} \n Para más información sobre los idiomas disponibles, consulte http://thinkbig-company.github.io/instantgram", + "alertWorksOnlyOn": "Sólo funciona en instagram.com", + "alertNotFound": "¿Has abierto algún post de Instagram? Como por ejemplo
    ", + "modalSettingsTitle": "Ajustes", + "modalSettingsAttention": "Atención: ¡Debes abrir esta aplicación nuevamente para que se apliquen los cambios!", + "modalSettingsGeneral": "General", + "modalSettingsGenTitle1": "Habilitar publicaciones monetizadas", + "modalSettingsGenDesc1": "Los bloqueadores de publicidad deben estar desactivados", + "modalSettingsGenTitle2": "Abrir descarga en una nueva pestaña", + "modalSettingsGenDesc2": "La descarga se abrirá en una nueva pestaña", + "modalSettingsGenTitle3": "Slideshow on/off", + "modalSettingsGenDesc3": "Activar/desactivar presentación de diapositivas", + "modalSettingsGenTitle4": "Cambiar el formato del nombre del archivo para descargas", + "modalSettingsGenDesc4": "Cambie el formato del nombre de archivo para las descargas aquí
    El formato predeterminado es
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}", + "modalSettingsStoriesTitle1": "Historias mudas", + "modalSettingsStoriesDesc1": "Las historias se silencian cuando se abren", + "modalSettingsStoriesTitle2": "Pausar historias al abrir", + "modalSettingsStoriesDesc2": "Las historias se pausan cuando se abren.", + "modalSettingsStoriesTitle3": "Mostrar historias individualmente", + "modalSettingsStoriesDesc3": "Las historias se muestran individualmente cuando se abren", + "download": "Descargar", + "save": "Ahorrar", + "saved": "Salvado", + "close": "Cerca", + "consoleWarnOutdatedInfo": "[instantgram] es obsoleto. Consulte en http://thinkbig-company.github.io/instantgram las actualizaciones disponibles.", + "consoleWarnOutdatedVersions": "[instantgram] Versión instalada: ${data.version} | Nueva actualización: ${data.onlineVersion}", "ad": "Publicidad" }, "pt-BR": { - "helpers.localize_defaultlang": "[instantgram] idioma configurado: ${LANG_DEFAULT} \npara mais informações sobre os idiomas suportados, acesse http://thinkbig-company.github.io/instantgram", - - "index@alert_onlyWorks": "[instantgram] somente funciona no instagram.com", - "index@profilepage_downloader_disabled": "Lamentamos que o programa de download ProfilePage esteja atualmente desativado porque o programa mudou seu sistema.\n\nTalvez, no futuro, haja uma solução para resolver o problema.", - - "index@alert_dontFound": "ops, você está em algum post do instagram? ex:
    ", - - "index#program#modal_settings@title": "Configurações", - "index#program#modal_settings@settings_attention": "Atenção: Você deve abrir este aplicativo novamente para que suas alterações sejam aplicadas!", - "index#program#modal_settings@settings_general_title_1": "Habilitar postagens monetizadas", - "index#program#modal_settings@settings_general_description_1": "Os bloqueadores de anúncios devem ser desativados", - "index#program#modal_settings@settings_general_title_2": "Abrir o download em uma nova guia", - "index#program#modal_settings@settings_general_description_2": "O download será aberto em uma nova guia", - "index#program#modal_settings@settings_general_title_3": "Ativação/desativação da apresentação de slides", - "index#program#modal_settings@settings_general_description_3": "Ativar ou desativar a apresentação automática de slides", - "index#program#modal_settings@settings_general_title_4": "Alterar o formato do nome do arquivo para downloads", - "index#program#modal_settings@settings_general_description_4": "Altere o formato do nome do arquivo para os downloads aqui
    O formato padrão é
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}", - - "index#program#modal_settings@settings_stories_title_1": "Histórias mudas", - "index#program#modal_settings@settings_stories_description_1": "As histórias são silenciadas quando abertas", - "index#program#modal_settings@settings_stories_title_2": "Pausar histórias ao abrir", - "index#program#modal_settings@settings_stories_description_2": "As histórias são pausadas quando abertas", - "index#program#modal_settings@settings_stories_title_3": "Exibir histórias individualmente", - "index#program#modal_settings@settings_stories_description_3": "As histórias são exibidas individualmente quando abertas", - - "index@download": "Download", - "index@save": "Salvar", - "index@saved": "Salvo em", - "index@close": "Fechar", - - "modules.update@consoleWarnOutdatedInfo": "[instantgram] está desatualizado. Acesse http://thinkbig-company.github.io/instantgram para atualizar", - "modules.update@consoleWarnOutdatedInfoVersions": "[instantgram] versão local: ${data.version} | nova versão: ${data.onlineVersion}", - "modules.update@determineIfGetUpdateIsNecessary_contacting": "[instantgram] está procurando atualizações...", - "modules.update@determineIfGetUpdateIsNecessary_updated": "[instantgram] informações locais atualizadas", - "modules.update@determineIfGetUpdateIsNecessary_@update_available": "Há uma nova atualização disponível", - "modules.update@determineIfGetUpdateIsNecessary_@load_update": "Carga de actualização", - + "helpers.localizeDefaultLang": "[instantgram] idioma configurado: ${LANG_DEFAULT} \npara mais informações sobre os idiomas suportados, acesse http://thinkbig-company.github.io/instantgram", + "alertWorksOnlyOn": "[instantgram] somente funciona no instagram.com", + "alertNotFound": "ops, você está em algum post do instagram? ex:
    ", + "modalSettingsTitle": "Configurações", + "modalSettingsAttention": "Atenção: Você deve abrir este aplicativo novamente para que suas alterações sejam aplicadas!", + "modalSettingsGeneral": "Geral", + "modalSettingsGenTitle1": "Habilitar postagens monetizadas", + "modalSettingsGenDesc1": "Os bloqueadores de anúncios devem ser desativados", + "modalSettingsGenTitle2": "Abrir o download em uma nova guia", + "modalSettingsGenDesc2": "O download será aberto em uma nova guia", + "modalSettingsGenTitle3": "Ativação/desativação da apresentação de slides", + "modalSettingsGenDesc3": "Ativar ou desativar a apresentação automática de slides", + "modalSettingsGenTitle4": "Alterar o formato do nome do arquivo para downloads", + "modalSettingsGenDesc4": "Altere o formato do nome do arquivo para os downloads aqui
    O formato padrão é
    {Username}__{Year}-{Month}-{Day}--{Hour}-{Minute}", + "modalSettingsStoriesTitle1": "Histórias mudas", + "modalSettingsStoriesDesc1": "As histórias são silenciadas quando abertas", + "modalSettingsStoriesTitle2": "Pausar histórias ao abrir", + "modalSettingsStoriesDesc2": "As histórias são pausadas quando abertas", + "modalSettingsStoriesTitle3": "Exibir histórias individualmente", + "modalSettingsStoriesDesc3": "As histórias são exibidas individualmente quando abertas", + "download": "Download", + "save": "Salvar", + "saved": "Salvo em", + "close": "Fechar", + "consoleWarnOutdatedInfo": "[instantgram] está desatualizado. Acesse http://thinkbig-company.github.io/instantgram para atualizar", + "consoleWarnOutdatedVersions": "[instantgram] versão local: ${data.version} | nova versão: ${data.onlineVersion}", "ad": "Patrocinado" } } diff --git a/src/model/mediaType.ts b/src/model/mediaType.ts index df2d12b..f434cbc 100644 --- a/src/model/mediaType.ts +++ b/src/model/mediaType.ts @@ -1,3 +1,4 @@ +/* eslint-disable */ export enum MediaType { Ad = "AD", Image = "IMAGE", diff --git a/src/modules/MediaScanner.ts b/src/modules/MediaScanner.ts index 6a4ed60..43761e3 100644 --- a/src/modules/MediaScanner.ts +++ b/src/modules/MediaScanner.ts @@ -2,7 +2,7 @@ import { Program } from "../App" import { Module } from "./Module" import { MediaScanResult } from "../model/MediaScanResult" import { Modal, ModalButton } from "../components/Modal" -import { cssCarouselSlider, cssGeneral, cssSlideOn } from "../components/Interconnect" +import { cssCarouselSlider, cssGeneral, cssSlideOn, logo } from "../components/Interconnect" import { FeedScanner } from "./FeedScanner" import { PostAndReelScanner } from "./PostAndReelScanner" import { ProfileScanner } from "./ProfileScanner" @@ -11,10 +11,49 @@ import { StoriesScanner } from "./StoriesScanner" import localize from "../helpers/localize" export class MediaScanner implements Module { + svgSettings: any = null + public getName(): string { return "MediaScanner" } + constructor() { + const svgNS = "http://www.w3.org/2000/svg" + this.svgSettings = document.createElementNS(svgNS, "svg") + this.svgSettings.setAttribute("style", "margin-left: auto; margin-right:auto; display:block;") + this.svgSettings.setAttribute("aria-label", "Optionen") + this.svgSettings.setAttribute("class", "x1lliihq x1n2onr6") + this.svgSettings.setAttribute("color", "rgb(255, 255, 255)") + this.svgSettings.setAttribute("fill", "rgb(255, 255, 255)") + this.svgSettings.setAttribute("height", "24") + this.svgSettings.setAttribute("role", "img") + this.svgSettings.setAttribute("viewBox", "0 0 24 24") + this.svgSettings.setAttribute("width", "24") + + const title = document.createElementNS(svgNS, "title") + title.textContent = "Optionen" + this.svgSettings.appendChild(title) + + const circle = document.createElementNS(svgNS, "circle") + circle.setAttribute("cx", "12") + circle.setAttribute("cy", "12") + circle.setAttribute("fill", "none") + circle.setAttribute("r", "8.635") + circle.setAttribute("stroke", "currentColor") + circle.setAttribute("stroke-linecap", "round") + circle.setAttribute("stroke-linejoin", "round") + circle.setAttribute("stroke-width", "2") + this.svgSettings.appendChild(circle) + + const path = document.createElementNS(svgNS, "path") + path.setAttribute("d", "M14.232 3.656a1.269 1.269 0 0 1-.796-.66L12.93 2h-1.86l-.505.996a1.269 1.269 0 0 1-.796.66m-.001 16.688a1.269 1.269 0 0 1 .796.66l.505.996h1.862l.505-.996a1.269 1.269 0 0 1 .796-.66M3.656 9.768a1.269 1.269 0 0 1-.66.796L2 11.07v1.862l.996.505a1.269 1.269 0 0 1 .66.796m16.688-.001a1.269 1.269 0 0 1 .66-.796L22 12.93v-1.86l-.996-.505a1.269 1.269 0 0 1-.66-.796M7.678 4.522a1.269 1.269 0 0 1-1.03.096l-1.06-.348L4.27 5.587l.348 1.062a1.269 1.269 0 0 1-.096 1.03m11.8 11.799a1.269 1.269 0 0 1 1.03-.096l1.06.348 1.318-1.317-.348-1.062a1.269 1.269 0 0 1 .096-1.03m-14.956.001a1.269 1.269 0 0 1 .096 1.03l-.348 1.06 1.317 1.318 1.062-.348a1.269 1.269 0 0 1 1.03.096m11.799-11.8a1.269 1.269 0 0 1-.096-1.03l.348-1.06-1.317-1.318-1.062.348a1.269 1.269 0 0 1-1.03-.096") + path.setAttribute("fill", "none") + path.setAttribute("stroke", "currentColor") + path.setAttribute("stroke-linejoin", "round") + path.setAttribute("stroke-width", "2") + this.svgSettings.appendChild(path) + } + /** Initialize settings listeners */ private initModalSettingsListeners(el: HTMLElement, program: Program) { const myTabs = document.querySelectorAll("div.nav-tabs > button.nav-link") @@ -22,13 +61,13 @@ export class MediaScanner implements Module { const handleClick = (e: MouseEvent): void => { e.preventDefault() - + myTabs.forEach(t => t.classList.remove("active")) panes.forEach(p => p.classList.remove("show", "active")) - + const target = e.currentTarget as HTMLElement target.classList.add("active") - + const activePaneID = target.getAttribute("data-target") if (activePaneID) { const activePane = document.querySelector(activePaneID) as HTMLElement @@ -60,9 +99,9 @@ export class MediaScanner implements Module { saveFilenameFormatBtn.addEventListener("click", (event: Event) => { event.preventDefault() localStorage.setItem(inputKey, inputFileFormat.value) - this.updateInputButtonStyle(saveFilenameFormatBtn, "index@saved", "btn-primary", "btn-success") + this.updateInputButtonStyle(saveFilenameFormatBtn, "saved", `${program.NAME}-primary`, `${program.NAME}-success`) setTimeout(() => { - this.updateInputButtonStyle(saveFilenameFormatBtn, "index@save", "btn-success", "btn-primary") + this.updateInputButtonStyle(saveFilenameFormatBtn, "save", `${program.NAME}-success`, `${program.NAME}-primary`) }, 1000) }) } @@ -100,35 +139,137 @@ export class MediaScanner implements Module { }).open() } - // private displayModalSettings(result: MediaScanResult, heading: any, bodyStyle: string, buttonList: ModalButton[], program: Program) { - // new Modal({ - // heading: [heading], - // body: [result.modalBody], - // bodyStyle: bodyStyle, - // buttonList: buttonList, - // callback: (_modal, el) => this.initModalSettingsListeners(el as HTMLElement, program), - // }).open() - // } - - /** Handles the case when media is not found */ - // private handleMediaNotFound(program: Program) { - // if (!program.foundByModule && !process.env.DEV) { - // new Modal({ - // heading: [ - // `
    - // [${program.NAME}] - // v${program.VERSION} - //
    ` - // ], - // body: [localize("index@alert_dontFound")], - // bodyStyle: "text-align:center", - // buttonList: [{ active: true, text: "Ok" }] - // }).open() - // } - // } + public handleSettingsButtonClick(program: Program): void { + const createElement = (tag, className = '', attributes = {}, str = '') => { + const el = document.createElement(tag) + if (className) el.className = className + Object.keys(attributes).forEach(attr => el.setAttribute(attr, attributes[attr])) + if (str) el.innerHTML = str + return el + } + + const createListGroupItem = (title, description, settingsName, isLargeInput) => { + const item = createElement('div', 'list-group-item') + const row = createElement('div', 'row align-items-center') + const col = createElement('div', 'col pr-0') + col.appendChild(createElement('strong', 'mb-0', {}, title)) + if (description) col.appendChild(createElement('p', 'text-muted mb-0', {}, description)) + + const colAuto = createElement('div', 'col-auto') + const label = createElement('label', 'slideon') + const input = createElement('input', '', { type: 'checkbox', id: `settings-${settingsName}` }) + const span = createElement('span', 'slideon-slider') + label.appendChild(input) + label.appendChild(span) + colAuto.appendChild(label) + + if (isLargeInput) { + // Create and add a paragraph to the new div + const div = createElement( + 'div', + 'form-group', + { style: 'display: flex; flex-direction: column; align-items: flex-start;' }, + `${title} +

    ${description}

    + + ` + ) + + row.appendChild(div) + } else { + row.appendChild(col) + row.appendChild(colAuto) + } + item.appendChild(row) + + return item + } + + const container = createElement('div', 'container') + const row = createElement('div', 'row justify-content-center') + const col = createElement('div', 'col-12 col-lg-10 col-xl-8 mx-auto') + const my4 = createElement('div', 'my-4') + const nav = createElement('nav') + const navTabs = createElement('div', 'nav nav-tabs', { id: 'nav-tab', role: 'tablist' }) + + navTabs.appendChild(createElement('button', 'nav-link active', { + id: 'nav-general-tab', 'data-toggle': 'tab', 'data-target': '#nav-general', type: 'button', role: 'tab', + 'aria-controls': 'nav-general', 'aria-selected': 'true' + }, `${localize("modalSettingsGeneral")}`)) + navTabs.appendChild(createElement('button', 'nav-link', { + id: 'nav-stories-tab', 'data-toggle': 'tab', 'data-target': '#nav-stories', type: 'button', role: 'tab', + 'aria-controls': 'nav-stories', 'aria-selected': 'false' + }, 'Stories')) + + const tabContent = createElement('div', 'tab-content', { id: 'nav-tabContent' }) + const generalPane = createElement('div', 'tab-pane fade active show', { id: 'nav-general', role: 'tabpanel', 'aria-labelledby': 'nav-general-tab' }) + const storiesPane = createElement('div', 'tab-pane fade', { id: 'nav-stories', role: 'tabpanel', 'aria-labelledby': 'nav-stories-tab' }) + + const items = [ + { title: 'modalSettingsGenTitle1', description: 'modalSettingsGenDesc1', settingsName: 'general-1' }, + { title: 'modalSettingsGenTitle2', description: 'modalSettingsGenDesc2', settingsName: 'general-2' }, + { title: 'modalSettingsGenTitle3', description: 'modalSettingsGenDesc3', settingsName: 'general-3' }, + { title: 'modalSettingsGenTitle4', description: 'modalSettingsGenDesc4', settingsName: 'general-4', isLargeInput: true }, + { title: 'modalSettingsStoriesTitle1', description: 'modalSettingsStoriesDesc1', settingsName: 'stories-1' }, + { title: 'modalSettingsStoriesTitle2', description: 'modalSettingsStoriesDesc2', settingsName: 'stories-2' }, + { title: 'modalSettingsStoriesTitle3', description: 'modalSettingsStoriesDesc3', settingsName: 'stories-3' }, + ] + items.forEach((item) => { + const pane = item.title.includes('Gen') ? generalPane : storiesPane + pane.appendChild(createListGroupItem(localize(item.title), localize(item.description), item.settingsName, item.isLargeInput)) + }) + + tabContent.appendChild(generalPane) + tabContent.appendChild(storiesPane) + + nav.appendChild(navTabs) + + my4.appendChild(nav) + my4.appendChild(tabContent) + my4.appendChild(createElement('div', 'alert alert-warning mt-3', {}, localize("modalSettingsAttention"))) + col.appendChild(my4) + row.appendChild(col) + container.appendChild(row) + + new Modal({ + heading: [ + `
    + ${logo} + ${localize("modalSettingsTitle")} + v${program.VERSION} +
    ` + ], + body: [container], + bodyStyle: null, + buttonList: [{ active: true, text: localize("close") }], + callback: (_modal, el) => { + this.initModalSettingsListeners(el as HTMLElement, program) + } + }).open() + } /** Handles different URL patterns */ private async handleURLPatterns(program: Program): Promise { + if (!program.hostname.includes("instagram.com")) { + new Modal({ + heading: [ + `
    + ${logo} + v${program.VERSION} +
    ` + ], + body: [localize("alertWorksOnlyOn")], + bodyStyle: "text-align:center;padding:20px", + buttonList: [{ active: true, text: "Ok" }], + callback: (_modal, el) => { + el.querySelector(`.${program.NAME}-settings`).addEventListener("click", () => { + this.handleSettingsButtonClick(program); + }) + } + }).open() + return + } + const tests = [ { regex: program.regexStoriesURI, scanner: StoriesScanner }, { regex: program.regexProfilePath, scanner: ProfileScanner }, @@ -145,21 +286,14 @@ export class MediaScanner implements Module { const scannerResult = await scanner.execute(program) if (scannerResult.found) { scannerResult.foundByModule = scanner.getName() - this.displayModal( - scannerResult, + this.displayModal(scannerResult, `
    - @${scannerResult.userName} - - -
    `, + ${logo} + @${scannerResult.userName} + + `, "padding:0!important;text-align:center", - [{ active: true, text: localize("index@close") }], + [{ active: true, text: localize("close") }], (_modal, el) => { if (el.querySelector(".slider")) { const slider = el.querySelector(".slider") @@ -240,119 +374,27 @@ export class MediaScanner implements Module { } el.querySelector(`.${program.NAME}-settings`).addEventListener("click", () => { - const createElement = (tag, className = '', attributes = {}, str = '') => { - const el = document.createElement(tag) - if (className) el.className = className - Object.keys(attributes).forEach(attr => el.setAttribute(attr, attributes[attr])) - if (str) el.innerHTML = str - return el - } - - const createListGroupItem = (title, description, settingsName, isLargeInput) => { - const item = createElement('div', 'list-group-item') - const row = createElement('div', 'row align-items-center') - const col = createElement('div', 'col pr-0') - col.appendChild(createElement('strong', 'mb-0', {}, title)) - if (description) col.appendChild(createElement('p', 'text-muted mb-0', {}, description)) - - const colAuto = createElement('div', 'col-auto') - const label = createElement('label', 'slideon') - const input = createElement('input', '', { type: 'checkbox', id: `settings-${settingsName}` }) - const span = createElement('span', 'slideon-slider') - label.appendChild(input) - label.appendChild(span) - colAuto.appendChild(label) - - if (isLargeInput) { - // Create and add a paragraph to the new div - const div = createElement('div', 'form-group', { style: 'display: flex;flex-direction: column;align-items: flex-start;' }, `${title} -

    ${description}

    - - - `) - row.appendChild(div) - } else { - row.appendChild(col) - row.appendChild(colAuto) - } - item.appendChild(row) - - return item - } - - const container = createElement('div', 'container') - const row = createElement('div', 'row justify-content-center') - const col = createElement('div', 'col-12 col-lg-10 col-xl-8 mx-auto') - const my4 = createElement('div', 'my-4') - const nav = createElement('nav') - const navTabs = createElement('div', 'nav nav-tabs', { id: 'nav-tab', role: 'tablist' }) - - navTabs.appendChild(createElement('button', 'nav-link active', { - id: 'nav-general-tab', 'data-toggle': 'tab', 'data-target': '#nav-general', type: 'button', role: 'tab', - 'aria-controls': 'nav-general', 'aria-selected': 'true' - }, 'General')) - navTabs.appendChild(createElement('button', 'nav-link', { - id: 'nav-stories-tab', 'data-toggle': 'tab', 'data-target': '#nav-stories', type: 'button', role: 'tab', - 'aria-controls': 'nav-stories', 'aria-selected': 'false' - }, 'Stories')) - - const tabContent = createElement('div', 'tab-content', { id: 'nav-tabContent' }) - const generalPane = createElement('div', 'tab-pane fade active show', { id: 'nav-general', role: 'tabpanel', 'aria-labelledby': 'nav-general-tab' }) - const storiesPane = createElement('div', 'tab-pane fade', { id: 'nav-stories', role: 'tabpanel', 'aria-labelledby': 'nav-stories-tab' }) - - const items = [ - { title: 'index#program#modal_settings@settings_general_title_1', description: 'index#program#modal_settings@settings_general_description_1', settingsName: 'general-1' }, - { title: 'index#program#modal_settings@settings_general_title_2', description: 'index#program#modal_settings@settings_general_description_2', settingsName: 'general-2' }, - { title: 'index#program#modal_settings@settings_general_title_3', description: 'index#program#modal_settings@settings_general_description_3', settingsName: 'general-3' }, - { title: 'index#program#modal_settings@settings_general_title_4', description: 'index#program#modal_settings@settings_general_description_4', settingsName: 'general-4', isLargeInput: true }, - { title: 'index#program#modal_settings@settings_stories_title_1', description: 'index#program#modal_settings@settings_stories_description_1', settingsName: 'stories-1' }, - { title: 'index#program#modal_settings@settings_stories_title_2', description: 'index#program#modal_settings@settings_stories_description_2', settingsName: 'stories-2' }, - { title: 'index#program#modal_settings@settings_stories_title_3', description: 'index#program#modal_settings@settings_stories_description_3', settingsName: 'stories-3' }, - ] - items.forEach((item) => { - const pane = item.title.includes('general') ? generalPane : storiesPane - pane.appendChild(createListGroupItem(localize(item.title), localize(item.description), item.settingsName, item.isLargeInput)) - }) - - tabContent.appendChild(generalPane) - tabContent.appendChild(storiesPane) - - nav.appendChild(navTabs) - - my4.appendChild(nav) - my4.appendChild(tabContent) - my4.appendChild(createElement('div', 'alert alert-warning mt-3', {}, localize("index#program#modal_settings@settings_attention"))) - col.appendChild(my4) - row.appendChild(col) - container.appendChild(row) - - new Modal({ - heading: [ - `
    - [${program.NAME}] - ${localize("index#program#modal_settings@title")} - v${program.VERSION} -
    ` - ], - body: [container], - bodyStyle: null, - buttonList: [{ active: true, text: localize("index@close") }], - callback: (_modal, el) => { - this.initModalSettingsListeners(el as HTMLElement, program) - } - }).open() + this.handleSettingsButtonClick(program) }) } ) } else { - // // if (scannerResult.foundMediaObj.mediaType == MediaType.Ad) { - // // return - // // } - // // if (scannerResult.regexProfilePath.test(window.location.pathname)) { - // // this.handleProfilePage() - // // } else { - // // this.handleMediaNotFound(program) - // // } + new Modal({ + heading: [ + `
    + ${logo} + v${program.VERSION} +
    ` + ], + body: [localize("alertNotFound")], + bodyStyle: "text-align:center;padding:20px", + buttonList: [{ active: true, text: "Ok" }], + callback: (_modal, el) => { + el.querySelector(`.${program.NAME}-settings`).addEventListener("click", () => { + this.handleSettingsButtonClick(program) + }) + } + }).open() } } catch (error) { const scanner = new test.scanner() diff --git a/src/modules/Module.ts b/src/modules/Module.ts index a8104a2..68063bc 100644 --- a/src/modules/Module.ts +++ b/src/modules/Module.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ import { Program } from "../App" import { MediaScanResult } from "../model/MediaScanResult" diff --git a/src/modules/ProfileScanner.ts b/src/modules/ProfileScanner.ts index 129b0f7..52ae25a 100644 --- a/src/modules/ProfileScanner.ts +++ b/src/modules/ProfileScanner.ts @@ -21,8 +21,7 @@ export class ProfileScanner implements Module { } const userId = userInfo.data.user.id const userDetails = await fetchDataFromApi({ type: 'getUserFromInfo', userId }) - - if (userDetails && userDetails.user.hd_profile_pic_url_info.url) { + if (userDetails && userDetails.user?.hd_profile_pic_url_info?.url) { return await generateModalBodyHelper(null, userDetails, userName, window.location.href, program) } else { return { found: false, errorMessage: 'Incomplete userDetails received' } diff --git a/src/modules/StoriesScanner.ts b/src/modules/StoriesScanner.ts index 4ae914b..bcd032e 100644 --- a/src/modules/StoriesScanner.ts +++ b/src/modules/StoriesScanner.ts @@ -1,7 +1,7 @@ import { Program } from "../App" import { Module } from "./Module" import { MediaScanResult } from "../model/MediaScanResult" -import { generateModalBody, getElementInViewPercentage } from "../helpers/utils" +import { generateModalBody, getElementWithHighestWidth, traverseReactDOMAndFindHidden } from "../helpers/utils" export class StoriesScanner implements Module { // Returns the name of the module @@ -14,32 +14,36 @@ export class StoriesScanner implements Module { // Find the path element within the SVG that corresponds to the specific pause/play icons const pathElement = Array.from(el.querySelectorAll("path")) .find(p => p.getAttribute("d") === "M15 1c-3.3 0-6 1.3-6 3v40c0 1.7 2.7 3 6 3s6-1.3 6-3V4c0-1.7-2.7-3-6-3zm18 0c-3.3 0-6 1.3-6 3v40c0 1.7 2.7 3 6 3s6-1.3 6-3V4c0-1.7-2.7-3-6-3z") - + if (pathElement) { const buttonElement = pathElement.closest('div[role="button"]') as HTMLElement | null buttonElement?.click() } } - // Find a story that is visible in the viewport - private findVisibleStory(container: HTMLElement): HTMLElement | null { - const sections = Array.from(container.querySelectorAll("section")) - return sections.find(section => getElementInViewPercentage(section))?.querySelector("div > div") - } - - // Find the first visible div element that could potentially contain a story - private findFirstVisibleDiv(container: HTMLElement): HTMLElement | null { - return Array.from(container.getElementsByTagName("div")) - .find(div => div.offsetWidth > 0 && div.offsetHeight > 0) - } - // Check local storage for a pause setting and determine if a story should be paused private shouldPauseStory(program: Program): boolean { return localStorage.getItem(program.STORAGE_NAME + "_settings_stories_2") === "true" } - private async handleHighlights(container: HTMLElement, program: Program): Promise { - const story = this.findVisibleStory(container) + // Find a story that is visible in the viewport + private findCurrentStory(container: HTMLElement): HTMLElement | null { + const sections = Array.from(container.querySelectorAll("section")); + let maxWidthElement: HTMLElement | null = null; + + for (const section of sections) { + const el = getElementWithHighestWidth(section); + if (el) { + maxWidthElement = el; + break; + } + } + + return maxWidthElement; + } + + private async handleHighlightsStories(container: HTMLElement, program: Program): Promise { + const story = this.findCurrentStory(container) if (!story) return null if (this.shouldPauseStory(program)) { @@ -49,7 +53,8 @@ export class StoriesScanner implements Module { } private async handleFeedStories(container: HTMLElement, program: Program): Promise { - const story = this.findFirstVisibleDiv(container) + let story = traverseReactDOMAndFindHidden(container.querySelector('div > div > div')) + story = this.findCurrentStory(story) if (!story) return null if (this.shouldPauseStory(program)) { @@ -61,14 +66,14 @@ export class StoriesScanner implements Module { // Main execution method to process stories based on URL path public async execute(program: Program): Promise { try { - const $container: HTMLElement = document.querySelector("body > div:nth-child(5)") + const $container: HTMLElement = document.querySelector('[id^="mount_"]') if (!$container) { return { found: false, errorMessage: 'No target found.' } } const path = window.location.pathname if (path.startsWith("/stories/highlights/")) { - return await this.handleHighlights($container, program) + return await this.handleHighlightsStories($container, program) } else if (path.startsWith("/stories/")) { return await this.handleFeedStories($container, program) } else { diff --git a/src/modules/Update.ts b/src/modules/Update.ts index a89240a..97488cc 100644 --- a/src/modules/Update.ts +++ b/src/modules/Update.ts @@ -1,6 +1,8 @@ import { Program } from "../App" import { Modal } from "../components/Modal" +import { logo } from "../components/Interconnect" import localize from "../helpers/localize" +import { MediaScanner } from "./MediaScanner" type Changelog = { date: string // Represents the date of the changelog or version release @@ -10,17 +12,23 @@ type Changelog = { export class VersionUpdater { program: Program storageKey: string - constructor(program) { + constructor(program: Program) { this.program = program this.storageKey = `${program.STORAGE_NAME}` } - public async update(localVersion: string): Promise { - if (this.isUpdateNecessary(localVersion)) { - const changelog = await this.fetchChangelog() + public async check(localVersion: string): Promise { + const changelog = await this.fetchChangelog() + const onlineVersion = changelog?.date || localVersion + + this.storeVersionInfo(localVersion, onlineVersion) + + if (this.isUpdateNecessary(localVersion, onlineVersion)) { if (changelog) { this.processChangelog(localVersion, changelog) } + } else { + console.info(`[${this.program.NAME}] No update required`) } } @@ -43,7 +51,6 @@ export class VersionUpdater { const ulHtml = this.generateHtmlListFromText(textBody) const onlineVersion = date - this.storeVersionInfo(localVersion, onlineVersion) console.info(localize("modules.update@update_successful")) if (new Date(onlineVersion) > new Date(localVersion)) { @@ -72,29 +79,36 @@ export class VersionUpdater { window.localStorage.setItem(this.storageKey, JSON.stringify(versionInfo)) } - private isUpdateNecessary(localVersion: string): boolean { + private isUpdateNecessary(localVersion: string, onlineVersion: string): boolean { const data = JSON.parse(window.localStorage.getItem(this.storageKey) || "{}") const installedVersion = new Date(localVersion) - const onlineVersion = new Date(data.onlineVersion) - const isVersionOutdated = onlineVersion > installedVersion + const latestOnlineVersion = new Date(onlineVersion) + const isVersionOutdated = latestOnlineVersion > installedVersion const isDataExpired = Date.now() > data.dateExpiration return isVersionOutdated || isDataExpired || !data } private showUpdateModal(localVersion: string, onlineVersion: string, changelogHtml: string): void { + const mS = new MediaScanner() + new Modal({ - heading: [`
    [${this.program.NAME}]v${localVersion}
    `], + heading: [`
    ${logo}v${localVersion}
    `], body: [`
    Update available v${onlineVersion}
    ${changelogHtml}
    `], - buttonList: [{ active: true, text: "Ok" }] + buttonList: [{ active: true, text: "Ok" }], + callback: (_modal, el) => { + el.querySelector(`.${this.program.NAME}-settings`).addEventListener("click", () => { + mS.handleSettingsButtonClick(this.program) + }) + } }).open() } private informOutdatedVersionInDevConsole(): void { const data = JSON.parse(window.localStorage.getItem(this.storageKey) || "{}") - console.warn(localize("modules.update@consoleWarnOutdatedInfo")) + console.warn(localize("consoleWarnOutdatedInfo")) console.warn( - localize("modules.update@consoleWarnOutdatedInfoVersions") + localize("consoleWarnOutdatedVersions") .replace("${data.version}", data.version) .replace("${data.onlineVersion}", data.onlineVersion) ) diff --git a/stylesheets/stylesheet.css b/stylesheets/stylesheet.css index 372bead..ce8b676 100644 --- a/stylesheets/stylesheet.css +++ b/stylesheets/stylesheet.css @@ -57,7 +57,7 @@ a { color: #fff; text-align: center; background-color: #fd1d1d; - background-image: linear-gradient(45deg, #405de6, #5851db, #833ab4, #c13584, #e1306c, #fd1d1d); + background-image: linear-gradient(45deg,#f6e2d8 0%,#68c2e8 100%); } @media screen and (min-width: 64em) {