listlace 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- listlace (0.0.6)
4
+ listlace (0.0.7)
5
5
  activerecord
6
6
  activesupport
7
7
  open4
@@ -123,14 +123,14 @@ module Listlace
123
123
  end
124
124
  when :playing
125
125
  if player.started?
126
- name = player.current_track.name
126
+ title = player.current_track.title
127
127
  artist = player.current_track.artist
128
- time = player.formatted_current_time
129
- total_time = player.current_track.formatted_total_time
128
+ time = Listlace.format_time(player.current_time, include_milliseconds: false)
129
+ total_time = Listlace.format_time(player.total_time, include_milliseconds: false)
130
130
  paused = player.paused? ? "|| " : ""
131
131
  speed = player.speed
132
132
  speed = speed != 1 ? "#{TIMES_SYMBOL}#{speed} " : ""
133
- puts "%s - %s (%s / %s) %s%s" % [name, artist, time, total_time, paused, speed]
133
+ puts "%s - %s (%s / %s) %s%s" % [title, artist, time, total_time, paused, speed]
134
134
  else
135
135
  puts "Stopped."
136
136
  end
@@ -41,7 +41,7 @@ module Listlace
41
41
  ActiveRecord::Schema.define do
42
42
  create_table :tracks do |t|
43
43
  t.integer :original_id
44
- t.string :name
44
+ t.string :title
45
45
  t.string :artist
46
46
  t.string :composer
47
47
  t.string :album
@@ -61,8 +61,7 @@ module Listlace
61
61
  t.integer :sample_rate
62
62
  t.text :comments
63
63
  t.integer :play_count
64
- t.integer :play_date
65
- t.datetime :play_date_utc
64
+ t.datetime :play_date
66
65
  t.integer :skip_count
67
66
  t.datetime :skip_date
68
67
  t.integer :rating
@@ -1,7 +1,7 @@
1
1
  module Listlace
2
2
  class Library
3
3
  module Selectors
4
- STRING_SELECTORS = %w(name artist composer album album_artist genre comments location)
4
+ STRING_SELECTORS = %w(title artist composer album album_artist genre comments location)
5
5
  INTEGER_SELECTORS = %w(disc_number disc_count track_number track_count year bit_rate sample_rate play_count skip_count rating)
6
6
 
7
7
  STRING_SELECTORS.each do |column|
@@ -20,10 +20,6 @@ module Listlace
20
20
  end
21
21
  end
22
22
 
23
- # rename the "name" selector to "song"
24
- alias_method :song, :name
25
- remove_method :name
26
-
27
23
  # The length selector is an integer selector for the length of a track. A
28
24
  # plain integer given to it represents the number of seconds. It can also take
29
25
  # a String in the format "1:23", to represent 83 seconds, for example. These
@@ -32,7 +28,7 @@ module Listlace
32
28
  normalize = lambda do |value|
33
29
  case value
34
30
  when String
35
- Track.parse_time(value)
31
+ Listlace.parse_time(value)
36
32
  when Integer
37
33
  value * 1000
38
34
  when Range
@@ -72,16 +68,12 @@ module Listlace
72
68
  #
73
69
  # This method shouldn't have to be used directly. Many convenient methods are
74
70
  # generated for you, one for each string field you may want to select on.
75
- # These are: artist, composer, album, album_artist, genre, comments, location.
76
- # For example:
71
+ # These are: title, artist, composer, album, album_artist, genre, comments,
72
+ # location. For example:
77
73
  #
78
74
  # artist :muse, match: :exact #=> playlist (108 tracks)
79
75
  # composer :rachmanino #=> playlist (33 tracks)
80
76
  #
81
- # To match the name of a track, use song:
82
- #
83
- # song "frontier psychiatrist" #=> playlist (1 track)
84
- #
85
77
  def string_selector(column, query, options = {})
86
78
  options[:match] ||= :middle
87
79
 
@@ -70,9 +70,11 @@ module Listlace
70
70
  # row already contains a hash of attributes almost ready to be passed to
71
71
  # ActiveRecord. We just need to modify the keys, e.g. change "Play Count"
72
72
  # to "play_count".
73
+ row["Title"] = row.delete("Name")
74
+ row["Play Date"] = row.delete("Play Date UTC")
75
+ row["Original ID"] = row.delete("Track ID")
73
76
  attributes = row.inject({}) do |acc, (key, value)|
