merge develop
This commit is contained in:
commit
30b13ed538
|
|
@ -54,6 +54,7 @@ gem 'gon'
|
||||||
gem 'resque'
|
gem 'resque'
|
||||||
gem 'resque-retry'
|
gem 'resque-retry'
|
||||||
gem 'resque-failed-job-mailer'
|
gem 'resque-failed-job-mailer'
|
||||||
|
gem 'resque-lonely_job', '~> 1.0.0'
|
||||||
|
|
||||||
gem 'eventmachine', '1.0.3'
|
gem 'eventmachine', '1.0.3'
|
||||||
gem 'amqp', '0.9.8'
|
gem 'amqp', '0.9.8'
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,18 @@ ActiveAdmin.register_page "Bootstrap" do
|
||||||
|
|
||||||
page_action :create_server, :method => :post do
|
page_action :create_server, :method => :post do
|
||||||
|
|
||||||
template = IcecastTemplate.find(params[:jam_ruby_icecast_server][:template_id])
|
template = IcecastTemplate.find_by_id(params[:jam_ruby_icecast_server][:template_id])
|
||||||
|
mount_template = IcecastMountTemplate.find_by_id(params[:jam_ruby_icecast_server][:mount_template_id])
|
||||||
hostname = params[:jam_ruby_icecast_server][:hostname]
|
hostname = params[:jam_ruby_icecast_server][:hostname]
|
||||||
|
|
||||||
server = IcecastServer.new
|
server = IcecastServer.new
|
||||||
server.template = template
|
server.template = template
|
||||||
|
server.mount_template = mount_template
|
||||||
server.hostname = hostname
|
server.hostname = hostname
|
||||||
server.server_id = hostname
|
server.server_id = hostname
|
||||||
server.save!
|
server.save!
|
||||||
|
|
||||||
redirect_to admin_bootstrap_path, :notice => "Server created. If you start job worker (bundle exec rake all_jobs), it should update your icecast config."
|
redirect_to admin_bootstrap_path, :notice => "Server created. If you start a job worker (bundle exec rake all_jobs in /web), it should update your icecast config."
|
||||||
end
|
end
|
||||||
|
|
||||||
page_action :brew_template, :method => :post do
|
page_action :brew_template, :method => :post do
|
||||||
|
|
@ -60,11 +62,11 @@ ActiveAdmin.register_page "Bootstrap" do
|
||||||
logging.save!
|
logging.save!
|
||||||
|
|
||||||
listen_socket1 = IcecastListenSocket.new
|
listen_socket1 = IcecastListenSocket.new
|
||||||
listen_socket1.port = 8000
|
listen_socket1.port = 9000
|
||||||
listen_socket1.save!
|
listen_socket1.save!
|
||||||
|
|
||||||
listen_socket2 = IcecastListenSocket.new
|
listen_socket2 = IcecastListenSocket.new
|
||||||
listen_socket2.port = 8001
|
listen_socket2.port = 9001
|
||||||
listen_socket2.save!
|
listen_socket2.save!
|
||||||
|
|
||||||
template = IcecastTemplate.new
|
template = IcecastTemplate.new
|
||||||
|
|
@ -81,23 +83,156 @@ ActiveAdmin.register_page "Bootstrap" do
|
||||||
template.save!
|
template.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
redirect_to admin_bootstrap_path, :notice => "Brew template created. Now, create a mount template."
|
||||||
|
end
|
||||||
|
|
||||||
redirect_to admin_bootstrap_path, :notice => "Brew template created. Create a server now with that template specified."
|
page_action :ubuntu_template, :method => :post do
|
||||||
|
# to make this template, I installed icecast233 from jenkins (or our chef'ed apt-repo, same difference), and then based the rest of this code on what I saw in /etc/icecast2/icecast.xml
|
||||||
|
|
||||||
|
IcecastServer.transaction do
|
||||||
|
|
||||||
|
limit = IcecastLimit.new
|
||||||
|
limit.clients = 100
|
||||||
|
limit.sources = 2
|
||||||
|
limit.queue_size = 524288
|
||||||
|
limit.client_timeout = 30
|
||||||
|
limit.header_timeout = 15
|
||||||
|
limit.source_timeout = 10
|
||||||
|
limit.burst_size = 65535
|
||||||
|
limit.save!
|
||||||
|
|
||||||
|
admin_auth = IcecastAdminAuthentication.new
|
||||||
|
admin_auth.source_pass = 'blueberryjam'
|
||||||
|
admin_auth.relay_user = 'jamjam'
|
||||||
|
admin_auth.relay_pass = 'blueberryjam'
|
||||||
|
admin_auth.admin_user = 'jamjam'
|
||||||
|
admin_auth.admin_pass = 'blueberryjam'
|
||||||
|
admin_auth.save!
|
||||||
|
path = IcecastPath.new
|
||||||
|
path.base_dir = '/usr/share/icecast2'
|
||||||
|
path.log_dir = '/var/log/icecast2'
|
||||||
|
path.web_root = '/usr/share/icecast2/web'
|
||||||
|
path.admin_root = '/usr/share/icecast2/admin'
|
||||||
|
path.pid_file = nil
|
||||||
|
path.save!
|
||||||
|
|
||||||
|
security = IcecastSecurity.new
|
||||||
|
security.chroot = false
|
||||||
|
security.save!
|
||||||
|
|
||||||
|
logging = IcecastLogging.new
|
||||||
|
logging.access_log = 'access.log'
|
||||||
|
logging.error_log = 'error.log'
|
||||||
|
logging.log_level = 3 # you might want to change this after creating the template
|
||||||
|
logging.log_size = 10000
|
||||||
|
logging.save!
|
||||||
|
|
||||||
|
listen_socket1 = IcecastListenSocket.new
|
||||||
|
listen_socket1.port = 9000
|
||||||
|
listen_socket1.save!
|
||||||
|
|
||||||
|
listen_socket2 = IcecastListenSocket.new
|
||||||
|
listen_socket2.port = 9001
|
||||||
|
listen_socket2.save!
|
||||||
|
|
||||||
|
template = IcecastTemplate.new
|
||||||
|
template.name = "Ubuntu-#{IcecastTemplate.count + 1}"
|
||||||
|
template.location = '@work'
|
||||||
|
template.admin_email = 'nobody@jamkazam.com'
|
||||||
|
template.fileserve = true
|
||||||
|
template.limit = limit
|
||||||
|
template.admin_auth = admin_auth
|
||||||
|
template.path = path
|
||||||
|
template.security = security
|
||||||
|
template.logging = logging
|
||||||
|
template.listen_sockets = [listen_socket1, listen_socket2]
|
||||||
|
template.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_bootstrap_path, :notice => "Ubuntu 12.04 template created. You should also install the icecast233 package: https://int.jamkazam.com/jenkins/job/icecast-debian/"
|
||||||
|
end
|
||||||
|
|
||||||
|
page_action :create_mount_template, :method => :post do
|
||||||
|
IcecastServer.transaction do
|
||||||
|
hostname = params[:jam_ruby_icecast_mount_template][:hostname]
|
||||||
|
type = params[:jam_ruby_icecast_mount_template][:default_mime_type]
|
||||||
|
|
||||||
|
auth = IcecastUserAuthentication.new
|
||||||
|
auth.authentication_type = 'url'
|
||||||
|
auth.mount_add = 'http://' + hostname + '/api/icecast/mount_add'
|
||||||
|
auth.mount_remove = 'http://' + hostname + '/api/icecast/mount_remove'
|
||||||
|
auth.listener_add = 'http://' + hostname + '/api/icecast/listener_add'
|
||||||
|
auth.listener_remove = 'http://' + hostname + '/api/icecast/listener_remove'
|
||||||
|
auth.auth_header = 'HTTP/1.1 200 OK'
|
||||||
|
auth.timelimit_header = 'icecast-auth-timelimit:'
|
||||||
|
auth.save!
|
||||||
|
|
||||||
|
mount_template = IcecastMountTemplate.new
|
||||||
|
mount_template.name = "#{type}-#{IcecastMountTemplate.count + 1}"
|
||||||
|
mount_template.source_username = nil # mount will override
|
||||||
|
mount_template.source_pass = nil # mount will override
|
||||||
|
mount_template.max_listeners = 20000 # huge
|
||||||
|
mount_template.max_listener_duration = 3600 * 24 # one day
|
||||||
|
mount_template.fallback_override = 1
|
||||||
|
mount_template.fallback_when_full = 1
|
||||||
|
mount_template.is_public = 0
|
||||||
|
mount_template.stream_name = nil # mount will override
|
||||||
|
mount_template.stream_description = nil # mount will override
|
||||||
|
mount_template.stream_url = nil # mount will override
|
||||||
|
mount_template.genre = nil # mount will override
|
||||||
|
mount_template.bitrate = 128
|
||||||
|
mount_template.burst_size = 65536
|
||||||
|
mount_template.hidden = 1
|
||||||
|
mount_template.on_connect = nil
|
||||||
|
mount_template.on_disconnect = nil
|
||||||
|
mount_template.authentication = auth
|
||||||
|
|
||||||
|
if type == 'ogg'
|
||||||
|
mount_template.mp3_metadata_interval = nil
|
||||||
|
mount_template.mime_type ='audio/ogg'
|
||||||
|
mount_template.subtype = 'vorbis'
|
||||||
|
mount_template.fallback_mount = "/fallback-#{mount_template.bitrate}.ogg"
|
||||||
|
else
|
||||||
|
mount_template.mp3_metadata_interval = 4096
|
||||||
|
mount_template.mime_type ='audio/mpeg'
|
||||||
|
mount_template.subtype = nil
|
||||||
|
mount_template.fallback_mount = "/fallback-#{mount_template.bitrate}.mp3"
|
||||||
|
end
|
||||||
|
mount_template.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_bootstrap_path, :notice => "Mount template created. Create a server now with your new templates specified."
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
action_item do
|
||||||
|
link_to "Create MacOSX (Brew) Template", admin_bootstrap_brew_template_path, :method => :post
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item do
|
action_item do
|
||||||
link_to "Create Brew Template", admin_bootstrap_brew_template_path, :method => :post
|
link_to "Create Ubuntu 12.04 Template", admin_bootstrap_ubuntu_template_path, :method => :post
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
content do
|
content do
|
||||||
|
|
||||||
if IcecastTemplate.count == 0
|
if IcecastTemplate.count == 0
|
||||||
para "You need to create at least one template for your environment"
|
para "You need to create at least one server template, and one mount template. Click one of the top-left buttons based on your platform"
|
||||||
|
|
||||||
|
elsif IcecastMountTemplate.count == 0
|
||||||
|
semantic_form_for IcecastMountTemplate.new, :url => admin_bootstrap_create_mount_template_path, :builder => ActiveAdmin::FormBuilder do |f|
|
||||||
|
f.inputs "New Mount Template" do
|
||||||
|
f.input :hostname, :label => "jam-web hostname:port"
|
||||||
|
f.input :default_mime_type, :as => :select, :collection => ["ogg", "mp3"]
|
||||||
|
end
|
||||||
|
f.actions
|
||||||
|
end
|
||||||
else
|
else
|
||||||
semantic_form_for IcecastServer.new, :url => admin_bootstrap_create_server_path, :builder => ActiveAdmin::FormBuilder do |f|
|
semantic_form_for IcecastServer.new, :url => admin_bootstrap_create_server_path, :builder => ActiveAdmin::FormBuilder do |f|
|
||||||
f.inputs "New Server" do
|
f.inputs "New Icecast Server" do
|
||||||
f.input :hostname
|
f.input :hostname, :hint => "Just the icecast hostname; no port"
|
||||||
f.input :template
|
f.input :template, :hint => "This is the template associated with the server. Not as useful for the 1st server, but subsequent servers can use this same template, and share config"
|
||||||
|
f.input :mount_template, :hint => "The mount template. When mounts are made as music sessions are created, this template will satisfy templatable values"
|
||||||
end
|
end
|
||||||
f.actions
|
f.actions
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
ActiveAdmin.register JamRuby::IcecastMountTemplate, :as => 'IcecastMountTemplate' do
|
||||||
|
menu :parent => 'Icecast'
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
ActiveAdmin.register JamRuby::IcecastServerGroup, :as => 'IcecastServerGroup' do
|
||||||
|
menu :parent => 'Icecast'
|
||||||
|
end
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,5 @@ module Utils
|
||||||
chars = ((('a'..'z').to_a + ('0'..'9').to_a) - %w(i o 0 1 l 0))
|
chars = ((('a'..'z').to_a + ('0'..'9').to_a) - %w(i o 0 1 l 0))
|
||||||
(1..size).collect{|a| cc = chars[rand(chars.size)]; 0==rand(2) ? cc.upcase : cc }.join
|
(1..size).collect{|a| cc = chars[rand(chars.size)]; 0==rand(2) ? cc.upcase : cc }.join
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
2
db/build
2
db/build
|
|
@ -19,7 +19,7 @@ rm -rf $TARGET
|
||||||
mkdir -p $PG_BUILD_OUT
|
mkdir -p $PG_BUILD_OUT
|
||||||
mkdir -p $PG_RUBY_PACKAGE_OUT
|
mkdir -p $PG_RUBY_PACKAGE_OUT
|
||||||
|
|
||||||
#bundle update
|
bundle update
|
||||||
|
|
||||||
echo "building migrations"
|
echo "building migrations"
|
||||||
bundle exec pg_migrate build --source . --out $PG_BUILD_OUT --test --verbose
|
bundle exec pg_migrate build --source . --out $PG_BUILD_OUT --test --verbose
|
||||||
|
|
|
||||||
|
|
@ -88,5 +88,8 @@ icecast.sql
|
||||||
home_page_promos.sql
|
home_page_promos.sql
|
||||||
mix_job_watch.sql
|
mix_job_watch.sql
|
||||||
music_session_constraints.sql
|
music_session_constraints.sql
|
||||||
|
mixes_drop_manifest_add_retry.sql
|
||||||
|
music_sessions_unlogged.sql
|
||||||
|
integrate_icecast_into_sessions.sql
|
||||||
ms_recording_anonymous_likes.sql
|
ms_recording_anonymous_likes.sql
|
||||||
ms_user_history_add_instruments.sql
|
ms_user_history_add_instruments.sql
|
||||||
|
|
@ -1,129 +1,96 @@
|
||||||
-- this manifest update makes every table associated with music_sessions UNLOGGED
|
|
||||||
|
|
||||||
-- tables to mark UNLOGGED
|
CREATE TABLE icecast_mount_templates(
|
||||||
-- connections, fan_invitations, invitations, genres_music_sessions, join_requests, tracks, music_sessions
|
id VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
|
name VARCHAR(256) NOT NULL,
|
||||||
-- tables to just get rid of
|
source_username VARCHAR(64),
|
||||||
-- session_plays
|
source_pass VARCHAR(64),
|
||||||
|
max_listeners INTEGER DEFAULT 4,
|
||||||
-- breaking foreign keys for tables
|
max_listener_duration INTEGER DEFAULT 3600,
|
||||||
-- connections: user_id
|
dump_file VARCHAR(1024),
|
||||||
-- fan_invitations: receiver_id, sender_id
|
intro VARCHAR(1024),
|
||||||
-- music_session: user_id, band_id, claimed_recording_id, claimed_recording_initiator_id
|
fallback_mount VARCHAR(1024),
|
||||||
-- genres_music_sessions: genre_id
|
fallback_override INTEGER DEFAULT 1,
|
||||||
-- invitations: sender_id, receiver_id
|
fallback_when_full INTEGER DEFAULT 1,
|
||||||
-- fan_invitations: user_id
|
charset VARCHAR(1024) DEFAULT 'ISO8859-1',
|
||||||
-- notifications: invitation_id, join_request_id, session_id
|
is_public INTEGER DEFAULT 0,
|
||||||
|
stream_name VARCHAR(1024),
|
||||||
DROP TABLE sessions_plays;
|
stream_description VARCHAR(10000),
|
||||||
|
stream_url VARCHAR(1024),
|
||||||
-- divorce notifications from UNLOGGED tables
|
genre VARCHAR(256),
|
||||||
|
bitrate INTEGER,
|
||||||
-- NOTIFICATIONS
|
mime_type VARCHAR(64) NOT NULL DEFAULT 'audio/mpeg',
|
||||||
----------------
|
subtype VARCHAR(64),
|
||||||
-- "notifications_session_id_fkey" FOREIGN KEY (session_id) REFERENCES music_sessions(id) ON DELETE CASCADE
|
burst_size INTEGER,
|
||||||
ALTER TABLE notifications DROP CONSTRAINT notifications_session_id_fkey;
|
mp3_metadata_interval INTEGER,
|
||||||
-- "notifications_join_request_id_fkey" FOREIGN KEY (join_request_id) REFERENCES join_requests(id) ON DELETE CASCADE
|
hidden INTEGER DEFAULT 1,
|
||||||
ALTER TABLE notifications DROP CONSTRAINT notifications_join_request_id_fkey;
|
on_connect VARCHAR(1024),
|
||||||
-- "notifications_invitation_id_fkey" FOREIGN KEY (invitation_id) REFERENCES invitations(id) ON DELETE CASCADE
|
on_disconnect VARCHAR(1024),
|
||||||
ALTER TABLE notifications DROP CONSTRAINT notifications_invitation_id_fkey;
|
authentication_id varchar(64) NOT NULL,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
-- FAN_INVITATIONS
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
------------------
|
|
||||||
DROP TABLE fan_invitations;
|
|
||||||
DROP TABLE invitations;
|
|
||||||
DROP TABLE join_requests;
|
|
||||||
DROP TABLE genres_music_sessions;
|
|
||||||
DROP TABLE tracks;
|
|
||||||
DROP TABLE connections;
|
|
||||||
DROP TABLE music_sessions;
|
|
||||||
|
|
||||||
-- MUSIC_SESSIONS
|
|
||||||
-----------------
|
|
||||||
CREATE UNLOGGED TABLE music_sessions (
|
|
||||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
|
||||||
description VARCHAR(8000),
|
|
||||||
user_id VARCHAR(64) NOT NULL,
|
|
||||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
|
||||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
|
||||||
musician_access BOOLEAN NOT NULL,
|
|
||||||
band_id VARCHAR(64),
|
|
||||||
approval_required BOOLEAN NOT NULL,
|
|
||||||
fan_access BOOLEAN NOT NULL,
|
|
||||||
fan_chat BOOLEAN NOT NULL,
|
|
||||||
claimed_recording_id VARCHAR(64),
|
|
||||||
claimed_recording_initiator_id VARCHAR(64)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
-- CONNECTIONS
|
ALTER TABLE icecast_mounts ALTER COLUMN mime_type DROP NOT NULL;
|
||||||
--------------
|
ALTER TABLE icecast_mounts ALTER COLUMN mime_type DROP DEFAULT;
|
||||||
CREATE UNLOGGED TABLE connections (
|
ALTER TABLE icecast_mounts ALTER COLUMN subtype DROP NOT NULL;
|
||||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
ALTER TABLE icecast_mounts ALTER COLUMN subtype DROP DEFAULT;
|
||||||
user_id VARCHAR(64),
|
ALTER TABLE icecast_mounts ADD COLUMN music_session_id VARCHAR(64) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
||||||
client_id VARCHAR(64) UNIQUE NOT NULL,
|
ALTER TABLE icecast_mounts ADD COLUMN icecast_server_id VARCHAR(64) NOT NULL REFERENCES icecast_servers(id);
|
||||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
ALTER TABLE icecast_mounts ADD COLUMN icecast_mount_template_id VARCHAR(64) REFERENCES icecast_mount_templates(id);
|
||||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
ALTER TABLE icecast_mounts ADD COLUMN sourced_needs_changing_at TIMESTAMP;
|
||||||
music_session_id VARCHAR(64),
|
;
|
||||||
ip_address VARCHAR(64),
|
CREATE TABLE icecast_server_groups (
|
||||||
as_musician BOOLEAN,
|
id VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
aasm_state VARCHAR(64) DEFAULT 'idle'::VARCHAR NOT NULL
|
name VARCHAR(255) UNIQUE NOT NULL
|
||||||
);
|
);
|
||||||
ALTER TABLE ONLY connections ADD CONSTRAINT connections_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE SET NULL;
|
-- bootstrap the default icecast group
|
||||||
|
INSERT INTO icecast_server_groups (id, name) VALUES ('default', 'default');
|
||||||
|
INSERT INTO icecast_server_groups (id, name) VALUES ('unused', 'unused');
|
||||||
|
|
||||||
-- GENRES_MUSIC_SESSIONS
|
ALTER TABLE users ADD COLUMN icecast_server_group_id VARCHAR(64) NOT NULL REFERENCES icecast_server_groups(id) DEFAULT 'default';
|
||||||
------------------------
|
|
||||||
CREATE UNLOGGED TABLE genres_music_sessions (
|
|
||||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
|
||||||
genre_id VARCHAR(64),
|
|
||||||
music_session_id VARCHAR(64)
|
|
||||||
);
|
|
||||||
ALTER TABLE ONLY genres_music_sessions ADD CONSTRAINT genres_music_sessions_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
CREATE UNLOGGED TABLE fan_invitations (
|
-- and by default, all servers and users are in this group
|
||||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
ALTER TABLE icecast_servers ADD COLUMN icecast_server_group_id VARCHAR(64) NOT NULL REFERENCES icecast_server_groups(id) DEFAULT 'default';
|
||||||
sender_id VARCHAR(64),
|
ALTER TABLE icecast_servers ADD COLUMN mount_template_id VARCHAR(64) REFERENCES icecast_mount_templates(id);
|
||||||
receiver_id VARCHAR(64),
|
|
||||||
music_session_id VARCHAR(64),
|
|
||||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
|
||||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL
|
|
||||||
);
|
|
||||||
ALTER TABLE ONLY fan_invitations ADD CONSTRAINT fan_invitations_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
CREATE UNLOGGED TABLE join_requests (
|
|
||||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
|
||||||
user_id VARCHAR(64),
|
|
||||||
music_session_id VARCHAR(64),
|
|
||||||
text VARCHAR(2000),
|
|
||||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
|
||||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL
|
|
||||||
);
|
|
||||||
ALTER TABLE ONLY join_requests ADD CONSTRAINT user_music_session_uniqkey UNIQUE (user_id, music_session_id);
|
|
||||||
ALTER TABLE ONLY join_requests ADD CONSTRAINT join_requests_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
-- INVITATIONS
|
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_admin_auth_id_fkey";
|
||||||
--------------
|
ALTER TABLE icecast_servers ADD CONSTRAINT "icecast_servers_admin_auth_id_fkey" FOREIGN KEY (admin_auth_id) REFERENCES icecast_admin_authentications(id) ON DELETE SET NULL;
|
||||||
CREATE UNLOGGED TABLE invitations (
|
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_mount_template_id_fkey";
|
||||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
ALTER TABLE icecast_servers ADD CONSTRAINT "icecast_servers_mount_template_id_fkey" FOREIGN KEY (mount_template_id) REFERENCES icecast_mount_templates(id) ON DELETE SET NULL;
|
||||||
sender_id VARCHAR(64),
|
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_directory_id_fkey";
|
||||||
receiver_id VARCHAR(64),
|
ALTER TABLE icecast_servers ADD CONSTRAINT "icecast_servers_directory_id_fkey" FOREIGN KEY (directory_id) REFERENCES icecast_directories(id) ON DELETE SET NULL;
|
||||||
music_session_id VARCHAR(64),
|
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_icecast_server_group_id_fkey";
|
||||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
ALTER TABLE icecast_servers ADD CONSTRAINT "icecast_servers_icecast_server_group_id_fkey" FOREIGN KEY (icecast_server_group_id) REFERENCES icecast_server_groups(id) ON DELETE SET NULL;
|
||||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_limit_id_fkey";
|
||||||
join_request_id VARCHAR(64)
|
ALTER TABLE icecast_servers ADD CONSTRAINT "icecast_servers_limit_id_fkey" FOREIGN KEY (limit_id) REFERENCES icecast_limits(id) ON DELETE SET NULL;
|
||||||
);
|
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_logging_id_fkey";
|
||||||
ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_uniqkey UNIQUE (sender_id, receiver_id, music_session_id);
|
ALTER TABLE icecast_servers ADD CONSTRAINT "icecast_servers_logging_id_fkey" FOREIGN KEY (logging_id) REFERENCES icecast_loggings(id) ON DELETE SET NULL;
|
||||||
ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_join_request_id_fkey FOREIGN KEY (join_request_id) REFERENCES join_requests(id) ON DELETE CASCADE;
|
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_master_relay_id_fkey";
|
||||||
ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
ALTER TABLE icecast_servers ADD CONSTRAINT "icecast_servers_master_relay_id_fkey" FOREIGN KEY (master_relay_id) REFERENCES icecast_master_server_relays(id) ON DELETE SET NULL;
|
||||||
|
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_path_id_fkey";
|
||||||
|
ALTER TABLE icecast_servers ADD CONSTRAINT "icecast_servers_path_id_fkey" FOREIGN KEY (path_id) REFERENCES icecast_paths(id) ON DELETE SET NULL;
|
||||||
|
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_security_id_fkey";
|
||||||
|
ALTER TABLE icecast_servers ADD CONSTRAINT "icecast_servers_security_id_fkey" FOREIGN KEY (security_id) REFERENCES icecast_securities(id) ON DELETE SET NULL;
|
||||||
|
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_template_id_fkey";
|
||||||
|
ALTER TABLE icecast_servers ADD CONSTRAINT "icecast_servers_template_id_fkey" FOREIGN KEY (template_id) REFERENCES icecast_templates(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
-- TRACKS
|
ALTER TABLE icecast_mounts DROP CONSTRAINT "icecast_mounts_icecast_mount_template_id_fkey";
|
||||||
---------
|
ALTER TABLE icecast_mounts ADD CONSTRAINT "icecast_mounts_icecast_mount_template_id_fkey" FOREIGN KEY (icecast_mount_template_id) REFERENCES icecast_mount_templates(id) ON DELETE SET NULL;
|
||||||
CREATE UNLOGGED TABLE tracks (
|
ALTER TABLE icecast_mounts DROP CONSTRAINT "icecast_mounts_icecast_server_id_fkey";
|
||||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
ALTER TABLE icecast_mounts ADD CONSTRAINT "icecast_mounts_icecast_server_id_fkey" FOREIGN KEY (icecast_server_id) REFERENCES icecast_servers(id) ON DELETE SET NULL;
|
||||||
connection_id VARCHAR(64),
|
|
||||||
instrument_id VARCHAR(64),
|
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_admin_auth_id_fkey";
|
||||||
sound VARCHAR(64) NOT NULL,
|
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_admin_auth_id_fkey" FOREIGN KEY (admin_auth_id) REFERENCES icecast_admin_authentications(id) ON DELETE SET NULL;
|
||||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_directory_id_fkey";
|
||||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_directory_id_fkey" FOREIGN KEY (directory_id) REFERENCES icecast_directories(id) ON DELETE SET NULL;
|
||||||
client_track_id VARCHAR(64) NOT NULL
|
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_limit_id_fkey";
|
||||||
);
|
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_limit_id_fkey" FOREIGN KEY (limit_id) REFERENCES icecast_limits(id) ON DELETE SET NULL;
|
||||||
ALTER TABLE ONLY tracks ADD CONSTRAINT connections_tracks_connection_id_fkey FOREIGN KEY (connection_id) REFERENCES connections(id) ON DELETE CASCADE;
|
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_logging_id_fkey";
|
||||||
|
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_logging_id_fkey" FOREIGN KEY (logging_id) REFERENCES icecast_loggings(id) ON DELETE SET NULL;
|
||||||
|
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_master_relay_id_fkey";
|
||||||
|
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_master_relay_id_fkey" FOREIGN KEY (master_relay_id) REFERENCES icecast_master_server_relays(id) ON DELETE SET NULL;
|
||||||
|
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_path_id_fkey";
|
||||||
|
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_path_id_fkey" FOREIGN KEY (path_id) REFERENCES icecast_paths(id) ON DELETE SET NULL;
|
||||||
|
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_security_id_fkey";
|
||||||
|
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_security_id_fkey" FOREIGN KEY (security_id) REFERENCES icecast_securities(id) ON DELETE SET NULL;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
-- this manifest update makes every table associated with music_sessions UNLOGGED
|
||||||
|
|
||||||
|
-- tables to mark UNLOGGED
|
||||||
|
-- connections, fan_invitations, invitations, genres_music_sessions, join_requests, tracks, music_sessions
|
||||||
|
|
||||||
|
-- breaking foreign keys for tables
|
||||||
|
-- connections: user_id
|
||||||
|
-- fan_invitations: receiver_id, sender_id
|
||||||
|
-- music_session: user_id, band_id, claimed_recording_id, claimed_recording_initiator_id
|
||||||
|
-- genres_music_sessions: genre_id
|
||||||
|
-- invitations: sender_id, receiver_id
|
||||||
|
-- fan_invitations: user_id
|
||||||
|
-- notifications: invitation_id, join_request_id, session_id
|
||||||
|
|
||||||
|
|
||||||
|
-- divorce notifications from UNLOGGED tables
|
||||||
|
|
||||||
|
DROP TABLE sessions_plays;
|
||||||
|
|
||||||
|
-- NOTIFICATIONS
|
||||||
|
----------------
|
||||||
|
-- "notifications_session_id_fkey" FOREIGN KEY (session_id) REFERENCES music_sessions(id) ON DELETE CASCADE
|
||||||
|
ALTER TABLE notifications DROP CONSTRAINT notifications_session_id_fkey;
|
||||||
|
-- "notifications_join_request_id_fkey" FOREIGN KEY (join_request_id) REFERENCES join_requests(id) ON DELETE CASCADE
|
||||||
|
ALTER TABLE notifications DROP CONSTRAINT notifications_join_request_id_fkey;
|
||||||
|
-- "notifications_invitation_id_fkey" FOREIGN KEY (invitation_id) REFERENCES invitations(id) ON DELETE CASCADE
|
||||||
|
ALTER TABLE notifications DROP CONSTRAINT notifications_invitation_id_fkey;
|
||||||
|
|
||||||
|
-- FAN_INVITATIONS
|
||||||
|
------------------
|
||||||
|
DROP TABLE fan_invitations;
|
||||||
|
DROP TABLE invitations;
|
||||||
|
DROP TABLE join_requests;
|
||||||
|
DROP TABLE genres_music_sessions;
|
||||||
|
DROP TABLE tracks;
|
||||||
|
DROP TABLE connections;
|
||||||
|
DROP TABLE music_sessions;
|
||||||
|
|
||||||
|
-- MUSIC_SESSIONS
|
||||||
|
-----------------
|
||||||
|
CREATE UNLOGGED TABLE music_sessions (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||||
|
description VARCHAR(8000),
|
||||||
|
user_id VARCHAR(64) NOT NULL,
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||||
|
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||||
|
musician_access BOOLEAN NOT NULL,
|
||||||
|
band_id VARCHAR(64),
|
||||||
|
approval_required BOOLEAN NOT NULL,
|
||||||
|
fan_access BOOLEAN NOT NULL,
|
||||||
|
fan_chat BOOLEAN NOT NULL,
|
||||||
|
claimed_recording_id VARCHAR(64),
|
||||||
|
claimed_recording_initiator_id VARCHAR(64)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CONNECTIONS
|
||||||
|
--------------
|
||||||
|
CREATE UNLOGGED TABLE connections (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||||
|
user_id VARCHAR(64),
|
||||||
|
client_id VARCHAR(64) UNIQUE NOT NULL,
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||||
|
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||||
|
music_session_id VARCHAR(64),
|
||||||
|
ip_address VARCHAR(64),
|
||||||
|
as_musician BOOLEAN,
|
||||||
|
aasm_state VARCHAR(64) DEFAULT 'idle'::VARCHAR NOT NULL
|
||||||
|
);
|
||||||
|
ALTER TABLE ONLY connections ADD CONSTRAINT connections_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE SET NULL;
|
||||||
|
|
||||||
|
-- GENRES_MUSIC_SESSIONS
|
||||||
|
------------------------
|
||||||
|
CREATE UNLOGGED TABLE genres_music_sessions (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||||
|
genre_id VARCHAR(64),
|
||||||
|
music_session_id VARCHAR(64)
|
||||||
|
);
|
||||||
|
ALTER TABLE ONLY genres_music_sessions ADD CONSTRAINT genres_music_sessions_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
CREATE UNLOGGED TABLE fan_invitations (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||||
|
sender_id VARCHAR(64),
|
||||||
|
receiver_id VARCHAR(64),
|
||||||
|
music_session_id VARCHAR(64),
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||||
|
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL
|
||||||
|
);
|
||||||
|
ALTER TABLE ONLY fan_invitations ADD CONSTRAINT fan_invitations_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
CREATE UNLOGGED TABLE join_requests (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||||
|
user_id VARCHAR(64),
|
||||||
|
music_session_id VARCHAR(64),
|
||||||
|
text VARCHAR(2000),
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||||
|
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL
|
||||||
|
);
|
||||||
|
ALTER TABLE ONLY join_requests ADD CONSTRAINT user_music_session_uniqkey UNIQUE (user_id, music_session_id);
|
||||||
|
ALTER TABLE ONLY join_requests ADD CONSTRAINT join_requests_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
-- INVITATIONS
|
||||||
|
--------------
|
||||||
|
CREATE UNLOGGED TABLE invitations (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||||
|
sender_id VARCHAR(64),
|
||||||
|
receiver_id VARCHAR(64),
|
||||||
|
music_session_id VARCHAR(64),
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||||
|
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||||
|
join_request_id VARCHAR(64)
|
||||||
|
);
|
||||||
|
ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_uniqkey UNIQUE (sender_id, receiver_id, music_session_id);
|
||||||
|
ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_join_request_id_fkey FOREIGN KEY (join_request_id) REFERENCES join_requests(id) ON DELETE CASCADE;
|
||||||
|
ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
-- TRACKS
|
||||||
|
---------
|
||||||
|
CREATE UNLOGGED TABLE tracks (
|
||||||
|
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||||
|
connection_id VARCHAR(64),
|
||||||
|
instrument_id VARCHAR(64),
|
||||||
|
sound VARCHAR(64) NOT NULL,
|
||||||
|
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||||
|
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||||
|
client_track_id VARCHAR(64) NOT NULL
|
||||||
|
);
|
||||||
|
ALTER TABLE ONLY tracks ADD CONSTRAINT connections_tracks_connection_id_fkey FOREIGN KEY (connection_id) REFERENCES connections(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
@ -9,67 +9,69 @@ package jampb;
|
||||||
|
|
||||||
message ClientMessage {
|
message ClientMessage {
|
||||||
enum Type {
|
enum Type {
|
||||||
LOGIN = 100;
|
LOGIN = 100;
|
||||||
LOGIN_ACK = 105;
|
LOGIN_ACK = 105;
|
||||||
LOGIN_MUSIC_SESSION = 110;
|
LOGIN_MUSIC_SESSION = 110;
|
||||||
LOGIN_MUSIC_SESSION_ACK = 115;
|
LOGIN_MUSIC_SESSION_ACK = 115;
|
||||||
LEAVE_MUSIC_SESSION = 120;
|
LEAVE_MUSIC_SESSION = 120;
|
||||||
LEAVE_MUSIC_SESSION_ACK = 125;
|
LEAVE_MUSIC_SESSION_ACK = 125;
|
||||||
HEARTBEAT = 130;
|
HEARTBEAT = 130;
|
||||||
HEARTBEAT_ACK = 135;
|
HEARTBEAT_ACK = 135;
|
||||||
|
|
||||||
// friend notifications
|
// friend notifications
|
||||||
FRIEND_UPDATE = 140;
|
FRIEND_UPDATE = 140;
|
||||||
FRIEND_REQUEST = 145;
|
FRIEND_REQUEST = 145;
|
||||||
FRIEND_REQUEST_ACCEPTED = 150;
|
FRIEND_REQUEST_ACCEPTED = 150;
|
||||||
FRIEND_SESSION_JOIN = 155;
|
FRIEND_SESSION_JOIN = 155;
|
||||||
NEW_USER_FOLLOWER = 160;
|
NEW_USER_FOLLOWER = 160;
|
||||||
NEW_BAND_FOLLOWER = 161;
|
NEW_BAND_FOLLOWER = 161;
|
||||||
|
|
||||||
// session invitations
|
// session invitations
|
||||||
SESSION_INVITATION = 165;
|
SESSION_INVITATION = 165;
|
||||||
SESSION_ENDED = 170;
|
SESSION_ENDED = 170;
|
||||||
JOIN_REQUEST = 175;
|
JOIN_REQUEST = 175;
|
||||||
JOIN_REQUEST_APPROVED = 180;
|
JOIN_REQUEST_APPROVED = 180;
|
||||||
JOIN_REQUEST_REJECTED = 185;
|
JOIN_REQUEST_REJECTED = 185;
|
||||||
SESSION_JOIN = 190;
|
SESSION_JOIN = 190;
|
||||||
SESSION_DEPART = 195;
|
SESSION_DEPART = 195;
|
||||||
MUSICIAN_SESSION_JOIN = 196;
|
MUSICIAN_SESSION_JOIN = 196;
|
||||||
|
|
||||||
// recording notifications
|
// recording notifications
|
||||||
MUSICIAN_RECORDING_SAVED = 200;
|
MUSICIAN_RECORDING_SAVED = 200;
|
||||||
BAND_RECORDING_SAVED = 205;
|
BAND_RECORDING_SAVED = 205;
|
||||||
RECORDING_STARTED = 210;
|
RECORDING_STARTED = 210;
|
||||||
RECORDING_ENDED = 215;
|
RECORDING_ENDED = 215;
|
||||||
RECORDING_MASTER_MIX_COMPLETE = 220;
|
RECORDING_MASTER_MIX_COMPLETE = 220;
|
||||||
DOWNLOAD_AVAILABLE = 221;
|
DOWNLOAD_AVAILABLE = 221;
|
||||||
|
|
||||||
// band notifications
|
// band notifications
|
||||||
BAND_INVITATION = 225;
|
BAND_INVITATION = 225;
|
||||||
BAND_INVITATION_ACCEPTED = 230;
|
BAND_INVITATION_ACCEPTED = 230;
|
||||||
BAND_SESSION_JOIN = 235;
|
BAND_SESSION_JOIN = 235;
|
||||||
|
|
||||||
MUSICIAN_SESSION_FRESH = 240;
|
MUSICIAN_SESSION_FRESH = 240;
|
||||||
MUSICIAN_SESSION_STALE = 245;
|
MUSICIAN_SESSION_STALE = 245;
|
||||||
|
|
||||||
|
|
||||||
// icecast notifications
|
// icecast notifications
|
||||||
SOURCE_UP_REQUESTED = 250;
|
SOURCE_UP_REQUESTED = 250;
|
||||||
SOURCE_DOWN_REQUESTED = 255;
|
SOURCE_DOWN_REQUESTED = 251;
|
||||||
|
SOURCE_UP = 252;
|
||||||
|
SOURCE_DOWN = 253;
|
||||||
|
|
||||||
TEST_SESSION_MESSAGE = 295;
|
TEST_SESSION_MESSAGE = 295;
|
||||||
|
|
||||||
PING_REQUEST = 300;
|
PING_REQUEST = 300;
|
||||||
PING_ACK = 305;
|
PING_ACK = 305;
|
||||||
PEER_MESSAGE = 310;
|
PEER_MESSAGE = 310;
|
||||||
TEST_CLIENT_MESSAGE = 315;
|
TEST_CLIENT_MESSAGE = 315;
|
||||||
|
|
||||||
SERVER_BAD_STATE_RECOVERED = 900;
|
SERVER_BAD_STATE_RECOVERED = 900;
|
||||||
|
|
||||||
SERVER_GENERIC_ERROR = 1000;
|
SERVER_GENERIC_ERROR = 1000;
|
||||||
SERVER_REJECTION_ERROR = 1005;
|
SERVER_REJECTION_ERROR = 1005;
|
||||||
SERVER_PERMISSION_ERROR = 1010;
|
SERVER_PERMISSION_ERROR = 1010;
|
||||||
SERVER_BAD_STATE_ERROR = 1015;
|
SERVER_BAD_STATE_ERROR = 1015;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identifies which inner message is filled in
|
// Identifies which inner message is filled in
|
||||||
|
|
@ -125,7 +127,9 @@ message ClientMessage {
|
||||||
|
|
||||||
// icecast notifications
|
// icecast notifications
|
||||||
optional SourceUpRequested source_up_requested = 250;
|
optional SourceUpRequested source_up_requested = 250;
|
||||||
optional SourceDownRequested source_down_requested = 255;
|
optional SourceDownRequested source_down_requested = 251;
|
||||||
|
optional SourceUp source_up = 252;
|
||||||
|
optional SourceDown source_down = 253;
|
||||||
|
|
||||||
// Client-Session messages (to/from)
|
// Client-Session messages (to/from)
|
||||||
optional TestSessionMessage test_session_message = 295;
|
optional TestSessionMessage test_session_message = 295;
|
||||||
|
|
@ -380,15 +384,26 @@ message MusicianSessionStale {
|
||||||
}
|
}
|
||||||
|
|
||||||
message SourceUpRequested {
|
message SourceUpRequested {
|
||||||
optional string host = 1; // icecast server host
|
optional string music_session = 1; // music session id
|
||||||
optional int32 port = 2; // icecast server port
|
optional string host = 2; // icecast server host
|
||||||
optional string mount = 3; // mount name
|
optional int32 port = 3; // icecast server port
|
||||||
optional string source_user = 4; // source user
|
optional string mount = 4; // mount name
|
||||||
optional string source_pass = 5; // source pass
|
optional string source_user = 5; // source user
|
||||||
|
optional string source_pass = 6; // source pass
|
||||||
|
optional int32 bitrate = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SourceDownRequested {
|
message SourceDownRequested {
|
||||||
optional string mount = 1; // mount name
|
optional string music_session = 1; // music session id
|
||||||
|
optional string mount = 2; // mount name
|
||||||
|
}
|
||||||
|
|
||||||
|
message SourceUp {
|
||||||
|
optional string music_session = 1; // music session id
|
||||||
|
}
|
||||||
|
|
||||||
|
message SourceDown {
|
||||||
|
optional string music_session = 1; // music session id
|
||||||
}
|
}
|
||||||
|
|
||||||
// route_to: session
|
// route_to: session
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ gem 'postgres_ext'
|
||||||
gem 'resque'
|
gem 'resque'
|
||||||
gem 'resque-retry'
|
gem 'resque-retry'
|
||||||
gem 'resque-failed-job-mailer' #, :path => "/Users/seth/workspace/resque_failed_job_mailer"
|
gem 'resque-failed-job-mailer' #, :path => "/Users/seth/workspace/resque_failed_job_mailer"
|
||||||
|
gem 'resque-lonely_job', '~> 1.0.0'
|
||||||
gem 'oj'
|
gem 'oj'
|
||||||
gem 'builder'
|
gem 'builder'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ require "jam_ruby/resque/audiomixer"
|
||||||
require "jam_ruby/resque/icecast_config_writer"
|
require "jam_ruby/resque/icecast_config_writer"
|
||||||
require "jam_ruby/resque/scheduled/audiomixer_retry"
|
require "jam_ruby/resque/scheduled/audiomixer_retry"
|
||||||
require "jam_ruby/resque/scheduled/icecast_config_retry"
|
require "jam_ruby/resque/scheduled/icecast_config_retry"
|
||||||
|
require "jam_ruby/resque/scheduled/icecast_source_check"
|
||||||
require "jam_ruby/mq_router"
|
require "jam_ruby/mq_router"
|
||||||
require "jam_ruby/base_manager"
|
require "jam_ruby/base_manager"
|
||||||
require "jam_ruby/connection_manager"
|
require "jam_ruby/connection_manager"
|
||||||
|
|
@ -113,6 +114,9 @@ require "jam_ruby/models/icecast_server_mount"
|
||||||
require "jam_ruby/models/icecast_server_relay"
|
require "jam_ruby/models/icecast_server_relay"
|
||||||
require "jam_ruby/models/icecast_server_socket"
|
require "jam_ruby/models/icecast_server_socket"
|
||||||
require "jam_ruby/models/icecast_template_socket"
|
require "jam_ruby/models/icecast_template_socket"
|
||||||
|
require "jam_ruby/models/icecast_server_group"
|
||||||
|
require "jam_ruby/models/icecast_mount_template"
|
||||||
|
|
||||||
|
|
||||||
include Jampb
|
include Jampb
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,12 @@ module JamRuby
|
||||||
return friend_ids
|
return friend_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# this simulates music_session destroy callbacks with activerecord
|
||||||
|
def before_destroy_music_session(music_session_id)
|
||||||
|
music_session = MusicSession.find_by_id(music_session_id)
|
||||||
|
music_session.before_destroy if music_session
|
||||||
|
end
|
||||||
|
|
||||||
# reclaim the existing connection,
|
# reclaim the existing connection,
|
||||||
def reconnect(conn, reconnect_music_session_id)
|
def reconnect(conn, reconnect_music_session_id)
|
||||||
music_session_id = nil
|
music_session_id = nil
|
||||||
|
|
@ -218,6 +224,7 @@ SQL
|
||||||
|
|
||||||
# same for session-if we are down to the last participant, delete the session
|
# same for session-if we are down to the last participant, delete the session
|
||||||
unless music_session_id.nil?
|
unless music_session_id.nil?
|
||||||
|
before_destroy_music_session(music_session_id)
|
||||||
result = conn.exec("DELETE FROM music_sessions WHERE id = $1 AND 0 = (select count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id])
|
result = conn.exec("DELETE FROM music_sessions WHERE id = $1 AND 0 = (select count(music_session_id) FROM connections where music_session_id = $1)", [music_session_id])
|
||||||
if result.cmd_tuples == 1
|
if result.cmd_tuples == 1
|
||||||
music_session_id = nil
|
music_session_id = nil
|
||||||
|
|
@ -258,6 +265,7 @@ SQL
|
||||||
end
|
end
|
||||||
if num_participants == 0
|
if num_participants == 0
|
||||||
# delete the music_session
|
# delete the music_session
|
||||||
|
before_destroy_music_session(previous_music_session_id)
|
||||||
conn.exec("DELETE from music_sessions WHERE id = $1",
|
conn.exec("DELETE from music_sessions WHERE id = $1",
|
||||||
[previous_music_session_id]) do |result|
|
[previous_music_session_id]) do |result|
|
||||||
if result.cmd_tuples == 1
|
if result.cmd_tuples == 1
|
||||||
|
|
|
||||||
|
|
@ -558,13 +558,15 @@ module JamRuby
|
||||||
|
|
||||||
# create a source up requested message to send to clients in a session,
|
# create a source up requested message to send to clients in a session,
|
||||||
# so that one of the clients will start sending source audio to icecast
|
# so that one of the clients will start sending source audio to icecast
|
||||||
def source_up_requested (session_id, host, port, mount, source_user, source_pass)
|
def source_up_requested (session_id, host, port, mount, source_user, source_pass, bitrate)
|
||||||
source_up_requested = Jampb::SourceUpRequested.new(
|
source_up_requested = Jampb::SourceUpRequested.new(
|
||||||
|
music_session: session_id,
|
||||||
host: host,
|
host: host,
|
||||||
port: port,
|
port: port,
|
||||||
mount: mount,
|
mount: mount,
|
||||||
source_user: source_user,
|
source_user: source_user,
|
||||||
source_pass: source_pass)
|
source_pass: source_pass,
|
||||||
|
bitrate: bitrate)
|
||||||
|
|
||||||
Jampb::ClientMessage.new(
|
Jampb::ClientMessage.new(
|
||||||
type: ClientMessage::Type::SOURCE_UP_REQUESTED,
|
type: ClientMessage::Type::SOURCE_UP_REQUESTED,
|
||||||
|
|
@ -575,7 +577,7 @@ module JamRuby
|
||||||
# create a source up requested message to send to clients in a session,
|
# create a source up requested message to send to clients in a session,
|
||||||
# so that one of the clients will start sending source audio to icecast
|
# so that one of the clients will start sending source audio to icecast
|
||||||
def source_down_requested (session_id, mount)
|
def source_down_requested (session_id, mount)
|
||||||
source_down_requested = Jampb::SourceDownRequested.new(mount: mount)
|
source_down_requested = Jampb::SourceDownRequested.new(music_session: session_id, mount: mount)
|
||||||
|
|
||||||
Jampb::ClientMessage.new(
|
Jampb::ClientMessage.new(
|
||||||
type: ClientMessage::Type::SOURCE_DOWN_REQUESTED,
|
type: ClientMessage::Type::SOURCE_DOWN_REQUESTED,
|
||||||
|
|
@ -583,6 +585,27 @@ module JamRuby
|
||||||
source_down_requested: source_down_requested)
|
source_down_requested: source_down_requested)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# let's someone know that the source came online. the stream activate shortly
|
||||||
|
# it might be necessary to refresh the client
|
||||||
|
def source_up (session_id)
|
||||||
|
source_up = Jampb::SourceUp.new(music_session: session_id)
|
||||||
|
|
||||||
|
Jampb::ClientMessage.new(
|
||||||
|
type: ClientMessage::Type::SOURCE_UP,
|
||||||
|
route_to: SESSION_TARGET_PREFIX + session_id,
|
||||||
|
source_up: source_up)
|
||||||
|
end
|
||||||
|
|
||||||
|
# let's someone know that the source went down. the stream will go offline
|
||||||
|
def source_down (session_id)
|
||||||
|
source_down = Jampb::SourceDown.new(music_session: session_id)
|
||||||
|
|
||||||
|
Jampb::ClientMessage.new(
|
||||||
|
type: ClientMessage::Type::SOURCE_DOWN,
|
||||||
|
route_to: SESSION_TARGET_PREFIX + session_id,
|
||||||
|
source_down: source_down)
|
||||||
|
end
|
||||||
|
|
||||||
# create a test message to send in session
|
# create a test message to send in session
|
||||||
def test_session_message(session_id, msg)
|
def test_session_message(session_id, msg)
|
||||||
test = Jampb::TestSessionMessage.new(:msg => msg)
|
test = Jampb::TestSessionMessage.new(:msg => msg)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ module JamRuby
|
||||||
PRODUCTS = ['JamClient/Win32', 'JamClient/MacOSX']
|
PRODUCTS = ['JamClient/Win32', 'JamClient/MacOSX']
|
||||||
|
|
||||||
self.primary_key = 'id'
|
self.primary_key = 'id'
|
||||||
attr_accessible :version, :uri, :sha1, :environment, :product
|
attr_accessible :version, :uri, :sha1, :environment, :product, as: :admin
|
||||||
|
|
||||||
|
|
||||||
mount_uploader :uri, ArtifactUploader
|
mount_uploader :uri, ArtifactUploader
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,5 @@ module JamRuby
|
||||||
|
|
||||||
# music sessions
|
# music sessions
|
||||||
has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions"
|
has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions"
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -12,7 +12,8 @@ module JamRuby
|
||||||
validates :relay_pass, presence: true, length: {minimum: 5}
|
validates :relay_pass, presence: true, length: {minimum: 5}
|
||||||
validates :admin_user, presence: true, length: {minimum: 5}
|
validates :admin_user, presence: true, length: {minimum: 5}
|
||||||
|
|
||||||
after_save :poke_config
|
before_destroy :poke_config
|
||||||
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
IcecastServer.update(servers, config_changed: 1)
|
IcecastServer.update(servers, config_changed: 1)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ module JamRuby
|
||||||
validates :yp_url_timeout, presence: true, numericality: {only_integer: true}, length: {in: 1..30}
|
validates :yp_url_timeout, presence: true, numericality: {only_integer: true}, length: {in: 1..30}
|
||||||
validates :yp_url, presence: true
|
validates :yp_url, presence: true
|
||||||
|
|
||||||
after_save :poke_config
|
before_destroy :poke_config
|
||||||
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
IcecastServer.update(servers, config_changed: 1)
|
IcecastServer.update(servers, config_changed: 1)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ module JamRuby
|
||||||
validates :source_timeout, presence: true, numericality: {only_integer: true}
|
validates :source_timeout, presence: true, numericality: {only_integer: true}
|
||||||
validates :burst_size, presence: true, numericality: {only_integer: true}
|
validates :burst_size, presence: true, numericality: {only_integer: true}
|
||||||
|
|
||||||
after_save :poke_config
|
before_destroy :poke_config
|
||||||
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
IcecastServer.update(servers, config_changed: 1)
|
IcecastServer.update(servers, config_changed: 1)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ module JamRuby
|
||||||
validates :port, presence: true, numericality: {only_integer: true}, length: {in: 1..65535}
|
validates :port, presence: true, numericality: {only_integer: true}, length: {in: 1..65535}
|
||||||
validates :shoutcast_compat, :inclusion => {:in => [nil, 0, 1]}
|
validates :shoutcast_compat, :inclusion => {:in => [nil, 0, 1]}
|
||||||
|
|
||||||
after_save :poke_config
|
before_destroy :poke_config
|
||||||
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
IcecastServer.update(servers, config_changed: 1)
|
IcecastServer.update(servers, config_changed: 1)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ module JamRuby
|
||||||
validates :log_archive, :inclusion => {:in => [nil, 0, 1]}
|
validates :log_archive, :inclusion => {:in => [nil, 0, 1]}
|
||||||
validates :log_size, numericality: {only_integer: true}, if: lambda {|m| m.log_size.present?}
|
validates :log_size, numericality: {only_integer: true}, if: lambda {|m| m.log_size.present?}
|
||||||
|
|
||||||
after_save :poke_config
|
before_destroy :poke_config
|
||||||
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
IcecastServer.update(servers, config_changed: 1)
|
IcecastServer.update(servers, config_changed: 1)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ module JamRuby
|
||||||
validates :master_pass, presence: true, length: {minimum: 5}
|
validates :master_pass, presence: true, length: {minimum: 5}
|
||||||
validates :relays_on_demand, :inclusion => {:in => [0, 1]}
|
validates :relays_on_demand, :inclusion => {:in => [0, 1]}
|
||||||
|
|
||||||
after_save :poke_config
|
before_destroy :poke_config
|
||||||
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
IcecastServer.update(servers, config_changed: 1)
|
IcecastServer.update(servers, config_changed: 1)
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,22 @@
|
||||||
module JamRuby
|
module JamRuby
|
||||||
class IcecastMount < ActiveRecord::Base
|
class IcecastMount < ActiveRecord::Base
|
||||||
|
|
||||||
|
@@log = Logging.logger[IcecastMount]
|
||||||
|
|
||||||
attr_accessible :authentication_id, :name, :source_username, :source_pass, :max_listeners, :max_listener_duration,
|
attr_accessible :authentication_id, :name, :source_username, :source_pass, :max_listeners, :max_listener_duration,
|
||||||
:dump_file, :intro, :fallback_mount, :fallback_override, :fallback_when_full, :charset, :is_public,
|
:dump_file, :intro, :fallback_mount, :fallback_override, :fallback_when_full, :charset, :is_public,
|
||||||
:stream_name, :stream_description, :stream_url, :genre, :bitrate, :mime_type, :subtype, :burst_size,
|
:stream_name, :stream_description, :stream_url, :genre, :bitrate, :mime_type, :subtype, :burst_size,
|
||||||
:mp3_metadata_interval, :hidden, :on_connect, :on_disconnect, as: :admin
|
:mp3_metadata_interval, :hidden, :on_connect, :on_disconnect,
|
||||||
|
:music_session_id, :icecast_server_id, :icecast_mount_template_id, :listeners, :sourced,
|
||||||
|
:sourced_needs_changing_at, as: :admin
|
||||||
|
|
||||||
belongs_to :authentication, class_name: "JamRuby::IcecastUserAuthentication", inverse_of: :mount, :foreign_key => 'authentication_id'
|
belongs_to :authentication, class_name: "JamRuby::IcecastUserAuthentication", inverse_of: :mount, :foreign_key => 'authentication_id'
|
||||||
|
belongs_to :music_session, class_name: "JamRuby::MusicSession", inverse_of: :mount, foreign_key: 'music_session_id'
|
||||||
|
|
||||||
has_many :server_mounts, :class_name => "JamRuby::IcecastServerMount", :inverse_of => :mount, :foreign_key => 'icecast_mount_id'
|
belongs_to :server, class_name: "JamRuby::IcecastServer", inverse_of: :mounts, foreign_key: 'icecast_server_id'
|
||||||
has_many :servers, :class_name => "JamRuby::IcecastServer", :through => :server_mounts, :source => :server
|
belongs_to :mount_template, class_name: "JamRuby::IcecastMountTemplate", inverse_of: :mounts, foreign_key: 'icecast_mount_template_id'
|
||||||
|
|
||||||
validates :name, presence: true
|
validates :name, presence: true, uniqueness: true
|
||||||
validates :source_username, length: {minimum: 5}, if: lambda {|m| m.source_username.present?}
|
validates :source_username, length: {minimum: 5}, if: lambda {|m| m.source_username.present?}
|
||||||
validates :source_pass, length: {minimum: 5}, if: lambda {|m| m.source_pass.present?}
|
validates :source_pass, length: {minimum: 5}, if: lambda {|m| m.source_pass.present?}
|
||||||
validates :max_listeners, length: {in: 1..15000}, if: lambda {|m| m.max_listeners.present?}
|
validates :max_listeners, length: {in: 1..15000}, if: lambda {|m| m.max_listeners.present?}
|
||||||
|
|
@ -19,93 +24,167 @@ module JamRuby
|
||||||
validates :fallback_override, :inclusion => {:in => [0, 1]} , if: lambda {|m| m.fallback_mount.present?}
|
validates :fallback_override, :inclusion => {:in => [0, 1]} , if: lambda {|m| m.fallback_mount.present?}
|
||||||
validates :fallback_when_full, :inclusion => {:in => [0, 1]} , if: lambda {|m| m.fallback_mount.present?}
|
validates :fallback_when_full, :inclusion => {:in => [0, 1]} , if: lambda {|m| m.fallback_mount.present?}
|
||||||
validates :is_public, presence: true, :inclusion => {:in => [-1, 0, 1]}
|
validates :is_public, presence: true, :inclusion => {:in => [-1, 0, 1]}
|
||||||
validates :stream_name, presence: true
|
|
||||||
validates :stream_description, presence: true
|
|
||||||
validates :stream_url, presence: true
|
|
||||||
validates :genre, presence: true
|
|
||||||
validates :bitrate, numericality: {only_integer: true}, if: lambda {|m| m.bitrate.present?}
|
validates :bitrate, numericality: {only_integer: true}, if: lambda {|m| m.bitrate.present?}
|
||||||
validates :mime_type, presence: true
|
|
||||||
validates :subtype, presence: true
|
|
||||||
validates :burst_size, numericality: {only_integer: true}, if: lambda {|m| m.burst_size.present?}
|
validates :burst_size, numericality: {only_integer: true}, if: lambda {|m| m.burst_size.present?}
|
||||||
validates :mp3_metadata_interval, numericality: {only_integer: true}, if: lambda {|m| m.mp3_metadata_interval.present?}
|
validates :mp3_metadata_interval, numericality: {only_integer: true}, if: lambda {|m| m.mp3_metadata_interval.present?}
|
||||||
validates :hidden, :inclusion => {:in => [0, 1]}
|
validates :hidden, :inclusion => {:in => [0, 1]}
|
||||||
validate :name_has_correct_format
|
validates :server, presence: true
|
||||||
|
validate :name_has_correct_format
|
||||||
|
|
||||||
before_save :sanitize_active_admin
|
before_save :sanitize_active_admin
|
||||||
after_save :after_save
|
after_save :after_save
|
||||||
after_commit :after_commit
|
after_save :poke_config
|
||||||
|
before_destroy :poke_config
|
||||||
|
|
||||||
def name_has_correct_format
|
def name_has_correct_format
|
||||||
errors.add(:name, "must start with /") unless name && name.start_with?('/')
|
errors.add(:name, "must start with /") unless name && name.start_with?('/')
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_save
|
def poke_config
|
||||||
IcecastServer.update(servers, config_changed: 1)
|
server.update_attribute(:config_changed, 1) if server
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_save
|
||||||
|
server.update_attribute(:config_changed, 1)
|
||||||
|
|
||||||
# transiting to sourced from not sourced
|
|
||||||
if !sourced_was && sourced
|
if !sourced_was && sourced
|
||||||
|
|
||||||
|
# went from NOT SOURCED to SOURCED
|
||||||
|
notify_source_up
|
||||||
|
|
||||||
|
elsif sourced_was && !sourced
|
||||||
|
|
||||||
|
# went from SOURCED to NOT SOURCED
|
||||||
|
notify_source_down
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if listeners_was == 0 && listeners > 0 && !sourced
|
||||||
|
# listener count went above 0 and there is no source. ask the musician clients to source
|
||||||
|
notify_source_up_requested
|
||||||
|
end
|
||||||
|
|
||||||
|
# Note:
|
||||||
|
# Notification.send_source_down_requested does not occur here.
|
||||||
|
# we set up a cron that checks for streams that have not been successfully source up/down (after timeout ) in IcecastSourceCheck
|
||||||
end
|
end
|
||||||
|
|
||||||
def sanitize_active_admin
|
def sanitize_active_admin
|
||||||
self.authentication_id = nil if self.authentication_id == ''
|
self.authentication_id = nil if self.authentication_id == ''
|
||||||
|
self.music_session_id = nil if self.music_session_id == ''
|
||||||
|
self.icecast_server_id = nil if self.icecast_server_id == ''
|
||||||
|
end
|
||||||
|
|
||||||
|
# creates a templated
|
||||||
|
def self.build_session_mount(music_session)
|
||||||
|
|
||||||
|
# only public sessions get mounts currently
|
||||||
|
return nil unless music_session.fan_access
|
||||||
|
|
||||||
|
icecast_server = IcecastServer.find_best_server_for_user(music_session.creator)
|
||||||
|
|
||||||
|
mount = nil
|
||||||
|
if icecast_server && icecast_server.mount_template_id
|
||||||
|
# we have a server with an associated mount_template; we can create a mount automatically
|
||||||
|
mount = icecast_server.mount_template.build_session_mount(music_session)
|
||||||
|
mount.server = icecast_server
|
||||||
|
end
|
||||||
|
mount
|
||||||
end
|
end
|
||||||
|
|
||||||
def source_up
|
def source_up
|
||||||
with_lock do
|
with_lock do
|
||||||
self.sourced = true
|
self.sourced = true
|
||||||
self.save(:validate => false)
|
self.sourced_needs_changing_at = nil
|
||||||
|
save(validate: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def source_down
|
def source_down
|
||||||
with_lock do
|
with_lock do
|
||||||
sourced = false
|
self.sourced = false
|
||||||
save(:validate => false)
|
self.sourced_needs_changing_at = nil
|
||||||
|
save(validate: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def listener_add
|
def listener_add
|
||||||
with_lock do
|
with_lock do
|
||||||
increment!(:listeners)
|
sourced_needs_changing_at = Time.now if listeners == 0
|
||||||
|
|
||||||
|
# this is completely unsafe without that 'with_lock' statement above
|
||||||
|
self.listeners = self.listeners + 1
|
||||||
|
|
||||||
|
save(validate: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def listener_remove
|
def listener_remove
|
||||||
|
if listeners == 0
|
||||||
|
@@log.warn("listeners is at 0, but we are being asked to remove a listener. maybe we missed a listener_add request earlier")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
with_lock do
|
with_lock do
|
||||||
decrement!(:listeners)
|
sourced_needs_changing_at = Time.now if listeners == 1
|
||||||
|
|
||||||
|
# this is completely unsafe without that 'with_lock' statement above
|
||||||
|
self.listeners = self.listeners - 1
|
||||||
|
|
||||||
|
save(validations: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def notify_source_up_requested
|
||||||
|
Notification.send_source_up_requested(music_session,
|
||||||
|
server.hostname,
|
||||||
|
server.pick_listen_socket(:port),
|
||||||
|
name,
|
||||||
|
resolve_string(:source_username),
|
||||||
|
resolve_string(:source_pass),
|
||||||
|
resolve_int(:bitrate)) if music_session_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_source_down_requested
|
||||||
|
Notification.send_source_down_requested(music_session, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_source_up
|
||||||
|
Notification.send_source_up(music_session) if music_session_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_source_down
|
||||||
|
Notification.send_source_down(music_session) if music_session_id
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if the icecast_mount specifies the value; if not, use the mount_template's value take effect
|
||||||
def dumpXml(builder)
|
def dumpXml(builder)
|
||||||
builder.tag! 'mount' do |mount|
|
builder.tag! 'mount' do |mount|
|
||||||
mount.tag! 'mount-name', name
|
mount.tag! 'mount-name', name
|
||||||
mount.tag! 'username', source_username if !source_username.nil? && !source_username.empty?
|
mount.tag! 'username', resolve_string(:source_username) if string_present?(:source_username)
|
||||||
mount.tag! 'password', source_pass if !source_pass.nil? && !source_pass.empty?
|
mount.tag! 'password', resolve_string(:source_pass) if string_present?(:source_pass)
|
||||||
mount.tag! 'max-listeners', max_listeners unless max_listeners.nil?
|
mount.tag! 'max-listeners', resolve_int(:max_listeners) if int_present?(:max_listeners)
|
||||||
mount.tag! 'max-listener-duration', max_listener_duration unless max_listener_duration.nil?
|
mount.tag! 'max-listener-duration', resolve_string(:max_listener_duration) if int_present?(:max_listener_duration)
|
||||||
mount.tag! 'dump-file', dump_file if !dump_file.nil? && !dump_file.empty?
|
mount.tag! 'dump-file', resolve_string(:dump_file) if string_present?(:dump_file)
|
||||||
mount.tag! 'intro', intro if !intro.nil? && !intro.empty?
|
mount.tag! 'intro', resolve_string(:intro) if string_present?(:intro)
|
||||||
mount.tag! 'fallback-mount', fallback_mount if !fallback_mount.nil? && !fallback_mount.empty?
|
mount.tag! 'fallback-mount', resolve_string(:fallback_mount) if string_present?(:fallback_mount)
|
||||||
mount.tag! 'fallback-override', fallback_override if fallback_override
|
mount.tag! 'fallback-override', resolve_int(:fallback_override) if int_present?(:fallback_override)
|
||||||
mount.tag! 'fallback-when-full', fallback_when_full if fallback_when_full
|
mount.tag! 'fallback-when-full', resolve_int(:fallback_when_full) if int_present?(:fallback_when_full)
|
||||||
mount.tag! 'charset', charset if charset
|
mount.tag! 'charset', resolve_string(:charset) if string_present?(:charset)
|
||||||
mount.tag! 'public', is_public
|
mount.tag! 'public', resolve_int(:is_public) if int_present?(:is_public)
|
||||||
mount.tag! 'stream-name', stream_name if !stream_name.nil? && !stream_name.empty?
|
mount.tag! 'stream-name', resolve_string(:stream_name) if string_present?(:stream_name)
|
||||||
mount.tag! 'stream-description', stream_description if !stream_description.nil? && !stream_description.empty?
|
mount.tag! 'stream-description', resolve_string(:stream_description) if string_present?(:stream_description)
|
||||||
mount.tag! 'stream-url', stream_url if !stream_url.nil? && !stream_url.empty?
|
mount.tag! 'stream-url', resolve_string(:stream_url) if string_present?(:stream_url)
|
||||||
mount.tag! 'genre', genre unless genre.empty?
|
mount.tag! 'genre', resolve_string(:genre) if string_present?(:genre)
|
||||||
mount.tag! 'bitrate', bitrate if bitrate
|
mount.tag! 'bitrate', resolve_int(:bitrate) if int_present?(:bitrate)
|
||||||
mount.tag! 'type', mime_type
|
mount.tag! 'type', resolve_string(:mime_type) if string_present?(:mime_type)
|
||||||
mount.tag! 'subtype', subtype
|
mount.tag! 'subtype', resolve_string(:subtype) if string_present?(:subtype)
|
||||||
mount.tag! 'burst-size', burst_size if burst_size
|
mount.tag! 'burst-size', resolve_int(:burst_size) if int_present?(:burst_size)
|
||||||
mount.tag! 'mp3-metadata-interval', mp3_metadata_interval unless mp3_metadata_interval.nil?
|
mount.tag! 'mp3-metadata-interval', resolve_int(:mp3_metadata_interval) if int_present?(:mp3_metadata_interval)
|
||||||
mount.tag! 'hidden', hidden
|
mount.tag! 'hidden', resolve_int(:hidden) if int_present?(:hidden)
|
||||||
mount.tag! 'on-connect', on_connect if on_connect
|
mount.tag! 'on-connect', resolve_string(:on_connect) if string_present?(:on_connect)
|
||||||
mount.tag! 'on-disconnect', on_disconnect if on_disconnect
|
mount.tag! 'on-disconnect', resolve_string(:on_disconnect) if string_present?(:on_disconnect)
|
||||||
|
|
||||||
authentication.dumpXml(builder) if authentication
|
authentication.dumpXml(builder) if authentication
|
||||||
end
|
end
|
||||||
|
|
@ -117,5 +196,23 @@ module JamRuby
|
||||||
|
|
||||||
"http://" + server_mount.server.hostname + self.name
|
"http://" + server_mount.server.hostname + self.name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_string(field)
|
||||||
|
self[field].present? ? self[field] : mount_template && mount_template[field]
|
||||||
|
end
|
||||||
|
|
||||||
|
def string_present?(field)
|
||||||
|
val = resolve_string(field)
|
||||||
|
val ? val.present? : false
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_int(field)
|
||||||
|
!self[field].nil? ? self[field]: mount_template && mount_template[field]
|
||||||
|
end
|
||||||
|
|
||||||
|
def int_present?(field)
|
||||||
|
resolve_int(field)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
module JamRuby
|
||||||
|
class IcecastMountTemplate < ActiveRecord::Base
|
||||||
|
|
||||||
|
attr_accessor :hostname, :default_mime_type # used by jam-admin
|
||||||
|
|
||||||
|
attr_accessible :authentication_id, :source_username, :source_pass, :max_listeners, :max_listener_duration,
|
||||||
|
:dump_file, :intro, :fallback_mount, :fallback_override, :fallback_when_full, :charset, :is_public,
|
||||||
|
:stream_name, :stream_description, :stream_url, :genre, :bitrate, :mime_type, :subtype, :burst_size,
|
||||||
|
:mp3_metadata_interval, :hidden, :on_connect, :on_disconnect, :name, as: :admin
|
||||||
|
|
||||||
|
belongs_to :authentication, class_name: "JamRuby::IcecastUserAuthentication", inverse_of: :mount, foreign_key: 'authentication_id'
|
||||||
|
has_many :mounts, class_name: "JamRuby::IcecastMount", inverse_of: :mount_template, foreign_key: 'icecast_mount_template_id'
|
||||||
|
has_many :servers, class_name: "JamRuby::IcecastServer", inverse_of: :mount_template, foreign_key: 'mount_template_id'
|
||||||
|
|
||||||
|
validates :source_username, length: {minimum: 5}, if: lambda {|m| m.source_username.present?}
|
||||||
|
validates :source_pass, length: {minimum: 5}, if: lambda {|m| m.source_pass.present?}
|
||||||
|
validates :max_listeners, length: {in: 1..15000}, if: lambda {|m| m.max_listeners.present?}
|
||||||
|
validates :max_listener_duration, length: {in: 1..3600 * 48}, if: lambda {|m| m.max_listener_duration.present?}
|
||||||
|
validates :fallback_override, :inclusion => {:in => [0, 1]} , if: lambda {|m| m.fallback_mount.present?}
|
||||||
|
validates :fallback_when_full, :inclusion => {:in => [0, 1]} , if: lambda {|m| m.fallback_mount.present?}
|
||||||
|
validates :is_public, presence: true, :inclusion => {:in => [-1, 0, 1]}
|
||||||
|
validates :bitrate, numericality: {only_integer: true}, if: lambda {|m| m.bitrate.present?}
|
||||||
|
validates :mime_type, presence: true
|
||||||
|
validates :burst_size, numericality: {only_integer: true}, if: lambda {|m| m.burst_size.present?}
|
||||||
|
validates :mp3_metadata_interval, numericality: {only_integer: true}, if: lambda {|m| m.mp3_metadata_interval.present?}
|
||||||
|
validates :hidden, :inclusion => {:in => [0, 1]}
|
||||||
|
|
||||||
|
before_save :sanitize_active_admin
|
||||||
|
after_save :poke_config
|
||||||
|
after_initialize :after_initialize
|
||||||
|
before_destroy :poke_config
|
||||||
|
|
||||||
|
def after_initialize # used by jam-admin
|
||||||
|
self.hostname = 'localhost:3000'
|
||||||
|
self.default_mime_type = 'mp3'
|
||||||
|
end
|
||||||
|
|
||||||
|
def poke_config
|
||||||
|
IcecastServer.update(servers, config_changed: 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def sanitize_active_admin
|
||||||
|
self.authentication_id = nil if self.authentication_id == ''
|
||||||
|
end
|
||||||
|
|
||||||
|
# pick a server that's in the same group as the user that is under the least load
|
||||||
|
def build_session_mount(music_session)
|
||||||
|
mount = IcecastMount.new
|
||||||
|
mount.authentication = authentication
|
||||||
|
mount.mount_template = self
|
||||||
|
mount.name = "/" + SecureRandom.urlsafe_base64
|
||||||
|
mount.music_session_id = music_session.id
|
||||||
|
mount.source_username = 'source'
|
||||||
|
mount.source_pass = SecureRandom.urlsafe_base64
|
||||||
|
mount.stream_name = "JamKazam music session created by #{music_session.creator.name}"
|
||||||
|
mount.stream_description = music_session.description
|
||||||
|
mount.stream_url = "http://www.jamkazam.com" ## TODO/XXX, the jamkazam url should be the page hosting the widget
|
||||||
|
mount.genre = music_session.genres.map {|genre| genre.description}.join(',')
|
||||||
|
mount
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -12,7 +12,8 @@ module JamRuby
|
||||||
validates :web_root, presence: true
|
validates :web_root, presence: true
|
||||||
validates :admin_root, presence: true
|
validates :admin_root, presence: true
|
||||||
|
|
||||||
after_save :poke_config
|
after_save :poke_config
|
||||||
|
before_destroy :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
IcecastServer.update(servers, config_changed: 1)
|
IcecastServer.update(servers, config_changed: 1)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ module JamRuby
|
||||||
validates :relay_shoutcast_metadata, :inclusion => {:in => [0, 1]}
|
validates :relay_shoutcast_metadata, :inclusion => {:in => [0, 1]}
|
||||||
validates :on_demand, presence: true, :inclusion => {:in => [0, 1]}
|
validates :on_demand, presence: true, :inclusion => {:in => [0, 1]}
|
||||||
|
|
||||||
after_save :poke_config
|
before_destroy :poke_config
|
||||||
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
IcecastServer.update(servers, :config_changed => true)
|
IcecastServer.update(servers, :config_changed => true)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ module JamRuby
|
||||||
|
|
||||||
validates :chroot, :inclusion => {:in => [0, 1]}
|
validates :chroot, :inclusion => {:in => [0, 1]}
|
||||||
|
|
||||||
after_save :poke_config
|
before_destroy :poke_config
|
||||||
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
IcecastServer.update(servers, config_changed: 1)
|
IcecastServer.update(servers, config_changed: 1)
|
||||||
|
|
|
||||||
|
|
@ -3,28 +3,31 @@ module JamRuby
|
||||||
|
|
||||||
attr_accessor :skip_config_changed_flag
|
attr_accessor :skip_config_changed_flag
|
||||||
|
|
||||||
attr_accessible :template_id, :limit_id, :admin_auth_id, :directory_id, :master_relay_id, :path_id, :logging_id,
|
attr_accessible :template_id, :mount_template_id, :limit_id, :admin_auth_id, :directory_id, :master_relay_id, :path_id, :logging_id,
|
||||||
:security_id, :config_changed, :hostname, :location, :admin_email, :fileserve, as: :admin
|
:security_id, :config_changed, :hostname, :location, :admin_email, :fileserve, :icecast_server_group_id, as: :admin
|
||||||
|
|
||||||
belongs_to :template, :class_name => "JamRuby::IcecastTemplate", foreign_key: 'template_id', :inverse_of => :servers
|
|
||||||
|
belongs_to :template, class_name: "JamRuby::IcecastTemplate", foreign_key: 'template_id', inverse_of: :servers
|
||||||
|
belongs_to :mount_template, class_name: "JamRuby::IcecastMountTemplate", foreign_key: 'mount_template_id', inverse_of: :servers
|
||||||
|
belongs_to :server_group, class_name: "JamRuby::IcecastServerGroup", foreign_key: 'icecast_server_group_id', inverse_of: :servers
|
||||||
|
|
||||||
# all are overrides, because the template defines all of these as well. When building the XML, we will prefer these if set
|
# all are overrides, because the template defines all of these as well. When building the XML, we will prefer these if set
|
||||||
belongs_to :limit, :class_name => "JamRuby::IcecastLimit", foreign_key: 'limit_id', :inverse_of => :servers
|
belongs_to :limit, class_name: "JamRuby::IcecastLimit", foreign_key: 'limit_id', inverse_of: :servers
|
||||||
belongs_to :admin_auth, :class_name => "JamRuby::IcecastAdminAuthentication", foreign_key: 'admin_auth_id', :inverse_of => :servers
|
belongs_to :admin_auth, class_name: "JamRuby::IcecastAdminAuthentication", foreign_key: 'admin_auth_id', inverse_of: :servers
|
||||||
belongs_to :directory, :class_name => "JamRuby::IcecastDirectory", foreign_key: 'directory_id', :inverse_of => :servers
|
belongs_to :directory, class_name: "JamRuby::IcecastDirectory", foreign_key: 'directory_id', inverse_of: :servers
|
||||||
belongs_to :master_relay, :class_name => "JamRuby::IcecastMasterServerRelay", foreign_key: 'master_relay_id', :inverse_of => :servers
|
belongs_to :master_relay, class_name: "JamRuby::IcecastMasterServerRelay", foreign_key: 'master_relay_id', inverse_of: :servers
|
||||||
belongs_to :path, :class_name => "JamRuby::IcecastPath", foreign_key: 'path_id', :inverse_of => :servers
|
belongs_to :path, class_name: "JamRuby::IcecastPath", foreign_key: 'path_id', inverse_of: :servers
|
||||||
belongs_to :logging, :class_name => "JamRuby::IcecastLogging", foreign_key: 'logging_id', :inverse_of => :servers
|
belongs_to :logging, class_name: "JamRuby::IcecastLogging", foreign_key: 'logging_id', inverse_of: :servers
|
||||||
belongs_to :security, :class_name => "JamRuby::IcecastSecurity", foreign_key: 'security_id', :inverse_of => :servers
|
belongs_to :security, class_name: "JamRuby::IcecastSecurity", foreign_key: 'security_id', inverse_of: :servers
|
||||||
has_many :listen_socket_servers, :class_name => "JamRuby::IcecastServerSocket", :inverse_of => :server
|
has_many :listen_socket_servers, class_name: "JamRuby::IcecastServerSocket", inverse_of: :server
|
||||||
has_many :listen_sockets, :class_name => "JamRuby::IcecastListenSocket", :through => :listen_socket_servers, :source => :socket
|
has_many :listen_sockets, class_name: "JamRuby::IcecastListenSocket", :through => :listen_socket_servers, :source => :socket
|
||||||
|
|
||||||
# mounts and relays are naturally server-specific, though
|
# mounts and relays are naturally server-specific, though
|
||||||
has_many :server_mounts, :class_name => "JamRuby::IcecastServerMount", :inverse_of => :server
|
#has_many :server_mounts, class_name: "JamRuby::IcecastServerMount", inverse_of: :server
|
||||||
has_many :mounts, :class_name => "JamRuby::IcecastMount", :through => :server_mounts, :source => :mount
|
has_many :mounts, class_name: "JamRuby::IcecastMount", inverse_of: :server, :foreign_key => 'icecast_server_id'
|
||||||
|
|
||||||
has_many :server_relays, :class_name => "JamRuby::IcecastServerRelay", :inverse_of => :relay
|
has_many :server_relays, class_name: "JamRuby::IcecastServerRelay", inverse_of: :relay
|
||||||
has_many :relays, :class_name => "JamRuby::IcecastRelay", :through => :server_relays, :source => :relay
|
has_many :relays, class_name: "JamRuby::IcecastRelay", :through => :server_relays, :source => :relay
|
||||||
|
|
||||||
validates :config_changed, :inclusion => {:in => [0, 1]}
|
validates :config_changed, :inclusion => {:in => [0, 1]}
|
||||||
validates :hostname, presence: true
|
validates :hostname, presence: true
|
||||||
|
|
@ -32,6 +35,7 @@ module JamRuby
|
||||||
validates :server_id, presence: true
|
validates :server_id, presence: true
|
||||||
|
|
||||||
validates :template, presence: true
|
validates :template, presence: true
|
||||||
|
validates :mount_template, presence: true
|
||||||
|
|
||||||
before_save :before_save, unless: lambda { skip_config_changed_flag }
|
before_save :before_save, unless: lambda { skip_config_changed_flag }
|
||||||
before_save :sanitize_active_admin
|
before_save :sanitize_active_admin
|
||||||
|
|
@ -75,8 +79,48 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def pick_listen_socket(field)
|
||||||
|
current_listen_sockets = listen_sockets.length > 0 ? listen_sockets : template.listen_sockets
|
||||||
|
socket = current_listen_sockets.first
|
||||||
|
socket[:field] if socket
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# pick an icecast server with the least listeners * sources
|
||||||
|
def self.find_best_server_for_user(user)
|
||||||
|
chosen_server_id = nil
|
||||||
|
chosen_server_weight = nil
|
||||||
|
|
||||||
|
ActiveRecord::Base.connection_pool.with_connection do |connection|
|
||||||
|
result = connection.execute('select SUM(listeners), SUM(sourced::int), icecast_servers.id
|
||||||
|
FROM icecast_servers
|
||||||
|
LEFT JOIN icecast_mounts ON icecast_servers.id = icecast_mounts.icecast_server_id
|
||||||
|
WHERE icecast_server_group_id = \'' + user.icecast_server_group_id + '\'
|
||||||
|
GROUP BY icecast_servers.id;')
|
||||||
|
|
||||||
|
result.cmd_tuples.times do |i|
|
||||||
|
listeners = result.getvalue(i, 0).to_i
|
||||||
|
sourced = result.getvalue(i, 1).to_i
|
||||||
|
icecast_server_id = result.getvalue(i, 2)
|
||||||
|
|
||||||
|
# compute weight. source is much more intensive than listener, based on load tests again 2.3.0
|
||||||
|
# http://icecast.org/loadtest2.php
|
||||||
|
|
||||||
|
weight = sourced * 10 + listeners
|
||||||
|
|
||||||
|
if !chosen_server_id || (weight < chosen_server_weight)
|
||||||
|
chosen_server_id = icecast_server_id
|
||||||
|
chosen_server_weight = weight
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
IcecastServer.find(chosen_server_id) if chosen_server_id
|
||||||
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
return server_id
|
server_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def dumpXml (output=$stdout, indent=1)
|
def dumpXml (output=$stdout, indent=1)
|
||||||
|
|
@ -84,29 +128,21 @@ module JamRuby
|
||||||
builder = ::Builder::XmlMarkup.new(:target => output, :indent => indent)
|
builder = ::Builder::XmlMarkup.new(:target => output, :indent => indent)
|
||||||
|
|
||||||
builder.tag! 'icecast' do |root|
|
builder.tag! 'icecast' do |root|
|
||||||
root.tag! 'hostname', hostname
|
root.tag! 'hostname', hostname
|
||||||
root.tag! 'location', (location.nil? || location.empty?) ? template.location : location
|
root.tag! 'server-id', server_id
|
||||||
root.tag! 'server-id', server_id
|
root.tag! 'location', resolve_string(:location) if string_present?(:location)
|
||||||
root.tag! 'admin', (admin_email.nil? || admin_email.empty?) ? template.admin_email : admin_email
|
root.tag! 'admin', resolve_string(:admin_email) if string_present?(:admin_email)
|
||||||
root.tag! 'fileserve', fileserve.nil? ? template.fileserve : fileserve
|
root.tag! 'fileserve', resolve_int(:fileserve) if int_present?(:fileserve)
|
||||||
|
|
||||||
|
resolve_association(:limit).dumpXml(builder) if association_present?(:limit)
|
||||||
|
resolve_association(:admin_auth).dumpXml(builder) if association_present?(:admin_auth)
|
||||||
|
resolve_association(:directory).dumpXml(builder) if association_present?(:directory)
|
||||||
|
resolve_association(:master_relay).dumpXml(builder) if association_present?(:master_relay)
|
||||||
|
resolve_association(:path).dumpXml(builder) if association_present?(:path)
|
||||||
|
resolve_association(:logging).dumpXml(builder) if association_present?(:logging)
|
||||||
|
resolve_association(:security).dumpXml(builder) if association_present?(:security)
|
||||||
|
|
||||||
# do we have an override specified? or do we go with the template
|
|
||||||
current_limit = limit ? limit : template.limit
|
|
||||||
current_admin_auth = admin_auth ? admin_auth : template.admin_auth
|
|
||||||
current_directory = directory ? directory : template.directory
|
|
||||||
current_master_relay = master_relay ? master_relay : template.master_relay
|
|
||||||
current_path = path ? path : template.path
|
|
||||||
current_logging = logging ? logging : template.logging
|
|
||||||
current_security = security ? security : template.security
|
|
||||||
current_listen_sockets = listen_sockets.length > 0 ? listen_sockets : template.listen_sockets
|
current_listen_sockets = listen_sockets.length > 0 ? listen_sockets : template.listen_sockets
|
||||||
|
|
||||||
current_limit.dumpXml(builder) unless current_limit.nil?
|
|
||||||
current_admin_auth.dumpXml(builder) unless current_admin_auth.nil?
|
|
||||||
current_directory.dumpXml(builder) unless current_directory.nil?
|
|
||||||
current_master_relay.dumpXml(builder) unless current_master_relay.nil?
|
|
||||||
current_path.dumpXml(builder) unless current_path.nil?
|
|
||||||
current_logging.dumpXml(builder) unless current_logging.nil?
|
|
||||||
current_security.dumpXml(builder) unless current_security.nil?
|
|
||||||
current_listen_sockets.each do |listen_socket|
|
current_listen_sockets.each do |listen_socket|
|
||||||
listen_socket.dumpXml(builder)
|
listen_socket.dumpXml(builder)
|
||||||
end
|
end
|
||||||
|
|
@ -120,5 +156,32 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def resolve_string(field)
|
||||||
|
self[field].present? ? self[field] : template && template[field]
|
||||||
|
end
|
||||||
|
|
||||||
|
def string_present?(field)
|
||||||
|
val = resolve_string(field)
|
||||||
|
val ? val.present? : false
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_int(field)
|
||||||
|
self[field] ? self[field]: template && template[field]
|
||||||
|
end
|
||||||
|
|
||||||
|
def int_present?(field)
|
||||||
|
resolve_int(field)
|
||||||
|
end
|
||||||
|
|
||||||
|
def resolve_association(field)
|
||||||
|
self.send(field) ? self.send(field) : template && template.send(field)
|
||||||
|
end
|
||||||
|
|
||||||
|
def association_present?(field)
|
||||||
|
resolve_association(field)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
module JamRuby
|
||||||
|
class IcecastServerGroup < ActiveRecord::Base
|
||||||
|
|
||||||
|
attr_accessible :name, as: :admin
|
||||||
|
|
||||||
|
has_many :users, class_name: "JamRuby::User", inverse_of: :icecast_server_group, foreign_key: 'icecast_server_group_id'
|
||||||
|
has_many :servers, class_name: "JamRuby::IcecastServer", inverse_of: :server_group, foreign_key: 'icecast_server_group_id'
|
||||||
|
|
||||||
|
validates :name, presence: true, uniqueness: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -11,5 +11,11 @@ module JamRuby
|
||||||
validates :socket, :presence => true
|
validates :socket, :presence => true
|
||||||
validates :server, :presence => true
|
validates :server, :presence => true
|
||||||
|
|
||||||
|
after_save :poke_config
|
||||||
|
before_destroy :poke_config
|
||||||
|
|
||||||
|
def poke_config
|
||||||
|
server.update_attribute(:config_changed, 1) if server
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -33,8 +33,8 @@ module JamRuby
|
||||||
validates :listen_sockets, length: {minimum: 1}
|
validates :listen_sockets, length: {minimum: 1}
|
||||||
|
|
||||||
before_save :sanitize_active_admin
|
before_save :sanitize_active_admin
|
||||||
after_save :poke_config
|
after_save :poke_config
|
||||||
|
before_destroy :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
IcecastServer.update(servers, config_changed: 1)
|
IcecastServer.update(servers, config_changed: 1)
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,11 @@ module JamRuby
|
||||||
validates :socket, :presence => true
|
validates :socket, :presence => true
|
||||||
validates :template, :presence => true
|
validates :template, :presence => true
|
||||||
|
|
||||||
|
after_save :poke_config
|
||||||
|
before_destroy :poke_config
|
||||||
|
|
||||||
|
def poke_config
|
||||||
|
IcecastServer.update(template.servers, config_changed: 1) if template
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -17,10 +17,11 @@ module JamRuby
|
||||||
validates :auth_header, presence: true, if: :url_auth?
|
validates :auth_header, presence: true, if: :url_auth?
|
||||||
validates :timelimit_header, presence: true, if: :url_auth?
|
validates :timelimit_header, presence: true, if: :url_auth?
|
||||||
|
|
||||||
|
before_destroy :poke_config
|
||||||
after_save :poke_config
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
IcecastServer.update(mount.servers, config_changed: 1) if mount
|
mount.server.update_attribute(:config_changed, 1) if mount && mount.server
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ module JamRuby
|
||||||
|
|
||||||
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
|
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
|
||||||
|
|
||||||
attr_accessible :email, :sender_id, :autofriend, :note
|
attr_accessible :email, :sender_id, :autofriend, :note, as: :admin
|
||||||
|
|
||||||
attr_accessor :accepted_twice
|
attr_accessor :accepted_twice
|
||||||
|
|
||||||
self.primary_key = 'id'
|
self.primary_key = 'id'
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ module JamRuby
|
||||||
belongs_to :claimed_recording, :class_name => "JamRuby::ClaimedRecording", :foreign_key => "claimed_recording_id", :inverse_of => :playing_sessions
|
belongs_to :claimed_recording, :class_name => "JamRuby::ClaimedRecording", :foreign_key => "claimed_recording_id", :inverse_of => :playing_sessions
|
||||||
belongs_to :claimed_recording_initiator, :class_name => "JamRuby::User", :inverse_of => :playing_claimed_recordings, :foreign_key => "claimed_recording_initiator_id"
|
belongs_to :claimed_recording_initiator, :class_name => "JamRuby::User", :inverse_of => :playing_claimed_recordings, :foreign_key => "claimed_recording_initiator_id"
|
||||||
|
|
||||||
|
has_one :mount, :class_name => "JamRuby::IcecastMount", :inverse_of => :music_session, :foreign_key => 'music_session_id'
|
||||||
|
|
||||||
has_many :connections, :class_name => "JamRuby::Connection"
|
has_many :connections, :class_name => "JamRuby::Connection"
|
||||||
has_many :users, :through => :connections, :class_name => "JamRuby::User"
|
has_many :users, :through => :connections, :class_name => "JamRuby::User"
|
||||||
has_and_belongs_to_many :genres, :class_name => "::JamRuby::Genre", :join_table => "genres_music_sessions"
|
has_and_belongs_to_many :genres, :class_name => "::JamRuby::Genre", :join_table => "genres_music_sessions"
|
||||||
|
|
@ -37,6 +39,9 @@ module JamRuby
|
||||||
validate :creator_is_musician
|
validate :creator_is_musician
|
||||||
validate :no_new_playback_while_playing
|
validate :no_new_playback_while_playing
|
||||||
|
|
||||||
|
def before_destroy
|
||||||
|
self.mount.destroy if self.mount
|
||||||
|
end
|
||||||
|
|
||||||
def creator_is_musician
|
def creator_is_musician
|
||||||
unless creator.musician?
|
unless creator.musician?
|
||||||
|
|
@ -53,6 +58,21 @@ module JamRuby
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# returns an array of client_id's that are in this session
|
||||||
|
# if as_musician is nil, all connections in the session ,regardless if it's a musician or not or not
|
||||||
|
# you can also exclude a client_id from the returned set by setting exclude_client_id
|
||||||
|
def get_connection_ids(options = {})
|
||||||
|
as_musician = options[:as_musician]
|
||||||
|
exclude_client_id = options[:exclude_client_id]
|
||||||
|
|
||||||
|
where = { :music_session_id => self.id }
|
||||||
|
where[:as_musician] = as_musician unless as_musician.nil?
|
||||||
|
|
||||||
|
exclude = "client_id != '#{exclude_client_id}'"unless exclude_client_id.nil?
|
||||||
|
|
||||||
|
Connection.select(:client_id).where(where).where(exclude).map(&:client_id)
|
||||||
|
end
|
||||||
|
|
||||||
# This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true
|
# This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true
|
||||||
# If so, then it's an OR condition. If both are false, you can get sessions with anyone.
|
# If so, then it's an OR condition. If both are false, you can get sessions with anyone.
|
||||||
def self.index(current_user, participants = nil, genres = nil, friends_only = false, my_bands_only = false, keyword = nil)
|
def self.index(current_user, participants = nil, genres = nil, friends_only = false, my_bands_only = false, keyword = nil)
|
||||||
|
|
|
||||||
|
|
@ -766,10 +766,10 @@ module JamRuby
|
||||||
@@mq_router.publish_to_user(user_id, msg)
|
@@mq_router.publish_to_user(user_id, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_source_up_requested(music_session, host, port, mount, source_user, source_pass)
|
def send_source_up_requested(music_session, host, port, mount, source_user, source_pass, bitrate)
|
||||||
msg = @@message_factory.source_up_requested(music_session.id, host, port, mount, source_user, source_pass)
|
msg = @@message_factory.source_up_requested(music_session.id, host, port, mount, source_user, source_pass, bitrate)
|
||||||
|
|
||||||
@@mg_router.server_publish_to_session(music_session, msg)
|
@@mq_router.server_publish_to_session(music_session, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_source_down_requested(music_session, mount)
|
def send_source_down_requested(music_session, mount)
|
||||||
|
|
@ -777,6 +777,18 @@ module JamRuby
|
||||||
|
|
||||||
@@mq_router.server_publish_to_session(music_session, msg)
|
@@mq_router.server_publish_to_session(music_session, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def send_source_up(music_session)
|
||||||
|
msg = @@message_factory.source_up(music_session.id)
|
||||||
|
|
||||||
|
@@mq_router.server_publish_to_everyone_in_session(music_session, msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_source_down(music_session)
|
||||||
|
msg = @@message_factory.source_up(music_session.id)
|
||||||
|
|
||||||
|
@@mq_router.server_publish_to_everyone_in_session(music_session, msg)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -74,8 +74,9 @@ module JamRuby
|
||||||
|
|
||||||
PARAM_MUSICIAN = :srch_m
|
PARAM_MUSICIAN = :srch_m
|
||||||
PARAM_BAND = :srch_b
|
PARAM_BAND = :srch_b
|
||||||
|
PARAM_FEED = :srch_f
|
||||||
|
|
||||||
B_PER_PAGE = M_PER_PAGE = 10
|
F_PER_PAGE = B_PER_PAGE = M_PER_PAGE = 10
|
||||||
M_MILES_DEFAULT = 500
|
M_MILES_DEFAULT = 500
|
||||||
B_MILES_DEFAULT = 0
|
B_MILES_DEFAULT = 0
|
||||||
|
|
||||||
|
|
@ -87,6 +88,18 @@ module JamRuby
|
||||||
|
|
||||||
DISTANCE_OPTS = B_DISTANCE_OPTS = M_DISTANCE_OPTS = [['Any', 0], [1000.to_s, 1000], [500.to_s, 500], [250.to_s, 250], [100.to_s, 100], [50.to_s, 50], [25.to_s, 25]]
|
DISTANCE_OPTS = B_DISTANCE_OPTS = M_DISTANCE_OPTS = [['Any', 0], [1000.to_s, 1000], [500.to_s, 500], [250.to_s, 250], [100.to_s, 100], [50.to_s, 50], [25.to_s, 25]]
|
||||||
|
|
||||||
|
F_SORT_RECENT = ['Most Recent', :recent]
|
||||||
|
F_SORT_OLDEST = ['Ending Soonest', :ending_soon]
|
||||||
|
F_SORT_LENGTH = ['Session Length', :session_length]
|
||||||
|
F_SORT_OPTS = [F_SORT_RECENT, F_SORT_LENGTH, F_SORT_OLDEST]
|
||||||
|
|
||||||
|
SHOW_BOTH = ['Both', :both]
|
||||||
|
SHOW_SESSIONS = ['Sessions', :sessions]
|
||||||
|
SHOW_RECORDINGS = ['Recordings', :recordings]
|
||||||
|
SHOW_OPTS = [SHOW_BOTH, SHOW_SESSIONS, SHOW_RECORDINGS]
|
||||||
|
|
||||||
|
DATE_OPTS = [['Today', 0], ['This week', 7], ['Past 2 weeks', 14], ['This month', 30], ['Past year', 365], ['All', -1]]
|
||||||
|
|
||||||
def self.order_param(params, keys=M_ORDERING_KEYS)
|
def self.order_param(params, keys=M_ORDERING_KEYS)
|
||||||
ordering = params[:orderby]
|
ordering = params[:orderby]
|
||||||
ordering.blank? ? keys[0] : keys.detect { |oo| oo.to_s == ordering }
|
ordering.blank? ? keys[0] : keys.detect { |oo| oo.to_s == ordering }
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ module JamRuby
|
||||||
# updating_password corresponds to a lost_password
|
# updating_password corresponds to a lost_password
|
||||||
attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field
|
attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field
|
||||||
|
|
||||||
|
belongs_to :icecast_server_group, class_name: "JamRuby::IcecastServerGroup", inverse_of: :users, foreign_key: 'icecast_server_group_id'
|
||||||
|
|
||||||
# authorizations (for facebook, etc -- omniauth)
|
# authorizations (for facebook, etc -- omniauth)
|
||||||
has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization"
|
has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,10 @@ class MQRouter
|
||||||
# sends a message to a session on behalf of a user
|
# sends a message to a session on behalf of a user
|
||||||
# if this is originating in the context of a client, it should be specified as :client_id => "value"
|
# if this is originating in the context of a client, it should be specified as :client_id => "value"
|
||||||
# client_msg should be a well-structure message (jam-pb message)
|
# client_msg should be a well-structure message (jam-pb message)
|
||||||
def user_publish_to_session(music_session, user, client_msg, sender = {:client_id => ""})
|
def user_publish_to_session(music_session, user, client_msg, sender = {:client_id => nil})
|
||||||
access_music_session(music_session, user)
|
access_music_session(music_session, user)
|
||||||
|
|
||||||
# gather up client_ids in the session
|
client_ids = music_session.get_connection_ids(as_musician: true, exclude_client_id: sender[:client_id])
|
||||||
client_ids = music_session.connections.map { |client| client.client_id }.reject { |client_id| client_id == sender[:client_id] }
|
|
||||||
|
|
||||||
publish_to_session(music_session.id, client_ids, client_msg.to_s, sender)
|
publish_to_session(music_session.id, client_ids, client_msg.to_s, sender)
|
||||||
end
|
end
|
||||||
|
|
@ -38,13 +37,21 @@ class MQRouter
|
||||||
# sends a message to a session from the server
|
# sends a message to a session from the server
|
||||||
# no access check as with user_publish_to_session
|
# no access check as with user_publish_to_session
|
||||||
# client_msg should be a well-structure message (jam-pb message)
|
# client_msg should be a well-structure message (jam-pb message)
|
||||||
def server_publish_to_session(music_session, client_msg, sender = {:client_id => ""})
|
def server_publish_to_session(music_session, client_msg, sender = {:client_id => nil})
|
||||||
# gather up client_ids in the session
|
# gather up client_ids in the session
|
||||||
client_ids = music_session.connections.map { |client| client.client_id }.reject { |client_id| client_id == sender[:client_id] }
|
client_ids = music_session.get_connection_ids(as_musician: true, exclude_client_id: sender[:client_id])
|
||||||
|
|
||||||
publish_to_session(music_session.id, client_ids, client_msg.to_s, sender)
|
publish_to_session(music_session.id, client_ids, client_msg.to_s, sender)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# sends a message to a session AND fans/listeners from the server
|
||||||
|
# client_msg should be a well-structure message (jam-pb message)
|
||||||
|
def server_publish_to_everyone_in_session(music_session, client_msg, sender = {:client_id => nil})
|
||||||
|
# gather up client_ids in the session
|
||||||
|
client_ids = music_session.get_connection_ids(exclude_client_id: sender[:client_id])
|
||||||
|
publish_to_session(music_session.id, client_ids, client_msg.to_s, sender)
|
||||||
|
end
|
||||||
|
|
||||||
# sends a message to a client with no checking of permissions (RAW USAGE)
|
# sends a message to a client with no checking of permissions (RAW USAGE)
|
||||||
# this method deliberately has no database interactivity/active_record objects
|
# this method deliberately has no database interactivity/active_record objects
|
||||||
def publish_to_client(client_id, client_msg, sender = {:client_id => ""})
|
def publish_to_client(client_id, client_msg, sender = {:client_id => ""})
|
||||||
|
|
@ -60,7 +67,7 @@ class MQRouter
|
||||||
|
|
||||||
# sends a message to a session with no checking of permissions (RAW USAGE)
|
# sends a message to a session with no checking of permissions (RAW USAGE)
|
||||||
# this method deliberately has no database interactivity/active_record objects
|
# this method deliberately has no database interactivity/active_record objects
|
||||||
def publish_to_session(music_session_id, client_ids, client_msg, sender = {:client_id => ""})
|
def publish_to_session(music_session_id, client_ids, client_msg, sender = {:client_id => nil})
|
||||||
EM.schedule do
|
EM.schedule do
|
||||||
sender_client_id = sender[:client_id]
|
sender_client_id = sender[:client_id]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
require 'json'
|
require 'json'
|
||||||
require 'resque'
|
require 'resque'
|
||||||
require 'resque-retry'
|
|
||||||
|
require 'resque-lonely_job'
|
||||||
require 'net/http'
|
require 'net/http'
|
||||||
require 'digest/md5'
|
require 'digest/md5'
|
||||||
|
|
||||||
|
|
@ -8,6 +9,7 @@ module JamRuby
|
||||||
|
|
||||||
# executes a mix of tracks, creating a final output mix
|
# executes a mix of tracks, creating a final output mix
|
||||||
class IcecastConfigWriter
|
class IcecastConfigWriter
|
||||||
|
extend Resque::Plugins::LonelyJob
|
||||||
|
|
||||||
@@log = Logging.logger[IcecastConfigWriter]
|
@@log = Logging.logger[IcecastConfigWriter]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
require 'json'
|
||||||
|
require 'resque'
|
||||||
|
require 'resque-retry'
|
||||||
|
require 'net/http'
|
||||||
|
require 'digest/md5'
|
||||||
|
|
||||||
|
module JamRuby
|
||||||
|
|
||||||
|
# http://blog.bignerdranch.com/1643-never-use-resque-for-serial-jobs/
|
||||||
|
# periodically scheduled to find sources that need to be brought down, or alternatively, it seems the client failed to start sourcing
|
||||||
|
class IcecastSourceCheck
|
||||||
|
|
||||||
|
@queue = :icecast_source_check
|
||||||
|
|
||||||
|
@@log = Logging.logger[IcecastSourceCheck]
|
||||||
|
|
||||||
|
def self.perform
|
||||||
|
@@log.debug("waking up")
|
||||||
|
|
||||||
|
# if we haven't seen updated_at be tickled in 5 minutes, but config_changed is still set to TRUE, this record has gotten stale
|
||||||
|
IcecastMount.find_each(:conditions => "sourced_needs_changing_at < (NOW() - interval '#{APP_CONFIG.icecast_max_sourced_changed} second')", :batch_size => 100) do |server|
|
||||||
|
server.with_lock do
|
||||||
|
IcecastConfigWriter.enqueue(server.server_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
@@log.debug("done")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
@ -14,7 +14,11 @@ module JamRuby
|
||||||
@@log = Logging.logger[IcecastConfigRetry]
|
@@log = Logging.logger[IcecastConfigRetry]
|
||||||
|
|
||||||
def self.perform
|
def self.perform
|
||||||
|
@@log.debug("waking up")
|
||||||
|
|
||||||
IcecastConfigWriter.queue_jobs_needing_retry
|
IcecastConfigWriter.queue_jobs_needing_retry
|
||||||
|
|
||||||
|
@@log.debug("done")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
require 'json'
|
||||||
|
require 'resque'
|
||||||
|
require 'resque-lonely_job'
|
||||||
|
require 'net/http'
|
||||||
|
require 'digest/md5'
|
||||||
|
|
||||||
|
module JamRuby
|
||||||
|
|
||||||
|
# http://blog.bignerdranch.com/1643-never-use-resque-for-serial-jobs/
|
||||||
|
# periodically scheduled to find sources that need to be brought down, or alternatively, it seems the client failed to start sourcing
|
||||||
|
class IcecastSourceCheck
|
||||||
|
extend Resque::Plugins::LonelyJob
|
||||||
|
|
||||||
|
|
||||||
|
@queue = :icecast_source_check
|
||||||
|
|
||||||
|
|
||||||
|
@@log = Logging.logger[IcecastSourceCheck]
|
||||||
|
|
||||||
|
|
||||||
|
def self.perform
|
||||||
|
@@log.debug("waking up")
|
||||||
|
|
||||||
|
JamWebEventMachine.run_wait_stop do
|
||||||
|
IcecastSourceCheck.new.run
|
||||||
|
end
|
||||||
|
|
||||||
|
@@log.debug("done")
|
||||||
|
end
|
||||||
|
|
||||||
|
def run # if we haven't seen updated_at be tickled in 5 minutes, but config_changed is still set to TRUE, this record has gotten stale
|
||||||
|
IcecastMount.find_each(lock: true, :conditions => "sourced_needs_changing_at < (NOW() - interval '#{APP_CONFIG.icecast_max_sourced_changed} second')", :batch_size => 100) do |mount|
|
||||||
|
if mount.music_session_id
|
||||||
|
mount.with_lock do
|
||||||
|
handle_notifications(mount)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_notifications(mount)
|
||||||
|
if mount.listeners == 0 && mount.sourced
|
||||||
|
# if no listeners, but we are sourced, then ask it to stop sourcing
|
||||||
|
@@log.debug("SOURCE_DOWN_REQUEST called on mount #{mount.name}")
|
||||||
|
|
||||||
|
mount.update_attribute(:sourced_needs_changing_at, Time.now) # we send out a source request, so we need to update the time
|
||||||
|
mount.notify_source_down_requested
|
||||||
|
|
||||||
|
elsif mount.listeners > 0 && !mount.sourced
|
||||||
|
# if we have some listeners, and still are not sourced, then ask to start sourcing again
|
||||||
|
@@log.debug("SOURCE_UP_REQUEST called on mount #{mount.name}")
|
||||||
|
|
||||||
|
mount.update_attribute(:sourced_needs_changing_at, Time.now) # we send out a source request, so we need to update the time
|
||||||
|
mount.notify_source_up_requested
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -195,7 +195,7 @@ FactoryGirl.define do
|
||||||
factory :icecast_mount, :class => JamRuby::IcecastMount do
|
factory :icecast_mount, :class => JamRuby::IcecastMount do
|
||||||
name "/" + Faker::Lorem.characters(10)
|
name "/" + Faker::Lorem.characters(10)
|
||||||
source_username Faker::Lorem.characters(10)
|
source_username Faker::Lorem.characters(10)
|
||||||
source_pass Faker::Lorem.characters(10)
|
source_pass Faker::Lorem.characters(10)
|
||||||
max_listeners 100
|
max_listeners 100
|
||||||
max_listener_duration 3600
|
max_listener_duration 3600
|
||||||
fallback_mount Faker::Lorem.characters(10)
|
fallback_mount Faker::Lorem.characters(10)
|
||||||
|
|
@ -207,10 +207,21 @@ FactoryGirl.define do
|
||||||
stream_url Faker::Lorem.characters(10)
|
stream_url Faker::Lorem.characters(10)
|
||||||
genre Faker::Lorem.characters(10)
|
genre Faker::Lorem.characters(10)
|
||||||
hidden 0
|
hidden 0
|
||||||
|
association :server, factory: :icecast_server_with_overrides
|
||||||
|
|
||||||
factory :icecast_mount_with_auth do
|
factory :icecast_mount_with_auth do
|
||||||
association :authentication, :factory => :icecast_user_authentication
|
association :authentication, :factory => :icecast_user_authentication
|
||||||
|
|
||||||
|
factory :iceast_mount_with_template do
|
||||||
|
association :mount_template, :factory => :icecast_mount_template
|
||||||
|
|
||||||
|
factory :iceast_mount_with_music_session do
|
||||||
|
association :music_session, :factory => :music_session
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :icecast_listen_socket, :class => JamRuby::IcecastListenSocket do
|
factory :icecast_listen_socket, :class => JamRuby::IcecastListenSocket do
|
||||||
|
|
@ -227,7 +238,7 @@ FactoryGirl.define do
|
||||||
factory :icecast_user_authentication, :class => JamRuby::IcecastUserAuthentication do
|
factory :icecast_user_authentication, :class => JamRuby::IcecastUserAuthentication do
|
||||||
authentication_type 'url'
|
authentication_type 'url'
|
||||||
unused_username Faker::Lorem.characters(10)
|
unused_username Faker::Lorem.characters(10)
|
||||||
unused_pass Faker::Lorem.characters(10)
|
unused_pass Faker::Lorem.characters(10)
|
||||||
mount_add Faker::Lorem.characters(10)
|
mount_add Faker::Lorem.characters(10)
|
||||||
mount_remove Faker::Lorem.characters(10)
|
mount_remove Faker::Lorem.characters(10)
|
||||||
listener_add Faker::Lorem.characters(10)
|
listener_add Faker::Lorem.characters(10)
|
||||||
|
|
@ -242,6 +253,7 @@ FactoryGirl.define do
|
||||||
|
|
||||||
factory :icecast_server_minimal do
|
factory :icecast_server_minimal do
|
||||||
association :template, :factory => :icecast_template_minimal
|
association :template, :factory => :icecast_template_minimal
|
||||||
|
association :mount_template, :factory => :icecast_mount_template
|
||||||
|
|
||||||
factory :icecast_server_with_overrides do
|
factory :icecast_server_with_overrides do
|
||||||
association :limit, :factory => :icecast_limit
|
association :limit, :factory => :icecast_limit
|
||||||
|
|
@ -274,4 +286,23 @@ FactoryGirl.define do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory :icecast_mount_template, :class => JamRuby::IcecastMountTemplate do
|
||||||
|
sequence(:name) { |n| "name-#{n}"}
|
||||||
|
source_username Faker::Lorem.characters(10)
|
||||||
|
source_pass Faker::Lorem.characters(10)
|
||||||
|
max_listeners 100
|
||||||
|
max_listener_duration 3600
|
||||||
|
fallback_mount Faker::Lorem.characters(10)
|
||||||
|
fallback_override 1
|
||||||
|
fallback_when_full 1
|
||||||
|
is_public -1
|
||||||
|
stream_name Faker::Lorem.characters(10)
|
||||||
|
stream_description Faker::Lorem.characters(10)
|
||||||
|
stream_url Faker::Lorem.characters(10)
|
||||||
|
genre Faker::Lorem.characters(10)
|
||||||
|
hidden 0
|
||||||
|
association :authentication, :factory => :icecast_user_authentication
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,22 @@ describe IcecastAdminAuthentication do
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "success when deleted via template" do
|
||||||
|
server.template.admin_auth.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
it "success via server" do
|
it "success via server" do
|
||||||
server.admin_auth.save!
|
server.admin_auth.save!
|
||||||
server.reload
|
server.reload
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "success when deleted via server" do
|
||||||
|
server.admin_auth.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -57,11 +57,23 @@ describe IcecastDirectory do
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via template" do
|
||||||
|
server.template.directory.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
it "success via server" do
|
it "success via server" do
|
||||||
server.directory.save!
|
server.directory.save!
|
||||||
server.reload
|
server.reload
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "destroy via server" do
|
||||||
|
server.directory.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,22 @@ describe IcecastLimit do
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via template" do
|
||||||
|
server.template.limit.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
it "success via server" do
|
it "success via server" do
|
||||||
server.limit.save!
|
server.limit.save!
|
||||||
server.reload
|
server.reload
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via server" do
|
||||||
|
server.limit.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,22 @@ describe IcecastListenSocket do
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via template" do
|
||||||
|
server.template.listen_sockets.first.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
it "success via server" do
|
it "success via server" do
|
||||||
server.listen_sockets.first.save!
|
server.listen_sockets.first.save!
|
||||||
server.reload
|
server.reload
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via server" do
|
||||||
|
server.listen_sockets.first.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,22 @@ describe IcecastLogging do
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via template" do
|
||||||
|
server.template.logging.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
it "success via server" do
|
it "success via server" do
|
||||||
server.logging.save!
|
server.logging.save!
|
||||||
server.reload
|
server.reload
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "deete via server" do
|
||||||
|
server.logging.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -53,11 +53,23 @@ describe IcecastMasterServerRelay do
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via template" do
|
||||||
|
server.template.master_relay.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
it "success via server" do
|
it "success via server" do
|
||||||
server.master_relay.save!
|
server.master_relay.save!
|
||||||
server.reload
|
server.reload
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via server" do
|
||||||
|
server.master_relay.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -10,10 +10,6 @@ describe IcecastMount do
|
||||||
mount = IcecastMount.new
|
mount = IcecastMount.new
|
||||||
mount.save.should be_false
|
mount.save.should be_false
|
||||||
mount.errors[:name].should == ["can't be blank", "must start with /"]
|
mount.errors[:name].should == ["can't be blank", "must start with /"]
|
||||||
mount.errors[:stream_name].should == ["can't be blank"]
|
|
||||||
mount.errors[:stream_description].should == ["can't be blank"]
|
|
||||||
mount.errors[:stream_url].should == ["can't be blank"]
|
|
||||||
mount.errors[:genre].should == ["can't be blank"]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -35,6 +31,7 @@ describe IcecastMount do
|
||||||
mount.max_listeners = 1000
|
mount.max_listeners = 1000
|
||||||
mount.max_listener_duration = 3600
|
mount.max_listener_duration = 3600
|
||||||
mount.authentication = FactoryGirl.create(:icecast_user_authentication)
|
mount.authentication = FactoryGirl.create(:icecast_user_authentication)
|
||||||
|
mount.server = FactoryGirl.create(:icecast_server_with_overrides)
|
||||||
|
|
||||||
mount.save!
|
mount.save!
|
||||||
|
|
||||||
|
|
@ -70,11 +67,39 @@ describe IcecastMount do
|
||||||
xml.css('mount authentication').length.should == 1 # no reason to test futher; it's tested in that model
|
xml.css('mount authentication').length.should == 1 # no reason to test futher; it's tested in that model
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "override xml over mount template" do
|
||||||
|
let(:mount) {FactoryGirl.create(:iceast_mount_with_template)}
|
||||||
|
|
||||||
|
it "should allow override by mount" do
|
||||||
|
mount.dumpXml(builder)
|
||||||
|
output.rewind
|
||||||
|
xml = Nokogiri::XML(output)
|
||||||
|
xml.css('mount mount-name').text.should == mount.name
|
||||||
|
xml.css('mount username').text.should == mount.source_username
|
||||||
|
xml.css('mount bitrate').text.should == mount.bitrate.to_s
|
||||||
|
xml.css('mount type').text.should == mount.mount_template.mime_type
|
||||||
|
xml.css('mount stream-url').text.should == mount.stream_url
|
||||||
|
|
||||||
|
# now see the stream_url, and bitrate, go back to the template's value because we set it to nil
|
||||||
|
mount.bitrate = nil
|
||||||
|
mount.stream_url = nil
|
||||||
|
mount.save!
|
||||||
|
|
||||||
|
output = StringIO.new
|
||||||
|
builder = ::Builder::XmlMarkup.new(:target => output, :indent => 1)
|
||||||
|
mount.dumpXml(builder)
|
||||||
|
output.rewind
|
||||||
|
xml = Nokogiri::XML(output)
|
||||||
|
xml.css('mount bitrate').text.should == mount.mount_template.bitrate.to_s
|
||||||
|
xml.css('mount stream-url').text.should == mount.mount_template.stream_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "poke configs" do
|
describe "poke configs" do
|
||||||
let(:server) { a = FactoryGirl.create(:icecast_server_with_overrides); a.config_updated; IcecastServer.find(a.id) }
|
let(:server) { a = FactoryGirl.create(:icecast_server_with_overrides); a.config_updated; IcecastServer.find(a.id) }
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
server.mounts << FactoryGirl.create(:icecast_mount)
|
server.mounts << FactoryGirl.create(:icecast_mount, server: server)
|
||||||
server.save!
|
server.save!
|
||||||
server.config_updated
|
server.config_updated
|
||||||
server.reload
|
server.reload
|
||||||
|
|
@ -86,6 +111,12 @@ describe IcecastMount do
|
||||||
server.reload
|
server.reload
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "success when deleted" do
|
||||||
|
server.mounts.first.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "icecast server callbacks" do
|
describe "icecast server callbacks" do
|
||||||
|
|
@ -93,4 +124,125 @@ describe IcecastMount do
|
||||||
icecast_mount.source_up
|
icecast_mount.source_up
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "listener/source" do
|
||||||
|
let(:mount) {FactoryGirl.create(:iceast_mount_with_template)}
|
||||||
|
|
||||||
|
describe "listeners" do
|
||||||
|
it "listener_add" do
|
||||||
|
mount.listener_add
|
||||||
|
mount.listeners.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it "listener_remove when at 0" do
|
||||||
|
mount.listener_remove
|
||||||
|
mount.listeners.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it "listener_remove" do
|
||||||
|
mount.listener_add
|
||||||
|
mount.listener_remove
|
||||||
|
mount.listeners.should == 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "sources" do
|
||||||
|
it "source_up" do
|
||||||
|
mount.source_up
|
||||||
|
mount.sourced.should == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "sources" do
|
||||||
|
it "source_down" do
|
||||||
|
mount.source_up
|
||||||
|
mount.source_down
|
||||||
|
mount.sourced.should == false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
describe "build_session_mount" do
|
||||||
|
|
||||||
|
let(:server1) {FactoryGirl.create(:icecast_server_minimal)}
|
||||||
|
let(:server2) {FactoryGirl.create(:icecast_server_with_overrides)}
|
||||||
|
let(:server3) {FactoryGirl.create(:icecast_server_with_overrides)}
|
||||||
|
let(:hidden_music_session) { FactoryGirl.create(:music_session, :fan_access => false)}
|
||||||
|
let(:public_music_session) { FactoryGirl.create(:music_session, :fan_access => true)}
|
||||||
|
let(:public_music_session2) { FactoryGirl.create(:music_session, :fan_access => true)}
|
||||||
|
let(:public_music_session3) { FactoryGirl.create(:music_session, :fan_access => true)}
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it "no fan access means no mount" do
|
||||||
|
mount = IcecastMount.build_session_mount(hidden_music_session)
|
||||||
|
mount.should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "with no servers" do
|
||||||
|
IcecastServer.count.should == 0
|
||||||
|
mount = IcecastMount.build_session_mount(public_music_session)
|
||||||
|
mount.should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "with a server that has a mount template" do
|
||||||
|
server1.mount_template.should_not be_nil
|
||||||
|
mount = IcecastMount.build_session_mount(public_music_session)
|
||||||
|
mount.should_not be_nil
|
||||||
|
mount.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
it "with a server that already has an associated mount" do
|
||||||
|
server1.mount_template.should_not be_nil
|
||||||
|
mount = IcecastMount.build_session_mount(public_music_session)
|
||||||
|
mount.save!
|
||||||
|
|
||||||
|
mount = IcecastMount.build_session_mount(public_music_session2)
|
||||||
|
mount.save!
|
||||||
|
server1.reload
|
||||||
|
server1.mounts.length.should == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
it "picks a second server once the 1st has been chosen" do
|
||||||
|
server1.touch
|
||||||
|
|
||||||
|
mount = IcecastMount.build_session_mount(public_music_session)
|
||||||
|
mount.listeners = 1 # affect the weight
|
||||||
|
mount.save!
|
||||||
|
|
||||||
|
server2.touch
|
||||||
|
|
||||||
|
mount = IcecastMount.build_session_mount(public_music_session2)
|
||||||
|
mount.save!
|
||||||
|
server1.reload
|
||||||
|
server1.mounts.length.should == 1
|
||||||
|
server2.reload
|
||||||
|
server2.mounts.length.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it "picks the 1st server again once the 2nd has higher weight" do
|
||||||
|
server1.touch
|
||||||
|
|
||||||
|
mount = IcecastMount.build_session_mount(public_music_session)
|
||||||
|
mount.listeners = 1 # affect the weight
|
||||||
|
mount.save!
|
||||||
|
|
||||||
|
server2.touch
|
||||||
|
|
||||||
|
mount = IcecastMount.build_session_mount(public_music_session2)
|
||||||
|
mount.sourced = 1
|
||||||
|
mount.save!
|
||||||
|
|
||||||
|
mount = IcecastMount.build_session_mount(public_music_session3)
|
||||||
|
mount.listeners = 1
|
||||||
|
mount.save!
|
||||||
|
|
||||||
|
server1.reload
|
||||||
|
server1.mounts.length.should == 2
|
||||||
|
server2.reload
|
||||||
|
server2.mounts.length.should == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe IcecastMountTemplate do
|
||||||
|
|
||||||
|
let(:mount_template) { template = FactoryGirl.create(:icecast_mount_template) }
|
||||||
|
|
||||||
|
it "save" do
|
||||||
|
mount_template.errors.any?.should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "poke configs" do
|
||||||
|
let(:server) { a = FactoryGirl.create(:icecast_server_with_overrides); a.config_updated; IcecastServer.find(a.id) }
|
||||||
|
let(:music_session) { FactoryGirl.create(:music_session, :fan_access => true)}
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
server.touch
|
||||||
|
mount = IcecastMount.build_session_mount(music_session)
|
||||||
|
mount.save!
|
||||||
|
server.save!
|
||||||
|
server.config_updated
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it "success via server" do
|
||||||
|
server.mounts.first.mount_template.save!
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it "delete via server" do
|
||||||
|
server.mounts.first.mount_template.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -64,10 +64,22 @@ describe IcecastPath do
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via template" do
|
||||||
|
server.template.path.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
it "success via server" do
|
it "success via server" do
|
||||||
server.path.save!
|
server.path.save!
|
||||||
server.reload
|
server.reload
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via server" do
|
||||||
|
server.path.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -48,5 +48,11 @@ describe IcecastRelay do
|
||||||
server.reload
|
server.reload
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via server" do
|
||||||
|
server.relays.first.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -40,10 +40,22 @@ describe IcecastSecurity do
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via template" do
|
||||||
|
server.template.security.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
it "success via server" do
|
it "success via server" do
|
||||||
server.security.save!
|
server.security.save!
|
||||||
server.reload
|
server.reload
|
||||||
server.config_changed.should == 1
|
server.config_changed.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "delete via server" do
|
||||||
|
server.security.destroy
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -30,4 +30,34 @@ describe IcecastServer do
|
||||||
xml.css('icecast security').length.should == 1
|
xml.css('icecast security').length.should == 1
|
||||||
xml.css('icecast listen-socket').length.should == 1
|
xml.css('icecast listen-socket').length.should == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "xml overrides" do
|
||||||
|
server = FactoryGirl.create(:icecast_server_minimal)
|
||||||
|
server.save!
|
||||||
|
server.reload
|
||||||
|
server.dumpXml(output)
|
||||||
|
|
||||||
|
output.rewind
|
||||||
|
|
||||||
|
xml = Nokogiri::XML(output)
|
||||||
|
xml.css('icecast location').text.should == server.template.location
|
||||||
|
xml.css('icecast fileserve').text.should == server.template.fileserve.to_s
|
||||||
|
xml.css('icecast limits').length.should == 1
|
||||||
|
xml.css('icecast limits queue-size').text.should == server.template.limit.queue_size.to_s
|
||||||
|
|
||||||
|
server.location = "override"
|
||||||
|
server.fileserve = 1
|
||||||
|
server.limit = FactoryGirl.create(:icecast_limit, :queue_size => 777)
|
||||||
|
server.save!
|
||||||
|
|
||||||
|
output = StringIO.new
|
||||||
|
builder = ::Builder::XmlMarkup.new(:target => output, :indent => 1)
|
||||||
|
server.dumpXml(builder)
|
||||||
|
output.rewind
|
||||||
|
xml = Nokogiri::XML(output)
|
||||||
|
xml.css('icecast location').text.should == server.location
|
||||||
|
xml.css('icecast fileserve').text.should == server.fileserve.to_s
|
||||||
|
xml.css('icecast limits').length.should == 1
|
||||||
|
xml.css('icecast limits queue-size').text.should == server.limit.queue_size.to_s
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe IcecastListenSocket do
|
describe IcecastTemplate do
|
||||||
|
|
||||||
let(:template) { template = FactoryGirl.create(:icecast_template_minimal) }
|
let(:template) { template = FactoryGirl.create(:icecast_template_minimal) }
|
||||||
|
|
||||||
it "save" do
|
it "save" do
|
||||||
template.errors.any?.should be_false
|
template.errors.any?.should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "poke configs" do
|
||||||
|
let(:server) { a = FactoryGirl.create(:icecast_server_with_overrides); a.config_updated; IcecastServer.find(a.id) }
|
||||||
|
|
||||||
|
it "success via template" do
|
||||||
|
server.template.save!
|
||||||
|
server.reload
|
||||||
|
server.config_changed.should == 1
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -480,5 +480,36 @@ describe MusicSession do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "get_connection_ids" do
|
||||||
|
before(:each) do
|
||||||
|
@user1 = FactoryGirl.create(:user)
|
||||||
|
@user2 = FactoryGirl.create(:user)
|
||||||
|
@music_session = FactoryGirl.create(:music_session, :creator => @user1, :musician_access => true)
|
||||||
|
@connection1 = FactoryGirl.create(:connection, :user => @user1, :music_session => @music_session, :as_musician => true)
|
||||||
|
@connection2 = FactoryGirl.create(:connection, :user => @user2, :music_session => @music_session, :as_musician => false)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it "get all connections" do
|
||||||
|
@music_session.get_connection_ids().should == [@connection1.client_id, @connection2.client_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "exclude non-musicians" do
|
||||||
|
@music_session.get_connection_ids(as_musician: true).should == [@connection1.client_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "exclude musicians" do
|
||||||
|
@music_session.get_connection_ids(as_musician: false).should == [@connection2.client_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "exclude particular client" do
|
||||||
|
@music_session.get_connection_ids(exclude_client_id: @connection1.client_id).should == [@connection2.client_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "exclude particular client and exclude non-musicians" do
|
||||||
|
@music_session.get_connection_ids(exclude_client_id: @connection2.client_id, as_musician: true).should == [@connection1.client_id]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'fileutils'
|
||||||
|
|
||||||
|
# these tests avoid the use of ActiveRecord and FactoryGirl to do blackbox, non test-instrumented tests
|
||||||
|
describe IcecastSourceCheck do
|
||||||
|
|
||||||
|
let(:check) { IcecastSourceCheck.new }
|
||||||
|
|
||||||
|
describe "integration" do
|
||||||
|
|
||||||
|
it "be OK with no mounts" do
|
||||||
|
IcecastMount.count().should == 0
|
||||||
|
check.should_not_receive(:handle_notifications)
|
||||||
|
check.run
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it "find no mounts if source_hanged timestamp is nil and listeners = 1/sourced = false" do
|
||||||
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, sourced: false, listeners: 1)
|
||||||
|
check.should_not_receive(:handle_notifications)
|
||||||
|
check.run
|
||||||
|
end
|
||||||
|
|
||||||
|
it "find no mounts if source_changed timestamp is nil and listeners = 0/sourced = true" do
|
||||||
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, sourced: true, listeners: 1)
|
||||||
|
check.should_not_receive(:handle_notifications)
|
||||||
|
check.run
|
||||||
|
end
|
||||||
|
|
||||||
|
it "find no mounts if source_changed timestamp is very recent and listeners = 1/sourced = false" do
|
||||||
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, sourced_needs_changing_at: Time.now, sourced: false, listeners: 1)
|
||||||
|
check.should_not_receive(:handle_notifications)
|
||||||
|
check.run
|
||||||
|
end
|
||||||
|
|
||||||
|
it "find no mounts if source_changed timestamp is very recent and listeners = 0/sourced = true" do
|
||||||
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, sourced_needs_changing_at: Time.now, sourced: true, listeners: 0)
|
||||||
|
check.should_not_receive(:handle_notifications)
|
||||||
|
check.run
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sends notify_source_down_requested when old source_changed timestamp, and sourced = true and listeners = 0" do
|
||||||
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, sourced_needs_changing_at: 2.days.ago, sourced:true, listeners: 0)
|
||||||
|
check.stub(:handle_notifications) do |mount|
|
||||||
|
mount.should_receive(:notify_source_down_requested).once
|
||||||
|
mount.should_not_receive(:notify_source_up_requested)
|
||||||
|
mount.should_not_receive(:notify_source_up)
|
||||||
|
mount.should_not_receive(:notify_source_down)
|
||||||
|
check.unstub!(:handle_notifications)
|
||||||
|
check.handle_notifications(mount)
|
||||||
|
end
|
||||||
|
check.run
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not send notify_source_down_requested when old source_changed timestamp, and sourced = true and listeners = 1" do
|
||||||
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, sourced_needs_changing_at: 2.days.ago, sourced:true, listeners: 1)
|
||||||
|
check.stub(:handle_notifications) do |mount|
|
||||||
|
mount.should_not_receive(:notify_source_down_requested)
|
||||||
|
mount.should_not_receive(:notify_source_up_requested)
|
||||||
|
mount.should_not_receive(:notify_source_up)
|
||||||
|
mount.should_not_receive(:notify_source_down)
|
||||||
|
check.unstub!(:handle_notifications)
|
||||||
|
check.handle_notifications(mount)
|
||||||
|
end
|
||||||
|
check.run
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sends notify_source_up_requested when old source_changed timestamp, and sourced = false and listeners = 1" do
|
||||||
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, sourced_needs_changing_at: 2.days.ago, sourced:false, listeners: 1)
|
||||||
|
check.stub(:handle_notifications) do |mount|
|
||||||
|
mount.should_not_receive(:notify_source_down_requested)
|
||||||
|
mount.should_receive(:notify_source_up_requested).once
|
||||||
|
mount.should_not_receive(:notify_source_up)
|
||||||
|
mount.should_not_receive(:notify_source_down)
|
||||||
|
check.unstub!(:handle_notifications)
|
||||||
|
check.handle_notifications(mount)
|
||||||
|
end
|
||||||
|
check.run
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
it "does not send notify_source_up_requested when old source_changed timestamp, and sourced = false and listeners = 0" do
|
||||||
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, sourced_needs_changing_at: 2.days.ago, sourced:false, listeners: 0)
|
||||||
|
check.stub(:handle_notifications) do |mount|
|
||||||
|
mount.should_not_receive(:notify_source_down_requested)
|
||||||
|
mount.should_not_receive(:notify_source_up_requested)
|
||||||
|
mount.should_not_receive(:notify_source_up)
|
||||||
|
mount.should_not_receive(:notify_source_down)
|
||||||
|
check.unstub!(:handle_notifications)
|
||||||
|
check.handle_notifications(mount)
|
||||||
|
end
|
||||||
|
check.run
|
||||||
|
end
|
||||||
|
|
||||||
|
it "resets source_changed_at when a notification is sent out" do
|
||||||
|
mount = FactoryGirl.create(:iceast_mount_with_music_session, sourced_needs_changing_at: 2.days.ago, sourced:false, listeners: 1)
|
||||||
|
check.stub(:handle_notifications) do |mount|
|
||||||
|
mount.should_not_receive(:notify_source_down_requested)
|
||||||
|
mount.should_receive(:notify_source_up_requested).once
|
||||||
|
mount.should_not_receive(:notify_source_up)
|
||||||
|
mount.should_not_receive(:notify_source_down)
|
||||||
|
check.unstub!(:handle_notifications)
|
||||||
|
check.handle_notifications(mount)
|
||||||
|
end
|
||||||
|
check.run
|
||||||
|
mount.reload
|
||||||
|
(mount.sourced_needs_changing_at.to_i - Time.now.to_i).abs.should < 10 # less than 5 seconds -- just a little slop for a very slow build server
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -79,8 +79,8 @@ Spork.prefork do
|
||||||
config.filter_run_excluding aws: true unless run_tests? :aws
|
config.filter_run_excluding aws: true unless run_tests? :aws
|
||||||
|
|
||||||
config.before(:suite) do
|
config.before(:suite) do
|
||||||
DatabaseCleaner.strategy = :truncation, {:except => %w[instruments genres] }
|
DatabaseCleaner.strategy = :truncation, {:except => %w[instruments genres icecast_server_groups] }
|
||||||
DatabaseCleaner.clean_with(:truncation, {:except => %w[instruments genres] })
|
DatabaseCleaner.clean_with(:truncation, {:except => %w[instruments genres icecast_server_groups] })
|
||||||
end
|
end
|
||||||
|
|
||||||
config.before(:each) do
|
config.before(:each) do
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ def app_config
|
||||||
2 * 60 # 2 minutes
|
2 * 60 # 2 minutes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def icecast_max_sourced_changed
|
||||||
|
15 # 15 seconds
|
||||||
|
end
|
||||||
|
|
||||||
def rabbitmq_host
|
def rabbitmq_host
|
||||||
"localhost"
|
"localhost"
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ gem 'resque'
|
||||||
gem 'resque-retry'
|
gem 'resque-retry'
|
||||||
gem 'resque-failed-job-mailer'
|
gem 'resque-failed-job-mailer'
|
||||||
gem 'resque-dynamic-queues'
|
gem 'resque-dynamic-queues'
|
||||||
|
gem 'resque-lonely_job', '~> 1.0.0'
|
||||||
gem 'quiet_assets', :group => :development
|
gem 'quiet_assets', :group => :development
|
||||||
gem "bugsnag"
|
gem "bugsnag"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,12 @@
|
||||||
BAND_INVITATION : "BAND_INVITATION",
|
BAND_INVITATION : "BAND_INVITATION",
|
||||||
BAND_INVITATION_ACCEPTED : "BAND_INVITATION_ACCEPTED",
|
BAND_INVITATION_ACCEPTED : "BAND_INVITATION_ACCEPTED",
|
||||||
|
|
||||||
|
// broadcast notifications
|
||||||
|
SOURCE_UP_REQUESTED : "SOURCE_UP_REQUESTED",
|
||||||
|
SOURCE_DOWN_REQUESTED : "SOURCE_DOWN_REQUESTED",
|
||||||
|
SOURCE_UP : "SOURCE_UP",
|
||||||
|
SOURCE_DOWN : "SOURCE_DOWN",
|
||||||
|
|
||||||
TEST_SESSION_MESSAGE : "TEST_SESSION_MESSAGE",
|
TEST_SESSION_MESSAGE : "TEST_SESSION_MESSAGE",
|
||||||
PING_REQUEST : "PING_REQUEST",
|
PING_REQUEST : "PING_REQUEST",
|
||||||
PING_ACK : "PING_ACK",
|
PING_ACK : "PING_ACK",
|
||||||
|
|
|
||||||
|
|
@ -7,81 +7,19 @@
|
||||||
var logger = context.JK.logger;
|
var logger = context.JK.logger;
|
||||||
var rest = context.JK.Rest();
|
var rest = context.JK.Rest();
|
||||||
var realtimeMessaging = context.JK.JamServer;
|
var realtimeMessaging = context.JK.JamServer;
|
||||||
var friendSelectorDialog = null;
|
|
||||||
var invitationDialog = null;
|
var invitationDialog = null;
|
||||||
var autoComplete = null;
|
var inviteMusiciansUtil = null;
|
||||||
var userNames = [];
|
|
||||||
var userIds = [];
|
|
||||||
var userPhotoUrls = [];
|
|
||||||
var MAX_GENRES = 1;
|
var MAX_GENRES = 1;
|
||||||
var selectedFriendIds = {};
|
|
||||||
var sessionSettings = {};
|
var sessionSettings = {};
|
||||||
|
|
||||||
function beforeShow(data) {
|
function beforeShow(data) {
|
||||||
userNames = [];
|
inviteMusiciansUtil.clearSelections();
|
||||||
userIds = [];
|
|
||||||
userPhotoUrls = [];
|
|
||||||
context.JK.GenreSelectorHelper.render('#create-session-genre');
|
context.JK.GenreSelectorHelper.render('#create-session-genre');
|
||||||
resetForm();
|
resetForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterShow(data) {
|
function afterShow(data) {
|
||||||
friendSelectorDialog.setCallback(friendSelectorCallback);
|
inviteMusiciansUtil.loadFriends();
|
||||||
|
|
||||||
var friends = rest.getFriends({ id: context.JK.currentUserId })
|
|
||||||
.done(function(friends) {
|
|
||||||
$.each(friends, function() {
|
|
||||||
userNames.push(this.name);
|
|
||||||
userIds.push(this.id);
|
|
||||||
userPhotoUrls.push(this.photo_url);
|
|
||||||
});
|
|
||||||
|
|
||||||
var autoCompleteOptions = {
|
|
||||||
lookup: { suggestions: userNames, data: userIds },
|
|
||||||
onSelect: addInvitation
|
|
||||||
};
|
|
||||||
|
|
||||||
$('#friend-input').attr("placeholder", "Type a friend\'s name").prop('disabled', false);
|
|
||||||
|
|
||||||
if (!autoComplete) {
|
|
||||||
autoComplete = $('#friend-input').autocomplete(autoCompleteOptions);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
autoComplete.setOptions(autoCompleteOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
$(".autocomplete").width("150px");
|
|
||||||
})
|
|
||||||
.fail(function() {
|
|
||||||
$('#friend-input').attr("placeholder", "Unable to lookup friends");
|
|
||||||
app.ajaxError(arguments);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function friendSelectorCallback(newSelections) {
|
|
||||||
var keys = Object.keys(newSelections);
|
|
||||||
for (var i=0; i < keys.length; i++) {
|
|
||||||
addInvitation(newSelections[keys[i]].userName, newSelections[keys[i]].userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addInvitation(value, data) {
|
|
||||||
if ($('#selected-friends div[user-id=' + data + ']').length === 0) {
|
|
||||||
var template = $('#template-added-invitation').html();
|
|
||||||
var invitationHtml = context.JK.fillTemplate(template, {userId: data, userName: value});
|
|
||||||
$('#selected-friends').append(invitationHtml);
|
|
||||||
$('#friend-input').select();
|
|
||||||
selectedFriendIds[data] = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$('#friend-input').select();
|
|
||||||
context.alert('Invitation already exists for this musician.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeInvitation(evt) {
|
|
||||||
delete selectedFriendIds[$(evt.currentTarget).parent().attr('user-id')];
|
|
||||||
$(evt.currentTarget).closest('.invitation').remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
|
|
@ -226,7 +164,7 @@
|
||||||
data: jsonData,
|
data: jsonData,
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
var newSessionId = response.id;
|
var newSessionId = response.id;
|
||||||
var invitationCount = createInvitations(newSessionId, function() {
|
var invitationCount = inviteMusiciansUtil.createInvitations(newSessionId, function() {
|
||||||
context.location = '#/session/' + newSessionId;
|
context.location = '#/session/' + newSessionId;
|
||||||
});
|
});
|
||||||
// Re-loading the session settings will cause the form to reset with the right stuff in it.
|
// Re-loading the session settings will cause the form to reset with the right stuff in it.
|
||||||
|
|
@ -248,49 +186,12 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createInvitations(sessionId, onComplete) {
|
|
||||||
var callCount = 0;
|
|
||||||
var totalInvitations = 0;
|
|
||||||
$('#selected-friends .invitation').each(function(index, invitation) {
|
|
||||||
callCount++;
|
|
||||||
totalInvitations++;
|
|
||||||
var invite_id = $(invitation).attr('user-id');
|
|
||||||
var invite = {
|
|
||||||
music_session: sessionId,
|
|
||||||
receiver: invite_id
|
|
||||||
};
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: "/api/invitations",
|
|
||||||
data: invite
|
|
||||||
}).done(function(response) {
|
|
||||||
callCount--;
|
|
||||||
}).fail(app.ajaxError);
|
|
||||||
});
|
|
||||||
// TODO - this is the second time I've used this pattern.
|
|
||||||
// refactor to make a common utility for this.
|
|
||||||
function checker() {
|
|
||||||
if (callCount === 0) {
|
|
||||||
onComplete();
|
|
||||||
} else {
|
|
||||||
context.setTimeout(checker, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checker();
|
|
||||||
return totalInvitations;
|
|
||||||
}
|
|
||||||
|
|
||||||
function events() {
|
function events() {
|
||||||
$('#create-session-form').on('submit', submitForm);
|
$('#create-session-form').on('submit', submitForm);
|
||||||
$('#btn-create-session').on("click", submitForm);
|
$('#btn-create-session').on("click", submitForm);
|
||||||
$('#selected-friends').on("click", ".invitation a", removeInvitation);
|
|
||||||
$('#musician-access').change(toggleMusicianAccess);
|
$('#musician-access').change(toggleMusicianAccess);
|
||||||
$('#fan-access').change(toggleFanAccess);
|
$('#fan-access').change(toggleFanAccess);
|
||||||
|
|
||||||
$('#btn-choose-friends').click(function() {
|
|
||||||
friendSelectorDialog.showDialog(selectedFriendIds);
|
|
||||||
});
|
|
||||||
|
|
||||||
$('div[layout-id="createSession"] .btn-email-invitation').click(function() {
|
$('div[layout-id="createSession"] .btn-email-invitation').click(function() {
|
||||||
invitationDialog.showEmailDialog();
|
invitationDialog.showEmailDialog();
|
||||||
});
|
});
|
||||||
|
|
@ -373,38 +274,10 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchFriends(query) {
|
function initialize(invitationDialogInstance, inviteMusiciansUtilInstance) {
|
||||||
if (query.length < 2) {
|
|
||||||
$('#friend-search-results').empty();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var url = "/api/search?query=" + query + "&userId=" + context.JK.currentUserId;
|
|
||||||
$.ajax({
|
|
||||||
type: "GET",
|
|
||||||
url: url,
|
|
||||||
success: friendSearchComplete
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function friendSearchComplete(response) {
|
|
||||||
// reset search results each time
|
|
||||||
$('#friend-search-results').empty();
|
|
||||||
|
|
||||||
// loop through each
|
|
||||||
$.each(response.friends, function() {
|
|
||||||
// only show friends who are musicians
|
|
||||||
if (this.musician === true) {
|
|
||||||
var template = $('#template-friend-search-results').html();
|
|
||||||
var searchResultHtml = context.JK.fillTemplate(template, {userId: this.id, name: this.first_name + ' ' + this.last_name});
|
|
||||||
$('#friend-search-results').append(searchResultHtml);
|
|
||||||
$('#friend-search-results').attr('style', 'display:block');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initialize(invitationDialogInstance, friendSelectorDialogInstance) {
|
|
||||||
friendSelectorDialog = friendSelectorDialogInstance;
|
|
||||||
invitationDialog = invitationDialogInstance;
|
invitationDialog = invitationDialogInstance;
|
||||||
|
inviteMusiciansUtil = inviteMusiciansUtilInstance;
|
||||||
|
inviteMusiciansUtil.inviteSessionCreate('#create-session-invite-musicians');
|
||||||
events();
|
events();
|
||||||
loadBands();
|
loadBands();
|
||||||
loadSessionSettings();
|
loadSessionSettings();
|
||||||
|
|
@ -419,8 +292,6 @@
|
||||||
this.submitForm = submitForm;
|
this.submitForm = submitForm;
|
||||||
this.validateForm = validateForm;
|
this.validateForm = validateForm;
|
||||||
this.loadBands = loadBands;
|
this.loadBands = loadBands;
|
||||||
this.searchFriends = searchFriends;
|
|
||||||
this.addInvitation = addInvitation;
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -571,6 +571,14 @@
|
||||||
function CloseRecording() {}
|
function CloseRecording() {}
|
||||||
function OnDownloadAvailable() {}
|
function OnDownloadAvailable() {}
|
||||||
|
|
||||||
|
function SessionLiveBroadcastStart(host, port, mount, sourceUser, sourcePass, preferredClientId, bitrate)
|
||||||
|
{
|
||||||
|
logger.debug("SessionLiveBroadcastStart requested");
|
||||||
|
}
|
||||||
|
|
||||||
|
function SessionLiveBroadcastStop() {
|
||||||
|
logger.debug("SessionLiveBroadcastStop requested");
|
||||||
|
}
|
||||||
|
|
||||||
// Javascript Bridge seems to camel-case
|
// Javascript Bridge seems to camel-case
|
||||||
// Set the instance functions:
|
// Set the instance functions:
|
||||||
|
|
@ -703,6 +711,10 @@
|
||||||
this.CloseRecording = CloseRecording;
|
this.CloseRecording = CloseRecording;
|
||||||
this.OnDownloadAvailable = OnDownloadAvailable;
|
this.OnDownloadAvailable = OnDownloadAvailable;
|
||||||
|
|
||||||
|
// Broadcasting
|
||||||
|
this.SessionLiveBroadcastStart = SessionLiveBroadcastStart;
|
||||||
|
this.SessionLiveBroadcastStop = SessionLiveBroadcastStop;
|
||||||
|
|
||||||
// fake calls; not a part of the actual jam client
|
// fake calls; not a part of the actual jam client
|
||||||
this.RegisterP2PMessageCallbacks = RegisterP2PMessageCallbacks;
|
this.RegisterP2PMessageCallbacks = RegisterP2PMessageCallbacks;
|
||||||
this.SetFakeRecordingImpl = SetFakeRecordingImpl;
|
this.SetFakeRecordingImpl = SetFakeRecordingImpl;
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,10 @@
|
||||||
|
|
||||||
for (ii=0, len=musicians.length; ii < len; ii++) {
|
for (ii=0, len=musicians.length; ii < len; ii++) {
|
||||||
mm = musicians[ii];
|
mm = musicians[ii];
|
||||||
|
if (context.JK.currentUserId === mm.id) {
|
||||||
|
// VRFS-294.3 (David) => skip if current user is musician
|
||||||
|
continue;
|
||||||
|
}
|
||||||
instr_logos = '';
|
instr_logos = '';
|
||||||
for (var jj=0, ilen=mm['instruments'].length; jj<ilen; jj++) {
|
for (var jj=0, ilen=mm['instruments'].length; jj<ilen; jj++) {
|
||||||
if (mm['instruments'][jj].instrument_id in instrument_logo_map) {
|
if (mm['instruments'][jj].instrument_id in instrument_logo_map) {
|
||||||
|
|
@ -110,8 +114,10 @@
|
||||||
}
|
}
|
||||||
var actionVals = {
|
var actionVals = {
|
||||||
profile_url: "/#/profile/" + mm.id,
|
profile_url: "/#/profile/" + mm.id,
|
||||||
button_friend: mm['is_friend'] ? '' : 'button-orange',
|
friend_class: 'button-' + (mm['is_friend'] ? 'grey' : 'orange'),
|
||||||
button_follow: mm['is_following'] ? '' : 'button-orange',
|
friend_caption: (mm.is_friend ? 'DIS':'')+'CONNECT',
|
||||||
|
follow_class: 'button-' + (mm['is_following'] ? 'grey' : 'orange'),
|
||||||
|
follow_caption: (mm.is_following ? 'UN':'')+'FOLLOW',
|
||||||
button_message: 'button-orange'
|
button_message: 'button-orange'
|
||||||
};
|
};
|
||||||
var musician_actions = context.JK.fillTemplate(aTemplate, actionVals);
|
var musician_actions = context.JK.fillTemplate(aTemplate, actionVals);
|
||||||
|
|
@ -122,7 +128,7 @@
|
||||||
musician_name: mm.name,
|
musician_name: mm.name,
|
||||||
musician_location: mm.city + ', ' + mm.state,
|
musician_location: mm.city + ', ' + mm.state,
|
||||||
instruments: instr_logos,
|
instruments: instr_logos,
|
||||||
biography: mm['biography'],
|
biography: mm['biography'] || 'Lorum Ipsum Nulla facilisi. In vel sem. Morbi id urna in diam dignissim feugiat. Proin molestie tortor eu velit. Aliquam erat volutpat. Nullam ultrices, diam tempus vulputate egestas, eros pede varius leo, sed imperdiet lectus est ornare odio.',
|
||||||
follow_count: mm['follow_count'],
|
follow_count: mm['follow_count'],
|
||||||
friend_count: mm['friend_count'],
|
friend_count: mm['friend_count'],
|
||||||
recording_count: mm['recording_count'],
|
recording_count: mm['recording_count'],
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,207 @@
|
||||||
|
(function(context,$) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
context.JK = context.JK || {};
|
||||||
|
context.JK.InviteMusiciansUtil = function(app) {
|
||||||
|
var logger = context.JK.logger;
|
||||||
|
var userNames = [];
|
||||||
|
var userIds = [];
|
||||||
|
var userPhotoUrls = [];
|
||||||
|
var friendSelectorDialog = null;
|
||||||
|
var invitedFriends = [];
|
||||||
|
var existingInvites = [];
|
||||||
|
var autoComplete = null;
|
||||||
|
var rest = context.JK.Rest();
|
||||||
|
var inviteAction = 'create'; // create/update
|
||||||
|
var updateSessionID = null;
|
||||||
|
|
||||||
|
this.inviteSessionCreate = function(elemSelector) {
|
||||||
|
inviteAction = 'create';
|
||||||
|
_appendFriendSelector($(elemSelector));
|
||||||
|
};
|
||||||
|
|
||||||
|
this.inviteSessionUpdate = function(elemSelector, sessionId) {
|
||||||
|
this.clearSelections();
|
||||||
|
updateSessionID = sessionId;
|
||||||
|
friendSelectorDialog.setCallback(friendSelectorCallback);
|
||||||
|
inviteAction = 'update';
|
||||||
|
if (0 == $(elemSelector + ' .friendbox').length) {
|
||||||
|
_appendFriendSelector($(elemSelector));
|
||||||
|
$('#btn-save-invites').click(function() {
|
||||||
|
createInvitations(updateSessionID);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/invitations",
|
||||||
|
data: { session_id: sessionId, sender: context.JK.currentUserId }
|
||||||
|
}).done(function(response) {
|
||||||
|
response.map(function(item) {
|
||||||
|
var dd = item['receiver'];
|
||||||
|
existingInvites.push(dd.id);
|
||||||
|
addInvitation(dd.name, dd.id);
|
||||||
|
});
|
||||||
|
}).fail(app.ajaxError);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clearSelections = function() {
|
||||||
|
userNames = [];
|
||||||
|
userIds = [];
|
||||||
|
userPhotoUrls = [];
|
||||||
|
invitedFriends = [];
|
||||||
|
existingInvites = [];
|
||||||
|
updateSessionID = null;
|
||||||
|
$('.selected-friends').empty();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.loadFriends = function() {
|
||||||
|
friendSelectorDialog.setCallback(friendSelectorCallback);
|
||||||
|
|
||||||
|
var friends = rest.getFriends({ id: context.JK.currentUserId })
|
||||||
|
.done(function(friends) {
|
||||||
|
$.each(friends, function() {
|
||||||
|
userNames.push(this.name);
|
||||||
|
userIds.push(this.id);
|
||||||
|
userPhotoUrls.push(this.photo_url);
|
||||||
|
});
|
||||||
|
|
||||||
|
var autoCompleteOptions = {
|
||||||
|
lookup: { suggestions: userNames, data: userIds },
|
||||||
|
onSelect: addInvitation
|
||||||
|
};
|
||||||
|
|
||||||
|
$('#friend-input').attr("placeholder", "Type a friend\'s name").prop('disabled', false);
|
||||||
|
|
||||||
|
if (!autoComplete) {
|
||||||
|
autoComplete = $('#friend-input').autocomplete(autoCompleteOptions);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
autoComplete.setOptions(autoCompleteOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".autocomplete").width("150px");
|
||||||
|
})
|
||||||
|
.fail(function() {
|
||||||
|
$('#friend-input').attr("placeholder", "Unable to lookup friends");
|
||||||
|
app.ajaxError(arguments);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function friendSelectorCallback(newSelections) {
|
||||||
|
var keys = Object.keys(newSelections);
|
||||||
|
for (var i=0; i < keys.length; i++) {
|
||||||
|
var dd = newSelections[keys[i]];
|
||||||
|
addInvitation(dd.userName, dd.userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _inviteExists(userID) {
|
||||||
|
return 0 <= existingInvites.indexOf(userID);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addInvitation(value, data) {
|
||||||
|
if (0 > invitedFriends.indexOf(data)) {
|
||||||
|
var template = $('#template-added-invitation').html();
|
||||||
|
var imgStyle = _inviteExists(data) ? 'display:none' : '';
|
||||||
|
var invitationHtml = context.JK.fillTemplate(template,
|
||||||
|
{userId: data,
|
||||||
|
userName: value,
|
||||||
|
imageStyle: imgStyle});
|
||||||
|
$('.selected-friends').append(invitationHtml);
|
||||||
|
$('#friend-input').select();
|
||||||
|
invitedFriends.push(data);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$('#friend-input').select();
|
||||||
|
context.alert('Invitation already exists for this musician.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeInvitation(evt) {
|
||||||
|
var idx = invitedFriends.indexOf($(evt.currentTarget).parent().attr('user-id'));
|
||||||
|
if (0 <= idx) invitedFriends.splice(idx, 1);
|
||||||
|
$(evt.currentTarget).closest('.invitation').remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createInvitations(sessionId, onComplete) {
|
||||||
|
var callCount = 0;
|
||||||
|
var totalInvitations = invitedFriends.length - existingInvites.length;
|
||||||
|
invitedFriends.map(function(invite_id) {
|
||||||
|
if (!_inviteExists(invite_id)) {
|
||||||
|
callCount++;
|
||||||
|
var invite = {
|
||||||
|
music_session: sessionId,
|
||||||
|
receiver: invite_id
|
||||||
|
};
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/api/invitations",
|
||||||
|
data: invite
|
||||||
|
}).done(function(response) {
|
||||||
|
callCount--;
|
||||||
|
}).fail(app.ajaxError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// TODO - this is the second time I've used this pattern.
|
||||||
|
// refactor to make a common utility for this.
|
||||||
|
function checker() {
|
||||||
|
callCount === 0 ? onComplete() : context.setTimeout(checker, 10);
|
||||||
|
}
|
||||||
|
if (onComplete) checker();
|
||||||
|
return totalInvitations;
|
||||||
|
}
|
||||||
|
this.createInvitations = createInvitations;
|
||||||
|
|
||||||
|
function searchFriends(query) {
|
||||||
|
if (query.length < 2) {
|
||||||
|
$('#friend-search-results').empty();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var url = "/api/search?query=" + query + "&userId=" + context.JK.currentUserId;
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: url,
|
||||||
|
success: friendSearchComplete
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function friendSearchComplete(response) {
|
||||||
|
// reset search results each time
|
||||||
|
$('#friend-search-results').empty();
|
||||||
|
|
||||||
|
// loop through each
|
||||||
|
$.each(response.friends, function() {
|
||||||
|
// only show friends who are musicians
|
||||||
|
if (this.musician === true) {
|
||||||
|
var template = $('#template-friend-search-results').html();
|
||||||
|
var searchResultHtml = context.JK.fillTemplate(template, {userId: this.id, name: this.first_name + ' ' + this.last_name});
|
||||||
|
$('#friend-search-results').append(searchResultHtml);
|
||||||
|
$('#friend-search-results').attr('style', 'display:block');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _friendSelectorHTML() {
|
||||||
|
return context.JK.fillTemplate($('#template-session-invite-musicians').html(),
|
||||||
|
{choose_friends_id: 'btn-choose-friends-'+inviteAction,
|
||||||
|
selected_friends_id: 'selected-friends-'+inviteAction});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _appendFriendSelector(elemSelector) {
|
||||||
|
elemSelector.append(_friendSelectorHTML());
|
||||||
|
$('#selected-friends-'+inviteAction).on("click", ".invitation a", removeInvitation);
|
||||||
|
$('#btn-choose-friends-'+inviteAction).click(function(){
|
||||||
|
var obj = {};
|
||||||
|
invitedFriends.map(function(uid) { obj[uid] = true; });
|
||||||
|
friendSelectorDialog.showDialog(obj);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.initialize = function(friendSelectorDialogInstance) {
|
||||||
|
friendSelectorDialog = friendSelectorDialogInstance;
|
||||||
|
};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
})(window,jQuery);
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
var addNewGearDialog;
|
var addNewGearDialog;
|
||||||
var localRecordingsDialog = null;
|
var localRecordingsDialog = null;
|
||||||
var recordingFinishedDialog = null;
|
var recordingFinishedDialog = null;
|
||||||
|
var friendSelectorDialog = null;
|
||||||
|
var inviteMusiciansUtil = null;
|
||||||
var screenActive = false;
|
var screenActive = false;
|
||||||
var currentMixerRangeMin = null;
|
var currentMixerRangeMin = null;
|
||||||
var currentMixerRangeMax = null;
|
var currentMixerRangeMax = null;
|
||||||
|
|
@ -1302,12 +1304,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function inviteMusicians() {
|
||||||
|
inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians', sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
function events() {
|
function events() {
|
||||||
$('#session-resync').on('click', sessionResync);
|
$('#session-resync').on('click', sessionResync);
|
||||||
$('#session-contents').on("click", '[action="delete"]', deleteSession);
|
$('#session-contents').on("click", '[action="delete"]', deleteSession);
|
||||||
$('#tracks').on('click', 'div[control="mute"]', toggleMute);
|
$('#tracks').on('click', 'div[control="mute"]', toggleMute);
|
||||||
$('#recording-start-stop').on('click', startStopRecording);
|
$('#recording-start-stop').on('click', startStopRecording);
|
||||||
$('#open-a-recording').on('click', openRecording);
|
$('#open-a-recording').on('click', openRecording);
|
||||||
|
$('#session-invite-musicians').on('click', inviteMusicians);
|
||||||
$('#track-settings').click(function() {
|
$('#track-settings').click(function() {
|
||||||
configureTrackDialog.showVoiceChatPanel(true);
|
configureTrackDialog.showVoiceChatPanel(true);
|
||||||
configureTrackDialog.showMusicAudioPanel(true);
|
configureTrackDialog.showMusicAudioPanel(true);
|
||||||
|
|
@ -1319,9 +1326,10 @@
|
||||||
.on('change-position', onChangePlayPosition);
|
.on('change-position', onChangePlayPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initialize = function(localRecordingsDialogInstance, recordingFinishedDialogInstance) {
|
this.initialize = function(localRecordingsDialogInstance, recordingFinishedDialogInstance, inviteMusiciansUtilInstance) {
|
||||||
localRecordingsDialog = localRecordingsDialogInstance;
|
localRecordingsDialog = localRecordingsDialogInstance;
|
||||||
recordingFinishedDialog = recordingFinishedDialogInstance;
|
recordingFinishedDialog = recordingFinishedDialogInstance;
|
||||||
|
inviteMusiciansUtil = inviteMusiciansUtilInstance;
|
||||||
context.jamClient.SetVURefreshRate(150);
|
context.jamClient.SetVURefreshRate(150);
|
||||||
playbackControls = new context.JK.PlaybackControls($('.session-recordings .recording-controls'));
|
playbackControls = new context.JK.PlaybackControls($('.session-recordings .recording-controls'));
|
||||||
events();
|
events();
|
||||||
|
|
|
||||||
|
|
@ -233,10 +233,95 @@
|
||||||
acceptBandInvitation({ "band_invitation_id": payload.band_invitation_id, "band_id": payload.band_id, "notification_id": payload.notification_id });
|
acceptBandInvitation({ "band_invitation_id": payload.band_invitation_id, "band_id": payload.band_id, "notification_id": payload.notification_id });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (type === context.JK.MessageType.BAND_INVITATION_ACCEPTED) {
|
else if (type === context.JK.MessageType.BAND_INVITATION_ACCEPTED) {
|
||||||
$notification.find('#div-actions').hide();
|
$notification.find('#div-actions').hide();
|
||||||
}
|
}
|
||||||
|
else if (type === context.JK.MessageType.SOURCE_UP_REQUESTED) {
|
||||||
|
var current_session_id = context.JK.CurrentSessionModel.id();
|
||||||
|
|
||||||
|
if (!current_session_id) {
|
||||||
|
// we are not in a session
|
||||||
|
var last_session = context.JK.CurrentSessionModel.getCurrentOrLastSession();
|
||||||
|
if(last_session && last_session.id == payload.music_session) {
|
||||||
|
// the last session we were in was responsible for this message. not that odd at all
|
||||||
|
logger.debug("SOURCE_UP_REQUESTED came in for session_id" + payload.music_session + ", but was dropped because we have left that session")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// this means we aren't in a session, and, what's worse,
|
||||||
|
// the last session we were in does not match the specified music_session id
|
||||||
|
throw "SOURCE_UP_REQUESTED came in for session_id:" + payload.music_session + ", but we are not in a session and the last session ID did not match the one specified";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// we are in a session
|
||||||
|
if(current_session_id == payload.music_session) {
|
||||||
|
context.jamClient.SessionLiveBroadcastStart(payload.host, payload.port, payload.mount,
|
||||||
|
payload.source_user, payload.source_pass,
|
||||||
|
'', payload.bitrate)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var last_session = context.JK.CurrentSessionModel.getCurrentOrLastSession();
|
||||||
|
if(last_session && last_session.id == payload.music_session) {
|
||||||
|
// the last session we were in was responsible for this message. not that odd at all
|
||||||
|
logger.debug("SOURCE_UP_REQUESTED came in for session_id" + payload.music_session + ", but was dropped because we have left that session and are in a new one")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// this means we aren't in a session, and, what's worse,
|
||||||
|
// the last session we were in does not match the specified music_session id
|
||||||
|
throw "SOURCE_UP_REQUESTED came in for session_id:" + payload.music_session + ", but we are in a session and the last session ID did not match the one specified";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type === context.JK.MessageType.SOURCE_DOWN_REQUESTED) {
|
||||||
|
var current_session_id = context.JK.CurrentSessionModel.id();
|
||||||
|
|
||||||
|
if (!current_session_id) {
|
||||||
|
// we are not in a session
|
||||||
|
var last_session = context.JK.CurrentSessionModel.getCurrentOrLastSession();
|
||||||
|
if(last_session && last_session.id == payload.music_session) {
|
||||||
|
// the last session we were in was responsible for this message. not that odd at all
|
||||||
|
logger.debug("SOURCE_DOWN_REQUESTED came in for session_id" + payload.music_session + ", but was dropped because we have left that session")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// this means we aren't in a session, and, what's worse,
|
||||||
|
// the last session we were in does not match the specified music_session id
|
||||||
|
throw "SOURCE_DOWN_REQUESTED came in for session_id:" + payload.music_session + ", but we are not in a session and the last session ID did not match the one specified";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// we are in a session
|
||||||
|
if(current_session_id == payload.music_session) {
|
||||||
|
context.jamClient.SessionLiveBroadcastStop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var last_session = context.JK.CurrentSessionModel.getCurrentOrLastSession();
|
||||||
|
if(last_session && last_session.id == payload.music_session) {
|
||||||
|
// the last session we were in was responsible for this message. not that odd at all
|
||||||
|
logger.debug("SOURCE_DOWN_REQUESTED came in for session_id" + payload.music_session + ", but was dropped because we have left that session and are in a new one")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// this means we aren't in a session, and, what's worse,
|
||||||
|
// the last session we were in does not match the specified music_session id
|
||||||
|
throw "SOURCE_DOWN_REQUESTED came in for session_id:" + payload.music_session + ", but we are in a session and the last session ID did not match the one specified";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type === context.JK.MessageType.SOURCE_UP) {
|
||||||
|
log.debug("session %o is now being broadcasted", payload.music_session);
|
||||||
|
app.notify({
|
||||||
|
"title": "Now Broadcasting",
|
||||||
|
"text": "This session is now being broadcasted."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (type === context.JK.MessageType.SOURCE_DOWN) {
|
||||||
|
log.debug("session %o is no longer being broadcasted", payload.music_session);
|
||||||
|
app.notify({
|
||||||
|
"title": "No Longer Broadcasting",
|
||||||
|
"text": "This session is no longer being broadcasted."
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteNotificationHandler(evt) {
|
function deleteNotificationHandler(evt) {
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,9 @@
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
> a.smallbutton {
|
> a.smallbutton {
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
|
&.button-grey {
|
||||||
|
display:none; // @FIXME VRFS-930 / VRFS-931 per comment from David - don't show.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,7 +207,6 @@
|
||||||
|
|
||||||
.friendbox {
|
.friendbox {
|
||||||
padding:5px;
|
padding:5px;
|
||||||
width:100%;
|
|
||||||
height:60px;
|
height:60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#btn-choose-friends {
|
.btn-choose-friends {
|
||||||
margin:0;
|
margin:0;
|
||||||
}
|
}
|
||||||
#create-session-genre select, #create-session-band select {
|
#create-session-genre select, #create-session-band select {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
@import "client/common.css.scss";
|
@import "client/common.css.scss";
|
||||||
|
|
||||||
|
.profile-head {
|
||||||
|
|
||||||
|
}
|
||||||
|
.profile-body {
|
||||||
|
|
||||||
|
}
|
||||||
.profile-header {
|
.profile-header {
|
||||||
padding:10px 20px;
|
padding:10px 20px;
|
||||||
// height:120px;
|
// height:120px;
|
||||||
|
|
|
||||||
|
|
@ -716,3 +716,6 @@ table.vu td {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#update-session-invite-musicians {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
@ -7,15 +7,14 @@ class ApiIcecastController < ApiController
|
||||||
|
|
||||||
|
|
||||||
def mount_add
|
def mount_add
|
||||||
mount = IcecastMount.find(@mount_id)
|
mount = IcecastMount.find_by_name!(@mount_id)
|
||||||
mount.source_up
|
mount.source_up
|
||||||
|
|
||||||
|
|
||||||
render text: '', :status => :ok
|
render text: '', :status => :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def mount_remove
|
def mount_remove
|
||||||
mount = IcecastMount.find(@mount_id)
|
mount = IcecastMount.find_by_name!(@mount_id)
|
||||||
mount.source_down
|
mount.source_down
|
||||||
|
|
||||||
render text: '', :status => :ok
|
render text: '', :status => :ok
|
||||||
|
|
@ -28,7 +27,7 @@ class ApiIcecastController < ApiController
|
||||||
remote_ip = params[:ip]
|
remote_ip = params[:ip]
|
||||||
remote_user_agent = params[:agent]
|
remote_user_agent = params[:agent]
|
||||||
|
|
||||||
mount = IcecastMount.find(@mount_id)
|
mount = IcecastMount.find_by_name!(@mount_id)
|
||||||
mount.listener_add
|
mount.listener_add
|
||||||
|
|
||||||
render text: '', :status => :ok
|
render text: '', :status => :ok
|
||||||
|
|
@ -40,7 +39,7 @@ class ApiIcecastController < ApiController
|
||||||
pass = params[:pass]
|
pass = params[:pass]
|
||||||
duration = params[:duration] # seconds connected to the listen stream
|
duration = params[:duration] # seconds connected to the listen stream
|
||||||
|
|
||||||
mount = IcecastMount.find(@mount_id)
|
mount = IcecastMount.find_by_name!(@mount_id)
|
||||||
mount.listener_remove
|
mount.listener_remove
|
||||||
|
|
||||||
render text: '', :status => :ok
|
render text: '', :status => :ok
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,11 @@ class ApiInvitationsController < ApiController
|
||||||
if current_user.id != sender_id
|
if current_user.id != sender_id
|
||||||
raise PermissionError, "You can only ask for your own sent invitations"
|
raise PermissionError, "You can only ask for your own sent invitations"
|
||||||
end
|
end
|
||||||
|
if session_id = params[:session_id]
|
||||||
@invitations = Invitation.where(:sender_id => current_user.id)
|
@invitations = Invitation.where(:sender_id => sender_id, :music_session_id => session_id)
|
||||||
|
else
|
||||||
|
@invitations = Invitation.where(:sender_id => current_user.id)
|
||||||
|
end
|
||||||
elsif !receiver_id.nil?
|
elsif !receiver_id.nil?
|
||||||
if current_user.id != receiver_id
|
if current_user.id != receiver_id
|
||||||
raise PermissionError, "You can only ask for your own received invitations"
|
raise PermissionError, "You can only ask for your own received invitations"
|
||||||
|
|
@ -34,25 +37,33 @@ class ApiInvitationsController < ApiController
|
||||||
sender = current_user
|
sender = current_user
|
||||||
join_request = JoinRequest.find(params[:join_request]) unless params[:join_request].nil?
|
join_request = JoinRequest.find(params[:join_request]) unless params[:join_request].nil?
|
||||||
|
|
||||||
@invitation = Invitation.new
|
@invitation = Invitation.limit(1)
|
||||||
@invitation.music_session = music_session
|
.where(:receiver_id => params[:receiver],
|
||||||
@invitation.sender = sender
|
:sender_id => current_user.id,
|
||||||
@invitation.receiver = receiver
|
:music_session_id => params[:music_session])
|
||||||
@invitation.join_request = join_request
|
.first
|
||||||
|
if @invitation
|
||||||
@invitation.save
|
|
||||||
|
|
||||||
unless @invitation.errors.any?
|
|
||||||
User.save_session_settings(current_user, music_session)
|
|
||||||
|
|
||||||
# send notification
|
|
||||||
Notification.send_session_invitation(receiver, current_user, music_session.id)
|
|
||||||
respond_with @invitation, :responder => ApiResponder, :location => api_invitation_detail_url(@invitation)
|
respond_with @invitation, :responder => ApiResponder, :location => api_invitation_detail_url(@invitation)
|
||||||
|
|
||||||
else
|
else
|
||||||
# we have to do this because api_invitation_detail_url will fail with a bad @invitation
|
@invitation = Invitation.new
|
||||||
response.status = :unprocessable_entity
|
@invitation.music_session = music_session
|
||||||
respond_with @invitation
|
@invitation.sender = sender
|
||||||
|
@invitation.receiver = receiver
|
||||||
|
@invitation.join_request = join_request
|
||||||
|
@invitation.save
|
||||||
|
|
||||||
|
unless @invitation.errors.any?
|
||||||
|
User.save_session_settings(current_user, music_session)
|
||||||
|
|
||||||
|
# send notification
|
||||||
|
Notification.send_session_invitation(receiver, current_user, music_session.id)
|
||||||
|
respond_with @invitation, :responder => ApiResponder, :location => api_invitation_detail_url(@invitation)
|
||||||
|
|
||||||
|
else
|
||||||
|
# we have to do this because api_invitation_detail_url will fail with a bad @invitation
|
||||||
|
response.status = :unprocessable_entity
|
||||||
|
respond_with @invitation
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,10 @@ if @search.musicians_filter_search?
|
||||||
@search.is_follower?(musician)
|
@search.is_follower?(musician)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
node :biography do |musician|
|
||||||
|
musician.biography.nil? ? "" : musician.biography
|
||||||
|
end
|
||||||
|
|
||||||
child :musician_instruments => :instruments do
|
child :musician_instruments => :instruments do
|
||||||
attributes :instrument_id, :description, :proficiency_level, :priority
|
attributes :instrument_id, :description, :proficiency_level, :priority
|
||||||
end
|
end
|
||||||
|
|
@ -73,6 +77,10 @@ if @search.bands_filter_search?
|
||||||
@search.is_follower?(band)
|
@search.is_follower?(band)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
node :biography do |band|
|
||||||
|
band.biography.nil? ? "" : band.biography
|
||||||
|
end
|
||||||
|
|
||||||
child :genres => :genres do
|
child :genres => :genres do
|
||||||
attributes :genre_id, :description
|
attributes :genre_id, :description
|
||||||
end
|
end
|
||||||
|
|
@ -81,7 +89,7 @@ if @search.bands_filter_search?
|
||||||
node :user_id do |uu| uu.id end
|
node :user_id do |uu| uu.id end
|
||||||
node :photo_url do |uu| uu.photo_url end
|
node :photo_url do |uu| uu.photo_url end
|
||||||
node :name do |uu| uu.name end
|
node :name do |uu| uu.name end
|
||||||
node :instruments do |uu| uu.instruments.map(&:id).join(',') end
|
node :instruments do |uu| uu.instruments.map(&:id).join(',') end
|
||||||
end
|
end
|
||||||
|
|
||||||
node :follow_count do |band| @search.follow_count(band) end
|
node :follow_count do |band| @search.follow_count(band) end
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,8 @@ node :genres do |band|
|
||||||
attributes :id, :description
|
attributes :id, :description
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
node :biography do |band|
|
||||||
|
band.biography.nil? ? "" : band.biography
|
||||||
|
end
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<!-- Session Row Template -->
|
<!-- Session Row Template -->
|
||||||
<script type="text/template" id="template-find-band-row">
|
<script type="text/template" id="template-find-band-row">
|
||||||
<div class="profile-band-list-result band-list-result">
|
<div class="profile-band-list-result band-list-result">
|
||||||
<div style="float:left">
|
<div class="left">
|
||||||
<!-- avatar -->
|
<!-- avatar -->
|
||||||
<div class="avatar-small"><img src="{avatar_url}" /></div>
|
<div class="avatar-small"><img src="{avatar_url}" /></div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,22 +84,7 @@
|
||||||
<h2>invite musicians</h2>
|
<h2>invite musicians</h2>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<div>
|
<div id="create-session-invite-musicians"></div>
|
||||||
<div class="right" layout-link="select-friends">
|
|
||||||
<a href="#" id="btn-choose-friends" class="button-grey">CHOOSE FRIENDS</a>
|
|
||||||
</div>
|
|
||||||
<div style="margin-right:140px;">
|
|
||||||
Start typing friends' names or:
|
|
||||||
</div>
|
|
||||||
<div class="clearall"></div>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<!-- friend invitation box -->
|
|
||||||
<div class="friendbox">
|
|
||||||
<div id="selected-friends"></div>
|
|
||||||
<input id="friend-input" type="text" placeholder="Looking up friends..." />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt35 mb15">
|
<div class="mt35 mb15">
|
||||||
Invite friends and contacts to join you on JamKazam from:
|
Invite friends and contacts to join you on JamKazam from:
|
||||||
|
|
@ -171,13 +156,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Added Invitation Template -->
|
|
||||||
<script type="text/template" id="template-added-invitation">
|
|
||||||
<div user-id="{userId}" class="invitation">{userName}
|
|
||||||
<a><%= image_tag "shared/icon_delete_sm.png", :size => "13x13" %></a>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Band option template -->
|
<!-- Band option template -->
|
||||||
<script type="text/template" id="template-band-option">
|
<script type="text/template" id="template-band-option">
|
||||||
<option value="{value}">{label}</option>
|
<option value="{value}">{label}</option>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,49 @@
|
||||||
<!-- Feed Screen -->
|
<!-- Feed Screen -->
|
||||||
<div layout="screen" layout-id="feed" class="screen secondary">
|
<%= content_tag(:div, :layout => 'screen', 'layout-id' => 'feed', :class => "screen secondary") do -%>
|
||||||
<div class="content">
|
<%= content_tag(:div, :class => :content) do -%>
|
||||||
<div class="content-head">
|
<%= content_tag(:div, :class => 'content-head') do -%>
|
||||||
|
<%= content_tag(:div, image_tag("content/icon_feed.png", {:height => 19, :width => 19}), :class => 'content-icon') %>
|
||||||
<div class="content-icon">
|
<%= content_tag(:h1, 'feed') %>
|
||||||
<%= image_tag "content/icon_feed.png", {:height => 19, :width => 19} %>
|
|
||||||
</div>
|
|
||||||
<h1>feed</h1>
|
|
||||||
<%= render "screen_navigation" %>
|
<%= render "screen_navigation" %>
|
||||||
</div>
|
<% end -%>
|
||||||
<div class="content-body">
|
<%= content_tag(:div, :class => 'content-body') do -%>
|
||||||
<div class="content-body-scroller">
|
<%= form_tag('', {:id => 'find-session-form', :class => 'inner-content'}) do -%>
|
||||||
<p>This feature not yet implemented</p>
|
<%= render(:partial => "web_filter", :locals => {:search_type => Search::PARAM_FEED}) %>
|
||||||
</div>
|
<%= content_tag(:div, :class => 'filter-body') do %>
|
||||||
</div>
|
<%= content_tag(:div, :class => 'content-body-scroller') do -%>
|
||||||
|
<p>This feature not yet implemented</p>
|
||||||
|
<%= content_tag(:div, content_tag(:div, '', :id => 'session-filter-results', :class => 'filter-results'), :class => 'content-wrapper') %>
|
||||||
|
<% end -%>
|
||||||
|
<% end -%>
|
||||||
|
<% end -%>
|
||||||
|
<% end -%>
|
||||||
|
<% end -%>
|
||||||
|
<% end -%>
|
||||||
|
|
||||||
|
<!-- Session Row Template -->
|
||||||
|
<script type="text/template" id="template-find-session-row">
|
||||||
|
<div class="profile-band-list-result band-list-result">
|
||||||
|
<div class="left">
|
||||||
|
<!-- avatar -->
|
||||||
|
<div class="avatar-small"><img src="{avatar_url}" /></div>
|
||||||
|
|
||||||
|
<!-- name & location -->
|
||||||
|
<div style="width:220px;" class="result-name">{band_name}<br />
|
||||||
|
<span class="result-location">{band_location}</span>
|
||||||
|
<br /><br />
|
||||||
|
<div id="result_sessions" class="nowrap">{genres}</div>
|
||||||
|
<br /><br />
|
||||||
|
{follow_count} <img src="../assets/content/icon_followers.png" width="22" height="12" align="absmiddle" /> {recording_count} <img src="../assets/content/icon_recordings.png" width="12" height="13" align="absmiddle" /> {session_count} <img src="../assets/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" /><br /><br />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="left ml20 f11 whitespace w35"><br />
|
||||||
|
{biography}<br />
|
||||||
|
<br />
|
||||||
|
<div data-band-id={band_id}>{band_action_template}</div>
|
||||||
|
</div>
|
||||||
|
<div class="left ml10 w25 band-players">
|
||||||
|
<table class="musicians" cellpadding="0" cellspacing="5">{band_player_template}</table>
|
||||||
|
</div>
|
||||||
|
<br clear="all" />
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
<!-- Session Update Invite Musicians Dialog -->
|
||||||
|
<div class="dialog invitemusicians-overlay" layout="dialog" layout-id="select-invites">
|
||||||
|
<div class="invitemusicians-inner" id="update-session-invite-musicians">
|
||||||
|
</div>
|
||||||
|
<br clear="all" />
|
||||||
|
<div class="left">
|
||||||
|
<a id="btn-cancel-invites" layout-action="close" class="button-grey">CANCEL</a>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<a id="btn-save-invites" layout-action="close" class="button-orange">INVITE</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- invite musician friend selector template -->
|
||||||
|
<script type="text/template" id="template-session-invite-musicians">
|
||||||
|
<div>
|
||||||
|
<div class="right" layout-link="select-friends">
|
||||||
|
<a href="#" class="btn-choose-friends button-grey" id="{choose_friends_id}">CHOOSE FRIENDS</a>
|
||||||
|
</div>
|
||||||
|
<div style="margin-right:140px;">
|
||||||
|
Start typing friends' names or:
|
||||||
|
</div>
|
||||||
|
<div class="clearall"></div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<!-- friend invitation box -->
|
||||||
|
<div class="friendbox">
|
||||||
|
<div class="selected-friends" id="{selected_friends_id}"></div>
|
||||||
|
<!--<input id="friend-input" type="text" placeholder="Looking up friends..." />-->
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Added Invitation Template -->
|
||||||
|
<script type="text/template" id="template-added-invitation">
|
||||||
|
<div user-id="{userId}" class="invitation">{userName}
|
||||||
|
<a><%= image_tag "shared/icon_delete_sm.png", :size => "13x13", :style => "{imageStyle}" %></a>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
@ -22,24 +22,35 @@
|
||||||
<!-- Session Row Template -->
|
<!-- Session Row Template -->
|
||||||
<script type="text/template" id="template-find-musician-row">
|
<script type="text/template" id="template-find-musician-row">
|
||||||
<div class="profile-band-list-result musician-list-result">
|
<div class="profile-band-list-result musician-list-result">
|
||||||
<div class="left">
|
<div class="left" data-hint="container">
|
||||||
<!-- avatar -->
|
<div class="left">
|
||||||
<div class="avatar-small"><img src="{avatar_url}" /></div>
|
<!-- avatar -->
|
||||||
|
<div class="avatar-small"><img src="{avatar_url}" /></div>
|
||||||
<!-- name & location -->
|
|
||||||
<div style="width:220px;" class="result-name">{musician_name}<br />
|
|
||||||
<span class="result-location">{musician_location}
|
|
||||||
<br /><br />
|
|
||||||
<div id="result_instruments" class="nowrap">{instruments}</div>
|
|
||||||
<br clear="all" /><br />
|
|
||||||
{friend_count} <img src="../assets/content/icon_friend.png" width="14" height="12" align="absmiddle" /> {follow_count} <img src="../assets/content/icon_followers.png" width="22" height="12" align="absmiddle" /> {recording_count} <img src="../assets/content/icon_recordings.png" width="12" height="13" align="absmiddle" /> {session_count} <img src="../assets/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" /></span><br /><br />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="left">
|
||||||
<div class="left ml35 f11 whitespace w40"><br />
|
<div data-hint="top-row">
|
||||||
{biography}<br />
|
<div class="left">
|
||||||
<br />
|
<!-- name & location -->
|
||||||
<div class="result-list-button-wrapper" data-musician-id={musician_id}>
|
<div style="" class="result-name">{musician_name}</div>
|
||||||
{musician_action_template}
|
<div class="result-location">{musician_location}</div>
|
||||||
|
<div id="result_instruments" class="nowrap">{instruments}</div>
|
||||||
|
</div>
|
||||||
|
<div class="left ml35 f11 whitespace w40">
|
||||||
|
<div class="biography">{biography}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div data-hint="button-row">
|
||||||
|
<div class="stats">
|
||||||
|
{friend_count} <img src="../assets/content/icon_friend.png" width="14" height="12" align="absmiddle" />
|
||||||
|
{follow_count} <img src="../assets/content/icon_followers.png" width="22" height="12" align="absmiddle" />
|
||||||
|
{recording_count} <img src="../assets/content/icon_recordings.png" width="12" height="13" align="absmiddle" />
|
||||||
|
{session_count} <img src="../assets/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="result-list-button-wrapper" data-musician-id={musician_id}>
|
||||||
|
{musician_action_template}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="left ml10 w20 musician-following">
|
<div class="left ml10 w20 musician-following">
|
||||||
|
|
@ -54,9 +65,9 @@
|
||||||
<script type="text/template" id="template-musician-action-btns">
|
<script type="text/template" id="template-musician-action-btns">
|
||||||
<a href="{profile_url}" class="button-orange smallbutton">PROFILE</a>
|
<a href="{profile_url}" class="button-orange smallbutton">PROFILE</a>
|
||||||
<% if current_user.musician? %>
|
<% if current_user.musician? %>
|
||||||
<a href="#" class="{button_friend} smallbutton search-m-friend">CONNECT</a>
|
<a href="#" class="{friend_class} smallbutton search-m-friend">{friend_caption}</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
<a href="#" class="{button_follow} smallbutton search-m-follow">FOLLOW</a>
|
<a href="#" class="{follow_class} smallbutton search-m-follow">{follow_caption}</a>
|
||||||
<!--<a href="#" class="{button_message} smallbutton search-m-like">MESSAGE</a>-->
|
<!--<a href="#" class="{button_message} smallbutton search-m-like">MESSAGE</a>-->
|
||||||
<div class="clearall"></div>
|
<div class="clearall"></div>
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@
|
||||||
<!-- live tracks -->
|
<!-- live tracks -->
|
||||||
<div class="session-livetracks">
|
<div class="session-livetracks">
|
||||||
<h2>live tracks</h2>
|
<h2>live tracks</h2>
|
||||||
<div class="session-add">
|
<div class="session-add" layout-link="select-invites">
|
||||||
<a>
|
<a href="#" id="session-invite-musicians">
|
||||||
<%= image_tag "content/icon_add.png", {:width => 19, :height => 19, :align => "texttop"} %> Invite Musicians
|
<%= image_tag "content/icon_add.png", {:width => 19, :height => 19, :align => "texttop"} %> Invite Musicians
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -178,3 +178,4 @@
|
||||||
<script type="text/template" id="template-genre-option">
|
<script type="text/template" id="template-genre-option">
|
||||||
<option value="{value}">{label}</option>
|
<option value="{value}">{label}</option>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
<input type="button" value="Get"/>
|
<input type="button" value="Get"/>
|
||||||
<textarea data-what="GetFTUE"></textarea>
|
<textarea data-what="GetFTUE"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div style="float:left;"
|
<div style="float:left;">
|
||||||
<h2>SetFTUE</h2>
|
<h2>SetFTUE</h2>
|
||||||
<form>
|
<form>
|
||||||
<label>true <input name="ftue" type="radio" value="true"/></label>
|
<label>true <input name="ftue" type="radio" value="true"/></label>
|
||||||
|
|
|
||||||
|
|
@ -4,36 +4,61 @@
|
||||||
filter_label = :band
|
filter_label = :band
|
||||||
when Search::PARAM_MUSICIAN
|
when Search::PARAM_MUSICIAN
|
||||||
filter_label = :musician
|
filter_label = :musician
|
||||||
|
when Search::PARAM_FEED
|
||||||
|
filter_label = :feed
|
||||||
end %>
|
end %>
|
||||||
<%= content_tag(:div, :id => defined?(id) ? id : 'session-controls', :class => "#{filter_label}-filter filter-head") do %>
|
<%= content_tag(:div, :id => defined?(id) ? id : 'session-controls', :class => "#{filter_label}-filter filter-head") do %>
|
||||||
<%= content_tag(:div, :class => "filter-element wrapper") do -%>
|
<%= content_tag(:div, :class => "filter-element wrapper") do -%>
|
||||||
<%= content_tag(:div, 'Filter By:', :class => 'filter-element desc') %>
|
<% if :feed == filter_label %>
|
||||||
<!-- order by filter -->
|
<!-- @begin sort filter -->
|
||||||
<%= select_tag("#{filter_label}_order_by", options_for_select(Search::ORDERINGS), {:class => "#{filter_label}-order-by"} ) %>
|
<%= content_tag(:div, 'Sort Feed by:', :class => 'filter-element desc') %>
|
||||||
|
<%= select_tag("#{filter_label}_order_by", options_for_select(Search::F_SORT_OPTS), {:class => "#{filter_label}-order-by"} ) %>
|
||||||
|
<!-- @end sort filter -->
|
||||||
|
<% else %>
|
||||||
|
<!-- @begin order by filter -->
|
||||||
|
<%= content_tag(:div, 'Filter By:', :class => 'filter-element desc') %>
|
||||||
|
<%= select_tag("#{filter_label}_order_by", options_for_select(Search::ORDERINGS), {:class => "#{filter_label}-order-by"} ) %>
|
||||||
|
<!-- @end order by filter -->
|
||||||
|
<% end %>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
<%= content_tag(:div, :class => 'filter-element wrapper') do -%>
|
<%= content_tag(:div, :class => 'filter-element wrapper') do -%>
|
||||||
<% if :musician == filter_label %>
|
<% if :musician == filter_label %>
|
||||||
<!-- instrument filter -->
|
<!-- @begin instrument filter -->
|
||||||
<%= content_tag(:div, 'Instrument:', :class => 'filter-element desc') %>
|
<%= content_tag(:div, 'Instrument:', :class => 'filter-element desc') %>
|
||||||
<%= select_tag("#{filter_label}_instrument",
|
<%= select_tag("#{filter_label}_instrument",
|
||||||
options_for_select([['Any', '']].concat(JamRuby::Instrument.all.collect { |ii| [ii.description, ii.id] }))) %>
|
options_for_select([['Any', '']].concat(JamRuby::Instrument.all.collect { |ii| [ii.description, ii.id] }))) %>
|
||||||
|
<!-- @end instrument filter -->
|
||||||
<% elsif :band == filter_label %>
|
<% elsif :band == filter_label %>
|
||||||
<!-- genre filter -->
|
<!-- @begin genre filter -->
|
||||||
<%= content_tag(:div, 'Genre:', :class => 'filter-element desc') %>
|
<%= content_tag(:div, 'Genre:', :class => 'filter-element desc') %>
|
||||||
<%= select_tag("#{filter_label}_genre",
|
<%= select_tag("#{filter_label}_genre",
|
||||||
options_for_select([['Any', '']].concat(JamRuby::Genre.all.collect { |ii| [ii.description, ii.id] }))) %>
|
options_for_select([['Any', '']].concat(JamRuby::Genre.all.collect { |ii| [ii.description, ii.id] }))) %>
|
||||||
|
<!-- @end genre filter -->
|
||||||
|
<% elsif :feed == filter_label %>
|
||||||
|
<!-- @begin date filter -->
|
||||||
|
<%= content_tag(:div, 'Include Dates:', :class => 'filter-element desc') %>
|
||||||
|
<%= select_tag("#{filter_label}_date", options_for_select(Search::DATE_OPTS)) %>
|
||||||
|
<!-- @end date filter -->
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
<!-- distance filter -->
|
|
||||||
<%= content_tag(:div, :class => 'filter-element wrapper') do -%>
|
<%= content_tag(:div, :class => 'filter-element wrapper') do -%>
|
||||||
<%= content_tag(:div, 'Within', :class => 'filter-element desc') %>
|
<% if :feed == filter_label %>
|
||||||
<%= content_tag(:div, :class => 'query-distance-params') do -%>
|
<!-- @begin show filter -->
|
||||||
<% default_distance = :musician == filter_label ? Search::M_MILES_DEFAULT : Search::B_MILES_DEFAULT %>
|
<%= content_tag(:div, 'Show:', :class => 'filter-element desc') %>
|
||||||
<%= select_tag("#{filter_label}_query_distance", options_for_select(Search::DISTANCE_OPTS, default_distance)) %>
|
<%= select_tag("#{filter_label}_show", options_for_select(Search::SHOW_OPTS)) %>
|
||||||
<% end -%>
|
<!-- @end show filter -->
|
||||||
<%= content_tag(:div, :class => 'filter-element desc') do -%>
|
<% else %>
|
||||||
miles of <%= content_tag(:span, current_user.current_city(request.remote_ip), :id => "#{filter_label}-filter-city") %>
|
<!-- @begin distance filter -->
|
||||||
<% end -%>
|
<%= content_tag(:div, 'Within', :class => 'filter-element desc') %>
|
||||||
|
<%= content_tag(:div, :class => 'query-distance-params') do -%>
|
||||||
|
<% default_distance = :musician == filter_label ? Search::M_MILES_DEFAULT : Search::B_MILES_DEFAULT %>
|
||||||
|
<%= select_tag("#{filter_label}_query_distance", options_for_select(Search::DISTANCE_OPTS, default_distance)) %>
|
||||||
|
<% end -%>
|
||||||
|
<%= content_tag(:div, :class => 'filter-element desc') do -%>
|
||||||
|
miles of <%= content_tag(:span, current_user.current_city(request.remote_ip), :id => "#{filter_label}-filter-city") %>
|
||||||
|
<% end -%>
|
||||||
|
<!-- @end distance filter -->
|
||||||
|
<% end %>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
<% end -%>
|
<% end -%>
|
||||||
<!-- @end web_filter -->
|
<!-- @end web_filter -->
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
<%= render "account_profile_avatar" %>
|
<%= render "account_profile_avatar" %>
|
||||||
<%= render "account_audio_profile" %>
|
<%= render "account_audio_profile" %>
|
||||||
<%= render "invitationDialog" %>
|
<%= render "invitationDialog" %>
|
||||||
|
<%= render "inviteMusicians" %>
|
||||||
<%= render "whatsNextDialog" %>
|
<%= render "whatsNextDialog" %>
|
||||||
<%= render "recordingFinishedDialog" %>
|
<%= render "recordingFinishedDialog" %>
|
||||||
<%= render "localRecordingsDialog" %>
|
<%= render "localRecordingsDialog" %>
|
||||||
|
|
@ -105,6 +106,9 @@
|
||||||
var friendSelectorDialog = new JK.FriendSelectorDialog(JK.app);
|
var friendSelectorDialog = new JK.FriendSelectorDialog(JK.app);
|
||||||
friendSelectorDialog.initialize();
|
friendSelectorDialog.initialize();
|
||||||
|
|
||||||
|
var inviteMusiciansUtil = new JK.InviteMusiciansUtil(JK.app);
|
||||||
|
inviteMusiciansUtil.initialize(friendSelectorDialog);
|
||||||
|
|
||||||
var userDropdown = new JK.UserDropdown(JK.app);
|
var userDropdown = new JK.UserDropdown(JK.app);
|
||||||
JK.UserDropdown = userDropdown;
|
JK.UserDropdown = userDropdown;
|
||||||
userDropdown.initialize(invitationDialog);
|
userDropdown.initialize(invitationDialog);
|
||||||
|
|
@ -148,7 +152,7 @@
|
||||||
JK.Banner.initialize();
|
JK.Banner.initialize();
|
||||||
|
|
||||||
var createSessionScreen = new JK.CreateSessionScreen(JK.app);
|
var createSessionScreen = new JK.CreateSessionScreen(JK.app);
|
||||||
createSessionScreen.initialize(invitationDialog, friendSelectorDialog);
|
createSessionScreen.initialize(invitationDialog, inviteMusiciansUtil);
|
||||||
|
|
||||||
var bandSetupScreen = new JK.BandSetupScreen(JK.app);
|
var bandSetupScreen = new JK.BandSetupScreen(JK.app);
|
||||||
bandSetupScreen.initialize(invitationDialog, friendSelectorDialog);
|
bandSetupScreen.initialize(invitationDialog, friendSelectorDialog);
|
||||||
|
|
@ -170,7 +174,7 @@
|
||||||
findBandScreen.initialize();
|
findBandScreen.initialize();
|
||||||
|
|
||||||
var sessionScreen = new JK.SessionScreen(JK.app);
|
var sessionScreen = new JK.SessionScreen(JK.app);
|
||||||
sessionScreen.initialize(localRecordingsDialog, recordingFinishedDialog);
|
sessionScreen.initialize(localRecordingsDialog, recordingFinishedDialog, inviteMusiciansUtil);
|
||||||
|
|
||||||
var sessionSettingsDialog = new JK.SessionSettingsDialog(JK.app, sessionScreen);
|
var sessionSettingsDialog = new JK.SessionSettingsDialog(JK.app, sessionScreen);
|
||||||
sessionSettingsDialog.initialize();
|
sessionSettingsDialog.initialize();
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
<input type="text" /> <br clear="all" /><br />
|
<input type="text" /> <br clear="all" /><br />
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<%= f.submit "CREATE ACCOUNT", class: "right button-orange" %><br/ style="clear:both;"><br/>
|
<%= f.submit "CREATE ACCOUNT", class: "right button-orange" %><br style="clear:both;"/><br/>
|
||||||
<a href="/auth/facebook" class="right"><img src="/fb-signup-button.png"></a>
|
<a href="/auth/facebook" class="right"><img src="/fb-signup-button.png"></a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -171,12 +171,13 @@ include JamRuby
|
||||||
config.audiomixer_path = "/var/lib/audiomixer/audiomixer/audiomixerapp"
|
config.audiomixer_path = "/var/lib/audiomixer/audiomixer/audiomixerapp"
|
||||||
|
|
||||||
# if it looks like linux, use init.d script; otherwise use kill
|
# if it looks like linux, use init.d script; otherwise use kill
|
||||||
config.icecast_reload_cmd = ENV['ICECAST_RELOAD_CMD'] || (File.exist?('/usr/bin/icecast2') ? '/etc/init.d/icecast2 reload' : "bash -l -c #{Shellwords.escape("kill -1 `ps -f | grep /usr/local/bin/icecast | grep -v grep | awk \'{print $2}\'`")}")
|
config.icecast_reload_cmd = ENV['ICECAST_RELOAD_CMD'] || (File.exist?('/usr/local/bin/icecast2') ? "bash -l -c #{Shellwords.escape("sudo /etc/init.d/icecast2 reload")}" : "bash -l -c #{Shellwords.escape("kill -1 `ps -f | grep /usr/local/bin/icecast | grep -v grep | awk \'{print $2}\'`")}")
|
||||||
# if it looks like linux, use that path; otherwise use the brew default path
|
# if it looks like linux, use that path; otherwise use the brew default path
|
||||||
config.icecast_config_file = ENV['ICECAST_CONFIG_FILE'] || (File.exist?('/etc/icecast2/icecast.xml') ? '/etc/icecast2/icecast.xml' : '/usr/local/etc/icecast.xml')
|
config.icecast_config_file = ENV['ICECAST_CONFIG_FILE'] || (File.exist?('/etc/icecast2/icecast.xml') ? '/etc/icecast2/icecast.xml' : '/usr/local/etc/icecast.xml')
|
||||||
# this will be the qualifier on the IcecastConfigWorker queue name
|
# this will be the qualifier on the IcecastConfigWorker queue name
|
||||||
config.icecast_server_id = ENV['ICECAST_SERVER_ID'] || 'localhost'
|
config.icecast_server_id = ENV['ICECAST_SERVER_ID'] || 'localhost'
|
||||||
config.icecast_max_missing_check = 2 * 60 # 2 minutes
|
config.icecast_max_missing_check = 2 * 60 # 2 minutes
|
||||||
|
config.icecast_max_sourced_changed = 15 # 15 seconds
|
||||||
|
|
||||||
config.email_alerts_alias = 'nobody@jamkazam.com' # should be used for 'oh no' server down/service down sorts of emails
|
config.email_alerts_alias = 'nobody@jamkazam.com' # should be used for 'oh no' server down/service down sorts of emails
|
||||||
config.email_generic_from = 'nobody@jamkazam.com'
|
config.email_generic_from = 'nobody@jamkazam.com'
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,10 @@ IcecastConfigRetry:
|
||||||
cron: 0 * * * *
|
cron: 0 * * * *
|
||||||
class: "JamRuby::IcecastConfigRetry"
|
class: "JamRuby::IcecastConfigRetry"
|
||||||
description: "Finds icecast servers that have had their config_changed, but no IcecastConfigWriter check recently"
|
description: "Finds icecast servers that have had their config_changed, but no IcecastConfigWriter check recently"
|
||||||
|
|
||||||
|
|
||||||
|
IcecastSourceCheck:
|
||||||
|
cron: "10 * * * * *"
|
||||||
|
class: "JamRuby::IcecastSourceCheck"
|
||||||
|
description: "Finds icecast mounts that need their 'sourced' state to change, but haven't in some time"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ MusicSessionManager < BaseManager
|
||||||
|
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
# check if we are connected to rabbitmq
|
# check if we are connected to rabbitmq
|
||||||
music_session = MusicSession.new()
|
music_session = MusicSession.new
|
||||||
|
music_session.id = SecureRandom.uuid
|
||||||
music_session.creator = user
|
music_session.creator = user
|
||||||
music_session.description = description
|
music_session.description = description
|
||||||
music_session.musician_access = musician_access
|
music_session.musician_access = musician_access
|
||||||
|
|
@ -24,9 +25,6 @@ MusicSessionManager < BaseManager
|
||||||
music_session.band = band
|
music_session.band = band
|
||||||
music_session.legal_terms = legal_terms
|
music_session.legal_terms = legal_terms
|
||||||
|
|
||||||
#genres = genres
|
|
||||||
@log.debug "Genres class: " + genres.class.to_s
|
|
||||||
|
|
||||||
unless genres.nil?
|
unless genres.nil?
|
||||||
genres.each do |genre_id|
|
genres.each do |genre_id|
|
||||||
loaded_genre = Genre.find(genre_id)
|
loaded_genre = Genre.find(genre_id)
|
||||||
|
|
@ -34,6 +32,13 @@ MusicSessionManager < BaseManager
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if fan_access
|
||||||
|
# create an icecast mount since regular users can listen in to the broadcast
|
||||||
|
music_session.mount = IcecastMount.build_session_mount(music_session)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
music_session.save
|
music_session.save
|
||||||
|
|
||||||
unless music_session.errors.any?
|
unless music_session.errors.any?
|
||||||
|
|
|
||||||
|
|
@ -53,13 +53,13 @@ describe "Musician Search API", :type => :api do
|
||||||
it "gets musicians for default instrument" do
|
it "gets musicians for default instrument" do
|
||||||
get_query({:instrument => 'electric guitar'})
|
get_query({:instrument => 'electric guitar'})
|
||||||
good_response
|
good_response
|
||||||
expect(json.count).to be >= 1
|
expect(json['musicians'].count).to be >= 1
|
||||||
end
|
end
|
||||||
|
|
||||||
it "gets no musicians for unused instruments" do
|
it "gets no musicians for unused instruments" do
|
||||||
get_query({:instrument => 'tuba'})
|
get_query({:instrument => 'tuba'})
|
||||||
good_response
|
good_response
|
||||||
expect(json.count).to eq(0)
|
expect(json['musicians'].count).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ gem 'postgres_ext'
|
||||||
gem 'resque'
|
gem 'resque'
|
||||||
gem 'resque-retry'
|
gem 'resque-retry'
|
||||||
gem 'resque-failed-job-mailer'
|
gem 'resque-failed-job-mailer'
|
||||||
|
gem 'resque-lonely_job', '~> 1.0.0'
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem 'pry'
|
gem 'pry'
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,8 @@ include Jampb
|
||||||
end
|
end
|
||||||
|
|
||||||
config.before(:suite) do
|
config.before(:suite) do
|
||||||
DatabaseCleaner.strategy = :truncation, {:except => %w[instruments genres] }
|
DatabaseCleaner.strategy = :truncation, {:except => %w[instruments genres icecast_server_groups] }
|
||||||
DatabaseCleaner.clean_with(:truncation, {:except => %w[instruments genres] })
|
DatabaseCleaner.clean_with(:truncation, {:except => %w[instruments genres icecast_server_groups] })
|
||||||
end
|
end
|
||||||
|
|
||||||
#config.after(:each) do
|
#config.after(:each) do
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue