From ba64c4956e10120e00b09ad24160d375727863ab Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 05:35:55 +0100 Subject: [PATCH 01/16] fix: enhance alternative format parsing to include tvg-chno attribute extraction --- src/modules/iptv/parsers/playlist-parser.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/iptv/parsers/playlist-parser.ts b/src/modules/iptv/parsers/playlist-parser.ts index f21c6c2..830d3d3 100644 --- a/src/modules/iptv/parsers/playlist-parser.ts +++ b/src/modules/iptv/parsers/playlist-parser.ts @@ -59,6 +59,7 @@ function parseAlternativeFormat(line: string): ChannelEntry | null { // Extract attributes flexibly without requiring specific order const tvgIdMatch = line.match(/tvg-id="([^"]+)"/); const tvgNameMatch = line.match(/tvg-name="([^"]+)"/); + const tvgChnoMatch = line.match(/tvg-chno="([^"]+)"/); const tvgLogoMatch = line.match(/tvg-logo="([^"]+)"/); const groupTitleMatch = line.match(/group-title="([^"]+)"/); @@ -70,6 +71,7 @@ function parseAlternativeFormat(line: string): ChannelEntry | null { } const tvg_id = tvgIdMatch ? tvgIdMatch[1] : ''; + const tvg_chno = tvgChnoMatch ? tvgChnoMatch[1] : ''; const tvg_name = tvgNameMatch ? tvgNameMatch[1] : (channelName || tvg_id); const tvg_logo = tvgLogoMatch ? tvgLogoMatch[1] : ''; const group_title = groupTitleMatch ? groupTitleMatch[1] : ''; @@ -110,6 +112,7 @@ function parseFlexibleFormat(line: string): ChannelEntry | null { // Extract available attributes const tvgIdMatch = line.match(/tvg-id="([^"]+)"/); + const tvgChnoMatch = line.match(/tvg-chno="([^"]+)"/); const tvgLogoMatch = line.match(/tvg-logo="([^"]+)"/); const groupTitleMatch = line.match(/group-title="([^"]+)"/); From 711069aaf20b01246536eca8847364e75c7ab9dd Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 05:40:48 +0100 Subject: [PATCH 02/16] fix: improve channel name extraction to prioritize non-numeric display names --- src/modules/iptv/parsers/xmltv-parser.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/modules/iptv/parsers/xmltv-parser.ts b/src/modules/iptv/parsers/xmltv-parser.ts index 700c006..09bd17f 100644 --- a/src/modules/iptv/parsers/xmltv-parser.ts +++ b/src/modules/iptv/parsers/xmltv-parser.ts @@ -94,14 +94,15 @@ function extractChannelData(channel: any): ChannelEntry { for (const nameElement of channel['display-name']) { const displayName = extractTextContent(nameElement); - // Use first display name as primary channel name - if (!channelName) { - channelName = displayName; - } // Identify numeric-only display names as channel numbers if (/^\d+$/.test(displayName) && !channelNum) { channelNum = displayName; } + // Use first non-numeric display name as primary channel name + // This prevents "1 One Piece" format from being used when "One Piece" is available + else if (!channelName || /^\d+\s/.test(channelName)) { + channelName = displayName; + } } } From 374f204ba0df4a6ebb66749e052ddeefc8166720 Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 06:25:21 +0100 Subject: [PATCH 03/16] fix: enhance programme entry building to include channel name cleanup --- src/modules/embeds/programme.ts | 3 +-- src/modules/iptv/parsers/xmltv-parser.ts | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/modules/embeds/programme.ts b/src/modules/embeds/programme.ts index 2cff50e..5b80634 100644 --- a/src/modules/embeds/programme.ts +++ b/src/modules/embeds/programme.ts @@ -222,7 +222,6 @@ export class ProgrammeEmbedProcessor extends BaseEmbedProcessor : new Date(programme.stop_timestamp ? programme.stop_timestamp * 1000 : Date.now()); const startTime = startDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false }); - const stopTime = stopDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false }); const description = typeof programme.description === 'string' ? (programme.description.length > 100 ? `${programme.description.substring(0, 100)}...` @@ -239,7 +238,7 @@ export class ProgrammeEmbedProcessor extends BaseEmbedProcessor : `${programme.title}${episodeText}`; dateEmbed.addFields({ - name: `${startTime} - ${stopTime}: ${showTitle}`, + name: `${startTime}: ${showTitle}`, value: description }); }); diff --git a/src/modules/iptv/parsers/xmltv-parser.ts b/src/modules/iptv/parsers/xmltv-parser.ts index 09bd17f..54f083c 100644 --- a/src/modules/iptv/parsers/xmltv-parser.ts +++ b/src/modules/iptv/parsers/xmltv-parser.ts @@ -51,6 +51,14 @@ export async function extractXMLTVData(filePath: string): Promise { }); } + // Build channel name map for title cleanup + const channelNameMap = new Map(); + output.channels.forEach(ch => { + if (ch.tvg_id && ch.tvg_name) { + channelNameMap.set(ch.tvg_id, ch.tvg_name); + } + }); + // Process programmes if available const programmeList = xmlData.tv.programme || []; if (programmeList.length > 0) { @@ -58,7 +66,7 @@ export async function extractXMLTVData(filePath: string): Promise { programmeList.forEach((programmeNode: any) => { try { - output.programmes.push(buildProgrammeEntry(programmeNode)); + output.programmes.push(buildProgrammeEntry(programmeNode, channelNameMap)); } catch (err) { const programmeTitle = extractTextContent(programmeNode['title']?.[0]) || 'unknown'; logger.error(`Programme extraction failed for "${programmeTitle}": ${err}`); @@ -126,12 +134,20 @@ function extractChannelData(channel: any): ChannelEntry { * Builds a structured programme entry from raw XMLTV programme data. * * @param programme - Raw programme data from XMLTV + * @param channelNameMap - Map of channel IDs to names for title cleanup * @returns Structured programme entry * @throws Error if programme is missing required fields */ -function buildProgrammeEntry(programme: any): ProgrammeEntry { +function buildProgrammeEntry(programme: any, channelNameMap: Map): ProgrammeEntry { // Extract text content fields - const title = extractTextContent(programme['title']?.[0]); + let title = extractTextContent(programme['title']?.[0]); + + // Strip duplicate channel name prefix if present (e.g., "One Piece: One Piece: The Movie" -> "One Piece: The Movie") + const channelId = programme.$.channel; + const channelName = channelNameMap.get(channelId); + if (channelName && title.startsWith(`${channelName}: `)) { + title = title.substring(channelName.length + 2); + } const description = extractTextContent(programme['desc']?.[0]); const category = extractTextContent(programme['category']?.[0]); const subtitle = extractTextContent(programme['sub-title']?.[0]); From b83f4702038478a0ab4f71c7d1d3ae90c710dc3e Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 06:40:51 +0100 Subject: [PATCH 04/16] fix: enhance startStreaming to detect HLS streams and adjust ffmpeg settings --- src/modules/streaming/index.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/modules/streaming/index.ts b/src/modules/streaming/index.ts index 4452b12..1295894 100644 --- a/src/modules/streaming/index.ts +++ b/src/modules/streaming/index.ts @@ -193,6 +193,9 @@ export async function startStreaming(channelEntry: ChannelEntry) { logger.info(`Stopping any possible existing stream.`); await stopStreaming(); + // Detect HLS stream and adjust settings accordingly + const isHLS = channelEntry.url.includes('streamMode=hls') || channelEntry.url.includes('.m3u8'); + const { command, output } = prepareStream(channelEntry.url, { noTranscoding: false, minimizeLatency: config.MINIMIZE_LATENCY, @@ -200,6 +203,11 @@ export async function startStreaming(channelEntry: ChannelEntry) { bitrateVideoMax: config.BITRATE_VIDEO_MAX, videoCodec: Utils.normalizeVideoCodec("H264"), h26xPreset: "veryfast", + customFfmpegFlags: isHLS ? [ + '-protocol_whitelist', 'file,http,https,tcp,tls,crypto', + '-fflags', '+genpts', + '-re' + ] : [], }, abortController.signal); currentChannelEntry = channelEntry; From e581d25fac4d7292187b1224775564e7273952a7 Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 06:43:02 +0100 Subject: [PATCH 05/16] fix: add DISABLE_TRANSCODE configuration option and update startStreaming to use it --- .env.example | 1 + README.md | 27 ++++++++++++++------------- src/modules/streaming/index.ts | 2 +- src/utils/config.ts | 2 ++ 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/.env.example b/.env.example index fb4722b..1f3b145 100644 --- a/.env.example +++ b/.env.example @@ -22,6 +22,7 @@ XMLTV="http://example.com/xmltv/guide.xml" # MINIMIZE_LATENCY=true # BITRATE_VIDEO=5000 # BITRATE_VIDEO_MAX=7500 +# DISABLE_TRANSCODE=false # Timezone configuration #TZ="UTC" diff --git a/README.md b/README.md index 09f8d92..5406709 100644 --- a/README.md +++ b/README.md @@ -116,19 +116,20 @@ The application uses the following environment variables, which should be define ### System and IPTV Configuration -| Variable | Description | Example/Default | Required | -|--------------------|--------------------------------------------------|------------------------------------------|----------| -| `PLAYLIST` | URL to the M3U playlist. | `http://example.com/m3u/playlist.m3u` | ✔ | -| `XMLTV` | URL to the XMLTV guide. | `http://example.com/xmltv/guide.xml` | ✘ | -| `REFRESH_IPTV` | Interval in minutes to refresh the IPTV data. | `1440` | ✘ | -| `RAM_CACHE` | Whether to use RAM for caching. | `true` | ✘ | -| `CACHE_DIR` | Directory for cache storage. | `../cache` | ✘ | -| `DEBUG` | Enable debug mode. | `false` | ✘ | -| `DEFAULT_STREAM_TIMEOUT` | Default stream timeout (when alone in channel) in minutes. | `10` | ✘ | -| `TZ` | Timezone for the container. Example: `Europe/Ljubljana` | `UTC` | ✘ | -| `MINIMIZE_LATENCY` | Minimize latency for the stream. | `true` | ✘ | -| `BITRATE_VIDEO` | Video bitrate in Kbps. | `5000` | ✘ | -| `BITRATE_VIDEO_MAX`| Maximum video bitrate in Kbps. | `7500` | ✘ | +| Variable | Description | Example/Default | Required | +|--------------------------|-----------------------------------------------------------------------|---------------------------------------|----------| +| `PLAYLIST` | URL to the M3U playlist. | `http://example.com/m3u/playlist.m3u` | ✔ | +| `XMLTV` | URL to the XMLTV guide. | `http://example.com/xmltv/guide.xml` | ✘ | +| `REFRESH_IPTV` | Interval in minutes to refresh the IPTV data. | `1440` | ✘ | +| `RAM_CACHE` | Whether to use RAM for caching. | `true` | ✘ | +| `CACHE_DIR` | Directory for cache storage. | `../cache` | ✘ | +| `DEBUG` | Enable debug mode. | `false` | ✘ | +| `DEFAULT_STREAM_TIMEOUT` | Default stream timeout (when alone in channel) in minutes. | `10` | ✘ | +| `TZ` | Timezone for the container. Example: `Europe/Ljubljana` | `UTC` | ✘ | +| `MINIMIZE_LATENCY` | Minimize latency for the stream. | `true` | ✘ | +| `BITRATE_VIDEO` | Video bitrate in Kbps. | `5000` | ✘ | +| `BITRATE_VIDEO_MAX` | Maximum video bitrate in Kbps. | `7500` | ✘ | +| `DISABLE_TRANSCODE` | Disable transcoding and pass stream through directly. | `false` | ✘ | > [!TIP] > There is a bunch of IPTV providers online. I recommend using a tool like [Threadfin](https://github.com/Threadfin/Threadfin) or [Dispatcharr](https://github.com/Dispatcharr/Dispatcharr) to sort out your IPTV channels. You can find public M3U playlists [here](https://github.com/iptv-org/iptv). More info on IPTV can be found [here](https://github.com/iptv-org/awesome-iptv). diff --git a/src/modules/streaming/index.ts b/src/modules/streaming/index.ts index 1295894..9e30b2f 100644 --- a/src/modules/streaming/index.ts +++ b/src/modules/streaming/index.ts @@ -197,7 +197,7 @@ export async function startStreaming(channelEntry: ChannelEntry) { const isHLS = channelEntry.url.includes('streamMode=hls') || channelEntry.url.includes('.m3u8'); const { command, output } = prepareStream(channelEntry.url, { - noTranscoding: false, + noTranscoding: config.DISABLE_TRANSCODE, minimizeLatency: config.MINIMIZE_LATENCY, bitrateVideo: config.BITRATE_VIDEO, bitrateVideoMax: config.BITRATE_VIDEO_MAX, diff --git a/src/utils/config.ts b/src/utils/config.ts index 75828b5..a7aaf5f 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -16,6 +16,7 @@ class Config { MINIMIZE_LATENCY: boolean; BITRATE_VIDEO: number; BITRATE_VIDEO_MAX: number; + DISABLE_TRANSCODE: boolean; constructor() { logger.info('Loading environment variables'); @@ -41,6 +42,7 @@ class Config { this.MINIMIZE_LATENCY = env.MINIMIZE_LATENCY?.trim().toLowerCase() !== 'false'; this.BITRATE_VIDEO = parseInt(env.BITRATE_VIDEO?.trim() || '5000'); this.BITRATE_VIDEO_MAX = parseInt(env.BITRATE_VIDEO_MAX?.trim() || '7500'); + this.DISABLE_TRANSCODE = env.DISABLE_TRANSCODE?.trim().toLowerCase() === 'true'; // Debug configuration this.DEBUG = env.DEBUG?.trim().toLowerCase() === 'true'; From e3a11118db9dd78ae452b85b210873df8ea63957 Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 06:44:54 +0100 Subject: [PATCH 06/16] fix: enhance startStreaming with debug logging and HLS detection improvements --- src/modules/streaming/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/streaming/index.ts b/src/modules/streaming/index.ts index 9e30b2f..97627fd 100644 --- a/src/modules/streaming/index.ts +++ b/src/modules/streaming/index.ts @@ -196,6 +196,10 @@ export async function startStreaming(channelEntry: ChannelEntry) { // Detect HLS stream and adjust settings accordingly const isHLS = channelEntry.url.includes('streamMode=hls') || channelEntry.url.includes('.m3u8'); + logger.debug(`Stream URL: ${channelEntry.url}`); + logger.debug(`HLS detected: ${isHLS}`); + logger.debug(`Transcode disabled: ${config.DISABLE_TRANSCODE}`); + const { command, output } = prepareStream(channelEntry.url, { noTranscoding: config.DISABLE_TRANSCODE, minimizeLatency: config.MINIMIZE_LATENCY, @@ -206,7 +210,8 @@ export async function startStreaming(channelEntry: ChannelEntry) { customFfmpegFlags: isHLS ? [ '-protocol_whitelist', 'file,http,https,tcp,tls,crypto', '-fflags', '+genpts', - '-re' + '-re', + '-user_agent', 'Mozilla/5.0' ] : [], }, abortController.signal); From 3fcca8cdd2a16ac8b34e4573b10c15384117fc11 Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 06:55:16 +0100 Subject: [PATCH 07/16] fix: enhance startStreaming to transform stream URL for better FFmpeg compatibility --- src/modules/streaming/index.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/modules/streaming/index.ts b/src/modules/streaming/index.ts index 97627fd..09362fc 100644 --- a/src/modules/streaming/index.ts +++ b/src/modules/streaming/index.ts @@ -193,14 +193,21 @@ export async function startStreaming(channelEntry: ChannelEntry) { logger.info(`Stopping any possible existing stream.`); await stopStreaming(); - // Detect HLS stream and adjust settings accordingly - const isHLS = channelEntry.url.includes('streamMode=hls') || channelEntry.url.includes('.m3u8'); + // Transform URL: replace ?streamMode=* with .m3u8 for better FFmpeg compatibility + let streamUrl = channelEntry.url; + if (streamUrl.includes('?streamMode=')) { + streamUrl = streamUrl.replace(/\?streamMode=[^&]*(&.*)?$/, '.m3u8'); + logger.debug(`Transformed URL from ${channelEntry.url} to ${streamUrl}`); + } + + // Detect HLS stream + const isHLS = streamUrl.includes('.m3u8'); - logger.debug(`Stream URL: ${channelEntry.url}`); + logger.debug(`Stream URL: ${streamUrl}`); logger.debug(`HLS detected: ${isHLS}`); logger.debug(`Transcode disabled: ${config.DISABLE_TRANSCODE}`); - const { command, output } = prepareStream(channelEntry.url, { + const { command, output } = prepareStream(streamUrl, { noTranscoding: config.DISABLE_TRANSCODE, minimizeLatency: config.MINIMIZE_LATENCY, bitrateVideo: config.BITRATE_VIDEO, From f41342ef76ae6864af35389d90b21ddbd80b3ba3 Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 06:59:08 +0100 Subject: [PATCH 08/16] fix: streamline startStreaming by removing URL transformation and enhancing HLS handling --- src/modules/streaming/index.ts | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/modules/streaming/index.ts b/src/modules/streaming/index.ts index 09362fc..162b437 100644 --- a/src/modules/streaming/index.ts +++ b/src/modules/streaming/index.ts @@ -193,33 +193,26 @@ export async function startStreaming(channelEntry: ChannelEntry) { logger.info(`Stopping any possible existing stream.`); await stopStreaming(); - // Transform URL: replace ?streamMode=* with .m3u8 for better FFmpeg compatibility - let streamUrl = channelEntry.url; - if (streamUrl.includes('?streamMode=')) { - streamUrl = streamUrl.replace(/\?streamMode=[^&]*(&.*)?$/, '.m3u8'); - logger.debug(`Transformed URL from ${channelEntry.url} to ${streamUrl}`); - } - - // Detect HLS stream - const isHLS = streamUrl.includes('.m3u8'); - - logger.debug(`Stream URL: ${streamUrl}`); - logger.debug(`HLS detected: ${isHLS}`); + logger.debug(`Stream URL: ${channelEntry.url}`); logger.debug(`Transcode disabled: ${config.DISABLE_TRANSCODE}`); - const { command, output } = prepareStream(streamUrl, { + const { command, output } = prepareStream(channelEntry.url, { noTranscoding: config.DISABLE_TRANSCODE, minimizeLatency: config.MINIMIZE_LATENCY, bitrateVideo: config.BITRATE_VIDEO, bitrateVideoMax: config.BITRATE_VIDEO_MAX, videoCodec: Utils.normalizeVideoCodec("H264"), h26xPreset: "veryfast", - customFfmpegFlags: isHLS ? [ + customFfmpegFlags: [ '-protocol_whitelist', 'file,http,https,tcp,tls,crypto', - '-fflags', '+genpts', - '-re', - '-user_agent', 'Mozilla/5.0' - ] : [], + '-reconnect', '1', + '-reconnect_streamed', '1', + '-reconnect_delay_max', '2', + '-user_agent', 'Mozilla/5.0', + '-headers', 'Accept: */*', + '-multiple_requests', '1', + '-f', 'hls' + ], }, abortController.signal); currentChannelEntry = channelEntry; From ba431fdd527a820cd828691071cc298887beae63 Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 07:03:31 +0100 Subject: [PATCH 09/16] fix: optimize FFmpeg flags in startStreaming for improved stream handling --- src/modules/streaming/index.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/modules/streaming/index.ts b/src/modules/streaming/index.ts index 162b437..a485ab0 100644 --- a/src/modules/streaming/index.ts +++ b/src/modules/streaming/index.ts @@ -204,14 +204,9 @@ export async function startStreaming(channelEntry: ChannelEntry) { videoCodec: Utils.normalizeVideoCodec("H264"), h26xPreset: "veryfast", customFfmpegFlags: [ - '-protocol_whitelist', 'file,http,https,tcp,tls,crypto', - '-reconnect', '1', - '-reconnect_streamed', '1', - '-reconnect_delay_max', '2', - '-user_agent', 'Mozilla/5.0', - '-headers', 'Accept: */*', - '-multiple_requests', '1', - '-f', 'hls' + '-analyzeduration', '5000000', + '-probesize', '10000000', + '-fflags', '+igndts' ], }, abortController.signal); From 1a3ece6588904f0bb80425027e1f9beaf4fe60bc Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 07:08:28 +0100 Subject: [PATCH 10/16] fix: remove debug logging for stream URL and transcode status in startStreaming --- src/modules/streaming/index.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/modules/streaming/index.ts b/src/modules/streaming/index.ts index a485ab0..4443269 100644 --- a/src/modules/streaming/index.ts +++ b/src/modules/streaming/index.ts @@ -193,8 +193,7 @@ export async function startStreaming(channelEntry: ChannelEntry) { logger.info(`Stopping any possible existing stream.`); await stopStreaming(); - logger.debug(`Stream URL: ${channelEntry.url}`); - logger.debug(`Transcode disabled: ${config.DISABLE_TRANSCODE}`); + //logger.debug(`Stream URL: ${channelEntry.url}`); const { command, output } = prepareStream(channelEntry.url, { noTranscoding: config.DISABLE_TRANSCODE, @@ -203,11 +202,6 @@ export async function startStreaming(channelEntry: ChannelEntry) { bitrateVideoMax: config.BITRATE_VIDEO_MAX, videoCodec: Utils.normalizeVideoCodec("H264"), h26xPreset: "veryfast", - customFfmpegFlags: [ - '-analyzeduration', '5000000', - '-probesize', '10000000', - '-fflags', '+igndts' - ], }, abortController.signal); currentChannelEntry = channelEntry; From 5e794daca935ac8be41c8dee516202ff12fc42ad Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 07:16:32 +0100 Subject: [PATCH 11/16] fix: add buffer size calculation and custom FFmpeg flags for improved streaming performance --- src/modules/streaming/index.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/modules/streaming/index.ts b/src/modules/streaming/index.ts index 4443269..bff2713 100644 --- a/src/modules/streaming/index.ts +++ b/src/modules/streaming/index.ts @@ -195,6 +195,9 @@ export async function startStreaming(channelEntry: ChannelEntry) { //logger.debug(`Stream URL: ${channelEntry.url}`); + // Calculate buffer size as 3x the video bitrate for smooth livestreaming + const bufferSize = config.BITRATE_VIDEO * 3; + const { command, output } = prepareStream(channelEntry.url, { noTranscoding: config.DISABLE_TRANSCODE, minimizeLatency: config.MINIMIZE_LATENCY, @@ -202,6 +205,12 @@ export async function startStreaming(channelEntry: ChannelEntry) { bitrateVideoMax: config.BITRATE_VIDEO_MAX, videoCodec: Utils.normalizeVideoCodec("H264"), h26xPreset: "veryfast", + customFfmpegFlags: [ + '-buffer_size', `${bufferSize}k`, + '-max_delay', '500000', + '-flags', 'low_delay', + '-strict', 'experimental' + ], }, abortController.signal); currentChannelEntry = channelEntry; From e61e978db58e76b15f3669754c41d50c1465fffd Mon Sep 17 00:00:00 2001 From: zbejas Date: Mon, 29 Dec 2025 07:24:42 +0100 Subject: [PATCH 12/16] fix: update logging in startStreaming and enhance FFmpeg flags for improved streaming reliability --- src/modules/streaming/index.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/modules/streaming/index.ts b/src/modules/streaming/index.ts index bff2713..f0acfa8 100644 --- a/src/modules/streaming/index.ts +++ b/src/modules/streaming/index.ts @@ -193,7 +193,7 @@ export async function startStreaming(channelEntry: ChannelEntry) { logger.info(`Stopping any possible existing stream.`); await stopStreaming(); - //logger.debug(`Stream URL: ${channelEntry.url}`); + logger.info(`Attempting to stream: ${channelEntry.url}`); // Calculate buffer size as 3x the video bitrate for smooth livestreaming const bufferSize = config.BITRATE_VIDEO * 3; @@ -206,10 +206,11 @@ export async function startStreaming(channelEntry: ChannelEntry) { videoCodec: Utils.normalizeVideoCodec("H264"), h26xPreset: "veryfast", customFfmpegFlags: [ + '-reconnect', '1', + '-reconnect_streamed', '1', + '-reconnect_delay_max', '5', '-buffer_size', `${bufferSize}k`, - '-max_delay', '500000', - '-flags', 'low_delay', - '-strict', 'experimental' + '-max_delay', '500000' ], }, abortController.signal); From e37704d05c4415a5ae8fc4e1ab0a20cc6262930d Mon Sep 17 00:00:00 2001 From: zbejas Date: Thu, 1 Jan 2026 15:37:41 +0100 Subject: [PATCH 13/16] feat: add GitHub Actions workflow for Docker image release on publish --- .github/workflows/{main.yml => master.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{main.yml => master.yml} (100%) diff --git a/.github/workflows/main.yml b/.github/workflows/master.yml similarity index 100% rename from .github/workflows/main.yml rename to .github/workflows/master.yml From 65b5cbf220a279bdfeac43bd2b8e3166c95c8bff Mon Sep 17 00:00:00 2001 From: zbejas Date: Thu, 1 Jan 2026 15:38:39 +0100 Subject: [PATCH 14/16] fix: correct workflow name from 'Main Branch Release' to 'Master Branch Release' --- .github/workflows/master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 36294a3..76a85c4 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -1,4 +1,4 @@ -name: Main Branch Release +name: Master Branch Release on: release: From fbaaf6148a09cf6e3efa003541c4e5aaa4ace391 Mon Sep 17 00:00:00 2001 From: zbejas Date: Thu, 1 Jan 2026 19:06:11 +0100 Subject: [PATCH 15/16] refactor: streamline Dockerfile by consolidating build and runtime stages --- Dockerfile | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/Dockerfile b/Dockerfile index 81ff7ba..baddc4e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,37 +1,19 @@ -# Build stage -FROM node:lts-alpine3.23 AS builder +FROM node:lts-alpine3.23 -RUN apk update && \ - apk add --no-cache python3 make g++ - -WORKDIR /app -COPY package.json ./ -RUN npm install - -COPY tsconfig.json ./ -COPY src ./src -RUN npm run build - -# Runtime stage -FROM node:lts-alpine3.23 AS runtime -# Had to switch from bun to node due to zeromq not being supported in bun yet. - -LABEL maintainer="Zbejas " -LABEL description="A Discord IPTV streaming bot." LABEL org.opencontainers.image.source="https://github.com/zbejas/orbiscast" -LABEL org.opencontainers.image.documentation="https://github.com/zbejas/orbiscast/blob/main/README.md" LABEL org.opencontainers.image.authors="Zbejas" LABEL org.opencontainers.image.licenses="GPL-3.0" -LABEL org.opencontainers.image.title="Orbiscast" -RUN apk update && \ - apk add --no-cache ffmpeg python3 make g++ +RUN apk add --no-cache ffmpeg python3 make g++ WORKDIR /app -COPY package.json ./ -RUN npm install --omit=dev && \ - apk del python3 make g++ -COPY --from=builder /app/dist ./dist +COPY package.json tsconfig.json ./ +COPY src ./src + +RUN npm install && \ + npm run build && \ + npm prune --omit=dev && \ + apk del python3 make g++ CMD ["npm", "run", "start:prod"] From fb8ad03c7bc99436fafada8f8c6eb5e98be358b5 Mon Sep 17 00:00:00 2001 From: zbejas Date: Thu, 1 Jan 2026 19:14:42 +0100 Subject: [PATCH 16/16] refactor: optimize Dockerfile by separating build and runtime stages and removing unnecessary dependencies --- Dockerfile | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index baddc4e..01eb92f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,34 @@ -FROM node:lts-alpine3.23 - -LABEL org.opencontainers.image.source="https://github.com/zbejas/orbiscast" -LABEL org.opencontainers.image.authors="Zbejas" -LABEL org.opencontainers.image.licenses="GPL-3.0" +# Build stage +FROM node:lts-alpine3.23 AS builder -RUN apk add --no-cache ffmpeg python3 make g++ +RUN apk add --no-cache python3 make g++ WORKDIR /app COPY package.json tsconfig.json ./ COPY src ./src -RUN npm install && \ - npm run build && \ - npm prune --omit=dev && \ - apk del python3 make g++ +RUN npm install && npm run build + +# Runtime stage +FROM node:lts-alpine3.23 + +RUN apk add --no-cache ffmpeg + +WORKDIR /app + +COPY package.json ./ +RUN npm install --omit=dev + +COPY --from=builder /app/dist ./dist + +# Metadata labels +LABEL maintainer="Zbejas " +LABEL description="A Discord IPTV streaming bot." +LABEL org.opencontainers.image.source="https://github.com/zbejas/orbiscast" +LABEL org.opencontainers.image.documentation="https://github.com/zbejas/orbiscast/blob/main/README.md" +LABEL org.opencontainers.image.authors="Zbejas" +LABEL org.opencontainers.image.licenses="GPL-3.0" +LABEL org.opencontainers.image.title="Orbiscast" CMD ["npm", "run", "start:prod"]