hearken 0.1.1 → 0.1.3
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.
- 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
|