Skip to content
911RSPublic

About

πŸ•Œ Tabligh β€” auto-generate cinematic, karaoke-synced Quran reels and publish them to TikTok, Instagram, Facebook & YouTube on a schedule, hands-free.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

Β 

History

72 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Tabligh

πŸ•Œ Tabligh

Ψ¨ΩŽΩ„ΩΩ‘ΨΊΩΩˆΨ§ ΨΉΩŽΩ†ΩΩ‘ΩŠ ΩˆΩŽΩ„ΩŽΩˆΩ’ Ψ’ΩŠΩŽΨ©Ω‹

"Convey from me, even one verse." β€” Prophet Muhammad ο·Ί (Bukhari)

Auto-generate cinematic, karaoke-synced Quran reels and publish them to TikTok, Instagram, Facebook & YouTube β€” on a schedule, hands-free.

Pick nothing. A scheduler chooses a random surah + passage, pulls the exact recitation, renders a vertical video with word-by-word highlighting over a calm background, and posts it several times a day.

License: MIT npm Node TypeScript

🌍 English Β· Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ© Β· FranΓ§ais Β· اردو Β· Bahasa Indonesia Β· TΓΌrkΓ§e Β· Bahasa Melayu Β· বাংলা Β· فارسی Β· EspaΓ±ol

β–Ά See it live: @eQurany on TikTok β€” every video there is auto-generated by this project.

β–Ά Play with sound

classic glass noor
demo-classic.mp4
demo-glass.mp4
demo-noor.mp4

Three built-in templates β€” switch with TEMPLATE or in the panel/menu. Every reel varies its background, and glass gets a unique waveform per video.


✨ Features

  • 🎬 Cinematic 1080Γ—1920 reels β€” full-bleed stock photo background (Pexels/Unsplash) with a strong legibility overlay, subtle drifting particles, and a slow Ken-Burns drift.
  • 🎨 Three visual templates β€” classic (photo + scrim), glass (one persistent frosted glassmorphism card with a live audio waveform), and noor (warm "Divine Light" β€” golden halo + gilded numerals). Set TEMPLATE or switch it in the panel/menu.
  • πŸ•‹ Bismillah intro β€” a passage that starts at ayah 1 always opens with the reciter's own Bismillah (recited, in their voice); optionally prepend it before every passage (BASMALA=always). At-Tawbah and Al-Fatiha are handled correctly.
  • 🎀 Karaoke word-fill β€” each word brightens in sync with the recitation (rightβ†’left), so viewers follow along.
  • πŸ–‹οΈ Authentic Arabic typography β€” full Uthmani text with correct shakl in the clean modern Mada face; headers in Aref Ruqaa calligraphy.
  • 🎯 Exact sync, zero AI β€” audio comes from everyayah.com as per-ayah files, so each ayah's timing is exact and free (no transcription).
  • πŸ”€ Auto content selection β€” random surah + a random consecutive passage (configurable length); short surahs render in full.
  • πŸŒ‡ Safe, tasteful backgrounds β€” a curated 50-keyword pool (mosques, nature, sea, sky…) plus a filter that drops any photo containing people or anything unsuitable. Pick your source: Pexels, Unsplash, or your own local image folder.
  • πŸ–₯️ Interactive command center β€” run tabligh (no arguments) for a stunning terminal UI to generate reels, start/stop the panel, manage the queue, browse history, edit settings, and run a health check. Localized EN/AR/FR.
  • 🎞️ Signature outro β€” the passage fades out and a αΉ£alawāt (with your logo) glides up over the same scene, then the whole video fades to black.
  • πŸ“€ Multi-platform publishing β€” TikTok, Instagram Reels, Facebook Reels and YouTube Shorts via Buffer, toggled per platform by env.
  • πŸŽ›οΈ Self-hosted control panel β€” a password-protected web UI to manage every setting, generate/preview/post a reel, queue passages, and browse history + analytics β€” all live, no redeploy. Localized (EN/AR/FR, full RTL).
  • ⏰ Set-and-forget scheduler β€” an always-on process posts N times a day in your timezone.
  • 🧹 Disk-friendly β€” local files are wiped right after posting; cloud objects are pruned automatically.
  • 🐳 One-container deploy β€” Dockerfile + works great on Coolify, Fly, Railway, or any Docker host.

