hearken 0.1.1 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +14 -0
- data/.tool-versions +1 -0
- data/Gemfile +7 -1
- data/Gemfile.lock +95 -0
- data/{README.rdoc → README.md} +12 -50
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/exe/hearken +9 -0
- data/exe/hearken_index +13 -0
- data/hearken.gemspec +35 -35
- data/lib/hearken/colour.rb +3 -1
- data/lib/hearken/command/enqueue.rb +35 -10
- data/lib/hearken/command/reload.rb +18 -6
- data/lib/hearken/command/search.rb +31 -20
- data/lib/hearken/console.rb +11 -27
- data/lib/hearken/debug.rb +6 -6
- data/lib/hearken/indexing/ffmpeg_file.rb +7 -0
- data/lib/hearken/indexing/file.rb +3 -1
- data/lib/hearken/indexing/indexer.rb +32 -24
- data/lib/hearken/library.rb +49 -41
- data/lib/hearken/monkey_violence.rb +9 -7
- data/lib/hearken/paths.rb +13 -19
- data/lib/hearken/range_expander.rb +18 -13
- data/lib/hearken/tagged.rb +15 -11
- data/lib/hearken/track.rb +53 -39
- data/lib/hearken.rb +2 -2
- metadata +52 -141
- data/.gitignore +0 -5
- data/HISTORY.rdoc +0 -38
- data/MIT-LICENSE +0 -20
- data/bin/hearken +0 -7
- data/bin/hearken_index +0 -12
- data/lib/hearken/command/list.rb +0 -32
- data/lib/hearken/command/love.rb +0 -7
- data/lib/hearken/command/profile.rb +0 -7
- data/lib/hearken/command/recent.rb +0 -38
- data/lib/hearken/command/remove.rb +0 -15
- data/lib/hearken/command/restart.rb +0 -7
- data/lib/hearken/command/scrobbling.rb +0 -14
- data/lib/hearken/command/setup_scrobbling.rb +0 -7
- data/lib/hearken/command/shuffle.rb +0 -13
- data/lib/hearken/command/start.rb +0 -7
- data/lib/hearken/command/status.rb +0 -7
- data/lib/hearken/command/stop.rb +0 -7
- data/lib/hearken/command.rb +0 -35
- data/lib/hearken/notification/growl_notifier.rb +0 -15
- data/lib/hearken/player.rb +0 -126
- data/lib/hearken/preferences.rb +0 -30
- data/lib/hearken/queue.rb +0 -33
- data/lib/hearken/scrobbler.rb +0 -77
- data/lib/hearken/simple_scrobbler.rb +0 -94
- data/media/ice_cream.png +0 -0
- data/spec/hearken/command/enqueue_spec.rb +0 -24
- data/spec/hearken/command/list_spec.rb +0 -31
- data/spec/hearken/command/reload_spec.rb +0 -20
- data/spec/hearken/command/shuffle_spec.rb +0 -31
- data/spec/hearken/player_spec.rb +0 -38
- data/spec/hearken/range_expander_spec.rb +0 -28
- data/spec/spec_helper.rb +0 -5
data/HISTORY.rdoc
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
= 0.1.1
|
2
|
-
|
3
|
-
* added use of sox to play more audio file formats (now m4a and mp3 with afplay and sox for everything else)
|
4
|
-
* fixed some issues with the parsing of ffmpeg output to get correct tag information
|
5
|
-
* added logging of played tracks to history file
|
6
|
-
* removed splat dependency (because it is just silly)
|
7
|
-
|
8
|
-
= 0.1.0
|
9
|
-
|
10
|
-
* removed several dependencies - simple_scrobbler and thor
|
11
|
-
* split hearken command into two
|
12
|
-
* added 'love' command (to mark current track as loved)
|
13
|
-
* added 'profile' command (to launch last.fm profile to see track history)
|
14
|
-
* replaced 'flush' with an 'rm' command that can remove tracks matches criteria
|
15
|
-
|
16
|
-
= 0.0.5
|
17
|
-
|
18
|
-
* fixed 'recent' command - the library is sorted on file modification date
|
19
|
-
* moved 'current_song' from working directory to .hearken directory
|
20
|
-
* Changed dequeue to return track instead of id - this fixed a bug where reindexing would cause player to start playing the wrong files.
|
21
|
-
|
22
|
-
= 0.0.4
|
23
|
-
|
24
|
-
* Partially fixed bug with growl (correctly escaping characters in message)
|
25
|
-
* moved all configuration, queues and index to a .hearken directory
|
26
|
-
|
27
|
-
= 0.0.3
|
28
|
-
|
29
|
-
* Fixed bug in setup_scrobbling command (using $stdin directly - 'gets' seems to have been aliased by thor)
|
30
|
-
* Added growl notification
|
31
|
-
|
32
|
-
= 0.0.2
|
33
|
-
|
34
|
-
* First functioning version - combination of features from audio_library and songbirdsh
|
35
|
-
|
36
|
-
= 0.0.1
|
37
|
-
|
38
|
-
* Released empty gem (to claim the name before proceeding any further)
|
data/MIT-LICENSE
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
Copyright (c) 2011 Mark Ryall
|
2
|
-
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
-
a copy of this software and associated documentation files (the
|
5
|
-
"Software"), to deal in the Software without restriction, including
|
6
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
-
permit persons to whom the Software is furnished to do so, subject to
|
9
|
-
the following conditions:
|
10
|
-
|
11
|
-
The above copyright notice and this permission notice shall be
|
12
|
-
included in all copies or substantial portions of the Software.
|
13
|
-
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/bin/hearken
DELETED
data/bin/hearken_index
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$: << File.dirname(__FILE__)+'/../lib'
|
4
|
-
|
5
|
-
require 'hearken/indexing'
|
6
|
-
|
7
|
-
unless ARGV.size == 1
|
8
|
-
puts "usage: hearken_index path_to_directory_containing_music_collection"
|
9
|
-
exit
|
10
|
-
end
|
11
|
-
|
12
|
-
Hearken::Indexing::Indexer.new(ARGV.shift).execute
|
data/lib/hearken/command/list.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'hearken/command'
|
2
|
-
require 'hearken/colour'
|
3
|
-
|
4
|
-
class Hearken::Command::List
|
5
|
-
include Hearken::Command
|
6
|
-
include Hearken::Colour
|
7
|
-
|
8
|
-
usage '*<word>'
|
9
|
-
help <<EOF
|
10
|
-
lists the contents of the track queue
|
11
|
-
these results can optionally be filtered by specified words
|
12
|
-
when playing, approximate times for each track will be displayed
|
13
|
-
EOF
|
14
|
-
execute do |text|
|
15
|
-
@terms = text.split(/\W/)
|
16
|
-
current = @player.current
|
17
|
-
if current
|
18
|
-
next_start_time = Time.at current.started
|
19
|
-
show next_start_time, current
|
20
|
-
end
|
21
|
-
next_start_time += current.time.to_i if next_start_time && current.time
|
22
|
-
@player.each do |track|
|
23
|
-
show next_start_time, track
|
24
|
-
next_start_time += track.time.to_i if next_start_time && track.time
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def show time, track
|
29
|
-
return unless @terms.empty? or @terms.all? {|term| track.search_string.include? term }
|
30
|
-
puts time ? "#{c time.strftime("%H:%M:%S %d/%m/%Y"), :blue} #{track}" : track
|
31
|
-
end
|
32
|
-
end
|
data/lib/hearken/command/love.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'hearken/command'
|
2
|
-
require 'hearken/colour'
|
3
|
-
|
4
|
-
class Hearken::Command::Recent
|
5
|
-
include Hearken::Command
|
6
|
-
include Hearken::Colour
|
7
|
-
|
8
|
-
usage '<count>'
|
9
|
-
help 'lists the specified number of recently added albums'
|
10
|
-
execute do |text|
|
11
|
-
@player.library.reload unless @player.library.tracks
|
12
|
-
all_tracks = @player.library.tracks.sort do |a, b|
|
13
|
-
tc = a.timestamp <=> b.timestamp
|
14
|
-
tc == 0 ? a.id <=> b.id : tc
|
15
|
-
end
|
16
|
-
maximum, current_album, tracks, total_count = text.to_i, nil, [], 0
|
17
|
-
all_tracks.reverse.each do |track|
|
18
|
-
unless current_album
|
19
|
-
current_album = track.album
|
20
|
-
tracks = [track]
|
21
|
-
next
|
22
|
-
end
|
23
|
-
if current_album==track.album
|
24
|
-
tracks << track
|
25
|
-
else
|
26
|
-
puts "#{c extract_artist(tracks), :yellow} #{c current_album, :cyan} #{tracks.size} tracks [#{tracks.last.search_id}-#{tracks.first.search_id}]"
|
27
|
-
current_album = track.album
|
28
|
-
tracks = [track]
|
29
|
-
total_count += 1
|
30
|
-
end
|
31
|
-
break if total_count >= maximum
|
32
|
-
end
|
33
|
-
end
|
34
|
-
private
|
35
|
-
def extract_artist tracks
|
36
|
-
tracks.map{|t| t.artist}.uniq.size == 1 ? tracks.first.artist : 'various artists'
|
37
|
-
end
|
38
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
require 'hearken/command'
|
2
|
-
|
3
|
-
class Hearken::Command::Remove
|
4
|
-
include Hearken::Command
|
5
|
-
usage '*<word>'
|
6
|
-
help 'removes all tracks that match the specified criteria - specifying no criteria will flush entire queue'
|
7
|
-
execute do |text|
|
8
|
-
@terms = text.split(/\W/)
|
9
|
-
ids = []
|
10
|
-
while track = @player.dequeue
|
11
|
-
ids << track.id unless @terms.all? {|term| track.search_string.include? term }
|
12
|
-
end
|
13
|
-
ids.each {|id| @player.enqueue id }
|
14
|
-
end
|
15
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'hearken/command'
|
2
|
-
|
3
|
-
class Hearken::Command::Scrobbling
|
4
|
-
include Hearken::Command
|
5
|
-
usage '<on|off>'
|
6
|
-
help 'turns interaction with lastfm on or off'
|
7
|
-
execute do |text|
|
8
|
-
scrobbling = (text == 'on')
|
9
|
-
return if @player.scrobbling == scrobbling
|
10
|
-
puts scrobbling ? 'Turning scrobbling on' : 'Turning scrobbling off'
|
11
|
-
@player.scrobbling = scrobbling
|
12
|
-
@player.restart
|
13
|
-
end
|
14
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'hearken/command'
|
2
|
-
|
3
|
-
class Hearken::Command::Shuffle
|
4
|
-
include Hearken::Command
|
5
|
-
help 'shuffles the current queue'
|
6
|
-
execute do |ignored=nil|
|
7
|
-
ids = []
|
8
|
-
while track = @player.dequeue
|
9
|
-
ids << track.id
|
10
|
-
end
|
11
|
-
ids.sort_by { rand }.each {|id| @player.enqueue id }
|
12
|
-
end
|
13
|
-
end
|
data/lib/hearken/command/stop.rb
DELETED
data/lib/hearken/command.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
module Hearken
|
2
|
-
module Command
|
3
|
-
attr_reader :usage, :help
|
4
|
-
|
5
|
-
def self.included cls
|
6
|
-
cls.extend ClassMethods
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.load name, *args
|
10
|
-
require "hearken/command/#{name}"
|
11
|
-
classname = name.to_s.split('_').map{|s|s.capitalize}.join
|
12
|
-
Hearken::Command.const_get(classname).new *args
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize player
|
16
|
-
@player = player
|
17
|
-
@usage = ''
|
18
|
-
@help = ''
|
19
|
-
end
|
20
|
-
|
21
|
-
module ClassMethods
|
22
|
-
def usage usage
|
23
|
-
define_method(:usage) { usage }
|
24
|
-
end
|
25
|
-
|
26
|
-
def help help
|
27
|
-
define_method(:help) { help }
|
28
|
-
end
|
29
|
-
|
30
|
-
def execute &block
|
31
|
-
define_method :execute, block
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
require 'hearken/monkey_violence'
|
2
|
-
|
3
|
-
module Hearken::Notification
|
4
|
-
end
|
5
|
-
|
6
|
-
class Hearken::Notification::GrowlNotifier
|
7
|
-
def initialize preferences
|
8
|
-
@growlnotify = !`which growlnotify`.chomp.empty?
|
9
|
-
@image_path = File.expand_path File.dirname(__FILE__)+'/../../../media/ice_cream.png'
|
10
|
-
end
|
11
|
-
|
12
|
-
def started track
|
13
|
-
`growlnotify -t "Hearken unto ..." --image #{@image_path} -m \"#{track.to_short_s.escape_for_sh_quoted}\"` if @growlnotify
|
14
|
-
end
|
15
|
-
end
|
data/lib/hearken/player.rb
DELETED
@@ -1,126 +0,0 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
|
3
|
-
require 'hearken/queue'
|
4
|
-
require 'hearken/scrobbler'
|
5
|
-
require 'hearken/notification/growl_notifier'
|
6
|
-
require 'hearken/library'
|
7
|
-
require 'hearken/colour'
|
8
|
-
|
9
|
-
module Hearken
|
10
|
-
class Player
|
11
|
-
include Queue
|
12
|
-
include Colour
|
13
|
-
attr_reader :library, :scrobbler
|
14
|
-
|
15
|
-
def initialize preferences
|
16
|
-
@scrobbler = Scrobbler.new preferences
|
17
|
-
@scrobbler.enabled = true
|
18
|
-
@growl = Hearken::Notification::GrowlNotifier.new preferences
|
19
|
-
@notifiers = [@scrobbler, @growl]
|
20
|
-
@library = Library.new preferences
|
21
|
-
@library.reload
|
22
|
-
create_paths
|
23
|
-
end
|
24
|
-
|
25
|
-
def status
|
26
|
-
if @pid
|
27
|
-
track = self.current
|
28
|
-
played = Time.now.to_i-track.started
|
29
|
-
timing = "(#{c track.time.to_i-played, :yellow} remaining)" if track.time
|
30
|
-
puts "#{c Time.at(track.started).strftime("%H:%M:%S %d/%m/%Y"), :blue}: #{track} #{timing}"
|
31
|
-
else
|
32
|
-
puts 'not playing'.foreground(:yellow)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def love
|
37
|
-
@scrobbler.love current
|
38
|
-
end
|
39
|
-
|
40
|
-
def profile
|
41
|
-
@scrobbler.profile
|
42
|
-
end
|
43
|
-
|
44
|
-
def current
|
45
|
-
in_base_dir do
|
46
|
-
(@pid and File.exist?('current_song')) ? YAML.load_file('current_song') : nil
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def register track
|
51
|
-
track.started = Time.now.to_i
|
52
|
-
in_base_dir do
|
53
|
-
File.open('current_song', 'w') {|f| f.print track.to_yaml }
|
54
|
-
File.open('history', 'a') {|f| f.puts "#{track.started},#{track.path}"}
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def notify_started track
|
59
|
-
@notifiers.each {|notifier| notifier.started track if notifier.respond_to? :started}
|
60
|
-
end
|
61
|
-
|
62
|
-
def notify_finished track
|
63
|
-
@notifiers.each {|notifier| notifier.finished track if notifier.respond_to? :finished}
|
64
|
-
end
|
65
|
-
|
66
|
-
def scrobbling tf
|
67
|
-
@scrobbler.enabled = tf
|
68
|
-
end
|
69
|
-
|
70
|
-
def random_track
|
71
|
-
@library.row (rand * @library.count).to_i
|
72
|
-
end
|
73
|
-
|
74
|
-
def start
|
75
|
-
return if @pid
|
76
|
-
if @library.count == 0
|
77
|
-
puts 'Player can not be started with an empty library'
|
78
|
-
puts 'Please run "hearken_index" in another shell and then \'reload\''
|
79
|
-
return
|
80
|
-
end
|
81
|
-
@pid = fork do
|
82
|
-
player_pid = nil
|
83
|
-
Signal.trap('TERM') do
|
84
|
-
Process.kill 'TERM', player_pid if player_pid
|
85
|
-
exit
|
86
|
-
end
|
87
|
-
loop do
|
88
|
-
track = dequeue || random_track
|
89
|
-
next unless track
|
90
|
-
unless File.exist? track.path
|
91
|
-
puts "skipping track as #{track.path} does not exist"
|
92
|
-
next
|
93
|
-
end
|
94
|
-
notify_started track
|
95
|
-
register track
|
96
|
-
player_pid = spawn play_command track.path
|
97
|
-
Process.wait player_pid
|
98
|
-
notify_finished track
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def play_command path
|
104
|
-
if %w{m4a mp3}.include? path.split('.').last
|
105
|
-
"afplay \"#{path.escape("\`")}\""
|
106
|
-
else
|
107
|
-
"play -q \"#{path.escape("\`")}\""
|
108
|
-
end.tap do |command|
|
109
|
-
in_base_dir do
|
110
|
-
File.open('player', 'a') {|f| f.puts command }
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def stop
|
116
|
-
return unless @pid
|
117
|
-
Process.kill 'TERM', @pid
|
118
|
-
@pid = nil
|
119
|
-
end
|
120
|
-
|
121
|
-
def restart
|
122
|
-
stop
|
123
|
-
start
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
data/lib/hearken/preferences.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'hearken/paths'
|
2
|
-
require 'hearken/monkey_violence'
|
3
|
-
|
4
|
-
module Hearken
|
5
|
-
class Preferences
|
6
|
-
include Hearken::Paths
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
create_paths
|
10
|
-
if File.exists? preferences_path
|
11
|
-
@preferences = YAML.load_file preferences_path
|
12
|
-
else
|
13
|
-
@preferences = {}
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def [] key
|
18
|
-
@preferences[key]
|
19
|
-
end
|
20
|
-
|
21
|
-
def []= key, value
|
22
|
-
@preferences[key] = value
|
23
|
-
persist
|
24
|
-
end
|
25
|
-
|
26
|
-
def persist
|
27
|
-
File.open(preferences_path, 'w') {|f| f.puts @preferences.to_yaml}
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
data/lib/hearken/queue.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'hearken/paths'
|
2
|
-
|
3
|
-
module Hearken::Queue
|
4
|
-
include Hearken::Paths
|
5
|
-
|
6
|
-
def enqueue id
|
7
|
-
in_queue_dir do
|
8
|
-
@sequence ||= 0
|
9
|
-
@library.with_track id do |track|
|
10
|
-
File.open("#{Time.now.to_i}-#{@sequence.to_s.rjust(8,'0')}.song", 'w') {|f| f.print track.to_yaml }
|
11
|
-
@sequence += 1
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def each
|
17
|
-
in_queue_dir do
|
18
|
-
Dir.glob('*.song').sort.each do |file|
|
19
|
-
yield YAML.load_file file
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def dequeue
|
25
|
-
in_queue_dir do
|
26
|
-
file = Dir.glob('*.song').sort.first
|
27
|
-
return nil unless file
|
28
|
-
hash = YAML.load_file file
|
29
|
-
FileUtils.rm file
|
30
|
-
hash
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
data/lib/hearken/scrobbler.rb
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
require 'hearken/simple_scrobbler'
|
2
|
-
require 'hearken/debug'
|
3
|
-
|
4
|
-
# Modified version of simple_scrobbler gem from https://github.com/threedaymonk/simple_scrobbler
|
5
|
-
module Hearken
|
6
|
-
class Scrobbler
|
7
|
-
API_KEY = '21f8c75ad38637220b20a03ad61219a4'
|
8
|
-
SECRET = 'ab77019c84eef8bc16bcfd5ba8db0c5d'
|
9
|
-
include Debug
|
10
|
-
|
11
|
-
def initialize preferences
|
12
|
-
@preferences = preferences
|
13
|
-
end
|
14
|
-
|
15
|
-
def enabled= tf
|
16
|
-
@scrobbler = nil
|
17
|
-
if @preferences['lastfm'] and tf
|
18
|
-
debug "Configuring scrobbler with #{@preferences['lastfm'].inspect}"
|
19
|
-
user, session = *%w{user session_key}.map{|k| @preferences['lastfm'][k]}
|
20
|
-
@scrobbler = SimpleScrobbler.new API_KEY, SECRET, user, session
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def finished track
|
25
|
-
return unless @scrobbler
|
26
|
-
debug "Scrobbling to last fm: #{track}"
|
27
|
-
send_to_scrobbler :scrobble, track
|
28
|
-
end
|
29
|
-
|
30
|
-
def started track
|
31
|
-
return unless @scrobbler
|
32
|
-
debug "Updating now listening with last fm: #{track}"
|
33
|
-
send_to_scrobbler :now_playing, track
|
34
|
-
end
|
35
|
-
|
36
|
-
def love track
|
37
|
-
return unless @scrobbler and track
|
38
|
-
debug "Sending love to last fm: #{track}"
|
39
|
-
send_to_scrobbler :love, track
|
40
|
-
end
|
41
|
-
|
42
|
-
def profile
|
43
|
-
return unless @scrobbler
|
44
|
-
@scrobbler.with_profile_url {|url| system "open #{url}" }
|
45
|
-
end
|
46
|
-
|
47
|
-
def ask question
|
48
|
-
print question
|
49
|
-
$stdin.gets.chomp
|
50
|
-
end
|
51
|
-
|
52
|
-
def setup
|
53
|
-
preferences = {}
|
54
|
-
preferences['user'] = ask 'What is your lastfm user name ? '
|
55
|
-
@scrobbler = SimpleScrobbler.new API_KEY, SECRET, preferences['user']
|
56
|
-
preferences['session_key'] = @scrobbler.fetch_session_key do |url|
|
57
|
-
system "open '#{url}'"
|
58
|
-
ask 'Please hit enter when you\'ve allowed this application access to your account'
|
59
|
-
end
|
60
|
-
@preferences['lastfm'] = preferences
|
61
|
-
end
|
62
|
-
private
|
63
|
-
def send_to_scrobbler message, track
|
64
|
-
begin
|
65
|
-
debug %w{artist title time album track}.map {|k| "#{k}=#{track.send(k)}"}.join(',')
|
66
|
-
@scrobbler.send message, track.artist,
|
67
|
-
track.title,
|
68
|
-
:duration => track.time,
|
69
|
-
:album => track.album,
|
70
|
-
:trackNumber => track.track.to_i,
|
71
|
-
:timestamp => Time.now.to_i
|
72
|
-
rescue Exception => e
|
73
|
-
puts "Failed to scrobble: #{e}"
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
@@ -1,94 +0,0 @@
|
|
1
|
-
require "net/http"
|
2
|
-
require "digest/md5"
|
3
|
-
require "uri"
|
4
|
-
require "cgi"
|
5
|
-
require 'nokogiri'
|
6
|
-
require 'hearken/debug'
|
7
|
-
|
8
|
-
module Hearken; end
|
9
|
-
|
10
|
-
class Hearken::SimpleScrobbler
|
11
|
-
include Hearken::Debug
|
12
|
-
|
13
|
-
SCROBBLER_URL = 'http://ws.audioscrobbler.com/2.0/'
|
14
|
-
|
15
|
-
SubmissionError = Class.new(RuntimeError)
|
16
|
-
SessionError = Class.new(RuntimeError)
|
17
|
-
|
18
|
-
def initialize api_key, secret, user, session_key=nil
|
19
|
-
@api_key = api_key
|
20
|
-
@secret = secret
|
21
|
-
@user = user
|
22
|
-
@session_key = session_key
|
23
|
-
end
|
24
|
-
|
25
|
-
attr_reader :user, :api_key, :secret, :session_key
|
26
|
-
|
27
|
-
def session_key
|
28
|
-
@session_key or raise SessionError, "The session key must be set or fetched"
|
29
|
-
end
|
30
|
-
|
31
|
-
def fetch_session_key
|
32
|
-
doc = lfm :get, 'auth.gettoken'
|
33
|
-
request_token = doc.at('token').inner_text
|
34
|
-
yield "http://www.last.fm/api/auth/?api_key=#{api_key}&token=#{request_token}"
|
35
|
-
doc = lfm :get, 'auth.getsession', :token => request_token
|
36
|
-
@session_key = doc.at('key').inner_text
|
37
|
-
@user = doc.at('name').inner_text
|
38
|
-
@session_key
|
39
|
-
end
|
40
|
-
|
41
|
-
def with_profile_url
|
42
|
-
yield "http://www.last.fm/user/#{user}" if user
|
43
|
-
end
|
44
|
-
|
45
|
-
# http://www.last.fm/api/show?service=443
|
46
|
-
def scrobble artist, title, params={}
|
47
|
-
lfm_track 'track.scrobble', artist, title, params
|
48
|
-
end
|
49
|
-
|
50
|
-
# See http://www.last.fm/api/show?service=454 for more details
|
51
|
-
def now_playing artist, title, params={}
|
52
|
-
lfm_track 'track.updateNowPlaying', artist, title, params
|
53
|
-
end
|
54
|
-
|
55
|
-
# http://www.last.fm/api/show?service=260
|
56
|
-
def love artist, title, params={}
|
57
|
-
lfm_track 'track.love', artist, title, params
|
58
|
-
end
|
59
|
-
private
|
60
|
-
def lfm_track method, artist, title, params
|
61
|
-
doc = lfm :post, method, params.merge(:sk => session_key, :artist => artist, :track => title)
|
62
|
-
status = doc.at('lfm')['status']
|
63
|
-
raise SubmissionError, status unless status == 'ok'
|
64
|
-
end
|
65
|
-
|
66
|
-
def lfm get_or_post, method, parameters={}
|
67
|
-
p = signed_parameters parameters.merge :api_key => api_key, :method => method
|
68
|
-
debug p.inspect
|
69
|
-
xml = self.send get_or_post, SCROBBLER_URL, p
|
70
|
-
debug xml
|
71
|
-
Nokogiri::XML xml
|
72
|
-
end
|
73
|
-
|
74
|
-
def get url, parameters
|
75
|
-
query_string = sort_parameters(parameters).
|
76
|
-
map{ |k, v| "#{k}=#{CGI.escape(v)}" }.
|
77
|
-
join("&")
|
78
|
-
Net::HTTP.get_response(URI.parse(url + "?" + query_string)).body
|
79
|
-
end
|
80
|
-
|
81
|
-
def post url, parameters
|
82
|
-
Net::HTTP.post_form(URI.parse(url), parameters).body
|
83
|
-
end
|
84
|
-
|
85
|
-
def signed_parameters parameters
|
86
|
-
sorted = sort_parameters parameters
|
87
|
-
signature = Digest::MD5.hexdigest(sorted.flatten.join + secret)
|
88
|
-
parameters.merge :api_sig => signature
|
89
|
-
end
|
90
|
-
|
91
|
-
def sort_parameters parameters
|
92
|
-
parameters.map{ |k, v| [k.to_s, v.to_s] }.sort
|
93
|
-
end
|
94
|
-
end
|
data/media/ice_cream.png
DELETED
Binary file
|