- Philips: fixes for ChannelMap_100, 105 and 110 formats

- Philips: support for FLASH/*.bin DVB-T/C and preset DVB-S lists (mgr_chan_s_pkg.db)
- Toshiba: lists with chmgt_type001\\chmgt.bin can now be opened without zipping them
- Toshiba: selecting the hotelopt_type001.bin will now also load the list (if the type is supported)
- Alden: added support for "Alden" Android SmartTV channel list format (dvr_rtk_tv.db)
This commit is contained in:
Horst Beham
2021-09-06 20:42:45 +02:00
parent 79fb621d69
commit 9c2a7b9195
20 changed files with 796 additions and 98 deletions

View File

@@ -0,0 +1,229 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Data.Sqlite;
using ChanSort.Api;
namespace ChanSort.Loader.Android
{
/*
Android TVs typically store a SQLite based data file containing an "android_metadata" table. The rest differs from brand to brand.
Philips ChannelMap_30 and _45 use the android database format for the tv.db file. That's handled in the Philips loaders, not here.
Alden uses a variant very similar to Philips, but instead of a single "channels" table containing channels from all sources,
it has distinct (atv|dtv)_(cable|antena|satellite)_channels tables. And TV and Radio channels are also put in different lists, starting at 1 each
*/
public class AldenSerializer : SerializerBase
{
private readonly ChannelList analChannels = new ChannelList(SignalSource.Analog, "Analog");
private readonly ChannelList dvbtTvChannels = new ChannelList(SignalSource.DvbT|SignalSource.Tv, "DVB-T TV");
private readonly ChannelList dvbtRadioChannels = new ChannelList(SignalSource.DvbT|SignalSource.Radio, "DVB-T Radio");
private readonly ChannelList dvbtDataChannels = new ChannelList(SignalSource.DvbT | SignalSource.Data, "DVB-T Data");
private readonly ChannelList dvbcTvChannels = new ChannelList(SignalSource.DvbC|SignalSource.Tv, "DVB-C TV");
private readonly ChannelList dvbcRadioChannels = new ChannelList(SignalSource.DvbC|SignalSource.Radio, "DVB-C Radio");
private readonly ChannelList dvbcDataChannels = new ChannelList(SignalSource.DvbC | SignalSource.Data, "DVB-C Data");
private readonly ChannelList dvbsTvChannels = new ChannelList(SignalSource.DvbS|SignalSource.Tv, "DVB-S TV");
private readonly ChannelList dvbsRadioChannels = new ChannelList(SignalSource.DvbS|SignalSource.Radio, "DVB-S Radio");
private readonly ChannelList dvbsDataChannels = new ChannelList(SignalSource.DvbS | SignalSource.Data, "DVB-S Data");
private readonly StringBuilder logMessages = new StringBuilder();
private readonly Tuple<string, ChannelList, ChannelList, ChannelList>[] subLists;
#region ctor()
public AldenSerializer(string inputFile) : base(inputFile)
{
this.Features.ChannelNameEdit = ChannelNameEditMode.None;
this.Features.CanSkipChannels = true;
this.Features.CanLockChannels = true;
this.Features.CanHideChannels = true;
this.Features.DeleteMode = DeleteMode.Physically;
this.Features.CanSaveAs = true;
this.Features.CanHaveGaps = true; // at least the DVB-S Data list had gaps
this.Features.FavoritesMode = FavoritesMode.OrderedPerSource;
this.Features.MaxFavoriteLists = 1;
this.Features.AllowGapsInFavNumbers = false;
this.Features.CanEditFavListNames = false;
this.subLists = new[]
{
Tuple.Create("atv_cable_channels", analChannels, (ChannelList)null, (ChannelList)null),
Tuple.Create("dtv_antena_channels", dvbtTvChannels, dvbtRadioChannels, dvbtDataChannels),
Tuple.Create("dtv_cable_channels", dvbcTvChannels, dvbcRadioChannels, dvbcDataChannels),
Tuple.Create("dtv_satellite_channels", dvbsTvChannels, dvbsRadioChannels, dvbsDataChannels)
};
foreach (var subList in subLists)
{
this.DataRoot.AddChannelList(subList.Item2);
if (subList.Item3 != null)
this.DataRoot.AddChannelList(subList.Item3);
if (subList.Item4 != null)
this.DataRoot.AddChannelList(subList.Item4);
}
foreach (var list in this.DataRoot.ChannelLists)
{
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.ShortName));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.ServiceType));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.ServiceTypeName));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Satellite));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Provider));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.PcrPid));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.VideoPid));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.AudioPid));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.ChannelOrTransponder));
list.VisibleColumnFieldNames.Remove(nameof(ChannelInfo.Provider));
}
}
#endregion
// loading
#region Load()
public override void Load()
{
using var conn = new SqliteConnection($"Data Source={this.FileName}");
conn.Open();
using var cmd = conn.CreateCommand();
foreach (var table in new[] { "dtv_satellite_channels" })
{
cmd.CommandText = $"select count(1) from sqlite_master where type='table' and name='{table}'";
if ((long)cmd.ExecuteScalar() == 0)
throw new FileLoadException(ERR_UnknownFormat);
}
var columns = "_id, type, service_type, original_network_id, transport_stream_id, service_id, display_number, display_name, browsable, searchable, locked, "
+ "internal_provider_flag1, internal_provider_flag4, favorite, scrambled, channel_index";
var fields = columns.Split(',');
var c = new Dictionary<string, int>();
for (int i = 0; i < fields.Length; i++)
c[fields[i].Trim()] = i;
foreach (var subList in this.subLists)
{
cmd.CommandText = $"select count(1) from sqlite_master where type='table' and name='{subList.Item1}'";
if ((long)cmd.ExecuteScalar() == 0)
continue;
cmd.CommandText = $"select {columns} from {subList.Item1}";
using var r = cmd.ExecuteReader();
while (r.Read())
{
var ch = new ChannelInfo(SignalSource.DvbS, r.GetInt32(c["_id"]), r.GetInt32(c["display_number"]), r.GetString(c["display_name"]));
ch.OriginalNetworkId = r.GetInt16(c["original_network_id"]);
ch.TransportStreamId = r.GetInt16(c["transport_stream_id"]);
ch.ServiceId = r.GetInt16(c["service_id"]);
ch.Hidden = r.GetInt16(c["browsable"]) == 0;
ch.Skip = r.GetInt16(c["searchable"]) == 0;
ch.Lock = r.GetInt16(c["locked"]) != 0;
ch.FreqInMhz = (decimal)r.GetInt64(c["internal_provider_flag1"]) / 100000;
ch.SymbolRate = r.GetInt32(c["internal_provider_flag4"]) / 1000;
var f = r.GetInt32(c["favorite"]); // unknown if this is a flag or an ordered number for a single fav list. assuming the latter for now
ch.SetOldPosition(1, f == 0 ? -1 : f);
ch.Encrypted = r.GetBoolean(c["scrambled"]);
ch.RecordOrder = r.GetInt32(c["channel_index"]);
var source = r.GetString(c["service_type"]);
ChannelList list;
if (source == "SERVICE_TYPE_AUDIO_VIDEO")
{
list = subList.Item2;
ch.SignalSource |= SignalSource.Tv;
}
else if (source == "SERVICE_TYPE_AUDIO")
{
list = subList.Item3;
ch.SignalSource |= SignalSource.Radio;
}
else
{
list = subList.Item4;
ch.SignalSource |= SignalSource.Data;
}
this.DataRoot.AddChannel(list, ch);
}
}
}
#endregion
#region Save()
/// <summary>
/// The "tv.db" file was reported to exist as early as in ChannelMap_25 format and has been seen in formats 30 and 45 too
/// </summary>
public override void Save(string outputFile)
{
this.FileName = outputFile;
using var conn = new SqliteConnection($"Data Source={outputFile}");
conn.Open();
using var trans = conn.BeginTransaction();
using var cmd = conn.CreateCommand();
cmd.Parameters.Clear();
cmd.Parameters.Add("@id", SqliteType.Integer);
cmd.Parameters.Add("@prNum", SqliteType.Text);
cmd.Parameters.Add("@name", SqliteType.Text);
cmd.Parameters.Add("@browsable", SqliteType.Integer);
cmd.Parameters.Add("@searchable", SqliteType.Integer);
cmd.Parameters.Add("@locked", SqliteType.Integer);
using var del = conn.CreateCommand();
del.Parameters.Add("@id", SqliteType.Integer);
foreach (var list in this.DataRoot.ChannelLists)
{
var table = this.subLists.First(sl => sl.Item2 == list || sl.Item3 == list || sl.Item4 == list).Item1;
cmd.CommandText = $"update {table} set display_number=@prNum, display_name=@name, browsable=@browsable, searchable=@searchable, locked=@locked where _id=@id";
cmd.Prepare();
del.CommandText = $"delete from {table} where _id=@id";
del.Prepare();
foreach (var ch in list.Channels)
{
if (ch.IsProxy)
continue;
if (ch.IsDeleted)
{
del.Parameters["@id"].Value = ch.RecordOrder;
del.ExecuteNonQuery();
}
else
{
cmd.Parameters["@id"].Value = ch.RecordIndex;
cmd.Parameters["@prNum"].Value = ch.NewProgramNr.ToString();
cmd.Parameters["@name"].Value = ch.Name;
cmd.Parameters["@browsable"].Value = ch.Hidden ? 0 : 1; // TODO check which one is which (Skip/Hide)
cmd.Parameters["@searchable"].Value = ch.Skip ? 0 : 1;
cmd.Parameters["@locked"].Value = ch.Lock ? 1 : 0;
cmd.ExecuteNonQuery();
}
}
}
trans.Commit();
conn.Close();
}
#endregion
// framework support methods
#region GetFileInformation
public override string GetFileInformation()
{
return base.GetFileInformation() + this.logMessages.Replace("\n", "\r\n");
}
#endregion
}
}

