lllibrary 0.0.1 → 0.0.2

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.md CHANGED
@@ -1,85 +1,21 @@
1
- #lllibrary
1
+ # lllibrary
2
2
 
3
3
  `lllibrary` is a Ruby library that manages music libraries. It stores tracks (with metadata) and playlists in a database (using ActiveRecord), and gives you a sort of DSL to build complex playlists with ease.
4
4
 
5
5
  ## Install
6
6
 
7
- $ gem install lllibrary
8
-
9
- ## Usage
10
-
11
- This gives you an idea. Better documentation coming soon.
12
-
13
- # Lllibrary
14
- library = Lllibrary.new(File.join(ENV["HOME"], "music.sqlite3"), "sqlite3") do |t|
15
- # provide a block like this if the database doesn't exist yet.
16
- # the location, created_at, and updated_at fields are always
17
- # created, the rest must be specified like so.
7
+ First, you need [taglib](http://taglib.github.com/). Get it from there and install it, or [install it with a package manager](https://github.com/robinst/taglib-ruby#installation). (Apparently Windows users don't have to do this, as the taglib DLL is bundled with the taglib-ruby gem.)
18
8
 
19
- # adds database fields for title, artist, etc.
20
- t.default_metadata
9
+ Then, just type:
21
10
 
22
- # adds all the database fields that iTunes uses
23
- t.itunes_metadata
24
-
25
- # you can also specify your own custom fields
26
- t.string :five_word_review
27
- t.integer :popularity, null: false, default: 0
28
- end
29
- library.tracks
30
- library.playlists
31
- library.add(Dir["Music/**/*.{mp3,m4a,ogg}"], &blk)
32
- library.add_playlist(:favourites, library.select { rating 100 })
33
- library.import(:itunes, "/path/to/iTunes Music Library.xml", &blk)
34
- library.clear_playlists
35
- library.clear_all
36
-
37
- # Lllibrary::Track
38
- track.playlists
39
- track.location
40
- track.created_at
41
- track.updated_at
42
- track.send(metadata_field)
43
-
44
- # Lllibrary::Playlist
45
- playlist.name
46
- playlist.tracks
47
- playlist.empty?
48
- playlist.length
49
- playlist.add(track_or_tracks, index = nil)
50
- playlist.remove(track_or_tracks)
51
- playlist.total_length
52
- playlist.sort(&blk)
53
- playlist.shuffle
54
- playlist.playlist_items
55
- playlist.created_at
56
- playlist.updated_at
57
- playlist.clear
11
+ $ gem install lllibrary
58
12
 
59
- # Lllibrary::PlaylistItem
60
- playlist_item.position
61
- playlist_item.track
62
- playlist_item.playlist
63
- playlist_item.created_at
64
- playlist_item.updated_at
13
+ ## Usage
65
14
 
66
- # Selectors DSL
67
- library.select do
68
- title("blackout") # equivalent to library.tracks.where("title LIKE '%blackout%'").all
69
- title("blackout", match: :left) # equivalent to library.tracks.where("title LIKE 'blackout%'").all
70
- title("blackout", match: :exact) # equivalent to library.tracks.where("title LIKE 'blackout'").all
71
- length("1:00".."2:00") # equivalent to library.tracks.where(length: 60000...120000).all
72
- rating(gte: 80) # equivalent to library.tracks.where("rating >= 80").all
73
- none # []
74
- all # library.tracks.all
75
- end
15
+ Erm... read the inline docs.
76
16
 
77
17
  ## TODO
78
18
 
79
19
  * Better error reporting
80
- * Fix bug with time selectors
81
- * Get metadata from tags in audio files without requiring a C library to be installed (like taglib)
82
- * Allow blocks to be passed to Lllibrary#add and Lllibrary#import
83
- * Figure out how to handle multiple database connections
84
20
  * Tests, documentation, the like
85
21
 
@@ -1,5 +1,6 @@
1
1
  require "active_record"
2
2
  require "plist"
3
+ require "taglib"
3
4
 
4
5
  require "lllibrary/track"
5
6
  require "lllibrary/playlist"
@@ -8,7 +9,66 @@ require "lllibrary/playlist_item"
8
9
  require "lllibrary/database"
9
10
  require "lllibrary/dsl"
10
11
 
12
+ # A Lllibrary represents a database of tracks and playlists. It helps you manage
13
+ # and query this database by automatically pulling metadata from the audio files
14
+ # you add, being able to import music libraries from other programs like iTunes,
15
+ # and providing a DSL for selecting songs from your music library with ease.
11
16
  class Lllibrary
17
+ attr_reader :dsl
18
+
19
+ # These are the fields that taglib can pull from audio files. The keys are
20
+ # taglib's names for them, the values are the column names that lllibrary uses
21
+ # by default.
22
+ TAGLIB_METADATA = {
23
+ tag: {
24
+ album: "album",
25
+ artist: "artist",
26
+ comment: "comments",
27
+ genre: "genre",
28
+ title: "title",
29
+ track: "track_number",
30
+ year: "year"
31
+ },
32
+ audio_properties: {
33
+ length: "total_time",
34
+ bitrate: "bit_rate",
35
+ channels: "channels",
36
+ sample_rate: "sample_rate"
37
+ }
38
+ }
39
+
40
+ # Create a new Lllibrary object. Takes the path to the database and the
41
+ # database adapter (e.g. "sqlite3"). It also takes a block which specifies
42
+ # the schema of the database. Without this block, the tracks table will only
43
+ # have three fields: location, created_at, and updated_at. You need to
44
+ # provide a block if you want your tracks table to have fields for metadata.
45
+ # Here's an example:
46
+ #
47
+ # library = Lllibrary.new("songs.sqlite3", "sqlite3") do |t|
48
+ # t.string :title
49
+ # t.string :artist
50
+ # t.string :album
51
+ # t.integer :year
52
+ # end
53
+ #
54
+ # There are also a couple aliases that automatically add a bunch of metadata
55
+ # fields for you. These are t.default_metadata and t.itunes_metadata.
56
+ #
57
+ # t.default_metadata adds these fields: album, artist, comments, genre, title,
58
+ # track_number, year, total_time, bit_rate, channels, sample_rate. These are
59
+ # the fields that taglib is able to fill in by examining the audio files you
60
+ # add.
61
+ #
62
+ # t.itunes_metadata add almost all of the metadata fields that iTunes uses,
63
+ # and will be filled in when you import your iTunes library. These fields are:
64
+ # original_id, title, artist, composer, album, album_artist, genre, total_time,
65
+ # disc_number, disc_count, track_number, track_count, year, date_modified,
66
+ # date_added, bit_rate, sample_rate, comments, play_count, play_date, skip_count,
67
+ # skip_date, rating.
68
+ #
69
+ # Note: ActiveRecord doesn't seem to allow you to connect to multiple databases
70
+ # at the same time. Please only instantiate one Lllibrary per process.
71
+ #
12
72
  def initialize(db_path, db_adapter, &schema_blk)
13
73
  @db = Lllibrary::Database.new(db_path, db_adapter)
14
74
  if schema_blk
@@ -22,26 +82,77 @@ class Lllibrary
22
82
  @dsl = Lllibrary::DSL.new(self)
23
83
  end
24
84
 
85
+ # Takes a block, and evaluates that block in the context of the Lllibrary's DSL.
86
+ # Inside the block, every database field on the tracks table becomes a method
87
+ # called a selector. Each selector takes a value to match tracks against, and
88
+ # returns an Array of those tracks. String-like fields have string selectors,
89
+ # and number-like fields have numeric selectors. There is also an all selector,
90
+ # a none selector, and a playlist selector. See dsl.rb for a detailed
91
+ # description of these.
92
+ #
93
+ # library.select do
94
+ # composer(:rachmanino) # example of string selector
95
+ # year(2010..2012) # example of numeric selector
96
+ # total_time(gt: "6:00") # example of time selector
97
+ # playlist(:energetic) # example of playlist selector
98
+ # all # returns array of all tracks
99
+ # none # returns empty array
100
+ # end
101
+ #
25
102
  def select(&blk)
26
103
  @dsl.instance_eval &blk
27
104
  end
28
105
 
106
+ # Returns a bare Relation of the Track model.
29
107
  def tracks
30
108
  Lllibrary::Track.scoped
31
109
  end
32
110
 
111
+ # Returns a bare Relation of the Playlist model.
33
112
  def playlists
34
113
  Lllibrary::Playlist.scoped
35
114
  end
36
115
 
116
+ # Adds one or more tracks to the library. Takes a path to the audio file you
117
+ # wanted added, or array of multiple paths. Uses taglib to fill in metadata
118
+ # for each track, if the corresponding database fields exist. After filling in
119
+ # the metadata, if a block was given, it yields the Track object to this block.
120
+ # If the block returns a Track object (with possible modifications of your own),
121
+ # it then saves the Track and goes on to the next one. If the block doesn't
122
+ # return a Track, the track is not saved.
123
+ #
124
+ # For example, here's how you would use the filename as the title of the track
125
+ # if the title is missing from the audio file's metadata:
126
+ #
127
+ # library.add(Dir["Music/**/*.mp3"]) do |track|
128
+ # track.title ||= File.basename(track.location, ".mp3")
129
+ # track
130
+ # end
131
+ #
37
132
  def add(paths_to_tracks, &blk)
38
133
  Array(paths_to_tracks).each do |path|
39
- track = tracks.new(location: path)
134
+ track = tracks.new(location: File.expand_path(path))
135
+
136
+ TagLib::FileRef.open(path) do |audio_file|
137
+ TAGLIB_METADATA.each do |tag_or_audio_properties, properties|
138
+ if audio_file.send(tag_or_audio_properties)
139
+ properties.each do |property, column|
140
+ value = audio_file.send(tag_or_audio_properties).send(property)
141
+ value *= 1000 if property == :length # convert seconds to milliseconds
142
+ value = nil if value == 0
143
+ track.send("#{column}=", value) if Track.column_names.include? column
144
+ end
145
+ end
146
+ end
147
+ end
148
+
40
149
  track = blk.call(track) if blk
41
150
  track.save! if track.is_a?(Track)
42
151
  end
43
152
  end
44
153
 
154
+ # Creates a new playlist and saves it. Takes a name for the playlist and an
155
+ # Array of Tracks. The order of the Tracks is preserved, of course.
45
156
  def add_playlist(name, tracks)
46
157
  playlist = playlists.new(name: name)
47
158
  playlist.save!
@@ -54,7 +165,14 @@ class Lllibrary
54
165
  end
55
166
  end
56
167
 
57
- def import(type, path, options = {})
168
+ # Imports a music library from another program, such as iTunes. Incidentally,
169
+ # iTunes is the only such program supported right now. Here's an example:
170
+ #
171
+ # library.import :itunes, "path/to/iTunes Music Library.xml", logger: method(:puts)
172
+ #
173
+ # I will probably redesign this method to be more flexible and such, so I'll
174
+ # curb my documenting of it any further till then.
175
+ def import(type, path, options = {}, &blk)
58
176
  logger = options[:logger]
59
177
  if type == :itunes
60
178
  logger.("Parsing XML...") if logger
@@ -93,7 +211,8 @@ class Lllibrary
93
211
  end
94
212
 
95
213
  track = tracks.new(attributes)
96
- if track.save
214
+ track = blk.call(track)
215
+ if track && track.save
97
216
  num_tracks += 1
98
217
  end
99
218
  end
@@ -125,10 +244,12 @@ class Lllibrary
125
244
  end
126
245
  end
127
246
 
247
+ # Deletes all your playlists.
128
248
  def clear_playlists
129
249
  playlists.destroy_all
130
250
  end
131
251
 
252
+ # Deletes all tracks and playlists.
132
253
  def clear_all
133
254
  tracks.destroy_all
134
255
  clear_playlists
@@ -1,12 +1,19 @@
1
1
  class Lllibrary
2
+ # Handles connecting to the database, and initializing the schema.
2
3
  class Database
3
4
  def initialize(path, adapter)
4
5
  @path, @adapter = path, adapter
6
+ connect
7
+ end
8
+
9
+ def connect
5
10
  ActiveRecord::Base.establish_connection(adapter: @adapter, database: @path)
11
+ @connected = true
6
12
  end
7
13
 
8
14
  def disconnect
9
15
  ActiveRecord::Base.remove_connection
16
+ @connected = false
10
17
  end
11
18
 
12
19
  def exists?
@@ -18,64 +25,74 @@ class Lllibrary
18
25
  end
19
26
 
20
27
  def connected?
21
- ActiveRecord::Base.connected?
28
+ @connected &&= ActiveRecord::Base.connected?
22
29
  end
23
30
 
24
31
  def generate_schema(&blk)
25
32
  ActiveRecord::Schema.define do
26
- create_table :tracks do |t|
27
- t.string :location
28
- t.timestamps
33
+ unless table_exists? :tracks
34
+ create_table :tracks do |t|
35
+ def t.default_metadata
36
+ # tag info supported by taglib
37
+ string :title
38
+ string :artist
39
+ string :album
40
+ string :genre
41
+ text :comments
42
+ integer :year
43
+ integer :track_number
29
44
 
30
- def t.default_metadata
31
- string :title
32
- string :artist
33
- string :album
34
- string :genre
35
- integer :length
36
- integer :track_number
37
- integer :year
38
- end
45
+ # audio properties supported by taglib
46
+ integer :total_time
47
+ integer :bit_rate
48
+ integer :sample_rate
49
+ integer :channels
50
+ end
39
51
 
40
- def t.itunes_metadata
41
- integer :original_id
42
- string :title
43
- string :artist
44
- string :composer
45
- string :album
46
- string :album_artist
47
- string :genre
48
- integer :total_time
49
- integer :disc_number
50
- integer :disc_count
51
- integer :track_number
52
- integer :track_count
53
- integer :year
54
- datetime :date_modified, null: false
55
- datetime :date_added, null: false
56
- integer :bit_rate
57
- integer :sample_rate
58
- text :comments
59
- integer :play_count, null: false, default: 0
60
- datetime :play_date
61
- integer :skip_count, null: false, default: 0
62
- datetime :skip_date
63
- integer :rating, null: false, default: 0
64
- end
52
+ def t.itunes_metadata
53
+ integer :original_id
54
+ string :title
55
+ string :artist
56
+ string :composer
57
+ string :album
58
+ string :album_artist
59
+ string :genre
60
+ integer :total_time
61
+ integer :disc_number
62
+ integer :disc_count
63
+ integer :track_number
64
+ integer :track_count
65
+ integer :year
66
+ datetime :date_modified, null: false
67
+ datetime :date_added, null: false
68
+ integer :bit_rate
69
+ integer :sample_rate
70
+ text :comments
71
+ integer :play_count, null: false, default: 0
72
+ datetime :play_date
73
+ integer :skip_count, null: false, default: 0
74
+ datetime :skip_date
75
+ integer :rating, null: false, default: 0
76
+ end
65
77
 
66
- blk.call(t)
78
+ blk.call(t)
79
+ end
67
80
  end
68
81
 
69
- create_table :playlists do |t|
70
- t.string :name
71
- t.datetime :created_at
72
- t.datetime :updated_at
82
+ unless table_exists? :playlists
83
+ create_table :playlists do |t|
84
+ t.string :name
85
+ t.datetime :created_at
86
+ t.datetime :updated_at
87
+ end
73
88
  end
74
89
 
75
- create_table :playlist_items do |t|
76
- t.references :playlist, null: false
77
- t.references :track, null: false
78
- t.integer :position
90
+ unless table_exists? :playlist_items
91
+ create_table :playlist_items do |t|
92
+ t.references :playlist, null: false
93
+ t.references :track, null: false
94
+ t.integer :position
95
+ end
79
96
  end
80
97
  end
81
98
  end
@@ -1,9 +1,14 @@
1
1
  class Lllibrary
2
+ # Contains all the selectors used in Lllibrary's DSL. See Lllibrary#select for
3
+ # how to access the DSL.
2
4
  class DSL
3
5
  def initialize(library)
4
6
  @library = library
5
7
  end
6
8
 
9
+ # Turns all the columns on the tracks table into methods that I call selectors.
10
+ # Based on the column's type in the database, this either delegates to
11
+ # DSL#numeric_selector or DSL#string_selector.
7
12
  def method_missing(field, *args)
8
13
  if Lllibrary::Track.column_names.include? field.to_s
9
14
  type = Lllibrary::Track.columns_hash[field.to_s].type
@@ -30,15 +35,21 @@ class Lllibrary
30
35
  []
31
36
  end
32
37
 
33
- # Makes a playlist out of tracks that match the string query on the given
38
+ # Returns an Array of tracks that match the string query on the given
34
39
  # column. It's SQL underneath, so you can use % and _ as wildcards in the
35
40
  # query. By default, % wildcards are inserted on the left and right of your
36
41
  # query. Use the :match option to change this:
37
42
  #
38
- # :match => :middle "%query%" (default)
39
- # :match => :left "query%"
40
- # :match => :right "%query"
41
- # :match => :exact "query"
43
+ # :match => :middle "%query%" (default)
44
+ # :match => :left "query%"
45
+ # :match => :right "%query"
46
+ # :match => :exact "query"
47
+ #
48
+ # Here are some examples:
49
+ #
50
+ # genre(:electronic, :edm) # matches "Electronic", "Electronica", "EDM", etc.
51
+ # genre(:tmbg, match: :exact) # only matches "TMBG" (but case-insensitively)
52
+ # composer(:rachmanino, match: :left) # matches "Rachmaninov", "Rachmaninoff", but not "Sergei Rachmaninov"
42
53
  #
43
54
  def string_selector(column, *queries)
44
55
  options = queries.last.is_a?(Hash) ? queries.pop : {}
@@ -57,23 +68,33 @@ class Lllibrary
57
68
  end.flatten
58
69
  end
59
70
 
60
- # Makes a playlist out of tracks that satisfy certain conditions on the given
71
+ # Returns an Array of tracks that satisfy certain conditions on the given
61
72
  # numeric column. You can pass an exact value to check for equality, a range,
62
- # or a hash that specifies greater-than and less-than options like this:
63
- #
64
- # numeric_selector :year, greater_than: 2000
73
+ # or a hash that specifies greater-than and less-than operators.
65
74
  #
66
75
  # The possible operators, with their shortcuts, are:
67
76
  #
68
- # :greater_than / :gt
69
- # :less_than / :lt
70
- # :greater_than_or_equal / :gte
71
- # :less_than_or_equal / :lte
72
- # :not_equal / :ne
77
+ # :greater_than / :gt
78
+ # :less_than / :lt
79
+ # :greater_than_or_equal / :gte
80
+ # :less_than_or_equal / :lte
81
+ # :not_equal / :ne
73
82
  #
74
83
  # Note: You can only use one of these operators at a time. If you want a
75
84
  # range, use a Range.
76
85
  #
86
+ # Time strings like "1:23" can be given, and will be converted to a Range of
87
+ # milliseconds. For example, "1:00" will be treated like 60000..60999.
88
+ # Obviously, any field this is used on should be storing time in milliseconds.
89
+ #
90
+ # Here's some examples:
91
+ #
92
+ # year(2010..2012) # equivalent to year(2010, 2011, 2012)
93
+ # year(2011) # only matches 2011
94
+ # year(gte: 2000) # matches 2000 and up
95
+ # total_time("2:30") # matches 150000..150999 milliseconds
96
+ # total_time("1:00".."2:00") # matches 60000..120999 milliseconds
97
+ #
77
98
  def numeric_selector(column, *values_or_hash)
78
99
  parse = lambda do |x|
79
100
  if x.is_a? String
@@ -86,6 +107,7 @@ class Lllibrary
86
107
  elsif x.is_a? Range
87
108
  left = x.begin.is_a?(String) ? Lllibrary.parse_time(x.begin) : x.begin
88
109
  right = x.end.is_a?(String) ? Lllibrary.parse_time(x.end) : x.end
110
+ right += 999 if right % 1000 == 0 && !x.exclude_end?
89
111
  Range.new(left, right, x.exclude_end?)
90
112
  else
91
113
  x
@@ -127,10 +149,15 @@ class Lllibrary
127
149
  raise ArgumentError, "'#{operator}' isn't a valid operator"
128
150
  end
129
151
  else
130
- @library.tracks.where(column => parse.(values_or_hash)).all
152
+ values_or_hash.map do |value|
153
+ @library.tracks.where(column => parse.(value)).all
154
+ end.flatten
131
155
  end
132
156
  end
133
157
 
158
+ # Returns an Array of all tracks that are in a Playlist whose name matches
159
+ # one of the strings given to this selector. It's kind of ugly, I will
160
+ # probably be changing this.
134
161
  def playlist(*names)
135
162
  names.map do |name|
136
163
  playlists = @library.playlists.arel_table
@@ -1,8 +1,12 @@
1
1
  class Lllibrary
2
+ # A Playlist is a list of Tracks in a particular order and with possible
3
+ # repeats of Tracks. A Playlist has a name, and that's about it.
2
4
  class Playlist < ActiveRecord::Base
3
5
  has_many :playlist_items, order: "playlist_items.position ASC", dependent: :destroy
4
6
  has_many :tracks, through: :playlist_items, order: "playlist_items.position ASC"
5
7
 
8
+ # Adds the given Track or Array of Tracks to the end of the Playlist. If
9
+ # an index is given, the track(s) are inserted at that position.
6
10
  def add(track_or_tracks, at = nil)
7
11
  base_position = nil
8
12
  if at
@@ -24,35 +28,57 @@ class Lllibrary
24
28
  reload
25
29
  end
26
30
 
31
+ # Removes the Track or Array of Tracks from the Playlist.
27
32
  def remove(track_or_tracks)
28
33
  playlist_items.where(track_id: track_or_tracks).destroy_all
29
34
  reload
30
35
  end
31
36
 
37
+ # Removes the Track at the given index. If a number is given in the
38
+ # second argument, removes that number of tracks starting from index.
39
+ def remove_at(index, n = 1)
40
+ playlist_items.offset(index).limit(n).all.each(&:destroy)
41
+ reload
42
+ end
43
+
44
+ # Returns true if the playlist is empty.
32
45
  def empty?
33
46
  playlist_items.empty?
34
47
  end
35
48
 
49
+ # Gets the number of items in the playlist.
36
50
  def length
37
51
  playlist_items.count
38
52
  end
39
53
 
54
+ # Calculates the total length of the playlist by summing the tracks'
55
+ # total_time column by default, which stores milliseconds by default.
56
+ # You can provide a different column using the :field option, like so:
57
+ #
58
+ # playlist.total_length(field: :length)
59
+ #
60
+ # If the given field stores time in milliseconds, this method returns
61
+ # milliseconds. If it stores time in seconds, this returns seconds.
62
+ # And so on.
40
63
  def total_length(options = {})
41
- tracks.sum(options[:field] || :length)
64
+ tracks.sum(options[:field] || :total_time)
42
65
  end
43
66
 
67
+ # Sorts the playlist using the given block, then saves. See Array#sort.
44
68
  def sort(&blk)
45
69
  sorted_tracks = tracks.sort(&blk)
46
70
  clear
47
71
  add(sorted_tracks)
48
72
  end
49
73
 
74
+ # Shuffles the playlist and saves.
50
75
  def shuffle
51
76
  shuffled_tracks = tracks.shuffle
52
77
  clear
53
78
  add(shuffled_tracks)
54
79
  end
55
80
 
81
+ # Clears the playlist and saves.
56
82
  def clear
57
83
  playlist_items.destroy_all
58
84
  reload
@@ -1,4 +1,6 @@
1
1
  class Lllibrary
2
+ # Joins the Playlist and Track tables. Contains a position column to keep
3
+ # track of the order, as well as created_at and updated_at columns.
2
4
  class PlaylistItem < ActiveRecord::Base
3
5
  belongs_to :playlist
4
6
  belongs_to :track
@@ -1,8 +1,17 @@
1
1
  class Lllibrary
2
+ # A Track represents an audio file. At minimum, it contains a location field
3
+ # which stores the path to the audio file it represents.
2
4
  class Track < ActiveRecord::Base
3
5
  has_many :playlist_items, dependent: :destroy
4
6
  has_many :playlists, through: :playlist_items
5
7
 
6
8
  validates :location, presence: true, uniqueness: true
9
+
10
+ # By default, Tracks are sorted by their file path.
11
+ #
12
+ # TODO: have a way of specifying sorting by parsing a Symbol like :artist_asc_album_asc_track_number_asc
13
+ def <=>(other)
14
+ location <=> other.location
15
+ end
7
16
  end
8
17
  end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "lllibrary"
3
- s.version = "0.0.1"
4
- s.date = "2012-12-17"
3
+ s.version = "0.0.2"
4
+ s.date = "2012-02-02"
5
5
  s.summary = "A library for managing and querying a music library."
6
6
  s.description = "lllibrary is a Ruby library for managing and querying a music library."
7
7
  s.author = "Jeremy Ruten"
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
13
13
  s.files = ["Gemfile", "Gemfile.lock", "LICENSE", "lllibrary.gemspec", "README.md"]
14
14
  s.files += Dir["lib/**/*.rb"]
15
15
 
16
- %w(bundler plist sqlite3 activerecord activesupport).each do |gem_name|
16
+ %w(bundler plist sqlite3 activerecord activesupport taglib-ruby).each do |gem_name|
17
17
  s.add_runtime_dependency gem_name
18
18
  end
19
19
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lllibrary
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-17 00:00:00.000000000 Z
12
+ date: 2012-02-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -91,6 +91,22 @@ dependencies:
91
91
  - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: taglib-ruby
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
94
110
  - !ruby/object:Gem::Dependency
95
111
  name: rake
96
112
  requirement: !ruby/object:Gem::Requirement