danchoi-itunes-command 1.6.3-x86-darwin-9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/bin/itunes-command +4 -0
  2. data/lib/itunes_command.rb +445 -0
  3. metadata +55 -0
@@ -0,0 +1,4 @@
1
+ #!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
2
+ require 'rubygems'
3
+ require 'itunes_command'
4
+ ItunesCommand.run ARGV
@@ -0,0 +1,445 @@
1
+ #!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
2
+
3
+ # If the shebang line doesn't point to your OS X installation of ruby, find it
4
+ # and correct the path above.
5
+ #
6
+ # itunes-command.rb
7
+ #
8
+ # Requirements:
9
+ #
10
+ # OS X Leopard
11
+ #
12
+ # Instructions:
13
+ #
14
+ # Save this file as 'itunes-command.rb' or whatever else you wish to call it.
15
+ #
16
+ # Run it with either:
17
+ #
18
+ # ruby itunes-command.rb
19
+ #
20
+ # or
21
+ #
22
+ # ./itunes-command.rb
23
+ #
24
+ # or itunes-command.rb
25
+ #
26
+ # The 2nd and 3rd options assume that you made the file executable with
27
+ #
28
+ # chmod u+x itunes-command.rb
29
+ #
30
+ # The 3d options also assumes that itunes-command.rb is on your PATH.
31
+ #
32
+ # Author: Daniel Choi
33
+ # Location: Cambridge, MA
34
+ # Affiliation: http://betahouse.org
35
+ # Email: dhchoi@gmail.com
36
+ # Project Homepage: http://danielchoi.com/software/itunes-command.html
37
+ #
38
+ # License: MIT
39
+ #
40
+ # Copyright (c) 2008 Daniel Choi
41
+ #
42
+ # Permission is hereby granted, free of charge, to any person
43
+ # obtaining a copy of this software and associated documentation
44
+ # files (the "Software"), to deal in the Software without
45
+ # restriction, including without limitation the rights to use,
46
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
47
+ # copies of the Software, and to permit persons to whom the
48
+ # Software is furnished to do so, subject to the following
49
+ # conditions:
50
+ #
51
+ # The above copyright notice and this permission notice shall be
52
+ # included in all copies or substantial portions of the Software.
53
+ #
54
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
55
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
56
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
57
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
58
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
59
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
60
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
61
+ # OTHER DEALINGS IN THE SOFTWARE.
62
+
63
+ require 'rubygems'
64
+ $:.unshift File.dirname(__FILE__)
65
+ require 'readline'
66
+ require 'osx/cocoa'
67
+ OSX.require_framework 'ScriptingBridge'
68
+
69
+ class ITunes
70
+ QUEUE_PLAYLIST = 'itunes-command'
71
+ def initialize
72
+ @app = OSX::SBApplication.applicationWithBundleIdentifier("com.apple.iTunes")
73
+ end
74
+
75
+ # Delegate all other methods to the SBAppliation object for iTunes
76
+ def method_missing(message, *args)
77
+ @app.send(message, *args)
78
+ end
79
+
80
+ def party_shuffle
81
+ playlists.detect {|x| x.name == "Party Shuffle"}
82
+ end
83
+
84
+ def playlists
85
+ @app.sources.first.playlists
86
+ end
87
+
88
+ def playlist_by_name(name)
89
+ playlists.detect {|p| p.name == name}
90
+ end
91
+
92
+ def library
93
+ playlists.first
94
+ end
95
+
96
+ def artists(playlist=library)
97
+ return {} if playlist.tracks.empty?
98
+ artists = playlist.tracks.arrayByApplyingSelector("artist").select {|x| x.to_s =~ /\w/}
99
+ # count tracks per artist, which is represented to number of occurrences
100
+ artist = Hash.new(0)
101
+ artists.each do |name|
102
+ artist[name.to_s] += 1
103
+ end
104
+ artist
105
+ end
106
+
107
+ # Pass in a string to get matching tracks. Pass in an integer to search by
108
+ # databaseID
109
+ def find_track(query)
110
+ if query.is_a?(String)
111
+ library.searchFor_only(query, nil)
112
+ elsif query.is_a?(Integer) # lookup by databaseID
113
+ predicate = OSX::NSPredicate.predicateWithFormat("databaseID == #{query}")
114
+ # assume that only one track matches, and return it
115
+ library.tracks.filteredArrayUsingPredicate(predicate).first
116
+ end
117
+ end
118
+
119
+ def find_tracks(track_ids)
120
+ predicate = OSX::NSPredicate.predicateWithFormat("databaseID IN {%s}" % track_ids.join(','))
121
+ library.tracks.filteredArrayUsingPredicate(predicate)
122
+ end
123
+
124
+ def add_track_to_playlist(track, playlist)
125
+ if playlist.is_a?(String)
126
+ playlist = playlist_by_name(playlist)
127
+ end
128
+ track = track.duplicateTo(playlist)
129
+ # The following does not work:
130
+ # arg1 = OSX::NSArray.arrayWithArray([track])
131
+ # puts arg1.class
132
+ # puts playlist.class
133
+ # puts @app.add_to_(arg1, playlist)
134
+ end
135
+
136
+ def add_tracks_to_playlist(tracks, playlist, credit=nil)
137
+ if playlist.is_a?(String)
138
+ playlist = playlist_by_name(playlist)
139
+ end
140
+ if tracks.is_a?(Array) # need to convert Ruby array into NSArray
141
+ tracks = OSX::NSArray.arrayWithArray(tracks)
142
+ end
143
+ tracks.makeObjectsPerformSelector_withObject("setEnabled:", 1)
144
+ if credit
145
+ tracks.makeObjectsPerformSelector_withObject("setComment:", credit)
146
+ end
147
+ # Note the colon in the selector string
148
+ tracks.makeObjectsPerformSelector_withObject("duplicateTo:", playlist)
149
+ # enable all tracks - does not work
150
+ end
151
+
152
+ def remove_track_from_playlist(track, playlist)
153
+ # looks dangerous!
154
+ return
155
+ track.delete
156
+ end
157
+
158
+ def create_playlist(name)
159
+ return if playlist_by_name(name)
160
+ props = {:name => name}
161
+ playlist = @app.classForScriptingClass("playlist").alloc.initWithProperties(props)
162
+ playlists.insertObject_atIndex(playlist, 0)
163
+ playlist
164
+ end
165
+
166
+ # makes sure the queue playlist is selected. importance for pause/play, skip,
167
+ # etc.
168
+ def select_queue_playlist
169
+ browserWindows.first.view = queue
170
+ end
171
+
172
+ # This is the playlist that itunes-rails uses
173
+ def queue
174
+ playlist_by_name(QUEUE_PLAYLIST) || create_playlist(QUEUE_PLAYLIST)
175
+ end
176
+
177
+ def queue_track(track)
178
+ add_track_to_playlist(track, queue)
179
+ end
180
+
181
+ def queue_tracks(tracks,credit=nil)
182
+ add_tracks_to_playlist(tracks, queue, credit)
183
+ end
184
+
185
+ def clear_queue
186
+ queue.tracks.removeAllObjects
187
+ end
188
+
189
+ end
190
+ class ItunesCommand
191
+ VERSION = '1.6.3'
192
+ attr_accessor :playlist_mode
193
+ def initialize
194
+ @i = ITunes.new
195
+ @playlists = []
196
+ @tracks = []
197
+ @playlist_mode = false
198
+ end
199
+
200
+ def parse(method, *args)
201
+ self.send method, args
202
+ end
203
+
204
+ def search(string=nil)
205
+ unless string
206
+ puts "Please enter a search string"
207
+ return
208
+ end
209
+ @tracks = @i.find_track(string)
210
+ print_names(@tracks)
211
+ end
212
+
213
+ def search_artist(string=nil)
214
+ unless string
215
+ puts "Please enter a search string"
216
+ return
217
+ end
218
+ @tracks = @i.find_track(string)
219
+ print_names(@tracks)
220
+ end
221
+
222
+ def print_names(items)
223
+ items = Array(items)
224
+ rows = []
225
+ items.each_with_index do |t, i|
226
+ begin
227
+ puts("%2d %s : %s" % [i, t.artist, t.name])
228
+ rescue
229
+ end
230
+ end
231
+ items
232
+ end
233
+
234
+ def play(index)
235
+ if @tracks.empty?
236
+ puts "No tracks in buffer yet. Try searching."
237
+ return
238
+ end
239
+ track = @tracks[index.to_i]
240
+ puts "Playing '#{track.name}' by #{track.artist} from #{track.album}"
241
+ track.playOnce(1)
242
+ end
243
+
244
+ def stop
245
+ @i.stop
246
+ end
247
+
248
+ def volume(level=nil)
249
+ unless level
250
+ puts "The volume is #{@i.soundVolume} out of 100"
251
+ return
252
+ end
253
+ @i.soundVolume = level
254
+ puts "The volume set to #{@i.soundVolume} out of 100"
255
+ end
256
+
257
+ def +(steps=10)
258
+ @i.soundVolume = @i.soundVolume + steps.to_i
259
+ volume
260
+ end
261
+
262
+ def -(steps=10)
263
+ @i.soundVolume = @i.soundVolume - steps.to_i
264
+ volume
265
+ end
266
+
267
+ def playlists
268
+ @playlist_mode = true
269
+ puts "Showing playlists"
270
+ playlists = @i.playlists
271
+ playlists.each_with_index do |p,i|
272
+ puts("%2d %s" % [i, p.name])
273
+ end
274
+ end
275
+
276
+ def select_playlist(index)
277
+ @current_playlist = @i.playlists[index.to_i]
278
+ # show tracks
279
+ @tracks = @current_playlist.tracks
280
+ print_names(@tracks)
281
+ @playlist_mode = false
282
+ end
283
+
284
+ def playlist(index)
285
+ @playlists ||= playlists
286
+ puts @playlists[index].name
287
+ end
288
+
289
+ def queue(index)
290
+ if index =~ /\d+-\d+/
291
+ start = index.split('-').first.to_i
292
+ last = index.split('-').last.to_i
293
+ Array(start..last).each do |i|
294
+ @i.queue_track(track=@tracks[i])
295
+ puts "Added #{track.name} to the queue"
296
+ end
297
+ else
298
+ @i.queue_track(track=@tracks[index.to_i])
299
+ puts "Added #{track.name} to the queue"
300
+ end
301
+ end
302
+
303
+ def playpause
304
+ puts %x{tell application "iTunes"
305
+ playpause
306
+ end tell}
307
+ state = `osascript -e 'tell application "iTunes" to player state as string'`.strip
308
+ puts state
309
+ return
310
+ if state == 'stopped'
311
+ @i.currentTrack.playOnce(1)
312
+ else
313
+ puts @i.currentTrack.name
314
+ @i.pause
315
+ end
316
+ end
317
+
318
+ def show_queue
319
+ if @i.queue.tracks.empty?
320
+ puts "The queue is empty"
321
+ return
322
+ end
323
+ state = `osascript -e 'tell application "iTunes" to player state as string'`.strip
324
+ if state != 'stopped'
325
+ current_track_index = `osascript -e 'tell application "iTunes" to index of current track as string'`.to_i
326
+ else
327
+ current_track_index = nil
328
+ end
329
+ @i.queue.tracks.each_with_index do |t, i|
330
+ if current_track_index && current_track_index - 1 == i
331
+ puts "%s : %s <--- currently playing" % [t.artist, t.name]
332
+ else
333
+ puts "%s : %s" % [t.artist, t.name]
334
+ end
335
+ end
336
+ end
337
+
338
+ def clear_queue
339
+ @i.clear_queue
340
+ puts "Cleared queue"
341
+ end
342
+
343
+ def start_queue
344
+ @i.stop
345
+ @i.queue.playOnce(1)
346
+ track = @i.currentTrack
347
+ puts "Playing '#{track.name}' by #{track.artist} from #{track.album}"
348
+ end
349
+
350
+ def skip
351
+ @i.nextTrack
352
+ track = @i.currentTrack
353
+ puts "Playing '#{track.name}' by #{track.artist} from #{track.album}"
354
+ end
355
+
356
+ def playpause
357
+ @i.playpause
358
+ end
359
+
360
+ def artists
361
+ rows = []
362
+ (@artists=@i.artists).keys.sort_by {|x| x.downcase}.each_with_index do |k, i|
363
+ rows << ("%s (%s tracks)" % [k, @artists[k]])
364
+ end
365
+ pager(rows)
366
+ end
367
+
368
+ def pager(rows)
369
+ out = rows.join("\n")
370
+ IO.popen("less", "w") do |less|
371
+ less.puts out
372
+ less.close_write
373
+ end
374
+ end
375
+
376
+ HELP = <<END
377
+ itunes-command
378
+
379
+ Commands:
380
+
381
+ q quit
382
+ h show commands
383
+ s <string> searches for tracks matching <string>
384
+ <track number> plays a track (assumes you've done a search and got results)
385
+ a lists all artists in the library
386
+ v show the current volume level
387
+ v <level> sets the volume level (1-100)
388
+ + <increment> increases the volume by <increment>; default is 10 steps
389
+ - <increment> decreases the volume by <increment>; default is 10 steps
390
+ x stop
391
+ " pause/play
392
+ p shows all playlists
393
+ <playlist number> shows all the tracks in a playlist
394
+ l list all tracks in the queue (which will play tracks in succession)
395
+ n <track number> put a track in the queue; can be a range, e.g. 3-5
396
+ c clear the queue
397
+ g start playing tracks in the queue
398
+ k skip to next track in queue
399
+ END
400
+
401
+ COMMANDS = { 's' => :search,
402
+ 'x' => :stop , 'play' => :play, 'v' => :volume, 'a' => :artists, '+' => :+, '-' => '-',
403
+ 'p' => :playlists, 'select_playlist' => :select_playlist, 'l' => :show_queue, 'n' => :queue,
404
+ 'c' => :clear_queue, 'g' => 'start_queue', 'k' => 'skip', '"' => :playpause }
405
+
406
+ def self.run(argv=ARGV)
407
+ i = ItunesCommand.new
408
+ puts HELP
409
+ loop do
410
+ command = Readline.readline(">> ").chomp
411
+ if command =~ /^q/
412
+ exit
413
+ elsif command =~ /^h/
414
+ puts HELP
415
+ next
416
+ end
417
+ args = command.split(' ')
418
+ if args.first =~ /\d+/
419
+ if i.playlist_mode
420
+ args.unshift 'select_playlist'
421
+ else
422
+ args.unshift 'play'
423
+ end
424
+ end
425
+ method = COMMANDS[args.shift]
426
+ if method.nil?
427
+ puts "Sorry, I don't recognize that command."
428
+ next
429
+ end
430
+ begin
431
+ args.empty? ? i.send(method) : i.send(method, args.join(' '))
432
+ rescue ArgumentError
433
+ puts "Invalid command."
434
+ end
435
+ end
436
+
437
+ end
438
+
439
+ end
440
+
441
+
442
+ if __FILE__ == $0
443
+ ItunesCommand.run ARGV
444
+ end
445
+
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: danchoi-itunes-command
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.6.3
5
+ platform: x86-darwin-9
6
+ authors:
7
+ - Daniel Choi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-20 00:00:00 -08:00
13
+ default_executable: itunes-command
14
+ dependencies: []
15
+
16
+ description: Search, queue, and play iTunes tracks from the command line.
17
+ email: dhchoi@gmail.com
18
+ executables:
19
+ - itunes-command
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/itunes_command.rb
26
+ - bin/itunes-command
27
+ has_rdoc: true
28
+ homepage: http://danielchoi.com/software/itunes-command
29
+ post_install_message:
30
+ rdoc_options:
31
+ - --inline-source
32
+ - --charset=UTF-8
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: "0"
40
+ version:
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ requirements: []
48
+
49
+ rubyforge_project:
50
+ rubygems_version: 1.2.0
51
+ signing_key:
52
+ specification_version: 2
53
+ summary: Search and play iTunes tracks from the command line
54
+ test_files: []
55
+