dhun 0.5.3 → 0.5.4

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.
data/FIX.md ADDED
@@ -0,0 +1,5 @@
1
+ ## Fixes/Improvements required
2
+
3
+ * Better Argument Handling. For e.g. `dhun play` w/o any arguments still works
4
+
5
+ * Maintaining history of all files played in the session
data/README.md CHANGED
@@ -26,9 +26,8 @@ the `gem` command to compile the native extensions.
26
26
  $ dhun start
27
27
  Starting Dhun
28
28
 
29
- Currently it runs in the foreground and displays a lot of random output. There
30
- are plans to daemonize the service once the codebase has stabilized a bit, and
31
- I have implemented some logging capabilities.
29
+ Pass the `-d` option to run the server as a daemon in the background. See
30
+ `dhun -h` for more options.
32
31
 
33
32
  ### Playing Files
34
33
 
@@ -49,7 +48,9 @@ You can also query the Spotlight database before playing the files.
49
48
  /Users/deepak/Dropbox/shared/music/Here Comes/01 - 40 Day Dream.mp3
50
49
  /Users/deepak/Dropbox/shared/music/Here Comes/03 - Carries On.mp3
51
50
 
52
- And then, when you are ready to play the files.
51
+ And then, when you are ready to play the files. Note that the `play` command
52
+ will remove anything that may be already there on your queue. To add files,
53
+ use `enqueue`.
53
54
 
54
55
  $ dhun play here
55
56
  9 files queued for playing
@@ -70,14 +71,12 @@ More advanced querying support is coming soon.
70
71
  Pausing playback.
71
72
 
72
73
  $ dhun pause
73
- Dhun is paused. Next track is /Users/deepak/Music/iTunes/iTunes Media/Music/Edward Sharpe & The Magnetic Zeros/Here Comes/02 Janglin.mp3
74
+ Dhun is paused at /Users/deepak/Dropbox/shared/music/Coke Studio/Jo-Meray.mp3
74
75
 
75
- Resuming playback. Currently, playback resumes from the next track in the
76
- queue. Ability to pause and play from the middle of a track is a bit tricky to
77
- implement, so it will be there in a future version.
76
+ Resuming playback.
78
77
 
79
78
  $ dhun resume
80
- Dhun is playing. Next track is /Users/deepak/Music/iTunes/iTunes Media/Music/Edward Sharpe & The Magnetic Zeros/Here Comes/02 Janglin.mp3
79
+ Dhun is playing /Users/deepak/Dropbox/shared/music/Coke Studio/Jo-Meray.mp3
81
80
 
82
81
  Skipping to next file
83
82
 
@@ -115,12 +114,9 @@ This will exit the process.
115
114
 
116
115
  These features are planned in the next few releases
117
116
 
118
- * Option to run Dhun server as a daemon
119
- * Logging
120
117
  * Playing previous song, using something like `dhun prev`
121
118
  * Skipping ahead by more than one file, like `dhun next 2` or `dhun prev 2`
122
119
  * Advanced querying support with filters, like `dhun play "artist:Rahman"`
123
- * Ability to pause and play in the middle of music files.
124
120
 
125
121
  And someday..
126
122
 
@@ -1,16 +1,18 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'dhun'
3
- s.version = '0.5.3'
3
+ s.version = '0.5.4'
4
4
  s.summary = "Minimalist music player for OS X"
5
- s.date = '2009-12-08'
5
+ s.date = '2009-12-10'
6
6
  s.email = 'deepak.jois@gmail.com'
7
7
  s.homepage = 'http://github.com/deepakjois/dhun'
8
8
  s.has_rdoc = false
9
9
  s.add_dependency('eventmachine', '>=0.12.10')
10
10
  s.add_dependency('json_pure', '>=1.2.0')
11
+ s.add_dependency('daemons', '>=1.0.10')
11
12
  s.authors = ["Deepak Jois"]
12
13
  # = MANIFEST =
