Metadata-Version: 2.4
Name: flet-media-scanner
Version: 1.0.2
Summary: Flet extension for Android MediaStore — scan, save, list, and delete videos in the Gallery
Author-email: Fazi-Gondal <nextinpk@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/fazi-gondal/flet-media-scanner
Project-URL: Repository, https://github.com/fazi-gondal/flet-media-scanner
Project-URL: Bug Tracker, https://github.com/fazi-gondal/flet-media-scanner/issues
Keywords: flet,android,media,mediascanner,gallery,flutter
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.14
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: flet>=1.0
Dynamic: license-file

# flet-media-scanner

A [Flet](https://flet.dev) 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>` without `WRITE_EXTERNAL_STORAGE` on Android 10+ (API 29+).
- 🖼️ **Instant Gallery Visibility**: Media is immediately visible in Google Photos and device Gallery apps via native `ContentResolver` publishing and `MediaScannerConnection`.
- 📋 **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

```bash
pip install flet-media-scanner
```

Or when developing locally in a workspace:

```toml
[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`:

```toml
[tool.flet.extensions]
flet_media_scanner = "flet_media_scanner.Extension"
```

---

## Usage

```python
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 inside `Movies/` (defaults to `"Vidsaver"`).
- **Returns**: [`SaveResult`](#saveresult-dataclass).

#### `await list_videos(album: str = "Vidsaver") -> list[dict]`
Queries all videos previously registered in the specified album.
- **`album`** *(str)*: Album subfolder inside `Movies/`.
- **Returns**: List of dictionaries with keys:
  - `display_name` *(str)*: File name shown in Gallery.
  - `content_uri` *(str)*: Android `content://` 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**: `True` if deleted, `False` otherwise.

#### `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**: `True` if successfully indexed, `False` otherwise.

---

### `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

[MIT](LICENSE)
