hearken 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +1 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +2 -4
  4. data/README.rdoc +48 -0
  5. data/Rakefile +5 -0
  6. data/bin/hearken +7 -0
  7. data/hearken.gemspec +9 -0
  8. data/lib/hearken.rb +1 -5
  9. data/lib/hearken/cli.rb +24 -0
  10. data/lib/hearken/command.rb +35 -0
  11. data/lib/hearken/command/enqueue.rb +12 -0
  12. data/lib/hearken/command/flush.rb +7 -0
  13. data/lib/hearken/command/list.rb +29 -0
  14. data/lib/hearken/command/recent.rb +31 -0
  15. data/lib/hearken/command/reload.rb +7 -0
  16. data/lib/hearken/command/restart.rb +7 -0
  17. data/lib/hearken/command/scrobbling.rb +14 -0
  18. data/lib/hearken/command/search.rb +25 -0
  19. data/lib/hearken/command/setup_scrobbling.rb +7 -0
  20. data/lib/hearken/command/show_properties.rb +9 -0
  21. data/lib/hearken/command/shuffle.rb +13 -0
  22. data/lib/hearken/command/start.rb +7 -0
  23. data/lib/hearken/command/status.rb +7 -0
  24. data/lib/hearken/command/stop.rb +7 -0
  25. data/lib/hearken/console.rb +34 -0
  26. data/lib/hearken/debug.rb +16 -0
  27. data/lib/hearken/indexing.rb +7 -0
  28. data/lib/hearken/indexing/audio_traverser.rb +19 -0
  29. data/lib/hearken/indexing/executor.rb +33 -0
  30. data/lib/hearken/indexing/ffmpeg_file.rb +51 -0
  31. data/lib/hearken/indexing/file.rb +13 -0
  32. data/lib/hearken/indexing/indexer.rb +28 -0
  33. data/lib/hearken/indexing/parser.rb +7 -0
  34. data/lib/hearken/indexing/persistant_traverser.rb +32 -0
  35. data/lib/hearken/indexing/persisted_traverser.rb +30 -0
  36. data/lib/hearken/library.rb +46 -0
  37. data/lib/hearken/player.rb +91 -0
  38. data/lib/hearken/preferences.rb +29 -0
  39. data/lib/hearken/queue.rb +26 -0
  40. data/lib/hearken/range_expander.rb +30 -0
  41. data/lib/hearken/scrobbler.rb +76 -0
  42. data/lib/hearken/tagged.rb +15 -0
  43. data/lib/hearken/track.rb +38 -0
  44. data/lib/hearken/version.rb +2 -2
  45. data/media/applause.mp3 +0 -0
  46. data/spec/hearken/command/enqueue_spec.rb +24 -0
  47. data/spec/hearken/command/list_spec.rb +31 -0
  48. data/spec/hearken/command/reload_spec.rb +20 -0
  49. data/spec/hearken/command/shuffle_spec.rb +27 -0
  50. data/spec/hearken/player_spec.rb +37 -0
  51. data/spec/hearken/range_expander_spec.rb +28 -0
  52. data/spec/spec_helper.rb +5 -0
  53. metadata +136 -8
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  *.gem
2
+ *song
2
3
  .bundle
3
4
  Gemfile.lock
4
5
  pkg/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.2@hearken --create
data/Gemfile CHANGED
@@ -1,4 +1,2 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in hearken.gemspec
4
- gemspec
1
+ source 'http://rubygems.org'
2
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,48 @@
1
+ = Hearken
2
+
3
+ This is a command line shell for queuing and playing music tracks.
4
+
5
+ It also extracts id3 tags from audio files for faster search.
6
+
7
+ This will eventually be platform independent but at this stage has only been used on mac os x
8
+
9
+ = Usage
10
+
11
+ Here you sit expectantly in front of a computer at the command line.
12
+
13
+ == Install
14
+
15
+ gem install hearken
16
+
17
+ == Dependencies
18
+
19
+ Tags are currently extracted using ffmpeg. On mac os x, this can be installed easily using brew:
20
+
21
+ brew install ffmpeg
22
+
23
+ == Indexing tracks
24
+
25
+ hearken index DIRECTORY
26
+
27
+ Will create a track index at ~/.music. Note that this might take a while the first time you run it.
28
+
29
+ Subsequent runs will only query tags for new or modified files so should be fairly fast.
30
+
31
+ == Console
32
+
33
+ hearken console
34
+
35
+ == Commands
36
+
37
+ ?
38
+
39
+ will list all commands
40
+
41
+ ? <command>
42
+
43
+ will describe the use and purpose of a particular command
44
+
45
+ = Future plans for world domination
46
+
47
+ * Get working on linux
48
+ * Get working on windows
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
+
3
+ desc 'execute specifications'
4
+ task :test do
5
+ sh 'bundle exec rspec spec'
6
+ end
data/bin/hearken ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.dirname(__FILE__)+'/../lib'
4
+
5
+ require 'hearken/cli'
6
+
7
+ Hearken::Cli.start
data/hearken.gemspec CHANGED
@@ -23,4 +23,13 @@ EOF
23
23
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
24
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
25
  s.require_paths = ["lib"]
