added support for Silva-Schneider .sdx channel lists

This commit is contained in:
hbeham
2019-07-13 22:11:28 +02:00
parent 40a5add66e
commit d10845a701
12 changed files with 376 additions and 22 deletions

View File

@@ -148,7 +148,7 @@ namespace ChanSort.Api
#region GetChannelsByNewOrder()
public IList<ChannelInfo> GetChannelsByNewOrder()
{
return this.Channels.OrderBy(c => c.NewProgramNr).ToList();
return this.Channels.OrderBy(c => c.NewProgramNr).ToList();
}
#endregion

View File

@@ -5,11 +5,11 @@ 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("CahSort.Loader.SamsungJ")]
[assembly: AssemblyTitle("ChanSort.Loader.SamsungJ")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CahSort.Loader.SamsungJ")]
[assembly: AssemblyProduct("ChanSort.Loader.SamsungJ")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E6279FF8-362A-41E6-AC0D-D0861D43F01C}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ChanSort.Loader.SilvaSchneider</RootNamespace>
<AssemblyName>ChanSort.Loader.SilvaSchneider</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Channels.cs" />
<Compile Include="Serializer.cs" />
<Compile Include="SerializerPlugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ChanSort.Api\ChanSort.Api.csproj">
<Project>{dccffa08-472b-4d17-bb90-8f513fc01392}</Project>
<Name>ChanSort.Api</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,64 @@
using System;
using System.Globalization;
using System.IO;
using ChanSort.Api;
namespace ChanSort.Loader.SilvaSchneider
{
internal class Channels : ChannelInfo
{
public int FileOffset { get; }
public int Length { get; }
#region ctor()
internal Channels(int pos, string line, byte[] data, int start, int length, DvbStringDecoder decoder)
{
this.FileOffset = start;
this.Length = length;
this.RecordIndex = pos;
this.RecordOrder = this.OldProgramNr = pos + 1;
if (!line.StartsWith("SATCODX"))
throw new FileLoadException("Only SAT channels are supported");
if (line.Length < 106)
throw new FileLoadException("Unrecognized channel format");
this.Satellite = line.Substring(10, 18);
var type = line[28];
this.SignalSource = SignalSource.Digital | SignalSource.Sat | (type == 'T' ? SignalSource.Tv : type == 'R' ? SignalSource.Radio : 0);
this.ServiceType = type == 'T' ? 1 : type == 'R' ? 2 : 0; // 1=SD-TV, 2=Radio
if (int.TryParse(line.Substring(34, 5), out var mhz))
this.FreqInMhz = mhz;
this.Polarity = line[39] == '1' ? 'H' : 'V';
byte[] nameBytes = new byte[8+17];
var nameLen2 = Math.Min(length - 115, 17); // version 103 has 12 extra bytes for channel name, version 105 has 17
Array.Copy(data, start + 43, nameBytes, 0, 8);
Array.Copy(data, start + 115, nameBytes, 8, nameLen2);
decoder.GetChannelNames(nameBytes,0, nameBytes.Length, out var longName, out var shortName);
this.Name = longName.TrimEnd();
this.ShortName = shortName.TrimEnd();
var spos = line.Substring(51, 4).TrimStart('0');
this.SatPosition = spos.Substring(0, spos.Length - 1) + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator + spos.Substring(spos.Length - 1);
if (int.TryParse(line.Substring(69, 5), out var symrate))
this.SymbolRate = symrate;
if (int.TryParse(line.Substring(87, 5), out var sid))
this.ServiceId = sid;
if (int.TryParse(line.Substring(92, 5), out var onid))
this.OriginalNetworkId = onid;
if (int.TryParse(line.Substring(97, 5), out var tsid))
this.TransportStreamId = tsid;
}
#endregion
}
}

View File

@@ -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.SilvaSchneider")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ChanSort.Loader.SilvaSchneider")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[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("FAEFA732-0096-45AE-97ED-DBE40764DA15")]
// 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")]

View File

