Source

libtaginfo / libtaginfo / mp4info.cc

Full commit
/*
 * Copyright (C) 2008-2013 J.Rios <anonbeat@gmail.com>
 * Copyright (C) 2013 Jörn Magens
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This Program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file LICENSE.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth 
 * Floor, Boston, MA  02110-1301  USA
 * https://www.gnu.org/licenses/lgpl-2.1.txt
 *
 * Author:
 * 	Jörn Magens <shuerhaaken@googlemail.com>
 * 	Matias De lellis <mati86dl@gmail.com>
 * 	Pavel Vasin <rat4vier@gmail.com>
 */


#include "taginfo.h"
#include "taginfo_internal.h"
#include "taginfo_mp4tags.h"


#include <string>
#include <mp4file.h>
#include <mp4coverart.h>

using namespace TagInfo;


// MP4



Mp4Info::Mp4Info(const String &filename) : Info(filename) {
    if(file_name.isEmpty()) {
        valid = false;
        printf("File name empty!\n");
    } else {
        taglib_file = new TagLib::MP4::File(file_name.toCString(false), true, TagLib::AudioProperties::Fast);
    }
    if(taglib_file) {
        mp4_tag = ((TagLib::MP4::File *) taglib_file)->tag();
        if(!mp4_tag || mp4_tag->isEmpty()) {
            if(!mp4_tag)
                printf("Mp4tag null for %s\n", filename.toCString(false));
            //printf("Use fallback! %s\n", filename.c_str());
            if(!taglib_tag)
                taglib_tag = taglib_file->tag();
            if(!taglib_tag) {
                taglib_fileref = new TagLib::FileRef(filename.toCString(false),
                                                     true, TagLib::AudioProperties::Fast);
                taglib_tag = taglib_fileref->tag();
            }
            if(!taglib_tag) {
                printf("Cant get tag object from '%s'\n", file_name.toCString(false));
                valid = false;
            }
        }
    }
    else {
        printf("2Cant get tag from '%s'\n", file_name.toCString(false));
        mp4_tag = NULL;
        valid = false;
    }
}


Mp4Info::~Mp4Info() {
}


bool Mp4Info::load(void) {
    if(Info::load()) {
        if(mp4_tag && !mp4_tag->isEmpty()) {
            if(!taglib_tag)
                load_base_tags((TagLib::Tag *)mp4_tag);
            if(mp4_tag->itemListMap().contains("aART")) {
                album_artist = mp4_tag->itemListMap()["aART"].toStringList().front();
            }
            if(mp4_tag->itemListMap().contains("trkn") ) { //&& !mp4_tag->itemListMap()["trkn"].isValid()
                track_number = mp4_tag->itemListMap()["trkn"].toIntPair().first;
                track_count  = mp4_tag->itemListMap()["trkn"].toIntPair().second;
            }
            if(mp4_tag->itemListMap().contains("\xA9wrt")) {
                composer = mp4_tag->itemListMap()["\xa9wrt"].toStringList().front();
            }
            if(mp4_tag->itemListMap().contains("\xa9prf")) {
                original_artist = mp4_tag->itemListMap()["\xa9prf"].toStringList().front();
            }
            if(mp4_tag->itemListMap().contains("\xa9" "cpy")) {
                copyright = mp4_tag->itemListMap()["\xa9" "cpy"].toStringList().front();
            }
            if(mp4_tag->itemListMap().contains("\xa9too")) {
                encoder = mp4_tag->itemListMap()["\xa9too"].toStringList().front();
            }
            if(mp4_tag->itemListMap().contains("\xa9src")) {
                homepage = mp4_tag->itemListMap()["\xa9src"].toStringList().front();
            }
            if(mp4_tag->itemListMap().contains("disk")) {
                volume_number = mp4_tag->itemListMap()["disk"].toIntPair().first;
                volume_count = mp4_tag->itemListMap()["disk"].toIntPair().second;
            }
            if(mp4_tag->itemListMap().contains("cpil")) {
                is_compilation = mp4_tag->itemListMap()["cpil"].toBool();
            }
            // Rating
            if(mp4_tag->itemListMap().contains("----:com.apple.iTunes:RATING")) {
                long rat = 0;
                rat = atol(mp4_tag->itemListMap()["----:com.apple.iTunes:RATING"].toStringList().front().toCString(true));
                if(rat) {
                    if(rat > 5) {
                        rating = popularity_to_rating(rat);
                    }
                    else {
                        rating = rat;
                    }
                }
            }
            if(mp4_tag->itemListMap().contains("----:com.apple.iTunes:PLAY_COUNTER")) {
                long PlayCount = 0;
                PlayCount = atol(mp4_tag->itemListMap()["----:com.apple.iTunes:PLAY_COUNTER"].toStringList().front().toCString(true));
                playcount = PlayCount;
            }
            // Labels
            if(track_labels.size() == 0) {
                if(mp4_tag->itemListMap().contains("----:com.apple.iTunes:TRACK_LABELS")) {
                    track_labels_string = mp4_tag->itemListMap()["----:com.apple.iTunes:TRACK_LABELS"].toStringList().front();
                    track_labels = split(track_labels_string, "|");
                }
            }
            if(artist_labels.size() == 0) {
                if(mp4_tag->itemListMap().contains("----:com.apple.iTunes:ARTIST_LABELS")) {
                    artist_labels_string = mp4_tag->itemListMap()["----:com.apple.iTunes:ARTIST_LABELS"].toStringList().front();
                    artist_labels = split(artist_labels_string, "|");
                }
            }
            if(album_labels.size() == 0) {
                if(mp4_tag->itemListMap().contains("----:com.apple.iTunes:ALBUM_LABELS")) {
                    album_labels_string = mp4_tag->itemListMap()["----:com.apple.iTunes:ALBUM_LABELS"].toStringList().front();
                    album_labels = split(album_labels_string, "|");
                }
            }
            if(mp4_tag->itemListMap().contains("covr")) {
                TagLib::MP4::CoverArtList covers = mp4_tag->itemListMap()[ "covr" ].toCoverArtList();
                has_image = (covers.size() > 0);
            }
            return true;
        }
    }
    return false;
}


