### Setup Status Rendering
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/dashboard.html
Fetches and renders the setup progress card, including dynamic list generation and HTML escaping for safety.
```javascript
async function loadSetupStatus() { const wrap = document.getElementById('setup-card'); if (!wrap) return; try { const res = await fetch('/api/admin/setup-status'); if (!res.ok) return; const data = await res.json(); const d = data.data || {}; const checks = d.checks || []; if (!checks.length || d.complete) { wrap.innerHTML = ''; return; } const rows = checks.map(c => ` ${escapeHtmlSetup(c.label)}${escapeHtmlSetup(c.hint)}`).join(''); const pct = Math.round((d.done / d.total) * 100); wrap.innerHTML = `
Finish setting up
${d.done} of ${d.total} steps done — complete these to go live.
${pct}%
${rows}
`; } catch (e) { /* ignore */ } }
```
--------------------------------
### Clone and Configure Repository
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
Initial setup steps to download the source code and prepare the configuration file.
```bash
git clone https://github.com/weebzone/Telegram-Stremio
cd Telegram-Stremio
cp sample_config.env config.env
nano config.env
```
--------------------------------
### Install Caddy Web Server
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
Commands to install Caddy for automatic HTTPS and reverse proxy functionality.
```bash
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
chmod o+r /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
```
--------------------------------
### Configure Caddyfile
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
Example configuration for the Caddyfile to enable reverse proxying to the application.
```caddy
your-domain.com {
reverse_proxy localhost:8000
}
```
--------------------------------
### Example config.env Configuration
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
A template for the required environment variables in the config.env file.
```env
API_ID="1234567"
API_HASH="abc123def456ghi789jkl012mno345pq"
BOT_TOKEN="1234567890:AAEabcdEFGhijkLMnOPqrsTUVwxyz12345"
USER_SESSION_STRING=""
OWNER_ID="987654321"
DATABASE="mongodb+srv://user:pass@cluster0.xxxx.mongodb.net/tracking,mongodb+srv://user:pass@cluster0.xxxx.mongodb.net/storage1"
PORT="8000"
```
--------------------------------
### Movie Filename Examples
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
Standard naming patterns for movies including title, year, and quality.
```text
Ghosted 2023 720p WEBRip Hindi x265 HEVC.mkv
Oppenheimer.2023.1080p.BluRay.x264.mkv
3 Idiots (2009) 2160p 4K HEVC.mkv
```
--------------------------------
### TV Show Filename Examples
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
Standard naming patterns for TV shows using SxxExx format.
```text
Harikatha Sambhavami Yuge Yuge S01E04 1080p WEB-DL.mkv
Loki.S02E03.720p.HEVC.mkv
Panchayat S03E05 Hindi 1080p.mkv
```
--------------------------------
### Initiate Manual Session
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/tools.html
Collects form data and sends a POST request to the backend to start a manual session for the selected media.
```javascript
async function startManualSession() { if (!msSelected) { showToast('Select a title first.', 'error', 'Session'); return; } const payload = { tmdb_id: msSelected.tmdb_id, db_index: msSelected.db_index, media_type: msSelected.media_type, selected_id: msSelected.selected_id, in_library: msSelected.in_library !== false, }; if (!msSelected.in_library && msSelected.media_type === 'tv') { const rs = document.getElementById('ms-real-season').value.trim(); if (rs !== '') payload.season = rs; } if (msSelected.is_personal) { if (msSelected.media_type === 'tv') { const s = document.getElementById('ms-season').value.trim(); if (s === '') { showToast('Season number is required for personal TV shows.', 'error', 'Session'); return; } payload.season = s; const ep = document.getElementById('ms-episode').value.trim(); if (ep !== '') payload.episode = ep; } const q = document.getElementById('ms-quality').value.trim(); if (q !== '') payload.quality = q; } else if (msSelected.media_type === 'tv') { const rs = document.getElementById('ms-real-season').value.trim(); if (rs !== '') payload.season = rs; } try { const res = await fetch('/api/admin/tools/manual-session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await res.json(); if (!res.ok) throw new Error(data.detail || 'Could not start session'); showToast('Session started.
```
--------------------------------
### Initialization Routines
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/tools.html
Initializes session data, channel lists, and starts polling for scan, database, and duplicate statuses if the corresponding UI elements indicate they are running.
```javascript
loadManualSession(); loadChannels(); pollScan().then(() => { const pill = document.getElementById('scan-status-pill'); if (pill.classList.contains('status-running')) startScanPolling(); }); pollDbc().then(() => { const pill = document.getElementById('dbc-status-pill'); if (pill.classList.contains('status-running')) startDbcPolling(); }); pollDup().then(() => { const pill = document.getElementById('dup-status-pill'); if (pill.classList.contains('status-running')) startDupPolling(); }); if (document.getElementById('ba-body')) loadBotAdmin();
```
--------------------------------
### Manage Manual Sessions
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/tools.html
Functions to start, end, load, and render the UI state for manual sessions via the admin API.
```javascript
async function endManualSession() { try { const res = await fetch('/api/admin/tools/manual-session', { method: 'DELETE' }); const data = await res.json(); if (!res.ok) throw new Error(data.detail || 'Could not end session'); showToast('Session ended.', 'info', 'Session'); renderManualSession(null); } catch (e) { showToast(e.message || 'Could not end session.', 'error', 'Session'); } }
```
```javascript
function renderManualSession(session) { const pill = document.getElementById('ms-status-pill'); const active = document.getElementById('ms-active'); const setup = document.getElementById('ms-setup'); if (session && session.tmdb_id !== undefined && session.tmdb_id !== null) { pill.className = 'status-pill status-running'; pill.textContent = 'Active'; active.classList.remove('hidden'); setup.classList.add('hidden'); const isTv = session.media_type === 'tv'; document.getElementById('ms-active-title').textContent = `${session.title || 'Title'}${session.year ? ' (' + session.year + ')' : ''}`; let detail = isTv ? 'TV Show' : 'Movie'; if (session.is_personal) { detail += ' · Personal'; if (isTv) { detail += ` · Season ${session.season}`; detail += (session.episode !== null && session.episode !== undefined) ? ` · Episode ${session.episode} (all files → this episode)` : ' · each file → next episode'; } else { detail += ' · each file → a new quality'; } detail += ' · forward to MANUAL channel only'; } else { detail += ' · TMDB/IMDb · auto-detected from each file'; if (isTv && session.season !== null && session.season !== undefined) { detail += ` · fallback Season ${session.season}`; } detail += ' · forward to AUTH or MANUAL channel'; } document.getElementById('ms-active-detail').textContent = detail; } else { pill.className = 'status-pill status-idle'; pill.textContent = 'No session'; active.classList.add('hidden'); setup.classList.add('hidden'); document.getElementById('ms-results').style.display = 'none'; document.getElementById('ms-selected-box').classList.add('hidden'); document.getElementById('ms-search-state').style.display = 'none'; msSelected = null; } }
```
```javascript
async function loadManualSession() { try { const res = await fetch('/api/admin/tools/manual-session'); const data = await res.json(); renderManualSession(data.session); } catch (e) { /* ignore */ } }
```
--------------------------------
### Initializing the Configuration File
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
Commands to copy the sample configuration file and open it for editing.
```bash
cp sample_config.env config.env
nano config.env
```
--------------------------------
### Deploy with Plain Docker
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
Manual deployment method using standard Docker commands.
```bash
docker build -t telegram-stremio .
docker run -d -p 8000:8000 telegram-stremio
```
--------------------------------
### Season Pack Filename Examples
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
Naming patterns for files containing multiple episodes or entire seasons.
```text
One Piece S01 E01-E13 1080p.mkv → Season 1, Episodes 1–13
Naruto.S02.E14-26.Combined.720p.mkv → Season 2, combined batch
Friends S03 1080p.mkv → whole Season 3 (no episode number)
```
--------------------------------
### Initialize Page Components
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/media_edit.html
Sets up event listeners and initializes UI elements like catalog dropdowns and collapsible season content on DOM load.
```javascript
document.addEventListener('DOMContentLoaded', () => { const select = document.getElementById('custom-catalog-select'); if (select) select.addEventListener('change', updateCatalogDropdownStatus); loadMediaCatalogDropdown(); loadMediaVisibilityDeps(); document.querySelectorAll('.season-content').forEach((content, index) => { if (index > 0) { content.classList.add('season-collapsed'); content.style.maxHeight = '0px'; content.style.opacity = '0'; const arrow = document.getElementById(`season-arrow-${index}`); if (arrow) arrow.style.transform = 'rotate(-90deg)'; } else { content.style.maxHeight = content.scrollHeight + 'px'; content.style.opacity = '1'; } }); });
```
--------------------------------
### Movie Subtitle Filename Examples
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
Naming conventions for movie subtitles using title/year or IMDb ID followed by the language.
```text
Wanted 2008 english.srt
Wanted 2008 ar.srt
tt3326054 arabic.srt
tt3326054 eng.srt
```
--------------------------------
### Deploy with Docker Compose
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
Recommended method for deployment using Docker Compose for easier maintenance.
```bash
docker compose up -d
```
--------------------------------
### TV Episode Subtitle Filename Examples
Source: https://github.com/weebzone/telegram-stremio/blob/master/README.md
Naming conventions for TV episode subtitles using title/IMDb ID, season/episode markers, and language.
```text
Sniffer S01E01 hindi.srt
The.Sniffer.S01E01.arabic.srt
tt3326054 S01E01 eng.srt
tt3326054 S01E01 ar.srt
```
--------------------------------
### Initialize Subtitle and Tab Logic
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/media_edit.html
Handles tab switching based on URL hash and triggers subtitle loading upon DOM content readiness.
```javascript
['details', 'content', 'subtitles', 'catalog', 'rescan']; const hash = (location.hash || '').replace('#', ''); if (valid.includes(hash)) { switchEditTab(hash, false); } else { refreshVisibleSeasonHeights(); } }); document.addEventListener('DOMContentLoaded', () => { const backLink = document.getElementById('back-to-list'); if (backLink) backLink.href = manageListUrl(); if (document.getElementById('subtitle-list')) { loadSubtitleLanguages().then(loadSubtitles); } }); {% endblock %}
```
--------------------------------
### Load Channels
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/tools.html
Fetches and displays the list of configured AUTH channels.
```javascript
async function loadChannels() { const list = document.getElementById('channel-list'); try { const res = await fetch('/api/admin/tools/channels'); const data = await res.json(); const channels = (data && data.data) || []; if (!channels.length) { list.innerHTML = `
No AUTH channels configured.
```
--------------------------------
### Render Bot List and Channel Configuration
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/tools.html
Renders a list of bots with main status indicators and handles the display of configured channels or fallback hints.
```javascript
block">${escapeHtml(b.name)}${b.is\_main ? ' (main)' : ''} ${b.username ? '@' + escapeHtml(b.username) : escapeHtml(String(b.user\_id))} ").join('') || 'No bots detected.'; const wrap = document.getElementById('ba-channels'); if (!channels.length) { wrap.innerHTML = `
No bot-served channels configured.
```
--------------------------------
### Media Visibility and Token Picker
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/media_edit.html
Functions to load visibility settings and render a UI component for selecting access tokens.
```javascript
async function loadMediaVisibilityDeps() { try { const [sRes, tRes] = await Promise.all([ fetch('/api/admin/settings'), fetch('/api/admin/access/tokens') ]); const sData = await sRes.json().catch(() => ({})); const tData = await tRes.json().catch(() => ({})); meSubscriptionOn = !!(sData.settings && sData.settings.subscription); meTokens = (tData.tokens || []).filter(t => t.token); } catch (e) {} const opt = document.querySelector('#media-visibility option[value="public"]'); if (opt) opt.textContent = mePublicLabel(); await loadMediaVisibility(); }
```
```javascript
function renderMediaTokenPicker(selected) { const box = document.getElementById('media-token-picker'); if (!box) return; const sel = new Set(selected || []); if (!meTokens.length) { box.innerHTML = '
No users/tokens found. Create access tokens on the Access page first.
'; return; }
```
--------------------------------
### Dashboard Initialization
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/admin_dashboard.html
Initializes dashboard data fetching and sets up periodic refresh intervals.
```javascript
document.addEventListener('DOMContentLoaded', () => { fetchStats(); fetchHealthReport(); fetchAnalytics(); fetchDeadLinks(); setInterval(fetchStats, 5000); // realtime bot load setInterval(fetchHealthReport, 20000); // infra checks (cached server-side) setInterval(fetchAnalytics, 15000); }); {% endblock %}
```
--------------------------------
### Data Loading Functions
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/access_manage.html
Fetches access tokens and subscription plans from the backend API and updates the UI.
```javascript
async function loadTokens() { document.getElementById('token-table-body').innerHTML = `
`; } function expiryHtml(t) { if (t.never_expires) return `Never expires`; if (t.expires_at) return `${fmtDate(t.expires_at)}`; return `—`; }
```
--------------------------------
### Manage Subscription and Token Operations
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/access_manage.html
Functions to handle token lifecycle, including creation, limit updates, revocation, and user linking.
```javascript
{ const name = document.getElementById('create-name').value.trim(); if (!name) { showToast('Token name is required.', 'error', 'Validation'); return; } const payload = { name, daily_limit_gb: Number(document.getElementById('create-daily').value || 0), monthly_limit_gb: Number(document.getElementById('create-monthly').value || 0), subscription_exempt: document.getElementById('create-lifetime').checked, }; try { const resp = await fetch('/api/tokens', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (resp.ok) { showToast('Token created.', 'success', 'Created'); closeModal('create-modal'); loadTokens(); } else { const d = await resp.json(); showToast(d.detail || 'Failed to create token.', 'error', 'Failed'); } } catch (err) { showToast('Error: ' + err.message, 'error', 'Network Error'); } } function openLimitsModal(token, daily, monthly) { document.getElementById('limits-token').value = token; document.getElementById('limits-daily').value = daily || 0; document.getElementById('limits-monthly').value = monthly || 0; openModal('limits-modal'); } async function saveLimits() { const token = document.getElementById('limits-token').value; const payload = { daily_limit_gb: Number(document.getElementById('limits-daily').value || 0), monthly_limit_gb: Number(document.getElementById('limits-monthly').value || 0), }; try { const resp = await fetch('/api/tokens/' + encodeURIComponent(token), { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (resp.ok) { showToast('Limits updated.', 'success', 'Updated'); closeModal('limits-modal'); loadTokens(); } else { showToast('Failed to update limits.', 'error', 'Failed'); } } catch (err) { showToast('Error: ' + err.message, 'error', 'Network Error'); } } async function revokeToken(token) { if (!confirm('Delete this token? The user will lose addon access immediately.')) return; try { const resp = await fetch('/api/admin/access/tokens/' + encodeURIComponent(token), { method: 'DELETE' }); if (resp.ok) { showToast('Token deleted.', 'success', 'Access Updated'); loadTokens(); } else { const d = await resp.json(); showToast(d.detail || 'Failed.', 'error', 'Delete Failed'); } } catch (err) { showToast('Error: ' + err.message, 'error', 'Network Error'); } } async function linkUser(token) { const userId = prompt('Enter the Telegram User ID to link this token to:'); if (!userId || isNaN(parseInt(userId))) { showToast('Invalid user ID.', 'error', 'Invalid Input'); return; } try { const resp = await fetch('/api/admin/access/tokens/' + encodeURIComponent(token) + '/link-user', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user_id: parseInt(userId) }), }); if (resp.ok) { showToast(`Token linked to user ${userId}.`, 'success', 'User Linked'); loadTokens(); } else { const d = await resp.json(); showToast(d.detail || 'Failed to link.', 'error', 'Link Failed'); } } catch (err) { showToast('Error: ' + err.message, 'error', 'Network Error'); } } function openAssignModal(userId, userName, token) { document.getElementById('assign-user-id').value = userId || ''; document.getElementById('assign-token').value = token || ''; document.getElementById('assign-user-label').textContent = userId ? `User: ${userName} (ID: ${userId})` : `Token: ${userName}`; document.getElementById('assign-custom-days').value = ''; document.getElementById('assign-plan-select').value = ''; //----- With subscription off there are no plans — just set an expiry document.getElementById('assign-plan-field').style.display = subEnabled ? '' : 'none'; document.getElementById('assign-modal-title').textContent = subEnabled ? 'Assign Subscription Plan' : 'Set Expiry'; document.getElementById('assign-days-label').textContent = subEnabled ? 'Or enter custom days' : 'Expires in (days)'; document.getElementById('assign-days-hint').textContent = subEnabled ? 'Custom days override the selected plan.
```
--------------------------------
### Subscription Management JavaScript Utilities
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/subscriptions_manage.html
Helper functions for formatting dates, generating user display names, and rendering status badges.
```javascript
let allUsers = []; let subPage = 1; function formatExpiry(expiryValue) { if (!expiryValue) return 'N/A'; const d = new Date(expiryValue); return d.toLocaleDateString('en-IN', { timeZone: 'Asia/Kolkata', day: '2-digit', month: 'short', year: 'numeric' }) + ' ' + d.toLocaleTimeString('en-IN', { timeZone: 'Asia/Kolkata', hour: '2-digit', minute: '2-digit' }); } function getUserDisplayName(u) { let name = u.first_name || ''; if (u.username) name += (name ? ' ' : '') + `(@${u.username})`; if (!name) name = `User ${u._id}`; return name; } function getStatusBadge(status) { return status === 'active' ? ' Active' : ' Expired'; } function getAdminBadge(u) { return u.is_admin ? ' Owner' : ''; } async function fetchPlans() { const container = document.getElementById('plans-container'); container.innerHTML = `
Loading plans...`
```
--------------------------------
### Refresh Settings from Server
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/settings.html
Fetches current settings from the API and triggers the UI render process.
```javascript
async function refreshSettingsFromServer() { try { const res = await fetch('/api/admin/settings'); if (!res.ok) return; const data = await res.json(); renderSettings(data.settings || {}); } catch (err) { console.error('Failed to refresh settings UI:', err); } }
```
--------------------------------
### Manage Subscription Plans
Source: https://github.com/weebzone/telegram-stremio/blob/master/Backend/fastapi/templates/subscriptions_manage.html
Functions to fetch, render, and manipulate subscription plan data via API calls.
```javascript
`; try { const res = await fetch('/api/admin/subscriptions/plans'); if (!res.ok) throw new Error('Failed to fetch plans'); const data = await res.json(); const plans = data.data || data.plans || []; if (!plans.length) { container.innerHTML = `