songbirdsh 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Mark Ryall
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,42 @@
1
+ = Songbird
2
+
3
+ This is a command line shell for queuing music tracks.
4
+
5
+ It relies on you having a songbird database that has all of your tracks imported.
6
+
7
+ This is eventually supposed to be platform independent but at this stage will probably only work 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 songbirdsh
16
+
17
+ == Launch
18
+
19
+ songbirdsh
20
+
21
+ == Commands
22
+
23
+ If readline was correctly installed, you should find that tab completion works for all of the commands.
24
+
25
+ * ? <command> - show commands/help for command
26
+ * help <command> - show commands/help for command
27
+ * + <id> - enqueue track with id
28
+ * next - skip currently playing track
29
+ * start - start music player
30
+ * stop - stop music player
31
+ * reload - reload track information (required for search)
32
+ * search <text> - search in memory list of tracks (see reload)
33
+ * exit - exit
34
+ * quit - exit
35
+
36
+ = Future plans for world domination
37
+
38
+ * Add documentation for all commands
39
+ * Improve search
40
+ * Add music progress indicator
41
+ * Add some color
42
+ * Get working on linux and windows
data/bin/songbirdsh ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.dirname(__FILE__)+'/../lib'
3
+ require 'songbirdsh/cli'
4
+ Songbirdsh::Cli.new.push
data/gemspec ADDED
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = 'songbirdsh'
3
+ spec.version = '0.0.1'
4
+ spec.summary = 'command line jukebox music player'
5
+ spec.description = <<-EOF
6
+ A command line jukebox music player that uses your songbird music player database
7
+ EOF
8
+ spec.authors << 'Mark Ryall'
9
+ spec.email = 'mark@ryall.name'
10
+ spec.homepage = 'http://github.com/markryall/songbirdsh'
11
+ spec.files = Dir['lib/**/*'] + Dir['bin/*'] + ['README.rdoc', 'MIT-LICENSE', 'gemspec']
12
+ spec.executables << 'songbirdsh'
13
+
14
+ spec.add_dependency 'splat', '~>0.1'
15
+ spec.add_dependency 'shell_shock', '~>0.0.5'
16
+ spec.add_dependency 'sequel', '~> 3'
17
+ spec.add_dependency 'splat', '~> 0.1'
18
+ spec.add_dependency 'sqlite3', '~> 1'
19
+
20
+ spec.add_development_dependency 'rake', '~>0.8.7'
21
+ spec.add_development_dependency 'gemesis', '~>0.0.4'
22
+ end
@@ -0,0 +1,32 @@
1
+ require 'shell_shock/context'
2
+
3
+ require 'songbirdsh'
4
+ require 'songbirdsh/player'
5
+ require 'songbirdsh/library'
6
+ require 'songbirdsh/command/enqueue'
7
+ require 'songbirdsh/command/show_properties'
8
+ require 'songbirdsh/command/start'
9
+ require 'songbirdsh/command/reload'
10
+ require 'songbirdsh/command/stop'
11
+ require 'songbirdsh/command/restart'
12
+ require 'songbirdsh/command/search'
13
+
14
+ class Songbirdsh::Cli
15
+ include ShellShock::Context
16
+
17
+ def initialize
18
+ library = Songbirdsh::Library.new
19
+ player = Songbirdsh::Player.new library
20
+ at_exit { player.stop }
21
+ @prompt_text = "sbsh > "
22
+ @commands = {
23
+ 'show' => Songbirdsh::Command::ShowProperties.new(library),
24
+ '+' => Songbirdsh::Command::Enqueue.new(library),
25
+ 'start' => Songbirdsh::Command::Start.new(player),
26
+ 'stop' => Songbirdsh::Command::Stop.new(player),
27
+ 'next' => Songbirdsh::Command::Restart.new(player),
28
+ 'reload' => Songbirdsh::Command::Reload.new(library),
29
+ 'search' => Songbirdsh::Command::Search.new(library)
30
+ }
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ require 'yaml'
2
+
3
+ class Songbirdsh::Command::Enqueue
4
+ def initialize library
5
+ @library = library
6
+ end
7
+
8
+ def execute text
9
+ ids = text.split(/\W/)
10
+ @library.with_db do |db|
11
+ ids.each do |id|
12
+ File.open("#{Time.now.to_i}-#{(rand*1000).to_i}.song", 'w') {|f| f.print db[:media_items][:media_item_id=>id].to_yaml }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ class Songbirdsh::Command::Reload
2
+ def initialize library
3
+ @library = library
4
+ end
5
+
6
+ def execute ignored=nil
7
+ @library.reload
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Songbirdsh::Command::Restart
2
+ def initialize player
3
+ @player = player
4
+ end
5
+
6
+ def execute ignored=nil
7
+ @player.restart
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ class Songbirdsh::Command::Search
2
+ def initialize library
3
+ @library = library
4
+ end
5
+
6
+ def execute text
7
+ @library.tracks.each do |track|
8
+ puts track[:display] if track[:search_string].include? text
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'pp'
2
+
3
+ class Songbirdsh::Command::ShowProperties
4
+ def initialize library
5
+ @library = library
6
+ end
7
+
8
+ def execute id
9
+ @library.with_track(id) {|track| pp track }
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ class Songbirdsh::Command::Start
2
+ def initialize player
3
+ @player = player
4
+ end
5
+
6
+ def execute ignored=nil
7
+ @player.start
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Songbirdsh::Command::Stop
2
+ def initialize player
3
+ @player = player
4
+ end
5
+
6
+ def execute ignored=nil
7
+ @player.stop
8
+ end
9
+ end
@@ -0,0 +1,66 @@
1
+ class Songbirdsh::Library
2
+ attr_reader :tracks
3
+
4
+ def initialize
5
+ home = File.expand_path '~'
6
+ debug "Home Directory is \"#{home}\""
7
+ songbird_home = "#{home}/Library/Application Support/Songbird2"
8
+ debug "Songbird home is \"#{songbird_home}\""
9
+ profiles = "#{songbird_home}/Profiles"
10
+ profile = `ls \"#{profiles}\"`.chomp
11
+ debug "found profile \"#{profile}\""
12
+ @db_path = "#{profiles}/#{profile}/db/main@library.songbirdnest.com.db"
13
+ debug "using db \"#{@db_path}\""
14
+ end
15
+
16
+ def with_db
17
+ db = Sequel.sqlite @db_path
18
+ begin
19
+ val = yield db
20
+ ensure
21
+ db.disconnect
22
+ end
23
+ val
24
+ end
25
+
26
+ def with_track id
27
+ with_db do |db|
28
+ db[:resource_properties].filter(:media_item_id=>id).each do |row|
29
+ yield to_track(row) if row[:property_id] == 3
30
+ end
31
+ end
32
+ end
33
+
34
+ def reload
35
+ s = Time.now.to_i
36
+ @tracks = []
37
+ with_db do |db|
38
+ db[:resource_properties].each do |row|
39
+ @tracks << to_track(row) if row[:property_id] == 3
40
+ end
41
+ end
42
+ puts "reloaded db with #{@tracks.size} tracks in #{Time.now.to_i-s} seconds"
43
+ end
44
+ private
45
+ def debug message
46
+ if ENV['DEBUG']
47
+ puts message
48
+ gets
49
+ end
50
+ end
51
+
52
+ def to_track row
53
+ rest = row[:obj_secondary_sortable].split("\037")
54
+ track = {
55
+ :id => row[:media_item_id],
56
+ :artist => row[:obj_sortable],
57
+ :album => rest.shift,
58
+ :disc => rest.shift.to_i,
59
+ :number => rest.shift.to_i,
60
+ :track => rest.shift
61
+ }
62
+ track[:search_string] = track[:artist]+track[:album]+track[:track]
63
+ track[:display] = "#{track[:id]}: #{track[:artist]} #{track[:album]} #{track[:number]} #{track[:track]}"
64
+ track
65
+ end
66
+ end
@@ -0,0 +1,52 @@
1
+ require 'songbirdsh/queue'
2
+ require 'cgi'
3
+ require 'yaml'
4
+ require 'fileutils'
5
+ require 'splat'
6
+
7
+ class Songbirdsh::Player
8
+ include Songbirdsh::Queue
9
+
10
+ def initialize library
11
+ @library=library
12
+ end
13
+
14
+ def start
15
+ if @pid
16
+ puts "Already started (pid #{@pid})"
17
+ return
18
+ end
19
+ @pid = fork do
20
+ player_pid = nil
21
+ Signal.trap('TERM') do
22
+ Process.kill 'TERM', player_pid if player_pid
23
+ exit
24
+ end
25
+ total_tracks = @library.with_db {|db| db[:media_items].count }
26
+ loop do
27
+ id = dequeue || (rand * total_tracks).to_i
28
+ row = @library.with_db {|db| db[:media_items][:media_item_id=>id] }
29
+ if row
30
+ path = CGI.unescape(row[:content_url].slice(7..-1)) if row[:content_url] =~ /^file/
31
+ if path
32
+ puts "playing #{id}: \"#{path}\""
33
+ player_pid = path.to_player
34
+ Process.wait player_pid
35
+ end
36
+ end
37
+ end
38
+ end
39
+ puts "Started (pid #{@pid})"
40
+ end
41
+
42
+ def stop
43
+ return unless @pid
44
+ Process.kill 'TERM', @pid
45
+ @pid = nil
46
+ end
47
+
48
+ def restart
49
+ stop
50
+ start
51
+ end
52
+ end
@@ -0,0 +1,16 @@
1
+ module Songbirdsh::Queue
2
+ def enqueue id
3
+ with_db do |db|
4
+ File.open("#{Time.now.to_i}-#{(rand*1000).to_i}.song", 'w') {|f| f.print db[:media_items][:media_item_id=>id].to_yaml }
5
+ end
6
+ end
7
+
8
+ def dequeue
9
+ file = Dir.glob('*.song').sort.first
10
+ return nil unless file
11
+ hash = YAML.load(File.read(file))
12
+ id = hash[:media_item_id] if hash
13
+ FileUtils.rm file
14
+ id
15
+ end
16
+ end
data/lib/songbirdsh.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'sequel'
3
+
4
+ module Songbirdsh
5
+ module Command
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,179 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: songbirdsh
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Mark Ryall
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-01-30 00:00:00 +10:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: splat
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ - 1
31
+ version: "0.1"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: shell_shock
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 0
44
+ - 0
45
+ - 5
46
+ version: 0.0.5
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: sequel
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 3
59
+ version: "3"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: splat
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ - 1
73
+ version: "0.1"
74
+ type: :runtime
75
+ version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: sqlite3
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ~>
83
+ - !ruby/object:Gem::Version
84
+ segments:
85
+ - 1
86
+ version: "1"
87
+ type: :runtime
88
+ version_requirements: *id005
89
+ - !ruby/object:Gem::Dependency
90
+ name: rake
91
+ prerelease: false
92
+ requirement: &id006 !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ~>
96
+ - !ruby/object:Gem::Version
97
+ segments:
98
+ - 0
99
+ - 8
100
+ - 7
101
+ version: 0.8.7
102
+ type: :development
103
+ version_requirements: *id006
104
+ - !ruby/object:Gem::Dependency
105
+ name: gemesis
106
+ prerelease: false
107
+ requirement: &id007 !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ~>
111
+ - !ruby/object:Gem::Version
112
+ segments:
113
+ - 0
114
+ - 0
115
+ - 4
116
+ version: 0.0.4
117
+ type: :development
118
+ version_requirements: *id007
119
+ description: |
120
+ A command line jukebox music player that uses your songbird music player database
121
+
122
+ email: mark@ryall.name
123
+ executables:
124
+ - songbirdsh
125
+ extensions: []
126
+
127
+ extra_rdoc_files: []
128
+
129
+ files:
130
+ - lib/songbirdsh/cli.rb
131
+ - lib/songbirdsh/command/enqueue.rb
132
+ - lib/songbirdsh/command/reload.rb
133
+ - lib/songbirdsh/command/restart.rb
134
+ - lib/songbirdsh/command/search.rb
135
+ - lib/songbirdsh/command/show_properties.rb
136
+ - lib/songbirdsh/command/start.rb
137
+ - lib/songbirdsh/command/stop.rb
138
+ - lib/songbirdsh/library.rb
139
+ - lib/songbirdsh/player.rb
140
+ - lib/songbirdsh/queue.rb
141
+ - lib/songbirdsh.rb
142
+ - bin/songbirdsh
143
+ - README.rdoc
144
+ - MIT-LICENSE
145
+ - gemspec
146
+ has_rdoc: true
147
+ homepage: http://github.com/markryall/songbirdsh
148
+ licenses: []
149
+
150
+ post_install_message:
151
+ rdoc_options: []
152
+
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ segments:
161
+ - 0
162
+ version: "0"
163
+ required_rubygems_version: !ruby/object:Gem::Requirement
164
+ none: false
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ segments:
169
+ - 0
170
+ version: "0"
171
+ requirements: []
172
+
173
+ rubyforge_project:
174
+ rubygems_version: 1.3.7
175
+ signing_key:
176
+ specification_version: 3
177
+ summary: command line jukebox music player
178
+ test_files: []
179
+