You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1093 lines
34 KiB
1093 lines
34 KiB
/**************************************************************************** |
|
* copyright :(C) 2006 Roel Meeuws <r.j.meeuws+amarok@gmail.com * |
|
* (C) 2005 Jeff Mitchell <kde-dev@emailgoeshere.com> * |
|
* (C) 2005 Seb Ruiz <me@sebruiz.net> * |
|
* * |
|
* With some code helpers from TDEIO_VFAT * |
|
* (c) 2004 Thomas Loeber <vfat@loeber1.de> * |
|
***************************************************************************/ |
|
|
|
/*************************************************************************** |
|
* * |
|
* 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. * |
|
* * |
|
***************************************************************************/ |
|
|
|
|
|
#define DEBUG_PREFIX "GenericMediaDevice" |
|
|
|
#include "genericmediadevice.h" |
|
|
|
AMAROK_EXPORT_PLUGIN( GenericMediaDevice ) |
|
|
|
#include "amarok.h" |
|
#include "debug.h" |
|
#include "medium.h" |
|
#include "metabundle.h" |
|
#include "collectiondb.h" |
|
#include "collectionbrowser.h" |
|
#include "k3bexporter.h" |
|
#include "playlist.h" |
|
#include "podcastbundle.h" |
|
#include "statusbar/statusbar.h" |
|
#include "transferdialog.h" |
|
#include "genericmediadeviceconfigdialog.h" |
|
|
|
#include <tdeapplication.h> |
|
#include <tdeconfig.h> //download saveLocation |
|
#include <kdiskfreesp.h> |
|
#include <kiconloader.h> //smallIcon |
|
#include <tdeio/job.h> |
|
#include <tdeio/jobclasses.h> |
|
#include <tdeio/netaccess.h> |
|
#include <tdemessagebox.h> |
|
#include <kmountpoint.h> |
|
#include <tdepopupmenu.h> |
|
#include <kurlrequester.h> //downloadSelectedItems() |
|
#include <kurlrequesterdlg.h> //downloadSelectedItems() |
|
|
|
#include <taglib/audioproperties.h> |
|
|
|
#include <unistd.h> //usleep() |
|
|
|
#include <tqcstring.h> |
|
#include <tqfile.h> |
|
#include <qstringx.h> |
|
#include <tqstringlist.h> |
|
|
|
#include <tqcombobox.h> |
|
#include <tqlistbox.h> |
|
#include <tqlineedit.h> |
|
|
|
typedef TQPtrList<GenericMediaFile> MediaFileList; |
|
typedef TQPtrListIterator<GenericMediaFile> MediaFileListIterator; |
|
|
|
/** |
|
* GenericMediaItem Class |
|
*/ |
|
|
|
class GenericMediaItem : public MediaItem |
|
{ |
|
public: |
|
GenericMediaItem( TQListView *parent, TQListViewItem *after = 0 ) |
|
: MediaItem( parent, after ) |
|
{ } |
|
|
|
GenericMediaItem( TQListViewItem *parent, TQListViewItem *after = 0 ) |
|
: MediaItem( parent, after ) |
|
{ } |
|
|
|
// List directories first, always |
|
int |
|
compare( TQListViewItem *i, int col, bool ascending ) const |
|
{ |
|
#define i static_cast<GenericMediaItem *>(i) |
|
switch( type() ) |
|
{ |
|
case MediaItem::DIRECTORY: |
|
if( i->type() == MediaItem::DIRECTORY ) |
|
break; |
|
return -1; |
|
|
|
default: |
|
if( i->type() == MediaItem::DIRECTORY ) |
|
return 1; |
|
} |
|
#undef i |
|
|
|
return MediaItem::compare(i, col, ascending); |
|
} |
|
|
|
private: |
|
bool m_dir; |
|
}; |
|
|
|
class GenericMediaFile |
|
{ |
|
public: |
|
GenericMediaFile( GenericMediaFile *parent, TQString basename, GenericMediaDevice *device ) |
|
: m_parent( parent ) |
|
, m_device( device ) |
|
{ |
|
m_listed = false; |
|
m_children = new MediaFileList(); |
|
|
|
if( m_parent ) |
|
{ |
|
if( m_parent == m_device->getInitialFile() ) |
|
m_viewItem = new GenericMediaItem( m_device->view() ); |
|
else |
|
m_viewItem = new GenericMediaItem( m_parent->getViewItem() ); |
|
setNamesFromBase( basename ); |
|
m_viewItem->setText( 0, m_baseName ); |
|
m_parent->getChildren()->append( this ); |
|
} |
|
else |
|
{ |
|
m_viewItem = 0; |
|
setNamesFromBase( basename ); |
|
} |
|
|
|
m_device->getItemMap()[m_viewItem] = this; |
|
|
|
if( m_device->getFileMap()[m_fullName] ) |
|
{ |
|
debug() << "Trying to create two GenericMediaFile items with same fullName!" << endl; |
|
debug() << "name already existing: " << m_device->getFileMap()[m_fullName]->getFullName() << endl; |
|
delete this; |
|
} |
|
else |
|
{ |
|
m_device->getFileMap()[m_fullName] = this; |
|
} |
|
|
|
} |
|
|
|
~GenericMediaFile() |
|
{ |
|
if( m_parent ) |
|
m_parent->removeChild( this ); |
|
m_device->getItemMap().erase( m_viewItem ); |
|
m_device->getFileMap().erase( m_fullName ); |
|
if ( m_children ) |
|
delete m_children; |
|
if ( m_viewItem ) |
|
delete m_viewItem; |
|
} |
|
|
|
GenericMediaFile* |
|
getParent() { return m_parent; } |
|
|
|
void |
|
setParent( GenericMediaFile* parent ) |
|
{ |
|
m_device->getFileMap().erase( m_fullName ); |
|
m_parent->getChildren()->remove( this ); |
|
m_parent = parent; |
|
if( m_parent ) |
|
m_parent->getChildren()->append( this ); |
|
setNamesFromBase( m_baseName ); |
|
m_device->getFileMap()[m_fullName] = this; |
|
} |
|
|
|
void |
|
removeChild( GenericMediaFile* childToDelete ) { m_children->remove( childToDelete ); } |
|
|
|
GenericMediaItem* |
|
getViewItem() { return m_viewItem; } |
|
|
|
bool |
|
getListed() { return m_listed; } |
|
|
|
void |
|
setListed( bool listed ) { m_listed = listed; } |
|
|
|
TQString |
|
getFullName() { return m_fullName; } |
|
|
|
TQString |
|
getBaseName() { return m_baseName; } |
|
|
|
//always follow this function with setNamesFromBase() |
|
void |
|
setBaseName( TQString &name ) { m_baseName = name; } |
|
|
|
void |
|
setNamesFromBase( const TQString &name = TQString() ) |
|
{ |
|
if( name != TQString() ) |
|
m_baseName = name; |
|
if( m_parent ) |
|
m_fullName = m_parent->getFullName() + '/' + m_baseName; |
|
else |
|
m_fullName = m_baseName; |
|
if( m_viewItem ) |
|
m_viewItem->setBundle( new MetaBundle( KURL::fromPathOrURL( m_fullName ), true, TagLib::AudioProperties::Fast ) ); |
|
} |
|
|
|
MediaFileList* |
|
getChildren() { return m_children; } |
|
|
|
void |
|
deleteAll( bool onlyChildren ) |
|
{ |
|
GenericMediaFile *vmf; |
|
if( m_children && !m_children->isEmpty() ) |
|
{ |
|
MediaFileListIterator it( *m_children ); |
|
while( ( vmf = it.current() ) != 0 ) |
|
{ |
|
++it; |
|
vmf->deleteAll( true ); |
|
} |
|
} |
|
if( onlyChildren ) |
|
delete this; |
|
} |
|
|
|
void |
|
renameAllChildren() |
|
{ |
|
GenericMediaFile *vmf; |
|
if( m_children && !m_children->isEmpty() ) |
|
{ |
|
for( vmf = m_children->first(); vmf; vmf = m_children->next() ) |
|
vmf->renameAllChildren(); |
|
} |
|
setNamesFromBase(); |
|
} |
|
|
|
private: |
|
TQString m_fullName; |
|
TQString m_baseName; |
|
GenericMediaFile *m_parent; |
|
MediaFileList *m_children; |
|
GenericMediaItem *m_viewItem; |
|
GenericMediaDevice* m_device; |
|
bool m_listed; |
|
}; |
|
|
|
TQString |
|
GenericMediaDevice::fileName( const MetaBundle &bundle ) |
|
{ |
|
TQString result = cleanPath( bundle.artist() ); |
|
|
|
if( !result.isEmpty() ) |
|
{ |
|
if( m_spacesToUnderscores ) |
|
result += "_-_"; |
|
else |
|
result += " - "; |
|
} |
|
|
|
if( bundle.track() ) |
|
{ |
|
result.sprintf( "%02d", bundle.track() ); |
|
|
|
if( m_spacesToUnderscores ) |
|
result += '_'; |
|
else |
|
result += ' '; |
|
} |
|
|
|
result += cleanPath( bundle.title() + '.' + bundle.type() ); |
|
|
|
return result; |
|
} |
|
|
|
|
|
/** |
|
* GenericMediaDevice Class |
|
*/ |
|
|
|
GenericMediaDevice::GenericMediaDevice() |
|
: MediaDevice() |
|
, m_kBSize( 0 ) |
|
, m_kBAvail( 0 ) |
|
, m_connected( false ) |
|
{ |
|
DEBUG_BLOCK |
|
m_name = i18n("Generic Audio Player"); |
|
m_dirLister = new KDirLister(); |
|
m_dirLister->setNameFilter( "*.mp3 *.wav *.asf *.flac *.wma *.ogg *.aac *.m4a *.mp4 *.mp2 *.ac3" ); |
|
m_dirLister->setAutoUpdate( false ); |
|
|
|
m_spacesToUnderscores = false; |
|
m_ignoreThePrefix = false; |
|
m_asciiTextOnly = false; |
|
|
|
m_songLocation = TQString(); |
|
m_podcastLocation = TQString(); |
|
|
|
m_supportedFileTypes.clear(); |
|
|
|
m_configDialog = 0; |
|
|
|
connect( m_dirLister, TQT_SIGNAL( newItems(const KFileItemList &) ), this, TQT_SLOT( newItems(const KFileItemList &) ) ); |
|
connect( m_dirLister, TQT_SIGNAL( completed() ), this, TQT_SLOT( dirListerCompleted() ) ); |
|
connect( m_dirLister, TQT_SIGNAL( clear() ), this, TQT_SLOT( dirListerClear() ) ); |
|
connect( m_dirLister, TQT_SIGNAL( clear(const KURL &) ), this, TQT_SLOT( dirListerClear(const KURL &) ) ); |
|
connect( m_dirLister, TQT_SIGNAL( deleteItem(KFileItem *) ), this, TQT_SLOT( dirListerDeleteItem(KFileItem *) ) ); |
|
} |
|
|
|
void |
|
GenericMediaDevice::init( MediaBrowser* parent ) |
|
{ |
|
MediaDevice::init( parent ); |
|
} |
|
|
|
GenericMediaDevice::~GenericMediaDevice() |
|
{ |
|
closeDevice(); |
|
} |
|
|
|
void |
|
GenericMediaDevice::applyConfig() |
|
{ |
|
if( m_configDialog != 0) |
|
{ |
|
m_supportedFileTypes.clear(); |
|
for( uint i = 0; i < m_configDialog->m_supportedListBox->count(); i++ ) |
|
{ |
|
TQString currentText = m_configDialog->m_supportedListBox->item( i )->text(); |
|
|
|
if( currentText == m_configDialog->m_convertComboBox->currentText() ) |
|
m_supportedFileTypes.prepend( currentText ); |
|
else |
|
m_supportedFileTypes.append( currentText ); |
|
} |
|
|
|
m_spacesToUnderscores = m_configDialog->m_spaceCheck->isChecked(); |
|
m_asciiTextOnly = m_configDialog->m_asciiCheck->isChecked(); |
|
m_vfatTextOnly = m_configDialog->m_vfatCheck->isChecked(); |
|
m_ignoreThePrefix = m_configDialog->m_ignoreTheCheck->isChecked(); |
|
|
|
m_songLocation = m_configDialog->m_songLocationBox->text(); |
|
m_podcastLocation = m_configDialog->m_podcastLocationBox->text(); |
|
} |
|
|
|
|
|
setConfigString( "songLocation" , m_songLocation ); |
|
setConfigString( "podcastLocation" , m_podcastLocation ); |
|
setConfigBool( "spacesToUnderscores", m_spacesToUnderscores ); |
|
setConfigBool( "ignoreThePrefix" , m_ignoreThePrefix ); |
|
setConfigBool( "asciiTextOnly" , m_asciiTextOnly ); |
|
setConfigBool( "vfatTextOnly" , m_vfatTextOnly ); |
|
setConfigString( "supportedFiletypes" , m_supportedFileTypes.join( ", " ) ); |
|
} |
|
|
|
|
|
void |
|
GenericMediaDevice::loadConfig() |
|
{ |
|
MediaDevice::loadConfig(); |
|
|
|
m_spacesToUnderscores = configBool( "spacesToUnderscores", false ); |
|
m_ignoreThePrefix = configBool( "ignoreThePrefix", false); |
|
m_asciiTextOnly = configBool( "asciiTextOnly", false ); |
|
m_vfatTextOnly = configBool( "vfatTextOnly", false ); |
|
|
|
m_songLocation = configString( "songLocation", "/%artist/%album/%title.%filetype" ); |
|
m_podcastLocation = configString( "podcastLocation", "/podcasts/" ); |
|
|
|
m_supportedFileTypes = TQStringList::split( ", ", configString( "supportedFiletypes", "mp3"), true); |
|
} |
|
|
|
bool |
|
GenericMediaDevice::openDevice( bool /*silent*/ ) |
|
{ |
|
DEBUG_BLOCK |
|
if( !m_medium.mountPoint() ) |
|
{ |
|
Amarok::StatusBar::instance()->longMessage( i18n( "Devices handled by this plugin must be mounted first.\n" |
|
"Please mount the device and click \"Connect\" again." ), |
|
KDE::StatusBar::Sorry ); |
|
return false; |
|
} |
|
|
|
KMountPoint::List currentmountpoints = KMountPoint::currentMountPoints(); |
|
KMountPoint::List::Iterator mountiter = currentmountpoints.begin(); |
|
for(; mountiter != currentmountpoints.end(); ++mountiter) |
|
{ |
|
if( m_medium.mountPoint() == (*mountiter)->mountPoint() ) |
|
m_medium.setFsType( (*mountiter)->mountType() ); |
|
} |
|
m_actuallyVfat = (m_medium.fsType() == "vfat" || m_medium.fsType() == "msdosfs") ? |
|
true : false; |
|
m_connected = true; |
|
KURL tempurl = KURL::fromPathOrURL( m_medium.mountPoint() ); |
|
TQString newMountPoint = tempurl.isLocalFile() ? tempurl.path( -1 ) : tempurl.prettyURL( -1 ); //no trailing slash |
|
m_transferDir = newMountPoint; |
|
m_initialFile = new GenericMediaFile( 0, newMountPoint, this ); |
|
listDir( newMountPoint ); |
|
connect( this, TQT_SIGNAL( startTransfer() ), MediaBrowser::instance(), TQT_SLOT( transferClicked() ) ); |
|
return true; |
|
} |
|
|
|
bool |
|
GenericMediaDevice::closeDevice() //SLOT |
|
{ |
|
if( m_connected ) |
|
{ |
|
m_initialFile->deleteAll( true ); |
|
m_view->clear(); |
|
m_connected = false; |
|
|
|
} |
|
//delete these? |
|
m_mfm.clear(); |
|
m_mim.clear(); |
|
return true; |
|
} |
|
|
|
/// Renaming |
|
|
|
void |
|
GenericMediaDevice::renameItem( TQListViewItem *item ) // SLOT |
|
{ |
|
|
|
if( !item ) |
|
return; |
|
|
|
#define item static_cast<GenericMediaItem*>(item) |
|
|
|
TQString src = m_mim[item]->getFullName(); |
|
TQString dst = m_mim[item]->getParent()->getFullName() + '/' + item->text(0); |
|
|
|
debug() << "Renaming: " << src << " to: " << dst << endl; |
|
|
|
//do we want a progress dialog? If so, set last false to true |
|
if( TDEIO::NetAccess::file_move( KURL::fromPathOrURL(src), KURL::fromPathOrURL(dst), -1, false, false, false ) ) |
|
{ |
|
m_mfm.erase( m_mim[item]->getFullName() ); |
|
m_mim[item]->setNamesFromBase( item->text(0) ); |
|
m_mfm[m_mim[item]->getFullName()] = m_mim[item]; |
|
} |
|
else |
|
{ |
|
debug() << "Renaming FAILED!" << endl; |
|
//failed, so set the item's text back to how it should be |
|
item->setText( 0, m_mim[item]->getBaseName() ); |
|
} |
|
|
|
|
|
refreshDir( m_mim[item]->getParent()->getFullName() ); |
|
m_mim[item]->renameAllChildren(); |
|
|
|
#undef item |
|
|
|
} |
|
|
|
/// Creating a directory |
|
|
|
MediaItem * |
|
GenericMediaDevice::newDirectory( const TQString &name, MediaItem *parent ) |
|
{ |
|
if( !m_connected || name.isEmpty() ) return 0; |
|
|
|
#define parent static_cast<GenericMediaItem*>(parent) |
|
|
|
TQString fullName = m_mim[parent]->getFullName(); |
|
TQString cleanedName = cleanPath( name ); |
|
TQString fullPath = fullName + '/' + cleanedName; |
|
debug() << "Creating directory: " << fullPath << endl; |
|
const KURL url( fullPath ); |
|
|
|
if( !TDEIO::NetAccess::mkdir( url, m_parent ) ) //failed |
|
{ |
|
debug() << "Failed to create directory " << fullPath << endl; |
|
return 0; |
|
} |
|
|
|
refreshDir( m_mim[parent]->getFullName() ); |
|
|
|
#undef parent |
|
|
|
return 0; |
|
} |
|
|
|
void |
|
GenericMediaDevice::addToDirectory( MediaItem *directory, TQPtrList<MediaItem> items ) |
|
{ |
|
if( items.isEmpty() ) return; |
|
|
|
GenericMediaFile *dropDir; |
|
if( !directory ) |
|
dropDir = m_initialFile; |
|
else |
|
{ |
|
if( directory->type() == MediaItem::TRACK ) |
|
#define directory static_cast<GenericMediaItem *>(directory) |
|
dropDir = m_mim[directory]->getParent(); |
|
else |
|
dropDir = m_mim[directory]; |
|
} |
|
|
|
for( TQPtrListIterator<MediaItem> it(items); *it; ++it ) |
|
{ |
|
GenericMediaItem *currItem = static_cast<GenericMediaItem *>(*it); |
|
TQString src = m_mim[currItem]->getFullName(); |
|
TQString dst = dropDir->getFullName() + '/' + currItem->text(0); |
|
debug() << "Moving: " << src << " to: " << dst << endl; |
|
|
|
const KURL srcurl(src); |
|
const KURL dsturl(dst); |
|
|
|
if ( !TDEIO::NetAccess::file_move( srcurl, dsturl, -1, false, false, m_parent ) ) |
|
debug() << "Failed moving " << src << " to " << dst << endl; |
|
else |
|
{ |
|
refreshDir( m_mim[currItem]->getParent()->getFullName() ); |
|
refreshDir( dropDir->getFullName() ); |
|
//smb: urls don't seem to refresh correctly, but this seems to be a samba issue? |
|
} |
|
} |
|
#undef directory |
|
} |
|
|
|
/// Uploading |
|
|
|
TQString |
|
GenericMediaDevice::buildDestination( const TQString &format, const MetaBundle &mb ) |
|
{ |
|
bool isCompilation = mb.compilation() == MetaBundle::CompilationYes; |
|
TQMap<TQString, TQString> args; |
|
TQString artist = mb.artist(); |
|
TQString albumartist = artist; |
|
if( isCompilation ) |
|
albumartist = i18n( "Various Artists" ); |
|
args["theartist"] = cleanPath( artist ); |
|
args["thealbumartist"] = cleanPath( albumartist ); |
|
if( m_ignoreThePrefix && artist.startsWith( "The " ) ) |
|
CollectionView::instance()->manipulateThe( artist, true ); |
|
artist = cleanPath( artist ); |
|
if( m_ignoreThePrefix && albumartist.startsWith( "The " ) ) |
|
CollectionView::instance()->manipulateThe( albumartist, true ); |
|
|
|
albumartist = cleanPath( albumartist ); |
|
for( int i = 0; i < MetaBundle::NUM_COLUMNS; i++ ) |
|
{ |
|
if( i == MetaBundle::Score || i == MetaBundle::PlayCount || i == MetaBundle::LastPlayed ) |
|
continue; |
|
args[mb.exactColumnName( i ).lower()] = cleanPath( mb.prettyText( i ) ); |
|
} |
|
args["artist"] = artist; |
|
args["albumartist"] = albumartist; |
|
args["initial"] = albumartist.mid( 0, 1 ).upper(); |
|
args["filetype"] = TQString(mb.url().pathOrURL().section( ".", -1 )).lower(); |
|
TQString track; |
|
if ( mb.track() ) |
|
track.sprintf( "%02d", mb.track() ); |
|
args["track"] = track; |
|
|
|
Amarok::QStringx formatx( format ); |
|
TQString result = formatx.namedOptArgs( args ); |
|
if( !result.startsWith( "/" ) ) |
|
result.prepend( "/" ); |
|
|
|
return result.replace( TQRegExp( "/\\.*" ), "/" ); |
|
} |
|
|
|
void |
|
GenericMediaDevice::checkAndBuildLocation( const TQString& location ) |
|
{ |
|
// check for every directory from the mount point to the location |
|
// whether they exist or not. |
|
int mountPointDepth = m_medium.mountPoint().contains( '/', false ); |
|
int locationDepth = location.contains( '/', false ); |
|
|
|
if( m_medium.mountPoint().endsWith( "/" ) ) |
|
mountPointDepth--; |
|
|
|
if( location.endsWith( "/") ) |
|
locationDepth--; |
|
|
|
// the locationDepth indicates the filename, in the following loop |
|
// however, we only look at the direcories. hence i < locationDepth |
|
// instead of '<=' |
|
for( int i = mountPointDepth; |
|
i < locationDepth; |
|
i++ ) |
|
{ |
|
|
|
TQString firstpart = location.section( '/', 0, i-1 ); |
|
TQString secondpart = cleanPath( location.section( '/', i, i ) ); |
|
KURL url = KURL::fromPathOrURL( firstpart + '/' + secondpart ); |
|
|
|
if( !TDEIO::NetAccess::exists( url, false, m_parent ) ) |
|
{ |
|
debug() << "directory does not exist, creating..." << url << endl; |
|
if( !TDEIO::NetAccess::mkdir(url, m_view ) ) //failed |
|
{ |
|
debug() << "Failed to create directory " << url << endl; |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
|
|
TQString |
|
GenericMediaDevice::buildPodcastDestination( const PodcastEpisodeBundle *bundle ) |
|
{ |
|
TQString location = m_podcastLocation.endsWith("/") ? m_podcastLocation : m_podcastLocation + '/'; |
|
// get info about the PodcastChannel |
|
TQString parentUrl = bundle->parent().url(); |
|
TQString sql = "SELECT title,parent FROM podcastchannels WHERE url='" + CollectionDB::instance()->escapeString( parentUrl ) + "';"; |
|
TQStringList values = CollectionDB::instance()->query( sql ); |
|
TQString channelTitle; |
|
int parent = 0; |
|
channelTitle = values.first(); |
|
parent = values.last().toInt(); |
|
// Put the file in a directory tree like in the playlistbrowser |
|
sql = "SELECT name,parent FROM podcastfolders WHERE id=%1;"; |
|
TQString name; |
|
while ( parent > 0 ) |
|
{ |
|
values = CollectionDB::instance()->query( sql.arg( parent ) ); |
|
name = values.first(); |
|
parent = values.last().toInt(); |
|
location += cleanPath( name ) + '/'; |
|
} |
|
location += cleanPath( channelTitle ) + '/' + cleanPath( bundle->localUrl().filename() ); |
|
return location; |
|
} |
|
|
|
|
|
MediaItem * |
|
GenericMediaDevice::copyTrackToDevice( const MetaBundle& bundle ) |
|
{ |
|
if( !m_connected ) return 0; |
|
|
|
// use different naming schemes for differen kinds of tracks |
|
TQString path = m_transferDir; |
|
debug() << "bundle exists: " << bundle.podcastBundle() << endl; |
|
if( bundle.podcastBundle() ) |
|
path += buildPodcastDestination( bundle.podcastBundle() ); |
|
else |
|
path += buildDestination( m_songLocation, bundle ); |
|
|
|
checkAndBuildLocation( path ); |
|
|
|
const KURL desturl = KURL::fromPathOrURL( path ); |
|
|
|
//kapp->processEvents( 100 ); |
|
|
|
if( !kioCopyTrack( bundle.url(), desturl ) ) |
|
{ |
|
debug() << "Failed to copy track: " << bundle.url().pathOrURL() << " to " << desturl.pathOrURL() << endl; |
|
return 0; |
|
} |
|
|
|
refreshDir( m_transferDir ); |
|
|
|
//the return value just can't be null, as nothing is done with it |
|
//other than to see if it is NULL or not |
|
//if we're here the transfer shouldn't have failed, so we shouldn't get into a loop by waiting... |
|
while( !m_view->firstChild() ) |
|
kapp->processEvents( 100 ); |
|
return static_cast<MediaItem*>(m_view->firstChild()); |
|
} |
|
|
|
//Somewhat related... |
|
|
|
MediaItem * |
|
GenericMediaDevice::trackExists( const MetaBundle& bundle ) |
|
{ |
|
TQString key; |
|
TQString path = buildDestination( m_songLocation, bundle); |
|
KURL url( path ); |
|
TQStringList directories = TQStringList::split( "/", url.directory(1,1), false ); |
|
|
|
TQListViewItem *it = view()->firstChild(); |
|
for( TQStringList::Iterator directory = directories.begin(); |
|
directory != directories.end(); |
|
directory++ ) |
|
{ |
|
key = *directory; |
|
while( it && it->text( 0 ) != key ) |
|
it = it->nextSibling(); |
|
if( !it ) |
|
return 0; |
|
if( !it->childCount() ) |
|
expandItem( it ); |
|
it = it->firstChild(); |
|
} |
|
|
|
key = url.fileName( true ); |
|
key = key.isEmpty() ? fileName( bundle ) : key; |
|
while( it && it->text( 0 ) != key ) |
|
it = it->nextSibling(); |
|
|
|
return dynamic_cast<MediaItem *>( it ); |
|
} |
|
|
|
/// File transfer methods |
|
|
|
|
|
void |
|
GenericMediaDevice::downloadSelectedItems() |
|
{ |
|
KURL::List urls = getSelectedItems(); |
|
|
|
CollectionView::instance()->organizeFiles( urls, i18n("Copy Files to Collection"), true ); |
|
|
|
hideProgress(); |
|
} |
|
|
|
KURL::List |
|
GenericMediaDevice::getSelectedItems() |
|
{ |
|
return m_view->nodeBuildDragList( static_cast<MediaItem*>(m_view->firstChild()), true ); |
|
} |
|
|
|
/// Deleting |
|
|
|
int |
|
GenericMediaDevice::deleteItemFromDevice( MediaItem *item, int /*flags*/ ) |
|
{ |
|
if( !item || !m_connected ) return -1; |
|
|
|
#define item static_cast<GenericMediaItem*>(item) |
|
|
|
TQString path = m_mim[item]->getFullName(); |
|
debug() << "Deleting path: " << path << endl; |
|
|
|
if ( !TDEIO::NetAccess::del( KURL::fromPathOrURL(path), m_view )) |
|
{ |
|
debug() << "Could not delete!" << endl; |
|
return -1; |
|
} |
|
|
|
if( m_mim[item] == m_initialFile ) |
|
{ |
|
m_mim[item]->deleteAll( false ); |
|
debug() << "Not deleting root directory of mount!" << endl; |
|
path = m_initialFile->getFullName(); |
|
} |
|
else |
|
{ |
|
path = m_mim[item]->getParent()->getFullName(); |
|
m_mim[item]->deleteAll( true ); |
|
} |
|
refreshDir( path ); |
|
|
|
setProgress( progress() + 1 ); |
|
|
|
#undef item |
|
return 1; |
|
} |
|
|
|
/// Directory Reading |
|
|
|
void |
|
GenericMediaDevice::expandItem( TQListViewItem *item ) // SLOT |
|
{ |
|
if( !item || !item->isExpandable() ) return; |
|
|
|
#define item static_cast<GenericMediaItem *>(item) |
|
m_dirListerComplete = false; |
|
listDir( m_mim[item]->getFullName() ); |
|
#undef item |
|
|
|
while( !m_dirListerComplete ) |
|
{ |
|
kapp->processEvents( 100 ); |
|
usleep(10000); |
|
} |
|
} |
|
|
|
void |
|
GenericMediaDevice::listDir( const TQString &dir ) |
|
{ |
|
m_dirListerComplete = false; |
|
if( m_mfm[dir]->getListed() ) |
|
m_dirLister->updateDirectory( KURL::fromPathOrURL(dir) ); |
|
else |
|
{ |
|
m_dirLister->openURL( KURL::fromPathOrURL(dir), true, true ); |
|
m_mfm[dir]->setListed( true ); |
|
} |
|
} |
|
|
|
void |
|
GenericMediaDevice::refreshDir( const TQString &dir ) |
|
{ |
|
m_dirListerComplete = false; |
|
m_dirLister->updateDirectory( KURL::fromPathOrURL(dir) ); |
|
} |
|
|
|
void |
|
GenericMediaDevice::newItems( const KFileItemList &items ) |
|
{ |
|
TQPtrListIterator<KFileItem> it( items ); |
|
KFileItem *kfi; |
|
while ( (kfi = it.current()) != 0 ) { |
|
++it; |
|
addTrackToList( kfi->isFile() ? MediaItem::TRACK : MediaItem::DIRECTORY, kfi->url(), 0 ); |
|
} |
|
} |
|
|
|
void |
|
GenericMediaDevice::dirListerCompleted() |
|
{ |
|
m_dirListerComplete = true; |
|
} |
|
|
|
void |
|
GenericMediaDevice::dirListerClear() |
|
{ |
|
m_initialFile->deleteAll( true ); |
|
|
|
m_view->clear(); |
|
m_mfm.clear(); |
|
m_mim.clear(); |
|
|
|
KURL tempurl = KURL::fromPathOrURL( m_medium.mountPoint() ); |
|
TQString newMountPoint = tempurl.isLocalFile() ? tempurl.path( -1 ) : tempurl.prettyURL( -1 ); //no trailing slash |
|
m_initialFile = new GenericMediaFile( 0, newMountPoint, this ); |
|
} |
|
|
|
void |
|
GenericMediaDevice::dirListerClear( const KURL &url ) |
|
{ |
|
TQString directory = url.pathOrURL(); |
|
GenericMediaFile *vmf = m_mfm[directory]; |
|
if( vmf ) |
|
vmf->deleteAll( false ); |
|
} |
|
|
|
void |
|
GenericMediaDevice::dirListerDeleteItem( KFileItem *fileitem ) |
|
{ |
|
TQString filename = fileitem->url().pathOrURL(); |
|
GenericMediaFile *vmf = m_mfm[filename]; |
|
if( vmf ) |
|
vmf->deleteAll( true ); |
|
} |
|
|
|
int |
|
GenericMediaDevice::addTrackToList( int type, KURL url, int /*size*/ ) |
|
{ |
|
TQString path = url.isLocalFile() ? url.path( -1 ) : url.prettyURL( -1 ); //no trailing slash |
|
int index = path.findRev( '/', -1 ); |
|
TQString baseName = path.right( path.length() - index - 1 ); |
|
TQString parentName = path.left( index ); |
|
|
|
GenericMediaFile* parent = m_mfm[parentName]; |
|
GenericMediaFile* newItem = new GenericMediaFile( parent, baseName, this ); |
|
|
|
if( type == MediaItem::DIRECTORY ) //directory |
|
newItem->getViewItem()->setType( MediaItem::DIRECTORY ); |
|
//TODO: this logic could maybe be taken out later...or the dirlister shouldn't |
|
//filter, one or the other...depends if we want to allow viewing any files |
|
//or just update the list in the plugin as appropriate |
|
else if( type == MediaItem::TRACK ) //file |
|
{ |
|
if( baseName.endsWith( "mp3", false ) || baseName.endsWith( "wma", false ) || |
|
baseName.endsWith( "wav", false ) || baseName.endsWith( "ogg", false ) || |
|
baseName.endsWith( "asf", false ) || baseName.endsWith( "flac", false ) || |
|
baseName.endsWith( "aac", false ) || baseName.endsWith( "m4a", false ) ) |
|
|
|
newItem->getViewItem()->setType( MediaItem::TRACK ); |
|
|
|
else |
|
newItem->getViewItem()->setType( MediaItem::UNKNOWN ); |
|
} |
|
|
|
refreshDir( parent->getFullName() ); |
|
|
|
return 0; |
|
} |
|
|
|
/// Capacity, in kB |
|
|
|
bool |
|
GenericMediaDevice::getCapacity( TDEIO::filesize_t *total, TDEIO::filesize_t *available ) |
|
{ |
|
if( !m_connected || !KURL::fromPathOrURL( m_medium.mountPoint() ).isLocalFile() ) return false; |
|
|
|
KDiskFreeSp* kdf = new KDiskFreeSp( TQT_TQOBJECT(m_parent), "generic_kdf" ); |
|
kdf->readDF( m_medium.mountPoint() ); |
|
connect(kdf, TQT_SIGNAL(foundMountPoint( const TQString &, unsigned long, unsigned long, unsigned long )), |
|
TQT_SLOT(foundMountPoint( const TQString &, unsigned long, unsigned long, unsigned long ))); |
|
|
|
int count = 0; |
|
|
|
while( m_kBSize == 0 && m_kBAvail == 0){ |
|
usleep( 10000 ); |
|
kapp->processEvents( 100 ); |
|
count++; |
|
if (count > 120){ |
|
debug() << "KDiskFreeSp taking too long. Returning false from getCapacity()" << endl; |
|
return false; |
|
} |
|
} |
|
|
|
*total = m_kBSize*1024; |
|
*available = m_kBAvail*1024; |
|
unsigned long localsize = m_kBSize; |
|
m_kBSize = 0; |
|
m_kBAvail = 0; |
|
|
|
return localsize > 0; |
|
} |
|
|
|
void |
|
GenericMediaDevice::foundMountPoint( const TQString & mountPoint, unsigned long kBSize, unsigned long /*kBUsed*/, unsigned long kBAvail ) |
|
{ |
|
if ( mountPoint == m_medium.mountPoint() ){ |
|
m_kBSize = kBSize; |
|
m_kBAvail = kBAvail; |
|
} |
|
} |
|
|
|
/// Helper functions |
|
|
|
void |
|
GenericMediaDevice::rmbPressed( TQListViewItem* qitem, const TQPoint& point, int ) |
|
{ |
|
enum Actions { APPEND, LOAD, QUEUE, |
|
DOWNLOAD, |
|
BURN_DATACD, BURN_AUDIOCD, |
|
DIRECTORY, RENAME, |
|
DELETE, TRANSFER_HERE }; |
|
|
|
MediaItem *item = static_cast<MediaItem *>(qitem); |
|
if ( item ) |
|
{ |
|
TDEPopupMenu menu( m_view ); |
|
menu.insertItem( SmallIconSet( Amarok::icon( "playlist" ) ), i18n( "&Load" ), LOAD ); |
|
menu.insertItem( SmallIconSet( Amarok::icon( "1downarrow" ) ), i18n( "&Append to Playlist" ), APPEND ); |
|
menu.insertItem( SmallIconSet( Amarok::icon( "fastforward" ) ), i18n( "&Queue Tracks" ), QUEUE ); |
|
menu.insertSeparator(); |
|
menu.insertItem( SmallIconSet( Amarok::icon( "collection" ) ), i18n( "&Copy Files to Collection..." ), DOWNLOAD ); |
|
menu.insertItem( SmallIconSet( Amarok::icon( "media-optical-cdrom" ) ), i18n( "Burn to CD as Data" ), BURN_DATACD ); |
|
menu.setItemEnabled( BURN_DATACD, K3bExporter::isAvailable() ); |
|
menu.insertItem( SmallIconSet( Amarok::icon( "media-optical-cdaudio" ) ), i18n( "Burn to CD as Audio" ), BURN_AUDIOCD ); |
|
menu.setItemEnabled( BURN_AUDIOCD, K3bExporter::isAvailable() ); |
|
menu.insertSeparator(); |
|
menu.insertItem( SmallIconSet( Amarok::icon( "folder" ) ), i18n( "Add Directory" ), DIRECTORY ); |
|
menu.insertItem( SmallIconSet( Amarok::icon( "edit" ) ), i18n( "Rename" ), RENAME ); |
|
menu.insertItem( SmallIconSet( Amarok::icon( "remove" ) ), i18n( "Delete" ), DELETE ); |
|
menu.insertSeparator(); |
|
// NOTE: need better icon |
|
menu.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n( "Transfer Queue to Here..." ), TRANSFER_HERE ); |
|
menu.setItemEnabled( TRANSFER_HERE, MediaBrowser::queue()->childCount() ); |
|
|
|
int id = menu.exec( point ); |
|
switch( id ) |
|
{ |
|
case LOAD: |
|
Playlist::instance()->insertMedia( getSelectedItems(), Playlist::Replace ); |
|
break; |
|
case APPEND: |
|
Playlist::instance()->insertMedia( getSelectedItems(), Playlist::Append ); |
|
break; |
|
case QUEUE: |
|
Playlist::instance()->insertMedia( getSelectedItems(), Playlist::Queue ); |
|
break; |
|
case DOWNLOAD: |
|
downloadSelectedItems(); |
|
break; |
|
case BURN_DATACD: |
|
K3bExporter::instance()->exportTracks( getSelectedItems(), K3bExporter::DataCD ); |
|
break; |
|
case BURN_AUDIOCD: |
|
K3bExporter::instance()->exportTracks( getSelectedItems(), K3bExporter::AudioCD ); |
|
break; |
|
|
|
case DIRECTORY: |
|
if( item->type() == MediaItem::DIRECTORY ) |
|
m_view->newDirectory( static_cast<MediaItem*>(item) ); |
|
else |
|
m_view->newDirectory( static_cast<MediaItem*>(item->parent()) ); |
|
break; |
|
|
|
case RENAME: |
|
m_view->rename( item, 0 ); |
|
break; |
|
|
|
case DELETE: |
|
deleteFromDevice(); |
|
break; |
|
|
|
case TRANSFER_HERE: |
|
#define item static_cast<GenericMediaItem*>(item) |
|
if( item->type() == MediaItem::DIRECTORY ) |
|
m_transferDir = m_mim[item]->getFullName(); |
|
else |
|
m_transferDir = m_mim[item]->getParent()->getFullName(); |
|
#undef item |
|
emit startTransfer(); |
|
break; |
|
} |
|
return; |
|
} |
|
|
|
if( isConnected() ) |
|
{ |
|
TDEPopupMenu menu( m_view ); |
|
menu.insertItem( SmallIconSet( Amarok::icon( "folder" ) ), i18n("Add Directory" ), DIRECTORY ); |
|
if ( MediaBrowser::queue()->childCount()) |
|
{ |
|
menu.insertSeparator(); |
|
menu.insertItem( SmallIconSet( Amarok::icon( "add_playlist" ) ), i18n(" Transfer queue to here..." ), TRANSFER_HERE ); |
|
} |
|
int id = menu.exec( point ); |
|
switch( id ) |
|
{ |
|
case DIRECTORY: |
|
m_view->newDirectory( 0 ); |
|
break; |
|
|
|
case TRANSFER_HERE: |
|
m_transferDir = m_medium.mountPoint(); |
|
emit startTransfer(); |
|
break; |
|
|
|
} |
|
} |
|
} |
|
|
|
|
|
TQString GenericMediaDevice::cleanPath( const TQString &component ) |
|
{ |
|
TQString result = Amarok::cleanPath( component ); |
|
|
|
if( m_asciiTextOnly ) |
|
result = Amarok::asciiPath( result ); |
|
|
|
result.simplifyWhiteSpace(); |
|
if( m_spacesToUnderscores ) |
|
result.replace( TQRegExp( "\\s" ), "_" ); |
|
if( m_actuallyVfat || m_vfatTextOnly ) |
|
result = Amarok::vfatPath( result ); |
|
|
|
result.replace( "/", "-" ); |
|
|
|
return result; |
|
} |
|
|
|
/// File Information functions |
|
|
|
bool GenericMediaDevice::isPlayable( const MetaBundle& bundle ) |
|
{ |
|
for( TQStringList::Iterator it = m_supportedFileTypes.begin(); it != m_supportedFileTypes.end() ; it++ ) |
|
{ |
|
if( bundle.type().lower() == (*it).lower() ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
bool GenericMediaDevice::isPreferredFormat( const MetaBundle &bundle ) |
|
{ |
|
return m_supportedFileTypes.first().lower() == bundle.type().lower(); |
|
} |
|
|
|
/// Configuration Dialog Extension |
|
|
|
void GenericMediaDevice::addConfigElements( TQWidget * parent ) |
|
{ |
|
m_configDialog = new GenericMediaDeviceConfigDialog( parent ); |
|
|
|
m_configDialog->setDevice( this ); |
|
} |
|
|
|
|
|
void GenericMediaDevice::removeConfigElements( TQWidget * /* parent */ ) |
|
{ |
|
if( m_configDialog != 0 ) |
|
delete m_configDialog; |
|
|
|
m_configDialog = 0; |
|
|
|
} |
|
|
|
|
|
#include "genericmediadevice.moc"
|
|
|