void mp4_check_label_frame(TagLib::MP4::Tag * mp4tag, const char * description, const String &value) {
    if(mp4tag->itemListMap().contains(description)) {
            if(!value.isEmpty()) {
            mp4tag->itemListMap()[ description ] = TagLib::MP4::Item(TagLib::StringList(value));
        }
        else {
            mp4tag->itemListMap().erase(description);
        }
    }
    else {
            if(!value.isEmpty()) {
            mp4tag->itemListMap().insert(description, TagLib::MP4::Item(TagLib::StringList(value)));
        }
    }
}


bool Mp4Info::save(void) {
    if(mp4_tag) {
        if(changedflag) {
            if(changedflag & CHANGED_DATA_ALBUMARTIST)
                mp4_tag->itemListMap()["aART"] = TagLib::StringList(album_artist);
            if(changedflag & CHANGED_COMPOSER_TAG)
                mp4_tag->itemListMap()["\xA9wrt"] = TagLib::StringList(composer);
            if(changedflag & CHANGED_ORIGINALARTIST_TAG)
                mp4_tag->itemListMap()["\xa9prf"] = TagLib::StringList(original_artist);
            if(changedflag & CHANGED_TRACK_NUMBER) {
                if(!(changedflag & CHANGED_TRACK_COUNT)) {
                    // prevent overwrite in save_base_tags()
                    changedflag &= ~(CHANGED_TRACK_NUMBER);
                    mp4_tag->setTrack(track_number);
                }
                else {
                    // prevent overwrite in save_base_tags()
                    changedflag &= ~(CHANGED_TRACK_NUMBER);
                    changedflag &= ~(CHANGED_TRACK_COUNT);
                    mp4_tag->itemListMap()["trkn"] = TagLib::MP4::Item(track_number, track_count);
                }
            }
            if(changedflag & CHANGED_TRACK_COUNT) {
                // prevent overwrite in save_base_tags()
                changedflag &= ~(CHANGED_TRACK_NUMBER);
                changedflag &= ~(CHANGED_TRACK_COUNT);
                mp4_tag->itemListMap()["trkn"] = TagLib::MP4::Item(track_number, track_count);
            }
            if(changedflag & CHANGED_DATA_VOL_NUM) {
                mp4_tag->itemListMap()["disk"] = TagLib::MP4::Item(volume_number, volume_count);
            }
            if(changedflag & CHANGED_IS_COMPILATION_TAG)
                mp4_tag->itemListMap()["cpil"] = TagLib::MP4::Item(is_compilation);
            if(changedflag & CHANGED_DATA_RATING) {
                mp4_tag->itemListMap()["----:com.apple.iTunes:RATING" ] =
                    TagLib::MP4::Item(format("%u", rating_to_popularity(rating)));
                
            }
            if(changedflag & CHANGED_DATA_PLAYCOUNT) {
                mp4_tag->itemListMap()[ "----:com.apple.iTunes:PLAY_COUNTER" ] =
                    TagLib::MP4::Item(format("%u", playcount));
            }
            // The Labels
            if(changedflag & CHANGED_ARTIST_LABELS)
                mp4_check_label_frame(mp4_tag, "----:com.apple.iTunes:ARTIST_LABELS", artist_labels_string);
            if(changedflag & CHANGED_ALBUM_LABELS)
                mp4_check_label_frame(mp4_tag, "----:com.apple.iTunes:ALBUM_LABELS", album_labels_string);
            if(changedflag & CHANGED_TRACK_LABELS)
                mp4_check_label_frame(mp4_tag, "----:com.apple.iTunes:TRACK_LABELS", track_labels_string);
            
            
            if(changedflag & CHANGED_COPYRIGHT_TAG)
                mp4_tag->itemListMap()["\xa9" "cpy"] = TagLib::StringList(copyright);
            if(changedflag & CHANGED_ENCODER_TAG)
                mp4_tag->itemListMap()["\xa9too"] = TagLib::StringList(encoder);
            if(changedflag & CHANGED_HOMEPAGE_TAG)
                mp4_tag->itemListMap()["\xa9src"] = TagLib::StringList(homepage);
            
            save_base_tags((TagLib::Tag *)mp4_tag);
        }
    }
    return Info::save();
}


