mirror of
https://github.com/PredatH0r/ChanSort.git
synced 2026-02-03 04:59:04 +01:00
- basic support for Enigma2 (Dreambox, Vu+,...) channel lists
- dynamic number of favorite lists (still limited to 64 due to bitmask)
This commit is contained in:
@@ -5,8 +5,6 @@ namespace ChanSort.Api
|
||||
{
|
||||
public class ChannelInfo
|
||||
{
|
||||
public const int MAX_FAV_LISTS = 16;
|
||||
|
||||
private string uid;
|
||||
private string serviceTypeName;
|
||||
private int newProgramNr;
|
||||
@@ -106,13 +104,8 @@ namespace ChanSort.Api
|
||||
{
|
||||
this.OldProgramNr = -1;
|
||||
this.NewProgramNr = -1;
|
||||
this.FavIndex = new List<int>(MAX_FAV_LISTS);
|
||||
this.OldFavIndex = new List<int>(MAX_FAV_LISTS);
|
||||
for (int i = 0; i < MAX_FAV_LISTS; i++)
|
||||
{
|
||||
this.FavIndex.Add(-1);
|
||||
this.OldFavIndex.Add(-1);
|
||||
}
|
||||
this.FavIndex = new List<int>();
|
||||
this.OldFavIndex = new List<int>();
|
||||
this.Name = "";
|
||||
this.ShortName = "";
|
||||
}
|
||||
@@ -315,7 +308,7 @@ namespace ChanSort.Api
|
||||
/// </summary>
|
||||
public int GetPosition(int subListIndex)
|
||||
{
|
||||
return subListIndex == 0 ? this.NewProgramNr : this.FavIndex[subListIndex - 1];
|
||||
return subListIndex < 0 ? -1 : subListIndex == 0 ? this.NewProgramNr : subListIndex - 1 < this.FavIndex.Count ? this.FavIndex[subListIndex - 1] : -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -323,7 +316,7 @@ namespace ChanSort.Api
|
||||
/// </summary>
|
||||
public int GetOldPosition(int subListIndex)
|
||||
{
|
||||
return subListIndex == 0 ? this.OldProgramNr : this.OldFavIndex[subListIndex - 1];
|
||||
return subListIndex < 0 ? -1 : subListIndex == 0 ? this.OldProgramNr : subListIndex - 1 < this.OldFavIndex.Count ? this.OldFavIndex[subListIndex - 1] : -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -331,10 +324,14 @@ namespace ChanSort.Api
|
||||
/// </summary>
|
||||
public void SetPosition(int subListIndex, int newPos)
|
||||
{
|
||||
if (subListIndex < 0)
|
||||
return;
|
||||
if (subListIndex == 0)
|
||||
this.NewProgramNr = newPos;
|
||||
else
|
||||
{
|
||||
for (int i=this.FavIndex.Count; i<=subListIndex;i++)
|
||||
this.FavIndex.Add(-1);
|
||||
this.FavIndex[subListIndex - 1] = newPos;
|
||||
int mask = 1 << (subListIndex - 1);
|
||||
if (newPos == -1)
|
||||
@@ -349,10 +346,16 @@ namespace ChanSort.Api
|
||||
/// </summary>
|
||||
public void SetOldPosition(int subListIndex, int oldPos)
|
||||
{
|
||||
if (subListIndex < 0)
|
||||
return;
|
||||
if (subListIndex == 0)
|
||||
this.OldProgramNr = oldPos;
|
||||
else
|
||||
{
|
||||
for (int i = this.OldFavIndex.Count; i <= subListIndex; i++)
|
||||
this.OldFavIndex.Add(-1);
|
||||
this.OldFavIndex[subListIndex - 1] = oldPos;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -363,7 +366,11 @@ namespace ChanSort.Api
|
||||
if (subListIndex == 0)
|
||||
this.NewProgramNr += delta;
|
||||
else
|
||||
this.FavIndex[subListIndex - 1] += delta;
|
||||
{
|
||||
for (int i = this.FavIndex.Count; i <= subListIndex; i++)
|
||||
this.FavIndex.Add(-1);
|
||||
this.FavIndex[subListIndex - 1] += delta;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace ChanSort.Api
|
||||
private int insertProgramNr = 1;
|
||||
private int duplicateUidCount;
|
||||
private int duplicateProgNrCount;
|
||||
public int FavListCount { get; set; }
|
||||
|
||||
|
||||
public static List<string> DefaultVisibleColumns { get; set; } = new List<string>(); // initialized by MainForm
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace ChanSort.Api
|
||||
public bool IsEmpty => this.channelLists.Count == 0;
|
||||
public bool NeedsSaving { get; set; }
|
||||
|
||||
|
||||
public Favorites SupportedFavorites => this.loader.Features.SupportedFavorites;
|
||||
public bool SortedFavorites => this.loader.Features.SortedFavorites;
|
||||
public bool MixedSourceFavorites => this.loader.Features.MixedSourceFavorites;
|
||||
@@ -111,6 +110,12 @@ namespace ChanSort.Api
|
||||
{
|
||||
foreach (var list in this.ChannelLists)
|
||||
{
|
||||
if (list.FavListCount == 0)
|
||||
{
|
||||
for (ulong m = (ulong) this.loader.Features.SupportedFavorites; m != 0; m >>= 1)
|
||||
++list.FavListCount;
|
||||
}
|
||||
|
||||
if (list.IsMixedSourceFavoritesList)
|
||||
{
|
||||
loader.Features.SortedFavorites = true; // all mixed source favorite lists must support ordering
|
||||
@@ -142,7 +147,7 @@ namespace ChanSort.Api
|
||||
int c = 0;
|
||||
if (this.MixedSourceFavorites || this.SortedFavorites)
|
||||
{
|
||||
for (int m = (int) this.SupportedFavorites; m != 0; m >>= 1)
|
||||
for (ulong m = (ulong) this.SupportedFavorites; m != 0; m >>= 1)
|
||||
++c;
|
||||
}
|
||||
|
||||
@@ -265,7 +270,7 @@ namespace ChanSort.Api
|
||||
var hasCaption = favListCaptions.TryGetValue(favIndex, out var caption);
|
||||
if (!asTabCaption)
|
||||
return caption;
|
||||
var letter = (char)('A' + favIndex);
|
||||
var letter = favIndex < 26 ? ((char)('A' + favIndex)).ToString() : (favIndex + 1).ToString();
|
||||
return hasCaption && !string.IsNullOrEmpty(caption) ? letter + ": " + caption : "Fav " + letter;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace ChanSort.Api
|
||||
#endregion
|
||||
|
||||
[Flags]
|
||||
public enum Favorites : byte { A = 0x01, B = 0x02, C = 0x04, D = 0x08, E = 0x10, F=0x20, G=0x40, H=0x80 }
|
||||
public enum Favorites : long { A = 0x01, B = 0x02, C = 0x04, D = 0x08, E = 0x10, F=0x20, G=0x40, H=0x80 }
|
||||
|
||||
public enum UnsortedChannelMode
|
||||
{
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?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>{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ChanSort.Loader.Enigma2</RootNamespace>
|
||||
<AssemblyName>ChanSort.Loader.Enigma2</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</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>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
</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>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<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="Channel.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Enigma2Plugin.cs" />
|
||||
<Compile Include="Serializer.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" />
|
||||
</Project>
|
||||
29
source/ChanSort.Loader.Enigma2/Channel.cs
Normal file
29
source/ChanSort.Loader.Enigma2/Channel.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Enigma2
|
||||
{
|
||||
internal class Channel : ChannelInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// first two fields of the lamedb entry
|
||||
/// </summary>
|
||||
public string Prefix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For DVB-S it is the orbital position * 10 (e.g. 192 for Astra 19.2E) * 65536
|
||||
/// </summary>
|
||||
public int DvbNamespace { get; set; }
|
||||
|
||||
public int ServiceNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// all fields after the DVB-namespace in the lamedb entry
|
||||
/// </summary>
|
||||
public string Suffix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// #DESCRIPTION of the userbouquet entry
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
}
|
||||
}
|
||||
16
source/ChanSort.Loader.Enigma2/Enigma2Plugin.cs
Normal file
16
source/ChanSort.Loader.Enigma2/Enigma2Plugin.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Enigma2
|
||||
{
|
||||
public class Enigma2Plugin : ISerializerPlugin
|
||||
{
|
||||
public string DllName { get; set; }
|
||||
public string PluginName => "Enigma2 (Linux Receiver)";
|
||||
public string FileFilter => "bouquets.*";
|
||||
|
||||
public SerializerBase CreateSerializer(string inputFile)
|
||||
{
|
||||
return new Serializer(inputFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
source/ChanSort.Loader.Enigma2/Properties/AssemblyInfo.cs
Normal file
36
source/ChanSort.Loader.Enigma2/Properties/AssemblyInfo.cs
Normal 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.Enigma2")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ChanSort.Loader.Enigma2")]
|
||||
[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("4ad7f77e-617c-4741-82ae-e7a41c85ee4d")]
|
||||
|
||||
// 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")]
|
||||
369
source/ChanSort.Loader.Enigma2/Serializer.cs
Normal file
369
source/ChanSort.Loader.Enigma2/Serializer.cs
Normal file
@@ -0,0 +1,369 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ChanSort.Api;
|
||||
|
||||
namespace ChanSort.Loader.Enigma2
|
||||
{
|
||||
/*
|
||||
* This class loads "userbouquet.*" channel lists from Enigma2 Linux set-top-boxes (Dreambox, Vu+, ...)
|
||||
*
|
||||
* lamedb version 4 format: https://www.satsupreme.com/showthread.php/194074-Lamedb-format-explained
|
||||
* userbouqet.* format: https://www.opena.tv/english-section/43964-iptv-service-4097-service-1-syntax.html#post376271
|
||||
*/
|
||||
internal class Serializer : SerializerBase
|
||||
{
|
||||
private static readonly Encoding utf8WithoutBom = new UTF8Encoding(false);
|
||||
|
||||
private ChannelList tv = new ChannelList(SignalSource.Digital | SignalSource.Tv, "TV");
|
||||
private ChannelList radio = new ChannelList(SignalSource.Digital | SignalSource.Radio, "Radio");
|
||||
|
||||
private readonly List<string> favListFileNames = new();
|
||||
private readonly Dictionary<string, Transponder> transponderByLamedbId = new();
|
||||
private readonly Dictionary<string, Channel> channelsByBouquetId = new();
|
||||
private DvbStringDecoder decoder;
|
||||
|
||||
private readonly StringBuilder log = new();
|
||||
|
||||
#region ctor()
|
||||
|
||||
public Serializer(string inputFile) : base(inputFile)
|
||||
{
|
||||
this.FileName = Path.Combine(Path.GetDirectoryName(inputFile), "lamedb");
|
||||
|
||||
this.Features.ChannelNameEdit = ChannelNameEditMode.None;
|
||||
this.Features.DeleteMode = DeleteMode.Physically;
|
||||
this.Features.CanSkipChannels = false;
|
||||
this.Features.CanLockChannels = false;
|
||||
this.Features.CanHideChannels = false;
|
||||
this.Features.MixedSourceFavorites = true;
|
||||
this.Features.SortedFavorites = true;
|
||||
this.Features.SupportedFavorites = 0; // dynamically added
|
||||
this.Features.CanSaveAs = false;
|
||||
|
||||
this.tv.IsMixedSourceFavoritesList = true;
|
||||
this.DataRoot.AddChannelList(this.tv);
|
||||
this.radio.IsMixedSourceFavoritesList = true;
|
||||
this.DataRoot.AddChannelList(this.radio);
|
||||
|
||||
// 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");
|
||||
list.VisibleColumnFieldNames.Remove("ServiceType");
|
||||
list.VisibleColumnFieldNames.Add("ServiceTypeName");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Load()
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
this.decoder = new DvbStringDecoder(this.DefaultEncoding);
|
||||
this.LoadLamedb();
|
||||
|
||||
int favId = 0;
|
||||
foreach(var file in Directory.GetFiles(Path.GetDirectoryName(this.FileName), "userbouquet.*"))
|
||||
this.LoadBouquet(file, ref favId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LoadLamedb()
|
||||
private void LoadLamedb()
|
||||
{
|
||||
var path = Path.Combine(Path.GetDirectoryName(this.FileName), "lamedb");
|
||||
if (!File.Exists(path))
|
||||
throw new FileLoadException($"Could not find required file \"{path}\"");
|
||||
|
||||
using var r = new StreamReader(File.OpenRead(path), utf8WithoutBom);
|
||||
var line = r.ReadLine();
|
||||
if (line != "eDVB services /4/")
|
||||
throw new FileLoadException($"lamedb version 4 is required");
|
||||
|
||||
string mode = null;
|
||||
Transponder tp = null;
|
||||
int tpId = 0, chanId = 0;
|
||||
|
||||
while ((line = r.ReadLine()) != null)
|
||||
{
|
||||
if (line.Trim() == "")
|
||||
continue;
|
||||
if (line == "transponders")
|
||||
mode = line;
|
||||
else if (line == "services")
|
||||
mode = "services";
|
||||
else if (line == "end")
|
||||
mode = null;
|
||||
else if (mode == "transponders")
|
||||
tp = ReadLamedbTransponderLine(line, tp, ref tpId);
|
||||
else if (mode == "services")
|
||||
ReadLamedbServiceLine(line, r, ref chanId);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadLamedbTransponderLine
|
||||
private Transponder ReadLamedbTransponderLine(string line, Transponder tp, ref int tpId)
|
||||
{
|
||||
if (line == "/")
|
||||
return tp;
|
||||
if (!line.StartsWith("\t"))
|
||||
{
|
||||
tp = new Transponder(++tpId);
|
||||
this.transponderByLamedbId[line] = tp;
|
||||
var parts = line.Split(':');
|
||||
tp.Number = FromHex(parts[0]);
|
||||
tp.TransportStreamId = FromHex(parts[1]);
|
||||
tp.OriginalNetworkId = FromHex(parts[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line[1] == 's')
|
||||
{
|
||||
var parts = line.Substring(3).Split(':');
|
||||
tp.FrequencyInMhz = int.Parse(parts[0]) / 1000;
|
||||
tp.SymbolRate = int.Parse(parts[1]) / 1000;
|
||||
tp.Polarity = "HVLR"[int.Parse(parts[2])];
|
||||
tp.Satellite = GetOrCreateSatellite(int.Parse(parts[4]));
|
||||
}
|
||||
}
|
||||
|
||||
return tp;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetOrCreateSatellite
|
||||
private Satellite GetOrCreateSatellite(int orbitalPos)
|
||||
{
|
||||
if (this.DataRoot.Satellites.TryGetValue(orbitalPos, out var sat))
|
||||
return sat;
|
||||
sat = new Satellite(orbitalPos);
|
||||
sat.OrbitalPosition = $"{(float) Math.Abs(orbitalPos) / 10:0.0}{(orbitalPos<0?'W':'E')}";
|
||||
sat.Name = sat.OrbitalPosition;
|
||||
this.DataRoot.Satellites.Add(orbitalPos, sat);
|
||||
return sat;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReadLamedbServiceLine
|
||||
private void ReadLamedbServiceLine(string line, StreamReader r, ref int chanId)
|
||||
{
|
||||
var line2 = r.ReadLine();
|
||||
var line3 = r.ReadLine();
|
||||
if (line2 == null || line3 == null)
|
||||
return;
|
||||
|
||||
var ch = new Channel();
|
||||
ch.SignalSource = SignalSource.Digital;
|
||||
|
||||
// line 1: SID:DvbNamespace:TSID:ONID:ServiceType:ServiceNumber
|
||||
var parts = line.Split(':');
|
||||
ch.RecordIndex = chanId;
|
||||
ch.RecordOrder = chanId;
|
||||
ch.OldProgramNr = ++chanId;
|
||||
ch.NewProgramNr = chanId;
|
||||
ch.IsDeleted = false;
|
||||
ch.ServiceId = FromHex(parts[0]);
|
||||
ch.DvbNamespace = FromHex(parts[1]);
|
||||
ch.TransportStreamId = FromHex(parts[2]);
|
||||
ch.OriginalNetworkId = FromHex(parts[3]);
|
||||
ch.ServiceType = int.Parse(parts[4]);
|
||||
ch.ServiceNumber = int.Parse(parts[5]);
|
||||
ch.SignalSource |= LookupData.Instance.IsRadioTvOrData(ch.ServiceType);
|
||||
|
||||
var tpId = parts[1] + ":" + parts[2] + ":" + parts[3];
|
||||
if (this.transponderByLamedbId.TryGetValue(tpId, out var tp))
|
||||
{
|
||||
ch.Satellite = tp.Satellite?.Name;
|
||||
ch.SymbolRate = tp.SymbolRate;
|
||||
ch.FreqInMhz = tp.FrequencyInMhz;
|
||||
ch.Polarity = tp.Polarity;
|
||||
}
|
||||
|
||||
// line 2: channel name (in raw DVB encoding)
|
||||
var rawName = new byte[line2.Length];
|
||||
for (int i = 0, c = rawName.Length; i < c; i++)
|
||||
rawName[i] = (byte)line2[i];
|
||||
this.decoder.GetChannelNames(rawName, 0, rawName.Length, out var longName, out var shortName);
|
||||
ch.Name = longName;
|
||||
ch.ShortName = shortName;
|
||||
|
||||
// line 3: provider and other info
|
||||
parts = line3.Split(',');
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var keyVal = part.Split(new char[] {':'}, 2);
|
||||
switch (keyVal[0])
|
||||
{
|
||||
case "p":
|
||||
ch.Provider = keyVal[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.DataRoot.AddChannel(this.tv, ch);
|
||||
this.channelsByBouquetId[$"{ch.DvbNamespace}:{ch.OriginalNetworkId}:{ch.TransportStreamId}:{ch.ServiceId}"] = ch;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region LoadBoquet
|
||||
private void LoadBouquet(string file, ref int favIndex)
|
||||
{
|
||||
ChannelList list;
|
||||
if (file.EndsWith(".tv"))
|
||||
list = this.tv;
|
||||
else if (file.EndsWith(".radio"))
|
||||
list = this.radio;
|
||||
else
|
||||
return;
|
||||
|
||||
using var r = new StreamReader(File.OpenRead(file), utf8WithoutBom);
|
||||
var line = r.ReadLine();
|
||||
if (line == null || !line.StartsWith("#NAME "))
|
||||
{
|
||||
log.AppendLine($"{file} does not start with #NAME");
|
||||
return;
|
||||
}
|
||||
|
||||
this.DataRoot.SetFavListCaption(favIndex, line.Substring(6));
|
||||
this.Features.SupportedFavorites = (Favorites)((int)this.Features.SupportedFavorites<<1)|Favorites.A;
|
||||
|
||||
int lineNr = 0;
|
||||
int progNr = 0;
|
||||
Channel ch = null;
|
||||
while ((line = r.ReadLine()) != null)
|
||||
{
|
||||
++lineNr;
|
||||
if (line.Trim() == "")
|
||||
continue;
|
||||
if (line.Contains(":FROM") && line.Contains("ORDER BY")) // ignore the root-level bouquet that only references other bouquet files
|
||||
return;
|
||||
if (line.StartsWith("#DESCRIPTION "))
|
||||
{
|
||||
if (ch != null)
|
||||
ch.Description = line.Substring(13);
|
||||
continue;
|
||||
}
|
||||
if (!line.StartsWith("#SERVICE "))
|
||||
continue;
|
||||
|
||||
var parts = line.Substring(9).Split(':');
|
||||
if (parts[0] != "1") // ignore non-DVB
|
||||
continue;
|
||||
if (parts[1] != "0") // ignore special-purpose rows
|
||||
continue;
|
||||
|
||||
var prefix = parts[0] + ":" + parts[1];
|
||||
|
||||
// parts[2] = DVB service type
|
||||
var sid = FromHex(parts[3]);
|
||||
var tsid = FromHex(parts[4]);
|
||||
var onid = FromHex(parts[5]);
|
||||
var dvbNamespace = FromHex(parts[6]);
|
||||
|
||||
var suffix = "";
|
||||
for (int i = 7; i < parts.Length; i++)
|
||||
suffix += ":" + parts[i];
|
||||
|
||||
var key = $"{dvbNamespace}:{onid}:{tsid}:{sid}";
|
||||
if (!this.channelsByBouquetId.TryGetValue(key, out ch))
|
||||
{
|
||||
log.AppendLine($"{file} line {lineNr}: service not found in lamedb");
|
||||
continue;
|
||||
}
|
||||
|
||||
ch.Prefix = prefix;
|
||||
ch.Suffix = suffix;
|
||||
ch.SetOldPosition(1+favIndex, ++progNr);
|
||||
}
|
||||
|
||||
this.favListFileNames.Add(file);
|
||||
++favIndex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region FromHex()
|
||||
private int FromHex(string str)
|
||||
{
|
||||
int result = 0;
|
||||
foreach (var ch in str)
|
||||
{
|
||||
if (Char.IsWhiteSpace(ch))
|
||||
continue;
|
||||
|
||||
result <<= 4;
|
||||
if (ch >= '0' && ch <= '9')
|
||||
result += ch - '0';
|
||||
else if (ch >= 'A' && ch <= 'F')
|
||||
result += ch - 'A' + 10;
|
||||
else if (ch >= 'a' && ch <= 'f')
|
||||
result += ch - 'a' + 10;
|
||||
else
|
||||
throw new ArgumentException(str + " contains invalid hex characters");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetDataFilePaths()
|
||||
public override IEnumerable<string> GetDataFilePaths()
|
||||
{
|
||||
var list = new List<string>(this.favListFileNames.Count + 1);
|
||||
list.Add(this.FileName); // lamedb
|
||||
list.AddRange(this.favListFileNames); // userbouquet*
|
||||
return list;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Save()
|
||||
|
||||
public override void Save(string tvOutputFile)
|
||||
{
|
||||
for (int favIndex = 0; favIndex < this.favListFileNames.Count; favIndex++)
|
||||
{
|
||||
var file = this.favListFileNames[favIndex];
|
||||
using var w = new StreamWriter(File.OpenWrite(file), utf8WithoutBom);
|
||||
w.WriteLine($"#NAME {this.DataRoot.GetFavListCaption(favIndex)}");
|
||||
foreach (var ch in this.tv.Channels.OrderBy(c => c.GetPosition(favIndex+1)))
|
||||
{
|
||||
if (!(ch is Channel c) || c.GetPosition(favIndex + 1) < 0)
|
||||
continue;
|
||||
|
||||
w.WriteLine($"#SERVICE {c.Prefix}:{c.ServiceType:X}:{c.ServiceId:X}:{c.TransportStreamId:X}:{c.OriginalNetworkId:X}:{c.DvbNamespace:X}{c.Suffix}");
|
||||
if (c.Description != null)
|
||||
w.WriteLine($"#DESCRIPTION {c.Description}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region GetFileInformation()
|
||||
|
||||
public override string GetFileInformation()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(base.GetFileInformation());
|
||||
sb.AppendLine();
|
||||
sb.Append(this.log);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -245,7 +245,7 @@ namespace ChanSort.Loader.Philips
|
||||
ch.Lock = mapping.GetByte("offLocked") != 0;
|
||||
ch.Favorites = mapping.GetByte("offIsFav") != 0 ? Favorites.A : 0;
|
||||
if (ch.Favorites != 0)
|
||||
ch.OldFavIndex[0] = ch.OldProgramNr;
|
||||
ch.SetOldPosition(1, ch.OldProgramNr);
|
||||
|
||||
this.DataRoot.AddChannel(list, ch);
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ namespace ChanSort.Loader.Philips
|
||||
chan.Lock = data.TryGet("ChannelLock") == "1";
|
||||
chan.Hidden = data.TryGet("UserHidden") == "1";
|
||||
var fav = ParseInt(data.TryGet("FavoriteNumber"));
|
||||
chan.OldFavIndex[0] = fav == 0 ? -1 : fav;
|
||||
chan.SetOldPosition(1, fav == 0 ? -1 : fav);
|
||||
chan.OriginalNetworkId = ParseInt(data.TryGet("Onid"));
|
||||
chan.TransportStreamId = ParseInt(data.TryGet("Tsid"));
|
||||
chan.ServiceId = ParseInt(data.TryGet("Sid"));
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace ChanSort.Loader.Samsung.Scm
|
||||
else if (sortedFavorites != FavoritesIndexMode.Boolean && favValue != -1)
|
||||
fav |= mask;
|
||||
if (sortedFavorites == FavoritesIndexMode.IndividuallySorted)
|
||||
this.OldFavIndex[favIndex] = favValue;
|
||||
this.SetOldPosition(1+favIndex, favValue);
|
||||
mask <<= 1;
|
||||
++favIndex;
|
||||
}
|
||||
|
||||
@@ -616,8 +616,8 @@ namespace ChanSort.Loader.Samsung.Zip
|
||||
// update favorites
|
||||
for (int i=0, mask=1; i<5; i++, mask <<= 1)
|
||||
{
|
||||
int oldPos = channel.OldFavIndex[i];
|
||||
int newPos = ((int)channel.Favorites & mask) != 0 ? channel.FavIndex[i] : -1;
|
||||
int oldPos = channel.GetOldPosition(1+i);
|
||||
int newPos = ((int)channel.Favorites & mask) != 0 ? channel.GetPosition(1+i) : -1;
|
||||
|
||||
if (newPos >= 0)
|
||||
{
|
||||
|
||||
@@ -69,6 +69,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.Grundig", "
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.Loader.M3u", "Test.Loader.M3u\Test.Loader.M3u.csproj", "{052692BF-D782-4888-B34D-89D6B1379340}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.Enigma2", "ChanSort.Loader.Enigma2\ChanSort.Loader.Enigma2.csproj", "{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -379,6 +381,18 @@ Global
|
||||
{052692BF-D782-4888-B34D-89D6B1379340}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{052692BF-D782-4888-B34D-89D6B1379340}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{052692BF-D782-4888-B34D-89D6B1379340}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Debug|x86.Build.0 = Debug|x86
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4AD7F77E-617C-4741-82AE-E7A41C85EE4D}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -482,6 +482,14 @@
|
||||
<Project>{DCCFFA08-472B-4D17-BB90-8F513FC01392}</Project>
|
||||
<Name>ChanSort.Api</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ChanSort.Loader.Enigma2\ChanSort.Loader.Enigma2.csproj">
|
||||
<Project>{4ad7f77e-617c-4741-82ae-e7a41c85ee4d}</Project>
|
||||
<Name>ChanSort.Loader.Enigma2</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ChanSort.Loader.Grundig\ChanSort.Loader.Grundig.csproj">
|
||||
<Project>{4d5af0a3-1b96-42c8-910d-0c4852ea22f4}</Project>
|
||||
<Name>ChanSort.Loader.Grundig</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ChanSort.Loader.Hisense\ChanSort.Loader.Hisense.csproj">
|
||||
<Project>{d093e7ee-d3ad-4e7b-af82-c6918ca017fb}</Project>
|
||||
<Name>ChanSort.Loader.Hisense</Name>
|
||||
@@ -506,6 +514,10 @@
|
||||
<Project>{a1c9a98d-368a-44e8-9b7f-7eaca46c9ec5}</Project>
|
||||
<Name>ChanSort.Loader.Samsung</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ChanSort.Loader.SatcoDX\ChanSort.Loader.SatcoDX.csproj">
|
||||
<Project>{e6279ff8-362a-41e6-ac0d-d0861d43f01c}</Project>
|
||||
<Name>ChanSort.Loader.SatcoDX</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ChanSort.Loader.Sony\ChanSort.Loader.Sony.csproj">
|
||||
<Project>{70e29c6b-b926-4859-9548-23375bf1e1b5}</Project>
|
||||
<Name>ChanSort.Loader.Sony</Name>
|
||||
|
||||
@@ -244,8 +244,8 @@ namespace ChanSort.Ui
|
||||
extension.Append(ext);
|
||||
extension.Append(";");
|
||||
}
|
||||
++numberOfFilters;
|
||||
}
|
||||
++numberOfFilters;
|
||||
}
|
||||
if (extension.Length > 0)
|
||||
extension.Remove(extension.Length - 1, 1);
|
||||
|
||||
@@ -18,6 +18,18 @@ namespace ChanSort.Ui
|
||||
private SerializerBase serializer;
|
||||
private readonly string[] closeButtonText;
|
||||
|
||||
class MixedSourceList
|
||||
{
|
||||
public ChannelList ChannelList { get; }
|
||||
public int FavIndex { get; }
|
||||
|
||||
public MixedSourceList(ChannelList list, int favIndex)
|
||||
{
|
||||
ChannelList = list;
|
||||
FavIndex = favIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public ReferenceListForm(MainForm main)
|
||||
{
|
||||
this.main = main;
|
||||
@@ -113,7 +125,13 @@ namespace ChanSort.Ui
|
||||
this.comboSource.Properties.Items.Clear();
|
||||
foreach (var list in serializer.DataRoot.ChannelLists)
|
||||
{
|
||||
if (!list.IsMixedSourceFavoritesList && list.Channels.Count > 0)
|
||||
if (list.Channels.Count == 0)
|
||||
continue;
|
||||
if (list.IsMixedSourceFavoritesList)
|
||||
{
|
||||
this.comboSource.Properties.Items.Add(new MixedSourceList(list, i))
|
||||
}
|
||||
else
|
||||
this.comboSource.Properties.Items.Add(list);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user