jam-cloud/ruby/lib/jam_ruby/models/recording.rb

372 lines
12 KiB
Ruby
Raw Normal View History

2012-11-16 02:08:37 +00:00
module JamRuby
class Recording < ActiveRecord::Base
2013-04-25 06:50:52 +00:00
2012-11-16 02:08:37 +00:00
self.primary_key = 'id'
attr_accessible :owner, :owner_id, :band, :band_id, :recorded_tracks_attributes, :mixes_attributes, :claimed_recordings_attributes, :name, :description, :genre, :is_public, :duration, as: :admin
has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
has_many :users, :through => :recorded_tracks, :class_name => "JamRuby::User"
has_many :mixes, :class_name => "JamRuby::Mix", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :foreign_key => :recording_id, :dependent => :destroy
has_many :comments, :class_name => "JamRuby::RecordingComment", :foreign_key => "recording_id"
has_many :likes, :class_name => "JamRuby::RecordingLiker", :foreign_key => "recording_id"
has_many :plays, :class_name => "JamRuby::PlayablePlay", :as => :playable, :dependent => :destroy
2014-02-17 02:52:00 +00:00
has_one :feed, :class_name => "JamRuby::Feed", :inverse_of => :recording, :foreign_key => 'recording_id', :dependent => :destroy
belongs_to :owner, :class_name => "JamRuby::User", :inverse_of => :owned_recordings, :foreign_key => 'owner_id'
2013-01-30 05:46:40 +00:00
belongs_to :band, :class_name => "JamRuby::Band", :inverse_of => :recordings
belongs_to :music_session, :class_name => "JamRuby::MusicSession", :inverse_of => :recordings
accepts_nested_attributes_for :recorded_tracks, :mixes, :claimed_recordings, allow_destroy: true
validate :not_already_recording, :on => :create
validate :not_still_finalizing_previous, :on => :create
2014-01-05 03:47:23 +00:00
validate :not_playback_recording, :on => :create
validate :already_stopped_recording
validate :only_one_mix
before_save :sanitize_active_admin
before_create :add_to_feed
def add_to_feed
feed = Feed.new
feed.recording = self
end
def sanitize_active_admin
self.owner_id = nil if self.owner_id == ''
self.band_id = nil if self.band_id == ''
end
2013-01-30 05:46:40 +00:00
def comment_count
self.comments.size
end
def has_mix?
self.mixes.length > 0 && self.mixes.first.completed
end
# this can probably be done more efficiently, but David needs this asap for a video
def grouped_tracks
tracks = []
sorted_tracks = self.recorded_tracks.sort { |a,b| a.user.id <=> b.user.id }
t = Track.new
t.instrument_ids = []
sorted_tracks.each_with_index do |track, index|
if index > 0
if sorted_tracks[index-1].user.id != sorted_tracks[index].user.id
t = Track.new
t.instrument_ids = []
t.instrument_ids << track.instrument.id
t.musician = track.user
tracks << t
else
if !t.instrument_ids.include? track.instrument.id
t.instrument_ids << track.instrument.id
end
end
else
t.musician = track.user
t.instrument_ids << track.instrument.id
tracks << t
end
end
tracks
end
def not_already_recording
2014-02-11 15:53:20 +00:00
if music_session && music_session.is_recording?
errors.add(:music_session, ValidationMessages::ALREADY_BEING_RECORDED)
end
end
2013-01-22 19:15:52 +00:00
def not_still_finalizing_previous
# after a recording is done, users need to keep or discard it.
# this checks if the previous recording is still being finalized
2014-02-11 15:53:20 +00:00
unless !music_session || music_session.is_recording?
previous_recording = music_session.most_recent_recording
if previous_recording
previous_recording.recorded_tracks.each do |recorded_track|
# if at least one user hasn't taken any action yet...
if recorded_track.discard.nil?
# and they are still around and in this music session still...
connection = Connection.find_by_client_id(recorded_track.client_id)
if !connection.nil? && connection.music_session == music_session
errors.add(:music_session, ValidationMessages::PREVIOUS_RECORDING_STILL_BEING_FINALIZED)
break
end
end
end
end
end
end
2014-01-05 03:47:23 +00:00
def not_playback_recording
2014-02-11 15:53:20 +00:00
if music_session && music_session.is_playing_recording?
2014-01-05 03:47:23 +00:00
errors.add(:music_session, ValidationMessages::ALREADY_PLAYBACK_RECORDING)
end
end
def already_stopped_recording
if is_done && is_done_was
errors.add(:music_session, ValidationMessages::NO_LONGER_RECORDING)
end
end
2013-01-24 01:48:13 +00:00
def only_one_mix
# we leave mixes as has_many because VRFS-1089 was very hard to do with has_one + cocoon add/remove
if mixes.length > 1
errors.add(:mixes, ValidationMessages::ONLY_ONE_MIX)
end
end
def recorded_tracks_for_user(user)
unless self.users.exists?(user)
raise PermissionError, "user was not in this session"
end
recorded_tracks.where(:user_id=> user.id)
end
def has_access?(user)
return users.exists?(user)
end
# Start recording a session.
def self.start(music_session, owner)
recording = nil
# Use a transaction and lock to avoid races.
music_session.with_lock do
2013-01-24 01:48:13 +00:00
recording = Recording.new
recording.music_session = music_session
2013-01-30 05:46:40 +00:00
recording.owner = owner
recording.band = music_session.band
if recording.save
music_session.connections.each do |connection|
connection.tracks.each do |track|
recording.recorded_tracks << RecordedTrack.create_from_track(track, recording)
end
2013-01-24 01:48:13 +00:00
end
2013-01-22 19:15:52 +00:00
end
2013-01-22 22:03:23 +00:00
end
2013-01-30 17:57:12 +00:00
2013-01-30 05:46:40 +00:00
recording
2013-01-22 19:15:52 +00:00
end
2013-01-24 01:48:13 +00:00
# Stop recording a session
def stop
# Use a transaction and lock to avoid races.
music_session = MusicSession.find_by_id(music_session_id)
locker = music_session.nil? ? self : music_session
locker.with_lock do
self.duration = Time.now - created_at
self.is_done = true
self.save
2013-01-24 01:48:13 +00:00
end
2014-01-02 19:57:16 +00:00
self
2013-01-24 01:48:13 +00:00
end
2013-04-25 06:50:52 +00:00
2013-04-25 06:50:52 +00:00
# Called when a user wants to "claim" a recording. To do this, the user must have been one of the tracks in the recording.
def claim(user, name, description, genre, is_public)
2013-04-25 06:50:52 +00:00
unless self.users.exists?(user)
2013-04-25 06:50:52 +00:00
raise PermissionError, "user was not in this session"
end
2013-04-25 06:50:52 +00:00
claimed_recording = ClaimedRecording.new
claimed_recording.user = user
claimed_recording.recording = self
claimed_recording.name = name
claimed_recording.description = description
2013-04-25 06:50:52 +00:00
claimed_recording.genre = genre
claimed_recording.is_public = is_public
self.claimed_recordings << claimed_recording
2013-01-22 19:15:52 +00:00
if claimed_recording.save
2014-01-06 21:51:48 +00:00
keep(user)
end
2013-04-25 06:50:52 +00:00
claimed_recording
end
2014-01-06 21:51:48 +00:00
# the user votes to keep their tracks for this recording
def keep(user)
recorded_tracks_for_user(user).update_all(:discard => false)
2014-01-30 21:51:05 +00:00
User.where(:id => user.id).update_all(:first_recording_at => Time.now ) unless user.first_recording_at
2014-01-06 21:51:48 +00:00
end
# the user votes to discard their tracks for this recording
def discard(user)
recorded_tracks_for_user(user).update_all(:discard => true)
2014-02-08 03:09:25 +00:00
# check if all recorded_tracks for this recording are discarded
if recorded_tracks.where('discard = false or discard is NULL').length == 0
self.all_discarded = true
self.save(:validate => false)
end
end
2013-04-25 06:50:52 +00:00
2013-02-05 02:07:08 +00:00
# Find out if all the tracks for this recording have been uploaded
def uploaded?
self.recorded_tracks.each do |recorded_track|
return false unless recorded_track.fully_uploaded
end
return true
end
def self.list_downloads(user, limit = 100, since = 0)
since = 0 unless since || since == '' # guard against nil
downloads = []
# That second join is important. It's saying join off of recordings, NOT user. If you take out the
# ":recordings =>" part, you'll just get the recorded_tracks that I played. Very different!
# we also only allow you to be told about downloads if you have claimed the recording
#User.joins(:recordings).joins(:recordings => :recorded_tracks).joins(:recordings => :claimed_recordings)
RecordedTrack.joins(:recording).joins(:recording => :claimed_recordings)
.order('recorded_tracks.id')
.where('recorded_tracks.fully_uploaded = TRUE')
.where('recorded_tracks.id > ?', since)
.where('claimed_recordings.user_id = ?', user).limit(limit).each do |recorded_track|
downloads.push(
{
:type => "recorded_track",
:id => recorded_track.client_track_id,
:recording_id => recorded_track.recording_id,
:length => recorded_track.length,
:md5 => recorded_track.md5,
:url => recorded_track[:url],
:next => recorded_track.id
}
)
end
latest_recorded_track = downloads[-1][:next] if downloads.length > 0
Mix.joins(:recording).joins(:recording => :claimed_recordings)
.order('mixes.id')
.where('mixes.completed_at IS NOT NULL')
.where('mixes.id > ?', since)
.where('claimed_recordings.user_id = ?', user)
.limit(limit).each do |mix|
downloads.push(
{
:type => "mix",
:id => mix.id.to_s,
:recording_id => mix.recording_id,
:length => mix.ogg_length,
:md5 => mix.ogg_md5,
:url => mix.ogg_url,
:created_at => mix.created_at,
:next => mix.id
}
)
end
latest_mix = downloads[-1][:next] if downloads.length > 0
if !latest_mix.nil? && !latest_recorded_track.nil?
next_date = [latest_mix, latest_recorded_track].max
elsif latest_mix.nil?
next_date = latest_recorded_track
else
next_date = latest_mix
end
if next_date.nil?
next_date = since # echo back to the client the same value they passed in, if there are no results
end
{
'downloads' => downloads,
'next' => next_date.to_s
}
end
def self.list_uploads(user, limit = 100, since = 0)
since = 0 unless since || since == '' # guard against nil
uploads = []
RecordedTrack
.joins(:recording)
.where(:user_id => user.id)
.where(:fully_uploaded => false)
.where('recorded_tracks.id > ?', since)
.where("upload_failures <= #{RecordedTrack::MAX_UPLOAD_FAILURES}")
.where("duration IS NOT NULL")
.where('all_discarded = false')
.order('recorded_tracks.id')
.limit(limit).each do |recorded_track|
uploads.push({
:type => "recorded_track",
:client_track_id => recorded_track.client_track_id,
:recording_id => recorded_track.recording_id,
:next => recorded_track.id
})
end
next_value = uploads.length > 0 ? uploads[-1][:next].to_s : nil
if next_value.nil?
next_value = since # echo back to the client the same value they passed in, if there are no results
end
{
"uploads" => uploads,
"next" => next_value.to_s
}
end
2013-05-06 23:47:20 +00:00
# Check to see if all files have been uploaded. If so, kick off a mix.
def upload_complete
# Don't allow multiple mixes for now.
raise JamRuby::JamArgumentError unless self.mixes.length == 0
# FIXME: There's a possible race condition here. If two users complete
# uploads at the same time, we'll schedule 2 mixes.
recorded_tracks.each do |recorded_track|
return unless recorded_track.fully_uploaded
end
self.mixes << Mix.schedule(self)
2013-05-06 23:47:20 +00:00
save
end
# meant to be used as a way to 'pluck' a claimed_recording appropriate for user.
def candidate_claimed_recording
claimed_recordings.where(is_public: true).first
end
2013-06-30 20:55:03 +00:00
private
def self.validate_user_is_band_member(user, band)
unless band.users.exists? user
raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR
2012-11-22 08:27:23 +00:00
end
2013-06-30 20:55:03 +00:00
end
2012-11-22 08:27:23 +00:00
2013-06-30 20:55:03 +00:00
def self.validate_user_is_creator(user, creator)
unless user.id == creator.id
raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
2012-11-22 08:27:23 +00:00
end
2013-06-30 20:55:03 +00:00
end
2012-11-22 08:27:23 +00:00
2013-06-30 20:55:03 +00:00
def self.validate_user_is_musician(user)
unless user.musician?
raise PermissionError, ValidationMessages::USER_NOT_MUSICIAN_VALIDATION_ERROR
2012-11-22 08:27:23 +00:00
end
2013-06-30 20:55:03 +00:00
end
2012-11-22 08:27:23 +00:00
2013-05-06 23:47:20 +00:00
end
2013-04-25 06:50:52 +00:00
end