예제 #1
0
void SoundSourceProxy::loadTrackMetadataAndCoverArt(
        bool withCoverArt,
        bool reloadFromFile) const {
    DEBUG_ASSERT(m_pTrack);

    if (!m_pSoundSource) {
        // Silently ignore requests for unsupported files
        qDebug() << "Unable to parse file tags without a SoundSource"
                 << getUrl().toString();
        return;
    }

    // Use the existing trackMetadata as default values. Otherwise
    // existing values in the library will be overwritten with
    // empty values if the corresponding file tags are missing.
    // Depending on the file type some kind of tags might even
    // not be supported at all and those would get lost!
    bool parsedFromFile = false;
    mixxx::TrackMetadata trackMetadata;
    m_pTrack->getTrackMetadata(&trackMetadata, &parsedFromFile);
    if (parsedFromFile && !reloadFromFile) {
        qDebug() << "Skip parsing of track metadata from file"
                 << getUrl().toString();
        return; // do not reload from file
    }

    // If parsing of the cover art image should be omitted the
    // 2nd output parameter must be set to nullptr. Cover art
    // is not reloaded from file once the metadata has been parsed!
    CoverInfoRelative coverInfoRelative;
    QImage coverImg;
    DEBUG_ASSERT(coverImg.isNull());
    QImage* pCoverImg = (withCoverArt && !parsedFromFile) ? &coverImg : nullptr;
    bool parsedCoverArt = false;

    // Parse the tags stored in the audio file.
    if (m_pSoundSource &&
            (m_pSoundSource->parseTrackMetadataAndCoverArt(&trackMetadata, pCoverImg) == OK)) {
        parsedFromFile = true;
        if (!coverImg.isNull()) {
            // Cover image has been parsed from the file
            // TODO() here we may introduce a duplicate hash code
            coverInfoRelative.hash = CoverArtUtils::calculateHash(coverImg);
            coverInfoRelative.coverLocation = QString();
            coverInfoRelative.type = CoverInfo::METADATA;
            coverInfoRelative.source = CoverInfo::GUESSED;
            parsedCoverArt = true;
        }
    } else {
        qWarning() << "Failed to parse track metadata from file"
                   << getUrl().toString();
        if (parsedFromFile) {
            // Don't overwrite any existing metadata that once has
            // been parsed successfully from file.
            return;
        }
    }

    // If Artist or title fields are blank try to parse them
    // from the file name.
    // TODO(rryan): Should we re-visit this decision?
    if (trackMetadata.getArtist().isEmpty() || trackMetadata.getTitle().isEmpty()) {
        parseMetadataFromFileName(&trackMetadata, m_pTrack->getFileInfo().fileName());
    }

    // Dump the trackMetadata extracted from the file back into the track.
    m_pTrack->setTrackMetadata(trackMetadata, parsedFromFile);
    if (parsedCoverArt) {
        m_pTrack->setCoverInfo(coverInfoRelative);
    }
}
예제 #2
0
void SoundSourceProxy::updateTrackFromSource(
        ImportTrackMetadataMode importTrackMetadataMode) const {
    DEBUG_ASSERT(m_pTrack);

    if (getUrl().isEmpty()) {
        // Silently skip tracks without a corresponding file
        return; // abort
    }
    if (!m_pSoundSource) {
        kLogger.warning()
                << "Unable to update track from unsupported file type"
                << getUrl().toString();
        return; // abort
    }

    // The SoundSource provides the actual type of the corresponding file
    m_pTrack->setType(m_pSoundSource->getType());

    // Use the existing track metadata as default values. Otherwise
    // existing values in the library would be overwritten with empty
    // values if the corresponding file tags are missing. Depending
    // on the file type some kind of tags might even not be supported
    // at all and this information would get lost entirely otherwise!
    mixxx::TrackMetadata trackMetadata;
    bool metadataSynchronized = false;
    m_pTrack->getTrackMetadata(&trackMetadata, &metadataSynchronized);
    // If the file tags have already been parsed at least once, the
    // existing track metadata should not be updated implicitly, i.e.
    // if the user did not explicitly choose to (re-)import metadata
    // explicitly from this file.
    if (metadataSynchronized &&
        (importTrackMetadataMode == ImportTrackMetadataMode::Once)) {
        if (kLogger.debugEnabled()) {
            kLogger.debug()
                    << "Skip importing of track metadata and embedded cover art from file"
                    << getUrl().toString();
        }
        return; // abort
    }

    // Embedded cover art is imported together with the track's metadata.
    // But only if the user has not selected external cover art for this
    // track!
    QImage coverImg;
    DEBUG_ASSERT(coverImg.isNull());
    QImage* pCoverImg; // pointer is also used as a flag
    const CoverInfoRelative coverInfoOld = m_pTrack->getCoverInfo();
    // Only re-import cover art if it is save to update, e.g. never
    // discard a users's custom choice! We are using a whitelisting
    // filter here that explicitly checks all valid preconditions
    // when it is permissible to update/replace existing cover art.
    if (((coverInfoOld.source == CoverInfo::UNKNOWN) || (coverInfoOld.source == CoverInfo::GUESSED)) &&
            ((coverInfoOld.type == CoverInfo::NONE) || (coverInfoOld.type == CoverInfo::METADATA))) {
        // Should import and update embedded cover art
        pCoverImg = &coverImg;
    } else {
        if (kLogger.debugEnabled()) {
            kLogger.debug()
                    << "Skip importing of embedded cover art from file"
                    << getUrl().toString();
        }
        // Skip import of embedded cover art
        pCoverImg = nullptr;
    }

    // Parse the tags stored in the audio file
    auto metadataImported =
            m_pSoundSource->importTrackMetadataAndCoverImage(
                    &trackMetadata, pCoverImg);
    if (metadataImported.first == mixxx::MetadataSource::ImportResult::Failed) {
        kLogger.warning()
                << "Failed to import track metadata"
                << (pCoverImg ? "and embedded cover art" : "")
                << "from file"
                << getUrl().toString();
        // Continue for now, but the abort may follow shortly if the
        // track already has metadata (see below)
    }
    if (metadataSynchronized) {
        // Metadata has been synchronized successfully at least
        // once in the past. Only overwrite this information if
        // new data has actually been imported, otherwise abort
        // and preserve the existing data!
        if (metadataImported.first != mixxx::MetadataSource::ImportResult::Succeeded) {
            return; // abort
        }
        if (kLogger.debugEnabled()) {
            kLogger.debug()
                    << "Updating track metadata"
                    << (pCoverImg ? "and embedded cover art" : "")
                    << "from file"
                    << getUrl().toString();
        }
    } else {
        DEBUG_ASSERT(pCoverImg);
        if (kLogger.debugEnabled()) {
            kLogger.debug()
                    << "Initializing track metadata and embedded cover art from file"
                    << getUrl().toString();
        }
    }

    // Fallback: If artist or title fields are blank then try to populate
    // them from the file name. This might happen if tags are unavailable,
    // unreadable, or partially/completely missing.
    if (trackMetadata.getTrackInfo().getArtist().isEmpty() ||
            trackMetadata.getTrackInfo().getTitle().isEmpty()) {
        kLogger.info()
                << "Adding missing artist/title from file name"
                << getUrl().toString();
        if (parseMetadataFromFileName(&trackMetadata, m_pTrack->getFileInfo().fileName()) &&
                metadataImported.second.isNull()) {
            // Since this is also some kind of metadata import, we mark the
            // track's metadata as synchronized with the time stamp of the file.
            metadataImported.second = m_pTrack->getFileInfo().lastModified();
        }
    }

    m_pTrack->setTrackMetadata(trackMetadata, metadataImported.second);

    if (pCoverImg) {
        // If the pointer is not null then the cover art should be guessed
        // from the embedded metadata
        CoverInfoRelative coverInfoNew;
        DEBUG_ASSERT(coverInfoNew.coverLocation.isNull());
        if (pCoverImg->isNull()) {
            // Cover art will be cleared
            DEBUG_ASSERT(coverInfoNew.source == CoverInfo::UNKNOWN);
            DEBUG_ASSERT(coverInfoNew.type == CoverInfo::NONE);
            if (kLogger.debugEnabled()) {
                kLogger.debug()
                        << "No embedded cover art found in file"
                        << getUrl().toString();
            }
        } else {
            coverInfoNew.source = CoverInfo::GUESSED;
            coverInfoNew.type = CoverInfo::METADATA;
            // TODO(XXX) here we may introduce a duplicate hash code
            coverInfoNew.hash = CoverArtUtils::calculateHash(coverImg);
            if (kLogger.debugEnabled()) {
                kLogger.debug()
                        << "Embedded cover art found in file"
                        << getUrl().toString();
            }
        }
        m_pTrack->setCoverInfo(coverInfoNew);
    }
}