1 
2 #include "cdparanoiaripperglobal.h"
3 
4 #include "soundkonverter_ripper_cdparanoia.h"
5 
6 #include <QWidget>
7 #include <QLayout>
8 #include <QHBoxLayout>
9 #include <QLabel>
10 #include <QComboBox>
11 #include <QCheckBox>
12 #include <QSpinBox>
13 #include <KLocale>
14 #include <KDialog>
15 
16 
17 soundkonverter_ripper_cdparanoia::soundkonverter_ripper_cdparanoia( QObject *parent, const QVariantList& args  )
18     : RipperPlugin( parent )
19 {
20     Q_UNUSED(args)
21 
22     configDialogForceReadSpeedCheckBox = 0;
23     configDialogForceReadSpeedSpinBox = 0;
24     configDialogForceEndiannessComboBox = 0;
25     configDialogMaximumRetriesSpinBox = 0;
26     configDialogEnableParanoiaCheckBox = 0;
27     configDialogEnableExtraParanoiaCheckBox = 0;
28 
29     binaries["cdparanoia"] = "";
30 
31     KSharedConfig::Ptr conf = KGlobal::config();
32     KConfigGroup group;
33 
34     group = conf->group( "Plugin-"+name() );
35     forceReadSpeed = group.readEntry( "forceReadSpeed", 0 );
36     forceEndianness = group.readEntry( "forceEndianness", 0 );
37     maximumRetries = group.readEntry( "maximumRetries", 20 );
38     enableParanoia = group.readEntry( "enableParanoia", true );
39     enableExtraParanoia = group.readEntry( "enableExtraParanoia", true );
40 }
41 
42 soundkonverter_ripper_cdparanoia::~soundkonverter_ripper_cdparanoia()
43 {}
44 
45 QString soundkonverter_ripper_cdparanoia::name() const
46 {
47     return global_plugin_name;
48 }
49 
50 QList<ConversionPipeTrunk> soundkonverter_ripper_cdparanoia::codecTable()
51 {
52     QList<ConversionPipeTrunk> table;
53     ConversionPipeTrunk newTrunk;
54 
55     newTrunk.codecFrom = "audio cd";
56     newTrunk.codecTo = "wav";
57     newTrunk.rating = 100;
58     newTrunk.enabled = ( binaries["cdparanoia"] != "" );
59     newTrunk.data.canRipEntireCd = true;
60     newTrunk.problemInfo = i18n( "In order to rip audio cds per track or to a single file, you need to install 'cdparanoia'.\n'cdparanoia' is usually shipped with your distribution, the package name can vary." );
61     table.append( newTrunk );
62 
63     return table;
64 }
65 
66 bool soundkonverter_ripper_cdparanoia::isConfigSupported( ActionType action, const QString& codecName )
67 {
68     Q_UNUSED(action)
69     Q_UNUSED(codecName)
70 
71     return true;
72 }
73 
74 void soundkonverter_ripper_cdparanoia::showConfigDialog( ActionType action, const QString& codecName, QWidget *parent )
75 {
76     Q_UNUSED(action)
77     Q_UNUSED(codecName)
78 
79     if( !configDialog.data() )
80     {
81         configDialog = new KDialog( parent );
82         configDialog.data()->setCaption( i18n("Configure %1").arg(global_plugin_name)  );
83         configDialog.data()->setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Default );
84 
85         QWidget *configDialogWidget = new QWidget( configDialog.data() );
86         QVBoxLayout *configDialogBox = new QVBoxLayout( configDialogWidget );
87 
88         QHBoxLayout *configDialogBox0 = new QHBoxLayout();
89         configDialogForceReadSpeedCheckBox = new QCheckBox( i18n("Force read speed:"), configDialogWidget );
90         configDialogBox0->addWidget( configDialogForceReadSpeedCheckBox );
91         configDialogForceReadSpeedSpinBox = new QSpinBox( configDialogWidget );
92         configDialogForceReadSpeedSpinBox->setRange(1, 64);
93         configDialogForceReadSpeedSpinBox->setSuffix(" x");
94         configDialogBox0->addWidget( configDialogForceReadSpeedSpinBox );
95         configDialogBox->addLayout( configDialogBox0 );
96         connect( configDialogForceReadSpeedCheckBox, SIGNAL( stateChanged(int) ), this, SLOT( configDialogForceReadSpeedChanged(int) ) );
97 
98         QHBoxLayout *configDialogBox1 = new QHBoxLayout();
99         QLabel *configDialogForceEndiannessLabel = new QLabel( i18nc("Byte-Order", "Endianness:"), configDialogWidget );
100         configDialogBox1->addWidget( configDialogForceEndiannessLabel );
101         configDialogForceEndiannessComboBox = new QComboBox( configDialogWidget );
102         configDialogForceEndiannessComboBox->addItem( "Auto" );
103         configDialogForceEndiannessComboBox->addItem( "Little endian" );
104         configDialogForceEndiannessComboBox->addItem( "Big endian" );
105         configDialogBox1->addWidget( configDialogForceEndiannessComboBox );
106         configDialogBox->addLayout( configDialogBox1 );
107 
108         QHBoxLayout *configDialogBox2 = new QHBoxLayout();
109         QLabel *configDialogMaximumRetriesLabel = new QLabel( i18n("Maximum read retries:"), configDialogWidget );
110         configDialogBox2->addWidget( configDialogMaximumRetriesLabel );
111         configDialogMaximumRetriesSpinBox = new QSpinBox( configDialogWidget );
112         configDialogMaximumRetriesSpinBox->setRange(0, 100);
113         configDialogBox2->addWidget( configDialogMaximumRetriesSpinBox );
114         configDialogBox->addLayout( configDialogBox2 );
115 
116         QHBoxLayout *configDialogBox3 = new QHBoxLayout( configDialogWidget );
117         configDialogEnableParanoiaCheckBox = new QCheckBox( i18n("Enable paranoia"), configDialogWidget );
118         configDialogBox3->addWidget( configDialogEnableParanoiaCheckBox );
119         configDialogBox->addLayout( configDialogBox3 );
120 
121         QHBoxLayout *configDialogBox4 = new QHBoxLayout( configDialogWidget );
122         configDialogEnableExtraParanoiaCheckBox = new QCheckBox( i18n("Enable extra paranoia"), configDialogWidget );
123         configDialogBox4->addWidget( configDialogEnableExtraParanoiaCheckBox );
124         configDialogBox->addLayout( configDialogBox4 );
125 
126         configDialog.data()->setMainWidget( configDialogWidget );
127         connect( configDialog.data(), SIGNAL( okClicked() ), this, SLOT( configDialogSave() ) );
128         connect( configDialog.data(), SIGNAL( defaultClicked() ), this, SLOT( configDialogDefault() ) );
129     }
130     configDialogForceReadSpeedCheckBox->setChecked( forceReadSpeed > 0 );
131     configDialogForceReadSpeedSpinBox->setValue( forceReadSpeed );
132     configDialogForceEndiannessComboBox->setCurrentIndex( forceEndianness );
133     configDialogMaximumRetriesSpinBox->setValue( maximumRetries );
134     configDialogEnableParanoiaCheckBox->setChecked( enableParanoia );
135     configDialogEnableExtraParanoiaCheckBox->setChecked( enableExtraParanoia );
136 
137     configDialogForceReadSpeedChanged( configDialogForceReadSpeedCheckBox->checkState() );
138 
139     configDialog.data()->show();
140 }
141 
142 void soundkonverter_ripper_cdparanoia::configDialogForceReadSpeedChanged( int state )
143 {
144     if( configDialog.data() )
145     {
146         configDialogForceReadSpeedSpinBox->setEnabled( state == Qt::Checked );
147     }
148 }
149 
150 void soundkonverter_ripper_cdparanoia::configDialogSave()
151 {
152     if( configDialog.data() )
153     {
154         forceReadSpeed = configDialogForceReadSpeedCheckBox->isChecked() ? configDialogForceReadSpeedSpinBox->value() : 0;
155         forceEndianness = configDialogForceEndiannessComboBox->currentIndex();
156         maximumRetries = configDialogMaximumRetriesSpinBox->value();
157         enableParanoia = configDialogEnableParanoiaCheckBox->isChecked();
158         enableExtraParanoia = configDialogEnableExtraParanoiaCheckBox->isChecked();
159 
160         KSharedConfig::Ptr conf = KGlobal::config();
161         KConfigGroup group;
162 
163         group = conf->group( "Plugin-"+name() );
164         group.writeEntry( "forceReadSpeed", forceReadSpeed );
165         group.writeEntry( "forceEndianness", forceEndianness );
166         group.writeEntry( "maximumRetries", maximumRetries );
167         group.writeEntry( "enableParanoia", enableParanoia );
168         group.writeEntry( "enableExtraParanoia", enableExtraParanoia );
169 
170         configDialog.data()->deleteLater();
171     }
172 }
173 
174 void soundkonverter_ripper_cdparanoia::configDialogDefault()
175 {
176     if( configDialog.data() )
177     {
178         configDialogForceReadSpeedCheckBox->setChecked( false );
179         configDialogForceReadSpeedSpinBox->setValue( 1 );
180         configDialogForceEndiannessComboBox->setCurrentIndex( 0 );
181         configDialogMaximumRetriesSpinBox->setValue( 20 );
182         configDialogEnableParanoiaCheckBox->setChecked( true );
183         configDialogEnableExtraParanoiaCheckBox->setChecked( true );
184     }
185 }
186 
187 bool soundkonverter_ripper_cdparanoia::hasInfo()
188 {
189     return false;
190 }
191 
192 void soundkonverter_ripper_cdparanoia::showInfo( QWidget *parent )
193 {
194     Q_UNUSED(parent)
195 }
196 
197 int soundkonverter_ripper_cdparanoia::rip( const QString& device, int track, int tracks, const KUrl& outputFile )
198 {
199     QStringList command;
200 
201     command += binaries["cdparanoia"];
202     command += "--stderr-progress";
203     command += "--force-cdrom-device";
204     command += device;
205     if( forceReadSpeed > 0 )
206     {
207         command += "--force-read-speed";
208         command += QString::number(forceReadSpeed);
209     }
210     if( forceEndianness == 1 )
211     {
212         command += "--force-cdrom-little-endian";
213     }
214     else if( forceEndianness == 2 )
215     {
216         command += "--force-cdrom-big-endian";
217     }
218     command += "--never-skip=" + QString::number(maximumRetries);
219     if( !enableExtraParanoia )
220     {
221         if( !enableParanoia )
222         {
223             command += "--disable-paranoia";
224         }
225         else
226         {
227             command += "--disable-extra-paranoia";
228         }
229     }
230     else if( !enableParanoia )
231     {
232         command += "--disable-paranoia";
233     }
234     if( track > 0 )
235     {
236         command += QString::number(track);
237     }
238     else
239     {
240         command += "1-" + QString::number(tracks);
241     }
242     command += "\"" + outputFile.toLocalFile() + "\"";
243 
244     RipperPluginItem *newItem = new RipperPluginItem( this );
245     newItem->id = lastId++;
246     newItem->process = new KProcess( newItem );
247     newItem->process->setOutputChannelMode( KProcess::MergedChannels );
248     connect( newItem->process, SIGNAL(readyRead()), this, SLOT(processOutput()) );
249     connect( newItem->process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processExit(int,QProcess::ExitStatus)) );
250 
251     newItem->process->clearProgram();
252     newItem->process->setShellCommand( command.join(" ") );
253     newItem->process->start();
254 
255     logCommand( newItem->id, command.join(" ") );
256 
257     backendItems.append( newItem );
258     return newItem->id;
259 }
260 
261 QStringList soundkonverter_ripper_cdparanoia::ripCommand( const QString& device, int track, int tracks, const KUrl& outputFile )
262 {
263     Q_UNUSED(device)
264     Q_UNUSED(track)
265     Q_UNUSED(tracks)
266     Q_UNUSED(outputFile)
267 
268     return QStringList();
269 }
270 
271 float soundkonverter_ripper_cdparanoia::parseOutput( const QString& output, int *fromSector, int *toSector )
272 {
273     // Ripping from sector       0 (track  1 [0:00.00])
274     //           to sector   16361 (track  1 [3:38.11])
275 
276     // ##: -2 [wrote] @ 19242887\n
277 
278     if( output.contains("sector") )
279     {
280         if( fromSector && output.contains("from sector") )
281         {
282             QString data = output;
283             data.remove( 0, data.indexOf("from sector") + 11 );
284             data = data.left( data.indexOf("(") );
285             data = data.simplified();
286             *fromSector = data.toInt();
287         }
288         if( toSector && output.contains("to sector") )
289         {
290             QString data = output;
291             data.remove( 0, data.indexOf("to sector") + 9 );
292             data = data.left( data.indexOf("(") );
293             data = data.simplified();
294             *toSector = data.toInt();
295         }
296         return -1;
297     }
298 
299     if( output == "" || !output.contains("@") ) return -1;
300     if( !output.contains("[wrote] @") ) return 0;
301 
302     QString data = output;
303     data.remove( 0, data.indexOf("[wrote] @") + 9 );
304     data = data.left( data.indexOf("\n") );
305     data = data.simplified();
306     return data.toFloat() / 1176;
307 }
308 
309 float soundkonverter_ripper_cdparanoia::parseOutput( const QString& output )
310 {
311     return parseOutput( output, 0, 0 );
312 }
313 
314 void soundkonverter_ripper_cdparanoia::processOutput()
315 {
316     for( int i=0; i<backendItems.size(); i++ )
317     {
318         if( backendItems.at(i)->process == QObject::sender() )
319         {
320             QString output = backendItems.at(i)->process->readAllStandardOutput().data();
321             RipperPluginItem *pluginItem = qobject_cast<RipperPluginItem*>(backendItems.at(i));
322 
323             float progress = parseOutput( output, &pluginItem->data.fromSector, &pluginItem->data.toSector );
324 
325             if( progress == -1 && !output.simplified().isEmpty() )
326                 logOutput( backendItems.at(i)->id, output );
327 
328             progress = (progress - (float)pluginItem->data.fromSector) * 100 / (float)(pluginItem->data.toSector - pluginItem->data.fromSector);
329 
330             if( progress > backendItems.at(i)->progress )
331                 backendItems.at(i)->progress = progress;
332 
333             return;
334         }
335     }
336 }
337 
338 K_PLUGIN_FACTORY(ripper_cdparanoia, registerPlugin<soundkonverter_ripper_cdparanoia>();)
339 
340 #include "soundkonverter_ripper_cdparanoia.moc"
341