🧠 How it works

config / random pick
        β”‚
        β–Ό
Quran text + translation  ──►  everyayah per-ayah audio (exact timing)
   (alquran.cloud)                       β”‚
        β”‚                                β–Ό
        └────────────►  TimedAyah[]  ──►  background (Pexels/Unsplash, person-filtered)
                                          β”‚
                                          β–Ό
              Chromium renders animated frames (karaoke, particles, outro)
                                          β”‚
                                          β–Ό
                    ffmpeg β†’ MP4 (1080Γ—1920) + recitation + silent outro
                                          β”‚
                                          β–Ό
              object storage (public URL)  ──►  Buffer  ──►  TikTok / IG / FB / YT
                                          β”‚
                                          β–Ό
                              cleanup (local now, cloud after ingest)

πŸš€ Quick start (local)

Requirements: Node β‰₯ 20 and ffmpeg on your PATH. (Chromium is downloaded automatically by Puppeteer.)

# The short way β€” no clone, no build:
npx tabligh                 # interactive command center
npm i -g tabligh            # or install it properly, then: tabligh render …

# Or from source:
git clone https://github.com/911RS/tabligh.git
cd tabligh
npm install
cp .env.example .env        # fill in what you need (see below)

# Render a specific passage to work/…/reel.mp4 (no publishing)
npm start render -- --surah 112 --from 1 --to 4 --reciter husary --translation en.sahih

# Render a random passage
npm start random

# Everything the CLI can do
npm start

The finished reel.mp4 (plus the timed ir.json) lands in work/<surah>_<range>_<reciter>__<tag>/.


βš™οΈ Configuration

Everything is driven by environment variables (.env). All are optional except where a feature needs a key.

Variable Purpose Default
TEMPLATE Reel visual style: classic / glass / noor classic
BASMALA Bismillah intro: off (only at ayah 1) / always (every passage) off
BACKGROUND_SOURCE auto / pexels / unsplash / local auto
BACKGROUND_LOCAL_DIR Folder of your own portrait images (when source = local) (empty)
PEXELS_API_KEY / UNSPLASH_ACCESS_KEY Stock photo backgrounds (unset β†’ gradient fallback)
BUFFER_ACCESS_TOKEN Buffer API token for publishing (unset β†’ no publishing)
BUFFER_TIKTOK_CHANNEL_IDS Comma-separated TikTok channel ids (empty)
BUFFER_INSTAGRAM_CHANNEL_IDS Instagram Reels channel ids (empty)
BUFFER_FACEBOOK_CHANNEL_IDS Facebook Reels channel ids (empty)
BUFFER_YOUTUBE_CHANNEL_IDS YouTube Shorts channel ids (empty)
MINIO_* S3-compatible storage (public bucket Buffer fetches from) bucket tabligh, port 9000
TZ / PUBLISH_TIMES Timezone + times of day to auto-post Africa/Tunis / 07:00,13:00,19:00
KARAOKE_ENABLED Word-by-word fill synced to recitation true
TEXT_FILL_COLOR Color of the recited (filled) text #ffffff
WATERMARK_ENABLED / WATERMARK_HANDLE Corner logo watermark (assets/logo.png) true / (empty)
FULL_SURAH_MAX_AYAHS Surahs this short render in full 7
RANDOM_MIN_AYAHS / RANDOM_MAX_AYAHS Passage length for random mode 5 / 10
MAX_VIDEO_SECONDS Cap recitation length (excl. outro); trims trailing ayahs to fit (overrides min) 0 (no limit)
RETENTION_DAYS / MINIO_RETENTION_HOURS Cleanup windows 7 days / 24 h
PORT / TRIGGER_TOKEN HTTP server + secret for the trigger endpoint 1998 / (unset β†’ disabled)
PANEL_ENABLED Serve the control panel (false = headless, scheduler only) true
UI_LANG Interactive terminal-menu language (en / ar / fr) en

