diff --git a/CHANGELOG.md b/CHANGELOG.md index 96a6da3ac..bc24f0121 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Fixed +- Fix regression from `v26.6.0` that broke `MPEG-TS` channels on Windows when the channel name or the ffmpeg path contains non-english characters (like `Télévision`) + - Affected channels would connect but never send any data + - `MPEG-TS (Legacy)` and channel preview were not affected + - Custom MPEG-TS scripts can now use the `ETV_CHANNEL_NAME`, `ETV_HLS_URL` and `ETV_FFMPEG_PATH` environment variables, which are not affected by this issue; the `{{ ChannelName }}`, `{{ HlsUrl }}` and `{{ FFmpegPath }}` template variables continue to work but remain affected ## [26.7.1] - 2026-07-31 ### Changed diff --git a/ErsatzTV.Infrastructure/FFmpeg/MpegTsScriptService.cs b/ErsatzTV.Infrastructure/FFmpeg/MpegTsScriptService.cs index 266ac289c..05401f13d 100644 --- a/ErsatzTV.Infrastructure/FFmpeg/MpegTsScriptService.cs +++ b/ErsatzTV.Infrastructure/FFmpeg/MpegTsScriptService.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.IO.Abstractions; using System.Runtime.InteropServices; using CliWrap; +using CliWrap.Builders; using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.FFmpeg; @@ -50,6 +51,11 @@ public class MpegTsScriptService( scriptFolder = kvp.Key; } + // the values below are passed through the environment (which the OS delivers as utf-16) + // rather than templated into the script; cmd.exe decodes batch files using the console + // code page, and at 65001 it aborts entirely on any multi-byte character + string channelName = channel.Name.Replace("\"", string.Empty); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { string scriptInput = Path.Combine(scriptFolder, script.WindowsScript); @@ -64,7 +70,8 @@ public class MpegTsScriptService( { var fileName = $"{tempFilePool.GetNextTempFile(TempFileCategory.MpegTsScript)}.bat"; await File.WriteAllTextAsync(fileName, finalScript); - return Cli.Wrap(fileName); + return Cli.Wrap(fileName) + .WithEnvironmentVariables(WithScriptVariables(hlsUrl, channelName, ffmpegPath)); } } } @@ -82,7 +89,9 @@ public class MpegTsScriptService( { string fileName = tempFilePool.GetNextTempFile(TempFileCategory.MpegTsScript); await File.WriteAllTextAsync(fileName, finalScript); - return Cli.Wrap("bash").WithArguments([fileName]); + return Cli.Wrap("bash") + .WithArguments([fileName]) + .WithEnvironmentVariables(WithScriptVariables(hlsUrl, channelName, ffmpegPath)); } } } @@ -90,6 +99,15 @@ public class MpegTsScriptService( return []; } + private static Action WithScriptVariables( + string hlsUrl, + string channelName, + string ffmpegPath) => + builder => builder + .Set("ETV_HLS_URL", hlsUrl) + .Set("ETV_CHANNEL_NAME", channelName) + .Set("ETV_FFMPEG_PATH", ffmpegPath); + private async Task> GetTemplatedScript( string fileName, string hlsUrl, diff --git a/ErsatzTV/Resources/Scripts/MpegTs/run.bat b/ErsatzTV/Resources/Scripts/MpegTs/run.bat index 747b9d645..9e2479a7f 100644 --- a/ErsatzTV/Resources/Scripts/MpegTs/run.bat +++ b/ErsatzTV/Resources/Scripts/MpegTs/run.bat @@ -1,3 +1,3 @@ @echo off -"{{ FFmpegPath }}" -nostdin -threads 1 -hide_banner -loglevel error -nostats -fflags +genpts+discardcorrupt+igndts -readrate 1.0 -i "{{ HlsUrl }}" -map 0 -c copy -metadata service_provider="ErsatzTV" -metadata service_name="{{ ChannelName }}" -f mpegts pipe:1 +"%ETV_FFMPEG_PATH%" -nostdin -threads 1 -hide_banner -loglevel error -nostats -fflags +genpts+discardcorrupt+igndts -readrate 1.0 -i "%ETV_HLS_URL%" -map 0 -c copy -metadata service_provider="ErsatzTV" -metadata service_name="%ETV_CHANNEL_NAME%" -f mpegts pipe:1 diff --git a/ErsatzTV/Resources/Scripts/MpegTs/run.sh b/ErsatzTV/Resources/Scripts/MpegTs/run.sh index 1b4b7d6b3..ffd866071 100644 --- a/ErsatzTV/Resources/Scripts/MpegTs/run.sh +++ b/ErsatzTV/Resources/Scripts/MpegTs/run.sh @@ -1,3 +1,3 @@ #! /bin/sh -"{{ FFmpegPath }}" -nostdin -threads 1 -hide_banner -loglevel error -nostats -fflags +genpts+discardcorrupt+igndts -readrate 1.0 -i "{{ HlsUrl }}" -map 0 -c copy -metadata service_provider="ErsatzTV" -metadata service_name="{{ ChannelName }}" -f mpegts pipe:1 +"$ETV_FFMPEG_PATH" -nostdin -threads 1 -hide_banner -loglevel error -nostats -fflags +genpts+discardcorrupt+igndts -readrate 1.0 -i "$ETV_HLS_URL" -map 0 -c copy -metadata service_provider="ErsatzTV" -metadata service_name="$ETV_CHANNEL_NAME" -f mpegts pipe:1