2015-09-22 01:01:39 +00:00
module JamRuby
# describes what users have rights to which tracks
class JamTrackMixdownPackage < ActiveRecord :: Base
include JamRuby :: S3ManagerMixin
@@log = Logging . logger [ JamTrackMixdownPackage ]
# these are used as extensions for the files stored in s3
2015-10-16 19:01:18 +00:00
FILE_TYPE_MP3 = 'mp3'
2015-09-22 01:01:39 +00:00
FILE_TYPE_OGG = 'ogg'
FILE_TYPE_AAC = 'aac'
2015-10-16 19:01:18 +00:00
FILE_TYPES = [ FILE_TYPE_MP3 , FILE_TYPE_OGG , FILE_TYPE_AAC ]
2015-09-22 01:01:39 +00:00
SAMPLE_RATE_44 = 44
SAMPLE_RATE_48 = 48
SAMPLE_RATES = [ SAMPLE_RATE_44 , SAMPLE_RATE_48 ]
ENCRYPT_TYPE_JKZ = 'jkz'
ENCRYPT_TYPES = [ ENCRYPT_TYPE_JKZ , nil ]
default_scope { order ( 'created_at desc' ) }
belongs_to :jam_track_mixdown , class_name : " JamRuby::JamTrackMixdown " , dependent : :destroy
validates :jam_track_mixdown , presence : true
validates :file_type , inclusion : { in : FILE_TYPES }
validates :sample_rate , inclusion : { in : SAMPLE_RATES }
validates :encrypt_type , inclusion : { in : ENCRYPT_TYPES }
validates_uniqueness_of :file_type , scope : [ :sample_rate , :encrypt_type , :jam_track_mixdown_id ]
validates :signing , inclusion : { in : [ true , false ] }
validates :signed , inclusion : { in : [ true , false ] }
validate :verify_download_count
before_destroy :delete_s3_files
after_save :after_save
MAX_JAM_TRACK_DOWNLOADS = 1000
def self . estimated_queue_time
jam_track_signing_count = JamTrackRight . where ( queued : true ) . count
2016-07-17 15:16:27 +00:00
#mixdowns = JamTrackMixdownPackage.unscoped.select('count(CASE WHEN queued THEN 1 ELSE NULL END) as queue_count, count(CASE WHEN speed_pitched THEN 1 ELSE NULL END) as speed_pitch_count').where(queued: true).limit(1)
mixdowns = ActiveRecord :: Base . connection . execute ( " select count(CASE WHEN queued THEN 1 ELSE NULL END) as queue_count, count(CASE WHEN speed_pitched THEN 1 ELSE NULL END) as speed_pitch_count FROM jam_track_mixdown_packages WHERE queued = true " ) [ 0 ]
2015-09-22 01:01:39 +00:00
total_mixdowns = mixdowns [ 'queue_count' ] . to_i
slow_mixdowns = mixdowns [ 'speed_pitch_count' ] . to_i
fast_mixdowns = total_mixdowns - slow_mixdowns
guess = APP_CONFIG . estimated_jam_track_time * jam_track_signing_count + APP_CONFIG . estimated_fast_mixdown_time * fast_mixdowns + APP_CONFIG . estimated_slow_mixdown_time * slow_mixdowns
Stats . write ( 'web.jam_track.queue_time' , { value : guess / 60 . 0 , jam_tracks : jam_track_signing_count , slow_mixdowns : slow_mixdowns , fast_mixdowns : fast_mixdowns } )
guess
end
def after_save
# try to catch major transitions:
# if just queue time changes, start time changes, or signed time changes, send out a notice
if signing_queued_at_was != signing_queued_at || signing_started_at_was != signing_started_at || last_signed_at_was != last_signed_at || current_packaging_step != current_packaging_step_was || packaging_steps != packaging_steps_was
SubscriptionMessage . mixdown_signing_job_change ( self )
end
end
def self . create ( mixdown , file_type , sample_rate , encrypt_type )
package = JamTrackMixdownPackage . new
package . speed_pitched = mixdown . will_pitch_shift?
package . jam_track_mixdown = mixdown
package . file_type = file_type
package . sample_rate = sample_rate
package . signed = false
package . signing = false
package . encrypt_type = encrypt_type
package . save
package
end
def verify_download_count
if ( self . download_count < 0 || self . download_count > MAX_JAM_TRACK_DOWNLOADS ) && ! @current_user . admin
errors . add ( :download_count , " must be less than or equal to #{ MAX_JAM_TRACK_DOWNLOADS } " )
end
end
def is_pitch_speed_shifted?
mix_settings = JSON . parse ( self . settings )
mix_settings [ " speed " ] || mix_settings [ " pitch " ]
end
def finish_errored ( error_reason , error_detail )
self . last_errored_at = Time . now
self . last_signed_at = Time . now
self . error_count = self . error_count + 1
self . error_reason = error_reason
self . error_detail = error_detail
self . should_retry = self . error_count < 5
self . signing = false
self . signing_queued_at = nil # if left set, throws off signing_state on subsequent signing attempts
if save
Notification . send_mixdown_sign_failed ( self )
else
raise " Error sending notification #{ self . errors } "
end
end
def finish_sign ( url , private_key , length , md5 )
self . url = url
self . private_key = private_key
self . signing_queued_at = nil # if left set, throws off signing_state on subsequent signing attempts
self . downloaded_since_sign = false
self . last_signed_at = Time . now
self . length = length
self . md5 = md5
self . signed = true
self . signing = false
self . error_count = 0
self . error_reason = nil
self . error_detail = nil
self . should_retry = false
save!
end
def store_dir
" jam_track_mixdowns/ #{ created_at . strftime ( '%m-%d-%Y' ) } / #{ self . jam_track_mixdown . user_id } "
end
def filename
if encrypt_type
" #{ id } . #{ encrypt_type } "
else
" #{ id } . #{ file_type } "
end
end
# creates a short-lived URL that has access to the object.
# the idea is that this is used when a user who has the rights to this tries to download this JamTrack
# we would verify their rights (can_download?), and generates a URL in response to the click so that they can download
# but the url is short lived enough so that it wouldn't be easily shared
2015-10-16 19:01:18 +00:00
def sign_url ( expiration_time = 120 , content_type = nil , response_content_disposition = nil )
options = { :expires = > expiration_time , :secure = > true }
options [ :response_content_type ] = content_type if content_type
options [ :response_content_disposition ] = response_content_disposition if response_content_disposition
s3_manager . sign_url ( self [ 'url' ] , options )
2015-09-22 01:01:39 +00:00
end
def enqueue
begin
self . signing_queued_at = Time . now
self . signing_started_at = nil
self . last_signed_at = nil
self . queued = true
self . save
queue_time = JamTrackMixdownPackage . estimated_queue_time
# is_pitch_speed_shifted?
Resque . enqueue ( JamTrackMixdownPackager , self . id )
return queue_time
rescue Exception = > e
puts " e: #{ e } "
# implies redis is down. we don't update started_at by bailing out here
false
end
end
# if the job is already signed, just queued up for signing, or currently signing, then don't enqueue... otherwise fire it off
def enqueue_if_needed
state = signing_state
if state == 'SIGNED' || state == 'SIGNING' || state == 'QUEUED'
false
else
return enqueue
end
end
def ready?
self . signed && self . url . present?
end
# returns easy to digest state field
# SIGNED - the package is ready to be downloaded
# ERROR - the package was built unsuccessfully
# SIGNING_TIMEOUT - the package was kicked off to be signed, but it seems to have hung
# SIGNING - the package is currently signing
# QUEUED_TIMEOUT - the package signing job (JamTrackBuilder) was queued, but never executed
# QUEUED - the package is queued to sign
# QUIET - the jam_track_right exists, but no job has been kicked off; a job needs to be enqueued
def signing_state
state = nil
if signed
state = 'SIGNED'
elsif signing_started_at && signing
# the maximum amount of time the packaging job can take is 10 seconds * num steps. For a 10 track song, this will be 110 seconds. It's a bit long.
if Time . now - signing_started_at > APP_CONFIG . signing_job_signing_max_time
state = 'SIGNING_TIMEOUT'
elsif Time . now - last_step_at > APP_CONFIG . mixdown_step_max_time
state = 'SIGNING_TIMEOUT'
else
state = 'SIGNING'
end
elsif signing_queued_at
if Time . now - signing_queued_at > APP_CONFIG . mixdown_job_queue_max_time
state = 'QUEUED_TIMEOUT'
else
state = 'QUEUED'
end
elsif error_count > 0
state = 'ERROR'
else
if Time . now - created_at > 60 # it should not take more than a minute to get QUIET out
state = 'QUIET_TIMEOUT'
else
state = 'QUIET' # needs to be poked to go build
end
end
state
end
def signed?
signed
end
def update_download_count ( count = 1 )
self . download_count = self . download_count + count
self . last_downloaded_at = Time . now
if self . signed
self . downloaded_since_sign = true
end
end
def self . stats
stats = { }
result = JamTrackMixdownPackage . unscoped . select ( 'count(id) as total, count(CASE WHEN signing THEN 1 ELSE NULL END) as signing_count' )
stats [ 'count' ] = result [ 0 ] [ 'total' ] . to_i
stats [ 'signing_count' ] = result [ 0 ] [ 'signing_count' ] . to_i
stats
end
def delete_s3_files
s3_manager . delete ( self . url ) if self . url && s3_manager . exists? ( self . url )
end
end
end