74
77
  attribute = key.gsub(" ", "").underscore
75
- attribute = "original_id" if attribute == "track_id"
76
78
  acc[attribute] = value if whitelist.include? attribute
77
79
  acc
78
80
  end
@@ -3,30 +3,14 @@ module Listlace
3
3
  has_many :playlist_items
4
4
  has_many :playlists, through: :playlist_items
5
5
 
6
- def formatted_total_time
7
- Track.format_time(total_time)
6
+ def increment_skip_count
7
+ increment! :skip_count
8
+ update_column :skip_date, Time.now
8
9
  end
9
10
 
10
- def self.format_time(milliseconds)
11
- total_seconds = milliseconds / 1000
12
-
13
- seconds = total_seconds % 60
14
- minutes = (total_seconds / 60) % 60
15
- hours = total_seconds / 3600
16
-
17
- if hours > 0
18
- "%d:%02d:%02d" % [hours, minutes, seconds]
19
- else
20
- "%d:%02d" % [minutes, seconds]
21
- end
22
- end
23
-
24
- def self.parse_time(string)
25
- parts = string.split(":").map(&:to_i)
26
- parts = [0] + parts if parts.length == 2
27
- hours, minutes, seconds = parts
28
- seconds = hours * 3600 + minutes * 60 + seconds
29
- seconds * 1000
11
+ def increment_play_count
12
+ increment! :play_count
13
+ update_column :play_date, Time.now
30
14
  end
31
15
  end
32
16
  end
@@ -18,7 +18,6 @@ module Listlace
18
18
  def initialize
19
19
  @single_player = DEFAULT_SINGLE_PLAYER.new
20
20
  @queue = []
21
- @queue.name = :queue
22
21
  @current_track = nil
23
22
  @current_track_index = nil
24
23
  @playlist_paused = false
@@ -28,11 +27,13 @@ module Listlace
28
27
 
29
28
  def queue(playlist = nil)
30
29
  if playlist.is_a? Array
31
- if @queue.empty? && playlist.name && !playlist.name.empty?
32
- @queue = playlist.dup
30
+ playlist = playlist.dup
31
+ playlist.map! { |track| track.is_a?(String) ? SimpleTrack.new(track) : track }
32
+ playlist.select! { |track| track.respond_to? :location }
33
+ if @queue.empty?
34
+ @queue = playlist
33
35
  else
34
36
  @queue += playlist
35
- @queue.name = :queue
36
37
  end
37
38
  end
38
39
  @queue.dup
@@ -41,7 +42,6 @@ module Listlace
41
42
  def clear
42
43
  stop
43
44
  @queue.clear
44
- @queue.name = :queue
45
45
  true
46
46
  end
47
47
 
@@ -118,9 +118,8 @@ module Listlace
118
118
  end
119
119
 
120
120
  def skip(n = 1)
121
- if @current_track
122
- @current_track.increment! :skip_count
123
- @current_track.update_column :skip_date, Time.now
121
+ if @current_track.respond_to?(:increment_skip_count)
122
+ @current_track.increment_skip_count
124
123
  end
125
124
  change_track(n)
126
125
  end
@@ -138,7 +137,7 @@ module Listlace
138
137
  seconds = where.begin * 60 + where.end
139
138
  @single_player.seek(seconds * 1000, :absolute)
140
139
  when String
141
- @single_player.seek(Track.parse_time(where), :absolute)
140
+ @single_player.seek(Listlace.parse_time(where), :absolute)
142
141
  when Hash
143
142
  if where[:abs]
144
143
  if where[:abs].is_a? Integer
@@ -154,11 +153,27 @@ module Listlace
154
153
  end
155
154
 
156
155
  def speed
157
- @single_player.active? ? @single_player.speed : 1.0
156
+ @single_player.speed || 1.0
158
157
  end
159
158
 
160
159
  def speed=(new_speed)
161
- @single_player.speed(new_speed)
160
+ @single_player.speed = new_speed
161
+ end
162
+
163
+ def mute
164
+ @single_player.mute
165
+ end
166
+
167
+ def unmute
168
+ @single_player.unmute
169
+ end
170
+
171
+ def volume
172
+ @single_player.volume
173
+ end
174
+
175
+ def volume=(new_volume)
176
+ @single_player.volume = new_volume
162
177
  end
