Hode / Dockerfile
Reooiq's picture
Replace Hode with Reoo88/Tktktk main (00967c2) exactly; PASSWORD=admin for login
56bfa33
Raw History Blame Contribute Delete
3.98 kB
FROM python:3.11-slim-trixie
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg mkvtoolnix ca-certificates tzdata && rm -rf /var/lib/apt/lists/*
RUN ffmpeg -hide_banner -h protocol=rtmps 2>&1 | grep -q rtmp_enhanced_codecs
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py relay.py youtube_api.py youtube_manager.py flv_bridge.py tiktok_auth.py index.html ./
# --- YouTube ingestion -------------------------------------------------------
# copy = forward the source untouched (original quality, no extra CPU). DEFAULT.
# The YouTube stream is created with cdn.resolution='variable' and
# cdn.frameRate='variable', which per the Live Streaming API means
# "YouTube should automatically detect the resolution of your streamed
# video". So a mid-stream resolution change does NOT require a new
# session, and YOUTUBE_FORMAT_GUARD=off keeps the RTMPS session alive
# across it. Restarting the session was itself a visible gap and, when
# the phone flipped resolution repeatedly, a reconnect storm.
# fixed1080 = constant 1080x1920 canvas, re-encodes to H.264. Zero gaps but costs
# CPU: measured 2.26x real time on 2 vCPU (~44% for one stream).
# fixed720 = constant 720x1280 canvas, 3.72x real time (~27%). Lighter.
# Both canvas modes stay available from Settings if YouTube's own healthStatus ever
# reports videoResolutionUnsupported; they are opt-in because of the CPU cost.
#
# YOUTUBE_FORMAT_GUARD=on -> tear the session down on a resolution change (old
# behaviour: 1-3 s gap, but YouTube re-declares the new size explicitly).
# YOUTUBE_FLAP_LIMIT: if resolution flips this many times in 10 minutes, copy mode
# falls back to YOUTUBE_FLAP_FALLBACK_MODE for the rest of the event. Dormant
# while the guard is off, kept as a safety net.
#
# YOUTUBE_FIFO_TIMESHIFT: the push goes through FFmpeg's fifo pseudo-muxer, which
# reconnects on its own when YouTube drops the connection instead of killing the
# whole process (that is why the recording kept running while the broadcast died).
# timeshift additionally delays the YouTube feed behind the live TikTok source and
# keeps that many seconds buffered, so a source hiccup is covered instead of
# starving YouTube's ingest, and packets survive a reconnect instead of being
# dropped. Cost is latency only, no CPU. https://ffmpeg.org/ffmpeg-formats.html#fifo
# NOTE: fifo's queue_size defaults to 60 packets, under one second of stream, so
# timeshift requires it to be raised or the queue overflows and the stream is
# destroyed instead of delayed. relay.py sizes it automatically (ts*250+500).
# YOUTUBE_HEALTH_INTERVAL: seconds between reads of YouTube's own
# healthStatus/configurationIssues while live. This is what reports videoIngestion
# Starved (viewers buffer), gopSizeLong, openGop, videoResolutionUnsupported and
# videoCodec, i.e. the ground truth instead of a guess.
ENV YOUTUBE_SEND_MODE=copy \
YOUTUBE_FORMAT_GUARD=off \
YOUTUBE_FLAP_LIMIT=4 \
YOUTUBE_FLAP_FALLBACK_MODE=fixed720 \
YOUTUBE_X264_PRESET=veryfast \
YOUTUBE_CRF=22 \
YOUTUBE_RECOVERY_WAIT=1 \
YOUTUBE_FIFO_TIMESHIFT=10 \
YOUTUBE_HEALTH_INTERVAL=180
# --- TikTok session token (age-restricted 18+ rooms) -------------------------
# Set TIKTOK_SESSIONID in Settings > Variables and secrets, or paste it into the
# UI panel (no Factory reboot needed). Sessions are short-lived (~6 h per
# sid_guard), so the UI panel is the practical way to refresh.
# Per account: TIKTOK_SESSIONID_<USER> (upper-case, non A-Z0-9 -> _)
# Compact map: TIKTOK_TOKENS=user1=token1;user2=token2
ENV TIKTOK_TT_TARGET_IDC=useast1a
ENV PORT=7860 PYTHONUNBUFFERED=1 YOUTUBE_VIDEO_MODE=original PASSWORD=admin
EXPOSE 7860
CMD ["sh", "-c", "exec uvicorn app:app --host 0.0.0.0 --port ${PORT:-7860} --timeout-graceful-shutdown 65"]