mirror of
https://github.com/PredatH0r/ChanSort.git
synced 2026-01-14 19:32:04 +01:00
- 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
372 lines
11 KiB
C#
372 lines
11 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using ChanSort.Api;
|
|
using Newtonsoft.Json;
|
|
|
|
namespace Spike.LgWebOs5
|
|
{
|
|
class Program
|
|
{
|
|
private static string basedir;
|
|
|
|
static void Main(string[] args)
|
|
{
|
|
basedir = args.Length > 0 ? args[0] : @"d:\sources\chansort\testfiles_lg";
|
|
|
|
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" };
|
|
|
|
csv.Write("\t\t\t\t\t\t\t\t");
|
|
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)
|
|
{
|
|
var fields = detailed ? ChanListStats.ColumnHeadersLong : ChanListStats.ColumnHeadersShort;
|
|
foreach (var field in fields)
|
|
csv.Write("\t" + field);
|
|
detailed = false;
|
|
}
|
|
csv.WriteLine();
|
|
|
|
ProcessWebOs5Files(basedir, csv);
|
|
}
|
|
|
|
private static void ProcessWebOs5Files(string dir, StreamWriter csv)
|
|
{
|
|
var files = Directory.GetFiles(dir, "GlobalClone*.tll");
|
|
|
|
foreach (var tll in files)
|
|
{
|
|
var bak = tll + ".bak";
|
|
var file = File.Exists(bak) ? bak : tll;
|
|
|
|
var line = file.Substring(basedir.Length + 1) + "\t" + ProcessFile(file);
|
|
csv.WriteLine(line);
|
|
}
|
|
|
|
foreach (var subdir in Directory.GetDirectories(dir))
|
|
ProcessWebOs5Files(subdir, csv);
|
|
}
|
|
|
|
#region ProcessFile()
|
|
private static string ProcessFile(string file)
|
|
{
|
|
var data = File.ReadAllBytes(file);
|
|
if (data.Length < 3)
|
|
return "too small";
|
|
if (data[0] != '<' || data[1] != '?')
|
|
return "not XML";
|
|
|
|
var xml = Encoding.UTF8.GetString(data);
|
|
var ending = "";
|
|
for (var i = xml.Length - 1; i >= 0; i--)
|
|
{
|
|
var ch = xml[i];
|
|
if (ch == '\n')
|
|
ending = "NL" + ending;
|
|
else if (ch == '\r')
|
|
ending = "CR" + ending;
|
|
else if (ch == ' ')
|
|
ending = "SP " + ending;
|
|
else
|
|
break;
|
|
}
|
|
|
|
var sb = new StringBuilder();
|
|
var doc = new XmlDocument();
|
|
try
|
|
{
|
|
doc.LoadXml(xml);
|
|
var mi = doc["TLLDATA"]["ModelInfo"];
|
|
sb.Append("\t" + mi["ModelName"]?.InnerText);
|
|
var cv = mi["CloneVersion"];
|
|
sb.Append("\t" + cv.Attributes["type"]?.InnerText + "." + cv["MajorVersion"]?.InnerText + "." + cv["MinorVersion"]?.InnerText + " / " + cv["SatelliteDBVersion"]?.InnerText);
|
|
sb.Append("\t" + mi["DTVInfo"]?.InnerText);
|
|
sb.Append("\t" + mi["BroadcastCountrySetting"]?.InnerText);
|
|
sb.Append("\t" + mi["country"]?.InnerText);
|
|
var legacy = doc["TLLDATA"]["CHANNEL"]["legacybroadcast"];
|
|
if (legacy == null)
|
|
sb.Append("\tnot webOS 5");
|
|
else
|
|
{
|
|
sb.Append("\t" + GetFirmwareVersionFromInfoFile(Path.Combine(Path.GetDirectoryName(file), @"LG Smart TV\TN\INFO")));
|
|
sb.Append(ProcessWebOs5JsonData(legacy.InnerText));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return ex.Message + "\t" + sb;
|
|
}
|
|
|
|
return "ok ("+ending+")" + sb;
|
|
}
|
|
|
|
private static string GetFirmwareVersionFromInfoFile(string file)
|
|
{
|
|
// this info file contains an escaped JSON string as the value of the INFO json property
|
|
if (!File.Exists(file))
|
|
return "";
|
|
|
|
var content = File.ReadAllText(file);
|
|
content = content.Replace("\\\"", "\"");
|
|
var key = "core_os_release\": \"";
|
|
var start = content.IndexOf(key);
|
|
if (start < 0)
|
|
return "";
|
|
var end = content.IndexOf("\"", start + key.Length + 1);
|
|
start += key.Length;
|
|
var value = content.Substring(start, end - start);
|
|
return value;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ProcessWebOs5JsonData()
|
|
private static string ProcessWebOs5JsonData(string json)
|
|
{
|
|
dynamic doc = JsonConvert.DeserializeObject(json);
|
|
var sb = new StringBuilder();
|
|
var channels = (IList) doc.channelList;
|
|
|
|
sb.Append("\t").Append(channels?.Count);
|
|
var tvAndRadioPerSignalSource = new Dictionary<string, ChanListStats>();
|
|
foreach (dynamic ch in channels)
|
|
{
|
|
var src = (string) ch.sourceIndex;
|
|
if (!tvAndRadioPerSignalSource.TryGetValue(src, out var stats))
|
|
{
|
|
stats = new ChanListStats();
|
|
tvAndRadioPerSignalSource[src] = stats;
|
|
}
|
|
stats.Add(ch);
|
|
}
|
|
|
|
var a = new ChanListStats();
|
|
var t = new ChanListStats();
|
|
var c = new ChanListStats();
|
|
var s = new ChanListStats();
|
|
var o = new ChanListStats();
|
|
var all = new ChanListStats();
|
|
foreach (var entry in tvAndRadioPerSignalSource)
|
|
{
|
|
switch (entry.Key)
|
|
{
|
|
case "ANTENNA ANALOG":
|
|
case "CABLE ANALOG":
|
|
case "SATELLITE ANALOG":
|
|
a += entry.Value;
|
|
break;
|
|
case "ANTENNA DIGITAL":
|
|
t += entry.Value;
|
|
break;
|
|
case "CABLE DIGITAL":
|
|
c += entry.Value;
|
|
break;
|
|
case "SATELLITE DIGITAL":
|
|
s += entry.Value;
|
|
break;
|
|
default:
|
|
o += entry.Value;
|
|
break;
|
|
}
|
|
|
|
all += entry.Value;
|
|
}
|
|
|
|
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
|
|
class ChanListStats
|
|
{
|
|
public int Tv;
|
|
public int Radio;
|
|
public int Radio0;
|
|
public int Radio4k;
|
|
public int RadioMaskServiceTypeMismatch;
|
|
public int maxMajorTv;
|
|
public int maxMajorRadio;
|
|
public bool inMajorOrder = true;
|
|
public bool hasGap = false;
|
|
public bool deletedMajor0 = false;
|
|
public bool deletedMajorNon0 = false;
|
|
|
|
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/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() => ToString(false);
|
|
public string ToString(bool full)
|
|
{
|
|
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)
|
|
{
|
|
var major = (int)ch.majorNumber;
|
|
var nr = major & 0x3FFF;
|
|
|
|
if ((major & 0x4000) != 0)
|
|
{
|
|
++Radio;
|
|
if (inMajorOrder && nr != 0 && nr <= maxMajorRadio)
|
|
inMajorOrder = false;
|
|
else
|
|
maxMajorRadio = nr;
|
|
|
|
hasGap |= nr != 0 && nr != maxMajorRadio;
|
|
}
|
|
else
|
|
{
|
|
++Tv;
|
|
if (inMajorOrder && major != 0 && major <= maxMajorTv)
|
|
inMajorOrder = false;
|
|
else
|
|
maxMajorTv = major;
|
|
|
|
hasGap |= nr != 0 && nr != maxMajorTv;
|
|
}
|
|
|
|
if (major == 0x4000)
|
|
++Radio4k;
|
|
|
|
if (ch.serviceType != null)
|
|
{
|
|
var serviceIsRadio = LookupData.Instance.IsRadioTvOrData((int)ch.serviceType) == SignalSource.Radio;
|
|
if (major == 0 && serviceIsRadio)
|
|
++Radio0;
|
|
if (((major & 0x4000) != 0) != serviceIsRadio)
|
|
++RadioMaskServiceTypeMismatch;
|
|
}
|
|
|
|
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)
|
|
{
|
|
var stats = new ChanListStats();
|
|
stats.Tv = a.Tv + b.Tv;
|
|
stats.Radio = a.Radio + b.Radio;
|
|
stats.RadioMaskServiceTypeMismatch = a.RadioMaskServiceTypeMismatch + b.RadioMaskServiceTypeMismatch;
|
|
stats.Radio0 = a.Radio0 + b.Radio0;
|
|
stats.Radio4k = a.Radio4k + b.Radio4k;
|
|
stats.inMajorOrder = a.inMajorOrder && b.inMajorOrder;
|
|
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;
|
|
}
|
|
}
|
|
#endregion
|
|
}
|