using System.Collections;
using System.Collections.Generic;
public class QuestVideoCompatLoader : MonoBehaviour
public VideoPlayer videoPlayer;
public List<SceneInfo> scenes;
public int activeSceneIndex = 0;
public class SceneInfo { public string video; } // "party" (without .mp4) or "party.mp4"
var videoFile = scenes[activeSceneIndex].video;
if (string.IsNullOrEmpty(videoFile)) return;
StartCoroutine(LoadAndPlayCompat(videoFile));
private IEnumerator LoadAndPlayCompat(string nameOrFile)
#if UNITY_ANDROID && !UNITY_EDITOR
string leaf = Path.GetFileName(nameOrFile);
if (!leaf.EndsWith(".mp4")) leaf += ".mp4";
// 1) Try your OWN app's media dir: /Android/media/<package>/
string ownPath = GetOwnAppMediaPath(leaf); // absolute filesystem path
if (!string.IsNullOrEmpty(ownPath) && File.Exists(ownPath))
PlayUrl("file://" + ownPath);
// 2) Otherwise, search all device videos via MediaStore and play content:// URI
yield return RequestVideoReadPermissionIfNeeded();
string contentUri = AndroidVideoFinder_GetVideoContentUriByFileName(leaf);
if (!string.IsNullOrEmpty(contentUri))
PlayUrl(contentUri); // content://media/external/video/media/12345
Debug.LogWarning($"Video not found: {leaf}. Checked own media dir and MediaStore.");
Debug.Log("Run this on an Android device (Quest).");
private void PlayUrl(string url)
if (videoPlayer == null) { Debug.LogError("VideoPlayer not set."); return; }
videoPlayer.source = VideoSource.Url;
#if UNITY_ANDROID && !UNITY_EDITOR
// ---- Own app media dir (no permission needed for your package) ----
private static string GetOwnAppMediaPath(string fileLeaf)
using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
AndroidJavaObject[] mediaDirs = activity.Call<AndroidJavaObject[]>("getExternalMediaDirs");
if (mediaDirs != null && mediaDirs.Length > 0 && mediaDirs[0] != null)
string basePath = mediaDirs[0].Call<string>("getAbsolutePath"); // /sdcard/Android/media/<pkg>/
if (!string.IsNullOrEmpty(basePath))
// If you keep videos in a subfolder, add it here:
// string basePath = Path.Combine(basePath, "Videos");
return Path.Combine(basePath, fileLeaf);
// ---- Permission helper (needed only for reading other apps' media via MediaStore) ----
private static IEnumerator RequestVideoReadPermissionIfNeeded()
string perm = GetVideoReadPermissionName();
if (UnityEngine.Android.Permission.HasUserAuthorizedPermission(perm))
UnityEngine.Android.Permission.RequestUserPermission(perm);
// Unity has no callback; give one frame to settle (or loop if you prefer).
private static string GetVideoReadPermissionName()
using (var version = new AndroidJavaClass("android.os.Build$VERSION"))
sdk = version.GetStatic<int>("SDK_INT");
return (sdk >= 33) ? "android.permission.READ_MEDIA_VIDEO" : "android.permission.READ_EXTERNAL_STORAGE";
// ---- Minimal MediaStore query by DISPLAY_NAME (case-insensitive) ----
private static string AndroidVideoFinder_GetVideoContentUriByFileName(string fileName)
string leaf = Path.GetFileName(fileName).ToLowerInvariant();
using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
using (var resolver = activity.Call<AndroidJavaObject>("getContentResolver"))
using (var mediaStoreVideo = new AndroidJavaClass("android.provider.MediaStore$Video$Media"))
using (var contentUris = new AndroidJavaClass("android.content.ContentUris"))
AndroidJavaObject externalUri = mediaStoreVideo.GetStatic<AndroidJavaObject>("EXTERNAL_CONTENT_URI");
string[] projection = new[] { "_id" };
string sortOrder = "date_added DESC";
// Exact match on DISPLAY_NAME
using (var cursor = resolver.Call<AndroidJavaObject>(
if (cursor != null && cursor.Call<bool>("moveToFirst"))
int idCol = cursor.Call<int>("getColumnIndexOrThrow", "_id");
long id = cursor.Call<long>("getLong", idCol);
using (var itemUri = contentUris.CallStatic<AndroidJavaObject>("withAppendedId", externalUri, id))
return itemUri.Call<string>("toString");
// Fallback: match by TITLE (filename without extension)
string title = Path.GetFileNameWithoutExtension(leaf);
if (!string.IsNullOrEmpty(title))
using (var cursor = resolver.Call<AndroidJavaObject>(
if (cursor != null && cursor.Call<bool>("moveToFirst"))
int idCol = cursor.Call<int>("getColumnIndexOrThrow", "_id");
long id = cursor.Call<long>("getLong", idCol);
using (var itemUri = contentUris.CallStatic<AndroidJavaObject>("withAppendedId", externalUri, id))
return itemUri.Call<string>("toString");
catch (System.Exception e)
Debug.LogWarning("[QuestVideoCompatLoader] MediaStore query failed: " + e.Message);