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