From e7bb88c554281f4a0ff9d6dea2c056c587da29a2 Mon Sep 17 00:00:00 2001 From: Horst Beham Date: Sat, 26 Dec 2020 16:51:33 +0100 Subject: [PATCH] 2020-12-26 - LG WebOS 5: added warning that support is only experimental. - Panasonic: Channel name editing is now supported for svl.bin files (unless there is no indicator what encoding to use) - Hungarian translation: added missing files to .zip --- .../GcJsonSerializer.cs | 92 +++++++-------- source/ChanSort.Loader.Panasonic/DbChannel.cs | 13 ++ .../ChanSort.Loader.Panasonic/Serializer.cs | 10 +- source/ChanSort/ActionBox.cs | 1 + source/Spike.LgWebOs5/Program.cs | 111 ++++++++++++++++-- source/changelog.md | 5 + source/makeDistribZip.cmd | 6 +- 7 files changed, 173 insertions(+), 65 deletions(-) diff --git a/source/ChanSort.Loader.GlobalClone/GcJsonSerializer.cs b/source/ChanSort.Loader.GlobalClone/GcJsonSerializer.cs index 3ec5aa8..95cef61 100644 --- a/source/ChanSort.Loader.GlobalClone/GcJsonSerializer.cs +++ b/source/ChanSort.Loader.GlobalClone/GcJsonSerializer.cs @@ -1,6 +1,4 @@ using System; -using System.Collections; -using System.Collections.Generic; using System.IO; using System.Text; using ChanSort.Api; @@ -9,6 +7,11 @@ using Newtonsoft.Json.Linq; namespace ChanSort.Loader.GlobalClone { + /* + * LG's webOS 5 firmware has severe limitations (or bugs) regarding the import of channel lists (at the time of this writing, end of 2020). + * There have been a few reports about successful imports, but no conclusions about steps that guarantee success. + * More information can be found on https://github.com/PredatH0r/ChanSort/discussions/207 + */ internal class GcJsonSerializer : SerializerBase { private readonly string content; @@ -16,14 +19,11 @@ namespace ChanSort.Loader.GlobalClone string xmlSuffix; private JObject doc; - //private readonly ChannelList tvList = new ChannelList(SignalSource.MaskAdInput | SignalSource.Tv, "TV"); - //private readonly ChannelList radioList = new ChannelList(SignalSource.MaskAdInput | SignalSource.Radio, "Radio"); - public GcJsonSerializer(string filename, string content) : base(filename) { this.content = content; - this.Features.DeleteMode = DeleteMode.FlagWithoutPrNr; + this.Features.DeleteMode = DeleteMode.NotSupported; //.FlagWithoutPrNr; this.Features.ChannelNameEdit = ChannelNameEditMode.All; this.Features.SupportedFavorites = 0; this.Features.CanSaveAs = true; @@ -40,11 +40,6 @@ namespace ChanSort.Loader.GlobalClone this.DataRoot.AddChannelList(new ChannelList(SignalSource.DvbC | SignalSource.Radio, "DVB-C Radio")); this.DataRoot.AddChannelList(new ChannelList(SignalSource.DvbS | SignalSource.Tv | SignalSource.Data, "DVB-S TV")); this.DataRoot.AddChannelList(new ChannelList(SignalSource.DvbS | SignalSource.Radio, "DVB-S Radio")); - //this.DataRoot.AddChannelList(tvList); - //this.DataRoot.AddChannelList(radioList); - - //foreach(var list in this.DataRoot.ChannelLists) - // list.VisibleColumnFieldNames.Add("Source"); } @@ -71,6 +66,32 @@ namespace ChanSort.Loader.GlobalClone this.doc = JObject.Parse(json); LoadSatellites(); LoadChannels(); + + if (View.Default == null) // can't show dialog while unit-testing + return; + + 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" + + "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); + dlg.AddAction("I read the information, accept the risk and want to give it a try", 2); + dlg.AddAction("Cancel", 0); + while (true) + { + dlg.ShowDialog(); + switch (dlg.SelectedAction) + { + case 0: + throw new FileLoadException("LG webOS 5 experimental support rejected"); + case 1: + System.Diagnostics.Process.Start("https://github.com/PredatH0r/ChanSort/discussions/207"); + break; + case 2: + return; + } + } } #region LoadSatellites() @@ -246,49 +267,22 @@ namespace ChanSort.Loader.GlobalClone node["skipped"] = ch.Skip; node["locked"] = ch.Lock; node["Invisible"] = ch.Hidden; - if (ch.NewProgramNr != Math.Max(ch.OldProgramNr, 0)) - { - node["userEditChNumber"] = true; - node["userSelCHNo"] = true; - } - //node["disableUpdate"] = true; // experimental to prevent "DTV Auto Update" of channel numbers right after importing the list + // 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 + //if (ch.NewProgramNr != Math.Max(ch.OldProgramNr, 0)) + //{ + // node["userEditChNumber"] = false; + // node["userSelCHNo"] = false; + //} + + //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; } } - - // it seems that channels must also be physically ordered by majorNumber - //var chList = this.doc["channelList"].Value(); - //var copy = new ArrayList(chList); - //var comp = new ChannelOrderComparer(this.SourceIndexOrder); - //copy.Sort(comp); - //chList.Clear(); - //foreach (var item in copy) - // chList.Add(item); } #endregion - - //class ChannelOrderComparer : IComparer - //{ - // private readonly IList sourceIndexOrder; - - // public ChannelOrderComparer(IList sourceIndexOrder) - // { - // this.sourceIndexOrder = sourceIndexOrder; - // } - - // public int Compare(object x, object y) - // { - // GcChannel a = (GcChannel)x; - // GcChannel b = (GcChannel)y; - - // var i = sourceIndexOrder.IndexOf((string)a.Node["sourceIndex"]); - // var j = sourceIndexOrder.IndexOf((string)b.Node["sourceIndex"]); - // if (i != j) - // return i < j ? -1 : +1; - - // return 0; - // } - //} } } diff --git a/source/ChanSort.Loader.Panasonic/DbChannel.cs b/source/ChanSort.Loader.Panasonic/DbChannel.cs index d21e4ac..0d889c1 100644 --- a/source/ChanSort.Loader.Panasonic/DbChannel.cs +++ b/source/ChanSort.Loader.Panasonic/DbChannel.cs @@ -238,5 +238,18 @@ namespace ChanSort.Loader.Panasonic shortName = sbShort.ToString(); } #endregion + + #region UpdateRawData() + public override void UpdateRawData() + { + if (IsNameModified) + { + var utf8 = Encoding.UTF8.GetBytes(this.Name); + this.RawName = new byte[utf8.Length + 1]; + this.RawName[0] = 0x15; // DVB encoding ID for UTF8 + Array.Copy(utf8, 0, this.RawName, 1, utf8.Length); + } + } + #endregion } } diff --git a/source/ChanSort.Loader.Panasonic/Serializer.cs b/source/ChanSort.Loader.Panasonic/Serializer.cs index 58266e3..b0c90d8 100644 --- a/source/ChanSort.Loader.Panasonic/Serializer.cs +++ b/source/ChanSort.Loader.Panasonic/Serializer.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Data; using System.Data.SQLite; +using System.Drawing; using System.IO; using System.Text; using ChanSort.Api; @@ -253,9 +254,11 @@ order by s.ntype,major_channel { while (r.Read()) { - ChannelInfo channel = new DbChannel(r, fields, this.DataRoot, this.DefaultEncoding); + var channel = new DbChannel(r, fields, this.DataRoot, this.DefaultEncoding); if (!channel.IsDeleted) { + if (channel.RawName.Length > 0 && channel.RawName[0] == 0x15) // if there is a channel with a 0x15 encoding ID (UTF-8), we can allow editing channels + this.Features.ChannelNameEdit = ChannelNameEditMode.All; var channelList = this.DataRoot.GetChannelList(channel.SignalSource); this.DataRoot.AddChannel(channelList, channel); } @@ -324,10 +327,11 @@ order by s.ntype,major_channel #region WriteChannels() private void WriteChannels(SQLiteCommand cmd, ChannelList channelList) { - cmd.CommandText = "update SVL set major_channel=@progNr, profile1index=@fav1, profile2index=@fav2, profile3index=@fav3, profile4index=@fav4, child_lock=@lock, skip=@skip where rowid=@rowid"; + cmd.CommandText = "update SVL set major_channel=@progNr, sname=@sname, profile1index=@fav1, profile2index=@fav2, profile3index=@fav3, profile4index=@fav4, child_lock=@lock, skip=@skip where rowid=@rowid"; cmd.Parameters.Clear(); cmd.Parameters.Add(new SQLiteParameter("@rowid", DbType.Int32)); cmd.Parameters.Add(new SQLiteParameter("@progNr", DbType.Int32)); + cmd.Parameters.Add(new SQLiteParameter("@sname", DbType.Binary)); cmd.Parameters.Add(new SQLiteParameter("@fav1", DbType.Int32)); cmd.Parameters.Add(new SQLiteParameter("@fav2", DbType.Int32)); cmd.Parameters.Add(new SQLiteParameter("@fav3", DbType.Int32)); @@ -342,8 +346,10 @@ order by s.ntype,major_channel continue; if (channel.IsDeleted && channel.OldProgramNr >= 0) continue; + channel.UpdateRawData(); cmd.Parameters["@rowid"].Value = channel.RecordIndex; cmd.Parameters["@progNr"].Value = channel.NewProgramNr; + cmd.Parameters["@sname"].Value = channel.RawName; for (int fav = 0; fav < 4; fav++) cmd.Parameters["@fav" + (fav + 1)].Value = Math.Max(0, channel.FavIndex[fav]); cmd.Parameters["@lock"].Value = channel.Lock; diff --git a/source/ChanSort/ActionBox.cs b/source/ChanSort/ActionBox.cs index 1ab3177..1d8af9c 100644 --- a/source/ChanSort/ActionBox.cs +++ b/source/ChanSort/ActionBox.cs @@ -58,6 +58,7 @@ namespace ChanSort.Ui button.Left = 10; button.Height = ButtonHeight; button.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right; + button.Padding = new Padding(20,button.Padding.Top, 20, button.Padding.Bottom); button.Tag = result; button.Click += button_Click; diff --git a/source/Spike.LgWebOs5/Program.cs b/source/Spike.LgWebOs5/Program.cs index 1ea4c50..bb629a2 100644 --- a/source/Spike.LgWebOs5/Program.cs +++ b/source/Spike.LgWebOs5/Program.cs @@ -17,24 +17,28 @@ namespace Spike.LgWebOs5 { basedir = args.Length > 0 ? args[0] : @"d:\sources\chansort\testfiles_lg"; - using var stream = new FileStream("c:\\temp\\lg.csv", FileMode.Create); + using var stream = new FileStream(@"d:\sources\chansort\testfiles_lg\__webOS5\lg.csv", FileMode.Create); using var csv = new StreamWriter(stream, Encoding.UTF8); var sources = new[] { "All", "Analog", "DVB-T", "DVB-C", "DVB-S", "Others" }; - var fields = ChanListStats.ColumnHeaders; csv.Write("\t\t\t\t\t\t\t\t"); - string skip = ""; - for (int i = 0; i < fields.Length - 1; i++) - skip += "\t"; - foreach(var src in sources) - csv.Write("\t" + src + skip); + bool detailed = true; + foreach (var src in sources) + { + csv.Write("\t" + src + Dup("\t", (detailed ? ChanListStats.ColumnHeadersLong.Length : ChanListStats.ColumnHeadersShort.Length) - 1)); + detailed = false; + } + csv.WriteLine(); csv.Write("Path\tStatus\tModelName\tVersion\tDTVInfo\tBroadcastCountrySettings\tcountry\tFW info\t#channels"); + detailed = true; foreach (var source in sources) { - foreach(var field in fields) + var fields = detailed ? ChanListStats.ColumnHeadersLong : ChanListStats.ColumnHeadersShort; + foreach (var field in fields) csv.Write("\t" + field); + detailed = false; } csv.WriteLine(); @@ -183,10 +187,18 @@ namespace Spike.LgWebOs5 all += entry.Value; } - sb.Append(all.ToString() + a + t + c + s + o); + sb.Append(all.ToString(true) + a + t + c + s + o); return sb.ToString(); } #endregion + + private static string Dup(string str, int count) + { + var sb = new StringBuilder(str.Length * count); + for (int i = 0; i < count; i++) + sb.Append(str); + return sb.ToString(); + } } #region class ChanListStats @@ -204,20 +216,56 @@ namespace Spike.LgWebOs5 public bool deletedMajor0 = false; public bool deletedMajorNon0 = false; - public static readonly string[] ColumnHeaders = { + public int UserEditChNumber; + public int UserSelChNo; + public int FactoryDefault; + public int Disabled; + public int Skipped; + public int Invisible; + public int Locked; + public int Deleted; + public int Discarded; + public int UserCustomize; + public int NumUnSel; + public int Lcn; + + public static readonly string[] ColumnHeadersLong = { "TV", "Radio", // "Rad 0/4K", "BadSvcType", - "InOrder", + "InOrder/Gaps", + "Del0/!0", + "UserEdit", + "FactDef", + "LCN", + "DelDisbDisc", + "SLHU" + }; + + public static readonly string[] ColumnHeadersShort = { + "TV", "Radio", + // "Rad 0/4K", "BadSvcType", + "InOrder/Gaps", "Del0/!0" }; - public override string ToString() + public override string ToString() => ToString(false); + public string ToString(bool full) { - return + var part1 = "\t" + Tv + "\t" + Radio // + "\t" + Radio0 + "/" + Radio4k + "\t" + RadioMaskServiceTypeMismatch + "\t" + (inMajorOrder ? "J" : "N") + "/" + (hasGap ? "J" : "N") + "\t" + (deletedMajor0 ? "J" : "N") + "/" + (deletedMajorNon0 ? "J" : "N"); + if (!full) + return part1; + + return + part1 + + "\t" + UserEditChNumber + "/" + UserSelChNo + + "\t" + FactoryDefault + + "\t" + Lcn + + "\t" + Deleted + "/" + Disabled + "/" + Discarded + + "\t" + Skipped + "/" + Locked + "/" + Invisible + "/" + NumUnSel; } public void Add(dynamic ch) @@ -260,11 +308,36 @@ namespace Spike.LgWebOs5 if (ch.deleted != null && (bool)ch.deleted) { + ++Deleted; if (nr == 0) deletedMajor0 = true; else deletedMajorNon0 = true; } + if (ch.diabled != null && (bool)ch.disabled) + ++Disabled; + if (ch.discarded != null && (bool)ch.discarded) + ++Discarded; + + if (ch.userEditChNumber != null && (bool) ch.userEditChNumber) + ++UserEditChNumber; + if (ch.userSelCHNo != null && (bool) ch.userSelCHNo) + ++UserSelChNo; + + if (ch.factoryDefault != null && (bool) ch.factoryDefault) + ++FactoryDefault; + + if (ch.skipped != null && (bool) ch.skipped) + ++Skipped; + if (ch.locked != null && (bool) ch.locked) + ++Locked; + if (ch.Invisible != null && (bool) ch.Invisible) + ++Invisible; + if (ch.NumUnSel != null && (bool) ch.NumUnSel) + ++NumUnSel; + + if (ch.validLCN != null && (bool) ch.validLCN) + ++Lcn; } public static ChanListStats operator +(ChanListStats a, ChanListStats b) @@ -279,6 +352,18 @@ namespace Spike.LgWebOs5 stats.hasGap = a.hasGap || b.hasGap; stats.deletedMajor0 = a.deletedMajor0 || b.deletedMajor0; stats.deletedMajorNon0 = a.deletedMajorNon0 || b.deletedMajorNon0; + + stats.UserEditChNumber = a.UserEditChNumber + b.UserEditChNumber; + stats.UserSelChNo = a.UserSelChNo + b.UserSelChNo; + stats.FactoryDefault = a.FactoryDefault + b.FactoryDefault; + stats.Disabled = a.Disabled + b.Disabled; + stats.Skipped = a.Skipped + b.Skipped; + stats.Invisible = a.Invisible + b.Invisible; + stats.Deleted = a.Deleted + b.Deleted; + stats.Discarded = a.Discarded + b.Discarded; + stats.UserCustomize = a.UserCustomize + b.UserCustomize; + stats.NumUnSel = a.NumUnSel + b.NumUnSel; + stats.Lcn = a.Lcn + b.Lcn; return stats; } } diff --git a/source/changelog.md b/source/changelog.md index 7d039dd..3f42640 100644 --- a/source/changelog.md +++ b/source/changelog.md @@ -1,6 +1,11 @@ ChanSort Change Log =================== +2020-12-26 +- LG WebOS 5: added warning that support is only experimental. +- Panasonic: Channel name editing is now supported for svl.bin files (unless there is no indicator what encoding to use) +- Hungarian translation: added missing files to .zip + 2020-12-05 - Philips: Fixed error saving the Favorite.xml file (effects Philips "ChannelMap_105" and later file format versions) - added Hungarian translation (credits to Istvan Krisko) diff --git a/source/makeDistribZip.cmd b/source/makeDistribZip.cmd index 7ada36a..06487c0 100644 --- a/source/makeDistribZip.cmd +++ b/source/makeDistribZip.cmd @@ -3,7 +3,7 @@ setlocal setlocal enabledelayedexpansion cd /d %~dp0 -set languages=cs de es pl pt ru tr +set languages=cs de es hu pl pt ru tr set curdate=%date:~6,4%-%date:~3,2%-%date:~0,2% set target=%cd%\..\..\ChanSort_%curdate% set DXversion=20.1 @@ -82,8 +82,12 @@ goto:eof :copyLangDll set source="C:\Program Files (x86)\DevExpress %DXversion%\Components\Bin\Framework\%2\DevExpress.%1.v%DXversion%.resources.dll" if exist %source% xcopy /idy %source% "%target%\%2" +set source="d:\downloads\DevExpress\DevExpressLocalizedResources_20%DXversion%_%2\DevExpress.%1.v%DXversion%.resources.dll" +if exist %source% xcopy /idy %source% "%target%\%2" set source="C:\Program Files (x86)\DevExpress %DXversion%\Components\Bin\Framework\%2\DevExpress.%1.v%DXversion%.Core.resources.dll" if exist %source% xcopy /idy %source% "%target%\%2" +set source="d:\downloads\DevExpress\DevExpressLocalizedResources_20%DXversion%_%2\DevExpress.%1.v%DXversion%.Core.resources.dll" +if exist %source% xcopy /idy %source% "%target%\%2" goto:eof :error