13
14
  s.files = %w[
15
+ FIX.md
14
16
  LICENSE.txt
15
17
  README.md
16
18
  Rakefile
@@ -29,6 +31,7 @@ Gem::Specification.new do |s|
29
31
  lib/dhun/dhun_client.rb
30
32
  lib/dhun/dhun_server.rb
31
33
  lib/dhun/handler.rb
34
+ lib/dhun/logger.rb
32
35
  lib/dhun/player.rb
33
36
  lib/dhun/query.rb
34
37
  lib/dhun/result.rb
@@ -18,17 +18,22 @@ static pthread_t posixThreadID;
18
18
  void Init_dhunruby();
19
19
 
20
20
  static VALUE method_play_file(VALUE self, VALUE fileName);
21
- static VALUE method_pause_play(VALUE self);
21
+ static VALUE method_stop_play(VALUE self);
22
22
  static VALUE method_query_spotlight(VALUE self, VALUE query);
23
23
  static VALUE method_is_playing(VALUE self);
24
+ static VALUE method_pause_play(VALUE self);
25
+ static VALUE method_resume_play(VALUE self);
24
26
 
25
27
  // The initialization method for this module
26
28
  void Init_dhun_ext() {
27
29
  DhunExt = rb_define_class("DhunExt", rb_cObject);
28
30
  rb_define_singleton_method(DhunExt, "play_file", method_play_file, 1);
29
31
  rb_define_singleton_method(DhunExt, "query_spotlight", method_query_spotlight, 1);
30
- rb_define_singleton_method(DhunExt, "pause_play", method_pause_play, 0);
32
+ rb_define_singleton_method(DhunExt, "stop", method_stop_play, 0);
31
33
  rb_define_singleton_method(DhunExt, "is_playing?", method_is_playing, 0);
34
+
35
+ rb_define_singleton_method(DhunExt, "pause", method_pause_play,0);
36
+ rb_define_singleton_method(DhunExt, "resume", method_resume_play,0);
32
37
  }
33
38
 
34
39
  static VALUE method_play_file(VALUE self, VALUE filename) {
@@ -51,6 +56,18 @@ static VALUE method_play_file(VALUE self, VALUE filename) {
51
56
  }
52
57
 
53
58
  static VALUE method_pause_play(VALUE self) {
59
+ if (aqData.mIsRunning == true)
60
+ AudioQueuePause(aqData.mQueue);
61
+ return Qnil;
62
+ }
63
+
64
+ static VALUE method_resume_play(VALUE self) {
65
+ if (aqData.mIsRunning == true)
66
+ AudioQueueStart(aqData.mQueue,NULL);
67
+ return Qnil;
68
+ }
69
+
70
+ static VALUE method_stop_play(VALUE self) {
54
71
  aqData.mIsRunning = false;
55
72
  return Qnil;
56
73
  }
@@ -1,6 +1,6 @@
1
1
  module Dhun
2
- VERSION = '0.5.3'
3
-
2
+ VERSION = '0.5.4'
3
+
4
4
  autoload :Runner, 'dhun/runner'
5
5
  autoload :Controller, 'dhun/controller'
6
6
  autoload :Server, 'dhun/server'
@@ -10,4 +10,5 @@ module Dhun
10
10
  autoload :Player, 'dhun/player'
11
11
  autoload :Query, 'dhun/query'
12
12
  autoload :Result, 'dhun/result'
13
+ autoload :Logger, 'dhun/logger'
13
14
  end
@@ -2,10 +2,11 @@ require 'json'
2
2
  module Dhun
3
3
  class Controller
4
4
 
5
- attr_accessor :options
5
+ attr_accessor :options,:logger
6
6
 
7
7
  def initialize(options)
8
8
  @options = options
9
+ @logger = Logger.instance
9
10
  end
10
11
 
11
12
  def start
@@ -90,6 +91,19 @@ module Dhun
90
91
  puts resp[:message] if resp
91
92
  end
92
93
 
94
+ def shuffle
95
+ resp = get_json_response("shuffle")
96
+ return unless resp
97
+ # Process response
98
+ case resp.success?
99
+ when true
100
+ puts resp[:message]
101
+ # Print list of files
102
+ print_list resp[:queue]
103
+ else
104
+ puts resp[:message]
105
+ end
106
+ end
93
107
 
94
108
  protected
95
109
  def send_command(command,arguments=[])
@@ -104,7 +118,7 @@ module Dhun
104
118
  return Result.from_json_str(resp)
105
119
  rescue
106
120
  puts "Invalid Response From Server"
107
- puts $!
121
+ logger.debug $!
108
122
  return nil
109
123
  end
110
124
  end
@@ -7,32 +7,34 @@ module Dhun
7
7
  end
8
8
 
9
9
  def receive_data data
10
+ @logger ||= Logger.instance
10
11
  begin
11
- puts data
12
+ @logger.debug data
12
13
  cmd = JSON.parse(data)
13
14
  @command = cmd["command"]
14
15
  @arguments = cmd["arguments"]
15
16
  handle_client_request
16
17
  rescue StandardError => ex
17
- puts "Error parsing command : #{ex.message}"
18
- puts ex.backtrace
18
+ @logger.log "Error parsing command : #{ex.message}"
19
+ @logger.log ex.backtrace
19
20
  ensure
20
21
  close_connection true
21
22
  end
22
23
  end
23
24
 
24
25
  def handle_client_request
26
+ @logger ||= Logger.instance
25
27
  handler = Handler.new
26
28
  begin
27
29
  if @command.nil?
28
30
  raise "Command Not Found"
29
31
  end
30
32
  result = handler.send(@command,*@arguments)
31
- puts "Sending #{result}"
33
+ @logger.debug "Sending #{result}"
32
34
  send_data result
33
35
  rescue StandardError => ex
34
- puts "-- error : #{ex.message}"
35
- puts ex.backtrace
36
+ @logger.log "-- error : #{ex.message}"
37
+ @logger.log ex.backtrace
36
38
  end
37
39
  end
38
40
 
@@ -47,7 +47,11 @@ module Dhun
47
47
 
48
48
  def status
49
49
  @player = Player.instance
50
- status_msg = (@player.status == :playing) ? "Dhun is running" : "Dhun is paused"
50
+ status_msg = case @player.status
51
+ when :playing then "Dhun is running"
52
+ when :paused then "Dhun is paused"
53
+ when :stopped then "Dhun is stopped"
54
+ end
51
55
  now_playing = @player.current
52
56
  queue = @player.queue
53
57
  result = Result.new :success, status_msg, :now_playing => now_playing, :queue => queue
@@ -63,17 +67,36 @@ module Dhun
63
67
 
64
68
  def pause
65
69
  @player = Player.instance
66
- @player.stop
67
- track = @player.queue.first
68
- result = Result.new :success, "Dhun is paused. " + (track ? "Next track is #{track}" : "No more tracks in queue.")
70
+ @player.pause
71
+ case @player.status
72
+ when :paused
73
+ result = Result.new :success, "Dhun is paused at #{@player.current}"
74
+ when :stopped
75
+ result = Result.new :error, "Dhun is already stopped"
76
+ end
69
77
  return result.to_json
70
78
  end
71
79
 
72
80
  def resume
73
81
  @player = Player.instance
74
- track = @player.queue.first
75
- @player.play
76
- result = Result.new :success, (track ? "Dhun is playing #{track}" : "No more tracks in queue.")
82
+ @player.resume
83
+ case @player.status
84
+ when :playing
85
+ result = Result.new :success, "Dhun is playing #{@player.current}"
86
+ when :stopped
87
+ result = Result.new :error, "Dhun is already stopped"
88
+ end
89
+ return result.to_json
90
+ end
91
+
92
+ def shuffle
93
+ @player = Player.instance
94
+ @player.shuffle
95
+ if @player.queue.empty?
96
+ result = Result.new :error, "Queue is empty"
97
+ else
98
+ result = Result.new :success, "Queue is shuffled", :queue => @player.queue
99
+ end
77
100
  return result.to_json
78
101
  end
79
102
  end
@@ -0,0 +1,25 @@
1
+ require 'singleton'
2
+ module Dhun
3
+ class Logger
4
+ include Singleton
5
+
6
+ attr_accessor :log_level,:file
7
+
8
+ def initialize
9
+ @file = STDOUT
10
+ end
11
+
12
+ def file=(f)
13
+ @file = File.open(f,'w')
14
+ end
15
+
16
+ def log(msg)
17
+ @file.puts "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} #{msg}" unless self.log_level == :silent
18
+ @file.flush
19
+ end
20
+
21
+ def debug(msg)
22
+ log(msg) if self.log_level == :debug
23
+ end
24
+ end
25
+ end
@@ -8,8 +8,12 @@ module Dhun
8
8
  attr_reader :status
9
9
  attr_reader :current
10
10
 
11
+ attr_reader :logger
12
+
11
13
  def initialize
12
14
  @queue = []
15
+ @logger = Logger.instance
16
+ @status = :stopped
13
17
  end
14
18
 
15
19
  def empty_queue
@@ -20,7 +24,7 @@ module Dhun
20
24
 
21
25
  def play_files(files)
22
26
  if files.empty?
23
- puts "Empty List"
27
+ logger.log "Empty Queue"
24
28
  else
25
29
  stop
26
30
  empty_queue
@@ -35,23 +39,38 @@ module Dhun
35
39
  end
36
40
 
37
41
  def play
38
- return if @status == :playing
42
+ return unless self.status == :stopped
39
43
  @status = :playing
40
44
  @player_thread = Thread.new do
41
45
  while @status == :playing and !queue.empty?
42
46
  @current = @queue.shift
43
- puts "playing #{@current}"
47
+ logger.log "Playing #{@current}"
44
48
  DhunExt.play_file @current
45
49
  end
46
50
  @status = :stopped
47
- puts "Player is stopped"
51
+ logger.log "Finished playing #{@current}"
52
+ @current = nil
53
+ end
54
+ end
55
+
56
+ def pause
57
+ if @status == :playing
58
+ @status = :paused
59
+ DhunExt.pause
60
+ end
61
+ end
62
+
63
+ def resume
64
+ if @status == :paused
65
+ @status = :playing
66
+ DhunExt.resume
48
67
  end
49
68
  end
50
69
 
51
70
  def stop
52
71
  @status = :stopped
53
72
  @current = nil
54
- DhunExt.pause_play
73
+ DhunExt.stop
55
74
  # Wait for @player_thread to exit cleanly
56
75
  @player_thread.join unless @player_thread.nil?
57
76
  end
@@ -62,5 +81,12 @@ module Dhun
62
81
  play # start playing with the next track
63
82
  return next_track
64
83
  end
84
+
85
+ def shuffle
86
+ return if @queue.empty?
87
+ s = @queue.size
88
+ s.downto(1) { |n| @queue.push @queue.delete_at(rand(n)) }
89
+ logger.debug @queue
90
+ end
65
91
  end
66
92
  end
@@ -5,16 +5,16 @@ module Dhun
5
5
  # Heavily lifted from Thin codebase
6
6
  class Runner
7
7
  COMMANDS = %w(start query)
8
- CLIENT_COMMANDS = %w(stop play pause resume next enqueue status)
8
+ CLIENT_COMMANDS = %w(stop play pause resume next enqueue status shuffle)
9
9
  # Parsed options
10
10
  attr_accessor :options
11
-
11
+
12
12
  # Name of the command to be runned.
13
13
  attr_accessor :command
14
-
14
+
15
15
  # Arguments to be passed to the command.
16
16
  attr_accessor :arguments
17
-
17
+
18
18
  # Return all available commands
19
19
  def self.commands
20
20
  commands = COMMANDS + CLIENT_COMMANDS
@@ -26,7 +26,7 @@ module Dhun
26
26
  # Default options values
27
27
  @options = {
28
28
  :socket => "/tmp/dhun.sock",
29
- :pid => 'tmp/pids/dhun.pid',
29
+ :default_log => "/tmp/dhun.log"
30
30
  }
31
31
  parse!
32
32
  end
@@ -37,7 +37,7 @@ module Dhun
37
37
  # +option+ keys are used to build the command line to launch other processes,
38
38
  # see <tt>lib/dhun/command.rb</tt>.
39
39
  @parser ||= OptionParser.new do |opts|
40
- opts.banner = <<-EOF
40
+ opts.banner = <<-EOF
41
41
  Usage:
42
42
  dhun start
43
43
  dhun play spirit
@@ -45,16 +45,25 @@ Usage:
45
45
  dhun resume
46
46
  dhun enqueue rahman
47
47
  dhun status
48
+ dhun shuffle
48
49
  dhun stop
49
-
50
+
50
51
  For more details see README at http://github.com/deepakjois/dhun
51
52
  EOF
52
53
  opts.separator ""
53
- opts.separator "Options:"
54
+ opts.on("-d", "--daemonize", "Run daemonized in the background") { @options[:daemonize] = true }
55
+ opts.on("-l", "--log FILE", "File to redirect output " +
56
+ "(default: #{@options[:default_log]})") { |file| @options[:log] = file }
57
+
58
+ opts.separator "Common options:"
54
59
  opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
60
+ opts.on_tail("-D", "--debug", "Set debugging on") { @options[:debug] = true }
61
+ opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
62
+ opts.on_tail('-v', '--version', "Show version") { puts "Dhun " + Dhun::VERSION; exit }
63
+
55
64
  end
56
65
  end
57
-
66
+
58
67
  def parse!
59
68
  parser.parse! @argv
60
69
  @command = @argv.shift
@@ -64,6 +73,8 @@ EOF
64
73
  # Parse the current shell arguments and run the command.
65
74
  # Exits on error.
66
75
  def run!
76
+ logger = Logger.instance
77
+ logger.log_level = :debug if @options[:debug]
67
78
  if self.class.commands.include?(@command)
68
79
  if CLIENT_COMMANDS.include?(@command)
69
80
  unless DhunClient.is_dhun_server_running?(@options[:socket])
@@ -75,7 +86,7 @@ EOF
75
86
  elsif @command.nil?
76
87
  puts "Command required"
77
88
  puts @parser
78
- exit 1
89
+ exit 1
79
90
  else
80
91
  abort "Unknown command: #{@command}. Use one of #{self.class.commands.join(', ')}"
81
92
  end
@@ -1,15 +1,27 @@
1
1
  require 'eventmachine'
2
+ require 'daemons'
2
3
  module Dhun
3
4
  class Server
4
5
 
6
+ attr_reader :logger
7
+
5
8
  def initialize(options)
6
9
  @options = options
7
10
  @socket = options[:socket]
11
+ @logger = Logger.instance
8
12
  setup_signals
9
13
  end
10
14
 
11
15
  def start
12
- puts "Starting Dhun"
16
+ if @options[:daemonize]
17
+ logger.log "Starting Dhun"
18
+ exit if fork
19
+ Process.setsid
20
+ log_file = @options[:log] || @options[:default_log]
21
+ exec("#{ENV['_']} start -l #{log_file} #{@options[:debug] ? "-D" : ""}")
22
+ end
23
+ logger.file = @options[:log] if @options[:log]
24
+ logger.log "Starting Dhun"
13
25
  at_exit { remove_socket_file }
14
26
  EventMachine::run {
15
27
  EventMachine::start_server @socket, DhunServer
@@ -17,12 +29,11 @@ module Dhun
17
29
  end
18
30
 
19
31
  def self.stop
20
- puts "Stopping Dhun"
32
+ Logger.instance.log "Stopping Dhun"
21
33
  EventMachine.stop if EventMachine.reactor_running?
22
34
  exit
23
35
  end
24
36
 
25
-
26
37
  protected
27
38
  # Register signals:
28
39
  # * calls +stop+ to shutdown gracefully.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dhun
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Deepak Jois
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-08 00:00:00 -08:00
12
+ date: 2009-12-10 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -32,6 +32,16 @@ dependencies:
32
32
  - !ruby/object:Gem::Version
33
33
  version: 1.2.0
34
34
  version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: daemons
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.10
44
+ version:
35
45
  description:
36
46
  email: deepak.jois@gmail.com
37
47
  executables:
@@ -41,6 +51,7 @@ extensions:
41
51
  extra_rdoc_files: []
42
52
 
43
53
  files:
54
+ - FIX.md
44
55
  - LICENSE.txt
45
56
  - README.md
46
57
  - Rakefile
@@ -59,6 +70,7 @@ files:
59
70
  - lib/dhun/dhun_client.rb
60
71
  - lib/dhun/dhun_server.rb
61
72
  - lib/dhun/handler.rb
73
+ - lib/dhun/logger.rb
62
74
  - lib/dhun/player.rb
63
75
  - lib/dhun/query.rb
64
76
  - lib/dhun/result.rb