From 50b48aec6ddd451a6d1709c0942477b503457663 Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 3 Feb 2010 02:15:56 +0000 Subject: Added abandoned KDE3 version of K3B git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/k3b@1084400 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- src/rip/Makefile.am | 32 + src/rip/base_k3baudiorippingoptionwidget.ui | 282 ++++++++ src/rip/base_k3bcddbpatternwidget.ui | 180 +++++ src/rip/k3baudiocdlistview.cpp | 66 ++ src/rip/k3baudiocdlistview.h | 45 ++ src/rip/k3baudiocdview.cpp | 631 ++++++++++++++++++ src/rip/k3baudiocdview.h | 107 +++ src/rip/k3baudioconvertingoptionwidget.cpp | 266 ++++++++ src/rip/k3baudioconvertingoptionwidget.h | 74 +++ src/rip/k3baudioprojectconvertingdialog.cpp | 371 +++++++++++ src/rip/k3baudioprojectconvertingdialog.h | 78 +++ src/rip/k3baudioprojectconvertingthread.cpp | 459 +++++++++++++ src/rip/k3baudioprojectconvertingthread.h | 101 +++ src/rip/k3baudioripjob.cpp | 77 +++ src/rip/k3baudioripjob.h | 71 ++ src/rip/k3baudiorippingdialog.cpp | 470 ++++++++++++++ src/rip/k3baudiorippingdialog.h | 92 +++ src/rip/k3baudioripthread.cpp | 602 +++++++++++++++++ src/rip/k3baudioripthread.h | 117 ++++ src/rip/k3bcddbpatternwidget.cpp | 175 +++++ src/rip/k3bcddbpatternwidget.h | 51 ++ src/rip/k3bcuefilewriter.cpp | 91 +++ src/rip/k3bcuefilewriter.h | 54 ++ src/rip/k3bpatternparser.cpp | 305 +++++++++ src/rip/k3bpatternparser.h | 52 ++ src/rip/k3bvideocdinfo.cpp | 247 +++++++ src/rip/k3bvideocdinfo.h | 107 +++ src/rip/k3bvideocdrip.cpp | 355 ++++++++++ src/rip/k3bvideocdrip.h | 74 +++ src/rip/k3bvideocdrippingdialog.cpp | 260 ++++++++ src/rip/k3bvideocdrippingdialog.h | 73 +++ src/rip/k3bvideocdrippingoptions.h | 74 +++ src/rip/k3bvideocdview.cpp | 509 +++++++++++++++ src/rip/k3bvideocdview.h | 105 +++ src/rip/videodvd/Makefile.am | 16 + src/rip/videodvd/base_k3bvideodvdrippingwidget.ui | 721 +++++++++++++++++++++ src/rip/videodvd/k3bvideodvdrippingdialog.cpp | 634 ++++++++++++++++++ src/rip/videodvd/k3bvideodvdrippingdialog.h | 82 +++ src/rip/videodvd/k3bvideodvdrippingjob.cpp | 385 +++++++++++ src/rip/videodvd/k3bvideodvdrippingjob.h | 106 +++ src/rip/videodvd/k3bvideodvdrippingpreview.cpp | 135 ++++ src/rip/videodvd/k3bvideodvdrippingpreview.h | 66 ++ .../videodvd/k3bvideodvdrippingtitlelistview.cpp | 410 ++++++++++++ src/rip/videodvd/k3bvideodvdrippingtitlelistview.h | 58 ++ src/rip/videodvd/k3bvideodvdrippingview.cpp | 256 ++++++++ src/rip/videodvd/k3bvideodvdrippingview.h | 66 ++ src/rip/videodvd/k3bvideodvdrippingwidget.cpp | 375 +++++++++++ src/rip/videodvd/k3bvideodvdrippingwidget.h | 67 ++ 48 files changed, 10030 insertions(+) create mode 100644 src/rip/Makefile.am create mode 100644 src/rip/base_k3baudiorippingoptionwidget.ui create mode 100644 src/rip/base_k3bcddbpatternwidget.ui create mode 100644 src/rip/k3baudiocdlistview.cpp create mode 100644 src/rip/k3baudiocdlistview.h create mode 100644 src/rip/k3baudiocdview.cpp create mode 100644 src/rip/k3baudiocdview.h create mode 100644 src/rip/k3baudioconvertingoptionwidget.cpp create mode 100644 src/rip/k3baudioconvertingoptionwidget.h create mode 100644 src/rip/k3baudioprojectconvertingdialog.cpp create mode 100644 src/rip/k3baudioprojectconvertingdialog.h create mode 100644 src/rip/k3baudioprojectconvertingthread.cpp create mode 100644 src/rip/k3baudioprojectconvertingthread.h create mode 100644 src/rip/k3baudioripjob.cpp create mode 100644 src/rip/k3baudioripjob.h create mode 100644 src/rip/k3baudiorippingdialog.cpp create mode 100644 src/rip/k3baudiorippingdialog.h create mode 100644 src/rip/k3baudioripthread.cpp create mode 100644 src/rip/k3baudioripthread.h create mode 100644 src/rip/k3bcddbpatternwidget.cpp create mode 100644 src/rip/k3bcddbpatternwidget.h create mode 100644 src/rip/k3bcuefilewriter.cpp create mode 100644 src/rip/k3bcuefilewriter.h create mode 100644 src/rip/k3bpatternparser.cpp create mode 100644 src/rip/k3bpatternparser.h create mode 100644 src/rip/k3bvideocdinfo.cpp create mode 100644 src/rip/k3bvideocdinfo.h create mode 100644 src/rip/k3bvideocdrip.cpp create mode 100644 src/rip/k3bvideocdrip.h create mode 100644 src/rip/k3bvideocdrippingdialog.cpp create mode 100644 src/rip/k3bvideocdrippingdialog.h create mode 100644 src/rip/k3bvideocdrippingoptions.h create mode 100644 src/rip/k3bvideocdview.cpp create mode 100644 src/rip/k3bvideocdview.h create mode 100644 src/rip/videodvd/Makefile.am create mode 100644 src/rip/videodvd/base_k3bvideodvdrippingwidget.ui create mode 100644 src/rip/videodvd/k3bvideodvdrippingdialog.cpp create mode 100644 src/rip/videodvd/k3bvideodvdrippingdialog.h create mode 100644 src/rip/videodvd/k3bvideodvdrippingjob.cpp create mode 100644 src/rip/videodvd/k3bvideodvdrippingjob.h create mode 100644 src/rip/videodvd/k3bvideodvdrippingpreview.cpp create mode 100644 src/rip/videodvd/k3bvideodvdrippingpreview.h create mode 100644 src/rip/videodvd/k3bvideodvdrippingtitlelistview.cpp create mode 100644 src/rip/videodvd/k3bvideodvdrippingtitlelistview.h create mode 100644 src/rip/videodvd/k3bvideodvdrippingview.cpp create mode 100644 src/rip/videodvd/k3bvideodvdrippingview.h create mode 100644 src/rip/videodvd/k3bvideodvdrippingwidget.cpp create mode 100644 src/rip/videodvd/k3bvideodvdrippingwidget.h (limited to 'src/rip') diff --git a/src/rip/Makefile.am b/src/rip/Makefile.am new file mode 100644 index 0000000..da023c7 --- /dev/null +++ b/src/rip/Makefile.am @@ -0,0 +1,32 @@ +if include_videodvdrip +VIDEODVDRIPDIR = videodvd +VIDEODVDRIPLIB = videodvd/libvideodvdrip.la +endif + +AM_CPPFLAGS = -I$(srcdir)/../../libk3b/core \ + -I$(srcdir)/../../libk3b/cddb \ + -I$(srcdir)/../../libk3bdevice \ + -I$(srcdir)/../../libk3b/plugin \ + -I$(srcdir)/../../libk3b/tools \ + -I$(srcdir)/../../libk3b/projects \ + -I$(srcdir)/../../libk3b/projects/audiocd \ + -I$(srcdir)/../../libk3b/cddb \ + -I$(srcdir)/../../libk3b/jobs/ \ + -I$(srcdir)/.. \ + -I$(srcdir)/../projects \ + $(all_includes) + +METASOURCES = AUTO + +noinst_LTLIBRARIES = librip.la +librip_la_LIBADD = $(VIDEODVDRIPLIB) + +librip_la_SOURCES = base_k3baudiorippingoptionwidget.ui \ + base_k3bcddbpatternwidget.ui k3bpatternparser.cpp k3baudiorippingdialog.cpp \ + k3baudioripthread.cpp k3baudiocdview.cpp k3bcddbpatternwidget.cpp \ + k3bvideocdinfo.cpp k3bvideocdview.cpp k3bvideocdrip.cpp \ + k3bvideocdrippingdialog.cpp k3bcuefilewriter.cpp k3baudioconvertingoptionwidget.cpp \ + k3baudiocdlistview.cpp k3baudioprojectconvertingdialog.cpp \ + k3baudioprojectconvertingthread.cpp k3baudioripjob.cpp + +SUBDIRS = $(VIDEODVDRIPDIR) \ No newline at end of file diff --git a/src/rip/base_k3baudiorippingoptionwidget.ui b/src/rip/base_k3baudiorippingoptionwidget.ui new file mode 100644 index 0000000..04355b4 --- /dev/null +++ b/src/rip/base_k3baudiorippingoptionwidget.ui @@ -0,0 +1,282 @@ + +base_K3bAudioRippingOptionWidget +Sebastian Trueg + + + Form1 + + + + 0 + 0 + 436 + 182 + + + + + unnamed + + + + groupBox2 + + + Filetype + + + + unnamed + + + + m_comboFileType + + + + 1 + 0 + 1 + 0 + + + + + + m_buttonConfigurePlugin + + + ... + + + Configure Plugin + + + + + + + groupBox3 + + + Options + + + + unnamed + + + + m_checkCreatePlaylist + + + Create m&3u playlist + + + Create playlist for the ripped files + + + <p>If this option is checked K3b will create a playlist of the ripped files +which can be used with programs like xmms or noatun. +<p>You may use the special strings to give the playlist a unique filename. + + + + + layout6 + + + + unnamed + + + + spacer1_2 + + + Horizontal + + + Fixed + + + + 20 + 16 + + + + + + m_checkPlaylistRelative + + + false + + + &Use relative paths + + + Use relative paths instead of absolute + + + <p>If this option is checked, the entries in the playlist will be relative to its location. +<p>Example: If your playlist is located in <em>/home/myself/music</em> and +your audio files are in <em>/home/myself/music/cool</em>; then the entries in the +playlist will look something like: <em>cool/track1.ogg</em>. + + + + + + + m_checkSingleFile + + + Create si&ngle file + + + Rip all tracks to a single file + + + <p>If this option is checked K3b will create only one +audio file no matter how many tracks are ripped. This +file will contain all tracks one after the other. +<p>This might be useful to rip a live album or a radio play. +<p><b>Caution:</b> The file will have the name of the first track. + + + + + layout6_2 + + + + unnamed + + + + spacer1_2_2 + + + Horizontal + + + Fixed + + + + 20 + 16 + + + + + + m_checkWriteCueFile + + + false + + + Write &cue file + + + Write a cuefile + + + <p>If this option is checked K3b will create a CDRWIN cue file which allows to easily write a copy of the audio CD on other systems. + + + + + + + + + groupBox1 + + + + 5 + 5 + 0 + 1 + + + + Target Folder + + + + unnamed + + + + textLabel1 + + + Free space in directory: + + + + + m_editBaseDir + + + + + m_labelFreeSpace + + + - + + + AlignVCenter|AlignRight + + + + + textLabel1_2 + + + Space needed: + + + + + m_labelNeededSpace + + + - + + + AlignVCenter|AlignRight + + + + + + + + + + + m_checkCreatePlaylist + toggled(bool) + m_checkPlaylistRelative + setEnabled(bool) + + + m_checkSingleFile + toggled(bool) + m_checkWriteCueFile + setEnabled(bool) + + + + + kurlrequester.h + kpushbutton.h + + diff --git a/src/rip/base_k3bcddbpatternwidget.ui b/src/rip/base_k3bcddbpatternwidget.ui new file mode 100644 index 0000000..cafaa5d --- /dev/null +++ b/src/rip/base_k3bcddbpatternwidget.ui @@ -0,0 +1,180 @@ + +base_K3bCddbPatternWidget + + + base_K3bPatternOptionTab + + + + 0 + 0 + 344 + 139 + + + + Ripping Pattern + + + + unnamed + + + 11 + + + 6 + + + + layout8 + + + + unnamed + + + + m_checkBlankReplace + + + Replace all blan&ks with: + + + + + m_editBlankReplace + + + false + + + _ + + + + + + + textLabel2 + + + Playlist pattern: + + + + + m_comboPlaylistPattern + + + + 1 + 0 + 1 + 0 + + + + true + + + + + textLabel1 + + + Ripped files pattern: + + + + + m_comboFilenamePattern + + + + 1 + 0 + 1 + 0 + + + + true + + + Insert your custom pattern here + + + + + layout2 + + + + unnamed + + + + spacer1 + + + Horizontal + + + Expanding + + + + 31 + 2 + + + + + + m_specialStringsLabel + + + See special strings + + + AlignVCenter|AlignRight + + + + + m_conditionalInclusionLabel + + + About conditional inclusion + + + AlignVCenter|AlignRight + + + + + + + + + + + m_checkBlankReplace + toggled(bool) + m_editBlankReplace + setEnabled(bool) + + + + m_comboFilenamePattern + m_comboPlaylistPattern + m_checkBlankReplace + m_editBlankReplace + + + + kurllabel.h + kurllabel.h + + diff --git a/src/rip/k3baudiocdlistview.cpp b/src/rip/k3baudiocdlistview.cpp new file mode 100644 index 0000000..b5f8566 --- /dev/null +++ b/src/rip/k3baudiocdlistview.cpp @@ -0,0 +1,66 @@ +/* + * + * $Id: k3baudiocdlistview.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3baudiocdlistview.h" +#include "k3baudiocdview.h" + +#include + +#include +#include + + +K3bAudioCdListView::K3bAudioCdListView( K3bAudioCdView* view, QWidget* parent, const char* name ) + : K3bListView( parent, name ), + m_view(view) +{ + setFullWidth(true); + setSorting(-1); + setAllColumnsShowFocus( true ); + setSelectionModeExt( Extended ); + setDragEnabled( true ); + addColumn( "" ); + addColumn( "" ); + addColumn( i18n("Artist") ); + addColumn( i18n("Title") ); + addColumn( i18n("Length") ); + addColumn( i18n("Size") ); + + setDoubleClickForEdit( true ); + + header()->setClickEnabled(false); + setColumnWidthMode( 0, QListView::Manual ); + setColumnWidth( 0, 20 ); + header()->setResizeEnabled( false,0 ); + + setColumnAlignment( 4, Qt::AlignHCenter ); + + QToolTip::add( viewport(), i18n("Check the tracks that should be ripped") ); +} + + +K3bAudioCdListView::~K3bAudioCdListView() +{ +} + + +QDragObject* K3bAudioCdListView::dragObject() +{ + return m_view->dragObject(); +} + + +#include "k3baudiocdlistview.moc" + diff --git a/src/rip/k3baudiocdlistview.h b/src/rip/k3baudiocdlistview.h new file mode 100644 index 0000000..1ce4a90 --- /dev/null +++ b/src/rip/k3baudiocdlistview.h @@ -0,0 +1,45 @@ +/* + * + * $Id: k3baudiocdlistview.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_AUDIO_CD_LISTVIEW_H_ +#define _K3B_AUDIO_CD_LISTVIEW_H_ + +#include + +class QDragObject; +class K3bAudioCdView; + +/** + * Internally used by K3bAudioCdView + */ +class K3bAudioCdListView : public K3bListView +{ + Q_OBJECT + + public: + K3bAudioCdListView( K3bAudioCdView*, QWidget* parent = 0, const char* name = 0 ); + ~K3bAudioCdListView(); + + protected: + /** + * @reimpl from KListView + */ + QDragObject* dragObject(); + + private: + K3bAudioCdView* m_view; +}; + +#endif diff --git a/src/rip/k3baudiocdview.cpp b/src/rip/k3baudiocdview.cpp new file mode 100644 index 0000000..d225922 --- /dev/null +++ b/src/rip/k3baudiocdview.cpp @@ -0,0 +1,631 @@ +/* + * + * $Id: k3baudiocdview.cpp 624215 2007-01-16 19:10:03Z trueg $ + * Copyright (C) 2003 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3baudiocdview.h" +#include "k3baudiorippingdialog.h" +#include "k3baudiocdlistview.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + + + +class K3bAudioCdView::AudioTrackViewItem : public K3bCheckListViewItem +{ +public: + AudioTrackViewItem( QListView* parent, + QListViewItem* after, + int _trackNumber, + const K3b::Msf& length) + : K3bCheckListViewItem( parent, after ) { + + setText( 1, QString::number(_trackNumber).rightJustify( 2, ' ' ) ); + setText( 3, i18n("Track %1").arg(_trackNumber) ); + setText( 4, " " + length.toString() + " " ); + setText( 5, " " + KIO::convertSize( length.audioBytes() ) + " " ); + + trackNumber = _trackNumber; + + setEditor( 2, LINE ); + setEditor( 3, LINE ); + + setChecked(true); + } + + void setup() { + K3bCheckListViewItem::setup(); + + setHeight( height() + 4 ); + } + + int trackNumber; + + void updateCddbData( const K3bCddbResultEntry& entry ) { + setText( 2, entry.artists[trackNumber-1] ); + setText( 3, entry.titles[trackNumber-1] ); + } +}; + + +K3bAudioCdView::K3bAudioCdView( QWidget* parent, const char *name ) + : K3bMediaContentsView( true, + K3bMedium::CONTENT_AUDIO, + K3bDevice::MEDIA_CD_ALL, + K3bDevice::STATE_INCOMPLETE|K3bDevice::STATE_COMPLETE, + parent, name ) +{ + QGridLayout* mainGrid = new QGridLayout( mainWidget() ); + + // toolbox + // ---------------------------------------------------------------------------------- + QHBoxLayout* toolBoxLayout = new QHBoxLayout( 0, 0, 0, "toolBoxLayout" ); + m_toolBox = new K3bToolBox( mainWidget() ); + toolBoxLayout->addWidget( m_toolBox ); + QSpacerItem* spacer = new QSpacerItem( 10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum ); + toolBoxLayout->addItem( spacer ); + m_labelLength = new QLabel( mainWidget() ); + m_labelLength->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) ); + toolBoxLayout->addWidget( m_labelLength ); + + + // the track view + // ---------------------------------------------------------------------------------- + m_trackView = new K3bAudioCdListView( this, mainWidget() ); + + connect( m_trackView, SIGNAL(itemRenamed(QListViewItem*, const QString&, int)), + this, SLOT(slotItemRenamed(QListViewItem*, const QString&, int)) ); + connect( m_trackView, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)), + this, SLOT(slotContextMenu(KListView*, QListViewItem*, const QPoint&)) ); +// connect( m_trackView, SIGNAL(selectionChanged(QListViewItem*)), +// this, SLOT(slotTrackSelectionChanged(QListViewItem*)) ); + + mainGrid->addLayout( toolBoxLayout, 0, 0 ); + mainGrid->addWidget( m_trackView, 1, 0 ); + + + m_cddb = new K3bCddb( this ); + + connect( m_cddb, SIGNAL(queryFinished(int)), + this, SLOT(slotCddbQueryFinished(int)) ); + + initActions(); + // slotTrackSelectionChanged(0); + + setLeftPixmap( K3bTheme::MEDIA_LEFT ); + setRightPixmap( K3bTheme::MEDIA_AUDIO ); + + m_busyInfoLabel = new K3bThemedLabel( i18n("Searching for Artist information..."), this ); + m_busyInfoLabel->setFrameStyle( QFrame::Box|QFrame::Plain ); + m_busyInfoLabel->setMargin( 6 ); + m_busyInfoLabel->hide(); +} + + +K3bAudioCdView::~K3bAudioCdView() +{ +} + + +void K3bAudioCdView::reloadMedium() +{ + m_toc = medium().toc(); + m_device = medium().device(); + + // initialize cddb info for editing + m_cddbInfo = K3bCddbResultEntry(); + m_cddbInfo.discid = QString::number( medium().toc().discId(), 16 ); + + for( int i = 0; i < (int)m_toc.count(); ++i ) { + m_cddbInfo.titles.append(""); + m_cddbInfo.artists.append(""); + m_cddbInfo.extInfos.append(""); + } + + m_trackView->clear(); + showBusyLabel(true); + + // create a listviewItem for every audio track + int index = 1; + for( K3bDevice::Toc::const_iterator it = m_toc.begin(); + it != m_toc.end(); ++it ) { + + // for now skip data tracks since we are not able to rip them to iso + if( (*it).type() == K3bTrack::AUDIO ) { + K3b::Msf length( (*it).length() ); + (void)new AudioTrackViewItem( m_trackView, + m_trackView->lastItem(), + index, + length ); + } + + index++; + } + + m_cdText = medium().cdText(); + + // simulate a cddb entry with the cdtext data + m_cddbInfo.cdTitle = m_cdText.title(); + m_cddbInfo.cdArtist = m_cdText.performer(); + m_cddbInfo.cdExtInfo = m_cdText.message(); + + for( unsigned int i = 0; i < m_cdText.count(); ++i ) { + m_cddbInfo.titles[i] = m_cdText[i].title(); + m_cddbInfo.artists[i] = m_cdText[i].performer(); + m_cddbInfo.extInfos[i] = m_cdText[i].message(); + } + + updateDisplay(); + + KConfig* c = k3bcore->config(); + c->setGroup("Cddb"); + bool useCddb = ( c->readBoolEntry( "use local cddb query", true ) || + c->readBoolEntry( "use remote cddb", false ) ); + + if( useCddb && + ( m_cdText.isEmpty() || + KMessageBox::questionYesNo( this, + i18n("Found Cd-Text. Do you want to use it instead of querying CDDB?"), + i18n("Found Cd-Text"), + i18n("Use CD-Text"), + i18n("Query CDDB"), + "prefereCdTextOverCddb" ) == KMessageBox::No ) ) + queryCddb(); + else + showBusyLabel(false); +} + + +void K3bAudioCdView::initActions() +{ + m_actionCollection = new KActionCollection( this ); + + KAction* actionSelectAll = new KAction( i18n("Check All"), 0, 0, this, + SLOT(slotCheckAll()), actionCollection(), + "check_all" ); + KAction* actionDeselectAll = new KAction( i18n("Uncheck All"), 0, 0, this, + SLOT(slotUncheckAll()), actionCollection(), + "uncheck_all" ); + KAction* actionSelect = new KAction( i18n("Check Track"), 0, 0, this, + SLOT(slotSelect()), actionCollection(), + "select_track" ); + KAction* actionDeselect = new KAction( i18n("Uncheck Track"), 0, 0, this, + SLOT(slotDeselect()), actionCollection(), + "deselect_track" ); + KAction* actionEditTrackCddbInfo = new KAction( i18n("Edit Track cddb Info"), "edit", 0, this, + SLOT(slotEditTrackCddb()), actionCollection(), + "edit_track_cddb" ); + KAction* actionEditAlbumCddbInfo = new KAction( i18n("Edit Album cddb Info"), "edit", 0, this, + SLOT(slotEditAlbumCddb()), actionCollection(), + "edit_album_cddb" ); + + KAction* actionStartRip = new KAction( i18n("Start Ripping"), "cddarip", 0, this, + SLOT(startRip()), actionCollection(), "start_rip" ); + + KAction* actionQueryCddb = new KAction( i18n("Query cddb"), "reload", 0, this, + SLOT(queryCddb()), actionCollection(), "query_cddb" ); + + KAction* actionSaveCddbLocally = new KAction( i18n("Save Cddb Entry Locally"), "filesave", 0, this, + SLOT(slotSaveCddbLocally()), actionCollection(), "save_cddb_local" ); + + // TODO: set the actions tooltips and whatsthis infos + + // setup the popup menu + m_popupMenu = new KActionMenu( actionCollection(), "popup_menu" ); + KAction* separator = new KActionSeparator( actionCollection(), "separator" ); + m_popupMenu->insert( actionSelect ); + m_popupMenu->insert( actionDeselect ); + m_popupMenu->insert( actionSelectAll ); + m_popupMenu->insert( actionDeselectAll ); + m_popupMenu->insert( separator ); + m_popupMenu->insert( actionEditTrackCddbInfo ); + m_popupMenu->insert( actionEditAlbumCddbInfo ); + m_popupMenu->insert( separator ); + m_popupMenu->insert( actionStartRip ); + + // setup the toolbox + m_toolBox->addButton( actionStartRip, true ); + m_toolBox->addSpacing(); + m_toolBox->addButton( actionQueryCddb ); + m_toolBox->addButton( actionSaveCddbLocally ); + m_toolBox->addButton( actionEditTrackCddbInfo ); + m_toolBox->addButton( actionEditAlbumCddbInfo ); +} + + +void K3bAudioCdView::slotContextMenu( KListView*, QListViewItem*, const QPoint& p ) +{ + m_popupMenu->popup(p); +} + + +void K3bAudioCdView::slotItemRenamed( QListViewItem* item, const QString& str, int col ) +{ + AudioTrackViewItem* a = (AudioTrackViewItem*)item; + if( col == 2 ) + m_cddbInfo.artists[a->trackNumber-1] = str; + else if( col == 3 ) + m_cddbInfo.titles[a->trackNumber-1] = str; + else if( col == 6 ) + m_cddbInfo.extInfos[a->trackNumber-1] = str; +} + + +void K3bAudioCdView::slotTrackSelectionChanged( QListViewItem* item ) +{ + actionCollection()->action("edit_track_cddb")->setEnabled( item != 0 ); + actionCollection()->action("select_track")->setEnabled( item != 0 ); + actionCollection()->action("deselect_track")->setEnabled( item != 0 ); +} + + +void K3bAudioCdView::startRip() +{ + QValueList trackNumbers; + for( QListViewItemIterator it( m_trackView ); it.current(); ++it ) { + AudioTrackViewItem* a = (AudioTrackViewItem*)it.current(); + if( a->isChecked() ) + trackNumbers.append( a->trackNumber ); + } + + if( trackNumbers.count() == 0 ) { + KMessageBox::error( this, i18n("Please select the tracks to rip."), + i18n("No Tracks Selected") ); + } + else { + K3bAudioRippingDialog rip( m_toc, + m_device, + m_cddbInfo, + trackNumbers, + this ); + rip.exec(); + } +} + + +void K3bAudioCdView::slotEditTrackCddb() +{ + QPtrList items( m_trackView->selectedItems() ); + if( !items.isEmpty() ) { + AudioTrackViewItem* a = static_cast(items.first()); + + KDialogBase d( this, "trackCddbDialog", true, i18n("Cddb Track %1").arg(a->trackNumber), + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true); + QWidget* w = new QWidget( &d ); + + KLineEdit* editTitle = new KLineEdit( m_cddbInfo.titles[a->trackNumber-1], w ); + KLineEdit* editArtist = new KLineEdit( m_cddbInfo.artists[a->trackNumber-1], w ); + KLineEdit* editExtInfo = new KLineEdit( m_cddbInfo.extInfos[a->trackNumber-1], w ); + QFrame* line = new QFrame( w ); + line->setFrameShape( QFrame::HLine ); + line->setFrameShadow( QFrame::Sunken ); + + QGridLayout* grid = new QGridLayout( w ); + grid->setSpacing( KDialog::spacingHint() ); + + grid->addWidget( new QLabel( i18n("Title:"), w ), 0, 0 ); + grid->addWidget( editTitle, 0, 1 ); + grid->addMultiCellWidget( line, 1, 1, 0, 1 ); + grid->addWidget( new QLabel( i18n("Artist:"), w ), 2, 0 ); + grid->addWidget( editArtist, 2, 1 ); + grid->addWidget( new QLabel( i18n("Extra info:"), w ), 3, 0 ); + grid->addWidget( editExtInfo, 3, 1 ); + grid->setRowStretch( 4, 1 ); + + d.setMainWidget(w); + d.resize( QMAX( QMAX(d.sizeHint().height(), d.sizeHint().width()), 300), d.sizeHint().height() ); + + if( d.exec() == QDialog::Accepted ) { + m_cddbInfo.titles[a->trackNumber-1] = editTitle->text(); + m_cddbInfo.artists[a->trackNumber-1] = editArtist->text(); + m_cddbInfo.extInfos[a->trackNumber-1] = editExtInfo->text(); + a->updateCddbData( m_cddbInfo ); + } + } +} + + +void K3bAudioCdView::slotEditAlbumCddb() +{ + KDialogBase d( this, "trackCddbDialog", true, i18n("Album Cddb"), + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true); + QWidget* w = new QWidget( &d ); + + KLineEdit* editTitle = new KLineEdit( m_cddbInfo.cdTitle, w ); + KLineEdit* editArtist = new KLineEdit( m_cddbInfo.cdArtist, w ); + KLineEdit* editExtInfo = new KLineEdit( m_cddbInfo.cdExtInfo, w ); + KLineEdit* editGenre = new KLineEdit( m_cddbInfo.genre, w ); + QSpinBox* spinYear = new QSpinBox( 0, 9999, 1, w ); + spinYear->setValue( m_cddbInfo.year ); + QFrame* line = new QFrame( w ); + line->setFrameShape( QFrame::HLine ); + line->setFrameShadow( QFrame::Sunken ); + KComboBox* comboCat = new KComboBox( w ); + comboCat->insertStringList( K3bCddbQuery::categories() ); + + // set the category + for( int i = 0; i < comboCat->count(); ++i ) + if( comboCat->text(i) == m_cddbInfo.category ) { + comboCat->setCurrentItem(i); + break; + } + + QGridLayout* grid = new QGridLayout( w ); + grid->setSpacing( KDialog::spacingHint() ); + + grid->addWidget( new QLabel( i18n("Title:"), w ), 0, 0 ); + grid->addWidget( editTitle, 0, 1 ); + grid->addMultiCellWidget( line, 1, 1, 0, 1 ); + grid->addWidget( new QLabel( i18n("Artist:"), w ), 2, 0 ); + grid->addWidget( editArtist, 2, 1 ); + grid->addWidget( new QLabel( i18n("Extra info:"), w ), 3, 0 ); + grid->addWidget( editExtInfo, 3, 1 ); + grid->addWidget( new QLabel( i18n("Genre:"), w ), 4, 0 ); + grid->addWidget( editGenre, 4, 1 ); + grid->addWidget( new QLabel( i18n("Year:"), w ), 5, 0 ); + grid->addWidget( spinYear, 5, 1 ); + grid->addWidget( new QLabel( i18n("Category:"), w ), 6, 0 ); + grid->addWidget( comboCat, 6, 1 ); + grid->setRowStretch( 7, 1 ); + + d.setMainWidget(w); + d.resize( QMAX( QMAX(d.sizeHint().height(), d.sizeHint().width()), 300), d.sizeHint().height() ); + + if( d.exec() == QDialog::Accepted ) { + m_cddbInfo.cdTitle = editTitle->text(); + m_cddbInfo.cdArtist = editArtist->text(); + m_cddbInfo.cdExtInfo = editExtInfo->text(); + m_cddbInfo.category = comboCat->currentText(); + m_cddbInfo.genre = editGenre->text(); + m_cddbInfo.year = spinYear->value(); + + updateDisplay(); + } +} + + +void K3bAudioCdView::queryCddb() +{ + KConfig* c = k3bcore->config(); + c->setGroup("Cddb"); + + m_cddb->readConfig( c ); + + if( c->readBoolEntry( "use local cddb query", true ) || + c->readBoolEntry( "use remote cddb", false ) ) { + + showBusyLabel(true); + + m_cddb->query( m_toc ); + } +} + + +void K3bAudioCdView::slotCddbQueryFinished( int error ) +{ + if( error == K3bCddbQuery::SUCCESS ) { + m_cddbInfo = m_cddb->result(); + + // save the entry locally + KConfig* c = k3bcore->config(); + c->setGroup( "Cddb" ); + if( c->readBoolEntry( "save cddb entries locally", true ) ) + m_cddb->saveEntry( m_cddbInfo ); + + updateDisplay(); + } + else if( error == K3bCddbQuery::NO_ENTRY_FOUND ) { + if( !KConfigGroup( k3bcore->config(), "Cddb" ).readBoolEntry( "use remote cddb", false ) ) + K3bPassivePopup::showPopup( i18n("