26
+
27
+ s.add_dependency 'thor', '~>0'
28
+ s.add_dependency 'splat', '~>0'
29
+ s.add_dependency 'shell_shock', '~>0'
30
+ s.add_dependency 'simple_scrobbler', '~> 0'
31
+ s.add_dependency 'rainbow', '~> 1'
32
+
33
+ s.add_development_dependency 'rake', '~>0'
34
+ s.add_development_dependency 'rspec', '~>2'
26
35
  end
data/lib/hearken.rb CHANGED
@@ -1,5 +1 @@
1
- require "hearken/version"
2
-
3
- module Hearken
4
- # Your code goes here...
5
- end
1
+ require 'hearken/version'
@@ -0,0 +1,24 @@
1
+ require 'thor'
2
+
3
+ require 'hearken/version'
4
+ require 'hearken/indexing'
5
+ require 'hearken/console'
6
+
7
+ module Hearken
8
+ class Cli < Thor
9
+ desc 'version', 'Prints the current version'
10
+ def version
11
+ puts "Current version is "+Hearken::VERSION
12
+ end
13
+
14
+ desc 'index DIRECTORY', 'Reindexes a music collection'
15
+ def index directory
16
+ Hearken::Indexing::Indexer.new(directory).execute
17
+ end
18
+
19
+ desc 'console', 'Enters console for queuing and playing tracks'
20
+ def console
21
+ Hearken::Console.new.push
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,35 @@
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
@@ -0,0 +1,12 @@
1
+ require 'hearken/range_expander'
2
+ require 'hearken/command'
3
+
4
+ class Hearken::Command::Enqueue
5
+ include Hearken::Command
6
+ usage '*<id>'
7
+ help 'enqueues the list of songs with the specified ids'
8
+ execute do |text|
9
+ @expander ||= Hearken::RangeExpander.new
10
+ @expander.expand(text).each {|id| @player.enqueue id }
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ require 'hearken/command'
2
+
3
+ class Hearken::Command::Flush
4
+ include Hearken::Command
5
+ help 'flushes the current queue'
6
+ execute {|ignored| loop { break unless @player.dequeue } }
7
+ end
@@ -0,0 +1,29 @@
1
+ require 'hearken/command'
2
+
3
+ class Hearken::Command::List
4
+ include Hearken::Command
5
+ usage '*<word>'
6
+ help <<EOF
7
+ lists the contents of the track queue
8
+ these results can optionally be filtered by specified words
9
+ when playing, approximate times for each track will be displayed
10
+ EOF
11
+ execute do |text|
12
+ @terms = text.split(/\W/)
13
+ current = @player.current
14
+ if current
15
+ next_start_time = Time.at current.started
16
+ show next_start_time, current
17
+ end
18
+ next_start_time += current.time.to_i if next_start_time && current.time
19
+ @player.each do |track|
20
+ show next_start_time, track
21
+ next_start_time += track.time.to_i if next_start_time && track.time
22
+ end
23
+ end
24
+
25
+ def show time, track
26
+ return unless @terms.empty? or @terms.all? {|term| track.search_string.include? term }
27
+ puts time ? "#{time.to_s.foreground(:blue)}\n\t#{track}" : track
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ require 'hearken/command'
2
+
3
+ class Hearken::Command::Recent
4
+ include Hearken::Command
5
+ usage '<count>'
6
+ help 'lists the specified number of recently added albums'
7
+ execute do |text|
8
+ @player.library.reload unless @player.library.tracks
9
+ maximum, current_album, tracks, total_count = text.to_i, nil, [], 0
10
+ @player.library.tracks.reverse.each do |track|
11
+ unless current_album
12
+ current_album = track.album
13
+ tracks = [track]
14
+ next
15
+ end
16
+ if current_album==track.album
17
+ tracks << track
18
+ else
19
+ puts "#{current_album} - #{extract_artist tracks} - #{tracks.size} tracks (#{tracks.last.search_id}-#{tracks.first.search_id})"
20
+ current_album = track.album
21
+ tracks = [track]
22
+ total_count += 1
23
+ end
24
+ break if total_count >= maximum
25
+ end
26
+ end
27
+ private
28
+ def extract_artist tracks
29
+ tracks.map{|t| t.artist}.uniq.size == 1 ? tracks.first.artist : 'various artists'
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ require 'hearken/command'
2
+
3
+ class Hearken::Command::Reload
4
+ include Hearken::Command
5
+ help 'reloads the contents of the music library for fast searching'
6
+ execute {|ignored| @player.library.reload }
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'hearken/command'
2
+
3
+ class Hearken::Command::Restart
4
+ include Hearken::Command
5
+ help 'stops and restarts the player (which will kill the current track)'
6
+ execute {|ignored| @player.restart }
7
+ end
@@ -0,0 +1,14 @@
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
@@ -0,0 +1,25 @@
1
+ require 'hearken/command'
2
+
3
+ class Hearken::Command::Search
4
+ include Hearken::Command
5
+ usage '*<word>'
6
+ help <<EOF
7
+ searches for tracks containing the specified words (in artist, title or album)
8
+ ids are placed on the clipboard for convenient use with +
9
+ EOF
10
+ execute do |text|
11
+ terms = text.split(/\W/)
12
+ matches = []
13
+ @player.library.reload unless @player.library.tracks
14
+ matches = []
15
+ @player.library.tracks.each do |track|
16
+ if terms.all? {|term| track.search_string.include? term }
17
+ puts track
18
+ matches << track.search_id
19
+ end
20
+ end
21
+ puts "Found #{matches.size} matches (ids have been placed on clipboard)"
22
+ matches.join(' ').to_clipboard
23
+ @player.matches = matches
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ require 'hearken/command'
2
+
3
+ class Hearken::Command::SetupScrobbling
4
+ include Hearken::Command
5
+ help 'runs through the steps required to get lastfm scrobbling working'
6
+ execute {|ignored| @player.scrobbler.setup }
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'hearken/command'
2
+ require 'pp'
3
+
4
+ class Hearken::Command::ShowProperties
5
+ include Hearken::Command
6
+ usage '<id>'
7
+ help 'show the track details for a specified id'
8
+ execute {|id| @player.library.with_track(id.to_i(36)) {|track| pp track } }
9
+ end
@@ -0,0 +1,13 @@
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 id = @player.dequeue
9
+ ids << id
10
+ end
11
+ ids.sort_by { rand }.each {|id| @player.enqueue id }
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ require 'hearken/command'
2
+
3
+ class Hearken::Command::Start
4
+ include Hearken::Command
5
+ help 'starts the player'
6
+ execute {|ignored| @player.start }
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'hearken/command'
2
+
3
+ class Hearken::Command::Status
4
+ include Hearken::Command
5
+ help 'shows the current player status'
6
+ execute {|ignored| puts @player.status }
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'hearken/command'
2
+
3
+ class Hearken::Command::Stop
4
+ include Hearken::Command
5
+ help 'stops the player'
6
+ execute {|ignored| @player.stop }
7
+ end
@@ -0,0 +1,34 @@
1
+ require 'shell_shock/context'
2
+
3
+ require 'hearken/player'
4
+ require 'hearken/preferences'
5
+ require 'hearken/command'
6
+
7
+ require 'yaml'
8
+
9
+ module Hearken
10
+ class Console
11
+ include ShellShock::Context
12
+
13
+ def with name, *aliases
14
+ aliases << name.to_s if aliases.empty?
15
+ add_command Command.load(name, @player), *aliases
16
+ end
17
+
18
+ def with_all *names
19
+ names.each {|name| with name}
20
+ end
21
+
22
+ def initialize
23
+ preferences = Preferences.new
24
+ @player = Player.new preferences
25
+ at_exit { @player.stop }
26
+ @prompt = "hearken > "
27
+ with :status, "'"
28
+ with :show_properties, 'show'
29
+ with :restart, 'next'
30
+ with :enqueue, '+'
31
+ with_all *%w{reload search start stop scrobbling shuffle list setup_scrobbling recent flush}
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ module Hearken
2
+ module Debug
3
+ def debug message
4
+ if ENV['DEBUG']
5
+ puts message
6
+ end
7
+ end
8
+
9
+ def pause
10
+ if ENV['DEBUG']
11
+ puts "Hit enter to continue"
12
+ gets
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ module Hearken
2
+ module Indexing
3
+ PATH = File.expand_path('~')+'/.music'
4
+ end
5
+ end
6
+
7
+ require 'hearken/indexing/indexer'