Skip to content

kusl/seal

Seal

Video/Audio Downloader for Android

English   |    简体中文   |    繁體中文   |    العربية   |    Portuguese   |    Українська   |    ภาษาไทย   |    فارسی   |    Italiano   |    Azərbaycanca   |    Русский   |    Српски   |    日本語   |    Indonesia   |    हिंदी   |    বাংলা

GitHub release (latest by date) GitHub release (latest by date including pre-releases) Supported-Sites

Important

This is a personal fork of JunkFood02/Seal maintained by @kusl. It diverges from upstream in several ways described below. For the original app, see the upstream repository.


📱 Screenshots


📖 Features

  • Download videos and audio files from platforms supported by yt-dlp (formerly youtube-dl).
  • Embed metadata and video thumbnails into extracted audio files via mutagen.
  • Download entire playlists with one tap.
  • Use embedded aria2c as an external downloader.
  • Embed subtitles into downloaded videos.
  • Execute custom yt-dlp commands using templates.
  • Manage in-app downloads and custom command templates.
  • Material Design 3 UI with dynamic color theming.
  • MAD architecture: pure Kotlin, single activity, no fragments, composable-only navigation.

⬇️ Download

This fork is distributed via Obtainium — add https://github.com/kusl/seal as a source.

For most devices the arm64-v8a APK is the right choice. A universal APK is also produced for other architectures.

Signed releases are built automatically via GitHub Actions on every push to main and published to GitHub Releases.

Note

This fork requires Android 14 or later (minSdk 34). If you need Android 7–13 support, use the upstream release instead.


🔧 Fork-specific Changes

This fork diverges from upstream in the following areas. Changes accumulate across rounds of work; each round preserves all fixes from prior rounds.

Freeze & ANR Diagnosis (Rounds 1–5)

A persistent UI freeze after heavy app-switching — where the app stops responding for 1–4 seconds and recovers, with no logs anywhere — was the main focus of this fork's engineering work.

Root causes identified and fixed, in order:

Round File Fix
1 DownloaderV2.kt Throttled SnapshotStateMap progress writes (500 ms gate + 0.5% delta + 5 s MMKV debounce) to stop flooding Compose recompositions on every yt-dlp callback
2 DownloadPageV2.kt Replaced filteredMap: Map<Task, Task.State> with sortedTasks: List<Task> in derivedStateOf so the list only recomputes on structural state changes, not progress ticks
3 App.kt Fixed a foreground-service binding race: isBound is now tracked synchronously at the call site (@Volatile + serviceLock) instead of inside the async onServiceConnected callback, making startService/stopService genuinely idempotent and ending connection leaks
3 App.kt Hardened startForeground against ForegroundServiceStartNotAllowedException; release VideoInfo references after task completion to keep heap flat over long sessions
4 VideoListViewModel.kt Fixed flowOn operator-ordering bug that was running file-size computation on the main thread
4 FileUtil.kt Added isDocumentUri guard to eliminate spurious SAF NPE Sentry breadcrumbs
5 MainActivity.kt Removed context = this.baseContext — this line overwrote the global App.context (used by Room, FileUtil, service binding) with the Activity's ContextImpl, which holds a back-reference to the Activity via mOuterContext. Every MainActivity recreation leaked the old instance together with its entire Compose composition. Under sustained app-switching pressure, accumulated leaks caused GC churn and progressive freezes. App.context is now assigned exactly once, in App.onCreate(), to the application context.
5 MainThreadWatchdog.kt New file. Sentry's ANRv2 only fires when the OS itself records an ANR (≥ 5 s of ignored input), which these sub-threshold freezes never trigger. The watchdog posts a heartbeat to the main Handler every 500 ms; if the heartbeat hasn't run after 2 s, it captures the main thread's stack trace plus all other threads and writes a report to: (1) logcat (adb logcat -s MainThreadWatchdog), (2) a rotating file at Android/data/com.junkfood.seal/files/watchdog/, and (3) a Sentry MainThreadStallException event that persists to disk and uploads on next launch even if the user force-kills the frozen app

