module JamRuby class MusicSession < ActiveRecord::Base self.primary_key = 'id' attr_accessor :legal_terms, :skip_genre_validation, :max_score attr_accessible :creator, :description, :musician_access, :approval_required, :fan_chat, :fan_access, :genres belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_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" has_one :music_session_history, :class_name => "JamRuby::MusicSessionHistory" 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" has_many :recordings, :class_name => "JamRuby::Recording", :inverse_of => :music_session has_many :chats, :class_name => "JamRuby::ChatMessages", :foreign_key => "session_id" belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id" after_create :started_session validate :require_at_least_one_genre, :limit_max_genres after_save :sync_music_session_history after_destroy do |obj| JamRuby::MusicSessionHistory.removed_music_session(obj.id) end validates :description, :presence => true, :no_profanity => true validates :fan_chat, :inclusion => {:in => [true, false]} validates :fan_access, :inclusion => {:in => [true, false]} validates :approval_required, :inclusion => {:in => [true, false]} validates :musician_access, :inclusion => {:in => [true, false]} validates :legal_terms, :inclusion => {:in => [true]}, :on => :create validates :creator, :presence => true validate :creator_is_musician validate :no_new_playback_while_playing #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 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 no_new_playback_while_playing # if we previous had a claimed recording and are trying to set one # and if also the previous initiator is different than the current one... it's a no go if !claimed_recording_id_was.nil? && !claimed_recording_id.nil? && claimed_recording_initiator_id_was != claimed_recording_initiator_id errors.add(:claimed_recording, 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 = MusicSession .joins( %Q{ INNER JOIN connections ON 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 = music_sessions.id AND invitations.receiver_id = '#{current_user.id}' } ) .group( %Q{ 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, 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 = music_sessions.id INNER JOIN icecast_servers ON icecast_mounts.icecast_server_id = icecast_servers.id') query = query.where(:fan_access => true) query = query.where("(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.joins(:genres).where("genres.id" => genres.split(',')) 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 # 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 = MusicSession .select("music_sessions.*, max(coalesce(current_scores.score, 1000)) as max_score") # 1000 is higher than the allowed max of 999 .joins( %Q{ INNER JOIN connections ON 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 = music_sessions.id AND invitations.receiver_id = '#{current_user.id}' } ) .group( %Q{ 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, music_sessions.created_at DESC } ) if (offset) query = query.offset(offset) end if (limit) query = query.limit(limit) end 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 = music_sessions.id INNER JOIN icecast_servers ON icecast_mounts.icecast_server_id = icecast_servers.id') query = query.where(:fan_access => true) query = query.where("(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.joins(:genres).where("genres.id" => genres.split(',')) 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 # Verifies that the specified user can join this music session def can_join? user, as_musician if as_musician if !user.musician return false # "a fan can not join a music session as a musician" raise PermissionError, "a fan can not join a music session as a musician" end if self.musician_access if self.approval_required return self.invited_musicians.exists?(user) else return true end else # the creator can always join, and the invited users can join return self.creator == user || self.invited_musicians.exists?(user) end else # it's a fan, and the only way a fan can join is if fan_access is true return self.fan_access end end # Verifies that the specified user can see this music session def can_see? user if self.musician_access || self.fan_access return true else return self.creator == user || self.invited_musicians.exists?(user) end end # Verifies that the specified user can delete this music session def can_delete? user # the creator can delete return self.creator == user end def access? user return self.users.exists? user end def most_recent_recording recordings.where(:music_session_id => self.id).order('created_at desc').limit(1).first 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.save end def claimed_recording_stop self.claimed_recording = nil self.claimed_recording_initiator = nil self.save end def to_s description 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 GoogleAnalyticsEvent.track_session_duration(self) GoogleAnalyticsEvent.track_band_real_session(self) end private def require_at_least_one_genre unless skip_genre_validation if self.genres.length < Limits::MIN_GENRES_PER_SESSION errors.add(:genres, ValidationMessages::SESSION_GENRE_MINIMUM_NOT_MET) end end end def limit_max_genres unless skip_genre_validation if self.genres.length > Limits::MAX_GENRES_PER_SESSION errors.add(:genres, ValidationMessages::SESSION_GENRE_LIMIT_EXCEEDED) end end end def sync_music_session_history MusicSessionHistory.save(self) end end end