rbpod 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +13 -2
- data/ext/rbpod/collection.c +0 -1
- data/ext/rbpod/database.c +16 -19
- data/ext/rbpod/device.c +22 -17
- data/ext/rbpod/error.c +0 -1
- data/ext/rbpod/extconf.rb +7 -2
- data/ext/rbpod/macros.h +99 -0
- data/ext/rbpod/playlist.c +52 -21
- data/ext/rbpod/playlist_collection.c +94 -8
- data/ext/rbpod/rbpod.c +0 -34
- data/ext/rbpod/rbpod.h +36 -10
- data/ext/rbpod/track.c +255 -83
- data/ext/rbpod/track_collection.c +108 -5
- data/ext/rbpod/utilities.c +27 -0
- data/lib/rbpod/version.rb +1 -1
- data/spec/rbpod/playlist_collection_spec.rb +20 -0
- data/spec/rbpod/playlist_spec.rb +58 -1
- metadata +6 -12
- data/ext/rbpod/collection.h +0 -10
- data/ext/rbpod/database.h +0 -10
- data/ext/rbpod/device.h +0 -10
- data/ext/rbpod/error.h +0 -12
- data/ext/rbpod/playlist.h +0 -10
- data/ext/rbpod/playlist_collection.h +0 -10
- data/ext/rbpod/track.h +0 -10
- data/ext/rbpod/track_collection.h +0 -10
data/README.md
CHANGED
@@ -104,10 +104,21 @@ playlist.podcast? # => false
|
|
104
104
|
|
105
105
|
playlist.tracks # => #<RbPod::TrackCollection:0xdeadbeef>
|
106
106
|
```
|
107
|
+
### RbPod::TrackCollection
|
107
108
|
|
108
|
-
|
109
|
+
All playlists contain a `tracks` method which returns an `RbPod::TrackCollection`:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
tracks = database.playlists.master.tracks
|
113
|
+
|
114
|
+
tracks.playlist # => #<RbPod::Playlist:0xdeadbeef>
|
115
|
+
tracks.length # => 400
|
109
116
|
|
110
|
-
|
117
|
+
tracks.first # => #<RbPod::Track:0xdeadbeef>
|
118
|
+
tracks[0] # => #<RbPod::Track:0xdeadbeef>
|
119
|
+
```
|
120
|
+
|
121
|
+
### RbPod::Track
|
111
122
|
|
112
123
|
```ruby
|
113
124
|
track = database.playlists.master.tracks.first
|
data/ext/rbpod/collection.c
CHANGED
data/ext/rbpod/database.c
CHANGED
@@ -1,12 +1,6 @@
|
|
1
1
|
/* database.c */
|
2
2
|
|
3
3
|
#include "rbpod.h"
|
4
|
-
#include "error.h"
|
5
|
-
#include "device.h"
|
6
|
-
#include "database.h"
|
7
|
-
#include "playlist.h"
|
8
|
-
#include "track_collection.h"
|
9
|
-
#include "playlist_collection.h"
|
10
4
|
|
11
5
|
/*
|
12
6
|
* call-seq:
|
@@ -71,7 +65,9 @@ static VALUE rbpod_database_tracks_get(VALUE self)
|
|
71
65
|
*/
|
72
66
|
static VALUE rbpod_database_device_get(VALUE self)
|
73
67
|
{
|
74
|
-
|
68
|
+
Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
|
69
|
+
VALUE mount_point = rb_str_new2(itdb_get_mountpoint(database));
|
70
|
+
return rb_class_new_instance(1, &mount_point, cRbPodDevice);
|
75
71
|
}
|
76
72
|
|
77
73
|
/*
|
@@ -203,34 +199,35 @@ static VALUE rbpod_database_create(int argc, VALUE *argv, VALUE self)
|
|
203
199
|
return Qnil;
|
204
200
|
}
|
205
201
|
|
206
|
-
/* If
|
207
|
-
if (RTEST(device_name)
|
208
|
-
device_name
|
209
|
-
}
|
202
|
+
/* If a device name was given, ensure it's a string. */
|
203
|
+
if (RTEST(device_name)) {
|
204
|
+
Check_Type(device_name, T_STRING);
|
210
205
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
206
|
+
if (RSTRING_LEN(device_name) < 4) {
|
207
|
+
rb_raise(eRbPodError, "Device name must be a string of at least four characters.");
|
208
|
+
return Qnil;
|
209
|
+
}
|
215
210
|
}
|
216
211
|
|
217
212
|
/* If a model number was given, ensure it's a string. */
|
218
213
|
if (RTEST(model_number)) {
|
219
|
-
Check_Type(
|
214
|
+
Check_Type(model_number, T_STRING);
|
220
215
|
|
221
|
-
model_matcher = rb_str_new2("/
|
216
|
+
model_matcher = rb_str_new2("/[xM]?[A-Z][0-9]{3}/");
|
222
217
|
ignorecase = rb_const_get(rb_cRegexp, rb_intern("IGNORECASE"));
|
223
218
|
model_regexp = rb_reg_new_str(model_matcher, ignorecase);
|
224
219
|
|
225
220
|
if (RTEST(rb_reg_match(model_regexp, model_number)) == FALSE) {
|
226
|
-
rb_raise(eRbPodError, "Model number must be a string matching: /
|
221
|
+
rb_raise(eRbPodError, "Model number must be a string matching: /[xM]?[A-Z][0-9]{3}/i");
|
227
222
|
return Qnil;
|
228
223
|
}
|
229
224
|
}
|
230
225
|
|
231
226
|
/* Extract pointers for glib use. */
|
232
227
|
_mount_point = StringValueCStr(mount_point);
|
233
|
-
|
228
|
+
|
229
|
+
/* GPod will use 'iPod' as the device name if it wasn't specified. */
|
230
|
+
_device_name = !NIL_P(device_name) ? StringValueCStr(device_name) : NULL;
|
234
231
|
|
235
232
|
/* GPod can function with a NULL model number, however, artwork may not function properly. */
|
236
233
|
_model_number = !NIL_P(model_number) ? StringValueCStr(model_number) : NULL;
|
data/ext/rbpod/device.c
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
/* device.c */
|
2
2
|
|
3
3
|
#include "rbpod.h"
|
4
|
-
#include "error.h"
|
5
|
-
#include "device.h"
|
6
|
-
#include "database.h"
|
7
4
|
|
8
5
|
/*
|
9
6
|
* call-seq:
|
@@ -189,32 +186,38 @@ static VALUE rbpod_device_sysinfo_save(VALUE self)
|
|
189
186
|
|
190
187
|
/*
|
191
188
|
* call-seq:
|
192
|
-
* initialize(
|
189
|
+
* initialize(mount_point) -> RbPod::Device
|
193
190
|
*
|
194
|
-
* Creates an RbPod::Device
|
191
|
+
* Creates an RbPod::Device mapped to a given device mount point.
|
195
192
|
*/
|
196
|
-
static VALUE rbpod_device_initialize(VALUE self, VALUE
|
193
|
+
static VALUE rbpod_device_initialize(VALUE self, VALUE mount_point)
|
197
194
|
{
|
198
|
-
|
195
|
+
Itdb_Device *device = TYPED_DATA_PTR(self, Itdb_Device);
|
199
196
|
|
200
|
-
/*
|
201
|
-
if (
|
202
|
-
rb_raise(eRbPodError, "
|
197
|
+
/* Check if the mount point is a directory. */
|
198
|
+
if (rb_file_directory_p(rb_cFile, mount_point) != Qtrue) {
|
199
|
+
rb_raise(eRbPodError, "The mount point must be a directory!");
|
203
200
|
return Qnil;
|
204
201
|
}
|
205
202
|
|
206
|
-
|
207
|
-
if (_database == NULL || _database->device == NULL) {
|
208
|
-
rb_raise(eRbPodError, "Given database and/or device was NULL.");
|
209
|
-
return Qnil;
|
210
|
-
}
|
203
|
+
itdb_device_set_mountpoint(device, StringValueCStr(mount_point));
|
211
204
|
|
212
|
-
|
213
|
-
DATA_PTR(self) = _database->device;
|
205
|
+
DATA_PTR(self) = device;
|
214
206
|
|
215
207
|
return self;
|
216
208
|
}
|
217
209
|
|
210
|
+
static void rbpod_device_deallocate(void *handle)
|
211
|
+
{
|
212
|
+
itdb_device_free((Itdb_Device *) handle);
|
213
|
+
}
|
214
|
+
|
215
|
+
static VALUE rbpod_device_allocate(VALUE self)
|
216
|
+
{
|
217
|
+
Itdb_Device *device = itdb_device_new();
|
218
|
+
return Data_Wrap_Struct(cRbPodDevice, NULL, rbpod_device_deallocate, (void *) device);
|
219
|
+
}
|
220
|
+
|
218
221
|
void Init_rbpod_device(void)
|
219
222
|
{
|
220
223
|
#if RDOC_CAN_PARSE_DOCUMENTATION
|
@@ -222,6 +225,8 @@ void Init_rbpod_device(void)
|
|
222
225
|
#endif
|
223
226
|
cRbPodDevice = rb_define_class_under(mRbPod, "Device", rb_cObject);
|
224
227
|
|
228
|
+
rb_define_alloc_func(cRbPodDevice, rbpod_device_allocate);
|
229
|
+
|
225
230
|
rb_define_method(cRbPodDevice, "initialize", rbpod_device_initialize, 1);
|
226
231
|
|
227
232
|
rb_define_method(cRbPodDevice, "[]", rbpod_device_sysinfo_get, 1);
|
data/ext/rbpod/error.c
CHANGED
data/ext/rbpod/extconf.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
require 'mkmf'
|
4
2
|
|
3
|
+
# Fail immediately if we don't have libgpod-1.0 installed here.
|
5
4
|
abort "Please install libgpod-1.0" unless pkg_config('libgpod-1.0')
|
6
5
|
|
7
6
|
# Unfortunately, rdoc isn't capable of a lot of things.
|
8
7
|
$defs.push("-DRDOC_CAN_PARSE_DOCUMENTATION=0")
|
9
8
|
|
9
|
+
# Provide HAVE_STDDEF_H to the pre-processor.
|
10
|
+
have_header('stddef.h')
|
11
|
+
|
12
|
+
# Provide HAVE_GPOD_ITDB_H to the pre-processor.
|
13
|
+
have_header('gpod/itdb.h')
|
14
|
+
|
10
15
|
create_makefile('rbpod/rbpod')
|
data/ext/rbpod/macros.h
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
/* macros.h */
|
2
|
+
|
3
|
+
#ifndef RBPOD_MACROS_H
|
4
|
+
#define RBPOD_MACROS_H
|
5
|
+
|
6
|
+
#define BooleanValue(value) (value) ? Qtrue : Qfalse
|
7
|
+
|
8
|
+
#define TYPED_DATA_PTR(self, type) ((type *) DATA_PTR(self))
|
9
|
+
|
10
|
+
#define DEF_ATTR_READER_STRING(c_type, prefix, field) \
|
11
|
+
static VALUE rbpod_##prefix##_##field##_get(VALUE self) \
|
12
|
+
{ \
|
13
|
+
c_type *ptr = TYPED_DATA_PTR(self, c_type); \
|
14
|
+
return (ptr->field == NULL) ? Qnil : rb_str_new2(ptr->field); \
|
15
|
+
}
|
16
|
+
|
17
|
+
#define DEF_ATTR_WRITER_STRING(c_type, prefix, field) \
|
18
|
+
static VALUE rbpod_##prefix##_##field##_set(VALUE self, VALUE value) \
|
19
|
+
{ \
|
20
|
+
c_type *ptr = TYPED_DATA_PTR(self, c_type); \
|
21
|
+
gchar *previous = (gchar *) ptr->field; \
|
22
|
+
ptr->field = StringValueCStr(value); \
|
23
|
+
if (previous != NULL) { \
|
24
|
+
g_free(previous); \
|
25
|
+
} \
|
26
|
+
return Qnil; \
|
27
|
+
}
|
28
|
+
|
29
|
+
#define DEF_ATTR_READER_FIXNUM(c_type, prefix, field) \
|
30
|
+
static VALUE rbpod_##prefix##_##field##_get(VALUE self) \
|
31
|
+
{ \
|
32
|
+
c_type *ptr = TYPED_DATA_PTR(self, c_type); \
|
33
|
+
return INT2NUM(ptr->field); \
|
34
|
+
}
|
35
|
+
|
36
|
+
#define DEF_ATTR_WRITER_FIXNUM(c_type, prefix, field) \
|
37
|
+
static VALUE rbpod_##prefix##_##field##_set(VALUE self, VALUE value) \
|
38
|
+
{ \
|
39
|
+
c_type *ptr = TYPED_DATA_PTR(self, c_type); \
|
40
|
+
ptr->field = NUM2INT(value); \
|
41
|
+
return Qnil; \
|
42
|
+
}
|
43
|
+
|
44
|
+
#define DEF_ATTR_READER_BOOLEAN(c_type, prefix, field) \
|
45
|
+
static VALUE rbpod_##prefix##_##field##_get(VALUE self) \
|
46
|
+
{ \
|
47
|
+
c_type *ptr = TYPED_DATA_PTR(self, c_type); \
|
48
|
+
return (ptr->field == 0) ? Qfalse : Qtrue; \
|
49
|
+
}
|
50
|
+
|
51
|
+
#define DEF_ATTR_WRITER_BOOLEAN(c_type, prefix, field) \
|
52
|
+
static VALUE rbpod_##prefix##_##field##_set(VALUE self, VALUE value) \
|
53
|
+
{ \
|
54
|
+
c_type *ptr = TYPED_DATA_PTR(self, c_type); \
|
55
|
+
ptr->field = (RTEST(value)) ? 1 : 0; \
|
56
|
+
return Qnil; \
|
57
|
+
}
|
58
|
+
|
59
|
+
#define DEF_ATTR_READER_DATETIME(c_type, prefix, field) \
|
60
|
+
static VALUE rbpod_##prefix##_##field##_get(VALUE self) \
|
61
|
+
{ \
|
62
|
+
c_type *ptr = TYPED_DATA_PTR(self, c_type); \
|
63
|
+
VALUE timestamp = INT2NUM(ptr->field); \
|
64
|
+
if (ptr->field == 0) { \
|
65
|
+
return Qnil; \
|
66
|
+
} \
|
67
|
+
return rb_funcall(rb_cTime, rb_intern("at"), 1, timestamp); \
|
68
|
+
}
|
69
|
+
|
70
|
+
#define DEF_ATTR_WRITER_DATETIME(c_type, prefix, field) \
|
71
|
+
static VALUE rbpod_##prefix##_##field##_set(VALUE self, VALUE value) \
|
72
|
+
{ \
|
73
|
+
c_type *ptr = TYPED_DATA_PTR(self, c_type); \
|
74
|
+
ptr->field = rb_funcall(value, rb_intern("to_i"), 0); \
|
75
|
+
return Qnil; \
|
76
|
+
}
|
77
|
+
|
78
|
+
#define DEF_ATTR_ACCESSOR(c_type, prefix, ruby_type, field) \
|
79
|
+
DEF_ATTR_READER_##ruby_type(c_type, prefix, field); \
|
80
|
+
DEF_ATTR_WRITER_##ruby_type(c_type, prefix, field)
|
81
|
+
|
82
|
+
#define DCL_ATTR_WRITER(ruby_class, prefix, name, field) \
|
83
|
+
rb_define_method(ruby_class, #name "=", rbpod_##prefix##_##field##_set, 1)
|
84
|
+
|
85
|
+
#define DCL_ATTR_PREDICATE(ruby_class, prefix, name, field) \
|
86
|
+
rb_define_method(ruby_class, #name "?", rbpod_##prefix##_##field##_get, 0)
|
87
|
+
|
88
|
+
#define DCL_ATTR_READER(ruby_class, prefix, name, field) \
|
89
|
+
rb_define_method(ruby_class, #name, rbpod_##prefix##_##field##_get, 0)
|
90
|
+
|
91
|
+
#define DCL_ATTR_P(ruby_class, prefix, name, field) \
|
92
|
+
DCL_ATTR_PREDICATE(ruby_class, prefix, name, field); \
|
93
|
+
DCL_ATTR_WRITER(ruby_class, prefix, name, field)
|
94
|
+
|
95
|
+
#define DCL_ATTR(ruby_class, prefix, name, field) \
|
96
|
+
DCL_ATTR_READER(ruby_class, prefix, name, field); \
|
97
|
+
DCL_ATTR_WRITER(ruby_class, prefix, name, field)
|
98
|
+
|
99
|
+
#endif /* RBPOD_MACROS_H */
|
data/ext/rbpod/playlist.c
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
/* playlist.c */
|
2
2
|
|
3
3
|
#include "rbpod.h"
|
4
|
-
#include "playlist.h"
|
5
|
-
#include "track_collection.h"
|
6
4
|
|
7
5
|
/*
|
8
6
|
* call-seq:
|
@@ -42,7 +40,21 @@ static VALUE rbpod_playlist_smart_p(VALUE self)
|
|
42
40
|
|
43
41
|
/*
|
44
42
|
* call-seq:
|
45
|
-
*
|
43
|
+
* smart=(flag) -> nil
|
44
|
+
*
|
45
|
+
* Set this playlist to a smart playlist.
|
46
|
+
*/
|
47
|
+
static VALUE rbpod_playlist_smart_set(VALUE self, VALUE flag)
|
48
|
+
{
|
49
|
+
Itdb_Playlist *playlist = TYPED_DATA_PTR(self, Itdb_Playlist);
|
50
|
+
|
51
|
+
playlist->is_spl = (RTEST(flag)) ? TRUE : FALSE;
|
52
|
+
|
53
|
+
return Qnil;
|
54
|
+
}
|
55
|
+
|
56
|
+
/*
|
57
|
+
* call-seq:
|
46
58
|
* created_on() -> Time
|
47
59
|
*
|
48
60
|
* Returns a Time object representing the time that this playlist was created.
|
@@ -89,6 +101,27 @@ static VALUE rbpod_playlist_length_get(VALUE self)
|
|
89
101
|
return INT2NUM(itdb_playlist_tracks_number(playlist));
|
90
102
|
}
|
91
103
|
|
104
|
+
/*
|
105
|
+
* call-seq:
|
106
|
+
* name=(string) -> nil
|
107
|
+
*
|
108
|
+
* Sets the name of this playlist.
|
109
|
+
*/
|
110
|
+
static VALUE rbpod_playlist_name_set(VALUE self, VALUE name)
|
111
|
+
{
|
112
|
+
Itdb_Playlist *playlist = TYPED_DATA_PTR(self, Itdb_Playlist);
|
113
|
+
gchar *new_name = StringValueCStr(name);
|
114
|
+
gchar *old_name = playlist->name;
|
115
|
+
|
116
|
+
/* Free up the old name. */
|
117
|
+
g_free(old_name);
|
118
|
+
|
119
|
+
/* Attach the new name. */
|
120
|
+
playlist->name = new_name;
|
121
|
+
|
122
|
+
return Qnil;
|
123
|
+
}
|
124
|
+
|
92
125
|
/*
|
93
126
|
* call-seq:
|
94
127
|
* name() -> String
|
@@ -116,24 +149,23 @@ static VALUE rbpod_playlist_compare(VALUE self, VALUE other)
|
|
116
149
|
return Qnil;
|
117
150
|
}
|
118
151
|
|
152
|
+
/* If this is the master playlist, it always comes first. */
|
153
|
+
if (rbpod_playlist_master_p(self) == Qtrue) {
|
154
|
+
return NUM2INT(-1);
|
155
|
+
}
|
156
|
+
|
157
|
+
/* If we're comparing against the master playlist, it comes first. */
|
158
|
+
if (rbpod_playlist_master_p(other) == Qtrue) {
|
159
|
+
return NUM2INT(1);
|
160
|
+
}
|
161
|
+
|
119
162
|
this_playlist_name = rbpod_playlist_name_get(self);
|
120
163
|
other_playlist_name = rbpod_playlist_name_get(other);
|
121
164
|
|
165
|
+
/* Otherwise, compare by playlist name. */
|
122
166
|
return rb_str_cmp(this_playlist_name, other_playlist_name);
|
123
167
|
}
|
124
168
|
|
125
|
-
/*
|
126
|
-
* call-seq:
|
127
|
-
* id() -> Fixnum
|
128
|
-
*
|
129
|
-
* Returns the internal ID number for this playlist. Do not use this method.
|
130
|
-
*/
|
131
|
-
static VALUE rbpod_playlist_id_get(VALUE self)
|
132
|
-
{
|
133
|
-
Itdb_Playlist *playlist = TYPED_DATA_PTR(self, Itdb_Playlist);
|
134
|
-
return INT2NUM(playlist->id);
|
135
|
-
}
|
136
|
-
|
137
169
|
/*
|
138
170
|
* call-seq:
|
139
171
|
* initialize() -> RbPod::Playlist
|
@@ -175,23 +207,22 @@ void Init_rbpod_playlist(void)
|
|
175
207
|
|
176
208
|
rb_define_method(cRbPodPlaylist, "initialize", rbpod_playlist_initialize, 0);
|
177
209
|
|
178
|
-
rb_define_private_method(cRbPodPlaylist, "id", rbpod_playlist_id_get, 0);
|
179
|
-
|
180
210
|
rb_define_method(cRbPodPlaylist, "<=>", rbpod_playlist_compare, 1);
|
181
211
|
|
182
212
|
rb_define_method(cRbPodPlaylist, "name", rbpod_playlist_name_get, 0);
|
183
213
|
rb_define_method(cRbPodPlaylist, "length", rbpod_playlist_length_get, 0);
|
184
214
|
rb_define_method(cRbPodPlaylist, "tracks", rbpod_playlist_tracks_get, 0);
|
185
|
-
rb_define_method(cRbPodPlaylist, "
|
186
|
-
rb_define_method(cRbPodPlaylist, "timestamp", rbpod_playlist_timestamp_get, 0);
|
215
|
+
rb_define_method(cRbPodPlaylist, "created_on", rbpod_playlist_timestamp_get, 0);
|
187
216
|
|
188
|
-
|
189
|
-
|
217
|
+
rb_define_method(cRbPodPlaylist, "name=", rbpod_playlist_name_set, 1);
|
218
|
+
rb_define_method(cRbPodPlaylist, "smart=", rbpod_playlist_smart_set, 1);
|
190
219
|
|
191
220
|
rb_define_method(cRbPodPlaylist, "smart?", rbpod_playlist_smart_p, 0);
|
192
221
|
rb_define_method(cRbPodPlaylist, "master?", rbpod_playlist_master_p, 0);
|
193
222
|
rb_define_method(cRbPodPlaylist, "podcast?", rbpod_playlist_podcast_p, 0);
|
194
223
|
|
224
|
+
rb_define_method(cRbPodPlaylist, "shuffle!", rbpod_playlist_shuffle_bang, 0);
|
225
|
+
|
195
226
|
return;
|
196
227
|
}
|
197
228
|
|
@@ -1,10 +1,6 @@
|
|
1
1
|
/* playlist_collection.c */
|
2
2
|
|
3
3
|
#include "rbpod.h"
|
4
|
-
#include "database.h"
|
5
|
-
#include "playlist.h"
|
6
|
-
#include "collection.h"
|
7
|
-
#include "playlist_collection.h"
|
8
4
|
|
9
5
|
/*
|
10
6
|
* call-seq:
|
@@ -19,24 +15,106 @@ static VALUE rbpod_playlist_collection_database(VALUE self)
|
|
19
15
|
|
20
16
|
/*
|
21
17
|
* call-seq:
|
22
|
-
*
|
18
|
+
* insert(playlist, position = -1) -> nil
|
19
|
+
*
|
20
|
+
* Adds the given RbPod::Playlist +playlist+ to the database.
|
21
|
+
*/
|
22
|
+
static VALUE rbpod_playlist_collection_insert(int argc, VALUE *argv, VALUE self)
|
23
|
+
{
|
24
|
+
VALUE database = rbpod_playlist_collection_database(self);
|
25
|
+
VALUE playlist = Qnil, position = Qnil;
|
26
|
+
Itdb_iTunesDB *_database = NULL;
|
27
|
+
Itdb_Playlist *_playlist = NULL;
|
28
|
+
gint32 _position = 0;
|
29
|
+
|
30
|
+
if (rb_scan_args(argc, argv, "11", &playlist, &position) < 1) {
|
31
|
+
rb_raise(eRbPodError, "Invalid arguments: Expected >= 1 argument!");
|
32
|
+
return Qnil;
|
33
|
+
}
|
34
|
+
|
35
|
+
_database = TYPED_DATA_PTR(database, Itdb_iTunesDB);
|
36
|
+
_playlist = TYPED_DATA_PTR(playlist, Itdb_Playlist);
|
37
|
+
_position = NIL_P(position) ? -1 : NUM2INT(position);
|
38
|
+
|
39
|
+
/* We we given a playlist? */
|
40
|
+
if (rb_obj_is_instance_of(playlist, cRbPodPlaylist) == FALSE) {
|
41
|
+
rb_raise(eRbPodError, "Invalid argument: expected RbPod::Playlist, got %s", StringValueCStr(playlist));
|
42
|
+
return Qnil;
|
43
|
+
}
|
44
|
+
|
45
|
+
/* Does this playlist already exist in the database? */
|
46
|
+
if (itdb_playlist_exists(_database, _playlist) == TRUE) {
|
47
|
+
rb_raise(eRbPodError, "Invalid argument: this playlist already exists in the database!");
|
48
|
+
return Qnil;
|
49
|
+
}
|
50
|
+
|
51
|
+
itdb_playlist_add(_database, _playlist, _position);
|
52
|
+
|
53
|
+
return Qnil;
|
54
|
+
}
|
55
|
+
|
56
|
+
/*
|
57
|
+
* call-seq:
|
58
|
+
* remove(playlist) -> nil
|
59
|
+
*
|
60
|
+
* Removes the given RbPod::Playlist +playlist+ from the database.
|
61
|
+
*/
|
62
|
+
static VALUE rbpod_playlist_collection_remove(VALUE self, VALUE playlist)
|
63
|
+
{
|
64
|
+
VALUE database = rbpod_playlist_collection_database(self);
|
65
|
+
Itdb_iTunesDB *_database = TYPED_DATA_PTR(database, Itdb_iTunesDB);
|
66
|
+
Itdb_Playlist *_playlist = TYPED_DATA_PTR(playlist, Itdb_Playlist);
|
67
|
+
|
68
|
+
/* We we given a playlist? */
|
69
|
+
if (rb_obj_is_instance_of(playlist, cRbPodPlaylist) == FALSE) {
|
70
|
+
rb_raise(eRbPodError, "Invalid argument: Expected RbPod::Playlist, got %s", StringValueCStr(playlist));
|
71
|
+
return Qnil;
|
72
|
+
}
|
73
|
+
|
74
|
+
/* Does this playlist already exist in the database? */
|
75
|
+
if (itdb_playlist_exists(_database, _playlist) == FALSE) {
|
76
|
+
rb_raise(eRbPodError, "Invalid argument: This playlist does not exist in the database!");
|
77
|
+
return Qnil;
|
78
|
+
}
|
79
|
+
|
80
|
+
itdb_playlist_remove(_playlist);
|
81
|
+
|
82
|
+
return Qnil;
|
83
|
+
}
|
84
|
+
|
85
|
+
/*
|
86
|
+
* call-seq:
|
87
|
+
* include?(playlist) -> boolean
|
23
88
|
*
|
24
89
|
* Returns true or false if the given RbPod::Playlist +playlist+ exists within the database.
|
25
90
|
*/
|
26
|
-
static VALUE
|
91
|
+
static VALUE rbpod_playlist_collection_include(VALUE self, VALUE playlist)
|
27
92
|
{
|
28
93
|
VALUE database = rbpod_playlist_collection_database(self);
|
29
94
|
Itdb_iTunesDB *_database = TYPED_DATA_PTR(database, Itdb_iTunesDB);
|
30
95
|
Itdb_Playlist *_playlist = TYPED_DATA_PTR(playlist, Itdb_Playlist);
|
31
96
|
|
32
97
|
if (rb_obj_is_instance_of(playlist, cRbPodPlaylist) == FALSE) {
|
33
|
-
rb_raise(eRbPodError, "Invalid argument:
|
98
|
+
rb_raise(eRbPodError, "Invalid argument: Expected RbPod::Playlist, got %s", StringValueCStr(playlist));
|
34
99
|
return Qfalse;
|
35
100
|
}
|
36
101
|
|
37
102
|
return BooleanValue(itdb_playlist_exists(_database, _playlist));
|
38
103
|
}
|
39
104
|
|
105
|
+
/*
|
106
|
+
* call-seq:
|
107
|
+
* size() -> integer
|
108
|
+
*
|
109
|
+
* Returns the total number of playlists in this database.
|
110
|
+
*/
|
111
|
+
static VALUE rbpod_playlist_collection_size(VALUE self)
|
112
|
+
{
|
113
|
+
VALUE database = rbpod_playlist_collection_database(self);
|
114
|
+
Itdb_iTunesDB *_database = TYPED_DATA_PTR(database, Itdb_iTunesDB);
|
115
|
+
return INT2NUM(itdb_playlists_number(_database));
|
116
|
+
}
|
117
|
+
|
40
118
|
/*
|
41
119
|
* call-seq:
|
42
120
|
* master() -> RbPod::Playlist
|
@@ -122,11 +200,19 @@ void Init_rbpod_playlist_collection(void)
|
|
122
200
|
|
123
201
|
rb_define_method(cRbPodPlaylistCollection, "database", rbpod_playlist_collection_database, 0);
|
124
202
|
|
125
|
-
rb_define_method(cRbPodPlaylistCollection, "include?",
|
203
|
+
rb_define_method(cRbPodPlaylistCollection, "include?", rbpod_playlist_collection_include, 1);
|
126
204
|
|
127
205
|
rb_define_method(cRbPodPlaylistCollection, "master", rbpod_playlist_collection_master, 0);
|
128
206
|
rb_define_method(cRbPodPlaylistCollection, "podcast", rbpod_playlist_collection_podcast, 0);
|
129
207
|
|
208
|
+
rb_define_method(cRbPodPlaylistCollection, "size", rbpod_playlist_collection_size, 0);
|
209
|
+
|
210
|
+
rb_define_method(cRbPodPlaylistCollection, "insert", rbpod_playlist_collection_insert, -1);
|
211
|
+
rb_define_method(cRbPodPlaylistCollection, "remove", rbpod_playlist_collection_remove, 1);
|
212
|
+
|
213
|
+
/* Syntactic sugar for adding a playlist. */
|
214
|
+
rb_define_alias(cRbPodPlaylistCollection, "<<", "insert");
|
215
|
+
|
130
216
|
return;
|
131
217
|
}
|
132
218
|
|
data/ext/rbpod/rbpod.c
CHANGED
@@ -1,14 +1,6 @@
|
|
1
1
|
/* rbpod.c */
|
2
2
|
|
3
3
|
#include "rbpod.h"
|
4
|
-
#include "error.h"
|
5
|
-
#include "track.h"
|
6
|
-
#include "device.h"
|
7
|
-
#include "playlist.h"
|
8
|
-
#include "database.h"
|
9
|
-
#include "collection.h"
|
10
|
-
#include "track_collection.h"
|
11
|
-
#include "playlist_collection.h"
|
12
4
|
|
13
5
|
VALUE mRbPod;
|
14
6
|
VALUE eRbPodError;
|
@@ -38,32 +30,6 @@ static VALUE rbpod_load_database(VALUE self, VALUE mount_point)
|
|
38
30
|
return rb_class_new_instance(1, &mount_point, cRbPodDatabase);
|
39
31
|
}
|
40
32
|
|
41
|
-
/*
|
42
|
-
* This is a hack used to inject a pointer from the data of one class instance into another.
|
43
|
-
*/
|
44
|
-
inline VALUE rb_class_new_instance_with_data(int argc, VALUE *argv, VALUE class, void *handle)
|
45
|
-
{
|
46
|
-
/* Create a new instance of this class. */
|
47
|
-
VALUE instance = rb_class_new_instance(argc, argv, class);
|
48
|
-
|
49
|
-
/* Assign it's DATA pointer to the given handle. */
|
50
|
-
DATA_PTR(instance) = handle;
|
51
|
-
|
52
|
-
return instance;
|
53
|
-
}
|
54
|
-
|
55
|
-
/*
|
56
|
-
* This is a hack so that including a module will trigger the +included+ singleton method.
|
57
|
-
*/
|
58
|
-
inline void rb_real_include_module(VALUE klass, VALUE module)
|
59
|
-
{
|
60
|
-
ID included = rb_intern("included");
|
61
|
-
|
62
|
-
rb_include_module(klass, module);
|
63
|
-
|
64
|
-
rb_funcall(mRbPodCollection, included, 1, klass);
|
65
|
-
}
|
66
|
-
|
67
33
|
void Init_rbpod(void)
|
68
34
|
{
|
69
35
|
/* This is the wrapper for all RbPod related classes. */
|
data/ext/rbpod/rbpod.h
CHANGED
@@ -4,19 +4,45 @@
|
|
4
4
|
#define RBPOD_H
|
5
5
|
|
6
6
|
#include <ruby.h>
|
7
|
-
#include <gpod/itdb.h>
|
8
|
-
|
9
|
-
#define BooleanValue(value) (value) ? Qtrue : Qfalse
|
10
7
|
|
11
|
-
#
|
8
|
+
#ifdef HAVE_STDDEF_H
|
9
|
+
#include <stddef.h>
|
10
|
+
#endif
|
12
11
|
|
13
|
-
|
14
|
-
|
12
|
+
#ifdef HAVE_GPOD_ITDB_H
|
13
|
+
#include <gpod/itdb.h>
|
14
|
+
#endif
|
15
|
+
|
16
|
+
/* Pre-processor macros. */
|
17
|
+
#include "macros.h"
|
18
|
+
|
19
|
+
/* Initialization function prototypes. */
|
20
|
+
RUBY_EXTERN void Init_rbpod(void); /* rbpod.c */
|
21
|
+
RUBY_EXTERN void Init_rbpod_error(void); /* error.c */
|
22
|
+
RUBY_EXTERN void Init_rbpod_database(void); /* database.c */
|
23
|
+
RUBY_EXTERN void Init_rbpod_device(void); /* device.c */
|
24
|
+
RUBY_EXTERN void Init_rbpod_track(void); /* track.c */
|
25
|
+
RUBY_EXTERN void Init_rbpod_playlist(void); /* playlist.c */
|
26
|
+
RUBY_EXTERN void Init_rbpod_collection(void); /* collection.c */
|
27
|
+
RUBY_EXTERN void Init_rbpod_track_collection(void); /* track_collection.c */
|
28
|
+
RUBY_EXTERN void Init_rbpod_playlist_collection(void); /* playlist_collection.c */
|
29
|
+
|
30
|
+
/* Helper function prototypes. */
|
31
|
+
RUBY_EXTERN inline VALUE rbpod_raise_error(GError *error);
|
32
|
+
RUBY_EXTERN inline VALUE rb_class_new_instance_with_data(int argc, VALUE *argv, VALUE class, void *handle);
|
33
|
+
RUBY_EXTERN inline void rb_real_include_module(VALUE klass, VALUE module);
|
34
|
+
|
35
|
+
/* Global variables. */
|
36
|
+
RUBY_EXTERN VALUE mRbPod; /* rbpod.c */
|
37
|
+
RUBY_EXTERN VALUE eRbPodError; /* error.c */
|
38
|
+
RUBY_EXTERN VALUE cRbPodDatabase; /* database.c */
|
39
|
+
RUBY_EXTERN VALUE cRbPodDevice; /* device.c */
|
40
|
+
RUBY_EXTERN VALUE cRbPodTrack; /* track.c */
|
41
|
+
RUBY_EXTERN VALUE cRbPodPlaylist; /* playlist.c */
|
42
|
+
RUBY_EXTERN VALUE mRbPodCollection; /* collection.c */
|
43
|
+
RUBY_EXTERN VALUE cRbPodTrackCollection; /* track_collection.c */
|
44
|
+
RUBY_EXTERN VALUE cRbPodPlaylistCollection; /* playlist_collection.c */
|
15
45
|
|
16
46
|
RUBY_EXTERN VALUE rb_cPathname;
|
17
47
|
|
18
|
-
inline VALUE rb_class_new_instance_with_data(int argc, VALUE *argv, VALUE class, void *handle);
|
19
|
-
|
20
|
-
inline void rb_real_include_module(VALUE klass, VALUE module);
|
21
|
-
|
22
48
|
#endif /* RBPOD_H */
|