* 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-retry'
|
||||
gem 'resque-failed-job-mailer'
|
||||
gem 'resque-lonely_job', '~> 1.0.0'
|
||||
|
||||
gem 'eventmachine', '1.0.3'
|
||||
gem 'amqp', '0.9.8'
|
||||
|
|
|
|||
|
|
@ -3,16 +3,18 @@ ActiveAdmin.register_page "Bootstrap" 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]
|
||||
|
||||
server = IcecastServer.new
|
||||
server.template = template
|
||||
server.mount_template = mount_template
|
||||
server.hostname = hostname
|
||||
server.server_id = hostname
|
||||
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
|
||||
|
||||
page_action :brew_template, :method => :post do
|
||||
|
|
@ -81,23 +83,85 @@ ActiveAdmin.register_page "Bootstrap" do
|
|||
template.save!
|
||||
end
|
||||
|
||||
|
||||
redirect_to admin_bootstrap_path, :notice => "Brew template created. Create a server now with that template specified."
|
||||
redirect_to admin_bootstrap_path, :notice => "Brew template created. Now, create a mount template."
|
||||
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
|
||||
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
|
||||
|
||||
content do
|
||||
|
||||
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
|
||||
semantic_form_for IcecastServer.new, :url => admin_bootstrap_create_server_path, :builder => ActiveAdmin::FormBuilder do |f|
|
||||
f.inputs "New Server" do
|
||||
f.input :hostname
|
||||
f.input :template
|
||||
f.inputs "New Icecast Server" do
|
||||
f.input :hostname, :hint => "Just the icecast hostname; no port"
|
||||
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
|
||||
f.actions
|
||||
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
|
||||
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@ module Utils
|
|||
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
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
2
db/build
2
db/build
|
|
@ -19,7 +19,7 @@ rm -rf $TARGET
|
|||
mkdir -p $PG_BUILD_OUT
|
||||
mkdir -p $PG_RUBY_PACKAGE_OUT
|
||||
|
||||
#bundle update
|
||||
bundle update
|
||||
|
||||
echo "building migrations"
|
||||
bundle exec pg_migrate build --source . --out $PG_BUILD_OUT --test --verbose
|
||||
|
|
|
|||
|
|
@ -88,4 +88,6 @@ icecast.sql
|
|||
home_page_promos.sql
|
||||
mix_job_watch.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
|
||||
-- connections, fan_invitations, invitations, genres_music_sessions, join_requests, tracks, music_sessions
|
||||
|
||||
-- tables to just get rid of
|
||||
-- session_plays
|
||||
|
||||
-- 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
|
||||
|
||||
DROP TABLE sessions_plays;
|
||||
|
||||
-- divorce notifications from UNLOGGED tables
|
||||
|
||||
-- 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)
|
||||
CREATE TABLE icecast_mount_templates(
|
||||
id VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
|
||||
name VARCHAR(256) NOT NULL,
|
||||
source_username VARCHAR(64),
|
||||
source_pass VARCHAR(64),
|
||||
max_listeners INTEGER DEFAULT 4,
|
||||
max_listener_duration INTEGER DEFAULT 3600,
|
||||
dump_file VARCHAR(1024),
|
||||
intro VARCHAR(1024),
|
||||
fallback_mount VARCHAR(1024),
|
||||
fallback_override INTEGER DEFAULT 1,
|
||||
fallback_when_full INTEGER DEFAULT 1,
|
||||
charset VARCHAR(1024) DEFAULT 'ISO8859-1',
|
||||
is_public INTEGER DEFAULT 0,
|
||||
stream_name VARCHAR(1024),
|
||||
stream_description VARCHAR(10000),
|
||||
stream_url VARCHAR(1024),
|
||||
genre VARCHAR(256),
|
||||
bitrate INTEGER,
|
||||
mime_type VARCHAR(64) NOT NULL DEFAULT 'audio/mpeg',
|
||||
subtype VARCHAR(64),
|
||||
burst_size INTEGER,
|
||||
mp3_metadata_interval INTEGER,
|
||||
hidden INTEGER DEFAULT 1,
|
||||
on_connect VARCHAR(1024),
|
||||
on_disconnect VARCHAR(1024),
|
||||
authentication_id varchar(64) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 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 icecast_mounts ALTER COLUMN mime_type DROP NOT NULL;
|
||||
ALTER TABLE icecast_mounts ALTER COLUMN mime_type DROP DEFAULT;
|
||||
ALTER TABLE icecast_mounts ALTER COLUMN subtype DROP NOT NULL;
|
||||
ALTER TABLE icecast_mounts ALTER COLUMN subtype DROP DEFAULT;
|
||||
ALTER TABLE icecast_mounts ADD COLUMN music_session_id VARCHAR(64) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
||||
ALTER TABLE icecast_mounts ADD COLUMN icecast_server_id VARCHAR(64) NOT NULL REFERENCES icecast_servers(id);
|
||||
ALTER TABLE icecast_mounts ADD COLUMN icecast_mount_template_id VARCHAR(64) REFERENCES icecast_mount_templates(id);
|
||||
ALTER TABLE icecast_mounts ADD COLUMN sourced_needs_changing_at TIMESTAMP;
|
||||
;
|
||||
CREATE TABLE icecast_server_groups (
|
||||
id VARCHAR(64) PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
|
||||
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
|
||||
------------------------
|
||||
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;
|
||||
ALTER TABLE users ADD COLUMN icecast_server_group_id VARCHAR(64) NOT NULL REFERENCES icecast_server_groups(id) DEFAULT 'default';
|
||||
|
||||
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;
|
||||
-- and by default, all servers and users are in this group
|
||||
ALTER TABLE icecast_servers ADD COLUMN icecast_server_group_id VARCHAR(64) NOT NULL REFERENCES icecast_server_groups(id) DEFAULT 'default';
|
||||
ALTER TABLE icecast_servers ADD COLUMN mount_template_id VARCHAR(64) REFERENCES icecast_mount_templates(id);
|
||||
|
||||
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;
|
||||
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;
|
||||
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_mount_template_id_fkey";
|
||||
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;
|
||||
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_directory_id_fkey";
|
||||
ALTER TABLE icecast_servers ADD CONSTRAINT "icecast_servers_directory_id_fkey" FOREIGN KEY (directory_id) REFERENCES icecast_directories(id) ON DELETE SET NULL;
|
||||
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_icecast_server_group_id_fkey";
|
||||
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;
|
||||
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_limit_id_fkey";
|
||||
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 icecast_servers ADD CONSTRAINT "icecast_servers_logging_id_fkey" FOREIGN KEY (logging_id) REFERENCES icecast_loggings(id) ON DELETE SET NULL;
|
||||
ALTER TABLE icecast_servers DROP CONSTRAINT "icecast_servers_master_relay_id_fkey";
|
||||
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
|
||||
---------
|
||||
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;
|
||||
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;
|
||||
ALTER TABLE icecast_mounts DROP CONSTRAINT "icecast_mounts_icecast_server_id_fkey";
|
||||
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;
|
||||
|
||||
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_admin_auth_id_fkey";
|
||||
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;
|
||||
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_directory_id_fkey";
|
||||
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_directory_id_fkey" FOREIGN KEY (directory_id) REFERENCES icecast_directories(id) ON DELETE SET 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 icecast_templates DROP CONSTRAINT "icecast_templates_logging_id_fkey";
|
||||
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_logging_id_fkey" FOREIGN KEY (logging_id) REFERENCES icecast_loggings(id) ON DELETE SET NULL;
|
||||
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_master_relay_id_fkey";
|
||||
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_master_relay_id_fkey" FOREIGN KEY (master_relay_id) REFERENCES icecast_master_server_relays(id) ON DELETE SET NULL;
|
||||
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_path_id_fkey";
|
||||
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_path_id_fkey" FOREIGN KEY (path_id) REFERENCES icecast_paths(id) ON DELETE SET NULL;
|
||||
ALTER TABLE icecast_templates DROP CONSTRAINT "icecast_templates_security_id_fkey";
|
||||
ALTER TABLE icecast_templates ADD CONSTRAINT "icecast_templates_security_id_fkey" FOREIGN KEY (security_id) REFERENCES icecast_securities(id) ON DELETE SET NULL;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,128 @@
|
|||
-- this manifest update makes every table associated with music_sessions UNLOGGED
|
||||
|
||||
-- tables to mark UNLOGGED
|
||||
-- connections, fan_invitations, invitations, genres_music_sessions, join_requests, tracks, music_sessions
|
||||
|
||||
-- breaking foreign keys for tables
|
||||
-- connections: user_id
|
||||
-- fan_invitations: receiver_id, sender_id
|
||||
-- music_session: user_id, band_id, claimed_recording_id, claimed_recording_initiator_id
|
||||
-- genres_music_sessions: genre_id
|
||||
-- invitations: sender_id, receiver_id
|
||||
-- fan_invitations: user_id
|
||||
-- notifications: invitation_id, join_request_id, session_id
|
||||
|
||||
|
||||
-- divorce notifications from UNLOGGED tables
|
||||
|
||||
DROP TABLE sessions_plays;
|
||||
|
||||
-- NOTIFICATIONS
|
||||
----------------
|
||||
-- "notifications_session_id_fkey" FOREIGN KEY (session_id) REFERENCES music_sessions(id) ON DELETE CASCADE
|
||||
ALTER TABLE notifications DROP CONSTRAINT notifications_session_id_fkey;
|
||||
-- "notifications_join_request_id_fkey" FOREIGN KEY (join_request_id) REFERENCES join_requests(id) ON DELETE CASCADE
|
||||
ALTER TABLE notifications DROP CONSTRAINT notifications_join_request_id_fkey;
|
||||
-- "notifications_invitation_id_fkey" FOREIGN KEY (invitation_id) REFERENCES invitations(id) ON DELETE CASCADE
|
||||
ALTER TABLE notifications DROP CONSTRAINT notifications_invitation_id_fkey;
|
||||
|
||||
-- FAN_INVITATIONS
|
||||
------------------
|
||||
DROP TABLE fan_invitations;
|
||||
DROP TABLE invitations;
|
||||
DROP TABLE join_requests;
|
||||
DROP TABLE genres_music_sessions;
|
||||
DROP TABLE tracks;
|
||||
DROP TABLE connections;
|
||||
DROP TABLE music_sessions;
|
||||
|
||||
-- MUSIC_SESSIONS
|
||||
-----------------
|
||||
CREATE UNLOGGED TABLE music_sessions (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||
description VARCHAR(8000),
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
musician_access BOOLEAN NOT NULL,
|
||||
band_id VARCHAR(64),
|
||||
approval_required BOOLEAN NOT NULL,
|
||||
fan_access BOOLEAN NOT NULL,
|
||||
fan_chat BOOLEAN NOT NULL,
|
||||
claimed_recording_id VARCHAR(64),
|
||||
claimed_recording_initiator_id VARCHAR(64)
|
||||
);
|
||||
|
||||
-- CONNECTIONS
|
||||
--------------
|
||||
CREATE UNLOGGED TABLE connections (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||
user_id VARCHAR(64),
|
||||
client_id VARCHAR(64) UNIQUE NOT NULL,
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
music_session_id VARCHAR(64),
|
||||
ip_address VARCHAR(64),
|
||||
as_musician BOOLEAN,
|
||||
aasm_state VARCHAR(64) DEFAULT 'idle'::VARCHAR NOT NULL
|
||||
);
|
||||
ALTER TABLE ONLY connections ADD CONSTRAINT connections_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE SET NULL;
|
||||
|
||||
-- GENRES_MUSIC_SESSIONS
|
||||
------------------------
|
||||
CREATE UNLOGGED TABLE genres_music_sessions (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||
genre_id VARCHAR(64),
|
||||
music_session_id VARCHAR(64)
|
||||
);
|
||||
ALTER TABLE ONLY genres_music_sessions ADD CONSTRAINT genres_music_sessions_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE UNLOGGED TABLE fan_invitations (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||
sender_id VARCHAR(64),
|
||||
receiver_id VARCHAR(64),
|
||||
music_session_id VARCHAR(64),
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL
|
||||
);
|
||||
ALTER TABLE ONLY fan_invitations ADD CONSTRAINT fan_invitations_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
||||
|
||||
CREATE UNLOGGED TABLE join_requests (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||
user_id VARCHAR(64),
|
||||
music_session_id VARCHAR(64),
|
||||
text VARCHAR(2000),
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL
|
||||
);
|
||||
ALTER TABLE ONLY join_requests ADD CONSTRAINT user_music_session_uniqkey UNIQUE (user_id, music_session_id);
|
||||
ALTER TABLE ONLY join_requests ADD CONSTRAINT join_requests_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
||||
|
||||
-- INVITATIONS
|
||||
--------------
|
||||
CREATE UNLOGGED TABLE invitations (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||
sender_id VARCHAR(64),
|
||||
receiver_id VARCHAR(64),
|
||||
music_session_id VARCHAR(64),
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
join_request_id VARCHAR(64)
|
||||
);
|
||||
ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_uniqkey UNIQUE (sender_id, receiver_id, music_session_id);
|
||||
ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_join_request_id_fkey FOREIGN KEY (join_request_id) REFERENCES join_requests(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions(id) ON DELETE CASCADE;
|
||||
|
||||
-- TRACKS
|
||||
---------
|
||||
CREATE UNLOGGED TABLE tracks (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
|
||||
connection_id VARCHAR(64),
|
||||
instrument_id VARCHAR(64),
|
||||
sound VARCHAR(64) NOT NULL,
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
client_track_id VARCHAR(64) NOT NULL
|
||||
);
|
||||
ALTER TABLE ONLY tracks ADD CONSTRAINT connections_tracks_connection_id_fkey FOREIGN KEY (connection_id) REFERENCES connections(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
@ -9,67 +9,69 @@ package jampb;
|
|||
|
||||
message ClientMessage {
|
||||
enum Type {
|
||||
LOGIN = 100;
|
||||
LOGIN_ACK = 105;
|
||||
LOGIN_MUSIC_SESSION = 110;
|
||||
LOGIN_MUSIC_SESSION_ACK = 115;
|
||||
LEAVE_MUSIC_SESSION = 120;
|
||||
LEAVE_MUSIC_SESSION_ACK = 125;
|
||||
HEARTBEAT = 130;
|
||||
HEARTBEAT_ACK = 135;
|
||||
LOGIN = 100;
|
||||
LOGIN_ACK = 105;
|
||||
LOGIN_MUSIC_SESSION = 110;
|
||||
LOGIN_MUSIC_SESSION_ACK = 115;
|
||||
LEAVE_MUSIC_SESSION = 120;
|
||||
LEAVE_MUSIC_SESSION_ACK = 125;
|
||||
HEARTBEAT = 130;
|
||||
HEARTBEAT_ACK = 135;
|
||||
|
||||
// friend notifications
|
||||
FRIEND_UPDATE = 140;
|
||||
FRIEND_REQUEST = 145;
|
||||
FRIEND_REQUEST_ACCEPTED = 150;
|
||||
FRIEND_SESSION_JOIN = 155;
|
||||
NEW_USER_FOLLOWER = 160;
|
||||
NEW_BAND_FOLLOWER = 161;
|
||||
FRIEND_UPDATE = 140;
|
||||
FRIEND_REQUEST = 145;
|
||||
FRIEND_REQUEST_ACCEPTED = 150;
|
||||
FRIEND_SESSION_JOIN = 155;
|
||||
NEW_USER_FOLLOWER = 160;
|
||||
NEW_BAND_FOLLOWER = 161;
|
||||
|
||||
// session invitations
|
||||
SESSION_INVITATION = 165;
|
||||
SESSION_ENDED = 170;
|
||||
JOIN_REQUEST = 175;
|
||||
JOIN_REQUEST_APPROVED = 180;
|
||||
JOIN_REQUEST_REJECTED = 185;
|
||||
SESSION_JOIN = 190;
|
||||
SESSION_DEPART = 195;
|
||||
MUSICIAN_SESSION_JOIN = 196;
|
||||
SESSION_INVITATION = 165;
|
||||
SESSION_ENDED = 170;
|
||||
JOIN_REQUEST = 175;
|
||||
JOIN_REQUEST_APPROVED = 180;
|
||||
JOIN_REQUEST_REJECTED = 185;
|
||||
SESSION_JOIN = 190;
|
||||
SESSION_DEPART = 195;
|
||||
MUSICIAN_SESSION_JOIN = 196;
|
||||
|
||||
// recording notifications
|
||||
MUSICIAN_RECORDING_SAVED = 200;
|
||||
BAND_RECORDING_SAVED = 205;
|
||||
RECORDING_STARTED = 210;
|
||||
RECORDING_ENDED = 215;
|
||||
RECORDING_MASTER_MIX_COMPLETE = 220;
|
||||
DOWNLOAD_AVAILABLE = 221;
|
||||
MUSICIAN_RECORDING_SAVED = 200;
|
||||
BAND_RECORDING_SAVED = 205;
|
||||
RECORDING_STARTED = 210;
|
||||
RECORDING_ENDED = 215;
|
||||
RECORDING_MASTER_MIX_COMPLETE = 220;
|
||||
DOWNLOAD_AVAILABLE = 221;
|
||||
|
||||
// band notifications
|
||||
BAND_INVITATION = 225;
|
||||
BAND_INVITATION_ACCEPTED = 230;
|
||||
BAND_SESSION_JOIN = 235;
|
||||
BAND_INVITATION = 225;
|
||||
BAND_INVITATION_ACCEPTED = 230;
|
||||
BAND_SESSION_JOIN = 235;
|
||||
|
||||
MUSICIAN_SESSION_FRESH = 240;
|
||||
MUSICIAN_SESSION_STALE = 245;
|
||||
MUSICIAN_SESSION_FRESH = 240;
|
||||
MUSICIAN_SESSION_STALE = 245;
|
||||
|
||||
|
||||
// icecast notifications
|
||||
SOURCE_UP_REQUESTED = 250;
|
||||
SOURCE_DOWN_REQUESTED = 255;
|
||||
SOURCE_UP_REQUESTED = 250;
|
||||
SOURCE_DOWN_REQUESTED = 251;
|
||||
SOURCE_UP = 252;
|
||||
SOURCE_DOWN = 253;
|
||||
|
||||
TEST_SESSION_MESSAGE = 295;
|
||||
TEST_SESSION_MESSAGE = 295;
|
||||
|
||||
PING_REQUEST = 300;
|
||||
PING_ACK = 305;
|
||||
PEER_MESSAGE = 310;
|
||||
TEST_CLIENT_MESSAGE = 315;
|
||||
PING_REQUEST = 300;
|
||||
PING_ACK = 305;
|
||||
PEER_MESSAGE = 310;
|
||||
TEST_CLIENT_MESSAGE = 315;
|
||||
|
||||
SERVER_BAD_STATE_RECOVERED = 900;
|
||||
SERVER_BAD_STATE_RECOVERED = 900;
|
||||
|
||||
SERVER_GENERIC_ERROR = 1000;
|
||||
SERVER_REJECTION_ERROR = 1005;
|
||||
SERVER_PERMISSION_ERROR = 1010;
|
||||
SERVER_BAD_STATE_ERROR = 1015;
|
||||
SERVER_GENERIC_ERROR = 1000;
|
||||
SERVER_REJECTION_ERROR = 1005;
|
||||
SERVER_PERMISSION_ERROR = 1010;
|
||||
SERVER_BAD_STATE_ERROR = 1015;
|
||||
}
|
||||
|
||||
// Identifies which inner message is filled in
|
||||
|
|
@ -126,7 +128,9 @@ message ClientMessage {
|
|||
|
||||
// icecast notifications
|
||||
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)
|
||||
optional TestSessionMessage test_session_message = 295;
|
||||
|
|
@ -381,15 +385,26 @@ message MusicianSessionStale {
|
|||
}
|
||||
|
||||
message SourceUpRequested {
|
||||
optional string host = 1; // icecast server host
|
||||
optional int32 port = 2; // icecast server port
|
||||
optional string mount = 3; // mount name
|
||||
optional string source_user = 4; // source user
|
||||
optional string source_pass = 5; // source pass
|
||||
optional string music_session = 1; // music session id
|
||||
optional string host = 2; // icecast server host
|
||||
optional int32 port = 3; // icecast server port
|
||||
optional string mount = 4; // mount name
|
||||
optional string source_user = 5; // source user
|
||||
optional string source_pass = 6; // source pass
|
||||
optional int32 bitrate = 7;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ gem 'postgres_ext'
|
|||
gem 'resque'
|
||||
gem 'resque-retry'
|
||||
gem 'resque-failed-job-mailer' #, :path => "/Users/seth/workspace/resque_failed_job_mailer"
|
||||
gem 'resque-lonely_job', '~> 1.0.0'
|
||||
gem 'oj'
|
||||
gem 'builder'
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ require "jam_ruby/resque/audiomixer"
|
|||
require "jam_ruby/resque/icecast_config_writer"
|
||||
require "jam_ruby/resque/scheduled/audiomixer_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/base_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_socket"
|
||||
require "jam_ruby/models/icecast_template_socket"
|
||||
require "jam_ruby/models/icecast_server_group"
|
||||
require "jam_ruby/models/icecast_mount_template"
|
||||
|
||||
|
||||
include Jampb
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,12 @@ module JamRuby
|
|||
return friend_ids
|
||||
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,
|
||||
def reconnect(conn, reconnect_music_session_id)
|
||||
music_session_id = nil
|
||||
|
|
@ -218,6 +224,7 @@ SQL
|
|||
|
||||
# same for session-if we are down to the last participant, delete the session
|
||||
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])
|
||||
if result.cmd_tuples == 1
|
||||
music_session_id = nil
|
||||
|
|
@ -258,6 +265,7 @@ SQL
|
|||
end
|
||||
if num_participants == 0
|
||||
# delete the music_session
|
||||
before_destroy_music_session(previous_music_session_id)
|
||||
conn.exec("DELETE from music_sessions WHERE id = $1",
|
||||
[previous_music_session_id]) do |result|
|
||||
if result.cmd_tuples == 1
|
||||
|
|
|
|||
|
|
@ -558,13 +558,15 @@ module JamRuby
|
|||
|
||||
# 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
|
||||
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(
|
||||
music_session: session_id,
|
||||
host: host,
|
||||
port: port,
|
||||
mount: mount,
|
||||
source_user: source_user,
|
||||
source_pass: source_pass)
|
||||
source_pass: source_pass,
|
||||
bitrate: bitrate)
|
||||
|
||||
Jampb::ClientMessage.new(
|
||||
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,
|
||||
# so that one of the clients will start sending source audio to icecast
|
||||
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(
|
||||
type: ClientMessage::Type::SOURCE_DOWN_REQUESTED,
|
||||
|
|
@ -583,6 +585,27 @@ module JamRuby
|
|||
source_down_requested: source_down_requested)
|
||||
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
|
||||
def test_session_message(session_id, msg)
|
||||
test = Jampb::TestSessionMessage.new(:msg => msg)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,5 @@ module JamRuby
|
|||
|
||||
# music sessions
|
||||
has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions"
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -12,7 +12,8 @@ module JamRuby
|
|||
validates :relay_pass, presence: true, length: {minimum: 5}
|
||||
validates :admin_user, presence: true, length: {minimum: 5}
|
||||
|
||||
after_save :poke_config
|
||||
before_destroy :poke_config
|
||||
after_save :poke_config
|
||||
|
||||
def poke_config
|
||||
IcecastServer.update(servers, config_changed: 1)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ module JamRuby
|
|||
validates :yp_url_timeout, presence: true, numericality: {only_integer: true}, length: {in: 1..30}
|
||||
validates :yp_url, presence: true
|
||||
|
||||
after_save :poke_config
|
||||
before_destroy :poke_config
|
||||
after_save :poke_config
|
||||
|
||||
def poke_config
|
||||
IcecastServer.update(servers, config_changed: 1)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ module JamRuby
|
|||
validates :source_timeout, presence: true, numericality: {only_integer: true}
|
||||
validates :burst_size, presence: true, numericality: {only_integer: true}
|
||||
|
||||
after_save :poke_config
|
||||
before_destroy :poke_config
|
||||
after_save :poke_config
|
||||
|
||||
def poke_config
|
||||
IcecastServer.update(servers, config_changed: 1)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ module JamRuby
|
|||
validates :port, presence: true, numericality: {only_integer: true}, length: {in: 1..65535}
|
||||
validates :shoutcast_compat, :inclusion => {:in => [nil, 0, 1]}
|
||||
|
||||
after_save :poke_config
|
||||
before_destroy :poke_config
|
||||
after_save :poke_config
|
||||
|
||||
def poke_config
|
||||
IcecastServer.update(servers, config_changed: 1)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ module JamRuby
|
|||
validates :log_archive, :inclusion => {:in => [nil, 0, 1]}
|
||||
validates :log_size, numericality: {only_integer: true}, if: lambda {|m| m.log_size.present?}
|
||||
|
||||
after_save :poke_config
|
||||
before_destroy :poke_config
|
||||
after_save :poke_config
|
||||
|
||||
def poke_config
|
||||
IcecastServer.update(servers, config_changed: 1)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ module JamRuby
|
|||
validates :master_pass, presence: true, length: {minimum: 5}
|
||||
validates :relays_on_demand, :inclusion => {:in => [0, 1]}
|
||||
|
||||
after_save :poke_config
|
||||
before_destroy :poke_config
|
||||
after_save :poke_config
|
||||
|
||||
def poke_config
|
||||
IcecastServer.update(servers, config_changed: 1)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,22 @@
|
|||
module JamRuby
|
||||
class IcecastMount < ActiveRecord::Base
|
||||
|
||||
@@log = Logging.logger[IcecastMount]
|
||||
|
||||
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,
|
||||
: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 :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'
|
||||
has_many :servers, :class_name => "JamRuby::IcecastServer", :through => :server_mounts, :source => :server
|
||||
belongs_to :server, class_name: "JamRuby::IcecastServer", inverse_of: :mounts, foreign_key: 'icecast_server_id'
|
||||
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_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?}
|
||||
|
|
@ -19,93 +24,167 @@ module JamRuby
|
|||
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 :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 :mime_type, presence: true
|
||||
validates :subtype, 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]}
|
||||
validate :name_has_correct_format
|
||||
validates :server, presence: true
|
||||
validate :name_has_correct_format
|
||||
|
||||
before_save :sanitize_active_admin
|
||||
after_save :after_save
|
||||
after_commit :after_commit
|
||||
before_save :sanitize_active_admin
|
||||
after_save :after_save
|
||||
after_save :poke_config
|
||||
before_destroy :poke_config
|
||||
|
||||
def name_has_correct_format
|
||||
errors.add(:name, "must start with /") unless name && name.start_with?('/')
|
||||
end
|
||||
|
||||
def after_save
|
||||
IcecastServer.update(servers, config_changed: 1)
|
||||
def poke_config
|
||||
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
|
||||
|
||||
# went from NOT SOURCED to SOURCED
|
||||
notify_source_up
|
||||
|
||||
elsif sourced_was && !sourced
|
||||
|
||||
# went from SOURCED to NOT SOURCED
|
||||
notify_source_down
|
||||
|
||||
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
|
||||
|
||||
def sanitize_active_admin
|
||||
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
|
||||
|
||||
def source_up
|
||||
with_lock do
|
||||
self.sourced = true
|
||||
self.save(:validate => false)
|
||||
self.sourced_needs_changing_at = nil
|
||||
save(validate: false)
|
||||
end
|
||||
end
|
||||
|
||||
def source_down
|
||||
with_lock do
|
||||
sourced = false
|
||||
save(:validate => false)
|
||||
self.sourced = false
|
||||
self.sourced_needs_changing_at = nil
|
||||
save(validate: false)
|
||||
end
|
||||
end
|
||||
|
||||
def listener_add
|
||||
with_lock do
|
||||
increment!(:listeners)
|
||||
sourced_needs_changing_at = Time.now if listeners == 0
|
||||
|
||||
# this is completely unsafe without that 'with_lock' statement above
|
||||
self.listeners = self.listeners + 1
|
||||
|
||||
save(validate: false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
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)
|
||||
builder.tag! 'mount' do |mount|
|
||||
mount.tag! 'mount-name', name
|
||||
mount.tag! 'username', source_username if !source_username.nil? && !source_username.empty?
|
||||
mount.tag! 'password', source_pass if !source_pass.nil? && !source_pass.empty?
|
||||
mount.tag! 'max-listeners', max_listeners unless max_listeners.nil?
|
||||
mount.tag! 'max-listener-duration', max_listener_duration unless max_listener_duration.nil?
|
||||
mount.tag! 'dump-file', dump_file if !dump_file.nil? && !dump_file.empty?
|
||||
mount.tag! 'intro', intro if !intro.nil? && !intro.empty?
|
||||
mount.tag! 'fallback-mount', fallback_mount if !fallback_mount.nil? && !fallback_mount.empty?
|
||||
mount.tag! 'fallback-override', fallback_override if fallback_override
|
||||
mount.tag! 'fallback-when-full', fallback_when_full if fallback_when_full
|
||||
mount.tag! 'charset', charset if charset
|
||||
mount.tag! 'public', is_public
|
||||
mount.tag! 'stream-name', stream_name if !stream_name.nil? && !stream_name.empty?
|
||||
mount.tag! 'stream-description', stream_description if !stream_description.nil? && !stream_description.empty?
|
||||
mount.tag! 'stream-url', stream_url if !stream_url.nil? && !stream_url.empty?
|
||||
mount.tag! 'genre', genre unless genre.empty?
|
||||
mount.tag! 'bitrate', bitrate if bitrate
|
||||
mount.tag! 'type', mime_type
|
||||
mount.tag! 'subtype', subtype
|
||||
mount.tag! 'burst-size', burst_size if burst_size
|
||||
mount.tag! 'mp3-metadata-interval', mp3_metadata_interval unless mp3_metadata_interval.nil?
|
||||
mount.tag! 'hidden', hidden
|
||||
mount.tag! 'on-connect', on_connect if on_connect
|
||||
mount.tag! 'on-disconnect', on_disconnect if on_disconnect
|
||||
mount.tag! 'username', resolve_string(:source_username) if string_present?(:source_username)
|
||||
mount.tag! 'password', resolve_string(:source_pass) if string_present?(:source_pass)
|
||||
mount.tag! 'max-listeners', resolve_int(:max_listeners) if int_present?(:max_listeners)
|
||||
mount.tag! 'max-listener-duration', resolve_string(:max_listener_duration) if int_present?(:max_listener_duration)
|
||||
mount.tag! 'dump-file', resolve_string(:dump_file) if string_present?(:dump_file)
|
||||
mount.tag! 'intro', resolve_string(:intro) if string_present?(:intro)
|
||||
mount.tag! 'fallback-mount', resolve_string(:fallback_mount) if string_present?(:fallback_mount)
|
||||
mount.tag! 'fallback-override', resolve_int(:fallback_override) if int_present?(:fallback_override)
|
||||
mount.tag! 'fallback-when-full', resolve_int(:fallback_when_full) if int_present?(:fallback_when_full)
|
||||
mount.tag! 'charset', resolve_string(:charset) if string_present?(:charset)
|
||||
mount.tag! 'public', resolve_int(:is_public) if int_present?(:is_public)
|
||||
mount.tag! 'stream-name', resolve_string(:stream_name) if string_present?(:stream_name)
|
||||
mount.tag! 'stream-description', resolve_string(:stream_description) if string_present?(:stream_description)
|
||||
mount.tag! 'stream-url', resolve_string(:stream_url) if string_present?(:stream_url)
|
||||
mount.tag! 'genre', resolve_string(:genre) if string_present?(:genre)
|
||||
mount.tag! 'bitrate', resolve_int(:bitrate) if int_present?(:bitrate)
|
||||
mount.tag! 'type', resolve_string(:mime_type) if string_present?(:mime_type)
|
||||
mount.tag! 'subtype', resolve_string(:subtype) if string_present?(:subtype)
|
||||
mount.tag! 'burst-size', resolve_int(:burst_size) if int_present?(:burst_size)
|
||||
mount.tag! 'mp3-metadata-interval', resolve_int(:mp3_metadata_interval) if int_present?(:mp3_metadata_interval)
|
||||
mount.tag! 'hidden', resolve_int(:hidden) if int_present?(:hidden)
|
||||
mount.tag! 'on-connect', resolve_string(:on_connect) if string_present?(:on_connect)
|
||||
mount.tag! 'on-disconnect', resolve_string(:on_disconnect) if string_present?(:on_disconnect)
|
||||
|
||||
authentication.dumpXml(builder) if authentication
|
||||
end
|
||||
|
|
@ -117,5 +196,23 @@ module JamRuby
|
|||
|
||||
"http://" + server_mount.server.hostname + self.name
|
||||
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
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
module JamRuby
|
||||
class IcecastMountTemplate < ActiveRecord::Base
|
||||
|
||||
attr_accessor :hostname, :default_mime_type # used by jam-admin
|
||||
|
||||
attr_accessible :authentication_id, :source_username, :source_pass, :max_listeners, :max_listener_duration,
|
||||
:dump_file, :intro, :fallback_mount, :fallback_override, :fallback_when_full, :charset, :is_public,
|
||||
:stream_name, :stream_description, :stream_url, :genre, :bitrate, :mime_type, :subtype, :burst_size,
|
||||
:mp3_metadata_interval, :hidden, :on_connect, :on_disconnect, :name, as: :admin
|
||||
|
||||
belongs_to :authentication, class_name: "JamRuby::IcecastUserAuthentication", inverse_of: :mount, foreign_key: 'authentication_id'
|
||||
has_many :mounts, class_name: "JamRuby::IcecastMount", inverse_of: :mount_template, foreign_key: 'icecast_mount_template_id'
|
||||
has_many :servers, class_name: "JamRuby::IcecastServer", inverse_of: :mount_template, foreign_key: 'mount_template_id'
|
||||
|
||||
validates :source_username, length: {minimum: 5}, if: lambda {|m| m.source_username.present?}
|
||||
validates :source_pass, length: {minimum: 5}, if: lambda {|m| m.source_pass.present?}
|
||||
validates :max_listeners, length: {in: 1..15000}, if: lambda {|m| m.max_listeners.present?}
|
||||
validates :max_listener_duration, length: {in: 1..3600 * 48}, if: lambda {|m| m.max_listener_duration.present?}
|
||||
validates :fallback_override, :inclusion => {:in => [0, 1]} , if: lambda {|m| m.fallback_mount.present?}
|
||||
validates :fallback_when_full, :inclusion => {:in => [0, 1]} , if: lambda {|m| m.fallback_mount.present?}
|
||||
validates :is_public, presence: true, :inclusion => {:in => [-1, 0, 1]}
|
||||
validates :bitrate, numericality: {only_integer: true}, if: lambda {|m| m.bitrate.present?}
|
||||
validates :mime_type, presence: true
|
||||
validates :burst_size, numericality: {only_integer: true}, if: lambda {|m| m.burst_size.present?}
|
||||
validates :mp3_metadata_interval, numericality: {only_integer: true}, if: lambda {|m| m.mp3_metadata_interval.present?}
|
||||
validates :hidden, :inclusion => {:in => [0, 1]}
|
||||
|
||||
before_save :sanitize_active_admin
|
||||
after_save :poke_config
|
||||
after_initialize :after_initialize
|
||||
before_destroy :poke_config
|
||||
|
||||
def after_initialize # used by jam-admin
|
||||
self.hostname = 'localhost:3000'
|
||||
self.default_mime_type = 'mp3'
|
||||
end
|
||||
|
||||
def poke_config
|
||||
IcecastServer.update(servers, config_changed: 1)
|
||||
end
|
||||
|
||||
def sanitize_active_admin
|
||||
self.authentication_id = nil if self.authentication_id == ''
|
||||
end
|
||||
|
||||
# pick a server that's in the same group as the user that is under the least load
|
||||
def build_session_mount(music_session)
|
||||
mount = IcecastMount.new
|
||||
mount.authentication = authentication
|
||||
mount.mount_template = self
|
||||
mount.name = "/" + SecureRandom.urlsafe_base64
|
||||
mount.music_session_id = music_session.id
|
||||
mount.source_username = 'source'
|
||||
mount.source_pass = SecureRandom.urlsafe_base64
|
||||
mount.stream_name = "JamKazam music session created by #{music_session.creator.name}"
|
||||
mount.stream_description = music_session.description
|
||||
mount.stream_url = "http://www.jamkazam.com" ## TODO/XXX, the jamkazam url should be the page hosting the widget
|
||||
mount.genre = music_session.genres.map {|genre| genre.description}.join(',')
|
||||
mount
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -12,7 +12,8 @@ module JamRuby
|
|||
validates :web_root, presence: true
|
||||
validates :admin_root, presence: true
|
||||
|
||||
after_save :poke_config
|
||||
after_save :poke_config
|
||||
before_destroy :poke_config
|
||||
|
||||
def poke_config
|
||||
IcecastServer.update(servers, config_changed: 1)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ module JamRuby
|
|||
validates :relay_shoutcast_metadata, :inclusion => {:in => [0, 1]}
|
||||
validates :on_demand, presence: true, :inclusion => {:in => [0, 1]}
|
||||
|
||||
after_save :poke_config
|
||||
before_destroy :poke_config
|
||||
after_save :poke_config
|
||||
|
||||
def poke_config
|
||||
IcecastServer.update(servers, :config_changed => true)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ module JamRuby
|
|||
|
||||
validates :chroot, :inclusion => {:in => [0, 1]}
|
||||
|
||||
after_save :poke_config
|
||||
before_destroy :poke_config
|
||||
after_save :poke_config
|
||||
|
||||
def poke_config
|
||||
IcecastServer.update(servers, config_changed: 1)
|
||||
|
|
|
|||
|
|
@ -3,28 +3,30 @@ module JamRuby
|
|||
|
||||
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
|
||||
|
||||
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
|
||||
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 :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 :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 :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_sockets, :class_name => "JamRuby::IcecastListenSocket", :through => :listen_socket_servers, :source => :socket
|
||||
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 :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 :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 :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_sockets, class_name: "JamRuby::IcecastListenSocket", :through => :listen_socket_servers, :source => :socket
|
||||
|
||||
# mounts and relays are naturally server-specific, though
|
||||
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 :server_mounts, class_name: "JamRuby::IcecastServerMount", inverse_of: :server
|
||||
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 :relays, :class_name => "JamRuby::IcecastRelay", :through => :server_relays, :source => :relay
|
||||
has_many :server_relays, class_name: "JamRuby::IcecastServerRelay", inverse_of: :relay
|
||||
has_many :relays, class_name: "JamRuby::IcecastRelay", :through => :server_relays, :source => :relay
|
||||
|
||||
validates :config_changed, :inclusion => {:in => [0, 1]}
|
||||
validates :hostname, presence: true
|
||||
|
|
@ -32,6 +34,7 @@ module JamRuby
|
|||
validates :server_id, presence: true
|
||||
|
||||
validates :template, presence: true
|
||||
validates :mount_template, presence: true
|
||||
|
||||
before_save :before_save, unless: lambda { skip_config_changed_flag }
|
||||
before_save :sanitize_active_admin
|
||||
|
|
@ -75,8 +78,48 @@ module JamRuby
|
|||
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
|
||||
return server_id
|
||||
server_id
|
||||
end
|
||||
|
||||
def dumpXml (output=$stdout, indent=1)
|
||||
|
|
@ -84,29 +127,21 @@ module JamRuby
|
|||
builder = ::Builder::XmlMarkup.new(:target => output, :indent => indent)
|
||||
|
||||
builder.tag! 'icecast' do |root|
|
||||
root.tag! 'hostname', hostname
|
||||
root.tag! 'location', (location.nil? || location.empty?) ? template.location : location
|
||||
root.tag! 'server-id', server_id
|
||||
root.tag! 'admin', (admin_email.nil? || admin_email.empty?) ? template.admin_email : admin_email
|
||||
root.tag! 'fileserve', fileserve.nil? ? template.fileserve : fileserve
|
||||
root.tag! 'hostname', hostname
|
||||
root.tag! 'server-id', server_id
|
||||
root.tag! 'location', resolve_string(:location) if string_present?(:location)
|
||||
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_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|
|
||||
listen_socket.dumpXml(builder)
|
||||
end
|
||||
|
|
@ -120,5 +155,32 @@ module JamRuby
|
|||
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
|
||||
|
|
@ -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 :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
|
||||
|
|
@ -33,8 +33,8 @@ module JamRuby
|
|||
validates :listen_sockets, length: {minimum: 1}
|
||||
|
||||
before_save :sanitize_active_admin
|
||||
after_save :poke_config
|
||||
|
||||
after_save :poke_config
|
||||
before_destroy :poke_config
|
||||
|
||||
def poke_config
|
||||
IcecastServer.update(servers, config_changed: 1)
|
||||
|
|
|
|||
|
|
@ -11,5 +11,11 @@ module JamRuby
|
|||
validates :socket, :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
|
||||
|
|
@ -17,10 +17,11 @@ module JamRuby
|
|||
validates :auth_header, presence: true, if: :url_auth?
|
||||
validates :timelimit_header, presence: true, if: :url_auth?
|
||||
|
||||
before_destroy :poke_config
|
||||
after_save :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
|
||||
|
||||
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_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 :users, :through => :connections, :class_name => "JamRuby::User"
|
||||
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 :no_new_playback_while_playing
|
||||
|
||||
def before_destroy
|
||||
self.mount.destroy if self.mount
|
||||
end
|
||||
|
||||
def creator_is_musician
|
||||
unless creator.musician?
|
||||
|
|
@ -53,6 +58,21 @@ module JamRuby
|
|||
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
|
||||
# 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)
|
||||
|
|
|
|||
|
|
@ -766,10 +766,10 @@ module JamRuby
|
|||
@@mq_router.publish_to_user(user_id, msg)
|
||||
end
|
||||
|
||||
def send_source_up_requested(music_session, host, port, mount, source_user, source_pass)
|
||||
msg = @@message_factory.source_up_requested(music_session.id, 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, bitrate)
|
||||
|
||||
@@mg_router.server_publish_to_session(music_session, msg)
|
||||
@@mq_router.server_publish_to_session(music_session, msg)
|
||||
end
|
||||
|
||||
def send_source_down_requested(music_session, mount)
|
||||
|
|
@ -777,6 +777,18 @@ module JamRuby
|
|||
|
||||
@@mq_router.server_publish_to_session(music_session, msg)
|
||||
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
|
||||
|
|
@ -18,6 +18,8 @@ module JamRuby
|
|||
# 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
|
||||
|
||||
belongs_to :icecast_server_group, class_name: "JamRuby::IcecastServerGroup", inverse_of: :users, foreign_key: 'icecast_server_group_id'
|
||||
|
||||
# authorizations (for facebook, etc -- omniauth)
|
||||
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
|
||||
# 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)
|
||||
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)
|
||||
|
||||
# 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)
|
||||
end
|
||||
|
|
@ -38,13 +37,21 @@ class MQRouter
|
|||
# sends a message to a session from the server
|
||||
# no access check as with user_publish_to_session
|
||||
# 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
|
||||
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)
|
||||
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)
|
||||
# this method deliberately has no database interactivity/active_record objects
|
||||
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)
|
||||
# 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
|
||||
sender_client_id = sender[:client_id]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
require 'json'
|
||||
require 'resque'
|
||||
require 'resque-retry'
|
||||
|
||||
require 'resque-lonely_job'
|
||||
require 'net/http'
|
||||
require 'digest/md5'
|
||||
|
||||
|
|
@ -8,6 +9,7 @@ module JamRuby
|
|||
|
||||
# executes a mix of tracks, creating a final output mix
|
||||
class IcecastConfigWriter
|
||||
extend Resque::Plugins::LonelyJob
|
||||
|
||||
@@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]
|
||||
|
||||
def self.perform
|
||||
@@log.debug("waking up")
|
||||
|
||||
IcecastConfigWriter.queue_jobs_needing_retry
|
||||
|
||||
@@log.debug("done")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
require 'json'
|
||||
require 'resque'
|
||||
require 'resque-lonely_job'
|
||||
require 'net/http'
|
||||
require 'digest/md5'
|
||||
|
||||
module JamRuby
|
||||
|
||||
# http://blog.bignerdranch.com/1643-never-use-resque-for-serial-jobs/
|
||||
# periodically scheduled to find sources that need to be brought down, or alternatively, it seems the client failed to start sourcing
|
||||
class IcecastSourceCheck
|
||||
extend Resque::Plugins::LonelyJob
|
||||
|
||||
|
||||
@queue = :icecast_source_check
|
||||
|
||||
|
||||
@@log = Logging.logger[IcecastSourceCheck]
|
||||
|
||||
|
||||
def self.perform
|
||||
@@log.debug("waking up")
|
||||
|
||||
JamWebEventMachine.run_wait_stop do
|
||||
IcecastSourceCheck.new.run
|
||||
end
|
||||
|
||||
@@log.debug("done")
|
||||
end
|
||||
|
||||
def run # if we haven't seen updated_at be tickled in 5 minutes, but config_changed is still set to TRUE, this record has gotten stale
|
||||
IcecastMount.find_each(lock: true, :conditions => "sourced_needs_changing_at < (NOW() - interval '#{APP_CONFIG.icecast_max_sourced_changed} second')", :batch_size => 100) do |mount|
|
||||
if mount.music_session_id
|
||||
mount.with_lock do
|
||||
handle_notifications(mount)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def handle_notifications(mount)
|
||||
if mount.listeners == 0 && mount.sourced
|
||||
# if no listeners, but we are sourced, then ask it to stop sourcing
|
||||
@@log.debug("SOURCE_DOWN_REQUEST called on mount #{mount.name}")
|
||||
|
||||
mount.update_attribute(:sourced_needs_changing_at, Time.now) # we send out a source request, so we need to update the time
|
||||
mount.notify_source_down_requested
|
||||
|
||||
elsif mount.listeners > 0 && !mount.sourced
|
||||
# if we have some listeners, and still are not sourced, then ask to start sourcing again
|
||||
@@log.debug("SOURCE_UP_REQUEST called on mount #{mount.name}")
|
||||
|
||||
mount.update_attribute(:sourced_needs_changing_at, Time.now) # we send out a source request, so we need to update the time
|
||||
mount.notify_source_up_requested
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -195,7 +195,7 @@ FactoryGirl.define do
|
|||
factory :icecast_mount, :class => JamRuby::IcecastMount do
|
||||
name "/" + Faker::Lorem.characters(10)
|
||||
source_username Faker::Lorem.characters(10)
|
||||
source_pass Faker::Lorem.characters(10)
|
||||
source_pass Faker::Lorem.characters(10)
|
||||
max_listeners 100
|
||||
max_listener_duration 3600
|
||||
fallback_mount Faker::Lorem.characters(10)
|
||||
|
|
@ -207,10 +207,21 @@ FactoryGirl.define do
|
|||
stream_url Faker::Lorem.characters(10)
|
||||
genre Faker::Lorem.characters(10)
|
||||
hidden 0
|
||||
association :server, factory: :icecast_server_with_overrides
|
||||
|
||||
factory :icecast_mount_with_auth do
|
||||
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
|
||||
|
||||
factory :icecast_listen_socket, :class => JamRuby::IcecastListenSocket do
|
||||
|
|
@ -227,7 +238,7 @@ FactoryGirl.define do
|
|||
factory :icecast_user_authentication, :class => JamRuby::IcecastUserAuthentication do
|
||||
authentication_type 'url'
|
||||
unused_username Faker::Lorem.characters(10)
|
||||
unused_pass Faker::Lorem.characters(10)
|
||||
unused_pass Faker::Lorem.characters(10)
|
||||
mount_add Faker::Lorem.characters(10)
|
||||
mount_remove Faker::Lorem.characters(10)
|
||||
listener_add Faker::Lorem.characters(10)
|
||||
|
|
@ -242,6 +253,7 @@ FactoryGirl.define do
|
|||
|
||||
factory :icecast_server_minimal do
|
||||
association :template, :factory => :icecast_template_minimal
|
||||
association :mount_template, :factory => :icecast_mount_template
|
||||
|
||||
factory :icecast_server_with_overrides do
|
||||
association :limit, :factory => :icecast_limit
|
||||
|
|
@ -274,4 +286,23 @@ FactoryGirl.define do
|
|||
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
|
||||
|
|
|
|||
|
|
@ -50,10 +50,22 @@ describe IcecastAdminAuthentication do
|
|||
server.config_changed.should == 1
|
||||
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
|
||||
server.admin_auth.save!
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "success when deleted via server" do
|
||||
server.admin_auth.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -57,11 +57,23 @@ describe IcecastDirectory do
|
|||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "delete via template" do
|
||||
server.template.directory.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "success via server" do
|
||||
server.directory.save!
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "destroy via server" do
|
||||
server.directory.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -66,10 +66,22 @@ describe IcecastLimit do
|
|||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "delete via template" do
|
||||
server.template.limit.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "success via server" do
|
||||
server.limit.save!
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "delete via server" do
|
||||
server.limit.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,10 +27,22 @@ describe IcecastListenSocket do
|
|||
server.config_changed.should == 1
|
||||
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
|
||||
server.listen_sockets.first.save!
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "delete via server" do
|
||||
server.listen_sockets.first.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -40,10 +40,22 @@ describe IcecastLogging do
|
|||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "delete via template" do
|
||||
server.template.logging.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "success via server" do
|
||||
server.logging.save!
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "deete via server" do
|
||||
server.logging.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -53,11 +53,23 @@ describe IcecastMasterServerRelay do
|
|||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "delete via template" do
|
||||
server.template.master_relay.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "success via server" do
|
||||
server.master_relay.save!
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "delete via server" do
|
||||
server.master_relay.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -10,10 +10,6 @@ describe IcecastMount do
|
|||
mount = IcecastMount.new
|
||||
mount.save.should be_false
|
||||
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
|
||||
|
||||
|
||||
|
|
@ -35,6 +31,7 @@ describe IcecastMount do
|
|||
mount.max_listeners = 1000
|
||||
mount.max_listener_duration = 3600
|
||||
mount.authentication = FactoryGirl.create(:icecast_user_authentication)
|
||||
mount.server = FactoryGirl.create(:icecast_server_with_overrides)
|
||||
|
||||
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
|
||||
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
|
||||
let(:server) { a = FactoryGirl.create(:icecast_server_with_overrides); a.config_updated; IcecastServer.find(a.id) }
|
||||
|
||||
before(:each) do
|
||||
server.mounts << FactoryGirl.create(:icecast_mount)
|
||||
server.mounts << FactoryGirl.create(:icecast_mount, server: server)
|
||||
server.save!
|
||||
server.config_updated
|
||||
server.reload
|
||||
|
|
@ -86,6 +111,12 @@ describe IcecastMount do
|
|||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "success when deleted" do
|
||||
server.mounts.first.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "icecast server callbacks" do
|
||||
|
|
@ -93,4 +124,125 @@ describe IcecastMount do
|
|||
icecast_mount.source_up
|
||||
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
|
||||
|
|
@ -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
|
||||
end
|
||||
|
||||
it "delete via template" do
|
||||
server.template.path.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "success via server" do
|
||||
server.path.save!
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "delete via server" do
|
||||
server.path.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -48,5 +48,11 @@ describe IcecastRelay do
|
|||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "delete via server" do
|
||||
server.relays.first.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -40,10 +40,22 @@ describe IcecastSecurity do
|
|||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "delete via template" do
|
||||
server.template.security.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "success via server" do
|
||||
server.security.save!
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
|
||||
it "delete via server" do
|
||||
server.security.destroy
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -30,4 +30,34 @@ describe IcecastServer do
|
|||
xml.css('icecast security').length.should == 1
|
||||
xml.css('icecast listen-socket').length.should == 1
|
||||
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
|
||||
|
|
@ -1,10 +1,20 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe IcecastListenSocket do
|
||||
describe IcecastTemplate do
|
||||
|
||||
let(:template) { template = FactoryGirl.create(:icecast_template_minimal) }
|
||||
|
||||
it "save" do
|
||||
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) }
|
||||
|
||||
it "success via template" do
|
||||
server.template.save!
|
||||
server.reload
|
||||
server.config_changed.should == 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -480,5 +480,36 @@ describe MusicSession do
|
|||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.before(:suite) do
|
||||
DatabaseCleaner.strategy = :truncation, {:except => %w[instruments genres] }
|
||||
DatabaseCleaner.clean_with(:truncation, {:except => %w[instruments genres] })
|
||||
DatabaseCleaner.strategy = :truncation, {:except => %w[instruments genres icecast_server_groups] }
|
||||
DatabaseCleaner.clean_with(:truncation, {:except => %w[instruments genres icecast_server_groups] })
|
||||
end
|
||||
|
||||
config.before(:each) do
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ def app_config
|
|||
2 * 60 # 2 minutes
|
||||
end
|
||||
|
||||
def icecast_max_sourced_changed
|
||||
15 # 15 seconds
|
||||
end
|
||||
|
||||
def rabbitmq_host
|
||||
"localhost"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ gem 'resque'
|
|||
gem 'resque-retry'
|
||||
gem 'resque-failed-job-mailer'
|
||||
gem 'resque-dynamic-queues'
|
||||
gem 'resque-lonely_job', '~> 1.0.0'
|
||||
gem 'quiet_assets', :group => :development
|
||||
gem "bugsnag"
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,12 @@
|
|||
BAND_INVITATION : "BAND_INVITATION",
|
||||
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",
|
||||
PING_REQUEST : "PING_REQUEST",
|
||||
PING_ACK : "PING_ACK",
|
||||
|
|
|
|||
|
|
@ -571,6 +571,14 @@
|
|||
function CloseRecording() {}
|
||||
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
|
||||
// Set the instance functions:
|
||||
|
|
@ -703,6 +711,10 @@
|
|||
this.CloseRecording = CloseRecording;
|
||||
this.OnDownloadAvailable = OnDownloadAvailable;
|
||||
|
||||
// Broadcasting
|
||||
this.SessionLiveBroadcastStart = SessionLiveBroadcastStart;
|
||||
this.SessionLiveBroadcastStop = SessionLiveBroadcastStop;
|
||||
|
||||
// fake calls; not a part of the actual jam client
|
||||
this.RegisterP2PMessageCallbacks = RegisterP2PMessageCallbacks;
|
||||
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 });
|
||||
});
|
||||
}
|
||||
|
||||
else if (type === context.JK.MessageType.BAND_INVITATION_ACCEPTED) {
|
||||
$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) {
|
||||
|
|
|
|||
|
|
@ -7,15 +7,14 @@ class ApiIcecastController < ApiController
|
|||
|
||||
|
||||
def mount_add
|
||||
mount = IcecastMount.find(@mount_id)
|
||||
mount = IcecastMount.find_by_name!(@mount_id)
|
||||
mount.source_up
|
||||
|
||||
|
||||
render text: '', :status => :ok
|
||||
end
|
||||
|
||||
def mount_remove
|
||||
mount = IcecastMount.find(@mount_id)
|
||||
mount = IcecastMount.find_by_name!(@mount_id)
|
||||
mount.source_down
|
||||
|
||||
render text: '', :status => :ok
|
||||
|
|
@ -28,7 +27,7 @@ class ApiIcecastController < ApiController
|
|||
remote_ip = params[:ip]
|
||||
remote_user_agent = params[:agent]
|
||||
|
||||
mount = IcecastMount.find(@mount_id)
|
||||
mount = IcecastMount.find_by_name!(@mount_id)
|
||||
mount.listener_add
|
||||
|
||||
render text: '', :status => :ok
|
||||
|
|
@ -40,7 +39,7 @@ class ApiIcecastController < ApiController
|
|||
pass = params[:pass]
|
||||
duration = params[:duration] # seconds connected to the listen stream
|
||||
|
||||
mount = IcecastMount.find(@mount_id)
|
||||
mount = IcecastMount.find_by_name!(@mount_id)
|
||||
mount.listener_remove
|
||||
|
||||
render text: '', :status => :ok
|
||||
|
|
|
|||
|
|
@ -177,6 +177,7 @@ include JamRuby
|
|||
# this will be the qualifier on the IcecastConfigWorker queue name
|
||||
config.icecast_server_id = ENV['ICECAST_SERVER_ID'] || 'localhost'
|
||||
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_generic_from = 'nobody@jamkazam.com'
|
||||
|
|
|
|||
|
|
@ -8,3 +8,10 @@ IcecastConfigRetry:
|
|||
cron: 0 * * * *
|
||||
class: "JamRuby::IcecastConfigRetry"
|
||||
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
|
||||
# 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.description = description
|
||||
music_session.musician_access = musician_access
|
||||
|
|
@ -24,9 +25,6 @@ MusicSessionManager < BaseManager
|
|||
music_session.band = band
|
||||
music_session.legal_terms = legal_terms
|
||||
|
||||
#genres = genres
|
||||
@log.debug "Genres class: " + genres.class.to_s
|
||||
|
||||
unless genres.nil?
|
||||
genres.each do |genre_id|
|
||||
loaded_genre = Genre.find(genre_id)
|
||||
|
|
@ -34,6 +32,13 @@ MusicSessionManager < BaseManager
|
|||
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
|
||||
|
||||
unless music_session.errors.any?
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ gem 'postgres_ext'
|
|||
gem 'resque'
|
||||
gem 'resque-retry'
|
||||
gem 'resque-failed-job-mailer'
|
||||
gem 'resque-lonely_job', '~> 1.0.0'
|
||||
|
||||
group :development do
|
||||
gem 'pry'
|
||||
|
|
|
|||
Loading…
Reference in New Issue