@@ -0,0 +1,99 @@
using System;
using System.IO;
using System.Text;
using ChanSort.Api;
namespace ChanSort.Loader.SilvaSchneider
{
internal class Serializer : SerializerBase
{
private readonly ChannelList allChannels = new ChannelList(SignalSource.DvbT | SignalSource.DvbC | SignalSource.DvbS | SignalSource.AnalogC | SignalSource.AnalogT | SignalSource.Tv | SignalSource.Radio, "All");
private byte[] content;
#region ctor()
public Serializer(string inputFile) : base(inputFile)
{
this.Features.ChannelNameEdit = ChannelNameEditMode.None;
this.DataRoot.SortedFavorites = false;
this.DataRoot.SupportedFavorites = 0;
this.DataRoot.AddChannelList(this.allChannels);
// hide columns for fields that don't exist in Silva-Schneider channel list
foreach (var list in this.DataRoot.ChannelLists)
{
list.VisibleColumnFieldNames.Remove("PcrPid");
list.VisibleColumnFieldNames.Remove("VideoPid");
list.VisibleColumnFieldNames.Remove("AudioPid");
list.VisibleColumnFieldNames.Remove("Lock");
list.VisibleColumnFieldNames.Remove("Skip");
list.VisibleColumnFieldNames.Remove("Hidden");
list.VisibleColumnFieldNames.Remove("Encrypted");
list.VisibleColumnFieldNames.Remove("Favorites");
}
}
#endregion
public override string DisplayName => "Silva Schneider *.sdx Loader";
#region Load()
public override void Load()
{
var decoder = new DvbStringDecoder(this.DefaultEncoding);
var pos = 0;
content = File.ReadAllBytes(this.FileName);
int prevPos = 0, nextPos;
while (prevPos < content.Length && (nextPos = Array.FindIndex(content, prevPos, ch => ch == (byte)'\n')) >= 0)
{
if (nextPos - prevPos == 0)
continue;
string line = Encoding.ASCII.GetString(content, prevPos, nextPos-prevPos);
ChannelInfo channel = new Channels(pos, line, content, prevPos, nextPos-prevPos, decoder);
this.DataRoot.AddChannel(this.allChannels, channel);
pos++;
prevPos = nextPos + 1;
}
}
#endregion
#region Save()
public override void Save(string tvOutputFile)
{
if (tvOutputFile != this.FileName)
{
File.Copy(this.FileName, tvOutputFile);
this.FileName = tvOutputFile;
}
using (var file = new FileStream(tvOutputFile, FileMode.Create))
{
foreach (var channel in this.allChannels.GetChannelsByNewOrder())
{
// when a reference list was applied, the list may contain proxy entries for deleted channels, which must be ignored
if (channel is Channels realChannel && channel.NewProgramNr >= 0)
file.Write(this.content, realChannel.FileOffset, realChannel.Length + 1);
}
}
}
#endregion
#region GetFileInformation()
public override string GetFileInformation()
{
var sb = new StringBuilder();
sb.Append(base.GetFileInformation());
return sb.ToString();
}
#endregion
}
}

View File

@@ -0,0 +1,16 @@
using ChanSort.Api;
namespace ChanSort.Loader.SilvaSchneider
{
public class SerializerPlugin : ISerializerPlugin
{
public string DllName { get; set; }
public string PluginName { get { return "Silva Schneider"; } }
public string FileFilter { get { return "*.sdx"; } }
public SerializerBase CreateSerializer(string inputFile)
{
return new Serializer(inputFile);
}
}
}

View File

