Commit 610b5a84cf367fc217c605fcd8bf9f304b7909fe
- Diff rendering mode:
- inline
- side by side
ChangeLog
(0 / 2)
|   | |||
| 5 | 5 | ||
| 6 | 6 | VERSION 2.3 | |
| 7 | 7 | FEATURES: | |
| 8 | * Support for embedded cover art. Patch by Andreas L. <ecroy@gmx.net>. | ||
| 9 | (BR 176402) | ||
| 10 | 8 | * Podcast channels and episodes can be dragged to add them to other | |
| 11 | 9 | providers. (BR 195704) | |
| 12 | 10 | * Trackaction buttons are now available in the label for the current track |
|   | |||
| 799 | 799 | directoryData.append( data ); | |
| 800 | 800 | currentDir = url.directory(); | |
| 801 | 801 | } | |
| 802 | |||
| 803 | //the boolean attribute 'apic' indicates that the file contains an embedded cover-art image | ||
| 804 | //we therefore add the path of the audio file to the list of images for this album | ||
| 805 | if( !attrs.value( "apic" ).isEmpty() ) | ||
| 806 | { | ||
| 807 | QList<QPair<QString, QString> > covers; | ||
| 808 | covers += qMakePair( attrs.value( "artist" ).toString(), attrs.value( "album" ).toString() ); | ||
| 809 | processor.addImage( attrs.value( "path" ).toString(), covers ); | ||
| 810 | } | ||
| 811 | 802 | } | |
| 812 | 803 | else if( localname == "folder" ) | |
| 813 | 804 | { |
|   | |||
| 188 | 188 | QString goodPath; | |
| 189 | 189 | foreach( const QString &path, paths ) | |
| 190 | 190 | { | |
| 191 | // skip embedded images | ||
| 192 | if( SqlAlbum::isEmbeddedImage( path ) ) | ||
| 193 | continue; | ||
| 194 | |||
| 195 | 191 | QString file = QFileInfo( path ).fileName(); | |
| 196 | 192 | ||
| 197 | 193 | //prioritize "front" | |
| … | … | ||
| 234 | 234 | if( !goodPath.isEmpty() ) | |
| 235 | 235 | return goodPath; | |
| 236 | 236 | ||
| 237 | //next: pick largest non-embedded image -- often a high-quality blowup of the front | ||
| 237 | //finally: pick largest image -- often a high-quality blowup of the front | ||
| 238 | 238 | //so that people can print it out | |
| 239 | qint64 size = -1; | ||
| 239 | qint64 size = 0; | ||
| 240 | 240 | QString current; | |
| 241 | 241 | foreach( const QString &path, paths ) | |
| 242 | 242 | { | |
| 243 | 243 | QFileInfo info( path ); | |
| 244 | if( info.size() > size && | ||
| 245 | ! SqlAlbum::isEmbeddedImage( path ) ) | ||
| 244 | if( info.size() > size ) | ||
| 246 | 245 | { | |
| 247 | 246 | size = info.size(); | |
| 248 | 247 | current = path; | |
| 249 | 248 | } | |
| 250 | 249 | } | |
| 251 | |||
| 252 | //finally: if all available images are embedded, simply pick the first one | ||
| 253 | if( size == -1 ) | ||
| 254 | return paths.first(); | ||
| 255 | |||
| 256 | 250 | return current; | |
| 251 | |||
| 257 | 252 | } | |
| 258 | 253 | ||
| 259 | 254 | void |
|   | |||
| 46 | 46 | #include <KLocale> | |
| 47 | 47 | #include <KSharedPtr> | |
| 48 | 48 | ||
| 49 | //Taglib: | ||
| 50 | #include <mpegfile.h> | ||
| 51 | #include <id3v2tag.h> | ||
| 52 | #include <attachedpictureframe.h> | ||
| 53 | |||
| 54 | 49 | using namespace Meta; | |
| 55 | 50 | ||
| 56 | 51 | QString | |
| … | … | ||
| 1113 | 1113 | ||
| 1114 | 1114 | //FIXME this cache doesn't differentiate between shadowed/unshadowed | |
| 1115 | 1115 | if( m_images.contains( size ) ) | |
| 1116 | { | ||
| 1117 | if (isEmbeddedImage( m_images.value( size ) ) ) | ||
| 1118 | return QPixmap::fromImage( loadImageFromTag( m_images.value( size ) ) ); | ||
| 1119 | else | ||
| 1120 | return QPixmap( m_images.value( size ) ); | ||
| 1121 | } | ||
| 1116 | return QPixmap( m_images.value( size ) ); | ||
| 1122 | 1117 | ||
| 1123 | 1118 | QString result; | |
| 1124 | 1119 | ||
| … | … | ||
| 1152 | 1152 | { | |
| 1153 | 1153 | m_hasImage = true; | |
| 1154 | 1154 | m_images.insert( size, result ); | |
| 1155 | if (isEmbeddedImage( m_images.value( size ) ) ) | ||
| 1156 | return QPixmap::fromImage( loadImageFromTag( result ) ); | ||
| 1157 | else | ||
| 1158 | return QPixmap( result ); | ||
| 1155 | return QPixmap( result ); | ||
| 1159 | 1156 | } | |
| 1160 | 1157 | ||
| 1161 | 1158 | // Cover fetching runs in another thread. If there is a retrieved cover | |
| … | … | ||
| 1411 | 1411 | // Don't overwrite if it already exists | |
| 1412 | 1412 | if( !QFile::exists( cachedImagePath ) ) | |
| 1413 | 1413 | { | |
| 1414 | QImage img; | ||
| 1415 | if ( isEmbeddedImage( path ) ) | ||
| 1416 | img = loadImageFromTag( path ); | ||
| 1417 | else | ||
| 1418 | img.load( path ); | ||
| 1419 | |||
| 1420 | if( img.isNull() ) | ||
| 1414 | QImage img( path ); | ||
| 1415 | if( img.isNull() ) | ||
| 1421 | 1416 | return QString(); | |
| 1422 | 1417 | ||
| 1423 | 1418 | // resize and save the image | |
| … | … | ||
| 1424 | 1424 | return QString(); | |
| 1425 | 1425 | } | |
| 1426 | 1426 | ||
| 1427 | QImage | ||
| 1428 | SqlAlbum::loadImageFromTag( const QString path ) const | ||
| 1429 | { | ||
| 1430 | #ifdef COMPLEX_TAGLIB_FILENAME | ||
| 1431 | const wchar_t * encodedName = reinterpret_cast<const wchar_t *>(path.utf16()); | ||
| 1432 | #else | ||
| 1433 | QByteArray fileName = QFile::encodeName( path ); | ||
| 1434 | const char * encodedName = fileName.constData(); // valid as long as fileName exists | ||
| 1435 | #endif | ||
| 1436 | |||
| 1437 | TagLib::FileRef fileref; | ||
| 1438 | TagLib::Tag *tag = 0; | ||
| 1439 | fileref = TagLib::FileRef( encodedName, true ); | ||
| 1440 | |||
| 1441 | if( !fileref.isNull() ) | ||
| 1442 | { | ||
| 1443 | tag = fileref.tag(); | ||
| 1444 | if ( tag ) | ||
| 1445 | { | ||
| 1446 | if ( TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File *>( fileref.file() ) ) | ||
| 1447 | { | ||
| 1448 | if ( !file->ID3v2Tag()->frameListMap()["APIC"].isEmpty() ) | ||
| 1449 | { | ||
| 1450 | TagLib::ID3v2::FrameList apicList = file->ID3v2Tag()->frameListMap()["APIC"]; | ||
| 1451 | for ( TagLib::ID3v2::FrameList::ConstIterator it = apicList.begin(), end = apicList.end(); it != end; ++it ) | ||
| 1452 | { | ||
| 1453 | TagLib::ID3v2::AttachedPictureFrame *apicFrame = (TagLib::ID3v2::AttachedPictureFrame *)(*it); | ||
| 1454 | // take first APIC frame which is a FrontCover | ||
| 1455 | if ( apicFrame->type() == TagLib::ID3v2::AttachedPictureFrame::FrontCover) | ||
| 1456 | { | ||
| 1457 | return QImage::fromData((uchar*)(apicFrame->picture().data()), apicFrame->picture().size()); | ||
| 1458 | } | ||
| 1459 | } | ||
| 1460 | // if there was no FrontCover, just take the first picture available | ||
| 1461 | TagLib::ID3v2::AttachedPictureFrame *apicFrame = (TagLib::ID3v2::AttachedPictureFrame *)(apicList[0]); | ||
| 1462 | return QImage::fromData((uchar*)(apicFrame->picture().data()), apicFrame->picture().size()); | ||
| 1463 | } | ||
| 1464 | } | ||
| 1465 | } | ||
| 1466 | } | ||
| 1467 | return QImage::QImage(); | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | 1427 | QString | |
| 1471 | 1428 | SqlAlbum::findImage( int size ) | |
| 1472 | 1429 | { | |
| … | … | ||
| 1671 | 1671 | SqlAlbum::createCapabilityInterface( Meta::Capability::Type type ) | |
| 1672 | 1672 | { | |
| 1673 | 1673 | return ( m_delegate ? m_delegate->createCapabilityInterface( type, this ) : 0 ); | |
| 1674 | } | ||
| 1675 | |||
| 1676 | bool | ||
| 1677 | SqlAlbum::isEmbeddedImage( const QString& path ) | ||
| 1678 | { | ||
| 1679 | //Entries in the images table are currently either image files or audio files with embedded cover-art images. | ||
| 1680 | //This function simply checks the file extension and determines whether it is an audio file or not, using the | ||
| 1681 | //same suffix criteria as in ScanResultProcessor::addTrack for now. | ||
| 1682 | //Future versions could extend the database scheme to include image type information. | ||
| 1683 | //No additional checks are made - we trust that the entry was put into the images table for a reason. | ||
| 1684 | |||
| 1685 | QStringList audioFiletypes; | ||
| 1686 | audioFiletypes << "mp3" << "ogg" << "oga" << "flac" << "wma" << "m4a" << "m4b"; | ||
| 1687 | return audioFiletypes.contains( QFileInfo( path ).suffix().toLower() ); | ||
| 1688 | 1674 | } | |
| 1689 | 1675 | ||
| 1690 | 1676 | //---------------SqlComposer--------------------------------- |
|   | |||
| 292 | 292 | ||
| 293 | 293 | virtual Meta::Capability* createCapabilityInterface( Meta::Capability::Type type ); | |
| 294 | 294 | ||
| 295 | static bool isEmbeddedImage( const QString& path ); | ||
| 296 | |||
| 297 | 295 | //SQL specific methods | |
| 298 | 296 | int id() const { return m_id; } | |
| 299 | 297 | ||
| … | … | ||
| 309 | 309 | void updateImage( const QString path ) const; // Updates the database to ensure the album has the correct path | |
| 310 | 310 | // Finds or creates a magic value in the database which tells Amarok not to auto fetch an image since it has been explicitly unset. | |
| 311 | 311 | int unsetImageId() const; | |
| 312 | QImage loadImageFromTag( const QString path ) const; | ||
| 313 | 312 | ||
| 314 | 313 | private: | |
| 315 | 314 | SqlCollection* m_collection; |
|   | |||
| 43 | 43 | #include <QTextStream> | |
| 44 | 44 | #include <QTime> | |
| 45 | 45 | #include <QTimer> | |
| 46 | #include <qpixmap.h> | ||
| 47 | 46 | ||
| 48 | 47 | //Taglib: | |
| 49 | 48 | #include <apetag.h> | |
| … | … | ||
| 61 | 61 | #include <tlist.h> | |
| 62 | 62 | #include <tstring.h> | |
| 63 | 63 | #include <vorbisfile.h> | |
| 64 | #include <attachedpictureframe.h> | ||
| 65 | #include <tbytevector.h> | ||
| 66 | 64 | ||
| 67 | 65 | #include <audiblefiletyperesolver.h> | |
| 68 | 66 | #include <realmediafiletyperesolver.h> | |
| … | … | ||
| 455 | 455 | ||
| 456 | 456 | else | |
| 457 | 457 | { | |
| 458 | //FIXME: PORT 2.0 | ||
| 459 | // QList<EmbeddedImage> images; | ||
| 458 | 460 | const AttributeHash attributes = readTags( path ); | |
| 459 | 461 | ||
| 460 | 462 | if( !attributes.empty() ) | |
| … | … | ||
| 467 | 467 | ||
| 468 | 468 | if( !covers.contains( cover ) ) | |
| 469 | 469 | covers += cover; | |
| 470 | |||
| 471 | //FIXME: PORT 2.0 | ||
| 472 | // foreach( EmbeddedImage image, images ) | ||
| 473 | // { | ||
| 474 | // AttributeHash attributes; | ||
| 475 | // if( m_batch && !m_rpath.isEmpty() ) | ||
| 476 | // { | ||
| 477 | // QString rpath = path; | ||
| 478 | // rpath.remove( QDir::cleanPath( QDir::currentPath() ) ); | ||
| 479 | // rpath.prepend( QDir::cleanPath( m_rpath + '/' ) ); | ||
| 480 | // attributes["path"] = rpath; | ||
| 481 | // } | ||
| 482 | // else | ||
| 483 | // attributes["path"] = path; | ||
| 484 | // attributes["hash"] = image.hash(); | ||
| 485 | // attributes["description"] = image.description(); | ||
| 486 | // writeElement( "embed", attributes ); | ||
| 487 | // } | ||
| 470 | 488 | } | |
| 471 | 489 | } | |
| 472 | 490 | ||
| … | … | ||
| 621 | 621 | if ( !file->ID3v2Tag()->frameListMap()["TCMP"].isEmpty() ) | |
| 622 | 622 | compilation = TStringToQString( file->ID3v2Tag()->frameListMap()["TCMP"].front()->toString() ).trimmed(); | |
| 623 | 623 | ||
| 624 | if ( !file->ID3v2Tag()->frameListMap()["APIC"].isEmpty() ) | ||
| 625 | attributes["apic"] = QString("true"); | ||
| 624 | //FIXME: Port 2.0 | ||
| 625 | // if( images ) | ||
| 626 | // loadImagesFromTag( *file->ID3v2Tag(), *images ); | ||
| 626 | 627 | } | |
| 627 | 628 | // HACK: charset-detector disabled, so all tags assumed utf-8 | |
| 628 | 629 | // TODO: fix charset-detector to detect encoding with higher accuracy |