See .env.example for the full annotated list. These values seed the store on first run only β€” after that, manage settings live in the panel or the tabligh menu.

Reciters: husary, minshawy, abdulbasit, hudhaify, ayyoub, shuraym, husary-muallim, tunaiji β€” or any raw everyayah folder. See src/quran/reciters.ts.

Translations: any alquran.cloud edition id, e.g. en.sahih, fr.hamidullah, or "" for Arabic only.


πŸ–₯️ Interactive command center

Run tabligh with no arguments in a terminal to open the interactive menu β€” a self-contained control center for everything:

Tabligh interactive command center β€” master–detail TUI with a gradient banner, live status, menu and working pane
tabligh                 # opens the menu (in a TTY)
  • Generate a reel β€” random or pick a passage; renders locally (with live progress), then offers to open the video or publish it.
  • Publish now β€” generate + publish in one step.
  • Control panel β€” Start / Stop / Restart the web panel as a background service, open it in your browser, or tail its logs β€” no separate process to babysit.
  • Queue β€” add/remove passages the scheduler plays before random picks.
  • History & analytics β€” totals, by-platform breakdown, recent posts.
  • Settings β€” language, background source (incl. local folder), scheduler on/off, schedule, content, channels, and API keys β€” all applied live.
  • Doctor β€” one-glance health check (ffmpeg, Chrome, keys, storage, disk).

Localized in English / Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ© / FranΓ§ais (set UI_LANG or switch it in Settings). Non-interactive contexts (pipes, Docker, CI) print the classic help instead, so scripting is unaffected. There's also tabligh menu (force it) and tabligh doctor (run just the health check).

πŸŽ›οΈ Web control panel & CLI

