move tags related functions to separate file
This commit is contained in:
@@ -40,6 +40,7 @@ ncmpcpp_SOURCES = \
|
|||||||
status.cpp \
|
status.cpp \
|
||||||
statusbar.cpp \
|
statusbar.cpp \
|
||||||
tag_editor.cpp \
|
tag_editor.cpp \
|
||||||
|
tags.cpp \
|
||||||
tiny_tag_editor.cpp \
|
tiny_tag_editor.cpp \
|
||||||
title.cpp \
|
title.cpp \
|
||||||
visualizer.cpp \
|
visualizer.cpp \
|
||||||
@@ -94,6 +95,7 @@ noinst_HEADERS = \
|
|||||||
status.h \
|
status.h \
|
||||||
statusbar.h \
|
statusbar.h \
|
||||||
tag_editor.h \
|
tag_editor.h \
|
||||||
|
tags.h \
|
||||||
tiny_tag_editor.h \
|
tiny_tag_editor.h \
|
||||||
title.h \
|
title.h \
|
||||||
visualizer.h \
|
visualizer.h \
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
#include "tiny_tag_editor.h"
|
#include "tiny_tag_editor.h"
|
||||||
#include "visualizer.h"
|
#include "visualizer.h"
|
||||||
#include "title.h"
|
#include "title.h"
|
||||||
|
#include "tags.h"
|
||||||
|
|
||||||
#ifdef HAVE_TAGLIB_H
|
#ifdef HAVE_TAGLIB_H
|
||||||
# include "fileref.h"
|
# include "fileref.h"
|
||||||
@@ -1343,7 +1344,7 @@ void EditLibraryTag::Run()
|
|||||||
es.setTags(set, new_tag, Config.tags_separator);
|
es.setTags(set, new_tag, Config.tags_separator);
|
||||||
Statusbar::msg("Updating tags in \"%s\"...", es.getName().c_str());
|
Statusbar::msg("Updating tags in \"%s\"...", es.getName().c_str());
|
||||||
std::string path = Config.mpd_music_dir + es.getURI();
|
std::string path = Config.mpd_music_dir + es.getURI();
|
||||||
if (!TagEditor::WriteTags(es))
|
if (!Tags::write(es))
|
||||||
{
|
{
|
||||||
const char msg[] = "Error while updating tags in \"%ls\"";
|
const char msg[] = "Error while updating tags in \"%ls\"";
|
||||||
Statusbar::msg(msg, wideShorten(ToWString(es.getURI()), COLS-const_strlen(msg)).c_str());
|
Statusbar::msg(msg, wideShorten(ToWString(es.getURI()), COLS-const_strlen(msg)).c_str());
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#include "tag_editor.h"
|
#include "tag_editor.h"
|
||||||
#include "utility/comparators.h"
|
#include "utility/comparators.h"
|
||||||
#include "title.h"
|
#include "title.h"
|
||||||
|
#include "tags.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
|
||||||
@@ -507,7 +508,7 @@ void Browser::GetLocalDirectory(MPD::ItemList &v, const std::string &directory,
|
|||||||
new_item.song = std::shared_ptr<MPD::Song>(s);
|
new_item.song = std::shared_ptr<MPD::Song>(s);
|
||||||
# ifdef HAVE_TAGLIB_H
|
# ifdef HAVE_TAGLIB_H
|
||||||
if (!recursively)
|
if (!recursively)
|
||||||
TagEditor::ReadTags(*s);
|
Tags::read(*s);
|
||||||
# endif // HAVE_TAGLIB_H
|
# endif // HAVE_TAGLIB_H
|
||||||
v.push_back(new_item);
|
v.push_back(new_item);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,20 +22,9 @@
|
|||||||
|
|
||||||
#ifdef HAVE_TAGLIB_H
|
#ifdef HAVE_TAGLIB_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
// taglib includes
|
|
||||||
#include "id3v1tag.h"
|
|
||||||
#include "id3v2tag.h"
|
|
||||||
#include "textidentificationframe.h"
|
|
||||||
#include "mpegfile.h"
|
|
||||||
#include "vorbisfile.h"
|
|
||||||
#include "flacfile.h"
|
|
||||||
#include "xiphcomment.h"
|
|
||||||
#include "fileref.h"
|
|
||||||
#include "tag.h"
|
|
||||||
|
|
||||||
#include "browser.h"
|
#include "browser.h"
|
||||||
#include "charset.h"
|
#include "charset.h"
|
||||||
@@ -47,6 +36,7 @@
|
|||||||
#include "statusbar.h"
|
#include "statusbar.h"
|
||||||
#include "utility/comparators.h"
|
#include "utility/comparators.h"
|
||||||
#include "title.h"
|
#include "title.h"
|
||||||
|
#include "tags.h"
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
|
||||||
@@ -81,18 +71,6 @@ std::string CapitalizeFirstLetters(const std::string &s);
|
|||||||
void CapitalizeFirstLetters(MPD::MutableSong &s);
|
void CapitalizeFirstLetters(MPD::MutableSong &s);
|
||||||
void LowerAllLetters(MPD::MutableSong &s);
|
void LowerAllLetters(MPD::MutableSong &s);
|
||||||
|
|
||||||
TagLib::StringList tagList(const MPD::MutableSong &s, MPD::Song::GetFunction f);
|
|
||||||
|
|
||||||
void readCommonTags(MPD::MutableSong &s, TagLib::Tag *tag);
|
|
||||||
void readID3v1Tags(MPD::MutableSong &s, TagLib::ID3v1::Tag *tag);
|
|
||||||
void readID3v2Tags(MPD::MutableSong &s, TagLib::ID3v2::Tag *tag);
|
|
||||||
void readXiphComments(MPD::MutableSong &s, TagLib::Ogg::XiphComment *tag);
|
|
||||||
|
|
||||||
void clearID3v1Tags(TagLib::ID3v1::Tag *tag);
|
|
||||||
void writeCommonTags(const MPD::MutableSong &s, TagLib::Tag *tag);
|
|
||||||
void writeID3v2Tags(const MPD::MutableSong &s, TagLib::ID3v2::Tag *tag);
|
|
||||||
void writeXiphComments(const MPD::MutableSong &s, TagLib::Ogg::XiphComment *tag);
|
|
||||||
|
|
||||||
void GetPatternList();
|
void GetPatternList();
|
||||||
void SavePatternList();
|
void SavePatternList();
|
||||||
|
|
||||||
@@ -607,7 +585,7 @@ void TagEditor::EnterPressed()
|
|||||||
for (auto it = EditedSongs.begin(); it != EditedSongs.end(); ++it)
|
for (auto it = EditedSongs.begin(); it != EditedSongs.end(); ++it)
|
||||||
{
|
{
|
||||||
Statusbar::msg("Writing tags in \"%s\"...", (*it)->getName().c_str());
|
Statusbar::msg("Writing tags in \"%s\"...", (*it)->getName().c_str());
|
||||||
if (!WriteTags(**it))
|
if (!Tags::write(**it))
|
||||||
{
|
{
|
||||||
const char msg[] = "Error while writing tags in \"%ls\"";
|
const char msg[] = "Error while writing tags in \"%ls\"";
|
||||||
Statusbar::msg(msg, wideShorten(ToWString((*it)->getURI()), COLS-const_strlen(msg)).c_str());
|
Statusbar::msg(msg, wideShorten(ToWString((*it)->getURI()), COLS-const_strlen(msg)).c_str());
|
||||||
@@ -1040,96 +1018,6 @@ void TagEditor::LocateSong(const MPD::Song &s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TagEditor::ReadTags(MPD::MutableSong &s)
|
|
||||||
{
|
|
||||||
TagLib::FileRef f(s.getURI().c_str());
|
|
||||||
if (f.isNull())
|
|
||||||
return;
|
|
||||||
|
|
||||||
s.setDuration(f.audioProperties()->length());
|
|
||||||
|
|
||||||
if (auto mpeg_file = dynamic_cast<TagLib::MPEG::File *>(f.file()))
|
|
||||||
{
|
|
||||||
if (auto id3v1 = mpeg_file->ID3v1Tag())
|
|
||||||
readID3v1Tags(s, id3v1);
|
|
||||||
if (auto id3v2 = mpeg_file->ID3v2Tag())
|
|
||||||
readID3v2Tags(s, id3v2);
|
|
||||||
}
|
|
||||||
else if (auto ogg_file = dynamic_cast<TagLib::Ogg::Vorbis::File *>(f.file()))
|
|
||||||
{
|
|
||||||
if (auto xiph = ogg_file->tag())
|
|
||||||
readXiphComments(s, xiph);
|
|
||||||
}
|
|
||||||
else if (auto flac_file = dynamic_cast<TagLib::FLAC::File *>(f.file()))
|
|
||||||
{
|
|
||||||
if (auto xiph = flac_file->xiphComment())
|
|
||||||
readXiphComments(s, xiph);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
readCommonTags(s, f.tag());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TagEditor::WriteTags(MPD::MutableSong &s)
|
|
||||||
{
|
|
||||||
std::string path_to_file;
|
|
||||||
bool file_is_from_db = s.isFromDatabase();
|
|
||||||
if (file_is_from_db)
|
|
||||||
path_to_file += Config.mpd_music_dir;
|
|
||||||
path_to_file += s.getURI();
|
|
||||||
TagLib::FileRef f(path_to_file.c_str());
|
|
||||||
if (!f.isNull())
|
|
||||||
{
|
|
||||||
if (auto mp3_file = dynamic_cast<TagLib::MPEG::File *>(f.file()))
|
|
||||||
{
|
|
||||||
clearID3v1Tags(mp3_file->ID3v1Tag());
|
|
||||||
writeID3v2Tags(s, mp3_file->ID3v2Tag(true));
|
|
||||||
}
|
|
||||||
else if (auto ogg_file = dynamic_cast<TagLib::Ogg::Vorbis::File *>(f.file()))
|
|
||||||
{
|
|
||||||
writeXiphComments(s, ogg_file->tag());
|
|
||||||
}
|
|
||||||
else if (auto flac_file = dynamic_cast<TagLib::FLAC::File *>(f.file()))
|
|
||||||
{
|
|
||||||
writeXiphComments(s, flac_file->xiphComment(true));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
writeCommonTags(s, f.tag());
|
|
||||||
if (!f.save())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!s.getNewURI().empty())
|
|
||||||
{
|
|
||||||
std::string new_name;
|
|
||||||
if (file_is_from_db)
|
|
||||||
new_name += Config.mpd_music_dir;
|
|
||||||
new_name += s.getDirectory() + "/" + s.getNewURI();
|
|
||||||
if (rename(path_to_file.c_str(), new_name.c_str()) == 0 && !file_is_from_db)
|
|
||||||
{
|
|
||||||
if (Global::myOldScreen == myPlaylist)
|
|
||||||
{
|
|
||||||
// if we rename local file, it won't get updated
|
|
||||||
// so just remove it from playlist and add again
|
|
||||||
size_t pos = myPlaylist->Items->choice();
|
|
||||||
Mpd.StartCommandsList();
|
|
||||||
Mpd.Delete(pos);
|
|
||||||
int id = Mpd.AddSong("file://" + new_name);
|
|
||||||
if (id >= 0)
|
|
||||||
{
|
|
||||||
s = myPlaylist->Items->back().value();
|
|
||||||
Mpd.Move(s.getPosition(), pos);
|
|
||||||
}
|
|
||||||
Mpd.CommitCommandsList();
|
|
||||||
}
|
|
||||||
else // only myBrowser->Main()
|
|
||||||
myBrowser->GetDirectory(myBrowser->CurrentDir());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {//
|
namespace {//
|
||||||
|
|
||||||
bool isAnyModified(const NC::Menu<MPD::MutableSong> &m)
|
bool isAnyModified(const NC::Menu<MPD::MutableSong> &m)
|
||||||
@@ -1173,138 +1061,6 @@ void LowerAllLetters(MPD::MutableSong &s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TagLib::StringList tagList(const MPD::MutableSong &s, MPD::Song::GetFunction f)
|
|
||||||
{
|
|
||||||
TagLib::StringList result;
|
|
||||||
unsigned idx = 0;
|
|
||||||
for (std::string value; !(value = (s.*f)(idx)).empty(); ++idx)
|
|
||||||
result.append(ToWString(value));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void readCommonTags(MPD::MutableSong &s, TagLib::Tag *tag)
|
|
||||||
{
|
|
||||||
s.setTitle(tag->title().to8Bit(true));
|
|
||||||
s.setArtist(tag->artist().to8Bit(true));
|
|
||||||
s.setAlbum(tag->album().to8Bit(true));
|
|
||||||
s.setDate(intTo<std::string>::apply(tag->year()));
|
|
||||||
s.setTrack(intTo<std::string>::apply(tag->track()));
|
|
||||||
s.setGenre(tag->genre().to8Bit(true));
|
|
||||||
s.setComment(tag->comment().to8Bit(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
void readID3v1Tags(MPD::MutableSong &s, TagLib::ID3v1::Tag *tag)
|
|
||||||
{
|
|
||||||
readCommonTags(s, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
void readID3v2Tags(MPD::MutableSong &s, TagLib::ID3v2::Tag *tag)
|
|
||||||
{
|
|
||||||
auto readFrame = [&s](const TagLib::ID3v2::FrameList &list, MPD::MutableSong::SetFunction f) {
|
|
||||||
unsigned idx = 0;
|
|
||||||
for (auto it = list.begin(); it != list.end(); ++it, ++idx)
|
|
||||||
(s.*f)((*it)->toString().to8Bit(true), idx);
|
|
||||||
};
|
|
||||||
auto &frames = tag->frameListMap();
|
|
||||||
readFrame(frames["TIT2"], &MPD::MutableSong::setTitle);
|
|
||||||
readFrame(frames["TPE1"], &MPD::MutableSong::setArtist);
|
|
||||||
readFrame(frames["TPE2"], &MPD::MutableSong::setAlbumArtist);
|
|
||||||
readFrame(frames["TALB"], &MPD::MutableSong::setAlbum);
|
|
||||||
readFrame(frames["TDRC"], &MPD::MutableSong::setDate);
|
|
||||||
readFrame(frames["TRCK"], &MPD::MutableSong::setTrack);
|
|
||||||
readFrame(frames["TCON"], &MPD::MutableSong::setGenre);
|
|
||||||
readFrame(frames["TCOM"], &MPD::MutableSong::setComposer);
|
|
||||||
readFrame(frames["TPE3"], &MPD::MutableSong::setPerformer);
|
|
||||||
readFrame(frames["TPOS"], &MPD::MutableSong::setDisc);
|
|
||||||
readFrame(frames["COMM"], &MPD::MutableSong::setComment);
|
|
||||||
}
|
|
||||||
|
|
||||||
void readXiphComments(MPD::MutableSong &s, TagLib::Ogg::XiphComment *tag)
|
|
||||||
{
|
|
||||||
auto readField = [&s](const TagLib::StringList &list, MPD::MutableSong::SetFunction f) {
|
|
||||||
unsigned idx = 0;
|
|
||||||
for (auto it = list.begin(); it != list.end(); ++it)
|
|
||||||
(s.*f)(it->to8Bit(true), idx);
|
|
||||||
};
|
|
||||||
auto &fields = tag->fieldListMap();
|
|
||||||
readField(fields["TITLE"], &MPD::MutableSong::setTitle);
|
|
||||||
readField(fields["ARTIST"], &MPD::MutableSong::setArtist);
|
|
||||||
readField(fields["ALBUMARTIST"], &MPD::MutableSong::setAlbumArtist);
|
|
||||||
readField(fields["ALBUM"], &MPD::MutableSong::setAlbum);
|
|
||||||
readField(fields["DATE"], &MPD::MutableSong::setDate);
|
|
||||||
readField(fields["TRACKNUMBER"], &MPD::MutableSong::setTrack);
|
|
||||||
readField(fields["GENRE"], &MPD::MutableSong::setGenre);
|
|
||||||
readField(fields["COMPOSER"], &MPD::MutableSong::setComposer);
|
|
||||||
readField(fields["PERFORMER"], &MPD::MutableSong::setPerformer);
|
|
||||||
readField(fields["DISCNUMBER"], &MPD::MutableSong::setDisc);
|
|
||||||
readField(fields["COMMENT"], &MPD::MutableSong::setComment);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearID3v1Tags(TagLib::ID3v1::Tag *tag)
|
|
||||||
{
|
|
||||||
tag->setTitle(TagLib::String::null);
|
|
||||||
tag->setArtist(TagLib::String::null);
|
|
||||||
tag->setAlbum(TagLib::String::null);
|
|
||||||
tag->setYear(0);
|
|
||||||
tag->setTrack(0);
|
|
||||||
tag->setGenre(TagLib::String::null);
|
|
||||||
tag->setComment(TagLib::String::null);
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeCommonTags(const MPD::MutableSong &s, TagLib::Tag *tag)
|
|
||||||
{
|
|
||||||
tag->setTitle(ToWString(s.getTitle()));
|
|
||||||
tag->setArtist(ToWString(s.getArtist()));
|
|
||||||
tag->setAlbum(ToWString(s.getAlbum()));
|
|
||||||
tag->setYear(stringToInt(s.getDate()));
|
|
||||||
tag->setTrack(stringToInt(s.getTrack()));
|
|
||||||
tag->setGenre(ToWString(s.getGenre()));
|
|
||||||
tag->setComment(ToWString(s.getComment()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeID3v2Tags(const MPD::MutableSong &s, TagLib::ID3v2::Tag *tag)
|
|
||||||
{
|
|
||||||
auto writeID3v2 = [&](const TagLib::ByteVector &type, const TagLib::StringList &list) {
|
|
||||||
tag->removeFrames(type);
|
|
||||||
auto frame = new TagLib::ID3v2::TextIdentificationFrame(type, TagLib::String::UTF8);
|
|
||||||
frame->setText(list);
|
|
||||||
tag->addFrame(frame);
|
|
||||||
};
|
|
||||||
writeID3v2("TIT2", tagList(s, &MPD::Song::getTitle));
|
|
||||||
writeID3v2("TPE1", tagList(s, &MPD::Song::getArtist));
|
|
||||||
writeID3v2("TPE2", tagList(s, &MPD::Song::getAlbumArtist));
|
|
||||||
writeID3v2("TALB", tagList(s, &MPD::Song::getAlbum));
|
|
||||||
writeID3v2("TDRC", tagList(s, &MPD::Song::getDate));
|
|
||||||
writeID3v2("TRCK", tagList(s, &MPD::Song::getTrack));
|
|
||||||
writeID3v2("TCON", tagList(s, &MPD::Song::getGenre));
|
|
||||||
writeID3v2("TCOM", tagList(s, &MPD::Song::getComposer));
|
|
||||||
writeID3v2("TPE3", tagList(s, &MPD::Song::getPerformer));
|
|
||||||
writeID3v2("TPOS", tagList(s, &MPD::Song::getDisc));
|
|
||||||
writeID3v2("COMM", tagList(s, &MPD::Song::getComment));
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeXiphComments(const MPD::MutableSong &s, TagLib::Ogg::XiphComment *tag)
|
|
||||||
{
|
|
||||||
auto writeXiph = [&](const TagLib::String &type, const TagLib::StringList &list) {
|
|
||||||
tag->removeField(type);
|
|
||||||
for (auto it = list.begin(); it != list.end(); ++it)
|
|
||||||
tag->addField(type, *it, false);
|
|
||||||
};
|
|
||||||
// remove field previously used as album artist
|
|
||||||
tag->removeField("ALBUM ARTIST");
|
|
||||||
writeXiph("TITLE", tagList(s, &MPD::Song::getTitle));
|
|
||||||
writeXiph("ARTIST", tagList(s, &MPD::Song::getArtist));
|
|
||||||
writeXiph("ALBUMARTIST", tagList(s, &MPD::Song::getAlbumArtist));
|
|
||||||
writeXiph("ALBUM", tagList(s, &MPD::Song::getAlbum));
|
|
||||||
writeXiph("DATE", tagList(s, &MPD::Song::getDate));
|
|
||||||
writeXiph("TRACKNUMBER", tagList(s, &MPD::Song::getTrack));
|
|
||||||
writeXiph("GENRE", tagList(s, &MPD::Song::getGenre));
|
|
||||||
writeXiph("COMPOSER", tagList(s, &MPD::Song::getComposer));
|
|
||||||
writeXiph("PERFORMER", tagList(s, &MPD::Song::getPerformer));
|
|
||||||
writeXiph("DISCNUMBER", tagList(s, &MPD::Song::getDisc));
|
|
||||||
writeXiph("COMMENT", tagList(s, &MPD::Song::getComment));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetPatternList()
|
void GetPatternList()
|
||||||
{
|
{
|
||||||
if (Patterns.empty())
|
if (Patterns.empty())
|
||||||
|
|||||||
@@ -87,9 +87,6 @@ class TagEditor : public Screen<NC::Window>, public Filterable, public HasColumn
|
|||||||
NC::Menu<std::string> *TagTypes;
|
NC::Menu<std::string> *TagTypes;
|
||||||
NC::Menu<MPD::MutableSong> *Tags;
|
NC::Menu<MPD::MutableSong> *Tags;
|
||||||
|
|
||||||
static void ReadTags(MPD::MutableSong &);
|
|
||||||
static bool WriteTags(MPD::MutableSong &);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Init();
|
virtual void Init();
|
||||||
virtual bool isLockable() { return true; }
|
virtual bool isLockable() { return true; }
|
||||||
|
|||||||
280
src/tags.cpp
Normal file
280
src/tags.cpp
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2008-2012 by Andrzej Rybczak *
|
||||||
|
* electricityispower@gmail.com *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 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; if not, write to the *
|
||||||
|
* Free Software Foundation, Inc., *
|
||||||
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include "tags.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_TAGLIB_H
|
||||||
|
|
||||||
|
// taglib includes
|
||||||
|
#include <id3v1tag.h>
|
||||||
|
#include <id3v2tag.h>
|
||||||
|
#include <fileref.h>
|
||||||
|
#include <flacfile.h>
|
||||||
|
#include <mpegfile.h>
|
||||||
|
#include <vorbisfile.h>
|
||||||
|
#include <tag.h>
|
||||||
|
#include <textidentificationframe.h>
|
||||||
|
#include <xiphcomment.h>
|
||||||
|
|
||||||
|
#include "browser.h"
|
||||||
|
#include "playlist.h"
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "utility/numeric_conversions.h"
|
||||||
|
#include "utility/wide_string.h"
|
||||||
|
|
||||||
|
namespace {//
|
||||||
|
|
||||||
|
TagLib::StringList tagList(const MPD::MutableSong &s, MPD::Song::GetFunction f)
|
||||||
|
{
|
||||||
|
TagLib::StringList result;
|
||||||
|
unsigned idx = 0;
|
||||||
|
for (std::string value; !(value = (s.*f)(idx)).empty(); ++idx)
|
||||||
|
result.append(ToWString(value));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void readCommonTags(MPD::MutableSong &s, TagLib::Tag *tag)
|
||||||
|
{
|
||||||
|
s.setTitle(tag->title().to8Bit(true));
|
||||||
|
s.setArtist(tag->artist().to8Bit(true));
|
||||||
|
s.setAlbum(tag->album().to8Bit(true));
|
||||||
|
s.setDate(intTo<std::string>::apply(tag->year()));
|
||||||
|
s.setTrack(intTo<std::string>::apply(tag->track()));
|
||||||
|
s.setGenre(tag->genre().to8Bit(true));
|
||||||
|
s.setComment(tag->comment().to8Bit(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
void readID3v1Tags(MPD::MutableSong &s, TagLib::ID3v1::Tag *tag)
|
||||||
|
{
|
||||||
|
readCommonTags(s, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void readID3v2Tags(MPD::MutableSong &s, TagLib::ID3v2::Tag *tag)
|
||||||
|
{
|
||||||
|
auto readFrame = [&s](const TagLib::ID3v2::FrameList &list, MPD::MutableSong::SetFunction f) {
|
||||||
|
unsigned idx = 0;
|
||||||
|
for (auto it = list.begin(); it != list.end(); ++it, ++idx)
|
||||||
|
(s.*f)((*it)->toString().to8Bit(true), idx);
|
||||||
|
};
|
||||||
|
auto &frames = tag->frameListMap();
|
||||||
|
readFrame(frames["TIT2"], &MPD::MutableSong::setTitle);
|
||||||
|
readFrame(frames["TPE1"], &MPD::MutableSong::setArtist);
|
||||||
|
readFrame(frames["TPE2"], &MPD::MutableSong::setAlbumArtist);
|
||||||
|
readFrame(frames["TALB"], &MPD::MutableSong::setAlbum);
|
||||||
|
readFrame(frames["TDRC"], &MPD::MutableSong::setDate);
|
||||||
|
readFrame(frames["TRCK"], &MPD::MutableSong::setTrack);
|
||||||
|
readFrame(frames["TCON"], &MPD::MutableSong::setGenre);
|
||||||
|
readFrame(frames["TCOM"], &MPD::MutableSong::setComposer);
|
||||||
|
readFrame(frames["TPE3"], &MPD::MutableSong::setPerformer);
|
||||||
|
readFrame(frames["TPOS"], &MPD::MutableSong::setDisc);
|
||||||
|
readFrame(frames["COMM"], &MPD::MutableSong::setComment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void readXiphComments(MPD::MutableSong &s, TagLib::Ogg::XiphComment *tag)
|
||||||
|
{
|
||||||
|
auto readField = [&s](const TagLib::StringList &list, MPD::MutableSong::SetFunction f) {
|
||||||
|
unsigned idx = 0;
|
||||||
|
for (auto it = list.begin(); it != list.end(); ++it)
|
||||||
|
(s.*f)(it->to8Bit(true), idx);
|
||||||
|
};
|
||||||
|
auto &fields = tag->fieldListMap();
|
||||||
|
readField(fields["TITLE"], &MPD::MutableSong::setTitle);
|
||||||
|
readField(fields["ARTIST"], &MPD::MutableSong::setArtist);
|
||||||
|
readField(fields["ALBUMARTIST"], &MPD::MutableSong::setAlbumArtist);
|
||||||
|
readField(fields["ALBUM"], &MPD::MutableSong::setAlbum);
|
||||||
|
readField(fields["DATE"], &MPD::MutableSong::setDate);
|
||||||
|
readField(fields["TRACKNUMBER"], &MPD::MutableSong::setTrack);
|
||||||
|
readField(fields["GENRE"], &MPD::MutableSong::setGenre);
|
||||||
|
readField(fields["COMPOSER"], &MPD::MutableSong::setComposer);
|
||||||
|
readField(fields["PERFORMER"], &MPD::MutableSong::setPerformer);
|
||||||
|
readField(fields["DISCNUMBER"], &MPD::MutableSong::setDisc);
|
||||||
|
readField(fields["COMMENT"], &MPD::MutableSong::setComment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearID3v1Tags(TagLib::ID3v1::Tag *tag)
|
||||||
|
{
|
||||||
|
tag->setTitle(TagLib::String::null);
|
||||||
|
tag->setArtist(TagLib::String::null);
|
||||||
|
tag->setAlbum(TagLib::String::null);
|
||||||
|
tag->setYear(0);
|
||||||
|
tag->setTrack(0);
|
||||||
|
tag->setGenre(TagLib::String::null);
|
||||||
|
tag->setComment(TagLib::String::null);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeCommonTags(const MPD::MutableSong &s, TagLib::Tag *tag)
|
||||||
|
{
|
||||||
|
tag->setTitle(ToWString(s.getTitle()));
|
||||||
|
tag->setArtist(ToWString(s.getArtist()));
|
||||||
|
tag->setAlbum(ToWString(s.getAlbum()));
|
||||||
|
tag->setYear(stringToInt(s.getDate()));
|
||||||
|
tag->setTrack(stringToInt(s.getTrack()));
|
||||||
|
tag->setGenre(ToWString(s.getGenre()));
|
||||||
|
tag->setComment(ToWString(s.getComment()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeID3v2Tags(const MPD::MutableSong &s, TagLib::ID3v2::Tag *tag)
|
||||||
|
{
|
||||||
|
auto writeID3v2 = [&](const TagLib::ByteVector &type, const TagLib::StringList &list) {
|
||||||
|
tag->removeFrames(type);
|
||||||
|
auto frame = new TagLib::ID3v2::TextIdentificationFrame(type, TagLib::String::UTF8);
|
||||||
|
frame->setText(list);
|
||||||
|
tag->addFrame(frame);
|
||||||
|
};
|
||||||
|
writeID3v2("TIT2", tagList(s, &MPD::Song::getTitle));
|
||||||
|
writeID3v2("TPE1", tagList(s, &MPD::Song::getArtist));
|
||||||
|
writeID3v2("TPE2", tagList(s, &MPD::Song::getAlbumArtist));
|
||||||
|
writeID3v2("TALB", tagList(s, &MPD::Song::getAlbum));
|
||||||
|
writeID3v2("TDRC", tagList(s, &MPD::Song::getDate));
|
||||||
|
writeID3v2("TRCK", tagList(s, &MPD::Song::getTrack));
|
||||||
|
writeID3v2("TCON", tagList(s, &MPD::Song::getGenre));
|
||||||
|
writeID3v2("TCOM", tagList(s, &MPD::Song::getComposer));
|
||||||
|
writeID3v2("TPE3", tagList(s, &MPD::Song::getPerformer));
|
||||||
|
writeID3v2("TPOS", tagList(s, &MPD::Song::getDisc));
|
||||||
|
writeID3v2("COMM", tagList(s, &MPD::Song::getComment));
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeXiphComments(const MPD::MutableSong &s, TagLib::Ogg::XiphComment *tag)
|
||||||
|
{
|
||||||
|
auto writeXiph = [&](const TagLib::String &type, const TagLib::StringList &list) {
|
||||||
|
tag->removeField(type);
|
||||||
|
for (auto it = list.begin(); it != list.end(); ++it)
|
||||||
|
tag->addField(type, *it, false);
|
||||||
|
};
|
||||||
|
// remove field previously used as album artist
|
||||||
|
tag->removeField("ALBUM ARTIST");
|
||||||
|
writeXiph("TITLE", tagList(s, &MPD::Song::getTitle));
|
||||||
|
writeXiph("ARTIST", tagList(s, &MPD::Song::getArtist));
|
||||||
|
writeXiph("ALBUMARTIST", tagList(s, &MPD::Song::getAlbumArtist));
|
||||||
|
writeXiph("ALBUM", tagList(s, &MPD::Song::getAlbum));
|
||||||
|
writeXiph("DATE", tagList(s, &MPD::Song::getDate));
|
||||||
|
writeXiph("TRACKNUMBER", tagList(s, &MPD::Song::getTrack));
|
||||||
|
writeXiph("GENRE", tagList(s, &MPD::Song::getGenre));
|
||||||
|
writeXiph("COMPOSER", tagList(s, &MPD::Song::getComposer));
|
||||||
|
writeXiph("PERFORMER", tagList(s, &MPD::Song::getPerformer));
|
||||||
|
writeXiph("DISCNUMBER", tagList(s, &MPD::Song::getDisc));
|
||||||
|
writeXiph("COMMENT", tagList(s, &MPD::Song::getComment));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Tags {//
|
||||||
|
|
||||||
|
bool extendedSetSupported(const TagLib::File *f)
|
||||||
|
{
|
||||||
|
return dynamic_cast<const TagLib::MPEG::File *>(f)
|
||||||
|
|| dynamic_cast<const TagLib::Ogg::Vorbis::File *>(f)
|
||||||
|
|| dynamic_cast<const TagLib::FLAC::File *>(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void read(MPD::MutableSong &s)
|
||||||
|
{
|
||||||
|
TagLib::FileRef f(s.getURI().c_str());
|
||||||
|
if (f.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
s.setDuration(f.audioProperties()->length());
|
||||||
|
|
||||||
|
if (auto mpeg_file = dynamic_cast<TagLib::MPEG::File *>(f.file()))
|
||||||
|
{
|
||||||
|
if (auto id3v1 = mpeg_file->ID3v1Tag())
|
||||||
|
readID3v1Tags(s, id3v1);
|
||||||
|
if (auto id3v2 = mpeg_file->ID3v2Tag())
|
||||||
|
readID3v2Tags(s, id3v2);
|
||||||
|
}
|
||||||
|
else if (auto ogg_file = dynamic_cast<TagLib::Ogg::Vorbis::File *>(f.file()))
|
||||||
|
{
|
||||||
|
if (auto xiph = ogg_file->tag())
|
||||||
|
readXiphComments(s, xiph);
|
||||||
|
}
|
||||||
|
else if (auto flac_file = dynamic_cast<TagLib::FLAC::File *>(f.file()))
|
||||||
|
{
|
||||||
|
if (auto xiph = flac_file->xiphComment())
|
||||||
|
readXiphComments(s, xiph);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
readCommonTags(s, f.tag());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool write(MPD::MutableSong &s)
|
||||||
|
{
|
||||||
|
std::string old_name;
|
||||||
|
if (s.isFromDatabase())
|
||||||
|
old_name += Config.mpd_music_dir;
|
||||||
|
old_name += s.getURI();
|
||||||
|
|
||||||
|
TagLib::FileRef f(old_name.c_str());
|
||||||
|
if (f.isNull())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (auto mp3_file = dynamic_cast<TagLib::MPEG::File *>(f.file()))
|
||||||
|
{
|
||||||
|
clearID3v1Tags(mp3_file->ID3v1Tag());
|
||||||
|
writeID3v2Tags(s, mp3_file->ID3v2Tag(true));
|
||||||
|
}
|
||||||
|
else if (auto ogg_file = dynamic_cast<TagLib::Ogg::Vorbis::File *>(f.file()))
|
||||||
|
{
|
||||||
|
writeXiphComments(s, ogg_file->tag());
|
||||||
|
}
|
||||||
|
else if (auto flac_file = dynamic_cast<TagLib::FLAC::File *>(f.file()))
|
||||||
|
{
|
||||||
|
writeXiphComments(s, flac_file->xiphComment(true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
writeCommonTags(s, f.tag());
|
||||||
|
|
||||||
|
if (!f.save())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!s.getNewURI().empty())
|
||||||
|
{
|
||||||
|
std::string new_name;
|
||||||
|
if (s.isFromDatabase())
|
||||||
|
new_name += Config.mpd_music_dir;
|
||||||
|
new_name += s.getDirectory() + "/" + s.getNewURI();
|
||||||
|
if (std::rename(old_name.c_str(), new_name.c_str()) == 0 && !s.isFromDatabase())
|
||||||
|
{
|
||||||
|
if (Global::myOldScreen == myPlaylist)
|
||||||
|
{
|
||||||
|
// if we rename local file, it won't get updated
|
||||||
|
// so just remove it from playlist and add again
|
||||||
|
size_t pos = myPlaylist->Items->choice();
|
||||||
|
Mpd.StartCommandsList();
|
||||||
|
Mpd.Delete(pos);
|
||||||
|
int id = Mpd.AddSong("file://" + new_name);
|
||||||
|
if (id >= 0)
|
||||||
|
{
|
||||||
|
s = myPlaylist->Items->back().value();
|
||||||
|
Mpd.Move(s.getPosition(), pos);
|
||||||
|
}
|
||||||
|
Mpd.CommitCommandsList();
|
||||||
|
}
|
||||||
|
else // only myBrowser->Main()
|
||||||
|
myBrowser->GetDirectory(myBrowser->CurrentDir());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_TAGLIB_H
|
||||||
42
src/tags.h
Normal file
42
src/tags.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* Copyright (C) 2008-2012 by Andrzej Rybczak *
|
||||||
|
* electricityispower@gmail.com *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or modify *
|
||||||
|
* it under the terms of the GNU General Public License as published by *
|
||||||
|
* the Free Software Foundation; either version 2 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; if not, write to the *
|
||||||
|
* Free Software Foundation, Inc., *
|
||||||
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#ifndef _TAGS_H
|
||||||
|
#define _TAGS_H
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_TAGLIB_H
|
||||||
|
|
||||||
|
#include <tfile.h>
|
||||||
|
#include "mutable_song.h"
|
||||||
|
|
||||||
|
namespace Tags {//
|
||||||
|
|
||||||
|
bool extendedSetSupported(const TagLib::File *f);
|
||||||
|
|
||||||
|
void read(MPD::MutableSong &);
|
||||||
|
bool write(MPD::MutableSong &);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_TAGLIB_H
|
||||||
|
|
||||||
|
#endif // _TAGS_H
|
||||||
@@ -23,10 +23,8 @@
|
|||||||
#ifdef HAVE_TAGLIB_H
|
#ifdef HAVE_TAGLIB_H
|
||||||
|
|
||||||
// taglib includes
|
// taglib includes
|
||||||
#include "mpegfile.h"
|
#include <fileref.h>
|
||||||
#include "vorbisfile.h"
|
#include <tag.h>
|
||||||
#include "flacfile.h"
|
|
||||||
#include "fileref.h"
|
|
||||||
|
|
||||||
#include "browser.h"
|
#include "browser.h"
|
||||||
#include "charset.h"
|
#include "charset.h"
|
||||||
@@ -39,6 +37,7 @@
|
|||||||
#include "statusbar.h"
|
#include "statusbar.h"
|
||||||
#include "tag_editor.h"
|
#include "tag_editor.h"
|
||||||
#include "title.h"
|
#include "title.h"
|
||||||
|
#include "tags.h"
|
||||||
|
|
||||||
using Global::MainHeight;
|
using Global::MainHeight;
|
||||||
using Global::MainStartY;
|
using Global::MainStartY;
|
||||||
@@ -134,7 +133,7 @@ void TinyTagEditor::EnterPressed()
|
|||||||
if (option == 22)
|
if (option == 22)
|
||||||
{
|
{
|
||||||
Statusbar::msg("Updating tags...");
|
Statusbar::msg("Updating tags...");
|
||||||
if (TagEditor::WriteTags(itsEdited))
|
if (Tags::write(itsEdited))
|
||||||
{
|
{
|
||||||
Statusbar::msg("Tags updated");
|
Statusbar::msg("Tags updated");
|
||||||
if (itsEdited.isFromDatabase())
|
if (itsEdited.isFromDatabase())
|
||||||
@@ -207,7 +206,7 @@ bool TinyTagEditor::getTags()
|
|||||||
w->at(19).setSeparator(true);
|
w->at(19).setSeparator(true);
|
||||||
w->at(21).setSeparator(true);
|
w->at(21).setSeparator(true);
|
||||||
|
|
||||||
if (!extendedTagsSupported(f.file()))
|
if (!Tags::extendedSetSupported(f.file()))
|
||||||
{
|
{
|
||||||
w->at(10).setInactive(true);
|
w->at(10).setInactive(true);
|
||||||
for (size_t i = 15; i <= 17; ++i)
|
for (size_t i = 15; i <= 17; ++i)
|
||||||
@@ -239,12 +238,5 @@ bool TinyTagEditor::getTags()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TinyTagEditor::extendedTagsSupported(TagLib::File *f)
|
|
||||||
{
|
|
||||||
return dynamic_cast<TagLib::MPEG::File *>(f)
|
|
||||||
|| dynamic_cast<TagLib::Ogg::Vorbis::File *>(f)
|
|
||||||
|| dynamic_cast<TagLib::FLAC::File *>(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // HAVE_TAGLIB_H
|
#endif // HAVE_TAGLIB_H
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,6 @@
|
|||||||
|
|
||||||
#ifdef HAVE_TAGLIB_H
|
#ifdef HAVE_TAGLIB_H
|
||||||
|
|
||||||
// taglib includes
|
|
||||||
#include "tfile.h"
|
|
||||||
|
|
||||||
#include "mutable_song.h"
|
#include "mutable_song.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
|
||||||
@@ -59,8 +56,6 @@ class TinyTagEditor : public Screen< NC::Menu<NC::Buffer> >
|
|||||||
private:
|
private:
|
||||||
bool getTags();
|
bool getTags();
|
||||||
MPD::MutableSong itsEdited;
|
MPD::MutableSong itsEdited;
|
||||||
|
|
||||||
static bool extendedTagsSupported(TagLib::File *);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern TinyTagEditor *myTinyTagEditor;
|
extern TinyTagEditor *myTinyTagEditor;
|
||||||
|
|||||||
Reference in New Issue
Block a user