ruby-taglib2 1.01
Sign up to get free protection for your applications and to get access to all the features.
- data/README +178 -0
- data/ext/extconf.rb +61 -0
- data/ext/ruby-taglib2.cpp +466 -0
- data/lib/ruby_taglib2.rb +6 -0
- metadata +58 -0
data/README
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
ruby-taglib2 is a compiled extension to ruby that provides access to the TagLib
|
2
|
+
library. ruby-taglib2 optionally uses the Mahoro library when available for
|
3
|
+
much more accurate detection of file formats (TagLib without Mahoro just looks
|
4
|
+
at the last few characters of the file name, while Mahoro looks at the
|
5
|
+
content).
|
6
|
+
|
7
|
+
ruby-taglib and ruby-taglib2: http://www.hakubi.us/ruby-taglib/
|
8
|
+
|
9
|
+
TagLib: http://developer.kde.org/~wheeler/taglib.html
|
10
|
+
|
11
|
+
Mahoro: http://mahoro.rubyforge.org/
|
12
|
+
|
13
|
+
#############
|
14
|
+
Installation#
|
15
|
+
#############
|
16
|
+
|
17
|
+
gem install ruby-taglib2 (gemcutter.org)
|
18
|
+
|
19
|
+
#######
|
20
|
+
Modules
|
21
|
+
#######
|
22
|
+
|
23
|
+
TagLib2
|
24
|
+
|
25
|
+
#######
|
26
|
+
Classes
|
27
|
+
#######
|
28
|
+
|
29
|
+
TagLib2::File - The central class that users of ruby-taglib2 will always use
|
30
|
+
TagLib2::Image - Stores information about images embedded in tags, can be gotten
|
31
|
+
through methods of TagLib2::File
|
32
|
+
TagLib2::FileRef - Internal use only
|
33
|
+
TagLib2::BadFile - Exception thrown when the file passed to TagLib2::File's
|
34
|
+
initializer cannot be loaded
|
35
|
+
|
36
|
+
#######
|
37
|
+
Methods
|
38
|
+
#######
|
39
|
+
|
40
|
+
All strings are to be in UTF8 format. Set KCODE to u when necessary.
|
41
|
+
|
42
|
+
TagLib2::File#title(string)
|
43
|
+
TagLib2::File#title=(string) - Get and set the title of a file
|
44
|
+
|
45
|
+
TagLib2::File#artist(string)
|
46
|
+
TagLib2::File#artist=(string) - Get and set the artist of a file
|
47
|
+
|
48
|
+
TagLib2::File#album(string)
|
49
|
+
TagLib2::File#album=(string) - Get and set the album the file is from
|
50
|
+
|
51
|
+
TagLib2::File#comment(string)
|
52
|
+
TagLib2::File#comment=(string) - Get and set a comment about the file
|
53
|
+
|
54
|
+
TagLib2::File#genre(string)
|
55
|
+
TagLib2::File#genre=(string) - Get and set a comment about the file
|
56
|
+
|
57
|
+
TagLib2::File#year(num)
|
58
|
+
TagLib2::File#year=(num) - Get and set the year of the file
|
59
|
+
|
60
|
+
TagLib2::File#track(num)
|
61
|
+
TagLib2::File#track=(num) - Get and set the the track number of the file on the
|
62
|
+
album
|
63
|
+
|
64
|
+
TagLib2::File#bitrate
|
65
|
+
TagLib2::File#sampleRate
|
66
|
+
TagLib2::File#sample_rate
|
67
|
+
TagLib2::File#channels
|
68
|
+
TagLib2::File#length - Get properties of the audio in the file
|
69
|
+
|
70
|
+
TagLib2::File#save - Save all changes to the tag
|
71
|
+
|
72
|
+
TagLib2::File#each_image
|
73
|
+
TagLib2::File#eachImage - Iterator over all images stored in the file's tag
|
74
|
+
|
75
|
+
TagLib2::File#image_count
|
76
|
+
TagLib2::File#imageCount - Number of images in the file's tag
|
77
|
+
|
78
|
+
TagLib2::File#image(num) - Returns image #num in order from eachImage
|
79
|
+
|
80
|
+
TagLib2::File#add_image(type, mimeType, description, data)
|
81
|
+
TagLib2::File#addImage(type, mimeType, description, data) - Add an image
|
82
|
+
type is an integer, the rest are strings. For
|
83
|
+
a remote image, set data to nil and place the
|
84
|
+
URL in mimeType. Otherwise mimeType should be
|
85
|
+
image/jpeg or image/png. description may be set
|
86
|
+
nil as well.
|
87
|
+
|
88
|
+
TagLib2::File#remove_image(image)
|
89
|
+
TagLib2::File#removeImage(image) - Remove an image from the tag, as returned by
|
90
|
+
#image
|
91
|
+
|
92
|
+
TagLib2::Image#type - Returns a number of the type
|
93
|
+
|
94
|
+
TagLib2::Image#description - Returns a string of the description
|
95
|
+
|
96
|
+
TagLib2::Image#mime_type
|
97
|
+
TagLib2::Image#mimeType - Returns a the mimeType of the data, or a URL
|
98
|
+
|
99
|
+
TagLib2::Image#data - Returns the actual image data, if mimeType is not a URL
|
100
|
+
|
101
|
+
######
|
102
|
+
Images
|
103
|
+
######
|
104
|
+
|
105
|
+
Image are only supported right now when in ID3v2 tags. Here is a list of what
|
106
|
+
the picture type numbers mean:
|
107
|
+
|
108
|
+
Picture type: 0x00 Other
|
109
|
+
0x01 32x32 pixels 'file icon' (PNG only)
|
110
|
+
0x02 Other file icon
|
111
|
+
0x03 Cover (front)
|
112
|
+
0x04 Cover (back)
|
113
|
+
0x05 Leaflet page
|
114
|
+
0x06 Media (e.g. label side of CD)
|
115
|
+
0x07 Lead artist/lead performer/soloist
|
116
|
+
0x08 Artist/performer
|
117
|
+
0x09 Conductor
|
118
|
+
0x0A Band/Orchestra
|
119
|
+
0x0B Composer
|
120
|
+
0x0C Lyricist/text writer
|
121
|
+
0x0D Recording Location
|
122
|
+
0x0E During recording
|
123
|
+
0x0F During performance
|
124
|
+
0x10 Movie/video screen capture
|
125
|
+
0x11 A bright coloured fish
|
126
|
+
0x12 Illustration
|
127
|
+
0x13 Band/artist logotype
|
128
|
+
0x14 Publisher/Studio logotype
|
129
|
+
|
130
|
+
Except where noted, all images should be of type image/jpeg or image/png.
|
131
|
+
|
132
|
+
#############
|
133
|
+
Sample of use
|
134
|
+
#############
|
135
|
+
|
136
|
+
require 'taglib2'
|
137
|
+
|
138
|
+
f = TagLib2::File.new(ARGV[0])
|
139
|
+
|
140
|
+
puts "Title: #{f.title}"
|
141
|
+
puts "Comment: #{f.comment}"
|
142
|
+
puts "Artist: #{f.artist}"
|
143
|
+
puts "Album: #{f.album}"
|
144
|
+
puts "Length: #{f.length}"
|
145
|
+
puts "Sample Rate: #{f.sampleRate}"
|
146
|
+
puts "Image Count: #{f.imageCount}"
|
147
|
+
if f.imageCount > 0
|
148
|
+
puts "Image 0 MimeType: #{f.image(0).mimeType}"
|
149
|
+
puts "Image 0 Type: #{f.image(0).type}"
|
150
|
+
puts "Image 0 Description: #{f.image(0).description}"
|
151
|
+
end
|
152
|
+
|
153
|
+
if ARGV[1] == '-setTitle'
|
154
|
+
puts "Setting Title to #{ARGV[2]}"
|
155
|
+
f.title = ARGV[2]
|
156
|
+
f.save
|
157
|
+
end
|
158
|
+
|
159
|
+
if ARGV[1] == '-dumpImage'
|
160
|
+
puts "Dumping image"
|
161
|
+
File::open(ARGV[3], 'w') do |file|
|
162
|
+
file.write(f.image(ARGV[2].to_i).data)
|
163
|
+
end
|
164
|
+
f.save
|
165
|
+
end
|
166
|
+
|
167
|
+
if ARGV[1] == '-removeImage'
|
168
|
+
puts "Removing image #{ARGV[2]}"
|
169
|
+
f.removeImage(f.image(ARGV[2].to_i))
|
170
|
+
f.save
|
171
|
+
end
|
172
|
+
|
173
|
+
if ARGV[1] == '-addImage'
|
174
|
+
puts "Adding image"
|
175
|
+
puts "f.addImage(#{ARGV[2].to_i}, #{ARGV[3]}, #{ARGV[4]}, IO::read(#{ARGV[5]}))"
|
176
|
+
f.addImage(ARGV[2].to_i, ARGV[3], ARGV[4], IO::read(ARGV[5]))
|
177
|
+
f.save
|
178
|
+
end
|
data/ext/extconf.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
# We need C++, not C, because Taglib is in C++
|
4
|
+
CONFIG['CC'] = 'g++'
|
5
|
+
CONFIG['CPP'].sub!(CONFIG['CPP'], 'g++ -E')
|
6
|
+
CONFIG['LDSHARED'].sub!(CONFIG['CC'], 'g++')
|
7
|
+
|
8
|
+
RbConfig::CONFIG['CC'] = 'g++'
|
9
|
+
RbConfig::CONFIG['CC'] = 'g++ -E'
|
10
|
+
|
11
|
+
# taglib-config does not implement libs-only-l, so we copy and paste pkg_config
|
12
|
+
# from mkmf then remove the call to taglib-config --libs-only-l
|
13
|
+
def pkg_config(pkg)
|
14
|
+
if pkgconfig = with_config("#{pkg}-config") and find_executable0(pkgconfig)
|
15
|
+
# iff package specific config command is given
|
16
|
+
get = proc {|opt| `#{pkgconfig} --#{opt}`.chomp}
|
17
|
+
elsif ($PKGCONFIG ||=
|
18
|
+
(pkgconfig = with_config("pkg-config", ("pkg-config" unless CROSS_COMPILING))) &&
|
19
|
+
find_executable0(pkgconfig) && pkgconfig) and
|
20
|
+
system("#{$PKGCONFIG} --exists #{pkg}")
|
21
|
+
# default to pkg-config command
|
22
|
+
get = proc {|opt| `#{$PKGCONFIG} --#{opt} #{pkg}`.chomp}
|
23
|
+
elsif find_executable0(pkgconfig = "#{pkg}-config")
|
24
|
+
# default to package specific config command, as a last resort.
|
25
|
+
get = proc {|opt| `#{pkgconfig} --#{opt}`.chomp}
|
26
|
+
end
|
27
|
+
if get
|
28
|
+
cflags = get['cflags']
|
29
|
+
ldflags = get['libs']
|
30
|
+
libs = ''#get['libs-only-l']
|
31
|
+
ldflags = (Shellwords.shellwords(ldflags) - Shellwords.shellwords(libs)).quote.join(" ")
|
32
|
+
$CFLAGS += " " << cflags
|
33
|
+
$LDFLAGS += " " << ldflags
|
34
|
+
$libs += " " << libs
|
35
|
+
Logging::message "package configuration for %s\n", pkg
|
36
|
+
Logging::message "cflags: %s\nldflags: %s\nlibs: %s\n\n",
|
37
|
+
cflags, ldflags, libs
|
38
|
+
[cflags, ldflags, libs]
|
39
|
+
else
|
40
|
+
Logging::message "package configuration for %s is not found\n", pkg
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
pkg_config('taglib')
|
46
|
+
|
47
|
+
testCode = <<-EOD
|
48
|
+
#include <tag.h>
|
49
|
+
int main(int, char **)
|
50
|
+
{
|
51
|
+
return 0;
|
52
|
+
}
|
53
|
+
EOD
|
54
|
+
|
55
|
+
if try_compile(testCode)
|
56
|
+
create_makefile('taglib2')
|
57
|
+
else
|
58
|
+
puts <<-EOD
|
59
|
+
Taglib not found. Please ensure taglib-config is in your PATH.
|
60
|
+
EOD
|
61
|
+
end
|
@@ -0,0 +1,466 @@
|
|
1
|
+
#include <attachedpictureframe.h>
|
2
|
+
#include <fileref.h>
|
3
|
+
#include <flacfile.h>
|
4
|
+
#include <id3v2frame.h>
|
5
|
+
#include <id3v2tag.h>
|
6
|
+
#include <mpegfile.h>
|
7
|
+
#include <tag.h>
|
8
|
+
#include <vorbisfile.h>
|
9
|
+
|
10
|
+
// Avoid redefinition warning on Mac OS
|
11
|
+
#ifdef __APPLE__
|
12
|
+
#undef alloca
|
13
|
+
#endif
|
14
|
+
|
15
|
+
extern "C"
|
16
|
+
{
|
17
|
+
#include "ruby.h"
|
18
|
+
}
|
19
|
+
|
20
|
+
////////////////////////
|
21
|
+
// Storage for module TagLib2 and its classes
|
22
|
+
////////////////////////
|
23
|
+
static VALUE TagLib2Module;
|
24
|
+
static VALUE FileClass;
|
25
|
+
static VALUE RefClass;
|
26
|
+
static VALUE ImageClass;
|
27
|
+
static VALUE BadFile;
|
28
|
+
static VALUE BadTag;
|
29
|
+
static VALUE BadAudioProperties;
|
30
|
+
|
31
|
+
////////////////////////
|
32
|
+
// Support functions useful repeatedly for bridging TagLib strings to Ruby strings
|
33
|
+
////////////////////////
|
34
|
+
static VALUE TStrToRubyStr(const TagLib::String str)
|
35
|
+
{
|
36
|
+
const char *s = str.toCString(true);
|
37
|
+
return rb_str_new(s, strlen(s));
|
38
|
+
}
|
39
|
+
|
40
|
+
static TagLib::String RubyStrToTStr(VALUE s)
|
41
|
+
{
|
42
|
+
VALUE rStr = StringValue(s);
|
43
|
+
char *cStr = new char[RSTRING_LEN(rStr) + 1];
|
44
|
+
cStr[RSTRING_LEN(rStr)] = 0;
|
45
|
+
memcpy(cStr, RSTRING_PTR(rStr), RSTRING_LEN(rStr));
|
46
|
+
TagLib::String string(cStr, TagLib::String::UTF8);
|
47
|
+
delete[] cStr;
|
48
|
+
return string;
|
49
|
+
}
|
50
|
+
|
51
|
+
////////////////////////
|
52
|
+
// Class to integrate Mahoro type detection into TagLib
|
53
|
+
////////////////////////
|
54
|
+
class MahoroFileTypeResolver : public TagLib::FileRef::FileTypeResolver
|
55
|
+
{
|
56
|
+
public:
|
57
|
+
virtual TagLib::File *createFile(const char *, bool, TagLib::AudioProperties::ReadStyle) const;
|
58
|
+
};
|
59
|
+
|
60
|
+
TagLib::File *MahoroFileTypeResolver::createFile(const char *fileName, bool read,
|
61
|
+
TagLib::AudioProperties::ReadStyle style) const
|
62
|
+
{
|
63
|
+
if(RTEST(rb_const_get(TagLib2Module, rb_intern("MAHORO_PRESENT"))))
|
64
|
+
{
|
65
|
+
VALUE Mahoro = rb_const_get(rb_cObject, rb_intern("Mahoro"));
|
66
|
+
VALUE mahoro = rb_class_new_instance(0, 0, Mahoro);
|
67
|
+
rb_funcall(mahoro, rb_intern("flags="), 1,
|
68
|
+
rb_const_get(Mahoro, rb_intern("NONE")));
|
69
|
+
|
70
|
+
VALUE mime = rb_funcall(mahoro, rb_intern("file"), 1,
|
71
|
+
rb_str_new(fileName, strlen(fileName)));
|
72
|
+
|
73
|
+
if(RTEST(rb_funcall(mime, rb_intern("include?"), 1, rb_str_new("MP3", 3))))
|
74
|
+
{
|
75
|
+
return new TagLib::MPEG::File(fileName, read, style);
|
76
|
+
}
|
77
|
+
|
78
|
+
if(RTEST(rb_funcall(mime, rb_intern("include?"), 1, rb_str_new("Ogg", 3))) ||
|
79
|
+
RTEST(rb_funcall(mime, rb_intern("include?"), 1, rb_str_new("ogg", 3))) )
|
80
|
+
{
|
81
|
+
if(RTEST(rb_funcall(mime, rb_intern("include?"), 1, rb_str_new("Vorbis", 6))) ||
|
82
|
+
RTEST(rb_funcall(mime, rb_intern("include?"), 1, rb_str_new("vorbis", 6))) )
|
83
|
+
{
|
84
|
+
return new TagLib::Vorbis::File(fileName, read, style);
|
85
|
+
}
|
86
|
+
if(RTEST(rb_funcall(mime, rb_intern("include?"), 1, rb_str_new("FLAC", 6))) )
|
87
|
+
{
|
88
|
+
return new TagLib::FLAC::File(fileName, read, style);
|
89
|
+
}
|
90
|
+
|
91
|
+
}
|
92
|
+
}
|
93
|
+
return 0;
|
94
|
+
}
|
95
|
+
|
96
|
+
////////////////////////
|
97
|
+
// Block that requires Mahoro, which we need to be able to eat the exception when
|
98
|
+
// Mahoro is not installed
|
99
|
+
////////////////////////
|
100
|
+
static VALUE requireMahoro(VALUE)
|
101
|
+
{
|
102
|
+
return rb_require("mahoro");
|
103
|
+
}
|
104
|
+
|
105
|
+
////////////////////////
|
106
|
+
// Deallocate class TagLib2::File::FileRef
|
107
|
+
////////////////////////
|
108
|
+
static void freeRef(void *ref)
|
109
|
+
{
|
110
|
+
TagLib::FileRef *f = static_cast<TagLib::FileRef *>(ref);
|
111
|
+
delete f;
|
112
|
+
}
|
113
|
+
|
114
|
+
////////////////////////
|
115
|
+
// Initialize class TagLib2::File
|
116
|
+
////////////////////////
|
117
|
+
static VALUE FileInitialize(VALUE self, VALUE path)
|
118
|
+
{
|
119
|
+
rb_iv_set(self, "@path", path);
|
120
|
+
TagLib::FileRef *f = new TagLib::FileRef(RSTRING_PTR(StringValue(path)));
|
121
|
+
|
122
|
+
if(!f || f->isNull())
|
123
|
+
{
|
124
|
+
rb_raise(BadFile, "Unable to open file with TagLib");
|
125
|
+
return Qnil;
|
126
|
+
}
|
127
|
+
|
128
|
+
VALUE ref = Data_Wrap_Struct(RefClass, 0, freeRef, f);
|
129
|
+
rb_iv_set(self, "@ref", ref);
|
130
|
+
|
131
|
+
return self;
|
132
|
+
}
|
133
|
+
|
134
|
+
////////////////////////
|
135
|
+
// Macros to collapse code common to most of the TagLib2::File accessors
|
136
|
+
////////////////////////
|
137
|
+
#define GetFileRef \
|
138
|
+
TagLib::FileRef *f; \
|
139
|
+
Data_Get_Struct(rb_iv_get(self, "@ref"), TagLib::FileRef, f);
|
140
|
+
|
141
|
+
#define GetID3Tag \
|
142
|
+
TagLib::MPEG::File *mpegFile = dynamic_cast<TagLib::MPEG::File *>(f->file()); \
|
143
|
+
if(!mpegFile) \
|
144
|
+
return Qnil; \
|
145
|
+
\
|
146
|
+
TagLib::ID3v2::Tag *tag = mpegFile->ID3v2Tag(); \
|
147
|
+
if(!tag) \
|
148
|
+
return Qnil;
|
149
|
+
|
150
|
+
#define CheckTag(f) \
|
151
|
+
if(!f->tag()) \
|
152
|
+
{ \
|
153
|
+
rb_raise(BadTag, "TagLib produced an invalid tag"); \
|
154
|
+
return Qnil; \
|
155
|
+
}
|
156
|
+
|
157
|
+
#define CheckProperties(f) \
|
158
|
+
if(!f->audioProperties()) \
|
159
|
+
{ \
|
160
|
+
rb_raise(BadAudioProperties, "TagLib produced invalid audio properties"); \
|
161
|
+
return Qnil; \
|
162
|
+
}
|
163
|
+
|
164
|
+
|
165
|
+
#define StringAttribute(name, capName) \
|
166
|
+
static VALUE FileRead##capName(VALUE self) \
|
167
|
+
{ \
|
168
|
+
GetFileRef \
|
169
|
+
CheckTag(f) \
|
170
|
+
return TStrToRubyStr(f->tag()->name()); \
|
171
|
+
} \
|
172
|
+
static VALUE FileWrite##capName(VALUE self, VALUE arg) \
|
173
|
+
{ \
|
174
|
+
GetFileRef \
|
175
|
+
CheckTag(f) \
|
176
|
+
f->tag()->set##capName(RubyStrToTStr(arg)); \
|
177
|
+
return arg; \
|
178
|
+
}
|
179
|
+
|
180
|
+
#define UIntAttribute(name, capName) \
|
181
|
+
static VALUE FileRead##capName(VALUE self) \
|
182
|
+
{ \
|
183
|
+
GetFileRef \
|
184
|
+
CheckTag(f) \
|
185
|
+
return UINT2NUM(f->tag()->name()); \
|
186
|
+
} \
|
187
|
+
static VALUE FileWrite##capName(VALUE self, VALUE arg) \
|
188
|
+
{ \
|
189
|
+
GetFileRef \
|
190
|
+
CheckTag(f) \
|
191
|
+
f->tag()->set##capName(NUM2UINT(arg)); \
|
192
|
+
return arg; \
|
193
|
+
}
|
194
|
+
|
195
|
+
#define AttributeMethods(name, capName) \
|
196
|
+
rb_define_method(FileClass, #name, (VALUE (*)(...))FileRead##capName, 0); \
|
197
|
+
rb_define_method(FileClass, #name "=", (VALUE (*)(...))FileWrite##capName, 1);
|
198
|
+
|
199
|
+
#define IntProperty(name, capName) \
|
200
|
+
static VALUE FileRead##capName(VALUE self) \
|
201
|
+
{ \
|
202
|
+
GetFileRef \
|
203
|
+
CheckProperties(f) \
|
204
|
+
return UINT2NUM(f->audioProperties()->name()); \
|
205
|
+
}
|
206
|
+
|
207
|
+
#define PropertyMethod(name, capName) \
|
208
|
+
rb_define_method(FileClass, #name, (VALUE (*)(...))FileRead##capName, 0); \
|
209
|
+
|
210
|
+
////////////////////////
|
211
|
+
// TagLib2::File accessors
|
212
|
+
////////////////////////
|
213
|
+
|
214
|
+
StringAttribute(title, Title)
|
215
|
+
StringAttribute(artist, Artist)
|
216
|
+
StringAttribute(album, Album)
|
217
|
+
StringAttribute(comment, Comment)
|
218
|
+
StringAttribute(genre, Genre)
|
219
|
+
UIntAttribute(year, Year)
|
220
|
+
UIntAttribute(track, Track)
|
221
|
+
IntProperty(bitrate, Bitrate)
|
222
|
+
IntProperty(sampleRate, SampleRate)
|
223
|
+
IntProperty(channels, Channels)
|
224
|
+
IntProperty(length, Length)
|
225
|
+
|
226
|
+
////////////////////////
|
227
|
+
// TagLib2::File#save
|
228
|
+
////////////////////////
|
229
|
+
static VALUE FileSave(VALUE self)
|
230
|
+
{
|
231
|
+
GetFileRef
|
232
|
+
|
233
|
+
f->save();
|
234
|
+
}
|
235
|
+
|
236
|
+
////////////////////////
|
237
|
+
// TagLib2::FileSave#eachImage
|
238
|
+
////////////////////////
|
239
|
+
static VALUE FileEachImage(VALUE self)
|
240
|
+
{
|
241
|
+
GetFileRef
|
242
|
+
GetID3Tag
|
243
|
+
|
244
|
+
VALUE apic = rb_str_new("APIC", 4);
|
245
|
+
for(TagLib::ID3v2::FrameList::ConstIterator i = tag->frameList().begin();
|
246
|
+
i != tag->frameList().end(); ++i)
|
247
|
+
{
|
248
|
+
TagLib::ID3v2::AttachedPictureFrame *f = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame *>(*i);
|
249
|
+
if(f)
|
250
|
+
{
|
251
|
+
VALUE id = rb_str_new((const char *)f->frameID().data(), f->frameID().size());
|
252
|
+
if(RTEST(rb_funcall(id, rb_intern("=="), 1, apic)))
|
253
|
+
{
|
254
|
+
VALUE image = Data_Wrap_Struct(ImageClass, 0, 0, f);
|
255
|
+
rb_yield(image);
|
256
|
+
}
|
257
|
+
}
|
258
|
+
}
|
259
|
+
return self;
|
260
|
+
}
|
261
|
+
|
262
|
+
////////////////////////
|
263
|
+
// Wrapper around FileEachImage for use with rb_iterate
|
264
|
+
////////////////////////
|
265
|
+
static VALUE callEachImage(VALUE obj)
|
266
|
+
{
|
267
|
+
return rb_funcall(obj, rb_intern("eachImage"), 0, 0);
|
268
|
+
}
|
269
|
+
|
270
|
+
////////////////////////
|
271
|
+
// Support block for FileImageCount
|
272
|
+
////////////////////////
|
273
|
+
static VALUE counter(VALUE image, VALUE *count)
|
274
|
+
{
|
275
|
+
return (*count) = rb_funcall(*count, rb_intern("+"), 1, UINT2NUM(1));
|
276
|
+
}
|
277
|
+
|
278
|
+
////////////////////////
|
279
|
+
// TagLib2::File#imageCount
|
280
|
+
////////////////////////
|
281
|
+
static VALUE FileImageCount(VALUE self)
|
282
|
+
{
|
283
|
+
VALUE count = UINT2NUM(0);
|
284
|
+
rb_iterate(callEachImage, self, (VALUE (*)(...))counter, (VALUE)&count);
|
285
|
+
return count;
|
286
|
+
}
|
287
|
+
|
288
|
+
////////////////////////
|
289
|
+
// Support block for FileImage
|
290
|
+
////////////////////////
|
291
|
+
static VALUE imageFinder(VALUE image, VALUE ary)
|
292
|
+
{
|
293
|
+
VALUE count = rb_ary_entry(ary, 0);
|
294
|
+
VALUE idx = rb_ary_entry(ary, 1);
|
295
|
+
if(count == idx)
|
296
|
+
{
|
297
|
+
rb_ary_store(ary, 2, image);
|
298
|
+
rb_iter_break();
|
299
|
+
}
|
300
|
+
rb_ary_store(ary, 0, rb_funcall(count, rb_intern("+"), 1, UINT2NUM(1)));
|
301
|
+
}
|
302
|
+
|
303
|
+
////////////////////////
|
304
|
+
// TagLib2::File#image
|
305
|
+
////////////////////////
|
306
|
+
static VALUE FileImage(VALUE self, VALUE idx)
|
307
|
+
{
|
308
|
+
VALUE ary = rb_ary_new3(2, UINT2NUM(0), idx);
|
309
|
+
rb_iterate(callEachImage, self, (VALUE (*)(...))imageFinder, ary);
|
310
|
+
return rb_ary_entry(ary, 2);
|
311
|
+
}
|
312
|
+
|
313
|
+
////////////////////////
|
314
|
+
// TagLib2::File#addImage
|
315
|
+
////////////////////////
|
316
|
+
static VALUE FileAddImage(VALUE self, VALUE type, VALUE mimeType,
|
317
|
+
VALUE description, VALUE data)
|
318
|
+
{
|
319
|
+
GetFileRef
|
320
|
+
GetID3Tag
|
321
|
+
TagLib::ID3v2::AttachedPictureFrame *frame = new TagLib::ID3v2::AttachedPictureFrame;
|
322
|
+
frame->setTextEncoding(TagLib::String::UTF8);
|
323
|
+
|
324
|
+
if(RTEST(type))
|
325
|
+
{
|
326
|
+
frame->setType((TagLib::ID3v2::AttachedPictureFrame::Type)NUM2UINT(type));
|
327
|
+
}
|
328
|
+
else
|
329
|
+
{
|
330
|
+
delete frame;
|
331
|
+
rb_raise(rb_eArgError, "Type must be specified");
|
332
|
+
}
|
333
|
+
|
334
|
+
if(RTEST(mimeType))
|
335
|
+
{
|
336
|
+
frame->setMimeType(RubyStrToTStr(StringValue(mimeType)));
|
337
|
+
}
|
338
|
+
else
|
339
|
+
{
|
340
|
+
delete frame;
|
341
|
+
rb_raise(rb_eArgError, "mimeType must be specified");
|
342
|
+
}
|
343
|
+
|
344
|
+
if(RTEST(description))
|
345
|
+
{
|
346
|
+
frame->setDescription(RubyStrToTStr(StringValue(description)));
|
347
|
+
}
|
348
|
+
|
349
|
+
if(RTEST(data))
|
350
|
+
{
|
351
|
+
VALUE str = StringValue(data);
|
352
|
+
frame->setPicture(TagLib::ByteVector(RSTRING_PTR(data), RSTRING_LEN(data)));
|
353
|
+
}
|
354
|
+
tag->addFrame(frame);
|
355
|
+
}
|
356
|
+
|
357
|
+
////////////////////////
|
358
|
+
// TagLib2::File#removeImage
|
359
|
+
////////////////////////
|
360
|
+
static VALUE FileRemoveImage(VALUE self, VALUE image)
|
361
|
+
{
|
362
|
+
GetFileRef
|
363
|
+
GetID3Tag
|
364
|
+
TagLib::ID3v2::AttachedPictureFrame *frame;
|
365
|
+
Data_Get_Struct(image, TagLib::ID3v2::AttachedPictureFrame, frame);
|
366
|
+
tag->removeFrame(frame, true);
|
367
|
+
}
|
368
|
+
|
369
|
+
////////////////////////
|
370
|
+
// TagLib2::Image#type
|
371
|
+
////////////////////////
|
372
|
+
static VALUE ImageType(VALUE self)
|
373
|
+
{
|
374
|
+
TagLib::ID3v2::AttachedPictureFrame *frame;
|
375
|
+
Data_Get_Struct(self, TagLib::ID3v2::AttachedPictureFrame, frame);
|
376
|
+
return UINT2NUM(frame->type());
|
377
|
+
}
|
378
|
+
|
379
|
+
////////////////////////
|
380
|
+
// TagLib2::Image#description
|
381
|
+
////////////////////////
|
382
|
+
static VALUE ImageDescription(VALUE self)
|
383
|
+
{
|
384
|
+
TagLib::ID3v2::AttachedPictureFrame *frame;
|
385
|
+
Data_Get_Struct(self, TagLib::ID3v2::AttachedPictureFrame, frame);
|
386
|
+
return TStrToRubyStr(frame->description());
|
387
|
+
}
|
388
|
+
|
389
|
+
////////////////////////
|
390
|
+
// TagLib2::Image#mimeType
|
391
|
+
////////////////////////
|
392
|
+
static VALUE ImageMimeType(VALUE self)
|
393
|
+
{
|
394
|
+
TagLib::ID3v2::AttachedPictureFrame *frame;
|
395
|
+
Data_Get_Struct(self, TagLib::ID3v2::AttachedPictureFrame, frame);
|
396
|
+
return TStrToRubyStr(frame->mimeType());
|
397
|
+
}
|
398
|
+
|
399
|
+
////////////////////////
|
400
|
+
// TagLib2::Image#data
|
401
|
+
////////////////////////
|
402
|
+
static VALUE ImageData(VALUE self)
|
403
|
+
{
|
404
|
+
TagLib::ID3v2::AttachedPictureFrame *frame;
|
405
|
+
Data_Get_Struct(self, TagLib::ID3v2::AttachedPictureFrame, frame);
|
406
|
+
return rb_str_new(frame->picture().data(), frame->picture().size());
|
407
|
+
}
|
408
|
+
|
409
|
+
////////////////////////
|
410
|
+
// TagLib2::Image#id
|
411
|
+
////////////////////////
|
412
|
+
static VALUE ImageID(VALUE self)
|
413
|
+
{
|
414
|
+
TagLib::ID3v2::AttachedPictureFrame *frame;
|
415
|
+
Data_Get_Struct(self, TagLib::ID3v2::AttachedPictureFrame, frame);
|
416
|
+
return UINT2NUM(frame->frameID().toUInt());
|
417
|
+
}
|
418
|
+
|
419
|
+
|
420
|
+
////////////////////////
|
421
|
+
// Main function to create module TagLib2 and its classes
|
422
|
+
////////////////////////
|
423
|
+
extern "C" void Init_taglib2(void)
|
424
|
+
{
|
425
|
+
TagLib2Module = rb_define_module("TagLib2");
|
426
|
+
|
427
|
+
BadFile = rb_define_class_under(TagLib2Module, "BadFile", rb_eRuntimeError);
|
428
|
+
ImageClass = rb_define_class_under(TagLib2Module, "Image", rb_cObject);
|
429
|
+
FileClass = rb_define_class_under(TagLib2Module, "File", rb_cObject);
|
430
|
+
RefClass = rb_define_class_under(FileClass, "FileRef", rb_cObject);
|
431
|
+
rb_define_method(FileClass, "initialize", (VALUE (*)(...))FileInitialize, 1);
|
432
|
+
AttributeMethods(title, Title)
|
433
|
+
AttributeMethods(artist, Artist)
|
434
|
+
AttributeMethods(album, Album)
|
435
|
+
AttributeMethods(comment, Comment)
|
436
|
+
AttributeMethods(genre, Genre)
|
437
|
+
AttributeMethods(year, Year)
|
438
|
+
AttributeMethods(track, Track)
|
439
|
+
PropertyMethod(bitrate, Bitrate)
|
440
|
+
PropertyMethod(sampleRate, SampleRate)
|
441
|
+
PropertyMethod(sample_rate, SampleRate)
|
442
|
+
PropertyMethod(channels, Channels)
|
443
|
+
PropertyMethod(length, Length)
|
444
|
+
rb_define_method(FileClass, "save", (VALUE (*)(...))FileSave, 0);
|
445
|
+
rb_define_method(FileClass, "eachImage", (VALUE (*)(...))FileEachImage, 0);
|
446
|
+
rb_define_method(FileClass, "each_image", (VALUE (*)(...))FileEachImage, 0);
|
447
|
+
rb_define_method(FileClass, "imageCount", (VALUE (*)(...))FileImageCount, 0);
|
448
|
+
rb_define_method(FileClass, "image_count", (VALUE (*)(...))FileImageCount, 0);
|
449
|
+
rb_define_method(FileClass, "image", (VALUE (*)(...))FileImage, 1);
|
450
|
+
rb_define_method(FileClass, "addImage", (VALUE (*)(...))FileAddImage, 4);
|
451
|
+
rb_define_method(FileClass, "add_image", (VALUE (*)(...))FileAddImage, 4);
|
452
|
+
rb_define_method(FileClass, "removeImage", (VALUE (*)(...))FileRemoveImage, 1);
|
453
|
+
rb_define_method(FileClass, "remove_image", (VALUE (*)(...))FileRemoveImage, 1);
|
454
|
+
rb_define_method(ImageClass, "type", (VALUE (*)(...))ImageType, 0);
|
455
|
+
rb_define_method(ImageClass, "description", (VALUE (*)(...))ImageDescription, 0);
|
456
|
+
rb_define_method(ImageClass, "mimeType", (VALUE (*)(...))ImageMimeType, 0);
|
457
|
+
rb_define_method(ImageClass, "mime_type", (VALUE (*)(...))ImageMimeType, 0);
|
458
|
+
rb_define_method(ImageClass, "data", (VALUE (*)(...))ImageData, 0);
|
459
|
+
rb_define_method(ImageClass, "id", (VALUE (*)(...))ImageID, 0);
|
460
|
+
|
461
|
+
// Load Mahoro if available
|
462
|
+
int failed = 0;
|
463
|
+
VALUE ret = rb_protect(requireMahoro, Qnil, &failed);
|
464
|
+
|
465
|
+
rb_define_const(TagLib2Module, "MAHORO_PRESENT", failed ? Qfalse : Qtrue);
|
466
|
+
}
|
data/lib/ruby_taglib2.rb
ADDED
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-taglib2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.01"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Neil Stevens
|
8
|
+
- Saimon Moore
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain:
|
12
|
+
date: 2009-11-19 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Ruby bindings for Taglib's C Library
|
17
|
+
email: neil@hakubi.us
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions:
|
21
|
+
- ext/extconf.rb
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README
|
26
|
+
- ext/extconf.rb
|
27
|
+
- ext/ruby-taglib2.cpp
|
28
|
+
- lib/ruby_taglib2.rb
|
29
|
+
has_rdoc: true
|
30
|
+
homepage: http://www.hakubi.us/ruby-taglib
|
31
|
+
licenses: []
|
32
|
+
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.0.0
|
43
|
+
version:
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: "0"
|
49
|
+
version:
|
50
|
+
requirements:
|
51
|
+
- Ruby bindings for Taglib's C Library
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 1.3.5
|
54
|
+
signing_key:
|
55
|
+
specification_version: 1
|
56
|
+
summary: ruby-taglib2 is a compiled extension to ruby that provides access to the TagLib library
|
57
|
+
test_files: []
|
58
|
+
|