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 255 Lines • ▼ Show 20 Line(s) | 291 | } else if (rating == 1) { | |||
294 | } else { | 295 | } else { | ||
295 | rating = 2; | 296 | rating = 2; | ||
296 | } | 297 | } | ||
297 | } else if (rating >= 1 && rating <= 255) { | 298 | } else if (rating >= 1 && rating <= 255) { | ||
298 | rating = static_cast<int>(0.032 * rating + 2); | 299 | rating = static_cast<int>(0.032 * rating + 2); | ||
299 | } | 300 | } | ||
300 | result->add(Property::Rating, rating); | 301 | result->add(Property::Rating, rating); | ||
301 | } | 302 | } | ||
303 | if (result->inputFlags() & ExtractionResult::ExtractBinaryData) { | ||||
304 | TagLib::ID3v2::FrameList lstID3v2; | ||||
305 | // Attached Front Picture. | ||||
306 | lstID3v2 = Id3Tags->frameListMap()["APIC"]; | ||||
307 | for (const auto& frame : qAsConst(lstID3v2)) | ||||
308 | { | ||||
309 | const auto *frontCoverFrame = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(frame); | ||||
310 | if (frontCoverFrame->type() == frontCoverFrame->FrontCover) { | ||||
311 | result->add(Property::FrontCover, | ||||
312 | QByteArray(frontCoverFrame->picture().data(), frontCoverFrame->picture().size())); | ||||
313 | } | ||||
314 | } | ||||
315 | } | ||||
302 | } | 316 | } | ||
303 | 317 | | |||
304 | void extractMp4Tags(TagLib::MP4::Tag* mp4Tags, ExtractionResult* result) | 318 | void extractMp4Tags(TagLib::MP4::Tag* mp4Tags, ExtractionResult* result) | ||
305 | { | 319 | { | ||
306 | if (mp4Tags->isEmpty()) { | 320 | if (mp4Tags->isEmpty()) { | ||
307 | return; | 321 | return; | ||
308 | } | 322 | } | ||
309 | TagLib::MP4::ItemListMap allTags = mp4Tags->itemListMap(); | 323 | TagLib::MP4::ItemListMap allTags = mp4Tags->itemListMap(); | ||
310 | 324 | | |||
311 | /* | 325 | /* | ||
312 | * There is no standard regarding ratings. Mimic MediaMonkey's behavior | 326 | * There is no standard regarding ratings. Mimic MediaMonkey's behavior | ||
313 | * with a range of 0 to 100 (stored in steps of 10) and make it compatible | 327 | * with a range of 0 to 100 (stored in steps of 10) and make it compatible | ||
314 | * with baloo rating with a range from 0 to 10. | 328 | * with baloo rating with a range from 0 to 10. | ||
315 | */ | 329 | */ | ||
316 | TagLib::MP4::ItemListMap::Iterator itRating = allTags.find("rate"); | 330 | TagLib::MP4::ItemListMap::Iterator itRating = allTags.find("rate"); | ||
317 | if (itRating != allTags.end()) { | 331 | if (itRating != allTags.end()) { | ||
318 | result->add(Property::Rating, itRating->second.toStringList().toString().toInt() / 10); | 332 | result->add(Property::Rating, itRating->second.toStringList().toString().toInt() / 10); | ||
319 | } | 333 | } | ||
334 | if (result->inputFlags() & ExtractionResult::ExtractBinaryData) { | ||||
335 | TagLib::MP4::Item coverArtItem = mp4Tags->item("covr"); | ||||
336 | if (coverArtItem.isValid()) | ||||
337 | { | ||||
338 | TagLib::MP4::CoverArtList coverArtList = coverArtItem.toCoverArtList(); | ||||
339 | TagLib::MP4::CoverArt& frontCover = coverArtList.front(); | ||||
340 | result->add(Property::FrontCover, QByteArray(frontCover.data().data(), frontCover.data().size())); | ||||
341 | } | ||||
342 | } | ||||
320 | } | 343 | } | ||
321 | 344 | | |||
322 | void extractAsfTags(TagLib::ASF::Tag* asfTags, ExtractionResult* result) | 345 | void extractAsfTags(TagLib::ASF::Tag* asfTags, ExtractionResult* result) | ||
323 | { | 346 | { | ||
324 | if (asfTags->isEmpty()) { | 347 | if (asfTags->isEmpty()) { | ||
325 | return; | 348 | return; | ||
326 | } | 349 | } | ||
327 | 350 | | |||
Show All 31 Lines | |||||
359 | * TagLib exports "WM/PUBLISHER" as "LABEL" in the PropertyMap, | 382 | * TagLib exports "WM/PUBLISHER" as "LABEL" in the PropertyMap, | ||
360 | * add it manually to Publisher. | 383 | * add it manually to Publisher. | ||
361 | */ | 384 | */ | ||
362 | lstASF = asfTags->attribute("WM/Publisher"); | 385 | lstASF = asfTags->attribute("WM/Publisher"); | ||
363 | if (!lstASF.isEmpty()) { | 386 | if (!lstASF.isEmpty()) { | ||
364 | const auto attribute = lstASF.front(); | 387 | const auto attribute = lstASF.front(); | ||
365 | result->add(Property::Publisher, TStringToQString(attribute.toString()).trimmed()); | 388 | result->add(Property::Publisher, TStringToQString(attribute.toString()).trimmed()); | ||
366 | } | 389 | } | ||
390 | | ||||
391 | if (result->inputFlags() & ExtractionResult::ExtractBinaryData) { | ||||
392 | TagLib::ASF::AttributeList lstASF = asfTags->attribute("WM/Picture"); | ||||
393 | for (const auto& attribute : qAsConst(lstASF)) { | ||||
394 | TagLib::ASF::Picture picture = attribute.toPicture(); | ||||
395 | if (picture.type() == TagLib::ASF::Picture::FrontCover) { | ||||
396 | TagLib::ByteVector pictureData = picture.picture(); | ||||
397 | result->add(Property::FrontCover, QByteArray(pictureData.data(), pictureData.size())); | ||||
398 | } | ||||
399 | } | ||||
400 | } | ||||
401 | } | ||||
402 | | ||||
403 | void extractApeTags(TagLib::APE::Tag* apeTags, ExtractionResult* result) | ||||
404 | { | ||||
405 | if (apeTags->isEmpty()) { | ||||
406 | return; | ||||
407 | } | ||||
408 | if (result->inputFlags() & ExtractionResult::ExtractBinaryData) { | ||||
409 | TagLib::APE::ItemListMap lstApe = apeTags->itemListMap(); | ||||
410 | TagLib::APE::ItemListMap::ConstIterator itApe; | ||||
411 | | ||||
412 | /* The cover art tag for APEv2 tags starts with the filename as string | ||||
413 | * with zero termination followed by the actual picture data */ | ||||
414 | itApe = lstApe.find("COVER ART (FRONT)"); | ||||
415 | if (itApe != lstApe.end()) { | ||||
416 | TagLib::ByteVector pictureData = (*itApe).second.binaryData(); | ||||
417 | int position = pictureData.find('\0'); | ||||
418 | if (position >= 0) { | ||||
419 | position += 1; | ||||
420 | result->add(Property::FrontCover, | ||||
421 | QByteArray(pictureData.data() + position, pictureData.size() - position)); | ||||
422 | } | ||||
423 | } | ||||
424 | } | ||||
425 | } | ||||
426 | | ||||
427 | void extractCoverFromFlacPicture(TagLib::List<TagLib::FLAC::Picture *> lstPic, ExtractionResult* result) | ||||
428 | { | ||||
429 | if (result->inputFlags() & ExtractionResult::ExtractBinaryData) { | ||||
430 | for (const auto &picture : qAsConst(lstPic)) { | ||||
431 | if (picture->type() == picture->FrontCover) { | ||||
432 | result->add(Property::FrontCover, QByteArray(picture->data().data(), picture->data().size())); | ||||
433 | } | ||||
434 | } | ||||
435 | } | ||||
367 | } | 436 | } | ||
368 | 437 | | |||
369 | } // anonymous namespace | 438 | } // anonymous namespace | ||
370 | 439 | | |||
371 | TagLibExtractor::TagLibExtractor(QObject* parent) | 440 | TagLibExtractor::TagLibExtractor(QObject* parent) | ||
372 | : ExtractorPlugin(parent) | 441 | : ExtractorPlugin(parent) | ||
373 | { | 442 | { | ||
374 | } | 443 | } | ||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Line(s) | 486 | if (file.hasID3v2Tag()) { | |||
418 | extractId3Tags(file.tag(), result); | 487 | extractId3Tags(file.tag(), result); | ||
419 | } | 488 | } | ||
420 | } | 489 | } | ||
421 | } else if (mimeType == QLatin1String("audio/x-musepack")) { | 490 | } else if (mimeType == QLatin1String("audio/x-musepack")) { | ||
422 | TagLib::MPC::File file(&stream, true); | 491 | TagLib::MPC::File file(&stream, true); | ||
423 | if (file.isValid()) { | 492 | if (file.isValid()) { | ||
424 | extractAudioProperties(&file, result); | 493 | extractAudioProperties(&file, result); | ||
425 | readGenericProperties(file.properties(), result); | 494 | readGenericProperties(file.properties(), result); | ||
495 | if (file.hasAPETag()) { | ||||
496 | extractApeTags(file.APETag(), result); | ||||
497 | } | ||||
426 | } | 498 | } | ||
427 | } else if (mimeType == QLatin1String("audio/x-ape")) { | 499 | } else if (mimeType == QLatin1String("audio/x-ape")) { | ||
428 | TagLib::APE::File file(&stream, true); | 500 | TagLib::APE::File file(&stream, true); | ||
429 | if (file.isValid()) { | 501 | if (file.isValid()) { | ||
430 | extractAudioProperties(&file, result); | 502 | extractAudioProperties(&file, result); | ||
431 | readGenericProperties(file.properties(), result); | 503 | readGenericProperties(file.properties(), result); | ||
504 | if (file.hasAPETag()) { | ||||
505 | extractApeTags(file.APETag(), result); | ||||
506 | } | ||||
432 | } | 507 | } | ||
433 | } else if (mimeType == QLatin1String("audio/x-wavpack")) { | 508 | } else if (mimeType == QLatin1String("audio/x-wavpack")) { | ||
434 | TagLib::WavPack::File file(&stream, true); | 509 | TagLib::WavPack::File file(&stream, true); | ||
435 | if (file.isValid()) { | 510 | if (file.isValid()) { | ||
436 | extractAudioProperties(&file, result); | 511 | extractAudioProperties(&file, result); | ||
437 | readGenericProperties(file.properties(), result); | 512 | readGenericProperties(file.properties(), result); | ||
513 | if (file.hasAPETag()) { | ||||
514 | extractApeTags(file.APETag(), result); | ||||
515 | } | ||||
438 | } | 516 | } | ||
439 | } else if (mimeType == QLatin1String("audio/mp4")) { | 517 | } else if (mimeType == QLatin1String("audio/mp4")) { | ||
440 | TagLib::MP4::File file(&stream, true); | 518 | TagLib::MP4::File file(&stream, true); | ||
441 | if (file.isValid()) { | 519 | if (file.isValid()) { | ||
442 | extractAudioProperties(&file, result); | 520 | extractAudioProperties(&file, result); | ||
443 | readGenericProperties(file.properties(), result); | 521 | readGenericProperties(file.properties(), result); | ||
444 | extractMp4Tags(file.tag(), result); | 522 | extractMp4Tags(file.tag(), result); | ||
445 | } | 523 | } | ||
446 | } else if (mimeType == QLatin1String("audio/flac")) { | 524 | } else if (mimeType == QLatin1String("audio/flac")) { | ||
447 | TagLib::FLAC::File file(&stream, TagLib::ID3v2::FrameFactory::instance(), true); | 525 | TagLib::FLAC::File file(&stream, TagLib::ID3v2::FrameFactory::instance(), true); | ||
448 | if (file.isValid()) { | 526 | if (file.isValid()) { | ||
449 | extractAudioProperties(&file, result); | 527 | extractAudioProperties(&file, result); | ||
450 | readGenericProperties(file.properties(), result); | 528 | readGenericProperties(file.properties(), result); | ||
529 | extractCoverFromFlacPicture(file.pictureList(), result); | ||||
451 | } | 530 | } | ||
452 | } else if (mimeType == QLatin1String("audio/ogg") || mimeType == QLatin1String("audio/x-vorbis+ogg")) { | 531 | } else if (mimeType == QLatin1String("audio/ogg") || mimeType == QLatin1String("audio/x-vorbis+ogg")) { | ||
453 | TagLib::Ogg::Vorbis::File file(&stream, true); | 532 | TagLib::Ogg::Vorbis::File file(&stream, true); | ||
454 | if (file.isValid()) { | 533 | if (file.isValid()) { | ||
455 | extractAudioProperties(&file, result); | 534 | extractAudioProperties(&file, result); | ||
456 | readGenericProperties(file.properties(), result); | 535 | readGenericProperties(file.properties(), result); | ||
536 | extractCoverFromFlacPicture(file.tag()->pictureList(), result); | ||||
457 | } | 537 | } | ||
458 | } else if (mimeType == QLatin1String("audio/opus") || mimeType == QLatin1String("audio/x-opus+ogg")) { | 538 | } else if (mimeType == QLatin1String("audio/opus") || mimeType == QLatin1String("audio/x-opus+ogg")) { | ||
459 | TagLib::Ogg::Opus::File file(&stream, true); | 539 | TagLib::Ogg::Opus::File file(&stream, true); | ||
460 | if (file.isValid()) { | 540 | if (file.isValid()) { | ||
461 | extractAudioProperties(&file, result); | 541 | extractAudioProperties(&file, result); | ||
462 | readGenericProperties(file.properties(), result); | 542 | readGenericProperties(file.properties(), result); | ||
543 | extractCoverFromFlacPicture(file.tag()->pictureList(), result); | ||||
463 | } | 544 | } | ||
464 | } else if (mimeType == QLatin1String("audio/speex") || mimeType == QLatin1String("audio/x-speex+ogg")) { | 545 | } else if (mimeType == QLatin1String("audio/speex") || mimeType == QLatin1String("audio/x-speex+ogg")) { | ||
465 | TagLib::Ogg::Speex::File file(&stream, true); | 546 | TagLib::Ogg::Speex::File file(&stream, true); | ||
466 | // Workaround for buggy taglib: | 547 | // Workaround for buggy taglib: | ||
467 | // isValid() returns true for invalid files, but XiphComment* tag() returns a nullptr | 548 | // isValid() returns true for invalid files, but XiphComment* tag() returns a nullptr | ||
468 | if (file.isValid() && file.tag()) { | 549 | if (file.isValid() && file.tag()) { | ||
469 | extractAudioProperties(&file, result); | 550 | extractAudioProperties(&file, result); | ||
470 | readGenericProperties(file.properties(), result); | 551 | readGenericProperties(file.properties(), result); | ||
552 | extractCoverFromFlacPicture(file.tag()->pictureList(), result); | ||||
471 | } | 553 | } | ||
472 | } else if (mimeType == QLatin1String("audio/x-ms-wma")) { | 554 | } else if (mimeType == QLatin1String("audio/x-ms-wma")) { | ||
473 | TagLib::ASF::File file(&stream, true); | 555 | TagLib::ASF::File file(&stream, true); | ||
474 | if (file.isValid()) { | 556 | if (file.isValid()) { | ||
475 | extractAudioProperties(&file, result); | 557 | extractAudioProperties(&file, result); | ||
476 | readGenericProperties(file.properties(), result); | 558 | readGenericProperties(file.properties(), result); | ||
477 | extractAsfTags(file.tag(), result); | 559 | extractAsfTags(file.tag(), result); | ||
478 | } | 560 | } | ||
Show All 33 Lines |