163
178
 
164
179
  def shuffle
@@ -180,20 +195,19 @@ module Listlace
180
195
  end
181
196
 
182
197
  def current_time
183
- @single_player.active? ? @single_player.current_time : 0
198
+ @single_player.current_time || 0
184
199
  end
185
200
 
186
- def formatted_current_time
187
- Track.format_time(current_time)
201
+ def total_time
202
+ @single_player.total_time
188
203
  end
189
204
 
190
205
  private
191
206
 
192
207
  def change_track(by = 1, options = {})
193
208
  if started?
194
- if options[:auto]
195
- @current_track.increment! :play_count
196
- @current_track.update_column :play_date_utc, Time.now
209
+ if options[:auto] && @current_track.respond_to?(:increment_play_count)
210
+ @current_track.increment_play_count
197
211
  end
198
212
  @current_track_index += by
199
213
  if options[:auto] && @repeat_mode
@@ -0,0 +1,13 @@
1
+ module Listlace
2
+ # The bare minimum needed to represent a track. This is used by the Player and
3
+ # SinglePlayer, in case they get passed a String containing a path to an audio
4
+ # file. That way, users don't have to worry about creating track objects, if
5
+ # they want.
6
+ class SimpleTrack
7
+ attr_accessor :location
8
+
9
+ def initialize(location = nil)
10
+ @location = location
11
+ end
12
+ end
13
+ end
@@ -20,9 +20,21 @@ module Listlace
20
20
  raise NotImplementedError
21
21
  end
22
22
 
23
- # Begin playing a track. The track should respond to #location, which is the
24
- # path where the audio file is located. The &on_end callback will be called
25
- # when the track is finished playing.
23
+ # Get the current track object which was passed to the play method.
24
+ def track
25
+ raise NotImplementedError
26
+ end
27
+
28
+ # Get the title of the current track. First tries to call the title method on
29
+ # the current track, if that doesn't work it tries to get the title from the
30
+ # metadata of the audio file, and if that doesn't work it uses the filename.
31
+ def track_title
32
+ raise NotImplementedError
33
+ end
34
+
35
+ # Begin playing a track. The track should be either a String representing a
36
+ # path to an audio file, or an object responding to #location. The &on_end
37
+ # callback will be called when the track is finished playing.
26
38
  def play(track, &on_end)
27
39
  raise NotImplementedError
28
40
  end
@@ -52,17 +64,48 @@ module Listlace
52
64
  raise NotImplementedError
53
65
  when :relative
54
66
  raise NotImplementedError
55
- when :perent
67
+ when :percent
56
68
  raise NotImplementedError
57
69
  else
58
70
  raise NotImplementedError
59
71
  end
60
72
  end
61
73
 
62
- # Gets or sets the playback speed. If a new speed is passed as an argument,
63
- # it sets it to that speed. The speed is a multiplier. For example, for
64
- # double speed you'd call speed(2) and for half-speed you'd call speed(0.5).
65
- def speed(new_speed = nil)
74
+ # Gets the current playback speed. The speed is a multiplier. For example,
75
+ # double speed is 2 and half-speed is 0.5. Normal speed is 1.
76
+ def speed
77
+ raise NotImplementedError
78
+ end
79
+
80
+ # Sets the playback speed. The speed is a multiplier. For example, for
81
+ # double speed you'd set it to 2 and for half-speed you'd set it to 0.5. And
82
+ # for normal speed: 1.
83
+ def speed=(new_speed)
84
+ raise NotImplementedError
85
+ end
86
+
87
+ # Returns true if audio is muted.
88
+ def muted?
89
+ raise NotImplementedError
90
+ end
91
+
92
+ # Mutes the audio player.
93
+ def mute
94
+ raise NotImplementedError
95
+ end
96
+
97
+ # Unmutes the audio player.
98
+ def unmute
99
+ raise NotImplementedError
100
+ end
101
+
102
+ # Get the current volume as a percentage.
103
+ def volume
104
+ raise NotImplementedError
105
+ end
106
+
107
+ # Set the volume as a percentage. The player is automatically unmuted.
108
+ def volume=(new_volume)
66
109
  raise NotImplementedError
67
110
  end
