Merge branch 'develop' into vrfs988

This commit is contained in:
Jonathan Kolyer 2014-01-12 02:53:11 -06:00
commit e539157d76
80 changed files with 1033 additions and 324 deletions

View File

@ -37,7 +37,8 @@ gem 'carrierwave', '0.9.0'
gem 'carrierwave_direct'
gem 'uuidtools', '2.1.2'
gem 'bcrypt-ruby', '3.0.1'
gem 'jquery-rails', '2.3.0' # pinned because jquery-ui-rails was split from jquery-rails, but activeadmin doesn't support this gem yet
gem 'jquery-rails' # , '2.3.0' # pinned because jquery-ui-rails was split from jquery-rails, but activeadmin doesn't support this gem yet
gem 'jquery-ui-rails'
gem 'rails3-jquery-autocomplete'
gem 'activeadmin', '0.6.2'
gem 'mime-types', '1.25'
@ -48,7 +49,8 @@ gem 'country-select'
gem 'aasm', '3.0.16'
gem 'postgres-copy', '0.6.0'
gem 'aws-sdk', '1.29.1'
gem 'bugsnag'
gem 'bugsnag'
gem 'gon'
gem 'resque'
gem 'resque-retry'
gem 'resque-failed-job-mailer'

View File

@ -0,0 +1,52 @@
ActiveAdmin.register JamRuby::Mix, :as => 'Errored Mixes' do
config.filters = true
config.per_page = 50
config.clear_action_items!
config.sort_order = "created_at_desc"
menu :parent => 'Sessions'
controller do
def scoped_collection
Mix.where('error_reason is not NULL and completed = FALSE')
end
def mix_again
@mix = Mix.find(params[:id])
@mix.enqueue
render :json => {}
end
end
index :as => :block do |mix|
div :for => mix do
h3 "Mix (Users: #{mix.recording.users.map { |u| u.name }.join ','}) (When: #{mix.created_at.strftime('%b %d %Y, %H:%M')})"
columns do
column do
panel 'Mix Details' do
attributes_table_for(mix) do
row :recording do |mix| auto_link(mix.recording, mix.recording.id) end
row :created_at do |mix| mix.created_at.strftime('%b %d %Y, %H:%M') end
row :s3_url do |mix| mix.url end
row :manifest do |mix| mix.manifest end
row :completed do |mix| "#{mix.completed ? "finished" : "not finished"}" end
if mix.completed
row :completed_at do |mix| mix.completed_at.strftime('%b %d %Y, %H:%M') end
elsif mix.error_count > 0
row :error_count do |mix| "#{mix.error_count} times failed" end
row :error_reason do |mix| "last reason failed: #{mix.error_reason}" end
row :error_detail do |mix| "last error detail: #{mix.error_detail}" end
row :mix_again do |mix| div :class => 'mix-again' do
span do link_to "Mix Again", '#', :class => 'mix-again', :'data-mix-id' => mix.id end
span do div :class => 'mix-again-dialog' do end end
end
end
end
end
end
end
end
end
end
end

View File

@ -6,6 +6,14 @@ ActiveAdmin.register JamRuby::Mix, :as => 'Mixes' do
config.sort_order = "created_at_desc"
menu :parent => 'Sessions'
controller do
def mix_again
@mix = Mix.find(params[:id])
@mix.enqueue
render :json => {}
end
end
index :as => :block do |mix|
div :for => mix do
@ -25,7 +33,11 @@ ActiveAdmin.register JamRuby::Mix, :as => 'Mixes' do
row :error_count do |mix| "#{mix.error_count} times failed" end
row :error_reason do |mix| "last reason failed: #{mix.error_reason}" end
row :error_detail do |mix| "last error detail: #{mix.error_detail}" end
row :what do |mix| link_to "Your Mom", '/' end
row :mix_again do |mix| div :class => 'mix-again' do
span do link_to "Mix Again", '#', :class => 'mix-again', :'data-mix-id' => mix.id end
span do div :class => 'mix-again-dialog' do end end
end
end
end
end
end

View File

@ -1,2 +1,11 @@
//= require active_admin/base
//= require autocomplete-rails
// //= require active_admin/base
//= require jquery
//= require jquery_ujs
//= require jquery.ui.core
//= require jquery.ui.widget
//= require jquery.ui.datepicker
//= require jquery.ui.dialog
//= require active_admin/application
//= require autocomplete-rails
//= require base
//= require_tree .

View File

@ -0,0 +1,38 @@
(function(context,$) {
/**
* Javascript wrappers for the REST API
*/
"use strict";
context.JK = context.JK || {};
context.JK.RestAdmin = function() {
var self = this;
var logger = context.JK.logger;
function tryMixAgain(options) {
var mixId = options['mix_id']
return $.ajax({
type: "POST",
dataType: "json",
url: gon.global.prefix + 'api/mix/' + mixId + '/enqueue',
contentType: 'application/json',
processData: false
});
}
function initialize() {
return self;
}
// Expose publics
this.initialize = initialize;
this.tryMixAgain = tryMixAgain;
return this;
};
})(window,jQuery);

View File

@ -12,4 +12,3 @@
//
//= require jquery
//= require jquery_ujs
//= require_tree .

View File

@ -0,0 +1,23 @@
(function(context,$) {
context.JK = {}
var console_methods = [
'log', 'debug', 'info', 'warn', 'error', 'assert',
'clear', 'dir', 'dirxml', 'trace', 'group',
'groupCollapsed', 'groupEnd', 'time', 'timeEnd',
'timeStamp', 'profile', 'profileEnd', 'count',
'exception', 'table'
];
if ('undefined' === typeof(context.console)) {
context.console = {};
$.each(console_methods, function(index, value) {
context.console[value] = $.noop;
});
}
context.JK.logger = context.console;
})(window, jQuery);

View File

View File

@ -0,0 +1,22 @@
(function(context,$) {
var restAdmin = context.JK.RestAdmin();
$(function() {
// convert mix again links to ajax
$('a.mix-again').click(function() {
var $link = $(this);
restAdmin.tryMixAgain({mix_id: $link.attr('data-mix-id')})
.done(function(response) {
$link.closest('div.mix-again').find('div.mix-again-dialog').html('<div>Mix enqueued</div><a href="' + gon.global.prefix + 'resque">Resque Web</a>').dialog();
})
.error(function(jqXHR) {
$link.closest('div.mix-again').find('div.mix-again-dialog').html('Mix failed: ' + jqXHR.responseText).dialog();
})
return false;
})
});
})(window, jQuery);

View File

@ -7,6 +7,9 @@
// For example, to change the sidebar width:
// $sidebar-width: 242px;
/*
*= require jquery.ui.all
*/
// Active Admin's got SASS!
@import "active_admin/mixins";
@import "active_admin/base";

View File

@ -9,5 +9,6 @@
* compiled file, but it's generally better to create a new file per style scope.
*
*= require_self
*= require jquery.ui.all
*= require_tree .
*/

View File

@ -1,3 +1,4 @@
.version-info {
font-size:small;
color:lightgray;

View File

@ -1,3 +1,10 @@
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :prepare_gon
def prepare_gon
gon.another = 'hello'
gon.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] || '/'
end
end

View File

