rbpod 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +69 -0
- data/Rakefile +1 -0
- data/bin/rbpod +3 -0
- data/ext/rbpod/collection.c +104 -0
- data/ext/rbpod/collection.h +12 -0
- data/ext/rbpod/database.c +173 -0
- data/ext/rbpod/database.h +12 -0
- data/ext/rbpod/device.c +136 -0
- data/ext/rbpod/device.h +12 -0
- data/ext/rbpod/extconf.rb +7 -0
- data/ext/rbpod/playlist.c +85 -0
- data/ext/rbpod/playlist.h +12 -0
- data/ext/rbpod/rbpod.c +34 -0
- data/ext/rbpod/rbpod.h +13 -0
- data/ext/rbpod/track.c +84 -0
- data/ext/rbpod/track.h +12 -0
- data/lib/rbpod/version.rb +3 -0
- data/lib/rbpod.rb +2 -0
- data/rbpod.gemspec +29 -0
- metadata +119 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Joshua M. Keyes
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# RbPod
|
2
|
+
|
3
|
+
A Ruby interface to the libgpod library.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rbpod'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rbpod
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Functional for most read-only purposes.
|
22
|
+
|
23
|
+
#!/usr/bin/env ruby
|
24
|
+
|
25
|
+
require 'rbpod'
|
26
|
+
|
27
|
+
# Explicitly load a database from the filesystem -- can be a mount point to an iPod.
|
28
|
+
database = RbPod::Database.new('/mnt/ipod/') # => #<RbPod::Database:0x8733fa4>
|
29
|
+
|
30
|
+
database.version # => 42
|
31
|
+
database.mountpoint # => "/mnt/ipod/"
|
32
|
+
database.filename # => "/mnt/ipod/iPod_Control/iTunes/iTunesDB"
|
33
|
+
database.synchronized? # => true
|
34
|
+
|
35
|
+
# Device information, including model name, number, and capacity.
|
36
|
+
database.device.capacity # => 4.0
|
37
|
+
database.device.generation # => "Nano Video (3rd Gen.)"
|
38
|
+
database.device.model_name # => "Nano (Silver)"
|
39
|
+
database.device.model_number # => "A978"
|
40
|
+
|
41
|
+
# Device feature support detection.
|
42
|
+
database.device.supports_photos? # => true
|
43
|
+
database.device.supports_videos? # => true
|
44
|
+
database.device.supports_artwork? # => true
|
45
|
+
database.device.supports_podcasts? # => true
|
46
|
+
database.device.supports_chapter_images? # => true
|
47
|
+
|
48
|
+
# Device SysInfo read/write.
|
49
|
+
database.device['ModelNumStr'] # => "xA978"
|
50
|
+
database.device['PotsdamConf45'] = "Kilroy Was Here"
|
51
|
+
database.device.save!
|
52
|
+
|
53
|
+
# Playlists (includes enumerable)
|
54
|
+
database.playlists.map(&:name) # => ["iPod", "Podcasts", "Recently Added"]
|
55
|
+
|
56
|
+
# Track Listing (also enumerable)
|
57
|
+
database.tracks.count # => 400
|
58
|
+
database.tracks.first.artist # => "Steppenwolf"
|
59
|
+
database.tracks.first.title # => "Born To Be Wild"
|
60
|
+
|
61
|
+
It's alpha quality -- many pieces still need to be implemented. Patches welcome.
|
62
|
+
|
63
|
+
## Contributing
|
64
|
+
|
65
|
+
1. Fork it
|
66
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
67
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
68
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
69
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/rbpod
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
/* collection.c */
|
2
|
+
|
3
|
+
#include "rbpod.h"
|
4
|
+
#include "collection.h"
|
5
|
+
|
6
|
+
VALUE cRbPodCollection;
|
7
|
+
|
8
|
+
struct collection { VALUE klass; GList *list; };
|
9
|
+
|
10
|
+
static inline struct collection *rbpod_collection_unwrap(VALUE self) {
|
11
|
+
struct collection *collection = NULL;
|
12
|
+
Data_Get_Struct(self, struct collection, collection);
|
13
|
+
return collection;
|
14
|
+
}
|
15
|
+
|
16
|
+
static VALUE rbpod_collection_get(VALUE self, VALUE key) {
|
17
|
+
struct collection *collection = rbpod_collection_unwrap(self);
|
18
|
+
GList *current = NULL;
|
19
|
+
|
20
|
+
if (FIXNUM_P(key) == FALSE)
|
21
|
+
return Qnil;
|
22
|
+
|
23
|
+
current = g_list_nth(collection->list, FIX2INT(key));
|
24
|
+
|
25
|
+
return Data_Wrap_Struct(collection->klass, NULL, NULL, (void *) current->data);;
|
26
|
+
}
|
27
|
+
|
28
|
+
static VALUE rbpod_collection_last(VALUE self) {
|
29
|
+
struct collection *collection = rbpod_collection_unwrap(self);
|
30
|
+
GList *current = g_list_last(collection->list);
|
31
|
+
return Data_Wrap_Struct(collection->klass, NULL, NULL, (void *) current->data);
|
32
|
+
}
|
33
|
+
|
34
|
+
static VALUE rbpod_collection_first(VALUE self) {
|
35
|
+
struct collection *collection = rbpod_collection_unwrap(self);
|
36
|
+
GList *current = g_list_first(collection->list);
|
37
|
+
return Data_Wrap_Struct(collection->klass, NULL, NULL, (void *) current->data);
|
38
|
+
}
|
39
|
+
|
40
|
+
static VALUE rbpod_collection_count(VALUE self) {
|
41
|
+
struct collection *collection = rbpod_collection_unwrap(self);
|
42
|
+
return INT2NUM(g_list_length(collection->list));
|
43
|
+
}
|
44
|
+
|
45
|
+
static VALUE rbpod_collection_each(VALUE self) {
|
46
|
+
struct collection *collection = rbpod_collection_unwrap(self);
|
47
|
+
GList *current = NULL;
|
48
|
+
VALUE item;
|
49
|
+
|
50
|
+
if (rb_block_given_p() == TRUE) {
|
51
|
+
/* If we were supplied a block, enumerate the entire list. */
|
52
|
+
for (current = collection->list; current != NULL; current = current->next) {
|
53
|
+
item = Data_Wrap_Struct(collection->klass, NULL, NULL, (void *) current->data);
|
54
|
+
rb_yield(item);
|
55
|
+
}
|
56
|
+
return Qnil;
|
57
|
+
} else {
|
58
|
+
/* Otherwise return an enumerator from this method to the caller. */
|
59
|
+
return rb_funcall(self, rb_intern("enum_for"), 1, ID2SYM(rb_intern("each")));
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
static VALUE rbpod_collection_initialize(VALUE self) {
|
64
|
+
return self;
|
65
|
+
}
|
66
|
+
|
67
|
+
static void rbpod_collection_deallocate(void *handle) {
|
68
|
+
struct collection *collection = (struct collection *) handle;
|
69
|
+
|
70
|
+
if (collection->list != NULL)
|
71
|
+
g_list_free(collection->list);
|
72
|
+
|
73
|
+
xfree(handle);
|
74
|
+
return;
|
75
|
+
}
|
76
|
+
|
77
|
+
inline VALUE rbpod_collection_wrap(GList *list, VALUE type, gboolean freeable) {
|
78
|
+
struct collection *collection = ALLOC(struct collection);
|
79
|
+
collection->list = list;
|
80
|
+
collection->klass = type;
|
81
|
+
return Data_Wrap_Struct(cRbPodCollection, NULL, freeable ? rbpod_collection_deallocate : NULL, (void *) collection);
|
82
|
+
}
|
83
|
+
|
84
|
+
static VALUE rbpod_collection_allocate(VALUE self) {
|
85
|
+
return rbpod_collection_wrap(g_list_alloc(), Qnil, TRUE);
|
86
|
+
}
|
87
|
+
|
88
|
+
void Init_rbpod_collection(void) {
|
89
|
+
cRbPodCollection = rb_define_class_under(mRbPod, "Collection", rb_cObject);
|
90
|
+
|
91
|
+
rb_define_alloc_func(cRbPodCollection, rbpod_collection_allocate);
|
92
|
+
|
93
|
+
rb_include_module(cRbPodCollection, rb_mEnumerable);
|
94
|
+
|
95
|
+
rb_define_method(cRbPodCollection, "initialize", rbpod_collection_initialize, 0);
|
96
|
+
|
97
|
+
rb_define_method(cRbPodCollection, "each", rbpod_collection_each, 0);
|
98
|
+
rb_define_method(cRbPodCollection, "count", rbpod_collection_count, 0);
|
99
|
+
|
100
|
+
rb_define_method(cRbPodCollection, "first", rbpod_collection_first, 0);
|
101
|
+
rb_define_method(cRbPodCollection, "last", rbpod_collection_last, 0);
|
102
|
+
|
103
|
+
rb_define_method(cRbPodCollection, "[]", rbpod_collection_get, 1);
|
104
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
/* collection.h */
|
2
|
+
|
3
|
+
#ifndef RBPOD_COLLECTION_H
|
4
|
+
#define RBPOD_COLLECTION_H
|
5
|
+
|
6
|
+
extern VALUE cRbPodCollection;
|
7
|
+
|
8
|
+
void Init_rbpod_collection(void);
|
9
|
+
|
10
|
+
inline VALUE rbpod_collection_wrap(GList *list, VALUE type, gboolean freeable);
|
11
|
+
|
12
|
+
#endif /* RBPOD_COLLECTION_H */
|
@@ -0,0 +1,173 @@
|
|
1
|
+
/* database.c */
|
2
|
+
|
3
|
+
#include "rbpod.h"
|
4
|
+
#include "track.h"
|
5
|
+
#include "device.h"
|
6
|
+
#include "playlist.h"
|
7
|
+
#include "database.h"
|
8
|
+
#include "collection.h"
|
9
|
+
|
10
|
+
/*
|
11
|
+
* Although libgpod has an itdb_new(), itdb_parse() also returns a pointer to a
|
12
|
+
* newly allocated Itdb_iTunesDB structure. Since overwriting the data pointer from
|
13
|
+
* Data_Get_Struct() at runtime is dangerous, we use a double-pointer to wrap the
|
14
|
+
* Itdb_iTunesDB handle, so as not to confuse the Ruby implementation internals.
|
15
|
+
*/
|
16
|
+
|
17
|
+
VALUE cRbPodDatabase;
|
18
|
+
|
19
|
+
static inline Itdb_iTunesDB *rbpod_database_unwrap(VALUE self) {
|
20
|
+
Itdb_iTunesDB **database = NULL;
|
21
|
+
Data_Get_Struct(self, Itdb_iTunesDB *, database);
|
22
|
+
return *database;
|
23
|
+
}
|
24
|
+
|
25
|
+
static VALUE rbpod_database_write(VALUE self) {
|
26
|
+
Itdb_iTunesDB *database = rbpod_database_unwrap(self);
|
27
|
+
gboolean success = itdb_write(database, NULL); /* TODO: Improve error handling. */
|
28
|
+
return BooleanValue(success);
|
29
|
+
}
|
30
|
+
|
31
|
+
static VALUE rbpod_database_is_synchronized(VALUE self) {
|
32
|
+
Itdb_iTunesDB *database = rbpod_database_unwrap(self);
|
33
|
+
guint32 nontransferred = itdb_tracks_number_nontransferred(database);
|
34
|
+
return BooleanValue(nontransferred == 0);
|
35
|
+
}
|
36
|
+
|
37
|
+
static VALUE rbpod_database_playlists_get(VALUE self) {
|
38
|
+
Itdb_iTunesDB *database = rbpod_database_unwrap(self);
|
39
|
+
return rbpod_collection_wrap(database->playlists, cRbPodPlaylist, FALSE);
|
40
|
+
}
|
41
|
+
|
42
|
+
static VALUE rbpod_database_tracks_get(VALUE self) {
|
43
|
+
Itdb_iTunesDB *database = rbpod_database_unwrap(self);
|
44
|
+
return rbpod_collection_wrap(database->tracks, cRbPodTrack, FALSE);
|
45
|
+
}
|
46
|
+
|
47
|
+
static VALUE rbpod_database_device_get(VALUE self) {
|
48
|
+
Itdb_iTunesDB *database = rbpod_database_unwrap(self);
|
49
|
+
return rbpod_device_wrap(database->device, FALSE);
|
50
|
+
}
|
51
|
+
|
52
|
+
static VALUE rbpod_database_filename_get(VALUE self) {
|
53
|
+
Itdb_iTunesDB *database = rbpod_database_unwrap(self);
|
54
|
+
return rb_str_new2(database->filename);
|
55
|
+
}
|
56
|
+
|
57
|
+
static VALUE rbpod_database_version_get(VALUE self) {
|
58
|
+
Itdb_iTunesDB *database = rbpod_database_unwrap(self);
|
59
|
+
return INT2NUM(database->version);
|
60
|
+
}
|
61
|
+
|
62
|
+
static VALUE rbpod_database_id_get(VALUE self) {
|
63
|
+
Itdb_iTunesDB *database = rbpod_database_unwrap(self);
|
64
|
+
return INT2NUM(database->id);
|
65
|
+
}
|
66
|
+
|
67
|
+
static VALUE rbpod_database_mountpoint_get(VALUE self) {
|
68
|
+
Itdb_iTunesDB *database = rbpod_database_unwrap(self);
|
69
|
+
return rb_str_new2(itdb_get_mountpoint(database));
|
70
|
+
}
|
71
|
+
|
72
|
+
static VALUE rbpod_database_mountpoint_set(VALUE self, VALUE path) {
|
73
|
+
Itdb_iTunesDB *database = rbpod_database_unwrap(self);
|
74
|
+
itdb_set_mountpoint(database, StringValueCStr(path));
|
75
|
+
return rbpod_database_mountpoint_get(self);
|
76
|
+
}
|
77
|
+
|
78
|
+
static void rbpod_database_deallocate(void *handle) {
|
79
|
+
Itdb_iTunesDB **database = (Itdb_iTunesDB **) handle;
|
80
|
+
itdb_free(*database);
|
81
|
+
xfree(handle);
|
82
|
+
return;
|
83
|
+
}
|
84
|
+
|
85
|
+
static VALUE rbpod_database_create(VALUE self, VALUE device_name, VALUE mount_point, VALUE model_number) {
|
86
|
+
Itdb_iTunesDB **database = ALLOC(Itdb_iTunesDB *);
|
87
|
+
gchar *_mount_point, *_model_number, *_device_name;
|
88
|
+
|
89
|
+
_device_name = StringValueCStr(device_name);
|
90
|
+
_mount_point = StringValueCStr(mount_point);
|
91
|
+
|
92
|
+
/* GPod can function with a NULL model number, however, artwork will not function. */
|
93
|
+
_model_number = !NIL_P(model_number) ? StringValueCStr(model_number) : NULL;
|
94
|
+
|
95
|
+
/* Initialize the iPod at this mount point, with this device name and model number. */
|
96
|
+
/* TODO: Improve error handling. */
|
97
|
+
if (itdb_init_ipod(_mount_point, _model_number, _device_name, NULL) == FALSE) {
|
98
|
+
free(database);
|
99
|
+
rb_raise(rb_eRuntimeError, "Unable to format this iPod.");
|
100
|
+
return Qfalse;
|
101
|
+
}
|
102
|
+
|
103
|
+
/* Parse the newly created database. */
|
104
|
+
/* TODO: Improve error handling. */
|
105
|
+
*database = itdb_parse(_mount_point, NULL);
|
106
|
+
|
107
|
+
if (*database == NULL) {
|
108
|
+
free(database);
|
109
|
+
rb_raise(rb_eRuntimeError, "Not an iPod mount point.");
|
110
|
+
return Qnil;
|
111
|
+
}
|
112
|
+
|
113
|
+
/* Return an instance of this class using the newly created database. */
|
114
|
+
return Data_Wrap_Struct(self, NULL, rbpod_database_deallocate, (void *) database);
|
115
|
+
}
|
116
|
+
|
117
|
+
static VALUE rbpod_database_initialize(VALUE self, VALUE mount_point) {
|
118
|
+
Itdb_iTunesDB **database;
|
119
|
+
|
120
|
+
/* Extract the wrapper from the class itself. */
|
121
|
+
Data_Get_Struct(self, Itdb_iTunesDB *, database);
|
122
|
+
|
123
|
+
/* Try to parse the database from the given mount point. */
|
124
|
+
/* TODO: Improve error handling. */
|
125
|
+
*database = itdb_parse(StringValueCStr(mount_point), NULL);
|
126
|
+
|
127
|
+
/* The given mount point was not an iPod mount point. */
|
128
|
+
if (*database == NULL) {
|
129
|
+
rb_raise(rb_eRuntimeError, "Not an iPod mount point.");
|
130
|
+
return Qnil;
|
131
|
+
}
|
132
|
+
|
133
|
+
return self;
|
134
|
+
}
|
135
|
+
|
136
|
+
inline VALUE rbpod_database_wrap(Itdb_iTunesDB **database, gboolean freeable) {
|
137
|
+
return Data_Wrap_Struct(cRbPodDatabase, NULL, freeable ? rbpod_database_deallocate : NULL, (void *) database);
|
138
|
+
}
|
139
|
+
|
140
|
+
static VALUE rbpod_database_allocate(VALUE self) {
|
141
|
+
Itdb_iTunesDB **database = ALLOC(Itdb_iTunesDB *);
|
142
|
+
|
143
|
+
/* Initialize a new, empty database. */
|
144
|
+
*database = itdb_new();
|
145
|
+
|
146
|
+
return rbpod_database_wrap(database, TRUE);
|
147
|
+
}
|
148
|
+
|
149
|
+
void Init_rbpod_database(void) {
|
150
|
+
cRbPodDatabase = rb_define_class_under(mRbPod, "Database", rb_cObject);
|
151
|
+
|
152
|
+
rb_define_alloc_func(cRbPodDatabase, rbpod_database_allocate);
|
153
|
+
|
154
|
+
rb_define_method(cRbPodDatabase, "initialize", rbpod_database_initialize, 1);
|
155
|
+
|
156
|
+
rb_define_singleton_method(cRbPodDatabase, "create!", rbpod_database_create, 3);
|
157
|
+
|
158
|
+
rb_define_method(cRbPodDatabase, "mountpoint", rbpod_database_mountpoint_get, 0);
|
159
|
+
rb_define_method(cRbPodDatabase, "mountpoint=", rbpod_database_mountpoint_set, 1);
|
160
|
+
|
161
|
+
rb_define_method(cRbPodDatabase, "id", rbpod_database_id_get, 0);
|
162
|
+
rb_define_method(cRbPodDatabase, "version", rbpod_database_version_get, 0);
|
163
|
+
rb_define_method(cRbPodDatabase, "filename", rbpod_database_filename_get, 0);
|
164
|
+
|
165
|
+
rb_define_method(cRbPodDatabase, "device", rbpod_database_device_get, 0);
|
166
|
+
rb_define_method(cRbPodDatabase, "tracks", rbpod_database_tracks_get, 0);
|
167
|
+
rb_define_method(cRbPodDatabase, "playlists", rbpod_database_playlists_get, 0);
|
168
|
+
|
169
|
+
rb_define_method(cRbPodDatabase, "synchronized?", rbpod_database_is_synchronized, 0);
|
170
|
+
|
171
|
+
rb_define_method(cRbPodDatabase, "write!", rbpod_database_write, 0);
|
172
|
+
}
|
173
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
/* database.h */
|
2
|
+
|
3
|
+
#ifndef RBPOD_DATABASE_H
|
4
|
+
#define RBPOD_DATABASE_H
|
5
|
+
|
6
|
+
extern VALUE cRbPodDatabase;
|
7
|
+
|
8
|
+
void Init_rbpod_database(void);
|
9
|
+
|
10
|
+
inline VALUE rbpod_database_wrap(Itdb_iTunesDB **database, gboolean freeable);
|
11
|
+
|
12
|
+
#endif /* RBPOD_DATABASE_H */
|
data/ext/rbpod/device.c
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
/* device.c */
|
2
|
+
|
3
|
+
#include "rbpod.h"
|
4
|
+
#include "device.h"
|
5
|
+
|
6
|
+
VALUE cRbPodDevice;
|
7
|
+
|
8
|
+
static inline Itdb_Device *rbpod_device_unwrap(VALUE self) {
|
9
|
+
Itdb_Device *device = NULL;
|
10
|
+
Data_Get_Struct(self, Itdb_Device, device);
|
11
|
+
return device;
|
12
|
+
}
|
13
|
+
|
14
|
+
static VALUE rbpod_device_has_chapter_image_support(VALUE self) {
|
15
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
16
|
+
return BooleanValue(itdb_device_supports_chapter_image(device));
|
17
|
+
}
|
18
|
+
|
19
|
+
static VALUE rbpod_device_has_podcast_support(VALUE self) {
|
20
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
21
|
+
return BooleanValue(itdb_device_supports_podcast(device));
|
22
|
+
}
|
23
|
+
|
24
|
+
static VALUE rbpod_device_has_artwork_support(VALUE self) {
|
25
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
26
|
+
return BooleanValue(itdb_device_supports_artwork(device));
|
27
|
+
}
|
28
|
+
|
29
|
+
static VALUE rbpod_device_has_video_support(VALUE self) {
|
30
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
31
|
+
return BooleanValue(itdb_device_supports_video(device));
|
32
|
+
}
|
33
|
+
|
34
|
+
static VALUE rbpod_device_has_photo_support(VALUE self) {
|
35
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
36
|
+
return BooleanValue(itdb_device_supports_photo(device));
|
37
|
+
}
|
38
|
+
|
39
|
+
static VALUE rbpod_device_model_name_get(VALUE self) {
|
40
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
41
|
+
Itdb_IpodInfo *info = itdb_device_get_ipod_info(device);
|
42
|
+
return rb_str_new2(itdb_info_get_ipod_model_name_string(info->ipod_model));
|
43
|
+
}
|
44
|
+
|
45
|
+
static VALUE rbpod_device_model_number_get(VALUE self) {
|
46
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
47
|
+
Itdb_IpodInfo *info = itdb_device_get_ipod_info(device);
|
48
|
+
return rb_str_new2(info->model_number);
|
49
|
+
}
|
50
|
+
|
51
|
+
static VALUE rbpod_device_generation_get(VALUE self) {
|
52
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
53
|
+
const Itdb_IpodInfo *info = itdb_device_get_ipod_info(device);
|
54
|
+
return rb_str_new2(itdb_info_get_ipod_generation_string(info->ipod_generation));
|
55
|
+
}
|
56
|
+
|
57
|
+
static VALUE rbpod_device_capacity_get(VALUE self) {
|
58
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
59
|
+
const Itdb_IpodInfo *info = itdb_device_get_ipod_info(device);
|
60
|
+
return DBL2NUM(info->capacity);
|
61
|
+
}
|
62
|
+
|
63
|
+
static VALUE rbpod_device_sysinfo_get(VALUE self, VALUE key) {
|
64
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
65
|
+
gchar *value = NULL;
|
66
|
+
VALUE result;
|
67
|
+
|
68
|
+
value = itdb_device_get_sysinfo(device, StringValueCStr(key));
|
69
|
+
result = (value == NULL) ? Qnil : rb_str_new2(value);
|
70
|
+
|
71
|
+
g_free(value);
|
72
|
+
return result;
|
73
|
+
|
74
|
+
}
|
75
|
+
|
76
|
+
static VALUE rbpod_device_sysinfo_set(VALUE self, VALUE key, VALUE value) {
|
77
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
78
|
+
gchar *_value, *_key;
|
79
|
+
|
80
|
+
_key = StringValueCStr(key);
|
81
|
+
_value = !NIL_P(value) ? StringValueCStr(value) : NULL;
|
82
|
+
|
83
|
+
itdb_device_set_sysinfo(device, _key, _value);
|
84
|
+
|
85
|
+
return rbpod_device_sysinfo_get(self, _key);
|
86
|
+
}
|
87
|
+
|
88
|
+
static VALUE rbpod_device_sysinfo_write(VALUE self) {
|
89
|
+
Itdb_Device *device = rbpod_device_unwrap(self);
|
90
|
+
/* TODO: Improve error handling. */
|
91
|
+
return BooleanValue(itdb_device_write_sysinfo(device, NULL));
|
92
|
+
}
|
93
|
+
|
94
|
+
static VALUE rbpod_device_initialize(VALUE self) {
|
95
|
+
/* Private. */
|
96
|
+
return self;
|
97
|
+
}
|
98
|
+
|
99
|
+
static void rbpod_device_deallocate(void *handle) {
|
100
|
+
itdb_device_free((Itdb_Device *) handle);
|
101
|
+
}
|
102
|
+
|
103
|
+
inline VALUE rbpod_device_wrap(Itdb_Device *device, gboolean freeable) {
|
104
|
+
return Data_Wrap_Struct(cRbPodDevice, NULL, freeable ? rbpod_device_deallocate : NULL, (void *) device);
|
105
|
+
}
|
106
|
+
|
107
|
+
static VALUE rbpod_device_allocate(VALUE self) {
|
108
|
+
return rbpod_device_wrap(itdb_device_new(), TRUE);
|
109
|
+
}
|
110
|
+
|
111
|
+
void Init_rbpod_device(void) {
|
112
|
+
cRbPodDevice = rb_define_class_under(mRbPod, "Device", rb_cObject);
|
113
|
+
|
114
|
+
rb_define_alloc_func(cRbPodDevice, rbpod_device_allocate);
|
115
|
+
|
116
|
+
/* We don't want to explicitly create these, so mark the constructor as private. */
|
117
|
+
rb_define_private_method(cRbPodDevice, "initialize", rbpod_device_initialize, 0);
|
118
|
+
|
119
|
+
rb_define_method(cRbPodDevice, "[]", rbpod_device_sysinfo_get, 1);
|
120
|
+
rb_define_method(cRbPodDevice, "[]=", rbpod_device_sysinfo_set, 2);
|
121
|
+
rb_define_method(cRbPodDevice, "save!", rbpod_device_sysinfo_write, 0);
|
122
|
+
|
123
|
+
rb_define_method(cRbPodDevice, "capacity", rbpod_device_capacity_get, 0);
|
124
|
+
rb_define_method(cRbPodDevice, "generation", rbpod_device_generation_get, 0);
|
125
|
+
rb_define_method(cRbPodDevice, "model_name", rbpod_device_model_name_get, 0);
|
126
|
+
rb_define_method(cRbPodDevice, "model_number", rbpod_device_model_number_get, 0);
|
127
|
+
|
128
|
+
rb_define_method(cRbPodDevice, "supports_photos?", rbpod_device_has_photo_support, 0);
|
129
|
+
rb_define_method(cRbPodDevice, "supports_videos?", rbpod_device_has_video_support, 0);
|
130
|
+
rb_define_method(cRbPodDevice, "supports_artwork?", rbpod_device_has_artwork_support, 0);
|
131
|
+
rb_define_method(cRbPodDevice, "supports_podcasts?", rbpod_device_has_podcast_support, 0);
|
132
|
+
rb_define_method(cRbPodDevice, "supports_chapter_images?", rbpod_device_has_chapter_image_support, 0);
|
133
|
+
|
134
|
+
return;
|
135
|
+
}
|
136
|
+
|
data/ext/rbpod/device.h
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
/* playlist.c */
|
2
|
+
|
3
|
+
#include "rbpod.h"
|
4
|
+
#include "track.h"
|
5
|
+
#include "playlist.h"
|
6
|
+
|
7
|
+
VALUE cRbPodPlaylist;
|
8
|
+
|
9
|
+
static inline Itdb_Playlist *rbpod_playlist_unwrap(VALUE self) {
|
10
|
+
Itdb_Playlist *playlist = NULL;
|
11
|
+
Data_Get_Struct(self, Itdb_Playlist, playlist);
|
12
|
+
return playlist;
|
13
|
+
}
|
14
|
+
|
15
|
+
static VALUE rbpod_playlist_is_podcast(VALUE self) {
|
16
|
+
Itdb_Playlist *playlist = rbpod_playlist_unwrap(self);
|
17
|
+
return BooleanValue(itdb_playlist_is_podcasts(playlist));
|
18
|
+
}
|
19
|
+
|
20
|
+
static VALUE rbpod_playlist_is_master(VALUE self) {
|
21
|
+
Itdb_Playlist *playlist = rbpod_playlist_unwrap(self);
|
22
|
+
return BooleanValue(itdb_playlist_is_mpl(playlist));
|
23
|
+
}
|
24
|
+
|
25
|
+
static VALUE rbpod_playlist_is_smart(VALUE self) {
|
26
|
+
Itdb_Playlist *playlist = rbpod_playlist_unwrap(self);
|
27
|
+
return BooleanValue(playlist->is_spl);
|
28
|
+
}
|
29
|
+
|
30
|
+
static VALUE rbpod_playlist_tracks_get(VALUE self) {
|
31
|
+
Itdb_Playlist *playlist = rbpod_playlist_unwrap(self);
|
32
|
+
return rbpod_collection_wrap(playlist->members, cRbPodTrack, FALSE);
|
33
|
+
}
|
34
|
+
|
35
|
+
static VALUE rbpod_playlist_length_get(VALUE self) {
|
36
|
+
Itdb_Playlist *playlist = rbpod_playlist_unwrap(self);
|
37
|
+
return INT2NUM(playlist->num);
|
38
|
+
}
|
39
|
+
|
40
|
+
static VALUE rbpod_playlist_name_get(VALUE self) {
|
41
|
+
Itdb_Playlist *playlist = rbpod_playlist_unwrap(self);
|
42
|
+
return rb_str_new2(playlist->name);
|
43
|
+
}
|
44
|
+
|
45
|
+
static VALUE rbpod_playlist_id_get(VALUE self) {
|
46
|
+
Itdb_Playlist *playlist = rbpod_playlist_unwrap(self);
|
47
|
+
return INT2FIX(playlist->id);
|
48
|
+
}
|
49
|
+
|
50
|
+
static VALUE rbpod_playlist_initialize(VALUE self) {
|
51
|
+
return self;
|
52
|
+
}
|
53
|
+
|
54
|
+
static void rbpod_playlist_deallocate(void *handle) {
|
55
|
+
itdb_playlist_free((Itdb_Playlist *) handle);
|
56
|
+
}
|
57
|
+
|
58
|
+
inline VALUE rbpod_playlist_wrap(Itdb_Playlist *playlist, gboolean freeable) {
|
59
|
+
return Data_Wrap_Struct(cRbPodPlaylist, NULL, freeable ? rbpod_playlist_deallocate : NULL, (void *) playlist);
|
60
|
+
}
|
61
|
+
|
62
|
+
static VALUE rbpod_playlist_allocate(VALUE self) {
|
63
|
+
Itdb_Playlist *playlist = itdb_playlist_new("Untitled Playlist", FALSE);
|
64
|
+
return rbpod_playlist_wrap(playlist, TRUE);
|
65
|
+
}
|
66
|
+
|
67
|
+
void Init_rbpod_playlist(void) {
|
68
|
+
cRbPodPlaylist = rb_define_class_under(mRbPod, "Playlist", rb_cObject);
|
69
|
+
|
70
|
+
rb_define_alloc_func(cRbPodPlaylist, rbpod_playlist_allocate);
|
71
|
+
|
72
|
+
rb_define_method(cRbPodPlaylist, "initialize", rbpod_playlist_initialize, 0);
|
73
|
+
|
74
|
+
rb_define_method(cRbPodPlaylist, "id", rbpod_playlist_id_get, 0);
|
75
|
+
rb_define_method(cRbPodPlaylist, "name", rbpod_playlist_name_get, 0);
|
76
|
+
rb_define_method(cRbPodPlaylist, "length", rbpod_playlist_length_get, 0);
|
77
|
+
rb_define_method(cRbPodPlaylist, "tracks", rbpod_playlist_tracks_get, 0);
|
78
|
+
|
79
|
+
rb_define_method(cRbPodPlaylist, "smart_playlist?", rbpod_playlist_is_smart, 0);
|
80
|
+
rb_define_method(cRbPodPlaylist, "master_playlist?", rbpod_playlist_is_master, 0);
|
81
|
+
rb_define_method(cRbPodPlaylist, "podcast_playlist?", rbpod_playlist_is_podcast, 0);
|
82
|
+
|
83
|
+
return;
|
84
|
+
}
|
85
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
/* playlist.h */
|
2
|
+
|
3
|
+
#ifndef RBPOD_PLAYLIST_H
|
4
|
+
#define RBPOD_PLAYLIST_H
|
5
|
+
|
6
|
+
extern VALUE cRbPodPlaylist;
|
7
|
+
|
8
|
+
void Init_rbpod_playlist(void);
|
9
|
+
|
10
|
+
inline VALUE rbpod_playlist_wrap(Itdb_Playlist *playlist, gboolean freeable);
|
11
|
+
|
12
|
+
#endif /* RBPOD_PLAYLIST_H */
|
data/ext/rbpod/rbpod.c
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
/* rbpod.c */
|
2
|
+
|
3
|
+
#include "rbpod.h"
|
4
|
+
#include "track.h"
|
5
|
+
#include "device.h"
|
6
|
+
#include "playlist.h"
|
7
|
+
#include "database.h"
|
8
|
+
#include "collection.h"
|
9
|
+
|
10
|
+
VALUE mRbPod;
|
11
|
+
|
12
|
+
void Init_rbpod(void) {
|
13
|
+
/* Set up the top-level RbPod module. */
|
14
|
+
mRbPod = rb_define_module("RbPod");
|
15
|
+
|
16
|
+
/* Set up the RbPod::Database class. */
|
17
|
+
Init_rbpod_database();
|
18
|
+
|
19
|
+
/* Set up the RbPod::Device class. */
|
20
|
+
Init_rbpod_device();
|
21
|
+
|
22
|
+
/* Set up the RbPod::Collection class. */
|
23
|
+
Init_rbpod_collection();
|
24
|
+
|
25
|
+
/* Set up the RbPod::Playlist class. */
|
26
|
+
Init_rbpod_playlist();
|
27
|
+
|
28
|
+
/* Set up the RbPod::Track class. */
|
29
|
+
Init_rbpod_track();
|
30
|
+
|
31
|
+
return;
|
32
|
+
}
|
33
|
+
|
34
|
+
|
data/ext/rbpod/rbpod.h
ADDED
data/ext/rbpod/track.c
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
/* track.c */
|
2
|
+
|
3
|
+
#include "rbpod.h"
|
4
|
+
#include "track.h"
|
5
|
+
|
6
|
+
VALUE cRbPodTrack;
|
7
|
+
|
8
|
+
static inline Itdb_Track *rbpod_track_unwrap(VALUE self) {
|
9
|
+
Itdb_Track *track = NULL;
|
10
|
+
Data_Get_Struct(self, Itdb_Track, track);
|
11
|
+
return track;
|
12
|
+
}
|
13
|
+
|
14
|
+
static VALUE rbpod_track_is_transferred(VALUE self) {
|
15
|
+
Itdb_Track *track = rbpod_track_unwrap(self);
|
16
|
+
return BooleanValue(track->transferred);
|
17
|
+
}
|
18
|
+
|
19
|
+
static VALUE rbpod_track_ipod_path_get(VALUE self) {
|
20
|
+
Itdb_Track *track = rbpod_track_unwrap(self);
|
21
|
+
return rb_str_new2(track->ipod_path);
|
22
|
+
}
|
23
|
+
|
24
|
+
static VALUE rbpod_track_file_type_get(VALUE self) {
|
25
|
+
Itdb_Track *track = rbpod_track_unwrap(self);
|
26
|
+
return rb_str_new2(track->filetype);
|
27
|
+
}
|
28
|
+
|
29
|
+
static VALUE rbpod_track_artist_get(VALUE self) {
|
30
|
+
Itdb_Track *track = rbpod_track_unwrap(self);
|
31
|
+
return rb_str_new2(track->artist);
|
32
|
+
}
|
33
|
+
|
34
|
+
static VALUE rbpod_track_album_get(VALUE self) {
|
35
|
+
Itdb_Track *track = rbpod_track_unwrap(self);
|
36
|
+
return rb_str_new2(track->album);
|
37
|
+
}
|
38
|
+
|
39
|
+
static VALUE rbpod_track_title_get(VALUE self) {
|
40
|
+
Itdb_Track *track = rbpod_track_unwrap(self);
|
41
|
+
return rb_str_new2(track->title);
|
42
|
+
}
|
43
|
+
|
44
|
+
static VALUE rbpod_track_id_get(VALUE self) {
|
45
|
+
Itdb_Track *track = rbpod_track_unwrap(self);
|
46
|
+
return rb_str_new2(track->filetype);
|
47
|
+
}
|
48
|
+
|
49
|
+
static VALUE rbpod_track_initialize(VALUE self) {
|
50
|
+
return self;
|
51
|
+
}
|
52
|
+
|
53
|
+
static void rbpod_track_deallocate(void *handle) {
|
54
|
+
itdb_track_free((Itdb_Track *) handle);
|
55
|
+
return;
|
56
|
+
}
|
57
|
+
|
58
|
+
inline VALUE rbpod_track_wrap(Itdb_Track *track, gboolean freeable) {
|
59
|
+
return Data_Wrap_Struct(cRbPodTrack, NULL, freeable ? rbpod_track_deallocate : NULL, (void *) track);
|
60
|
+
}
|
61
|
+
|
62
|
+
static VALUE rbpod_track_allocate(VALUE self) {
|
63
|
+
return rbpod_track_wrap(itdb_track_new(), TRUE);
|
64
|
+
}
|
65
|
+
|
66
|
+
void Init_rbpod_track(void) {
|
67
|
+
cRbPodTrack = rb_define_class_under(mRbPod, "Track", rb_cObject);
|
68
|
+
|
69
|
+
rb_define_alloc_func(cRbPodTrack, rbpod_track_allocate);
|
70
|
+
|
71
|
+
rb_define_method(cRbPodTrack, "initialize", rbpod_track_initialize, 0);
|
72
|
+
|
73
|
+
rb_define_method(cRbPodTrack, "id", rbpod_track_id_get, 0);
|
74
|
+
rb_define_method(cRbPodTrack, "title", rbpod_track_title_get, 0);
|
75
|
+
rb_define_method(cRbPodTrack, "album", rbpod_track_album_get, 0);
|
76
|
+
rb_define_method(cRbPodTrack, "artist", rbpod_track_artist_get, 0);
|
77
|
+
rb_define_method(cRbPodTrack, "file_type", rbpod_track_file_type_get, 0);
|
78
|
+
rb_define_method(cRbPodTrack, "ipod_path", rbpod_track_ipod_path_get, 0);
|
79
|
+
|
80
|
+
rb_define_method(cRbPodTrack, "transferred?", rbpod_track_is_transferred, 0);
|
81
|
+
|
82
|
+
return;
|
83
|
+
}
|
84
|
+
|
data/ext/rbpod/track.h
ADDED
data/lib/rbpod.rb
ADDED
data/rbpod.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rbpod/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rbpod"
|
8
|
+
spec.version = RbPod::VERSION
|
9
|
+
|
10
|
+
spec.authors = ["Joshua M. Keyes"]
|
11
|
+
spec.email = ["joshua.michael.keyes@gmail.com"]
|
12
|
+
|
13
|
+
spec.summary = %q{Ruby bindings to the libgpod library.}
|
14
|
+
spec.description = %q{Lightweight native bindings to the libgpod library.}
|
15
|
+
spec.homepage = "https://github.com/jmkeyes/rbpod"
|
16
|
+
spec.requirements << 'libgpod-1.0'
|
17
|
+
spec.license = 'MIT'
|
18
|
+
|
19
|
+
spec.files = `git ls-files`.split($/)
|
20
|
+
spec.extensions = ["ext/rbpod/extconf.rb"]
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
26
|
+
spec.add_development_dependency 'rake'
|
27
|
+
|
28
|
+
spec.add_dependency 'trollop', '~> 2.0'
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rbpod
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joshua M. Keyes
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-10-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: trollop
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.0'
|
62
|
+
description: Lightweight native bindings to the libgpod library.
|
63
|
+
email:
|
64
|
+
- joshua.michael.keyes@gmail.com
|
65
|
+
executables:
|
66
|
+
- rbpod
|
67
|
+
extensions:
|
68
|
+
- ext/rbpod/extconf.rb
|
69
|
+
extra_rdoc_files: []
|
70
|
+
files:
|
71
|
+
- .gitignore
|
72
|
+
- Gemfile
|
73
|
+
- LICENSE.txt
|
74
|
+
- README.md
|
75
|
+
- Rakefile
|
76
|
+
- bin/rbpod
|
77
|
+
- ext/rbpod/collection.c
|
78
|
+
- ext/rbpod/collection.h
|
79
|
+
- ext/rbpod/database.c
|
80
|
+
- ext/rbpod/database.h
|
81
|
+
- ext/rbpod/device.c
|
82
|
+
- ext/rbpod/device.h
|
83
|
+
- ext/rbpod/extconf.rb
|
84
|
+
- ext/rbpod/playlist.c
|
85
|
+
- ext/rbpod/playlist.h
|
86
|
+
- ext/rbpod/rbpod.c
|
87
|
+
- ext/rbpod/rbpod.h
|
88
|
+
- ext/rbpod/track.c
|
89
|
+
- ext/rbpod/track.h
|
90
|
+
- lib/rbpod.rb
|
91
|
+
- lib/rbpod/version.rb
|
92
|
+
- rbpod.gemspec
|
93
|
+
homepage: https://github.com/jmkeyes/rbpod
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements:
|
113
|
+
- libgpod-1.0
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 1.8.23
|
116
|
+
signing_key:
|
117
|
+
specification_version: 3
|
118
|
+
summary: Ruby bindings to the libgpod library.
|
119
|
+
test_files: []
|