Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ dmypy.json
*~
.DS_Store

# Node.js / Frontend
node_modules/
.vite/

# Swift Package Manager (macOS app)
EXStreamTVApp/.build/

Expand Down
4 changes: 2 additions & 2 deletions exstreamtv/database/models/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Template Database Models

Defines Template, TemplateGroup, and TemplateItem for scheduling templates.
ErsatzTV feature: reusable schedule patterns.
Reusable schedule patterns for programming automation.
"""

from datetime import time
Expand Down Expand Up @@ -42,7 +42,7 @@ class Template(Base, TimestampMixin):
"""
Schedule template for reusable programming patterns.

ErsatzTV feature: define a day's programming once, apply to multiple days.
Define a day's programming once, then apply to multiple days.
"""

__tablename__ = "templates"
Expand Down
12 changes: 6 additions & 6 deletions exstreamtv/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
EXStreamTV Main Application

FastAPI application entry point combining StreamTV and ErsatzTV features.
FastAPI application entry point — unified IPTV streaming platform.
"""

import asyncio
Expand Down Expand Up @@ -406,7 +406,7 @@ def create_app() -> FastAPI:
"""
app = FastAPI(
title="EXStreamTV",
description="Unified IPTV streaming platform combining StreamTV and ErsatzTV",
description="Unified IPTV streaming platform",
version=__version__,
lifespan=lifespan,
docs_url="/api/docs",
Expand Down Expand Up @@ -522,7 +522,7 @@ async def root_lineup():
except ImportError as e:
logger.warning(f"HDHomeRun router not available: {e}")

# Migration router for ErsatzTV/StreamTV imports
# Migration router for legacy imports
try:
from exstreamtv.api import migration_api
app.include_router(migration_api.router, prefix="/api/migration", tags=["Migration"])
Expand Down Expand Up @@ -997,7 +997,7 @@ async def test_stream_page(request: Request):

<h2>Features</h2>
<ul>
<li>ErsatzTV-style continuous background streaming</li>
<li>Continuous background streaming with anchor-time playout</li>
<li>HDHomeRun emulation for Plex/Emby/Jellyfin</li>
<li>M3U playlist support with XMLTV EPG</li>
<li>FFmpeg transcoding with hardware acceleration</li>
Expand Down Expand Up @@ -1271,10 +1271,10 @@ async def import_m3u_page(request: Request):
)
return RedirectResponse(url="/api/docs")

# Migration page - Import from ErsatzTV/StreamTV
# Migration page - Import from legacy systems
@app.get("/migration", response_model=None)
async def migration_page(request: Request):
"""Migration page for importing from ErsatzTV and StreamTV."""
"""Migration page for importing from legacy systems."""
if templates:
return templates.TemplateResponse(
"migration.html",
Expand Down
10 changes: 5 additions & 5 deletions exstreamtv/playout/scheduler.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
Playout scheduler for determining what plays when.

Ported from ErsatzTV PlayoutModeScheduler*.cs files.
Supports four scheduling modes: ONE, MULTIPLE, DURATION, and FLOOD.
"""

from abc import ABC, abstractmethod
Expand All @@ -22,7 +22,8 @@ class ScheduleMode(Enum):
"""
Schedule item playback mode.

Ported from ErsatzTV PlayoutMode.
ONE: play one item then advance. MULTIPLE: play N items. DURATION: play
for a time span. FLOOD: play until a fixed end time.
"""

ONE = "one" # Play one item, then move to next schedule item
Expand All @@ -35,8 +36,6 @@ class ScheduleMode(Enum):
class ScheduleItem:
"""
A schedule item defining what to play and when.

Ported from ErsatzTV ProgramScheduleItem.
"""

id: int
Expand Down Expand Up @@ -84,7 +83,8 @@ class PlayoutScheduler:
"""
Main scheduler that determines playout items.

Ported from ErsatzTV PlayoutModeScheduler* classes.
Processes schedule items in sequence, delegating to mode-specific logic
(ONE, MULTIPLE, DURATION, FLOOD) to fill a playout timeline.
"""

def __init__(
Expand Down
16 changes: 8 additions & 8 deletions exstreamtv/streaming/channel_manager.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""
Channel Manager for continuous background streaming (ErsatzTV-style).
Channel Manager for continuous background streaming.

Ported from StreamTV with playout timeline tracking and resume capability.
Provides playout timeline tracking and resume capability.

Enhanced with Tunarr/dizqueTV integrations:
Enhanced with integrations:
- Session tracking for connection management
- Stream throttling for buffer control
- Error screen fallback for graceful failures
Expand Down Expand Up @@ -107,7 +107,7 @@ def _safe_import(module_path: str, name: str) -> Optional[Any]:

class ChannelStream:
"""
Manages a continuous stream for a single channel (ErsatzTV-style).
Manages a continuous stream for a single channel.

Features:
- Continuous background streaming
Expand Down Expand Up @@ -153,7 +153,7 @@ def __init__(
self._lock = asyncio.Lock()
self._client_count = 0

# Playout timeline tracking (ErsatzTV-style)
# Playout timeline tracking
self._playout_start_time: datetime | None = None
self._schedule_items: list[dict] = []
self._current_item_index = 0
Expand Down Expand Up @@ -226,7 +226,7 @@ async def _load_or_initialize_position(self) -> None:
"""
Load saved anchor time or initialize for continuous streaming.

ErsatzTV-style approach: Use an anchor time to calculate the current
Uses an anchor time to calculate the current
position based on elapsed wall-clock time. This ensures:
1. All viewers see the same content at the same time
2. Restarting the server continues from the correct time-based position
Expand Down Expand Up @@ -460,7 +460,7 @@ async def get_stream(self) -> AsyncIterator[bytes]:
"""
Get the current stream.

Joins existing continuous stream at current position (ErsatzTV-style).
Joins existing continuous stream at current position.

Yields:
MPEG-TS data chunks.
Expand Down Expand Up @@ -763,7 +763,7 @@ async def _stream_loop(
f"Channel {self.channel_number} playing: {playout_item.get('title')}"
)

# Get seek offset from playout item (ErsatzTV-style)
# Get seek offset from playout item
seek_offset = playout_item.get("seek_offset", 0.0)

# Update current item tracking (EPG uses this for guide alignment)
Expand Down
Loading