listlace 0.0.7 → 0.0.8

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/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: