From 3e3e1465c0ac23606dd69bd84c325b6c648b9006 Mon Sep 17 00:00:00 2001 From: ketmar Date: Sat, 14 Mar 2015 19:00:20 +0000 Subject: [PATCH] taglib wrapper moved to "iv"; tagtool code cleanup FossilOrigin-Name: fea9294ec12a958d3f7b8a5ec2829c75d6a8ae47ad77b2f1cbdb48624fe2435e --- taglib.d | 450 -------------------------------------------------------------- tagtool.d | 78 ++++++----- 2 files changed, 42 insertions(+), 486 deletions(-) delete mode 100644 taglib.d diff --git a/taglib.d b/taglib.d deleted file mode 100644 index afa320c..0000000 --- a/taglib.d +++ /dev/null @@ -1,450 +0,0 @@ -///WARING! this MAY BE 64-bit unsafe! -module taglib; - -pragma(lib, "tag_c"); -pragma(lib, "tag"); - - -//////////////////////////////////////////////////////////////////////////////// -import std.conv; -import std.string; - - -class TagLibException : Exception { - this (string msg, string file=__FILE__, size_t line=__LINE__, Throwable next=null) @safe @pure @nothrow { - super(msg, file, line, next); - } -} - - -struct TagFile { - enum FileType { - Autodetect=-1, // k8 extension, do not use in C API! - MPEG, - OggVorbis, - FLAC, - MPC, - OggFlac, - WavPack, - Speex, - TrueAudio, - MP4, - ASF - } - - - this (string fname, FileType type=FileType.Autodetect) @trusted { - loadInfo(fname, type); - } - - ~this () @trusted { - clear(); - } - - void clear () @trusted { - if (mInited) { - mInited = false; - taglib_file_free(mFL); - mFL = null; - mTags = null; - mFName = mArtist = mAlbum = mTitle = mComment = mGenre = null; - mYear = mTrack = 0; - } - } - - void save () @trusted { - if (!mInited) throw new TagLibException("can't save tags object to empty file"); - if (!taglib_file_save(mFL)) throw new TagLibException("can't save tags object to file '"~mFName~"'"); - } - - @property bool valid () const @trusted @nothrow { return mInited; } - - @property string filename () const @trusted @nothrow { return (mInited ? mFName : null); } - - mixin(strPropMixin("artist")); - mixin(strPropMixin("album")); - mixin(strPropMixin("title")); - mixin(strPropMixin("genre")); - mixin(strPropMixin("comment")); - - @property uint year () const @trusted @nothrow { return (mInited ? mYear : 0); } - @property void year (uint v) @trusted { - if (!mInited) throw new TagLibException("can't set YEAR tag for empty file"); - if (v > 0) { - uint ov; - if (v < 50) v += 2000; - else if (v < 100) v += 1900; - else if (v < 1930) v = 0; - if (v < 1930 || v > 2099) throw new TagLibException("invalid YEAR tag value: "~to!string(ov)); - } - mYear = v; - taglib_tag_set_year(mTags, v); - } - - @property uint track () const @trusted @nothrow { return (mInited ? mTrack : 0); } - @property void track (uint v) @trusted { - if (!mInited) throw new TagLibException("can't set TRACK tag for empty file"); - if (v > 999) throw new TagLibException("invalid TRACK tag value: "~to!string(v)); - mTrack = v; - taglib_tag_set_track(mTags, v); - } - - mixin(uintPropMixin("length")); // file length in seconds - mixin(uintPropMixin("channels")); // number of channels in file - mixin(uintPropMixin("bitrate")); // file bitrate in kb/s - mixin(uintPropMixin("samplerate")); // file samplerate in Hz - -private: - static string uintPropMixin (string propName) @pure @safe @nothrow { - return - `@property uint `~propName~` () @trusted {`~ - `if (!mInited) return 0;`~ - `auto tp = taglib_file_audioproperties(mFL);`~ - `if (!tp) throw new TagLibException("can't get audio properties for file '"~mFName~"'");`~ - `auto r = taglib_audioproperties_`~propName~`(tp);`~ - `return (r < 0 ? 0 : r);`~ - `}`; - } - - static string strPropMixin (string propName) @pure @safe { - import std.string; - return - `@property void `~propName~` (string v) @trusted {`~ - `if (!mInited) throw new TagLibException("can't set `~toUpper(propName)~` tag for empty file");`~ - `string s = trimStr(v);`~ - `taglib_tag_set_`~propName~`(mTags, s.toStringz);`~ - `m`~capitalize(propName)~` = s.idup;`~ - `}`~ - `@property string `~propName~` () const @trusted @nothrow { return (mInited ? m`~capitalize(propName)~` : null); }`; - } - - static string stripL (string str) @trusted { - import std.uni; - foreach (i, dchar c; str) if (c != '_' && !std.uni.isWhite(c)) return str[i..$]; - return str[$..$]; - } - - static string stripR (string str) @trusted { - import std.uni, std.utf; - foreach_reverse (i, dchar c; str) if (c != '_' && !std.uni.isWhite(c)) return str[0..i+codeLength!char(c)]; - return str[0..0]; - } - - static string trimStr (string s) @trusted { - import std.array; - auto res = appender!string(); - dchar pch = 0; - foreach (dchar ch; s) { - if (ch < ' ') ch = ' '; - // remove duplicate underlines - if (pch == '_' && ch == '_') { pch = ch; continue; } - // remove duplicate spaces - if (pch == ' ' && ch == ' ') { pch = ch; continue; } - res.put(ch); - } - return stripL(stripR(res.data)); - } - - static string trimStr (char* s) @trusted { - if (s) { - auto res = trimStr(to!string(s)); - taglib_free(s); - return res; - } - return ""; - } - - void loadInfo (string fname, FileType type=FileType.Autodetect) @trusted { - clear(); - if (type == FileType.Autodetect) { - mFL = taglib_file_new(fname.toStringz); - } else { - mFL = taglib_file_new_type(fname.toStringz, cast(TagLibFileType)type); - } - scope(failure) clear(); - if (!mFL) throw new TagLibException("can't open file '"~fname~"'"); - mTags = taglib_file_tag(mFL); - if (!mTags) throw new TagLibException("can't init tags object for file '"~fname~"'"); - mArtist = trimStr(taglib_tag_artist(mTags)); - mAlbum = trimStr(taglib_tag_album(mTags)); - mTitle = trimStr(taglib_tag_title(mTags)); - mGenre = trimStr(taglib_tag_genre(mTags)); - mYear = taglib_tag_year(mTags); - if (mYear > 0) { - if (mYear < 50) mYear += 2000; - else if (mYear < 100) mYear += 1900; - else if (mYear < 1930) mYear = 0; - } - if (mYear < 1930 || mYear > 2099) mYear = 0; - mTrack = taglib_tag_track(mTags); - if (mTrack > 999) mTrack = 0; - mFName = fname.idup; - mInited = true; - } - -private: - bool mInited; - TagLibFile mFL; - TagLibTag mTags; - string mFName; - string mArtist; - string mAlbum; - string mTitle; - string mComment; - string mGenre; - uint mYear; - uint mTrack; -} - - -//////////////////////////////////////////////////////////////////////////////// -shared static this () { - taglib_set_strings_unicode(true); - taglib_set_string_management_enabled(false); -} - - -//////////////////////////////////////////////////////////////////////////////// -private: -extern(C): -@nothrow: -@trusted: - -struct TagLibFileT {} -struct TagLibTagT {} -struct TagLibAudioPropertiesT {} - -alias TagLibFile = TagLibFileT*; -alias TagLibTag = TagLibTagT*; -alias TagLibAudioProperties = TagLibAudioPropertiesT*; -alias TagBool = uint; -alias TagCString = const(char)*; - - -/*! - * By default all strings coming into or out of TagLib's C API are in UTF8. - * However, it may be desirable for TagLib to operate on Latin1 (ISO-8859-1) - * strings in which case this should be set to FALSE. - */ -void taglib_set_strings_unicode (TagBool unicode); - -/*! - * TagLib can keep track of strings that are created when outputting tag values - * and clear them using taglib_tag_clear_strings(). This is enabled by default. - * However if you wish to do more fine grained management of strings, you can do - * so by setting \a management to FALSE. - */ -void taglib_set_string_management_enabled (TagBool management); - -/*! - * Explicitly free a string returned from TagLib - */ -void taglib_free (void* pointer); - - -/******************************************************************************* - * File API - ******************************************************************************/ -enum TagLibFileType { - MPEG, - OggVorbis, - FLAC, - MPC, - OggFlac, - WavPack, - Speex, - TrueAudio, - MP4, - ASF -} - -/*! - * Creates a TagLib file based on \a filename. TagLib will try to guess the file - * type. - * - * \returns NULL if the file type cannot be determined or the file cannot - * be opened. - */ -TagLibFile taglib_file_new (TagCString filename); - -/*! - * Creates a TagLib file based on \a filename. Rather than attempting to guess - * the type, it will use the one specified by \a type. - */ -TagLibFile taglib_file_new_type (TagCString filename, TagLibFileType type); - -/*! - * Frees and closes the file. - */ -void taglib_file_free (TagLibFile file); - -/*! - * Returns true if the file is open and readble and valid information for - * the Tag and / or AudioProperties was found. - */ - -TagBool taglib_file_is_valid (const(TagLibFile) file); - -/*! - * Returns a pointer to the tag associated with this file. This will be freed - * automatically when the file is freed. - */ -TagLibTag taglib_file_tag (const(TagLibFile) file); - -/*! - * Returns a pointer to the the audio properties associated with this file. This - * will be freed automatically when the file is freed. - */ -const(TagLibAudioProperties) taglib_file_audioproperties (const(TagLibFile) file); - -/*! - * Saves the \a file to disk. - */ -TagBool taglib_file_save (TagLibFile file); - - -/****************************************************************************** - * Tag API - ******************************************************************************/ - -/*! - * Returns a string with this tag's title. - * - * \note By default this string should be UTF8 encoded and its memory should be - * freed using taglib_tag_free_strings(). - */ -char *taglib_tag_title (const(TagLibTag) tag); - -/*! - * Returns a string with this tag's artist. - * - * \note By default this string should be UTF8 encoded and its memory should be - * freed using taglib_tag_free_strings(). - */ -char *taglib_tag_artist (const(TagLibTag) tag); - -/*! - * Returns a string with this tag's album name. - * - * \note By default this string should be UTF8 encoded and its memory should be - * freed using taglib_tag_free_strings(). - */ -char *taglib_tag_album (const(TagLibTag) tag); - -/*! - * Returns a string with this tag's comment. - * - * \note By default this string should be UTF8 encoded and its memory should be - * freed using taglib_tag_free_strings(). - */ -char *taglib_tag_comment (const(TagLibTag) tag); - -/*! - * Returns a string with this tag's genre. - * - * \note By default this string should be UTF8 encoded and its memory should be - * freed using taglib_tag_free_strings(). - */ -char *taglib_tag_genre (const(TagLibTag) tag); - -/*! - * Returns the tag's year or 0 if year is not set. - */ -uint taglib_tag_year (const(TagLibTag) tag); - -/*! - * Returns the tag's track number or 0 if track number is not set. - */ -uint taglib_tag_track (const(TagLibTag) tag); - -/*! - * Sets the tag's title. - * - * \note By default this string should be UTF8 encoded. - */ -void taglib_tag_set_title (TagLibTag tag, TagCString title); - -/*! - * Sets the tag's artist. - * - * \note By default this string should be UTF8 encoded. - */ -void taglib_tag_set_artist (TagLibTag tag, TagCString artist); - -/*! - * Sets the tag's album. - * - * \note By default this string should be UTF8 encoded. - */ -void taglib_tag_set_album (TagLibTag tag, TagCString album); - -/*! - * Sets the tag's comment. - * - * \note By default this string should be UTF8 encoded. - */ -void taglib_tag_set_comment (TagLibTag tag, TagCString comment); - -/*! - * Sets the tag's genre. - * - * \note By default this string should be UTF8 encoded. - */ -void taglib_tag_set_genre (TagLibTag tag, TagCString genre); - -/*! - * Sets the tag's year. 0 indicates that this field should be cleared. - */ -void taglib_tag_set_year (TagLibTag tag, uint year); - -/*! - * Sets the tag's track number. 0 indicates that this field should be cleared. - */ -void taglib_tag_set_track (TagLibTag tag, uint track); - -/*! - * Frees all of the strings that have been created by the tag. - */ -void taglib_tag_free_strings (); - - -/****************************************************************************** - * Audio Properties API - ******************************************************************************/ - -/*! - * Returns the length of the file in seconds. - */ -int taglib_audioproperties_length (const(TagLibAudioProperties) audioProperties); - -/*! - * Returns the bitrate of the file in kb/s. - */ -int taglib_audioproperties_bitrate (const(TagLibAudioProperties) audioProperties); - -/*! - * Returns the sample rate of the file in Hz. - */ -int taglib_audioproperties_samplerate (const(TagLibAudioProperties) audioProperties); - -/*! - * Returns the number of channels in the audio stream. - */ -int taglib_audioproperties_channels (const(TagLibAudioProperties) audioProperties); - - -/******************************************************************************* - * Special convenience ID3v2 functions - *******************************************************************************/ -enum TagLibID3v2Encoding { - Latin1, - UTF16, - UTF16BE, - UTF8 -} - - -/*! - * This sets the default encoding for ID3v2 frames that are written to tags. - */ -void taglib_id3v2_set_default_text_encoding (TagLibID3v2Encoding encoding); diff --git a/tagtool.d b/tagtool.d index 5646565..ba9cee9 100644 --- a/tagtool.d +++ b/tagtool.d @@ -1,4 +1,3 @@ -module tagtool; /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar) * Understanding is not required. Only obedience. * @@ -15,19 +14,18 @@ module tagtool; * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -import core.exception; -import std.array; -import std.conv; -import std.encoding; -import std.getopt; -import std.stdio; -import std.string; +module tagtool is aliced; +private: -import taglib; -import k8.encoding; +import core.exception : ExitException; + +import iv.taglib; +import iv.writer; string utf2koi (const(char)[] s) { + import std.encoding : EncodingScheme, INVALID_SEQUENCE; + import iv.encoding; auto efrom = EncodingScheme.create("utf-8"); auto eto = EncodingScheme.create("koi8-u"); ubyte[] res; @@ -44,6 +42,8 @@ string utf2koi (const(char)[] s) { string koi2utf (const(char)[] s) { + import std.encoding : EncodingScheme, INVALID_SEQUENCE; + import iv.encoding; auto efrom = EncodingScheme.create("koi8-u"); auto eto = EncodingScheme.create("utf-8"); ubyte[] res; @@ -59,7 +59,7 @@ string koi2utf (const(char)[] s) { } -private void showTags (string fname) { +void showTags (string fname) { auto tf = TagFile(fname); if (tf.artist.length) writeln("ARTIST=", utf2koi(tf.artist)); if (tf.album.length) writeln("ALBUM=", utf2koi(tf.album)); @@ -77,20 +77,17 @@ void main (string[] args) { string fileName; void usage (string opt=null) { - import std.c.process; - stdout.write(" + write(" usage: tagtool [--add] filename [tagfile] tagtool [--add] [--tags] filename [name=value] short options: -a --add -t --tags "); - //throw new Exception("nothing to do"); throw new ExitException(); - //exit(1); } - uint s2i (string s) { + static uint s2i (string s) { import std.ascii; if (s.length == 0) return 0; uint res = 0; @@ -103,13 +100,14 @@ short options: } try { + import std.getopt; getopt(args, std.getopt.config.caseSensitive, std.getopt.config.bundling, std.getopt.config.stopOnFirstNonOption, "add|a", &optAdd, "tags|t", &optTags, "help|h", &usage ); } catch (Exception e) { - stderr.writeln("FATAL: ", e.msg); + errwriteln("FATAL: ", e.msg); throw new ExitException(); } if (args.length < 2) usage(); @@ -122,6 +120,7 @@ short options: } else { string artist, album, title, genre, comment; uint year, track; + bool artistChanged, albumChanged, titleChanged, genreChanged, commentChanged, yearChanged, trackChanged; if (optAdd) { // load original tags auto tf = TagFile(fileName); @@ -145,6 +144,7 @@ short options: } void processLn (string v) { + import std.string : indexOf, strip, toUpper; v = koi2utf(v.idup); v = v.strip; if (v.length == 0 || v[0] == ';' || v[0] == '#') return; @@ -152,15 +152,16 @@ short options: if (ep < 1) return; string name = v[0..ep].toUpper; v = v[ep+1..$].strip; - debug stdout.writeln("<", name, ">=<", v, ">"); + debug writeln("<", name, ">=<", v, ">"); + uint tmp; switch (name) { - case "ARTIST": artist = v; break; - case "ALBUM": album = v; break; - case "TITLE": title = v; break; - case "GENRE": genre = v; break; - case "COMMENT": comment = v; break; - case "YEAR": case "DATE": year = s2i(v); break; - case "TRACK": case "TRACKNUMBER": track = s2i(v); break; + case "ARTIST": artistChanged = (artist != v); artist = v; break; + case "ALBUM": albumChanged = (album != v); album = v; break; + case "TITLE": titleChanged = (title != v); title = v; break; + case "GENRE": genreChanged = (genre != v); genre = v; break; + case "COMMENT": commentChanged = (comment != v); comment = v; break; + case "YEAR": case "DATE": tmp = s2i(v); yearChanged = (year != tmp); year = tmp; break; + case "TRACK": case "TRACKNUMBER": tmp = s2i(v); trackChanged = (track != tmp); track = tmp; break; default: } } @@ -169,10 +170,12 @@ short options: foreach (tag; args) processLn(tag); } else if (args[0] != "-") { foreach (fname; args) { - foreach (ln; File(fname, "r").byLine) processLn(cast(string)ln); + import std.stdio : File; + foreach (auto ln; File(fname, "r").byLine) processLn(cast(string)ln); } } else { - foreach (ln; stdin.byLine) processLn(cast(string)ln); + import std.stdio : stdin; + foreach (auto ln; stdin.byLine) processLn(cast(string)ln); } debug { writeln("final:"); @@ -184,15 +187,18 @@ short options: writeln(" YEAR=", year); writeln(" TRACKNUMBER=", track); } - auto tf = TagFile(fileName); - tf.artist = artist; - tf.album = album; - tf.title = title; - tf.genre = genre; - tf.comment = comment; - tf.year = year; - tf.track = track; - tf.save(); + // write it + if (artistChanged || albumChanged || titleChanged || genreChanged || commentChanged || yearChanged || trackChanged) { + auto tf = TagFile(fileName); + tf.artist = artist; + tf.album = album; + tf.title = title; + tf.genre = genre; + tf.comment = comment; + tf.year = year; + tf.track = track; + tf.save(); + } } } catch(Exception e) { debug writeln("EXPECTION: ", e.msg); -- 2.11.4.GIT