@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
# Visual Studio Version 16
VisualStudioVersion = 16.0.29025.244
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort", "ChanSort\ChanSort.csproj", "{5FAFDABC-A52F-498C-BD2F-AFFC4119797A}"
ProjectSection(ProjectDependencies) = postProject
@@ -48,6 +48,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.Loader.Samsung", "Test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.Hisense2017", "ChanSort.Loader.Hisense2017\ChanSort.Loader.Hisense2017.csproj", "{9282E1DB-CD1F-400A-ACA1-17E0C4562ACF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.SilvaSchneider", "ChanSort.Loader.SilvaSchneider\ChanSort.Loader.SilvaSchneider.csproj", "{E6279FF8-362A-41E6-AC0D-D0861D43F01C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -226,8 +228,23 @@ Global
{9282E1DB-CD1F-400A-ACA1-17E0C4562ACF}.Release|Mixed Platforms.Build.0 = Release|x86
{9282E1DB-CD1F-400A-ACA1-17E0C4562ACF}.Release|x86.ActiveCfg = Release|x86
{9282E1DB-CD1F-400A-ACA1-17E0C4562ACF}.Release|x86.Build.0 = Release|x86
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Debug|x86.ActiveCfg = Debug|x86
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Debug|x86.Build.0 = Debug|x86
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Release|Any CPU.Build.0 = Release|Any CPU
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Release|x86.ActiveCfg = Release|Any CPU
{E6279FF8-362A-41E6-AC0D-D0861D43F01C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BBF1F6CB-9445-4F6C-A69A-15C4DA246814}
EndGlobalSection
EndGlobal

View File

@@ -392,6 +392,10 @@
<Project>{a1c9a98d-368a-44e8-9b7f-7eaca46c9ec5}</Project>
<Name>ChanSort.Loader.Samsung</Name>
</ProjectReference>
<ProjectReference Include="..\ChanSort.Loader.SilvaSchneider\ChanSort.Loader.SilvaSchneider.csproj">
<Project>{e6279ff8-362a-41e6-ac0d-d0861d43f01c}</Project>
<Name>ChanSort.Loader.SilvaSchneider</Name>
</ProjectReference>
<ProjectReference Include="..\ChanSort.Loader.Toshiba\ChanSort.Loader.Toshiba.csproj">
<Project>{f6f02792-07f1-48d5-9af3-f945ca5e3931}</Project>
<Name>ChanSort.Loader.Toshiba</Name>

View File

@@ -160,6 +160,7 @@
this.miIsoCharSets = new DevExpress.XtraBars.BarListItem();
this.miShowWarningsAfterLoad = new DevExpress.XtraBars.BarCheckItem();
this.miAllowEditPredefinedLists = new DevExpress.XtraBars.BarButtonItem();
this.miExplorerIntegration = new DevExpress.XtraBars.BarButtonItem();
this.mnuAccessibility = new DevExpress.XtraBars.BarSubItem();
this.mnuGotoChannelList = new DevExpress.XtraBars.BarSubItem();
this.mnuInputSource = new DevExpress.XtraBars.BarLinkContainerItem();
@@ -205,7 +206,6 @@
this.pageProgNr = new DevExpress.XtraTab.XtraTabPage();
this.popupInputSource = new DevExpress.XtraBars.PopupMenu(this.components);
this.popupFavList = new DevExpress.XtraBars.PopupMenu(this.components);
this.miExplorerIntegration = new DevExpress.XtraBars.BarButtonItem();
((System.ComponentModel.ISupportInitialize)(this.splitContainerControl1)).BeginInit();
this.splitContainerControl1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.grpOutputList)).BeginInit();
@@ -1642,6 +1642,14 @@
this.miAllowEditPredefinedLists.Name = "miAllowEditPredefinedLists";
this.miAllowEditPredefinedLists.DownChanged += new DevExpress.XtraBars.ItemClickEventHandler(this.miAllowEditPredefinedLists_DownChanged);
//
// miExplorerIntegration
//
this.miExplorerIntegration.ButtonStyle = DevExpress.XtraBars.BarButtonStyle.Check;
resources.ApplyResources(this.miExplorerIntegration, "miExplorerIntegration");
this.miExplorerIntegration.Id = 97;
this.miExplorerIntegration.Name = "miExplorerIntegration";
this.miExplorerIntegration.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miExplorerIntegration_ItemClick);
//
// mnuAccessibility
//
resources.ApplyResources(this.mnuAccessibility, "mnuAccessibility");
@@ -2044,14 +2052,6 @@
this.popupFavList.Name = "popupFavList";
this.popupFavList.ShowCaption = true;
//
// miExplorerIntegration
//
this.miExplorerIntegration.ButtonStyle = DevExpress.XtraBars.BarButtonStyle.Check;
resources.ApplyResources(this.miExplorerIntegration, "miExplorerIntegration");
this.miExplorerIntegration.Id = 97;
this.miExplorerIntegration.Name = "miExplorerIntegration";
this.miExplorerIntegration.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miExplorerIntegration_ItemClick);
//
// MainForm
//
this.AllowDrop = true;

View File

