Aggregator drama pendek lintas platform — satu UI, banyak sumber.
Frontend modern mobile-first dengan pengalaman immersive ala DramaBox/TikTok, di-back oleh Flask yang modular dan mudah dikembangkan.
- Ringkasan
- Fitur
- Demo & Tangkapan Layar
- Arsitektur
- Struktur Direktori
- Instalasi Cepat
- Konfigurasi (.env)
- Menjalankan
- Deployment
- API Reference
- Frontend & UX
- Menambah Platform Baru
- Troubleshooting
- Roadmap
- Kontribusi
- License
- Disclaimer
DramSi adalah single-page experience yang menyatukan beberapa platform drama pendek ke dalam satu aplikasi web yang ringan, mobile-first, dan terasa seperti aplikasi native. Backend-nya kecil (Flask + requests) tapi rapi: app factory, blueprint per platform, middleware terpisah, dan proxy stream/subtitle bawaan untuk menyelesaikan masalah CORS dan mixed-content.
Saat ini mendukung tiga platform:
| Platform | Orientasi | ID Internal |
|---|---|---|
| DramaNova | Horizontal | dramanova |
| GoodShort | Vertikal | goodshort |
| DramaBite | Vertikal | dramabite |
- Mode immersive di mobile. Begitu drama dipilih, player mengisi seluruh viewport. Topbar dan bottom nav ikut tersembunyi, jadi nuansanya benar-benar app, bukan halaman web.
- Floating control rail di sisi kanan player: prev episode, daftar episode (badge jumlah), next episode.
- Swipe vertikal untuk ganti episode. Geser ke atas → episode berikutnya, geser ke bawah → episode sebelumnya. Threshold yang tuned supaya tidak salah trigger saat scrub.
- Auto-hide overlay saat playback. Overlay fade out otomatis setelah idle 2.5 detik saat video berputar; tap di area video untuk memunculkan kembali. Saat pause, overlay tetap terlihat.
- Bottom-sheet daftar episode ala app native (slide-up dari bawah) khusus mobile.
- Auto-next episode saat video selesai.
- Auto-fallback proxy. Kalau native playback gagal atau timeout, stream otomatis dialihkan ke proxy internal
/proxy/stream. - Subtitle Indonesia/English (saat tersedia) — SRT di-convert ke WebVTT on-the-fly dengan posisi yang tidak menutupi control bar.
- Object-fit pintar: drama vertikal pakai
cover, drama horizontal pakaicontain.
- Hero carousel dengan autoplay + swipe.
- Rail "Trending" (dengan ranking), "Rilis Baru", dan grid "Untuk Kamu".
- Halaman Discover, Search, Library (riwayat + favorit) — semuanya client-side.
- Multi-bahasa UI: ID / EN / PT / TH dengan persistensi di localStorage.
- PWA-ready: manifest, theme color, dan ikon maskable.
- App factory + blueprint per platform.
- HTTP client global dengan keep-alive dan connection pooling.
- Response envelope konsisten lintas endpoint.
- Generic stream proxy dengan dukungan
Rangeheader (untuk seek). - Subtitle proxy SRT → WebVTT.
- HLS playlist proxy + AES key proxy untuk GoodShort.
- Security headers ringan (
X-Content-Type-Options,Referrer-Policy, dll). - CORS configurable per environment.
- Logging request sederhana (
X-Response-Time+ structured log).
┌─────────────────────┐
│ Browser │
│ (HTML + Vanilla JS) │
└──────────┬──────────┘
│ HTTPS (JSON / HLS / WebVTT)
▼
┌───────────────────────────────────┐
│ Flask App │
│ ┌──────────────────────────────┐ │
│ │ Middleware │ │
│ │ CORS · Security · Errors │ │
│ │ · Observability │ │
│ └──────────────────────────────┘ │
│ ┌──────────────────────────────┐ │
│ │ Blueprints │ │
│ │ /goodshort · /dramabite │ │
│ │ /dramanova · /proxy/* │ │
│ │ / (web frontend) │ │
│ └──────────────────────────────┘ │
│ ┌──────────────────────────────┐ │
│ │ Core (HttpClient · utils) │ │
│ └──────────────────────────────┘ │
└─────────────┬─────────────────────┘
│ requests.Session (keep-alive)
▼
┌────────────────────┐
│ Upstream APIs & │
│ CDN drama pendek │
└────────────────────┘
dramsi/
├── app/
│ ├── __init__.py # Application factory
│ ├── core/
│ │ ├── http.py # HttpClient + global session
│ │ ├── responses.py # Envelope JSON konsisten
│ │ └── utils.py # Helper umum
│ ├── middleware/
│ │ ├── errors.py # Error handler global
│ │ ├── observability.py # Logging + X-Response-Time
│ │ └── security.py # Security headers
│ ├── platforms/
│ │ ├── goodshort/ # service.py · routes.py · hls_proxy.py (AES key)
│ │ ├── dramabite/ # service.py · routes.py
│ │ └── dramanova/ # service.py · routes.py
│ ├── stream_proxy.py # Generic CDN proxy (Range support)
│ ├── subtitle_proxy.py # SRT → WebVTT
│ └── web.py # Frontend routes & PWA manifest
├── templates/ # Jinja2 (base, index, watch, discover, ...)
├── static/
│ ├── css/app.css # Tailwind extension layer
│ ├── js/ # api · state · ui · index · discover · search · library · watch
│ └── img/ # Favicon + PWA icons (SVG)
├── config.py # Environment-aware config
├── run.py # Dev entry (python run.py)
├── wsgi.py # Production entry (gunicorn / Vercel)
├── requirements.txt
├── vercel.json
└── .env.example
Persyaratan: Python 3.10+ dan pip.
git clone https://github.com/hirotomasato/dramsi.git
cd dramsi
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
python run.pygit clone https://github.com/hirotomasato/dramsi.git
cd dramsi
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
copy .env.example .env
python run.pyLalu buka http://localhost:5000.
Salin .env.example ke .env dan sesuaikan. Semua kunci bersifat opsional kecuali FLASK_ENV (memilih profil config) dan PORT (kalau mau ganti port).
| Variabel | Default | Deskripsi |
|---|---|---|
FLASK_ENV |
development |
development atau production |
PORT |
5000 |
Port HTTP saat dijalankan via run.py |
REQUEST_TIMEOUT |
8 |
Timeout (detik) ke upstream API |
APP_CREATOR |
MasantoID |
Nilai field creator di response envelope |
CORS_ORIGINS |
https://dramacina.vip,https://www.dramacina.vip |
Origin yang diizinkan (comma-separated) di production |
CORS_SUPPORTS_CREDENTIALS |
false |
Set true kalau frontend mengirim cookie |
TOKEN_MAIN |
preset | Token upstream bersama (override kalau punya sendiri) |
GS_BASE / DBT_BASE / DNV_BASE |
preset | Override base URL upstream per platform |
Catatan keamanan: jangan commit
.envke git. File ini sudah diabaikan oleh.gitignore.
source .venv/bin/activate
python run.pyAuto-reload Flask aktif saat FLASK_ENV=development.
export FLASK_ENV=production
gunicorn -w 2 -k gthread --threads 4 -b 0.0.0.0:5000 wsgi:appBuat /etc/systemd/system/dramsi.service:
[Unit]
Description=DramSi Gunicorn Service
After=network.target
[Service]
User=www-data
WorkingDirectory=/opt/dramsi
EnvironmentFile=/opt/dramsi/.env
ExecStart=/opt/dramsi/.venv/bin/gunicorn \
--workers 2 --threads 4 -k gthread \
--bind 127.0.0.1:5000 \
--access-logfile - --error-logfile - \
wsgi:app
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetAktifkan:
sudo systemctl daemon-reload
sudo systemctl enable --now dramsi.service
sudo systemctl status dramsi.servicePasang reverse proxy (Nginx/Caddy) di depannya untuk TLS. Stream proxy di /proxy/* butuh koneksi yang bisa long-running, jadi pastikan timeout reverse proxy cukup besar (mis. 60–120 detik).
Sudah ada vercel.json. Push ke GitHub lalu import project di dashboard Vercel.
Limitasi Vercel: function timeout 10 detik (Hobby) / 60 detik (Pro). Endpoint
/proxy/streamdan playlist HLS GoodShort kurang cocok di Vercel untuk file besar. Untuk pengalaman penuh, gunakan VPS.
Semua response JSON menggunakan envelope berikut:
{
"creator": "MasantoID",
"status": true,
"code": 200,
"action": "home",
"source": "goodshort",
"result": { "...": "..." }
}| Method | Path | Query |
|---|---|---|
| GET | /goodshort/home |
page, channel (id / pt / kr / th) |
| GET | /goodshort/search |
q, page |
| GET | /goodshort/detail |
id |
| GET | /goodshort/stream |
id, ep, quality |
| GET | /goodshort/stream_fast |
id, ep, quality |
| GET | /goodshort/unlock |
id, quality |
| GET | /goodshort/playlist |
url, k (HLS proxy + AES key URI rewrite) |
| GET | /goodshort/aeskey |
k (raw 16-byte AES key) |
| Method | Path | Query |
|---|---|---|
| GET | /dramabite/dramas |
lang, page |
| GET | /dramabite/foryou |
lang, page |
| GET | /dramabite/hot |
lang |
| GET | /dramabite/recommend |
lang, page |
| GET | /dramabite/search |
q, lang, limit |
| GET | /dramabite/detail |
id, lang |
| GET | /dramabite/likes |
id, lang |
| GET | /dramabite/episode |
id, ep, lang, quality |
| Method | Path | Query |
|---|---|---|
| GET | /dramanova/dramas |
lang, page, size |
| GET | /dramanova/detail |
id, lang |
| GET | /dramanova/video |
id, ep, lang |
| GET | /dramanova/search |
q, lang |
| GET | /dramanova/modules |
lang |
| GET | /dramanova/recommend |
lang, category, page, size, limit |
| Method | Path | Query |
|---|---|---|
| GET | /proxy/stream |
url (whitelisted CDN, support Range) |
| GET | /proxy/subtitle |
url (SRT → WebVTT, CORS-enabled) |
| Method | Path | Deskripsi |
|---|---|---|
| GET | /health |
Healthcheck { "status": "ok" } |
| GET | /manifest.webmanifest |
PWA manifest |
Vanilla JS murni — tanpa build step. Tailwind di-load via CDN, HLS.js di-load via CDN, dan ikon pakai Lucide.
| File | Tugas |
|---|---|
static/js/state.js |
Bahasa, platform aktif, library/history (localStorage), i18n |
static/js/api.js |
Wrapper fetch + adapter per platform (D.Platforms[*]) |
static/js/ui.js |
Komponen umum: bottom sheet, poster, tab platform |
static/js/index.js |
Beranda: hero carousel + rails |
static/js/discover.js |
Halaman jelajah dengan filter platform |
static/js/search.js |
Pencarian lintas platform |
static/js/library.js |
Riwayat tonton & favorit |
static/js/watch.js |
Player HLS, episode picker, swipe gesture, auto-hide overlay |
| Key | Isi |
|---|---|
dramsi.lang |
id / en / pt / th |
dramsi.platform |
platform aktif |
dramsi.history |
sampai 60 entri terakhir |
dramsi.library |
favorit |
- Buat folder
app/platforms/<nama>/berisi__init__.py,service.py,routes.py. - Implementasi service-nya pakai
HttpClientdariapp.core.http. - Daftarkan blueprint di
app/platforms/__init__.py(tambahkan kePLATFORM_BLUEPRINTS). - Tambahkan adapter ke
static/js/api.js(D.Platforms.<nama>) dan masukkan kePLATFORMSlist distatic/js/state.js. - (Opsional) Sesuaikan
LANG_MAPdistate.jskalau platform pakai kode bahasa berbeda.
Pola adapter di api.js cukup ringkas:
D.Platforms.example = {
id: 'example',
label: 'Example',
orientation: 'horizontal', // atau 'vertical'
home: (page = 1) => D.api(`/example/home?page=${page}`),
detail: (id) => D.api(`/example/detail?id=${encodeURIComponent(id)}`),
stream: (id, ep) => D.api(`/example/episode?id=${id}&ep=${ep}`),
};Video stuck di "Memuat video…"
Browser kemungkinan memblokir mixed-content (HTTPS halaman, HTTP stream). Player akan otomatis mencoba /proxy/stream setelah 8 detik. Pastikan kamu tidak menjalankan di lingkungan yang memblokir endpoint tersebut.
CORS error di production
Set CORS_ORIGINS di .env hanya untuk origin yang diperlukan. Wildcard * tidak dianjurkan saat CORS_SUPPORTS_CREDENTIALS=true.
Subtitle tidak muncul
Cek tab Network. Endpoint /proxy/subtitle?url=... harus respond 200 text/vtt. Kalau upstream blokir IP server kamu, stream subtitle akan kosong.
Video terpotong di drama vertikal
Cek class body.is-vertical aktif. Object-fit untuk vertical adalah cover agar mengisi viewport ala TikTok.
Hot reload tidak jalan
Pastikan FLASK_ENV=development di .env.
- Resume playback dari posisi terakhir per episode
- Lock orientasi landscape otomatis untuk drama horizontal
- Cast / AirPlay button
- Service worker untuk caching shell PWA
- Tema terang (light mode)
- Server-side rendering minimal untuk halaman utama (SEO)
- Adapter platform tambahan
Pull request dipersilakan. Untuk perubahan besar, buka issue dulu untuk diskusi.
- Fork repo ini
git checkout -b feature/nama-fitur- Commit perubahan dengan pesan deskriptif
- Push & buka pull request
Style: ikuti pola yang sudah ada (PEP 8 untuk Python, idiom Vanilla JS yang ringan untuk frontend).
Distributed under the MIT License. See LICENSE for more information.
DramSi adalah proyek pembelajaran dan client agregator. Aplikasi ini tidak meng-host konten apa pun; semua data dan stream berasal dari layanan pihak ketiga. Pengguna bertanggung jawab atas penggunaan sesuai dengan ketentuan layanan platform asal masing-masing dan hukum yang berlaku di wilayah mereka.