Sentry Crash & ANR Reporting

This build integrates Sentry for automatic crash, ANR, and performance reporting. See the full disclosure in the Crash & ANR Reporting section below.

Android 14+ Only (minSdk 34)

This fork targets Android 14+ (minSdk = 34, compileSdk = targetSdk = 37). Compared with upstream's minSdk = 24 this still removes a large number of SDK version guards that were constant-true or constant-false, while compileSdk/targetSdk 37 keep the build on the current Android 17 platform (a hard requirement of androidx.core 1.19.0). Note that only 64-bit APKs (arm64-v8a, x86_64) are produced — MMKV 2.x ships no 32-bit libraries — so the rare 32-bit-only Android 14 device cannot install this fork.

Dependency Modernisation (June 2026)

All libraries updated to their June 2026 latest-stable releases:

Library Old New
AGP 8.7.2 9.1.1 (new DSL + built-in Kotlin; required for compileSdk 37 — APK naming moved to the release workflow, Sentry plugin 6.x supports AGP 9)
Gradle wrapper 8.11.1 9.5.1
Kotlin 2.0.20 2.4.0 (compiled by AGP 9's built-in Kotlin; the kotlin-android plugin is no longer applied)
KSP 2.0.20-1.0.25 2.3.9 (now independently versioned)
Compose BOM compose-bom-alpha 2025.03.01 compose-bom 2026.05.01 (stable; maps to M3 1.4.x)
material-icons-extended from BOM 1.7.8 pinned (removed from BOM; 1.7.8 is the final release)
Coil 2.5.0 3.4.0 + coil-network-okhttp (network fetcher split out in Coil 3; both artifacts required)
OkHttp 5.0.0-alpha.10 5.4.0 stable (enables Sentry OkHttp instrumentation)
MMKV 1.3.12 2.4.0 (64-bit only; 32-bit APKs no longer produced)
Room 2.6.1 2.8.4
Koin 4.0.0 4.2.1
accompanist-permissions 0.34.0 0.37.3
accompanist-webview / pager-indicators 0.34.0 stays 0.34.0 (removed upstream after that version)
lifecycle / navigation 2.8.7 / 2.8.9 2.10.0 / 2.9.8
foojay resolver 0.4.0 1.0.0
GitHub Actions checkout v4, java v4, android v3, gradle v4, release v2 v6 / v5 / v4 / v6 / v3

CI & Build System

  • Builds happen exclusively via GitHub Actions — there is no local build workflow.
  • Signed APKs are published to GitHub Releases on every push to main using a timestamp-based version scheme (2.0.0-alpha.YYYYMMDD.HHMM).
  • The Gradle daemon heap was raised from 2 GB to 4 GB + 1 GB metaspace to comfortably fit AGP 9.1 + Kotlin 2.4 + R8 full-mode.
  • Parallel builds and build caching are enabled; the setup-gradle action caches across CI runs.
  • 32-bit ABI splits (armeabi-v7a, x86) removed — only arm64-v8a, x86_64, and universal are produced. The surviving ABI version-code offsets are unchanged (arm64=2, x86_64=4) for Obtainium update continuity.

📡 Crash & ANR Reporting (Sentry)

Important

This is a fork-specific addition and is not part of upstream Seal.

To help diagnose stability problems — in particular the UI freeze / ANR that can occur during heavy "copy link → switch app → paste → download" workflows — this build integrates Sentry for automatic crash, ANR, and diagnostic reporting.

What gets collected and sent

  • Crashes (unhandled exceptions and native/NDK crashes) and ANRs, including the OS thread dump and a stack-profile (flamegraph) of the main thread at the time of an ANR.
  • Sub-threshold freeze reports from MainThreadWatchdog (2-second threshold), including the main thread's full stack trace and all other threads at the moment of the stall.
  • Breadcrumbs: a trail of recent app events (the app's own Log.* output, activity lifecycle, scheduler decisions, service bind/unbind timing, and system events).
  • Performance traces for database, file I/O, and OkHttp operations, used to pinpoint slow work on the main thread.
  • A screenshot and a view-hierarchy snapshot captured at the moment an error occurs.
  • Device & app context: model, manufacturer, OS version, ABI, app version, memory/battery/storage/connectivity state, and the bundled yt-dlp version.
  • sendDefaultPii is enabled, meaning events may include your IP address, device name, and whatever is visible on screen at the time of the event (e.g. a URL being downloaded).

Where it goes: the collabs-with-kushal organization's seal project on Sentry's EU-region servers.

Offline behavior: every event is written to the app's cache directory first, then delivered. If the device is offline when a freeze or crash occurs, the report is stored on disk and uploaded automatically on the next launch or when connectivity returns — meaning reports are not lost even if the app is force-killed while frozen.

Builds without telemetry: the F-Droid flavor (fdroid) ships with the DSN blanked out. Sentry is never initialized in that build and nothing is ever collected or transmitted.

How to disable telemetry entirely: build the fdroid flavor, or set SENTRY_DSN in app/build.gradle.kts to "". The on-device crash screen continues to work with or without Sentry.

Disabling just the ANR profiler: set the io.sentry.anr.profiling.sample-rate meta-data value in app/src/main/AndroidManifest.xml to 0.0.

Reading watchdog reports locally: stall reports are also written to Android/data/com.junkfood.seal/files/watchdog/ (readable via any on-device file manager) and to logcat with adb logcat -s MainThreadWatchdog.


🤖 AI Usage Disclosure

A substantial portion of the engineering work in this fork — including the freeze diagnosis, the design and implementation of MainThreadWatchdog, the multi-round refactoring of App.kt and DownloaderV2.kt, the Sentry integration, the dependency version research, and this README — was produced with the assistance of Claude (Anthropic). The human maintainer (@kusl) directed the work, reviewed every change, and made the final decisions on what to apply.

Specific AI-assisted contributions include:

  • Root-cause analysis of the UI freeze across five rounds of debugging (progress throttling, derivedStateOf fix, foreground-service binding race, flowOn operator order, Activity context leak).
  • Design and full implementation of MainThreadWatchdog.kt.
  • Sentry SDK integration and configuration (App.kt, AndroidManifest.xml, app/build.gradle.kts).
  • Dependency upgrade research — all library versions were web-verified against current stable releases by Claude before being written.
  • Refactoring of MainActivity.kt, DownloaderV2.kt, and AsyncImageImpl.kt (Coil 3 migration).
  • Build system modernisation across all Gradle files and GitHub Actions workflows.
  • This README.

Code produced by AI was reviewed for correctness, tested via CI, and is covered by the same GPLv3 license as the rest of the project.


💬 Contact (Upstream)

Join the upstream Telegram Channel or Matrix Space for discussion about the original Seal app.

For issues specific to this fork, open an issue at github.com/kusl/seal.


🤝 Contributing (Upstream)

You can help translate Seal on Hosted Weblate.

Translate status

Note

For submitting bug reports, feature requests, questions, or any other ideas about the upstream app, please read CONTRIBUTING.md first.


⭐️ Star History (Upstream)

Star History Chart


🧱 Credits

Seal is a GUI for yt-dlp, built on youtubedl-android.

Some UI designs and code are adapted from Read You and Music You.

dvd · Material color utilities · Monet


📃 License

GitHub

Warning

Except for the source code licensed under the GPLv3 license, all other parties are prohibited from using Seal's name as a downloader app, and the same is true for Seal's derivatives. Derivatives include but are not limited to forks and unofficial builds.

About

No description, website, or topics provided.

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors