Dev Investigation Notes

When video uploads on Android only open Google Photos: accept doesn't filter files, it changes which picker opens

This is a translated article. Read the original in Korean ↗

I was building a web app (formation) for a dance studio. Each person keeps the choreography video on their own phone, and everyone watches it together while the leader controls playback. On Android phones, picking a video file showed the filename, but the Create Room and Join buttons never enabled. Opening the picker either launched Google Photos or just showed a notice saying "클라우드 미디어에 액세스합니다" ("Accesses cloud media") .

Conclusion

If accept lists only image or video MIME types, as in <input type="file" accept="video/*">, Android (Chromium-based browsers) may open the system Photo Picker instead of the regular file picker. The Photo Picker works mainly with the photo library and cloud media, so videos saved from KakaoTalk or sitting in the Downloads folder can be hard to find in it. This behavior can also be confirmed outside the session in the file-selection logic of the Chromium source code.

A common workaround says that listing only file extensions in accept (.mp4,.mov,.webm and so on) switches you to the document/file picker. You can't rely on it. Inside Chromium, extensions are converted back into MIME types through Android's MimeTypeMap, and then the same check runs. Common extensions like .mp4, .mov and .webm turn back into video/mp4 and video/quicktime, so the Photo Picker can still appear. Only two workarounds are confirmed:

<!-- Problem: with only media MIME wildcards, the Photo Picker opens -->
<input type="file" accept="video/*">

