require 'aws-sdk' class ApiMusicSessionsController < ApiController # have to be signed in currently to see this screen before_filter :api_signed_in_user, :except => [ :add_like, :show, :show_history, :add_session_info_comment ] before_filter :lookup_session, only: [:show, :update, :delete, :claimed_recording_start, :claimed_recording_stop, :track_sync, :jam_track_open, :jam_track_close, :backing_track_open, :backing_track_close, :metronome_open, :metronome_close] skip_before_filter :api_signed_in_user, only: [:perf_upload] respond_to :json def index # returns a list of sessions which are hopefully interesting to you as three buckets (concatenated). The 1st bucket # is those session to which you've been invited. The 2nd bucket is those sessions which are about a band that you # are in or in which a friend is participating. The 3rd bucket is everything else (sessions - invites - friends - bands). # params[:participants] is either nil, meaning "everything", or it's an array of musician ids # params[:genres] is either nil, meaning "everything", or it's an array of genre ids # params[:friends_only] does the obvious. you get invited and friends sessions only. # params[:my_bands_only] also does the obvious. you get invited and bands sessions only. # params[:keyword] matches only sessions with that text in the description # params[:as_musician] only returns sessions you can join as a musician (if true) or listen to a fan (if false) # Importantly, friends_only and my_bands_only are ORed not ANDed. So, if you specify both as true, you'll get more # results than if only one or the other is true, not fewer. if either is true you won't see the "everything else" # sessions. @music_sessions = ActiveMusicSession.index(current_user, participants: params[:participants], genres: params[:genres], friends_only: params[:friends_only], my_bands_only: params[:my_bands_only], keyword: params[:keyword], as_musician: params[:as_musician]) end def nindex # returns a list of sessions which are hopefully interesting to you as three buckets (concatenated). The 1st bucket # is those session to which you've been invited. The 2nd bucket is those sessions which are about a band that you # are in or in which a friend is participating. The 3rd bucket is everything else (sessions - invites - friends - bands). # pretty much the same as #index above, except scores are also returned. # params[:client_id] is the client_id of the client making the call. needed to resovle scoring. # params[:participants] is either nil, meaning "everything", or it's an array of musician ids # params[:genres] is either nil, meaning "everything", or it's an array of genre ids # params[:friends_only] does the obvious. you get invited and friends sessions only. # params[:my_bands_only] also does the obvious. you get invited and bands sessions only. # params[:keyword] matches only sessions with that text in the description # params[:as_musician] only returns sessions you can join as a musician (if true) or listen to a fan (if false) # params[:offset] also does the obvious, starts at offset in the results (e.g. 0) # params[:limit] also does the obvious, limits number of rows returned (e.g. 20) # Importantly, friends_only and my_bands_only are ORed not ANDed. So, if you specify both as true, you'll get more # results than if only one or the other is true, not fewer. if either is true you won't see the "everything else" # sessions. @music_sessions = ActiveMusicSession.nindex(current_user, client_id: params[:client_id], participants: params[:participants], genres: params[:genres], friends_only: params[:friends_only], my_bands_only: params[:my_bands_only], keyword: params[:keyword], as_musician: params[:as_musician], offset: params[:offset], limit: params[:limit]) end def ams_index # returns a relation which will produce a list of music_sessions which are active and augmented with attributes # tag and latency, then sorted by tag, latency, and finally music_sessions.id (for stability). the list is # filtered by genre, lang, and keyword, then paged by offset and limit (those are record numbers not page numbers). # tag is 1 for chosen rsvp'd sessions, 2 for invited sessions, 3 for all others (musician_access). # TODO: if you're the creator of a session it will be treated the same as if you had rsvp'd and been accepted. ActiveRecord::Base.transaction do @music_sessions, @user_scores = ActiveMusicSession.ams_index(current_user, params) end end def sms_index # returns a relation which will produce a list of music_sessions which are scheduled and augmented with attributes # tag and latency, then sorted by tag, latency, and finally music_sessions.id (for stability). the list is # filtered by genre, lang, and keyword, then paged by offset and limit (those are record numbers not page numbers). # tag is 1 for chosen rsvp'd sessions, 2 for invited sessions, 3 for all others (musician_access). # TODO: if you're the creator of a session it will be treated the same as if you had rsvp'd and been accepted. ActiveRecord::Base.transaction do @music_sessions, @user_scores = MusicSession.sms_index(current_user, params) end end def scheduled @music_sessions = MusicSession.scheduled(current_user) end def scheduled_rsvp @music_sessions = MusicSession.scheduled_rsvp(current_user) end def create @music_session = MusicSession.create(current_user, params) if @music_session.errors.any? response.status = :unprocessable_entity respond_with @music_session else respond_with @music_session, responder: ApiResponder end end def create_legacy client_id = params[:client_id] if client_id.nil? raise JamArgumentError, "client_id must be specified" end unless params[:intellectual_property] raise JamArgumentError, "You must agree to the intellectual property terms" end band = Band.find(params[:band]) unless params[:band].nil? # creating the MusicSession right here was added as part of the scheduled sessions changes # Why? The new order of things is to always have a MusicSession before a ActiveMusicSession # So, we have to make MusicSession, and pass in it's .id to MusicSessionManager.new.create() # so that the ActiveMusicSession can have the same .id as the MusicSession history = MusicSession.new history.name = params[:description][0..40] history.description = params[:description] history.musician_access = params[:musician_access] history.approval_required = params[:approval_required] history.fan_chat = params[:fan_chat] history.fan_access = params[:fan_access] history.band = band history.genre_id = (params[:genres].length > 0 ? params[:genres][0] : nil) if params[:genres] history.legal_terms = params[:legal_terms] history.language = 'eng' history.legal_policy = 'standard' history.creator = current_user history.save if history.errors.any? @music_session = history response.status = :unprocessable_entity respond_with @music_session else history.reload # to get .id back @music_session = MusicSessionManager.new.create( history, current_user, client_id, params[:description], params[:musician_access], params[:approval_required], params[:fan_chat], params[:fan_access], band, params[:genres], params[:tracks], params[:legal_terms], params[:audio_latency]) if @music_session.errors.any? response.status = :unprocessable_entity respond_with @music_session else respond_with @music_session, responder: ApiResponder, :location => api_session_detail_url(@music_session) end end end def show unless @music_session.can_see? current_user raise ActiveRecord::RecordNotFound end end def update @music_session = MusicSessionManager.new.update( @music_session.music_session, params[:name], params[:description], params[:genre] ? Genre.find(params[:genre]) : nil, params[:language], params[:musician_access], params[:approval_required], params[:fan_chat], params[:fan_access]) if @music_session.errors.any? # we have to do this because api_session_detail_url will fail with a bad @music_session response.status = :unprocessable_entity respond_with @music_session else respond_with @music_session, responder: ApiResponder, :location => api_session_detail_url(@music_session) end end def session_update begin @music_session = MusicSession.find(params[:id]) if @music_session.creator == current_user @music_session.name = params[:name] if params.include? :name @music_session.description = params[:description] if params.include? :description @music_session.musician_access = params[:musician_access] if params.include? :musician_access @music_session.approval_required = params[:approval_required] if params.include? :approval_required @music_session.fan_chat = params[:fan_chat] if params.include? :fan_chat @music_session.fan_access = params[:fan_access] if params.include? :fan_access @music_session.genre = Genre.find_by_id(params[:genres][0]) if params.include?(:genres) && params[:genres] && params[:genres].length > 0 @music_session.legal_policy = params[:legal_policy] if params.include? :legal_policy @music_session.language = params[:language] if params.include? :language @music_session.scheduled_start = MusicSession.parse_scheduled_start(params[:start], params[:timezone]) if params.include?(:start) && params.include?(:timezone) @music_session.scheduled_duration = params[:duration] + ' minutes' if params.include? :duration @music_session.timezone = params[:timezone] if params.include? :timezone @music_session.recurring_mode = params[:reoccurrence] if params.include? :reoccurrence @music_session.open_rsvps = params[:open_rsvps] if params.include? :open_rsvps @music_session.band = (params[:band] ? Band.find(params[:band]) : nil) if params.include? :band @music_session.save if params.include? :music_notations notations = JSON.parse(params[:music_notations]) notations.each do |n| notation = MusicNotation.find(n["id"]) notation.music_session = @music_session notation.save @music_session.music_notations << notation end end if @music_session.errors.any? response.status = :unprocessable_entity respond_with @music_session else Notification.send_scheduled_session_rescheduled(@music_session) if @music_session.scheduling_info_changed respond_with @music_session, responder: ApiResponder, :location => api_session_history_detail_url(@music_session) end else render :json => { :message => ValidationMessages::PERMISSION_VALIDATION_ERROR }, :status => 403 end rescue ActiveRecord::RecordNotFound render :json => { :message => ValidationMessages::SESSION_NOT_FOUND }, :status => 404 end end def destroy begin music_session = MusicSession.find(params[:id]) if music_session.can_cancel? current_user Notification.send_scheduled_session_cancelled music_session music_session.canceled = true music_session.save respond_with responder: ApiResponder, :status => 204 else render :json => { :message => ValidationMessages::PERMISSION_VALIDATION_ERROR }, :status => 403 end rescue ActiveRecord::RecordNotFound render :json => { :message => ValidationMessages::SESSION_NOT_FOUND }, :status => 404 end end def participant_show @connection = Connection.find_by_client_id(params[:id]) end def participant_create_legacy @connection = MusicSessionManager.new.participant_create( current_user, params[:id], params[:client_id], params[:as_musician], params[:tracks], params[:audio_latency]) if @connection.errors.any? response.status = :unprocessable_entity respond_with @connection else respond_with @connection, responder: ApiResponder, :location => api_session_participant_detail_url(@connection.client_id) end end def participant_create client_id = params[:client_id] if client_id.nil? raise JamArgumentError, "client_id must be specified" end begin @connection = ActiveMusicSession.participant_create( current_user, params[:id], params[:client_id], params[:as_musician], params[:tracks], params[:audio_latency] ) if @connection.errors.any? response.status = :unprocessable_entity respond_with @connection else respond_with @connection, responder: ApiResponder, :location => api_session_participant_detail_url(@connection.client_id) end rescue ActiveRecord::RecordNotFound render :json => { :message => ValidationMessages::SESSION_NOT_FOUND }, :status => 404 end end def participant_delete client_id = params[:id] if client_id.present? && client_id != 'undefined' @connection = Connection.find_by_client_id!(client_id) active_music_session = ActiveMusicSession.find(@connection.music_session_id) MusicSessionManager.new.participant_delete(current_user, @connection, active_music_session) end respond_with @connection, responder: ApiResponder end def participant_rating if @history = MusicSessionUserHistory.latest_history(params[:client_id]) if request.post? @history.add_rating(params[:rating], params[:comment]) @history.save if @history.errors.any? response.status = :unprocessable_entity respond_with @history else if @history.good_rating? && @history.user.first_good_music_session_at.nil? @history.user.first_good_music_session_at = Time.now @history.user.save end render :json => {}, :status => :ok end elsif request.get? render :json => { :should_rate_session => @history.should_rate_session? }, :status => :ok end else render :json => { :message => ValidationMessages::SESSION_NOT_FOUND }, :status => 404 end end def track_index @tracks = Track.index(current_user, params[:id]) end def track_show begin @track = Track.joins(:connection) .where(:connections => {:user_id => "#{current_user.id}"}) .find(params[:track_id]) rescue ActiveRecord::RecordNotFound render :json => { :message => ValidationMessages::TRACK_NOT_FOUND }, :status => 404 end end def track_sync @tracks = MusicSessionManager.new.sync_tracks(@music_session, params[:client_id], params[:tracks], params[:backing_tracks], params[:metronome_open]) unless @tracks.kind_of? Array # we have to do this because api_session_detail_url will fail with a bad @tracks response.status = :unprocessable_entity respond_with @tracks else respond_with @tracks, responder: ApiResponder end end def track_create @track = Track.save(nil, params[:connection_id], params[:instrument_id], params[:sound], params[:client_track_id], params[:client_resource_id]) respond_with @track, responder: ApiResponder, :status => 201, :location => api_session_track_detail_url(@track.connection.music_session, @track) end def track_update begin @track = Track.save(params[:track_id], nil, params[:instrument_id], params[:sound], params[:client_track_id], params[:client_resource_id]) respond_with @track, responder: ApiResponder, :status => 200 rescue ActiveRecord::RecordNotFound render :json => { :message => ValidationMessages::TRACK_NOT_FOUND }, :status => 404 end end def track_destroy begin @track = Track.find(params[:track_id]) unless @track.nil? @track.delete respond_with responder: ApiResponder, :status => 204 end rescue ActiveRecord::RecordNotFound render :json => { :message => ValidationMessages::TRACK_NOT_FOUND }, :status => 404 end end def perf_upload # example of using curl to access this API: # curl -L -T some_file -X PUT http://localhost:3000/api/sessions/[SESSION_ID]/perf.json?client_id=[CLIENT_ID] music_session = MusicSession.find(params[:id]) msuh = MusicSessionUserHistory.find_by_client_id(params[:client_id]) @perfdata = MusicSessionPerfData.new @perfdata.client_id = params[:client_id] @perfdata.music_session = music_session unless @perfdata.save # we have to do this because api_session_detail_url will fail with a bad @music_session response.status = :unprocessable_entity respond_with @perfdata return end if Rails.application.config.storage_type == :fog uri = @perfdata.uri s3 = AWS::S3.new(:access_key_id => Rails.application.config.aws_access_key_id, :secret_access_key => Rails.application.config.aws_secret_access_key) bucket = s3.buckets[SampleApp::Application.config.aws_bucket] expire = Time.now + 20.years read_url = bucket.objects[uri].url_for(:read, :expires => expire, :'response_content_type' => 'text/csv').to_s @perfdata.update_attribute(:uri, read_url) logger.debug("*** client can read url #{read_url}") write_url = bucket.objects[uri].url_for(:write, :expires => expire, :'response_content_type' => 'text/csv').to_s logger.debug("*** client can upload to url #{write_url}") redirect_to write_url else if params[:redirected_back].nil? || !params[:redirected_back] # first time that a client has asked to do a PUT (not redirected back here) redirect_to request.fullpath + '&redirected_back=true' else # we should store it here to aid in development, but we don't have to until someone wants the feature # so... just return 200 render :json => { :id => @perfdata.id }, :status => 200 end end end def add_comment if params[:id].blank? render :json => { :message => "Session ID is required" }, :status => 400 return end if params[:user_id].blank? render :json => { :message => "User ID is required" }, :status => 400 return end if params[:comment].blank? render :json => { :message => "Comment is required" }, :status => 400 return end comment = MusicSessionComment.new comment.music_session_id = params[:id] comment.creator_id = params[:user_id] comment.comment = @@html_encoder.encode(params[:comment]) comment.ip_address = request.remote_ip comment.save if comment.errors.any? render :json => { :message => "Unexpected error occurred" }, :status => 500 return else render :json => {}, :status => 201 return end end def add_session_info_comment if params[:id].blank? render :json => { :message => "Session ID is required" }, :status => 400 return end if params[:comment].blank? render :json => { :message => "Comment is required" }, :status => 400 return end comment = SessionInfoComment.new comment.music_session_id = params[:id] comment.creator_id = current_user.id comment.comment = @@html_encoder.encode(params[:comment]) comment.save if comment.errors.any? render :json => { :message => "Unexpected error occurred" }, :status => 500 return else music_session = MusicSession.find(params[:id]) Notification.send_scheduled_session_comment(music_session, current_user, params[:comment]) render :json => {}, :status => 201 return end end def add_like if params[:id].blank? render :json => { :message => "Session ID is required" }, :status => 400 return end liker = MusicSessionLiker.new liker.music_session_id = params[:id] liker.liker_id = params[:user_id] liker.ip_address = request.remote_ip liker.save if liker.errors.any? render :json => { :message => "Unexpected error occurred" }, :status => 500 return else render :json => {}, :status => 201 return end end def show_history if current_user ActiveRecord::Base.transaction do @history, @user_scores = MusicSession.session_with_scores(current_user, params[:id], params[:includePending]) end else @history = MusicSession.find(params[:id]) end end def claimed_recording_start @music_session.claimed_recording_start(current_user, ClaimedRecording.find(params[:claimed_recording_id])) if @music_session.errors.any? # we have to do this because api_session_detail_url will fail with a bad @music_session response.status = :unprocessable_entity respond_with @music_session else respond_with @music_session, responder: ApiResponder end end def claimed_recording_stop @music_session.claimed_recording_stop if @music_session.errors.any? # we have to do this because api_session_detail_url will fail with a bad @music_session response.status = :unprocessable_entity respond_with @music_session else respond_with @music_session, responder: ApiResponder end end def jam_track_open unless @music_session.users.exists?(current_user) raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end @jam_track = JamTrack.find(params[:jam_track_id]) unless @jam_track.right_for_user(current_user) raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end @music_session.open_jam_track(current_user, @jam_track) respond_with_model(@music_session) end def jam_track_close unless @music_session.users.exists?(current_user) raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end @music_session.close_jam_track respond_with_model(@music_session) end def backing_track_open unless @music_session.users.exists?(current_user) raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end @backing_track_path = params[:backing_track_path] @music_session.open_backing_track(current_user, @backing_track_path) respond_with_model(@music_session) end def backing_track_close unless @music_session.users.exists?(current_user) raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end @music_session.close_backing_track() respond_with_model(@music_session) end def metronome_open unless @music_session.users.exists?(current_user) raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end @music_session.open_metronome(current_user) respond_with_model(@music_session) end def metronome_close unless @music_session.users.exists?(current_user) raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR end @music_session.close_metronome() respond_with_model(@music_session) end private def lookup_session @music_session = ActiveMusicSession.find(params[:id]) end end