View File

@@ -0,0 +1,23 @@
using System.IO;
using ChanSort.Api;
namespace ChanSort.Loader.Android
{
public class AndroidPlugin : ISerializerPlugin
{
public string DllName { get; set; }
public string PluginName => "Android (*.db)";
public string FileFilter => "*.db";
public SerializerBase CreateSerializer(string inputFile)
{
var file = Path.GetFileName(inputFile).ToLowerInvariant();
// dvr_rtk_tv.db known from "Alden"
if (file.StartsWith("dvr_rtk_tv") && file.EndsWith(".db"))
return new AldenSerializer(inputFile);
throw new FileLoadException(SerializerBase.ERR_UnknownFormat);
}
}
}

View File

@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" 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>{5088DB0D-6BDE-4678-8C50-A14E6A294A45}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ChanSort.Loader.Android</RootNamespace>
<AssemblyName>ChanSort.Loader.Android</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>latest</LangVersion>
</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>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Data.Sqlite, Version=5.0.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Data.Sqlite.Core.5.0.8\lib\netstandard2.0\Microsoft.Data.Sqlite.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.batteries_v2, Version=2.0.4.976, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCLRaw.bundle_e_sqlite3.2.0.4\lib\net461\SQLitePCLRaw.batteries_v2.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.core, Version=2.0.4.976, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCLRaw.core.2.0.4\lib\netstandard2.0\SQLitePCLRaw.core.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.nativelibrary, Version=2.0.4.976, Culture=neutral, PublicKeyToken=502ed628492ab262, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCLRaw.bundle_e_sqlite3.2.0.4\lib\net461\SQLitePCLRaw.nativelibrary.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.provider.dynamic_cdecl, Version=2.0.4.976, Culture=neutral, PublicKeyToken=b68184102cba0b3b, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCLRaw.provider.dynamic_cdecl.2.0.4\lib\netstandard2.0\SQLitePCLRaw.provider.dynamic_cdecl.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AndroidPlugin.cs" />
<Compile Include="AldenSerializer.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>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.4\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets" Condition="Exists('..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.4\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.4\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.4\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets'))" />
</Target>
</Project>

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.Android")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ChanSort.Loader.Android")]
[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("5088db0d-6bde-4678-8c50-a14e6a294a45")]
// 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,13 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Data.Sqlite" version="5.0.8" targetFramework="net48" />
<package id="Microsoft.Data.Sqlite.Core" version="5.0.8" targetFramework="net48" />
<package id="SQLitePCLRaw.bundle_e_sqlite3" version="2.0.4" targetFramework="net48" />
<package id="SQLitePCLRaw.core" version="2.0.4" targetFramework="net48" />
<package id="SQLitePCLRaw.lib.e_sqlite3" version="2.0.4" targetFramework="net48" />
<package id="SQLitePCLRaw.provider.dynamic_cdecl" version="2.0.4" targetFramework="net48" />
<package id="System.Buffers" version="4.4.0" targetFramework="net48" />
<package id="System.Memory" version="4.5.3" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net48" />
</packages>