play 0.0.1

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 (54) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +87 -0
  3. data/README.md +139 -0
  4. data/Rakefile +185 -0
  5. data/bin/play +49 -0
  6. data/config.ru +10 -0
  7. data/db/migrate/01_create_schema.rb +55 -0
  8. data/lib/play.rb +73 -0
  9. data/lib/play/album.rb +6 -0
  10. data/lib/play/app.rb +118 -0
  11. data/lib/play/app/api.rb +110 -0
  12. data/lib/play/artist.rb +21 -0
  13. data/lib/play/client.rb +67 -0
  14. data/lib/play/core_ext/hash.rb +6 -0
  15. data/lib/play/history.rb +7 -0
  16. data/lib/play/library.rb +58 -0
  17. data/lib/play/office.rb +34 -0
  18. data/lib/play/song.rb +97 -0
  19. data/lib/play/templates/album_songs.mustache +5 -0
  20. data/lib/play/templates/artist_songs.mustache +5 -0
  21. data/lib/play/templates/index.mustache +18 -0
  22. data/lib/play/templates/layout.mustache +23 -0
  23. data/lib/play/templates/now_playing.mustache +5 -0
  24. data/lib/play/templates/play_history.mustache +3 -0
  25. data/lib/play/templates/profile.mustache +24 -0
  26. data/lib/play/templates/search.mustache +3 -0
  27. data/lib/play/templates/show_song.mustache +9 -0
  28. data/lib/play/templates/song.mustache +21 -0
  29. data/lib/play/user.rb +59 -0
  30. data/lib/play/views/album_songs.rb +9 -0
  31. data/lib/play/views/artist_songs.rb +9 -0
  32. data/lib/play/views/index.rb +9 -0
  33. data/lib/play/views/layout.rb +6 -0
  34. data/lib/play/views/now_playing.rb +17 -0
  35. data/lib/play/views/play_history.rb +9 -0
  36. data/lib/play/views/profile.rb +9 -0
  37. data/lib/play/views/search.rb +9 -0
  38. data/lib/play/views/show_song.rb +19 -0
  39. data/lib/play/vote.rb +7 -0
  40. data/play.gemspec +129 -0
  41. data/play.yml.example +22 -0
  42. data/public/css/base.css +129 -0
  43. data/test/helper.rb +33 -0
  44. data/test/spec/mini.rb +24 -0
  45. data/test/test_api.rb +118 -0
  46. data/test/test_app.rb +57 -0
  47. data/test/test_artist.rb +15 -0
  48. data/test/test_client.rb +11 -0
  49. data/test/test_library.rb +19 -0
  50. data/test/test_office.rb +26 -0
  51. data/test/test_play.rb +17 -0
  52. data/test/test_song.rb +78 -0
  53. data/test/test_user.rb +21 -0
  54. metadata +299 -0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,87 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ play (0.0.1)