68
111
 
@@ -70,5 +113,17 @@ module Listlace
70
113
  def current_time
71
114
  raise NotImplementedError
72
115
  end
116
+
117
+ # Returns the length of the current track, in milliseconds.
118
+ def total_time
119
+ raise NotImplementedError
120
+ end
121
+
122
+ # Get metadata for the current track from the audio player. Returns a Hash
123
+ # with keys like :artist and :album. Values should be Strings, or nil if the
124
+ # value is blank.
125
+ def metadata
126
+ raise NotImplementedError
127
+ end
73
128
  end
74
129
  end
@@ -1,37 +1,89 @@
1
1
  module Listlace
2
2
  module SinglePlayers
3
+ # This is the SinglePlayer implementation for mplayer. It requires mplayer
4
+ # to be in your $PATH. It uses open4 to start up and communicate with the
5
+ # mplayer process, and mplayer's slave protocol to issue commands to mplayer.
3
6
  class MPlayer < SinglePlayer
7
+ # Create a new MPlayer. The mplayer process is only started when the #play
8
+ # method is called to start playing a song. The process quits when the
9
+ # song ends. Even though a new process is started for each song, the
10
+ # MPlayer object keeps track of the volume, speed, and mute properties and
11
+ # sets these properties when a new song is played.
4
12
  def initialize
5
- @active = false
6
13
  @paused = false
14
+ @muted = false
15
+ @volume = 50
16
+ @speed = 1.0
17
+ @track = nil
7
18
  end
8
19
 
9
20
  def active?
10
- @active
21
+ not @track.nil?
11
22
  end
12
23
 
13
24
  def paused?
14
25
  @paused
15
26
  end
16
27
 
28
+ def track
29
+ @track
30
+ end
31
+
32
+ def track_title
33
+ if active?
34
+ if @track.respond_to? :title
35
+ @track.title
36
+ elsif title = metadata[:title]
37
+ title
38
+ else
39
+ _command "get_file_name", expect_answer: /^ANS_FILENAME='(.+)'$/
40
+ end
41
+ else
42
+ false
43
+ end
44
+ end
45
+
17
46
  def play(track, &on_end)
47
+ # Make sure we're only playing one song at any one time.
18
48
  _quit
19
49
 
50
+ # If a path to an audio file passed as the track, wrap it in a SimpleTrack.
51
+ track = SimpleTrack.new(track) if track.is_a? String
52
+
53
+ # The track object must respond to #location to be a track.
54
+ if not track.respond_to? :location
55
+ raise ArgumentError, "got a #{track.class} instead of a track"
56
+ end
57
+
20
58
  if File.exists? track.location
59
+ # Run the mplayer process in slave mode, passing it the location of
60
+ # the track's audio file.
21
61
  cmd = ["mplayer", "-slave", "-quiet", track.location]
22
62
  @pid, @stdin, @stdout, @stderr = Open4.popen4(*cmd)
23
63
 
64
+ # This should skip past mplayer's initial lines of output so we can
65
+ # start reading its replies to our commands.
24
66
  until @stdout.gets["playback"]
25
67
  end
26
68
 
27
- @active = true
28
69
  @paused = false
70
+ @track = track
71
+
72
+ # Persist the previous speed, volume, and mute properties into this
73
+ # process.
74
+ self.speed = @speed
75
+ self.volume = @volume
76
+ mute if @muted
29
77
 
78
+ # Start a thread that waits for the mplayer process to end, then calls
79
+ # the end of song callback. If the #quit method is called, this thread
80
+ # will be killed if it's still waiting for the process to end.
30
81
  @quit_hook_active = false
31
82
  @quit_hook = Thread.new do
32
83
  Process.wait(@pid)
33
84
  @quit_hook_active = true
34
- @active = false
85
+ @paused = false
86
+ @track = nil
35
87
  on_end.call
36
88
  end
37
89
 
@@ -47,6 +99,7 @@ module Listlace
47
99
 
48
100
  def pause
49
101
  if not @paused
102
+ @paused = true
50
103
  _command "pause"
51
104
  else
52
105
  false
@@ -55,6 +108,7 @@ module Listlace
55
108
 
56
109
  def resume
57
110
  if @paused
111
+ @paused = false
58
112
  _command "pause"
59
113
  else
60
114
  false
