ipod_db 0.2.8 → 0.2.9

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/Guardfile CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  guard 'bundler' do
5
5
  watch('Gemfile')
6
+ watch('ipod_db.gemspec')
6
7
  end
7
8
 
8
9
  guard 'minitest' do
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  NAME
5
5
  ====
6
6
 
7
- ipod_db v0.2.8
7
+ ipod_db v0.2.9
8
8
 
9
9
  SYNOPSIS
10
10
  ========
data/bin/ipod CHANGED
@@ -2,38 +2,29 @@
2
2
 
3
3
  Main {
4
4
  version IpodDB::VERSION
5
+ author 'artm <femistofel@gmail.com>'
6
+ name 'ipod_db'
5
7
 
6
8
  description <<-__
7
9
  A couple of tools for working with iPod Shuffle (2nd gen) database from
8
10
  command line. Each subcommand understands -h/--help flag.
9
11
  __
10
12
 
11
- author 'artm <femistofel@gmail.com>'
12
-
13
- name 'ipod_db'
14
- config(
15
- ipod_root: "/media/#{ENV['USER']}/IPOD",
16
- group_together: [
17
- # Just an example
18
- [ "60-Second", "Science Talk", "This Week in Science" ],
19
- [ "StarShipSofa", "Tales To Terrify" ],
20
- ])
21
13
  option('books','b') {
22
14
  argument_required
23
15
  default 'books'
24
16
  description 'subdirectory of ipod with bookmarkable media'
25
17
  }
18
+
26
19
  option('songs','s') {
27
20
  argument_required
28
21
  default 'songs'
29
22
  description 'subdirectory of ipod with non-bookmarkable media'
30
23
  }
24
+
31
25
  option('version','v') {
32
26
  description 'show package version and exit'
33
27
  }
34
- IgnorePlaybackPosUnder = 10
35
- IgnoreProgressUnder = 0.01
36
- FinishedProgress = 0.97
37
28
 
38
29
  def run
39
30
  if param['version'].given?
@@ -96,7 +87,7 @@ Main {
96
87
  track numbers to delete from device (ranges like
97
88
  2-5 are accepted too). As a special case, the word
98
89
  'done' means all finished tracks, i.e. with play count
99
- above zero or progress above #{ (100*FinishedProgress).floor }%
90
+ above zero or progress above #{ (100*Ipod::Track::FinishedProgress).floor }%
100
91
  __
101
92
  }
102
93
  option('pretend','n') {
@@ -123,7 +114,9 @@ Main {
123
114
  end
124
115
 
125
116
  def ls
126
- @ipod_db.each_track_with_index {|track,i| list_track i, track}
117
+ @ipod_db.each_track_with_index do |track,i|
118
+ list_track i, Ipod::Track.new(track.merge(ipod_root: @ipod_root))
119
+ end
127
120
  end
128
121
 
129
122
  def rm
@@ -133,8 +126,8 @@ Main {
133
126
  unless @pretend
134
127
  if agree "Are you sure you want them gone (Y/n)?", true
135
128
  FileUtils.rm tracks.map{|i,path|resolve_ipod_path path}
129
+ sync
136
130
  end
137
- sync
138
131
  end
139
132
  end
140
133
 
@@ -146,10 +139,6 @@ Main {
146
139
  File.join @ipod_root, ipath
147
140
  end
148
141
 
149
- def track_length( path )
150
- TagLib::FileRef.open(path){|file| file.audio_properties.length}
151
- end
152
-
153
142
  def load_ipod_db
154
143
  @ipod_root = config['ipod_root']
155
144
  unless IpodDB.looks_like_ipod? @ipod_root
@@ -189,48 +178,35 @@ Main {
189
178
  IpodDB::ExtToFileType.include? File.extname(path)
190
179
  end
191
180
 
192
- def track_info track
193
- info = Map.new
194
- info['playcount'] = track.playcount if track.fetch('playcount',0) > 0
195
- info['skipcount'] = track.skippedcount if track.fetch('skippedcount',0) > 0
196
- if track.fetch('bookmarktime',0) > 0
197
- abs_path = resolve_ipod_path track.filename
198
- total_time = track_length(abs_path)
199
- # ipod keeps time in 256 ms increments
200
- pos = [total_time, track.bookmarktime * 0.256].min
201
- progress = pos / total_time
202
- if pos > IgnorePlaybackPosUnder && progress >= IgnoreProgressUnder
203
- info['pos'] = Pretty.seconds pos
204
- info['total'] = Pretty.seconds total_time
205
- info['progress'] = progress
206
- end
207
- end
208
- info
209
- end
210
-
211
181
  def list_track i, track
212
- abs_path = resolve_ipod_path track.filename
213
- exists = File.exists?(abs_path)
214
- track_color = exists ? :green : :red
215
-
216
- listing_entry = "%2d: %s" % [i,track.filename.apply_format( color: track_color )]
182
+ listing_entry = "%2d: %s" % [i,track.filename.apply_format( color: track_color(track))]
217
183
  listing_entry = listing_entry.bold if @ipod_db.playback_state.trackno == i
218
184
  puts listing_entry
185
+ display_progress track
186
+ end
187
+
188
+ def track_color track
189
+ if track.exists? then
190
+ if track.finished?
191
+ :cyan
192
+ else
193
+ :green
194
+ end
195
+ else
196
+ :red
197
+ end
198
+ end
219
199
 
220
- return unless exists
200
+ def display_progress track
201
+ if track.exists? and track.pos >= IgnoreProgressUnder and
202
+ track.progress.between?(IgnoreProgressUnder,Ipod::Track::FinishedProgress)
221
203
 
222
- info = track_info(track)
223
- if info.include? 'progress'
224
204
  progress = ProgressBar.create(
225
- format: " [%b #{info.pos.yellow} %P%%%i] #{info.total.yellow}",
226
- starting_at: (100*info.progress),
205
+ format: " [%b #{Pretty.seconds(track.pos).yellow} %P%%%i] #{Pretty.seconds(track.length).yellow}",
206
+ starting_at: (100*track.progress),
227
207
  )
228
208
  puts
229
209
  end
230
- ['pos','total','progress'].each{|f| info.delete(f)}
231
- if info.count > 0
232
- puts " " + info.map{|label,value| "#{label}: #{value.to_s.yellow}"}.join(" ")
233
- end
234
210
  end
235
211
 
236
212
  def parse_track_list args
@@ -254,10 +230,6 @@ Main {
254
230
  tracks
255
231
  end
256
232
 
257
- def finished? track
258
- track.fetch('playcount',0) > 0 || track_info(track).fetch('progress',0) >= FinishedProgress
259
- end
260
-
261
233
  def spread paths, group_lambda = lambda {|path| track_group(path)}
262
234
  bins = paths.group_by &group_lambda
263
235
  bins.each do |key, bin|
@@ -274,6 +246,17 @@ Main {
274
246
  end
275
247
  File.dirname(track)
276
248
  end
249
+
250
+ IgnorePlaybackPosUnder = 10
251
+ IgnoreProgressUnder = 0.01
252
+
253
+ config({
254
+ ipod_root: "/media/#{ENV['USER']}/IPOD",
255
+ group_together: [
256
+ # Just an example
257
+ [ "60-Second", "Science Talk", "This Week in Science" ],
258
+ [ "StarShipSofa", "Tales To Terrify" ],
259
+ ]})
277
260
  }
278
261
 
279
262
  BEGIN {
@@ -282,7 +265,6 @@ BEGIN {
282
265
  require 'pathname'
283
266
  require 'smart_colored/extend'
284
267
  require 'map'
285
- require 'taglib'
286
268
  require 'ruby-progressbar'
287
269
  require 'highline/import'
288
270
  require 'fileutils'
@@ -290,6 +272,7 @@ BEGIN {
290
272
 
291
273
  # our own
292
274
  require 'ipod_db'
275
+ require 'ipod/track'
293
276
  require 'pretty'
294
277
  require 'spread'
295
278
  }
data/ipod_db.gemspec CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |s|
29
29
  s.add_development_dependency 'rake'
30
30
  s.add_development_dependency 'bundler', '~> 1.3'
31
31
  # not sure where this belongs, they are part of my development process
32
- s.add_development_dependency 'purdytest'
32
+ s.add_development_dependency 'minitest-reporters'
33
33
  s.add_development_dependency 'guard'
34
34
  s.add_development_dependency 'guard-minitest'
35
35
  s.add_development_dependency 'guard-bundler'
data/lib/ipod/track.rb ADDED
@@ -0,0 +1,54 @@
1
+ require 'ostruct'
2
+ require 'taglib'
3
+ require 'fileutils'
4
+
5
+ module Ipod
6
+ class Track < OpenStruct
7
+ FinishedProgress = 0.97
8
+ SecondsPerTick = 0.256
9
+
10
+ def absolute_path
11
+ File.join ipod_root, filename
12
+ end
13
+
14
+ def exists?
15
+ @exists ||= File.exists? absolute_path
16
+ end
17
+
18
+ def finished?
19
+ playcount > 0 or progress > FinishedProgress
20
+ end
21
+
22
+ def progress
23
+ pos / length
24
+ end
25
+
26
+ def pos
27
+ Track.ticks_to_sec(bookmarktime)
28
+ end
29
+
30
+ def length
31
+ #
32
+ # if file is writable TagLib opens it read-write and appears to write something when the file
33
+ # gets closed, or in any case closing a file open read-write is slow on slow media.
34
+ #
35
+ # if file is readonly TagLib still opens it but closing is much faster.
36
+ #
37
+ old_stat = File::Stat.new(absolute_path)
38
+ begin
39
+ FileUtils.chmod('a-w', absolute_path)
40
+ TagLib::FileRef.open(absolute_path){|file| file.audio_properties.length}
41
+ ensure
42
+ FileUtils.chmod(old_stat.mode, absolute_path)
43
+ end
44
+ end
45
+
46
+ def self.sec_to_ticks(sec)
47
+ sec / SecondsPerTick
48
+ end
49
+
50
+ def self.ticks_to_sec(ticks)
51
+ ticks * SecondsPerTick
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,3 @@
1
1
  class IpodDB
2
- VERSION = '0.2.8'
2
+ VERSION = '0.2.9'
3
3
  end
@@ -0,0 +1,68 @@
1
+ require "spec_helper"
2
+ require "ipod/track"
3
+
4
+ describe Ipod::Track do
5
+
6
+ describe "#exists?" do
7
+
8
+ it "knows if file exists" do
9
+ track = Ipod::Track.new filename: "/books/chapter1.mp3", ipod_root: "/mount/ipod"
10
+
11
+ File.mock_stub :exists?, true, ["/mount/ipod/books/chapter1.mp3"] do
12
+ assert track.exists?
13
+ end
14
+ end
15
+
16
+ it "knows if file doesn't exist" do
17
+ track = Ipod::Track.new filename: "/books/chapter1.mp3", ipod_root: "/mount/ipod"
18
+
19
+ File.mock_stub :exists?, false, ["/mount/ipod/books/chapter1.mp3"] do
20
+ refute track.exists?
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ describe '#finished?' do
27
+
28
+ it 'is true if playcount > 0' do
29
+ track = Ipod::Track.new playcount: 1
30
+ assert track.finished?
31
+ end
32
+
33
+ it 'is true if progress above threshold' do
34
+ track = Ipod::Track.new playcount: 0, bookmarktime: Ipod::Track.sec_to_ticks(99)
35
+ track.stub :length, 100 do
36
+ assert track.finished?
37
+ end
38
+ end
39
+
40
+ it 'is false if progress below threshold' do
41
+ track = Ipod::Track.new playcount: 0, bookmarktime: Ipod::Track.sec_to_ticks(5)
42
+ track.stub :length, 100 do
43
+ refute track.finished?
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ describe '#pos' do
50
+
51
+ it 'represents bookmarktime in seconds' do
52
+ track = Ipod::Track.new bookmarktime: 1
53
+ assert_in_delta track.pos, Ipod::Track::SecondsPerTick
54
+ end
55
+
56
+ end
57
+
58
+ describe '#progress' do
59
+
60
+ it 'represents progress in track' do
61
+ track = Ipod::Track.new bookmarktime: Ipod::Track.sec_to_ticks(25)
62
+ track.stub :length, 100 do
63
+ assert_in_delta track.progress, 0.25
64
+ end
65
+ end
66
+
67
+ end
68
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  require 'minitest/autorun'
2
- require 'purdytest'
2
+ require "minitest/reporters"
3
+ MiniTest::Reporters.use! [
4
+ MiniTest::Reporters::DefaultReporter.new,
5
+ MiniTest::Reporters::GuardReporter.new,
6
+ ]
3
7
  $LOAD_PATH.push "File.dirname(__FILE__)/../lib"
4
8
  require 'bindata'
5
9
 
@@ -51,3 +55,14 @@ module Enumerable
51
55
  ds
52
56
  end
53
57
  end
58
+
59
+ class Object
60
+ def mock_stub name, retval, args=[], &block
61
+ mock = MiniTest::Mock.new
62
+ mock.expect name, retval, args
63
+ stub name, proc{|*args| mock.method_missing(name,*args)} do
64
+ yield
65
+ end
66
+ mock.verify
67
+ end
68
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ipod_db
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.2.9
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: 2013-04-03 00:00:00.000000000 Z
12
+ date: 2013-04-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bindata
@@ -172,7 +172,7 @@ dependencies:
172
172
  - !ruby/object:Gem::Version
173
173
  version: '1.3'
174
174
  - !ruby/object:Gem::Dependency
175
- name: purdytest
175
+ name: minitest-reporters
176
176
  requirement: !ruby/object:Gem::Requirement
177
177
  none: false
178
178
  requirements:
@@ -303,10 +303,12 @@ files:
303
303
  - doc/ITunesDB - wikiPodLinux.html
304
304
  - ipod_db.gemspec
305
305
  - lib/bindata/itypes.rb
306
+ - lib/ipod/track.rb
306
307
  - lib/ipod_db.rb
307
308
  - lib/ipod_db/version.rb
308
309
  - lib/pretty.rb
309
310
  - lib/spread.rb
311
+ - spec/ipod/track_spec.rb
310
312
  - spec/ipod_db_spec.rb
311
313
  - spec/spec_helper.rb
312
314
  - spec/spread_spec.rb
@@ -332,7 +334,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
332
334
  version: '0'
333
335
  segments:
334
336
  - 0
335
- hash: 1960172244731333251
337
+ hash: 4257127508463692162
336
338
  required_rubygems_version: !ruby/object:Gem::Requirement
337
339
  none: false
338
340
  requirements:
@@ -341,7 +343,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
341
343
  version: '0'
342
344
  segments:
343
345
  - 0
344
- hash: 1960172244731333251
346
+ hash: 4257127508463692162
345
347
  requirements: []
346
348
  rubyforge_project:
347
349
  rubygems_version: 1.8.23
@@ -349,6 +351,7 @@ signing_key:
349
351
  specification_version: 3
350
352
  summary: ipod database access
351
353
  test_files:
354
+ - spec/ipod/track_spec.rb
352
355
  - spec/ipod_db_spec.rb
353
356
  - spec/spec_helper.rb
354
357
  - spec/spread_spec.rb