flet-media-scanner
A Flet extension for native Android MediaStore integration — save, list, and delete videos in the device Gallery without requiring broad storage permissions.
Built with 100% native Android Kotlin (MediaStore & MediaScannerConnection) and pure Dart Flet service bindings, with zero third-party Flutter plugin dependencies.
Features
- 📱 Android MediaStore Scoped Storage: Save videos directly to
Movies/<Album>withoutWRITE_EXTERNAL_STORAGEon Android 10+ (API 29+). - 🖼️ Instant Gallery Visibility: Media is immediately visible in Google Photos and device Gallery apps via native
ContentResolverpublishing andMediaScannerConnection. - 📋 List & Query: Query videos belonging to your app's custom album.
- 🗑️ Content URI Deletion: Cleanly delete media using Android
content://URIs. - ⚡ Zero Third-Party Dependencies: No external Flutter packages needed.
Requirements
- Platform: Android only (no-op / unsupported on desktop & iOS)
- Android: API 21+ (minSdk 21; Scoped Storage optimized for API 29+)
- Flet:
>= 1.0.0 - Python:
>= 3.10
Installation
pip install flet-media-scanner
Or when developing locally in a workspace:
[tool.flet.extensions]
flet_media_scanner = "flet_media_scanner.Extension"
[tool.flet.dev_packages]
flet-media-scanner = "packages/flet-media-scanner"
Setup
Register the extension in your application's pyproject.toml:
[tool.flet.extensions]
flet_media_scanner = "flet_media_scanner.Extension"
Usage
import flet as ft
from flet_media_scanner import MediaScanner, SaveResult
async def main(page: ft.Page):
# Initialize and register the service
scanner = MediaScanner()
page.services.append(scanner)
page.update()
# 1. Save a downloaded video to Gallery (Movies/MyVideos folder)
result: SaveResult = await scanner.save_video(
file_path="/data/user/0/com.example.app/cache/video.mp4",
file_name="awesome_video.mp4",
album="MyVideos",
)
if result.success:
print(f"Saved successfully: {result.content_uri}")
print(f"Gallery relative path: {result.relative_path}")
else:
print(f"Failed to save: {result.error}")
# 2. List all videos in the custom album
videos = await scanner.list_videos(album="MyVideos")
for v in videos:
print(f"Video: {v['display_name']} | URI: {v['content_uri']}")
# 3. Delete a video by content URI
if result.content_uri:
deleted = await scanner.delete_video(result.content_uri)
print("Deleted:", deleted)
# 4. Trigger legacy media scanner for existing public files
scanned = await scanner.scan_media("/storage/emulated/0/Movies/MyVideos/awesome_video.mp4")
print("Scanned:", scanned)
ft.run(main)
API Reference
MediaScanner (Service)
Inherits from ft.Service. Must be added to page.services before invoking methods.
await save_video(file_path: str, file_name: str | None = None, album: str = "Vidsaver") -> SaveResult
Copies an app-private cache or temp video file into Android MediaStore.Video.Media (Movies/<album>).
file_path(str): Absolute filesystem path to the source video.file_name(str | None): Target file name in the Gallery (defaults to the file basename).album(str): Subdirectory name insideMovies/(defaults to"Vidsaver").- Returns:
SaveResult.
await list_videos(album: str = "Vidsaver") -> list[dict]
Queries all videos previously registered in the specified album.
album(str): Album subfolder insideMovies/.- Returns: List of dictionaries with keys:
display_name(str): File name shown in Gallery.content_uri(str): Androidcontent://URI.mime_type(str): Video MIME type (e.g.video/mp4).relative_path(str): Directory path in storage (e.g.Movies/Vidsaver).size(int): File size in bytes.date_added(int): Unix timestamp added.date_modified(int): Unix timestamp modified.
await delete_video(content_uri: str) -> bool
Deletes a media item from MediaStore using its content:// URI.
content_uri(str): Android MediaStore content URI.- Returns:
Trueif deleted,Falseotherwise.
await scan_media(file_path: str) -> bool
Triggers Android native MediaScannerConnection.scanFile to index a file that already resides in public storage.
file_path(str): Absolute path to the file.- Returns:
Trueif successfully indexed,Falseotherwise.
SaveResult (Dataclass)
| Field | Type | Description |
|---|---|---|
success |
bool |
True if the video was successfully saved and published. |
content_uri |
str |
Android MediaStore content:// URI for the saved media. |
display_name |
str |
Display name of the file in the Gallery. |
mime_type |
str |
MIME type (e.g., video/mp4). |
relative_path |
str |
Destination relative path (e.g., Movies/Vidsaver). |
source_path |
str |
Original source path passed into save_video. |
size |
int |
Size of the saved file in bytes. |
error |
str |
Error description if success is False. |
License
Metadata
Release files for flet-media-scanner 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| flet_media_scanner-1.0.2.tar.gz | 11.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| flet_media_scanner-1.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.6 kB
Release files / flet_media_scanner-1.0.2.tar.gz
| Download URL | flet_media_scanner-1.0.2.tar.gz |
|---|---|
| Size | 11.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
30b9e0466004dceac829c986ba3eced4e8f63a8fee9bc24f311e237e7f996809
|
|
BLAKE2b-256 checksum How to use checksums |
b2193379dd7ce9233cc7356aacdddcaf0e510c50aee3ed3506c905108a4bef6a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.6
|
Release files / flet_media_scanner-1.0.2-py3-none-any.whl
| Download URL | flet_media_scanner-1.0.2-py3-none-any.whl |
|---|---|
| Size | 12.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
465b0d12e0037b0b6ff4d75227a44ff5ebf6f81e5516ec0e9fd7f92815bd3a3f
|
|
BLAKE2b-256 checksum How to use checksums |
d08fbb23bd22348085c141f6e8e5612732af89814645bcbd4a739350e5ef1e47
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.6
|