Media library: remove argument to SortSongs contructor to simplify sorting
[ncmpcpp.git] / extras / artist_to_albumartist.cpp
blob1f691123c3183e0217369106f0712c36c90ddd6b
1 /***************************************************************************
2 * Copyright (C) 2008-2013 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <cstring>
22 #include <iostream>
24 #include <fileref.h>
25 #include <flacfile.h>
26 #include <mpegfile.h>
27 #include <vorbisfile.h>
28 #include <textidentificationframe.h>
29 #include <id3v2tag.h>
30 #include <xiphcomment.h>
32 enum class CopyResult { Success, NoArtist, AlbumArtistAlreadyInPlace };
34 bool is_framelist_empty(const TagLib::ID3v2::FrameList &list)
36 for (auto it = list.begin(); it != list.end(); ++it)
37 if ((*it)->toString() != TagLib::String::null)
38 return false;
39 return true;
42 CopyResult copy_album_artist(TagLib::ID3v2::Tag *tag)
44 typedef TagLib::ID3v2::TextIdentificationFrame TextFrame;
46 TagLib::ByteVector album_artist = "TPE2";
47 if (!is_framelist_empty(tag->frameList(album_artist)))
48 return CopyResult::AlbumArtistAlreadyInPlace;
50 auto artists = tag->frameList("TPE1");
51 if (artists.isEmpty())
52 return CopyResult::NoArtist;
54 for (auto it = artists.begin(); it != artists.end(); ++it)
56 // this cast should always succeed.
57 auto &textIt = dynamic_cast<TextFrame &>(**it);
58 auto frame = new TextFrame(album_artist, TagLib::String::UTF8);
59 frame->setText(textIt.fieldList());
60 tag->addFrame(frame);
63 return CopyResult::Success;
66 CopyResult copy_album_artist(TagLib::Ogg::XiphComment *tag)
68 if (tag->contains("ALBUM ARTIST") || tag->contains("ALBUMARTIST"))
69 return CopyResult::AlbumArtistAlreadyInPlace;
71 auto artists = tag->fieldListMap()["ARTIST"];
72 if (artists.isEmpty())
73 return CopyResult::NoArtist;
75 for (auto it = artists.begin(); it != artists.end(); ++it)
76 tag->addField("ALBUMARTIST", *it, false);
78 return CopyResult::Success;
81 void convert(int n, char **files, bool dry_run)
83 if (n == 0)
85 std::cout << "No files to convert, exiting.\n";
86 return;
88 if (dry_run)
89 std::cout << "Dry run mode enabled, pretending to modify files.\n";
91 for (int i = 0; i < n; ++i)
93 std::cout << "Modifying " << files[i] << "... ";
95 TagLib::FileRef f(files[i]);
96 if (!f.isNull())
98 CopyResult result;
99 if (auto mp3_f = dynamic_cast<TagLib::MPEG::File *>(f.file()))
101 result = copy_album_artist(mp3_f->ID3v2Tag(true));
103 else if (auto ogg_f = dynamic_cast<TagLib::Ogg::Vorbis::File *>(f.file()))
105 result = copy_album_artist(ogg_f->tag());
107 else if (auto flac_f = dynamic_cast<TagLib::FLAC::File *>(f.file()))
109 result = copy_album_artist(flac_f->xiphComment(true));
111 else
113 std::cout << "Not mp3/ogg/flac file, skipping.\n";
114 continue;
117 switch (result)
119 case CopyResult::Success:
120 if (!dry_run)
121 f.save();
122 std::cout << "Done.\n";
123 break;
124 case CopyResult::NoArtist:
125 std::cout << "Artist not found, skipping.\n";
126 break;
127 case CopyResult::AlbumArtistAlreadyInPlace:
128 std::cout << "AlbumArtist is already there, skipping.\n";
129 break;
132 else
133 std::cout << "Could not open file, skipping.\n";
137 int main(int argc, char **argv)
139 if (argc < 2)
141 std::cout << "This little script copies Artist tag (if present) to\n";
142 std::cout << "AlbumArtist (if not present) for given mp3/ogg/flac files.\n";
143 std::cout << "\n";
144 std::cout << "Usage: " << argv[0] << " [--dry-run] files\n";
145 std::cout << "\n";
146 std::cout << "Note: to run it recursively for all your files, you can use:\n";
147 std::cout << "$ find DIRECTORY \\( -name \"*.flac\" -o -name \"*.mp3\" -o -name \"*.ogg\" \\) -exec ./artist_to_albumartist [--dry-run] {} \\;\n";
149 else
151 bool dry_run = !strcmp(argv[1], "--dry-run");
152 convert(argc-1-dry_run, &argv[1+dry_run], dry_run);
154 return 0;