Changeset View
Changeset View
Standalone View
Standalone View
src/extractors/taglibextractor.cpp
Show All 22 Lines | |||||
23 | 23 | | |||
24 | // Taglib includes | 24 | // Taglib includes | ||
25 | #include <taglib.h> | 25 | #include <taglib.h> | ||
26 | #include <tag.h> | 26 | #include <tag.h> | ||
27 | #include <tfilestream.h> | 27 | #include <tfilestream.h> | ||
28 | #include <tpropertymap.h> | 28 | #include <tpropertymap.h> | ||
29 | #include <aifffile.h> | 29 | #include <aifffile.h> | ||
30 | #include <apefile.h> | 30 | #include <apefile.h> | ||
31 | #include <apetag.h> | ||||
31 | #include <asffile.h> | 32 | #include <asffile.h> | ||
32 | #include <flacfile.h> | 33 | #include <flacfile.h> | ||
33 | #include <mp4file.h> | 34 | #include <mp4file.h> | ||
34 | #include <mpcfile.h> | 35 | #include <mpcfile.h> | ||
35 | #include <mpegfile.h> | 36 | #include <mpegfile.h> | ||
36 | #include <oggfile.h> | 37 | #include <oggfile.h> | ||
37 | #include <opusfile.h> | 38 | #include <opusfile.h> | ||
38 | #include <speexfile.h> | 39 | #include <speexfile.h> | ||
▲ Show 20 Lines • Show All 263 Lines • ▼ Show 20 Line(s) | 299 | } else if (rating == 1) { | |||
302 | } else { | 303 | } else { | ||
303 | rating = 2; | 304 | rating = 2; | ||
304 | } | 305 | } | ||
305 | } else if (rating >= 1 && rating <= 255) { | 306 | } else if (rating >= 1 && rating <= 255) { | ||
306 | rating = static_cast<int>(0.032 * rating + 2); | 307 | rating = static_cast<int>(0.032 * rating + 2); | ||
307 | } | 308 | } | ||
308 | result->add(Property::Rating, rating); | 309 | result->add(Property::Rating, rating); | ||
309 | } | 310 | } | ||
311 | if (result->inputFlags() & ExtractionResult::ExtractBinaryData) { | ||||
312 | TagLib::ID3v2::FrameList lstID3v2; | ||||
313 | // Attached Front Picture. | ||||
314 | lstID3v2 = Id3Tags->frameListMap()["APIC"]; | ||||
315 | for (const auto& frame : qAsConst(lstID3v2)) | ||||
316 | { | ||||
317 | const auto *frontCoverFrame = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(frame); | ||||
318 | if (frontCoverFrame->type() == frontCoverFrame->FrontCover) { | ||||
319 | result->add(Property::FrontCover, | ||||
320 | QByteArray(frontCoverFrame->picture().data(), frontCoverFrame->picture().size())); | ||||
321 | } | ||||
322 | } | ||||
323 | } | ||||
310 | } | 324 | } | ||
311 | 325 | | |||
312 | void extractMp4Tags(TagLib::MP4::Tag* mp4Tags, ExtractionResult* result) | 326 | void extractMp4Tags(TagLib::MP4::Tag* mp4Tags, ExtractionResult* result) | ||
313 | { | 327 | { | ||
314 | if (mp4Tags->isEmpty()) { | 328 | if (mp4Tags->isEmpty()) { | ||
315 | return; | 329 | return; | ||
316 | } | 330 | } | ||
317 | TagLib::MP4::ItemListMap allTags = mp4Tags->itemListMap(); | 331 | TagLib::MP4::ItemListMap allTags = mp4Tags->itemListMap(); | ||
318 | 332 | | |||
319 | /* | 333 | /* | ||
320 | * There is no standard regarding ratings. Mimic MediaMonkey's behavior | 334 | * There is no standard regarding ratings. Mimic MediaMonkey's behavior | ||
321 | * with a range of 0 to 100 (stored in steps of 10) and make it compatible | 335 | * with a range of 0 to 100 (stored in steps of 10) and make it compatible | ||
322 | * with baloo rating with a range from 0 to 10. | 336 | * with baloo rating with a range from 0 to 10. | ||
323 | */ | 337 | */ | ||
324 | TagLib::MP4::ItemListMap::Iterator itRating = allTags.find("rate"); | 338 | TagLib::MP4::ItemListMap::Iterator itRating = allTags.find("rate"); | ||
325 | if (itRating != allTags.end()) { | 339 | if (itRating != allTags.end()) { | ||
326 | result->add(Property::Rating, itRating->second.toStringList().toString().toInt() / 10); | 340 | result->add(Property::Rating, itRating->second.toStringList().toString().toInt() / 10); | ||
327 | } | 341 | } | ||
342 | if (result->inputFlags() & ExtractionResult::ExtractBinaryData) { | ||||
343 | TagLib::MP4::Item coverArtItem = mp4Tags->item("covr"); | ||||
344 | if (coverArtItem.isValid()) | ||||
345 | { | ||||
346 | TagLib::MP4::CoverArtList coverArtList = coverArtItem.toCoverArtList(); | ||||
347 | TagLib::MP4::CoverArt& frontCover = coverArtList.front(); | ||||
348 | result->add(Property::FrontCover, QByteArray(frontCover.data().data(), frontCover.data().size())); | ||||
349 | } | ||||
350 | } | ||||
328 | } | 351 | } | ||
329 | 352 | | |||
330 | void extractAsfTags(TagLib::ASF::Tag* asfTags, ExtractionResult* result) | 353 | void extractAsfTags(TagLib::ASF::Tag* asfTags, ExtractionResult* result) | ||
331 | { | 354 | { | ||
332 | if (asfTags->isEmpty()) { | 355 | if (asfTags->isEmpty()) { | ||
333 | return; | 356 | return; | ||
334 | } | 357 | } | ||
335 | 358 | | |||
Show All 31 Lines | |||||
367 | * TagLib exports "WM/PUBLISHER" as "LABEL" in the PropertyMap, | 390 | * TagLib exports "WM/PUBLISHER" as "LABEL" in the PropertyMap, | ||
368 | * add it manually to Publisher. | 391 | * add it manually to Publisher. | ||
369 | */ | 392 | */ | ||
370 | lstASF = asfTags->attribute("WM/Publisher"); | 393 | lstASF = asfTags->attribute("WM/Publisher"); | ||
371 | if (!lstASF.isEmpty()) { | 394 | if (!lstASF.isEmpty()) { | ||
372 | const auto attribute = lstASF.front(); | 395 | const auto attribute = lstASF.front(); | ||
373 | result->add(Property::Publisher, TStringToQString(attribute.toString()).trimmed()); | 396 | result->add(Property::Publisher, TStringToQString(attribute.toString()).trimmed()); | ||
374 | } | 397 | } | ||
398 | | ||||
399 | if (result->inputFlags() & ExtractionResult::ExtractBinaryData) { | ||||
400 | TagLib::ASF::AttributeList lstASF = asfTags->attribute("WM/Picture"); | ||||
401 | for (const auto& attribute : qAsConst(lstASF)) { | ||||
402 | TagLib::ASF::Picture picture = attribute.toPicture(); | ||||
403 | if (picture.type() == TagLib::ASF::Picture::FrontCover) { | ||||
404 | TagLib::ByteVector pictureData = picture.picture(); | ||||
405 | result->add(Property::FrontCover, QByteArray(pictureData.data(), pictureData.size())); | ||||
406 | } | ||||
407 | } | ||||
408 | } | ||||
409 | } | ||||
410 | | ||||
411 | void extractApeTags(TagLib::APE::Tag* apeTags, ExtractionResult* result) | ||||
412 | { | ||||
413 | if (apeTags->isEmpty()) { | ||||
414 | return; | ||||
415 | } | ||||
416 | if (result->inputFlags() & ExtractionResult::ExtractBinaryData) { | ||||
417 | TagLib::APE::ItemListMap lstApe = apeTags->itemListMap(); | ||||
418 | TagLib::APE::ItemListMap::ConstIterator itApe; | ||||
419 | | ||||
420 | /* The cover art tag for APEv2 tags starts with the filename as string | ||||
421 | * with zero termination followed by the actual picture data */ | ||||
422 | itApe = lstApe.find("COVER ART (FRONT)"); | ||||
423 | if (itApe != lstApe.end()) { | ||||
424 | TagLib::ByteVector pictureData = (*itApe).second.binaryData(); | ||||
425 | int position = pictureData.find('\0'); | ||||
426 | if (position >= 0) { | ||||
427 | position += 1; | ||||
428 | result->add(Property::FrontCover, | ||||
429 | QByteArray(pictureData.data() + position, pictureData.size() - position)); | ||||
430 | } | ||||
431 | } | ||||
432 | } | ||||
433 | } | ||||
434 | | ||||
435 | void extractCoverFromFlacPicture(TagLib::List<TagLib::FLAC::Picture *> lstPic, ExtractionResult* result) | ||||
436 | { | ||||
437 | if (result->inputFlags() & ExtractionResult::ExtractBinaryData) { | ||||
438 | for (const auto &picture : qAsConst(lstPic)) { | ||||
439 | if (picture->type() == picture->FrontCover) { | ||||
440 | result->add(Property::FrontCover, QByteArray(picture->data().data(), picture->data().size())); | ||||
441 | } | ||||
442 | } | ||||
443 | } | ||||
375 | } | 444 | } | ||
376 | 445 | | |||
377 | void TagLibExtractor::extract(ExtractionResult* result) | 446 | void TagLibExtractor::extract(ExtractionResult* result) | ||
378 | { | 447 | { | ||
379 | const QString fileUrl = result->inputUrl(); | 448 | const QString fileUrl = result->inputUrl(); | ||
380 | const QString mimeType = getSupportedMimeType(result->inputMimetype()); | 449 | const QString mimeType = getSupportedMimeType(result->inputMimetype()); | ||
381 | 450 | | |||
382 | // Open the file readonly. Important if we're sandboxed. | 451 | // Open the file readonly. Important if we're sandboxed. | ||
Show All 31 Lines | 482 | if (file.hasID3v2Tag()) { | |||
414 | extractId3Tags(file.tag(), result); | 483 | extractId3Tags(file.tag(), result); | ||
415 | } | 484 | } | ||
416 | } | 485 | } | ||
417 | } else if (mimeType == QLatin1String("audio/x-musepack")) { | 486 | } else if (mimeType == QLatin1String("audio/x-musepack")) { | ||
418 | TagLib::MPC::File file(&stream, true); | 487 | TagLib::MPC::File file(&stream, true); | ||
419 | if (file.isValid()) { | 488 | if (file.isValid()) { | ||
420 | extractAudioProperties(&file, result); | 489 | extractAudioProperties(&file, result); | ||
421 | readGenericProperties(file.properties(), result); | 490 | readGenericProperties(file.properties(), result); | ||
491 | if (file.hasAPETag()) { | ||||
492 | extractApeTags(file.APETag(), result); | ||||
493 | } | ||||
422 | } | 494 | } | ||
423 | } else if (mimeType == QLatin1String("audio/x-ape")) { | 495 | } else if (mimeType == QLatin1String("audio/x-ape")) { | ||
424 | TagLib::APE::File file(&stream, true); | 496 | TagLib::APE::File file(&stream, true); | ||
425 | if (file.isValid()) { | 497 | if (file.isValid()) { | ||
426 | extractAudioProperties(&file, result); | 498 | extractAudioProperties(&file, result); | ||
427 | readGenericProperties(file.properties(), result); | 499 | readGenericProperties(file.properties(), result); | ||
500 | if (file.hasAPETag()) { | ||||
501 | extractApeTags(file.APETag(), result); | ||||
502 | } | ||||
428 | } | 503 | } | ||
429 | } else if (mimeType == QLatin1String("audio/x-wavpack")) { | 504 | } else if (mimeType == QLatin1String("audio/x-wavpack")) { | ||
430 | TagLib::WavPack::File file(&stream, true); | 505 | TagLib::WavPack::File file(&stream, true); | ||
431 | if (file.isValid()) { | 506 | if (file.isValid()) { | ||
432 | extractAudioProperties(&file, result); | 507 | extractAudioProperties(&file, result); | ||
433 | readGenericProperties(file.properties(), result); | 508 | readGenericProperties(file.properties(), result); | ||
509 | if (file.hasAPETag()) { | ||||
510 | extractApeTags(file.APETag(), result); | ||||
511 | } | ||||
434 | } | 512 | } | ||
435 | } else if (mimeType == QLatin1String("audio/mp4")) { | 513 | } else if (mimeType == QLatin1String("audio/mp4")) { | ||
436 | TagLib::MP4::File file(&stream, true); | 514 | TagLib::MP4::File file(&stream, true); | ||
437 | if (file.isValid()) { | 515 | if (file.isValid()) { | ||
438 | extractAudioProperties(&file, result); | 516 | extractAudioProperties(&file, result); | ||
439 | readGenericProperties(file.properties(), result); | 517 | readGenericProperties(file.properties(), result); | ||
440 | extractMp4Tags(file.tag(), result); | 518 | extractMp4Tags(file.tag(), result); | ||
441 | } | 519 | } | ||
442 | } else if (mimeType == QLatin1String("audio/flac")) { | 520 | } else if (mimeType == QLatin1String("audio/flac")) { | ||
443 | TagLib::FLAC::File file(&stream, TagLib::ID3v2::FrameFactory::instance(), true); | 521 | TagLib::FLAC::File file(&stream, TagLib::ID3v2::FrameFactory::instance(), true); | ||
444 | if (file.isValid()) { | 522 | if (file.isValid()) { | ||
445 | extractAudioProperties(&file, result); | 523 | extractAudioProperties(&file, result); | ||
446 | readGenericProperties(file.properties(), result); | 524 | readGenericProperties(file.properties(), result); | ||
525 | extractCoverFromFlacPicture(file.pictureList(), result); | ||||
447 | } | 526 | } | ||
448 | } else if (mimeType == QLatin1String("audio/ogg") || mimeType == QLatin1String("audio/x-vorbis+ogg")) { | 527 | } else if (mimeType == QLatin1String("audio/ogg") || mimeType == QLatin1String("audio/x-vorbis+ogg")) { | ||
449 | TagLib::Ogg::Vorbis::File file(&stream, true); | 528 | TagLib::Ogg::Vorbis::File file(&stream, true); | ||
450 | if (file.isValid()) { | 529 | if (file.isValid()) { | ||
451 | extractAudioProperties(&file, result); | 530 | extractAudioProperties(&file, result); | ||
452 | readGenericProperties(file.properties(), result); | 531 | readGenericProperties(file.properties(), result); | ||
532 | extractCoverFromFlacPicture(file.tag()->pictureList(), result); | ||||
453 | } | 533 | } | ||
454 | } else if (mimeType == QLatin1String("audio/opus") || mimeType == QLatin1String("audio/x-opus+ogg")) { | 534 | } else if (mimeType == QLatin1String("audio/opus") || mimeType == QLatin1String("audio/x-opus+ogg")) { | ||
455 | TagLib::Ogg::Opus::File file(&stream, true); | 535 | TagLib::Ogg::Opus::File file(&stream, true); | ||
456 | if (file.isValid()) { | 536 | if (file.isValid()) { | ||
457 | extractAudioProperties(&file, result); | 537 | extractAudioProperties(&file, result); | ||
458 | readGenericProperties(file.properties(), result); | 538 | readGenericProperties(file.properties(), result); | ||
539 | extractCoverFromFlacPicture(file.tag()->pictureList(), result); | ||||
459 | } | 540 | } | ||
460 | } else if (mimeType == QLatin1String("audio/speex") || mimeType == QLatin1String("audio/x-speex+ogg")) { | 541 | } else if (mimeType == QLatin1String("audio/speex") || mimeType == QLatin1String("audio/x-speex+ogg")) { | ||
461 | TagLib::Ogg::Speex::File file(&stream, true); | 542 | TagLib::Ogg::Speex::File file(&stream, true); | ||
462 | // Workaround for buggy taglib: | 543 | // Workaround for buggy taglib: | ||
463 | // isValid() returns true for invalid files, but XiphComment* tag() returns a nullptr | 544 | // isValid() returns true for invalid files, but XiphComment* tag() returns a nullptr | ||
464 | if (file.isValid() && file.tag()) { | 545 | if (file.isValid() && file.tag()) { | ||
465 | extractAudioProperties(&file, result); | 546 | extractAudioProperties(&file, result); | ||
466 | readGenericProperties(file.properties(), result); | 547 | readGenericProperties(file.properties(), result); | ||
548 | extractCoverFromFlacPicture(file.tag()->pictureList(), result); | ||||
467 | } | 549 | } | ||
468 | } else if (mimeType == QLatin1String("audio/x-ms-wma")) { | 550 | } else if (mimeType == QLatin1String("audio/x-ms-wma")) { | ||
469 | TagLib::ASF::File file(&stream, true); | 551 | TagLib::ASF::File file(&stream, true); | ||
470 | if (file.isValid()) { | 552 | if (file.isValid()) { | ||
471 | extractAudioProperties(&file, result); | 553 | extractAudioProperties(&file, result); | ||
472 | readGenericProperties(file.properties(), result); | 554 | readGenericProperties(file.properties(), result); | ||
473 | extractAsfTags(file.tag(), result); | 555 | extractAsfTags(file.tag(), result); | ||
474 | } | 556 | } | ||
Show All 33 Lines |