336 lines
11 KiB
Ruby
336 lines
11 KiB
Ruby
module JamRuby
|
|
class Recording < ActiveRecord::Base
|
|
|
|
self.primary_key = 'id'
|
|
|
|
attr_accessible :name, :description, :genre, :is_public, :is_downloadable
|
|
|
|
has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :recording
|
|
has_many :users, :through => :recorded_tracks, :class_name => "JamRuby::User"
|
|
has_many :mixes, :class_name => "JamRuby::Mix", :inverse_of => :recording
|
|
has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :foreign_key => :recording_id
|
|
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::RecordingPlay", :foreign_key => "recording_id"
|
|
|
|
belongs_to :owner, :class_name => "JamRuby::User", :inverse_of => :owned_recordings
|
|
belongs_to :band, :class_name => "JamRuby::Band", :inverse_of => :recordings
|
|
belongs_to :music_session, :class_name => "JamRuby::MusicSession", :inverse_of => :recordings
|
|
|
|
validates :music_session, :presence => true
|
|
validate :not_already_recording, :on => :create
|
|
validate :not_still_finalizing_previous, :on => :create
|
|
validate :not_playback_recording, :on => :create
|
|
validate :already_stopped_recording
|
|
|
|
def comment_count
|
|
self.comments.size
|
|
end
|
|
|
|
def like_count
|
|
self.likes.size
|
|
end
|
|
|
|
def play_count
|
|
self.plays.size
|
|
end
|
|
|
|
def not_already_recording
|
|
if music_session.is_recording?
|
|
errors.add(:music_session, ValidationMessages::ALREADY_BEING_RECORDED)
|
|
end
|
|
end
|
|
|
|
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
|
|
|
|
unless 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
|
|
|
|
def not_playback_recording
|
|
if music_session.is_playing_recording?
|
|
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
|
|
|
|
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
|
|
recording = Recording.new
|
|
recording.music_session = music_session
|
|
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
|
|
end
|
|
end
|
|
end
|
|
|
|
connection = Connection.where(:user_id => owner.id).where(:music_session_id => music_session.id).first
|
|
Notification.send_recording_started(music_session, connection, owner)
|
|
|
|
recording
|
|
end
|
|
|
|
# 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
|
|
end
|
|
|
|
connection = Connection.where(:user_id => self.owner.id).where(:music_session_id => music_session.id).first
|
|
Notification.send_recording_ended(music_session, connection, self.owner)
|
|
|
|
self
|
|
end
|
|
|
|
|
|
# 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, is_downloadable)
|
|
|
|
unless self.users.exists?(user)
|
|
raise PermissionError, "user was not in this session"
|
|
end
|
|
|
|
claimed_recording = ClaimedRecording.new
|
|
claimed_recording.user = user
|
|
claimed_recording.recording = self
|
|
claimed_recording.name = name
|
|
claimed_recording.description = description
|
|
claimed_recording.genre = genre
|
|
claimed_recording.is_public = is_public
|
|
claimed_recording.is_downloadable = is_downloadable
|
|
self.claimed_recordings << claimed_recording
|
|
|
|
if claimed_recording.save
|
|
keep(user)
|
|
end
|
|
|
|
claimed_recording
|
|
end
|
|
|
|
# the user votes to keep their tracks for this recording
|
|
def keep(user)
|
|
recorded_tracks_for_user(user).update_all(:discard => false)
|
|
|
|
User.where(:id => user.id).update_all(:first_recording_at => Time.now ) unless user.first_recording_at
|
|
end
|
|
|
|
|
|
# the user votes to discard their tracks for this recording
|
|
def discard(user)
|
|
recorded_tracks_for_user(user).update_all(:discard => true)
|
|
end
|
|
|
|
# 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")
|
|
.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
|
|
|
|
# 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)
|
|
|
|
save
|
|
end
|
|
|
|
=begin
|
|
# This is no longer remotely right.
|
|
def self.search(query, options = { :limit => 10 })
|
|
|
|
# only issue search if at least 2 characters are specified
|
|
if query.nil? || query.length < 2
|
|
return []
|
|
end
|
|
|
|
# create 'anded' statement
|
|
query = Search.create_tsquery(query)
|
|
|
|
if query.nil? || query.length == 0
|
|
return []
|
|
end
|
|
|
|
return Recording.where("description_tsv @@ to_tsquery('jamenglish', ?)", query).limit(options[:limit])
|
|
end
|
|
=end
|
|
|
|
private
|
|
def self.validate_user_is_band_member(user, band)
|
|
unless band.users.exists? user
|
|
raise PermissionError, ValidationMessages::USER_NOT_BAND_MEMBER_VALIDATION_ERROR
|
|
end
|
|
end
|
|
|
|
def self.validate_user_is_creator(user, creator)
|
|
unless user.id == creator.id
|
|
raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
|
|
end
|
|
end
|
|
|
|
def self.validate_user_is_musician(user)
|
|
unless user.musician?
|
|
raise PermissionError, ValidationMessages::USER_NOT_MUSICIAN_VALIDATION_ERROR
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|