<!-- Unreliable: common extensions get converted back to video/* MIME types internally,
     so the Photo Picker may still open -->
<input type="file" accept=".mp4,.mov,.webm">

<!-- Confirmed workaround 1: remove accept entirely -->
<input type="file">

<!-- Confirmed workaround 2: add one MIME type that doesn't start with image/ or video/
     (this breaks the "every entry must start with image/video" condition itself) -->
<input type="file" accept="video/mp4,application/x-not-a-real-type">

If the filename shows up but the button stays disabled, don't touch accept yet. Check this first. In this incident, the button wasn't blocked by accept or the Photo Picker. A separate script file never loaded in the browser, so the global object that script was supposed to define stayed undefined. Right after page load, the inline script threw an exception on that line and stopped completely. The code that runs after file selection, including the button-enabling code, never got a chance to run . The cause was the tracking protection in the Whale browser on Android. It mistook the filename (fingerprint.js) for a browser fingerprinting script and blocked it . So check in this order. Add a debug flag to the end of the URL (here it was ?debug=1) or open the browser console. Then, before you even pick a file, look for an initialization error like "Cannot destructure property ... of ... undefined" that is already logged. If one is there, changing accept won't help. First check whether your shared script filenames match ad- or tracker-blocklist keywords (fingerprint, tracker, analytics, etc.). If one does, rename it and redeploy, and only then try the accept workarounds above. If there's no error, go straight to workaround 1 or 2.

Situation

In this app, the leader creates a room and picks a video file. Participants find their room in the room list, join it, pick the video they prepared, and watch it in sync with the leader's playback position. It started as a local server on a laptop and later moved to a cloud version: people connect through a public URL, and playback signals go over WebSocket . Participants used a mix of Android phones and iPhones, with Chrome, Whale and other browsers, so device- and browser-specific problems came up often.

Symptom

Right after deployment, creating and joining rooms worked on desktop and iPhone. On Android, picking a video only showed the filename, and the Create Room and Join buttons never enabled. The viewer screen stayed on "방 정보를 불러오는 중" ("Loading room info") .

Someone noticed that opening the file picker seemed to launch Google Photos, the default app, right away. Even after clearing the default, notices still appeared: "클라우드 미디어 설정" ("Cloud media settings") and "앱 또는 웹사이트에서 사진이나 동영상을 선택하라고 요청하면 클라우드 미디어에 액세스합니다" ("Accesses cloud media when an app or website asks you to select photos or videos"). This is the standard screen of the system Photo Picker introduced in Android 13 .

Cause

It's confirmed behavior that accept="video/*" launches a "pick a video" intent on Android and opens the Photo Picker by default . The file-selection logic in the Chromium source (SelectFileDialog.java) decides the Photo Picker can be used when every entry in the converted MIME list starts with image/ or video/. The Photo Picker works mainly with the camera roll and cloud media library, so an mp4 received over KakaoTalk and saved to Downloads is hard to find.

The trouble came with the next attempts. Replacing video/* with specific MIME types didn't work . Removing every MIME type and keeping only extensions (.mp4,.mov,.webm,...) also got the report "안돼네" ("still not working") . These failure reports alone don't prove the extension approach is wrong. As it later turned out, a separate shared script called fingerprint.js was not being loaded by Whale at this point, so the whole inline script was dying during initialization. In that state, no accept value could have enabled the button . So the "extension approach failed" result seen in the session was mixed up with a separate failure that had nothing to do with how accept works.

Even so, checking the Chromium source outside the session gives independent evidence that the extension approach was never reliable. convertToSupportedMimeTypes() converts the extensions in accept into MIME types using Android's MimeTypeMap.getMimeTypeFromExtension(), then runs the same "is everything image/video?" check. Standard extensions like .mp4 → video/mp4 and .mov → video/quicktime still meet the condition after conversion, so the Photo Picker can still open. In the other direction, mixing in one MIME type that doesn't start with image/ or video/ breaks the "is every entry a Photo Picker–supported MIME type?" condition, and you may get the regular file picker instead.

Fix

Nothing in this session verifies that changing accept alone solved the problem. What actually fixed things in Whale was renaming fingerprint.js to mediacheck.js, which brought the dead initialization script back to life . After that, at the user's request, accept=".mp4,.m4v,.mov,.webm,.mkv,.avi,.3gp,.ts,.mpeg,.mpg" was put back on the video input and deployed. The session never confirmed whether this value actually opened the picker they wanted (the file picker, not the Photo Picker) .

If you hit the same problem, here's the practical takeaway. If you want users to pick videos from KakaoTalk or the Downloads folder in an Android web app, don't count on listing extensions. Try workaround 1 (remove accept completely) or workaround 2 (add a MIME type that doesn't start with image/ or video/) from the code above first. If you really need to restrict file types, don't filter with accept. Check the selected file's extension and MIME type in JS instead, which keeps that check separate from the Photo Picker problem.

Bonus — the same symptom can have more than one cause

In this incident, "the file picker is acting weird" and "the button won't enable" were really two separate failures. The accept behavior (the Photo Picker) was a real problem, but what directly blocked the button in this session was Whale mistaking the filename fingerprint.js for a tracking script and blocking it. As long as the shared script was dead, no accept fix could enable the button. Check the on-screen log first to see whether script initialization was already failing before any file was picked.

Reporter's Notes — we fixed the filter, so why was it "still not working" every time?

Park Doeun
You know the app where everyone in the studio watches the same choreography video on their own phone? On Android, when I pick a video, the Create Room button doesn't enable. And it's weird that Google Photos pops up.
Jung Bada
I've got a pretty good guess. With accept="video/*", Android opens a photos-only picker. If Google Photos is the default, it jumps straight there.
Jung Haneul
Then why don't files received over KakaoTalk show up? The photo library only has stuff shot with the camera.
Jung Bada
That's an Android system feature called the Photo Picker. It only shows the photo library and cloud media. It can't browse the Downloads folder.
Park Doeun
So can't we drop video/* and just use extensions? With .json the file picker opens just fine.
Jung Haneul
Worth testing. I deployed with only extensions. What do you see now?
Park Doeun
Still not working. It shows the filename and that's it, the button doesn't enable.
Jung Bada
That's weird, extensions alone should drop you into the document picker… Fine, let's just get rid of accept entirely.
Park Dohyun
Hold on, before that. When you say switching to extensions didn't work, do you mean "the picker screen didn't change," or "the file got picked but the button didn't enable"? Those are different problems.
Park Doeun
The second one. I added an on-screen debug panel, and an error is already logged before I even pick a file. It says window.FormationFingerprint is undefined.
Jung Haneul
What's that? What does it have to do with picking files?
Park Dohyun
It's the shared object that computes the video fingerprint (a value used to check whether two files are the same). Without it, the whole script stops on that line right after the page loads. The code after file selection never got a chance to run.
Jung Bada
So… my extension filter change never actually got tested? If the script is dead, no accept value is going to enable the button.
Jung Haneul
Right. Let's revive that dead script first. The file is named fingerprint.js, and I think Whale mistakes that name for a tracking script and blocks it. I'll rename it to mediacheck.js and deploy.
Park Doeun
It works!! The button's enabled!
Park Dohyun
Good, but the filter problem is a separate thing and it's not done yet. Can you be sure extensions alone are safe?
Jung Haneul
No, honestly, this session can't confirm that. The dead script meant we never saw the filter's effect at all. I put the extension filter back and deployed it, but whether it actually opens the picker we want is unconfirmed.
Park Dohyun
Then we can't just say "extensions work" and call it done. When one symptom has two causes, fixing one doesn't verify the other.