5
+ SystemTimer
6
+ activerecord
7
+ mustache
8
+ mysql2
9
+ oa-oauth
10
+ rack (~> 1.2.2)
11
+ ruby-audioinfo
12
+ sinatra
13
+ sqlite3
14
+ yajl-ruby
15
+
16
+ GEM
17
+ remote: http://rubygems.org/
18
+ specs:
19
+ SystemTimer (1.2.3)
20
+ activemodel (3.0.7)
21
+ activesupport (= 3.0.7)
22
+ builder (~> 2.1.2)
23
+ i18n (~> 0.5.0)
24
+ activerecord (3.0.7)
25
+ activemodel (= 3.0.7)
26
+ activesupport (= 3.0.7)
27
+ arel (~> 2.0.2)
28
+ tzinfo (~> 0.3.23)
29
+ activesupport (3.0.7)
30
+ addressable (2.2.5)
31
+ apetag (1.1.2)
32
+ cicphash (>= 1.0.0)
33
+ arel (2.0.9)
34
+ builder (2.1.2)
35
+ cicphash (1.0.0)
36
+ faraday (0.6.1)
37
+ addressable (~> 2.2.4)
38
+ multipart-post (~> 1.1.0)
39
+ rack (< 2, >= 1.1.0)
40
+ flacinfo-rb (0.4)
41
+ i18n (0.5.0)
42
+ mocha (0.9.12)
43
+ mp4info (1.7.3)
44
+ multi_json (1.0.1)
45
+ multipart-post (1.1.0)
46
+ mustache (0.99.3)
47
+ mysql2 (0.2.7)
48
+ nokogiri (1.4.4)
49
+ oa-core (0.2.4)
50
+ oa-oauth (0.2.4)
51
+ faraday (~> 0.6.1)
52
+ multi_json (>= 0.0.5)
53
+ nokogiri (~> 1.4.2)
54
+ oa-core (= 0.2.4)
55
+ oauth (~> 0.4.0)
56
+ oauth2 (~> 0.4.1)
57
+ oauth (0.4.4)
58
+ oauth2 (0.4.1)
59
+ faraday (~> 0.6.1)
60
+ multi_json (>= 0.0.5)
61
+ rack (1.2.2)
62
+ ruby-audioinfo (0.1.7)
63
+ apetag (= 1.1.2)
64
+ flacinfo-rb (>= 0.4)
65
+ mp4info (>= 1.7.3)
66
+ ruby-mp3info (>= 0.6.3)
67
+ ruby-ogginfo (>= 0.3.1)
68
+ wmainfo-rb (>= 0.5)
69
+ ruby-mp3info (0.6.13)
70
+ ruby-ogginfo (0.6.5)
71
+ running_man (0.2.1)
72
+ sinatra (1.2.6)
73
+ rack (~> 1.1)
74
+ tilt (< 2.0, >= 1.2.2)
75
+ sqlite3 (1.3.3)
76
+ tilt (1.3)
77
+ tzinfo (0.3.27)
78
+ wmainfo-rb (0.6)
79
+ yajl-ruby (0.8.2)
80
+
81
+ PLATFORMS
82
+ ruby
83
+
84
+ DEPENDENCIES
85
+ mocha
86
+ play!
87
+ running_man
data/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # Play
2
+ Play is your company's dj.
3
+
4
+ ## Background
5
+
6
+ We want to play music at our office. Everyone has their own library on their
7
+ own machines, and everyone except for me plays shitty music. Play is designed
8
+ to make office music more palatable.
9
+
10
+ Play is **api-driven** and **web-driven**. All music is dropped on a central
11
+ Mac system. Once it's available to Play, users can control what's being played.
12
+ Users can either use a nice web view or the API, which lends itself for use on
13
+ the command line or through Campfire.
14
+
15
+ Play will play all the songs that are added to its Queue. Play will play the
16
+ crap out of that Queue. And you know what? If there's nothing left in the
17
+ Queue, Play will figure out who's in the office and play something that they'll
18
+ like.
19
+
20
+ No shit.
21
+
22
+ ## Install
23
+
24
+ The underlying tech of Play uses `afplay` to control music (for now), so you'll
25
+ need a Mac. `afplay` is just a simple command-line wrapper to OS X's underlying
26
+ music libraries, so it should come preinstalled with your Mac, and it should
27
+ play anything iTunes can play.
28
+
29
+ Play also expects MySQL to be installed.
30
+
31
+ ### Install the gem
32
+
33
+ Play itself is installed with a gem.
34
+
35
+ gem install play
36
+
37
+ ### Fill out ~/.play.yml
38
+
39
+ You'll need to set up your configuration values, which we store in
40
+ `~/.play.yml`. You can view the [example file on
41
+ GitHub](https://github.com/holman/play/blob/master/play.yml.example).
42
+
43
+ ### Set up your database
44
+
45
+ This is a bit of a pain that we'll correct eventually. For now, create your
46
+ MySQL database by hand. We expect the database to be called `play`, but it's
47
+ really pulled from whatever you have in `~/.play.yml`. When that's set up, run:
48
+
49
+ bin/play --migrate
50
+
51
+ ### Set up your GitHub application
52
+
53
+ Next, go to GitHub and [register a new OAuth
54
+ application](https://github.com/account/applications/new). Users sign in with
55
+ their GitHub account. Copy the Client ID and Client Secret into Play's
56
+ `~/.play.yml` file.
57
+
58
+ ### Set up your music folder
59
+
60
+ Next, tell Play where to look for music. It's the `path` attribute in
61
+ `~/.play.yml`. We'll then look at your path and import everything
62
+ recursively when you run:
63
+
64
+ play -i
65
+
66
+ ## Play
67
+
68
+ Once you're all set up, you can spin up the web app with:
69
+
70
+ play -w
71
+
72
+ You can hit the server at [localhost:5050](http://localhost:5050). Queue some
73
+ hawt, hawt music up. We'll wait.
74
+
75
+ Ready? Cool. The only thing left to do is actually start the music server.
76
+ That's done with:
77
+
78
+ play -d
79
+
80
+ You'll detach it and put it in the background, where it will sit waiting for
81
+ salacious music to play for you. When you want to kill it for reals, run:
82
+
83
+ play -s
84
+
85
+ For all the fun commands and stuff you can do, just run:
86
+
87
+ play -h
88
+
89
+
90
+ ## Set up your office (optional)
91
+
92
+ This isn't a required step. If nothing's in the queue and Play has still been
93
+ told to play something, it'll just play random music. But you can set it up so
94
+ it will play a suitable artist for someone who's currently in the office.
95
+
96
+ That particular step is left to the reader's imagination — here at GitHub we
97
+ poll our router's ARP tables and update an internal application with MAC
98
+ addresses — but all Play cares about is a URL that returns comma-separated
99
+ string identifiers. We get that string by hitting the `office_url` in
100
+ `~/.play.yml`. The string that's returned from that URL should look
101
+ something like this:
102
+
103
+ holman,kneath,defunkt
104
+
105
+ That means those three handsome lads are in the office right now. Once we get
106
+ that, we'll compare each of those with the users we have in our database. We do
107
+ that by checking a user attribute called `office_string`, which is just a
108
+ unique identifier to associate back to Play's user accounts. In this example,
109
+ I'd log into my account and change my `office_string` to be "holman" so I could
110
+ match up. It could be anything, though; we actually use MAC addresses here.
111
+
112
+ ## Local development
113
+
114
+ If you're going to hack on this locally, you can use the normal process for
115
+ writing a Sintara app (`rackup`, `shotgun`, et cetera). The only thing we kind
116
+ of do differently in Play is we disable the GitHub OAuth callback (since your
117
+ development URL is different than it would be in production).
118
+
119
+ To actually use it locally, we'll automatically create a user called `user` for
120
+ you when you first access the app. That way you can actually mess around
121
+ without having to hit GitHub or create a user manually.
122
+
123
+ None of this happens if you launch Play with `bin/play -d`.
124
+
125
+ ## Current Status
126
+
127
+ This is pretty rough. For the most part it should run pretty reliably for you,
128
+ but there's a bit of setup and configuration that I'd like to refine and do
129
+ away with until it's "ready" for prime time.
130
+
131
+ Once it's ready, pretty sure I'm going to make the most awesome screencast, and
132
+ then it's balls-out from there.
133
+
134
+ ## ♬ ★★★
135
+
136
+ This was created by [Zach Holman](http://zachholman.com). You can follow me on
137
+ Twitter as [@holman](http://twitter.com).
138
+
139
+ I usually find myself playing Justice, Kanye West, and Muse at the office.
data/Rakefile ADDED
@@ -0,0 +1,185 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'date'
4
+
5
+ #############################################################################
6
+ #
7
+ # Helper functions
8
+ #
9
+ #############################################################################
10
+
11
+ def name
12
+ @name ||= Dir['*.gemspec'].first.split('.').first
13
+ end
14
+
15
+ def version
16
+ line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
17
+ line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
18
+ end
19
+
20
+ def date
21
+ Date.today.to_s
22
+ end
23
+
24
+ def rubyforge_project
25
+ name
26
+ end
27
+
28
+ def gemspec_file
29
+ "#{name}.gemspec"
30
+ end
31
+
32
+ def gem_file
33
+ "#{name}-#{version}.gem"
34
+ end
35
+
36
+ def replace_header(head, header_name)
37
+ head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
38
+ end
39
+
40
+ #############################################################################
41
+ #
42
+ # Standard tasks
43
+ #
44
+ #############################################################################
45
+
46
+ task :default => :test
47
+
48
+ require 'rake/testtask'
49
+ Rake::TestTask.new(:test) do |test|
50
+ test.libs << 'lib' << 'test'
51
+ test.pattern = 'test/**/test_*.rb'
52
+ test.verbose = true
53
+ end
54
+
55
+ desc "Generate RCov test coverage and open in your browser"
56
+ task :coverage do
57
+ require 'rcov'
58
+ sh "rm -fr coverage"
59
+ sh "rcov test/test_*.rb"
60
+ sh "open coverage/index.html"
61
+ end
62
+
63
+ require 'rake/rdoctask'
64
+ Rake::RDocTask.new do |rdoc|
65
+ rdoc.rdoc_dir = 'rdoc'
66
+ rdoc.title = "#{name} #{version}"
67
+ rdoc.rdoc_files.include('README*')
68
+ rdoc.rdoc_files.include('lib/**/*.rb')
69
+ end
70
+
71
+ desc "Open an irb session preloaded with this library"
72
+ task :console do
73
+ sh "irb -rubygems -r ./lib/#{name}.rb"
74
+ end
75
+
76
+ #############################################################################
77
+ #
78
+ # Custom tasks (add your own tasks here)
79
+ #
80
+ #############################################################################
81
+
82
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/lib')
83
+
84
+ require 'yaml'
85
+
86
+ task :environment do
87
+ ENV['RACK_ENV'] ||= 'development'
88
+ require 'lib/play'
89
+ require "bundler/setup"
90
+ end
91
+
92
+ desc "Open an irb session preloaded with this library"
93
+ task :console do
94
+ sh "irb -rubygems -r ./lib/play"
95
+ end
96
+
97
+ namespace :db do
98
+ task :create do
99
+ config = YAML::load(File.open("#{ENV['HOME']}/.play.yml"))
100
+ `mysql -u#{config['db']['user']} \
101
+ --password='#{config['db']['password']}' \
102
+ --execute=\'CREATE DATABASE #{config['db']['database']} CHARACTER SET utf8 COLLATE utf8_unicode_ci;'`
103
+ end
104
+
105
+ task :drop do
106
+ config = YAML::load(File.open("#{ENV['HOME']}/.play.yml"))
107
+ `mysql --user=#{config['db']['user']} \
108
+ --password='#{config['db']['password']}' \
109
+ --execute='DROP DATABASE #{config['db']['database']};'`
110
+ end
111
+
112
+ desc "Migrate the database through scripts in db/migrate."
113
+ task :migrate => :environment do
114
+ ActiveRecord::Base.establish_connection(Play.config['db'])
115
+ ActiveRecord::Migrator.migrate("db/migrate")
116
+ end
117
+ end
118
+
119
+ #############################################################################
120
+ #
121
+ # Packaging tasks
122
+ #
123
+ #############################################################################
124
+
125
+ desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
126
+ task :release => :build do
127
+ unless `git branch` =~ /^\* master$/
128
+ puts "You must be on the master branch to release!"
129
+ exit!
130
+ end
131
+ sh "git commit --allow-empty -a -m 'Release #{version}'"
132
+ sh "git tag v#{version}"
133
+ sh "git push origin master"
134
+ sh "git push origin v#{version}"
135
+ sh "gem push pkg/#{name}-#{version}.gem"
136
+ end
137
+
138
+ desc "Build #{gem_file} into the pkg directory"
139
+ task :build => :gemspec do
140
+ sh "mkdir -p pkg"
141
+ sh "gem build #{gemspec_file}"
142
+ sh "mv #{gem_file} pkg"
143
+ end
144
+
145
+ desc "Generate #{gemspec_file}"
146
+ task :gemspec => :validate do
147
+ # read spec file and split out manifest section
148
+ spec = File.read(gemspec_file)
149
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
150
+
151
+ # replace name version and date
152
+ replace_header(head, :name)
153
+ replace_header(head, :version)
154
+ replace_header(head, :date)
155
+ #comment this out if your rubyforge_project has a different name
156
+ replace_header(head, :rubyforge_project)
157
+
158
+ # determine file list from git ls-files
159
+ files = `git ls-files`.
160
+ split("\n").
161
+ sort.
162
+ reject { |file| file =~ /^\./ }.
163
+ reject { |file| file =~ /^(rdoc|pkg)/ }.
164
+ map { |file| " #{file}" }.
165
+ join("\n")
166
+
167
+ # piece file back together and write
168
+ manifest = " s.files = %w[\n#{files}\n ]\n"
169
+ spec = [head, manifest, tail].join(" # = MANIFEST =\n")
170
+ File.open(gemspec_file, 'w') { |io| io.write(spec) }
171
+ puts "Updated #{gemspec_file}"
172
+ end
173
+
174
+ desc "Validate #{gemspec_file}"
175
+ task :validate do
176
+ libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
177
+ unless libfiles.empty?
178
+ puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
179
+ exit!
180
+ end
181
+ unless Dir['VERSION*'].empty?
182
+ puts "A `VERSION` file at root level violates Gem best practices."
183
+ exit!
184
+ end
185
+ end
data/bin/play ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'play'
6
+ require 'optparse'
7
+
8
+ parser = OptionParser.new do |opts|
9
+ opts.banner = "Usage: play [options] COMMAND"
10
+
11
+ opts.separator ""
12
+ opts.separator "Options:"
13
+
14
+ opts.on("--migrate", "Setup the database") do
15
+ ActiveRecord::Base.establish_connection(Play.config['db'])
16
+ ActiveRecord::Migrator.migrate("#{File.dirname(__FILE__)}/db/migrate")
17
+ end
18
+
19
+ opts.on("-d", "--detach", "Start the music server") do
20
+ ENV['RACK_ENV'] = 'production'
21
+ pid = fork { Play::Client.loop }
22
+ Process.detach(pid)
23
+ end
24
+
25
+ opts.on("-s", "--stop", "Stop the music server") do
26
+ Play::Client.stop
27
+ end
28
+
29
+ opts.on("-w", "--web", "Run the web instance") do
30
+ system("rackup -p 5050")
31
+ end
32
+
33
+ opts.on("-p", "--path", "Pause the music server") do |path|
34
+ Play::Client.pause
35
+ end
36
+
37
+ opts.on("-i", "--import", "Import new songs") do |import|
38
+ Play::Library.import_songs
39
+ exit
40
+ end
41
+
42
+ opts.on("-h", "--help", "Show this message") do
43
+ puts opts
44
+ exit
45
+ end
46
+
47
+ end
48
+
49
+ parser.parse!