AI2026Shipped
Naqi: Halal Video Filter
Strips music and censors imagery in a video, entirely on your device.
Naqi filters video on your device. Stem separation removes music while retaining dialogue, and a detector blurs faces or frames at the selected level. It uses no cloud, account, or telemetry, and leaves the original file unchanged. Android is shipped; a Swift rewrite targets iPhone, iPad, and Mac.

Project detailsتفاصيل المشروع
- Role
- Solo: architecture, ML pipeline and both platforms
- Year
- 2026
- Status
- Shipped
- Built with
- Kotlin
- Swift
- ONNX Runtime
- MediaCodec
- OpenGL ES
- AVFoundation
- htdemucs
- NudeNet
- Jetpack Compose
- SwiftUI
- Where to find it
- Google PlaySource
What it is
Many video filters require an upload. Naqi processes video on the device so other parties do not receive it. The app bundles its models; users can choose an optional model download.
Two operations run independently or together. Removing music runs htdemucs stem separation and keeps vocals, or vocals plus other, while drums and bass are never kept. Censoring blurs the faces of a chosen gender for their whole on-screen span, and censors the entire frame while an NSFW classifier gate is firing, with pre-roll so nothing slips through on the frame the detector fired.
The Android app is the reference implementation. I am rebuilding the Apple version in Swift because the Android pipeline depends on MediaCodec, GLES, and ORT-Android.
كثير من الأدوات تُرشِّح الفيديو نيابةً عنك إن رفعته إلى مكان ما. هذه المقايضة مرفوضة تمامًا لمن يريدون هذا أكثر من غيرهم، فجوهر الفكرة ألا يرى أحد آخر ما تشاهده. لذلك يُنفّذ «نقيّ» كل شيء على الجهاز: النماذج داخل التطبيق، والاتصال الشبكي الوحيد تنزيل اختياري للنماذج.
تعمل عمليتان مستقلتين أو معًا. إزالة الموسيقى تُشغّل فصل المسارات htdemucs وتُبقي الأصوات البشرية، أو الأصوات مع «أخرى»، ولا تُبقي الطبول والبيس أبدًا. أما الحجب فيُشوّش وجوه الجنس المختار طوال ظهورها على الشاشة، ويحجب الإطار بأكمله ما دامت بوابة مصنّف المحتوى الإباحي مفعّلة، مع تمهيد زمني حتى لا يفلت شيء في الإطار الذي عمل فيه الكاشف.
تطبيق أندرويد هو التنفيذ المرجعي المُصدَر. أما نسخة Apple فإعادة كتابة كاملة بـ Swift لا نقل: خط المعالجة في أندرويد مرتبط بـ MediaCodec وGLES وORT-Android، ولا شيء من ذلك يعبر.
Challenges
The pipeline does not port
The problem
The obvious plan for the Apple version was to reuse the Android core. It does not survive contact: decode, GPU effects and inference are each welded to an Android-only API. Attempting a shared layer would have produced a lowest-common-denominator abstraction that fought both platforms.
What I did
I accepted the rewrite and moved the shared asset up a level: instead of shared code, the Android build plus its QA clips became the parity suite. The Swift version is correct when its output matches the Android output on the same inputs, which is a far stronger contract than shared source would have given.
A detector that fires late lets frames through
The problem
A classifier decides frame by frame, so it necessarily fires on the frame that already contains what you did not want to see. Filtering only the flagged frames means the user sees the thing, briefly, every single time.
What I did
Detection and rendering are separated by an edit decision list. A first pass builds spans with pre-roll before each detection and hysteresis so a flickering detector does not produce flickering censorship; the second pass renders those spans. The cost is two passes over the video, which is worth it.
Feature-length video on a phone
The problem
Stem separation and per-frame inference over a two-hour file is long enough that the OS will kill the app, the user will switch away, or the battery will run out mid-job. Restarting from zero each time makes the feature unusable.
What I did
Jobs are checkpointed per segment and survive an app kill or a reboot. Processing resumes where it stopped rather than restarting. Progress is reported per stage with a live estimate, so a long job at least looks like a long job instead of a frozen screen.
Being honest about what it cannot do
The problem
The failure mode of this category of app is overpromising. No detector is perfect, and a user who believes the filter is absolute is worse off than one who knows its limits.
What I did
The limits are documented in the README and stated in the app: uncertain detections are censored rather than skipped, an occasionally over-blurred frame is the accepted cost, and the output should be verified before it is relied on. The bias is deliberate and it is written down.
Results
- Shipped on Google Play; Apple rewrite in progress against an Android parity suite.
- Runs fully offline: models bundled, no accounts, no telemetry.
- Resumable jobs survive app kills and reboots.
- صدر على Google Play؛ وإعادة كتابة Apple جارية مقابل مجموعة تكافؤ أندرويد.
- يعمل دون اتصال بالكامل: نماذج مضمّنة، بلا حسابات وبلا تتبّع.
- مهام قابلة للاستئناف تنجو من إغلاق التطبيق وإعادة التشغيل.