@ -22,7 +22,7 @@ cp ../pb/target/ruby/jampb/jampb-${GEM_VERSION}.gem vendor/cache/ || { echo "una
cp ../ruby/jam_ruby-${GEM_VERSION}.gem vendor/cache/ || { echo "unable to copy jam-ruby gem"; exit 1; }
# put all dependencies into vendor/bundle
rm -rf vendor/bundle
#rm -rf vendor/bundle -- let jenkins config 'wipe workspace' decide this
rm Gemfile.lock # if we don't want versions to float, pin it in the Gemfile, not count on Gemfile.lock
bundle install --path vendor/bundle
bundle update

View File

@ -80,14 +80,26 @@ module JamAdmin
config.storage_type = :fog
# these only need to be set if store_artifact_to_files = false
config.aws_artifact_access_key_id = ENV['AWS_KEY']
config.aws_artifact_secret_access_key = ENV['AWS_SECRET']
config.aws_artifact_region = 'us-east-1'
config.aws_artifact_bucket_public = 'jamkazam-dev-public'
config.aws_artifact_bucket = 'jamkazam-dev'
config.aws_artifact_cache = '315576000'
config.aws_access_key_id = ENV['AWS_KEY']
config.aws_secret_access_key = ENV['AWS_SECRET']
config.aws_region = 'us-east-1'
config.aws_bucket_public = 'jamkazam-dev-public'
config.aws_bucket = 'jamkazam-dev'
config.aws_cache = '315576000'
# for carrierwave_direct
config.action_controller.allow_forgery_protection = false
config.redis_host = "localhost:6379"
config.email_alerts_alias = 'alerts@jamkazam.com' # should be used for 'oh no' server down/service down sorts of emails
config.email_generic_from = 'nobody@jamkazam.com'
config.email_smtp_address = 'smtp.sendgrid.net'
config.email_smtp_port = 587
config.email_smtp_domain = 'www.jamkazam.com'
config.email_smtp_authentication = :plain
config.email_smtp_user_name = 'jamkazam'
config.email_smtp_password = 'jamjamblueberryjam'
config.email_smtp_starttls_auto = true
end
end

View File

@ -1,5 +1,7 @@
# Load the rails application
require File.expand_path('../application', __FILE__)
APP_CONFIG = Rails.application.config
# Initialize the rails application
JamAdmin::Application.initialize!

View File

@ -71,6 +71,6 @@ JamAdmin::Application.configure do
# Show the logging configuration on STDOUT
config.show_log_configuration = false
config.aws_artifact_bucket_public = 'jamkazam-public'
config.aws_artifact_bucket = 'jamkazam'
config.aws_bucket_public = 'jamkazam-public'
config.aws_bucket = 'jamkazam'
end

View File

@ -2,6 +2,7 @@ class Footer < ActiveAdmin::Component
def build
super(id: "footer")
para "version info: web=#{::JamAdmin::VERSION} lib=#{JamRuby::VERSION} db=#{JamDb::VERSION}"
render :inline => include_gon
end
end

View File

@ -10,13 +10,13 @@ CarrierWave.configure do |config|
config.storage = :fog
config.fog_credentials = {
:provider => 'AWS',
:aws_access_key_id => JamAdmin::Application.config.aws_artifact_access_key_id,
:aws_secret_access_key => JamAdmin::Application.config.aws_artifact_secret_access_key,
:region => JamAdmin::Application.config.aws_artifact_region,
:aws_access_key_id => JamAdmin::Application.config.aws_access_key_id,
:aws_secret_access_key => JamAdmin::Application.config.aws_secret_access_key,
:region => JamAdmin::Application.config.aws_region,
}
config.fog_directory = JamAdmin::Application.config.aws_artifact_bucket_public # required
config.fog_directory = JamAdmin::Application.config.aws_bucket_public # required
config.fog_public = true # optional, defaults to true
config.fog_attributes = {'Cache-Control'=>"max-age=#{JamAdmin::Application.config.aws_artifact_cache}"} # optional, defaults to {}
config.fog_attributes = {'Cache-Control'=>"max-age=#{JamAdmin::Application.config.aws_cache}"} # optional, defaults to {}
end
end

View File

@ -1,11 +1,11 @@
ActionMailer::Base.raise_delivery_errors = true
ActionMailer::Base.delivery_method = Rails.env == "test" ? :test : :smtp
ActionMailer::Base.smtp_settings = {
:address => "smtp.sendgrid.net",
:port => 587,
:domain => "www.jamkazam.com",
:authentication => :plain,
:user_name => "jamkazam",
:password => "jamjamblueberryjam",
:enable_starttls_auto => true
:address => Rails.application.config.email_smtp_address,
:port => Rails.application.config.email_smtp_port,
:domain => Rails.application.config.email_smtp_domain,
:authentication => Rails.application.config.email_smtp_authentication,
:user_name => Rails.application.config.email_smtp_user_name,
:password => Rails.application.config.email_smtp_password ,
:enable_starttls_auto => Rails.application.config.email_smtp_starttls_auto
}

View File

@ -0,0 +1 @@
Gon.global.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] || '/'

View File

@ -0,0 +1 @@
Resque.redis = Rails.application.config.redis_host

View File

@ -15,6 +15,7 @@ JamAdmin::Application.routes.draw do
ActiveAdmin.routes(self)
match '/api/artifacts' => 'artifacts#update_artifacts', :via => :post
match '/api/mix/:id/enqueue' => 'admin/mixes#mix_again', :via => :post
mount Resque::Server.new, :at => "/resque"

View File

@ -87,4 +87,5 @@ discardable_recorded_tracks2.sql
icecast.sql
home_page_promos.sql
mix_job_watch.sql
music_session_constraints.sql
music_session_constraints.sql
mixes_drop_manifest_add_retry.sql

View File

@ -147,11 +147,11 @@ create table icecast_relays (
-- mount at server. eg /example.ogg
mount VARCHAR(128) not null,
-- eg /different.ogg
local_mount VARCHAR(128) not null,
local_mount VARCHAR(128) not null default '/relaymout.ogg',
-- eg joe. could be null
username VARCHAR(64) default NULL ,
username VARCHAR(64) not null default 'jkicerelayusr' ,
-- user password
password VARCHAR(64) default null ,
password VARCHAR(64) not null default 'jkicerelaypwd' ,
relay_shoutcast_metadata INTEGER default 0,
--- relay only if we have someone wanting to listen
on_demand INTEGER default 0,
@ -164,28 +164,28 @@ create TABLE icecast_user_authentications(
id VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
--"htpasswd or url"
-- real name is type
stype VARCHAR(16) DEFAULT NULL ,
stype VARCHAR(16) DEFAULT 'url' ,
-- these are for httpasswd
filename VARCHAR(256) default NULL,
allow_duplicate_users INTEGER DEFAULT 0,
allow_duplicate_users INTEGER DEFAULT 1,
-- these options are for url
-- eg value="http://myauthserver.com/stream_start.php"
mount_add VARCHAR(256) default NULL,
mount_add VARCHAR(256) DEFAULT 'http://icecastauth.jamkazam.com/stream_start.php',
--value="http://myauthserver.com/stream_end.php"
mount_remove VARCHAR(256) default NULL,
mount_remove VARCHAR(256) default 'http://icecastauth.jamkazam.com/stream_end.php',
--value="http://myauthserver.com/listener_joined.php"
listener_add VARCHAR(256) default NULL,
listener_add VARCHAR(256) default 'http://icecastauth.jamkazam.com/listener_joined.php',
--value="http://myauthserver.com/listener_left.php"
listener_remove VARCHAR(256) default NULL,
listener_remove VARCHAR(256) default 'http://icecastauth.jamkazam.com/listener_left.php',
-- value="user"
username VARCHAR(64) default NULL,
username VARCHAR(64) default 'jkuser',
-- value="pass"
password VARCHAR(64) default NULL,
password VARCHAR(64) DEFAULT 'jkhackpass',
-- value="icecast-auth-user: 1"
auth_header VARCHAR(64) default NULL,
auth_header VARCHAR(64) default 'icecast-auth-user: 1',
-- value="icecast-auth-timelimit:"
timelimit_header VARCHAR(64) default NULL,
timelimit_header VARCHAR(64) default 'icecast-auth-timelimit:',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
@ -197,7 +197,7 @@ create table icecast_mounts (
-- eg/example-complex.ogg
mount_name VARCHAR(128) UNIQUE NOT NULL,
username VARCHAR(64) NOT NULL DEFAULT 'jamsource',
password VARCHAR(64) NOT NULL DEFAULT 'jamksource',
password VARCHAR(64) NOT NULL DEFAULT 'jamhackmesourcepwd',
max_listeners INTEGER NOT NULL DEFAULT 4,
max_listener_duration INTEGER NOT NULL DEFAULT 3600,
-- dump of the stream coming through on this mountpoint.
@ -248,6 +248,8 @@ create table icecast_mounts (
on_connect VARCHAR(256) DEFAULT '/home/icecast/bin/source-start',
on_disconnect VARCHAR(256) DEFAULT '/home/icecast/bin/source-end',
user_authentication_id varchar(64) DEFAULT NULL references icecast_user_authentications(id),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
@ -294,15 +296,60 @@ create table icecast_securities (
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
create TABLE icecast_servers(
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
icecast_limit_id VARCHAR(64) REFERENCES icecast_limits(id)
--use this to mark the server configuration as needing to be regenerated
config_changed INTEGER DEFAULT 0,
limit_id VARCHAR(64) REFERENCES icecast_limits(id),
adminauth_id VARCHAR(64) REFERENCES icecast_admin_authentications(id),
directory_id VARCHAR(64) REFERENCES icecast_directories(id),
misc_id VARCHAR(64) REFERENCES icecast_servermiscs(id),
master_relay_id VARCHAR(64) REFERENCES icecast_mastersvr_relays(id),
/* relay_ids VARCHAR(64) REFERENCES icecast_serverrelays(id),
mount_ids VARCHAR(64) REFERENCES icecast_servermounts(id),
socket_ids VARCHAR(64) REFERENCES icecast_serversockets(id) ,
*/
-- configs
-- mounts
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE icecast_servermounts (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
mount_id VARCHAR(64) REFERENCES icecast_mounts(id) ON DELETE CASCADE,
server_id VARCHAR(64) REFERENCES icecast_servers(id) ON DELETE CASCADE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE icecast_servermounts ADD CONSTRAINT server_mount_uniqkey UNIQUE (server_id, mount_id);
CREATE TABLE icecast_serverrelays (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
relay_id VARCHAR(64) REFERENCES icecast_relays(id) ON DELETE CASCADE,
server_id VARCHAR(64) REFERENCES icecast_servers(id) ON DELETE CASCADE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE icecast_serverrelays ADD CONSTRAINT server_relay_uniqkey UNIQUE (server_id, relay_id);
CREATE TABLE icecast_serversockets (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
socket_id VARCHAR(64) REFERENCES icecast_listen_sockets(id) ON DELETE CASCADE,
server_id VARCHAR(64) REFERENCES icecast_servers(id) ON DELETE CASCADE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE icecast_serversockets ADD CONSTRAINT server_socket_uniqkey UNIQUE (server_id, socket_id);

View File

@ -0,0 +1,2 @@
ALTER TABLE mixes DROP COLUMN manifest;
ALTER TABLE mixes ADD COLUMN should_retry BOOLEAN NOT NULL DEFAULT FALSE;

View File

@ -29,6 +29,7 @@ require "jam_ruby/lib/s3_util"
require "jam_ruby/lib/s3_manager"
require "jam_ruby/lib/profanity"
require "jam_ruby/resque/audiomixer"
require "jam_ruby/resque/scheduled/audiomixer_retry"
require "jam_ruby/mq_router"
require "jam_ruby/base_manager"
require "jam_ruby/connection_manager"

View File

@ -1,26 +1,19 @@
module JSONable
def jdumpXml (ovb, nm, ident=1, output=$stdout)
serialized = Hash.new
ovb.myattr_accessor.each do |attribute|
#serialized[attribute] = ovb[attribute]
puts "attribute = #{attribute}"
#serialized[attribute] = self.public_send attribute
end
hash = serialized
module JAmXml
def jdumpXml (hash, nm, ident=1, output=$stdout, child=nil)
tb = "\t"
tbs = tb * ident
tbse = tb * (ident-1)
output.puts "#{tbse}<#{nm}>"
unless ident <= 0
tbse = tb * (ident-1)
output.puts "#{tbse}<#{nm}>"
end
hash.each do |key, val|
#puts "attrib: key=#{key} val=#{val}"
el = key
if key.present?
if !key.nil? && !key.empty?
el = key.gsub(/_/, '-')
end
@ -28,15 +21,45 @@ module JSONable
sv = val
if val.to_s.empty?
#skip ???
next if val.to_s.empty?
else
if val.instance_of? String
#encode the string to be xml safe
sv = CGI.escapeHTML(val)
end
end
output.puts "#{tbs}<#{el}>#{sv}</#{el}>"
if key.empty?
output.puts "#{tbs}<#{sv}/>"
else
output.puts "#{tbs}<#{el}>#{sv}</#{el}>"
end
end
puts "#{tbse}</#{nm}>"
if !child.nil?
if child.kind_of? Array
child.each { |x| x.dumpXml(ident+1,output)}
else
child.dumpXml(ident+1,output)
end
end
unless ident <= 0
puts "#{tbse}</#{nm}>"
end
end
def jMakeStrXmlSafe(val)
sv = val
if val.to_s.empty?
#skip ???
sv =""
else
if val.instance_of? String
#encode the string to be xml safe
sv = CGI.escapeHTML(val)
end
end
return sv
end
end
@ -44,7 +67,7 @@ end
module JamRuby
class IcecastAdminAuthentication < ActiveRecord::Base
include JSONable
include JAmXml
attr_accessible :source_password, :relay_user, :relay_password, :admin_user, :admin_password
#myattr_accessor = [:source_password, :relay_user, :relay_password, :admin_user, :admin_password ]
@ -65,7 +88,13 @@ module JamRuby
validates :admin_password, presence: true, length: {minimum: 5}
def dumpXml (ident=1, output=$stdout)
self.jdumpXml(self,"authentication", ident,output)
hash = Hash["source_password" => self.source_password,
"relay_user" => self.relay_user,
"relay_password" => self.relay_password,
"admin_user" => self.admin_user,
"admin_password" => self.admin_password]
self.jdumpXml(hash,"authentication", ident,output)
end

View File

@ -1,10 +1,17 @@
module JamRuby
class IcecastDirectory < ActiveRecord::Base
include JAmXml
self.primary_key = 'id'
attr_accessible :yp_url_timeout, :yp_url
attr_accessor :yp_url_timeout, :yp_url
validates :yp_url_timeout, numericality: {only_integer: true}, length: {in: 1..30}
def dumpXml (ident=1, output=$stdout)
hash = Hash["yp_url_timeout" => self.yp_url_timeout,
"yp_url" => self.yp_url]
self.jdumpXml(hash, "directory", ident, output)
end
end
end

View File

@ -1,14 +1,13 @@
module JamRuby
class IcecastLimit < ActiveRecord::Base
include JSONable
include JAmXml
self.primary_key = 'id'
attr_accessible :clients, :sources, :queue_size, :client_timeout, :header_timeout, :source_timeout, :burst_size
#attr_accessor :clients, :sources, :queue_size, :client_timeout, :header_timeout, :source_timeout, :burst_size
#validates :clients, numericality: {only_integer: true}, length: {in: 1..15000}
validates :clients, numericality: {only_integer: true}
validates :clients, numericality: {only_integer: true}, length: {in: 1..15000}
after_initialize :init
@ -23,12 +22,17 @@ module JamRuby
self.burst_size ||= 65536
end
def setclients(val)
@clients = val
end
def dumpXml (ident=1, output=$stdout)
self.jdumpXml(self, "limits", ident, output)
hash = Hash["clients" => self.clients,
"sources" => self.sources,
"queue_size" => self.queue_size,
"client_timeout" => self.client_timeout,
"header_timeout" => self.header_timeout]
hash.merge! "source_timeout" => self.source_timeout,
"burst_size" => self.burst_size
self.jdumpXml(hash, "limits", ident, output)
end
end
end
end

View File

@ -1,13 +1,19 @@
module JamRuby
class IcecastListenSocket < ActiveRecord::Base
include JAmXml
self.primary_key = 'id'
attr_accessible :port, :bind_address, :shoutcast_mount, :shoutcast_compat
attr_accessor :port, :bind_address, :shoutcast_mount, :shoutcast_compat
def dumpXml()
belongs_to :server, :class_name => "JamRuby::IcecastServer" , :inverse_of => :sockets
def dumpXml (ident=1, output=$stdout)
hash = Hash["port" => self.port,
"bind_address" => self.bind_address,
"shoutcast_mount" => self.shoutcast_mount,
"shoutcast_compat" => self.shoutcast_compat]
self.jdumpXml(hash, "listen-socket", ident, output)
end

View File

@ -1,12 +1,18 @@
module JamRuby
class IcecastLogging < ActiveRecord::Base
include JAmXml
self.primary_key = 'id'
attr_accessible :accesslog, :errorlog, :playlistlog, :loglevel
attr_accessor :accesslog, :errorlog, :playlistlog, :loglevel
def dumpXml()
def dumpXml (ident=1, output=$stdout)
hash = Hash["accesslog" => self.accesslog,
"errorlog" => self.errorlog,
"loglevel" => self.loglevel,
"playlistlog" => self.playlistlog]
self.jdumpXml(hash, "logging", ident, output)
end

View File

@ -1,12 +1,18 @@
module JamRuby
class IcecastMastersvrRelay < ActiveRecord::Base
include JAmXml
self.primary_key = 'id'
attr_accessible :master_server, :master_server_port, :master_username, :master_password, :relays_on_demand
attr_accessible :master_server, :master_server_port, :master_username, :master_password, :relays_on_demand
validates :master_password, :master_username, presence: true
def dumpXml()
def dumpXml (ident=0, output=$stdout)
hash = Hash["master_server" => self.master_server,
"master_server_port" => self.master_server_port,
"master_username" => self.master_username,
"master_password" => self.master_password,
"relays_on_demand" => self.relays_on_demand]
self.jdumpXml(hash, "masterrelay", ident, output)
end
end
end

View File

@ -1,24 +1,64 @@
module JamRuby
class IcecastMount < ActiveRecord::Base
include JAmXml
self.primary_key = 'id'
attr_accessible :mount_name, :username, :password, :max_listeners, :max_listener_duration, :dump_file
attr_accessor :mount_name, :username, :password, :max_listeners, :max_listener_duration, :dump_file
attr_accessible :intro, :fallback_mount, :fallback_override, :fallback_when_full, :charset
attr_accessor :intro, :fallback_mount, :fallback_override, :fallback_when_full, :charset
attr_accessible :publicc, :stream_name, :stream_description, :stream_url, :genre, :bitrate
attr_accessor :publicc, :stream_name, :stream_description, :stream_url, :genre, :bitrate
attr_accessible :mtype, :subtype, :hidden, :burst_size, :mp3_metadata_interval, :on_connect, :on_disconnect
attr_accessor :mtype, :subtype, :hidden, :burst_size, :mp3_metadata_interval, :on_connect, :on_disconnect
validates :mount_name, presence: true
validates :username, presence: true
validates :password, presence: true
#has_one :authentication, :class_name => "IcecastUserAuthentication"
has_one :authentication, :class_name => "IcecastUserAuthentication"
belongs_to :servermount, :class_name => "JamRuby::IcecastServermount" , :inverse_of => :mount
def dumpXml()
def dumpXml (ident=1, output=$stdout)
hash = Hash["mount_name" => self.mount_name,
"username" => self.username,
"password" => self.password,
"max_listeners" => self.max_listeners,
"max_listener_duration" => self.max_listener_duration,
"dump_file" => self.dump_file,
"intro" => self.intro,
"fallback_mount" => self.fallback_mount,
"fallback_override" => self.fallback_override,
"fallback_when_full" => self.fallback_when_full,
"charset" => self.charset,
"public" => self.publicc,
"stream_name" => self.stream_name,
"stream_description" => self.stream_description,
"stream_url" => self.stream_url,
"genre" => self.genre,
"bitrate" => self.bitrate,
"type" => self.mtype,
"subtype" => self.subtype,
"hidden" => self.hidden,
"burst_size" => self.burst_size,
"mp3_metadata_interval" => self.mp3_metadata_interval,
"on_connect" => self.on_connect,
"on_disconnect" => self.on_disconnect
]
self.jdumpXml(hash, "mount", ident, output, self.authentication)
end
def getMediaUrl
if !self.servermount.nil?
@server = self.servermount.server
@misc = @server.misc
url = "http://" + @misc.hostname + self.mount_name
return url;
else
raise ("Undefined severid")
end
end
end
end

View File

@ -1,13 +1,24 @@
module JamRuby
class IcecastPath < ActiveRecord::Base
include JAmXml
self.primary_key = 'id'
attr_accessible :basedir, :logdir, :pidfile, :webroot, :adminroot, :allow_ip, :deny_ip, :aliass
attr_accessor :basedir, :logdir, :pidfile, :webroot, :adminroot, :allow_ip, :deny_ip, :aliass
def dumpXml()
def dumpXml (ident=1, output=$stdout)
a = "alias #{self.aliass}"
hash = Hash["basedir" => self.basedir,
"logdir" => self.logdir,
"pidfile" => self.pidfile,
"webroot" => self.webroot,
"adminroot" => self.adminroot,
"allow_ip" => self.allow_ip,
"deny_ip" => self.deny_ip,
"" => a,
]
self.jdumpXml(hash, "paths", ident, output)
end
end

View File

@ -1,14 +1,35 @@
module JamRuby
class IcecastRelay < ActiveRecord::Base
include JAmXml
self.primary_key = 'id'
attr_accessible :server, :port, :mount, :local_mount, :username, :password, :relay_shoutcast_metadata, :on_demand
attr_accessor :server, :port, :mount, :local_mount, :username, :password, :relay_shoutcast_metadata, :on_demand
def dumpXml()
validates :port, numericality: {only_integer: true}, length: {in: 1..65000}
validates :mount, presence: true
validates :server, presence: true
validates :local_mount, presence: true
validates :username, presence: true
validates :password, presence: true
belongs_to :sserver, :class_name => "JamRuby::IcecastServerrelay"
def dumpXml (ident=1, output=$stdout)
hash = Hash["server" => self.server,
"port" => self.port,
"mount" => self.mount,
"local_mount" => self.local_mount,
"username" => self.username,
"password" => self.password,
"relay_shoutcast_metadata" => self.relay_shoutcast_metadata,
"on_demand" => self.on_demand
]
self.jdumpXml(hash, "relay", ident, output)
end
end
end

View File

@ -1,12 +1,39 @@
module JamRuby
class IcecastSecurity < ActiveRecord::Base
include JAmXml
self.primary_key = 'id'
attr_accessible :chroot, :changeowner_user, :changeowner_group
attr_accessor :chroot, :changeowner_user, :changeowner_group
def dumpXml()
validates :chroot, numericality: {only_integer: true}, length: {in: 0..1}
def dumpXml (ident=1, output=$stdout)
tb = "\t"
tbs = tb * (ident)
tbs2 = tb * (ident+1)
tbse = tb * (ident-1)
if tbse.empty? || tbse.nil?
tbse=""
end
nm = "security"
output.puts "#{tbse}<#{nm}>"
output.puts "#{tbs}<chroot>#{self.chroot}</chroot>"
output.puts "#{tbs}<changeowner>"
v = jMakeStrXmlSafe(self.changeowner_user)
output.puts "#{tbs2}<user>#{v}</user>"
v = jMakeStrXmlSafe(self.changeowner_group)
output.puts "#{tbs2}<group>#{v}</group>"
output.puts "#{tbs}<changeowner>"
output.puts "#{tbse}</#{nm}>"
end

View File

@ -1,21 +1,75 @@
module JamRuby
class IcecastServer < ActiveRecord::Base
include JAmXml
self.primary_key = 'id'
has_one :limit, :class_name => "JamRuby::IcecastLimit"
has_one :adminauth, :class_name => "JamRuby::IcecastAdminAuthentication"
has_one :directory, :class_name => "JamRuby::IcecastDirectory"
has_one :misc, :class_name => "JamRuby::IcecastServermisc"
has_many :listen_sockets, :class_name => "JamRuby::IcecastListenSocket"
has_one :master_relay, :class_name => "JamRuby::IcecastMastersvrRelay"
has_one :relay, :class_name => "JamRuby::IcecastRelay"
has_many :mounts, :class_name => "JamRuby::IcecastMount"
has_one :path, :class_name => "JamRuby::IcecastPath"
has_one :logging, :class_name => "JamRuby::IcecastLogging"
has_one :security, :class_name => "JamRuby::IceCastSecurity"
has_one :limit, :class_name => "JamRuby::IcecastLimit" , foreign_key: "id"
has_one :adminauth, :class_name => "JamRuby::IcecastAdminAuthentication" , foreign_key: "id"
has_one :directory, :class_name => "JamRuby::IcecastDirectory" , foreign_key: "id"
has_one :misc, :class_name => "JamRuby::IcecastServermisc" , foreign_key: "id"
def dumpXml()
has_one :master_relay, :class_name => "JamRuby::IcecastMastersvrRelay" , foreign_key: "id"
has_many :smounts, :class_name => "JamRuby::IcecastServermount"
has_many :mounts, :class_name => "JamRuby::IcecastMount", :through => :smounts
has_many :ssockets, :class_name => "JamRuby::IcecastServersocket"
has_many :sockets, :class_name => "JamRuby::IcecastListenSocket" ,:through => :ssockets
has_many :srelays, :class_name => "JamRuby::IcecastServerrelay" , :inverse_of => :server
has_many :relays, :class_name => "JamRuby::IcecastRelay" , :through => :srelays
has_one :path, :class_name => "JamRuby::IcecastPath" , foreign_key: "id"
has_one :logging, :class_name => "JamRuby::IcecastLogging" , foreign_key: "id"
has_one :security, :class_name => "JamRuby::IcecastSecurity" , foreign_key: "id"
def dumpXml (ident=1, output=$stdout)
ident += 1
unless self.limit.nil?
self.limit.dumpXml(ident,output)
end
unless self.adminauth.nil?
self.adminauth.dumpXml(ident,output)
end
unless self.directory.nil?
self.directory.dumpXml(ident,output)
end
unless self.misc.nil?
self.misc.dumpXml(ident,output)
end
unless self.master_relay.nil?
self.master_relay.dumpXml(ident,output)
end
unless self.path.nil?
self.path.dumpXml(ident,output)
end
unless self.logging.nil?
self.logging.dumpXml(ident,output)
end
unless self.security.nil?
self.security.dumpXml(ident,output)
end
self.relays.each do |rl|
sock.rl(ident,output)
end
self.sockets.each do |sock|
sock.dumpXml(ident,output)
end
self.mounts.each do |mount|
mount.dumpXml(ident,output)
end
end

View File

@ -1,12 +1,12 @@
module JamRuby
class IcecastServermisc < ActiveRecord::Base
include JAmXml
self.primary_key = 'id'
attr_accessible :hostname, :location, :admin, :fileserve, :server_id
attr_accessor :hostname, :location, :admin, :fileserve, :server_id
def dumpXml()
def dumpXml (ident=0, output=$stdout)
end

View File

@ -0,0 +1,14 @@
module JamRuby
class IcecastServermount < ActiveRecord::Base
self.primary_key = 'id'
belongs_to :mount, :class_name => "JamRuby::IcecastMount" , :inverse_of => :servermount
belongs_to :server, :class_name => "JamRuby::IcecastServer"
validates :server_id, :presence => true
validates :mount_id, :presence => true
end
end

View File

@ -0,0 +1,14 @@
module JamRuby
class IcecastServerrelay < ActiveRecord::Base
self.primary_key = 'id'
belongs_to :relay, :class_name => "JamRuby::IcecastRelay"
belongs_to :server, :class_name => "JamRuby::IcecastServer"
validates :server_id, :presence => true
validates :relay_id, :presence => true
end
end

View File

@ -0,0 +1,14 @@
module JamRuby
class IcecastServersocket < ActiveRecord::Base
self.primary_key = 'id'
belongs_to :socket, :class_name => "JamRuby::IcecastListenSocket" , foreign_key: "id"
belongs_to :server, :class_name => "JamRuby::IcecastServer" , foreign_key: "id"
validates :socket_id, :presence => true
validates :server_id, :presence => true
end
end

View File

@ -1,16 +1,76 @@
module JamRuby
class IcecastUserAuthentication < ActiveRecord::Base
include JAmXml
self.primary_key = 'id'
attr_accessible :stype, :filename, :allow_duplicate_users, :mount_add, :mount_remove,
:listener_add, :listener_remove, :username, :password, :auth_header, :timelimit_header
attr_accessor :stype, :filename, :allow_duplicate_users, :mount_add, :mount_remove,
:listener_add, :listener_remove, :username, :password, :auth_header, :timelimit_header
def dumpXml()
validates :stype, presence: true
def dumpXml (ident=1, output=$stdout)
tb = "\t"
tbs = tb * (ident)
tbse = tb * (ident-1)
if tbse.empty? || tbse.nil?
tbse=""
end
if self.stype.nil?
return
elsif self.stype.empty?
return
elsif self.stype == "htpasswd"
nm = "<authentication type=\"htpasswd\">"
output.puts "#{tbse}#{nm}"
fname = jMakeStrXmlSafe(self.filename)
output.puts "#{tbs}<option name=\"filename\" value=\"#{fname}\"/>"
output.puts "#{tbs}<option name=\"allow_duplicate_users\" value=\"#{self.allow_duplicate_users}\"/>"
nm = "authentication"
output.puts "#{tbse}</#{nm}>"
elsif self.stype == 'url'
nm = "<authentication type=\"url\">"
output.puts "#{tbse}#{nm}"
v = jMakeStrXmlSafe(self.mount_add)
output.puts "#{tbs}<option name=\"mount_add\" value=\"#{v}\"/>"
v = jMakeStrXmlSafe(self.mount_remove)
output.puts "#{tbs}<option name=\"mount_remove\" value=\"#{v}\"/>"
v = jMakeStrXmlSafe(self.listener_add)
output.puts "#{tbs}<option name=\"listener_add\" value=\"#{v}\"/>"
v = jMakeStrXmlSafe(self.listener_remove)
output.puts "#{tbs}<option name=\"listener_remove\" value=\"#{v}\"/>"
v = jMakeStrXmlSafe(self.listener_remove)
output.puts "#{tbs}<option name=\"listener_remove\" value=\"#{v}\"/>"
v = jMakeStrXmlSafe(self.username)
output.puts "#{tbs}<option name=\"username\" value=\"#{v}\"/>"
v = jMakeStrXmlSafe(self.password)
output.puts "#{tbs}<option name=\"password\" value=\"#{v}\"/>"
v = jMakeStrXmlSafe(self.auth_header)
output.puts "#{tbs}<option name=\"auth_header\" value=\"#{v}\"/>"
v = jMakeStrXmlSafe(self.timelimit_header)
output.puts "#{tbs}<option name=\"timelimit_header\" value=\"#{v}\"/>"
nm = "authentication"
output.puts "#{tbse}</#{nm}>"
else
raise 'Unsupported authentication format #{self.stype}'
end
end
end

View File

@ -9,13 +9,13 @@ module JamRuby
self.primary_key = 'id'
belongs_to :recording, :class_name => "JamRuby::Recording", :inverse_of => :mixes
def self.schedule(recording, manifest)
def self.schedule(recording)
raise if recording.nil?
raise if manifest.nil?
mix = Mix.new
mix.recording = recording
mix.manifest = manifest.to_json
mix.save
mix.url = construct_filename(mix.created_at, recording.id, mix.id)
if mix.save
@ -26,7 +26,16 @@ module JamRuby
end
def enqueue
Resque.enqueue(AudioMixer, self.id, self.sign_put)
begin
Resque.enqueue(AudioMixer, self.id, self.sign_put)
rescue
# implies redis is down. we don't update started_at
false
end
Mix.where(:id => self.id).update_all(:started_at => Time.now)
true
end
def can_download?(some_user)
@ -53,6 +62,23 @@ module JamRuby
end
end
# valid for 1 day; because the s3 urls eventually expire
def manifest
one_day = 60 * 60 * 24
manifest = { "files" => [], "timeline" => [] }
mix_params = []
recording.recorded_tracks.each do |recorded_track|
manifest["files"] << { "filename" => recorded_track.sign_url(one_day), "codec" => "vorbis", "offset" => 0 }
mix_params << { "level" => 100, "balance" => 0 }
end
manifest["timeline"] << { "timestamp" => 0, "mix" => mix_params }
manifest["output"] = { "codec" => "vorbis" }
manifest["recording_id"] = self.id
manifest
end
def s3_url
s3_manager.s3_url(url)
end
@ -70,6 +96,12 @@ module JamRuby
s3_manager.sign_url(self.url, {:expires => expiration_time, :content_type => 'audio/ogg', :secure => false}, :put)
end
def self.queue_jobs_needing_retry
Mix.find_each(:conditions => 'should_retry = TRUE or started_at is NULL', :batch_size => 100) do |mix|
mix.enqueue
end
end
private
def delete_s3_files

View File

@ -285,7 +285,7 @@ module JamRuby
return unless recorded_track.fully_uploaded
end
self.mixes << Mix.schedule(self, base_mix_manifest)
self.mixes << Mix.schedule(self)
save
end
@ -310,21 +310,6 @@ module JamRuby
end
=end
def base_mix_manifest
manifest = { "files" => [], "timeline" => [] }
mix_params = []
recorded_tracks.each do |recorded_track|
return nil unless recorded_track.fully_uploaded
manifest["files"] << { "filename" => recorded_track.sign_url(60 * 60 * 24), "codec" => "vorbis", "offset" => 0 }
mix_params << { "level" => 100, "balance" => 0 }
end
manifest["timeline"] << { "timestamp" => 0, "mix" => mix_params }
manifest["output"] = { "codec" => "vorbis" }
manifest["recording_id"] = self.id
manifest
end
private
def self.validate_user_is_band_member(user, band)
unless band.users.exists? user

View File

@ -6,12 +6,11 @@ require 'digest/md5'
module JamRuby
# executes a mix of tracks, creating a final output mix
class AudioMixer
@queue = :audiomixer
#extend Resque::Plugins::Retry
@@log = Logging.logger[AudioMixer]
attr_accessor :mix_id, :manifest, :manifest_file, :output_filename, :error_out_filename, :postback_output_url,
@ -210,7 +209,7 @@ module JamRuby
return
end
@manifest = symbolize_keys(JSON.parse(mix.manifest))
@manifest = symbolize_keys(mix.manifest)
@manifest[:mix_id] = mix_id # slip in the mix_id so that the job can add it to the ogg comments
# sanity check the manifest

View File

@ -0,0 +1,22 @@
require 'json'
require 'resque'
require 'resque-retry'
require 'net/http'
require 'digest/md5'
module JamRuby
# periodically scheduled to find jobs that need retrying
class AudioMixerRetry
@queue = :audiomixer_retry
@@log = Logging.logger[AudioMixerRetry]
def self.perform
Mix.queue_jobs_needing_retry
end
end
end

View File

@ -8,8 +8,10 @@ describe IcecastDirectory do
end
it "save" do
idir.save.should be_true
idir.dumpXml
end
end

View File

@ -16,13 +16,13 @@ describe IcecastLimit do
v = IcecastLimit.find(limit.id)
end
=begin
it "non-integer clients should be checked" do
limit.setclients 'a'
puts limit.clients
puts limit.inspect
limit.clients = 'a'
#puts limit.clients
#puts limit.inspect
limit.save.should be_false
limit.errors[:clients].should == ['is not a number']
limit.errors[:clients].should === ['is not a number']
end
=end
end

View File

@ -10,6 +10,7 @@ describe IcecastListenSocket do
it "save" do
iobj.save.should be_true
iobj.dumpXml
end
end

View File

@ -10,6 +10,7 @@ describe IcecastLogging do
it "save" do
iobj.save.should be_true
iobj.dumpXml
end
end

View File

@ -9,12 +9,13 @@ describe IcecastMastersvrRelay do
end
=begin
it "should not save" do
iobj.save.should be_false
iobj.errors[:master_server].should_equal "is required"
#puts iobj.errors.inspect
iobj.errors[:master_password].should === ["can't be blank"]
end
=end
it "should save" do
iobj.master_server = "test.www.com"
@ -22,6 +23,7 @@ describe IcecastMastersvrRelay do
iobj.master_username = "hack"
iobj.master_password = "hackme"
iobj.save.should be_true
#iobj.dumpXml
end
end

View File

@ -9,9 +9,36 @@ describe IcecastMount do
end
=begin
it "save" do
iobj.save.should be_true
iobj.build_authentication
@ua = iobj.authentication
@ua.username ="testcase"
iobj.mount_name = "Dhdhjd<>&%&&s@#$% gg8io9 fdfdj"
iobj.save
#puts iobj.inspect
iobj.dumpXml
iobj.errors.any?.should be_false
end
it "save 2" do
iobj.build_authentication
@ua = iobj.authentication
@ua.stype =""
iobj.mount_name = "No auth 473873434"
iobj.save
#puts iobj.inspect
#puts iobj.errors.inspect
iobj.dumpXml
iobj.errors.any?.should be_false
end
=end
end

View File

@ -10,6 +10,7 @@ describe IcecastPath do
it "save" do
iobj.save.should be_true
iobj.dumpXml
end
end

View File

@ -8,10 +8,15 @@ describe IcecastRelay do
end
=begin
it "save" do
:iobj.save.should be_true
iobj.server ="10.7.0.99"
iobj.mount ="/lcrock.ogg"
iobj.save.should be_true
iobj.dumpXml
end
=end
end

View File

@ -0,0 +1,20 @@
require 'spec_helper'
describe IcecastSecurity do
let(:iobj) { IcecastSecurity.new }
before(:all) do
end
it "save" do
iobj.changeowner_user ="hotdog"
iobj.changeowner_group ="mongrel"
iobj.save.should be_true
iobj.dumpXml
end
end

View File

@ -0,0 +1,68 @@
require 'spec_helper'
describe IcecastServer do
let(:iobj) { IcecastServer.new }
before(:all) do
end
=begin
it "save master" do
iobj.build_limit
iobj.build_adminauth
iobj.build_directory
iobj.build_logging
iobj.build_path
iobj.build_master_relay
iobj.build_misc
m1 = IcecastMount.new
m1.mount_name = "/m1hottstuff.og"
m2 = IcecastMount.new
m1.mount_name = "/m2.ogg"
sm = IcecastServermount.new
sm.build_mount= iobj,m1
sm.build_mount= iobj,m2
# iobj.mounts=m1,m2
iobj.save
puts iobj.errors.inspect
iobj.dumpXml
iobj.errors.any?.should be_false
end
it "save relay" do
iobj.build_limit
iobj.build_adminauth
iobj.build_directory
iobj.build_path
iobj.build_relay
iobj.build_misc
iobj.build_logging
iobj.build_security
m1 = IcecastMount.new
m1.mount_name = "/relaymout.ogg"
m2 = IcecastMount.new
m1.mount_name = "/local.ogg"
iobj.mounts=m1,m2
iobj.save
#puts iobj.inspect
iobj.dumpXml
iobj.errors.any?.should be_false
end
=end
end

View File

@ -0,0 +1,31 @@
require 'spec_helper'
describe IcecastUserAuthentication do
let(:iobj) { IcecastUserAuthentication.new }
before(:all) do
end
it "save" do
iobj.save
#puts iobj.inspect
iobj.dumpXml
iobj.errors.any?.should be_false
end
it "http autthenication" do
iobj.stype = 'htpasswd'
#puts limit.clients
#puts limit.inspect
iobj.filename ="/dfdfd</$%%%6566.auth"
iobj.save.should be_true
iobj.dumpXml
end
end

View File

@ -14,27 +14,17 @@ describe Mix do
@recording.stop
@recording.claim(@user, "name", "description", Genre.first, true, true)
@recording.errors.any?.should be_false
@mix = Mix.schedule(@recording, {})
@mix = Mix.schedule(@recording)
@mix.reload
end
it "should create a mix for a user's recording properly" do
@mix.recording_id.should == @recording.id
@mix.manifest.should == {}.to_json
@mix.mix_server.should be_nil
@mix.started_at.should be_nil
@mix.started_at.should_not be_nil
@mix.completed_at.should be_nil
end
it "should fail to create a mix if the userid doesn't own the recording" do
@user2 = FactoryGirl.create(:user)
expect { Mix.schedule(@recording) }.to raise_error
end
it "should fail if the recording doesn't exist" do
expect { @mix2 = Mix.schedule(Recording.find('lskdjflsd')) }.to raise_error
end
it "should record when a mix has finished" do
Mix.find(@mix.id).finish(10000, "md5hash")
@mix.reload

View File

@ -175,13 +175,15 @@ describe AudioMixer do
AudioMixer.any_instance.stub(:postback) # don't actually post resulting off file up
end
describe "perform" do
# this case does not talk to redis, does not run the real audiomixer, and does not actually talk with s3
# but it does talk to the database and verifies all the other logic
it "success" do
@mix = Mix.schedule(@recording, local_files_manifest)
Mix.any_instance.stub(:manifest).and_return(local_files_manifest) # don't actually post resulting off file up
@mix = Mix.schedule(@recording)
@mix.reload
@mix.started_at.should_not be_nil
AudioMixer.perform(@mix.id, @mix.sign_url(60 * 60 * 24))
@mix.reload
@mix.completed.should be_true
@ -191,7 +193,8 @@ describe AudioMixer do
it "errored" do
local_files_manifest[:files][0][:filename] = '/some/path/to/nowhere'
@mix = Mix.schedule(@recording, local_files_manifest)
Mix.any_instance.stub(:manifest).and_return(local_files_manifest)
@mix = Mix.schedule(@recording)
expect{ AudioMixer.perform(@mix.id, @mix.sign_url(60 * 60 * 24)) }.to raise_error
@mix.reload
@mix.completed.should be_false
@ -208,13 +211,15 @@ describe AudioMixer do
end
it "should have been enqueued because mix got scheduled" do
@mix = Mix.schedule(@recording, local_files_manifest)
Mix.any_instance.stub(:manifest).and_return(local_files_manifest)
@mix = Mix.schedule(@recording)
AudioMixer.should have_queue_size_of(1)
end
it "should actually run the job" do
with_resque do
@mix = Mix.schedule(@recording, local_files_manifest)
Mix.any_instance.stub(:manifest).and_return(local_files_manifest)
@mix = Mix.schedule(@recording)
end
@mix.reload
@ -225,7 +230,8 @@ describe AudioMixer do
it "bails out with no error if already completed" do
with_resque do
@mix = Mix.schedule(@recording, local_files_manifest)
Mix.any_instance.stub(:manifest).and_return(local_files_manifest)
@mix = Mix.schedule(@recording)
end
@mix.reload
@ -274,7 +280,8 @@ describe AudioMixer do
it "completes" do
with_resque do
@mix = Mix.schedule(@recording, s3_manifest)
Mix.any_instance.stub(:manifest).and_return(s3_manifest)
@mix = Mix.schedule(@recording)
end
@mix.reload
@ -286,7 +293,8 @@ describe AudioMixer do
it "fails" do
with_resque do
s3_manifest[:files][0][:filename] = @s3_manager.url('audiomixer/bogus.ogg', :secure => false) # take off some of the trailing chars of the url
expect{ Mix.schedule(@recording, s3_manifest) }.to raise_error
Mix.any_instance.stub(:manifest).and_return(s3_manifest)
expect{ Mix.schedule(@recording) }.to raise_error
end
@mix = Mix.order('id desc').limit(1).first()

View File

@ -61,6 +61,7 @@ gem 'haml-rails'
gem 'resque'
gem 'resque-retry'
gem 'resque-failed-job-mailer'
gem 'resque-dynamic-queues'
gem 'quiet_assets', :group => :development
@ -90,12 +91,12 @@ end
group :test, :cucumber do
gem 'capybara'
if ENV['JAMWEB_QT5'] == '1'
# necessary on platforms such as arch linux, where pacman -S qt5-webkit is your easiet option
gem "capybara-webkit", :git => 'git://github.com/thoughtbot/capybara-webkit.git'
else
#if ENV['JAMWEB_QT5'] == '1'
# # necessary on platforms such as arch linux, where pacman -S qt5-webkit is your easiet option
# gem "capybara-webkit", :git => 'git://github.com/thoughtbot/capybara-webkit.git'
#else
gem "capybara-webkit"
end
#end
gem 'capybara-screenshot'
gem 'cucumber-rails', :require => false #, '1.3.0', :require => false
gem 'guard-spork', '0.3.2'

View File

@ -10,14 +10,6 @@ class ApiMixesController < ApiController
respond_to :json
def schedule
begin
Mix.schedule(params[:recording_id], current_user, params[:description], params[:manifest])
respond_with responder: ApiResponder, :status => 204
rescue
render :json => { :message => "mix could not be scheduled" }, :status => 403
end
end
def next
begin

View File

@ -8,7 +8,8 @@ rm -rf $DIR/target
mkdir $DIR/target
mkdir $DIR/target/deb
rm -rf vendor/bundle
#rm -rf vendor/bundle -- let jenkins config wipe workspace, or not
rm -rf tmp/capybara
rm -f Gemfile.lock # if we don't want versions to float, pin it in the Gemfile, not count on Gemfile.lock
# put all dependencies into vendor/bundle

View File

@ -167,5 +167,15 @@ include JamRuby
config.redis_host = "localhost:6379"
config.audiomixer_path = "/var/lib/audiomixer/audiomixer/audiomixerapp"
config.email_alerts_alias = 'alerts@jamkazam.com' # should be used for 'oh no' server down/service down sorts of emails
config.email_generic_from = 'nobody@jamkazam.com'
config.email_smtp_address = 'smtp.sendgrid.net'
config.email_smtp_port = 587
config.email_smtp_domain = 'www.jamkazam.com'
config.email_smtp_authentication = :plain
config.email_smtp_user_name = 'jamkazam'
config.email_smtp_password = 'jamjamblueberryjam'
config.email_smtp_starttls_auto = true
end
end

View File

@ -1,6 +1,9 @@
rails_env = ENV['RAILS_ENV'] || "production"
rails_root = ENV['RAILS_ROOT'] || "/data/github/current"
num_workers = rails_env == 'production' ? 5 : 2
# this probably needs to be removed from jam-web source, and moved only to chef
rails_root = ENV['RAILS_ROOT'] || '.'
rails_env = ENV['RAILS_ENV'] || "development"
num_workers = ENV['NUM_WORKERS'] || 2
queues = ENV['QUEUE'] || '*'
num_workers.times do |num|
God.watch do |w|
@ -8,11 +11,11 @@ num_workers.times do |num|
w.name = "resque-#{num}"
w.group = 'resque'
w.interval = 30.seconds
w.env = {"QUEUE"=>"critical,high,low", "RAILS_ENV"=>rails_env}
w.start = "/usr/bin/rake -f #{rails_root}/Rakefile environment resque:work"
w.env = {"QUEUE"=>queues, "RAILS_ENV"=>rails_env}
w.start = "/usr/local/bin/bundle exec rake environment resque:work"
w.uid = 'git'
w.gid = 'git'
#w.uid = 'jam-resque'
#w.gid = 'jam-resque'
# restart if memory gets too high
w.transition(:up, :restart) do |on|

View File

@ -1,11 +1,11 @@
ActionMailer::Base.raise_delivery_errors = true
ActionMailer::Base.delivery_method = Rails.env == "test" ? :test : :smtp
ActionMailer::Base.smtp_settings = {
:address => "smtp.sendgrid.net",
:port => 587,
:domain => "www.jamkazam.com",
:authentication => :plain,
:user_name => "jamkazam",
:password => "jamjamblueberryjam",
:enable_starttls_auto => true
:address => Rails.application.config.email_smtp_address,
:port => Rails.application.config.email_smtp_port,
:domain => Rails.application.config.email_smtp_domain,
:authentication => Rails.application.config.email_smtp_authentication,
:user_name => Rails.application.config.email_smtp_user_name,
:password => Rails.application.config.email_smtp_password ,
:enable_starttls_auto => Rails.application.config.email_smtp_starttls_auto
}

View File

@ -1,6 +1,6 @@
require 'resque_failed_job_mailer'
Resque::Failure::Notifier.configure do |config|
config.from = 'seth@jamkazam.com'
config.to = 'seth@jamkazam.com'
config.from = Rails.application.config.email_alerts_alias
config.to = Rails.application.config.email_generic_from
end

View File

@ -296,8 +296,6 @@ SampleApp::Application.routes.draw do
match '/claimed_recordings/:id' => 'api_claimed_recordings#update', :via => :put
match '/claimed_recordings/:id' => 'api_claimed_recordings#delete', :via => :delete
# Mixes
match '/mixes/schedule' => 'api_mixes#schedule', :via => :post
match '/mixes/next' => 'api_mixes#next', :via => :get
match '/mixes/:id/finish' => 'api_mixes#finish', :via => :put
match '/mixes/:id/download' => 'api_mixes#download', :via => :get

5
web/config/scheduler.yml Normal file
View File

@ -0,0 +1,5 @@
# add job scheduler classes here
AudioMixerRetry:
cron: 0 * * * *
class: "JamRuby::AudioMixerRetry"
description: "Retries mixes that set the should_retry flag or never started"

View File

@ -0,0 +1,29 @@
# Resque tasks
require 'resque/tasks'
require 'resque_scheduler/tasks'
require 'resque'
require 'resque_scheduler'
task :scheduler => :environment do
# If you want to be able to dynamically change the schedule,
# uncomment this line. A dynamic schedule can be updated via the
# Resque::Scheduler.set_schedule (and remove_schedule) methods.
# When dynamic is set to true, the scheduler process looks for
# schedule changes and applies them on the fly.
# Note: This feature is only available in >=2.0.0.
#Resque::Scheduler.dynamic = true
# The schedule doesn't need to be stored in a YAML, it just needs to
# be a hash. YAML is usually the easiest.
Resque.schedule = YAML.load_file(File.join(File.dirname(__FILE__), '../..', 'config/scheduler.yml'))
# If your schedule already has +queue+ set for each job, you don't
# need to require your jobs. This can be an advantage since it's
# less code that resque-scheduler needs to know about. But in a small
# project, it's usually easier to just include you job classes here.
# So, something like this:
#require 'jobs'
Rake::Task['resque:scheduler'].invoke
end

View File

@ -1,14 +1,34 @@
# this rake file is meant to hold shortcuts/helpers for starting onerous command line executions
# bunde exec rake all_jobs
task :all_jobs do
Rake::Task['environment'].invoke
ENV['QUEUE'] = '*'
Rake::Task['resque:work'].invoke
end
# bundle exec rake audiomixer
task :audiomixer do
Rails.application.config.websocket_gateway_enable = false # prevent websocket gateway from starting
Rake::Task['environment'].invoke
ENV['QUEUE'] = 'audiomixer'
Rake::Task['resque:work'].invoke
end
# bundle exec rake icecast
task :icecast do
Rake::Task['environment'].invoke
ENV['QUEUE'] = 'icecast'
Rake::Task['resque:work'].invoke
end
# bundle exec rake odd_jobs
# this command is the same as used in production
task :odd_jobs do
Rake::Task['environment'].invoke
ENV['QUEUE'] = '*,!icecast,!audiomixer'
Rake::Task['resque:work'].invoke
end

View File

@ -1,19 +0,0 @@
#!/bin/bash -l
# default config values
PORT=3000
BUILD_NUMBER=`cat /var/lib/jam-web/BUILD_NUMBER`
CONFIG_FILE="/etc/jam-web/audiomixer-worker-upstart.conf"
if [ -e "$CONFIG_FILE" ]; then
. "$CONFIG_FILE"
fi
# I don't like doing this, but the next command (bundle exec) retouches/generates
# the gemfile. This unfortunately means the next debian update doesn't update this file.
# Ultimately this means an old Gemfile.lock is left behind for a new package,
# and bundle won't run because it thinks it has the wrong versions of gems
rm -f Gemfile.lock
RAILS_ENV=production BUILD_NUMBER=$BUILD_NUMBER QUEUE=audiomixer exec bundle exec rake environment resque:work

View File

@ -1,7 +0,0 @@
description "audiomixer-worker"
start on startup
start on runlevel [2345]
stop on runlevel [016]
exec start-stop-daemon --start --chdir /var/lib/jam-web --exec /var/lib/jam-web/script/package/audiomixer-worker-upstart-run.sh

View File

@ -19,21 +19,13 @@ chown -R $USER:$GROUP /var/lib/$NAME
chown -R $USER:$GROUP /etc/$NAME
chown -R $USER:$GROUP /var/log/$NAME
# make log folders for jobs
mkdir -p /var/log/audiomixer-worker
mkdir -p /var/log/icecast-worker
mkdir -p /var/log/any-job-worker
mkdir -p /var/log/scheduler-worker
# do the same for audiomixer-worker
NAME="audiomixer-worker"
USER="$NAME"
GROUP="$NAME"
# copy upstart file
cp /var/lib/$NAME/script/package/$NAME.conf /etc/init/$NAME.conf
mkdir -p /var/lib/$NAME/log
mkdir -p /var/lib/$NAME/tmp
mkdir -p /etc/$NAME
mkdir -p /var/log/$NAME
chown -R $USER:$GROUP /var/lib/$NAME
chown -R $USER:$GROUP /etc/$NAME
chown -R $USER:$GROUP /var/log/$NAME
chown -R $USER:$GROUP /var/log/audiomixer-worker
chown -R $USER:$GROUP /var/log/icecast-worker
chown -R $USER:$GROUP /var/log/any-job-worker
chown -R $USER:$GROUP /var/log/scheduler-worker

View File

@ -24,30 +24,4 @@ then
fi
userdel $NAME
fi
NAME="audiomixer-worker"
set -e
if [ "$1" = "remove" ]
then
set +e
# stop the process, if any is found. we don't want this failing to cause an error, though.
sudo stop $NAME
set -e
if [ -f /etc/init/$NAME.conf ]; then
rm /etc/init/$NAME.conf
fi
fi
if [ "$1" = "purge" ]
then
if [ -d /var/lib/$NAME ]; then
rm -rf /var/lib/$NAME
fi
userdel $NAME
fi
fi

View File

@ -34,42 +34,4 @@ then
fi
# NIS no longer a possible problem; stop ignoring errors
set -e
# do the same for audiomixer-worker
NAME="audiomixer-worker"
set -eu
HOME="/var/lib/$NAME"
USER="$NAME"
GROUP="$NAME"
# if NIS is used, then errors can occur but be non-fatal
if which ypwhich >/dev/null 2>&1 && ypwhich >/dev/null 2>&1
then
set +e
fi
if ! getent group "$GROUP" >/dev/null
then
addgroup --system "$GROUP" >/dev/null
fi
# creating user if it isn't already there
if ! getent passwd "$USER" >/dev/null
then
adduser \
--system \
--home $HOME \
--shell /bin/false \
--disabled-login \
--ingroup "$GROUP" \
--gecos "$USER" \
"$USER" >/dev/null
fi
# NIS no longer a possible problem; stop ignoring errors
set -e
set -e

View File

@ -39,7 +39,7 @@ describe "Music Session", :js => true, :type => :feature, :capybara_feature => t
describe "abruptly leaves music session" do
it "should delete connection and update music session user session history" do
#pending
pending "still intermittently fails on build server"
should have_link('session-leave')
page.evaluate_script("JK.JamServer.close(true)")
leave_music_session_sleep_delay

View File

@ -121,6 +121,7 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature
end
it "discard the recording" do
pending "fails intermittently on the build server"
in_client(creator) do
find('#recording-finished-dialog h1')
find('#discard-session-recording').trigger(:click)
@ -145,6 +146,7 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature
end
it "claim recording with unique names/descriptions" do
pending "intermittent failure on build server, hard to repro on local system"
@users.each do |user|
name = "#{user.name}'s recording"
desc = "#{user.name}'s description"
@ -177,6 +179,7 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature
end
it "a 'Description' is optional" do
pending "intermittent failure on build server, hard to repro on local system"
@users.each do |user|
in_client(user) do
claim_recording("my recording", '')

View File

@ -21,7 +21,7 @@ cp ../pb/target/ruby/jampb/jampb-${GEM_VERSION}.gem vendor/cache/ || { echo "una
cp ../ruby/jam_ruby-${GEM_VERSION}.gem vendor/cache/ || { echo "unable to copy jam-ruby gem"; exit 1; }
# put all dependencies into vendor/bundle
rm -rf vendor/bundle
#rm -rf vendor/bundle -- let checkins config 'wipe workspace' decide this
echo "updating dependencies"
bundle install --path vendor/bundle

View File

@ -1,3 +1,3 @@
module JamWebsockets
VERSION = "0.0.1"
VERSION = "0.1.1"
end