Open the app root (http://localhost:1998) for a password-protected panel:

  • First run shows a setup screen to create your password (or run tabligh init for a terminal wizard β€” it now ends by showing your dashboard URL and offering to start the panel).
  • Dashboard β€” status, one-click Generate now / + publish, latest preview.
  • Generate β€” pick a passage or go random, preview before it posts.
  • Settings β€” schedule (timezone + time picker), content (translation, ayah count, max length), branding (karaoke, fill color, particles, animated background, outro promo), platform channel ids, API keys & storage β€” applied live.
  • Queue β€” plan specific passages; the scheduler plays them before random picks.
  • History / Analytics β€” every render + post, totals, by-platform, recent logs.
  • Language β€” switch the panel between English, Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ© (RTL) and FranΓ§ais.

Login is rate-limited (5 tries β†’ 15-min lockout). Reset the password from the server with tabligh set-password <new> (e.g. docker exec <container> tabligh set-password …).

Settings live in the store and persist on the /app/data volume; .env only seeds them on first run.

🌐 Accessing the panel

The app has no domain of its own β€” it just listens on a port (1998 by default, override with PORT) on all interfaces. What URL you open depends on where it runs:

Where it runs URL you open HTTPS?
Your own computer (npm / local) http://localhost:1998 β€” (local, fine)
Cloud VPS, raw port exposed http://<your-server-ip>:1998 ❌ no
VPS behind a reverse proxy https://yourdomain.com βœ… proxy provides it
  • Locally, the setup wizard prints the exact link on start (http://localhost:1998).
  • On a VPS, reaching http://<server-ip>:1998 also requires your firewall / security group to allow inbound 1998.
  • ⚠️ Don't leave the raw port exposed to the internet. The panel serves plain HTTP, so your login password would travel unencrypted. Put it behind a reverse proxy that terminates TLS:
    • Coolify (recommended) β€” set a domain on the app and point it at port 1998; Coolify's Traefik handles routing and a Let's Encrypt certificate automatically.
    • Nginx / Caddy β€” proxy_pass http://127.0.0.1:1998 behind your domain + cert.

The app never needs to know its public domain; the proxy owns the domain and HTTPS and forwards to 1998 internally.


πŸ“€ Publishing

Publishing goes through Buffer, which fans out to every connected platform.

  1. Create a Buffer account and connect your TikTok / Instagram / Facebook / YouTube channels.
  2. Set BUFFER_ACCESS_TOKEN, then run npm start channels to list your channel ids.
  3. Put the ids into the matching BUFFER_*_CHANNEL_IDS vars (any subset β€” TikTok only is fine).
  4. npm start random -- --publish (or let the scheduler do it).

Each platform gets the right format automatically (Reel / Short). The caption includes the surah, ayah range, reciter, photo credit, and hashtags.

Storage β€” you don't need to install MinIO

Object storage is used only for publishing: the reel is uploaded to an S3 bucket so Buffer's servers can fetch it from a public URL. If you only render locally (no publishing), you need no storage at all.

The MINIO_* settings are just standard S3 credentials β€” any S3-compatible provider works, not only MinIO:

Provider Install? Notes
Cloudflare R2 ❌ Free tier + public buckets β€” easiest
AWS S3 / Backblaze B2 / Wasabi / DO Spaces ❌ Cloud bucket + access keys
Self-hosted MinIO βœ… Only worth it on a server with a public domain

⚠️ Local caveat: Buffer fetches over the public internet, so the bucket's MINIO_PUBLIC_URL must be reachable from outside your machine. A MinIO on localhost/your LAN won't work (Buffer can't reach it) β€” use a cloud bucket, or host MinIO behind a public domain (e.g. on the same box as your panel). The app auto-creates the bucket and sets a public-read policy on first publish, and prunes old objects after MINIO_RETENTION_HOURS.


🐳 Deployment (always-on scheduler + control panel)

One command β€” persistence included, nothing to set up:

cp .env.example .env      # fill in your keys (optional β€” you can also do it in the panel)
docker compose up -d      # scheduler + control panel, on http://localhost:1998

That's it. On first boot the app creates its own config store at data/store.json (seeded from your .env) β€” you never create or "link" anything. The bundled docker-compose.yml mounts a named volume at /app/data, so your settings, panel password, queue and history persist across restarts and rebuilds automatically.

serve (the default command) starts:

  • a control panel at / β€” password-protected; manage settings, generate/preview a reel, post now, browse history/analytics, and queue passages. Change anything live, no redeploy.
  • an internal scheduler that renders + publishes at every PUBLISH_TIMES in your TZ;
  • GET /health and a token-secured GET /trigger?key=<TRIGGER_TOKEN> for scripting.

Headless mode: run tabligh serve --no-panel (or set PANEL_ENABLED=false) to keep the scheduler posting while exposing no HTTP surface at all β€” ideal if you only manage the app from the terminal (tabligh menu) and don't want a web panel to secure.

Config lives in the store after first boot (so the panel can edit it live). .env only seeds it once β€” to change things later, use the panel (or tabligh set-password to reset the password). Delete data/store.json to re-seed from .env.


🌍 Public web studio (tabligh web)

Everything above is yours β€” your keys, your accounts, your schedule. The web studio is the opposite: an anonymous, render-only site anyone can use without installing anything. It is what runs at tabligh.cc.

The SPA lives in web/, in this repo. npm run build:all compiles the CLI and builds it into dist/web, which tabligh web then serves.

npm run build:all         # compiles the CLI and builds the SPA into dist/web
tabligh web               # public studio on :1999

docker compose up -d brings up both services from the same image β€” the private panel on 127.0.0.1:1998 and the public studio on :1999.

What makes it safe to expose

The public app is a separate process from the panel, and the separation is structural rather than a matter of checking a flag:

  • It cannot publish. publish: false is hard-coded at the point the job is built. There is no route, parameter or setting that reaches Buffer or your social accounts.
  • It cannot read or write your settings. In compose, /app/data is mounted read-only.
  • It has no session, no login, no cookies β€” so there is nothing to steal or escalate.
  • Every input is validated against a closed list. The reciter must exist in the registry (it becomes part of a URL), the karaoke colour must be a literal hex (it is interpolated into the render page's CSS), and the local-image folder is simply not settable from the web.

Cost, and the limits that follow from it

A reel is rendered frame by frame β€” roughly 25 Puppeteer screenshots per second of recitation, then an x264 encode. That is minutes of pinned CPU per video, and it is the real constraint on the whole thing. The defaults keep a small VPS usable:

Variable Default What it controls
WEB_CONCURRENCY 1 Simultaneous renders. Each pins a Chrome + an ffmpeg.
WEB_MAX_AYAHS 10 Longest passage. The single biggest lever on cost.
WEB_RATE_PER_HOUR 5 Renders per IP per hour.
WEB_QUEUE_MAX 20 Refuse new jobs rather than promise a 40-minute wait.
WEB_JOB_TTL_MINUTES 60 How long a finished MP4 stays downloadable.
WEB_TRUST_PROXY false Honour X-Forwarded-For. Only enable behind a proxy you control.
TURNSTILE_SITE_KEY / TURNSTILE_SECRET empty Optional Cloudflare captcha.

Nothing is stored: the job lives in memory, the file is swept after the TTL, and no request detail is logged or tracked.

The site itself

  • Ten languages (en Β· ar Β· fr Β· es Β· tr Β· id Β· ms Β· ur Β· fa Β· bn) with full RTL, matching the READMEs and the terminal UI.
  • Server-rendered SEO β€” a Vite SPA ships an empty <div>, so the Node app rewrites the built index.html per request: per-locale title/description/canonical, hreflang across all ten locales, OpenGraph/Twitter cards, JSON-LD (WebApplication, FAQPage, HowTo), plus a crawlable copy of the marketing text on first byte. /sitemap.xml and /robots.txt included.
  • SITE_URL sets the canonical origin and the address shown in the outro of every reel.

Reciter photos

The reciter cards fall back to a monogram. Drop <reciter-id>.jpg (e.g. husary.jpg) into web/public/reciters/ and it is picked up automatically β€” no code change. Ids come from src/quran/reciters.ts.

Frontend development

npm run web               # API on :1999
npm run dev:web           # Vite on :5273, proxying /api and /d to :1999

On Coolify / Railway / Fly: point it at this repo. If you deploy the docker-compose.yml, the volume is created for you β€” zero manual steps. If you use the plain Dockerfile, the VOLUME /app/data line makes most platforms persist it automatically; on Coolify you can also add one Persistent Storage mounted at /app/data. Set your env vars and deploy.

First run with no CLI? Just open the panel β€” it shows a setup screen to create your password. Prefer the terminal? Run tabligh init for an interactive wizard.


πŸ—ΊοΈ Roadmap

  • YouTube-source mode (yt-dlp + Whisper) for arbitrary recitations
  • True forced-alignment karaoke (word-accurate) via Quran.com segments
  • De-dup ledger so passages don't repeat until the Mushaf is cycled
  • More layouts / themes

πŸ™ Credits

  • Recitations: everyayah.com Β· Text & translations: alquran.cloud
  • Backgrounds: Pexels / Unsplash (credited in every caption)
  • Fonts: Mada (ayah body), Aref Ruqaa (headers), Reem Kufi (outro), Ubuntu (UI) (SIL OFL / UFL)
  • Rendering: Puppeteer + ffmpeg

πŸ“œ License

MIT β€” do good with it. Please keep recitations and Qur'anic text presented respectfully.

If this helps you spread a good word, ⭐ the repo and check out @eQurany.

About

πŸ•Œ Tabligh β€” auto-generate cinematic, karaoke-synced Quran reels and publish them to TikTok, Instagram, Facebook & YouTube on a schedule, hands-free.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages