From d9f9b490e5f8b1c2ce5eb1ed2c8e6e1d477748f3 Mon Sep 17 00:00:00 2001 From: Horst Beham Date: Sun, 19 Sep 2021 23:32:38 +0200 Subject: [PATCH] - Panasonic: added exerimental support for channel\_list.xml lists (Panasonic Android TVs, 2019 and later) Unfortunately the only information included in this format is the channel number and a truncated channel name. --- .../ChanSort.Loader.Panasonic.csproj | 4 + .../ChanSort.Loader.Panasonic.ini | 3 + .../PanasonicPlugin.cs | 21 +- .../XmlSerializer.cs | 182 ++++++++++++++++++ source/ChanSort/ReferenceListForm.cs | 2 +- source/changelog.md | 4 +- 6 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 source/ChanSort.Loader.Panasonic/ChanSort.Loader.Panasonic.ini create mode 100644 source/ChanSort.Loader.Panasonic/XmlSerializer.cs diff --git a/source/ChanSort.Loader.Panasonic/ChanSort.Loader.Panasonic.csproj b/source/ChanSort.Loader.Panasonic/ChanSort.Loader.Panasonic.csproj index 1335b0e..74eaa91 100644 --- a/source/ChanSort.Loader.Panasonic/ChanSort.Loader.Panasonic.csproj +++ b/source/ChanSort.Loader.Panasonic/ChanSort.Loader.Panasonic.csproj @@ -101,6 +101,7 @@ + @@ -111,6 +112,9 @@ + + Always + diff --git a/source/ChanSort.Loader.Panasonic/ChanSort.Loader.Panasonic.ini b/source/ChanSort.Loader.Panasonic/ChanSort.Loader.Panasonic.ini new file mode 100644 index 0000000..1d0aa92 --- /dev/null +++ b/source/ChanSort.Loader.Panasonic/ChanSort.Loader.Panasonic.ini @@ -0,0 +1,3 @@ +[channel_list.xml] +reorderRecordsByChannelNumber=true +setIsModified=false diff --git a/source/ChanSort.Loader.Panasonic/PanasonicPlugin.cs b/source/ChanSort.Loader.Panasonic/PanasonicPlugin.cs index 50b1b02..6e99643 100644 --- a/source/ChanSort.Loader.Panasonic/PanasonicPlugin.cs +++ b/source/ChanSort.Loader.Panasonic/PanasonicPlugin.cs @@ -1,15 +1,30 @@ -using ChanSort.Api; +using System.IO; +using System.Text; +using ChanSort.Api; namespace ChanSort.Loader.Panasonic { public class PanasonicPlugin : ISerializerPlugin { public string DllName { get; set; } - public string PluginName => "Panasonic (*.db, *.bin)"; - public string FileFilter => "*.db;*.bin"; + public string PluginName => "Panasonic (*.db, *.bin, *.xml)"; + public string FileFilter => "*.db;*.bin;*.xml"; public SerializerBase CreateSerializer(string inputFile) { + if (Path.GetExtension(inputFile).ToLowerInvariant() == ".xml") + { + var data = File.ReadAllBytes(inputFile); + var header = Encoding.ASCII.GetBytes("\n channelLists = new(); + public XmlDocument doc; + public readonly IniFile ini; + + #region ctor() + public XmlSerializer(string inputFile) : base(inputFile) + { + this.Features.ChannelNameEdit = ChannelNameEditMode.None; + this.Features.CanSkipChannels = false; + this.Features.CanLockChannels = false; + this.Features.CanHideChannels = false; + this.Features.DeleteMode = DeleteMode.NotSupported; + this.Features.CanSaveAs = true; + this.Features.AllowGapsInFavNumbers = true; + this.Features.CanEditFavListNames = false; + + string iniFile = Assembly.GetExecutingAssembly().Location.Replace(".dll", ".ini"); + this.ini = new IniFile(iniFile); + } + #endregion + + #region GetOrCreateList() + private ChannelList GetOrCreateList(string name) + { + if (this.channelLists.TryGetValue(name, out var list)) + return list; + + list = new ChannelList(SignalSource.All, name); + this.channelLists[name] = list; + var cols = list.VisibleColumnFieldNames; + cols.Clear(); + cols.Add("Position"); + cols.Add("OldPosition"); + cols.Add(nameof(ChannelInfo.Name)); + cols.Add(nameof(ChannelInfo.ShortName)); + this.DataRoot.AddChannelList(list); + return list; + } + #endregion + + #region Load() + public override void Load() + { + bool fail = false; + try + { + doc = new XmlDocument(); + var content = File.ReadAllBytes(this.FileName); + var textContent = Encoding.UTF8.GetString(content); + + var settings = new XmlReaderSettings + { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + ValidationFlags = XmlSchemaValidationFlags.None, + DtdProcessing = DtdProcessing.Ignore + }; + using var reader = XmlReader.Create(new StringReader(textContent), settings); + doc.Load(reader); + } + catch + { + fail = true; + } + + var root = doc.FirstChild; + if (root is XmlDeclaration) + root = root.NextSibling; + if (fail || root == null || root.LocalName != "ChannelList" || !root.HasChildNodes || root.ChildNodes[0].LocalName != "ChannelInfo") + throw new FileLoadException("File is not a supported Panasonic XML file"); + + foreach (XmlNode child in root.ChildNodes) + { + switch (child.LocalName) + { + case "ChannelInfo": + this.ReadChannel(child); + break; + } + } + } + #endregion + + #region ReadChannel() + private void ReadChannel(XmlNode node) + { + var channelType = node.Attributes?["ChannelType"]?.InnerText; + var list = this.GetOrCreateList(channelType); + var chan = new XmlChannel(list.Count, node); + DataRoot.AddChannel(list, chan); + } + #endregion + + + #region Save() + + public override void Save(string tvOutputFile) + { + var sec = ini.GetSection("channel_list.xml"); + var reorder = sec?.GetBool("reorderRecordsByChannelNumber", true) ?? true; + var setIsModified = sec?.GetBool("setIsModified", false) ?? false; + + foreach (var list in this.DataRoot.ChannelLists) + { + var seq = reorder ? list.Channels.OrderBy(c => c.NewProgramNr).ThenBy(c => c.RecordIndex).ToList() : list.Channels; + XmlNode prevNode = null; + foreach (var chan in seq) + { + if (chan is not XmlChannel ch) + continue; + ch.Node.Attributes["ChannelNumber"].InnerText = ch.NewProgramNr.ToString(); + if (setIsModified && ch.NewProgramNr != ch.OldProgramNr) + ch.Node.Attributes["IsModified"].InnerText = "1"; + if (reorder) + { + var parent = ch.Node.ParentNode; + parent.RemoveChild(ch.Node); + parent.InsertAfter(ch.Node, prevNode); + prevNode = ch.Node; + } + } + } + + var xmlSettings = new XmlWriterSettings(); + xmlSettings.Encoding = new UTF8Encoding(false); + xmlSettings.CheckCharacters = false; + xmlSettings.Indent = true; + xmlSettings.IndentChars = ""; + xmlSettings.NewLineHandling = NewLineHandling.None; + xmlSettings.NewLineChars = "\n"; + xmlSettings.OmitXmlDeclaration = true; + using var w = XmlWriter.Create(tvOutputFile, xmlSettings); + doc.WriteTo(w); + this.FileName = tvOutputFile; + } + + #endregion + + + #region class XmlChannel + + class XmlChannel : ChannelInfo + { + internal XmlNode Node; + + public XmlChannel(int index, XmlNode node) : base(0, index, 0, null) + { + this.Node = node; + + this.OldProgramNr = int.Parse(node.Attributes["ChannelNumber"]?.InnerText); + this.Name = node.Attributes["ChannelName"].InnerText; + var svlId = node.Attributes["SvlId"].InnerText; + this.ShortName = $"SvlId: {svlId}"; + } + + } + #endregion + } +} diff --git a/source/ChanSort/ReferenceListForm.cs b/source/ChanSort/ReferenceListForm.cs index bf30711..ec30950 100644 --- a/source/ChanSort/ReferenceListForm.cs +++ b/source/ChanSort/ReferenceListForm.cs @@ -48,7 +48,7 @@ namespace ChanSort.Ui if (disposing) { this.components?.Dispose(); - this.serializer.Dispose(); + this.serializer?.Dispose(); } base.Dispose(disposing); } diff --git a/source/changelog.md b/source/changelog.md index b00962c..e084ef0 100644 --- a/source/changelog.md +++ b/source/changelog.md @@ -7,8 +7,8 @@ ChanSort Change Log - Philips: improved experimental support for Philips FLASH\_\*/\*.db file formats (read-only by default, can be enabled in Philips.ini for testing) - added Polish readme and updated translation (by JakubDriver) - -2021-09-13 +- Panasonic: added exerimental support for channel\_list.xml lists (Panasonic Android TVs, 2019 and later) + Unfortunately the only information included in this format is the channel number and a truncated channel name. - column order is now preserved between program starts even when lists with different supported columns were loaded and columns reordered. - added option to enabled/disable auto-loading of the last opened list when starting the program