dhun 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,30 @@
1
+ require 'socket'
2
+ require 'json'
3
+ module Dhun
4
+ class DhunClient
5
+ def initialize(options)
6
+ @options = options
7
+ @socket = options[:socket]
8
+ unless DhunClient.is_dhun_server_running?(@socket)
9
+ raise "Dhun server is not running"
10
+ end
11
+ end
12
+
13
+ def send(message)
14
+ u = UNIXSocket.new(@socket)
15
+ u.puts message
16
+ resp = u.read
17
+ u.close
18
+ return resp
19
+ end
20
+
21
+ def self.is_dhun_server_running?(socket)
22
+ begin
23
+ u = UNIXSocket.new(socket)
24
+ return true
25
+ rescue StandardError => ex
26
+ return false
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,43 @@
1
+ require 'json'
2
+ module Dhun
3
+ # Handler for commands sent from client
4
+ module DhunServer
5
+ def post_init
6
+ #puts "-- client connected"
7
+ end
8
+
9
+ def receive_data data
10
+ begin
11
+ puts data
12
+ cmd = JSON.parse(data)
13
+ @command = cmd["command"]
14
+ @arguments = cmd["arguments"]
15
+ handle_client_request
16
+ rescue StandardError => ex
17
+ puts "Error parsing command : #{ex.message}"
18
+ puts ex.backtrace
19
+ ensure
20
+ close_connection true
21
+ end
22
+ end
23
+
24
+ def handle_client_request
25
+ handler = Handler.new
26
+ begin
27
+ if @command.nil?
28
+ raise "Command Not Found"
29
+ end
30
+ result = handler.send(@command,*@arguments)
31
+ puts "Sending #{result}"
32
+ send_data result
33
+ rescue StandardError => ex
34
+ puts "-- error : #{ex.message}"
35
+ puts ex.backtrace
36
+ end
37
+ end
38
+
39
+ def unbind
40
+ #puts "-- client disconnected"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,80 @@
1
+ require 'json'
2
+ module Dhun
3
+ # Handling commands sent by Dhun client
4
+ class Handler
5
+ def stop
6
+ result = Result.new :success, "Dhun is stopping"
7
+ Server.stop
8
+ Player.instance.pause
9
+ return result.to_json
10
+ end
11
+
12
+ def play(*args)
13
+ @player = Player.instance
14
+ q = Query.new(args.join(" "))
15
+ if q.is_valid?
16
+ files = q.execute_spotlight_query
17
+ if files.empty?
18
+ result = Result.new :error, "No Results Found"
19
+ else
20
+ @player.play_files files
21
+ result = Result.new :success, "#{files.size} files queued for playing",
22
+ :files => files
23
+ end
24
+ else
25
+ result = Result.new :error, "Invalid query syntax. See dhun -h for correct syntax"
26
+ end
27
+ result.to_json
28
+ end
29
+
30
+ def enqueue(*args)
31
+ @player = Player.instance
32
+ q = Query.new(args.join(" "))
33
+ if q.is_valid?
34
+ files = q.execute_spotlight_query
35
+ if files.empty?
36
+ result = Result.new :error, "No Results Found"
37
+ else
38
+ @player.enqueue files
39
+ result = Result.new :success, "#{files.size} files queued for playing.",
40
+ :files => files
41
+ end
42
+ else
43
+ result = Result.new :error, "Invalid query syntax. See dhun -h for correct syntax"
44
+ end
45
+ result.to_json
46
+ end
47
+
48
+ def status
49
+ @player = Player.instance
50
+ status_msg = (@player.status == :playing) ? "Dhun is running" : "Dhun is paused"
51
+ now_playing = @player.current
52
+ queue = @player.queue
53
+ result = Result.new :success, status_msg, :now_playing => now_playing, :queue => queue
54
+ result.to_json
55
+ end
56
+
57
+ def next(*args)
58
+ @player = Player.instance
59
+ next_track = @player.next
60
+ result = Result.new :success, (next_track || "No More Tracks")
61
+ return result.to_json
62
+ end
63
+
64
+ def pause
65
+ @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.")
69
+ return result.to_json
70
+ end
71
+
72
+ def resume
73
+ @player = Player.instance
74
+ track = @player.queue.first
75
+ @player.play
76
+ result = Result.new :success, (track ? "Dhun is playing. Next track is #{track}" : "No more tracks in queue.")
77
+ return result.to_json
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,66 @@
1
+ require 'singleton'
2
+ require 'dhun_ext'
3
+ module Dhun
4
+ class Player
5
+ include Singleton
6
+
7
+ attr_reader :queue
8
+ attr_reader :status
9
+ attr_reader :current
10
+
11
+ def initialize
12
+ @queue = []
13
+ end
14
+
15
+ def empty_queue
16
+ stop
17
+ @queue.clear
18
+ end
19
+
20
+
21
+ def play_files(files)
22
+ if files.empty?
23
+ puts "Empty List"
24
+ else
25
+ stop
26
+ empty_queue
27
+ files.each { |f| self.queue.push f }
28
+ play
29
+ end
30
+ end
31
+
32
+ def enqueue(files)
33
+ files.each { |f| self.queue.push f }
34
+ play
35
+ end
36
+
37
+ def play
38
+ return if @status == :playing
39
+ @status = :playing
40
+ @player_thread = Thread.new do
41
+ while @status == :playing and !queue.empty?
42
+ @current = @queue.shift
43
+ puts "playing #{@current}"
44
+ DhunExt.play_file @current
45
+ end
46
+ @status = :stopped
47
+ puts "Player is stopped"
48
+ end
49
+ end
50
+
51
+ def stop
52
+ @status = :stopped
53
+ @current = nil
54
+ DhunExt.pause_play
55
+ # Wait for @player_thread to exit cleanly
56
+ @player_thread.join unless @player_thread.nil?
57
+ end
58
+
59
+ def next
60
+ stop # stops current track
61
+ next_track = @queue.first
62
+ play # start playing with the next track
63
+ return next_track
64
+ end
65
+ end
66
+ end
data/lib/dhun/query.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'dhun_ext'
2
+ module Dhun
3
+ class Query
4
+
5
+ MD_ITEMS = [:kMDItemAlbum, :kMDItemAuthors, :kMDItemComposer, :kMDItemDisplayName, :kMDItemFSName, :kMDItemTitle, :kMDItemMusicalGenre]
6
+ attr_reader :query_string
7
+ attr_reader :spotlight_query
8
+
9
+ def initialize(query_string)
10
+ @query_string = query_string
11
+ parse
12
+ end
13
+
14
+ def parse
15
+ str = MD_ITEMS.collect do |item|
16
+ "#{item.to_s} == '#{@query_string}'wc"
17
+ end.join(" || ")
18
+
19
+ @spotlight_query = "kMDItemContentTypeTree == 'public.audio' && (#{str})"
20
+ #puts @spotlight_query
21
+ @is_valid = true
22
+ end
23
+
24
+ def is_valid?
25
+ @is_valid
26
+ end
27
+
28
+ # Use extension to query spotlight
29
+ def execute_spotlight_query
30
+ return DhunExt.query_spotlight(@spotlight_query)
31
+ end
32
+
33
+ def get_metadata_item(field)
34
+ case field
35
+ when "album" then :kMDItemAlbum
36
+ when "artist" then :kMDItemAuthors
37
+ when "composer" then :kMDItemComposer
38
+ when "title" then :kMDItemTitle
39
+ when "genre" then :kMDItemMusicalGenre
40
+ else "Unknown"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,32 @@
1
+ require 'json'
2
+ module Dhun
3
+ class Result
4
+
5
+ def initialize(result, message, options = {})
6
+ @response = { :result => result,:message => message}
7
+ @response.merge!(options)
8
+ end
9
+
10
+ def success?
11
+ @response[:result].to_sym == :success
12
+ end
13
+
14
+ def error?
15
+ @response[:result].to_sym == :error
16
+ end
17
+
18
+ def [](sym)
19
+ @response[sym] || @response[sym.to_s]
20
+ end
21
+
22
+ def to_json
23
+ @response.to_json
24
+ end
25
+
26
+ def self.from_json_str(resp_json)
27
+ resp = JSON.parse(resp_json)
28
+ Result.new(resp["result"],resp["message"],resp)
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,87 @@
1
+ require 'optparse'
2
+
3
+ module Dhun
4
+
5
+ # Heavily lifted from Thin codebase
6
+ class Runner
7
+ COMMANDS = %w(start query)
8
+ CLIENT_COMMANDS = %w(stop play pause resume next enqueue status)
9
+ # Parsed options
10
+ attr_accessor :options
11
+
12
+ # Name of the command to be runned.
13
+ attr_accessor :command
14
+
15
+ # Arguments to be passed to the command.
16
+ attr_accessor :arguments
17
+
18
+ # Return all available commands
19
+ def self.commands
20
+ commands = COMMANDS + CLIENT_COMMANDS
21
+ commands
22
+ end
23
+
24
+ def initialize(argv)
25
+ @argv = argv
26
+ # Default options values
27
+ @options = {
28
+ :socket => "/tmp/dhun.sock",
29
+ :pid => 'tmp/pids/dhun.pid',
30
+ }
31
+ parse!
32
+ end
33
+
34
+ def parser
35
+ # NOTE: If you add an option here make sure the key in the +options+ hash is the
36
+ # same as the name of the command line option.
37
+ # +option+ keys are used to build the command line to launch other processes,
38
+ # see <tt>lib/dhun/command.rb</tt>.
39
+ @parser ||= OptionParser.new do |opts|
40
+ opts.banner = <<-EOF
41
+ Usage:
42
+ dhun start
43
+ dhun play spirit
44
+ dhun pause
45
+ dhun resume
46
+ dhun enqueue rahman
47
+ dhun status
48
+ dhun stop
49
+ EOF
50
+ opts.separator ""
51
+ opts.separator "Options:"
52
+ opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
53
+ end
54
+ end
55
+
56
+ def parse!
57
+ parser.parse! @argv
58
+ @command = @argv.shift
59
+ @arguments = @argv
60
+ end
61
+
62
+ # Parse the current shell arguments and run the command.
63
+ # Exits on error.
64
+ def run!
65
+ if self.class.commands.include?(@command)
66
+ if CLIENT_COMMANDS.include?(@command)
67
+ unless DhunClient.is_dhun_server_running?(@options[:socket])
68
+ puts "Please start Dhun server first with : dhun start"
69
+ exit 1
70
+ end
71
+ end
72
+ run_command
73
+ elsif @command.nil?
74
+ puts "Command required"
75
+ puts @parser
76
+ exit 1
77
+ else
78
+ abort "Unknown command: #{@command}. Use one of #{self.class.commands.join(', ')}"
79
+ end
80
+ end
81
+
82
+ def run_command
83
+ controller = Controller.new(@options)
84
+ controller.send(@command,*@arguments)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,39 @@
1
+ require 'eventmachine'
2
+ module Dhun
3
+ class Server
4
+
5
+ def initialize(options)
6
+ @options = options
7
+ @socket = options[:socket]
8
+ setup_signals
9
+ end
10
+
11
+ def start
12
+ puts "Starting Dhun"
13
+ at_exit { remove_socket_file }
14
+ EventMachine::run {
15
+ EventMachine::start_server @socket, DhunServer
16
+ }
17
+ end
18
+
19
+ def self.stop
20
+ puts "Stopping Dhun"
21
+ EventMachine.stop if EventMachine.reactor_running?
22
+ exit
23
+ end
24
+
25
+
26
+ protected
27
+ # Register signals:
28
+ # * calls +stop+ to shutdown gracefully.
29
+ def setup_signals
30
+ trap('QUIT') { Server.stop }
31
+ trap('INT') { Server.stop }
32
+ trap('TERM') { Server.stop }
33
+ end
34
+
35
+ def remove_socket_file
36
+ File.delete(@socket) if File.exist?(@socket)
37
+ end
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dhun
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Deepak Jois
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-08 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: deepak.jois@gmail.com
18
+ executables:
19
+ - dhun
20
+ extensions:
21
+ - ext/extconf.rb
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - TODO.md
29
+ - bin/dhun
30
+ - ext/Makefile
31
+ - ext/dhun.h
32
+ - ext/dhun_ext.c
33
+ - ext/extconf.rb
34
+ - ext/player.c
35
+ - ext/query.c
36
+ - lib/dhun.rb
37
+ - lib/dhun/command.rb
38
+ - lib/dhun/controller.rb
39
+ - lib/dhun/dhun_client.rb
40
+ - lib/dhun/dhun_server.rb
41
+ - lib/dhun/handler.rb
42
+ - lib/dhun/player.rb
43
+ - lib/dhun/query.rb
44
+ - lib/dhun/result.rb
45
+ - lib/dhun/runner.rb
46
+ - lib/dhun/server.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/deepakjois/dhun
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project: dhun
71
+ rubygems_version: 1.3.5
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: Minimalist MP3 Player for OS X
75
+ test_files: []
76
+