ImageFileType get_image_file_type_from_format(TagLib::MP4::CoverArt::Format format) {
    switch(format) {
        case MP4::CoverArt::JPEG:
            return IMAGE_FILE_TYPE_JPEG;
        case MP4::CoverArt::PNG:
            return IMAGE_FILE_TYPE_PNG;
        case MP4::CoverArt::BMP:
            return IMAGE_FILE_TYPE_BMP;
        case MP4::CoverArt::GIF:
            return IMAGE_FILE_TYPE_GIF;
        default:
            return IMAGE_FILE_TYPE_UNKNOWN;
    }
}

TagLib::MP4::CoverArt::Format get_format_from_image_file_type(ImageFileType image_file_type) {
    switch(image_file_type) {
        case IMAGE_FILE_TYPE_JPEG:
            return MP4::CoverArt::JPEG;
            break;
        case IMAGE_FILE_TYPE_PNG:
            return MP4::CoverArt::PNG;
            break;
        case IMAGE_FILE_TYPE_BMP:
            return MP4::CoverArt::BMP;
            break;
        case IMAGE_FILE_TYPE_GIF:
            return MP4::CoverArt::GIF;
            break;
        default:
            return MP4::CoverArt::JPEG;
    }
}


Image ** Mp4Info::get_images(int &image_count) const {
    image_count = 0;
    if(mp4_tag) {
        Image** images;
        if(mp4_tag && mp4_tag->itemListMap().contains("covr")) {
            TagLib::MP4::CoverArtList covers = mp4_tag->itemListMap()[ "covr" ].toCoverArtList();
            int cnt = covers.size();
            images = new Image*[cnt];
            Image * image = NULL;
            int i = 0;
            for(TagLib::MP4::CoverArtList::Iterator it = covers.begin(); it != covers.end(); it++) {
                image = new Image();
                image->image_type = IMAGE_TYPE_OTHER;
                image->data_length = it->data().size();
                image->data = new char[image->data_length];
                memcpy(image->data, it->data().data(), it->data().size());
                image->image_file_type = get_image_file_type_from_format(it->format());
                
                if(image->data && image->data_length > 0) {
                    image->description = NULL;
                    images[i] = image;
                    i++;
                }
            }
            image_count = i;
        }
        return images;
    }
    return NULL;
}


void Mp4Info::set_images(const Image ** images, const int image_count) {
    if(mp4_tag) {
        if(mp4_tag->itemListMap().contains("covr")) {
            mp4_tag->itemListMap().erase("covr");
        }
        TagLib::MP4::CoverArtList cover_list;
        for(int p = 0; p < image_count; p++) {
            const Image * image = images[p];
            if(image->data && image->data_length > 0) {
                ByteVector image_data((TagLib::uint) image->data_length);
                memcpy(image_data.data(), image->data, image->data_length);
                TagLib::MP4::CoverArt::Format format = get_format_from_image_file_type(image->image_file_type);
                TagLib::MP4::CoverArt cover(format, image_data);
                cover_list.append(cover);
            }
        }
        if(0 < cover_list.size())
            mp4_tag->itemListMap()[ "covr" ] = cover_list;
    }
}


String Mp4Info::get_lyrics(void) const {
    if(mp4_tag) {
            if(mp4_tag->itemListMap().contains("\xa9lyr"))
                return mp4_tag->itemListMap()[ "\xa9lyr" ].toStringList().front();
    }
    return String();
}


bool Mp4Info::set_lyrics(const String &lyrics) {
    if(mp4_tag) {
            if(mp4_tag->itemListMap().contains("\xa9lyr")) {
            mp4_tag->itemListMap().erase("\xa9lyr");
        }
        if(!lyrics.isEmpty()) {
            mp4_tag->itemListMap()[ "\xa9lyr" ] = TagLib::StringList(lyrics);
        }
        return true;
    }
    return false;
}