Some random work
[amarok.git] / src / mediadevice / mtp / mtpmediadevice.cpp
blob5031a5a37c054bc17a486256774631d66596d1da
1 /***************************************************************************
2 * copyright : (C) 2006 Andy Kelk <andy@mopoke.co.uk> *
3 ***************************************************************************/
5 /***************************************************************************
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 ***************************************************************************/
14 /**
15 * Based on njb mediadevice with some code hints from the libmtp
16 * example tools
19 /**
20 * MTP media device
21 * @author Andy Kelk <andy@mopoke.co.uk>
22 * @see http://libmtp.sourceforge.net/
25 #define DEBUG_PREFIX "MtpMediaDevice"
27 #include "config-amarok.h"
28 #include "mtpmediadevice.h"
29 #include "MediaItem.h"
30 //Added by qt3to4:
31 #include <QLabel>
32 #include <Q3PtrList>
34 AMAROK_EXPORT_PLUGIN( MtpMediaDevice )
36 // Amarok
37 #include <debug.h>
38 #include <statusbar/statusbar.h>
39 #include <statusbar/popupMessage.h>
41 // KDE
42 #include <kapplication.h>
43 #include <kiconloader.h>
44 #include <kmenu.h>
45 #include <kmessagebox.h>
47 #include <ktempdir.h>
49 // Qt
50 #include <QDir>
51 #include <q3listview.h>
52 #include <QToolTip>
53 #include <QLineEdit>
54 #include <QRegExp>
55 #include <QBuffer>
56 #include <qmap.h>
58 /**
59 * MtpMediaDevice Class
62 MtpMediaDevice::MtpMediaDevice() : MediaDevice()
64 m_name = i18n("MTP Media Device");
65 m_device = 0;
66 m_folders = 0;
67 m_playlistItem = 0;
68 setDisconnected();
69 m_hasMountPoint = false;
70 m_syncStats = false;
71 m_transcode = false;
72 m_transcodeAlways = false;
73 m_transcodeRemove = false;
74 m_configure = false;
75 m_customButton = true;
76 m_transfer = true;
78 KToolBarButton *customButton = MediaBrowser::instance()->getToolBar()->getButton( MediaBrowser::CUSTOM );
79 customButton->setText( i18n("Special device functions") );
80 QToolTip::remove( customButton );
81 customButton->setToolTip( i18n( "Special functions of your device" ) );
83 mtpFileTypes[LIBMTP_FILETYPE_WAV] = "wav";
84 mtpFileTypes[LIBMTP_FILETYPE_MP3] = "mp3";
85 mtpFileTypes[LIBMTP_FILETYPE_WMA] = "wma";
86 mtpFileTypes[LIBMTP_FILETYPE_OGG] = "ogg";
87 mtpFileTypes[LIBMTP_FILETYPE_AUDIBLE] = "aa"; // audible
88 mtpFileTypes[LIBMTP_FILETYPE_MP4] = "mp4";
89 mtpFileTypes[LIBMTP_FILETYPE_UNDEF_AUDIO] = "undef-audio";
90 mtpFileTypes[LIBMTP_FILETYPE_WMV] = "wmv";
91 mtpFileTypes[LIBMTP_FILETYPE_AVI] = "avi";
92 mtpFileTypes[LIBMTP_FILETYPE_MPEG] = "mpg";
93 mtpFileTypes[LIBMTP_FILETYPE_ASF] = "asf";
94 mtpFileTypes[LIBMTP_FILETYPE_QT] = "mov";
95 mtpFileTypes[LIBMTP_FILETYPE_UNDEF_VIDEO] = "undef-video";
96 mtpFileTypes[LIBMTP_FILETYPE_JPEG] = "jpg";
97 mtpFileTypes[LIBMTP_FILETYPE_JFIF] = "jpg";
98 mtpFileTypes[LIBMTP_FILETYPE_TIFF] = "tiff";
99 mtpFileTypes[LIBMTP_FILETYPE_BMP] = "bmp";
100 mtpFileTypes[LIBMTP_FILETYPE_GIF] = "gif";
101 mtpFileTypes[LIBMTP_FILETYPE_PICT] = "pict";
102 mtpFileTypes[LIBMTP_FILETYPE_PNG] = "png";
103 mtpFileTypes[LIBMTP_FILETYPE_VCALENDAR1] = "vcs"; // vcal1
104 mtpFileTypes[LIBMTP_FILETYPE_VCALENDAR2] = "vcs"; // vcal2
105 mtpFileTypes[LIBMTP_FILETYPE_VCARD2] = "vcf"; // vcard2
106 mtpFileTypes[LIBMTP_FILETYPE_VCARD3] = "vcf"; // vcard3
107 mtpFileTypes[LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT] = "wim"; // windows image format
108 mtpFileTypes[LIBMTP_FILETYPE_WINEXEC] = "exe";
109 mtpFileTypes[LIBMTP_FILETYPE_TEXT] = "txt";
110 mtpFileTypes[LIBMTP_FILETYPE_HTML] = "html";
111 mtpFileTypes[LIBMTP_FILETYPE_UNKNOWN] = "unknown";
114 void
115 MtpMediaDevice::init( MediaBrowser *parent )
117 MediaDevice::init( parent );
120 bool
121 MtpMediaDevice::isConnected()
123 return !( m_device == 0 );
127 * File types that we support
129 QStringList
130 MtpMediaDevice::supportedFiletypes()
132 return m_supportedFiles;
137 MtpMediaDevice::progressCallback( uint64_t const sent, uint64_t const total, void const * const data )
139 Q_UNUSED( sent );
140 Q_UNUSED( total );
142 kapp->processEvents( 100 );
144 MtpMediaDevice *dev = (MtpMediaDevice*)(data);
146 if( dev->isCanceled() )
148 debug() << "Canceling transfer operation";
149 dev->setCanceled( true );
150 return 1;
153 return 0;
157 * Copy a track to the device
159 MediaItem
160 *MtpMediaDevice::copyTrackToDevice( TrackPtr track )
162 DEBUG_BLOCK
164 QString genericError = i18n( "Could not send track" );
166 LIBMTP_track_t *trackmeta = LIBMTP_new_track_t();
167 trackmeta->item_id = 0;
168 QString type = track.type();
169 debug() << "filetype : " << type;
170 if( type().compare( "mp3", Qt::CaseInsensitive ) == 0 )
172 trackmeta->filetype = LIBMTP_FILETYPE_MP3;
174 else if( type().compare( "ogg", Qt::CaseInsensitive ) == 0 )
176 trackmeta->filetype = LIBMTP_FILETYPE_OGG;
178 else if( type().compare( "wma", Qt::CaseInsensitive ) == 0 )
180 trackmeta->filetype = LIBMTP_FILETYPE_WMA;
182 else if( type().compare( "mp4", Qt::CaseInsensitive ) == 0 )
184 trackmeta->filetype = LIBMTP_FILETYPE_MP4;
186 else
188 // Couldn't recognise an Amarok filetype.
189 // fallback to checking the extension (e.g. .wma, .ogg, etc)
190 debug() << "No filetype found by Amarok filetype";
192 const QString extension = bundle.url().path().section( ".", -1 ).toLower();
194 int libmtp_type = m_supportedFiles.findIndex( extension );
195 if( libmtp_type >= 0 )
197 int keyIndex = mtpFileTypes.values().findIndex( extension );
198 libmtp_type = mtpFileTypes.keys()[keyIndex];
199 trackmeta->filetype = (LIBMTP_filetype_t) libmtp_type;
200 debug() << "set filetype to " << libmtp_type << " based on extension of ." << extension;
202 else
204 debug() << "We don't support the extension ." << extension;
205 Amarok::StatusBar::instance()->shortLongMessage(
206 genericError,
207 i18n( "Cannot determine a valid file type" ),
208 KDE::StatusBar::Error
210 return 0;
214 if( track.prettyName().isEmpty() )
216 trackmeta->title = qstrdup( i18n( "Unknown title" ).toUtf8() );
218 else
220 trackmeta->title = qstrdup( track.prettyName().toUtf8() );
223 if( track.album().prettyName().isEmpty() )
225 trackmeta->album = qstrdup( i18n( "Unknown album" ).toUtf8() );
227 else
229 trackmeta->album = qstrdup( track.album().prettyName().toUtf8() );
232 if( track.artist().prettyName().isEmpty() )
234 trackmeta->artist = qstrdup( i18n( "Unknown artist" ).toUtf8() );
236 else
238 trackmeta->artist = qstrdup( track.artist().prettyName().toUtf8() );
241 if( track.genre().prettyName().isEmpty() )
243 trackmeta->genre = qstrdup( i18n( "Unknown genre" ).toUtf8() );
245 else
247 trackmeta->genre = qstrdup( track.genre().prettyName().toUtf8() );
250 if( track.year() > 0 )
252 QString date;
253 QTextOStream( &date ) << track.year() << "0101T0000.0";
254 trackmeta->date = qstrdup( date.toUtf8() );
256 else
258 trackmeta->date = qstrdup( "00010101T0000.0" );
261 if( track.trackNumber() > 0 )
263 trackmeta->tracknumber = track.trackNumber();
265 if( track.length() > 0 )
267 // Multiply by 1000 since this is in milliseconds
268 trackmeta->duration = track.length() * 1000;
270 if( !track.playableUrl().filename().isEmpty() )
272 trackmeta->filename = qstrdup( track.playableUrl().filename().toUtf8() );
274 trackmeta->filesize = track.filesize();
276 // try and create the requested folder structure
277 uint32_t parent_id = 0;
278 if( !m_folderStructure.isEmpty() )
280 parent_id = checkFolderStructure( track );
281 if( parent_id == 0 )
283 debug() << "Couldn't create new parent (" << m_folderStructure << ")";
284 Amarok::StatusBar::instance()->shortLongMessage(
285 genericError,
286 i18n( "Cannot create parent folder. Check your structure." ),
287 KDE::StatusBar::Error
289 return 0;
292 else
294 parent_id = getDefaultParentId();
296 debug() << "Parent id : " << parent_id;
298 m_critical_mutex.lock();
299 debug() << "Sending track... " << track.playableUrl().path().toUtf8();
300 int ret = LIBMTP_Send_Track_From_File(
301 m_device, track.playableUrl().path().toUtf8(), trackmeta,
302 progressCallback, this, parent_id
304 m_critical_mutex.unlock();
306 if( ret < 0 )
308 debug() << "Could not write file " << ret;
309 Amarok::StatusBar::instance()->shortLongMessage(
310 genericError,
311 i18n( "File write failed" ),
312 KDE::StatusBar::Error
314 return 0;
317 MetaBundle temp( bundle );
318 MtpMediaDeviceTrack *taggedTrack = new MtpMediaDeviceTrack( trackmeta );
319 taggedTrack->setFolderId( parent_id );
321 LIBMTP_destroy_track_t( trackmeta );
323 kapp->processEvents();
325 // add track to view and to new tracks list
326 addTrackToCollection( taggedTrack );
327 m_newTracks->append( taggedTrack );
328 return taggedTrack;
332 * Get the cover image for a track, convert it to a format supported on the
333 * device and set it as the cover art.
335 // void
336 // MtpMediaDevice::sendAlbumArt( Q3PtrList<MediaItem> *items )
337 // {
338 // QString image;
339 // image = CollectionDB::instance()->albumImage(items->first()->bundle()->artist(), items->first()->bundle()->album(), false, 100);
340 // if( ! image.endsWith( "@nocover.png" ) )
341 // {
342 // debug() << "image " << image << " found for " << items->first()->bundle()->album();
343 // QByteArray *imagedata = getSupportedImage( image );
344 // if( imagedata == 0 )
345 // {
346 // debug() << "Cannot generate a supported image format";
347 // return;
348 // }
349 // if( imagedata->size() )
350 // {
351 // m_critical_mutex.lock();
352 // LIBMTP_album_t *album_object = getOrCreateAlbum( items );
353 // if( album_object )
354 // {
355 // LIBMTP_filesampledata_t *imagefile = LIBMTP_new_filesampledata_t();
356 // imagefile->data = (char *) imagedata->data();
357 // imagefile->size = imagedata->size();
358 // imagefile->filetype = LIBMTP_FILETYPE_JPEG;
359 // int ret = LIBMTP_Send_Representative_Sample( m_device, album_object->album_id, imagefile );
360 // if( ret != 0 )
361 // {
362 // debug() << "image send failed : " << ret;
363 // }
364 // }
365 // m_critical_mutex.unlock();
366 // }
367 // }
368 // }
370 uint32_t
371 MtpMediaDevice::getDefaultParentId( void )
373 // Decide which folder to send it to:
374 // If the device gave us a parent_folder setting, we use it
375 uint32_t parent_id = 0;
376 if( m_default_parent_folder )
378 parent_id = m_default_parent_folder;
380 // Otherwise look for a folder called "Music"
381 else
383 parent_id = m_device->default_music_folder;
385 return parent_id;
389 * Takes path to an existing cover image and converts it to a format
390 * supported on the device
392 QByteArray
393 *MtpMediaDevice::getSupportedImage( QString path )
395 if( m_format == 0 )
396 return 0;
398 debug() << "Will convert image to " << m_format;
400 // open image
401 const QImage original( path );
403 // save as new image
404 QImage newformat( original );
405 QByteArray *newimage = new QByteArray();
406 QBuffer buffer( *newimage );
407 buffer.open( QIODevice::WriteOnly );
408 if( newformat.save( &buffer, m_format.ascii() ) )
410 buffer.close();
411 return newimage;
413 return 0;
417 * Update cover art for a number of tracks
419 // void
420 // MtpMediaDevice::updateAlbumArt( Q3PtrList<MediaItem> *items )
421 // {
422 // DEBUG_BLOCK
424 // if( m_format == 0 ) // no supported image types. Don't even bother.
425 // return;
427 // setCanceled( false );
429 // kapp->processEvents( 100 );
430 // QMap< QString, Q3PtrList<MediaItem> > albumList;
432 // for( MtpMediaItem *it = dynamic_cast<MtpMediaItem*>(items->first()); it && !(m_canceled); it = dynamic_cast<MtpMediaItem*>(items->next()) )
433 // {
434 // // build album list
435 // if( it->type() == MediaItem::TRACK )
436 // {
437 // albumList[ it->bundle()->album() ].append( it );
438 // }
439 // if( it->type() == MediaItem::ALBUM )
440 // {
441 // debug() << "look, we get albums too!";
442 // }
443 // }
444 // int i = 0;
445 // setProgress( i, albumList.count() );
446 // kapp->processEvents( 100 );
447 // QMap< QString, Q3PtrList<MediaItem> >::Iterator it;
448 // for( it = albumList.begin(); it != albumList.end(); ++it )
449 // {
450 // sendAlbumArt( &it.data() );
451 // setProgress( ++i );
452 // if( i % 20 == 0 )
453 // kapp->processEvents( 100 );
454 // }
455 // hideProgress();
456 // }
459 * Retrieve existing or create new album object.
461 LIBMTP_album_t
462 *MtpMediaDevice::getOrCreateAlbum( Q3PtrList<MediaItem> *items )//uint32_t track_id, const MetaBundle &bundle )
464 LIBMTP_album_t *album_object = 0;
465 uint32_t albumid = 0;
466 int ret;
467 QMap<uint32_t,MtpAlbum*>::Iterator it;
468 for( it = m_idToAlbum.begin(); it != m_idToAlbum.end(); ++it )
470 if( it.data()->album() == items->first()->bundle()->album() )
472 albumid = it.data()->id();
473 break;
476 if( albumid )
478 debug() << "reusing existing album " << albumid;
479 album_object = LIBMTP_Get_Album( m_device, albumid );
480 if( album_object == 0 )
482 debug() << "retrieving album failed.";
483 return 0;
485 uint32_t i;
486 uint32_t trackCount = album_object->no_tracks;
487 for( MtpMediaItem *it = dynamic_cast<MtpMediaItem*>(items->first()); it; it = dynamic_cast<MtpMediaItem*>(items->next()) )
489 bool exists = false;
490 for( i = 0; i < album_object->no_tracks; i++ )
492 if( album_object->tracks[i] == it->track()->id() )
494 exists = true;
495 break;
498 if( ! exists )
500 debug() << "adding track " << it->track()->id() << " to existing album " << albumid;
501 album_object->no_tracks++;
502 album_object->tracks = (uint32_t *)realloc( album_object->tracks, album_object->no_tracks * sizeof( uint32_t ) );
503 album_object->tracks[ ( album_object->no_tracks - 1 ) ] = it->track()->id();
506 if( trackCount != album_object->no_tracks ) // album needs an update
508 ret = LIBMTP_Update_Album( m_device, album_object );
509 if( ret != 0 )
510 debug() << "updating album failed : " << ret;
513 else
515 debug() << "creating new album ";
516 album_object = LIBMTP_new_album_t();
517 album_object->name = qstrdup( items->first()->bundle()->album().string().toUtf8() );
518 album_object->tracks = (uint32_t *) malloc(items->count() * sizeof(uint32_t));
519 int i = 0;
520 for( MtpMediaItem *it = dynamic_cast<MtpMediaItem*>(items->first()); it; it = dynamic_cast<MtpMediaItem*>(items->next()) )
521 album_object->tracks[i++] = it->track()->id();
522 album_object->no_tracks = items->count();
523 ret = LIBMTP_Create_New_Album( m_device, album_object, 0 );
524 if( ret != 0 )
526 debug() << "creating album failed : " << ret;
527 return 0;
529 m_idToAlbum[ album_object->album_id ] = new MtpAlbum( album_object );
531 return album_object;
535 * Check (and optionally create) the folder structure to put a
536 * track into. Return the (possibly new) parent folder ID
538 uint32_t
539 MtpMediaDevice::checkFolderStructure( const MetaBundle &bundle, bool create )
541 QString artist = bundle.artist();
542 if( artist.isEmpty() )
543 artist = i18n( "Unknown Artist" );
544 if( bundle.compilation() == MetaBundle::CompilationYes )
545 artist = i18n( "Various Artists" );
546 QString album = bundle.album();
547 if( album.isEmpty() )
548 album = i18n( "Unknown Album" );
549 QString genre = bundle.genre();
550 if( genre.isEmpty() )
551 genre = i18n( "Unknown Genre" );
552 m_critical_mutex.lock();
553 uint32_t parent_id = getDefaultParentId();
554 QStringList folders = QStringList::split( "/", m_folderStructure ); // use slash as a dir separator
555 QString completePath;
556 for( QStringList::Iterator it = folders.begin(); it != folders.end(); ++it )
558 if( (*it).isEmpty() )
559 continue;
560 // substitute %a , %b , %g
561 (*it).replace( QRegExp( "%a" ), artist )
562 .replace( QRegExp( "%b" ), album )
563 .replace( QRegExp( "%g" ), genre );
564 // check if it exists
565 uint32_t check_folder = subfolderNameToID( (*it).toUtf8(), m_folders, parent_id );
566 // create if not exists (if requested)
567 if( check_folder == 0 )
569 if( create )
571 check_folder = createFolder( (*it).toUtf8() , parent_id );
572 if( check_folder == 0 )
574 m_critical_mutex.unlock();
575 return 0;
578 else
580 m_critical_mutex.unlock();
581 return 0;
584 completePath += (*it).toUtf8() + '/';
585 // set new parent
586 parent_id = check_folder;
588 m_critical_mutex.unlock();
589 debug() << "Folder path : " << completePath;
590 // return parent
591 return parent_id;
595 * Create a new mtp folder
597 uint32_t
598 MtpMediaDevice::createFolder( const char *name, uint32_t parent_id )
600 debug() << "Creating new folder '" << name << "' as a child of "<< parent_id;
601 char *name_copy = qstrdup( name );
602 uint32_t new_folder_id = LIBMTP_Create_Folder( m_device, name_copy, parent_id );
603 delete(name_copy);
604 debug() << "New folder ID: " << new_folder_id;
605 if( new_folder_id == 0 )
607 debug() << "Attempt to create folder '" << name << "' failed.";
608 return 0;
610 updateFolders();
612 return new_folder_id;
616 * Recursively search the folder list for a matching one under the specified
617 * parent ID and return the child's ID
619 uint32_t
620 MtpMediaDevice::subfolderNameToID( const char *name, LIBMTP_folder_t *folderlist, uint32_t parent_id )
622 uint32_t i;
624 if( folderlist == 0 )
625 return 0;
627 if( !strcasecmp( name, folderlist->name ) && folderlist->parent_id == parent_id )
628 return folderlist->folder_id;
630 if( ( i = ( subfolderNameToID( name, folderlist->child, parent_id ) ) ) )
631 return i;
632 if( ( i = ( subfolderNameToID( name, folderlist->sibling, parent_id ) ) ) )
633 return i;
635 return 0;
639 * Recursively search the folder list for a matching one
640 * and return its ID
642 uint32_t
643 MtpMediaDevice::folderNameToID( char *name, LIBMTP_folder_t *folderlist )
645 uint32_t i;
647 if( folderlist == 0 )
648 return 0;
650 if( !strcasecmp( name, folderlist->name ) )
651 return folderlist->folder_id;
653 if( ( i = ( folderNameToID( name, folderlist->child ) ) ) )
654 return i;
655 if( ( i = ( folderNameToID( name, folderlist->sibling ) ) ) )
656 return i;
658 return 0;
662 * Get a list of selected items, download them to a temporary location and
663 * organize.
666 MtpMediaDevice::downloadSelectedItemsToCollection()
668 Q3PtrList<MediaItem> items;
669 m_view->getSelectedLeaves( 0, &items );
671 KTempDir tempdir( QString() );
672 tempdir.setAutoDelete( true );
673 KUrl::List urls;
674 QString genericError = i18n( "Could not copy track from device." );
676 int total,progress;
677 total = items.count();
678 progress = 0;
680 if( total == 0 )
681 return 0;
683 setProgress( progress, total );
684 for( MtpMediaItem *it = dynamic_cast<MtpMediaItem*>(items.first()); it && !(m_canceled); it = dynamic_cast<MtpMediaItem*>(items.next()) )
686 if( it->type() == MediaItem::TRACK )
688 QString filename = tempdir.name() + it->bundle()->filename();
689 int ret = LIBMTP_Get_Track_To_File(
690 m_device, it->track()->id(), filename.toUtf8(),
691 progressCallback, this
693 if( ret != 0 )
695 debug() << "Get Track failed: " << ret;
696 Amarok::StatusBar::instance()->shortLongMessage(
697 genericError,
698 i18n( "Could not copy track from device." ),
699 KDE::StatusBar::Error
702 else
704 urls << filename;
705 progress++;
706 setProgress( progress );
709 else
711 total--;
712 setProgress( progress, total );
715 hideProgress();
716 //PORT 2.0 CollectionView::instance()->organizeFiles( urls, i18n( "Move Files To Collection" ), false );
717 return 0;
721 * Write any pending changes to the device, such as database changes
723 void
724 MtpMediaDevice::synchronizeDevice()
726 updateAlbumArt( m_newTracks );
727 m_newTracks->clear();
728 return;
732 * Create a new playlist
734 // MtpMediaItem
735 // *MtpMediaDevice::newPlaylist( const QString &name, MediaItem *parent, Q3PtrList<MediaItem> items )
736 // {
737 // DEBUG_BLOCK
738 // MtpMediaItem *item = new MtpMediaItem( parent, this );
739 // item->setType( MediaItem::PLAYLIST );
740 // item->setText( 0, name );
741 // item->setPlaylist( new MtpPlaylist() );
743 // addToPlaylist( item, 0, items );
745 // if( ! isTransferring() )
746 // m_view->rename( item, 0 );
748 // return item;
749 // }
752 * Add an item to a playlist
754 // void
755 // MtpMediaDevice::addToPlaylist( MediaItem *mlist, MediaItem *after, Q3PtrList<MediaItem> items )
756 // {
757 // DEBUG_BLOCK
758 // MtpMediaItem *list = dynamic_cast<MtpMediaItem *>( mlist );
759 // if( !list )
760 // return;
762 // int order;
763 // MtpMediaItem *it;
764 // if( after )
765 // {
766 // order = after->m_order + 1;
767 // it = dynamic_cast<MtpMediaItem*>(after->nextSibling());
768 // }
769 // else
770 // {
771 // order = 0;
772 // it = dynamic_cast<MtpMediaItem*>( list->firstChild() );
773 // }
775 // for( ; it; it = dynamic_cast<MtpMediaItem *>( it->nextSibling() ) )
776 // {
777 // it->m_order += items.count();
778 // }
780 // for( MtpMediaItem *it = dynamic_cast<MtpMediaItem *>(items.first() );
781 // it;
782 // it = dynamic_cast<MtpMediaItem *>( items.next() ) )
783 // {
784 // if( !it->track() )
785 // continue;
787 // MtpMediaItem *add;
788 // if( it->parent() == list )
789 // {
790 // add = it;
791 // if( after )
792 // {
793 // it->moveItem(after);
794 // }
795 // else
796 // {
797 // list->takeItem(it);
798 // list->insertItem(it);
799 // }
800 // }
801 // else
802 // {
803 // if( after )
804 // {
805 // add = new MtpMediaItem( list, after );
806 // }
807 // else
808 // {
809 // add = new MtpMediaItem( list, this );
810 // }
811 // }
812 // after = add;
814 // add->setType( MediaItem::PLAYLISTITEM );
815 // add->setTrack( it->track() );
816 // add->setBundle( new MetaBundle( *(it->bundle()) ) );
817 // add->m_device = this;
818 // add->setText( 0, it->bundle()->artist() + " - " + it->bundle()->title() );
819 // add->m_order = order;
820 // order++;
821 // }
823 // // make numbering consecutive
824 // int i = 0;
825 // for( MtpMediaItem *it = dynamic_cast<MtpMediaItem *>( list->firstChild() );
826 // it;
827 // it = dynamic_cast<MtpMediaItem *>( it->nextSibling() ) )
828 // {
829 // it->m_order = i;
830 // i++;
831 // }
833 // playlistFromItem( list );
834 // }
837 * When a playlist has been renamed, we must save it
839 // void
840 // MtpMediaDevice::playlistRenamed( Q3ListViewItem *item, const QString &, int ) // SLOT
841 // {
842 // DEBUG_BLOCK
843 // MtpMediaItem *playlist = static_cast<MtpMediaItem*>( item );
844 // if( playlist->type() == MediaItem::PLAYLIST )
845 // playlistFromItem( playlist );
846 // }
849 * Save a playlist
851 // void
852 // MtpMediaDevice::playlistFromItem( MtpMediaItem *item )
853 // {
854 // if( item->childCount() == 0 )
855 // return;
856 // m_critical_mutex.lock();
857 // LIBMTP_playlist_t *metadata = LIBMTP_new_playlist_t();
858 // metadata->name = qstrdup( item->text( 0 ).toUtf8() );
859 // const int trackCount = item->childCount();
860 // if (trackCount > 0) {
861 // uint32_t *tracks = ( uint32_t* )malloc( sizeof( uint32_t ) * trackCount );
862 // uint32_t i = 0;
863 // for( MtpMediaItem *it = dynamic_cast<MtpMediaItem *>(item->firstChild());
864 // it;
865 // it = dynamic_cast<MtpMediaItem *>(it->nextSibling()) )
866 // {
867 // tracks[i] = it->track()->id();
868 // i++;
869 // }
870 // metadata->tracks = tracks;
871 // metadata->no_tracks = i;
872 // } else {
873 // debug() << "no tracks available for playlist " << metadata->name
874 // ;
875 // metadata->no_tracks = 0;
876 // }
877 // QString genericError = i18n( "Could not save playlist." );
879 // uint32_t *tracks = ( uint32_t* )malloc( sizeof( uint32_t ) * item->childCount() );
880 // uint32_t i = 0;
881 // for( MtpMediaItem *it = dynamic_cast<MtpMediaItem *>(item->firstChild());
882 // it;
883 // it = dynamic_cast<MtpMediaItem *>(it->nextSibling()) )
884 // {
885 // tracks[i] = it->track()->id();
886 // i++;
887 // }
888 // metadata->tracks = tracks;
889 // metadata->no_tracks = i;
891 // QString genericError = i18n( "Could not save playlist." );
893 // if( item->playlist()->id() == 0 )
894 // {
895 // debug() << "creating new playlist : " << metadata->name;
896 // int ret = LIBMTP_Create_New_Playlist( m_device, metadata, 0 );
897 // if( ret == 0 )
898 // {
899 // item->playlist()->setId( metadata->playlist_id );
900 // debug() << "playlist saved : " << metadata->playlist_id;
901 // }
902 // else
903 // {
904 // Amarok::StatusBar::instance()->shortLongMessage(
905 // genericError,
906 // i18n( "Could not create new playlist on device." ),
907 // KDE::StatusBar::Error
908 // );
909 // }
910 // }
911 // else
912 // {
913 // metadata->playlist_id = item->playlist()->id();
914 // debug() << "updating playlist : " << metadata->name;
915 // int ret = LIBMTP_Update_Playlist( m_device, metadata );
916 // if( ret != 0 )
917 // {
918 // Amarok::StatusBar::instance()->shortLongMessage(
919 // genericError,
920 // i18n( "Could not update playlist on device." ),
921 // KDE::StatusBar::Error
922 // );
923 // }
924 // }
925 // m_critical_mutex.unlock();
926 // }
929 * Recursively remove MediaItem from the device and media view
932 MtpMediaDevice::deleteItemFromDevice(MediaItem* item, int flags )
935 int result = 0;
936 if( isCanceled() || !item )
938 return -1;
940 MediaItem *next = 0;
942 switch( item->type() )
944 case MediaItem::PLAYLIST:
945 case MediaItem::TRACK:
946 if( isCanceled() )
947 break;
948 if( item )
950 int res = deleteObject( dynamic_cast<MtpMediaItem *> ( item ) );
951 if( res >=0 && result >= 0 )
952 result += res;
953 else
954 result = -1;
956 break;
957 case MediaItem::PLAYLISTITEM:
958 if( isCanceled() )
959 break;
960 if( item )
962 MtpMediaItem *parent = dynamic_cast<MtpMediaItem *> ( item->parent() );
963 if( parent && parent->type() == MediaItem::PLAYLIST ) {
964 delete( item );
965 playlistFromItem( parent );
968 break;
969 case MediaItem::ALBUM:
970 case MediaItem::ARTIST:
971 // Recurse through the lists
972 next = 0;
974 if( isCanceled() )
975 break;
977 for( MediaItem *it = dynamic_cast<MediaItem *>( item->firstChild() ); it ; it = next )
979 next = dynamic_cast<MediaItem *>( it->nextSibling() );
980 int res = deleteItemFromDevice( it, flags );
981 if( res >= 0 && result >= 0 )
982 result += res;
983 else
984 result = -1;
987 if( item )
988 delete dynamic_cast<MediaItem *>( item );
989 break;
990 default:
991 result = 0;
993 return result;
997 * Actually delete a track or playlist
1000 MtpMediaDevice::deleteObject( MtpMediaItem *deleteItem )
1002 DEBUG_BLOCK
1004 u_int32_t object_id;
1005 if( deleteItem->type() == MediaItem::PLAYLIST )
1006 object_id = deleteItem->playlist()->id();
1007 else
1008 object_id = deleteItem->track()->id();
1010 QString genericError = i18n( "Could not delete item" );
1012 debug() << "delete this id : " << object_id;
1014 m_critical_mutex.lock();
1015 int status = LIBMTP_Delete_Object( m_device, object_id );
1016 m_critical_mutex.unlock();
1018 if( status != 0 )
1020 debug() << "delete object failed";
1021 Amarok::StatusBar::instance()->shortLongMessage(
1022 genericError,
1023 i18n( "Delete failed" ),
1024 KDE::StatusBar::Error
1026 return -1;
1028 debug() << "object deleted";
1030 // clear cached filename
1031 if( deleteItem->type() == MediaItem::TRACK )
1032 m_fileNameToItem.remove( QString( "%1/%2" ).arg( deleteItem->track()->folderId() ).arg( deleteItem->bundle()->filename() ) );
1033 // remove from the media view
1034 delete deleteItem;
1035 kapp->processEvents( 100 );
1037 return 1;
1041 * Update local cache of mtp folders
1043 void
1044 MtpMediaDevice::updateFolders( void )
1046 LIBMTP_destroy_folder_t( m_folders );
1047 m_folders = 0;
1048 m_folders = LIBMTP_Get_Folder_List( m_device );
1052 * Set cancellation of an operation
1054 void
1055 MtpMediaDevice::cancelTransfer()
1057 m_canceled = true;
1061 * Connect to device, and populate m_view with MediaItems
1063 bool
1064 MtpMediaDevice::openDevice( bool silent )
1066 DEBUG_BLOCK
1068 Q_UNUSED( silent );
1070 if( m_device != 0 )
1071 return true;
1073 QString genericError = i18n( "Could not connect to MTP Device" );
1075 m_critical_mutex.lock();
1076 LIBMTP_Init();
1077 m_device = LIBMTP_Get_First_Device();
1078 m_critical_mutex.unlock();
1079 if( m_device == 0 ) {
1080 debug() << "No devices.";
1081 Amarok::StatusBar::instance()->shortLongMessage(
1082 genericError,
1083 i18n( "MTP device could not be opened" ),
1084 KDE::StatusBar::Error
1086 setDisconnected();
1087 return false;
1090 QString modelname = QString( LIBMTP_Get_Modelname( m_device ) );
1091 QString ownername = QString( LIBMTP_Get_Friendlyname( m_device ) );
1092 m_name = modelname;
1093 if(! ownername.isEmpty() )
1094 m_name += " (" + ownername + ')';
1096 m_default_parent_folder = m_device->default_music_folder;
1097 debug() << "setting default parent : " << m_default_parent_folder;
1099 MtpMediaDevice::readMtpMusic();
1101 m_critical_mutex.lock();
1102 m_folders = LIBMTP_Get_Folder_List( m_device );
1103 uint16_t *filetypes;
1104 uint16_t filetypes_len;
1105 int ret = LIBMTP_Get_Supported_Filetypes( m_device, &filetypes, &filetypes_len );
1106 if( ret == 0 )
1108 uint16_t i;
1109 for( i = 0; i < filetypes_len; i++ )
1110 m_supportedFiles << mtpFileTypes[ filetypes[ i ] ];
1112 // find supported image types (for album art).
1113 if( m_supportedFiles.findIndex( "jpg" ) )
1114 m_format = "JPEG";
1115 else if( m_supportedFiles.findIndex( "png" ) )
1116 m_format = "PNG";
1117 else if( m_supportedFiles.findIndex( "gif" ) )
1118 m_format = "GIF";
1119 free( filetypes );
1120 m_critical_mutex.unlock();
1122 return true;
1126 * Start the view (add default folders such as for playlists)
1128 void
1129 MtpMediaDevice::initView()
1131 if( ! isConnected() )
1132 return;
1133 m_playlistItem = new MtpMediaItem( m_view, this );
1134 m_playlistItem->setText( 0, i18n("Playlists") );
1135 m_playlistItem->setType( MediaItem::PLAYLISTSROOT );
1136 m_playlistItem->m_order = -1;
1140 * Wrap up any loose ends and close the device
1142 bool
1143 MtpMediaDevice::closeDevice()
1145 DEBUG_BLOCK
1147 // clear folder structure
1148 if( m_folders != 0 )
1150 m_critical_mutex.lock();
1151 LIBMTP_destroy_folder_t( m_folders );
1152 m_critical_mutex.unlock();
1153 m_folders = 0;
1154 debug() << "Folders destroyed";
1157 // release device
1158 if( m_device != 0 )
1160 m_critical_mutex.lock();
1161 LIBMTP_Release_Device( m_device );
1162 m_critical_mutex.unlock();
1163 setDisconnected();
1164 debug() << "Device released";
1167 // clear the cached mappings
1168 m_idToAlbum.clear();
1169 m_idToTrack.clear();
1170 m_fileNameToItem.clear();
1172 // clean up the view
1173 clearItems();
1175 return true;
1179 * Get the capacity and freespace available on the device, in KB
1181 bool
1182 MtpMediaDevice::getCapacity( KIO::filesize_t *total, KIO::filesize_t *available )
1184 if( !isConnected() )
1185 return false;
1187 // TODO : Follow the links so we sum up all the device's storage.
1188 *total = m_device->storage->MaxCapacity;
1189 *available = m_device->storage->FreeSpaceInBytes;
1190 return true;
1194 * Get custom information about the device via MTP
1196 void
1197 MtpMediaDevice::customClicked()
1199 QString Information;
1200 if( isConnected() )
1202 QString batteryLevel;
1203 QString secureTime;
1204 QString supportedFiles;
1206 uint8_t maxbattlevel;
1207 uint8_t currbattlevel;
1208 char *sectime;
1211 m_critical_mutex.lock();
1212 LIBMTP_Get_Batterylevel( m_device, &maxbattlevel, &currbattlevel );
1213 LIBMTP_Get_Secure_Time( m_device, &sectime );
1214 m_critical_mutex.unlock();
1216 batteryLevel = i18n("Battery level: ")
1217 + QString::number( (int) ( (float) currbattlevel / (float) maxbattlevel * 100.0 ) )
1218 + '%';
1219 secureTime = i18n("Secure time: ") + sectime;
1220 supportedFiles = i18n("Supported file types: ")
1221 + m_supportedFiles.join( ", " );
1223 Information = ( i18n( "Player Information for " )
1224 + m_name + '\n' + batteryLevel
1225 + '\n' + secureTime + '\n'
1226 + supportedFiles );
1227 free(sectime);
1229 else
1231 Information = i18n( "Player not connected" );
1234 KMessageBox::information( 0, Information, i18n( "Device information" ) );
1238 * Current device
1240 LIBMTP_mtpdevice_t
1241 *MtpMediaDevice::current_device()
1243 return m_device;
1247 * We use a 0 device to show a disconnected device.
1248 * This sets the device to that.
1250 void
1251 MtpMediaDevice::setDisconnected()
1253 m_device = 0;
1257 * Handle clicking of the right mouse button
1259 void
1260 MtpMediaDevice::rmbPressed( Q3ListViewItem *qitem, const QPoint &point, int )
1263 enum Actions {RENAME, DOWNLOAD, DELETE, MAKE_PLAYLIST, UPDATE_ALBUM_ART};
1265 MtpMediaItem *item = static_cast<MtpMediaItem *>( qitem );
1266 if( item )
1268 KMenu menu( m_view );
1269 switch( item->type() )
1271 case MediaItem::ARTIST:
1272 case MediaItem::ALBUM:
1273 case MediaItem::TRACK:
1274 menu.insertItem( SmallIconSet( Amarok::icon( "collection" ) ), i18n("&Copy Files to Collection..."), DOWNLOAD );
1275 menu.insertItem( SmallIconSet( Amarok::icon( "playlist" ) ), i18n( "Make Media Device Playlist" ), MAKE_PLAYLIST );
1276 menu.insertItem( SmallIconSet( Amarok::icon( "covermanager" ) ), i18n( "Refresh Cover Images" ), UPDATE_ALBUM_ART );
1277 break;
1278 case MediaItem::PLAYLIST:
1279 menu.insertItem( SmallIconSet( Amarok::icon( "edit" ) ), i18n( "Rename" ), RENAME );
1280 break;
1281 default:
1282 break;
1285 menu.insertItem( SmallIconSet( Amarok::icon( "remove" ) ), i18n( "Delete from device" ), DELETE );
1287 int id = menu.exec( point );
1288 switch( id )
1290 case MAKE_PLAYLIST:
1292 Q3PtrList<MediaItem> items;
1293 m_view->getSelectedLeaves( 0, &items );
1294 QString name = i18n( "New Playlist" );
1295 newPlaylist( name, m_playlistItem, items );
1297 break;
1298 case DELETE:
1299 MediaDevice::deleteFromDevice();
1300 break;
1301 case RENAME:
1302 if( item->type() == MediaItem::PLAYLIST && ! isTransferring() )
1304 m_view->rename( item, 0 );
1306 break;
1307 case DOWNLOAD:
1308 downloadSelectedItemsToCollection();
1309 break;
1310 case UPDATE_ALBUM_ART:
1312 Q3PtrList<MediaItem> *items = new Q3PtrList<MediaItem>;
1313 m_view->getSelectedLeaves( 0, items );
1315 if( items->count() > 100 )
1317 int button = KMessageBox::warningContinueCancel( m_parent,
1318 i18np( "<p>You are updating cover art for 1 track. This may take some time.",
1319 "<p>You are updating cover art for %n tracks. This may take some time.",
1320 items->count()
1322 QString() );
1324 if( button != KMessageBox::Continue )
1325 return;
1327 updateAlbumArt( items );
1328 break;
1332 return;
1336 * Add gui elements to the device configuration
1338 void
1339 MtpMediaDevice::addConfigElements( QWidget *parent )
1342 m_folderLabel = new QLabel( parent );
1343 m_folderLabel->setText( i18n( "Folder structure:" ) );
1345 m_folderStructureBox = new QLineEdit( parent );
1346 m_folderStructureBox->setText( m_folderStructure );
1347 m_folderStructureBox->setToolTip(
1348 i18n( "Files copied to the device will be placed in this folder." ) + '\n'
1349 + i18n( "/ is used as folder separator." ) + '\n'
1350 + i18n( "%a will be replaced with the artist name, ")
1351 + i18n( "%b with the album name," ) + '\n'
1352 + i18n( "%g with the genre.") + '\n'
1353 + i18n( "An empty path means the files will be placed unsorted in the default music folder." ) );
1357 * Remove gui elements from the device configuration
1359 void
1360 MtpMediaDevice::removeConfigElements( QWidget *parent)
1362 Q_UNUSED(parent)
1364 delete m_folderStructureBox;
1365 m_folderStructureBox = 0;
1367 delete m_folderLabel;
1368 m_folderLabel = 0;
1372 * Save changed config after dialog commit
1374 void
1375 MtpMediaDevice::applyConfig()
1377 m_folderStructure = m_folderStructureBox->text();
1378 setConfigString( "FolderStructure", m_folderStructure );
1382 * Load config from the amarokrc file
1384 void
1385 MtpMediaDevice::loadConfig()
1387 m_folderStructure = configString( "FolderStructure","%a - %b" );
1391 * Add a track to the current Collection
1393 MtpMediaItem
1394 *MtpMediaDevice::addTrackToCollection( MtpTrack *track, MtpMediaItem *item )
1396 QString artistName = track->bundle()->artist();
1398 MtpMediaItem *artist = dynamic_cast<MtpMediaItem *>( m_view->findItem( artistName, 0 ) );
1399 if( !artist )
1401 artist = new MtpMediaItem(m_view);
1402 artist->m_device = this;
1403 artist->setText( 0, artistName );
1404 artist->setType( MediaItem::ARTIST );
1407 QString albumName = track->bundle()->album();
1408 MtpMediaItem *album = dynamic_cast<MtpMediaItem *>( artist->findItem( albumName ) );
1409 if( !album )
1411 album = new MtpMediaItem( artist );
1412 album->setText( 0, albumName );
1413 album->setType( MediaItem::ALBUM );
1414 album->m_device = this;
1417 if( item )
1418 album->insertItem( item );
1419 else
1421 item = new MtpMediaItem( album );
1422 item->m_device = this;
1423 QString titleName = track->bundle()->title();
1424 item->setTrack( track );
1425 item->m_order = track->bundle()->track();
1426 item->setText( 0, titleName );
1427 item->setType( MediaItem::TRACK );
1428 item->setBundle( track->bundle() );
1429 item->track()->setId( track->id() );
1430 m_fileNameToItem[ QString( "%1/%2" ).arg( track->folderId() ).arg( track->bundle()->filename() ) ] = item;
1431 m_idToTrack[ track->id() ] = track;
1433 return item;
1437 * Get tracks and add them to the listview
1440 MtpMediaDevice::readMtpMusic()
1442 DEBUG_BLOCK
1444 clearItems();
1446 m_critical_mutex.lock();
1448 QString genericError = i18n( "Could not get music from MTP Device" );
1450 int total = 100;
1451 int progress = 0;
1452 setProgress( progress, total ); // we don't know how many tracks. fake progress bar.
1454 kapp->processEvents( 100 );
1456 LIBMTP_track_t *tracks = LIBMTP_Get_Tracklisting_With_Callback( m_device, progressCallback, this );
1458 debug() << "Got tracks from device";
1460 if( tracks == 0 )
1462 debug() << "0 tracks returned. Empty device...";
1464 else
1466 LIBMTP_track_t *tmp = tracks;
1467 total = 0;
1468 // spin through once to determine size of the list
1469 while( tracks != 0 )
1471 tracks = tracks->next;
1472 total++;
1474 setProgress( progress, total );
1475 tracks = tmp;
1476 // now process the tracks
1477 while( tracks != 0 )
1479 MtpTrack *mtp_track = new MtpTrack( tracks );
1480 mtp_track->readMetaData( tracks );
1481 addTrackToView( mtp_track );
1482 tmp = tracks;
1483 tracks = tracks->next;
1484 LIBMTP_destroy_track_t( tmp );
1485 progress++;
1486 setProgress( progress );
1487 if( progress % 50 == 0 )
1488 kapp->processEvents( 100 );
1492 readPlaylists();
1493 readAlbums();
1495 setProgress( total );
1496 hideProgress();
1498 m_critical_mutex.unlock();
1500 return 0;
1504 * Populate playlists
1506 void
1507 MtpMediaDevice::readPlaylists()
1509 LIBMTP_playlist_t *playlists = LIBMTP_Get_Playlist_List( m_device );
1511 if( playlists != 0 )
1513 LIBMTP_playlist_t *tmp;
1514 while( playlists != 0 )
1516 MtpMediaItem *playlist = new MtpMediaItem( m_playlistItem, this );
1517 playlist->setText( 0, QString::fromUtf8( playlists->name ) );
1518 playlist->setType( MediaItem::PLAYLIST );
1519 playlist->setPlaylist( new MtpPlaylist() );
1520 playlist->playlist()->setId( playlists->playlist_id );
1521 uint32_t i;
1522 for( i = 0; i < playlists->no_tracks; i++ )
1524 MtpTrack *track = m_idToTrack[ playlists->tracks[i] ];
1525 if( track == 0 ) // skip invalid playlist entries
1526 continue;
1527 MtpMediaItem *item = new MtpMediaItem( playlist );
1528 item->setText( 0, track->bundle()->artist() + " - " + track->bundle()->title() );
1529 item->setType( MediaItem::PLAYLISTITEM );
1530 item->setBundle( track->bundle() );
1531 item->setTrack( track );
1532 item->m_order = i;
1533 item->m_device = this;
1535 tmp = playlists;
1536 playlists = playlists->next;
1537 LIBMTP_destroy_playlist_t( tmp );
1538 kapp->processEvents( 50 );
1545 * Read existing albums
1547 void
1548 MtpMediaDevice::readAlbums()
1550 LIBMTP_album_t *albums = LIBMTP_Get_Album_List( m_device );
1552 if( albums != 0 )
1554 LIBMTP_album_t *tmp;
1555 while( albums != 0 )
1557 m_idToAlbum[ albums->album_id ] = new MtpAlbum( albums );
1558 tmp = albums;
1559 albums = albums->next;
1560 LIBMTP_destroy_album_t( tmp );
1561 kapp->processEvents( 50 );
1567 * Clear the current Collection
1569 void
1570 MtpMediaDevice::clearItems()
1576 * MtpTrack Class
1578 MtpTrack::MtpTrack( LIBMTP_track_t *track )
1580 m_id = track->item_id;
1584 * Read track properties from the device and set it on the track
1586 void
1587 MtpTrack::readMetaData( LIBMTP_track_t *track )
1589 MetaBundle *bundle = new MetaBundle();
1591 if( track->genre != 0 )
1592 bundle->setGenre( AtomicString( QString::fromUtf8( track->genre ) ) );
1593 if( track->artist != 0 )
1594 bundle->setArtist( AtomicString( QString::fromUtf8( track->artist ) ) );
1595 if( track->album != 0 )
1596 bundle->setAlbum( AtomicString( QString::fromUtf8( track->album ) ) );
1597 if( track->title != 0 )
1598 bundle->setTitle( AtomicString( QString::fromUtf8( track->title ) ) );
1599 if( track->filename != 0 )
1600 bundle->setPath( AtomicString( QString::fromUtf8( track->filename ) ) );
1602 // translate codecs to file types
1603 if( track->filetype == LIBMTP_FILETYPE_MP3 )
1604 bundle->setFileType( MetaBundle::mp3 );
1605 else if( track->filetype == LIBMTP_FILETYPE_WMA )
1606 bundle->setFileType( MetaBundle::wma );
1607 else if( track->filetype == LIBMTP_FILETYPE_OGG )
1608 bundle->setFileType( MetaBundle::ogg );
1609 else
1610 bundle->setFileType( MetaBundle::other );
1612 if( track->date != 0 )
1613 bundle->setYear( QString( QString::fromUtf8( track->date ) ).mid( 0, 4 ).toUInt() );
1614 if( track->tracknumber > 0 )
1615 bundle->setTrack( track->tracknumber );
1616 if( track->duration > 0 )
1617 bundle->setLength( track->duration / 1000 ); // Divide by 1000 since this is in milliseconds
1619 this->setFolderId( track->parent_id );
1621 this->setBundle( *bundle );
1625 * MtpAlbum Class
1627 MtpAlbum::MtpAlbum( LIBMTP_album_t *album )
1629 m_id = album->album_id;
1630 m_album = QString::fromUtf8( album->name );