* icecast working locally on my mac (VRFS-1002)
This commit is contained in:
parent
c91940852b
commit
fcec0a776b
|
|
@ -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
|
||||||
|
|
@ -81,23 +83,85 @@ 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."
|
||||||
redirect_to admin_bootstrap_path, :notice => "Brew template created. Create a server now with that template specified."
|
|
||||||
end
|
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}"
|
||||||
|
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
|
action_item do
|
||||||
link_to "Create Brew Template", admin_bootstrap_brew_template_path, :method => :post
|
link_to "Create MacOSX (Brew) Template", admin_bootstrap_brew_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
|
||||||
|
|
|
||||||
|
|
@ -89,3 +89,5 @@ 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
|
mixes_drop_manifest_add_retry.sql
|
||||||
|
music_sessions_unlogged.sql
|
||||||
|
integrate_icecast_into_sessions.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;
|
||||||
|
|
||||||
|
|
@ -55,7 +55,9 @@ message ClientMessage {
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
||||||
|
|
@ -126,7 +128,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;
|
||||||
|
|
@ -381,15 +385,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)
|
||||||
|
|
|
||||||
|
|
@ -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,6 +12,7 @@ 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}
|
||||||
|
|
||||||
|
before_destroy :poke_config
|
||||||
after_save :poke_config
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ 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
|
||||||
|
|
||||||
|
before_destroy :poke_config
|
||||||
after_save :poke_config
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ 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}
|
||||||
|
|
||||||
|
before_destroy :poke_config
|
||||||
after_save :poke_config
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ 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]}
|
||||||
|
|
||||||
|
before_destroy :poke_config
|
||||||
after_save :poke_config
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ 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?}
|
||||||
|
|
||||||
|
before_destroy :poke_config
|
||||||
after_save :poke_config
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ 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]}
|
||||||
|
|
||||||
|
before_destroy :poke_config
|
||||||
after_save :poke_config
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
|
|
|
||||||
|
|
@ -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]}
|
||||||
|
validates :server, presence: true
|
||||||
validate :name_has_correct_format
|
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
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
# this is completely unsafe without that 'with_lock' statement above
|
||||||
|
self.listeners = self.listeners + 1
|
||||||
|
|
||||||
|
save(validate: false)
|
||||||
|
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
|
||||||
|
|
@ -13,6 +13,7 @@ module JamRuby
|
||||||
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,6 +13,7 @@ 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]}
|
||||||
|
|
||||||
|
before_destroy :poke_config
|
||||||
after_save :poke_config
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ module JamRuby
|
||||||
|
|
||||||
validates :chroot, :inclusion => {:in => [0, 1]}
|
validates :chroot, :inclusion => {:in => [0, 1]}
|
||||||
|
|
||||||
|
before_destroy :poke_config
|
||||||
after_save :poke_config
|
after_save :poke_config
|
||||||
|
|
||||||
def poke_config
|
def poke_config
|
||||||
|
|
|
||||||
|
|
@ -3,28 +3,30 @@ 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, 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 +34,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 +78,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)
|
||||||
|
|
@ -85,28 +128,20 @@ module JamRuby
|
||||||
|
|
||||||
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! 'admin', (admin_email.nil? || admin_email.empty?) ? template.admin_email : admin_email
|
root.tag! 'location', resolve_string(:location) if string_present?(:location)
|
||||||
root.tag! 'fileserve', fileserve.nil? ? template.fileserve : fileserve
|
root.tag! 'admin', resolve_string(:admin_email) if string_present?(:admin_email)
|
||||||
|
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 +155,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
|
||||||
|
|
@ -34,7 +34,7 @@ module JamRuby
|
||||||
|
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -207,11 +207,22 @@ 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
|
||||||
port 8000
|
port 8000
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,7 @@ include JamRuby
|
||||||
# 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?
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue