diff --git a/source/ChanSort.Api/Controller/SerializerBase.cs b/source/ChanSort.Api/Controller/SerializerBase.cs index 37dacae7..dcb3347c 100644 --- a/source/ChanSort.Api/Controller/SerializerBase.cs +++ b/source/ChanSort.Api/Controller/SerializerBase.cs @@ -38,6 +38,8 @@ public class SupportedFeatures public bool MixedSourceFavorites { get; set; } public bool AllowGapsInFavNumbers { get; set; } public bool CanEditFavListNames { get; set; } + + public bool CanEditAudioPid { get; set; } } #endregion diff --git a/source/ChanSort.Loader.GlobalClone/GcJsonSerializer.cs b/source/ChanSort.Loader.GlobalClone/GcJsonSerializer.cs index 95cef61b..8656b7e4 100644 --- a/source/ChanSort.Loader.GlobalClone/GcJsonSerializer.cs +++ b/source/ChanSort.Loader.GlobalClone/GcJsonSerializer.cs @@ -31,6 +31,7 @@ public GcJsonSerializer(string filename, string content) : base(filename) this.Features.CanHideChannels = true; this.Features.CanSkipChannels = true; this.Features.CanLockChannels = true; + this.Features.CanEditAudioPid = false; this.DataRoot.AddChannelList(new ChannelList(SignalSource.AnalogT | SignalSource.Tv | SignalSource.Data, "Analog Antenna")); this.DataRoot.AddChannelList(new ChannelList(SignalSource.DvbT | SignalSource.Tv | SignalSource.Data, "DVB-T TV")); @@ -72,7 +73,7 @@ public override void Load() var dlg = View.Default.CreateActionBox("!!! WARNING !!!\n\n" + "Support for LG webOS 5 channel lists is experimental only!\n" + - "There is a HIGH RISK that your TV will not import the list correctly and you need to run a new search or even reset the TV.\n" + + "There is a RISK that your TV will not import the list correctly and you need to run a new search or even reset the TV.\n" + "Please read the information on github with steps that MAY lead to a successful import.\n" + "Any feedback about failure or success is highly appreciated."); dlg.AddAction("Read information about webOS 5 support on github.com", 1); @@ -267,6 +268,7 @@ private void UpdateJsonDoc() node["skipped"] = ch.Skip; node["locked"] = ch.Lock; node["Invisible"] = ch.Hidden; + node["audioPid"] = ch.AudioPid; // the only successfully imported file was one where these flags were NOT set by ChanSort // these flags do get set when changing numbers through the TV's menu, but then prevent further modifications, e.g. through an import @@ -277,7 +279,7 @@ private void UpdateJsonDoc() //} //node["disableUpdate"] = true; // No-Go! This blocked the whole list and required a factory reset. Regardless of the setting, the TV showed wrong numbers. - + //node["factoryDefault"] = true; // an exported file after manually changing numbers through the TV-menu had all channels set to userEditChNumber=true, userSelCHNo=true, factoryDefault=true; } } diff --git a/source/ChanSort.Loader.Grundig/ChanSort.Loader.Grundig.csproj b/source/ChanSort.Loader.Grundig/ChanSort.Loader.Grundig.csproj new file mode 100644 index 00000000..b30266b0 --- /dev/null +++ b/source/ChanSort.Loader.Grundig/ChanSort.Loader.Grundig.csproj @@ -0,0 +1,76 @@ + + + + + Debug + AnyCPU + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4} + Library + Properties + ChanSort.Loader.Grundig + ChanSort.Loader.Grundig + v4.8 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + latest + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + latest + + + true + ..\Debug\ + DEBUG;TRACE + full + x86 + latest + prompt + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + latest + prompt + + + + + + + + + + + + + + + + + + + + {dccffa08-472b-4d17-bb90-8f513fc01392} + ChanSort.Api + + + + \ No newline at end of file diff --git a/source/ChanSort.Loader.Grundig/Channel.cs b/source/ChanSort.Loader.Grundig/Channel.cs new file mode 100644 index 00000000..10706082 --- /dev/null +++ b/source/ChanSort.Loader.Grundig/Channel.cs @@ -0,0 +1,21 @@ +using System.Xml; +using ChanSort.Api; + +namespace ChanSort.Loader.Grundig +{ + internal class Channel : ChannelInfo + { + public readonly XmlNode Node; + public string RawName; + public string RawSatellite; + public int Format; + + internal Channel(SignalSource source, int order, int rowId, XmlNode node) + { + this.SignalSource = source; + this.RecordOrder = order; + this.RecordIndex = rowId; + this.Node = node; + } + } +} diff --git a/source/ChanSort.Loader.Grundig/Properties/AssemblyInfo.cs b/source/ChanSort.Loader.Grundig/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..98e4602b --- /dev/null +++ b/source/ChanSort.Loader.Grundig/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ChanSort.Loader.Grundig")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ChanSort.Loader.Grundig")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4d5af0a3-1b96-42c8-910d-0c4852ea22f4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/source/ChanSort.Loader.Grundig/Serializer.cs b/source/ChanSort.Loader.Grundig/Serializer.cs new file mode 100644 index 00000000..b4b84ce5 --- /dev/null +++ b/source/ChanSort.Loader.Grundig/Serializer.cs @@ -0,0 +1,401 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using ChanSort.Api; + +namespace ChanSort.Loader.Grundig +{ + class Serializer : SerializerBase + { + private readonly ChannelList terrChannels = new ChannelList(SignalSource.Antenna, "Antenna"); + private readonly ChannelList cableChannels = new ChannelList(SignalSource.Cable, "Cable"); + private readonly ChannelList satChannels = new ChannelList(SignalSource.Sat, "Satellite"); + + private readonly List fileDataList = new List(); + private readonly StringBuilder logMessages = new StringBuilder(); + + + #region ctor() + public Serializer(string inputFile) : base(inputFile) + { + this.Features.ChannelNameEdit = ChannelNameEditMode.All; + this.Features.CanSkipChannels = true; + this.Features.CanLockChannels = true; + this.Features.CanHideChannels = true; + this.Features.DeleteMode = DeleteMode.Physically; + this.Features.CanSaveAs = false; + this.Features.AllowGapsInFavNumbers = false; + this.Features.CanEditFavListNames = false; + this.Features.SortedFavorites = true; + this.Features.MixedSourceFavorites = false; + this.Features.SupportedFavorites = Favorites.A | Favorites.B | Favorites.C | Favorites.D; + + + this.DataRoot.AddChannelList(this.terrChannels); + this.DataRoot.AddChannelList(this.cableChannels); + this.DataRoot.AddChannelList(this.satChannels); + + foreach (var list in this.DataRoot.ChannelLists) + { + list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.ServiceTypeName)); + list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.PcrPid)); + list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.VideoPid)); + list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.AudioPid)); + list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.ShortName)); + } + + this.terrChannels.VisibleColumnFieldNames.Add(nameof(ChannelInfo.Source)); + this.cableChannels.VisibleColumnFieldNames.Add(nameof(ChannelInfo.Source)); + } + #endregion + + #region Load() + public override void Load() + { + // read all files from a directory structure that looks like + // My_Channellist\dvbs_config.xml + // My_Channellist\dvbc_config.xml + // My_Channellist\dvbt_config.xml + + + var dataFiles = new[] { "dvbt_config.xml", "dvbc_config.xml", "dvbs_config.xml" }; + var dir = Path.GetDirectoryName(this.FileName) ?? ""; + foreach (var file in dataFiles) + { + var fullPath = Path.GetFullPath(Path.Combine(dir, file)); + this.LoadFile(fullPath); + } + if (this.fileDataList.Count == 0) + throw new FileLoadException("No XML files found in folder structure"); + } + #endregion + + #region LoadFile() + + private void LoadFile(string fileName) + { + if (!File.Exists(fileName)) + return; + bool fail = false; + var fileData = new FileData(); + try + { + var content = File.ReadAllBytes(fileName); + fileData.path = fileName; + fileData.hasBom = content.Length >= 3 && content[0] == 0xef && content[1] == 0xbb && content[2] == 0xbf; + var textContent = Encoding.UTF8.GetString(content, fileData.hasBom ? 3 : 0, content.Length - (fileData.hasBom ? 3 : 0)); + fileData.newline = textContent.Contains("\r\n") ? "\r\n" : "\n"; + fileData.indent = textContent.Contains(" <"); + fileData.doc = new XmlDocument(); + fileData.doc.PreserveWhitespace = true; + + var settings = new XmlReaderSettings + { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + ValidationFlags = XmlSchemaValidationFlags.None, + DtdProcessing = DtdProcessing.Ignore + }; + using var reader = XmlReader.Create(new StringReader(textContent), settings); + fileData.doc.Load(reader); + } + catch + { + fail = true; + } + + var root = fileData.doc.FirstChild; + if (root is XmlDeclaration) + root = root.NextSibling; + while (root.LocalName == "#whitespace") + root = root.NextSibling; + if (fail || root == null || root.LocalName != "CONFIG") + throw new FileLoadException("\"" + fileName + "\" is not a supported Grundig XML file"); + + int transponderId = 0; + int chanId = 0; + foreach (XmlNode child in root.ChildNodes) + { + switch (child.LocalName) + { + case "Digital": + ReadDigitalChannels(child, transponderId, ref chanId); + break; + case "Analog": + ReadAnalogChannels(child, ref chanId); + break; + } + } + this.fileDataList.Add(fileData); + } + #endregion + + #region ReadAnalogChannels + + private void ReadAnalogChannels(XmlNode analog, ref int chanId) + { + SignalSource src; + var type = analog.Attributes?["type"]?.InnerText; + switch (type) + { + case "DVBT_ANALOG": + src = SignalSource.AnalogT; + break; + case "DVBC_ANALOG": + src = SignalSource.AnalogC; + break; + default: + logMessages.AppendFormat("skipped unsupported analog source type: " + type); + return; + } + + foreach (XmlNode service in analog["channels"].ChildNodes) + { + if (service.LocalName != "service") + continue; + + var c = new Channel(src, chanId, chanId, service); + c.Source = type; + c.FreqInMhz = Int32.Parse(service.Attributes["frq"].InnerText) / 20m; + c.Hidden = service.Attributes["hid"].InnerText == "1"; + ReadCommonChannelData(c, service); + var list = this.DataRoot.GetChannelList(src); + this.DataRoot.AddChannel(list, c); + } + } + #endregion + + #region ReadDigitalChannels + private void ReadDigitalChannels(XmlNode digital, int transponderId, ref int chanId) + { + SignalSource src; + decimal freqDivider; + var type = digital.Attributes?["type"]?.InnerText; + switch (type) + { + case "DVBC": + src = SignalSource.DvbC; + freqDivider = 1000; + break; + case "DVBT": + src = SignalSource.DvbT; + freqDivider = 1000; + break; + case "DVBS": + src = SignalSource.DvbS; + freqDivider = 1; + break; + default: + logMessages.AppendFormat("skipped unsupported digital source type: " + type); + return; + } + + foreach (XmlNode networkNode in digital["channels"].ChildNodes) + { + if (networkNode.LocalName != "network") + continue; + var provider = networkNode.Attributes["nwname"]?.InnerText; + foreach (XmlNode mux in networkNode.ChildNodes) + { + if (mux.LocalName != "mux") + continue; + var t = CreateTransponder(mux, ref transponderId, freqDivider); + foreach (XmlNode service in mux.ChildNodes) + { + if (service.LocalName != "service") + continue; + + var c = CreateChannel(service, src, t, ref chanId); + if (c == null) + continue; + c.Source = type; + c.Provider = provider; + + var list = this.DataRoot.GetChannelList(src); + this.DataRoot.AddChannel(list, c); + ++chanId; + } + } + } + } + #endregion + + #region CreateChannel + private Channel CreateChannel(XmlNode service, SignalSource src, Transponder transponder, ref int chanId) + { + var c = new Channel(src, chanId, chanId, service); + c.Transponder = transponder; + c.Polarity = transponder.Polarity; + c.FreqInMhz = transponder.FrequencyInMhz; + c.SymbolRate = transponder.SymbolRate; + c.OriginalNetworkId = transponder.OriginalNetworkId; + c.TransportStreamId = transponder.TransportStreamId; + ReadCommonChannelData(c, service); + c.Hidden = service.Attributes["vis"].InnerText == "0"; + c.ServiceId = Int32.Parse(service.Attributes["sid"].InnerText); + c.Encrypted = service.Attributes["ca"].InnerText == "1"; + c.IsDeleted = service.Attributes["del"].InnerText == "1"; + return c; + } + + #endregion + + #region ReadCommonChannelData + private void ReadCommonChannelData(Channel c, XmlNode service) + { + c.OldProgramNr = Int32.Parse(service.Attributes["num"].InnerText); + for (int f = 1; f <= 4; f++) + { + var n = Int32.Parse(service.Attributes["f" + f].InnerText); + c.SetOldPosition(f, n == 0 ? -1 : n); + } + c.Lock = service.Attributes["lck"].InnerText == "1"; + c.Skip = service.Attributes["skp"].InnerText == "1"; + c.Name = service.Attributes["name"].InnerText; + } + #endregion + + #region CreateTransponder + private Transponder CreateTransponder(XmlNode mux, ref int transponderId, decimal freqDivider) + { + var t = new Transponder(++transponderId); + t.Polarity = mux.Attributes["pol"].InnerText == "1" ? 'H' : 'V'; + t.SymbolRate = Int32.Parse(mux.Attributes["sym"].InnerText); + t.FrequencyInMhz = Int32.Parse(mux.Attributes["frq"].InnerText) / freqDivider; + t.OriginalNetworkId = Int32.Parse(mux.Attributes["onid"].InnerText); + t.TransportStreamId = Int32.Parse(mux.Attributes["tsid"].InnerText); + return t; + } + #endregion + + + + #region Save() + + public override void Save(string tvOutputFile) + { + // "Save As..." is not supported by this loader + + foreach (var list in this.DataRoot.ChannelLists) + this.UpdateChannelList(list); + + foreach (var file in this.fileDataList) + this.SaveFile(file); + } + + #endregion + + #region SaveFile() + private void SaveFile(FileData file) + { + // use xmlWriterSettings and some post-processing to maintain the original white spacing as much as possible (new line characters, indentation, empty element close tag, ...), + // so that the original and modified files can be hex-compared + // From the 2 test files available so far, one only has \n after the XML processing instruction and the document end, all other white spaces are removed. + // The other file uses \r\n after all start/end tags and 2 spaces for indentation + var xmlSettings = new XmlWriterSettings(); + xmlSettings.Encoding = this.DefaultEncoding; + xmlSettings.CheckCharacters = false; + xmlSettings.Indent = file.indent; + xmlSettings.IndentChars = " "; + xmlSettings.NewLineHandling = NewLineHandling.Replace; + xmlSettings.NewLineChars = file.newline; + xmlSettings.OmitXmlDeclaration = false; + + using var sw = new StringWriter(); + using var w = XmlWriter.Create(sw, xmlSettings); + file.doc.WriteTo(w); + w.Flush(); + var xml = sw.ToString(); + + if (!file.indent) + xml = xml.Replace("\" />", "\"/>"); + var enc = new UTF8Encoding(file.hasBom, false); + File.WriteAllText(file.path, xml, enc); + } + #endregion + + #region UpdateChannelList() + private void UpdateChannelList(ChannelList list) + { + foreach (var channel in list.Channels) + { + var ch = channel as Channel; + if (ch == null) + continue; // might be a proxy channel from a reference list + + if (ch.IsDeleted) + continue; + if (ch.NewProgramNr < 0) + { + if ((ch.SignalSource & SignalSource.Digital) != 0) + ch.IsDeleted = true; + else + { + // analog channels can only be physically removed (no "del" attribute) + ch.Node.ParentNode.RemoveChild(ch.Node); + continue; + } + } + + this.UpdateChannel(ch); + } + } + #endregion + + #region UpdateChannel + private void UpdateChannel(Channel ch) + { + var att = ch.Node.Attributes; + + if (ch.IsDeleted) + { + att["del"].InnerText = "1"; + return; // "num" stays as-is and can be a dupe + } + + att["num"].InnerText = ch.NewProgramNr.ToString(); + if (ch.IsNameModified) + att["name"].Value = ch.Name; + for (int i=1; i<=4; i++) + att["f"+i].Value = Math.Max(0, ch.FavIndex[i-1]).ToString(); // convert -1 to 0 + att["skp"].InnerText = ch.Skip ? "1" : "0"; + att["lck"].InnerText = ch.Lock ? "1" : "0"; + if ((ch.SignalSource & SignalSource.Digital) != 0) + att["vis"].InnerText = ch.Hidden ? "0" : "1"; + else + att["hid"].InnerText = ch.Hidden ? "1" : "0"; + } + + #endregion + + #region GetDataFilePaths() + public override IEnumerable GetDataFilePaths() + { + return this.fileDataList.Select(fd => fd.path); + } + #endregion + + #region GetFileInformation() + public override string GetFileInformation() + { + return base.GetFileInformation() + this.logMessages.Replace("\n", "\r\n"); + } + #endregion + + + #region class FileData + private class FileData + { + public string path; + public bool hasBom; + public string newline; + public bool indent; + public XmlDocument doc; + } + #endregion + } +} diff --git a/source/ChanSort.Loader.Grundig/SerializerPlugin.cs b/source/ChanSort.Loader.Grundig/SerializerPlugin.cs new file mode 100644 index 00000000..cdc2f676 --- /dev/null +++ b/source/ChanSort.Loader.Grundig/SerializerPlugin.cs @@ -0,0 +1,16 @@ +using ChanSort.Api; + +namespace ChanSort.Loader.Grundig +{ + public class SerializerPlugin : ISerializerPlugin + { + public string DllName { get; set; } + public string PluginName => "Grundig dvb?_config.xml"; + public string FileFilter => "*.xml"; + + public SerializerBase CreateSerializer(string inputFile) + { + return new Serializer(inputFile); + } + } +} diff --git a/source/ChanSort.sln b/source/ChanSort.sln index 77363684..68d62bd9 100644 --- a/source/ChanSort.sln +++ b/source/ChanSort.sln @@ -11,6 +11,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort", "ChanSort\ChanSo {A1C9A98D-368A-44E8-9B7F-7EACA46C9EC5} = {A1C9A98D-368A-44E8-9B7F-7EACA46C9EC5} {F6F02792-07F1-48D5-9AF3-F945CA5E3931} = {F6F02792-07F1-48D5-9AF3-F945CA5E3931} {E972D8A1-2F5F-421C-AC91-CFF45E5191BE} = {E972D8A1-2F5F-421C-AC91-CFF45E5191BE} + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4} = {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4} {5361C8CB-F737-4709-AF8C-E1F0456F3C5B} = {5361C8CB-F737-4709-AF8C-E1F0456F3C5B} {D093E7EE-D3AD-4E7B-AF82-C6918CA017FB} = {D093E7EE-D3AD-4E7B-AF82-C6918CA017FB} EndProjectSection @@ -82,6 +83,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.Loader.PhilipsBin", "T EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spike.LgWebOs5", "Spike.LgWebOs5\Spike.LgWebOs5.csproj", "{32EFB306-DEF8-4488-B1AE-46D5B183C373}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.Grundig", "ChanSort.Loader.Grundig\ChanSort.Loader.Grundig.csproj", "{4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -464,6 +467,18 @@ Global {32EFB306-DEF8-4488-B1AE-46D5B183C373}.Release|Mixed Platforms.Build.0 = Release|Any CPU {32EFB306-DEF8-4488-B1AE-46D5B183C373}.Release|x86.ActiveCfg = Release|Any CPU {32EFB306-DEF8-4488-B1AE-46D5B183C373}.Release|x86.Build.0 = Release|Any CPU + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Debug|x86.ActiveCfg = Debug|x86 + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Debug|x86.Build.0 = Debug|x86 + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Release|Any CPU.Build.0 = Release|Any CPU + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Release|x86.ActiveCfg = Release|Any CPU + {4D5AF0A3-1B96-42C8-910D-0C4852EA22F4}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/source/ChanSort/MainForm.cs b/source/ChanSort/MainForm.cs index 4afb5779..d1aa2d30 100644 --- a/source/ChanSort/MainForm.cs +++ b/source/ChanSort/MainForm.cs @@ -310,6 +310,7 @@ private void LoadFiles(ISerializerPlugin plugin, string tvDataFile) //this.SetControlsEnabled(!this.dataRoot.IsEmpty); this.UpdateFavoritesEditor(this.DataRoot.SupportedFavorites); this.colEncrypted.OptionsColumn.AllowEdit = this.currentTvSerializer.Features.EncryptedFlagEdit; + this.colAudioPid.OptionsColumn.AllowEdit = this.currentTvSerializer.Features.CanEditAudioPid; this.UpdateMenu(true); if (this.DataRoot.Warnings.Length > 0 && this.miShowWarningsAfterLoad.Checked) @@ -1735,26 +1736,26 @@ private void RestoreBackupFile() return; } - try + foreach (var dataFilePath in this.currentTvSerializer.GetDataFilePaths()) { - foreach (var dataFilePath in this.currentTvSerializer.GetDataFilePaths()) + bakFile = dataFilePath + ".bak"; + try { - bakFile = dataFilePath + ".bak"; File.Copy(bakFile, dataFilePath, true); var attr = File.GetAttributes(dataFilePath); File.SetAttributes(dataFilePath, attr & ~FileAttributes.ReadOnly); } - - this.currentTvSerializer.DataRoot.NeedsSaving = false; - if (this.currentPlugin != null) - this.LoadFiles(this.currentPlugin, this.currentTvFile); - } - catch (Exception) - { - XtraMessageBox.Show(this, string.Format(Resources.MainForm_miRestoreOriginal_Message, this.currentTvFile), - this.miRestoreOriginal.Caption, - MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + catch (Exception) + { + XtraMessageBox.Show(this, string.Format(Resources.MainForm_miRestoreOriginal_Message, dataFilePath), + this.miRestoreOriginal.Caption, + MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + } } + + this.currentTvSerializer.DataRoot.NeedsSaving = false; + if (this.currentPlugin != null) + this.LoadFiles(this.currentPlugin, this.currentTvFile); } #endregion diff --git a/source/ChanSort/UpdateCheck.cs b/source/ChanSort/UpdateCheck.cs index 381b1908..c764ef4e 100644 --- a/source/ChanSort/UpdateCheck.cs +++ b/source/ChanSort/UpdateCheck.cs @@ -52,7 +52,7 @@ private string GetLatestVersion() { int end = response.IndexOf(".zip", start); int len = end - start - SearchString.Length; - if (len >= 10) // YYYY-MM-DD plus optional suffix for a revision + if (len >= 10) // YYYY-MM-DD plus optional _HHmm suffix for a revision return response.Substring(start + SearchString.Length, len); } return string.Empty; diff --git a/source/changelog.md b/source/changelog.md index e679a57c..5ff8e750 100644 --- a/source/changelog.md +++ b/source/changelog.md @@ -1,6 +1,9 @@ ChanSort Change Log =================== +2021-01-02 +- Grundig: added support for dvb*_config.xml channel lists + 2020-12-29 - update check could not distinguish between 2 program versions from the same day (kept showing "an update is available")