dhun 0.5.0

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.
@@ -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
+