1 
2 #include "config.h"
3 #include "logger.h"
4 #include "global.h"
5 
6 #include <KConfigGroup>
7 #include <QDir>
8 #include <QDomElement>
9 #include <QTime>
10 #include <solid/device.h>
11 #include <KStandardDirs>
12 
13 
Config(Logger * _logger,QObject * parent)14 Config::Config( Logger *_logger, QObject *parent )
15     : QObject( parent ),
16     logger( _logger )
17 {
18     connect( this, SIGNAL(updateWriteLogFilesSetting(bool)), logger, SLOT(updateWriteSetting(bool)) );
19 
20     pPluginLoader = new PluginLoader( logger, this );
21     pTagEngine = new TagEngine( this );
22     pConversionOptionsManager = new ConversionOptionsManager( pPluginLoader, this );
23 }
24 
~Config()25 Config::~Config()
26 {
27     save();
28     qDeleteAll(data.profiles);
29 }
30 
load()31 void Config::load()
32 {
33     QTime time;
34     time.start();
35 
36     QStringList formats;
37 
38     KSharedConfig::Ptr conf = KGlobal::config();
39     KConfigGroup group;
40 
41     group = conf->group( "General" );
42     data.app.configVersion = group.readEntry( "configVersion", 0 );
43     data.general.startTab = group.readEntry( "startTab", 0 );
44     data.general.lastTab = group.readEntry( "lastTab", 0 );
45     data.general.defaultProfile = group.readEntry( "defaultProfile", i18n("Last used") );
46     data.general.lastProfile = group.readEntry( "lastProfile", i18n("High") );
47     data.general.defaultFormat = group.readEntry( "defaultFormat", i18n("Last used") );
48     data.general.lastFormat = group.readEntry( "lastFormat", "ogg vorbis" );
49     data.general.lastOutputDirectoryMode = group.readEntry( "lastOutputDirectoryMode", 0 );
50     data.general.specifyOutputDirectory = group.readEntry( "specifyOutputDirectory", QDir::homePath() + "/soundKonverter" );
51     data.general.metaDataOutputDirectory = group.readEntry( "metaDataOutputDirectory", QDir::homePath() + "/soundKonverter/%b/%d - %n - %a - %t" );
52     data.general.copyStructureOutputDirectory = group.readEntry( "copyStructureOutputDirectory", QDir::homePath() + "/soundKonverter" );
53     data.general.lastMetaDataOutputDirectoryPaths = group.readEntry( "lastMetaDataOutputDirectoryPaths", QStringList() );
54     data.general.lastNormalOutputDirectoryPaths = group.readEntry( "lastNormalOutputDirectoryPaths", QStringList() );
55     data.general.waitForAlbumGain = group.readEntry( "waitForAlbumGain", true );
56     data.general.useVFATNames = group.readEntry( "useVFATNames", false );
57     data.general.copyIfSameCodec = group.readEntry( "copyIfSameCodec", false );
58     data.general.writeLogFiles = group.readEntry( "writeLogFiles", false );
59     data.general.conflictHandling = (Config::Data::General::ConflictHandling)group.readEntry( "conflictHandling", 0 );
60 //     data.general.priority = group.readEntry( "priority", 10 );
61     data.general.numFiles = group.readEntry( "numFiles", 0 );
62     data.general.numReplayGainFiles = group.readEntry( "numReplayGainFiles", 0 );
63     if( data.general.numFiles == 0 || data.general.numReplayGainFiles == 0 )
64     {
65         QList<Solid::Device> processors = Solid::Device::listFromType(Solid::DeviceInterface::Processor, QString());
66         const int num = processors.count() > 0 ? processors.count() : 1;
67         if( data.general.numFiles == 0 )
68             data.general.numFiles = num;
69         if( data.general.numReplayGainFiles == 0 )
70             data.general.numReplayGainFiles = num;
71     }
72 //     data.general.executeUserScript = group.readEntry( "executeUserScript", false );
73 //     data.general.showToolBar = group.readEntry( "showToolBar", false );
74 //     data.general.outputFilePermissions = group.readEntry( "outputFilePermissions", 644 );
75     data.general.actionMenuConvertMimeTypes = group.readEntry( "actionMenuConvertMimeTypes", QStringList() );
76     data.general.actionMenuReplayGainMimeTypes = group.readEntry( "actionMenuReplayGainMimeTypes", QStringList() );
77     data.general.replayGainGrouping = (Config::Data::General::ReplayGainGrouping)group.readEntry( "replayGainGrouping", 0 );
78     data.general.preferredOggVorbisExtension = group.readEntry( "preferredOggVorbisExtension", "ogg" );
79     data.general.preferredVorbisCommentCommentTag = group.readEntry( "preferredVorbisCommentCommentTag", "DESCRIPTION" );
80     data.general.preferredVorbisCommentTrackTotalTag = group.readEntry( "preferredVorbisCommentTrackTotalTag", "TRACKTOTAL" );
81     data.general.preferredVorbisCommentDiscTotalTag = group.readEntry( "preferredVorbisCommentDiscTotalTag", "DISCTOTAL" );
82 
83     // due to a bug lastNormalOutputDirectoryPaths could have more than 5 items
84     while( data.general.lastNormalOutputDirectoryPaths.count() > 5 )
85         data.general.lastNormalOutputDirectoryPaths.takeLast();
86 
87     group = conf->group( "Advanced" );
88     data.advanced.useSharedMemoryForTempFiles = group.readEntry( "useSharedMemoryForTempFiles", false );
89     data.advanced.sharedMemorySize = 0;
90     if( QFile::exists("/dev/shm") )
91     {
92         system("df -B 1M /dev/shm | tail -1 > /dev/shm/soundkonverter_shm_size");
93         QFile chkdf("/dev/shm/soundkonverter_shm_size");
94         if( chkdf.open(QIODevice::ReadOnly|QIODevice::Text) )
95         {
96             QTextStream t( &chkdf );
97             QString s = t.readLine();
98             QRegExp rxlen( "^(?:\\S+)(?:\\s+)(?:\\s+)(\\d+)(?:\\s+)(\\d+)(?:\\s+)(\\d+)(?:\\s+)(\\d+)" );
99             if( s.contains(rxlen) )
100             {
101                 data.advanced.sharedMemorySize = rxlen.cap(1).toInt();
102             }
103             chkdf.close();
104         }
105         chkdf.remove();
106     }
107     data.advanced.maxSizeForSharedMemoryTempFiles = group.readEntry( "maxSizeForSharedMemoryTempFiles", data.advanced.sharedMemorySize / 4 );
108     data.advanced.usePipes = group.readEntry( "usePipes", false );
109     data.advanced.ejectCdAfterRip = group.readEntry( "ejectCdAfterRip", true );
110 
111     group = conf->group( "CoverArt" );
112     data.coverArt.writeCovers = group.readEntry( "writeCovers", 1 );
113     data.coverArt.writeCoverName = group.readEntry( "writeCoverName", 0 );
114     data.coverArt.writeCoverDefaultName = group.readEntry( "writeCoverDefaultName", i18nc("cover file name","cover") );
115 
116     group = conf->group( "Backends" );
117     formats = group.readEntry( "formats", QStringList() );
118     foreach( const QString& format, formats )
119     {
120         CodecData codecData;
121         codecData.codecName = format;
122         codecData.encoders = group.readEntry( format + "_encoders", QStringList() );
123         codecData.decoders = group.readEntry( format + "_decoders", QStringList() );
124         codecData.replaygain = group.readEntry( format + "_replaygain", QStringList() );
125         data.backends.codecs += codecData;
126     }
127     data.backends.filters = group.readEntry( "filters", QStringList() );
128     data.backends.enabledFilters = group.readEntry( "enabledFilters", QStringList() );
129 
130     pPluginLoader->load();
131 
132     QString pluginName;
133     QStringList enabledPlugins;
134     QStringList newPlugins;
135     int codecIndex;
136 
137     formats = pPluginLoader->formatList( PluginLoader::Possibilities(PluginLoader::Encode|PluginLoader::Decode|PluginLoader::ReplayGain), PluginLoader::CompressionType(PluginLoader::InferiorQuality|PluginLoader::Lossy|PluginLoader::Lossless|PluginLoader::Hybrid) );
138 
139     // build default backend priority list
140 
141     foreach( const QString& format, formats )
142     {
143         if( format == "wav" )
144             continue;
145 
146         // get the index of the format in the data.backends.codecs list for direct access
147         codecIndex = -1;
148         for( int i=0; i<data.backends.codecs.count(); i++ )
149         {
150             if( data.backends.codecs.at(i).codecName == format )
151             {
152                 codecIndex = i;
153                 break;
154             }
155         }
156         // add format to the data.backends.codecs list if it isn't already in it
157         if( codecIndex == -1 )
158         {
159             CodecData codecData;
160             codecData.codecName = format;
161             data.backends.codecs += codecData;
162             codecIndex = data.backends.codecs.count() - 1;
163         }
164 
165         // encoders
166         enabledPlugins.clear();
167         newPlugins.clear();
168         // register existing enabled plugins as such and list new enabled plugins
169         foreach( const ConversionPipeTrunk& trunk, pPluginLoader->conversionFilterPipeTrunks )
170         {
171             if( trunk.codecTo == format && trunk.enabled )
172             {
173                 pluginName = trunk.plugin->name();
174                 enabledPlugins += pluginName;
175                 if( !data.backends.codecs.at(codecIndex).encoders.contains(pluginName) && newPlugins.filter(QRegExp("[0-9]{8,8}"+pluginName)).count()==0 )
176                 {
177                     newPlugins += QString::number(trunk.rating).rightJustified(8,'0') + pluginName;
178                 }
179             }
180         }
181         // remove plugins from the encoder list if they aren't enabled any more
182         for( int i=0; i<data.backends.codecs.at(codecIndex).encoders.count(); i++ )
183         {
184             if( !enabledPlugins.contains(data.backends.codecs.at(codecIndex).encoders.at(i)) )
185             {
186                 data.backends.codecs[codecIndex].encoders.removeAt(i);
187                 i--;
188             }
189         }
190         // sort new enabled plugins and append them to the encoder list
191         newPlugins.sort();
192         for( int i=newPlugins.count()-1; i>=0; i-- ) // QStringList doesn't support sorting in descending order
193         {
194             data.backends.codecs[codecIndex].encoders += newPlugins.at(i).right(newPlugins.at(i).length()-8);
195         }
196 
197         // decoders
198         enabledPlugins.clear();
199         newPlugins.clear();
200         // register existing enabled plugins as such and list new enabled plugins
201         foreach( const ConversionPipeTrunk& trunk, pPluginLoader->conversionFilterPipeTrunks )
202         {
203             if( trunk.codecFrom == format && trunk.enabled )
204             {
205                 pluginName = trunk.plugin->name();
206                 enabledPlugins += pluginName;
207                 if( !data.backends.codecs.at(codecIndex).decoders.contains(pluginName) && newPlugins.filter(QRegExp("[0-9]{8,8}"+pluginName)).count()==0 )
208                 {
209                     newPlugins += QString::number(trunk.rating).rightJustified(8,'0') + pluginName;
210                 }
211             }
212         }
213         // remove plugins from the decoder list if they aren't enabled any more
214         for( int i=0; i<data.backends.codecs.at(codecIndex).decoders.count(); i++ )
215         {
216             if( !enabledPlugins.contains(data.backends.codecs.at(codecIndex).decoders.at(i)) )
217             {
218                 data.backends.codecs[codecIndex].decoders.removeAt(i);
219                 i--;
220             }
221         }
222         // sort new enabled plugins and append them to the decoder list
223         newPlugins.sort();
224         for( int i=newPlugins.count()-1; i>=0; i-- ) // QStringList doesn't support sorting in descending order
225         {
226             data.backends.codecs[codecIndex].decoders += newPlugins.at(i).right(newPlugins.at(i).length()-8);
227         }
228 
229         // replaygain
230         enabledPlugins.clear();
231         const bool internalReplayGainEnabled = pPluginLoader->hasCodecInternalReplayGain(format);
232         if( internalReplayGainEnabled )
233         {
234             enabledPlugins += i18n("Try internal");
235         }
236         newPlugins.clear();
237         // register existing enabled plugins as such and list new enabled plugins
238         foreach( const ReplayGainPipe& pipe, pPluginLoader->replaygainPipes )
239         {
240             if( pipe.codecName == format && pipe.enabled )
241             {
242                 pluginName = pipe.plugin->name();
243                 enabledPlugins += pluginName;
244                 if( !data.backends.codecs.at(codecIndex).replaygain.contains(pluginName) && newPlugins.filter(QRegExp("[0-9]{8,8}"+pluginName)).count()==0 )
245                 {
246                     newPlugins += QString::number(pipe.rating).rightJustified(8,'0') + pluginName;
247                 }
248             }
249         }
250         // remove plugins from the replay gain list if they aren't enabled any more
251         for( int i=0; i<data.backends.codecs.at(codecIndex).replaygain.count(); i++ )
252         {
253             if( !enabledPlugins.contains(data.backends.codecs.at(codecIndex).replaygain.at(i)) )
254             {
255                 data.backends.codecs[codecIndex].replaygain.removeAt(i);
256                 i--;
257             }
258         }
259         // append internal replay gain if it is enabled
260         if( internalReplayGainEnabled && !data.backends.codecs.at(codecIndex).replaygain.contains(i18n("Try internal")) )
261         {
262             data.backends.codecs[codecIndex].replaygain += i18n("Try internal");
263         }
264         // sort new enabled plugins and append them to the replay gain list
265         newPlugins.sort();
266         for( int i=newPlugins.count()-1; i>=0; i-- ) // QStringList doesn't support sorting in descending order
267         {
268             data.backends.codecs[codecIndex].replaygain += newPlugins.at(i).right(newPlugins.at(i).length()-8);
269         }
270     }
271 
272     // filters
273     enabledPlugins.clear();
274     newPlugins.clear();
275     // register existing enabled plugins as such and list new enabled plugins
276     foreach( FilterPlugin *plugin, pPluginLoader->getAllFilterPlugins() )
277     {
278         pluginName = plugin->name();
279         foreach( const ConversionPipeTrunk& trunk, plugin->codecTable() )
280         {
281             if( trunk.enabled && trunk.codecFrom == "wav" && trunk.codecTo == "wav" )
282             {
283                 enabledPlugins += pluginName;
284                 if( !data.backends.filters.contains(pluginName) && newPlugins.filter(QRegExp("[0-9]{8,8}"+pluginName)).count()==0 )
285                 {
286                     newPlugins += QString::number(trunk.rating).rightJustified(8,'0') + pluginName;
287                     break;
288                 }
289             }
290         }
291     }
292     // remove plugins from the filter list if they aren't enabled any more
293     for( int i=0; i<data.backends.filters.count(); i++ )
294     {
295         if( !enabledPlugins.contains(data.backends.filters.at(i)) )
296         {
297             data.backends.filters.removeAt(i);
298             i--;
299         }
300     }
301     // sort new enabled plugins and append them to the filter list
302     newPlugins.sort();
303     for( int i=newPlugins.count()-1; i>=0; i-- ) // QStringList doesn't support sorting in descending order
304     {
305         data.backends.filters += newPlugins.at(i).right(newPlugins.at(i).length()-8);
306     }
307     // since filters can be completely disabled we have to keep track of data.backends.enabledFilters as well
308     // remove plugins from the enabledFilters list if they aren't enabled any more
309     for( int i=0; i<data.backends.enabledFilters.count(); i++ )
310     {
311         if( !data.backends.filters.contains(data.backends.enabledFilters.at(i)) )
312         {
313             data.backends.enabledFilters.removeAt(i);
314             i--;
315         }
316     }
317     // always enable the first filter
318     if( data.app.configVersion < 1005 && data.backends.enabledFilters.isEmpty() && data.backends.filters.count() > 0 )
319     {
320         data.backends.enabledFilters.append( data.backends.filters.first() );
321     }
322 
323     // import KDE4 profiles - paths are hard-coded because I don't know how to properly get the KDE4 data path
324     if( data.app.configVersion < 1006 )
325     {
326         const QString src = QDir::homePath() + "/.kde4/share/apps/soundkonverter/profiles.xml";
327         const QString dest = KStandardDirs::locateLocal("data","soundkonverter/profiles.xml");
328         if( QFile::exists(src) && !QFile::exists(dest) )
329         {
330             QFile::copy(src, dest);
331             logger->log( 1000, i18n("Importing old profiles from: %1").arg(src) );
332         }
333     }
334 
335     logger->log( 1000, "\nloading profiles ..." );
336     QFile listFile( KStandardDirs::locateLocal("data","soundkonverter/profiles.xml") );
337     if( listFile.open( QIODevice::ReadOnly ) )
338     {
339         QDomDocument list("soundkonverter_profilelist");
340         if( list.setContent( &listFile ) )
341         {
342             QDomElement root = list.documentElement();
343             if( root.nodeName() == "soundkonverter" && root.attribute("type") == "profilelist" )
344             {
345                 QDomNodeList conversionOptionsElements = root.elementsByTagName("conversionOptions");
346                 for( int i=0; i<conversionOptionsElements.count(); i++ )
347                 {
348                     const QString profileName = conversionOptionsElements.at(i).toElement().attribute("profileName");
349                     const QString pluginName = conversionOptionsElements.at(i).toElement().attribute("pluginName");
350                     CodecPlugin *plugin = qobject_cast<CodecPlugin*>(pPluginLoader->backendPluginByName( pluginName ));
351                     if( plugin )
352                     {
353                         QList<QDomElement> filterOptionsElements;
354                         ConversionOptions *conversionOptions = plugin->conversionOptionsFromXml( conversionOptionsElements.at(i).toElement(), &filterOptionsElements );
355                         if( conversionOptions )
356                         {
357                             foreach( const QDomElement& filterOptionsElement, filterOptionsElements )
358                             {
359                                 FilterOptions *filterOptions = 0;
360                                 const QString filterPluginName = filterOptionsElement.attribute("pluginName");
361                                 FilterPlugin *filterPlugin = qobject_cast<FilterPlugin*>(pPluginLoader->backendPluginByName( filterPluginName ));
362                                 if( filterPlugin )
363                                 {
364                                     filterOptions = filterPlugin->filterOptionsFromXml( filterOptionsElement );
365                                 }
366                                 else
367                                 {
368                                     logger->log( 1000, "\tcannot load filter for profile: " + profileName );
369                                     continue;
370                                 }
371                                 conversionOptions->filterOptions.append( filterOptions );
372                             }
373 
374                             pConversionOptionsManager->addConversionOptions( conversionOptions );
375 
376                             data.profiles[profileName] = conversionOptions->copy();
377                             if( profileName != "soundkonverter_last_used" )
378                                 logger->log( 1000, "\tname: " + profileName + ", plugin: " + pluginName );
379                         }
380                     }
381                     else
382                     {
383                         logger->log( 1000, "\tname: " + profileName + ", plugin: " + pluginName );
384                         logger->log( 1000, "\t\tcannot be loaded beacause the plugin cannot be found" );
385                         continue;
386                     }
387                 }
388             }
389         }
390         listFile.close();
391     }
392     logger->log( 1000, "... all profiles loaded\n" );
393 
394     QString profile;
395     QStringList sFormat;
396     QStringList sProfile;
397     sProfile += i18n("Last used");
398     sProfile += i18n("Very low");
399     sProfile += i18n("Low");
400     sProfile += i18n("Medium");
401     sProfile += i18n("High");
402     sProfile += i18n("Very high");
403     sProfile += i18n("Lossless");
404     sProfile += i18n("Hybrid");
405     sProfile += customProfiles();
406     if( sProfile.indexOf(data.general.defaultProfile) == -1 )
407     {
408         data.general.defaultProfile = i18n("High");
409     }
410     else
411     {
412         profile = data.general.defaultProfile;
413 
414         if( profile == i18n("Very low") || profile == i18n("Low") || profile == i18n("Medium") || profile == i18n("High") || profile == i18n("Very high") )
415         {
416             sFormat = pPluginLoader->formatList(PluginLoader::Encode,PluginLoader::Lossy);
417         }
418         else if( profile == i18n("Lossless") )
419         {
420             sFormat = pPluginLoader->formatList(PluginLoader::Encode,PluginLoader::Lossless);
421         }
422         else if( profile == i18n("Hybrid") )
423         {
424             sFormat = pPluginLoader->formatList(PluginLoader::Encode,PluginLoader::Hybrid);
425         }
426         else if( profile == i18n("User defined") )
427         {
428             sFormat = pPluginLoader->formatList(PluginLoader::Encode,PluginLoader::CompressionType(PluginLoader::InferiorQuality|PluginLoader::Lossy|PluginLoader::Lossless|PluginLoader::Hybrid));
429         }
430         else
431         {
432             const ConversionOptions *conversionOptions = data.profiles.value( profile );
433             if( conversionOptions )
434                 sFormat += conversionOptions->codecName;
435         }
436         if( sFormat.indexOf(data.general.defaultFormat) == -1 )
437         {
438             data.general.defaultFormat = i18n("Last used");
439         }
440     }
441 
442     group = conf->group( "BackendOptimizationsIgnoreList" );
443     const int backendOptimizationsIgnoreListCount = group.readEntry( "count", 0 );
444 
445     CodecOptimizations::Optimization optimization;
446     for( int i=0; i<backendOptimizationsIgnoreListCount; i++ )
447     {
448         const QStringList backendOptimization = group.readEntry( QString("ignore_%1").arg(i), QStringList() );
449         optimization.codecName = backendOptimization.at(0);
450         const QString mode = backendOptimization.at(1);
451         if( mode == "Encode" )
452         {
453             optimization.mode = CodecOptimizations::Optimization::Encode;
454         }
455         else if( mode == "Decode" )
456         {
457             optimization.mode = CodecOptimizations::Optimization::Decode;
458         }
459         else if( mode == "ReplayGain" )
460         {
461             optimization.mode = CodecOptimizations::Optimization::ReplayGain;
462         }
463         optimization.currentBackend = backendOptimization.at(2);
464         optimization.betterBackend = backendOptimization.at(3);
465         optimization.solution = CodecOptimizations::Optimization::Ignore;
466         data.backendOptimizationsIgnoreList.optimizationList.append(optimization);
467     }
468 
469     writeServiceMenu();
470 
471     logger->log( 1000, QString("loading of the configuration took %1 ms").arg(time.elapsed()) );
472 }
473 
save()474 void Config::save()
475 {
476     writeServiceMenu();
477 
478     KSharedConfig::Ptr conf = KGlobal::config();
479     KConfigGroup group;
480 
481     group = conf->group( "General" );
482     group.writeEntry( "configVersion", SOUNDKONVERTER_VERSION_NUMBER );
483     group.writeEntry( "startTab", data.general.startTab );
484     group.writeEntry( "lastTab", data.general.lastTab );
485     group.writeEntry( "defaultProfile", data.general.defaultProfile );
486     group.writeEntry( "lastProfile", data.general.lastProfile );
487     group.writeEntry( "defaultFormat", data.general.defaultFormat );
488     group.writeEntry( "lastFormat", data.general.lastFormat );
489     group.writeEntry( "lastOutputDirectoryMode", data.general.lastOutputDirectoryMode );
490     group.writeEntry( "specifyOutputDirectory", data.general.specifyOutputDirectory );
491     group.writeEntry( "metaDataOutputDirectory", data.general.metaDataOutputDirectory );
492     group.writeEntry( "copyStructureOutputDirectory", data.general.copyStructureOutputDirectory );
493     group.writeEntry( "lastMetaDataOutputDirectoryPaths", data.general.lastMetaDataOutputDirectoryPaths );
494     group.writeEntry( "lastNormalOutputDirectoryPaths", data.general.lastNormalOutputDirectoryPaths );
495     group.writeEntry( "waitForAlbumGain", data.general.waitForAlbumGain );
496     group.writeEntry( "useVFATNames", data.general.useVFATNames );
497     group.writeEntry( "copyIfSameCodec", data.general.copyIfSameCodec );
498     group.writeEntry( "writeLogFiles", data.general.writeLogFiles );
499     group.writeEntry( "conflictHandling", (int)data.general.conflictHandling );
500 //     group.writeEntry( "priority", data.general.priority );
501     group.writeEntry( "numFiles", data.general.numFiles );
502     group.writeEntry( "numReplayGainFiles", data.general.numReplayGainFiles );
503 //     group.writeEntry( "executeUserScript", data.general.executeUserScript );
504 //     group.writeEntry( "showToolBar", data.general.showToolBar );
505 //     group.writeEntry( "outputFilePermissions", data.general.outputFilePermissions );
506     group.writeEntry( "actionMenuConvertMimeTypes", data.general.actionMenuConvertMimeTypes );
507     group.writeEntry( "actionMenuReplayGainMimeTypes", data.general.actionMenuReplayGainMimeTypes );
508     group.writeEntry( "replayGainGrouping", (int)data.general.replayGainGrouping );
509     group.writeEntry( "preferredOggVorbisExtension", data.general.preferredOggVorbisExtension );
510     group.writeEntry( "preferredVorbisCommentCommentTag", data.general.preferredVorbisCommentCommentTag );
511     group.writeEntry( "preferredVorbisCommentTrackTotalTag", data.general.preferredVorbisCommentTrackTotalTag );
512     group.writeEntry( "preferredVorbisCommentDiscTotalTag", data.general.preferredVorbisCommentDiscTotalTag );
513 
514     group = conf->group( "Advanced" );
515     group.writeEntry( "useSharedMemoryForTempFiles", data.advanced.useSharedMemoryForTempFiles );
516     group.writeEntry( "maxSizeForSharedMemoryTempFiles", data.advanced.maxSizeForSharedMemoryTempFiles );
517     group.writeEntry( "usePipes", data.advanced.usePipes );
518     group.writeEntry( "ejectCdAfterRip", data.advanced.ejectCdAfterRip );
519 
520     group = conf->group( "CoverArt" );
521     group.writeEntry( "writeCovers", data.coverArt.writeCovers );
522     group.writeEntry( "writeCoverName", data.coverArt.writeCoverName );
523     group.writeEntry( "writeCoverDefaultName", data.coverArt.writeCoverDefaultName );
524 
525     group = conf->group( "Backends" );
526     group.deleteEntry( "rippers" );
527     QStringList formats;
528     foreach( const CodecData& codec, data.backends.codecs )
529     {
530         const QString format = codec.codecName;
531         group.writeEntry( format + "_encoders", codec.encoders );
532         group.writeEntry( format + "_decoders", codec.decoders );
533         group.writeEntry( format + "_replaygain", codec.replaygain );
534         formats += format;
535     }
536     group.writeEntry( "formats", formats );
537     group.writeEntry( "filters", data.backends.filters );
538     group.writeEntry( "enabledFilters", data.backends.enabledFilters );
539 
540     group = conf->group( "BackendOptimizationsIgnoreList" );
541     group.writeEntry( "count", data.backendOptimizationsIgnoreList.optimizationList.count() );
542 
543     for( int i=0; i<data.backendOptimizationsIgnoreList.optimizationList.count(); i++ )
544     {
545         QStringList backendOptimization;
546         backendOptimization << data.backendOptimizationsIgnoreList.optimizationList.at(i).codecName;
547         if( data.backendOptimizationsIgnoreList.optimizationList.at(i).mode == CodecOptimizations::Optimization::Encode )
548         {
549             backendOptimization << "Encode";
550         }
551         else if( data.backendOptimizationsIgnoreList.optimizationList.at(i).mode == CodecOptimizations::Optimization::Decode )
552         {
553             backendOptimization << "Decode";
554         }
555         else if( data.backendOptimizationsIgnoreList.optimizationList.at(i).mode == CodecOptimizations::Optimization::ReplayGain )
556         {
557             backendOptimization << "ReplayGain";
558         }
559         backendOptimization << data.backendOptimizationsIgnoreList.optimizationList.at(i).currentBackend;
560         backendOptimization << data.backendOptimizationsIgnoreList.optimizationList.at(i).betterBackend;
561         group.writeEntry( QString("ignore_%1").arg(i), backendOptimization );
562     }
563 
564     emit updateWriteLogFilesSetting( data.general.writeLogFiles );
565 }
566 
writeServiceMenu()567 void Config::writeServiceMenu()
568 {
569     QString content;
570     QStringList mimeTypes;
571 
572     content = "";
573     content += "[Desktop Entry]\n";
574     content += "Type=Service\n";
575     content += "Encoding=UTF-8\n";
576 
577     const QStringList convertFormats = pPluginLoader->formatList( PluginLoader::Decode, PluginLoader::CompressionType(PluginLoader::InferiorQuality|PluginLoader::Lossy|PluginLoader::Lossless|PluginLoader::Hybrid) );
578 
579     mimeTypes.clear();
580     foreach( const QString& format, convertFormats )
581     {
582         mimeTypes += pPluginLoader->codecMimeTypes( format );
583     }
584 
585     content += "ServiceTypes=KonqPopupMenu/Plugin," + mimeTypes.join(",") + "\n";
586 
587     content += "Icon=soundkonverter\n";
588     content += "Actions=ConvertWithSoundkonverter;\n\n";
589 
590     content += "[Desktop Action ConvertWithSoundkonverter]\n";
591     content += "Name="+i18n("Convert with soundKonverter")+"\n";
592     content += "Icon=soundkonverter\n";
593     content += "Exec=soundkonverter %F\n";
594 
595     const QString convertActionFileName = KStandardDirs::locateLocal( "services", "ServiceMenus/convert_with_soundkonverter.desktop" );
596     if( ( data.general.actionMenuConvertMimeTypes != mimeTypes || !QFile::exists(convertActionFileName) ) && mimeTypes.count() > 0 )
597     {
598         QFile convertActionFile( convertActionFileName );
599         if( convertActionFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
600         {
601             QTextStream convertActionStream( &convertActionFile );
602             convertActionStream << content;
603             convertActionFile.close();
604         }
605         data.general.actionMenuConvertMimeTypes = mimeTypes;
606     }
607 
608     content = "";
609     content += "[Desktop Entry]\n";
610     content += "Type=Service\n";
611     content += "Encoding=UTF-8\n";
612 
613     const QStringList replaygainFormats = pPluginLoader->formatList( PluginLoader::ReplayGain, PluginLoader::CompressionType(PluginLoader::InferiorQuality|PluginLoader::Lossy|PluginLoader::Lossless|PluginLoader::Hybrid) );
614 
615     mimeTypes.clear();
616     foreach( const QString& format, replaygainFormats )
617     {
618         mimeTypes += pPluginLoader->codecMimeTypes( format );
619     }
620 
621     content += "ServiceTypes=KonqPopupMenu/Plugin," + mimeTypes.join(",") + "\n";
622 
623     content += "Icon=soundkonverter_replaygain\n";
624     content += "Actions=AddReplayGainWithSoundkonverter;\n\n";
625 
626     content += "[Desktop Action AddReplayGainWithSoundkonverter]\n";
627     content += "Name="+i18n("Add Replay Gain with soundKonverter")+"\n";
628     content += "Icon=soundkonverter-replaygain\n";
629     content += "Exec=soundkonverter --replaygain %F\n";
630 
631     const QString replaygainActionFileName = KStandardDirs::locateLocal( "services", "ServiceMenus/add_replaygain_with_soundkonverter.desktop" );
632     if( ( data.general.actionMenuReplayGainMimeTypes != mimeTypes || !QFile::exists(replaygainActionFileName) ) && mimeTypes.count() > 0 )
633     {
634         QFile replaygainActionFile( replaygainActionFileName );
635         if( replaygainActionFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
636         {
637             QTextStream replaygainActionStream( &replaygainActionFile );
638             replaygainActionStream << content;
639             replaygainActionFile.close();
640         }
641         data.general.actionMenuReplayGainMimeTypes = mimeTypes;
642     }
643 }
644 
customProfiles()645 QStringList Config::customProfiles()
646 {
647     QStringList profiles;
648 
649     foreach( const QString& profileName, data.profiles.keys() )
650     {
651         if( profileName == "soundkonverter_last_used" )
652             continue;
653 
654         const ConversionOptions* profileConversionOptions = data.profiles.value(profileName);
655 
656         QList<CodecPlugin*> plugins = pPluginLoader->encodersForCodec( profileConversionOptions->codecName );
657         foreach( const CodecPlugin *plugin, plugins )
658         {
659             if( plugin->name() == profileConversionOptions->pluginName )
660             {
661                 profiles.append( profileName );
662                 break;
663             }
664         }
665     }
666 
667     return profiles;
668 }
669 
getOptimizations(bool includeIgnored)670 QList<CodecOptimizations::Optimization> Config::getOptimizations( bool includeIgnored )
671 {
672     QTime time;
673     time.start();
674 
675     QList<CodecOptimizations::Optimization> optimizationList;
676     CodecOptimizations::Optimization optimization;
677 
678     QStringList tempPluginList;
679     QStringList optimizedPluginList;
680     int currentBackendRating = 0;
681     int betterBackendRating = 0;
682     int codecIndex;
683     bool ignore;
684 
685     const QStringList formats = pPluginLoader->formatList( PluginLoader::Possibilities(PluginLoader::Encode|PluginLoader::Decode|PluginLoader::ReplayGain), PluginLoader::CompressionType(PluginLoader::InferiorQuality|PluginLoader::Lossy|PluginLoader::Lossless|PluginLoader::Hybrid) );
686     foreach( const QString& format, formats )
687     {
688         if( format == "wav" )
689             continue;
690 
691         codecIndex = -1;
692         for( int j=0; j<data.backends.codecs.count(); j++ )
693         {
694             if( data.backends.codecs.at(j).codecName == format )
695             {
696                 codecIndex = j;
697                 break;
698             }
699         }
700         if( codecIndex == -1 )
701             continue;
702 
703         // encoders
704         tempPluginList.clear();
705         for( int j=0; j<pPluginLoader->conversionPipeTrunks.count(); j++ )
706         {
707             if( pPluginLoader->conversionPipeTrunks.at(j).codecTo == format && pPluginLoader->conversionPipeTrunks.at(j).enabled && pPluginLoader->conversionPipeTrunks.at(j).plugin->type() == "codec" )
708             {
709                 const QString pluginName = pPluginLoader->conversionPipeTrunks.at(j).plugin->name();
710                 if( tempPluginList.filter(QRegExp("[0-9]{8,8}"+pluginName)).count() == 0 )
711                 {
712                     tempPluginList += QString::number(pPluginLoader->conversionPipeTrunks.at(j).rating).rightJustified(8,'0') + pluginName;
713                 }
714             }
715         }
716         for( int j=0; j<pPluginLoader->filterPipeTrunks.count(); j++ )
717         {
718             if( pPluginLoader->filterPipeTrunks.at(j).codecTo == format && pPluginLoader->filterPipeTrunks.at(j).enabled && pPluginLoader->filterPipeTrunks.at(j).plugin->type() == "filter" )
719             {
720                 const QString pluginName = pPluginLoader->filterPipeTrunks.at(j).plugin->name();
721                 if( tempPluginList.filter(QRegExp("[0-9]{8,8}"+pluginName)).count() == 0 )
722                 {
723                     tempPluginList += QString::number(pPluginLoader->filterPipeTrunks.at(j).rating).rightJustified(8,'0') + pluginName;
724                 }
725             }
726         }
727         tempPluginList.sort();
728         optimizedPluginList.clear();
729         for( int j=tempPluginList.count()-1; j>=0; j-- )
730         {
731             const QString pluginName = tempPluginList.at(j).right(tempPluginList.at(j).length()-8);
732             const int pluginRating = tempPluginList.at(j).left(8).toInt();
733             optimizedPluginList += pluginName;
734             if( data.backends.codecs.at(codecIndex).encoders.count() > 0 && pluginName == data.backends.codecs.at(codecIndex).encoders.first() )
735             {
736                 currentBackendRating = pluginRating;
737             }
738             if( j == tempPluginList.count()-1 )
739             {
740                 betterBackendRating = pluginRating;
741             }
742         }
743         if( optimizedPluginList.count() != 0 && data.backends.codecs.at(codecIndex).encoders.count() != 0 )
744         {
745             ignore = false;
746             for( int j=0; j<data.backendOptimizationsIgnoreList.optimizationList.count(); j++ )
747             {
748                 if( data.backendOptimizationsIgnoreList.optimizationList.at(j).codecName == format &&
749                     data.backendOptimizationsIgnoreList.optimizationList.at(j).mode == CodecOptimizations::Optimization::Encode &&
750                     data.backendOptimizationsIgnoreList.optimizationList.at(j).currentBackend == data.backends.codecs.at(codecIndex).encoders.first() &&
751                     data.backendOptimizationsIgnoreList.optimizationList.at(j).betterBackend == optimizedPluginList.first()
752                 )
753                 {
754                     ignore = true;
755                 }
756             }
757 
758             // is there a better plugin available and has the better plugin really a higher rating or was it just sorted alphabetically at the top
759             if( optimizedPluginList.first() != data.backends.codecs.at(codecIndex).encoders.first() && betterBackendRating > currentBackendRating )
760             {
761                 if( ignore && includeIgnored )
762                 {
763                     optimization.codecName = format;
764                     optimization.mode = CodecOptimizations::Optimization::Encode;
765                     optimization.currentBackend = data.backends.codecs.at(codecIndex).encoders.first();
766                     optimization.betterBackend = optimizedPluginList.first();
767                     optimization.solution = CodecOptimizations::Optimization::Ignore;
768                     optimizationList.append(optimization);
769                 }
770                 else if( !ignore )
771                 {
772                     optimization.codecName = format;
773                     optimization.mode = CodecOptimizations::Optimization::Encode;
774                     optimization.currentBackend = data.backends.codecs.at(codecIndex).encoders.first();
775                     optimization.betterBackend = optimizedPluginList.first();
776                     optimization.solution = CodecOptimizations::Optimization::Undecided;
777                     optimizationList.append(optimization);
778                 }
779             }
780         }
781 
782         // decoders
783         tempPluginList.clear();
784         for( int j=0; j<pPluginLoader->conversionPipeTrunks.count(); j++ )
785         {
786             if( pPluginLoader->conversionPipeTrunks.at(j).codecFrom == format && pPluginLoader->conversionPipeTrunks.at(j).enabled && pPluginLoader->conversionPipeTrunks.at(j).plugin->type() == "codec" )
787             {
788                 const QString pluginName = pPluginLoader->conversionPipeTrunks.at(j).plugin->name();
789                 if( tempPluginList.filter(QRegExp("[0-9]{8,8}"+pluginName)).count() == 0 )
790                 {
791                     tempPluginList += QString::number(pPluginLoader->conversionPipeTrunks.at(j).rating).rightJustified(8,'0') + pluginName;
792                 }
793             }
794         }
795         for( int j=0; j<pPluginLoader->filterPipeTrunks.count(); j++ )
796         {
797             if( pPluginLoader->filterPipeTrunks.at(j).codecFrom == format && pPluginLoader->filterPipeTrunks.at(j).enabled && pPluginLoader->filterPipeTrunks.at(j).plugin->type() == "filter" )
798             {
799                 const QString pluginName = pPluginLoader->filterPipeTrunks.at(j).plugin->name();
800                 if( tempPluginList.filter(QRegExp("[0-9]{8,8}"+pluginName)).count() == 0 )
801                 {
802                     tempPluginList += QString::number(pPluginLoader->filterPipeTrunks.at(j).rating).rightJustified(8,'0') + pluginName;
803                 }
804             }
805         }
806         tempPluginList.sort();
807         optimizedPluginList.clear();
808         for( int j=tempPluginList.count()-1; j>=0; j-- )
809         {
810             const QString pluginName = tempPluginList.at(j).right(tempPluginList.at(j).length()-8);
811             const int pluginRating = tempPluginList.at(j).left(8).toInt();
812             optimizedPluginList += pluginName;
813             if( data.backends.codecs.at(codecIndex).decoders.count() > 0 && pluginName == data.backends.codecs.at(codecIndex).decoders.first() )
814             {
815                 currentBackendRating = pluginRating;
816             }
817             if( j == tempPluginList.count()-1 )
818             {
819                 betterBackendRating = pluginRating;
820             }
821         }
822         if( optimizedPluginList.count() != 0 && data.backends.codecs.at(codecIndex).decoders.count() != 0 )
823         {
824             ignore = false;
825             for( int j=0; j<data.backendOptimizationsIgnoreList.optimizationList.count(); j++ )
826             {
827                 if( data.backendOptimizationsIgnoreList.optimizationList.at(j).codecName == format &&
828                     data.backendOptimizationsIgnoreList.optimizationList.at(j).mode == CodecOptimizations::Optimization::Decode &&
829                     data.backendOptimizationsIgnoreList.optimizationList.at(j).currentBackend == data.backends.codecs.at(codecIndex).decoders.first() &&
830                     data.backendOptimizationsIgnoreList.optimizationList.at(j).betterBackend == optimizedPluginList.first()
831                 )
832                 {
833                     ignore = true;
834                 }
835             }
836 
837             // is there a better plugin available and has the better plugin really a higher rating or was it just sorted alphabetically at the top
838             if( optimizedPluginList.first() != data.backends.codecs.at(codecIndex).decoders.first() && betterBackendRating > currentBackendRating )
839             {
840                 if( ignore && includeIgnored )
841                 {
842                     optimization.codecName = format;
843                     optimization.mode = CodecOptimizations::Optimization::Decode;
844                     optimization.currentBackend = data.backends.codecs.at(codecIndex).decoders.first();
845                     optimization.betterBackend = optimizedPluginList.first();
846                     optimization.solution = CodecOptimizations::Optimization::Ignore;
847                     optimizationList.append(optimization);
848                 }
849                 else if( !ignore )
850                 {
851                     optimization.codecName = format;
852                     optimization.mode = CodecOptimizations::Optimization::Decode;
853                     optimization.currentBackend = data.backends.codecs.at(codecIndex).decoders.first();
854                     optimization.betterBackend = optimizedPluginList.first();
855                     optimization.solution = CodecOptimizations::Optimization::Undecided;
856                     optimizationList.append(optimization);
857                 }
858             }
859         }
860 
861         // replaygain
862         tempPluginList.clear();
863         for( int j=0; j<pPluginLoader->replaygainPipes.count(); j++ )
864         {
865             if( pPluginLoader->replaygainPipes.at(j).codecName == format && pPluginLoader->replaygainPipes.at(j).enabled )
866             {
867                 const QString pluginName = pPluginLoader->replaygainPipes.at(j).plugin->name();
868                 if( tempPluginList.filter(QRegExp("[0-9]{8,8}"+pluginName)).count() == 0 )
869                 {
870                     tempPluginList += QString::number(pPluginLoader->replaygainPipes.at(j).rating).rightJustified(8,'0') + pluginName;
871                 }
872             }
873         }
874         tempPluginList.sort();
875         optimizedPluginList.clear();
876         for( int j=tempPluginList.count()-1; j>=0; j-- )
877         {
878             const QString pluginName = tempPluginList.at(j).right(tempPluginList.at(j).length()-8);
879             const int pluginRating = tempPluginList.at(j).left(8).toInt();
880             optimizedPluginList += pluginName;
881             if( data.backends.codecs.at(codecIndex).replaygain.count() > 0 && pluginName == data.backends.codecs.at(codecIndex).replaygain.first() )
882             {
883                 currentBackendRating = pluginRating;
884             }
885             if( j == tempPluginList.count()-1 )
886             {
887                 betterBackendRating = pluginRating;
888             }
889         }
890         if( optimizedPluginList.count() != 0 && data.backends.codecs.at(codecIndex).replaygain.count() != 0 )
891         {
892             ignore = false;
893             for( int j=0; j<data.backendOptimizationsIgnoreList.optimizationList.count(); j++ )
894             {
895                 if( data.backendOptimizationsIgnoreList.optimizationList.at(j).codecName == format &&
896                     data.backendOptimizationsIgnoreList.optimizationList.at(j).mode == CodecOptimizations::Optimization::ReplayGain &&
897                     data.backendOptimizationsIgnoreList.optimizationList.at(j).currentBackend == data.backends.codecs.at(codecIndex).replaygain.first() &&
898                     data.backendOptimizationsIgnoreList.optimizationList.at(j).betterBackend == optimizedPluginList.first()
899                 )
900                 {
901                     ignore = true;
902                     break;
903                 }
904             }
905 
906             // is there a better plugin available and has the better plugin really a higher rating or was it just sorted alphabetically at the top
907             if( optimizedPluginList.first() != data.backends.codecs.at(codecIndex).replaygain.first() && betterBackendRating > currentBackendRating )
908             {
909                 if( ignore && includeIgnored )
910                 {
911                     optimization.codecName = format;
912                     optimization.mode = CodecOptimizations::Optimization::ReplayGain;
913                     optimization.currentBackend = data.backends.codecs.at(codecIndex).replaygain.first();
914                     optimization.betterBackend = optimizedPluginList.first();
915                     optimization.solution = CodecOptimizations::Optimization::Ignore;
916                     optimizationList.append(optimization);
917                 }
918                 else if( !ignore )
919                 {
920                     optimization.codecName = format;
921                     optimization.mode = CodecOptimizations::Optimization::ReplayGain;
922                     optimization.currentBackend = data.backends.codecs.at(codecIndex).replaygain.first();
923                     optimization.betterBackend = optimizedPluginList.first();
924                     optimization.solution = CodecOptimizations::Optimization::Undecided;
925                     optimizationList.append(optimization);
926                 }
927             }
928         }
929     }
930 
931     logger->log( 1000, QString("generation of the optimization list took %1 ms").arg(time.elapsed()) );
932 
933     return optimizationList;
934 }
935 
doOptimizations(const QList<CodecOptimizations::Optimization> & optimizationList)936 void Config::doOptimizations( const QList<CodecOptimizations::Optimization>& optimizationList )
937 {
938     int codecIndex;
939 
940     for( int i=0; i<optimizationList.count(); i++ )
941     {
942         if( optimizationList.at(i).solution == CodecOptimizations::Optimization::Fix )
943         {
944             codecIndex = -1;
945             for( int j=0; j<data.backends.codecs.count(); j++ )
946             {
947                 if( data.backends.codecs.at(j).codecName == optimizationList.at(i).codecName )
948                 {
949                     codecIndex = j;
950                     break;
951                 }
952             }
953             if( codecIndex == -1 )
954                 continue;
955 
956             for( int j=0; j<data.backendOptimizationsIgnoreList.optimizationList.count(); j++ )
957             {
958                 if( data.backendOptimizationsIgnoreList.optimizationList.at(j).codecName == optimizationList.at(i).codecName &&
959                     data.backendOptimizationsIgnoreList.optimizationList.at(j).mode == optimizationList.at(i).mode &&
960                     data.backendOptimizationsIgnoreList.optimizationList.at(j).currentBackend == optimizationList.at(i).currentBackend &&
961                     data.backendOptimizationsIgnoreList.optimizationList.at(j).betterBackend == optimizationList.at(i).betterBackend
962                 )
963                 {
964                     data.backendOptimizationsIgnoreList.optimizationList.removeAt( j );
965                     break;
966                 }
967             }
968 
969             if( optimizationList.at(i).mode == CodecOptimizations::Optimization::Encode )
970             {
971                 data.backends.codecs[codecIndex].encoders.removeAll( optimizationList.at(i).betterBackend );
972                 data.backends.codecs[codecIndex].encoders.prepend( optimizationList.at(i).betterBackend );
973             }
974             else if( optimizationList.at(i).mode == CodecOptimizations::Optimization::Decode )
975             {
976                 data.backends.codecs[codecIndex].decoders.removeAll( optimizationList.at(i).betterBackend );
977                 data.backends.codecs[codecIndex].decoders.prepend( optimizationList.at(i).betterBackend );
978             }
979             else if( optimizationList.at(i).mode == CodecOptimizations::Optimization::ReplayGain )
980             {
981                 data.backends.codecs[codecIndex].replaygain.removeAll( optimizationList.at(i).betterBackend );
982                 data.backends.codecs[codecIndex].replaygain.prepend( optimizationList.at(i).betterBackend );
983             }
984         }
985         else if( optimizationList.at(i).solution == CodecOptimizations::Optimization::Ignore )
986         {
987             bool found = false;
988 
989             for( int j=0; j<data.backendOptimizationsIgnoreList.optimizationList.count(); j++ )
990             {
991                 if( data.backendOptimizationsIgnoreList.optimizationList.at(j).codecName == optimizationList.at(i).codecName &&
992                     data.backendOptimizationsIgnoreList.optimizationList.at(j).mode == optimizationList.at(i).mode &&
993                     data.backendOptimizationsIgnoreList.optimizationList.at(j).currentBackend == optimizationList.at(i).currentBackend &&
994                     data.backendOptimizationsIgnoreList.optimizationList.at(j).betterBackend == optimizationList.at(i).betterBackend
995                 )
996                 {
997                     found = true;
998                     break;
999                 }
1000             }
1001 
1002             if( !found )
1003                 data.backendOptimizationsIgnoreList.optimizationList.append(optimizationList.at(i));
1004         }
1005     }
1006 }
1007 
1008