ruby-taglib2 1.01
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|