No CDDB entry found. Enable remote CDDB queries in the K3b settings to get access " + "to more entries through the internet."), i18n("CDDB") ); + else + K3bPassivePopup::showPopup( i18n("No CDDB entry found."), i18n("CDDB") ); + } + else if( error != K3bCddbQuery::CANCELED ) { + K3bPassivePopup::showPopup( m_cddb->errorString(), i18n("CDDB Error") ); + } + + enableInteraction(true); +} + + +void K3bAudioCdView::slotSaveCddbLocally() +{ + // check if the minimal info has been inserted + if( m_cddbInfo.category.isEmpty() ) { + KMessageBox::sorry( this, i18n("Please set the category before saving.") ); + return; + } + + if( m_cddbInfo.cdTitle.isEmpty() || m_cddbInfo.cdArtist.isEmpty() ) { + KMessageBox::sorry( this, i18n("Please set CD artist and title before saving.") ); + return; + } + + bool missingTitle = false; + bool missingArtist = false; + bool allTrackArtistsEmpty = true; + for( unsigned int i = 0; i < m_cddbInfo.titles.count(); ++i ) { + if( m_cddbInfo.titles[i].isEmpty() ) + missingTitle = true; + if( m_cddbInfo.artists[i].isEmpty() ) + missingArtist = true; + if( !m_cddbInfo.artists[i].isEmpty() ) + allTrackArtistsEmpty = false; + } + + if( missingTitle || + ( missingArtist && !allTrackArtistsEmpty ) ) { + KMessageBox::sorry( this, i18n("Please set at least artist and title on all tracks before saving.") ); + return; + } + + // make sure the data gets updated (bad design like a lot in the cddb stuff! :( + m_cddbInfo.rawData.truncate(0); + + KConfig* c = k3bcore->config(); + c->setGroup("Cddb"); + + m_cddb->readConfig( c ); + + m_cddb->saveEntry( m_cddbInfo ); + K3bPassivePopup::showPopup( i18n("Saved entry (%1) in category %2.") + .arg(m_cddbInfo.discid) + .arg(m_cddbInfo.category), + i18n("CDDB") ); +} + + +void K3bAudioCdView::slotCheckAll() +{ + for( QListViewItemIterator it( m_trackView ); it.current(); ++it ) + ((AudioTrackViewItem*)it.current())->setChecked(true); +} + +void K3bAudioCdView::slotUncheckAll() +{ + for( QListViewItemIterator it( m_trackView ); it.current(); ++it ) + ((AudioTrackViewItem*)it.current())->setChecked(false); +} + +void K3bAudioCdView::slotSelect() +{ + QPtrList items( m_trackView->selectedItems() ); + for( QPtrListIterator it( items ); + it.current(); ++it ) + static_cast(it.current())->setChecked(true); +} + +void K3bAudioCdView::slotDeselect() +{ + QPtrList items( m_trackView->selectedItems() ); + for( QPtrListIterator it( items ); + it.current(); ++it ) + static_cast(it.current())->setChecked(false); +} + +void K3bAudioCdView::updateDisplay() +{ + // update the listview + for( QListViewItemIterator it( m_trackView ); it.current(); ++it ) { + AudioTrackViewItem* item = (AudioTrackViewItem*)it.current(); + item->updateCddbData( m_cddbInfo ); + } + + if( !m_cddbInfo.cdTitle.isEmpty() ) { + QString s = m_cddbInfo.cdTitle; + if( !m_cddbInfo.cdArtist.isEmpty() ) + s += " (" + m_cddbInfo.cdArtist + ")"; + setTitle( s ); + } + else + setTitle( i18n("Audio CD") ); + + m_labelLength->setText( i18n("1 track (%1)", + "%n tracks (%1)", + m_toc.count()).arg(K3b::Msf(m_toc.length()).toString()) ); +} + + +void K3bAudioCdView::showBusyLabel( bool b ) +{ + if( !b ) { + actionCollection()->action( "start_rip" )->setEnabled( true ); + m_trackView->setEnabled( true ); + m_busyInfoLabel->hide(); + } + else { + // the themed label is a cut label, thus its size hint is + // based on the cut text, we force it to be full + m_busyInfoLabel->resize( width(), height() ); + m_busyInfoLabel->resize( m_busyInfoLabel->sizeHint() ); + int x = (width() - m_busyInfoLabel->width())/2; + int y = (height() - m_busyInfoLabel->height())/2; + QRect r( QPoint( x, y ), m_busyInfoLabel->size() ); + m_busyInfoLabel->setGeometry( r ); + m_busyInfoLabel->show(); + + m_trackView->setEnabled( false ); + enableInteraction( false ); + } +} + + +void K3bAudioCdView::enableInteraction( bool b ) +{ + // we leave the track view enabled in default disabled mode + // since drag'n'drop to audio projects does not need an inserted CD + actionCollection()->action( "start_rip" )->setEnabled( b ); + if( b ) + showBusyLabel( false ); +} + + +QDragObject* K3bAudioCdView::dragObject() +{ + QPtrList items = m_trackView->selectedItems(); + QValueList tracks; + for( QPtrListIterator it( items ); + it.current(); ++it ) + tracks.append( static_cast(it.current())->trackNumber ); + + if( !items.isEmpty() ) { + QDragObject* drag = new K3bAudioCdTrackDrag( m_toc, + tracks, + m_cddbInfo, + m_device, + this ); + drag->setPixmap( m_trackView->createDragPixmap( items ) ); + return drag; + } + else + return 0; +} + +#include "k3baudiocdview.moc" diff --git a/src/rip/k3baudiocdview.h b/src/rip/k3baudiocdview.h new file mode 100644 index 0000000..257a0f5 --- /dev/null +++ b/src/rip/k3baudiocdview.h @@ -0,0 +1,107 @@ +/* + * + * $Id: k3baudiocdview.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef _K3B_AUDIOCD_VIEW_H_ +#define _K3B_AUDIOCD_VIEW_H_ + +#include +#include + +#include +#include +#include + +class K3bListView; +class KListView; +class QListViewItem; +class QPoint; +class KActionCollection; +class KActionMenu; +class K3bCddb; +class QLabel; +class K3bToolBox; +class QDragObject; + + +namespace K3bDevice { + class Device; +} + + +class K3bAudioCdView : public K3bMediaContentsView +{ + Q_OBJECT + + public: + K3bAudioCdView( QWidget* parent = 0, const char * name = 0 ); + ~K3bAudioCdView(); + + KActionCollection* actionCollection() const { return m_actionCollection; } + + /** + * internal + */ + QDragObject* dragObject(); + + public slots: + void queryCddb(); + + private slots: + void slotContextMenu( KListView*, QListViewItem*, const QPoint& ); + void slotItemRenamed( QListViewItem*, const QString&, int ); + void slotCddbQueryFinished( int ); + void slotTrackSelectionChanged( QListViewItem* ); + void slotSaveCddbLocally(); + + void slotEditTrackCddb(); + void slotEditAlbumCddb(); + void startRip(); + void slotCheckAll(); + void slotUncheckAll(); + void slotSelect(); + void slotDeselect(); + + private: + void reloadMedium(); + + void initActions(); + void updateDisplay(); + void enableInteraction( bool ); + void showBusyLabel( bool ); + + K3bDevice::Toc m_toc; + K3bDevice::Device* m_device; + + K3bCddbResultEntry m_cddbInfo; + + KActionCollection* m_actionCollection; + KActionMenu* m_popupMenu; + + K3bListView* m_trackView; + K3bToolBox* m_toolBox; + QLabel* m_labelLength; + + class AudioTrackViewItem; + + K3bCddb* m_cddb; + + K3bDevice::CdText m_cdText; + + QLabel* m_busyInfoLabel; +}; + + +#endif diff --git a/src/rip/k3baudioconvertingoptionwidget.cpp b/src/rip/k3baudioconvertingoptionwidget.cpp new file mode 100644 index 0000000..a044dc5 --- /dev/null +++ b/src/rip/k3baudioconvertingoptionwidget.cpp @@ -0,0 +1,266 @@ +/* + * + * $Id: k3baudioconvertingoptionwidget.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3baudioconvertingoptionwidget.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + + +class K3bAudioConvertingOptionWidget::Private +{ +public: + QIntDict encoderMap; + QMap extensionMap; + + QTimer freeSpaceUpdateTimer; + + KIO::filesize_t neededSize; + + int getDefaultFormat() { + // we prefere formats in this order: + // 1. ogg + // 2. mp3 + // 3. flac + // 4. wave + int ogg = -1; + int mp3 = -1; + int flac = -1; + for( QMap::const_iterator it = extensionMap.constBegin(); + it != extensionMap.constEnd(); ++it ) { + if( it.data() == "ogg" ) + ogg = it.key(); + else if( it.data() == "mp3" ) + mp3 = it.key(); + else if( it.data() == "flac" ) + flac = it.key(); + } + + if( ogg > -1 ) + return ogg; + else if( mp3 > -1 ) + return mp3; + else if( flac > -1 ) + return flac; + else + return 0; + } +}; + + +K3bAudioConvertingOptionWidget::K3bAudioConvertingOptionWidget( QWidget* parent, const char* name ) + : base_K3bAudioRippingOptionWidget( parent, name ) +{ + d = new Private(); + + connect( m_editBaseDir, SIGNAL(textChanged(const QString&)), + this, SLOT(slotUpdateFreeTempSpace()) ); + connect( m_comboFileType, SIGNAL(activated(int)), + this, SLOT(slotEncoderChanged()) ); + connect( &d->freeSpaceUpdateTimer, SIGNAL(timeout()), + this, SLOT(slotUpdateFreeTempSpace()) ); + connect( m_checkCreatePlaylist, SIGNAL(toggled(bool)), this, SIGNAL(changed()) ); + connect( m_checkSingleFile, SIGNAL(toggled(bool)), this, SIGNAL(changed()) ); + connect( m_checkWriteCueFile, SIGNAL(toggled(bool)), this, SIGNAL(changed()) ); + connect( m_comboFileType, SIGNAL(activated(int)), this, SIGNAL(changed()) ); + connect( m_editBaseDir, SIGNAL(textChanged(const QString&)), this, SIGNAL(changed()) ); + connect( m_buttonConfigurePlugin, SIGNAL(clicked()), this, SLOT(slotConfigurePlugin()) ); + + m_editBaseDir->setMode( KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly ); + m_buttonConfigurePlugin->setIconSet( SmallIconSet( "gear" ) ); + + // FIXME: see if sox and the sox encoder are installed and if so do not put the internal wave + // writer in the list of encoders. + + d->encoderMap.clear(); + d->extensionMap.clear(); + m_comboFileType->clear(); + m_comboFileType->insertItem( i18n("Wave") ); + d->extensionMap[0] = "wav"; + + // check the available encoding plugins + QPtrList fl = k3bcore->pluginManager()->plugins( "AudioEncoder" ); + for( QPtrListIterator it( fl ); it.current(); ++it ) { + K3bAudioEncoder* f = (K3bAudioEncoder*)it.current(); + QStringList exL = f->extensions(); + + for( QStringList::const_iterator exIt = exL.begin(); + exIt != exL.end(); ++exIt ) { + d->extensionMap.insert( m_comboFileType->count(), *exIt ); + d->encoderMap.insert( m_comboFileType->count(), f ); + m_comboFileType->insertItem( f->fileTypeComment(*exIt) ); + } + } + + // refresh every 2 seconds + d->freeSpaceUpdateTimer.start(2000); + slotUpdateFreeTempSpace(); +} + + +K3bAudioConvertingOptionWidget::~K3bAudioConvertingOptionWidget() +{ + delete d; +} + + +QString K3bAudioConvertingOptionWidget::baseDir() const +{ + return m_editBaseDir->url(); +} + + +void K3bAudioConvertingOptionWidget::setBaseDir( const QString& path ) +{ + m_editBaseDir->setURL( path ); +} + + +void K3bAudioConvertingOptionWidget::setNeededSize( KIO::filesize_t size ) +{ + d->neededSize = size; + if( size > 0 ) + m_labelNeededSpace->setText( KIO::convertSize( size ) ); + else + m_labelNeededSpace->setText( i18n("unknown") ); + + slotUpdateFreeTempSpace(); +} + + +void K3bAudioConvertingOptionWidget::slotConfigurePlugin() +{ + // 0 for wave + K3bAudioEncoder* encoder = d->encoderMap[m_comboFileType->currentItem()]; + if( encoder ) + k3bcore->pluginManager()->execPluginDialog( encoder, this ); +} + + +void K3bAudioConvertingOptionWidget::slotUpdateFreeTempSpace() +{ + QString path = m_editBaseDir->url(); + + if( !QFile::exists( path ) ) + path.truncate( path.findRev('/') ); + + unsigned long size, avail; + if( K3b::kbFreeOnFs( path, size, avail ) ) { + m_labelFreeSpace->setText( KIO::convertSizeFromKB(avail) ); + if( avail < d->neededSize/1024 ) + m_labelNeededSpace->setPaletteForegroundColor( Qt::red ); + else + m_labelNeededSpace->setPaletteForegroundColor( paletteForegroundColor() ); + } + else { + m_labelFreeSpace->setText("-"); + m_labelNeededSpace->setPaletteForegroundColor( paletteForegroundColor() ); + } +} + + +void K3bAudioConvertingOptionWidget::slotEncoderChanged() +{ + // 0 for wave + m_buttonConfigurePlugin->setEnabled( d->encoderMap[m_comboFileType->currentItem()] != 0 ); +} + + +K3bAudioEncoder* K3bAudioConvertingOptionWidget::encoder() const +{ + return d->encoderMap[m_comboFileType->currentItem()]; // 0 for wave +} + + +QString K3bAudioConvertingOptionWidget::extension() const +{ + return d->extensionMap[m_comboFileType->currentItem()]; +} + + +void K3bAudioConvertingOptionWidget::loadDefaults() +{ + m_editBaseDir->setURL( QDir::homeDirPath() ); + m_checkSingleFile->setChecked( false ); + m_checkWriteCueFile->setChecked( false ); + m_comboFileType->setCurrentItem( d->getDefaultFormat() ); + m_checkCreatePlaylist->setChecked(false); + m_checkPlaylistRelative->setChecked(false); + + slotEncoderChanged(); +} + + +void K3bAudioConvertingOptionWidget::loadConfig( KConfigBase* c ) +{ + m_editBaseDir->setURL( c->readPathEntry( "last ripping directory", QDir::homeDirPath() ) ); + + m_checkSingleFile->setChecked( c->readBoolEntry( "single_file", false ) ); + m_checkWriteCueFile->setChecked( c->readBoolEntry( "write_cue_file", false ) ); + + m_checkCreatePlaylist->setChecked( c->readBoolEntry( "create_playlist", false ) ); + m_checkPlaylistRelative->setChecked( c->readBoolEntry( "relative_path_in_playlist", false ) ); + + QString filetype = c->readEntry( "filetype", d->extensionMap[d->getDefaultFormat()] ); + if( filetype == "wav" ) + m_comboFileType->setCurrentItem(0); + else { + for( QMap::iterator it = d->extensionMap.begin(); + it != d->extensionMap.end(); ++it ) { + if( it.data() == filetype ) { + m_comboFileType->setCurrentItem( it.key() ); + break; + } + } + } + + slotEncoderChanged(); +} + + +void K3bAudioConvertingOptionWidget::saveConfig( KConfigBase* c ) +{ + c->writePathEntry( "last ripping directory", m_editBaseDir->url() ); + + c->writeEntry( "single_file", m_checkSingleFile->isChecked() ); + c->writeEntry( "write_cue_file", m_checkWriteCueFile->isChecked() ); + + c->writeEntry( "create_playlist", m_checkCreatePlaylist->isChecked() ); + c->writeEntry( "relative_path_in_playlist", m_checkPlaylistRelative->isChecked() ); + + if( d->extensionMap.contains(m_comboFileType->currentItem()) ) + c->writeEntry( "filetype", d->extensionMap[m_comboFileType->currentItem()] ); + else + c->writeEntry( "filetype", "wav" ); +} + +#include "k3baudioconvertingoptionwidget.moc" diff --git a/src/rip/k3baudioconvertingoptionwidget.h b/src/rip/k3baudioconvertingoptionwidget.h new file mode 100644 index 0000000..ff4cd45 --- /dev/null +++ b/src/rip/k3baudioconvertingoptionwidget.h @@ -0,0 +1,74 @@ +/* + * + * $Id: k3baudioconvertingoptionwidget.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_AUDIO_CONVERTING_OPTION_WIDGET_H_ +#define _K3B_AUDIO_CONVERTING_OPTION_WIDGET_H_ + +#include "base_k3baudiorippingoptionwidget.h" + +#include +#include + +class K3bAudioEncoder; +class KConfigBase; + + +/** + * Internally used by K3bAudioConvertingDialog + */ +class K3bAudioConvertingOptionWidget : public base_K3bAudioRippingOptionWidget +{ + Q_OBJECT + + public: + K3bAudioConvertingOptionWidget( QWidget* parent, const char* name = 0 ); + ~K3bAudioConvertingOptionWidget(); + + void setBaseDir( const QString& path ); + + void setNeededSize( KIO::filesize_t ); + + /** + * @returns 0 if wave is selected + */ + K3bAudioEncoder* encoder() const; + QString extension() const; + + QString baseDir() const; + + bool createPlaylist() const { return m_checkCreatePlaylist->isChecked(); } + bool playlistRelativePath() const { return m_checkPlaylistRelative->isChecked(); } + bool createSingleFile() const { return m_checkSingleFile->isChecked(); } + bool createCueFile() const { return m_checkWriteCueFile->isChecked(); } + + public slots: + void loadDefaults(); + void loadConfig( KConfigBase* ); + void saveConfig( KConfigBase* ); + + signals: + void changed(); + + private slots: + void slotConfigurePlugin(); + void slotUpdateFreeTempSpace(); + void slotEncoderChanged(); + + private: + class Private; + Private* d; +}; + +#endif diff --git a/src/rip/k3baudioprojectconvertingdialog.cpp b/src/rip/k3baudioprojectconvertingdialog.cpp new file mode 100644 index 0000000..8a3eadc --- /dev/null +++ b/src/rip/k3baudioprojectconvertingdialog.cpp @@ -0,0 +1,371 @@ +/* + * + * $Id: k3baudioprojectconvertingdialog.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#include "k3baudioprojectconvertingdialog.h" +#include "k3baudioprojectconvertingthread.h" +#include "k3bpatternparser.h" +#include "k3bcddbpatternwidget.h" +#include "k3baudioconvertingoptionwidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + + +class K3bAudioProjectConvertingDialog::Private +{ +public: + Private() { + } + + QValueVector filenames; + QString playlistFilename; + QString cueFilename; +}; + + +K3bAudioProjectConvertingDialog::K3bAudioProjectConvertingDialog( K3bAudioDoc* doc, QWidget *parent, const char *name ) + : K3bInteractionDialog( parent, name, + QString::null, + QString::null, + START_BUTTON|CANCEL_BUTTON, + START_BUTTON, + "Audio Project Converting" ), // config group + m_doc(doc) +{ + d = new Private(); + + setupGui(); + + setTitle( i18n("Audio Project Conversion"), + i18n("1 track (%1)", "%n tracks (%1)", + m_doc->numOfTracks()).arg(m_doc->length().toString()) ); + + refresh(); +} + + +K3bAudioProjectConvertingDialog::~K3bAudioProjectConvertingDialog() +{ + delete d; +} + + +void K3bAudioProjectConvertingDialog::setupGui() +{ + QWidget *frame = mainWidget(); + QGridLayout* Form1Layout = new QGridLayout( frame ); + Form1Layout->setSpacing( KDialog::spacingHint() ); + Form1Layout->setMargin( 0 ); + + m_viewTracks = new K3bListView( frame, "m_viewTracks" ); + m_viewTracks->addColumn(i18n( "Filename (relative to base directory)") ); + m_viewTracks->addColumn(i18n( "Length") ); + m_viewTracks->addColumn(i18n( "File Size") ); + m_viewTracks->setSorting(-1); + m_viewTracks->setAllColumnsShowFocus(true); + m_viewTracks->setFullWidth(true); + + QTabWidget* mainTab = new QTabWidget( frame ); + + m_optionWidget = new K3bAudioConvertingOptionWidget( mainTab ); + mainTab->addTab( m_optionWidget, i18n("Settings") ); + + + // setup filename pattern page + // ------------------------------------------------------------------------------------------- + m_patternWidget = new K3bCddbPatternWidget( mainTab ); + mainTab->addTab( m_patternWidget, i18n("File Naming") ); + connect( m_patternWidget, SIGNAL(changed()), this, SLOT(refresh()) ); + + Form1Layout->addWidget( m_viewTracks, 0, 0 ); + Form1Layout->addWidget( mainTab, 1, 0 ); + Form1Layout->setRowStretch( 0, 1 ); + + connect( m_optionWidget, SIGNAL(changed()), this, SLOT(refresh()) ); +} + + +void K3bAudioProjectConvertingDialog::slotStartClicked() +{ + // make sure we have the tracks just for ourselves + static_cast(m_doc->view())->player()->stop(); + + // check if all filenames differ + if( d->filenames.count() > 1 ) { + bool differ = true; + // the most stupid version to compare but most cds have about 12 tracks + // that's a size where algorithms do not need any optimization! ;) + for( unsigned int i = 0; i < d->filenames.count(); ++i ) { + for( unsigned int j = i+1; j < d->filenames.count(); ++j ) + if( d->filenames[i] == d->filenames[j] ) { + differ = false; + break; + } + } + + if( !differ ) { + KMessageBox::sorry( this, i18n("Please check the naming pattern. All filenames need to be unique.") ); + return; + } + } + + // check if we need to overwrite some files... + QListViewItemIterator it( m_viewTracks ); + QStringList filesToOverwrite; + for( unsigned int i = 0; i < d->filenames.count(); ++i ) { + if( QFile::exists( d->filenames[i] ) ) + filesToOverwrite.append( d->filenames[i] ); + } + + if( m_optionWidget->createPlaylist() && QFile::exists( d->playlistFilename ) ) + filesToOverwrite.append( d->playlistFilename ); + + if( !filesToOverwrite.isEmpty() ) + if( KMessageBox::warningContinueCancelList( this, + i18n("Do you want to overwrite these files?"), + filesToOverwrite, + i18n("Files Exist"), i18n("Overwrite") ) == KMessageBox::Cancel ) + return; + + + // just generate a fake m_tracks list for now so we can keep most of the methods + // like they are in K3bAudioRipThread. This way future combination is easier + QValueVector > tracksToRip; + int i = 0; + K3bAudioTrack* track = m_doc->firstTrack(); + while( track ) { + tracksToRip.append( qMakePair( i+1, d->filenames[(m_optionWidget->createSingleFile() ? 0 : i)] ) ); + ++i; + track = track->next(); + } + + K3bAudioEncoder* encoder = m_optionWidget->encoder(); + + K3bAudioProjectConvertingThread* thread = new K3bAudioProjectConvertingThread( m_doc ); + thread->setCddbEntry( createCddbEntryFromDoc( m_doc ) ); + thread->setTracksToRip( tracksToRip ); + thread->setSingleFile( m_optionWidget->createSingleFile() ); + thread->setWriteCueFile( m_optionWidget->createCueFile() ); + thread->setEncoder( encoder ); + thread->setWritePlaylist( m_optionWidget->createPlaylist() ); + thread->setPlaylistFilename( d->playlistFilename ); + thread->setUseRelativePathInPlaylist( m_optionWidget->playlistRelativePath() ); + if( encoder ) + thread->setFileType( m_optionWidget->extension() ); + + K3bJobProgressDialog progressDialog( parentWidget() ); + + K3bThreadJob job( thread, &progressDialog, this ); + + hide(); + progressDialog.startJob(&job); + + delete thread; + + close(); +} + + +void K3bAudioProjectConvertingDialog::refresh() +{ + m_viewTracks->clear(); + d->filenames.clear(); + + // FIXME: this is bad and needs to be improved + // create a cddb entry from the doc to use in the patternparser + K3bCddbResultEntry cddbEntry = createCddbEntryFromDoc( m_doc ); + + QString baseDir = K3b::prepareDir( m_optionWidget->baseDir() ); + + QString extension = m_optionWidget->extension(); + + KIO::filesize_t overallSize = 0; + + if( m_optionWidget->createSingleFile() ) { + QString filename; + long long filesize = 0; + if( m_optionWidget->encoder() == 0 ) { + filesize = m_doc->length().audioBytes() + 44; + } + else { + filesize = m_optionWidget->encoder()->fileSize( extension, m_doc->length() ); + } + + if( filesize > 0 ) + overallSize = filesize; + + filename = K3bPatternParser::parsePattern( cddbEntry, 1, + m_patternWidget->filenamePattern(), + m_patternWidget->replaceBlanks(), + m_patternWidget->blankReplaceString() ); + + + (void)new KListViewItem( m_viewTracks, + m_viewTracks->lastItem(), + filename + "." + extension, + m_doc->length().toString(), + filesize < 0 ? i18n("unknown") : KIO::convertSize( filesize ) ); + + d->filenames.append( K3b::fixupPath( baseDir + "/" + filename + "." + extension ) ); + + if( m_optionWidget->createCueFile() ) { + d->cueFilename = K3b::fixupPath( baseDir + "/" + filename + ".cue" ); + (void)new KListViewItem( m_viewTracks, + m_viewTracks->lastItem(), + filename + ".cue", + "-", + "-", + i18n("Cue-file") ); + } + } + else { + K3bAudioTrack* track = m_doc->firstTrack(); + unsigned int i = 1; + while( track ) { + long long filesize = 0; + if( m_optionWidget->encoder() == 0 ) { + filesize = track->length().audioBytes() + 44; + } + else { + filesize = m_optionWidget->encoder()->fileSize( extension, track->length() ); + } + + if( filesize > 0 ) + overallSize += filesize; + + QString filename = K3bPatternParser::parsePattern( cddbEntry, i, + m_patternWidget->filenamePattern(), + m_patternWidget->replaceBlanks(), + m_patternWidget->blankReplaceString() ) + "." + extension; + + (void)new KListViewItem( m_viewTracks, + m_viewTracks->lastItem(), + filename, + track->length().toString(), + filesize < 0 ? i18n("unknown") : KIO::convertSize( filesize ) ); + + d->filenames.append( K3b::fixupPath( baseDir + "/" + filename ) ); + + track = track->next(); + ++i; + } + } + + // create playlist item + if( m_optionWidget->createPlaylist() ) { + QString filename = K3bPatternParser::parsePattern( cddbEntry, 1, + m_patternWidget->playlistPattern(), + m_patternWidget->replaceBlanks(), + m_patternWidget->blankReplaceString() ) + ".m3u"; + + (void)new KListViewItem( m_viewTracks, + m_viewTracks->lastItem(), + filename, + "-", + "-", + i18n("Playlist") ); + + d->playlistFilename = K3b::fixupPath( baseDir + "/" + filename ); + } + + if( overallSize > 0 ) + m_optionWidget->setNeededSize( overallSize ); + else + m_optionWidget->setNeededSize( 0 ); +} + + +void K3bAudioProjectConvertingDialog::setBaseDir( const QString& path ) +{ + m_optionWidget->setBaseDir( path ); +} + + +void K3bAudioProjectConvertingDialog::loadK3bDefaults() +{ + m_optionWidget->loadDefaults(); + m_patternWidget->loadDefaults(); + + refresh(); +} + +void K3bAudioProjectConvertingDialog::loadUserDefaults( KConfigBase* c ) +{ + m_optionWidget->loadConfig( c ); + m_patternWidget->loadConfig( c ); + + refresh(); +} + + +void K3bAudioProjectConvertingDialog::saveUserDefaults( KConfigBase* c ) +{ + m_optionWidget->saveConfig( c ); + m_patternWidget->saveConfig( c ); +} + + +K3bCddbResultEntry K3bAudioProjectConvertingDialog::createCddbEntryFromDoc( K3bAudioDoc* doc ) +{ + K3bCddbResultEntry e; + + // global + e.cdTitle = doc->title(); + e.cdArtist = doc->artist(); + e.cdExtInfo = doc->cdTextMessage(); + + // tracks + K3bAudioTrack* track = doc->firstTrack(); + while( track ) { + e.titles.append( track->title() ); + e.artists.append( track->artist() ); + e.extInfos.append( track->cdTextMessage() ); + + track = track->next(); + } + + return e; +} + +#include "k3baudioprojectconvertingdialog.moc" diff --git a/src/rip/k3baudioprojectconvertingdialog.h b/src/rip/k3baudioprojectconvertingdialog.h new file mode 100644 index 0000000..1816ea2 --- /dev/null +++ b/src/rip/k3baudioprojectconvertingdialog.h @@ -0,0 +1,78 @@ +/* + * + * $Id: k3baudioprojectconvertingdialog.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef _K3B_AUDIO_PROJECT_CONVERTING_DIALOG_H_ +#define _K3B_AUDIO_PROJECT_CONVERTING_DIALOG_H_ + +#include +#include + +#include + + +class K3bListView; +class QCheckBox; +class QSpinBox; +class QComboBox; +class K3bCddbPatternWidget; +class QToolButton; +class K3bAudioConvertingOptionWidget; +class K3bCddbResultEntry; +class K3bAudioConvertingJob; +class K3bJobHandler; +class K3bAudioDoc; + + +/** + *@author Sebastian Trueg + */ +class K3bAudioProjectConvertingDialog : public K3bInteractionDialog +{ + Q_OBJECT + + public: + K3bAudioProjectConvertingDialog( K3bAudioDoc*, QWidget *parent = 0, const char *name = 0 ); + ~K3bAudioProjectConvertingDialog(); + + void setBaseDir( const QString& path ); + + public slots: + void refresh(); + + protected: + void loadK3bDefaults(); + void loadUserDefaults( KConfigBase* ); + void saveUserDefaults( KConfigBase* ); + + private: + K3bCddbPatternWidget* m_patternWidget; + K3bAudioConvertingOptionWidget* m_optionWidget; + + K3bListView* m_viewTracks; + K3bAudioDoc* m_doc; + + void setupGui(); + + static K3bCddbResultEntry createCddbEntryFromDoc( K3bAudioDoc* ); + + class Private; + Private* d; + + private slots: + void slotStartClicked(); +}; + +#endif diff --git a/src/rip/k3baudioprojectconvertingthread.cpp b/src/rip/k3baudioprojectconvertingthread.cpp new file mode 100644 index 0000000..8c175fb --- /dev/null +++ b/src/rip/k3baudioprojectconvertingthread.cpp @@ -0,0 +1,459 @@ +/* + * + * $Id: k3baudioprojectconvertingthread.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2005 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#include "k3baudioprojectconvertingthread.h" +#include "k3bpatternparser.h" + +#include +#include +#include +#include +#include +#include "k3bcuefilewriter.h" + +#include + +#include +#include + +#include +#include +#include + + + + +class K3bAudioProjectConvertingThread::Private +{ +public: + Private() + : encoder(0), + waveFileWriter(0), + canceled(false) { + } + + // the index of the currently ripped track in m_tracks + int currentTrackIndex; + long long overallBytesRead; + long long overallBytesToRead; + + K3bAudioEncoder* encoder; + K3bWaveFileWriter* waveFileWriter; + + bool canceled; + + QString fileType; +}; + + +K3bAudioProjectConvertingThread::K3bAudioProjectConvertingThread( K3bAudioDoc* doc ) + : K3bThread(), + m_doc(doc) +{ + d = new Private(); +} + + +K3bAudioProjectConvertingThread::~K3bAudioProjectConvertingThread() +{ + delete d->waveFileWriter; + delete d; +} + + +void K3bAudioProjectConvertingThread::setFileType( const QString& t ) +{ + d->fileType = t; +} + + +void K3bAudioProjectConvertingThread::setEncoder( K3bAudioEncoder* f ) +{ + d->encoder = f; +} + + +void K3bAudioProjectConvertingThread::init() +{ + d->canceled = false; +} + + +void K3bAudioProjectConvertingThread::run() +{ + emitStarted(); + emitNewTask( i18n("Converting Audio Tracks") ); + + if( !d->encoder ) + if( !d->waveFileWriter ) + d->waveFileWriter = new K3bWaveFileWriter(); + + + d->overallBytesRead = 0; + d->overallBytesToRead = m_doc->length().audioBytes(); + + if( m_singleFile ) { + QString& filename = m_tracks[0].second; + + QString dir = filename.left( filename.findRev("/") ); + if( !KStandardDirs::makeDir( dir ) ) { + emitInfoMessage( i18n("Unable to create directory %1").arg(dir), K3bJob::ERROR ); + emitFinished(false); + return; + } + + // initialize + bool isOpen = true; + if( d->encoder ) { + if( isOpen = d->encoder->openFile( d->fileType, filename, m_doc->length() ) ) { + // here we use cd Title and Artist + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_ARTIST, m_cddbEntry.cdArtist ); + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_TITLE, m_cddbEntry.cdTitle ); + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_COMMENT, m_cddbEntry.cdExtInfo ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_ARTIST, m_cddbEntry.cdArtist ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_TITLE, m_cddbEntry.cdTitle ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_COMMENT, m_cddbEntry.cdExtInfo ); + d->encoder->setMetaData( K3bAudioEncoder::META_YEAR, QString::number(m_cddbEntry.year) ); + d->encoder->setMetaData( K3bAudioEncoder::META_GENRE, m_cddbEntry.genre ); + } + else + emitInfoMessage( d->encoder->lastErrorString(), K3bJob::ERROR ); + } + else { + isOpen = d->waveFileWriter->open( filename ); + } + + if( !isOpen ) { + emitInfoMessage( i18n("Unable to open '%1' for writing.").arg(filename), K3bJob::ERROR ); + emitFinished(false); + return; + } + + emitInfoMessage( i18n("Converting to single file '%1'.").arg(filename), K3bJob::INFO ); + } + + bool success = true; + K3bAudioTrack* track = m_doc->firstTrack(); + unsigned int i = 0; + while( track ) { + d->currentTrackIndex = i; + if( !convertTrack( track, m_singleFile ? m_tracks[0].second : m_tracks[i].second ) ) { + success = false; + break; + } + + emitInfoMessage( i18n("Successfully converted track %1.").arg(i+1), K3bJob::INFO ); + + track = track->next(); + ++i; + } + + if( m_singleFile ) { + if( d->encoder ) + d->encoder->closeFile(); + else + d->waveFileWriter->close(); + } + + if( !d->canceled && success && m_writePlaylist ) { + success = success && writePlaylist(); + } + + if( !d->canceled && success && m_writeCueFile && m_singleFile ) { + success = success && writeCueFile(); + } + + if( d->canceled ) { + if( d->currentTrackIndex >= 0 && d->currentTrackIndex < (int)m_tracks.count() ) { + if( QFile::exists( m_tracks[d->currentTrackIndex].second ) ) { + QFile::remove( m_tracks[d->currentTrackIndex].second ); + emitInfoMessage( i18n("Removed partial file '%1'.").arg(m_tracks[d->currentTrackIndex].second), K3bJob::INFO ); + } + } + + emitCanceled(); + emitFinished(false); + } + else + emitFinished(success); +} + + +bool K3bAudioProjectConvertingThread::convertTrack( K3bAudioTrack* track, const QString& filename ) +{ + QString dir = filename.left( filename.findRev("/") ); + if( !KStandardDirs::makeDir( dir ) ) { + emitInfoMessage( i18n("Unable to create directory %1").arg(dir), K3bJob::ERROR ); + return false; + } + + // initialize + bool isOpen = true; + if( !m_singleFile ) { + if( d->encoder ) { + if( isOpen = d->encoder->openFile( d->fileType, + filename, + track->length() ) ) { + + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_ARTIST, m_cddbEntry.artists[d->currentTrackIndex] ); + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_TITLE, m_cddbEntry.titles[d->currentTrackIndex] ); + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_COMMENT, m_cddbEntry.extInfos[d->currentTrackIndex] ); + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_NUMBER, QString::number(d->currentTrackIndex+1).rightJustify( 2, '0' ) ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_ARTIST, m_cddbEntry.cdArtist ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_TITLE, m_cddbEntry.cdTitle ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_COMMENT, m_cddbEntry.cdExtInfo ); + d->encoder->setMetaData( K3bAudioEncoder::META_YEAR, QString::number(m_cddbEntry.year) ); + d->encoder->setMetaData( K3bAudioEncoder::META_GENRE, m_cddbEntry.genre ); + } + else + emitInfoMessage( d->encoder->lastErrorString(), K3bJob::ERROR ); + } + else { + isOpen = d->waveFileWriter->open( filename ); + } + + if( !isOpen ) { + emitInfoMessage( i18n("Unable to open '%1' for writing.").arg(filename), K3bJob::ERROR ); + return false; + } + } + + + if( !m_cddbEntry.artists[d->currentTrackIndex].isEmpty() && + !m_cddbEntry.titles[d->currentTrackIndex].isEmpty() ) + emitNewSubTask( i18n("Converting track %1 (%2 - %3)") + .arg(d->currentTrackIndex+1) + .arg(m_cddbEntry.artists[d->currentTrackIndex]) + .arg(m_cddbEntry.titles[d->currentTrackIndex]) ); + else + emitNewSubTask( i18n("Converting track %1").arg(d->currentTrackIndex+1) ); + + + // do the conversion + // ---------------------- + + char buffer[10*1024]; + const int bufferLength = 10*1024; + int readLength = 0; + long long readFile = 0; + track->seek(0); + while( !d->canceled && ( readLength = track->read( buffer, bufferLength ) ) > 0 ) { + + if( d->encoder ) { + // the tracks produce big endian samples + // so we need to swap the bytes here + char b; + for( int i = 0; i < bufferLength-1; i+=2 ) { + b = buffer[i]; + buffer[i] = buffer[i+1]; + buffer[i+1] = b; + } + + if( d->encoder->encode( buffer, readLength ) < 0 ) { + kdDebug() << "(K3bAudioProjectConvertingThread) error while encoding." << endl; + emitInfoMessage( d->encoder->lastErrorString(), K3bJob::ERROR ); + emitInfoMessage( i18n("Error while encoding track %1.").arg(d->currentTrackIndex+1), K3bJob::ERROR ); + return false; + } + } + else { + d->waveFileWriter->write( buffer, + readLength, + K3bWaveFileWriter::BigEndian ); + } + + d->overallBytesRead += readLength; + readFile += readLength; + emitSubPercent( 100*readFile/track->size() ); + emitPercent( 100*d->overallBytesRead/d->overallBytesToRead ); + } + + if( !m_singleFile ) { + if( d->encoder ) + d->encoder->closeFile(); + else + d->waveFileWriter->close(); + } + + return ( readLength == 0 ); +} + + +void K3bAudioProjectConvertingThread::cancel() +{ + d->canceled = true; +} + + +bool K3bAudioProjectConvertingThread::writePlaylist() +{ + // this is an absolut path so there is always a "/" + QString playlistDir = m_playlistFilename.left( m_playlistFilename.findRev( "/" ) ); + + if( !KStandardDirs::makeDir( playlistDir ) ) { + emitInfoMessage( i18n("Unable to create directory %1").arg(playlistDir), K3bJob::ERROR ); + return false; + } + + emitInfoMessage( i18n("Writing playlist to %1.").arg( m_playlistFilename ), K3bJob::INFO ); + + QFile f( m_playlistFilename ); + if( f.open( IO_WriteOnly ) ) { + QTextStream t( &f ); + + // format descriptor + t << "#EXTM3U" << endl; + + // now write the entries (or the entry if m_singleFile) + if( m_singleFile ) { + // extra info + t << "#EXTINF:" << m_doc->length().lba() << ","; + if( !m_cddbEntry.cdArtist.isEmpty() && !m_cddbEntry.cdTitle.isEmpty() ) + t << m_cddbEntry.cdArtist << " - " << m_cddbEntry.cdTitle << endl; + else + t << m_tracks[0].second.mid(m_tracks[0].second.findRev("/") + 1, + m_tracks[0].second.length() - m_tracks[0].second.findRev("/") - 5) + << endl; // filename without extension + + // filename + if( m_relativePathInPlaylist ) + t << findRelativePath( m_tracks[0].second, playlistDir ) + << endl; + else + t << m_tracks[0].second << endl; + } + else { + for( unsigned int i = 0; i < m_tracks.count(); ++i ) { + int trackIndex = m_tracks[i].first-1; + + // extra info + t << "#EXTINF:" << m_doc->length().totalFrames()/75 << ","; + + if( !m_cddbEntry.artists[trackIndex].isEmpty() && !m_cddbEntry.titles[trackIndex].isEmpty() ) + t << m_cddbEntry.artists[trackIndex] << " - " << m_cddbEntry.titles[trackIndex] << endl; + else + t << m_tracks[i].second.mid(m_tracks[i].second.findRev("/") + 1, + m_tracks[i].second.length() + - m_tracks[i].second.findRev("/") - 5) + << endl; // filename without extension + + // filename + if( m_relativePathInPlaylist ) + t << findRelativePath( m_tracks[i].second, playlistDir ) + << endl; + else + t << m_tracks[i].second << endl; + } + } + + return ( t.device()->status() == IO_Ok ); + } + else { + emitInfoMessage( i18n("Unable to open '%1' for writing.").arg(m_playlistFilename), K3bJob::ERROR ); + kdDebug() << "(K3bAudioProjectConvertingThread) could not open file " << m_playlistFilename << " for writing." << endl; + return false; + } +} + + +bool K3bAudioProjectConvertingThread::writeCueFile() +{ + K3bCueFileWriter cueWriter; + + // create a new toc and cd-text + K3bDevice::Toc toc; + K3bDevice::CdText text; + text.setPerformer( m_cddbEntry.cdArtist ); + text.setTitle( m_cddbEntry.cdTitle ); + text.reserve( m_tracks.count() ); + K3b::Msf currentSector; + K3bAudioTrack* track = m_doc->firstTrack(); + int trackNum = 1; + while( track ) { + + K3bDevice::Track newTrack( currentSector, (currentSector+=track->length()) - 1, K3bDevice::Track::AUDIO ); + toc.append( newTrack ); + + K3bDevice::TrackCdText trackText; + trackText.setPerformer( m_cddbEntry.artists[trackNum-1] ); + trackText.setTitle( m_cddbEntry.titles[trackNum-1] ); + text.append( trackText ); + + track = track->next(); + ++trackNum; + } + + cueWriter.setData( toc ); + cueWriter.setCdText( text ); + + + // we always use a relative filename here + QString imageFile = m_tracks[0].second.section( '/', -1 ); + cueWriter.setImage( imageFile, ( d->fileType.isEmpty() ? QString("WAVE") : d->fileType ) ); + + // use the same base name as the image file + QString cueFile = m_tracks[0].second; + cueFile.truncate( cueFile.findRev(".") ); + cueFile += ".cue"; + + emitInfoMessage( i18n("Writing cue file to %1.").arg(cueFile), K3bJob::INFO ); + + return cueWriter.save( cueFile ); +} + + +QString K3bAudioProjectConvertingThread::findRelativePath( const QString& absPath, const QString& baseDir ) +{ + QString baseDir_ = K3b::prepareDir( K3b::fixupPath(baseDir) ); + QString path = K3b::fixupPath( absPath ); + + // both paths have an equal beginning. That's just how it's configured by K3b + int pos = baseDir_.find( "/" ); + int oldPos = pos; + while( pos != -1 && path.left( pos+1 ) == baseDir_.left( pos+1 ) ) { + oldPos = pos; + pos = baseDir_.find( "/", pos+1 ); + } + + // now the paths are equal up to oldPos, so that's how "deep" we go + path = path.mid( oldPos+1 ); + baseDir_ = baseDir_.mid( oldPos+1 ); + int numberOfDirs = baseDir_.contains( '/' ); + for( int i = 0; i < numberOfDirs; ++i ) + path.prepend( "../" ); + + return path; +} + + +QString K3bAudioProjectConvertingThread::jobDescription() const +{ + if( m_cddbEntry.cdTitle.isEmpty() ) + return i18n("Converting Audio Tracks"); + else + return i18n("Converting Audio Tracks From '%1'").arg(m_cddbEntry.cdTitle); +} + +QString K3bAudioProjectConvertingThread::jobDetails() const +{ + if( d->encoder ) + return i18n("1 track (encoding to %1)", + "%n tracks (encoding to %1)", + m_tracks.count() ).arg(d->encoder->fileTypeComment(d->fileType)); + else + return i18n("1 track", "%n tracks", m_doc->numOfTracks() ); +} + diff --git a/src/rip/k3baudioprojectconvertingthread.h b/src/rip/k3baudioprojectconvertingthread.h new file mode 100644 index 0000000..aeda217 --- /dev/null +++ b/src/rip/k3baudioprojectconvertingthread.h @@ -0,0 +1,101 @@ +/* + * + * $Id: k3baudioprojectconvertingthread.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef K3B_AUDIO_PROJECT_CONVERTING_THREAD_H +#define K3B_AUDIO_PROJECT_CONVERTING_THREAD_H + +#include +#include +#include +#include + +#include + + +class K3bAudioEncoder; +class K3bAudioDoc; +class K3bAudioTrack; + + +class K3bAudioProjectConvertingThread : public K3bThread +{ + public: + K3bAudioProjectConvertingThread( K3bAudioDoc* ); + ~K3bAudioProjectConvertingThread(); + + QString jobDescription() const; + QString jobDetails() const; + + void setSingleFile( bool b ) { m_singleFile = b; } + + void setCddbEntry( const K3bCddbResultEntry& e ) { m_cddbEntry = e; } + + // if 0 (default) wave files are created + void setEncoder( K3bAudioEncoder* f ); + + /** + * Used for encoders that support multiple formats + */ + void setFileType( const QString& ); + + /** + * 1 is the first track + */ + void setTracksToRip( const QValueVector >& t ) { m_tracks = t; } + + void setWritePlaylist( bool b ) { m_writePlaylist = b; } + void setPlaylistFilename( const QString& s ) { m_playlistFilename = s; } + void setUseRelativePathInPlaylist( bool b ) { m_relativePathInPlaylist = b; } + void setWriteCueFile( bool b ) { m_writeCueFile = b; } + + /** + * \reimplemented from K3bThread + */ + void init(); + + void cancel(); + + private: + /** reimplemented from QThread. Does the work */ + void run(); + + bool convertTrack( K3bAudioTrack*, const QString& filename ); + bool writePlaylist(); + bool writeCueFile(); + + /** + * Finds a relative path from baseDir to absPath + */ + QString findRelativePath( const QString& absPath, const QString& baseDir ); + + K3bCddbResultEntry m_cddbEntry; + + bool m_singleFile; + bool m_writePlaylist; + bool m_relativePathInPlaylist; + QString m_playlistFilename; + + bool m_writeCueFile; + + QValueVector > m_tracks; + + K3bAudioDoc* m_doc; + + class Private; + Private* d; +}; + +#endif diff --git a/src/rip/k3baudioripjob.cpp b/src/rip/k3baudioripjob.cpp new file mode 100644 index 0000000..ef520e1 --- /dev/null +++ b/src/rip/k3baudioripjob.cpp @@ -0,0 +1,77 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3baudioripjob.h" +#include "k3baudioripthread.h" + +#include +#include + +#include +#include + + +K3bAudioRipJob::K3bAudioRipJob( K3bJobHandler* hdl, QObject* parent ) + : K3bJob( hdl, parent ) +{ + m_thread = new K3bAudioRipThread(); + m_threadJob = new K3bThreadJob( m_thread, this, this ); + connectSubJob( m_threadJob, + SLOT(slotRippingFinished(bool)), + SIGNAL(newTask(const QString&)), + SIGNAL(newSubTask(const QString&)), + SIGNAL(percent(int)), + SIGNAL(subPercent(int)) ); +} + + +K3bAudioRipJob::~K3bAudioRipJob() +{ +} + + +QString K3bAudioRipJob::jobDescription() const +{ + return m_thread->jobDescription(); +} + + +QString K3bAudioRipJob::jobDetails() const +{ + return m_thread->jobDetails(); +} + + +void K3bAudioRipJob::start() +{ + jobStarted(); + k3bcore->blockDevice( m_thread->m_device ); + m_threadJob->start(); +} + + +void K3bAudioRipJob::cancel() +{ + m_threadJob->cancel(); +} + + +void K3bAudioRipJob::slotRippingFinished( bool success ) +{ + k3bcore->unblockDevice( m_thread->m_device ); + jobFinished( success ); +} + +#include "k3baudioripjob.moc" diff --git a/src/rip/k3baudioripjob.h b/src/rip/k3baudioripjob.h new file mode 100644 index 0000000..adf47f3 --- /dev/null +++ b/src/rip/k3baudioripjob.h @@ -0,0 +1,71 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_AUDIORIP_JOB_H_ +#define _K3B_AUDIORIP_JOB_H_ + +#include + +#include "k3baudioripthread.h" +#include +#include +#include + +#include + +class K3bInterferingSystemsHandler; +class K3bThreadJob; + + +class K3bAudioRipJob : public K3bJob +{ + Q_OBJECT + + public: + K3bAudioRipJob( K3bJobHandler* hdl, QObject* parent ); + ~K3bAudioRipJob(); + + QString jobDescription() const; + QString jobDetails() const; + + public slots: + void start(); + void cancel(); + + void setDevice( K3bDevice::Device* dev ) { m_thread->setDevice( dev ); } + void setCddbEntry( const K3bCddbResultEntry& entry ) { m_thread->setCddbEntry( entry ); } + void setTracksToRip( const QValueVector >& tracksToRip ) { m_thread->setTracksToRip( tracksToRip ); } + void setParanoiaMode( int mode ) { m_thread->setParanoiaMode( mode ); } + void setMaxRetries( int retries ) { m_thread->setMaxRetries( retries ); } + void setNeverSkip( bool neverSkip ) { m_thread->setNeverSkip( neverSkip ); } + void setSingleFile( bool singleFile ) { m_thread->setSingleFile( singleFile ); } + void setWriteCueFile( bool cue ) { m_thread->setWriteCueFile( cue ); } + void setEncoder( K3bAudioEncoder* encoder ) { m_thread->setEncoder( encoder ); } + void setWritePlaylist( bool playlist ) { m_thread->setWritePlaylist( playlist ); } + void setPlaylistFilename( const QString& filename ) { m_thread->setPlaylistFilename( filename ); } + void setUseRelativePathInPlaylist( bool relative ) { m_thread->setUseRelativePathInPlaylist( relative ); } + void setUseIndex0( bool index0 ) { m_thread->setUseIndex0( index0 ); } + void setFileType( const QString& filetype ) { m_thread->setFileType( filetype ); } + + private slots: + void slotRippingFinished( bool ); + + private: + K3bInterferingSystemsHandler* m_interferingSystemsHandler; + K3bThreadJob* m_threadJob; + K3bAudioRipThread* m_thread; +}; + +#endif diff --git a/src/rip/k3baudiorippingdialog.cpp b/src/rip/k3baudiorippingdialog.cpp new file mode 100644 index 0000000..693230f --- /dev/null +++ b/src/rip/k3baudiorippingdialog.cpp @@ -0,0 +1,470 @@ +/* + * + * $Id: k3baudiorippingdialog.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#include "k3baudiorippingdialog.h" +#include "k3baudioripjob.h" +#include "k3bpatternparser.h" +#include "k3bcddbpatternwidget.h" +#include "k3baudioconvertingoptionwidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class K3bAudioRippingDialog::Private +{ +public: + Private() { + } + + QValueVector filenames; + QString playlistFilename; + K3bFileSystemInfo fsInfo; +}; + + +K3bAudioRippingDialog::K3bAudioRippingDialog(const K3bDevice::Toc& toc, + K3bDevice::Device* device, + const K3bCddbResultEntry& entry, + const QValueList& tracks, + QWidget *parent, const char *name ) + : K3bInteractionDialog( parent, name, + QString::null, + QString::null, + START_BUTTON|CANCEL_BUTTON, + START_BUTTON, + "Audio Ripping" ), // config group + m_toc( toc ), + m_device( device ), + m_cddbEntry( entry ), + m_trackNumbers( tracks ) +{ + d = new Private(); + + setupGui(); + setupContextHelp(); + + K3b::Msf length; + for( QValueList::const_iterator it = m_trackNumbers.begin(); + it != m_trackNumbers.end(); ++it ) { + length += m_toc[*it-1].length(); + } + setTitle( i18n("CD Ripping"), + i18n("1 track (%1)", "%n tracks (%1)", + m_trackNumbers.count()).arg(length.toString()) ); +} + + +K3bAudioRippingDialog::~K3bAudioRippingDialog() +{ + delete d; +} + + +void K3bAudioRippingDialog::setupGui() +{ + QWidget *frame = mainWidget(); + QGridLayout* Form1Layout = new QGridLayout( frame ); + Form1Layout->setSpacing( KDialog::spacingHint() ); + Form1Layout->setMargin( 0 ); + + m_viewTracks = new KListView( frame, "m_viewTracks" ); + m_viewTracks->addColumn(i18n( "Filename") ); + m_viewTracks->addColumn(i18n( "Length") ); + m_viewTracks->addColumn(i18n( "File Size") ); + m_viewTracks->addColumn(i18n( "Type") ); + m_viewTracks->setSorting(-1); + m_viewTracks->setAllColumnsShowFocus(true); + m_viewTracks->setFullWidth(true); + + QTabWidget* mainTab = new QTabWidget( frame ); + + m_optionWidget = new K3bAudioConvertingOptionWidget( mainTab ); + mainTab->addTab( m_optionWidget, i18n("Settings") ); + + + // setup filename pattern page + // ------------------------------------------------------------------------------------------- + m_patternWidget = new K3bCddbPatternWidget( mainTab ); + mainTab->addTab( m_patternWidget, i18n("File Naming") ); + connect( m_patternWidget, SIGNAL(changed()), this, SLOT(refresh()) ); + + + // setup advanced page + // ------------------------------------------------------------------------------------------- + QWidget* advancedPage = new QWidget( mainTab ); + QGridLayout* advancedPageLayout = new QGridLayout( advancedPage ); + advancedPageLayout->setMargin( marginHint() ); + advancedPageLayout->setSpacing( spacingHint() ); + mainTab->addTab( advancedPage, i18n("Advanced") ); + + m_comboParanoiaMode = K3bStdGuiItems::paranoiaModeComboBox( advancedPage ); + m_spinRetries = new QSpinBox( advancedPage ); + m_checkIgnoreReadErrors = new QCheckBox( i18n("Ignore read errors"), advancedPage ); + m_checkUseIndex0 = new QCheckBox( i18n("Don't read pregaps"), advancedPage ); + + advancedPageLayout->addWidget( new QLabel( i18n("Paranoia mode:"), advancedPage ), 0, 0 ); + advancedPageLayout->addWidget( m_comboParanoiaMode, 0, 1 ); + advancedPageLayout->addWidget( new QLabel( i18n("Read retries:"), advancedPage ), 1, 0 ); + advancedPageLayout->addWidget( m_spinRetries, 1, 1 ); + advancedPageLayout->addMultiCellWidget( m_checkIgnoreReadErrors, 2, 2, 0, 1 ); + advancedPageLayout->addMultiCellWidget( m_checkUseIndex0, 3, 3, 0, 1 ); + advancedPageLayout->setRowStretch( 4, 1 ); + advancedPageLayout->setColStretch( 2, 1 ); + + // ------------------------------------------------------------------------------------------- + + + Form1Layout->addWidget( m_viewTracks, 0, 0 ); + Form1Layout->addWidget( mainTab, 1, 0 ); + Form1Layout->setRowStretch( 0, 1 ); + + setStartButtonText( i18n( "Start Ripping" ), i18n( "Starts copying the selected tracks") ); + + connect( m_checkUseIndex0, SIGNAL(toggled(bool)), this, SLOT(refresh()) ); + connect( m_optionWidget, SIGNAL(changed()), this, SLOT(refresh()) ); +} + + +void K3bAudioRippingDialog::setupContextHelp() +{ + QToolTip::add( m_spinRetries, i18n("Maximal number of read retries") ); + QWhatsThis::add( m_spinRetries, i18n("

