use new channel identifiers in M3U and XMLTV to disambiguate in Plex (#1920)
This commit is contained in:
@@ -4,6 +4,9 @@ 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]
|
||||
### Changed
|
||||
- **BREAKING CHANGE**: Change channel identifiers used in XMLTV to work around bad behavior in Plex
|
||||
|
||||
### Fixed
|
||||
- Fix startup error with MySql backend caused by database cleaner
|
||||
- Fix emptying trash with ElasticSearch backend
|
||||
|
||||
@@ -7,6 +7,7 @@ using ErsatzTV.Core.Domain.Filler;
|
||||
using ErsatzTV.Core.Emby;
|
||||
using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Core.Iptv;
|
||||
using ErsatzTV.Core.Jellyfin;
|
||||
using ErsatzTV.Core.Streaming;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
@@ -514,6 +515,8 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
|
||||
{
|
||||
ProgrammeStart = start,
|
||||
ProgrammeStop = stop,
|
||||
ChannelId = ChannelIdentifier.FromNumber(request.ChannelNumber),
|
||||
ChannelIdLegacy = ChannelIdentifier.LegacyFromNumber(request.ChannelNumber),
|
||||
request.ChannelNumber,
|
||||
HasCustomTitle = hasCustomTitle,
|
||||
displayItem.CustomTitle,
|
||||
@@ -569,6 +572,8 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
|
||||
{
|
||||
ProgrammeStart = start,
|
||||
ProgrammeStop = stop,
|
||||
ChannelId = ChannelIdentifier.FromNumber(request.ChannelNumber),
|
||||
ChannelIdLegacy = ChannelIdentifier.LegacyFromNumber(request.ChannelNumber),
|
||||
request.ChannelNumber,
|
||||
HasCustomTitle = hasCustomTitle,
|
||||
displayItem.CustomTitle,
|
||||
@@ -629,6 +634,8 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
|
||||
{
|
||||
ProgrammeStart = start,
|
||||
ProgrammeStop = stop,
|
||||
ChannelId = ChannelIdentifier.FromNumber(request.ChannelNumber),
|
||||
ChannelIdLegacy = ChannelIdentifier.LegacyFromNumber(request.ChannelNumber),
|
||||
request.ChannelNumber,
|
||||
HasCustomTitle = hasCustomTitle,
|
||||
displayItem.CustomTitle,
|
||||
@@ -686,6 +693,8 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
|
||||
{
|
||||
ProgrammeStart = start,
|
||||
ProgrammeStop = stop,
|
||||
ChannelId = ChannelIdentifier.FromNumber(request.ChannelNumber),
|
||||
ChannelIdLegacy = ChannelIdentifier.LegacyFromNumber(request.ChannelNumber),
|
||||
request.ChannelNumber,
|
||||
HasCustomTitle = hasCustomTitle,
|
||||
displayItem.CustomTitle,
|
||||
@@ -738,6 +747,8 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
|
||||
{
|
||||
ProgrammeStart = start,
|
||||
ProgrammeStop = stop,
|
||||
ChannelId = ChannelIdentifier.FromNumber(request.ChannelNumber),
|
||||
ChannelIdLegacy = ChannelIdentifier.LegacyFromNumber(request.ChannelNumber),
|
||||
request.ChannelNumber,
|
||||
HasCustomTitle = hasCustomTitle,
|
||||
displayItem.CustomTitle,
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Xml;
|
||||
using Dapper;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Core.Iptv;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -78,6 +79,8 @@ public class RefreshChannelListHandler : IRequestHandler<RefreshChannelList>
|
||||
{
|
||||
var data = new
|
||||
{
|
||||
ChannelId = ChannelIdentifier.FromNumber(channel.Number),
|
||||
ChannelIdLegacy = ChannelIdentifier.LegacyFromNumber(channel.Number),
|
||||
ChannelNumber = channel.Number,
|
||||
ChannelName = channel.Name,
|
||||
ChannelCategories = GetCategories(channel.Categories),
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using ErsatzTV.Core.Iptv;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ErsatzTV.Core.Tests.Iptv;
|
||||
|
||||
[TestFixture]
|
||||
public class ChannelIdentifierTests
|
||||
{
|
||||
[TestCase("1.23", "1.23.etv")]
|
||||
[TestCase("12.3", "12.3.etv")]
|
||||
[TestCase("123", "123.etv")]
|
||||
[TestCase("1.24", "1.24.etv")]
|
||||
[TestCase("12.4", "12.4.etv")]
|
||||
[TestCase("124", "124.etv")]
|
||||
public void TestLegacy(string channelNumber, string expected)
|
||||
{
|
||||
string actual = ChannelIdentifier.LegacyFromNumber(channelNumber);
|
||||
actual.Should().Be(expected);
|
||||
}
|
||||
|
||||
[TestCase("1.23", "C1.23.150.ersatztv.org")]
|
||||
[TestCase("12.3", "C12.3.198.ersatztv.org")]
|
||||
[TestCase("123", "C123.246.ersatztv.org")]
|
||||
[TestCase("1.24", "C1.24.151.ersatztv.org")]
|
||||
[TestCase("12.4", "C12.4.199.ersatztv.org")]
|
||||
[TestCase("124", "C124.247.ersatztv.org")]
|
||||
public void TestNew(string channelNumber, string expected)
|
||||
{
|
||||
string actual = ChannelIdentifier.FromNumber(channelNumber);
|
||||
actual.Should().Be(expected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace ErsatzTV.Core.Iptv;
|
||||
|
||||
public static class ChannelIdentifier
|
||||
{
|
||||
public static string LegacyFromNumber(string channelNumber)
|
||||
{
|
||||
return $"{channelNumber}.etv";
|
||||
}
|
||||
|
||||
public static string FromNumber(string channelNumber)
|
||||
{
|
||||
// get rid of any decimal (only two are allowed)
|
||||
int number = (int)(decimal.Parse(channelNumber, CultureInfo.InvariantCulture) * 100);
|
||||
int id = 0;
|
||||
while (number != 0)
|
||||
{
|
||||
id += number % 10 + 48;
|
||||
number /= 10;
|
||||
}
|
||||
|
||||
return $"C{channelNumber}.{id}.ersatztv.org";
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ public class ChannelPlaylist
|
||||
|
||||
sb.AppendLine(
|
||||
CultureInfo.InvariantCulture,
|
||||
$"#EXTINF:0 tvg-id=\"{channel.Number}.etv\" channel-id=\"{shortUniqueId}\" channel-number=\"{channel.Number}\" CUID=\"{shortUniqueId}\" tvg-chno=\"{channel.Number}\" tvg-name=\"{channel.Name}\" tvg-logo=\"{logo}\" group-title=\"{channel.Group}\" tvc-stream-vcodec=\"{vcodec}\" tvc-stream-acodec=\"{acodec}\", {channel.Name}");
|
||||
$"#EXTINF:0 tvg-id=\"{ChannelIdentifier.FromNumber(channel.Number)}\" channel-id=\"{shortUniqueId}\" channel-number=\"{channel.Number}\" CUID=\"{shortUniqueId}\" tvg-chno=\"{channel.Number}\" tvg-name=\"{channel.Name}\" tvg-logo=\"{logo}\" group-title=\"{channel.Group}\" tvc-stream-vcodec=\"{vcodec}\" tvc-stream-acodec=\"{acodec}\", {channel.Name}");
|
||||
sb.AppendLine(
|
||||
CultureInfo.InvariantCulture,
|
||||
$"{_scheme}://{_host}{_baseUrl}/iptv/channel/{channel.Number}.{format}");
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{{ ##
|
||||
|
||||
Available values:
|
||||
- channel_id
|
||||
- channel_id_legacy
|
||||
- channel_number
|
||||
- channel_name
|
||||
- channel_categories
|
||||
@@ -16,7 +18,7 @@ The resulting XML will be minified by ErsatzTV - so feel free to keep things nic
|
||||
|
||||
## }}
|
||||
|
||||
<channel id="{{ channel_number }}.etv">
|
||||
<channel id="{{ channel_id }}">
|
||||
<display-name>{{ channel_number }} {{ channel_name }}</display-name>
|
||||
<display-name>{{ channel_number }}</display-name>
|
||||
<display-name>{{ channel_name }}</display-name>
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
Available values:
|
||||
- programme_start
|
||||
- programme_stop
|
||||
- channel_id
|
||||
- channel_id_legacy
|
||||
- channel_number
|
||||
- has_custom_title
|
||||
- custom_title
|
||||
@@ -27,7 +29,7 @@ The resulting XML will be minified by ErsatzTV - so feel free to keep things nic
|
||||
|
||||
## }}
|
||||
|
||||
<programme start="{{ programme_start }}" stop="{{ programme_stop }}" channel="{{ channel_number }}.etv">
|
||||
<programme start="{{ programme_start }}" stop="{{ programme_stop }}" channel="{{ channel_id }}">
|
||||
{{ if has_custom_title }}
|
||||
<title lang="en">{{ custom_title }}</title>
|
||||
{{ else }}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
Available values:
|
||||
- programme_start
|
||||
- programme_stop
|
||||
- channel_id
|
||||
- channel_id_legacy
|
||||
- channel_number
|
||||
- has_custom_title
|
||||
- custom_title
|
||||
@@ -22,7 +24,7 @@ The resulting XML will be minified by ErsatzTV - so feel free to keep things nic
|
||||
|
||||
## }}
|
||||
|
||||
<programme start="{{ programme_start }}" stop="{{ programme_stop }}" channel="{{ channel_number }}.etv">
|
||||
<programme start="{{ programme_start }}" stop="{{ programme_stop }}" channel="{{ channel_id }}">
|
||||
{{ if has_custom_title }}
|
||||
<title lang="en">{{ custom_title }}</title>
|
||||
{{ else }}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
Available values:
|
||||
- programme_start
|
||||
- programme_stop
|
||||
- channel_id
|
||||
- channel_id_legacy
|
||||
- channel_number
|
||||
- has_custom_title
|
||||
- custom_title
|
||||
@@ -30,7 +32,7 @@ The resulting XML will be minified by ErsatzTV - so feel free to keep things nic
|
||||
|
||||
## }}
|
||||
|
||||
<programme start="{{ programme_start }}" stop="{{ programme_stop }}" channel="{{ channel_number }}.etv">
|
||||
<programme start="{{ programme_start }}" stop="{{ programme_stop }}" channel="{{ channel_id }}">
|
||||
{{ if has_custom_title }}
|
||||
<title lang="en">{{ custom_title }}</title>
|
||||
{{ else }}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
Available values:
|
||||
- programme_start
|
||||
- programme_stop
|
||||
- channel_id
|
||||
- channel_id_legacy
|
||||
- channel_number
|
||||
- has_custom_title
|
||||
- custom_title
|
||||
@@ -19,7 +21,7 @@ The resulting XML will be minified by ErsatzTV - so feel free to keep things nic
|
||||
|
||||
## }}
|
||||
|
||||
<programme start="{{ programme_start }}" stop="{{ programme_stop }}" channel="{{ channel_number }}.etv">
|
||||
<programme start="{{ programme_start }}" stop="{{ programme_stop }}" channel="{{ channel_id }}">
|
||||
{{ if has_custom_title }}
|
||||
<title lang="en">{{ custom_title }}</title>
|
||||
{{ else }}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
Available values:
|
||||
- programme_start
|
||||
- programme_stop
|
||||
- channel_id
|
||||
- channel_id_legacy
|
||||
- channel_number
|
||||
- has_custom_title
|
||||
- custom_title
|
||||
@@ -28,7 +30,7 @@ The resulting XML will be minified by ErsatzTV - so feel free to keep things nic
|
||||
|
||||
## }}
|
||||
|
||||
<programme start="{{ programme_start }}" stop="{{ programme_stop }}" channel="{{ channel_number }}.etv">
|
||||
<programme start="{{ programme_start }}" stop="{{ programme_stop }}" channel="{{ channel_id }}">
|
||||
{{ if has_custom_title }}
|
||||
<title lang="en">{{ custom_title }}</title>
|
||||
{{ else }}
|
||||
|
||||
Reference in New Issue
Block a user