@@ -62,7 +116,8 @@ module Listlace
62
116
  end
63
117
 
64
118
  def seek(where, type = :absolute)
65
- seconds = where.to_f / 1000
119
+ # mplayer talks seconds, not milliseconds.
120
+ seconds = where.to_f / 1_000
66
121
  case type
67
122
  when :absolute
68
123
  _command "seek #{seconds} 2", expect_answer: true
@@ -75,44 +130,97 @@ module Listlace
75
130
  end
76
131
  end
77
132
 
78
- def speed(new_speed = nil)
79
- if new_speed
80
- answer = _command "speed_set #{new_speed.to_f}", expect_answer: true
81
- !!answer
82
- else
83
- answer = _command "get_property speed", expect_answer: true
84
- if answer && answer =~ /^ANS_speed=([0-9.]+)$/
85
- $1.to_f
86
- else
87
- false
88
- end
89
- end
133
+ def speed
134
+ @speed
135
+ end
136
+
137
+ def speed=(new_speed)
138
+ @speed = new_speed.to_f
139
+ answer = _command "speed_set #{@speed}", expect_answer: true
140
+ !!answer
141
+ end
142
+
143
+ def muted?
144
+ @muted
145
+ end
146
+
147
+ def mute
148
+ @muted = true
149
+ answer = _command "mute 1", expect_answer: true
150
+ !!answer
151
+ end
152
+
153
+ def unmute
154
+ @muted = false
155
+ answer = _command "mute 0", expect_answer: true
156
+ !!answer
157
+ end
158
+
159
+ def volume
160
+ @volume
161
+ end
162
+
163
+ def volume=(new_volume)
164
+ @muted = false
165
+ @volume = new_volume.to_f
166
+ answer = _command "volume #{@volume} 1", expect_answer: true
167
+ !!answer
90
168
  end
91
169
 
92
170
  def current_time
93
- answer = _command "get_time_pos", expect_answer: true
94
- if answer && answer =~ /^ANS_TIME_POSITION=([0-9.]+)$/
95
- ($1.to_f * 1000).to_i
96
- else
97
- false
171
+ answer = _command "get_time_pos", expect_answer: /^ANS_TIME_POSITION=([0-9.]+)$/
172
+ answer ? (answer.to_f * 1000).to_i : false
173
+ end
174
+
175
+ def total_time
176
+ answer = _command "get_time_length", expect_answer: /^ANS_LENGTH=([0-9.]+)$/
177
+ answer ? (answer.to_f * 1000).to_i : false
178
+ end
179
+
180
+ def metadata
181
+ properties = %w(album artist comment genre title track year)
182
+ properties.inject({}) do |hash, property|
183
+ answer = _command "get_meta_#{property}", expect_answer: /^ANS_META_#{property.upcase}='(.+)'$/
184
+ hash[property.to_sym] = answer || nil
185
+ hash
98
186
  end
99
187
  end
100
188
 
101
189
  private
102
190
 
191
+ # Issue a command to mplayer through the slave protocol. False is returned
192
+ # if the process is dead (not playing anything).
193
+ #
194
+ # If :expect_answer option is set to true, this will wait for a legible
195
+ # answer back from mplayer, and send it as a return value. If :expect_answer
196
+ # is set to a Regexp, the answer mplayer gives back will be matched to that
197
+ # Regexp and the first match will be returned. If there are no matches, nil
198
+ # will be returned.
103
199
  def _command(cmd, options = {})
104
200
  if _alive? and active?
105
- if cmd == "pause"
106
- @paused = !@paused
107
- elsif @paused
201
+ # If the player is paused, prefix the command with "pausing ".
202
+ # Otherwise it unpauses when it runs a command. The only exception to
203
+ # this is when the "pause" command itself is issued.
204
+ if paused? and cmd != "pause"
108
205
  cmd = "pausing #{cmd}"
109
206
  end
110
207
 
208
+ # Send the command to mplayer.
111
209
  @stdin.puts cmd
112
210
 
113
211
  if options[:expect_answer]
212
+ # Read lines of output from mplayer until we get an actual message.
114
213
  answer = "\n"
