Advanced Downloader is a custom Home Assistant integration that greatly extends file downloading capabilities beyond the built-in downloader integration. It downloads, normalizes, and manages media files directly from Home Assistant through simple services — and leverages Video Tools as a dependency for all video processing.
Domain:
advanced_downloader
Advanced Downloader is a full superset of the coredownloaderintegration. If you havedownloader:in yourconfiguration.yaml, a persistent notification will appear at startup reminding you to remove it. After removing it, restart Home Assistant.
- Download files from any URL directly into a configured folder.
- Optional subdirectories and custom filenames.
- Overwrite policy (default or per-call).
- Delete a single file or all files in a directory via services.
- Automatic aspect ratio normalization for downloaded videos (powered by Video Tools).
- Automatic thumbnail generation and embedding for correct video previews in Telegram and mobile players.
- Optional video resizing (width/height) if dimensions differ.
- Persistent status sensor (
sensor.advanced_downloader_status) to track operations (idle/working). - Event support for all processes: download, normalize, thumbnail, resize, and job completion.
- Fully compatible with automations and scripts in Home Assistant.
- Video Tools — required for video processing (aspect normalization, thumbnail embedding, resizing). Install it via HACS before adding Advanced Downloader.
- Home Assistant 2024.1.0 or newer.
- A valid writable directory for storing media files (e.g.,
/mediaor/config/media). ffmpegandffprobemust be installed and available in the system path.- Video Tools installed as a HACS integration.
- Install Video Tools first.
- Download the latest release from GitHub.
- Copy the folder
advanced_downloaderinto:/config/custom_components/advanced_downloader/ - Restart Home Assistant.
- Add the integration from Settings → Devices & Services → Add Integration → Advanced Downloader.
- Install Video Tools via HACS first.
- Go to HACS → Integrations → Custom Repositories.
- Add the repository URL:
https://github.com/Geek-MD/Advanced_Downloader - Select Integration as category.
- Install Advanced Downloader from HACS.
- Restart Home Assistant.
- Add the integration from Settings → Devices & Services → Add Integration → Advanced Downloader.
When adding the integration:
- Base download directory → Absolute path where files will be saved.
- Overwrite → Whether existing files should be replaced by default.
- Default file delete path → Optional fallback for the
delete_fileservice. - Default directory delete path → Optional fallback for the
delete_files_in_directoryservice.
You can modify these settings later via the integration options.
Downloads a file from a given URL. For video files, the following steps are always performed via Video Tools:
- Aspect ratio normalization (
setsar=1,setdar=width/height). - Thumbnail generation and embedding (ensures correct previews in Telegram and other platforms).
- Optional resize if
resize_enabled: true.
| Field | Required | Description |
|---|---|---|
url |
yes | File URL to download. |
subdir |
no | Optional subdirectory under the base directory. |
filename |
no | Optional filename (auto-detected if omitted). |
overwrite |
no | Override default overwrite policy. |
timeout |
no | Timeout in seconds (default 300). |
resize_enabled |
no | If true, resize the video when dimensions mismatch. |
resize_width |
no | Target width for resize (default 640). |
resize_height |
no | Target height for resize (default 360). |
target_aspect_ratio |
no | Target display aspect ratio as decimal (e.g. 1.777 for 16:9). Passed to Video Tools during normalization. Omit to let Video Tools infer from the video's own dimensions. |
- service: advanced_downloader.download_file
data:
url: "https://example.com/video.mp4"
subdir: "ring"
filename: "video.mp4"
resize_enabled: true
resize_width: 640
resize_height: 360Deletes a single file.
If no path is provided, the default UI-configured path will be used.
| Field | Required | Description |
|---|---|---|
path |
no | Absolute path of the file to delete. |
Deletes all files inside a directory.
If no path is provided, the default UI-configured directory will be used.
| Field | Required | Description |
|---|---|---|
path |
no | Absolute path of the directory to clear. |
The integration provides the persistent entity:
sensor.advanced_downloader_status
idle→ No active processes.working→ At least one active process (download, normalize, thumbnail, resize, or delete).
| Attribute | Description |
|---|---|
last_changed |
Datetime when state last changed. |
subprocess |
Current subprocess name (downloading, resizing, file_deleting, dir_deleting). |
active_processes |
List of all currently active subprocesses. |
last_job |
Outcome of the last download job: null (no job yet), success, or failed. |
| Event Name | Triggered When | Data Fields |
|---|---|---|
advanced_downloader_download_completed |
Download finished successfully. | url, path |
advanced_downloader_aspect_normalized |
Video aspect normalized successfully. | path |
advanced_downloader_thumbnail_embedded |
Thumbnail successfully generated and embedded. | path |
advanced_downloader_resize_completed |
Video resized successfully. | path, width, height |
advanced_downloader_resize_failed |
Resize process failed. | path |
advanced_downloader_download_failed |
Download failed. | url, error |
advanced_downloader_job_completed |
Entire workflow completed. | url, path |
- service: advanced_downloader.download_file
data:
url: "https://example.com/camera/video.mp4"
subdir: "ring"
filename: "ring_front.mp4"
resize_enabled: true
resize_width: 640
resize_height: 360
- wait_for_trigger:
- platform: event
event_type: advanced_downloader_job_completed
timeout: "00:05:00"
continue_on_timeout: true
- service: telegram_bot.send_video
data:
target: -123456789
video: "{{ wait.trigger.event.data.path }}"
caption: "New video from Ring (normalized with thumbnail)."If you previously had automations that combined the core Downloader integration
(downloader.download_file) with the standalone Video Tools integration
(video_normalizer.normalize_video), follow this guide to migrate them to
Advanced Downloader, which performs both operations in a single service call.
# 1. Download the file using the core Downloader integration
- action: downloader.download_file
data:
url: "{{ url }}"
subdir: ring
filename: ring.mp4
overwrite: true
# 2. Wait for the download event fired by the core Downloader
- wait_for_trigger:
- trigger: event
event_type: downloader_download_completed
continue_on_timeout: false
# 3. Normalize the video using the standalone Video Tools integration
- action: video_normalizer.normalize_video
data:
overwrite: true
normalize_aspect: true
generate_thumbnail: true
input_file_path: /media/ring/ring.mp4
target_aspect_ratio: 1.777
# 4. Wait for Video Tools to finish (poll the sensor)
- if:
- condition: state
entity_id: sensor.video_normalizer_status
state: idle
then: []
else:
- wait_for_trigger:
- trigger: state
entity_id: sensor.video_normalizer_status
to: idle
# 5. Act on outcome via last_job attribute
- if:
- condition: template
value_template: >-
{{ state_attr('sensor.video_normalizer_status', 'last_job') in
['success', 'skipped'] }}
then:
- action: telegram_bot.send_video
data:
file: /media/ring/ring.mp4
caption: Se detectó movimiento en Entrada.
else:
- action: telegram_bot.send_message
data:
message: Se detectó movimiento en Entrada.# 1. Download + normalize in one call
- action: advanced_downloader.download_file
data:
url: "{{ url }}"
subdir: ring
filename: ring.mp4
overwrite: true
target_aspect_ratio: 1.777 # forwarded to Video Tools
# 2. Wait for job completion (success) or failure
- wait_for_trigger:
- trigger: event
event_type: advanced_downloader_job_completed
- trigger: event
event_type: advanced_downloader_download_failed
continue_on_timeout: false
# 3. Act on outcome via the trigger event type
- if:
- condition: template
value_template: >-
{{ wait.trigger.event.event_type ==
'advanced_downloader_job_completed' }}
then:
- action: telegram_bot.send_video
data:
file: /media/ring/ring.mp4
caption: Se detectó movimiento en Entrada.
else:
- action: telegram_bot.send_message
data:
message: Se detectó movimiento en Entrada.Alternatively, you can check the last_job attribute on
sensor.advanced_downloader_status (values: success or failed) the same
way you previously checked sensor.video_normalizer_status:
- if:
- condition: template
value_template: >-
{{ state_attr('sensor.advanced_downloader_status', 'last_job') ==
'success' }}
then: ...| Old | New |
|---|---|
downloader.download_file |
advanced_downloader.download_file |
video_normalizer.normalize_video |
(built-in — no separate call needed) |
downloader_download_completed event + poll sensor.video_normalizer_status → idle |
advanced_downloader_job_completed event (success) or advanced_downloader_download_failed event (failure) |
sensor.video_normalizer_status state |
sensor.advanced_downloader_status state |
sensor.video_normalizer_status last_job |
sensor.advanced_downloader_status last_job |
MIT License. See LICENSE for details.
See CHANGELOG.md for the full version history.
💻 Proudly developed with GitHub Copilot 🚀