This specifies the maximum number of retries to " + "read a sector of audio data from the cd. After that " + "K3b will either skip the sector if the Ignore Read Errors " + "option is enabled or stop the process.") ); + QToolTip::add( m_checkUseIndex0, i18n("Do not read the pregaps at the end of every track") ); + QWhatsThis::add( m_checkUseIndex0, i18n("

If this option is checked K3b will not rip the audio " + "data in the pregaps. Most audio tracks contain an empty " + "pregap which does not belong to the track itself.

" + "

Although the default behaviour of nearly all ripping " + "software is to include the pregaps for most CDs it makes more " + "sense to ignore them. When creating a K3b audio project you " + "will regenerate these pregaps anyway.

") ); +} + + +void K3bAudioRippingDialog::init() +{ + refresh(); +} + + +void K3bAudioRippingDialog::slotStartClicked() +{ + // check if all filenames differ + if( d->filenames.count() > 1 ) { + bool differ = true; + // the most stupid version to compare but most cds have about 12 tracks + // that's a size where algorithms do not need any optimization! ;) + for( unsigned int i = 0; i < d->filenames.count(); ++i ) { + for( unsigned int j = i+1; j < d->filenames.count(); ++j ) + if( d->filenames[i] == d->filenames[j] ) { + differ = false; + break; + } + } + + if( !differ ) { + KMessageBox::sorry( this, i18n("Please check the naming pattern. All filenames need to be unique.") ); + return; + } + } + + // check if we need to overwrite some files... + QListViewItemIterator it( m_viewTracks ); + QStringList filesToOverwrite; + for( unsigned int i = 0; i < d->filenames.count(); ++i ) { + if( QFile::exists( d->filenames[i] ) ) + filesToOverwrite.append( d->filenames[i] ); + } + + if( m_optionWidget->createPlaylist() && QFile::exists( d->playlistFilename ) ) + filesToOverwrite.append( d->playlistFilename ); + + if( !filesToOverwrite.isEmpty() ) + if( KMessageBox::questionYesNoList( this, + i18n("Do you want to overwrite these files?"), + filesToOverwrite, + i18n("Files Exist"), i18n("Overwrite"), KStdGuiItem::cancel() ) == KMessageBox::No ) + return; + + + // prepare list of tracks to rip + QValueVector > tracksToRip; + unsigned int i = 0; + for( QValueList::const_iterator trackIt = m_trackNumbers.begin(); + trackIt != m_trackNumbers.end(); ++trackIt ) { + tracksToRip.append( qMakePair( *trackIt, d->filenames[(m_optionWidget->createSingleFile() ? 0 : i)] ) ); + ++i; + } + + K3bJobProgressDialog ripDialog( parentWidget(), "Ripping" ); + + K3bAudioEncoder* encoder = m_optionWidget->encoder(); + K3bAudioRipJob* job = new K3bAudioRipJob( &ripDialog, this ); + job->setDevice( m_device ); + job->setCddbEntry( m_cddbEntry ); + job->setTracksToRip( tracksToRip ); + job->setParanoiaMode( m_comboParanoiaMode->currentText().toInt() ); + job->setMaxRetries( m_spinRetries->value() ); + job->setNeverSkip( !m_checkIgnoreReadErrors->isChecked() ); + job->setSingleFile( m_optionWidget->createSingleFile() ); + job->setWriteCueFile( m_optionWidget->createCueFile() ); + job->setEncoder( encoder ); + job->setWritePlaylist( m_optionWidget->createPlaylist() ); + job->setPlaylistFilename( d->playlistFilename ); + job->setUseRelativePathInPlaylist( m_optionWidget->playlistRelativePath() ); + job->setUseIndex0( m_checkUseIndex0->isChecked() ); + if( encoder ) + job->setFileType( m_optionWidget->extension() ); + + hide(); + ripDialog.startJob(job); + + kdDebug() << "(K3bAudioRippingDialog) deleting ripjob." << endl; + delete job; + + close(); +} + + +void K3bAudioRippingDialog::refresh() +{ + m_viewTracks->clear(); + d->filenames.clear(); + + QString baseDir = K3b::prepareDir( m_optionWidget->baseDir() ); + d->fsInfo.setPath( baseDir ); + + KIO::filesize_t overallSize = 0; + + if( m_optionWidget->createSingleFile() ) { + long length = 0; + for( QValueList::const_iterator it = m_trackNumbers.begin(); + it != m_trackNumbers.end(); ++it ) { + length += ( m_checkUseIndex0->isChecked() + ? m_toc[*it-1].realAudioLength().lba() + : m_toc[*it-1].length().lba() ); + } + + QString filename; + QString extension; + long long fileSize = 0; + if( m_optionWidget->encoder() == 0 ) { + extension = "wav"; + fileSize = length * 2352 + 44; + } + else { + extension = m_optionWidget->extension(); + fileSize = m_optionWidget->encoder()->fileSize( extension, length ); + } + + if( fileSize > 0 ) + overallSize = fileSize; + + if( (int)m_cddbEntry.titles.count() >= 1 ) { + filename = K3bPatternParser::parsePattern( m_cddbEntry, 1, + m_patternWidget->filenamePattern(), + m_patternWidget->replaceBlanks(), + m_patternWidget->blankReplaceString() ); + } + else { + filename = i18n("Album"); + } + + filename = d->fsInfo.fixupPath( filename ); + + (void)new KListViewItem( m_viewTracks, + m_viewTracks->lastItem(), + filename + "." + extension, + K3b::Msf(length).toString(), + fileSize < 0 ? i18n("unknown") : KIO::convertSize( fileSize ), + i18n("Audio") ); + d->filenames.append( baseDir + "/" + filename + "." + extension ); + + if( m_optionWidget->createCueFile() ) + (void)new KListViewItem( m_viewTracks, + m_viewTracks->lastItem(), + filename + ".cue", + "-", + "-", + i18n("Cue-file") ); + } + else { + for( QValueList::const_iterator it = m_trackNumbers.begin(); + it != m_trackNumbers.end(); ++it ) { + int index = *it - 1; + + QString extension; + long long fileSize = 0; + K3b::Msf trackLength = ( m_checkUseIndex0->isChecked() + ? m_toc[index].realAudioLength() + : m_toc[index].length() ); + if( m_optionWidget->encoder() == 0 ) { + extension = "wav"; + fileSize = trackLength.audioBytes() + 44; + } + else { + extension = m_optionWidget->extension(); + fileSize = m_optionWidget->encoder()->fileSize( extension, trackLength ); + } + + if( fileSize > 0 ) + overallSize += fileSize; + + if( m_toc[index].type() == K3bTrack::DATA ) { + extension = ".iso"; + continue; // TODO: find out how to rip the iso data + } + + + QString filename; + + if( (int)m_cddbEntry.titles.count() >= *it ) { + filename = K3bPatternParser::parsePattern( m_cddbEntry, *it, + m_patternWidget->filenamePattern(), + m_patternWidget->replaceBlanks(), + m_patternWidget->blankReplaceString() ) + "." + extension; + } + else { + filename = i18n("Track%1").arg( QString::number( *it ).rightJustify( 2, '0' ) ) + "." + extension; + } + + filename = d->fsInfo.fixupPath( filename ); + + (void)new KListViewItem( m_viewTracks, + m_viewTracks->lastItem(), + filename, + trackLength.toString(), + fileSize < 0 ? i18n("unknown") : KIO::convertSize( fileSize ), + (m_toc[index].type() == K3bTrack::AUDIO ? i18n("Audio") : i18n("Data") ) ); + + d->filenames.append( baseDir + "/" + filename ); + } + } + + // create playlist item + if( m_optionWidget->createPlaylist() ) { + QString filename = K3bPatternParser::parsePattern( m_cddbEntry, 1, + m_patternWidget->playlistPattern(), + m_patternWidget->replaceBlanks(), + m_patternWidget->blankReplaceString() ) + ".m3u"; + + (void)new KListViewItem( m_viewTracks, + m_viewTracks->lastItem(), + filename, + "-", + "-", + i18n("Playlist") ); + + d->playlistFilename = d->fsInfo.fixupPath( baseDir + "/" + filename ); + } + + if( overallSize > 0 ) + m_optionWidget->setNeededSize( overallSize ); + else + m_optionWidget->setNeededSize( 0 ); +} + + +void K3bAudioRippingDialog::setStaticDir( const QString& path ) +{ + m_optionWidget->setBaseDir( path ); +} + + +void K3bAudioRippingDialog::loadK3bDefaults() +{ + m_comboParanoiaMode->setCurrentItem( 0 ); + m_spinRetries->setValue(5); + m_checkIgnoreReadErrors->setChecked( true ); + m_checkUseIndex0->setChecked( false ); + + m_optionWidget->loadDefaults(); + m_patternWidget->loadDefaults(); + + refresh(); +} + +void K3bAudioRippingDialog::loadUserDefaults( KConfigBase* c ) +{ + m_comboParanoiaMode->setCurrentItem( c->readNumEntry( "paranoia_mode", 0 ) ); + m_spinRetries->setValue( c->readNumEntry( "read_retries", 5 ) ); + m_checkIgnoreReadErrors->setChecked( !c->readBoolEntry( "never_skip", true ) ); + m_checkUseIndex0->setChecked( c->readBoolEntry( "use_index0", false ) ); + + m_optionWidget->loadConfig( c ); + m_patternWidget->loadConfig( c ); + + refresh(); +} + +void K3bAudioRippingDialog::saveUserDefaults( KConfigBase* c ) +{ + c->writeEntry( "paranoia_mode", m_comboParanoiaMode->currentText().toInt() ); + c->writeEntry( "read_retries", m_spinRetries->value() ); + c->writeEntry( "never_skip", !m_checkIgnoreReadErrors->isChecked() ); + c->writeEntry( "use_index0", m_checkUseIndex0->isChecked() ); + + m_optionWidget->saveConfig( c ); + m_patternWidget->saveConfig( c ); +} + + +#include "k3baudiorippingdialog.moc" diff --git a/src/rip/k3baudiorippingdialog.h b/src/rip/k3baudiorippingdialog.h new file mode 100644 index 0000000..83fe0dc --- /dev/null +++ b/src/rip/k3baudiorippingdialog.h @@ -0,0 +1,92 @@ +/* + * + * $Id: k3baudiorippingdialog.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef _K3B_AUDIO_RIPPING_DIALOG_H_ +#define _K3B_AUDIO_RIPPING_DIALOG_H_ + +#include + +#include + +#include + +namespace K3bDevice { + class Device; + class Toc; +} + + +class KListView; +class QCheckBox; +class QSpinBox; +class QComboBox; +class K3bCddbPatternWidget; +class QToolButton; +class K3bAudioConvertingOptionWidget; + + +/** + *@author Sebastian Trueg + */ +class K3bAudioRippingDialog : public K3bInteractionDialog +{ + Q_OBJECT + + public: + K3bAudioRippingDialog( const K3bDevice::Toc&, + K3bDevice::Device*, + const K3bCddbResultEntry&, + const QValueList&, + QWidget *parent = 0, const char *name = 0 ); + ~K3bAudioRippingDialog(); + + void setStaticDir( const QString& path ); + + public slots: + void refresh(); + void init(); + + private: + K3bDevice::Toc m_toc; + K3bDevice::Device* m_device; + K3bCddbResultEntry m_cddbEntry; + QValueList m_trackNumbers; + + KListView* m_viewTracks; + + QComboBox* m_comboParanoiaMode; + QSpinBox* m_spinRetries; + QCheckBox* m_checkIgnoreReadErrors; + QCheckBox* m_checkUseIndex0; + + K3bCddbPatternWidget* m_patternWidget; + K3bAudioConvertingOptionWidget* m_optionWidget; + + void setupGui(); + void setupContextHelp(); + + void loadK3bDefaults(); + void loadUserDefaults( KConfigBase* ); + void saveUserDefaults( KConfigBase* ); + + class Private; + Private* d; + + private slots: + void slotStartClicked(); +}; + +#endif diff --git a/src/rip/k3baudioripthread.cpp b/src/rip/k3baudioripthread.cpp new file mode 100644 index 0000000..8a5a6e9 --- /dev/null +++ b/src/rip/k3baudioripthread.cpp @@ -0,0 +1,602 @@ +/* + * + * $Id: k3baudioripthread.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#include "k3baudioripthread.h" +#include "k3bpatternparser.h" + +#include +#include +#include +#include +#include +#include +#include "k3bcuefilewriter.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + + + + +class K3bAudioRipThread::Private +{ +public: + Private() + : paranoiaRetries(5), + neverSkip(false), + encoder(0), + waveFileWriter(0), + paranoiaLib(0), + canceled(false) { + } + + // the index of the currently ripped track in m_tracks + int currentTrackIndex; + long overallSectorsRead; + long overallSectorsToRead; + + int paranoiaMode; + int paranoiaRetries; + int neverSkip; + + K3bAudioEncoder* encoder; + K3bWaveFileWriter* waveFileWriter; + + K3bCdparanoiaLib* paranoiaLib; + + bool canceled; + + K3bDevice::Toc toc; + + QString fileType; +}; + + +K3bAudioRipThread::K3bAudioRipThread() + : QObject(), + K3bThread(), + m_device(0), + m_useIndex0(false) +{ + d = new Private(); +} + + +K3bAudioRipThread::~K3bAudioRipThread() +{ + delete d->waveFileWriter; + delete d->paranoiaLib; + delete d; +} + + +void K3bAudioRipThread::setFileType( const QString& t ) +{ + d->fileType = t; +} + + +void K3bAudioRipThread::setParanoiaMode( int mode ) +{ + d->paranoiaMode = mode; +} + + +void K3bAudioRipThread::setMaxRetries( int r ) +{ + d->paranoiaRetries = r; +} + + +void K3bAudioRipThread::setNeverSkip( bool b ) +{ + d->neverSkip = b; +} + + +void K3bAudioRipThread::setEncoder( K3bAudioEncoder* f ) +{ + d->encoder = f; +} + + +void K3bAudioRipThread::run() +{ + emitStarted(); + emitNewTask( i18n("Extracting Digital Audio") ); + + if( !d->paranoiaLib ) { + d->paranoiaLib = K3bCdparanoiaLib::create(); + } + + if( !d->paranoiaLib ) { + emitInfoMessage( i18n("Could not load libcdparanoia."), K3bJob::ERROR ); + emitFinished(false); + return; + } + + // try to open the device + if( !m_device ) { + emitFinished(false); + return; + } + + m_device->block(true); + + emitInfoMessage( i18n("Reading CD table of contents."), K3bJob::INFO ); + d->toc = m_device->readToc(); + + if( !d->paranoiaLib->initParanoia( m_device, d->toc ) ) { + emitInfoMessage( i18n("Could not open device %1").arg(m_device->blockDeviceName()), + K3bJob::ERROR ); + m_device->block(false); + + // check if we have write access to the generic device + if( m_device->interfaceType() == K3bDevice::SCSI && + !m_device->genericDevice().isEmpty() && + !QFileInfo( m_device->genericDevice() ).isWritable() ) + emitInfoMessage( i18n("You need write access to %1").arg( m_device->genericDevice() ), K3bJob::ERROR ); + + emitFinished(false); + return; + } + d->paranoiaLib->setParanoiaMode( d->paranoiaMode ); + d->paranoiaLib->setNeverSkip( d->neverSkip ); + d->paranoiaLib->setMaxRetries( d->paranoiaRetries ); + + + if( !d->encoder ) + if( !d->waveFileWriter ) { + d->waveFileWriter = new K3bWaveFileWriter(); + } + + + if( m_useIndex0 ) { + emitNewSubTask( i18n("Searching index 0 for all tracks") ); + m_device->indexScan( d->toc ); + } + + + d->canceled = false; + d->overallSectorsRead = 0; + d->overallSectorsToRead = 0; + for( unsigned int i = 0; i < m_tracks.count(); ++i ) { + if( m_useIndex0 ) + d->overallSectorsToRead += d->toc[m_tracks[i].first-1].realAudioLength().lba(); + else + d->overallSectorsToRead += d->toc[m_tracks[i].first-1].length().lba(); + } + + + if( m_singleFile ) { + QString& filename = m_tracks[0].second; + + QString dir = filename.left( filename.findRev("/") ); + if( !KStandardDirs::makeDir( dir, 0777 ) ) { + d->paranoiaLib->close(); + emitInfoMessage( i18n("Unable to create directory %1").arg(dir), K3bJob::ERROR ); + m_device->block(false); + emitFinished(false); + return; + } + + // initialize + bool isOpen = true; + if( d->encoder ) { + if( isOpen = d->encoder->openFile( d->fileType, filename, d->overallSectorsToRead ) ) { + // here we use cd Title and Artist + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_ARTIST, m_cddbEntry.cdArtist ); + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_TITLE, m_cddbEntry.cdTitle ); + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_COMMENT, m_cddbEntry.cdExtInfo ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_ARTIST, m_cddbEntry.cdArtist ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_TITLE, m_cddbEntry.cdTitle ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_COMMENT, m_cddbEntry.cdExtInfo ); + d->encoder->setMetaData( K3bAudioEncoder::META_YEAR, QString::number(m_cddbEntry.year) ); + d->encoder->setMetaData( K3bAudioEncoder::META_GENRE, m_cddbEntry.genre ); + } + else + emitInfoMessage( d->encoder->lastErrorString(), K3bJob::ERROR ); + } + else { + isOpen = d->waveFileWriter->open( filename ); + } + + if( !isOpen ) { + d->paranoiaLib->close(); + emitInfoMessage( i18n("Unable to open '%1' for writing.").arg(filename), K3bJob::ERROR ); + m_device->block(false); + emitFinished(false); + return; + } + + emitInfoMessage( i18n("Ripping to single file '%1'.").arg(filename), K3bJob::INFO ); + } + + emitInfoMessage( i18n("Starting digital audio extraction (ripping)."), K3bJob::INFO ); + + bool success = true; + for( unsigned int i = 0; i < m_tracks.count(); ++i ) { + d->currentTrackIndex = i; + if( !ripTrack( m_tracks[i].first, m_singleFile ? m_tracks[0].second : m_tracks[i].second ) ) { + success = false; + break; + } + } + + if( m_singleFile ) { + if( d->encoder ) + d->encoder->closeFile(); + else + d->waveFileWriter->close(); + + if( success && !d->canceled ) { + QString& filename = m_tracks[0].second; + emitInfoMessage( i18n("Successfully ripped to %2.").arg(filename), K3bJob::INFO ); + } + } + + if( !d->canceled && m_writePlaylist ) { + success = success && writePlaylist(); + } + + if( !d->canceled && m_writeCueFile && m_singleFile ) { + if( !m_useIndex0 ) { + emitNewSubTask( i18n("Searching index 0 for all tracks") ); + m_device->indexScan( d->toc ); + } + success = success && writeCueFile(); + } + + d->paranoiaLib->close(); + m_device->block(false); + + if( d->canceled ) { + emitCanceled(); + emitFinished(false); + } + else { + if( k3bcore->globalSettings()->ejectMedia() ) + m_device->eject(); + + emitFinished(success); + } +} + + +bool K3bAudioRipThread::ripTrack( int track, const QString& filename ) +{ + const K3bTrack& tt = d->toc[track-1]; + + long endSec = ( (m_useIndex0 && tt.index0() > 0) + ? tt.firstSector().lba() + tt.index0().lba() - 1 + : tt.lastSector().lba() ); + + if( d->paranoiaLib->initReading( tt.firstSector().lba(), endSec ) ) { + + long trackSectorsRead = 0; + + QString dir = filename.left( filename.findRev("/") ); + if( !KStandardDirs::makeDir( dir, 0777 ) ) { + emitInfoMessage( i18n("Unable to create directory %1").arg(dir), K3bJob::ERROR ); + return false; + } + + // initialize + bool isOpen = true; + if( !m_singleFile ) { + if( d->encoder ) { + if( isOpen = d->encoder->openFile( d->fileType, + filename, + m_useIndex0 ? d->toc[track-1].realAudioLength() : d->toc[track-1].length() ) ) { + + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_ARTIST, m_cddbEntry.artists[track-1] ); + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_TITLE, m_cddbEntry.titles[track-1] ); + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_COMMENT, m_cddbEntry.extInfos[track-1] ); + d->encoder->setMetaData( K3bAudioEncoder::META_TRACK_NUMBER, QString::number(track).rightJustify( 2, '0' ) ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_ARTIST, m_cddbEntry.cdArtist ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_TITLE, m_cddbEntry.cdTitle ); + d->encoder->setMetaData( K3bAudioEncoder::META_ALBUM_COMMENT, m_cddbEntry.cdExtInfo ); + d->encoder->setMetaData( K3bAudioEncoder::META_YEAR, QString::number(m_cddbEntry.year) ); + d->encoder->setMetaData( K3bAudioEncoder::META_GENRE, m_cddbEntry.genre ); + } + else + emitInfoMessage( d->encoder->lastErrorString(), K3bJob::ERROR ); + } + else { + isOpen = d->waveFileWriter->open( filename ); + } + + if( !isOpen ) { + emitInfoMessage( i18n("Unable to open '%1' for writing.").arg(filename), K3bJob::ERROR ); + return false; + } + } + + if( !m_cddbEntry.artists[track-1].isEmpty() && + !m_cddbEntry.titles[track-1].isEmpty() ) + emitNewSubTask( i18n("Ripping track %1 (%2 - %3)").arg(track).arg(m_cddbEntry.artists[track-1]).arg(m_cddbEntry.titles[track-1]) ); + else + emitNewSubTask( i18n("Ripping track %1").arg(track) ); + + int status; + while( 1 ) { + if( d->canceled ) { + cleanupAfterCancellation(); + return false; + } + + char* buf = d->paranoiaLib->read( &status ); + if( status == K3bCdparanoiaLib::S_OK ) { + if( buf == 0 ) { + if( m_singleFile ) + emitInfoMessage( i18n("Successfully ripped track %1.").arg(track), K3bJob::INFO ); + else + emitInfoMessage( i18n("Successfully ripped track %1 to %2.").arg(track).arg(filename), K3bJob::INFO ); + + if( !m_singleFile ) { + if( d->encoder ) + d->encoder->closeFile(); + else + d->waveFileWriter->close(); + } + + return true; + } + else { + if( d->encoder ) { + if( d->encoder->encode( buf, + CD_FRAMESIZE_RAW ) < 0 ) { + kdDebug() << "(K3bAudioRipThread) error while encoding." << endl; + emitInfoMessage( d->encoder->lastErrorString(), K3bJob::ERROR ); + emitInfoMessage( i18n("Error while encoding track %1.").arg(track), K3bJob::ERROR ); + return false; + } + } + else + d->waveFileWriter->write( buf, + CD_FRAMESIZE_RAW, + K3bWaveFileWriter::LittleEndian ); + + trackSectorsRead++; + d->overallSectorsRead++; + emitSubPercent( 100*trackSectorsRead/d->toc[track-1].length().lba() ); + emitPercent( 100*d->overallSectorsRead/d->overallSectorsToRead ); + } + } + else { + emitInfoMessage( i18n("Unrecoverable error while ripping track %1.").arg(track), K3bJob::ERROR ); + return false; + } + } + return true; + } + else { + emitInfoMessage( i18n("Error while initializing audio ripping."), K3bJob::ERROR ); + return false; + } +} + + +void K3bAudioRipThread::cancel() +{ + d->canceled = true; + + // what if paranoia is stuck in paranoia_read? + // we need to terminate in that case + // wait for 1 second. I the thread still is working terminate it + // and trigger the finished slot manually + emitInfoMessage( i18n("Cancellation could take a while..."), K3bJob::INFO ); + QTimer::singleShot( 1000, this, SLOT(slotCheckIfThreadStillRunning()) ); +} + + +void K3bAudioRipThread::slotCheckIfThreadStillRunning() +{ + if( running() ) { + d->paranoiaLib->close(); + m_device->block(false); + + // this could happen if the thread is stuck in paranoia_read + // because of an unreadable cd + terminate(); + cleanupAfterCancellation(); + emitCanceled(); + emitFinished(false); + } +} + + +// this needs to be called if the thread was killed due to a hung paranoia_read +void K3bAudioRipThread::cleanupAfterCancellation() +{ + if( d->currentTrackIndex >= 0 && d->currentTrackIndex < (int)m_tracks.count() ) { + if( QFile::exists( m_tracks[d->currentTrackIndex].second ) ) { + QFile::remove( m_tracks[d->currentTrackIndex].second ); + emitInfoMessage( i18n("Removed partial file '%1'.").arg(m_tracks[d->currentTrackIndex].second), K3bJob::INFO ); + } + } +} + + +bool K3bAudioRipThread::writePlaylist() +{ + // this is an absolut path so there is always a "/" + QString playlistDir = m_playlistFilename.left( m_playlistFilename.findRev( "/" ) ); + + if( !KStandardDirs::makeDir( playlistDir ) ) { + emitInfoMessage( i18n("Unable to create directory %1").arg(playlistDir), K3bJob::ERROR ); + return false; + } + + emitInfoMessage( i18n("Writing playlist to %1.").arg( m_playlistFilename ), K3bJob::INFO ); + + QFile f( m_playlistFilename ); + if( f.open( IO_WriteOnly ) ) { + QTextStream t( &f ); + + // format descriptor + t << "#EXTM3U" << endl; + + // now write the entries (or the entry if m_singleFile) + if( m_singleFile ) { + // extra info + t << "#EXTINF:" << d->overallSectorsToRead/75 << ","; + if( !m_cddbEntry.cdArtist.isEmpty() && !m_cddbEntry.cdTitle.isEmpty() ) + t << m_cddbEntry.cdArtist << " - " << m_cddbEntry.cdTitle << endl; + else + t << m_tracks[0].second.mid(m_tracks[0].second.findRev("/") + 1, + m_tracks[0].second.length() - m_tracks[0].second.findRev("/") - 5) + << endl; // filename without extension + + // filename + if( m_relativePathInPlaylist ) + t << findRelativePath( m_tracks[0].second, playlistDir ) + << endl; + else + t << m_tracks[0].second << endl; + } + else { + for( unsigned int i = 0; i < m_tracks.count(); ++i ) { + int trackIndex = m_tracks[i].first-1; + + // extra info + t << "#EXTINF:" << d->toc[trackIndex].length().totalFrames()/75 << ","; + + if( !m_cddbEntry.artists[trackIndex].isEmpty() && !m_cddbEntry.titles[trackIndex].isEmpty() ) + t << m_cddbEntry.artists[trackIndex] << " - " << m_cddbEntry.titles[trackIndex] << endl; + else + t << m_tracks[i].second.mid(m_tracks[i].second.findRev("/") + 1, + m_tracks[i].second.length() + - m_tracks[i].second.findRev("/") - 5) + << endl; // filename without extension + + // filename + if( m_relativePathInPlaylist ) + t << findRelativePath( m_tracks[i].second, playlistDir ) + << endl; + else + t << m_tracks[i].second << endl; + } + } + + return ( t.device()->status() == IO_Ok ); + } + else { + emitInfoMessage( i18n("Unable to open '%1' for writing.").arg(m_playlistFilename), K3bJob::ERROR ); + kdDebug() << "(K3bAudioRipThread) could not open file " << m_playlistFilename << " for writing." << endl; + return false; + } +} + + +bool K3bAudioRipThread::writeCueFile() +{ + K3bCueFileWriter cueWriter; + + // create a new toc and cd-text + K3bDevice::Toc toc; + K3bDevice::CdText text; + text.setPerformer( m_cddbEntry.cdArtist ); + text.setTitle( m_cddbEntry.cdTitle ); + text.reserve( m_tracks.count() ); + K3b::Msf currentSector; + for( unsigned int i = 0; i < m_tracks.count(); ++i ) { + int trackNum = m_tracks[i].first; + + const K3bDevice::Track& oldTrack = d->toc[trackNum-1]; + K3bDevice::Track newTrack( oldTrack ); + newTrack.setFirstSector( currentSector ); + newTrack.setLastSector( (currentSector+=oldTrack.length()) - 1 ); + toc.append( newTrack ); + + K3bDevice::TrackCdText trackText; + trackText.setPerformer( m_cddbEntry.artists[trackNum-1] ); + trackText.setTitle( m_cddbEntry.titles[trackNum-1] ); + text.append( trackText ); + } + + cueWriter.setData( toc ); + cueWriter.setCdText( text ); + + + // we always use a relative filename here + QString imageFile = m_tracks[0].second.section( '/', -1 ); + cueWriter.setImage( imageFile, ( d->fileType.isEmpty() ? QString("WAVE") : d->fileType ) ); + + // use the same base name as the image file + QString cueFile = m_tracks[0].second; + cueFile.truncate( cueFile.findRev(".") ); + cueFile += ".cue"; + + emitInfoMessage( i18n("Writing cue file to %1.").arg(cueFile), K3bJob::INFO ); + + return cueWriter.save( cueFile ); +} + + +QString K3bAudioRipThread::findRelativePath( const QString& absPath, const QString& baseDir ) +{ + QString baseDir_ = K3b::prepareDir( K3b::fixupPath(baseDir) ); + QString path = K3b::fixupPath( absPath ); + + // both paths have an equal beginning. That's just how it's configured by K3b + int pos = baseDir_.find( "/" ); + int oldPos = pos; + while( pos != -1 && path.left( pos+1 ) == baseDir_.left( pos+1 ) ) { + oldPos = pos; + pos = baseDir_.find( "/", pos+1 ); + } + + // now the paths are equal up to oldPos, so that's how "deep" we go + path = path.mid( oldPos+1 ); + baseDir_ = baseDir_.mid( oldPos+1 ); + int numberOfDirs = baseDir_.contains( '/' ); + for( int i = 0; i < numberOfDirs; ++i ) + path.prepend( "../" ); + + return path; +} + + +QString K3bAudioRipThread::jobDescription() const +{ + if( m_cddbEntry.cdTitle.isEmpty() ) + return i18n("Ripping Audio Tracks"); + else + return i18n("Ripping Audio Tracks From '%1'").arg(m_cddbEntry.cdTitle); +} + +QString K3bAudioRipThread::jobDetails() const +{ + if( d->encoder ) + return i18n("1 track (encoding to %1)", + "%n tracks (encoding to %1)", + m_tracks.count() ).arg(d->encoder->fileTypeComment(d->fileType)); + else + return i18n("1 track", "%n tracks", m_tracks.count() ); +} + +#include "k3baudioripthread.moc" diff --git a/src/rip/k3baudioripthread.h b/src/rip/k3baudioripthread.h new file mode 100644 index 0000000..93d600f --- /dev/null +++ b/src/rip/k3baudioripthread.h @@ -0,0 +1,117 @@ +/* + * + * $Id: k3baudioripthread.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef K3B_AUDIO_RIP_THREAD_H +#define K3B_AUDIO_RIP_THREAD_H + +#include +#include +#include +#include + +#include + + +class K3bAudioEncoder; +class K3bCdparanoiaLib; +namespace K3bDevice { + class Device; +} + + +class K3bAudioRipThread : public QObject, public K3bThread +{ + Q_OBJECT + + public: + K3bAudioRipThread(); + ~K3bAudioRipThread(); + + QString jobDescription() const; + QString jobDetails() const; + + // paranoia settings + void setParanoiaMode( int mode ); + void setMaxRetries( int r ); + void setNeverSkip( bool b ); + + void setSingleFile( bool b ) { m_singleFile = b; } + + void setUseIndex0( bool b ) { m_useIndex0 = b; } + + void setDevice( K3bDevice::Device* dev ) { m_device = dev; } + + void setCddbEntry( const K3bCddbResultEntry& e ) { m_cddbEntry = e; } + + // if 0 (default) wave files are created + void setEncoder( K3bAudioEncoder* f ); + + /** + * Used for encoders that support multiple formats + */ + void setFileType( const QString& ); + + /** + * 1 is the first track + */ + void setTracksToRip( const QValueVector >& t ) { m_tracks = t; } + + void setWritePlaylist( bool b ) { m_writePlaylist = b; } + void setPlaylistFilename( const QString& s ) { m_playlistFilename = s; } + void setUseRelativePathInPlaylist( bool b ) { m_relativePathInPlaylist = b; } + void setWriteCueFile( bool b ) { m_writeCueFile = b; } + + void cancel(); + + private slots: + void slotCheckIfThreadStillRunning(); + + private: + /** reimplemented from QThread. Does the work */ + void run(); + + bool ripTrack( int track, const QString& filename ); + void cleanupAfterCancellation(); + bool writePlaylist(); + bool writeCueFile(); + + /** + * Finds a relative path from baseDir to absPath + */ + QString findRelativePath( const QString& absPath, const QString& baseDir ); + + K3bCddbResultEntry m_cddbEntry; + K3bDevice::Device* m_device; + + bool m_bUsePattern; + bool m_singleFile; + bool m_useIndex0; + + bool m_writePlaylist; + bool m_relativePathInPlaylist; + QString m_playlistFilename; + + bool m_writeCueFile; + + QValueVector > m_tracks; + + friend class K3bAudioRipJob; + + class Private; + Private* d; +}; + +#endif diff --git a/src/rip/k3bcddbpatternwidget.cpp b/src/rip/k3bcddbpatternwidget.cpp new file mode 100644 index 0000000..9cea4d5 --- /dev/null +++ b/src/rip/k3bcddbpatternwidget.cpp @@ -0,0 +1,175 @@ +/* + * + * $Id: k3bcddbpatternwidget.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003-2007 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bcddbpatternwidget.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +K3bCddbPatternWidget::K3bCddbPatternWidget( QWidget* parent, const char* name ) + : base_K3bCddbPatternWidget( parent, name ) +{ + // fix the layout + ((QGridLayout*)layout())->setRowStretch( 4, 1 ); + + // setup validators + // there can never be one of the following characters in both dir and filename: + // * ? " + // additional the filename can never contain a slash / + // and the dir should never start with a slash since it should always be a relative path + + QRegExpValidator* dirValidator = new QRegExpValidator( QRegExp( "[^/][^?\\*\\\"]*" ), this ); + m_comboFilenamePattern->setValidator( dirValidator ); + m_comboPlaylistPattern->setValidator( dirValidator ); + m_editBlankReplace->setValidator( dirValidator ); + + // default pattern + m_comboFilenamePattern->insertItem( i18n("%A - %T/%n - !a='%A'{%a - }%t") ); + m_comboFilenamePattern->insertItem( i18n( "%{albumartist} - %{albumtitle}/%{number} - %{artist} - %{title}" ) ); + m_comboFilenamePattern->insertItem( i18n( "%{genre}/%{albumartist} - %{albumtitle}/Track%{number}" ) ); + m_comboFilenamePattern->insertItem( i18n( "music/ripped-tracks/%a - %t" ) ); + + m_comboPlaylistPattern->insertItem( i18n( "%{albumartist} - %{albumtitle}" ) ); + m_comboPlaylistPattern->insertItem( i18n( "Playlist" ) ); + m_comboPlaylistPattern->insertItem( i18n( "playlists/%{albumartist}/%{albumtitle }" ) ); + + connect( m_comboFilenamePattern, SIGNAL(textChanged(const QString&)), + this, SIGNAL(changed()) ); + connect( m_comboPlaylistPattern, SIGNAL(textChanged(const QString&)), + this, SIGNAL(changed()) ); + connect( m_editBlankReplace, SIGNAL(textChanged(const QString&)), + this, SIGNAL(changed()) ); + connect( m_checkBlankReplace, SIGNAL(toggled(bool)), + this, SIGNAL(changed()) ); + connect( m_specialStringsLabel, SIGNAL(leftClickedURL()), + this, SLOT(slotSeeSpecialStrings()) ); + connect( m_conditionalInclusionLabel, SIGNAL(leftClickedURL()), + this, SLOT(slotSeeConditionalInclusion()) ); +} + + +K3bCddbPatternWidget::~K3bCddbPatternWidget() +{ +} + + +QString K3bCddbPatternWidget::filenamePattern() const +{ + return m_comboFilenamePattern->currentText(); +} + + +QString K3bCddbPatternWidget::playlistPattern() const +{ + return m_comboPlaylistPattern->currentText(); +} + + +QString K3bCddbPatternWidget::blankReplaceString() const +{ + return m_editBlankReplace->text(); +} + + +bool K3bCddbPatternWidget::replaceBlanks() const +{ + return m_checkBlankReplace->isChecked(); +} + + +void K3bCddbPatternWidget::loadConfig( KConfigBase* c ) +{ + m_comboPlaylistPattern->setEditText( c->readEntry( "playlist pattern", m_comboPlaylistPattern->text(0) ) ); + m_comboFilenamePattern->setEditText( c->readEntry( "filename pattern", m_comboFilenamePattern->text(0) ) ); + m_checkBlankReplace->setChecked( c->readBoolEntry( "replace blanks", false ) ); + m_editBlankReplace->setText( c->readEntry( "blank replace string", "_" ) ); +} + + +void K3bCddbPatternWidget::saveConfig( KConfigBase* c ) +{ + c->writeEntry( "playlist pattern", m_comboPlaylistPattern->currentText() ); + c->writeEntry( "filename pattern", m_comboFilenamePattern->currentText() ); + c->writeEntry( "replace blanks", m_checkBlankReplace->isChecked() ); + c->writeEntry( "blank replace string", m_editBlankReplace->text() ); +} + + +void K3bCddbPatternWidget::loadDefaults() +{ + m_comboPlaylistPattern->setEditText( m_comboPlaylistPattern->text(0) ); + m_comboFilenamePattern->setEditText( m_comboFilenamePattern->text(0) ); + m_checkBlankReplace->setChecked( false ); + m_editBlankReplace->setText( "_" ); +} + + +void K3bCddbPatternWidget::slotSeeSpecialStrings() +{ + QWhatsThis::display( i18n( "

Pattern special strings:" + "

The following strings will be replaced with their respective meaning in every " + "track name.
" + "Hint: %A differs from %a only on soundtracks or compilations." + "

" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
MeaningAlternatives
%aartist of the track%{a} or %{artist}
%ttitle of the track%{t} or %{title}
%ntrack number%{n} or %{number}
%yyear of the CD%{y} or %{year}
%cextended track information%{c} or %{comment}
%ggenre of the CD%{g} or %{genre}
%Aalbum artist%{A} or %{albumartist}
%Talbum title%{T} or %{albumtitle}
%Cextended CD information%{C} or %{albumcomment}
%dcurrent date%{d} or %{date}
") ); +} + +void K3bCddbPatternWidget::slotSeeConditionalInclusion() +{ + QWhatsThis::display( i18n( "

Conditional inclusion:" + "

These patterns make it possible to selectively include texts, " + "depending on the value of CDDB entries. You can choose only to " + "include or exclude texts if one of the entries is empty, " + "or if it has a specific value. Examples:" + "

    " + "
  • @T{TEXT} includes TEXT if the album title is specified" + "
  • !T{TEXT} includes TEXT if the album title is not specified" + "
  • @C=\'Soundtrack\'{TEXT} includes TEXT if the CD's extended " + "information is named Soundtrack" + "
  • !C=\'Soundtrack\'{TEXT} includes TEXT if the CD's extended " + "information is anything else but Soundtrack" + "
  • It is also possible to include special strings in texts and conditions, " + "e.g. !a='%A'{%a} only includes the title's artist information " + "if it does not differ from the album artist." + "
" + "

Conditional includes make use of the same characters as the special " + "strings, which means that the X in @X{...} can be one character out of " + "[atnycgATCd]." ) ); +} + +#include "k3bcddbpatternwidget.moc" + diff --git a/src/rip/k3bcddbpatternwidget.h b/src/rip/k3bcddbpatternwidget.h new file mode 100644 index 0000000..928b95b --- /dev/null +++ b/src/rip/k3bcddbpatternwidget.h @@ -0,0 +1,51 @@ +/* + * + * $Id: k3bcddbpatternwidget.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef _K3B_CDDB_PATTERN_WIDGET_H_ +#define _K3B_CDDB_PATTERN_WIDGET_H_ + +#include "base_k3bcddbpatternwidget.h" + +class KConfigBase; + + +class K3bCddbPatternWidget : public base_K3bCddbPatternWidget +{ + Q_OBJECT + + public: + K3bCddbPatternWidget( QWidget* parent = 0, const char* name = 0 ); + ~K3bCddbPatternWidget(); + + QString filenamePattern() const; + QString playlistPattern() const; + QString blankReplaceString() const; + bool replaceBlanks() const; + + signals: + void changed(); + + public slots: + void loadConfig( KConfigBase* ); + void saveConfig( KConfigBase* ); + void loadDefaults(); + + private slots: + void slotSeeSpecialStrings(); + void slotSeeConditionalInclusion(); +}; + +#endif diff --git a/src/rip/k3bcuefilewriter.cpp b/src/rip/k3bcuefilewriter.cpp new file mode 100644 index 0000000..087002d --- /dev/null +++ b/src/rip/k3bcuefilewriter.cpp @@ -0,0 +1,91 @@ +/* + * + * $Id: k3bcuefilewriter.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bcuefilewriter.h" + +#include +#include +#include +#include + +#include +#include +#include + + +K3bCueFileWriter::K3bCueFileWriter() +{ +} + + +bool K3bCueFileWriter::save( const QString& filename ) +{ + QFile f( filename ); + + if( !f.open( IO_WriteOnly ) ) { + kdDebug() << "(K3bCueFileWriter) could not open file " << f.name() << endl; + return false; + } + + QTextStream s( &f ); + + return save( s ); +} + + +bool K3bCueFileWriter::save( QTextStream& t ) +{ + t << "REM Cue file written by K3b " << k3bcore->version() << endl + << endl; + + if( !m_cdText.isEmpty() ) { + t << "PERFORMER \"" << m_cdText.performer() << "\"" << endl; + t << "TITLE \"" << m_cdText.title() << "\"" << endl; + } + + t << "FILE \"" << m_image << "\" " << m_dataType.upper() << endl; + + // the tracks + unsigned int i = 0; + for( K3bDevice::Toc::const_iterator it = m_toc.begin(); + it != m_toc.end(); ++it ) { + + const K3bDevice::Track& track = *it; + + t << " TRACK " << QString::number(i+1).rightJustify( 2, '0' ) << " AUDIO" << endl; + + if( m_cdText.count() > i && !m_cdText[i].isEmpty() ) { + t << " PERFORMER \"" << m_cdText[i].performer() << "\"" << endl; + t << " TITLE \"" << m_cdText[i].title() << "\"" << endl; + } + + // + // the pregap is part of the current track like in toc files + // and not part of the last track as on the CD + // + if( i > 0 ) { + --it; + if( (*it).index0() > 0 ) + t << " INDEX 00 " << ((*it).firstSector() + (*it).index0()).toString() << endl; + ++it; + } + t << " INDEX 01 " << track.firstSector().toString() << endl; + // TODO: add additional indices + + i++; + } + + return ( t.device()->status() == IO_Ok ); +} diff --git a/src/rip/k3bcuefilewriter.h b/src/rip/k3bcuefilewriter.h new file mode 100644 index 0000000..88f7ffe --- /dev/null +++ b/src/rip/k3bcuefilewriter.h @@ -0,0 +1,54 @@ +/* + * + * $Id: k3bcuefilewriter.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2004 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_CUE_FILE_WRITER_H_ +#define _K3B_CUE_FILE_WRITER_H_ + +#include +#include + +#include +#include + +namespace K3bDevice { + class TrackCdText; +} + +/** + * Write a CDRWIN cue file. + * For now this writer only supports audio CDs + * for usage in the K3b audio CD ripper. + */ + +class K3bCueFileWriter +{ + public: + K3bCueFileWriter(); + + bool save( QTextStream& ); + bool save( const QString& filename ); + + void setData( const K3bDevice::Toc& toc ) { m_toc = toc; } + void setCdText( const K3bDevice::CdText& text ) { m_cdText = text; } + void setImage( const QString& name, const QString& type ) { m_image = name; m_dataType = type; } + + private: + K3bDevice::Toc m_toc; + K3bDevice::CdText m_cdText; + QString m_image; + QString m_dataType; +}; + +#endif diff --git a/src/rip/k3bpatternparser.cpp b/src/rip/k3bpatternparser.cpp new file mode 100644 index 0000000..8ba04e6 --- /dev/null +++ b/src/rip/k3bpatternparser.cpp @@ -0,0 +1,305 @@ +/* + * + * $Id: k3bpatternparser.cpp 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg + * Copyright (C) 2004-2005 Jakob Petsovits + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#include "k3bpatternparser.h" + +#include +#include +#include + +#include +#include + + +QString K3bPatternParser::parsePattern( const K3bCddbResultEntry& entry, + unsigned int trackNumber, + const QString& pattern, + bool replace, + const QString& replaceString ) +{ + if( entry.titles.count() < trackNumber ) + return ""; + + QString dir, s; + char c = ' '; // contains the character representation of a special string + unsigned int len; // length of the current special string + + + for( unsigned int i = 0; i < pattern.length(); ++i ) { + + if( pattern[i] == '%' ) { + + if( i + 1 < pattern.length() ) { + len = 2; + + if( pattern[i+1] != '{' ) { // strings like %a + c = pattern[i+1]; + } + else if( i + 3 >= pattern.length() ) { // too short to contain a %{*} string + c = ' '; + } + else { // long enough to contain %{*} + + if( pattern[i+3] == '}' ) { // strings like %{a} + c = pattern[i+2]; + len = 4; + } + else { // strings like %{artist}, or anything like %{* + + while( i + len - 1 < pattern.length() ) { + ++len; + + if( pattern[i + len - 1] == '%' ) { // don't touch other special strings + c = ' '; + --len; + break; + } + else if( pattern[i + len - 1] == '}' ) { + s = pattern.mid( i + 2, len - 3 ); + + if( s == "title" ) { + c = TITLE; + } + else if( s == "artist" ) { + c = ARTIST; + } + else if( s == "number" ) { + c = NUMBER; + } + else if( s == "comment" ) { + c = COMMENT; + } + else if( s == "year" ) { + c = YEAR; + } + else if( s == "genre" ) { + c = GENRE; + } + else if( s == "albumtitle" ) { + c = ALBUMTITLE; + } + else if( s == "albumartist" ) { + c = ALBUMARTIST; + } + else if( s == "albumcomment" ) { + c = ALBUMCOMMENT; + } + else if( s == "date" ) { + c = DATE; + } + else { // no valid pattern in here, don't replace anything + c = ' '; + } + break; // finished parsing %{* string + } + } // end of while(...) + + } // end of %{* strings + + } // end of if( long enough to contain %{*} ) + + switch( c ) { + case ARTIST: + s = entry.artists[trackNumber-1]; + s.replace( '/', '_' ); + s.replace( '*', '_' ); + s.replace( '}', '*' ); // for conditional inclusion + dir.append( s.isEmpty() + ? i18n("unknown") + QString(" %1").arg(trackNumber) + : s ); + break; + case TITLE: + s = entry.titles[trackNumber-1]; + s.replace( '/', '_' ); + s.replace( '*', '_' ); + s.replace( '}', '*' ); + dir.append( s.isEmpty() + ? i18n("Track %1").arg(trackNumber) + : s ); + break; + case NUMBER: + dir.append( QString::number(trackNumber).rightJustify( 2, '0' ) ); + break; + case YEAR: + dir.append( QString::number( entry.year ) ); + break; + case COMMENT: + s = entry.extInfos[trackNumber-1]; + s.replace( '/', '_' ); + s.replace( '*', '_' ); + s.replace( '}', '*' ); + dir.append( s ); + break; + case GENRE: + s = ( entry.genre.isEmpty() ? entry.category : entry.genre ); + s.replace( '/', '_' ); + s.replace( '*', '_' ); + s.replace( '}', '*' ); + dir.append( s ); + break; + case ALBUMARTIST: + dir.append( entry.cdArtist.isEmpty() + ? i18n("unknown") : entry.cdArtist ); + break; + case ALBUMTITLE: + s = entry.cdTitle; + s.replace( '/', '_' ); + s.replace( '*', '_' ); + s.replace( '}', '*' ); + dir.append( s.isEmpty() + ? i18n("unknown") : s ); + break; + case ALBUMCOMMENT: + s = entry.cdExtInfo; + s.replace( '/', '_' ); + s.replace( '*', '_' ); + s.replace( '}', '*' ); + dir.append( s ); // I think it makes more sense to allow empty comments + break; + case DATE: + dir.append( KGlobal::locale()->formatDate( QDate::currentDate() ) ); + break; + default: + dir.append( pattern.mid(i, len) ); + break; + } + i += len - 1; + } + else { // end of pattern + dir.append( "%" ); + } + } + else { + dir.append( pattern[i] ); + } + } + + + + // /* delete line comment to comment out + // the following part: Conditional Inclusion + + QValueStack offsetStack; + QString inclusion; + bool isIncluded; + + static QRegExp conditionrx( "^[@|!][atyegrmx](?:='.*')?\\{" ); + conditionrx.setMinimal( TRUE ); + + for( unsigned int i = 0; i < dir.length(); ++i ) { + + offsetStack.push( + conditionrx.search(dir, i, QRegExp::CaretAtOffset) ); + + if( offsetStack.top() == -1 ) { + offsetStack.pop(); + } + else { + i += conditionrx.matchedLength() - 1; + continue; + } + + if( dir[i] == '}' && !offsetStack.isEmpty() ) { + + int offset = offsetStack.pop(); + int length = i - offset + 1; + + switch( (QChar) dir[offset+1] ) { + case ARTIST: + s = entry.artists[trackNumber-1]; + break; + case TITLE: + s = entry.titles[trackNumber-1]; + break; + case NUMBER: + s = QString::number( trackNumber ); + break; + case YEAR: + s = QString::number( entry.year ); + break; + case COMMENT: + s = entry.extInfos[trackNumber-1]; + break; + case GENRE: + s = ( entry.genre.isEmpty() ? entry.category : entry.genre ); + break; + case ALBUMARTIST: + s = entry.cdArtist; + break; + case ALBUMTITLE: + s = entry.cdTitle; + break; + case ALBUMCOMMENT: + s = entry.cdExtInfo; + break; + case DATE: + s = KGlobal::locale()->formatDate( QDate::currentDate() ); + break; + default: // we must never get here, + break; // all choices should be covered + } + + if( dir[offset+2] == '{' ) { // no string matching, e.g. ?y{text} + switch( (QChar) dir[offset+1] ) { + case YEAR: + isIncluded = (s != "0"); + break; + default: + isIncluded = !s.isEmpty(); + break; + } + inclusion = dir.mid( offset + 3, length - 4 ); + } + else { // with string matching, e.g. ?y='2004'{text} + + // Be aware that there might be ' in the condition text + int endOfCondition = dir.find( '{', offset+4 )-1; + QString condition = dir.mid( offset+4, + endOfCondition - (offset+4) ); + + isIncluded = (s == condition); + inclusion = dir.mid( endOfCondition+2, + i - (endOfCondition+2) ); + } + + if( dir[offset] == '!' ) + isIncluded = !isIncluded; + // Leave it when it's '@'. + + dir.replace( offset, length, ( isIncluded ? inclusion : QString("") ) ); + + if( isIncluded == TRUE ) + i -= length - inclusion.length(); + else + i = offset - 1; // start next loop at offset + + continue; + + } // end of replace (at closing bracket '}') + + } // end of conditional inclusion for(...) + + // end of Conditional Inclusion */ + + + dir.replace( '*', '}' ); // bring the brackets back, if there were any + + if( replace ) + dir.replace( QRegExp( "\\s" ), replaceString ); + + return dir; +} diff --git a/src/rip/k3bpatternparser.h b/src/rip/k3bpatternparser.h new file mode 100644 index 0000000..88a725d --- /dev/null +++ b/src/rip/k3bpatternparser.h @@ -0,0 +1,52 @@ +/* + * + * $Id: k3bpatternparser.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef K3BPATTERNPARSER_H +#define K3BPATTERNPARSER_H + +#include + +#include + + +/** + *@author Sebastian Trueg + */ +class K3bPatternParser +{ + public: + static QString parsePattern( const K3bCddbResultEntry& entry, + unsigned int trackNumber, + const QString& pattern, + bool replace = false, + const QString& replaceString = "_" ); + + private: + enum { + TITLE = 't', + ARTIST = 'a', + NUMBER = 'n', + COMMENT = 'c', + YEAR = 'y', + GENRE = 'g', + ALBUMTITLE = 'T', + ALBUMARTIST = 'A', + ALBUMCOMMENT = 'C', + DATE = 'd' + }; +}; + +#endif diff --git a/src/rip/k3bvideocdinfo.cpp b/src/rip/k3bvideocdinfo.cpp new file mode 100644 index 0000000..ac30d40 --- /dev/null +++ b/src/rip/k3bvideocdinfo.cpp @@ -0,0 +1,247 @@ +/* +* +* $Id: k3bvideocdinfo.cpp 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003 Christian Kvasny +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + + + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "k3bvideocdinfo.h" + +#include +#include + + +K3bVideoCdInfo::K3bVideoCdInfo( QObject* parent, const char* name ) + : QObject( parent, name ) +{ + m_process = 0L; + m_isXml = false; +} + + +K3bVideoCdInfo::~K3bVideoCdInfo() +{ + delete m_process; +} + +void K3bVideoCdInfo::cancelAll() +{ + if ( m_process->isRunning() ) { + m_process->disconnect( this ); + m_process->kill(); + } +} + +void K3bVideoCdInfo::info( const QString& device ) +{ + if ( !k3bcore ->externalBinManager() ->foundBin( "vcdxrip" ) ) { + kdDebug() << "(K3bVideoCdInfo::info) could not find vcdxrip executable" << endl; + emit infoFinished( false ); + return ; + } + + delete m_process; + m_process = new K3bProcess(); + + *m_process << k3bcore ->externalBinManager() ->binPath( "vcdxrip" ); + + *m_process << "-q" << "--norip" << "-i" << device << "-o" << "-"; + + connect( m_process, SIGNAL( receivedStderr( KProcess*, char*, int ) ), + this, SLOT( slotParseOutput( KProcess*, char*, int ) ) ); + connect( m_process, SIGNAL( receivedStdout( KProcess*, char*, int ) ), + this, SLOT( slotParseOutput( KProcess*, char*, int ) ) ); + connect( m_process, SIGNAL( processExited( KProcess* ) ), + this, SLOT( slotInfoFinished() ) ); + + if ( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) { + kdDebug() << "(K3bVideoCdInfo::info) could not start vcdxrip" << endl; + cancelAll(); + emit infoFinished( false ); + } +} + +void K3bVideoCdInfo::slotParseOutput( KProcess*, char* output, int len ) +{ + QString buffer = QString::fromLocal8Bit( output, len ); + + // split to lines + QStringList lines = QStringList::split( "\n", buffer ); + QStringList::Iterator end( lines.end()); + for ( QStringList::Iterator str = lines.begin(); str != end; ++str ) { + + if ( ( *str ).contains( "" ) ) + m_isXml = false; + } +} + +void K3bVideoCdInfo::slotInfoFinished() +{ + if ( m_process->normalExit() ) { + // TODO: check the process' exitStatus() + switch ( m_process->exitStatus() ) { + case 0: + break; + default: + cancelAll(); + emit infoFinished( false ); + return ; + } + } else { + cancelAll(); + emit infoFinished( false ); + return ; + } + + if ( m_xmlData.isEmpty() ) { + emit infoFinished( false ); + return ; + } + + parseXmlData(); + emit infoFinished( true ); +} + +void K3bVideoCdInfo::parseXmlData() +{ + QDomDocument xml_doc; + QDomElement xml_root; + + m_Result.xmlData = m_xmlData; + + xml_doc.setContent( m_xmlData ); + xml_root = xml_doc.documentElement(); + + m_Result.type = xml_root.attribute( "class" ); + m_Result.version = xml_root.attribute( "version" ); + + for ( QDomNode node = xml_root.firstChild(); !node.isNull(); node = node.nextSibling() ) { + QDomElement el = node.toElement(); + QString tagName = el.tagName().lower(); + + if ( tagName == "pvd" ) { + for ( QDomNode snode = node.firstChild(); !snode.isNull(); snode = snode.nextSibling() ) { + QDomElement sel = snode.toElement(); + QString pvdElement = sel.tagName().lower(); + QString pvdElementText = sel.text(); + if ( pvdElement == "volume-id" ) + m_Result.volumeId = pvdElementText; + } + + } else if ( tagName == "sequence-items" ) { + for ( QDomNode snode = node.firstChild(); !snode.isNull(); snode = snode.nextSibling() ) { + QDomElement sel = snode.toElement(); + QString seqElement = sel.tagName().lower(); + m_Result.addEntry( K3bVideoCdInfoResultEntry( + sel.attribute( "src" ), + sel.attribute( "id" ) ), + K3bVideoCdInfoResult::SEQUENCE + ); + } + } else if ( tagName == "segment-items" ) { + for ( QDomNode snode = node.firstChild(); !snode.isNull(); snode = snode.nextSibling() ) { + QDomElement sel = snode.toElement(); + QString seqElement = sel.tagName().lower(); + m_Result.addEntry( K3bVideoCdInfoResultEntry( + sel.attribute( "src" ), + sel.attribute( "id" ) ), + K3bVideoCdInfoResult::SEGMENT + ); + } + } else { + kdDebug() << QString( "(K3bVideoCdInfo::parseXmlData) tagName '%1' not used" ).arg( tagName ) << endl; + } + } +} + +const K3bVideoCdInfoResult& K3bVideoCdInfo::result() const +{ + return m_Result; +} + +const K3bVideoCdInfoResultEntry& K3bVideoCdInfoResult::entry( unsigned int number, int type ) const +{ + switch ( type ) { + case K3bVideoCdInfoResult::FILE: + if ( number >= m_fileEntry.count() ) + return m_emptyEntry; + return m_fileEntry[ number ]; + case K3bVideoCdInfoResult::SEGMENT: + if ( number >= m_segmentEntry.count() ) + return m_emptyEntry; + return m_segmentEntry[ number ]; + case K3bVideoCdInfoResult::SEQUENCE: + if ( number >= m_sequenceEntry.count() ) + return m_emptyEntry; + return m_sequenceEntry[ number ]; + default: + kdDebug() << "(K3bVideoCdInfoResult::entry) not supported entrytype." << endl; + } + + return m_emptyEntry; + +} + + +void K3bVideoCdInfoResult::addEntry( const K3bVideoCdInfoResultEntry& entry, int type ) +{ + switch ( type ) { + case K3bVideoCdInfoResult::FILE: + m_fileEntry.append( entry ); + break; + case K3bVideoCdInfoResult::SEGMENT: + m_segmentEntry.append( entry ); + break; + case K3bVideoCdInfoResult::SEQUENCE: + m_sequenceEntry.append( entry ); + break; + default: + kdDebug() << "(K3bVideoCdInfoResult::addEntry) not supported entrytype." << endl; + } +} + +int K3bVideoCdInfoResult::foundEntries( int type ) const +{ + switch ( type ) { + case K3bVideoCdInfoResult::FILE: + return m_fileEntry.count(); + case K3bVideoCdInfoResult::SEGMENT: + return m_segmentEntry.count(); + case K3bVideoCdInfoResult::SEQUENCE: + return m_sequenceEntry.count(); + default: + kdDebug() << "(K3bVideoCdInfoResult::addEntry) not supported entrytype." << endl; + } + return 0; +} + +#include "k3bvideocdinfo.moc" + diff --git a/src/rip/k3bvideocdinfo.h b/src/rip/k3bvideocdinfo.h new file mode 100644 index 0000000..0b295e6 --- /dev/null +++ b/src/rip/k3bvideocdinfo.h @@ -0,0 +1,107 @@ +/* +* +* $Id: k3bvideocdinfo.h 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003 Christian Kvasny +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + + +#ifndef K3BVIDEOCDINFO_H +#define K3BVIDEOCDINFO_H + +#include +#include +#include + +#include +#include + +class KProcess; + +class K3bVideoCdInfoResultEntry +{ + public: + K3bVideoCdInfoResultEntry() : name( 0 ), id( 0 ) + {} + + K3bVideoCdInfoResultEntry( const QString& name, const QString& id ) + : name( name ), id( id ) + {} + + QString name; + QString id; + + long size; +}; + +class K3bVideoCdInfoResult +{ + public: + K3bVideoCdInfoResult() + {} + + enum type {NONE = 0, FILE, SEGMENT, SEQUENCE}; + + void addEntry( const K3bVideoCdInfoResultEntry& = K3bVideoCdInfoResultEntry(), int type = K3bVideoCdInfoResult::SEQUENCE ); + const K3bVideoCdInfoResultEntry& entry( unsigned int number = 0 , int type = K3bVideoCdInfoResult::SEQUENCE ) const; + int foundEntries( int type = K3bVideoCdInfoResult::SEQUENCE ) const; + + QString volumeId; + QString type; + QString version; + + QString xmlData; + + private: + QValueList m_fileEntry; + QValueList m_segmentEntry; + QValueList m_sequenceEntry; + + K3bVideoCdInfoResultEntry m_emptyEntry; +}; + +class K3bVideoCdInfo : public QObject +{ + Q_OBJECT + + public: + K3bVideoCdInfo( QObject* parent = 0, const char* name = 0 ); + ~K3bVideoCdInfo(); + + /** + * Do NOT call this before queryResult has + * been emitted + */ + const K3bVideoCdInfoResult& result() const; + + void info( const QString& ); + + signals: + void infoFinished( bool success ); + + private slots: + void slotInfoFinished(); + void slotParseOutput( KProcess*, char* output, int len ); + + private: + void cancelAll(); + + K3bVideoCdInfoResult m_Result; + void parseXmlData(); + + KProcess* m_process; + + QString m_xmlData; + bool m_isXml; + +}; + +#endif diff --git a/src/rip/k3bvideocdrip.cpp b/src/rip/k3bvideocdrip.cpp new file mode 100644 index 0000000..a7467c9 --- /dev/null +++ b/src/rip/k3bvideocdrip.cpp @@ -0,0 +1,355 @@ +/* +* +* $Id: k3bvideocdrip.cpp 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003 Christian Kvasny +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// K3b Includes +#include "k3bvideocdrip.h" +#include +#include +#include +#include + +K3bVideoCdRip::K3bVideoCdRip( K3bJobHandler* hdl, K3bVideoCdRippingOptions* options, QObject* parent, const char* name ) + : K3bJob( hdl, parent, name ), + m_ripsourceType( 0 ), + m_subPosition ( 0 ), + m_videooptions( options ), + m_canceled( false ), + m_process( 0 ) +{} + + +K3bVideoCdRip::~K3bVideoCdRip() +{ + if ( m_process ) + delete m_process; + +} + + +void K3bVideoCdRip::cancel() +{ + cancelAll(); + + emit infoMessage( i18n( "Job canceled by user." ), K3bJob::ERROR ); + emit canceled(); + jobFinished( false ); +} + + +void K3bVideoCdRip::cancelAll() +{ + m_canceled = true; + + if ( m_process->isRunning() ) { + m_process->disconnect( this ); + m_process->kill(); + } +} + + +void K3bVideoCdRip::start() +{ + kdDebug() << "(K3bVideoCdRip) starting job" << endl; + + jobStarted(); + m_canceled = false; + + vcdxRip(); +} + +void K3bVideoCdRip::vcdxRip() +{ + emit newTask( i18n( "Check files" ) ); + + m_stage = stageUnknown; + delete m_process; + m_process = new K3bProcess(); + + const K3bExternalBin* bin = k3bcore ->externalBinManager() ->binObject( "vcdxrip" ); + + if ( !bin ) { + kdDebug() << "(K3bVideoCdRip) could not find vcdxrip executable" << endl; + emit infoMessage( i18n( "Could not find %1 executable." ).arg( "vcdxrip" ), K3bJob::ERROR ); + emit infoMessage( i18n( "To rip VideoCD's you must install VcdImager Version %1." ).arg( ">= 0.7.12" ), K3bJob::INFO ); + emit infoMessage( i18n( "You can find this on your distribution disks or download it from http://www.vcdimager.org" ), K3bJob::INFO ); + cancelAll(); + jobFinished( false ); + return ; + } + + if( bin->version < K3bVersion("0.7.12") ) { + kdDebug() << "(K3bVideoCdRip) vcdxrip executable too old!" << endl; + emit infoMessage( i18n( "%1 executable too old! Need version %2 or greater" ).arg( "Vcdxrip" ).arg( "0.7.12" ), K3bJob::ERROR ); + emit infoMessage( i18n( "You can find this on your distribution disks or download it from http://www.vcdimager.org" ), K3bJob::INFO ); + cancelAll(); + jobFinished( false ); + return ; + } + + if ( !bin->copyright.isEmpty() ) + emit infoMessage( i18n( "Using %1 %2 - Copyright (C) %3" ).arg( bin->name() ).arg( bin->version ).arg( bin->copyright ), INFO ); + + + *m_process << k3bcore ->externalBinManager() ->binPath( "vcdxrip" ); + + // additional user parameters from config + const QStringList& params = k3bcore->externalBinManager() ->program( "vcdxrip" ) ->userParameters(); + for ( QStringList::const_iterator it = params.begin(); it != params.end(); ++it ) + *m_process << *it; + + *m_process << "--gui" << "--progress"; + + if ( !m_videooptions ->getVideoCdRipFiles() ) + *m_process << "--nofiles"; + + if ( !m_videooptions ->getVideoCdRipSegments() ) + *m_process << "--nosegments"; + + if ( !m_videooptions ->getVideoCdRipSequences() ) + *m_process << "--nosequences"; + + if ( m_videooptions ->getVideoCdIgnoreExt() ) + *m_process << "--no-ext-psd"; + + if ( m_videooptions ->getVideoCdSector2336() ) + *m_process << "--sector-2336"; + + *m_process << "-i" << QString( "%1" ).arg( QFile::encodeName( m_videooptions ->getVideoCdSource() ) ); + + if ( m_videooptions ->getVideoCdExtractXml() ) + *m_process << "-o" << QString( "%1" ).arg( QFile::encodeName( m_videooptions ->getVideoCdDescription() + ".xml" ) ); + else + *m_process << "-o" << "/dev/null"; + + + connect( m_process, SIGNAL( receivedStderr( KProcess*, char*, int ) ), + this, SLOT( slotParseVcdXRipOutput( KProcess*, char*, int ) ) ); + connect( m_process, SIGNAL( receivedStdout( KProcess*, char*, int ) ), + this, SLOT( slotParseVcdXRipOutput( KProcess*, char*, int ) ) ); + connect( m_process, SIGNAL( processExited( KProcess* ) ), + this, SLOT( slotVcdXRipFinished() ) ); + + m_process->setWorkingDirectory( QUrl( m_videooptions ->getVideoCdDestination() ).dirPath() ); + + // vcdxrip commandline parameters + kdDebug() << "***** vcdxrip parameters:" << endl; + ; + const QValueList& args = m_process->args(); + QString s; + for ( QValueList::const_iterator it = args.begin(); it != args.end(); ++it ) { + s += *it + " "; + } + kdDebug() << s << flush << endl; + emit debuggingOutput( "vcdxrip command:", s ); + + emit newTask( i18n( "Extracting" ) ); + emit infoMessage( i18n( "Start extracting." ), K3bJob::INFO ); + emit infoMessage( i18n( "Extract files from %1 to %2." ).arg( m_videooptions ->getVideoCdSource() ).arg( m_videooptions ->getVideoCdDestination() ), K3bJob::INFO ); + + if ( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) { + kdDebug() << "(K3bVideoCdRip) could not start vcdxrip" << endl; + emit infoMessage( i18n( "Could not start %1." ).arg( "vcdxrip" ), K3bJob::ERROR ); + cancelAll(); + jobFinished( false ); + } +} + +void K3bVideoCdRip::slotParseVcdXRipOutput( KProcess*, char* output, int len ) +{ + QString buffer = QString::fromLocal8Bit( output, len ); + + // split to lines + QStringList lines = QStringList::split( "\n", buffer ); + + QDomDocument xml_doc; + QDomElement xml_root; + + // do every line + QStringList::Iterator end( lines.end()); + for ( QStringList::Iterator str = lines.begin(); str != end; ++str ) { + *str = ( *str ).stripWhiteSpace(); + + emit debuggingOutput( "vcdxrip", *str ); + + xml_doc.setContent( QString( "" ) + *str + "" ); + + xml_root = xml_doc.documentElement(); + + for ( QDomNode node = xml_root.firstChild(); !node.isNull(); node = node.nextSibling() ) { + QDomElement el = node.toElement(); + if ( el.isNull() ) + continue; + + const QString tagName = el.tagName().lower(); + + if ( tagName == "progress" ) { + const QString oper = el.attribute( "operation" ).lower(); + const unsigned long long overallPos = el.attribute( "position" ).toLong(); + const unsigned long long pos = overallPos - m_subPosition; + const unsigned long long size = el.attribute( "size" ).toLong() - m_subPosition; + + if ( oper == "extract" ) { + emit subPercent( ( int ) ( 100.0 * ( double ) pos / ( double ) size ) ); + emit processedSubSize( ( pos * 2352 ) / 1024 / 1024 , ( size * 2352 ) / 1024 / 1024 ); + + m_bytesFinished = pos; + + kdDebug() << "(slotParseVcdXRipOutput) overall: " << ((long)overallPos * 2352) + << ", videocdsize: " << m_videooptions->getVideoCdSize() << endl; + double relOverallWritten = ( ( double ) overallPos * 2352 ) / ( double ) m_videooptions ->getVideoCdSize() ; + int newpercent = ( int ) ( 100 * relOverallWritten ); + if ( newpercent > m_oldpercent ) { + emit percent( newpercent ); + m_oldpercent = newpercent; + } + + } else { + return ; + } + + } else if ( tagName == "log" ) { + QDomText tel = el.firstChild().toText(); + const QString level = el.attribute( "level" ).lower(); + if ( tel.isText() ) { + const QString text = tel.data(); + if ( level == "information" ) { + kdDebug() << QString( "(K3bVideoCdRip) vcdxrip information, %1" ).arg( text ) << endl; + parseInformation( text ); + } else { + if ( level != "error" ) { + kdDebug() << QString( "(K3bVideoCdRip) vcdxrip warning, %1" ).arg( text ) << endl; + emit debuggingOutput( "vcdxrip", text ); + parseInformation( text ); + } else { + kdDebug() << QString( "(K3bVideoCdRip) vcdxrip error, %1" ).arg( text ) << endl; + emit infoMessage( text, K3bJob::ERROR ); + } + } + } + } + } + } +} + + +void K3bVideoCdRip::slotVcdXRipFinished() +{ + if ( m_process->normalExit() ) { + // TODO: check the process' exitStatus() + switch ( m_process->exitStatus() ) { + case 0: + emit infoMessage( i18n( "Files successfully extracted." ), K3bJob::SUCCESS ); + break; + default: + emit infoMessage( i18n( "%1 returned an unknown error (code %2)." ).arg( "vcdxrip" ).arg( m_process->exitStatus() ), K3bJob::ERROR ); + emit infoMessage( i18n( "Please send me an email with the last output..." ), K3bJob::ERROR ); + cancelAll(); + jobFinished( false ); + return ; + } + } else { + emit infoMessage( i18n( "%1 did not exit cleanly." ).arg( "Vcdxrip" ), K3bJob::ERROR ); + cancelAll(); + jobFinished( false ); + return ; + } + + jobFinished( true ); +} + +void K3bVideoCdRip::parseInformation( QString text ) +{ + // parse warning + if ( text.contains( "encountered non-form2 sector" ) ) { + // I think this is an error not a warning. Finish ripping with invalid mpegs. + emit infoMessage( i18n( "%1 encountered non-form2 sector" ).arg("Vcdxrip"), K3bJob::ERROR ); + emit infoMessage( i18n( "leaving loop" ), K3bJob::ERROR ); + cancelAll(); + jobFinished( false ); + return; + } + + // parse extra info + else if ( text.contains( "detected extended VCD2.0 PBC files" ) ) + emit infoMessage( i18n( "detected extended VCD2.0 PBC files" ), K3bJob::INFO ); + + // parse startposition and extracting sequence info + // extracting avseq05.mpg... (start lsn 32603 (+28514)) + else if ( text.startsWith( "extracting" ) ) { + if ( text.contains( "(start lsn" ) ) { + int index = text.find( "(start lsn" ); + int end = text.find( " (+" ); + if ( end > 0) { + m_subPosition = text.mid( index + 11, end - index - 11 ).stripWhiteSpace().toLong(); + } + else { + // found segment here we can get only the start lsn :) + // extracting item0001.mpg... (start lsn 225, 1 segments) + int end = text.find( ",", index ); + int overallPos = text.mid( index + 11, end - index - 11 ).stripWhiteSpace().toLong(); + double relOverallWritten = ( ( double ) overallPos * 2352 ) / ( double ) m_videooptions ->getVideoCdSize() ; + int newpercent = ( int ) ( 100 * relOverallWritten ); + if ( newpercent > m_oldpercent ) { + emit percent( newpercent ); + m_oldpercent = newpercent; + } + } + + + index = 11; + end = text.find( "(start lsn" ); + emit newSubTask( i18n( "Extracting %1" ).arg( text.mid( index, end - index ).stripWhiteSpace() ) ); + } + // parse extracting files info + // extracting CDI/CDI_IMAG.RTF to _cdi_cdi_imag.rtf (lsn 258, size 1315168, raw 1) + else if ( text.contains( "(lsn" ) && text.contains( "size" ) ) { + int index = 11; + int end = text.find( "to" ); + QString extractFileName = text.mid( index, end - index ).stripWhiteSpace(); + index = text.find( " to " ); + end = text.find( " (lsn" ); + QString toFileName = text.mid( index + 4, end - index - 4 ).stripWhiteSpace(); + emit newSubTask( i18n( "Extracting %1 to %2" ).arg( extractFileName ).arg( toFileName ) ); + } + } +} + +QString K3bVideoCdRip::jobDescription() const +{ + return i18n( "Extracting %1" ).arg( m_videooptions ->getVideoCdDescription() ); +} + +QString K3bVideoCdRip::jobDetails() const +{ + return QString( "(%1)" ).arg ( KIO::convertSize( m_videooptions ->getVideoCdSize() ) ); +} + +#include "k3bvideocdrip.moc" diff --git a/src/rip/k3bvideocdrip.h b/src/rip/k3bvideocdrip.h new file mode 100644 index 0000000..fe0d6f9 --- /dev/null +++ b/src/rip/k3bvideocdrip.h @@ -0,0 +1,74 @@ +/* +* +* $Id: k3bvideocdrip.h 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003 Christian Kvasny +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +#ifndef K3BVIDEOCDRIP_H +#define K3BVIDEOCDRIP_H + +#include +#include +#include "k3bvideocdrippingoptions.h" + +class QString; +class KProcess; +class QDataStream; + +class K3bVideoCdRip : public K3bJob +{ + Q_OBJECT + + public: + K3bVideoCdRip( K3bJobHandler*, K3bVideoCdRippingOptions* options, QObject* parent = 0, const char* name = 0 ); + ~K3bVideoCdRip(); + + enum { CDROM, BIN_IMAGE, NRG_IMAGE }; + + QString jobDescription() const; + QString jobDetails() const; + + public slots: + void start(); + void cancel(); + + private slots: + void cancelAll(); + + protected slots: + void slotVcdXRipFinished(); + void slotParseVcdXRipOutput( KProcess*, char* output, int len ); + + private: + void vcdxRip(); + void parseInformation( QString ); + + enum { stageUnknown, stageScan, stageFinished, _stage_max }; + + int m_stage; + int m_bytesFinished; + int m_ripsourceType; + int m_oldpercent; + + long m_subPosition; + + QString m_collectedOutput; + + K3bVideoCdRippingOptions * m_videooptions; + + bool m_canceled; + + KProcess* m_process; + +}; + +#endif diff --git a/src/rip/k3bvideocdrippingdialog.cpp b/src/rip/k3bvideocdrippingdialog.cpp new file mode 100644 index 0000000..490f531 --- /dev/null +++ b/src/rip/k3bvideocdrippingdialog.cpp @@ -0,0 +1,260 @@ +/* +* +* $Id: k3bvideocdrippingdialog.cpp 640370 2007-03-07 19:44:32Z trueg $ +* Copyright (C) 2003 Christian Kvasny +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + + +// kde include +#include +#include +#include +#include +#include +#include +#include + +// qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// k3b includes +#include "k3bvideocdrippingdialog.h" +#include "k3bvideocdrip.h" + +#include +#include +#include +#include + +K3bVideoCdRippingDialog::K3bVideoCdRippingDialog( K3bVideoCdRippingOptions* options, QWidget* parent, const char* name ) + : K3bInteractionDialog( parent, name, + i18n( "Video CD Ripping" ), + QString::null, + START_BUTTON|CANCEL_BUTTON, + START_BUTTON, + "Video CD Ripping" ), // config group + m_videooptions( options ) +{ + setupGui(); + setupContextHelp(); +} + + +K3bVideoCdRippingDialog::~K3bVideoCdRippingDialog() +{ +} + + +void K3bVideoCdRippingDialog::setupGui() +{ + QWidget * frame = mainWidget(); + QGridLayout* MainLayout = new QGridLayout( frame ); + MainLayout->setSpacing( KDialog::spacingHint() ); + MainLayout->setMargin( 0 ); + + // ---------------------------------------------------- Directory group --- + QGroupBox* groupDirectory = new QGroupBox( 0, Qt::Vertical, i18n( "Destination Directory" ), frame ); + groupDirectory->layout() ->setSpacing( KDialog::spacingHint() ); + groupDirectory->layout() ->setMargin( KDialog::marginHint() ); + + QGridLayout* groupDirectoryLayout = new QGridLayout( groupDirectory->layout() ); + groupDirectoryLayout->setAlignment( Qt::AlignTop ); + + QLabel* rippathLabel = new QLabel( i18n( "Rip files to:" ), groupDirectory ); + m_editDirectory = new KURLRequester( groupDirectory, "m_editDirectory" ); + m_editDirectory->setURL( QDir::homeDirPath() ); + m_editDirectory->setMode( KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly ); + + rippathLabel->setBuddy( m_editDirectory ); + + QHBox* freeSpaceBox = new QHBox( groupDirectory ); + freeSpaceBox->setSpacing( KDialog::spacingHint() ); + ( void ) new QLabel( i18n( "Free space in directory:" ), freeSpaceBox, "FreeSpaceLabel" ); + m_labelFreeSpace = new QLabel( " ", freeSpaceBox, "m_labelFreeSpace" ); + m_labelFreeSpace->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) ); + + QHBox* necessarySizeBox = new QHBox( groupDirectory ); + necessarySizeBox->setSpacing( KDialog::spacingHint() ); + ( void ) new QLabel( i18n( "Necessary storage size:" ), necessarySizeBox, "StorSize" ); + m_labelNecessarySize = new QLabel( " ", necessarySizeBox, "m_labelNecessarySize" ); + m_labelNecessarySize->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) ); + + + groupDirectoryLayout->addWidget( rippathLabel, 0, 0 ); + groupDirectoryLayout->addWidget( m_editDirectory, 0, 1 ); + groupDirectoryLayout->addWidget( freeSpaceBox, 1, 1 ); + groupDirectoryLayout->addWidget( necessarySizeBox, 2, 1 ); + + // ---------------------------------------------------- Options group --- + QGroupBox* groupOptions = new QGroupBox( 4, Qt::Vertical, i18n( "Settings" ), frame ); + + m_ignoreExt = new QCheckBox( i18n( "Ignore /EXT/PSD_X.VCD" ), groupOptions ); + + m_sector2336 = new QCheckBox( i18n( "Use 2336 byte sector mode for image file" ), groupOptions ); + // Only available for image file ripping + m_sector2336->setEnabled( false ); + m_sector2336->setChecked( false ); + + m_extractXML = new QCheckBox( i18n( "Extract XML structure" ), groupOptions ); + + + MainLayout->addWidget( groupDirectory, 0, 0 ); + MainLayout->addWidget( groupOptions, 1, 0 ); + MainLayout->setRowStretch( 0, 1 ); + + setStartButtonText( i18n( "Start Ripping" ), i18n( "Starts extracting the selected VideoCd tracks" ) ); + // ---------------------------------------------------------------------------------- + + connect( m_editDirectory, SIGNAL(textChanged(const QString&)), this, SLOT(slotUpdateFreeSpace()) ); + + m_labelNecessarySize ->setText( KIO::convertSize( m_videooptions ->getVideoCdSize() ) ); +} + + +void K3bVideoCdRippingDialog::setupContextHelp() +{ + QToolTip::add( m_labelFreeSpace, i18n("Free space on destination directory: %1").arg( m_editDirectory ->url() ) ); + + QToolTip::add( m_labelNecessarySize, i18n("Necessary space for extracted files") ); + + QToolTip::add( m_ignoreExt, i18n("Ignore extended PSD") ); + QWhatsThis::add( m_ignoreExt, i18n("

Ignore extended PSD (located in the ISO-9660 filesystem under `/EXT/PSD_X.VCD') and use the standard PSD.

") ); + + QToolTip::add( m_sector2336, i18n("Assume a 2336-byte sector mode") ); + QWhatsThis::add( m_sector2336, i18n("

This option only makes sense if you are reading from a BIN CD disk image. This indicates to `vcdxrip' to assume a 2336-byte sector mode for image file.

" + "Note: This option is slated to disappear.") ); + + QToolTip::add( m_extractXML, i18n("Create XML description file.") ); + QWhatsThis::add( m_extractXML, i18n("

This option creates an XML description file with all video CD information.

" + "

This file will always contain all of the information.

" + "

Example: If you only extract sequences, the description file will also hold the information for files and segments.

" + "

The filename is the same as the video CD name, with a .xml extension. The default is VIDEOCD.xml.

") ); +} + +void K3bVideoCdRippingDialog::slotStartClicked() +{ + + QStringList filesExists; + QDir d; + d.setPath( m_editDirectory ->url() ); + if( !d.exists() ) { + if( KMessageBox::warningYesNo( this, i18n("Image folder '%1' does not exist. Do you want K3b to create it?").arg( m_editDirectory->url() ) ) + == KMessageBox::Yes ) { + if( !KStandardDirs::makeDir( m_editDirectory->url() ) ) { + KMessageBox::error( this, i18n("Failed to create folder '%1'.").arg( m_editDirectory->url() ) ); + return; + } + } + } + const QFileInfoList* list = d.entryInfoList(); + QFileInfoListIterator it( *list ); + QFileInfo* fi; + while ( ( fi = it.current() ) != 0 ) { + if ( fi ->fileName() != "." && fi ->fileName() != ".." ) + filesExists.append( QString( "%1 (%2)" ).arg( QFile::encodeName( fi ->fileName() ) ).arg( KIO::convertSize( fi ->size() ) ) ); + ++it; + } + + if( !filesExists.isEmpty() ) + if( KMessageBox::questionYesNoList( this, + i18n("Continue although the folder is not empty?"), + filesExists, + i18n("Files Exist"),KStdGuiItem::cont(),KStdGuiItem::cancel() ) == KMessageBox::No ) + return; + + m_videooptions ->setVideoCdIgnoreExt( m_ignoreExt ->isChecked() ); + m_videooptions ->setVideoCdSector2336( m_sector2336 ->isChecked() ); + m_videooptions ->setVideoCdExtractXml( m_extractXML ->isChecked() ); + m_videooptions ->setVideoCdDestination( m_editDirectory ->url() ); + + K3bJobProgressDialog ripDialog( kapp->mainWidget(), "Ripping" ); + K3bVideoCdRip * rip = new K3bVideoCdRip( &ripDialog, m_videooptions ); + + hide(); + ripDialog.startJob( rip ); + + delete rip; + + close(); +} + +void K3bVideoCdRippingDialog::slotFreeSpace(const QString&, + unsigned long, + unsigned long, + unsigned long kbAvail) +{ + m_labelFreeSpace->setText( KIO::convertSizeFromKB(kbAvail) ); + + m_freeSpace = kbAvail; + + if( m_freeSpace < m_videooptions ->getVideoCdSize() /1024 ) + m_labelNecessarySize->setPaletteForegroundColor( red ); + else + m_labelNecessarySize->setPaletteForegroundColor( m_labelFreeSpace->paletteForegroundColor() ); + + QTimer::singleShot( 1000, this, SLOT(slotUpdateFreeSpace()) ); +} + + +void K3bVideoCdRippingDialog::slotUpdateFreeSpace() +{ + QString path = m_editDirectory->url(); + + if( !QFile::exists( path ) ) + path.truncate( path.findRev('/') ); + + unsigned long size, avail; + if( K3b::kbFreeOnFs( path, size, avail ) ) + slotFreeSpace( path, size, 0, avail ); + else + m_labelFreeSpace->setText("-"); +} + +void K3bVideoCdRippingDialog::loadK3bDefaults() +{ + m_editDirectory->setURL( QDir::homeDirPath() ); + m_ignoreExt ->setChecked( false ); + m_sector2336 ->setChecked( false ); + m_extractXML ->setChecked( false ); + + slotUpdateFreeSpace(); +} + +void K3bVideoCdRippingDialog::loadUserDefaults( KConfigBase* c ) +{ + m_editDirectory ->setURL( c->readPathEntry( "last ripping directory", QDir::homeDirPath() ) ); + m_ignoreExt ->setChecked( c->readBoolEntry( "ignore ext", false ) ); + m_sector2336 ->setChecked( c->readBoolEntry( "sector 2336", false ) ); + m_extractXML ->setChecked( c->readBoolEntry( "extract xml", false ) ); + + slotUpdateFreeSpace(); +} + +void K3bVideoCdRippingDialog::saveUserDefaults( KConfigBase* c ) +{ + c->writePathEntry( "last ripping directory", m_editDirectory->url() ); + c->writeEntry( "ignore ext", m_ignoreExt ->isChecked( ) ); + c->writeEntry( "sector 2336", m_sector2336 ->isChecked( ) ); + c->writeEntry( "extract xml", m_extractXML ->isChecked( ) ); +} + +#include "k3bvideocdrippingdialog.moc" diff --git a/src/rip/k3bvideocdrippingdialog.h b/src/rip/k3bvideocdrippingdialog.h new file mode 100644 index 0000000..98d16f6 --- /dev/null +++ b/src/rip/k3bvideocdrippingdialog.h @@ -0,0 +1,73 @@ +/* + * + * $Id: k3bvideocdrippingdialog.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Christian Kvasny + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + + +#ifndef _K3B_VIDEOCD_RIPPING_DIALOG_H_ +#define _K3B_VIDEOCD_RIPPING_DIALOG_H_ + +#include + +#include +#include +#include "k3bvideocdrippingoptions.h" + +class KListView; +class QCheckBox; +class QLabel; +class QSpinBox; +class QComboBox; +class QToolButton; +class KURLRequester; +class K3bTempDirSelectionWidget; + +class K3bVideoCdRippingDialog : public K3bInteractionDialog +{ + Q_OBJECT + + public: + K3bVideoCdRippingDialog( K3bVideoCdRippingOptions* options, QWidget* parent = 0, const char* name = 0 ); + ~K3bVideoCdRippingDialog(); + + private: + void setupGui(); + void setupContextHelp(); + + void loadK3bDefaults(); + void loadUserDefaults( KConfigBase* ); + void saveUserDefaults( KConfigBase* ); + + K3bTempDirSelectionWidget* m_tempDirSelectionWidget; + + KURLRequester* m_editDirectory; + + QLabel* m_labelFreeSpace; + QLabel* m_labelNecessarySize; + QCheckBox* m_ignoreExt; + QCheckBox* m_sector2336; + QCheckBox* m_extractXML; + + K3bVideoCdRippingOptions* m_videooptions; + + unsigned long m_freeSpace; + + private slots: + void slotStartClicked(); + + void slotUpdateFreeSpace(); + void slotFreeSpace(const QString&, unsigned long, unsigned long, unsigned long); + +}; + +#endif diff --git a/src/rip/k3bvideocdrippingoptions.h b/src/rip/k3bvideocdrippingoptions.h new file mode 100644 index 0000000..bd8aea9 --- /dev/null +++ b/src/rip/k3bvideocdrippingoptions.h @@ -0,0 +1,74 @@ +/* + * + * $Id: k3bvideocdrippingoptions.h 619556 2007-01-03 17:38:12Z trueg $ + * Copyright (C) 2003 Christian Kvasny + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_VIDEOCD_OPTIONS_H_ +#define _K3B_VIDEOCD_OPTIONS_H_ + +#include + +class K3bVideoCdRippingOptions +{ + public: + K3bVideoCdRippingOptions() + : m_videocdsize( 0 ), + m_videocdsource( "/dev/cdrom" ), + m_videocddestination( "/tmp" ), + m_videocddescription( i18n( "Video CD" ) ), + m_videocdripfiles( false ), + m_videocdripsegments( false ), + m_videocdripsequences( false ), + m_ignoreExt( false ), + m_sector2336( false ), + m_extractXML( false ) + {} + + void setVideoCdSize( unsigned long size ) { m_videocdsize = size;} + void setVideoCdSource( const QString& source ) { m_videocdsource = source;} + void setVideoCdDestination( const QString& destination ) { m_videocddestination = destination;} + void setVideoCdDescription( const QString& description ) { m_videocddescription = description;} + void setVideoCdRipFiles( bool ripfiles ) { m_videocdripfiles = ripfiles;} + void setVideoCdRipSegments( bool ripsegments ) { m_videocdripsegments = ripsegments;} + void setVideoCdRipSequences( bool ripsequences ) { m_videocdripsequences = ripsequences;} + void setVideoCdIgnoreExt( bool ignoreext ) { m_ignoreExt = ignoreext;} + void setVideoCdSector2336( bool sector2336 ) { m_sector2336 = sector2336;} + void setVideoCdExtractXml( bool extractxml ) { m_extractXML = extractxml;} + + unsigned long getVideoCdSize( ) { return m_videocdsize;} + QString getVideoCdSource( ) { return m_videocdsource;} + QString getVideoCdDestination( ) { return m_videocddestination;} + QString getVideoCdDescription( ) { return m_videocddescription;} + bool getVideoCdRipFiles( ) { return m_videocdripfiles;} + bool getVideoCdRipSegments( ) { return m_videocdripsegments;} + bool getVideoCdRipSequences( ) { return m_videocdripsequences;} + bool getVideoCdIgnoreExt( ) { return m_ignoreExt;} + bool getVideoCdSector2336( ) { return m_sector2336;} + bool getVideoCdExtractXml( ) { return m_extractXML;} + + private: + unsigned long m_videocdsize; + + QString m_videocdsource; + QString m_videocddestination; + QString m_videocddescription; + + bool m_videocdripfiles; + bool m_videocdripsegments; + bool m_videocdripsequences; + bool m_ignoreExt; + bool m_sector2336; + bool m_extractXML; +}; + +#endif diff --git a/src/rip/k3bvideocdview.cpp b/src/rip/k3bvideocdview.cpp new file mode 100644 index 0000000..c0f5ae2 --- /dev/null +++ b/src/rip/k3bvideocdview.cpp @@ -0,0 +1,509 @@ +/* +* +* $Id: k3bvideocdview.cpp 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003 Christian Kvasny +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + +// kde includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// qt includes +#include +#include +#include +#include +#include +#include +#include + +// k3b includes +#include "k3bvideocdview.h" +#include "k3bvideocdrippingdialog.h" + +#include +#include +#include +#include +#include +#include +#include + + +class K3bVideoCdView::VideoTrackViewItem : public QListViewItem +{ + public: + VideoTrackViewItem( QListViewItem* parent, QListViewItem* after ) + : QListViewItem( parent, after ) + { + setSelectable( false ); + } + + VideoTrackViewItem( QListView* parent, QListViewItem* after ) + : QListViewItem( parent, after ) + { + setSelectable( false ); + } + + VideoTrackViewItem( QListViewItem* parent, + const QString& name, + const QString& id, + int _trackNumber, + const K3b::Msf& length ) + : QListViewItem( parent ) + { + setText( 0, QString( "%1. %2" ).arg( _trackNumber ).arg( id ) ); + setText( 1, name ); + if ( length > 0 ) { + setText( 2, length.toString() ); + setText( 3, KIO::convertSize( length.mode2Form2Bytes() ) ); + } + + trackNumber = _trackNumber; + setSelectable( false ); + } + + int trackNumber; + + void updateData( const K3bVideoCdInfoResultEntry& resultEntry ) + { + setText( 0, QString( "%1. %2" ).arg( trackNumber ).arg( resultEntry.id ) ); + setText( 1, resultEntry.name ); + } + +}; + +class K3bVideoCdView::VideoTrackViewCheckItem : public QCheckListItem +{ + public: + VideoTrackViewCheckItem( QListViewItem* parent, + const QString& desc ) + : QCheckListItem( parent, + QString::null, + QCheckListItem::CheckBox ) + { + setText( 0, desc ); + + setOn( true ); + } + + VideoTrackViewCheckItem( QListView* parent, + const QString& desc ) + : QCheckListItem( parent, + QString::null, + QCheckListItem::CheckBox ) + { + setText( 0, desc ); + + setOn( true ); + } + + VideoTrackViewCheckItem( VideoTrackViewCheckItem* parent, + const QString& desc ) + : QCheckListItem( parent, + QString::null, + QCheckListItem::CheckBox ) + { + setText( 0, desc ); + + setOn( true ); + } + + void updateData( const K3b::Msf& length, bool form2 = false ) + { + setText( 2, length.toString() ); + if ( form2 ) + setText( 3, KIO::convertSize( length.mode2Form2Bytes() ) ); + else + setText( 3, KIO::convertSize( length.mode2Form1Bytes() ) ); + } + +}; + +K3bVideoCdView::K3bVideoCdView( QWidget* parent, const char *name ) + : K3bMediaContentsView( true, + K3bMedium::CONTENT_VIDEO_CD, + K3bDevice::MEDIA_CD_ALL, + K3bDevice::STATE_INCOMPLETE|K3bDevice::STATE_COMPLETE, + parent, name ) +{ + QGridLayout * mainGrid = new QGridLayout( mainWidget() ); + + // toolbox + // ---------------------------------------------------------------------------------- + QHBoxLayout* toolBoxLayout = new QHBoxLayout( 0, 0, 0, "toolBoxLayout" ); + m_toolBox = new K3bToolBox( mainWidget() ); + toolBoxLayout->addWidget( m_toolBox ); + QSpacerItem* spacer = new QSpacerItem( 10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum ); + toolBoxLayout->addItem( spacer ); + m_labelLength = new QLabel( mainWidget() ); + m_labelLength->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) ); + toolBoxLayout->addWidget( m_labelLength ); + + // the track view + // ---------------------------------------------------------------------------------- + m_trackView = new K3bListView( mainWidget() ); + m_trackView->setFullWidth( true ); + m_trackView->setAllColumnsShowFocus( true ); + m_trackView->setSelectionMode( QListView::Single ); + m_trackView->setDragEnabled( true ); + m_trackView->addColumn( i18n( "Item Name" ) ); + m_trackView->addColumn( i18n( "Extracted Name" ) ); + m_trackView->addColumn( i18n( "Length" ) ); + m_trackView->addColumn( i18n( "Size" ) ); + + m_trackView->header() ->setClickEnabled( false ); + + m_trackView->setItemsRenameable( false ); + m_trackView->setRootIsDecorated( true ); + + connect( m_trackView, SIGNAL( contextMenu( KListView*, QListViewItem*, const QPoint& ) ), + this, SLOT( slotContextMenu( KListView*, QListViewItem*, const QPoint& ) ) ); + connect( m_trackView, SIGNAL( selectionChanged( QListViewItem* ) ), + this, SLOT( slotTrackSelectionChanged( QListViewItem* ) ) ); + connect( m_trackView, SIGNAL( clicked( QListViewItem* ) ), + this, SLOT( slotStateChanged( QListViewItem* ) ) ); + connect( m_trackView, SIGNAL( spacePressed( QListViewItem* ) ), + this, SLOT( slotStateChanged( QListViewItem* ) ) ); + + + mainGrid->addLayout( toolBoxLayout, 0, 0 ); + mainGrid->addWidget( m_trackView, 1, 0 ); + + initActions(); + slotTrackSelectionChanged( 0 ); + + m_videocdinfo = 0L; + m_videooptions = new K3bVideoCdRippingOptions(); + + m_contentList.clear(); +} + + +K3bVideoCdView::~K3bVideoCdView() +{ + delete m_videocdinfo; + delete m_videooptions; +} + + +void K3bVideoCdView::reloadMedium() +{ + m_toc = medium().toc(); + + m_trackView->clear(); + + m_trackView->setEnabled( false ); + m_toolBox->setEnabled( false ); + QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); + + m_contentList.append( new VideoTrackViewCheckItem( m_trackView, i18n("Video CD MPEG tracks") ) ); + m_contentList.append( new VideoTrackViewCheckItem( m_trackView, i18n("Video CD DATA track" ) ) ); + + ( ( VideoTrackViewCheckItem* ) m_contentList[ 0 ] ) ->setOpen( true ); + + // create a listviewItem for every video track + int index = 0; + m_videocddatasize = 0; + m_videocdmpegsize = 0; + + K3b::Msf sequenceSize; + + for ( K3bDevice::Toc::const_iterator it = m_toc.begin(); + it != m_toc.end(); ++it ) { + + if ( index > 0 ) { + K3b::Msf length( ( *it ).length() ); + sequenceSize += length; + m_videocdmpegsize += length.mode2Form2Bytes(); + ( void ) new VideoTrackViewItem( ( VideoTrackViewCheckItem* ) m_contentList[ 0 ], i18n( "Sequence-%1" ).arg( index ), "", index, length ); + } else { + K3b::Msf length( ( *it ).length() ); + m_videocddatasize += length.mode2Form1Bytes(); + ( ( VideoTrackViewCheckItem* ) m_contentList[ 1 ] ) ->updateData( length ); + ( void ) new VideoTrackViewCheckItem( ( VideoTrackViewCheckItem* ) m_contentList[ 1 ], i18n( "Files" ) ); + ( void ) new VideoTrackViewCheckItem( ( VideoTrackViewCheckItem* ) m_contentList[ 1 ], i18n( "Segments" ) ); + } + + index++; + } + + ( ( VideoTrackViewCheckItem* ) m_contentList[ 0 ] ) ->updateData( sequenceSize, true ); + + m_videooptions ->setVideoCdSource( device()->devicename() ); + + m_videocdinfo = new K3bVideoCdInfo( this ); + m_videocdinfo->info( device()->devicename() ); + + connect( m_videocdinfo, SIGNAL( infoFinished( bool ) ), + this, SLOT( slotVideoCdInfoFinished( bool ) ) ); + +} + +void K3bVideoCdView::slotVideoCdInfoFinished( bool success ) +{ + if ( success ) { + m_videocdinfoResult = m_videocdinfo->result(); + updateDisplay(); + } + + m_trackView->setEnabled( true ); + m_toolBox->setEnabled( true ); + QApplication::restoreOverrideCursor(); + +} + +void K3bVideoCdView::updateDisplay() +{ + // update the listview + + VideoTrackViewItem * item = ( VideoTrackViewItem* ) m_contentList[ 0 ] ->firstChild(); + int index = 0; + while ( item ) { + item->updateData( m_videocdinfoResult.entry( index, K3bVideoCdInfoResult::SEQUENCE ) ); + item = ( VideoTrackViewItem* ) item->nextSibling(); + index++; + } + + VideoTrackViewCheckItem* check_item = ( VideoTrackViewCheckItem* ) m_contentList[ 1 ] ->firstChild(); + while ( check_item ) { + if ( check_item->key( 0, false ).compare( i18n( "Files" ) ) == 0 ) { + if ( domTree.setContent( m_videocdinfoResult.xmlData ) ) { + + QDomElement root = domTree.documentElement(); + QDomNode node; + node = root.firstChild(); + while ( !node.isNull() ) { + if ( node.isElement() && node.nodeName() == "filesystem" ) { + QDomElement body = node.toElement(); + buildTree( check_item, body ); + break; + } + node = node.nextSibling(); + } + } + } else { + for ( index = 0; index < m_videocdinfoResult.foundEntries( K3bVideoCdInfoResult::SEGMENT ); index++ ) { + ( void ) new VideoTrackViewItem( check_item, m_videocdinfoResult.entry( index, K3bVideoCdInfoResult::SEGMENT ).name, m_videocdinfoResult.entry( index, K3bVideoCdInfoResult::SEGMENT ).id , index + 1, 0 ); + } + } + check_item = ( VideoTrackViewCheckItem* ) check_item->nextSibling(); + } + + if ( !m_videocdinfoResult.volumeId.isEmpty() ) { + QString description = m_videocdinfoResult.volumeId + " (" + m_videocdinfoResult.type + " " + m_videocdinfoResult.version + ")" ; + setTitle( description ); + m_videooptions ->setVideoCdDescription( description ); + } + else + setTitle( i18n( "Video CD" ) ); + + m_labelLength->setText( i18n( "1 track (%1)", "%n tracks (%1)", m_toc.count() ).arg( K3b::Msf( m_toc.length() ).toString() ) ); +} + + +void K3bVideoCdView::initActions() +{ + m_actionCollection = new KActionCollection( this ); + + KAction* actionSelectAll = KStdAction::selectAll( this, SLOT( slotSelectAll() ), + m_actionCollection, "select_all" ); + KAction* actionDeselectAll = KStdAction::deselect( this, SLOT( slotDeselectAll() ), + m_actionCollection, "deselect_all" ); + actionDeselectAll->setText( i18n( "Dese&lect All" ) ); + KAction* actionSelect = new KAction( i18n( "Select Track" ), 0, 0, this, + SLOT( slotSelect() ), actionCollection(), + "select_track" ); + KAction* actionDeselect = new KAction( i18n( "Deselect Track" ), 0, 0, this, + SLOT( slotDeselect() ), actionCollection(), + "deselect_track" ); + + KAction* actionStartRip = new KAction( i18n( "Start Ripping" ), "run", 0, this, + SLOT( startRip() ), actionCollection(), "start_rip" ); + + // TODO: set the actions tooltips and whatsthis infos + + // setup the popup menu + m_popupMenu = new KActionMenu( actionCollection(), "popup_menu" ); + KAction* separator = new KActionSeparator( actionCollection(), "separator" ); + m_popupMenu->insert( actionSelect ); + m_popupMenu->insert( actionDeselect ); + m_popupMenu->insert( actionSelectAll ); + m_popupMenu->insert( actionDeselectAll ); + m_popupMenu->insert( separator ); + m_popupMenu->insert( actionStartRip ); + + // setup the toolbox + m_toolBox->addButton( actionStartRip, true ); +} + + +void K3bVideoCdView::slotContextMenu( KListView*, QListViewItem*, const QPoint& p ) +{ + m_popupMenu->popup( p ); +} + + +void K3bVideoCdView::slotTrackSelectionChanged( QListViewItem* item ) +{ + actionCollection() ->action( "select_track" ) ->setEnabled( item != 0 ); + actionCollection() ->action( "deselect_track" ) ->setEnabled( item != 0 ); +} + +void K3bVideoCdView::slotStateChanged( QListViewItem* item ) +{ + /* > QT 3.1 + if ( !item == 0 && item ->isSelectable() ) { + if ( ( ( VideoTrackViewCheckItem* ) item) ->state() == QCheckListItem::On) + slotSelect(); + else if ( ( ( VideoTrackViewCheckItem* ) item) ->state() == QCheckListItem::Off) + slotDeselect(); + } + */ + if ( !item == 0 && item ->isSelectable() ) { + if ( ( ( VideoTrackViewCheckItem* ) item) ->isOn() ) + slotSelect(); + else + slotDeselect(); + } +} + +void K3bVideoCdView::startRip() +{ + + int selectedItems = 0; + for ( QListViewItemIterator it( m_trackView ); it.current(); ++it ) { + if ( it.current() ->isSelectable() ) { + if ( ( ( ( VideoTrackViewCheckItem* ) it.current()) ->key( 0, false ).compare( i18n("Video CD MPEG tracks" ) ) == 0 ) && ( ( VideoTrackViewCheckItem* ) it.current() ) ->isOn() ) { + m_videooptions ->setVideoCdRipSequences( true ); + selectedItems++; + } + else if ( ( ( ( VideoTrackViewCheckItem* ) it.current()) ->key( 0, false ).compare( i18n("Files" ) ) == 0 ) && ( ( VideoTrackViewCheckItem* ) it.current() ) ->isOn() ) { + m_videooptions ->setVideoCdRipFiles( true ); + selectedItems++; + } + else if ( ( ( ( VideoTrackViewCheckItem* ) it.current()) ->key( 0, false ).compare( i18n("Segments" ) ) == 0 ) && ( ( VideoTrackViewCheckItem* ) it.current() ) ->isOn() ) { + m_videooptions ->setVideoCdRipSegments( true ); + selectedItems++; + } + } + } + + if( selectedItems == 0 ) { + KMessageBox::error( this, i18n("Please select the tracks to rip."), i18n("No Tracks Selected") ); + } + else { + unsigned long videocdsize = 0; + // TODO: split SegmentSize and FileSize. Have no infos now + if ( m_videooptions ->getVideoCdRipSegments() || m_videooptions ->getVideoCdRipFiles()) + videocdsize += m_videocddatasize; + if ( m_videooptions ->getVideoCdRipSequences() ) + videocdsize += m_videocdmpegsize; + + kdDebug() << QString("(K3bVideoCdView::startRip()) m_videooptions ->setVideoCdSize( %1)").arg( videocdsize ) << endl; + m_videooptions ->setVideoCdSize( videocdsize ); + K3bVideoCdRippingDialog rip( m_videooptions, this ); + rip.exec(); + } +} + +void K3bVideoCdView::slotSelectAll() +{ + for ( QListViewItemIterator it( m_trackView ); it.current(); ++it ) + if ( it.current() ->isSelectable() ) + ( ( VideoTrackViewCheckItem* ) it.current() ) ->setOn( true ); +} + +void K3bVideoCdView::slotDeselectAll() +{ + for ( QListViewItemIterator it( m_trackView ); it.current(); ++it ) + if ( it.current() ->isSelectable() ) + ( ( VideoTrackViewCheckItem* ) it.current() ) ->setOn( false ); +} + +void K3bVideoCdView::slotSelect() +{ + if ( QListViewItem * sel = m_trackView->selectedItem() ) { + ( ( VideoTrackViewCheckItem* ) sel) ->setOn( true ); + QListViewItem * item = sel ->firstChild(); + while ( item ) { + if ( item ->isSelectable() ) + ( ( VideoTrackViewCheckItem* ) item) ->setOn( true ); + + item = item->nextSibling(); + } + } +} + +void K3bVideoCdView::slotDeselect() +{ + if ( QListViewItem * sel = m_trackView->selectedItem() ) { + ( ( VideoTrackViewCheckItem* ) sel) ->setOn( false ); + QListViewItem * item = sel ->firstChild(); + while ( item ) { + if ( item ->isSelectable() ) + ( ( VideoTrackViewCheckItem* ) item) ->setOn( false ); + + item = item->nextSibling(); + } + } +} + +void K3bVideoCdView::enableInteraction( bool b ) +{ + actionCollection()->action( "start_rip" )->setEnabled( b ); +} + +void K3bVideoCdView::buildTree( QListViewItem *parentItem, const QDomElement &parentElement, const QString& pname ) +{ + VideoTrackViewItem * thisItem = 0; + QDomNode node = parentElement.firstChild(); + + while ( !node.isNull() ) { + if ( node.isElement() && node.nodeName() == "folder" || node.nodeName() == "file" ) { + if ( parentItem == 0 ) + thisItem = new VideoTrackViewItem( m_trackView, thisItem ); + else + thisItem = new VideoTrackViewItem( parentItem, thisItem ); + + QString txt = node.firstChild().toElement().text(); + thisItem->setText( 0, txt); + if ( node.nodeName() == "folder" ) { + buildTree( thisItem, node.toElement(), pname + "_" + txt.lower() ); + } + else { + thisItem->setText( 1, pname + "_" + txt.lower() ); + buildTree( thisItem, node.toElement(), pname ); + } + } else if ( node.isElement() && node.nodeName() == "segment-item" || node.nodeName() == "sequence-item" ) { + if ( parentItem == 0 ) + thisItem = new VideoTrackViewItem( m_trackView, thisItem ); + else + thisItem = new VideoTrackViewItem( parentItem, thisItem ); + + thisItem->setText( 0, node.toElement().attribute( "src" ) ); + + buildTree( thisItem, node.toElement() ); + } + + node = node.nextSibling(); + } +} + +#include "k3bvideocdview.moc" diff --git a/src/rip/k3bvideocdview.h b/src/rip/k3bvideocdview.h new file mode 100644 index 0000000..cb3e7a6 --- /dev/null +++ b/src/rip/k3bvideocdview.h @@ -0,0 +1,105 @@ +/* +* +* $Id: k3bvideocdview.h 619556 2007-01-03 17:38:12Z trueg $ +* Copyright (C) 2003 Christian Kvasny +* +* This file is part of the K3b project. +* Copyright (C) 1998-2007 Sebastian Trueg +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* See the file "COPYING" for the exact licensing terms. +*/ + + +#ifndef _K3B_VIDEOCDVIEW_H_ +#define _K3B_VIDEOCDVIEW_H_ + +#include + +#include +#include + +#include "k3bvideocdinfo.h" + +class KActionCollection; +class KActionMenu; +class KListView; + +class QLabel; +class QListViewItem; + +class K3bListView; +class K3bToolBox; +class K3bVideoCdRippingOptions; + +namespace K3bDevice +{ + class DiskInfoDetector; + class Toc; + class Device; +} + + +class K3bVideoCdView : public K3bMediaContentsView +{ + Q_OBJECT + + public: + K3bVideoCdView( QWidget* parent = 0, const char * name = 0 ); + ~K3bVideoCdView(); + + KActionCollection* actionCollection() const + { + return m_actionCollection; + } + + private slots: + void slotContextMenu( KListView*, QListViewItem*, const QPoint& ); + void slotTrackSelectionChanged( QListViewItem* ); + void slotStateChanged( QListViewItem* ); + void slotVideoCdInfoFinished( bool ); + + void startRip(); + void slotSelectAll(); + void slotDeselectAll(); + void slotSelect(); + void slotDeselect(); + + private: + + class VideoTrackViewCheckItem; + class VideoTrackViewItem; + + void reloadMedium(); + + void initActions(); + void updateDisplay(); + void enableInteraction( bool ); + void buildTree( QListViewItem *parentItem, const QDomElement &parentElement, const QString& pname = QString::null ); + + K3bDevice::Toc m_toc; + + KActionCollection* m_actionCollection; + KActionMenu* m_popupMenu; + + K3bVideoCdInfoResult m_videocdinfoResult; + K3bVideoCdInfo* m_videocdinfo; + K3bVideoCdRippingOptions* m_videooptions; + + K3bListView* m_trackView; + K3bToolBox* m_toolBox; + QLabel* m_labelLength; + + QDomDocument domTree; + + QValueList m_contentList; + + unsigned long m_videocddatasize; + unsigned long m_videocdmpegsize; + +}; + +#endif diff --git a/src/rip/videodvd/Makefile.am b/src/rip/videodvd/Makefile.am new file mode 100644 index 0000000..bac9b0a --- /dev/null +++ b/src/rip/videodvd/Makefile.am @@ -0,0 +1,16 @@ +AM_CPPFLAGS = -I$(srcdir)/../../../libk3b/core \ + -I$(srcdir)/../../../libk3bdevice \ + -I$(srcdir)/../../../libk3b/tools \ + -I$(srcdir)/../../../libk3b/videodvd/ \ + -I$(srcdir)/../../../libk3b/jobs/ \ + -I$(srcdir)/../.. \ + $(all_includes) + +METASOURCES = AUTO + +noinst_LTLIBRARIES = libvideodvdrip.la + +libvideodvdrip_la_SOURCES = base_k3bvideodvdrippingwidget.ui k3bvideodvdrippingview.cpp \ + k3bvideodvdrippingtitlelistview.cpp \ + k3bvideodvdrippingjob.cpp k3bvideodvdrippingwidget.cpp \ + k3bvideodvdrippingdialog.cpp k3bvideodvdrippingpreview.cpp diff --git a/src/rip/videodvd/base_k3bvideodvdrippingwidget.ui b/src/rip/videodvd/base_k3bvideodvdrippingwidget.ui new file mode 100644 index 0000000..72b03b4 --- /dev/null +++ b/src/rip/videodvd/base_k3bvideodvdrippingwidget.ui @@ -0,0 +1,721 @@ + +base_K3bVideoDVDRippingWidget +Seastian Trueg + + + Form1 + + + + 0 + 0 + 644 + 387 + + + + + unnamed + + + 0 + + + + textLabel4 + + + Please select the audio streams you want to include in every ripped title + + + + + m_titleView + + + true + + + true + + + + + tabWidget2 + + + + tab + + + Setti&ngs + + + + unnamed + + + + groupBox6 + + + Video Quality + + + + unnamed + + + + layout9 + + + + unnamed + + + + textLabel2_2 + + + Video Size: + + + + + m_comboVideoSize + + + + 1 + 0 + 1 + 0 + + + + + + m_buttonCustomPictureSize + + + &Custom... + + + + + + + layout9 + + + + unnamed + + + + textLabel2_3 + + + Video Bitrate: + + + + + m_spinVideoBitrate + + + true + + + + 1 + 0 + 1 + 0 + + + + kbps + + + 10000 + + + 1800 + + + + + + + + + groupBox1_2 + + + + 5 + 5 + 0 + 1 + + + + Target Folder + + + + unnamed + + + + textLabel1_2 + + + Free space in directory: + + + + + m_editBaseDir + + + + + m_labelFreeSpace + + + - + + + AlignVCenter|AlignRight + + + + + textLabel1_2_2 + + + Space needed: + + + + + m_labelNeededSpace + + + - + + + AlignVCenter|AlignRight + + + + + + + groupBox4 + + + Audio Quality + + + + unnamed + + + + m_stackAudioQuality + + + + m_stackPageAudioQualityMp3 + + + 0 + + + + unnamed + + + 0 + + + + m_checkAudioVBR + + + Variable &Bitrate + + + + + layout4 + + + + unnamed + + + + m_labelAudioBitrate + + + Audio Bitrate: + + + + + m_comboAudioBitrate + + + false + + + + + + + + + m_stackPageAudioQualityAC3Pt + + + 1 + + + + unnamed + + + 0 + + + + m_labelNoAudioSettings + + + + 5 + 7 + 0 + 0 + + + + <p>No Audio Quality settings available for <em>AC3 pass-through</em>. The audio stream from the Video DVD is used without any changes. + + + + + + + m_stackPageAudioQualityAC3 + + + 2 + + + + unnamed + + + 0 + + + + layout8 + + + + unnamed + + + + textLabel1_3 + + + Audio Bitrate: + + + + + m_spinAudioBitrate + + + kbps + + + 640 + + + 32 + + + 2 + + + 128 + + + + + + + spacer3 + + + Vertical + + + Expanding + + + + 20 + 0 + + + + + + + + + + + groupBox1 + + + Filetype + + + + unnamed + + + + layout10 + + + + unnamed + + + + textLabel1 + + + Video Codec: + + + + + m_comboVideoCodec + + + Select the Video codec used to encode the DVD titles + + + + + + + layout9 + + + + unnamed + + + + textLabel2 + + + Audio Codec: + + + + + m_comboAudioCodec + + + Select the Audio codec used to encode the DVD titles + + + + + + + + + + + TabPage + + + File Namin&g + + + + unnamed + + + + layout14 + + + + unnamed + + + + textLabel1_4 + + + Ripped files pattern: + + + + + m_comboFilenamePattern + + + + 1 + 0 + 1 + 0 + + + + true + + + true + + + false + + + + + + + m_specialStringsLabel + + + See special strings + + + AlignVCenter|AlignRight + + + + + layout8 + + + + unnamed + + + + m_checkBlankReplace + + + Replace all &blanks with: + + + + + m_editBlankReplace + + + false + + + + 7 + 0 + 1 + 0 + + + + _ + + + + + + + spacer5 + + + Vertical + + + Expanding + + + + 5 + 1 + + + + + + + + tab + + + &Advanced + + + + unnamed + + + + m_checkTwoPassEncoding + + + &2-pass encoding + + + Alt+2 + + + true + + + Enable 2-pass encoding + + + <p>If this option is checked K3b encodes the video titles in two passes. The first pass is used to gather information about the video in order to improve the distribution of bits in the second pass. The resulting video will have a higher quality using a variable bitrate. +<p>If this option is not checked K3b will create video files with a constant bitrate and a lower quality. +<p>2-pass encoding results in a doubled encoding time. + + + + + m_checkAutoClipping + + + Automatic &Video Clipping + + + true + + + Automatically detect the black borders of the video + + + <p>Most Video DVDs are encoded in a letterboxed format. <em>Letterboxed</em> refers to black bars used at the top and bottom (and sometimes at the sides) of the video to force it into one of the aspect ratios supported by the Video DVD standard. +<p>If this option is checked K3b will automatically detect and remove these black bars from the resulting video. +<p>Although this method is very reliable there may be problems if the source material is exceptionally short or dark. + + + + + m_checkAudioResampling + + + Resample Audio to &44.1 KHz + + + Alt+4 + + + true + + + Change the sample rate of the audio stream to 44.1 KHz + + + <p>Video DVD audio streams normally are encoded with a sampling rate of 48000 Hz. Audio CDs on the other hand are encoded with a sampling rate of 44100 Hz. +<p>If this option is checked K3b will change the sampling rate of the audio stream to 44100 Hz. + + + + + m_checkLowPriority + + + Low s&cheduling priority for the video transcoding process + + + true + + + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 1 + + + + + + + + + + + K3bIntMapComboBox +
k3bintmapcombobox.h
+ + -1 + -1 + + 0 + + 5 + 5 + 0 + 0 + + image0 +
+
+ + + 89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000042c49444154388db5954f6c14551cc73fefcd7476b65bdaae4bb78bb5502a14d404e4801c88182d1c4c2c693da847400f9c24c68b878684238660e2b1e01f12c19493012ef2478c814412d354a46017a8a564bb6da5bbedccee767776e63d0ffb073751d483bfe49799974c3eeffb7ebf37df9fd05a530b2184040cc0042420aaf9a4d0d554800f045a6b256ae0e1e1e1d6bebebe838ee31c48a7d39b5cd7fd075e251cc7617272f2ded8d8d819cff33e0316819259537aead4a9839d5dd6d1784f91f55b0a94830242088404d304292bef68a89f520802a598fecddaa04f1a876f5c250c7c0a64cdeac686e33807e23d45e6b297c8b877f1831542614550b6599835c83c2a81b6786a75134faf2f1169f12997350881d9021d0903e06de0745d3160a6d3e94dbd5b0a64dcbb94b5831d0e3375ab892b1772dcf9790528543f8dd0d367b36768153b5e31503a0f1aecb004580b44ffac58baae8b1714f0833c7638cc8dab303a320f4822ab4c7a37c69196203de3319d5ce1c4d13c733331dedc67a129a154fd128401ab0616d55a130ac3d42d93d1913940d13fd0c9ee0183685c60da01c5421bd72f7a8c8efccef9afd374267ad93d642365be0636a0d28ec7600941d9e6f23917f0e97f23ce5bef35d19ec863da0ed9059b2be70bec196c66dfa10ec0e49b338f7017258651bf95021035c595429bb0903248fe52a2b5b595dd7b4d945cc2340cdca536be389ee3f67886c5798f773fe8e0dac508c989659277a2180da4ca4ff07821058b8b251445d63d6b13ed1098a6417e39cac85197dbe31962ab9bd9f1f22a226d45366f6d0620fdb08c900d281af6110284b20085b414861d905d88f2e52739ee8cbb8022143259d3dd84691730aa2d52da441a8de0c6958068870022a41e9629ad3473fd3b8fdbe319dadb9b4924da994d2d716c7896fbe35152f78b48245d6b2da4507faf582be8eaf159b721cc837b05ae7debb1f79d08cb8b515edad942a22bc4b1c33eb3d34b1c797f06af90a72d16e2f96d9a74aa11dca8586b222d01af0fb60070f6c402d72f15d97f28c6f6d7027a5f5ce6c3233dc4e2ede496b278be4fff608cee8d3e1add806aeca51094cbb06397c1ecc328e746537c7e3ccdb5cb1136bf60635882d4d41c6ec6836ab37efa214f72208ed9f4d7cdd38ee310280542e38b1c43fb6de26b3672e1ec3cc99bcb246f66a938a3241ab3e91f7c861fbf77710b1e5e49915bae974203ba0e9e9c9cbc373d6d6d305a040a89c2a77f50b27d5782bbbf7acccf28349235dd16cf6dd374f7295e1de8a45c02d37499182b01cc0201a085d61a2144d8b2ac8fb6ed340e77240c4261890e04c250185262546d534a032154b59e0ad394e41c98182bf268ce6721ed9f064e0253356f6da2e24c1f030f783c15fe6da680af8021602bd051532ca9b8521488559f61aa86c29343578fbf0264a94c906c7d3409214c20043457a116ff6de6795578012889ff6b98fe016ea0ce1c6a2573410000000049454e44ae426082 + + + + + m_checkBlankReplace + toggled(bool) + m_editBlankReplace + setEnabled(bool) + + + + + klistview.h + kurlrequester.h + klineedit.h + kpushbutton.h + kactivelabel.h + k3bintmapcombobox.h + k3bintmapcombobox.h + kurllabel.h + klineedit.h + +
diff --git a/src/rip/videodvd/k3bvideodvdrippingdialog.cpp b/src/rip/videodvd/k3bvideodvdrippingdialog.cpp new file mode 100644 index 0000000..ddb5ff2 --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingdialog.cpp @@ -0,0 +1,634 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bvideodvdrippingdialog.h" +#include "k3bvideodvdrippingwidget.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +static QString videoCodecId( K3bVideoDVDTitleTranscodingJob::VideoCodec codec ) +{ + switch( codec ) { + case K3bVideoDVDTitleTranscodingJob::VIDEO_CODEC_FFMPEG_MPEG4: + return "ffmpeg_mpeg4"; + case K3bVideoDVDTitleTranscodingJob::VIDEO_CODEC_XVID: + return "xvid"; + default: + return "none"; + } +} + + +static QString audioCodecId( K3bVideoDVDTitleTranscodingJob::AudioCodec codec ) +{ + switch( codec ) { + case K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_MP3: + return "mp3"; + case K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_AC3_STEREO: + return "ac3_stereo"; + case K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_AC3_PASSTHROUGH: + return "ac3_passthrough"; + default: + return "none"; + } +} + + +static K3bVideoDVDTitleTranscodingJob::VideoCodec videoCodecFromId( const QString& codec ) +{ + if( codec == "xvid" ) + return K3bVideoDVDTitleTranscodingJob::VIDEO_CODEC_XVID; + else // if( codec == "ffmpeg_mpeg4" ) + return K3bVideoDVDTitleTranscodingJob::VIDEO_CODEC_FFMPEG_MPEG4; +} + + +static K3bVideoDVDTitleTranscodingJob::AudioCodec audioCodecFromId( const QString& codec ) +{ + if( codec == "ac3_stereo" ) + return K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_AC3_STEREO; + else if( codec == "ac3_passthrough" ) + return K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_AC3_PASSTHROUGH; + else // if( codec == "mp3" ) + return K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_MP3; +} + + +// resize according to aspect ratio +static QSize resizeTitle( const K3bVideoDVD::VideoStream& title, const QSize& size ) +{ + int w = size.width(); + int h = size.height(); + int rw = title.realPictureWidth(); + int rh = title.realPictureHeight(); + + if( w == 0 && h == 0 ) { + w = rw; + h = rh; + } + else if( w == 0 ) { + w = h * rw / rh; + } + else if( h == 0 ) { + h = w * rh / rw; + } + + return QSize(w,h); +} + + + +class K3bVideoDVDRippingDialog::AudioStreamViewItem : public QCheckListItem +{ +public: + AudioStreamViewItem( K3bVideoDVDRippingDialog* dlg, + QCheckListItem* parent, QListViewItem* after, const QString& text, + int audioStream ) + : QCheckListItem( parent, after, text, RadioButton ), + m_audioStream( audioStream ), + m_dlg( dlg ) { + } + +private: + void stateChange( bool ) { + if( state() == On ) { + m_dlg->m_titleRipInfos[static_cast(parent())].audioStream = m_audioStream; + m_dlg->slotUpdateFilenames(); + } + } + + int m_audioStream; + K3bVideoDVDRippingDialog* m_dlg; +}; + + +class K3bVideoDVDRippingDialog::Private +{ +public: + K3bFileSystemInfo fsInfo; +}; + + +K3bVideoDVDRippingDialog::K3bVideoDVDRippingDialog( const K3bVideoDVD::VideoDVD& dvd, + const QValueList& titles, + QWidget* parent, const char* name ) + : K3bInteractionDialog( parent, name, + i18n("Video DVD Ripping"), + QString::null, + START_BUTTON|CANCEL_BUTTON, + START_BUTTON, + "VideoDVD Ripping" ), // config group + m_dvd( dvd ) +{ + d = new Private; + + QWidget* frame = mainWidget(); + QHBoxLayout* frameLayout = new QHBoxLayout( frame ); + frameLayout->setMargin( 0 ); + frameLayout->setAutoAdd( true ); + m_w = new K3bVideoDVDRippingWidget( frame ); + + connect( m_w, SIGNAL(changed()), + this, SLOT(slotUpdateFilesizes()) ); + connect( m_w, SIGNAL(changed()), + this, SLOT(slotUpdateFilenames()) ); + connect( m_w, SIGNAL(changed()), + this, SLOT(slotUpdateVideoSizes()) ); + + setTitle( i18n("Video DVD Ripping"), + i18n("1 title from %1", "%n titles from %1", titles.count()) + .arg( k3bappcore->mediaCache()->medium(m_dvd.device()).beautifiedVolumeId() ) ); + + // populate list map + populateTitleView( titles ); +} + + +K3bVideoDVDRippingDialog::~K3bVideoDVDRippingDialog() +{ + delete d; +} + + +void K3bVideoDVDRippingDialog::populateTitleView( const QValueList& titles ) +{ + m_w->m_titleView->clear(); + m_titleRipInfos.clear(); + + QCheckListItem* titleItem = 0; + for( QValueList::const_iterator it = titles.begin(); it != titles.end(); ++it ) { + titleItem = new QCheckListItem( m_w->m_titleView, + titleItem, + i18n("Title %1 (%2)") + .arg(*it) + .arg(m_dvd[*it-1].playbackTime().toString()), + QCheckListItem::RadioButtonController ); + titleItem->setText( 1, QString("%1x%2") + .arg(m_dvd[*it-1].videoStream().realPictureWidth()) + .arg(m_dvd[*it-1].videoStream().realPictureHeight()) ); + titleItem->setText( 3, QString("%1 Title %2.avi").arg(m_dvd.volumeIdentifier()).arg(*it) ); + + // now for the rip info + K3bVideoDVDRippingJob::TitleRipInfo ri( *it ); + + // + // Determine default language selection: + // first try the configured locale, if that fails, fall back to the first audio stream + // + ri.audioStream = 0; + for( unsigned int i = 0; i < m_dvd[*it-1].numAudioStreams(); ++i ) { + if( m_dvd[*it-1].audioStream(i).langCode() == KGlobal::locale()->language() && + m_dvd[*it-1].audioStream(i).format() != K3bVideoDVD::AUDIO_FORMAT_DTS ) { + ri.audioStream = i; + break; + } + } + + QListViewItem* asI = 0; + for( unsigned int i = 0; i < m_dvd[*it-1].numAudioStreams(); ++i ) { + QString text = i18n("%1 %2Ch (%3%4)") + .arg( K3bVideoDVD::audioFormatString( m_dvd[*it-1].audioStream(i).format() ) ) + .arg( m_dvd[*it-1].audioStream(i).channels() ) + .arg( m_dvd[*it-1].audioStream(i).langCode().isEmpty() + ? i18n("unknown language") + : KGlobal::locale()->twoAlphaToLanguageName( m_dvd[*it-1].audioStream(i).langCode() ) ) + .arg( m_dvd[*it-1].audioStream(i).codeExtension() != K3bVideoDVD::AUDIO_CODE_EXT_UNSPECIFIED + ? QString(" ") + K3bVideoDVD::audioCodeExtensionString( m_dvd[*it-1].audioStream(i).codeExtension() ) + : QString::null ); + + if( m_dvd[*it-1].audioStream(i).format() == K3bVideoDVD::AUDIO_FORMAT_DTS ) { + // width of the radio button from QCheckListItem::paintCell + int buttonSize = style().pixelMetric( QStyle::PM_CheckListButtonSize, m_w->m_titleView ) + 4; + int spaceWidth = fontMetrics().width( ' ' ); + int numSpaces = buttonSize/spaceWidth; + asI = new QListViewItem( titleItem, asI, QString().fill( ' ', numSpaces ) + text + " (" + i18n("not supported") + ")" ); + } + else { + asI = new AudioStreamViewItem( this, titleItem, asI, text, i ); + + if( ri.audioStream == (int)i ) + ((AudioStreamViewItem*)asI)->setState( QCheckListItem::On ); + } + } + + titleItem->setOpen( true ); + + m_titleRipInfos[titleItem] = ri; + } +} + + +void K3bVideoDVDRippingDialog::slotUpdateFilenames() +{ + QString baseDir = K3b::prepareDir( m_w->m_editBaseDir->url() ); + d->fsInfo.setPath( baseDir ); + + for( QMap::iterator it = m_titleRipInfos.begin(); + it != m_titleRipInfos.end(); ++it ) { + QString f = d->fsInfo.fixupPath( createFilename( it.data(), m_w->m_comboFilenamePattern->currentText() ) ); + if( m_w->m_checkBlankReplace->isChecked() ) + f.replace( QRegExp( "\\s" ), m_w->m_editBlankReplace->text() ); + it.data().filename = baseDir + f; + it.key()->setText( 3, f ); + } +} + + +void K3bVideoDVDRippingDialog::slotUpdateFilesizes() +{ + double bitrate = (double)m_w->m_spinVideoBitrate->value(); + KIO::filesize_t overallSize = 0ULL; + + // update file sizes + for( QMap::iterator it = m_titleRipInfos.begin(); + it != m_titleRipInfos.end(); ++it ) { + + double sec = m_dvd[it.data().title-1].playbackTime().totalSeconds(); + + // estimate the filesize + KIO::filesize_t size = (KIO::filesize_t)( sec * bitrate * 1000.0 / 8.0 ); + + // add audio stream size + // FIXME: consider AC3 passthrough + size += (KIO::filesize_t)( sec * m_w->selectedAudioBitrate() / 8.0 * 1024.0 ); + + it.key()->setText( 2, KIO::convertSize( size ) ); + + overallSize += size; + } + + m_w->setNeededSize( overallSize ); +} + + +void K3bVideoDVDRippingDialog::slotUpdateVideoSizes() +{ + QSize size = m_w->selectedPictureSize(); + for( QMap::iterator it = m_titleRipInfos.begin(); + it != m_titleRipInfos.end(); ++it ) { + QSize s( resizeTitle( m_dvd[it.data().title-1].videoStream(), size ) ); + it.key()->setText( 1, QString("%1x%2").arg(s.width()).arg(s.height()) ); + } +} + + +void K3bVideoDVDRippingDialog::setBaseDir( const QString& path ) +{ + m_w->m_editBaseDir->setURL( path ); +} + + +QString K3bVideoDVDRippingDialog::createFilename( const K3bVideoDVDRippingJob::TitleRipInfo& info, const QString& pattern ) const +{ + QString f; + + const K3bVideoDVD::Title& title = m_dvd[info.title-1]; + + for( unsigned int i = 0; i < pattern.length(); ++i ) { + // + // every pattern starts with a % sign + // + if( pattern[i] == '%' ) { + ++i; // skip the % + QChar c = pattern[i]; + + // + // first check if we have a long keyword instead of a one-char + // + if( pattern[i] == '{' ) { + int j = pattern.find( '}', i ); + if( j < 0 ) // no closing bracket -> no valid pattern + c = '*'; + else { + QString keyword = pattern.mid( i+1, j-i-1 ); + if( keyword == "titlenumber" || + keyword == "title_number" || + keyword == "title" ) { + c = PATTERN_TITLE_NUMBER; + } + else if( keyword == "volumeid" || + keyword == "volume_id" || + keyword == "volid" || + keyword == "vol_id" ) { + c = PATTERN_VOLUME_ID; + } + else if( keyword == "beautifiedvolumeid" || + keyword == "beautified_volumeid" || + keyword == "beautified_volume_id" || + keyword == "beautifiedvolid" || + keyword == "beautified_volid" || + keyword == "beautified_vol_id" || + keyword == "nicevolid" || + keyword == "nice_volid" || + keyword == "nice_vol_id" ) { + c = PATTERN_BEAUTIFIED_VOLUME_ID; + } + else if( keyword == "languagecode" || + keyword == "language_code" || + keyword == "langcode" || + keyword == "lang_code" ) { + c = PATTERN_LANGUAGE_CODE; + + } + else if( keyword == "lang" || + keyword == "language" || + keyword == "langname" || + keyword == "languagename" || + keyword == "lang_name" || + keyword == "language_name" ) { + c = PATTERN_LANGUAGE_NAME; + } + else if( keyword == "audioformat" || + keyword == "audio_format" || + keyword == "audio" ) { + c = PATTERN_AUDIO_FORMAT; + } + else if( keyword == "channels" || + keyword == "audiochannels" || + keyword == "audio_channels" || + keyword == "ch" ) { + c = PATTERN_AUDIO_CHANNELS; + } + else if( keyword == "videosize" || + keyword == "video_size" || + keyword == "vsize" ) { + c = PATTERN_VIDEO_SIZE; + } + else if( keyword == "originalvideosize" || + keyword == "original_video_size" || + keyword == "origvideosize" || + keyword == "orig_video_size" || + keyword == "origvsize" ) { + c = PATTERN_ORIG_VIDEO_SIZE; + } + else if( keyword == "aspect_ratio" || + keyword == "aspectratio" || + keyword == "ratio" ) { + c = PATTERN_ASPECT_RATIO; + } + else if( keyword == "current_date" || + keyword == "currentdate" || + keyword == "date" ) { + c = PATTERN_CURRENT_DATE; + } + else { + // unusable pattern + c = '*'; + } + + // + // skip the keyword and the closing bracket + // + if( c != '*' ) { + i += keyword.length() + 1; + } + } + } + + switch( c ) { + case PATTERN_TITLE_NUMBER: + f.append( QString::number(info.title).rightJustify( 2, '0' ) ); + break; + case PATTERN_VOLUME_ID: + f.append( m_dvd.volumeIdentifier() ); + break; + case PATTERN_BEAUTIFIED_VOLUME_ID: + f.append( k3bappcore->mediaCache()->medium( m_dvd.device() ).beautifiedVolumeId() ); + break; + case PATTERN_LANGUAGE_CODE: + if( title.numAudioStreams() > 0 ) + f.append( title.audioStream( info.audioStream ).langCode() ); + break; + case PATTERN_LANGUAGE_NAME: + if( title.numAudioStreams() > 0 ) + f.append( KGlobal::locale()->twoAlphaToLanguageName( title.audioStream( info.audioStream ).langCode() ) ); + break; + case PATTERN_AUDIO_FORMAT: + // FIXME: what about MPEG audio streams? + if( title.numAudioStreams() > 0 ) { + if( m_w->selectedAudioCodec() == K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_MP3 ) + f.append( K3bVideoDVDTitleTranscodingJob::audioCodecString( m_w->selectedAudioCodec() ) ); + else + f.append( K3bVideoDVD::audioFormatString( title.audioStream( info.audioStream ).format() ) ); + } + break; + case PATTERN_AUDIO_CHANNELS: + if( title.numAudioStreams() > 0 ) + f.append( i18n("%nCh", "%nCh", + m_w->selectedAudioCodec() == K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_AC3_PASSTHROUGH + ? title.audioStream( info.audioStream ).channels() + : 2 ) ); + break; + case PATTERN_ORIG_VIDEO_SIZE: + f.append( QString("%1x%2") + .arg(title.videoStream().pictureWidth()) + .arg(title.videoStream().pictureHeight()) ); + break; + case PATTERN_VIDEO_SIZE: { + QSize s( resizeTitle( m_dvd[info.title-1].videoStream(), m_w->selectedPictureSize() ) ); + f.append( QString("%1x%2").arg(s.width()).arg(s.height()) ); + break; + } + case PATTERN_ASPECT_RATIO: + if( title.videoStream().displayAspectRatio() == K3bVideoDVD::VIDEO_ASPECT_RATIO_4_3 ) + f.append( "4:3" ); + else + f.append( "16:9" ); + break; + case PATTERN_CURRENT_DATE: + f.append( KGlobal::locale()->formatDate( QDate::currentDate() ) ); + break; + default: + f.append( pattern[i-1] ); + f.append( pattern[i] ); + } + } + + // + // normal character -> just append to filename + // + else { + f.append( pattern[i] ); + } + } + + // + // and the extension (for now only avi) + // + f.append( ".avi" ); + + return f; +} + + +void K3bVideoDVDRippingDialog::loadK3bDefaults() +{ + m_w->m_spinVideoBitrate->setValue( 1800 ); + m_w->m_checkTwoPassEncoding->setChecked( true ); + m_w->m_checkAudioResampling->setChecked( false ); + m_w->m_checkAutoClipping->setChecked( false ); + m_w->m_checkLowPriority->setChecked( true ); + m_w->m_checkAudioVBR->setChecked( true ); + m_w->setSelectedAudioBitrate( 128 ); + m_w->setSelectedVideoCodec( K3bVideoDVDTitleTranscodingJob::VIDEO_CODEC_FFMPEG_MPEG4 ); + m_w->setSelectedAudioCodec( K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_MP3 ); + m_w->m_checkBlankReplace->setChecked( false ); + m_w->m_editBlankReplace->setText( "_" ); + m_w->m_comboFilenamePattern->setEditText( m_w->m_comboFilenamePattern->text(0) ); + m_w->m_editBaseDir->setURL( K3b::defaultTempPath() ); +} + + +void K3bVideoDVDRippingDialog::loadUserDefaults( KConfigBase* c ) +{ + m_w->m_spinVideoBitrate->setValue( c->readNumEntry( "video bitrate", 1200 ) ); + m_w->m_checkTwoPassEncoding->setChecked( c->readBoolEntry( "two pass encoding", true ) ); + m_w->m_checkAudioResampling->setChecked( c->readBoolEntry( "audio resampling", false ) ); + m_w->m_checkAutoClipping->setChecked( c->readBoolEntry( "auto clipping", false ) ); + m_w->m_checkLowPriority->setChecked( c->readBoolEntry( "low priority", true ) ); + m_w->m_checkAudioVBR->setChecked( c->readBoolEntry( "vbr audio", true ) ); + m_w->setSelectedAudioBitrate( c->readNumEntry( "audio bitrate", 128 ) ); + m_w->setSelectedVideoCodec( videoCodecFromId( c->readEntry( "video codec", videoCodecId( K3bVideoDVDTitleTranscodingJob::VIDEO_CODEC_FFMPEG_MPEG4 ) ) ) ); + m_w->setSelectedAudioCodec( audioCodecFromId( c->readEntry( "audio codec", audioCodecId( K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_MP3 ) ) ) ); + m_w->m_checkBlankReplace->setChecked( c->readBoolEntry( "replace blanks", false ) ); + m_w->m_editBlankReplace->setText( c->readEntry( "blank replace string", "_" ) ); + m_w->m_comboFilenamePattern->setEditText( c->readEntry( "filename pattern", m_w->m_comboFilenamePattern->text(0) ) ); + m_w->m_editBaseDir->setURL( c->readPathEntry( "base dir", K3b::defaultTempPath() ) ); +} + + +void K3bVideoDVDRippingDialog::saveUserDefaults( KConfigBase* c ) +{ + c->writeEntry( "video bitrate", m_w->m_spinVideoBitrate->value() ); + c->writeEntry( "two pass encoding", m_w->m_checkTwoPassEncoding->isChecked() ); + c->writeEntry( "audio resampling", m_w->m_checkAudioResampling->isChecked() ); + c->writeEntry( "auto clipping", m_w->m_checkAutoClipping->isChecked() ); + c->writeEntry( "low priority", m_w->m_checkLowPriority->isChecked() ); + c->writeEntry( "vbr audio", m_w->m_checkAudioVBR->isChecked() ); + c->writeEntry( "audio bitrate", m_w->selectedAudioBitrate() ); + c->writeEntry( "video codec", videoCodecId( m_w->selectedVideoCodec() ) ); + c->writeEntry( "audio codec", audioCodecId( m_w->selectedAudioCodec() ) ); + c->writeEntry( "replace blanks", m_w->m_checkBlankReplace->isChecked() ); + c->writeEntry( "blank replace string", m_w->m_editBlankReplace->text() ); + c->writeEntry( "filename pattern", m_w->m_comboFilenamePattern->currentText() ); + c->writePathEntry( "base dir", m_w->m_editBaseDir->url() ); +} + + +void K3bVideoDVDRippingDialog::slotStartClicked() +{ + // + // check if the selected audio codec is usable for all selected audio streams + // We can only use the AC3 pass-through mode for AC3 streams + // + if( m_w->selectedAudioCodec() == K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_AC3_PASSTHROUGH ) { + for( QMap::iterator it = m_titleRipInfos.begin(); + it != m_titleRipInfos.end(); ++it ) { + if( m_dvd[it.data().title-1].numAudioStreams() > 0 && + m_dvd[it.data().title-1].audioStream(it.data().audioStream).format() != K3bVideoDVD::AUDIO_FORMAT_AC3 ) { + KMessageBox::sorry( this, i18n("

When using the AC3 pass-through audio codec all selected audio " + "streams need to be in AC3 format. Please select another audio codec or " + "choose AC3 audio streams for all ripped titles."), + i18n("AC3 Pass-through") ); + return; + } + } + } + + // check if we need to overwrite some files... + QStringList filesToOverwrite; + for( QMap::iterator it = m_titleRipInfos.begin(); + it != m_titleRipInfos.end(); ++it ) { + if( QFile::exists( it.data().filename ) ) + filesToOverwrite.append( it.data().filename ); + } + + if( !filesToOverwrite.isEmpty() ) + if( KMessageBox::questionYesNoList( this, + i18n("Do you want to overwrite these files?"), + filesToOverwrite, + i18n("Files Exist"), i18n("Overwrite"), KStdGuiItem::cancel() ) == KMessageBox::No ) + return; + + + QSize videoSize = m_w->selectedPictureSize(); + int i = 0; + QValueVector titles( m_titleRipInfos.count() ); + for( QMapConstIterator it = m_titleRipInfos.begin(); + it != m_titleRipInfos.end(); ++it ) { + titles[i] = it.data(); + titles[i].videoBitrate = 0; // use the global bitrate set below + titles[i].width = videoSize.width(); + titles[i].height = videoSize.height(); + ++i; + } + + // sort the titles which come from a map and are thus not sorted properly + // simple bubble sort for these small arrays is sufficient + for( unsigned int i = 0; i < titles.count(); ++i ) { + for( unsigned int j = i+1; j < titles.count(); ++j ) { + if( titles[i].title > titles[j].title ) { + K3bVideoDVDRippingJob::TitleRipInfo tmp = titles[i]; + titles[i] = titles[j]; + titles[j] = tmp; + } + } + } + + // start the job + K3bJobProgressDialog dlg( parentWidget() ); + K3bVideoDVDRippingJob* job = new K3bVideoDVDRippingJob( &dlg, &dlg ); + job->setVideoDVD( m_dvd ); + job->setTitles( titles ); + + job->setVideoBitrate( m_w->m_spinVideoBitrate->value() ); + job->setTwoPassEncoding( m_w->m_checkTwoPassEncoding->isChecked() ); + job->setResampleAudioTo44100( m_w->m_checkAudioResampling->isChecked() ); + job->setAutoClipping( m_w->m_checkAutoClipping->isChecked() ); + job->setVideoCodec( m_w->selectedVideoCodec() ); + job->setAudioCodec( m_w->selectedAudioCodec() ); + job->setLowPriority( m_w->m_checkLowPriority->isChecked() ); + job->setAudioBitrate( m_w->selectedAudioBitrate() ); + job->setAudioVBR( m_w->m_checkAudioVBR->isChecked() ); + + hide(); + dlg.startJob( job ); + close(); +} + +#include "k3bvideodvdrippingdialog.moc" diff --git a/src/rip/videodvd/k3bvideodvdrippingdialog.h b/src/rip/videodvd/k3bvideodvdrippingdialog.h new file mode 100644 index 0000000..1acad53 --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingdialog.h @@ -0,0 +1,82 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_VIDEODVD_RIPPING_DIALOG_H_ +#define _K3B_VIDEODVD_RIPPING_DIALOG_H_ + +#include +#include +#include "k3bvideodvdrippingjob.h" + +#include +#include + + +class K3bVideoDVDRippingWidget; +class QCheckListItem; + +class K3bVideoDVDRippingDialog : public K3bInteractionDialog +{ + Q_OBJECT + + public: + K3bVideoDVDRippingDialog( const K3bVideoDVD::VideoDVD& dvd, + const QValueList& titles, + QWidget *parent = 0, const char *name = 0 ); + ~K3bVideoDVDRippingDialog(); + + void setBaseDir( const QString& path ); + + enum FileNamingPattern { + PATTERN_TITLE_NUMBER = 't', + PATTERN_VOLUME_ID = 'i', + PATTERN_BEAUTIFIED_VOLUME_ID = 'b', + PATTERN_LANGUAGE_CODE = 'l', + PATTERN_LANGUAGE_NAME = 'n', + PATTERN_AUDIO_FORMAT = 'a', + PATTERN_AUDIO_CHANNELS = 'c', + PATTERN_ORIG_VIDEO_SIZE = 'v', + PATTERN_VIDEO_SIZE = 's', + PATTERN_ASPECT_RATIO = 'r', + PATTERN_CURRENT_DATE = 'd' + }; + + private slots: + void slotStartClicked(); + void slotUpdateFilenames(); + void slotUpdateFilesizes(); + void slotUpdateVideoSizes(); + + private: + void populateTitleView( const QValueList& titles ); + + QString createFilename( const K3bVideoDVDRippingJob::TitleRipInfo& info, const QString& pattern ) const; + + void loadK3bDefaults(); + void loadUserDefaults( KConfigBase* ); + void saveUserDefaults( KConfigBase* ); + + K3bVideoDVDRippingWidget* m_w; + + K3bVideoDVD::VideoDVD m_dvd; + QMap m_titleRipInfos; + + class AudioStreamViewItem; + + class Private; + Private* d; +}; + +#endif diff --git a/src/rip/videodvd/k3bvideodvdrippingjob.cpp b/src/rip/videodvd/k3bvideodvdrippingjob.cpp new file mode 100644 index 0000000..c10c127 --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingjob.cpp @@ -0,0 +1,385 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bvideodvdrippingjob.h" + +#include +#include + +#include +#include + + +K3bVideoDVDRippingJob::TitleRipInfo::TitleRipInfo() + : title(1), + audioStream(0), + width(0), + height(0), + videoBitrate(0), + clipTop(0), + clipLeft(0), + clipBottom(0), + clipRight(0) +{ +} + + +K3bVideoDVDRippingJob::TitleRipInfo::TitleRipInfo( int _title, + int _audioStream, + const QString& fn, + int _width, + int _height, + int _videoBitrate, + int _clipTop, + int _clipLeft, + int _clipBottom, + int _clipRight ) + : title(_title), + audioStream(_audioStream), + filename(fn), + width(_width), + height(_height), + videoBitrate(_videoBitrate), + clipTop(_clipTop), + clipLeft(_clipLeft), + clipBottom(_clipBottom), + clipRight(_clipRight) +{ +} + + + +class K3bVideoDVDRippingJob::Private { +public: + Private() + : autoClipping( true ) { + } + + unsigned int currentTitleInfoIndex; + bool autoClipping; + + bool canceled; + + int videoBitrate; + + int failedTitles; + + QValueVector titleProgressParts; + QValueVector titleClippingProgressParts; +}; + + + +K3bVideoDVDRippingJob::K3bVideoDVDRippingJob( K3bJobHandler* hdl, QObject* parent ) + : K3bJob( hdl, parent ) +{ + d = new Private(); + + m_transcodingJob = new K3bVideoDVDTitleTranscodingJob( this, this ); + connectSubJob( m_transcodingJob, + SLOT(slotTranscodingJobFinished(bool)), + SIGNAL(newTask(const QString&)), + SIGNAL(newSubTask(const QString&)), + SLOT(slotTranscodingProgress(int)), + SIGNAL(subPercent(int)), + 0, + 0 ); + m_detectClippingJob = 0; +} + + +K3bVideoDVDRippingJob::~K3bVideoDVDRippingJob() +{ + delete d; +} + + +QString K3bVideoDVDRippingJob::jobDescription() const +{ + return i18n("Ripping Video DVD Titles"); +} + + +QString K3bVideoDVDRippingJob::jobDetails() const +{ + return i18n("Transcoding %n title to %1/%2", "Transcoding %n titles to %1/%2", m_titleRipInfos.count() ) + .arg( K3bVideoDVDTitleTranscodingJob::videoCodecString( m_transcodingJob->videoCodec() ) ) + .arg( K3bVideoDVDTitleTranscodingJob::audioCodecString( m_transcodingJob->audioCodec() ) ); +} + + +void K3bVideoDVDRippingJob::start() +{ + jobStarted(); + d->canceled = false; + d->failedTitles = 0; + + initProgressInfo(); + + if( d->autoClipping ) + startDetectClipping( 0 ); + else + startTranscoding( 0 ); +} + + +void K3bVideoDVDRippingJob::slotTranscodingJobFinished( bool success ) +{ + if( d->canceled ) { + emit canceled(); + jobFinished( false ); + } + else { + if( success ) + emit infoMessage( i18n("Successfully ripped title %1").arg(m_titleRipInfos[d->currentTitleInfoIndex].title), SUCCESS ); + else { + d->failedTitles++; + emit infoMessage( i18n("Failed to rip title %1").arg(m_titleRipInfos[d->currentTitleInfoIndex].title), ERROR ); + } + + ++d->currentTitleInfoIndex ; + if( d->currentTitleInfoIndex < m_titleRipInfos.count() ) { + if( d->autoClipping ) + startDetectClipping( d->currentTitleInfoIndex ); + else + startTranscoding( d->currentTitleInfoIndex ); + } + else { + jobFinished( d->failedTitles == 0 ); + } + } +} + + +void K3bVideoDVDRippingJob::slotDetectClippingJobFinished( bool success ) +{ + if( d->canceled ) { + emit canceled(); + jobFinished( false ); + } + else { + m_titleRipInfos[d->currentTitleInfoIndex].clipTop = 0; + m_titleRipInfos[d->currentTitleInfoIndex].clipLeft = 0; + m_titleRipInfos[d->currentTitleInfoIndex].clipBottom = 0; + m_titleRipInfos[d->currentTitleInfoIndex].clipRight = 0; + + if( success ) { + emit infoMessage( i18n("Determined clipping values for title %1").arg(m_titleRipInfos[d->currentTitleInfoIndex].title), SUCCESS ); + emit infoMessage( i18n("Top: %1, Bottom: %2") + .arg(m_detectClippingJob->clippingTop()).arg(m_detectClippingJob->clippingBottom()), INFO ); + emit infoMessage( i18n("Left: %1, Right: %2") + .arg(m_detectClippingJob->clippingLeft()).arg(m_detectClippingJob->clippingRight()), INFO ); + + // let's see if the clipping values make sense + if( m_detectClippingJob->clippingTop() + m_detectClippingJob->clippingBottom() + >= (int)m_dvd[d->currentTitleInfoIndex].videoStream().pictureHeight() || + m_detectClippingJob->clippingLeft() + m_detectClippingJob->clippingRight() + >= (int)m_dvd[d->currentTitleInfoIndex].videoStream().pictureWidth() ) { + emit infoMessage( i18n("Insane clipping values. No clipping will be done at all."), WARNING ); + } + else { + m_titleRipInfos[d->currentTitleInfoIndex].clipTop = m_detectClippingJob->clippingTop(); + m_titleRipInfos[d->currentTitleInfoIndex].clipLeft = m_detectClippingJob->clippingLeft(); + m_titleRipInfos[d->currentTitleInfoIndex].clipBottom = m_detectClippingJob->clippingBottom(); + m_titleRipInfos[d->currentTitleInfoIndex].clipRight = m_detectClippingJob->clippingRight(); + } + } + else + emit infoMessage( i18n("Failed to determine clipping values for title %1").arg(m_titleRipInfos[d->currentTitleInfoIndex].title), ERROR ); + + startTranscoding( d->currentTitleInfoIndex ); + } +} + + +void K3bVideoDVDRippingJob::startTranscoding( int ripInfoIndex ) +{ + d->currentTitleInfoIndex = ripInfoIndex; + + m_transcodingJob->setVideoDVD( m_dvd ); + m_transcodingJob->setTitle( m_titleRipInfos[ripInfoIndex].title ); + m_transcodingJob->setAudioStream( m_titleRipInfos[ripInfoIndex].audioStream ); + m_transcodingJob->setClipping( m_titleRipInfos[ripInfoIndex].clipTop, + m_titleRipInfos[ripInfoIndex].clipLeft, + m_titleRipInfos[ripInfoIndex].clipBottom, + m_titleRipInfos[ripInfoIndex].clipRight ); + m_transcodingJob->setSize( m_titleRipInfos[ripInfoIndex].width, m_titleRipInfos[ripInfoIndex].height ); + m_transcodingJob->setFilename( m_titleRipInfos[ripInfoIndex].filename ); + + if( m_titleRipInfos[ripInfoIndex].videoBitrate > 0 ) + m_transcodingJob->setVideoBitrate( m_titleRipInfos[ripInfoIndex].videoBitrate ); + else + m_transcodingJob->setVideoBitrate( d->videoBitrate ); + + m_transcodingJob->start(); +} + + +void K3bVideoDVDRippingJob::startDetectClipping( int ripInfoIndex ) +{ + d->currentTitleInfoIndex = ripInfoIndex; + + if( !m_detectClippingJob ) { + m_detectClippingJob = new K3bVideoDVDTitleDetectClippingJob( this, this ); + connectSubJob( m_detectClippingJob, + SLOT(slotDetectClippingJobFinished(bool)), + SIGNAL(newTask(const QString&)), + SIGNAL(newSubTask(const QString&)), + SLOT(slotDetectClippingProgress(int)), + SIGNAL(subPercent(int)), + 0, + 0 ); + } + + m_detectClippingJob->setVideoDVD( m_dvd ); + m_detectClippingJob->setTitle( m_titleRipInfos[ripInfoIndex].title ); + m_detectClippingJob->setLowPriority( m_transcodingJob->lowPriority() ); + + m_detectClippingJob->start(); +} + + +void K3bVideoDVDRippingJob::slotTranscodingProgress( int p ) +{ + // calculate the part already done + double doneParts = 0.0; + for( unsigned int i = 0; i < d->currentTitleInfoIndex; ++i ) { + doneParts += d->titleProgressParts[i]; + if( d->autoClipping ) + doneParts += d->titleClippingProgressParts[i]; + } + if( d->autoClipping ) + doneParts += d->titleClippingProgressParts[d->currentTitleInfoIndex]; + + // and the current thing + doneParts += (double)p/100.0*d->titleProgressParts[d->currentTitleInfoIndex]; + + emit percent( (int)( 100.0*doneParts ) ); +} + + +void K3bVideoDVDRippingJob::slotDetectClippingProgress( int p ) +{ + // calculate the part already done + double doneParts = 0.0; + for( unsigned int i = 0; i < d->currentTitleInfoIndex; ++i ) { + doneParts += d->titleProgressParts[i]; + doneParts += d->titleClippingProgressParts[i]; + } + + // and the current thing + doneParts += (double)p/100.0*d->titleClippingProgressParts[d->currentTitleInfoIndex]; + + emit percent( (int)( 100.0*doneParts ) ); +} + + +void K3bVideoDVDRippingJob::cancel() +{ + d->canceled = true; + if( m_transcodingJob->active() ) + m_transcodingJob->cancel(); + else if( m_detectClippingJob && m_detectClippingJob->active() ) + m_detectClippingJob->cancel(); +} + + +void K3bVideoDVDRippingJob::setVideoCodec( K3bVideoDVDTitleTranscodingJob::VideoCodec codec ) +{ + m_transcodingJob->setVideoCodec( codec ); +} + + +void K3bVideoDVDRippingJob::setVideoBitrate( int bitrate ) +{ + d->videoBitrate = bitrate; +} + + +void K3bVideoDVDRippingJob::setTwoPassEncoding( bool b ) +{ + m_transcodingJob->setTwoPassEncoding( b ); +} + + +void K3bVideoDVDRippingJob::setAudioCodec( K3bVideoDVDTitleTranscodingJob::AudioCodec codec ) +{ + m_transcodingJob->setAudioCodec( codec ); +} + + +void K3bVideoDVDRippingJob::setAudioBitrate( int bitrate ) +{ + m_transcodingJob->setAudioBitrate( bitrate ); +} + + +void K3bVideoDVDRippingJob::setAudioVBR( bool vbr ) +{ + m_transcodingJob->setAudioVBR( vbr ); +} + + +void K3bVideoDVDRippingJob::setResampleAudioTo44100( bool b ) +{ + m_transcodingJob->setResampleAudioTo44100( b ); +} + + +void K3bVideoDVDRippingJob::setLowPriority( bool b ) +{ + m_transcodingJob->setLowPriority( b ); +} + + +void K3bVideoDVDRippingJob::setAutoClipping( bool b ) +{ + d->autoClipping = b; +} + + +void K3bVideoDVDRippingJob::initProgressInfo() +{ + d->titleProgressParts.resize( m_titleRipInfos.count() ); + d->titleClippingProgressParts.resize( m_titleRipInfos.count() ); + + unsigned long long totalFrames = 0ULL; + for( unsigned int i = 0; i < m_titleRipInfos.count(); ++i ) { + if( m_transcodingJob->twoPassEncoding() ) + totalFrames += m_dvd[m_titleRipInfos[i].title-1].playbackTime().totalFrames() * 2; + else + totalFrames += m_dvd[m_titleRipInfos[i].title-1].playbackTime().totalFrames(); + + // using my knowledge of the internals of the clipping detection job: it decodes 200 frames + // of every chapter + if( d->autoClipping ) + totalFrames += m_dvd[m_titleRipInfos[i].title-1].numChapters() * 200; + } + + for( unsigned int i = 0; i < m_titleRipInfos.count(); ++i ) { + unsigned long long titleFrames = m_dvd[m_titleRipInfos[i].title-1].playbackTime().totalFrames(); + if( m_transcodingJob->twoPassEncoding() ) + titleFrames *= 2; + + // using my knowledge of the internals of the clipping detection job: it decodes 200 frames + // of every chapter + unsigned long long titleClippingFrames = m_dvd[m_titleRipInfos[i].title-1].numChapters() * 200; + + d->titleProgressParts[i] = (double)titleFrames/(double)totalFrames; + d->titleClippingProgressParts[i] = (double)titleClippingFrames/(double)totalFrames; + } +} + +#include "k3bvideodvdrippingjob.moc" diff --git a/src/rip/videodvd/k3bvideodvdrippingjob.h b/src/rip/videodvd/k3bvideodvdrippingjob.h new file mode 100644 index 0000000..7c9f4d2 --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingjob.h @@ -0,0 +1,106 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_VIDEODVD_RIPPING_JOB_H_ +#define _K3B_VIDEODVD_RIPPING_JOB_H_ + +#include +#include +#include + +#include + + +class K3bVideoDVDTitleDetectClippingJob; + + +/** + * For details on the options see K3bVideoDVDTitleTranscodingJob + */ +class K3bVideoDVDRippingJob : public K3bJob +{ + Q_OBJECT + + public: + K3bVideoDVDRippingJob( K3bJobHandler* hdl, QObject* parent ); + ~K3bVideoDVDRippingJob(); + + class TitleRipInfo { + public: + TitleRipInfo(); + TitleRipInfo( int title, + int audioStream = 0, + const QString& fn = QString::null, + int width = 0, // 0 -> no resize + int height = 0, // 0 -> no resize + int videoBitrate = 0, // 0 -> use default from job settings + int clipTop = 0, + int clipLeft = 0, + int clipBottom = 0, + int clipRight = 0 ); + int title; + int audioStream; + QString filename; + int width; + int height; + int videoBitrate; + int clipTop; + int clipLeft; + int clipBottom; + int clipRight; + }; + + QString jobDescription() const; + QString jobDetails() const; + + public slots: + void start(); + void cancel(); + + void setVideoDVD( const K3bVideoDVD::VideoDVD& dvd ) { m_dvd = dvd; } + void setTitles( const QValueVector& titles ) { m_titleRipInfos = titles; } + + void setVideoCodec( K3bVideoDVDTitleTranscodingJob::VideoCodec codec ); + void setVideoBitrate( int bitrate ); + void setTwoPassEncoding( bool b ); + void setAudioCodec( K3bVideoDVDTitleTranscodingJob::AudioCodec codec ); + void setAudioBitrate( int bitrate ); + void setAudioVBR( bool vbr ); + void setResampleAudioTo44100( bool b ); + void setLowPriority( bool b ); + void setAutoClipping( bool b ); + + private slots: + void slotTranscodingJobFinished( bool ); + void slotDetectClippingJobFinished( bool ); + void slotTranscodingProgress( int ); + void slotDetectClippingProgress( int ); + + private: + void startTranscoding( int ripInfoIndex ); + void startDetectClipping( int ripInfoIndex ); + void initProgressInfo(); + + K3bVideoDVD::VideoDVD m_dvd; + QValueVector m_titleRipInfos; + + K3bVideoDVDTitleTranscodingJob* m_transcodingJob; + K3bVideoDVDTitleDetectClippingJob* m_detectClippingJob; + + class Private; + Private* d; +}; + +#endif diff --git a/src/rip/videodvd/k3bvideodvdrippingpreview.cpp b/src/rip/videodvd/k3bvideodvdrippingpreview.cpp new file mode 100644 index 0000000..3ba7582 --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingpreview.cpp @@ -0,0 +1,135 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bvideodvdrippingpreview.h" + +#include +#include +#include + +#include +#include +#include + +#include + + + +K3bVideoDVDRippingPreview::K3bVideoDVDRippingPreview( QObject* parent ) + : QObject( parent ), + m_tempDir( 0 ), + m_process( 0 ) +{ +} + + +K3bVideoDVDRippingPreview::~K3bVideoDVDRippingPreview() +{ + delete m_process; + delete m_tempDir; +} + + +void K3bVideoDVDRippingPreview::generatePreview( const K3bVideoDVD::VideoDVD& dvd, int title, int chapter ) +{ + // cleanup first + delete m_process; + delete m_tempDir; + m_process = 0; + m_tempDir = 0; + m_canceled = false; + + const K3bExternalBin* bin = k3bcore->externalBinManager()->binObject("transcode"); + if( !bin ) { + emit previewDone( false ); + return; + } + + // auto-select a chapter + // choose the center chapter, but not the first or last if possible + if( chapter == 0 ) + chapter = QMIN( QMAX( dvd[title-1].numChapters()/2, 2 ), QMAX( dvd[title-1].numChapters() - 1, 1 ) ); + + // select a frame number + unsigned int frame = 30; + if( dvd[title-1][chapter-1].playbackTime().totalFrames() < frame ) + frame = dvd[title-1][chapter-1].playbackTime().totalFrames() / 2; + + m_dvd = dvd; + m_title = title; + m_chapter = chapter; + + m_tempDir = new KTempDir(); + m_tempDir->setAutoDelete( true ); + + m_process = new KProcess(); + *m_process << bin->path; + *m_process << "-i" << dvd.device()->blockDeviceName(); + *m_process << "-T" << QString("%1,%2").arg(title).arg(chapter); + *m_process << "-x" << "dvd,null"; + *m_process << "--dvd_access_delay" << "0"; + *m_process << "-y" << "ppm,null"; + *m_process << "-c" << QString("%1-%2").arg( frame ).arg( frame+1 ); + *m_process << "-Z" << "x200"; + *m_process << "-o" << m_tempDir->name(); + + connect( m_process, SIGNAL(processExited(KProcess*)), + this, SLOT(slotTranscodeFinished(KProcess*)) ); + if( !m_process->start( KProcess::NotifyOnExit, KProcess::AllOutput ) ) { // we use AllOutput to not pollute stdout + // something went wrong when starting the program + // it "should" be the executable + kdDebug() << "(K3bVideoDVDRippingPreview) Could not start transcode." << endl; + delete m_process; + delete m_tempDir; + m_process = 0; + m_tempDir = 0; + emit previewDone( false ); + } +} + + +void K3bVideoDVDRippingPreview::cancel() +{ + if( m_process && m_process->isRunning() ) { + m_canceled = true; + m_process->kill(); + } +} + + +void K3bVideoDVDRippingPreview::slotTranscodeFinished( KProcess* ) +{ + // read the image + QString filename = m_tempDir->name() + "000000.ppm";// + tempQDir->entryList( QDir::Files ).first(); + kdDebug() << "(K3bVideoDVDRippingPreview) reading from file " << filename << endl; + m_preview = QImage( filename ); + bool success = !m_preview.isNull() && !m_canceled; + + // remove temp files + delete m_tempDir; + m_tempDir = 0; + + // clean up + delete m_process; + m_process = 0; + + // retry the first chapter in case another failed + if( !success && m_chapter > 1 ) + generatePreview( m_dvd, m_title, 1 ); + else + emit previewDone( success ); +} + +#include "k3bvideodvdrippingpreview.moc" diff --git a/src/rip/videodvd/k3bvideodvdrippingpreview.h b/src/rip/videodvd/k3bvideodvdrippingpreview.h new file mode 100644 index 0000000..f5beb69 --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingpreview.h @@ -0,0 +1,66 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_VIDEODVD_RIPPING_PREVIEW_H_ +#define _K3B_VIDEODVD_RIPPING_PREVIEW_H_ + +#include +#include + +#include + + +class KTempDir; +class KProcess; + +class K3bVideoDVDRippingPreview : public QObject +{ + Q_OBJECT + + public: + K3bVideoDVDRippingPreview( QObject* parent = 0 ); + ~K3bVideoDVDRippingPreview(); + + const QImage& preview() const { return m_preview; } + + public slots: + /** + * \param dvd The Video DVD object + * \param title The Video DVD title to generate the preview for + * \param chapter The Chapter number to use for the preview. + * If 0 the middle of the title is used. + */ + void generatePreview( const K3bVideoDVD::VideoDVD& dvd, int title, int chapter = 0 ); + + void cancel(); + + signals: + void previewDone( bool ); + + private slots: + void slotTranscodeFinished( KProcess* ); + + private: + QImage m_preview; + KTempDir* m_tempDir; + KProcess* m_process; + int m_title; + int m_chapter; + K3bVideoDVD::VideoDVD m_dvd; + + bool m_canceled; +}; + +#endif diff --git a/src/rip/videodvd/k3bvideodvdrippingtitlelistview.cpp b/src/rip/videodvd/k3bvideodvdrippingtitlelistview.cpp new file mode 100644 index 0000000..85379dc --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingtitlelistview.cpp @@ -0,0 +1,410 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bvideodvdrippingtitlelistview.h" +#include "k3bvideodvdrippingpreview.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +static QString audioStreamString( const K3bVideoDVD::Title& title, unsigned int maxLines = 9999, bool includeExtInfo = true ) +{ + QString s = "

"; + for( unsigned int i = 0; i < QMIN( title.numAudioStreams(), maxLines ); ++i ) { + if( i > 0 ) + s += "
"; + s += QString::number(i+1) + ": " + + i18n("%1 %2Ch (%3%4)") + .arg( K3bVideoDVD::audioFormatString( title.audioStream(i).format() ) ) + .arg( title.audioStream(i).channels() ) + .arg( title.audioStream(i).langCode().isEmpty() + ? i18n("unknown language") + : KGlobal::locale()->twoAlphaToLanguageName( title.audioStream(i).langCode() ) ) + .arg( includeExtInfo && title.audioStream(i).codeExtension() != K3bVideoDVD::AUDIO_CODE_EXT_UNSPECIFIED + ? QString(" ") + K3bVideoDVD::audioCodeExtensionString( title.audioStream(i).codeExtension() ) + : QString::null ); + } + if( title.numAudioStreams() > maxLines ) + s += "..."; + + return s; +} + + +static QString subpictureStreamString( const K3bVideoDVD::Title& title, unsigned int maxLines = 9999, bool includeExtInfo = true ) +{ + QString s = "

"; + for( unsigned int i = 0; i < QMIN( title.numSubPictureStreams(), maxLines ); ++i ) { + if( i > 0 ) + s += "
"; + s += QString::number(i+1) + ": " + + QString("%1 (%2%3)") + .arg( title.subPictureStream(i).codeMode() == K3bVideoDVD::SUBPIC_CODE_MODE_RLE + ? i18n("RLE") + : i18n("Extended") ) + .arg( title.subPictureStream(i).langCode().isEmpty() + ? i18n("unknown language") + : KGlobal::locale()->twoAlphaToLanguageName( title.subPictureStream(i).langCode() ) ) + .arg( includeExtInfo && title.subPictureStream(i).codeExtension() != K3bVideoDVD::SUBPIC_CODE_EXT_UNSPECIFIED + ? QString(" ") + K3bVideoDVD::subPictureCodeExtensionString( title.subPictureStream(i).codeExtension() ) + : QString::null ); + } + if( title.numSubPictureStreams() > maxLines ) + s += "..."; + + return s; +} + + + +class K3bVideoDVDRippingTitleListView::TitleViewItem : public K3bCheckListViewItem +{ +public: + TitleViewItem( K3bVideoDVDRippingTitleListView* parent, QListViewItem* after, const K3bVideoDVD::Title& title ) + : K3bCheckListViewItem( parent, after ), + m_title( title ) { + + setMarginVertical( 4 ); + setMarginHorizontal( 1, 2 ); + setMarginHorizontal( 2, 2 ); + setMarginHorizontal( 3, 2 ); + setMarginHorizontal( 4, 2 ); + setMarginHorizontal( 5, 2 ); + setChecked(true); + + m_previewSet = false; + } + + const K3bVideoDVD::Title& videoDVDTitle() const { return m_title; } + + void setup() { + widthChanged(); + + // set a valid height + int maxH = 0; + for( int c = 1; c <= 4; ++c ) { + QSimpleRichText rt( text(c), listView()->font() ); + rt.setWidth( 600 ); // way to big to avoid line breaks + maxH = QMAX( maxH, rt.height() ); + } + + setHeight( maxH + 2*marginVertical() ); + } + + int width( const QFontMetrics& fm, const QListView* lv, int c ) const { + if( c == 0 ) + return K3bCheckListViewItem::width( fm, lv, c ); + else { + QSimpleRichText rt( text(c), lv->font() ); + rt.setWidth( 600 ); // way to big to avoid line breaks + return rt.widthUsed() + 2*marginHorizontal( c ); + } + } + + void setPreview( const QImage& preview ) { + m_preview = preview; + m_scaledPreview = QPixmap(); + + m_previewSet = true; + + repaint(); + } + + const QImage& preview() const { + return m_preview; + } + +protected: + void paintK3bCell( QPainter* p, const QColorGroup& cg, int col, int w, int align ) { + p->save(); + + if( col == 0 ) { + // the check mark + K3bCheckListViewItem::paintK3bCell( p, cg, col, w, align ); + } + else if( col == 2 ) { + if( isSelected() ) { + p->fillRect( 0, 0, w, height(), + cg.brush( QColorGroup::Highlight ) ); + p->setPen( cg.highlightedText() ); + } + else { + p->fillRect( 0, 0, w, height(), cg.base() ); + p->setPen( cg.text() ); + } + + // draw the preview + int h = height(); + h -= 2*marginVertical(); + h -= 1; // the separator + if( !m_preview.isNull() ) { + if( m_scaledPreview.height() != h ) { + // recreate scaled preview + int preH = m_preview.height()*w/m_preview.width(); + int preW = m_preview.width()*h/m_preview.height(); + if( preH > h ) + preH = m_preview.height()*preW/m_preview.width(); + if( preW > w ) + preW = m_preview.width()*preH/m_preview.height(); + m_scaledPreview.convertFromImage( m_preview.smoothScale( preW, preH ), 0 ); + } + + // center the preview in the column + int yPos = ( height() - m_scaledPreview.height() ) / 2; + int xPos = ( w - m_scaledPreview.width() ) / 2; + + p->drawPixmap( xPos, yPos, m_scaledPreview ); + } + else if( m_previewSet ) { + int preW = 0; + if( m_title.videoStream().displayAspectRatio() == K3bVideoDVD::VIDEO_ASPECT_RATIO_4_3 ) + preW = h*4/3; + else + preW = h*16/9; + + p->drawRect( ( w - preW ) / 2, ( height() - h ) / 2, preW, h ); + QPixmap noIcon = KApplication::kApplication()->iconLoader()->loadIcon( "no", KIcon::NoGroup, KIcon::SizeSmall, KIcon::DefaultState, 0, true ); + p->drawPixmap( ( w - noIcon.width() ) / 2, ( height() - noIcon.height() ) / 2, noIcon ); + } + else { + p->drawText( 0, 0, w, height(), Qt::AlignCenter, "..." ); + } + } + else { + QString s = text( col ); + if( s.isEmpty() ) + K3bCheckListViewItem::paintK3bCell( p, cg, col, w, align ); + else { + QColorGroup cg1( cg ); + if( isSelected() ) { + p->fillRect( 0, 0, w, height(), + cg.brush( QColorGroup::Highlight ) ); + cg1.setColor( QColorGroup::Text, cg.highlightedText() ); + } + else { + p->fillRect( 0, 0, w, height(), cg.base() ); + } + + // paint using QSimpleRichText + QSimpleRichText rt( text(col), listView()->font() ); + rt.setWidth( 600 ); // way to big to avoid line breaks + // normally we would have to clip the height to height()-2*marginVertical(). But if we do that + // some characters are cut (such as p or q). It seems as if QSimpleRichText does not properly + // calculate it's height... + rt.draw( p, 0, marginVertical(), QRect( 0, 0, w, height() ), cg1 ); + } + } + + // draw the separator + if( listView()->firstChild() != this ) { + p->translate( -1*marginHorizontal(col), 0 ); + // FIXME: modify the value from palette().disabled().foreground() to be lighter (or darker, depending on the background color ) + p->setPen( Qt::lightGray ); + p->drawLine( 0, 0, w+2*marginHorizontal(col), 0 ); + } + + p->restore(); + } + +private: + QString text( int col ) const { + switch( col ) { + case 1: + // Title X + length + return i18n("

Title %1 (%2)
" + "%3") + .arg( m_title.titleNumber(), 2 ) + .arg( m_title.playbackTime().toString( false ) ) + .arg( i18n("%n chapter", "%n chapters", m_title.numPTTs() ) ); + + case 3: + // video stream info + return QString("

%1 %2x%3
%4%5") + .arg( m_title.videoStream().mpegVersion() == 0 ? i18n("MPEG1") : i18n("MPEG2") ) + .arg( m_title.videoStream().pictureWidth() ) + .arg( m_title.videoStream().pictureHeight() ) + .arg( m_title.videoStream().displayAspectRatio() == K3bVideoDVD::VIDEO_ASPECT_RATIO_4_3 ? "4:3" : "16:9" ) + .arg( m_title.videoStream().letterboxed() ? QString(" - ") + i18n("letterboxed") + QString(""): + m_title.videoStream().permittedDf() == K3bVideoDVD::VIDEO_PERMITTED_DF_LETTERBOXED + ? QString(" - ") + i18n("anamorph") + QString("") : QString::null ); + + case 4: + // audio streams info + if( m_title.numAudioStreams() > 0 ) + return audioStreamString( m_title, 2, false ); + else + return "

" + i18n("No audio streams") + ""; + + case 5: + // subpicture streams info + if( m_title.numSubPictureStreams() > 0 ) + return subpictureStreamString( m_title, 2, false ); + else + return "

" + i18n("No Subpicture streams") + ""; + + default: + return K3bCheckListViewItem::text( col ); + } + } + + K3bVideoDVD::Title m_title; + + bool m_previewSet; + QImage m_preview; + QPixmap m_scaledPreview; +}; + + +class K3bVideoDVDRippingTitleListView::TitleToolTip : public K3bToolTip +{ +public: + TitleToolTip( K3bVideoDVDRippingTitleListView* view ) + : K3bToolTip( view->viewport() ), + m_view( view ) { + } + + void maybeTip( const QPoint& pos ) { + TitleViewItem* item = static_cast( m_view->itemAt( pos ) ); + QPoint contentsPos = m_view->viewportToContents( pos ); + if( !item ) + return; + int col = m_view->header()->sectionAt( contentsPos.x() ); + + QRect r = m_view->itemRect( item ); + int headerPos = m_view->header()->sectionPos( col ); + r.setLeft( headerPos ); + r.setRight( headerPos + m_view->header()->sectionSize( col ) ); + + switch( col ) { + case 2: + if( !item->preview().isNull() ) { + QPixmap previewPix; + if( previewPix.convertFromImage( item->preview() ) ) + tip( r, previewPix, 0 ); + } + break; + case 4: + if( item->videoDVDTitle().numAudioStreams() > 0 ) + tip( r, "

" + i18n("Audio Streams") + "

" + audioStreamString( item->videoDVDTitle() ), 0 ); + break; + case 5: + if( item->videoDVDTitle().numSubPictureStreams() > 0 ) + tip( r, "

" + i18n("Subpicture Streams") + "

" + subpictureStreamString( item->videoDVDTitle() ), 0 ); + break; + } + } + +private: + K3bVideoDVDRippingTitleListView* m_view; +}; + + + +K3bVideoDVDRippingTitleListView::K3bVideoDVDRippingTitleListView( QWidget* parent ) + : K3bListView( parent ) +{ + setFullWidth(true); + setSorting(-1); + setAllColumnsShowFocus( true ); + setSelectionModeExt( Single ); + + addColumn( "" ); + addColumn( i18n("Title") ); + addColumn( i18n("Preview") ); + addColumn( i18n("Video") ); + addColumn( i18n("Audio") ); + addColumn( i18n("Subpicture") ); + + header()->setClickEnabled( false ); + setColumnWidthMode( 0, QListView::Manual ); + setColumnWidth( 0, 20 ); + header()->setResizeEnabled( false, 0 ); + + m_toolTip = new TitleToolTip( this ); + + m_previewGen = new K3bVideoDVDRippingPreview( this ); + connect( m_previewGen, SIGNAL(previewDone(bool)), + this, SLOT(slotPreviewDone(bool)) ); +} + + +K3bVideoDVDRippingTitleListView::~K3bVideoDVDRippingTitleListView() +{ + delete m_toolTip; +} + + +void K3bVideoDVDRippingTitleListView::setVideoDVD( const K3bVideoDVD::VideoDVD& dvd ) +{ + clear(); + + m_dvd = dvd; + m_medium = k3bappcore->mediaCache()->medium( m_dvd.device() ); + m_itemMap.resize( dvd.numTitles() ); + + for( unsigned int i = 0; i < dvd.numTitles(); ++i ) + m_itemMap[i] = new TitleViewItem( this, lastItem(), dvd.title(i) ); + + m_currentPreviewTitle = 1; + m_previewGen->generatePreview( m_dvd, 1 ); +} + + +void K3bVideoDVDRippingTitleListView::slotPreviewDone( bool success ) +{ + if( success ) + m_itemMap[m_currentPreviewTitle-1]->setPreview( m_previewGen->preview() ); + else + m_itemMap[m_currentPreviewTitle-1]->setPreview( QImage() ); + + // cancel if we got hidden or if the medium changed. + if( isVisible() && m_medium == k3bappcore->mediaCache()->medium( m_dvd.device() ) ) { + ++m_currentPreviewTitle; + if( m_currentPreviewTitle <= m_dvd.numTitles() ) + m_previewGen->generatePreview( m_dvd, m_currentPreviewTitle ); + } +} + + +void K3bVideoDVDRippingTitleListView::hideEvent( QHideEvent* e ) +{ + // + // For now we do it the easy way: just stop the preview generation + // once this view is hidden + // + m_previewGen->cancel(); + + K3bListView::hideEvent( e ); +} + +#include "k3bvideodvdrippingtitlelistview.moc" diff --git a/src/rip/videodvd/k3bvideodvdrippingtitlelistview.h b/src/rip/videodvd/k3bvideodvdrippingtitlelistview.h new file mode 100644 index 0000000..7c21815 --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingtitlelistview.h @@ -0,0 +1,58 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_VIDEODVD_RIPPING_TITLE_LISTVIEW_H_ +#define _K3B_VIDEODVD_RIPPING_TITLE_LISTVIEW_H_ + +#include +#include +#include + +#include + + +class K3bVideoDVDRippingPreview; +class QHideEvent; + +class K3bVideoDVDRippingTitleListView : public K3bListView +{ + Q_OBJECT + + public: + K3bVideoDVDRippingTitleListView( QWidget* parent ); + ~K3bVideoDVDRippingTitleListView(); + + void setVideoDVD( const K3bVideoDVD::VideoDVD& dvd ); + + private slots: + void slotPreviewDone( bool ); + + private: + void hideEvent( QHideEvent* ); + + class TitleViewItem; + class TitleToolTip; + + TitleToolTip* m_toolTip; + + QValueVector m_itemMap; + K3bVideoDVDRippingPreview* m_previewGen; + unsigned int m_currentPreviewTitle; + + K3bVideoDVD::VideoDVD m_dvd; + K3bMedium m_medium; +}; + +#endif diff --git a/src/rip/videodvd/k3bvideodvdrippingview.cpp b/src/rip/videodvd/k3bvideodvdrippingview.cpp new file mode 100644 index 0000000..f6c8c8d --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingview.cpp @@ -0,0 +1,256 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bvideodvdrippingview.h" +#include "k3bvideodvdrippingtitlelistview.h" +#include "k3bvideodvdrippingdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +K3bVideoDVDRippingView::K3bVideoDVDRippingView( QWidget* parent, const char * name ) + : K3bMediaContentsView( true, + K3bMedium::CONTENT_VIDEO_DVD, + K3bDevice::MEDIA_DVD_ALL, + K3bDevice::STATE_INCOMPLETE|K3bDevice::STATE_COMPLETE, + parent, name ) +{ + QGridLayout* mainGrid = new QGridLayout( mainWidget() ); + + // toolbox + // ---------------------------------------------------------------------------------- + QHBoxLayout* toolBoxLayout = new QHBoxLayout( 0, 0, 0, "toolBoxLayout" ); + m_toolBox = new K3bToolBox( mainWidget() ); + toolBoxLayout->addWidget( m_toolBox ); + QSpacerItem* spacer = new QSpacerItem( 10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum ); + toolBoxLayout->addItem( spacer ); + m_labelLength = new QLabel( mainWidget() ); + m_labelLength->setAlignment( int( QLabel::AlignVCenter | QLabel::AlignRight ) ); + toolBoxLayout->addWidget( m_labelLength ); + + + // the title view + // ---------------------------------------------------------------------------------- + m_titleView = new K3bVideoDVDRippingTitleListView( mainWidget() ); + + connect( m_titleView, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)), + this, SLOT(slotContextMenu(KListView*, QListViewItem*, const QPoint&)) ); + + // general layout + // ---------------------------------------------------------------------------------- + mainGrid->addLayout( toolBoxLayout, 0, 0 ); + mainGrid->addWidget( m_titleView, 1, 0 ); + + + initActions(); + + m_toolBox->addButton( actionCollection()->action("start_rip"), true ); + + setLeftPixmap( K3bTheme::MEDIA_LEFT ); + setRightPixmap( K3bTheme::MEDIA_VIDEO ); +} + + +K3bVideoDVDRippingView::~K3bVideoDVDRippingView() +{ +} + + +void K3bVideoDVDRippingView::reloadMedium() +{ + // + // For VideoDVD reading it is important that the DVD is not mounted + // + if( K3b::isMounted( device() ) && !K3b::unmount( device() ) ) { + KMessageBox::error( this, + i18n("K3b was unable to unmount device '%1' containing medium '%2'. " + "Video DVD ripping will not work if the device is mounted. " + "Please unmount manually."), + i18n("Unmounting failed") ); + } + + // + // K3bVideoDVD::open does not necessarily fail on encrypted DVDs if dvdcss is not + // available. Thus, we test the availability of libdvdcss here + // + if( device()->copyrightProtectionSystemType() == 1 ) { + K3bLibDvdCss* css = K3bLibDvdCss::create(); + if( !css ) { + KMessageBox::error( this, i18n("

Unable to read Video DVD contents: Found encrypted Video DVD." + "

Install libdvdcss to get Video DVD decryption support.") ); + return; + } + else + delete css; + } + + QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); + + if( m_dvd.open( device() ) ) { + setTitle( medium().beautifiedVolumeId() + " (" + i18n("Video DVD") + ")" ); + m_labelLength->setText( i18n("%n title", "%n titles", m_dvd.numTitles() ) ); + m_titleView->setVideoDVD( m_dvd ); + QApplication::restoreOverrideCursor(); + + bool transcodeUsable = true; + + if( !k3bcore ->externalBinManager() ->foundBin( "transcode" ) ) { + KMessageBox::sorry( this, + i18n("K3b uses transcode to rip Video DVDs. " + "Please make sure it is installed.") ); + transcodeUsable = false; + } + else { + int vc = 0, ac = 0; + for( int i = 0; i < K3bVideoDVDTitleTranscodingJob::VIDEO_CODEC_NUM_ENTRIES; ++i ) + if( K3bVideoDVDTitleTranscodingJob::transcodeBinaryHasSupportFor( (K3bVideoDVDTitleTranscodingJob::VideoCodec)i ) ) + ++vc; + for( int i = 0; i < K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_NUM_ENTRIES; ++i ) + if( K3bVideoDVDTitleTranscodingJob::transcodeBinaryHasSupportFor( (K3bVideoDVDTitleTranscodingJob::AudioCodec)i ) ) + ++ac; + if( !ac || !vc ) { + KMessageBox::sorry( this, + i18n("

K3b uses transcode to rip Video DVDs. " + "Your installation of transcode (%1) lacks support for any of the " + "codecs supported by K3b." + "

Please make sure it is installed properly.") ); + transcodeUsable = false; + } + } + + actionCollection()->action("start_rip")->setEnabled( transcodeUsable ); + } + else { + QApplication::restoreOverrideCursor(); + + KMessageBox::error( this, i18n("Unable to read Video DVD contents.") ); + } +} + + +void K3bVideoDVDRippingView::slotStartRipping() +{ + QValueList titles; + int i = 1; + for( QListViewItemIterator it( m_titleView ); *it; ++it, ++i ) + if( static_cast( *it )->isChecked() ) + titles.append( i ); + + if( titles.isEmpty() ) { + KMessageBox::error( this, i18n("Please select the titles to rip."), + i18n("No Titles Selected") ); + } + else { + K3bVideoDVDRippingDialog dlg( m_dvd, titles, this ); + dlg.exec(); + } +} + + +void K3bVideoDVDRippingView::slotContextMenu( KListView*, QListViewItem*, const QPoint& p ) +{ + m_popupMenu->popup(p); +} + + +void K3bVideoDVDRippingView::slotCheckAll() +{ + for( QListViewItemIterator it( m_titleView ); it.current(); ++it ) + dynamic_cast(it.current())->setChecked(true); +} + + +void K3bVideoDVDRippingView::slotUncheckAll() +{ + for( QListViewItemIterator it( m_titleView ); it.current(); ++it ) + dynamic_cast(it.current())->setChecked(false); +} + + +void K3bVideoDVDRippingView::slotCheck() +{ + QPtrList items( m_titleView->selectedItems() ); + for( QPtrListIterator it( items ); + it.current(); ++it ) + dynamic_cast(it.current())->setChecked(true); +} + + +void K3bVideoDVDRippingView::slotUncheck() +{ + QPtrList items( m_titleView->selectedItems() ); + for( QPtrListIterator it( items ); + it.current(); ++it ) + dynamic_cast(it.current())->setChecked(false); +} + + +void K3bVideoDVDRippingView::initActions() +{ + m_actionCollection = new KActionCollection( this ); + + KAction* actionSelectAll = new KAction( i18n("Check All"), 0, 0, this, + SLOT(slotCheckAll()), actionCollection(), + "check_all" ); + KAction* actionDeselectAll = new KAction( i18n("Uncheck All"), 0, 0, this, + SLOT(slotUncheckAll()), actionCollection(), + "uncheck_all" ); + KAction* actionSelect = new KAction( i18n("Check Track"), 0, 0, this, + SLOT(slotCheck()), actionCollection(), + "select_track" ); + KAction* actionDeselect = new KAction( i18n("Uncheck Track"), 0, 0, this, + SLOT(slotUncheck()), actionCollection(), + "deselect_track" ); + KAction* actionStartRip = new KAction( i18n("Start Ripping"), "gear", 0, this, + SLOT(slotStartRipping()), m_actionCollection, "start_rip" ); + + actionStartRip->setToolTip( i18n("Open the Video DVD ripping dialog") ); + + // setup the popup menu + m_popupMenu = new KActionMenu( actionCollection(), "popup_menu" ); + KAction* separator = new KActionSeparator( actionCollection(), "separator" ); + m_popupMenu->insert( actionSelect ); + m_popupMenu->insert( actionDeselect ); + m_popupMenu->insert( actionSelectAll ); + m_popupMenu->insert( actionDeselectAll ); + m_popupMenu->insert( separator ); + m_popupMenu->insert( actionStartRip ); +} + + +void K3bVideoDVDRippingView::enableInteraction( bool enable ) +{ + actionCollection()->action( "start_rip" )->setEnabled( enable ); +} + + +#include "k3bvideodvdrippingview.moc" diff --git a/src/rip/videodvd/k3bvideodvdrippingview.h b/src/rip/videodvd/k3bvideodvdrippingview.h new file mode 100644 index 0000000..679ea09 --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingview.h @@ -0,0 +1,66 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_VIDEODVD_RIPPING_VIEW_H_ +#define _K3B_VIDEODVD_RIPPING_VIEW_H_ + +#include +#include +#include + +class K3bVideoDVDRippingTitleListView; +class K3bToolBox; +class QLabel; +class KActionCollection; +class KActionMenu; +class KListView; +class QListViewItem; + +class K3bVideoDVDRippingView : public K3bMediaContentsView +{ + Q_OBJECT + + public: + K3bVideoDVDRippingView( QWidget* parent = 0, const char * name = 0 ); + ~K3bVideoDVDRippingView(); + + KActionCollection* actionCollection() const { return m_actionCollection; } + + private slots: + void slotStartRipping(); + + void slotContextMenu( KListView*, QListViewItem*, const QPoint& ); + + void slotCheckAll(); + void slotUncheckAll(); + void slotCheck(); + void slotUncheck(); + + private: + void reloadMedium(); + void enableInteraction( bool enable ); + void initActions(); + + KActionCollection* m_actionCollection; + KActionMenu* m_popupMenu; + + K3bToolBox* m_toolBox; + QLabel* m_labelLength; + K3bVideoDVDRippingTitleListView* m_titleView; + + K3bVideoDVD::VideoDVD m_dvd; +}; + +#endif diff --git a/src/rip/videodvd/k3bvideodvdrippingwidget.cpp b/src/rip/videodvd/k3bvideodvdrippingwidget.cpp new file mode 100644 index 0000000..721d191 --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingwidget.cpp @@ -0,0 +1,375 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#include "k3bvideodvdrippingwidget.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static const int s_mp3Bitrates[] = { + 32, + 40, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 160, + 192, + 224, + 256, + 320, + 0 // just used for the loops below +}; + + +static const int PICTURE_SIZE_ORIGINAL = 0; +static const int PICTURE_SIZE_640 = 1; +static const int PICTURE_SIZE_320 = 2; +static const int PICTURE_SIZE_CUSTOM = 3; +static const int PICTURE_SIZE_MAX = 4; + +static const char* s_pictureSizeNames[] = { + I18N_NOOP("Keep original dimensions"), + I18N_NOOP("640x? (automatic height)"), + I18N_NOOP("320x? (automatic height)"), + I18N_NOOP("Custom") +}; + + +K3bVideoDVDRippingWidget::K3bVideoDVDRippingWidget( QWidget* parent ) + : base_K3bVideoDVDRippingWidget( parent ) +{ + m_editBaseDir->setMode( KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly ); + + m_titleView->addColumn( i18n("Title") ); + m_titleView->addColumn( i18n("Video Size") ); + m_titleView->addColumn( i18n("File Size") ); + m_titleView->addColumn( i18n("Filename") ); + m_titleView->setSorting( -1 ); + + // + // Example filename pattern + // + m_comboFilenamePattern->insertItem( QString( "%b - %1 %t (%n %a %c)" ).arg(i18n("Title") ) ); + m_comboFilenamePattern->insertItem( QString( "%{volumeid} (%{title})" ) ); + + + // + // Add the Audio bitrates + // + for( int i = 0; s_mp3Bitrates[i]; ++i ) + m_comboAudioBitrate->insertItem( i18n("%1 kbps" ).arg(s_mp3Bitrates[i]) ); + + + for( int i = 0; i < K3bVideoDVDTitleTranscodingJob::VIDEO_CODEC_NUM_ENTRIES; ++i ) { + K3bVideoDVDTitleTranscodingJob::VideoCodec codec( (K3bVideoDVDTitleTranscodingJob::VideoCodec)i ); + if( K3bVideoDVDTitleTranscodingJob::transcodeBinaryHasSupportFor( codec ) ) + m_comboVideoCodec->insertItem( i, + K3bVideoDVDTitleTranscodingJob::videoCodecString( codec ), + K3bVideoDVDTitleTranscodingJob::videoCodecDescription( codec ) ); + } + for( int i = 0; i < K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_NUM_ENTRIES; ++i ) { + K3bVideoDVDTitleTranscodingJob::AudioCodec codec( (K3bVideoDVDTitleTranscodingJob::AudioCodec)i ); + if( K3bVideoDVDTitleTranscodingJob::transcodeBinaryHasSupportFor( codec ) ) + m_comboAudioCodec->insertItem( i, + K3bVideoDVDTitleTranscodingJob::audioCodecString( codec ), + K3bVideoDVDTitleTranscodingJob::audioCodecDescription( codec ) ); + } + + for( int i = 0; i < PICTURE_SIZE_MAX; ++i ) { + m_comboVideoSize->insertItem( i18n( s_pictureSizeNames[i] ) ); + } + + slotAudioCodecChanged( m_comboAudioCodec->selectedValue() ); + + connect( m_comboAudioBitrate, SIGNAL(textChanged(const QString&)), + this, SIGNAL(changed()) ); + connect( m_spinVideoBitrate, SIGNAL(valueChanged(int)), + this, SIGNAL(changed()) ); + connect( m_checkBlankReplace, SIGNAL(toggled(bool)), + this, SIGNAL(changed()) ); + connect( m_editBlankReplace, SIGNAL(textChanged(const QString&)), + this, SIGNAL(changed()) ); + connect( m_comboFilenamePattern, SIGNAL(textChanged(const QString&)), + this, SIGNAL(changed()) ); + connect( m_editBaseDir, SIGNAL(textChanged(const QString&)), + this, SIGNAL(changed()) ); + + connect( m_comboAudioCodec, SIGNAL(valueChanged(int)), + this, SLOT(slotAudioCodecChanged(int)) ); + connect( m_specialStringsLabel, SIGNAL(leftClickedURL()), + this, SLOT(slotSeeSpecialStrings()) ); + connect( m_buttonCustomPictureSize, SIGNAL(clicked()), + this, SLOT(slotCustomPictureSize()) ); + connect( m_comboVideoSize, SIGNAL(activated(int)), + this, SLOT(slotVideoSizeChanged(int)) ); + + // refresh every 2 seconds + m_freeSpaceUpdateTimer = new QTimer( this ); + connect( m_freeSpaceUpdateTimer, SIGNAL(timeout()), + this, SLOT(slotUpdateFreeTempSpace()) ); + m_freeSpaceUpdateTimer->start(2000); + slotUpdateFreeTempSpace(); +} + + +K3bVideoDVDRippingWidget::~K3bVideoDVDRippingWidget() +{ +} + + +K3bVideoDVDTitleTranscodingJob::VideoCodec K3bVideoDVDRippingWidget::selectedVideoCodec() const +{ + return (K3bVideoDVDTitleTranscodingJob::VideoCodec)m_comboVideoCodec->selectedValue(); +} + + +QSize K3bVideoDVDRippingWidget::selectedPictureSize() const +{ + switch( m_comboVideoSize->currentItem() ) { + case PICTURE_SIZE_ORIGINAL: + return QSize(0,0); + case PICTURE_SIZE_640: + return QSize(640,0); + case PICTURE_SIZE_320: + return QSize(320,0); + default: + return m_customVideoSize; + } +} + + +void K3bVideoDVDRippingWidget::setSelectedPictureSize( const QSize& size ) +{ + m_customVideoSize = size; + if( size == QSize(0,0) ) + m_comboVideoSize->setCurrentItem( PICTURE_SIZE_ORIGINAL ); + else if( size == QSize(640,0) ) + m_comboVideoSize->setCurrentItem( PICTURE_SIZE_640 ); + else if( size == QSize(320,0) ) + m_comboVideoSize->setCurrentItem( PICTURE_SIZE_320 ); + else { + m_comboVideoSize->changeItem( i18n(s_pictureSizeNames[PICTURE_SIZE_CUSTOM]) + + QString(" (%1x%2)") + .arg(size.width() == 0 ? i18n("auto") : QString::number(size.width())) + .arg(size.height() == 0 ? i18n("auto") : QString::number(size.height())), + PICTURE_SIZE_CUSTOM ); + m_comboVideoSize->setCurrentItem( PICTURE_SIZE_CUSTOM ); + } +} + + +void K3bVideoDVDRippingWidget::setSelectedVideoCodec( K3bVideoDVDTitleTranscodingJob::VideoCodec codec ) +{ + m_comboVideoCodec->setSelectedValue( (int)codec ); +} + + +K3bVideoDVDTitleTranscodingJob::AudioCodec K3bVideoDVDRippingWidget::selectedAudioCodec() const +{ + return (K3bVideoDVDTitleTranscodingJob::AudioCodec)m_comboAudioCodec->selectedValue(); +} + + +void K3bVideoDVDRippingWidget::setSelectedAudioCodec( K3bVideoDVDTitleTranscodingJob::AudioCodec codec ) +{ + m_comboAudioCodec->setSelectedValue( (int)codec ); + slotAudioCodecChanged( (int)codec ); +} + + +int K3bVideoDVDRippingWidget::selectedAudioBitrate() const +{ + if( selectedAudioCodec() == K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_MP3 ) + return s_mp3Bitrates[m_comboAudioBitrate->currentItem()]; + else + return m_spinAudioBitrate->value(); +} + + +void K3bVideoDVDRippingWidget::setSelectedAudioBitrate( int bitrate ) +{ + m_spinAudioBitrate->setValue( bitrate ); + + // select the bitrate closest to "bitrate" + int bi = 0; + int diff = 1000; + for( int i = 0; s_mp3Bitrates[i]; ++i ) { + int newDiff = s_mp3Bitrates[i] - bitrate; + if( newDiff < 0 ) + newDiff = -1 * newDiff; + if( newDiff < diff ) { + diff = newDiff; + bi = i; + } + } + + m_comboAudioBitrate->setCurrentItem( bi ); +} + + +void K3bVideoDVDRippingWidget::slotUpdateFreeTempSpace() +{ + QString path = m_editBaseDir->url(); + + if( !QFile::exists( path ) ) + path.truncate( path.findRev('/') ); + + unsigned long size, avail; + if( K3b::kbFreeOnFs( path, size, avail ) ) { + m_labelFreeSpace->setText( KIO::convertSizeFromKB(avail) ); + if( avail < m_neededSize/1024 ) + m_labelNeededSpace->setPaletteForegroundColor( Qt::red ); + else + m_labelNeededSpace->setPaletteForegroundColor( paletteForegroundColor() ); + } + else { + m_labelFreeSpace->setText("-"); + m_labelNeededSpace->setPaletteForegroundColor( paletteForegroundColor() ); + } +} + + +void K3bVideoDVDRippingWidget::setNeededSize( KIO::filesize_t size ) +{ + m_neededSize = size; + if( size > 0 ) + m_labelNeededSpace->setText( KIO::convertSize( size ) ); + else + m_labelNeededSpace->setText( i18n("unknown") ); + + slotUpdateFreeTempSpace(); +} + + +void K3bVideoDVDRippingWidget::slotSeeSpecialStrings() +{ + QWhatsThis::display( i18n( "

Pattern special strings:" + "

The following strings will be replaced with their respective meaning in every " + "track name.
" + "

" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
MeaningAlternatives
%ttitle number%{t} or %{title_number}
%ivolume id (mostly the name of the Video DVD)%{i} or %{volume_id}
%bbeautified volume id%{b} or %{beautified_volume_id}
%ltwo chars language code%{l} or %{lang_code}
%nlanguage name%{n} or %{lang_name}
%aaudio format (on the Video DVD)%{a} or %{audio_format}
%cnumber of audio channels (on the Video DVD)%{c} or %{channels}
%vsize of the original video%{v} or %{orig_video_size}
%ssize of the resulting video (Caution: auto-clipping values are not taken into account!)%{s} or %{video_size}
%raspect ratio of the original video%{r} or %{aspect_ratio}
%dcurrent date%{d} or %{date}
" + "

Hint: K3b also accepts slight variations of the long special strings. " + "One can, for example, leave out the underscores.") ); +} + + +void K3bVideoDVDRippingWidget::slotAudioCodecChanged( int codec ) +{ + switch( codec ) { + case K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_MP3: + m_stackAudioQuality->raiseWidget( m_stackPageAudioQualityMp3 ); + break; + case K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_AC3_STEREO: + m_stackAudioQuality->raiseWidget( m_stackPageAudioQualityAC3 ); + break; + case K3bVideoDVDTitleTranscodingJob::AUDIO_CODEC_AC3_PASSTHROUGH: + m_stackAudioQuality->raiseWidget( m_stackPageAudioQualityAC3Pt ); + break; + } + + emit changed(); +} + + +void K3bVideoDVDRippingWidget::slotVideoSizeChanged( int sizeIndex ) +{ + if( sizeIndex == PICTURE_SIZE_CUSTOM ) + slotCustomPictureSize(); + else + emit changed(); +} + + +void K3bVideoDVDRippingWidget::slotCustomPictureSize() +{ + KDialogBase dlg( KDialogBase::Plain, + i18n("Video Picture Size"), + KDialogBase::Ok|KDialogBase::Cancel, + KDialogBase::Ok, + this, + 0, + true, + true ); + K3bRichTextLabel* label = new K3bRichTextLabel( i18n("

Please choose the width and height of the resulting video. " + "If one value is set to Auto K3b will choose this value " + "depending on the aspect ratio of the video picture.
" + "Be aware that setting both the width and the height to fixed values " + "will result in no aspect ratio correction to be performed."), + dlg.plainPage() ); + QSpinBox* spinWidth = new QSpinBox( 0, 20000, 16, dlg.plainPage() ); + QSpinBox* spinHeight = new QSpinBox( 0, 20000, 16, dlg.plainPage() ); + spinWidth->setSpecialValueText( i18n("Auto") ); + spinHeight->setSpecialValueText( i18n("Auto") ); + QLabel* labelW = new QLabel( spinWidth, i18n("Width") + ':', dlg.plainPage() ); + QLabel* labelH = new QLabel( spinHeight, i18n("Height") + ':', dlg.plainPage() ); + labelW->setAlignment( Qt::AlignRight|Qt::AlignVCenter ); + labelH->setAlignment( Qt::AlignRight|Qt::AlignVCenter ); + + QGridLayout* grid = new QGridLayout( dlg.plainPage() ); + grid->setMargin( 0 ); + grid->setSpacing( KDialog::spacingHint() ); + grid->addMultiCellWidget( label, 0, 0, 0, 3 ); + grid->addWidget( labelW, 1, 0 ); + grid->addWidget( spinWidth, 1, 1 ); + grid->addWidget( labelH, 1, 2 ); + grid->addWidget( spinHeight, 1, 3 ); + + spinWidth->setValue( m_customVideoSize.width() ); + spinHeight->setValue( m_customVideoSize.height() ); + + if( dlg.exec() ) { + setSelectedPictureSize( QSize( spinWidth->value(), spinHeight->value() ) ); + emit changed(); + } +} + +#include "k3bvideodvdrippingwidget.moc" diff --git a/src/rip/videodvd/k3bvideodvdrippingwidget.h b/src/rip/videodvd/k3bvideodvdrippingwidget.h new file mode 100644 index 0000000..2d10da7 --- /dev/null +++ b/src/rip/videodvd/k3bvideodvdrippingwidget.h @@ -0,0 +1,67 @@ +/* + * + * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $ + * Copyright (C) 2006 Sebastian Trueg + * + * This file is part of the K3b project. + * Copyright (C) 1998-2007 Sebastian Trueg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file "COPYING" for the exact licensing terms. + */ + +#ifndef _K3B_VIDEODVD_RIPPING_WIDGET_H_ +#define _K3B_VIDEODVD_RIPPING_WIDGET_H_ + +#include "base_k3bvideodvdrippingwidget.h" + +#include + +#include +#include + +#include + +class QTimer; + +class K3bVideoDVDRippingWidget : public base_K3bVideoDVDRippingWidget +{ + Q_OBJECT + + public: + K3bVideoDVDRippingWidget( QWidget* parent ); + ~K3bVideoDVDRippingWidget(); + + K3bVideoDVDTitleTranscodingJob::VideoCodec selectedVideoCodec() const; + K3bVideoDVDTitleTranscodingJob::AudioCodec selectedAudioCodec() const; + int selectedAudioBitrate() const; + QSize selectedPictureSize() const; + + void setSelectedVideoCodec( K3bVideoDVDTitleTranscodingJob::VideoCodec codec ); + void setSelectedAudioCodec( K3bVideoDVDTitleTranscodingJob::AudioCodec codec ); + void setSelectedAudioBitrate( int bitrate ); + void setSelectedPictureSize( const QSize& ); + + void setNeededSize( KIO::filesize_t ); + + signals: + void changed(); + + private slots: + void slotUpdateFreeTempSpace(); + void slotSeeSpecialStrings(); + void slotAudioCodecChanged( int codec ); + void slotVideoSizeChanged( int sizeIndex ); + void slotCustomPictureSize(); + + private: + QTimer* m_freeSpaceUpdateTimer; + KIO::filesize_t m_neededSize; + + QSize m_customVideoSize; +}; + +#endif -- cgit v1.2.1