- Philips ChannelMap_45: TV did not remember last selected favorite list when first fav list was created by ChanSort.

- Philips ChannelMap_100 and later: "Channel" XML elements inside the DVB*.xml files are now reordered by program nr.
- Philips ChannelMap_105 and 110: fixed saving favorite lists (keeping FavoriteNumber="0" in DVB*.xml and only
  setting the numbers in Favorites.xml)
- m3u: keep original end-of-line characters (CRLF or LF)
- m3u: detect whether channel names are prefixed with a program number or not, and save the file in the same way.
This commit is contained in:
Horst Beham
2021-02-24 11:05:47 +01:00
parent fb37239685
commit bc4b650f20
6 changed files with 182 additions and 81 deletions

View File

@@ -90,6 +90,20 @@ namespace ChanSort.Api
}
#endregion
#region GetBool()
public bool GetBool(string key, bool defaultValue = false)
{
var val = GetString(key)?.ToLowerInvariant();
if (val == null)
return defaultValue;
if (val == "false" || val == "off" || val == "no" || val == "0")
return false;
if (val == "true" || val == "on" || val == "yes" || val == "1")
return true;
return defaultValue;
}
#endregion
#region ParseNumber()
private int ParseNumber(string value)
{

View File

@@ -20,6 +20,7 @@ namespace ChanSort.Loader.M3u
private Encoding overrideEncoding;
private string newLine = "\r\n";
private bool allChannelsPrefixedWithProgNr = true;
private List<string> headerLines = new List<string>();
private List<string> trailingLines = new List<string>(); // comment and blank lines after the last URI line
@@ -55,7 +56,7 @@ namespace ChanSort.Loader.M3u
overrideEncoding = new UTF8Encoding(false);
// detect line separator
int idx = Array.IndexOf(content, '\n');
int idx = Array.IndexOf(content, (byte)'\n');
this.newLine = idx >= 1 && content[idx-1] == '\r' ? "\r\n" : "\n";
var rdr = new StreamReader(new MemoryStream(content), overrideEncoding ?? this.DefaultEncoding);
@@ -126,6 +127,7 @@ namespace ChanSort.Loader.M3u
if (extInfLine != null)
{
bool extInfContainsProgNr = false;
extInfTrackNameIndex = FindExtInfTrackName(extInfLine);
if (extInfTrackNameIndex >= 0)
{
@@ -135,10 +137,13 @@ namespace ChanSort.Loader.M3u
{
progNr = this.ParseInt(match.Groups[1].Value);
name = match.Groups[2].Value;
extInfContainsProgNr = true;
}
}
this.allChannelsPrefixedWithProgNr &= extInfContainsProgNr;
}
if (progNr == 0)
progNr = this.allChannels.Count + 1;
@@ -231,7 +236,10 @@ namespace ChanSort.Loader.M3u
foreach (var line in chan.Lines)
{
if (line.StartsWith("#EXTINF:"))
file.WriteLine($"{line.Substring(0, chan.ExtInfTrackNameIndex)}{chan.NewProgramNr}. {chan.Name}");
{
var progNrPrefix = this.allChannelsPrefixedWithProgNr ? chan.NewProgramNr + ". " : "";
file.WriteLine($"{line.Substring(0, chan.ExtInfTrackNameIndex)}{progNrPrefix}{chan.Name}");
}
else
file.WriteLine(line);
}

View File

@@ -56,9 +56,9 @@ namespace ChanSort.Loader.Philips
private ChanLstBin chanLstBin;
private readonly StringBuilder logMessages = new StringBuilder();
private readonly List<int> favListIndexToId = new();
private readonly Dictionary<int, int> favListIdToIndex = new();
private readonly ChannelList favChannels = new ChannelList(SignalSource.All, "Favorites");
private const int FavListCount = 8;
private bool mustFixFavListIds;
#region ctor()
public BinarySerializer(string inputFile) : base(inputFile)
@@ -647,40 +647,45 @@ namespace ChanSort.Loader.Philips
this.favChannels.AddChannel(chan);
}
}
this.Features.SupportedFavorites = (Favorites)0xFF;
this.Features.SortedFavorites = true;
this.Features.MixedSourceFavorites = true;
this.Features.AllowGapsInFavNumbers = false;
using var conn = new SQLiteConnection($"Data Source={listDb}");
conn.Open();
// older versions of ChanSort wrote invalid "list_id" values starting at 0 instead of 1 and going past 8.
// if everything is in the range of 1-8, this code keeps the current ids. otherwise it remaps them to 1-8.
using var cmd = conn.CreateCommand();
cmd.CommandText = "select list_id, list_name from List";
this.Features.SupportedFavorites = 0;
this.Features.SortedFavorites = true;
cmd.CommandText = "select min(list_id), max(list_id) from List";
using (var r = cmd.ExecuteReader())
{
int i = 0;
while (r.Read() && i < ChannelInfo.MAX_FAV_LISTS)
{
this.favListIndexToId.Add(r.GetInt32(0));
this.favListIdToIndex.Add(r.GetInt32(0), i);
this.DataRoot.SetFavListCaption(i, r.GetString(1));
this.Features.SupportedFavorites |= (Favorites) (1 << i);
i++;
}
r.Read();
mustFixFavListIds = !r.IsDBNull(0) && (r.GetInt16(0) < 1 || r.GetInt16(1) > 8);
if (mustFixFavListIds)
logMessages.AppendLine("invalid list_id values in list.db will be corrected");
}
for (; i < 8; i++)
cmd.CommandText = "select list_id, list_name from List order by list_id";
var listIds = new List<int>();
using (var r = cmd.ExecuteReader())
{
var listIndex = 0;
while (r.Read())
{
this.favListIndexToId.Add(-i-1);
this.favListIdToIndex.Add(-i-1, i);
this.DataRoot.SetFavListCaption(i, "Fav " + (i+1));
this.Features.SupportedFavorites |= (Favorites)(1 << i);
var listId = r.GetInt16(0);
listIds.Add(listId);
if (!this.mustFixFavListIds)
listIndex = listId - 1;
this.DataRoot.SetFavListCaption(listIndex, r.GetString(1));
++listIndex;
}
}
for (int listIndex = 0; listIndex < this.favListIndexToId.Count; listIndex++)
for (int listIndex = 0; listIndex < listIds.Count; listIndex++)
{
cmd.CommandText = $"select channel_id from FavoriteChannels where fav_list_id={favListIndexToId[listIndex]} order by rank";
cmd.CommandText = $"select channel_id from FavoriteChannels where fav_list_id={listIds[listIndex]} order by rank";
using var r = cmd.ExecuteReader();
int seq = 0;
while (r.Read())
@@ -997,29 +1002,30 @@ namespace ChanSort.Loader.Philips
conn.Open();
using var trans = conn.BeginTransaction();
using var cmd = conn.CreateCommand();
for (int favListIndex = 0; favListIndex < this.favListIndexToId.Count; favListIndex++)
cmd.CommandText = "delete from FavoriteChannels";
cmd.ExecuteNonQuery();
if (this.mustFixFavListIds)
{
var favListId = favListIndexToId[favListIndex];
string sqlInsertOrUpdateList;
if (favListId >= 0)
{
cmd.CommandText = $"delete from FavoriteChannels where fav_list_id={favListId}";
cmd.ExecuteNonQuery();
sqlInsertOrUpdateList = "update List set list_name=@name, list_version=list_version+1 where list_id=@id";
}
else
{
favListId = favListIndexToId.Count == 0 ? 1 : favListIndexToId.Max() + 1;
favListIndexToId[favListIndex] = favListId;
favListIdToIndex[favListId] = favListIndex;
sqlInsertOrUpdateList = "insert into List (list_id, list_name, list_version) values (@id,@name,1)";
}
cmd.CommandText = "delete from List";
cmd.ExecuteNonQuery();
}
var incFavList = (ini.GetSection("Map" + chanLstBin.VersionMajor)?.GetBool("incrementFavListVersion", true) ?? true)
? ", list_version=list_version+1"
: "";
for (int favListIndex = 0; favListIndex < FavListCount; favListIndex++)
{
var favListId = favListIndex + 1;
cmd.CommandText = $"select count(1) from List where list_id={favListId}";
cmd.CommandText = (long) cmd.ExecuteScalar() == 0 ?
"insert into List (list_id, list_name, list_version) values (@id,@name,1)" :
"update List set list_name=@name" + incFavList + " where list_id=@id";
cmd.CommandText = sqlInsertOrUpdateList;
cmd.Parameters.Add(new SQLiteParameter("@id", DbType.Int16));
cmd.Parameters.Add(new SQLiteParameter("@name", DbType.String));
cmd.Parameters["@id"].Value = favListId;
cmd.Parameters["@name"].Value = DataRoot.GetFavListCaption(favListIndex);
cmd.Parameters["@name"].Value = DataRoot.GetFavListCaption(favListIndex) ?? "Fav " + (favListIndex + 1);
cmd.ExecuteNonQuery();
cmd.CommandText = "insert into FavoriteChannels(fav_list_id, channel_id, rank) values (@listId,@channelId,@rank)";
@@ -1049,7 +1055,7 @@ namespace ChanSort.Loader.Philips
cmd.ExecuteNonQuery();
// make sure the last_watched_channel_id is valid in the list
cmd.CommandText = "update List set last_watched_channel_id=(select min(channel_id) from FavoriteChannels f where f.fav_list_id=List.list_id)";
cmd.CommandText = @"update List set last_watched_channel_id=(select channel_id from FavoriteChannels f where f.fav_list_id=List.list_id order by rank limit 1) where last_watched_channel_id not in (select channel_id from FavoriteChannels f where f.fav_list_id=List.list_id)";
cmd.ExecuteNonQuery();
trans.Commit();

View File

@@ -88,3 +88,17 @@ lenSatName=64
offUnk1=48
offUnk2=68
offPolarity=72
[Map45]
incrementFavListVersion=true
[Map100]
setFavoriteNumber=true
[Map105]
incrementFavListVersion=true
setFavoriteNumber=false
[Map110]
incrementFavListVersion=true
setFavoriteNumber=false

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Schema;
@@ -59,6 +60,7 @@ namespace ChanSort.Loader.Philips
private readonly List<FileData> fileDataList = new List<FileData>();
private ChanLstBin chanLstBin;
private readonly StringBuilder logMessages = new StringBuilder();
private readonly IniFile ini;
#region ctor()
@@ -103,6 +105,9 @@ namespace ChanSort.Loader.Philips
this.favChannels.IsMixedSourceFavoritesList = true;
string iniFile = Assembly.GetExecutingAssembly().Location.Replace(".dll", ".ini");
this.ini = new IniFile(iniFile);
}
#endregion
@@ -490,42 +495,23 @@ namespace ChanSort.Loader.Philips
}
foreach (var file in this.fileDataList)
{
if (Path.GetFileName(file.path).ToLowerInvariant().StartsWith("dvb"))
this.ReorderNodes(file);
this.SaveFile(file);
}
this.chanLstBin?.Save(this.FileName);
}
#endregion
#region SaveFile()
private void SaveFile(FileData file)
{
// by default .NET reformats the whole XML. These settings produce almost same format as the TV xml files use
var xmlSettings = new XmlWriterSettings();
xmlSettings.Encoding = new UTF8Encoding(false);
xmlSettings.CheckCharacters = false;
xmlSettings.Indent = true;
xmlSettings.IndentChars = file.indent;
xmlSettings.NewLineHandling = NewLineHandling.None;
xmlSettings.NewLineChars = file.newline;
xmlSettings.OmitXmlDeclaration = false;
string xml;
using (var sw = new StringWriter())
using (var w = new CustomXmlWriter(sw, xmlSettings, false))
{
file.doc.WriteTo(w);
w.Flush();
xml = sw.ToString();
}
var enc = new UTF8Encoding(false, false);
File.WriteAllText(file.path, xml, enc);
}
#endregion
#region UpdateChannelList()
private void UpdateChannelList(ChannelList list)
{
var sec = ini.GetSection("Map" + (this.chanLstBin?.VersionMajor ?? 0));
var setFavoriteNumber = sec?.GetBool("setFavoriteNumber", false) ?? false;
foreach (var channel in list.Channels)
{
var ch = channel as Channel;
@@ -539,7 +525,7 @@ namespace ChanSort.Loader.Philips
}
if (ch.Format == 1)
this.UpdateChannelFormat1(ch);
this.UpdateChannelFormat1(ch, setFavoriteNumber);
else if (ch.Format == 2)
this.UpdateChannelFormat2(ch);
}
@@ -547,22 +533,28 @@ namespace ChanSort.Loader.Philips
#endregion
#region UpdateChannelFormat1 and 2
private void UpdateChannelFormat1(Channel ch)
private void UpdateChannelFormat1(Channel ch, bool setFavoriteNumber)
{
ch.SetupNode.Attributes["ChannelNumber"].Value = ch.NewProgramNr.ToString();
var attr = ch.SetupNode.Attributes["UserReorderChannel"]; // introduced with format 110
if (attr != null)
attr.InnerText = "1";
if (ch.IsNameModified)
{
ch.SetupNode.Attributes["ChannelName"].InnerText = EncodeName(ch.Name, (ch.SetupNode.Attributes["ChannelName"].InnerText.Length + 1) / 5, true);
attr = ch.SetupNode.Attributes["UserModifiedName"];
var attr = ch.SetupNode.Attributes["UserModifiedName"];
if (attr != null)
attr.InnerText = "1";
}
ch.SetupNode.Attributes["FavoriteNumber"].Value = Math.Max(ch.FavIndex[0], 0).ToString();
// ChannelMap_100 supports a single fav list and stores the favorite number directly here in the channel.
// ChannelMap_105 and later always store the value 0 in the channel and instead use a separate Favorites.xml file.
ch.SetupNode.Attributes["FavoriteNumber"].Value = setFavoriteNumber ? Math.Max(ch.FavIndex[0], 0).ToString() : "0";
if (ch.OldProgramNr != ch.NewProgramNr)
{
var attr = ch.SetupNode.Attributes["UserReorderChannel"]; // introduced with format 110, but not always present
if (attr != null)
attr.InnerText = "1";
}
}
private void UpdateChannelFormat2(Channel ch)
@@ -588,10 +580,14 @@ namespace ChanSort.Loader.Philips
var attr = favListNode.Attributes?["Name"];
if (attr != null)
attr.InnerText = EncodeName(this.DataRoot.GetFavListCaption(index - 1), (attr.InnerText.Length + 1)/5, false);
attr = favListNode.Attributes?["Version"];
if (attr != null && int.TryParse(attr.Value, out var version))
attr.InnerText = (version + 1).ToString();
// increment fav list version, unless disabled in .ini file
if (chanLstBin != null && (ini.GetSection("Map" + chanLstBin.VersionMajor)?.GetBool("incrementFavListVersion", true) ?? true))
{
attr = favListNode.Attributes?["Version"];
if (attr != null && int.TryParse(attr.Value, out var version))
attr.InnerText = (version + 1).ToString();
}
foreach (var ch in favChannels.Channels.OrderBy(ch => ch.GetPosition(index)))
{
@@ -628,6 +624,61 @@ namespace ChanSort.Loader.Philips
}
#endregion
#region ReorderNodes
private void ReorderNodes(FileData file)
{
if (file.formatVersion != 1)
return;
var nodes = file.doc.DocumentElement.GetElementsByTagName("Channel");
var list = new List<XmlElement>();
foreach(var node in nodes)
list.Add((XmlElement)node);
foreach (var node in list)
file.doc.DocumentElement.RemoveChild(node);
foreach(var node in list.OrderBy(elem => int.Parse(elem["Setup"].Attributes["ChannelNumber"].InnerText)))
file.doc.DocumentElement.AppendChild(node);
}
#endregion
#region SaveFile()
private void SaveFile(FileData file)
{
// by default .NET reformats the whole XML. These settings produce almost same format as the TV xml files use
var xmlSettings = new XmlWriterSettings();
xmlSettings.Encoding = new UTF8Encoding(false);
xmlSettings.CheckCharacters = false;
xmlSettings.Indent = true;
xmlSettings.IndentChars = file.indent;
xmlSettings.NewLineHandling = NewLineHandling.None;
xmlSettings.NewLineChars = file.newline;
xmlSettings.OmitXmlDeclaration = true;
string xml;
using (var sw = new StringWriter())
{
// write unmodified XML declaration (the DVB*.xml files use a different one than the Favorite.xml file)
var i = file.textContent.IndexOf("?>");
if (i >= 0)
sw.Write(file.textContent.Substring(0, i + 2 + file.newline.Length));
using (var w = new CustomXmlWriter(sw, xmlSettings, false))
{
file.doc.WriteTo(w);
w.Flush();
xml = sw.ToString();
}
}
// append trailing newline, if the original file had one
if (file.textContent.EndsWith(file.newline) && !xml.EndsWith(file.newline))
xml += file.newline;
var enc = new UTF8Encoding(false, false);
File.WriteAllText(file.path, xml, enc);
}
#endregion
public override string GetFileInformation()
{
return base.GetFileInformation() + this.logMessages.Replace("\n", "\r\n");

View File

@@ -1,6 +1,14 @@
ChanSort Change Log
===================
2021-02-24
- Philips ChannelMap\_45: TV did not remember last selected favorite list when first fav list was created by ChanSort.
- Philips ChannelMap\_100 and later: "Channel" XML elements inside the DVB\*.xml files are now reordered by program nr.
- Philips ChannelMap\_105 and 110: fixed saving favorite lists (keeping FavoriteNumber="0" in DVB\*.xml and only
setting the numbers in Favorites.xml)
- m3u: keep original end-of-line characters (CRLF or LF)
- m3u: detect whether channel names are prefixed with a program number or not, and save the file in the same way.
2021-02-17_2
- Philips ChannelMap\_105 and 110: fixed broken favorites.xml file and DVB\*.xml when channels were renamed