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
;
20 import core
.exception
: ExitException
;
26 string
utf2koi (const(char)[] s
) {
27 import std
.encoding
: EncodingScheme
, INVALID_SEQUENCE
;
29 auto efrom
= EncodingScheme
.create("utf-8");
30 auto eto
= EncodingScheme
.create("koi8-u");
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
= '?';
40 return cast(string
)res
;
44 string
koi2utf (const(char)[] s
) {
45 import std
.encoding
: EncodingScheme
, INVALID_SEQUENCE
;
47 auto efrom
= EncodingScheme
.create("koi8-u");
48 auto eto
= EncodingScheme
.create("utf-8");
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
);
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
) {
79 void usage (string opt
=null) {
81 usage: tagtool [--add] filename [tagfile]
82 tagtool [--add] [--tags] filename [name=value]
87 throw new ExitException();
90 static uint s2i (string s
) {
92 if (s
.length
== 0) return 0;
95 if (!isDigit(s
[0])) return 0;
96 res
= res
*10+s
[0]-'0';
104 getopt(args
, std
.getopt
.config
.caseSensitive
, std
.getopt
.config
.bundling
, std
.getopt
.config
.stopOnFirstNonOption
,
109 } catch (Exception e
) {
110 errwriteln("FATAL: ", e
.msg
);
111 throw new ExitException();
113 if (args
.length
< 2) usage();
118 if (args
.length
== 0) {
121 string artist
, album
, title
, genre
, comment
;
123 bool artistChanged
, albumChanged
, titleChanged
, genreChanged
, commentChanged
, yearChanged
, trackChanged
;
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
;
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
;
150 if (v
.length
== 0 || v
[0] == ';' || v
[0] == '#') return;
151 auto ep
= v
.indexOf('=');
153 string name
= v
[0..ep
].toUpper
;
154 v
= v
[ep
+1..$].strip
;
155 debug writeln("<", name
, ">=<", v
, ">");
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;
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
);
177 import std
.stdio
: stdin
;
178 foreach (auto ln
; stdin
.byLine
) processLn(cast(string
)ln
);
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
);
191 if (artistChanged || albumChanged || titleChanged || genreChanged || commentChanged || yearChanged || trackChanged
) {
192 auto tf
= TagFile(fileName
);
197 tf
.comment
= comment
;
203 } catch(Exception e
) {
204 debug writeln("EXPECTION: ", e
.msg
);