All notable changes to this project are documented here.
Format follows Keep a Changelog. Versioning follows Semantic Versioning.
-
P0 — Missing
main()entry point. Theif __name__ == "__main__":block inrun_tracker.pyhas been wrapped indef main(argv=None):and called viaraise SystemExit(main()). Thepyproject.tomlentry pointtracker.run_tracker:mainnow resolves correctly. Runninguv run instagram-diffor any installed script invocation no longer raisesAttributeError. -
P0 — Version mismatch.
pyproject.tomlversion corrected from1.0.0to4.0.1to match the CHANGELOG, userscript@version, and all other version references. -
P1 —
append_historyis now atomic. The previous implementation usedopen(..., "a")withflush()+fsync(), which could produce a torn/partial row if the process crashed mid-write between multiple events. The rewrite reads existing rows, merges new events in memory, then writes the full file via_atomic_csv_write(NamedTemporaryFile+Path.replace()). A crash at any point leaves the originalhistory.csvintact. Additionally,load_historynow callsvalidate_csv()and skips malformed rows with a warning, consistent with the defensive pattern already present inload_exportandload_snapshot. -
P1 —
download_pics.pymodule-level side effect removed.os.makedirs(PICS_DIR)was executing unconditionally at import time, creating acache-pfp/directory in the working directory whenever the module was imported. It has been moved insideif __name__ == "__main__":. Importing the module no longer produces filesystem side effects. -
P1 — Module-level globals no longer mutated at runtime.
run_tracker.pypreviously overwrote six module-level constants (FOLLOWERS_CSV,FOLLOWING_CSV,SNAPSHOT_CSV,HISTORY_CSV,OUTPUT_PDF) with CLI argument values at runtime. These are now local variables insidemain(). The module-level constants remain as documented defaults only.generate_report()accepts an explicitoutput_pdfparameter instead of reading from module scope.
.gitattributes— normalizes all text file line endings to LF across platforms, resolving the CRLF/LF inconsistency betweenscraper.user.jsandmain.js.
Engine rewrite — complete ground-up rewrite of floriandiud's main.min.js backbone.
fetch()intercept — Instagram has migrated some endpoints; original only patched XHRBoundedCacheclass — FIFO eviction forlocationNameCache(200) andprofileUsernamesCache(1000)removePatches()— restores originalXHR.prototype.sendandwindow.fetchcleanly_patchActiveguard — prevents patch stacking on reinject or double-initwindow.__igScraperLoadedboot flag — single-init guarantee- IDB failure surfaced to UI — on-screen warning banner if IndexedDB unavailable (private browsing)
{ degraded }return fromAccountStore.init()— caller knows persistence state- Incremental DOM updates in
HistoryLog— one<li>created per entry, deleted individually crypto.randomUUID()— replaces customrandomString(10)URL.revokeObjectURLafter CSV export click — fixes blob URL memory leak
sourceGlobalmutable global eliminated — source derived per-call, passed explicitly (fixes race condition)- Duplicate
addElems + updateCounter + addHistoryLogpattern × 3 collapsed into single_persist(accounts, source) readystatechangelistener: named function, removed viaremoveEventListeneratreadyState === 4- All user-supplied strings rendered via
textContent— eliminates XSS risk in history log - Minimal native IDB wrapper (~80 lines) replaces bundled
idblibrary (~200 lines) - Single
Appclass owns all mutable state — eliminates module-level globals
__awaiter× 2 polyfills — nativeasync/awaitused throughout__restpolyfill — native destructuring used throughout- Bundled
idblibrary — replaced with minimal native wrapper randomString(10)— replaced withcrypto.randomUUID().lastIndex = 0on non-/gregexes — cargo-cult, removed
Scroll panel rewrite — complete ground-up rewrite of SH3LL v2.3 scraper.user.js.
createRegistry()— cleanup registry pattern; all listeners/timers/DOM nodes self-register,flush()removes everything LIFOcreateMachine()— explicit state machine:IDLE → SCROLLING → DONEcreateScroller()— rAF-driven scroll loop with cancellation token (rafId,timerId,cancelledflag)- Scroll-end detection —
scrollHeightstabilization overSTABLE_THRESHOLD = 3consecutive checks - Live progress counter —
Loaded: N,End check N/3,Done: N accounts GM_notificationon scroll complete- SPA navigation handling —
pushState/replaceStatepatched, session tears down and re-injects on profile navigation - Panel re-injection guard — ID check prevents duplicate panels
pagehideteardown — full cleanup on page unloadtrackedTimeout— boot timer tracked in registry, cancelled ifpagehidefires first
position: absolute→position: fixed— panel no longer scrolls off screenpaddingX(invalid CSS) →padding: 5px 11px- All event listeners wrapped in
onEvent(reg, ...)— removed on session teardown @namespaceupdated tohttps://github.com/raldisk/instagram-diff@homepageURLand@supportURLfields added
setInterval+ nestedsetTimeout— replaced by single rAF loopMutationObserver— never disconnected in original; removed entirelyloadScraper/GM_xmlhttpRequestremote fetch — engine now bundled inline- Scrape button — engine boots independently, no manual inject step needed
- Global scope pollution — everything inside IIFE
_atomic_csv_write()—NamedTemporaryFile+Path.replace()for crash-safe writesappend_history()—flush()+os.fsync()for durable appends_setup_font()— cross-platform DejaVuSans discovery (bundled → Linux → Windows → macOS → Helvetica fallback)argparseCLI —--followers,--following,--snapshot,--history,--output,--debug,--no-pdfPermissionError+FileNotFoundErrorhandling on file loads@lru_cache(maxsize=2048)+deepcopy()on_cached_initials_drawing()— avatar caching without Flowable mutationlogging.basicConfig()moved inside__main__—--debugflag works correctly
build_status_map()rewritten with set operations (&,-) — single pass, no repeated membership checks- Explicit
snapshot.keys()instead of implicitset(snapshot)dict iteration - Hardcoded Python 3.12 font path removed —
_setup_font()handles all platforms
tracker/— Python scripts moved hereuserscript/— JS files and userscript READMEtests/— pytest suite (5 test cases covering coredetect_changes()invariants)pyproject.toml— replacesrequirements.txt(PEP 517/518,uvcompatible)CHANGELOG.md— this file
requirements.txt— superseded bypyproject.toml- Flat file layout
- Initial
run_tracker.pyby raldisk - Based on SH3LL v2.3 userscript concept
- Based on floriandiud instagram-users-scraper engine