2015-04-17 21:59:44 +02:00
using System ;
using System.Collections.Generic ;
using System.Data ;
using System.IO ;
2019-11-08 02:31:44 +01:00
using System.Linq ;
2015-04-22 09:51:21 +02:00
using System.Text ;
2021-04-25 18:31:05 +02:00
using Microsoft.Data.Sqlite ;
2015-04-17 21:59:44 +02:00
using ChanSort.Api ;
2021-01-23 14:22:18 +01:00
namespace ChanSort.Loader.Samsung.Zip
2015-04-17 21:59:44 +02:00
{
2019-02-10 15:56:03 +01:00
/// <summary>
2020-07-13 10:00:41 +02:00
/// Loader for Samsung J/K/M/N/R/Q series .zip files (2015 - 2020)
2019-02-10 15:56:03 +01:00
/// </summary>
2015-04-17 21:59:44 +02:00
class DbSerializer : SerializerBase
{
private readonly Dictionary < long , DbChannel > channelById = new Dictionary < long , DbChannel > ( ) ;
2015-04-22 09:51:21 +02:00
private readonly Dictionary < ChannelList , string > dbPathByChannelList = new Dictionary < ChannelList , string > ( ) ;
2020-03-14 16:54:42 +01:00
private readonly List < string > tableNames = new List < string > ( ) ;
2020-07-12 02:39:43 +02:00
private Encoding encoding ;
2015-04-17 21:59:44 +02:00
2020-03-20 01:25:27 +01:00
private enum FileType { Unknown , SatDb , ChannelDbDvb , ChannelDbAnalog , ChannelDbIp }
2015-04-22 09:51:21 +02:00
2015-04-17 21:59:44 +02:00
#region ctor ( )
public DbSerializer ( string inputFile ) : base ( inputFile )
{
2015-06-13 18:37:59 +02:00
this . Features . ChannelNameEdit = ChannelNameEditMode . All ;
2019-11-08 02:31:44 +01:00
this . Features . DeleteMode = DeleteMode . Physically ;
2019-11-24 20:00:48 +01:00
this . Features . CanSkipChannels = true ;
this . Features . CanLockChannels = true ;
this . Features . CanHideChannels = true ;
2021-03-14 22:13:22 +01:00
this . Features . FavoritesMode = FavoritesMode . OrderedPerSource ;
this . Features . MaxFavoriteLists = 5 ;
2020-03-20 01:25:27 +01:00
this . Features . AllowGapsInFavNumbers = true ;
2015-04-17 21:59:44 +02:00
}
#endregion
#region Load ( )
public override void Load ( )
{
2019-11-08 02:31:44 +01:00
try
2015-06-05 06:08:40 +02:00
{
2019-11-08 19:35:59 +01:00
this . UnzipFileToTempFolder ( ) ;
if ( File . Exists ( this . TempPath + "\\sat" ) )
2015-06-05 06:08:40 +02:00
{
2019-11-08 02:31:44 +01:00
try
{
2021-04-25 18:31:05 +02:00
using ( var conn = new SqliteConnection ( "Data Source=" + this . TempPath + "\\sat" ) )
2019-11-08 02:31:44 +01:00
{
conn . Open ( ) ;
this . ReadSatDatabase ( conn ) ;
}
}
catch
2015-06-05 06:08:40 +02:00
{
}
}
2019-11-08 19:35:59 +01:00
var files = Directory . GetFiles ( this . TempPath , "*." ) ;
2019-11-08 02:31:44 +01:00
if ( files . Length = = 0 )
throw new FileLoadException ( "The Samsung .zip channel list archive does not contain any supported files." ) ;
2017-06-08 20:01:42 +02:00
2019-11-08 02:31:44 +01:00
foreach ( var filePath in files )
2015-04-17 21:59:44 +02:00
{
2019-11-08 02:31:44 +01:00
var filename = Path . GetFileName ( filePath ) ? ? "" ;
if ( filename . StartsWith ( "vconf_" ) )
continue ;
try
2015-04-22 09:51:21 +02:00
{
2021-04-25 18:31:05 +02:00
using ( var conn = new SqliteConnection ( "Data Source=" + filePath ) )
2015-04-22 09:51:21 +02:00
{
2019-11-08 02:31:44 +01:00
FileType type ;
conn . Open ( ) ;
using ( var cmd = conn . CreateCommand ( ) )
{
this . RepairCorruptedDatabaseImage ( cmd ) ;
type = this . DetectFileType ( cmd ) ;
}
switch ( type )
{
2020-03-20 01:25:27 +01:00
case FileType . SatDb :
2019-11-08 02:31:44 +01:00
break ;
2020-03-20 01:25:27 +01:00
case FileType . ChannelDbAnalog :
2019-11-08 02:31:44 +01:00
case FileType . ChannelDbDvb :
2020-03-20 01:25:27 +01:00
case FileType . ChannelDbIp :
ReadChannelDatabase ( conn , filePath , type ) ;
2019-11-08 02:31:44 +01:00
break ;
}
2015-04-22 09:51:21 +02:00
}
}
2019-11-08 02:31:44 +01:00
catch
{
}
2015-04-17 21:59:44 +02:00
}
}
2019-11-08 02:31:44 +01:00
finally
2015-04-17 21:59:44 +02:00
{
2019-11-08 02:31:44 +01:00
// force closing the file and releasing the locks
2021-04-25 18:31:05 +02:00
//SqliteConnection.ClearAllPools();
2019-11-08 02:31:44 +01:00
GC . Collect ( ) ;
GC . WaitForPendingFinalizers ( ) ;
2015-04-17 21:59:44 +02:00
}
}
#endregion
#region RepairCorruptedDatabaseImage ( )
2021-04-25 18:31:05 +02:00
private void RepairCorruptedDatabaseImage ( IDbCommand cmd )
2015-04-17 21:59:44 +02:00
{
cmd . CommandText = "REINDEX" ;
cmd . ExecuteNonQuery ( ) ;
}
#endregion
2015-04-22 09:51:21 +02:00
#region DetectFileType ( )
2021-04-25 18:31:05 +02:00
private FileType DetectFileType ( IDbCommand cmd )
2015-04-22 09:51:21 +02:00
{
2020-03-14 16:54:42 +01:00
this . tableNames . Clear ( ) ;
cmd . CommandText = "select name from sqlite_master where type='table'" ;
using var r = cmd . ExecuteReader ( ) ;
while ( r . Read ( ) )
this . tableNames . Add ( r . GetString ( 0 ) . ToUpper ( ) ) ;
if ( tableNames . Contains ( "SAT" ) & & tableNames . Contains ( "SAT_TP" ) )
2015-04-22 09:51:21 +02:00
return FileType . SatDb ;
2020-03-14 16:54:42 +01:00
if ( tableNames . Contains ( "CHNL" ) & & tableNames . Contains ( "SRV" ) & & tableNames . Contains ( "SRV_DVB" ) )
2015-06-05 06:08:40 +02:00
return FileType . ChannelDbDvb ;
2020-03-14 16:54:42 +01:00
if ( tableNames . Contains ( "CHNL" ) & & tableNames . Contains ( "SRV" ) & & tableNames . Contains ( "SRV_ANL" ) )
2015-06-05 06:08:40 +02:00
return FileType . ChannelDbAnalog ;
2015-04-22 09:51:21 +02:00
2020-05-02 19:04:43 +02:00
if ( tableNames . Contains ( "CHNL" ) & & tableNames . Contains ( "SRV" ) & & tableNames . Contains ( "SRV_IP" ) )
return FileType . ChannelDbIp ;
2020-03-20 01:25:27 +01:00
2015-04-22 09:51:21 +02:00
return FileType . Unknown ;
}
#endregion
#region ReadSatDatabase ( )
2021-04-25 18:31:05 +02:00
private void ReadSatDatabase ( SqliteConnection conn )
2015-04-22 09:51:21 +02:00
{
using ( var cmd = conn . CreateCommand ( ) )
{
this . RepairCorruptedDatabaseImage ( cmd ) ;
this . ReadSatellites ( cmd ) ;
this . ReadTransponders ( cmd ) ;
}
}
#endregion
2015-04-17 21:59:44 +02:00
#region ReadSatellites ( )
2021-04-25 18:31:05 +02:00
private void ReadSatellites ( IDbCommand cmd )
2015-04-17 21:59:44 +02:00
{
2015-06-05 06:08:40 +02:00
cmd . CommandText = "select distinct satId, cast(satName as blob), satPos, satDir from SAT" ;
2015-04-17 21:59:44 +02:00
using ( var r = cmd . ExecuteReader ( ) )
{
while ( r . Read ( ) )
{
Satellite sat = new Satellite ( r . GetInt32 ( 0 ) ) ;
int pos = Math . Abs ( r . GetInt32 ( 2 ) ) ;
2017-10-28 13:20:39 +02:00
// 171027 - ohuseyinoglu: For user-defined satellites, the direction may be -1
// (and not just 1 for "E", 0 for "W")
int dir = r . GetInt32 ( 3 ) ;
2019-02-05 19:30:47 +01:00
sat . OrbitalPosition = $"{pos / 10}.{pos % 10}{(dir == 1 ? " E " : dir == 0 ? " W " : " ")}" ;
sat . Name = ReadUtf16 ( r , 1 ) ;
2015-04-17 21:59:44 +02:00
this . DataRoot . AddSatellite ( sat ) ;
}
}
}
#endregion
#region ReadTransponders ( )
2021-04-25 18:31:05 +02:00
private void ReadTransponders ( IDbCommand cmd )
2015-04-17 21:59:44 +02:00
{
cmd . CommandText = "select satId, tpFreq, tpPol, tpSr, tpId from SAT_TP" ;
using ( var r = cmd . ExecuteReader ( ) )
{
while ( r . Read ( ) )
{
2017-10-28 13:20:39 +02:00
// 171027 - ohuseyinoglu: tpId is the primary key of this table, we should be able to use it as "id/dict. index"
// It will also be our lookup value for the CHNL table
int id = r . GetInt32 ( 4 ) ;
2015-04-17 21:59:44 +02:00
Transponder tp = new Transponder ( id ) ;
2017-10-28 13:20:39 +02:00
tp . FrequencyInMhz = ( decimal ) r . GetInt32 ( 1 ) / 1000 ;
tp . Number = id ;
2015-04-17 21:59:44 +02:00
tp . Polarity = r . GetInt32 ( 2 ) = = 0 ? 'H' : 'V' ;
2017-10-28 13:20:39 +02:00
tp . Satellite = this . DataRoot . Satellites . TryGet ( r . GetInt32 ( 0 ) ) ;
2015-06-05 06:08:40 +02:00
tp . SymbolRate = r . GetInt32 ( 3 ) ;
2015-04-17 21:59:44 +02:00
this . DataRoot . AddTransponder ( tp . Satellite , tp ) ;
}
}
}
#endregion
2015-04-22 09:51:21 +02:00
#region ReadChannelDatabase ( )
2021-04-25 18:31:05 +02:00
private void ReadChannelDatabase ( SqliteConnection conn , string dbPath , FileType fileType )
2015-04-17 21:59:44 +02:00
{
2015-04-22 09:51:21 +02:00
this . channelById . Clear ( ) ;
using ( var cmd = conn . CreateCommand ( ) )
2015-04-17 21:59:44 +02:00
{
2020-03-20 01:25:27 +01:00
var providers = fileType = = FileType . ChannelDbDvb ? this . ReadProviders ( cmd ) : null ;
var channelList = this . ReadChannels ( cmd , dbPath , providers , fileType ) ;
2015-04-22 09:51:21 +02:00
this . ReadFavorites ( cmd ) ;
this . dbPathByChannelList . Add ( channelList , dbPath ) ;
2015-04-17 21:59:44 +02:00
}
}
#endregion
2015-04-22 09:51:21 +02:00
#region ReadProviders ( )
2021-04-25 18:31:05 +02:00
private Dictionary < long , string > ReadProviders ( IDbCommand cmd )
2015-04-17 21:59:44 +02:00
{
2015-04-22 09:51:21 +02:00
var dict = new Dictionary < long , string > ( ) ;
try
2015-04-17 21:59:44 +02:00
{
2015-04-22 09:51:21 +02:00
cmd . CommandText = "select provId, cast(provName as blob) from PROV" ;
2020-07-13 10:00:41 +02:00
var prevEncoding = this . encoding ;
this . encoding = Encoding . BigEndianUnicode ; // while Sat and Service names might be utf16 binary data inside an utf8 envelope, the providers are always plain utf16
2015-04-22 09:51:21 +02:00
using ( var r = cmd . ExecuteReader ( ) )
2015-04-17 21:59:44 +02:00
{
2015-04-22 09:51:21 +02:00
while ( r . Read ( ) )
dict . Add ( r . GetInt64 ( 0 ) , ReadUtf16 ( r , 1 ) ) ;
2015-04-17 21:59:44 +02:00
}
2020-07-13 10:00:41 +02:00
this . encoding = prevEncoding ;
2015-04-17 21:59:44 +02:00
}
2015-04-22 09:51:21 +02:00
catch
{
}
return dict ;
2015-04-17 21:59:44 +02:00
}
#endregion
2015-06-05 06:08:40 +02:00
#region ReadChannels ( )
2021-04-25 18:31:05 +02:00
private ChannelList ReadChannels ( IDbCommand cmd , string dbPath , Dictionary < long , string > providers , FileType fileType )
2015-04-17 21:59:44 +02:00
{
2020-03-20 01:25:27 +01:00
var signalSource = DetectSignalSource ( cmd , fileType ) ;
2015-04-17 21:59:44 +02:00
2015-04-22 09:51:21 +02:00
string name = Path . GetFileName ( dbPath ) ;
2020-05-02 19:04:43 +02:00
ChannelList channelList = this . CreateChannelList ( signalSource , name ) ;
2020-03-20 01:25:27 +01:00
string table = fileType = = FileType . ChannelDbDvb ? "SRV_DVB" : fileType = = FileType . ChannelDbAnalog ? "SRV_ANL" : "SRV_IP" ;
2015-06-05 06:08:40 +02:00
List < string > fieldNames = new List < string > {
"chType" , "chNum" , "freq" , // CHNL
2020-03-20 01:25:27 +01:00
"SRV.srvId" , "major" , "progNum" , "cast(srvName as blob) srvName" , "srvType" , "hidden" , "scrambled" , "lockMode" , "numSel" , "elim" // SRV
2015-06-05 06:08:40 +02:00
} ;
2020-03-20 01:25:27 +01:00
if ( fileType = = FileType . ChannelDbDvb )
fieldNames . AddRange ( new [ ] { "onid" , "tsid" , "vidPid" , "provId" , "cast(shrtSrvName as blob) shrtSrvName" , "lcn" } ) ; // SRV_DVB
2015-11-27 01:51:08 +01:00
2015-06-05 06:08:40 +02:00
var sql = this . BuildQuery ( table , fieldNames ) ;
2015-04-17 21:59:44 +02:00
var fields = this . GetFieldMap ( fieldNames ) ;
cmd . CommandText = sql ;
using ( var r = cmd . ExecuteReader ( ) )
{
2020-03-20 01:25:27 +01:00
int prevNr = 0 ;
2015-04-17 21:59:44 +02:00
while ( r . Read ( ) )
{
2020-03-20 01:25:27 +01:00
if ( r . GetInt32 ( fields [ "elim" ] ) ! = 0 )
continue ;
2017-10-28 13:20:39 +02:00
// 171027 - ohuseyinoglu: With our change in transponder indexing, we can directly look it up by "chNum" now!
var tp = this . DataRoot . Transponder . TryGet ( r . GetInt32 ( 1 ) ) ;
// ... and get the satellite from that transponder - if set
// Note that we can have channels from multiple satellites in the same list, so this is a loop variable now
var sat = tp ? . Satellite ;
2020-07-12 02:39:43 +02:00
var channel = new DbChannel ( r , fields , this , providers , sat , tp ) ;
2020-03-20 01:25:27 +01:00
if ( channel . OldProgramNr = = prevNr ) // when there is a SRV_EXT_APP table in the database, the service with the highest ext_app "recState" takes priority
continue ;
2019-11-08 02:31:44 +01:00
this . DataRoot . AddChannel ( channelList , channel ) ;
this . channelById . Add ( channel . RecordIndex , channel ) ;
2020-03-20 01:25:27 +01:00
prevNr = channel . OldProgramNr ;
2015-04-17 21:59:44 +02:00
}
}
2015-06-05 06:08:40 +02:00
this . DataRoot . AddChannelList ( channelList ) ;
return channelList ;
}
#endregion
2020-07-12 02:39:43 +02:00
#region CreateChannelList ( )
2020-05-02 19:04:43 +02:00
private ChannelList CreateChannelList ( SignalSource signalSource , string name )
{
var list = new ChannelList ( signalSource , name ) ;
if ( ( list . SignalSource & SignalSource . IP ) ! = 0 )
{
list . VisibleColumnFieldNames = new List < string >
{
"OldPosition" , "Position" , "PrNr" , "Name" , "Favorites" , "SymbolRate"
} ;
}
return list ;
}
2020-07-12 02:39:43 +02:00
#endregion
2020-05-02 19:04:43 +02:00
2015-06-05 06:08:40 +02:00
#region DetectSignalSource ( )
2021-04-25 18:31:05 +02:00
private static SignalSource DetectSignalSource ( IDbCommand cmd , FileType fileType )
2015-06-05 06:08:40 +02:00
{
2020-03-20 01:25:27 +01:00
if ( fileType = = FileType . ChannelDbIp )
return SignalSource . IP | SignalSource . Digital ;
var signalSource = fileType = = FileType . ChannelDbAnalog ? SignalSource . Analog : SignalSource . Digital ;
2015-06-05 06:08:40 +02:00
cmd . CommandText = "select distinct chType from CHNL" ;
using ( var r = cmd . ExecuteReader ( ) )
{
if ( r . Read ( ) )
{
var ss = ChTypeToSignalSource ( r . GetInt32 ( 0 ) ) ;
if ( ss ! = 0 )
signalSource = ss ;
}
}
2019-08-29 16:57:20 +02:00
return signalSource ;
2015-06-05 06:08:40 +02:00
}
#endregion
#region ChTypeToSignalSource ( )
internal static SignalSource ChTypeToSignalSource ( int chType )
{
switch ( chType )
{
case 1 : return SignalSource . AnalogT ;
case 2 : return SignalSource . DvbT ;
case 3 : return SignalSource . AnalogC ;
case 4 : return SignalSource . DvbC ;
case 7 : return SignalSource . DvbS ;
default : return 0 ;
}
}
#endregion
#region BuildQuery ( )
private string BuildQuery ( string table , IList < string > fieldNames )
2015-04-17 21:59:44 +02:00
{
string sql = "select " ;
2015-06-05 06:08:40 +02:00
for ( int i = 0 ; i < fieldNames . Count ; i + + )
2015-04-17 21:59:44 +02:00
{
if ( i > 0 )
sql + = "," ;
sql + = fieldNames [ i ] ;
}
sql + = " from " + table + " inner join SRV on SRV.srvId=" + table + ".srvId inner join CHNL on CHNL.chId=SRV.chId" ;
2020-03-14 16:54:42 +01:00
2020-03-20 01:25:27 +01:00
if ( this . tableNames . Contains ( "SRV_EXT_APP" ) ) // in format 1352.0 there are duplicate "major" values in SRV and this recState seems to be the only difference
2021-04-10 10:08:45 +02:00
sql + = " left outer join SRV_EXT_APP on SRV_EXT_APP.srvId=SRV.srvId order by SRV.major, ifnull(SRV_EXT_APP.recState,0) desc" ;
2020-03-14 16:54:42 +01:00
2015-04-17 21:59:44 +02:00
return sql ;
}
#endregion
#region GetFieldMap ( )
2015-06-05 06:08:40 +02:00
private IDictionary < string , int > GetFieldMap ( IList < string > fieldNames )
2015-04-17 21:59:44 +02:00
{
Dictionary < string , int > field = new Dictionary < string , int > ( ) ;
2015-06-05 06:08:40 +02:00
for ( int i = 0 ; i < fieldNames . Count ; i + + )
2020-03-20 01:25:27 +01:00
{
var idx = fieldNames [ i ] . LastIndexOf ( ' ' ) + 1 ;
field [ fieldNames [ i ] . Substring ( idx ) ] = i ;
}
2015-04-17 21:59:44 +02:00
return field ;
}
#endregion
#region ReadFavorites ( )
2021-04-25 18:31:05 +02:00
private void ReadFavorites ( IDbCommand cmd )
2015-04-17 21:59:44 +02:00
{
cmd . CommandText = "select srvId, fav, pos from SRV_FAV" ;
var r = cmd . ExecuteReader ( ) ;
2020-03-20 01:25:27 +01:00
int favPosAdjust = tableNames . Contains ( "SRV_EXT_APP" ) ? 0 : 1 ;
2015-04-17 21:59:44 +02:00
while ( r . Read ( ) )
{
var channel = this . channelById . TryGet ( r . GetInt64 ( 0 ) ) ;
if ( channel = = null )
continue ;
2015-11-29 20:36:56 +01:00
int fav = r . GetInt32 ( 1 ) - 1 ; // fav values start with 1 in the table
2020-03-20 01:25:27 +01:00
int pos = r . GetInt32 ( 2 ) + favPosAdjust ; // pos values start with 0 or 1
2015-04-22 09:51:21 +02:00
if ( pos > = 0 )
2015-11-29 20:36:56 +01:00
{
2015-04-17 21:59:44 +02:00
channel . Favorites | = ( Favorites ) ( 1 < < fav ) ;
2021-03-13 18:11:30 +01:00
channel . SetOldPosition ( fav + 1 , pos ) ;
2015-11-29 20:36:56 +01:00
}
2015-04-17 21:59:44 +02:00
}
}
#endregion
2015-04-22 09:51:21 +02:00
#region ReadUtf16 ( )
2021-04-25 18:31:05 +02:00
internal string ReadUtf16 ( IDataReader r , int fieldIndex )
2015-04-22 09:51:21 +02:00
{
if ( r . IsDBNull ( fieldIndex ) )
return null ;
byte [ ] nameBytes = new byte [ 200 ] ;
2021-04-25 18:31:05 +02:00
// Microsoft.Data.SqlDataReader (and the underlying native DLLs) are bugged and throw a memory access violation when using r.GetBytes(...)
// nameLen = (int)r.GetBytes(fieldIndex, 0, nameBytes, 0, nameBytes.Length);
int nameLen = 0 ;
var obj = r . GetValue ( fieldIndex ) ;
if ( obj is byte [ ] buffer )
{
nameBytes = buffer ;
nameLen = buffer . Length ;
}
2020-07-13 10:00:41 +02:00
this . encoding ? ? = AutoDetectUtf16Encoding ( nameBytes , nameLen ) ;
2020-07-12 02:39:43 +02:00
if ( this . encoding = = null )
return string . Empty ;
return encoding . GetString ( nameBytes , 0 , nameLen ) . Replace ( "\0" , "" ) ; // remove trailing \0 characters found in Samsung "_T_..." channel list
}
#endregion
#region AutoDetectUtf16Endian ( )
2020-07-13 10:00:41 +02:00
private Encoding AutoDetectUtf16Encoding ( byte [ ] nameBytes , int nameLen )
2020-07-12 02:39:43 +02:00
{
if ( this . DefaultEncoding is UnicodeEncoding )
return this . DefaultEncoding ;
int evenBytesZero = 0 ;
int oddBytesZero = 0 ;
2020-07-13 10:00:41 +02:00
int bytesAbove128 = 0 ;
2020-07-12 02:39:43 +02:00
for ( int i = 0 ; i < nameLen ; i + = 2 )
{
if ( nameBytes [ i ] = = 0 )
+ + evenBytesZero ;
2020-07-13 10:00:41 +02:00
if ( nameBytes [ i ] > = 128 )
+ + bytesAbove128 ;
2020-07-12 02:39:43 +02:00
if ( nameBytes [ i + 1 ] = = 0 )
+ + oddBytesZero ;
2020-07-13 10:00:41 +02:00
if ( nameBytes [ i + 1 ] > = 128 )
+ + bytesAbove128 ;
2020-07-12 02:39:43 +02:00
}
if ( evenBytesZero + oddBytesZero = = nameLen )
return null ;
2020-07-13 10:00:41 +02:00
if ( bytesAbove128 + 1 > = nameLen )
{
//this.Features.ChannelNameEdit = ChannelNameEditMode.None; // unclear if the encoder produces byte sequences that the TV can decode again
return new Utf16InsideUtf8EnvelopeEncoding ( ) ;
}
2020-07-12 02:39:43 +02:00
return evenBytesZero > = oddBytesZero ? Encoding . BigEndianUnicode : Encoding . Unicode ;
}
#endregion
#region DefaultEncoding
public override Encoding DefaultEncoding
{
get = > base . DefaultEncoding ;
set
{
if ( ! ( value is UnicodeEncoding ) )
return ;
var oldEncoding = base . DefaultEncoding ;
if ( oldEncoding ! = null )
{
// change encoding of channel names
foreach ( var list in this . DataRoot . ChannelLists )
{
foreach ( var chan in list . Channels )
{
byte [ ] bytes ;
if ( chan . Name ! = null )
{
bytes = oldEncoding . GetBytes ( chan . Name ) ;
chan . Name = value . GetString ( bytes ) ;
}
if ( chan . ShortName ! = null )
{
bytes = oldEncoding . GetBytes ( chan . ShortName ) ;
chan . ShortName = value . GetString ( bytes ) ;
}
}
}
}
base . DefaultEncoding = value ;
}
2015-04-22 09:51:21 +02:00
}
#endregion
2015-04-17 21:59:44 +02:00
#region Save ( )
public override void Save ( string tvOutputFile )
{
2019-11-08 02:31:44 +01:00
foreach ( var channelList in this . DataRoot . ChannelLists )
2015-04-17 21:59:44 +02:00
{
2019-11-08 02:31:44 +01:00
var dbPath = this . dbPathByChannelList [ channelList ] ;
SaveChannelList ( channelList , dbPath ) ;
2015-04-17 21:59:44 +02:00
}
2019-11-08 02:31:44 +01:00
// force closing the file and releasing the locks
2021-04-25 18:31:05 +02:00
//SqliteConnection.ClearAllPools();
2019-11-08 02:31:44 +01:00
GC . Collect ( ) ;
GC . WaitForPendingFinalizers ( ) ;
2015-04-22 09:51:21 +02:00
2019-11-08 02:31:44 +01:00
this . ZipToOutputFile ( tvOutputFile ) ;
2015-04-22 09:51:21 +02:00
}
#endregion
#region SaveChannelList ( )
private void SaveChannelList ( ChannelList channelList , string dbPath )
{
2021-04-25 18:31:05 +02:00
using var conn = new SqliteConnection ( "Data Source=" + dbPath ) ;
2020-07-13 10:00:41 +02:00
conn . Open ( ) ;
using ( var trans = conn . BeginTransaction ( ) )
2015-04-17 21:59:44 +02:00
{
2021-04-25 18:31:05 +02:00
using var cmdUpdateSrv = PrepareUpdateCommand ( conn ) ;
using var cmdDeleteSrv = PrepareDeleteCommand ( conn , ( channelList . SignalSource & SignalSource . Digital ) ! = 0 ) ;
using var cmdInsertFav = PrepareInsertFavCommand ( conn ) ;
using var cmdUpdateFav = PrepareUpdateFavCommand ( conn ) ;
using var cmdDeleteFav = PrepareDeleteFavCommand ( conn ) ;
2020-07-13 10:00:41 +02:00
Editor . SequentializeFavPos ( channelList , 5 ) ;
this . WriteChannels ( cmdUpdateSrv , cmdDeleteSrv , cmdInsertFav , cmdUpdateFav , cmdDeleteFav , channelList ) ;
trans . Commit ( ) ;
2015-04-17 21:59:44 +02:00
}
2021-04-25 18:31:05 +02:00
using var cmd = conn . CreateCommand ( ) ;
this . RepairCorruptedDatabaseImage ( cmd ) ;
2015-04-17 21:59:44 +02:00
}
2015-04-22 09:51:21 +02:00
2015-04-17 21:59:44 +02:00
#endregion
2015-09-19 23:24:31 +02:00
#region Prepare * Command ( )
2021-04-25 18:31:05 +02:00
private SqliteCommand PrepareUpdateCommand ( SqliteConnection conn )
2015-04-17 21:59:44 +02:00
{
2020-07-13 10:00:41 +02:00
var canUpdateNames = this . Features . ChannelNameEdit ! = ChannelNameEditMode . None ;
2015-09-19 23:24:31 +02:00
var cmd = conn . CreateCommand ( ) ;
2020-07-13 10:00:41 +02:00
var updateSrvName = canUpdateNames ? ", srvName=cast(@srvname as varchar)" : "" ;
cmd . CommandText = "update SRV set major=@nr, lockMode=@lock, hideGuide=@hidden, hidden=@hidden, numSel=@numsel" + updateSrvName + " where srvId=@id" ;
2021-04-25 18:31:05 +02:00
cmd . Parameters . Add ( "@id" , SqliteType . Integer ) ;
cmd . Parameters . Add ( "@nr" , SqliteType . Integer ) ;
cmd . Parameters . Add ( "@lock" , SqliteType . Integer ) ;
cmd . Parameters . Add ( "@hidden" , SqliteType . Integer ) ;
cmd . Parameters . Add ( "@numsel" , SqliteType . Integer ) ;
2020-07-13 10:00:41 +02:00
if ( canUpdateNames )
2021-04-25 18:31:05 +02:00
cmd . Parameters . Add ( "@srvname" , SqliteType . Blob ) ;
2015-09-19 23:24:31 +02:00
cmd . Prepare ( ) ;
return cmd ;
2015-04-17 21:59:44 +02:00
}
2015-09-19 23:24:31 +02:00
2021-04-25 18:31:05 +02:00
private SqliteCommand PrepareDeleteCommand ( SqliteConnection conn , bool digital )
2015-09-19 23:24:31 +02:00
{
var cmd = conn . CreateCommand ( ) ;
2015-11-27 01:51:08 +01:00
var sql = new StringBuilder ( ) ;
cmd . CommandText = "select name from sqlite_master where sql like '%srvId integer%' order by name desc" ;
using ( var r = cmd . ExecuteReader ( ) )
2015-11-21 19:34:30 +01:00
{
2015-11-27 01:51:08 +01:00
while ( r . Read ( ) )
sql . AppendLine ( $"; delete from {r.GetString(0)} where srvId=@id" ) ;
2015-11-21 19:34:30 +01:00
}
2015-11-27 01:51:08 +01:00
cmd . CommandText = sql . ToString ( ) ;
2021-04-25 18:31:05 +02:00
cmd . Parameters . Add ( "@id" , SqliteType . Integer ) ;
2015-09-19 23:24:31 +02:00
cmd . Prepare ( ) ;
return cmd ;
}
2021-04-25 18:31:05 +02:00
private SqliteCommand PrepareInsertFavCommand ( SqliteConnection conn )
2015-09-19 23:24:31 +02:00
{
var cmd = conn . CreateCommand ( ) ;
cmd . CommandText = "insert into SRV_FAV (srvId, fav, pos) values (@id, @fav, @pos)" ;
2021-04-25 18:31:05 +02:00
cmd . Parameters . Add ( "@id" , SqliteType . Integer ) ;
cmd . Parameters . Add ( "@fav" , SqliteType . Integer ) ;
cmd . Parameters . Add ( "@pos" , SqliteType . Integer ) ;
2015-09-19 23:24:31 +02:00
cmd . Prepare ( ) ;
return cmd ;
}
2019-11-08 02:31:44 +01:00
2021-04-25 18:31:05 +02:00
private SqliteCommand PrepareUpdateFavCommand ( SqliteConnection conn )
2015-09-19 23:24:31 +02:00
{
var cmd = conn . CreateCommand ( ) ;
cmd . CommandText = "update SRV_FAV set pos=@pos where srvId=@id and fav=@fav" ;
2021-04-25 18:31:05 +02:00
cmd . Parameters . Add ( "@id" , SqliteType . Integer ) ;
cmd . Parameters . Add ( "@fav" , SqliteType . Integer ) ;
cmd . Parameters . Add ( "@pos" , SqliteType . Integer ) ;
2015-09-19 23:24:31 +02:00
cmd . Prepare ( ) ;
return cmd ;
}
2021-04-25 18:31:05 +02:00
private SqliteCommand PrepareDeleteFavCommand ( SqliteConnection conn )
2015-09-19 23:24:31 +02:00
{
var cmd = conn . CreateCommand ( ) ;
cmd . CommandText = "delete from SRV_FAV where srvId=@id and fav=@fav" ;
2021-04-25 18:31:05 +02:00
cmd . Parameters . Add ( "@id" , SqliteType . Integer ) ;
cmd . Parameters . Add ( "@fav" , SqliteType . Integer ) ;
2015-09-19 23:24:31 +02:00
cmd . Prepare ( ) ;
return cmd ;
}
2015-04-17 21:59:44 +02:00
#endregion
#region WriteChannels ( )
2021-04-25 18:31:05 +02:00
private void WriteChannels ( SqliteCommand cmdUpdateSrv , SqliteCommand cmdDeleteSrv , SqliteCommand cmdInsertFav , SqliteCommand cmdUpdateFav , SqliteCommand cmdDeleteFav ,
2015-06-13 18:37:59 +02:00
ChannelList channelList , bool analog = false )
2015-11-29 20:36:56 +01:00
{
2020-07-13 10:00:41 +02:00
bool canUpdateNames = this . Features . ChannelNameEdit ! = ChannelNameEditMode . None ;
2019-11-08 02:31:44 +01:00
foreach ( ChannelInfo channelInfo in channelList . Channels . ToList ( ) )
2015-04-17 21:59:44 +02:00
{
var channel = channelInfo as DbChannel ;
if ( channel = = null ) // ignore reference list proxy channels
continue ;
2019-02-10 15:56:03 +01:00
2019-11-08 02:31:44 +01:00
if ( channel . IsDeleted )
2015-06-13 18:37:59 +02:00
{
2015-11-29 20:36:56 +01:00
// delete channel from all tables that have a reference to srvId
2015-06-13 18:37:59 +02:00
cmdDeleteSrv . Parameters [ "@id" ] . Value = channel . RecordIndex ;
cmdDeleteSrv . ExecuteNonQuery ( ) ;
2019-11-08 02:31:44 +01:00
channelList . Channels . Remove ( channelInfo ) ;
2015-11-29 20:36:56 +01:00
continue ;
2015-06-13 18:37:59 +02:00
}
2015-11-29 20:36:56 +01:00
// update channel record
cmdUpdateSrv . Parameters [ "@id" ] . Value = channel . RecordIndex ;
2019-08-11 10:04:29 +02:00
cmdUpdateSrv . Parameters [ "@nr" ] . Value = channel . NewProgramNr ;
2015-11-29 20:36:56 +01:00
cmdUpdateSrv . Parameters [ "@lock" ] . Value = channel . Lock ;
cmdUpdateSrv . Parameters [ "@hidden" ] . Value = channel . Hidden ;
cmdUpdateSrv . Parameters [ "@numsel" ] . Value = ! channel . Skip ;
2020-07-13 10:00:41 +02:00
if ( canUpdateNames )
cmdUpdateSrv . Parameters [ "@srvname" ] . Value = channel . Name = = null ? ( object ) DBNull . Value : encoding . GetBytes ( channel . Name ) ;
2015-11-29 20:36:56 +01:00
cmdUpdateSrv . ExecuteNonQuery ( ) ;
// update favorites
2015-06-13 18:37:59 +02:00
for ( int i = 0 , mask = 1 ; i < 5 ; i + + , mask < < = 1 )
2015-04-17 21:59:44 +02:00
{
2021-03-07 16:12:21 +01:00
int oldPos = channel . GetOldPosition ( 1 + i ) ;
int newPos = ( ( int ) channel . Favorites & mask ) ! = 0 ? channel . GetPosition ( 1 + i ) : - 1 ;
2015-11-29 20:36:56 +01:00
if ( newPos > = 0 )
2015-04-17 21:59:44 +02:00
{
2015-06-13 18:37:59 +02:00
var c = oldPos < 0 ? cmdInsertFav : cmdUpdateFav ;
2015-04-17 21:59:44 +02:00
c . Parameters [ "@id" ] . Value = channel . RecordIndex ;
2015-04-22 09:51:21 +02:00
c . Parameters [ "@fav" ] . Value = i + 1 ;
2015-11-29 20:36:56 +01:00
c . Parameters [ "@pos" ] . Value = newPos - 1 ;
2015-04-17 21:59:44 +02:00
c . ExecuteNonQuery ( ) ;
}
else
{
2015-06-13 18:37:59 +02:00
cmdDeleteFav . Parameters [ "@id" ] . Value = channel . RecordIndex ;
cmdDeleteFav . Parameters [ "@fav" ] . Value = i + 1 ;
cmdDeleteFav . ExecuteNonQuery ( ) ;
2015-04-17 21:59:44 +02:00
}
2015-11-29 20:36:56 +01:00
2021-03-13 18:11:30 +01:00
channel . SetPosition ( i + 1 , newPos ) ;
2015-04-17 21:59:44 +02:00
}
}
}
#endregion
}
}