taglib wrapper moved to "iv"; tagtool code cleanup
[taglib.d.git] / tagtool.d
blobba9cee9c6a2b60a8f384dea2c86d90f4fc8a604a
1 /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module tagtool is aliced;
18 private:
20 import core.exception : ExitException;
22 import iv.taglib;
23 import iv.writer;
26 string utf2koi (const(char)[] s) {
27 import std.encoding : EncodingScheme, INVALID_SEQUENCE;
28 import iv.encoding;
29 auto efrom = EncodingScheme.create("utf-8");
30 auto eto = EncodingScheme.create("koi8-u");
31 ubyte[] res;
32 ubyte[8] buf;
33 const(ubyte)[] ub = cast(const(ubyte)[])s;
34 while (ub.length > 0) {
35 dchar dc = efrom.safeDecode(ub);
36 if (dc == INVALID_SEQUENCE) dc = '?';
37 eto.encode(dc, buf);
38 res ~= buf[0];
40 return cast(string)res;
44 string koi2utf (const(char)[] s) {
45 import std.encoding : EncodingScheme, INVALID_SEQUENCE;
46 import iv.encoding;
47 auto efrom = EncodingScheme.create("koi8-u");
48 auto eto = EncodingScheme.create("utf-8");
49 ubyte[] res;
50 ubyte[8] buf;
51 const(ubyte)[] ub = cast(const(ubyte)[])s;
52 while (ub.length > 0) {
53 dchar dc = efrom.safeDecode(ub);
54 if (dc == INVALID_SEQUENCE) dc = '?';
55 auto eclen = eto.encode(dc, buf);
56 res ~= buf[0..eclen];
58 return cast(string)res;
62 void showTags (string fname) {
63 auto tf = TagFile(fname);
64 if (tf.artist.length) writeln("ARTIST=", utf2koi(tf.artist));
65 if (tf.album.length) writeln("ALBUM=", utf2koi(tf.album));
66 if (tf.title.length) writeln("TITLE=", utf2koi(tf.title));
67 if (tf.genre.length) writeln("GENRE=", utf2koi(tf.genre));
68 if (tf.year) writeln("YEAR=", tf.year);
69 if (tf.track) writeln("TRACKNUMBER=", tf.track);
70 if (tf.comment.length) writeln("COMMENT=", utf2koi(tf.comment));
74 void main (string[] args) {
75 bool optAdd = false;
76 bool optTags = false;
77 string fileName;
79 void usage (string opt=null) {
80 write("
81 usage: tagtool [--add] filename [tagfile]
82 tagtool [--add] [--tags] filename [name=value]
83 short options:
84 -a --add
85 -t --tags
86 ");
87 throw new ExitException();
90 static uint s2i (string s) {
91 import std.ascii;
92 if (s.length == 0) return 0;
93 uint res = 0;
94 while (s.length) {
95 if (!isDigit(s[0])) return 0;
96 res = res*10+s[0]-'0';
97 s = s[1..$];
99 return res;
102 try {
103 import std.getopt;
104 getopt(args, std.getopt.config.caseSensitive, std.getopt.config.bundling, std.getopt.config.stopOnFirstNonOption,
105 "add|a", &optAdd,
106 "tags|t", &optTags,
107 "help|h", &usage
109 } catch (Exception e) {
110 errwriteln("FATAL: ", e.msg);
111 throw new ExitException();
113 if (args.length < 2) usage();
114 fileName = args[1];
115 args = args[2..$];
117 try {
118 if (args.length == 0) {
119 showTags(fileName);
120 } else {
121 string artist, album, title, genre, comment;
122 uint year, track;
123 bool artistChanged, albumChanged, titleChanged, genreChanged, commentChanged, yearChanged, trackChanged;
124 if (optAdd) {
125 // load original tags
126 auto tf = TagFile(fileName);
127 if (tf.artist.length) artist = tf.artist;
128 if (tf.album.length) album = tf.album;
129 if (tf.title.length) title = tf.title;
130 if (tf.genre.length) genre = tf.genre;
131 if (tf.comment.length) comment = tf.comment;
132 if (tf.year) year = tf.year;
133 if (tf.track) track = tf.track;
134 debug {
135 writeln("optadd!");
136 writeln(" ARTIST=", utf2koi(artist));
137 writeln(" ALBUM=", utf2koi(album));
138 writeln(" TITLE=", utf2koi(title));
139 writeln(" GENRE=", utf2koi(genre));
140 writeln(" COMMENT=", utf2koi(comment));
141 writeln(" YEAR=", year);
142 writeln(" TRACKNUMBER=", track);
146 void processLn (string v) {
147 import std.string : indexOf, strip, toUpper;
148 v = koi2utf(v.idup);
149 v = v.strip;
150 if (v.length == 0 || v[0] == ';' || v[0] == '#') return;
151 auto ep = v.indexOf('=');
152 if (ep < 1) return;
153 string name = v[0..ep].toUpper;
154 v = v[ep+1..$].strip;
155 debug writeln("<", name, ">=<", v, ">");
156 uint tmp;
157 switch (name) {
158 case "ARTIST": artistChanged = (artist != v); artist = v; break;
159 case "ALBUM": albumChanged = (album != v); album = v; break;
160 case "TITLE": titleChanged = (title != v); title = v; break;
161 case "GENRE": genreChanged = (genre != v); genre = v; break;
162 case "COMMENT": commentChanged = (comment != v); comment = v; break;
163 case "YEAR": case "DATE": tmp = s2i(v); yearChanged = (year != tmp); year = tmp; break;
164 case "TRACK": case "TRACKNUMBER": tmp = s2i(v); trackChanged = (track != tmp); track = tmp; break;
165 default:
169 if (optTags) {
170 foreach (tag; args) processLn(tag);
171 } else if (args[0] != "-") {
172 foreach (fname; args) {
173 import std.stdio : File;
174 foreach (auto ln; File(fname, "r").byLine) processLn(cast(string)ln);
176 } else {
177 import std.stdio : stdin;
178 foreach (auto ln; stdin.byLine) processLn(cast(string)ln);
180 debug {
181 writeln("final:");
182 writeln(" ARTIST=", utf2koi(artist));
183 writeln(" ALBUM=", utf2koi(album));
184 writeln(" TITLE=", utf2koi(title));
185 writeln(" GENRE=", utf2koi(genre));
186 writeln(" COMMENT=", utf2koi(comment));
187 writeln(" YEAR=", year);
188 writeln(" TRACKNUMBER=", track);
190 // write it
191 if (artistChanged || albumChanged || titleChanged || genreChanged || commentChanged || yearChanged || trackChanged) {
192 auto tf = TagFile(fileName);
193 tf.artist = artist;
194 tf.album = album;
195 tf.title = title;
196 tf.genre = genre;
197 tf.comment = comment;
198 tf.year = year;
199 tf.track = track;
200 tf.save();
203 } catch(Exception e) {
204 debug writeln("EXPECTION: ", e.msg);