953 lines
32 KiB
Ruby
953 lines
32 KiB
Ruby
module JamRuby
|
|
class ActiveMusicSession < ActiveRecord::Base
|
|
|
|
@@log = Logging.logger[ActiveMusicSession]
|
|
|
|
self.primary_key = 'id'
|
|
|
|
self.table_name = 'active_music_sessions'
|
|
|
|
attr_accessor :legal_terms, :max_score, :opening_jam_track, :opening_recording, :opening_backing_track, :opening_metronome, :jam_track_id
|
|
|
|
belongs_to :claimed_recording, :class_name => "JamRuby::ClaimedRecording", :foreign_key => "claimed_recording_id", :inverse_of => :playing_sessions
|
|
belongs_to :claimed_recording_initiator, :class_name => "JamRuby::User", :inverse_of => :playing_claimed_recordings, :foreign_key => "claimed_recording_initiator_id"
|
|
|
|
belongs_to :jam_track, :class_name => "JamRuby::JamTrack", :foreign_key => "jam_track_id", :inverse_of => :playing_sessions
|
|
belongs_to :jam_track_initiator, :class_name => "JamRuby::User", :inverse_of => :playing_jam_tracks, :foreign_key => "jam_track_initiator_id"
|
|
|
|
belongs_to :backing_track_initiator, :class_name => "JamRuby::User", :inverse_of => :playing_jam_tracks, :foreign_key => "backing_track_initiator_id"
|
|
belongs_to :metronome_initiator, :class_name => "JamRuby::User", :inverse_of => :playing_jam_tracks, :foreign_key => "metronome_initiator_id"
|
|
|
|
has_one :music_session, :class_name => "JamRuby::MusicSession", :foreign_key => 'music_session_id'
|
|
has_one :mount, :class_name => "JamRuby::IcecastMount", :inverse_of => :music_session, :foreign_key => 'music_session_id'
|
|
belongs_to :creator, :class_name => 'JamRuby::User', :foreign_key => :user_id
|
|
|
|
has_many :connections, :class_name => "JamRuby::Connection", foreign_key: :music_session_id
|
|
has_many :users, :through => :connections, :class_name => "JamRuby::User"
|
|
has_many :recordings, :class_name => "JamRuby::Recording", :inverse_of => :music_session, foreign_key: :music_session_id
|
|
has_many :chats, :class_name => "JamRuby::ChatMessages", :foreign_key => "session_id"
|
|
validates :creator, :presence => true
|
|
validate :creator_is_musician
|
|
validate :validate_opening_recording, :if => :opening_recording
|
|
validate :validate_opening_jam_track, :if => :opening_jam_track
|
|
validate :validate_opening_backing_track, :if => :opening_backing_track
|
|
|
|
# not sure if this is helpful since if one opens, it always stays open
|
|
validate :validate_opening_metronome, :if => :opening_metronome
|
|
|
|
after_create :started_session
|
|
|
|
after_destroy do |obj|
|
|
JamRuby::MusicSession.removed_music_session(obj.id)
|
|
end
|
|
|
|
#default_scope :select => "*, 0 as score"
|
|
|
|
def attributes
|
|
super.merge('max_score' => self.max_score)
|
|
end
|
|
|
|
def max_score
|
|
nil unless has_attribute?(:max_score)
|
|
read_attribute(:max_score).to_i
|
|
end
|
|
|
|
before_create :create_uuid
|
|
def create_uuid
|
|
#self.id = SecureRandom.uuid
|
|
end
|
|
|
|
def before_destroy
|
|
feed = Feed.find_by_music_session_id(self.id)
|
|
unless feed.nil?
|
|
feed.active = false
|
|
feed.save
|
|
end
|
|
self.mount.destroy if self.mount
|
|
end
|
|
|
|
def creator_is_musician
|
|
unless creator.musician?
|
|
errors.add(:creator, ValidationMessages::MUST_BE_A_MUSICIAN)
|
|
end
|
|
end
|
|
|
|
def validate_opening_recording
|
|
unless claimed_recording_id_was.nil?
|
|
errors.add(:claimed_recording, ValidationMessages::CLAIMED_RECORDING_ALREADY_IN_PROGRESS)
|
|
end
|
|
|
|
if is_jam_track_open?
|
|
errors.add(:claimed_recording, ValidationMessages::JAM_TRACK_ALREADY_OPEN)
|
|
end
|
|
|
|
if is_backing_track_open?
|
|
errors.add(:claimed_recording, ValidationMessages::BACKING_TRACK_ALREADY_OPEN)
|
|
end
|
|
|
|
if is_metronome_open?
|
|
errors.add(:claimed_recording, ValidationMessages::METRONOME_ALREADY_OPEN)
|
|
end
|
|
end
|
|
|
|
def validate_opening_jam_track
|
|
validate_other_audio(:jam_track)
|
|
end
|
|
|
|
def validate_opening_backing_track
|
|
validate_other_audio(:backing_track)
|
|
end
|
|
|
|
def validate_opening_metronome
|
|
validate_other_audio(:metronome)
|
|
end
|
|
|
|
def validate_other_audio(error_key)
|
|
# validate that there is no backing track already open in this session
|
|
if backing_track_path_was.present?
|
|
errors.add(error_key, ValidationMessages::BACKING_TRACK_ALREADY_OPEN)
|
|
end
|
|
|
|
# validate that there is no jam track already open in this session
|
|
if jam_track_id_was.present?
|
|
errors.add(error_key, ValidationMessages::JAM_TRACK_ALREADY_OPEN)
|
|
end
|
|
|
|
# validate that there is no recording being made
|
|
if is_recording?
|
|
errors.add(error_key, ValidationMessages::RECORDING_ALREADY_IN_PROGRESS)
|
|
end
|
|
|
|
# validate that there is no recording being played back to the session
|
|
if is_playing_recording?
|
|
errors.add(error_key, ValidationMessages::CLAIMED_RECORDING_ALREADY_IN_PROGRESS)
|
|
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, options = {})
|
|
participants = options[:participants]
|
|
genres = options[:genres]
|
|
keyword = options[:keyword]
|
|
friends_only = options[:friends_only].nil? ? false : options[:friends_only]
|
|
my_bands_only = options[:my_bands_only].nil? ? false : options[:my_bands_only]
|
|
as_musician = options[:as_musician].nil? ? true : options[:as_musician]
|
|
|
|
query = ActiveMusicSession
|
|
.joins(
|
|
%Q{
|
|
INNER JOIN
|
|
music_sessions
|
|
ON
|
|
active_music_sessions.id = music_sessions.id
|
|
}
|
|
)
|
|
.joins(
|
|
%Q{
|
|
INNER JOIN
|
|
connections
|
|
ON
|
|
active_music_sessions.id = connections.music_session_id
|
|
}
|
|
)
|
|
.joins(
|
|
%Q{
|
|
LEFT OUTER JOIN
|
|
friendships
|
|
ON
|
|
connections.user_id = friendships.user_id
|
|
AND
|
|
friendships.friend_id = '#{current_user.id}'
|
|
}
|
|
)
|
|
.joins(
|
|
%Q{
|
|
LEFT OUTER JOIN
|
|
invitations
|
|
ON
|
|
invitations.music_session_id = active_music_sessions.id
|
|
AND
|
|
invitations.receiver_id = '#{current_user.id}'
|
|
}
|
|
)
|
|
.group(
|
|
%Q{
|
|
active_music_sessions.id
|
|
}
|
|
)
|
|
.order(
|
|
%Q{
|
|
SUM(CASE WHEN invitations.id IS NULL THEN 0 ELSE 1 END) DESC,
|
|
SUM(CASE WHEN friendships.user_id IS NULL THEN 0 ELSE 1 END) DESC,
|
|
active_music_sessions.created_at DESC
|
|
}
|
|
)
|
|
|
|
if as_musician
|
|
query = query.where(
|
|
%Q{
|
|
musician_access = true
|
|
OR
|
|
invitations.id IS NOT NULL
|
|
}
|
|
)
|
|
else
|
|
# if you are trying to join the session as a fan/listener,
|
|
# we have to have a mount, fan_access has to be true, and we have to allow for the reload of icecast to have taken effect
|
|
query = query.joins('INNER JOIN icecast_mounts ON icecast_mounts.music_session_id = active_music_sessions.id INNER JOIN icecast_servers ON icecast_mounts.icecast_server_id = icecast_servers.id')
|
|
query = query.where('music_sessions.fan_access = true')
|
|
query = query.where("(active_music_sessions.created_at < icecast_servers.config_updated_at)")
|
|
end
|
|
|
|
query = query.where("music_sessions.description like '%#{keyword}%'") unless keyword.nil?
|
|
query = query.where("connections.user_id" => participants.split(',')) unless participants.nil?
|
|
query = query.where("music_sessions.genre_id in (?)", genres) unless genres.nil?
|
|
|
|
if my_bands_only
|
|
query = query.joins(
|
|
%Q{
|
|
LEFT OUTER JOIN
|
|
bands_musicians
|
|
ON
|
|
bands_musicians.user_id = '#{current_user.id}'
|
|
}
|
|
)
|
|
end
|
|
|
|
if my_bands_only || friends_only
|
|
query = query.where(
|
|
%Q{
|
|
#{friends_only ? "friendships.user_id IS NOT NULL" : "false"}
|
|
OR
|
|
#{my_bands_only ? "bands_musicians.band_id = music_sessions.band_id" : "false"}
|
|
}
|
|
)
|
|
end
|
|
|
|
return query
|
|
end
|
|
|
|
|
|
# all sessions that are private and active, yet I can see
|
|
def self.friend_active_index(user, options)
|
|
|
|
session_id = options[:session_id]
|
|
genre = options[:genre]
|
|
lang = options[:lang]
|
|
keyword = options[:keyword]
|
|
offset = options[:offset]
|
|
limit = options[:limit]
|
|
|
|
query = MusicSession.select('music_sessions.*')
|
|
|
|
query = query.joins("INNER JOIN active_music_sessions ON music_sessions.id = active_music_sessions.id")
|
|
|
|
# one flaw in the join below is that we only consider if the creator of the session has asked you to be your friend, but you have not accepted. While this means you may always see a session by someone you haven't friended, the goal is to match up new users more quickly
|
|
query = query.joins(
|
|
%Q{
|
|
|
|
LEFT OUTER JOIN
|
|
rsvp_requests
|
|
ON rsvp_requests.music_session_id = music_sessions.id and rsvp_requests.user_id = '#{user.id}' AND rsvp_requests.chosen = true
|
|
|
|
LEFT OUTER JOIN
|
|
invitations
|
|
ON
|
|
active_music_sessions.id = invitations.music_session_id AND invitations.receiver_id = '#{user.id}'
|
|
LEFT OUTER JOIN
|
|
friendships
|
|
ON
|
|
active_music_sessions.user_id = friendships.user_id AND friendships.friend_id = '#{user.id}'
|
|
LEFT OUTER JOIN
|
|
friendships as friendships_2
|
|
ON
|
|
active_music_sessions.user_id = friendships_2.friend_id AND friendships_2.user_id = '#{user.id}'
|
|
}
|
|
)
|
|
|
|
# keep only rsvp/invitation/friend results. Nice tailored active list now!
|
|
query = query.where("rsvp_requests.id IS NOT NULL OR invitations.id IS NOT NULL or active_music_sessions.user_id = '#{user.id}' OR (friendships.id IS NOT NULL AND friendships_2.id IS NOT NULL)")
|
|
|
|
# if not specified, default offset to 0
|
|
offset ||= 0
|
|
offset = offset.to_i
|
|
# if not specified, default limit to 20
|
|
limit ||= 20
|
|
limit = limit.to_i
|
|
|
|
query = query.offset(offset)
|
|
query = query.limit(limit)
|
|
query = query.where("music_sessions.genre_id = ?", genre) unless genre.blank?
|
|
query = query.where('music_sessions.language = ?', lang) unless lang.blank?
|
|
query = query.where('music_sessions.id = ?', session_id) unless session_id.blank?
|
|
query = query.where("(description_tsv @@ to_tsquery('jamenglish', ?))", ActiveRecord::Base.connection.quote(keyword) + ':*') unless keyword.blank?
|
|
query = query.group("music_sessions.id")
|
|
query
|
|
end
|
|
|
|
# all sessions that are private and active, yet I can see
|
|
def self.public_index(user, options)
|
|
|
|
session_id = options[:session_id]
|
|
genre = options[:genre]
|
|
lang = options[:lang]
|
|
keyword = options[:keyword]
|
|
offset = options[:offset]
|
|
limit = options[:limit]
|
|
|
|
query = MusicSession.select('music_sessions.*')
|
|
|
|
query = query.joins("INNER JOIN active_music_sessions ON music_sessions.id = active_music_sessions.id")
|
|
|
|
query = query.where("musician_access = TRUE")
|
|
|
|
|
|
# if not specified, default offset to 0
|
|
offset ||= 0
|
|
offset = offset.to_i
|
|
# if not specified, default limit to 20
|
|
limit ||= 20
|
|
limit = limit.to_i
|
|
|
|
query = query.offset(offset)
|
|
query = query.limit(limit)
|
|
query = query.where("music_sessions.genre_id = ?", genre) unless genre.blank?
|
|
query = query.where('music_sessions.language = ?', lang) unless lang.blank?
|
|
query = query.where('music_sessions.id = ?', session_id) unless session_id.blank?
|
|
query = query.where("(description_tsv @@ to_tsquery('jamenglish', ?))", ActiveRecord::Base.connection.quote(keyword) + ':*') unless keyword.blank?
|
|
query = query.group("music_sessions.id")
|
|
query
|
|
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.
|
|
# note, this is mostly the same as above but includes paging through the result and and scores.
|
|
# thus it needs the client_id...
|
|
def self.nindex(current_user, options = {})
|
|
client_id = options[:client_id]
|
|
participants = options[:participants]
|
|
genres = options[:genres]
|
|
keyword = options[:keyword]
|
|
friends_only = options[:friends_only].nil? ? false : options[:friends_only]
|
|
my_bands_only = options[:my_bands_only].nil? ? false : options[:my_bands_only]
|
|
as_musician = options[:as_musician].nil? ? true : options[:as_musician]
|
|
offset = options[:offset]
|
|
limit = options[:limit]
|
|
|
|
connection = Connection.where(client_id: client_id).first!
|
|
locidispid = connection.locidispid
|
|
|
|
query = ActiveMusicSession
|
|
.select("active_music_sessions.*, max(coalesce(current_scores.score, 1000)) as max_score") # 1000 is higher than the allowed max of 999
|
|
.joins(
|
|
%Q{
|
|
INNER JOIN
|
|
music_sessions
|
|
ON
|
|
active_music_sessions.id = music_sessions.id
|
|
}
|
|
)
|
|
.joins(
|
|
%Q{
|
|
INNER JOIN
|
|
connections
|
|
ON
|
|
active_music_sessions.id = connections.music_session_id
|
|
}
|
|
)
|
|
.joins(
|
|
%Q{
|
|
LEFT OUTER JOIN
|
|
current_scores
|
|
ON
|
|
current_scores.alocidispid = connections.locidispid
|
|
AND
|
|
current_scores.blocidispid = #{locidispid}
|
|
}
|
|
)
|
|
.joins(
|
|
%Q{
|
|
LEFT OUTER JOIN
|
|
friendships
|
|
ON
|
|
connections.user_id = friendships.user_id
|
|
AND
|
|
friendships.friend_id = '#{current_user.id}'
|
|
}
|
|
)
|
|
.joins(
|
|
%Q{
|
|
LEFT OUTER JOIN
|
|
invitations
|
|
ON
|
|
invitations.music_session_id = active_music_sessions.id
|
|
AND
|
|
invitations.receiver_id = '#{current_user.id}'
|
|
}
|
|
)
|
|
.group(
|
|
%Q{
|
|
active_music_sessions.id
|
|
}
|
|
)
|
|
.order(
|
|
%Q{
|
|
SUM(CASE WHEN invitations.id IS NULL THEN 0 ELSE 1 END) DESC,
|
|
SUM(CASE WHEN friendships.user_id IS NULL THEN 0 ELSE 1 END) DESC,
|
|
active_music_sessions.created_at DESC
|
|
}
|
|
)
|
|
|
|
query = query.offset(offset) if offset
|
|
query = query.limit(limit) if limit
|
|
|
|
if as_musician
|
|
query = query.where(
|
|
%Q{
|
|
musician_access = true
|
|
OR
|
|
music_sessions.user_id = '#{current_user.id}'
|
|
OR
|
|
invitations.id IS NOT NULL
|
|
}
|
|
)
|
|
else
|
|
# if you are trying to join the session as a fan/listener,
|
|
# we have to have a mount, fan_access has to be true, and we have to allow for the reload of icecast to have taken effect
|
|
query = query.joins('INNER JOIN icecast_mounts ON icecast_mounts.music_session_id = active_music_sessions.id INNER JOIN icecast_servers ON icecast_mounts.icecast_server_id = icecast_servers.id')
|
|
query = query.where('music_sessions.fan_access = true')
|
|
query = query.where("(active_music_sessions.created_at < icecast_servers.config_updated_at)")
|
|
end
|
|
|
|
query = query.where("music_sessions.description like '%#{keyword}%'") unless keyword.nil?
|
|
query = query.where("connections.user_id" => participants.split(',')) unless participants.nil?
|
|
query = query.where("music_sessions.genre_id in (?)", genres) unless genres.nil?
|
|
|
|
if my_bands_only
|
|
query = query.joins(
|
|
%Q{
|
|
LEFT OUTER JOIN
|
|
bands_musicians
|
|
ON
|
|
bands_musicians.user_id = '#{current_user.id}'
|
|
}
|
|
)
|
|
end
|
|
|
|
if my_bands_only || friends_only
|
|
query = query.where(
|
|
%Q{
|
|
#{friends_only ? "friendships.user_id IS NOT NULL" : "false"}
|
|
OR
|
|
#{my_bands_only ? "bands_musicians.band_id = music_sessions.band_id" : "false"}
|
|
}
|
|
)
|
|
end
|
|
|
|
return query
|
|
end
|
|
|
|
# initialize the two temporary tables we use to drive ams_index and ams_users
|
|
def self.ams_init(current_user, options = {})
|
|
my_locidispid = current_user.last_jam_locidispid
|
|
# 13 is an average audio gear value we use if they have not qualified any gear
|
|
my_audio_latency = current_user.last_jam_audio_latency || 13
|
|
locidispid_expr = my_locidispid ? "#{my_locidispid}::bigint" : '0::bigint'
|
|
|
|
self.connection.execute("select ams_index('#{current_user.id}'::varchar, #{locidispid_expr}, #{my_audio_latency}::integer)").check
|
|
end
|
|
|
|
# Generate a list of music sessions (that are active) filtered by genre, language, keyword, and sorted
|
|
# (and tagged) by rsvp'd (1st), invited (2nd), and musician can join (3rd). within a group tagged the
|
|
# same, sorted by score. date seems irrelevant as these are active sessions. ams_init must be called
|
|
# first.
|
|
def self.ams_query(current_user, options = {})
|
|
session_id = options[:session_id]
|
|
client_id = options[:client_id]
|
|
genre = options[:genre]
|
|
lang = options[:lang]
|
|
keyword = options[:keyword]
|
|
offset = options[:offset]
|
|
limit = options[:limit]
|
|
day = options[:day]
|
|
timezone_offset = options[:timezone_offset]
|
|
|
|
query = MusicSession
|
|
.select('music_sessions.*')
|
|
|
|
# this is not really needed when ams_music_session_tmp is joined
|
|
# unless there is something specific we need out of active_music_sessions
|
|
# query = query.joins(
|
|
# %Q{
|
|
# INNER JOIN
|
|
# active_music_sessions
|
|
# ON
|
|
# active_music_sessions.id = music_sessions.id
|
|
# }
|
|
# )
|
|
# .select('1::integer as tag, 15::integer as latency')
|
|
|
|
# integrate ams_music_session_tmp into the processing
|
|
# then we can join ams_music_session_tmp and not join active_music_sessions
|
|
query = query.joins(
|
|
%Q{
|
|
INNER JOIN
|
|
ams_music_session_tmp
|
|
ON
|
|
ams_music_session_tmp.music_session_id = music_sessions.id
|
|
}
|
|
)
|
|
.select('ams_music_session_tmp.tag, ams_music_session_tmp.latency')
|
|
|
|
query = query.order(
|
|
%Q{
|
|
tag, latency, music_sessions.id
|
|
}
|
|
)
|
|
.group(
|
|
%Q{
|
|
tag, latency, music_sessions.id
|
|
}
|
|
)
|
|
|
|
# if not specified, default offset to 0
|
|
offset ||= 0
|
|
offset = offset.to_i
|
|
# if not specified, default limit to 20
|
|
limit ||= 20
|
|
limit = limit.to_i
|
|
|
|
query = query.offset(offset)
|
|
query = query.limit(limit)
|
|
query = query.where("music_sessions.genre_id = ?", genre) unless genre.blank?
|
|
query = query.where('music_sessions.language = ?', lang) unless lang.blank?
|
|
query = query.where('music_sessions.id = ?', session_id) unless session_id.blank?
|
|
query = query.where("(description_tsv @@ to_tsquery('jamenglish', ?))", ActiveRecord::Base.connection.quote(keyword) + ':*') unless keyword.blank?
|
|
|
|
if !day.blank? && !timezone_offset.blank?
|
|
begin
|
|
day = Date.parse(day)
|
|
next_day = day + 1
|
|
timezone_offset = timezone_offset.to_i
|
|
if timezone_offset > 0
|
|
timezone_offset = "+#{timezone_offset}"
|
|
end
|
|
query = query.where("scheduled_start BETWEEN TIMESTAMP WITH TIME ZONE '#{day} 00:00:00#{timezone_offset}'
|
|
AND TIMESTAMP WITH TIME ZONE '#{next_day} 00:00:00#{timezone_offset}'")
|
|
rescue Exception => e
|
|
# do nothing. bad date probably
|
|
@@log.warn("unable to parse day=#{day}, timezone_offset=#{timezone_offset}, e=#{e}")
|
|
end
|
|
end
|
|
|
|
return query
|
|
end
|
|
|
|
# returns the set of users in a music_sessions and the music_session they are in and their latency.
|
|
# ams_init must be called first.
|
|
# user.audio_latency / 2 , + other_user.audio_latency of them / 2, + network latency /2
|
|
def self.ams_users
|
|
return User.select('users.*, ams_users_tmp.music_session_id, ams_users_tmp.full_score, ams_users_tmp.audio_latency, ams_users_tmp.internet_score')
|
|
.joins(
|
|
%Q{
|
|
INNER JOIN
|
|
ams_users_tmp
|
|
ON
|
|
ams_users_tmp.user_id = users.id
|
|
}
|
|
)
|
|
.order('ams_users_tmp.music_session_id, ams_users_tmp.user_id')
|
|
end
|
|
|
|
# wrap me in a transaction!
|
|
# note that these queries must be actualized before the end of the transaction
|
|
# else the temporary tables created by sms_init will be gone.
|
|
def self.ams_index(current_user, params)
|
|
ActiveMusicSession.ams_init(current_user, params)
|
|
|
|
music_sessions = ActiveMusicSession.ams_query(current_user, params).all
|
|
|
|
music_session_users = ActiveMusicSession.ams_users.all
|
|
|
|
user_scores = {}
|
|
music_session_users.each do |user|
|
|
user_scores[user.id] = {full_score: user.full_score, audio_latency: user.audio_latency, internet_score: user.internet_score}
|
|
end
|
|
|
|
[music_sessions, user_scores]
|
|
end
|
|
|
|
def self.participant_create(user, music_session_id, client_id, as_musician, tracks, audio_latency, client_role = nil, parent_client_id = nil, video_sources=nil)
|
|
music_session = MusicSession.find(music_session_id)
|
|
|
|
# USERS ARE ALREADY IN SESSION
|
|
if music_session.active_music_session
|
|
connection = nil
|
|
active_music_session = music_session.active_music_session
|
|
|
|
ActiveRecord::Base.transaction do
|
|
|
|
active_music_session.with_lock do # VRFS-1297
|
|
active_music_session.tick_track_changes
|
|
# VRFS-3986
|
|
connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency, client_role, parent_client_id, video_sources)
|
|
|
|
if connection.errors.any?
|
|
# rollback the transaction to make sure nothing is disturbed in the database
|
|
raise ActiveRecord::Rollback
|
|
end
|
|
end
|
|
end
|
|
|
|
unless connection.errors.any?
|
|
user.update_progression_field(:first_music_session_at)
|
|
MusicSessionUserHistory.save(music_session_id, user.id, client_id, tracks)
|
|
|
|
if as_musician
|
|
# send to session participants
|
|
Notification.send_session_join(active_music_session, connection, user)
|
|
|
|
# send "musician joined session" notification only if it's not a band session since there will be a "band joined session" notification
|
|
if music_session.band.nil?
|
|
Notification.send_musician_session_join(music_session, user)
|
|
end
|
|
end
|
|
end
|
|
|
|
connection
|
|
|
|
# FIRST USER TO JOIN SESSION
|
|
else
|
|
return_value = nil
|
|
|
|
time = Benchmark.realtime do
|
|
ActiveRecord::Base.transaction do
|
|
|
|
# we need to lock the icecast server in this transaction for writing, to make sure thath IcecastConfigWriter
|
|
# doesn't dumpXML as we are changing the server's configuraion
|
|
icecast_server = IcecastServer.find_best_server_for_user(user) if music_session.fan_access
|
|
icecast_server.lock! if icecast_server
|
|
|
|
music_session.running_recordings.each do |recording|
|
|
recording.stop
|
|
end
|
|
|
|
# check if we are connected to rabbitmq
|
|
active_music_session = ActiveMusicSession.new
|
|
active_music_session.id = music_session.id # copy the .id from music_session to active_music_session
|
|
active_music_session.creator = user
|
|
|
|
if music_session.fan_access
|
|
# create an icecast mount since regular users can listen in to the broadcast
|
|
active_music_session.mount = IcecastMount.build_session_mount(music_session, active_music_session, icecast_server)
|
|
end
|
|
|
|
active_music_session.save
|
|
|
|
unless active_music_session.errors.any?
|
|
music_session.started_at = active_music_session.created_at
|
|
music_session.save(:validate => false)
|
|
|
|
# save session parameters for next session
|
|
User.save_session_settings(user, music_session)
|
|
|
|
# auto-join this user into the newly created session
|
|
as_musician = true
|
|
connection = ConnectionManager.new.join_music_session(user, client_id, active_music_session, as_musician, tracks, audio_latency, client_role, parent_client_id, video_sources)
|
|
|
|
unless connection.errors.any?
|
|
user.update_progression_field(:first_music_session_at)
|
|
MusicSessionUserHistory.save(active_music_session.id, user.id, client_id, tracks)
|
|
|
|
Notification.send_session_join(active_music_session, connection, user)
|
|
|
|
# only send this notification if it's a band session
|
|
unless music_session.band.nil?
|
|
Notification.send_band_session_join(music_session, music_session.band)
|
|
else
|
|
Notification.send_musician_session_join(music_session, user)
|
|
end
|
|
|
|
return_value = connection
|
|
else
|
|
return_value = connection
|
|
# rollback the transaction to make sure nothing is disturbed in the database
|
|
raise ActiveRecord::Rollback
|
|
end
|
|
else
|
|
return_value = active_music_session
|
|
# rollback the transaction to make sure nothing is disturbed in the database
|
|
raise ActiveRecord::Rollback
|
|
end
|
|
end
|
|
end
|
|
|
|
if time > 2
|
|
Logging.logger[self].warn "creating a music session took #{time*1000} milliseconds"
|
|
end
|
|
|
|
return_value
|
|
end
|
|
end
|
|
|
|
# Verifies that the specified user can delete this music session
|
|
def can_delete? user
|
|
# the creator can delete
|
|
self.creator == user
|
|
end
|
|
|
|
def access? user
|
|
music_session.part_of_session? user
|
|
end
|
|
|
|
def most_recent_recording
|
|
recordings.where(:music_session_id => self.id).order('created_at desc').limit(1).first
|
|
end
|
|
|
|
def is_jam_track_open?
|
|
!self.jam_track.nil?
|
|
end
|
|
|
|
def is_backing_track_open?
|
|
self.backing_track_path.present?
|
|
end
|
|
|
|
def is_metronome_open?
|
|
self.metronome_active.present?
|
|
end
|
|
|
|
# is this music session currently recording?
|
|
def is_recording?
|
|
recordings.where(:duration => nil).count > 0
|
|
end
|
|
|
|
def is_playing_recording?
|
|
!self.claimed_recording.nil?
|
|
end
|
|
|
|
def recording
|
|
recordings.where(:duration => nil).first
|
|
end
|
|
|
|
# stops any active recording
|
|
def stop_recording
|
|
current_recording = self.recording
|
|
current_recording.stop unless current_recording.nil?
|
|
end
|
|
|
|
def claimed_recording_start(owner, claimed_recording)
|
|
self.claimed_recording = claimed_recording
|
|
self.claimed_recording_initiator = owner
|
|
self.opening_recording = true
|
|
self.save
|
|
self.opening_recording = false
|
|
end
|
|
|
|
def claimed_recording_stop
|
|
self.claimed_recording = nil
|
|
self.claimed_recording_initiator = nil
|
|
self.save
|
|
end
|
|
|
|
def invitations
|
|
music_session.invitations
|
|
end
|
|
|
|
def invited_musicians
|
|
music_session.invited_musicians
|
|
end
|
|
|
|
def join_requests
|
|
music_session.join_requests
|
|
end
|
|
|
|
def fan_invitations
|
|
music_session.fan_invitations
|
|
end
|
|
|
|
def to_s
|
|
description
|
|
end
|
|
|
|
def is_lesson_member?(user)
|
|
music_session.is_lesson_member?(user)
|
|
end
|
|
|
|
def musician_access
|
|
music_session.musician_access
|
|
end
|
|
|
|
def fan_access
|
|
music_session.fan_access
|
|
end
|
|
|
|
def description
|
|
music_session.description
|
|
end
|
|
|
|
def name
|
|
music_session.name
|
|
end
|
|
|
|
def genre
|
|
music_session.genre
|
|
end
|
|
|
|
def fan_chat
|
|
music_session.fan_chat
|
|
end
|
|
|
|
def band
|
|
music_session.band
|
|
end
|
|
|
|
def approval_required
|
|
music_session.approval_required
|
|
end
|
|
|
|
def music_notations
|
|
music_session.music_notations
|
|
end
|
|
|
|
# Verifies that the specified user can join this music session
|
|
def can_join? user, as_musician
|
|
music_session.can_join? user, as_musician
|
|
end
|
|
|
|
# Verifies that the specified user can see this music session
|
|
def can_see? user
|
|
music_session.can_see? user
|
|
end
|
|
|
|
def tick_track_changes
|
|
self.track_changes_counter += 1
|
|
self.save!(:validate => false)
|
|
end
|
|
|
|
def connected_participant_count
|
|
Connection.where(:music_session_id => self.id,
|
|
:aasm_state => Connection::CONNECT_STATE.to_s,
|
|
:as_musician => true)
|
|
.count
|
|
end
|
|
|
|
def started_session
|
|
raise "active_music_sessions.id must be set by caller" unless self.id
|
|
# associate this active_music_session with the music_session formally
|
|
session = MusicSession.find(self.id)
|
|
session.active_music_session = self
|
|
self.music_session = session # needed because of the Google Analytics below in some test cases
|
|
session.scheduled_start = self.created_at unless session.scheduled_start
|
|
session.save!
|
|
|
|
feed = Feed.find_by_music_session_id(self.id)
|
|
|
|
# this should never be hit since the feed entry is created when the music_session record is created
|
|
if feed.nil?
|
|
feed = Feed.new
|
|
feed.music_session_id = self.id
|
|
end
|
|
|
|
feed.active = true
|
|
feed.save
|
|
|
|
#GoogleAnalyticsEvent.track_session_duration(self)
|
|
#GoogleAnalyticsEvent.track_band_real_session(self)
|
|
end
|
|
|
|
def open_jam_track(user, jam_track)
|
|
self.jam_track = jam_track
|
|
self.jam_track_initiator = user
|
|
self.opening_jam_track = true
|
|
self.save
|
|
self.opening_jam_track = false
|
|
|
|
JamTrackSession.create_session(jam_track, user, self.music_session) if jam_track && user
|
|
#self.tick_track_changes
|
|
end
|
|
|
|
def close_jam_track
|
|
self.jam_track = nil
|
|
self.jam_track_initiator = nil
|
|
self.save
|
|
end
|
|
|
|
# @param backing_track_path is a relative path:
|
|
def open_backing_track(user, backing_track_path)
|
|
self.backing_track_path = backing_track_path
|
|
self.backing_track_initiator = user
|
|
self.opening_backing_track = true
|
|
self.save
|
|
self.opening_backing_track = false
|
|
end
|
|
|
|
def close_backing_track
|
|
self.backing_track_path = nil
|
|
self.backing_track_initiator = nil
|
|
self.save
|
|
end
|
|
|
|
def open_metronome(user)
|
|
self.metronome_active = true
|
|
self.metronome_initiator = user
|
|
self.opening_metronome = true
|
|
self.save
|
|
self.opening_metronome = false
|
|
end
|
|
|
|
def close_metronome
|
|
self.metronome_active = false
|
|
self.metronome_initiator = nil
|
|
self.save
|
|
end
|
|
|
|
def self.sync(session_history)
|
|
music_session = MusicSession.find_by_id(session_history.id)
|
|
|
|
if music_session.nil?
|
|
music_session = MusicSession.new
|
|
music_session.id = session_history.id
|
|
end
|
|
|
|
music_session.user_id = session_history.creator.id
|
|
music_session.band_id = session_history.band.id unless session_history.band.nil?
|
|
session_history.save!
|
|
end
|
|
|
|
def self.stats
|
|
stats = {}
|
|
|
|
result = ActiveMusicSession.select('count(distinct(id)) AS total, count(distinct(jam_track_initiator_id)) as jam_track_count, count(distinct(backing_track_initiator_id)) as backing_track_count, count(distinct(metronome_initiator_id)) as metronome_count, count(distinct(claimed_recording_initiator_id)) as recording_count')[0]
|
|
|
|
stats['count'] = result['total'].to_i
|
|
stats['jam_track_count'] = result['jam_track_count'].to_i
|
|
stats['backing_track_count'] = result['backing_track_count'].to_i
|
|
stats['metronome_count'] = result['metronome_count'].to_i
|
|
stats['recording_count'] = result['recording_count'].to_i
|
|
|
|
stats
|
|
end
|
|
|
|
def lesson_session
|
|
music_session.lesson_session
|
|
end
|
|
end
|
|
end
|