Skip to content

Fix Download option missing on your own stories - #200

Open
izadiegizabal wants to merge 2 commits into
ReSo7200:mainfrom
izadiegizabal:fix/self-story-download
Open

Fix Download option missing on your own stories#200
izadiegizabal wants to merge 2 commits into
ReSo7200:mainfrom
izadiegizabal:fix/self-story-download

Conversation

@izadiegizabal

Copy link
Copy Markdown

Downloading your own story wasn't possible — the module's Download row never appeared there, so the only option was Instagram's native Save, which drops the music track from stories that have one. InstaEclipse's downloader grabs the rendered video_version, so the music is preserved.

Tested on Instagram 439.0.0.37.89 (Pixel 8 Pro, Android 16), own + other users' stories, video and photo.

Two independent fixes, one commit each — happy to split into separate PRs if you'd prefer.


1. fix(story) — own-story menu — StoryDownloadHook.java

Instagram builds the self-story option list with a different method than the others'-story one, and dispatches its taps through a different handler:

Menu Builder Options
Your own story 3 args Delete, Archive, Save video, Highlight, Edit AI label, Share, …
Someone else's 1 arg Report, Mute, AI info

The button injector filtered on paramCount(1) and stopped at the first CharSequence[] candidate, so it only ever found the others'-story builder. The click matcher required "explore_viewer" and "friendships/mute_friend_reel/%s/" — Explore-viewer and Mute only exist on someone else's story, so it could never match the self-story dispatcher.

Now every CharSequence[] candidate behind the "[INTERNAL] Pause Playback" anchor is hooked, and every void handler that receives the tapped label. Static methods are deliberately kept as candidates: unlike the post overflow menu, the story dispatchers are static helpers taking the outer class as a parameter.

Resolving the media needed a change too — the self-story dispatcher passes the ReelItem and the Context on separate arguments, so the Context is now looked up across this and every argument rather than on the ReelItem holder alone.

No new toggle; folded into the existing enableStoryDownload. Others'-story behaviour is unchanged.

2. fix(download) — wrong username getter — FeedVideoDownloadHook.java

Found while testing the above, but not story-specific — it affects every download type.

"username".hashCode() (-265713450) matches two zero-arg String getters on com.instagram.user.model.User: the username, and a display-name getter that reads username and falls back to full_name. get(0) picked the latter.

Nothing downstream caught it: UserUtils.isValidUsername() accepts any lowercase string, so an all-lowercase display name passes as a handle. On my account (izadiegizabal, display name izadi) every download — posts, reels, stories, profile pictures — was filed under izadi. Accounts whose display name has capitals or spaces failed the regex and fell through to the fallback scan, which is why this went unnoticed.

The fix drops candidates that also read full_name, leaving the plain username getter. Both field ids are named constants now, with a note that they're just String.hashCode() of the Pando field name and so stay valid across builds. Cache key bumped to _v2 since an existing cache would restore the display-name getter.


Notes

  • StoryMentionHook.java uses the same two anchors with the same take-the-first shape, so View Mentions is likely missing on own stories for the same reason. Left out to keep this diff reviewable.
  • No changes to FeatureFlags, SettingsManager, FeatureManager, FeaturesFragment, strings.xml, Module.java, version.json, or the README.

izadiegizabal and others added 2 commits July 27, 2026 00:42
Instagram builds the option list for your own story with a different
method than the one used for other users' stories, and dispatches its
taps through a different handler. The button injector stopped at the
first CharSequence[] candidate — and filtered on paramCount(1), which
the 3-arg self-story builder could never match — while the click
matcher required "explore_viewer" and "friendships/mute_friend_reel/%s/",
two options that only exist on someone else's story. Neither hook ever
reached the self-story path, leaving only Instagram's native Save,
which drops the music track.

Hook every CharSequence[] candidate behind the "[INTERNAL] Pause
Playback" anchor, and every void handler that receives the tapped
label. Static methods are kept as candidates here: unlike the post
overflow menu, the story dispatchers are static helpers that take the
outer class as a parameter.

Resolving the media also had to change. The self-story dispatcher
passes the ReelItem and the Context on separate arguments, so the
Context is now looked up across 'this' and every argument instead of
on the ReelItem holder alone.

No new toggle — folded into the existing enableStoryDownload flag.
Others'-story behaviour is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VY3pmFCvKrxiKWx8ebaF6k
"username".hashCode() matches two zero-arg String getters on
com.instagram.user.model.User: the username itself, and a display-name
getter that reads username and falls back to full_name. get(0) picked
the latter.

Nothing downstream could catch it — UserUtils.isValidUsername() accepts
any lowercase string, so an all-lowercase display name passes as a
handle. Every download from an account whose display name is lowercase
was filed under that name instead of the username, across posts, reels,
stories and profile pictures.

Drop the candidates that also read full_name; the remaining one is the
plain username getter. Both field ids are named constants now, with a
note that they are just String.hashCode() of the Pando field name and
so stay valid across Instagram builds.

The cache key is bumped to _v2 because an existing cache would restore
the display-name getter, and a cache hit now requires the getter to be
present: without it UserUtils falls back to scanning User for any
lowercase String, which is the same bug by another route.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VY3pmFCvKrxiKWx8ebaF6k
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant