hearken 0.0.1 → 0.0.2

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.
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'