lllibrary 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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