ankidroid / ankidroid/Anki-Android

[BUG]: Video files in [sound:] tags don't load — file:// src blocked by cross-origin policy from http://127.0.0.1 base URL

未关闭
#20,668 13 条评论 0 个 reaction 已指派 1 人 已被 @Ayush-Patel-56 认领 在 GitHub 查看
Can't reproduce
主要语言
Kotlin
星标
11.8k
派生
2.9k
平均合并
2 天 3 小时
30 天内合并 PR
171

描述

### Checked for duplicates?

- This issue is not a duplicate

### Does it also happen in the desktop version?

- This bug does not occur in the latest version of Anki Desktop

### What are the steps to reproduce this bug?

1. Create a card with a video field using `[sound:video.mp4]` syntax
2. Card template example:
```
Front:

{{Snapshot}}

{{Video}}

Back:
{{FrontSide}}



{{Audio}}
```
Where the `Video` field contains `[sound:somefile.mp4]`
3. Open the reviewer to study the card

### Expected behaviour

The video plays automatically when the card loads, as it did in version 2.16.5.

### Actual behaviour

The `` element renders in the HTML but the source fails to load. A broken media icon is displayed with video controls stuck at 0:00. The video cannot be played even by tapping. Audio `[sound:*.mp3]` tags on the back of the card work correctly.

### Root cause

There are two independent problems that together prevent video playback:

**Problem 1: `file://` video source blocked by cross-origin policy**

In `SoundUtils.kt`, the `asHtmlVideo()` function generates an absolute `file:///` URI for the `` element's `src` attribute:

```kotlin
val path = Paths.get(mediaDir.absolutePath, filename).toString()
val uri = getFileUri(path)
// Produces: file:///storage/emulated/0/.../collection.media/video.mp4
```

Since 2.17, card HTML is loaded via `loadDataWithBaseURL(server.baseUrl(), ...)` where the base URL is `http://127.0.0.1:/`. Modern Android WebView blocks `file://` resources requested from an `http://` origin due to cross-origin security restrictions. Images and audio are unaffected because images use relative paths resolved through `ViewerResourceHandler.shouldInterceptRequest()`, and audio plays through the native Android `MediaPlayer` (bypassing the WebView entirely).

**Problem 2: `hasVideoThumbnail` misclassifies short video clips as audio**

Even after fixing the video source URL, `getTagType()` in `SoundUtils.kt` calls `isAudioFileInVideoContainer()` which uses `CompatHelper.compat.hasVideoThumbnail()` to determine if an mp4 file contains video. This check returns false for short video clips (e.g. 3-5 second anime dialogue clips), causing them to be classified as `Type.AUDIO`. They are then played through `SoundTagPlayer.playSound()` which uses `MediaPlayer` without a video surface — so only the audio track plays.

### History

- **2.16.5 and earlier**: Video worked. `[sound:*.mp4]` was detected as video and played through a dedicated `VideoPlayer` Activity with a native `VideoView` and `MediaPlayer.setDisplay()`. This was a fullscreen activity that overlaid the card viewer and finished on completion.
- **2.17**: Switched to `http://127.0.0.1` base URL for cards. Video playback was broken entirely (issue #14693).
- **2.18 (PR #15840)**: Attempted to fix video autoplay by rendering `` HTML elements in the WebView. The `file://` source URL is blocked by cross-origin policy on most devices. The old `VideoPlayer` Activity was removed.
- **2.18 through current (2.24.0alpha12)**: Video remains broken.

### Proposed solution: restore the native VideoPlayer Activity

The HTML `` element approach (PR #15840) is fundamentally incompatible with the `http://127.0.0.1` base URL architecture. The reliable fix is to restore the native `VideoPlayer` Activity from 2.16.5. This requires:

1. **`VideoPlayerActivity.kt`** — A fullscreen `Activity` with a `VideoView`. Uses `MediaPlayer.setDisplay(videoView.holder)` to render video frames and `MediaPlayer.setDataSource()` to play the file. Finishes on completion and calls a completion listener so the next sound/TTS in the queue can play.

2. **`video_player.xml`** — Layout with a centered `VideoView`.

3. **`AndroidManifest.xml`** — Register the new activity.

4. **`SoundTagPlayer.kt`** — Change `playVideo()` to launch `VideoPlayerActivity` with the media file path instead of evaluating JavaScript on a `` element.

5. **`SoundUtils.kt`** — Render video tags as audio play buttons (since the native player handles the visual display). Also fix `getTagType()` to always classify files with video extensions (mp4, webm, mpg, mpeg, mov, mkv) as `Type.VIDEO` without the unreliable `hasVideoThumbnail` check.

I have built and tested this approach on Android 13 with AnkiDroid 2.24.0alpha12 and it works — videos autoplay on card load with both visual and audio, replicating the 2.16.5 behaviour.

### Related issues

- #14693 — 2.17: videos no longer autoplay
- #15840 — feat: autoplay videos (the PR that introduced the HTML `` approach)
- #7760 — `file://` blocked by CORS policy in WebView (same underlying cross-origin issue)
- #16373 — Fetch API cannot load `file://` URL scheme (same underlying cross-origin issue)
- #16407 — Autoplay of videos sometimes causes card answer steps to skip

### Debug info

```
AnkiDroid Version = 2.24.0alpha12
Android Version = 13
```

### Research

- [x] I am reporting a bug specific to AnkiDroid (Android app)
- [x] I have checked the manual and the FAQ and could not find a solution to my issue
- [x] I have confirmed the issue is not resolved in the latest alpha release

贡献指南

打开贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。