@@ -1252,7 +1252,7 @@
<value>globalImageCollection1</value>
</data>
<data name="&gt;&gt;globalImageCollection1.Type" xml:space="preserve">
<value>ChanSort.Ui.GlobalImageCollection, ChanSort, Version=1.0.7133.19230, Culture=neutral, PublicKeyToken=null</value>
<value>ChanSort.Ui.GlobalImageCollection, ChanSort, Version=1.0.7133.39305, Culture=neutral, PublicKeyToken=null</value>
</data>
<data name="&gt;&gt;gviewRight.Name" xml:space="preserve">
<value>gviewRight</value>
@@ -1800,6 +1800,12 @@
<data name="&gt;&gt;miAllowEditPredefinedLists.Type" xml:space="preserve">
<value>DevExpress.XtraBars.BarButtonItem, DevExpress.XtraBars.v19.1, Version=19.1.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data>
<data name="&gt;&gt;miExplorerIntegration.Name" xml:space="preserve">
<value>miExplorerIntegration</value>
</data>
<data name="&gt;&gt;miExplorerIntegration.Type" xml:space="preserve">
<value>DevExpress.XtraBars.BarButtonItem, DevExpress.XtraBars.v19.1, Version=19.1.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data>
<data name="&gt;&gt;mnuAccessibility.Name" xml:space="preserve">
<value>mnuAccessibility</value>
</data>
@@ -1962,12 +1968,6 @@
<data name="&gt;&gt;popupFavList.Type" xml:space="preserve">
<value>DevExpress.XtraBars.PopupMenu, DevExpress.XtraBars.v19.1, Version=19.1.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data>
<data name="&gt;&gt;miExplorerIntegration.Name" xml:space="preserve">
<value>miExplorerIntegration</value>
</data>
<data name="&gt;&gt;miExplorerIntegration.Type" xml:space="preserve">
<value>DevExpress.XtraBars.BarButtonItem, DevExpress.XtraBars.v19.1, Version=19.1.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data>
<data name="&gt;&gt;$this.Name" xml:space="preserve">
<value>MainForm</value>
</data>
@@ -1975,7 +1975,7 @@
<value>DevExpress.XtraEditors.XtraForm, DevExpress.Utils.v19.1, Version=19.1.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data>
<data name="SharedImageCollection.Timestamp" type="System.DateTime, mscorlib">
<value>07/13/2019 11:21:37</value>
<value>07/13/2019 21:51:28</value>
</data>
<data name="SharedImageCollection.ImageSize" type="System.Drawing.Size, System.Drawing">
<value>16, 16</value>

View File

@@ -1,5 +1,38 @@
ChanSort Change Log
===================
2019-07-14
- added support for Silva-Schneider .sdx channel lists
2019-07-13
- support for channel file name as command line argument
- added explorer integration option in settings menu (to register file types)
- added drag/drop support to drag a file from windows explorer onto chansort to open it
- added reference list for vodafone cable TV in Berlin
- added missing DLL required for printing
- upgrade to DX 19.1
2019-02-12
- fixed "hidden" flag for Samsung C/D/E/F series (.scm lists)
2019-02-10
- fixed "delete channels", which were added at the end regardless of the user selection
- fixed handling of SCM lists where the value of the "hidden" flag is 255 (using best-guess based on another field)
- changed logic how channel lists can control which columns are visible in the UI
- added column for PcrPid
- removed column AudioPid from SCM lists
- fixed saving VDR lists after applying a reference list which contained no longer existing channels (cast exception for the proxy ChannelInfo record)
- no longer load samsung .zip lists with invalid internal folder structure (all files must be in the root folder of the .zip)
2019-02-06
- fixed deployment script to include DevExpress 18.2 DLLs, which are required to run ChanSort
2019-02-05
- upgraded to DevExpress 18.2
- hide VPID and APID columns for Panasonic (no data provided)
- Samsung J lists could have a \0 character at the end of a unicode16 string, which caused "Copy to Excel" to truncate the text
- fixed new-version check to work with github (only supports TLS 1.2 and newer now, not SSL 3.0)
- added pull-request with GB Sky reference channel list
2017-11-30
- fixed: deleting channels and selecting to "Remove unsorted channels"
when saving could produce problems loading the list on the TV.