115
- answer = @stdout.gets.sub("\e[A\r\e[K", "") while answer == "\n"
214
+ while answer == "\n"
215
+ answer = @stdout.gets.sub("\e[A\r\e[K", "")
216
+ answer = "\n" if options[:expect_answer].is_a?(Regexp) && answer !~ options[:expect_answer]
217
+ end
218
+
219
+ if options[:expect_answer].is_a? Regexp
220
+ matches = answer.match(options[:expect_answer])
221
+ answer = matches && matches[1]
222
+ end
223
+
116
224
  answer
117
225
  else
118
226
  true
@@ -122,16 +230,19 @@ module Listlace
122
230
  end
123
231
  end
124
232
 
233
+ # Quit the mplayer process, stopping playback. The end of song callback
234
+ # will not be called if this method is called.
125
235
  def _quit
126
236
  if _alive?
127
237
  @quit_hook.kill unless @quit_hook_active
128
238
  _command "quit"
129
- @active = false
130
239
  @paused = false
240
+ @track = nil
131
241
  end
132
242
  true
133
243
  end
134
244
 
245
+ # Check if the mplayer process is still around.
135
246
  def _alive?
136
247
  return false if @pid.nil?
137
248
  Process.getpgid(@pid)
@@ -0,0 +1,34 @@
1
+ module Listlace
2
+ # Helper method to format a number of milliseconds as a string like
3
+ # "1:03:56.555". The only option is :include_milliseconds, true by default. If
4
+ # false, milliseconds won't be included in the formatted string.
5
+ def self.format_time(milliseconds, options = {})
6
+ ms = milliseconds % 1000
7
+ seconds = (milliseconds / 1000) % 60
8
+ minutes = (milliseconds / 60000) % 60
9
+ hours = milliseconds / 3600000
10
+
11
+ if ms.zero? || options[:include_milliseconds] == false
12
+ ms_string = ""
13
+ else
14
+ ms_string = ".%03d" % [ms]
15
+ end
16
+
17
+ if hours > 0
18
+ "%d:%02d:%02d%s" % [hours, minutes, seconds, ms_string]
19
+ else
20
+ "%d:%02d%s" % [minutes, seconds, ms_string]
21
+ end
22
+ end
23
+
24
+ # Helper method to parse a string like "1:03:56.555" and return the number of
25
+ # milliseconds that time length represents.
26
+ def self.parse_time(string)
27
+ parts = string.split(":").map(&:to_f)
28
+ parts = [0] + parts if parts.length == 2
29
+ hours, minutes, seconds = parts
30
+ seconds = hours * 3600 + minutes * 60 + seconds
31
+ milliseconds = seconds * 1000
32
+ milliseconds.to_i
33
+ end
34
+ end
data/lib/listlace.rb CHANGED
@@ -5,6 +5,7 @@ require "plist"
5
5
  require "active_support/core_ext/string"
6
6
 
7
7
  require "listlace/core_ext/array"
8
+ require "listlace/time_helper"
8
9
 
9
10
  require "listlace/models/track"
10
11
  require "listlace/models/playlist"
@@ -14,6 +15,7 @@ require "listlace/library"
14
15
  require "listlace/library/database"
15
16
  require "listlace/library/selectors"
16
17
 
18
+ require "listlace/simple_track"
17
19
  require "listlace/single_player"
18
20
  require "listlace/single_players/mplayer"
19
21
  require "listlace/player"
data/listlace.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "listlace"
3
- s.version = "0.0.7"
4
- s.date = "2012-09-03"
3
+ s.version = "0.0.8"
4
+ s.date = "2012-09-07"
5
5
  s.summary = "A music player in a REPL."
6
6
  s.description = "Listlace is a music player which is interacted with through a Ruby REPL."
7
7
  s.author = "Jeremy Ruten"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: listlace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
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-09-03 00:00:00.000000000 Z
12
+ date: 2012-09-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pry
@@ -147,8 +147,10 @@ files:
147
147
  - lib/listlace/models/playlist_item.rb
148
148
  - lib/listlace/models/track.rb
149
149
  - lib/listlace/player.rb
150
+ - lib/listlace/simple_track.rb
150
151
  - lib/listlace/single_player.rb
151
152
  - lib/listlace/single_players/mplayer.rb
153
+ - lib/listlace/time_helper.rb
152
154
  - lib/listlace.rb
153
155
  homepage: http://github.com/yjerem/listlace
154
156
  licenses: