diff --git a/admin/app/admin/jam_tracks.rb b/admin/app/admin/jam_tracks.rb
index 0f6d99e08..ed1e746ab 100644
--- a/admin/app/admin/jam_tracks.rb
+++ b/admin/app/admin/jam_tracks.rb
@@ -11,6 +11,18 @@ ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do
form :partial => 'form'
index do
+
+ # default_actions # use this for all view/edit/delete links
+ column "Actions" do |jam_track|
+ links = ''.html_safe
+ clz = "member_link view_link show_tracks"
+ clz += ' expand' if params[:focus_track]==jam_track.id
+ links << link_to("Show Tracks", '#', :class => clz)
+ links << link_to("Update", edit_resource_path(jam_track), :class => "member_link edit_link")
+ links
+ end
+
+
column :id
column :name
column :description
@@ -40,18 +52,35 @@ ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do
column :track_type
column :instrument
column :part
- column :track do |track|
+
+ column "" do |track|
+ if track.position > 1
+ link_to 'Move Up', "jam_tracks/#{track.id}/move_up"
+ end
+ end
+ column "" do |track|
+ if track.position < jam_track.jam_track_tracks.count
+ link_to 'Move Down', "jam_tracks/#{track.id}/move_down"
+ end
+ end
+ column "" do |track|
link_to 'Play', '#'
end
end
end
- # default_actions # use this for all view/edit/delete links
- column "Actions" do |jam_track|
- links = ''.html_safe
- links << link_to("Show Tracks", '#', :class => "member_link view_link show_tracks")
- links << link_to("Update", edit_resource_path(jam_track), :class => "member_link edit_link")
- links
- end
+
+ end
+
+ member_action :move_up, :method => :get do
+ track = JamTrackTrack.where("id=?",params[:id]).first
+ track.move_up
+ redirect_to("/admin/jam_tracks?focus_track=#{track.jam_track_id}", {:notice => "Moved Up."})
+ end
+
+ member_action :move_down, :method => :get do
+ track = JamTrackTrack.where("id=?",params[:id]).first
+ track.move_down
+ redirect_to("/admin/jam_tracks?focus_track=#{track.jam_track_id}", {:notice => "Moved Down."})
end
end
\ No newline at end of file
diff --git a/admin/app/assets/javascripts/jam_track.js b/admin/app/assets/javascripts/jam_track.js
index a8781672a..e1b6852bf 100644
--- a/admin/app/assets/javascripts/jam_track.js
+++ b/admin/app/assets/javascripts/jam_track.js
@@ -1,43 +1,48 @@
+function showTracks(rowJamTrack) {
+ var $jamTrackTracks = rowJamTrack.find("td.jam_track_tracks");
+
+ var name=rowJamTrack.find("td.name").text()
+ var count = $jamTrackTracks.find("table tbody tr").length;
+
+ if (rowJamTrack.next().attr('id') == "jam_track_tracks_detail") {
+ $(this).html("Show Tracks");
+ rowJamTrack.next().remove();
+ } else {
+ $(this).html('Hide Tracks');
+ if (count == 0) {
+ rowJamTrack.after(
+ $("
").html(
+ $(" | ")
+ ).append(
+ $(" | ").html(
+ "No Tracks"
+ )
+ )
+ );
+ }
+ else {
+ rowJamTrack.after(
+ $("
").html(
+ $(" | Tracks in '" + name + "': | ")
+ ).append(
+ $(" | ").html(
+ $jamTrackTracks.html()
+ )
+ )
+ );
+ }
+ }
+}
+
$(document).ready(function() {
$("th.jam_track_tracks").css('display', 'none');
$("td.jam_track_tracks").css('display', 'none');
-
+ showTracks($("a.expand").parents("tr"))
+
$(".show_tracks").click(function(e) {
e.preventDefault();
var $rowJamTrack = $(this).parents('tr');
- var $jamTrackTracks = $($rowJamTrack).find("td.jam_track_tracks");
-
- var count = $jamTrackTracks.find("table tbody tr").length;
-
- if ($rowJamTrack.next().attr('id') == "jam_track_tracks_detail") {
- $(this).html("Show Tracks");
- $rowJamTrack.next().remove();
- }
- else {
- $(this).html('Hide Tracks');
- if (count == 0) {
- $rowJamTrack.after(
- $("
").html(
- $(" | ")
- ).append(
- $(" | ").html(
- "No Tracks"
- )
- )
- );
- }
- else {
- $rowJamTrack.after(
- $("
").html(
- $(" | ")
- ).append(
- $(" | ").html(
- $jamTrackTracks.html()
- )
- )
- );
- }
- }
+ showTracks($rowJamTrack)
})
});
\ No newline at end of file
diff --git a/admin/app/views/admin/jam_tracks/_form.html.haml b/admin/app/views/admin/jam_tracks/_form.html.haml
index 5c2028673..fadc93b32 100644
--- a/admin/app/views/admin/jam_tracks/_form.html.haml
+++ b/admin/app/views/admin/jam_tracks/_form.html.haml
@@ -2,25 +2,26 @@
= f.semantic_errors *f.object.errors.keys
= f.inputs name: 'JamTrack fields' do
- = f.input :name
- = f.input :description
+ = f.input :name, :input_html => { :rows=>1, :maxlength=>200 }
+ = f.input :description, :input_html => { :rows=>5, :maxlength=>1000 }
= f.input :bpm
- = f.input :time_signature, collection: JamRuby::JamTrack::TIME_SIGNATURES
- = f.input :status, collection: JamRuby::JamTrack::STATUS
- = f.input :recording_type, collection: JamRuby::JamTrack::RECORDING_TYPE
- = f.input :original_artist
- = f.input :songwriter
- = f.input :publisher
- = f.input :licensor, collection: JamRuby::JamTrackLicensor.all
- = f.input :pro, collection: JamRuby::JamTrack::PRO
- = f.input :genre, collection: JamRuby::Genre.all
- = f.input :sales_region, collection: JamRuby::JamTrack::SALES_REGION
- = f.input :price
+ = f.input :time_signature, collection: JamRuby::JamTrack::TIME_SIGNATURES, include_blank: false
+ = f.input :status, collection: JamRuby::JamTrack::STATUS, include_blank: false
+ = f.input :recording_type, collection: JamRuby::JamTrack::RECORDING_TYPE, include_blank: false
+ = f.input :original_artist, :input_html => { :rows=>2, :maxlength=>200 }
+ = f.input :songwriter, :input_html => { :rows=>5, :maxlength=>1000 }
+ = f.input :publisher, :input_html => { :rows=>5, :maxlength=>1000 }
+ = f.input :licensor, collection: JamRuby::JamTrackLicensor.all, include_blank: false
+ = f.input :pro, collection: JamRuby::JamTrack::PRO, include_blank: false
+ = f.input :genre, collection: JamRuby::Genre.all, include_blank: false
+ = f.input :sales_region, collection: JamRuby::JamTrack::SALES_REGION, include_blank: false
+ = f.input :price, :required=>true, :input_html=>{type:'numeric'}
= f.input :reproduction_royalty, :label => 'Reproduction Royalty'
= f.input :public_performance_royalty, :label => 'Public Performance Royalty'
- = f.input :reproduction_royalty_amount
- = f.input :licensor_royalty_amount
- = f.input :pro_royalty_amount
+ = f.input :reproduction_royalty_amount, :required=>true, :input_html=>{type:'numeric'}
+ = f.input :licensor_royalty_amount, :required=>true, :input_html=>{type:'numeric'}
+ = f.input :pro_royalty_amount, :required=>true, :input_html=>{type:'numeric'}
+ = f.input :plan_code, :label=>'Recurly Plan Code', :required=>true
= f.input :url, :as => :file, :label => 'Audio File'
= f.semantic_fields_for :jam_track_tracks do |track|
diff --git a/admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.haml b/admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.haml
index 11200771b..a388e0f8e 100644
--- a/admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.haml
+++ b/admin/app/views/admin/jam_tracks/_jam_track_track_fields.html.haml
@@ -1,9 +1,10 @@
= f.inputs name: 'Track fields' do
%ol.nested-fields
- = f.input :track_type, :as => :select, collection: JamRuby::JamTrackTrack::TRACK_TYPE
- = f.input :instrument, collection: Instrument.all
- = f.input :part
+ = f.input :track_type, :as => :select, collection: JamRuby::JamTrackTrack::TRACK_TYPE, include_blank: false
+ = f.input :instrument, collection: Instrument.all, include_blank: false
+ = f.input :part, :required=>true, :input_html => { :rows=>1, :maxlength=>20, :type=>'numeric' }
+
= f.input :position
- if f.object.new_record?
diff --git a/admin/config/environments/production.rb b/admin/config/environments/production.rb
index a071fa961..981c09d7c 100644
--- a/admin/config/environments/production.rb
+++ b/admin/config/environments/production.rb
@@ -19,7 +19,7 @@ JamAdmin::Application.configure do
# Generate digests for assets URLs
config.assets.digest = true
-
+
# Defaults to nil and saved in location specified by config.assets.prefix
# config.assets.manifest = YOUR_PATH
@@ -73,4 +73,5 @@ JamAdmin::Application.configure do
config.aws_bucket_public = 'jamkazam-public'
config.aws_bucket = 'jamkazam'
+
end
diff --git a/db/manifest b/db/manifest
index 4777a1dc8..7337e35b6 100755
--- a/db/manifest
+++ b/db/manifest
@@ -233,4 +233,6 @@ add_track_resource_id.sql
user_genres.sql
user_online.sql
icecast_source_changes.sql
-diagnostics_user_id_index.sql
\ No newline at end of file
+diagnostics_user_id_index.sql
+jam_track_updates.sql
+private_key_in_jam_track_rights.sql
diff --git a/db/up/jam_track_updates.sql b/db/up/jam_track_updates.sql
new file mode 100644
index 000000000..3ee39e190
--- /dev/null
+++ b/db/up/jam_track_updates.sql
@@ -0,0 +1,44 @@
+-- Drop Jam Track Tracks constraints:
+ALTER TABLE jam_track_tracks
+ DROP CONSTRAINT jam_track_tracks_jam_track_id_fkey;
+
+-- Drop Jam Track Tracks constraints:
+ALTER TABLE jam_track_rights
+ DROP CONSTRAINT jam_track_rights_user_id_fkey,
+ DROP CONSTRAINT jam_track_rights_jam_track_id_fkey;
+
+-- Change Jam Tracks ID type to BIGINT so it can work like the other downloadable items:
+CREATE SEQUENCE jam_tracks_next_seq;
+ALTER TABLE jam_tracks
+ ALTER COLUMN id DROP DEFAULT,
+ ALTER COLUMN id TYPE BIGINT USING nextval('jam_tracks_next_seq'),
+ ALTER COLUMN id SET DEFAULT nextval('jam_tracks_next_seq');
+
+-- Change referencing ID type and re-add constraints:
+ALTER TABLE jam_track_tracks
+ ALTER COLUMN jam_track_id TYPE BIGINT USING 0,
+ ALTER COLUMN jam_track_id SET NOT NULL,
+ ADD CONSTRAINT jam_track_tracks_jam_track_id_fkey FOREIGN KEY(jam_track_id) REFERENCES jam_tracks(id) ON DELETE CASCADE;
+
+-- Change referencing ID type and re-add constraints. Also
+-- add S3 URL for user-specific downloads:
+ALTER TABLE jam_track_rights
+ ALTER COLUMN id DROP DEFAULT,
+ ALTER COLUMN id TYPE BIGINT USING nextval('tracks_next_tracker_seq'),
+ ALTER COLUMN id SET DEFAULT nextval('tracks_next_tracker_seq'),
+ ADD COLUMN url VARCHAR(2048),
+ ADD COLUMN md5 VARCHAR,
+ ADD COLUMN length INTEGER NOT NULL DEFAULT 0,
+ ADD COLUMN download_count INTEGER NOT NULL DEFAULT 0,
+ ADD COLUMN signed BOOLEAN NOT NULL DEFAULT FALSE,
+ ADD COLUMN downloaded_since_sign BOOLEAN NOT NULL DEFAULT FALSE,
+ ADD COLUMN last_signed_at timestamp without time zone NULL,
+ ADD COLUMN last_downloaded_at timestamp without time zone NULL,
+ ADD COLUMN created_at timestamp without time zone NOT NULL,
+ ADD COLUMN updated_at timestamp without time zone NOT NULL,
+ ALTER COLUMN jam_track_id TYPE BIGINT USING 0,
+ ALTER COLUMN jam_track_id SET NOT NULL,
+ ADD CONSTRAINT jam_track_rights_user_id_fkey FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
+ ADD CONSTRAINT jam_track_rights_jam_track_id_fkey FOREIGN KEY(jam_track_id) REFERENCES jam_tracks(id) ON DELETE CASCADE;
+
+ALTER TABLE notifications ADD COLUMN jam_track_right_id BIGINT REFERENCES jam_track_rights(id);
\ No newline at end of file
diff --git a/db/up/private_key_in_jam_track_rights.sql b/db/up/private_key_in_jam_track_rights.sql
new file mode 100644
index 000000000..6bd4ff223
--- /dev/null
+++ b/db/up/private_key_in_jam_track_rights.sql
@@ -0,0 +1 @@
+ALTER TABLE jam_track_rights ADD COLUMN private_key VARCHAR;
\ No newline at end of file
diff --git a/pb/src/client_container.proto b/pb/src/client_container.proto
index b339465c7..252e9166b 100644
--- a/pb/src/client_container.proto
+++ b/pb/src/client_container.proto
@@ -78,6 +78,9 @@ message ClientMessage {
SOURCE_UP = 252;
SOURCE_DOWN = 253;
+ // jamtracks notifications
+ JAM_TRACK_SIGN_COMPLETE = 260;
+
TEST_SESSION_MESSAGE = 295;
PING_REQUEST = 300;
@@ -180,6 +183,9 @@ message ClientMessage {
optional SourceUp source_up = 252;
optional SourceDown source_down = 253;
+ // jamtracks notification
+ optional JamTrackSignComplete jam_track_sign_complete=260;
+
// Client-Session messages (to/from)
optional TestSessionMessage test_session_message = 295;
@@ -594,6 +600,11 @@ message SourceDown {
optional string music_session = 1; // music session id
}
+
+message JamTrackSignComplete {
+ required int32 jam_track_right_id = 1; // jam track right id
+}
+
message SubscriptionMessage {
optional string type = 1; // the type of the subscription
optional string id = 2; // data about what to subscribe to, specifically
diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb
index 695dd0434..e26894ac4 100755
--- a/ruby/lib/jam_ruby.rb
+++ b/ruby/lib/jam_ruby.rb
@@ -55,6 +55,8 @@ require "jam_ruby/resque/scheduled/active_music_session_cleaner"
require "jam_ruby/resque/scheduled/score_history_sweeper"
require "jam_ruby/resque/scheduled/scheduled_music_session_cleaner"
require "jam_ruby/resque/scheduled/recordings_cleaner"
+require "jam_ruby/resque/scheduled/jam_tracks_cleaner"
+require "jam_ruby/resque/jam_tracks_builder"
require "jam_ruby/resque/scheduled/stats_maker"
require "jam_ruby/resque/google_analytics_event"
require "jam_ruby/resque/batch_email_job"
@@ -75,6 +77,7 @@ require "jam_ruby/app/uploaders/mix_uploader"
require "jam_ruby/app/uploaders/music_notation_uploader"
require "jam_ruby/app/uploaders/jam_track_uploader"
require "jam_ruby/app/uploaders/jam_track_track_uploader"
+require "jam_ruby/app/uploaders/jam_track_right_uploader"
require "jam_ruby/app/uploaders/max_mind_release_uploader"
require "jam_ruby/lib/desk_multipass"
require "jam_ruby/lib/ip"
@@ -193,6 +196,7 @@ require "jam_ruby/models/jam_company"
require "jam_ruby/models/user_sync"
require "jam_ruby/models/video_source"
require "jam_ruby/models/recorded_video"
+require "jam_ruby/jam_tracks_manager"
include Jampb
diff --git a/ruby/lib/jam_ruby/app/uploaders/jam_track_right_uploader.rb b/ruby/lib/jam_ruby/app/uploaders/jam_track_right_uploader.rb
new file mode 100644
index 000000000..e33494063
--- /dev/null
+++ b/ruby/lib/jam_ruby/app/uploaders/jam_track_right_uploader.rb
@@ -0,0 +1,28 @@
+class JamTrackRightUploader < CarrierWave::Uploader::Base
+ # include CarrierWaveDirect::Uploader
+ include CarrierWave::MimeTypes
+
+ process :set_content_type
+
+ def initialize(*)
+ super
+ JamRuby::UploaderConfiguration.set_aws_private_configuration(self)
+ end
+
+ # Add a white list of extensions which are allowed to be uploaded.
+ def extension_white_list
+ %w(jkz)
+ end
+
+ def store_dir
+ nil
+ end
+
+ def md5
+ @md5 ||= ::Digest::MD5.file(current_path).hexdigest
+ end
+
+ def filename
+ "#{model.store_dir}/#{model.filename}" if model.id
+ end
+end
diff --git a/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb b/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb
index 784d646c4..98e555e3d 100644
--- a/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb
+++ b/ruby/lib/jam_ruby/app/uploaders/jam_track_uploader.rb
@@ -11,7 +11,7 @@ class JamTrackUploader < CarrierWave::Uploader::Base
# Add a white list of extensions which are allowed to be uploaded.
def extension_white_list
- %w(jka)
+ %w(jkz)
end
def store_dir
diff --git a/ruby/lib/jam_ruby/constants/notification_types.rb b/ruby/lib/jam_ruby/constants/notification_types.rb
index 515f56d04..87dbe14e5 100644
--- a/ruby/lib/jam_ruby/constants/notification_types.rb
+++ b/ruby/lib/jam_ruby/constants/notification_types.rb
@@ -45,5 +45,7 @@ module NotificationTypes
# general purpose text message
TEXT_MESSAGE = "TEXT_MESSAGE"
+ # Jam Tracks:
+ JAM_TRACK_SIGN_COMPLETE = "JAM_TRACK_SIGN_COMPLETE"
end
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/jam_tracks_manager.rb b/ruby/lib/jam_ruby/jam_tracks_manager.rb
new file mode 100644
index 000000000..e9518b549
--- /dev/null
+++ b/ruby/lib/jam_ruby/jam_tracks_manager.rb
@@ -0,0 +1,86 @@
+require 'json'
+require 'tempfile'
+require 'open3'
+require 'fileutils'
+require 'open-uri'
+
+module JamRuby
+
+ # Interact with external python tools to create the JKZ
+ class JamTracksManager
+ class << self
+ def save_jam_track_jkz(user, jam_track)
+ jam_track_right = jam_track.right_for_user(user)
+ raise ArgumentError if jam_track_right.nil?
+ save_jam_track_right_jkz(jam_track_right)
+ end
+
+ def save_jam_track_right_jkz(jam_track_right)
+ jam_track = jam_track_right.jam_track
+ #py_root = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "jamtracks"))
+ py_root = APP_CONFIG.jamtracks_dir
+ Dir.mktmpdir do |tmp_dir|
+ jam_file_opts=""
+ jam_track.jam_track_tracks.each do |jam_track_track|
+ nm=jam_track_track.filename
+ nm.gsub!(" ", "_")
+ track_filename = File.join(tmp_dir, nm)
+ track_url = jam_track_track.sign_url
+ copy_url_to_file(track_url, track_filename)
+ copy_url_to_file(track_url, File.join(".", nm))
+ jam_file_opts << " -i '#{track_filename}+#{jam_track_track.part}'"
+ end
+ #puts "LS + " + `ls -la '#{tmp_dir}'`
+
+ sku=jam_track.id
+ title=jam_track.name
+ output_jkz=File.join(tmp_dir, "#{title.parameterize}.jkz")
+ py_file = File.join(py_root, "jkcreate.py")
+ puts "Executing python source in #{py_file}, outputting to #{tmp_dir} (#{output_jkz})"
+
+ # From http://stackoverflow.com/questions/690151/getting-output-of-system-calls-in-ruby/5970819#5970819:
+ cli = "python #{py_file} -D -k #{sku} -p #{tmp_dir}/pkey.pem -s #{tmp_dir}/skey.pem #{jam_file_opts} -o #{output_jkz} -t '#{title}'"
+ Open3.popen3(cli) do |stdin, stdout, stderr, wait_thr|
+ pid = wait_thr.pid
+ exit_status = wait_thr.value
+ err = stderr.read(1000)
+ out = stdout.read(1000)
+ #puts "stdout: #{out}, stderr: #{err}"
+ raise ArgumentError, "Error calling python script: #{err}" if err.present?
+ raise ArgumentError, "Error calling python script: #{out}" if out && (out.index("No track files specified") || out.index("Cannot find file"))
+ jam_track_right[:url]
+
+ #raise ArgumentError, "output_jkz is empty #{output_jkz}" unless File.exists?(output_jkz)
+
+ jam_track_right.url.store!(File.open(output_jkz, "rb"))
+ jam_track_right.signed=true
+ jam_track_right.downloaded_since_sign=false
+ jam_track_right.private_key=File.read("#{tmp_dir}/skey.pem")
+ jam_track_right.save!
+ end
+ end # mktmpdir
+ jam_track_right
+ end # save_jam_track_jkz
+
+ def copy_url_to_file(url, filename)
+ uri = URI(url)
+ open(filename, 'w+b') do |io|
+ Net::HTTP.start(uri.host, uri.port) do |http|
+ request = Net::HTTP::Get.new uri
+ http.request request do |response|
+ response_code = response.code.to_i
+ unless response_code >= 200 && response_code <= 299
+ puts "Response from server was #{response_code} / #{response.message}"
+ raise "bad status code: #{response_code}. body: #{response.body}"
+ end
+ response.read_body do |chunk|
+ io.write chunk
+ end
+ end
+ end
+ end
+ end # copy_url_to_file
+
+ end # self
+ end # class
+end # module
diff --git a/ruby/lib/jam_ruby/lib/em_helper.rb b/ruby/lib/jam_ruby/lib/em_helper.rb
index ba6e582e8..5867424ef 100644
--- a/ruby/lib/jam_ruby/lib/em_helper.rb
+++ b/ruby/lib/jam_ruby/lib/em_helper.rb
@@ -13,11 +13,15 @@ module JamWebEventMachine
@@log = Logging.logger[JamWebEventMachine]
+
+ # THIS WAS USED BY resque jobs needing EventMachine/AMQP, but it's no longer needed. It's useful code though
+
# starts amqp & eventmachine up first.
# and then calls your block.
# After the supplied block is done,
# waits until all EM tasks scheduled in the supplied block are done, or timeout
def self.run_wait_stop(timeout = 30, &blk)
+
JamWebEventMachine.run
thread = Thread.current
diff --git a/ruby/lib/jam_ruby/lib/stats.rb b/ruby/lib/jam_ruby/lib/stats.rb
index bb73931f5..c51a14ae1 100644
--- a/ruby/lib/jam_ruby/lib/stats.rb
+++ b/ruby/lib/jam_ruby/lib/stats.rb
@@ -1,5 +1,41 @@
+
require 'influxdb'
+# monkey patch InfluxDB client to clear the queue when asked to stop
+module InfluxDB
+ class Client
+ def stop!
+ @queue.clear if @queue
+ @stopped = true
+ end
+ end
+end
+
+module InfluxDB
+ class Worker
+ def spawn_threads!
+ NUM_WORKER_THREADS.times do |thread_num|
+ log :debug, "Spawning background worker thread #{thread_num}."
+
+ Thread.new do
+ Thread.current[:influxdb] = self.object_id
+
+ at_exit do
+ log :debug, "Thread exiting, bailing out (not flushing queue)"
+ end
+
+ while !client.stopped?
+ self.check_background_queue(thread_num)
+ sleep rand(SLEEP_INTERVAL)
+ end
+ end
+ end
+
+ end
+
+ end
+end
+
module JamRuby
class Stats
@@ -10,6 +46,7 @@ module JamRuby
def self.destroy!
if @client
+ @client.queue.clear if @client.queue
@client.stop!
end
end
diff --git a/ruby/lib/jam_ruby/message_factory.rb b/ruby/lib/jam_ruby/message_factory.rb
index 19be72de7..fab355772 100644
--- a/ruby/lib/jam_ruby/message_factory.rb
+++ b/ruby/lib/jam_ruby/message_factory.rb
@@ -712,6 +712,19 @@ module JamRuby
)
end
+ def jam_track_sign_complete(receiver_id, jam_track_right_id)
+ signed = Jampb::JamTrackSignComplete.new(
+ :jam_track_right_id => jam_track_right_id
+ )
+
+ Jampb::ClientMessage.new(
+ :type => ClientMessage::Type::JAM_TRACK_SIGN_COMPLETE,
+ :route_to => USER_TARGET_PREFIX + receiver_id, #:route_to => CLIENT_TARGET,
+ :jam_track_sign_complete => signed
+ )
+ end
+
+
def recording_master_mix_complete(receiver_id, recording_id, claimed_recording_id, band_id, msg, notification_id, created_at)
recording_master_mix_complete = Jampb::RecordingMasterMixComplete.new(
:recording_id => recording_id,
diff --git a/ruby/lib/jam_ruby/models/connection.rb b/ruby/lib/jam_ruby/models/connection.rb
index 53714d6b8..8a813f9ce 100644
--- a/ruby/lib/jam_ruby/models/connection.rb
+++ b/ruby/lib/jam_ruby/models/connection.rb
@@ -229,11 +229,12 @@ module JamRuby
CLIENT_TYPES.each do |type|
stats[type] = 0
end
- Connection.select('count(client_type) AS client_type_count, client_type') do |result|
- stats[result['client_type']] = result['client_type_count']
+
+ Connection.select('count(client_type) AS client_type_count, client_type').group('client_type').all.each do |result|
+ stats[result['client_type']] = result['client_type_count'].to_i
end
- result = Connection.select('count(id) AS total, count(scoring_timeout) AS scoring_timeout_count, count(music_session_id) AS in_session, count(as_musician) AS musicians, count(udp_reachable) AS udp_reachable_count, count(is_network_testing) AS is_network_testing_count').first
+ result = Connection.select('count(id) AS total, count(CASE WHEN scoring_timeout > NOW() THEN 1 ELSE null END) AS scoring_timeout_count, count(music_session_id) AS in_session, count(as_musician) AS musicians, count(CASE WHEN udp_reachable THEN 1 ELSE null END) AS udp_reachable_count, count(CASE WHEN is_network_testing THEN 1 ELSE null END) AS is_network_testing_count').first
stats['count'] = result['total'].to_i
stats['scoring_timeout'] = result['scoring_timeout_count'].to_i
diff --git a/ruby/lib/jam_ruby/models/jam_track.rb b/ruby/lib/jam_ruby/models/jam_track.rb
index b3f11dbb1..c90281d48 100644
--- a/ruby/lib/jam_ruby/models/jam_track.rb
+++ b/ruby/lib/jam_ruby/models/jam_track.rb
@@ -15,7 +15,7 @@ module JamRuby
attr_accessible :name, :description, :bpm, :time_signature, :status, :recording_type,
:original_artist, :songwriter, :publisher, :licensor, :licensor_id, :pro, :genre, :genre_id, :sales_region, :price,
:reproduction_royalty, :public_performance_royalty, :reproduction_royalty_amount,
- :licensor_royalty_amount, :pro_royalty_amount, :jam_track_tracks_attributes, as: :admin
+ :licensor_royalty_amount, :pro_royalty_amount, :jam_track_tracks_attributes, :plan_code, as: :admin
validates :name, presence: true, uniqueness: true, length: {maximum: 200}
validates :description, length: {maximum: 1000}
@@ -43,19 +43,47 @@ module JamRuby
has_many :jam_track_tracks, :class_name => "JamRuby::JamTrackTrack", order: 'position ASC'
- has_many :jam_track_rights, :class_name => "JamRuby::JamTrackRight", inverse_of: 'jam_track', :foreign_key => "jam_track_id"
+ has_many :jam_track_rights, :class_name => "JamRuby::JamTrackRight" #, inverse_of: 'jam_track', :foreign_key => "jam_track_id"
has_many :owners, :through => :jam_track_rights, :class_name => "JamRuby::User", :source => :user
accepts_nested_attributes_for :jam_track_tracks, allow_destroy: true
+ class << self
+ def index(options = {})
+ limit = options[:limit]
+ limit ||= 20
+ limit = limit.to_i
+
+ start = options[:start].presence
+ start = start.to_i || 0
+
+ query = JamTrack.joins(:jam_track_tracks)
+ .offset(start)
+ .limit(limit)
+
+ query = query.where("jam_tracks.genre_id = '#{options[:genre]}'") unless options[:genre].blank?
+ query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}'") unless options[:instrument].blank?
+ query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?
+ query = query.group("jam_tracks.id")
+
+ if query.length == 0
+ [query, nil]
+ elsif query.length < limit
+ [query, nil]
+ else
+ [query, start + limit]
+ end
+ end
+ end
+
# create storage directory that will house this jam_track, as well as
def store_dir
- "jam_tracks/#{created_at.strftime('%m-%d-%Y')}/#{id}"
+ "jam_tracks/#{id}"
end
# create name of the file
def filename
- "#{name}.jka"
+ "#{name}.jkz"
end
# creates a short-lived URL that has access to the object.
@@ -63,40 +91,49 @@ module JamRuby
# 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
def sign_url(expiration_time = 120)
- s3_manager.sign_url(self[:url], {:expires => expiration_time, :response_content_type => 'audio/jka', :secure => false})
+ s3_manager.sign_url(self[:url], {:expires => expiration_time, :response_content_type => 'audio/jkz', :secure => false})
end
+
def can_download?(user)
owners.include?(user)
end
- def self.index user, options = {}
- limit = options[:limit]
- limit ||= 20
- limit = limit.to_i
+ def right_for_user(user)
+ jam_track_rights.where("user_id=?", user).first
+ end
- start = options[:start].presence
- start = start.to_i || 0
+ def self.list_downloads(user, limit = 100, since = 0)
+ since = 0 unless since || since == '' # guard against nil
+ downloads = []
- query = JamTrack.joins(:jam_track_tracks)
- .offset(start)
- .limit(limit)
-
- query = query.where("jam_tracks.genre_id = '#{options[:genre]}'") unless options[:genre].blank?
- query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}'") unless options[:instrument].blank?
- query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?
- query = query.group("jam_tracks.id")
-
- if query.length == 0
- [query, nil]
- elsif query.length < limit
- [query, nil]
- else
- [query, start + limit]
+ user.jam_track_rights
+ .limit(limit)
+ .where('jam_track_rights.id > ?', since)
+ .each do |jam_track_right|
+ downloads << {
+ :type => "jam_track",
+ :id => jam_track_right.id.to_s,
+ :jam_track_id => jam_track_right.jam_track_id,
+ :length => jam_track_right.length,
+ :md5 => jam_track_right.md5,
+ :url => jam_track_right.url,
+ :created_at => jam_track_right.created_at,
+ :next => jam_track_right.id
+ }
end
+
+ next_id = downloads[-1][:next] if downloads.length > 0
+ next_id = since if next_id.nil? # echo back to the client the same value they passed in, if there are no results
+
+ {
+ 'downloads' => downloads,
+ 'next' => next_id.to_s
+ }
end
- private
+
+ private
def sanitize_active_admin
self.genre_id = nil if self.genre_id == ''
diff --git a/ruby/lib/jam_ruby/models/jam_track_right.rb b/ruby/lib/jam_ruby/models/jam_track_right.rb
index 2a741a3a3..fbd97bc78 100644
--- a/ruby/lib/jam_ruby/models/jam_track_right.rb
+++ b/ruby/lib/jam_ruby/models/jam_track_right.rb
@@ -2,13 +2,94 @@ module JamRuby
# describes what users have rights to which tracks
class JamTrackRight < ActiveRecord::Base
-
+ include JamRuby::S3ManagerMixin
+ attr_accessible :user, :jam_track, :user_id, :jam_track_id, :url, :md5, :length, :download_count
belongs_to :user, class_name: "JamRuby::User" # the owner, or purchaser of the jam_track
belongs_to :jam_track, class_name: "JamRuby::JamTrack"
validates :user, presence:true
validates :jam_track, presence:true
+ validate :verify_download_count
validates_uniqueness_of :user_id, scope: :jam_track_id
+
+ # Uploads the JKZ:
+ mount_uploader :url, JamTrackRightUploader
+ before_destroy :delete_s3_files
+
+ MAX_JAM_TRACK_DOWNLOADS = 1000
+
+ def store_dir
+ "#{jam_track.store_dir}/rights"
+ end
+
+ # create name of the file
+ def filename
+ "#{jam_track.name}.jkz"
+ 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 self.ready_to_clean
+ JamTrackRight.where("downloaded_since_sign=? AND updated_at <= ?", true, 5.minutes.ago).limit(1000)
+ end
+
+ def finish_sign(length, md5)
+ self.last_signed_at = Time.now
+ self.length = length
+ self.md5 = md5
+ self.signed = true
+ if save
+ Notification.send_jam_track_sign_complete(self)
+ else
+ raise "Error sending notification #{self.errors}"
+ 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
+ def sign_url(expiration_time = 120)
+ s3_manager.sign_url(self[:url], {:expires => expiration_time, :secure => false})
+ end
+
+ def delete_s3_files
+ remove_url!
+ end
+
+ def enqueue
+ begin
+ Resque.enqueue(JamTracksBuilder, self.id)
+ rescue Exception => e
+ # implies redis is down. we don't update started_at by bailing out here
+ false
+ end
+
+ # avoid db validations
+ JamTrackRight.where(:id => self.id).update_all(:last_downloaded_at => Time.now)
+
+ true
+ end
+
+ def update_download_count(count=1)
+ self.download_count = self.download_count + count
+ self.last_downloaded_at = Time.now
+ end
+
+ def self.list_keys(user, jamtracks)
+ if jamtracks.nil?
+ return []
+ end
+
+ JamTrack.select('jam_tracks.id, jam_track_rights.private_key AS private_key, jam_track_rights.id AS jam_track_right_id')
+ .joins("LEFT OUTER JOIN jam_track_rights ON jam_tracks.id = jam_track_rights.jam_track_id AND jam_track_rights.user_id = '#{user.id}'")
+ .where('jam_tracks.id IN (?)', jamtracks)
+ end
+
end
end
diff --git a/ruby/lib/jam_ruby/models/jam_track_track.rb b/ruby/lib/jam_ruby/models/jam_track_track.rb
index f9e8ebcb9..978ed29ab 100644
--- a/ruby/lib/jam_ruby/models/jam_track_track.rb
+++ b/ruby/lib/jam_ruby/models/jam_track_track.rb
@@ -9,7 +9,7 @@ module JamRuby
mount_uploader :url, JamTrackTrackUploader
- attr_accessible :track_type, :instrument, :instrument_id, :position, :part, :url, as: :admin
+ attr_accessible :jam_track_id, :track_type, :instrument, :instrument_id, :position, :part, :url, as: :admin
validates :position, presence: true, numericality: {only_integer: true}, length: {in: 1..1000}
validates :part, length: {maximum: 20}
@@ -43,5 +43,52 @@ module JamRuby
# I think we have to make a special case for 'previews', but maybe that's just up to the controller to not check can_download?
jam_track.owners.include?(user)
end
- end
-end
+
+ def move_up
+ #normalize_position
+ if self.position > 1
+ # Switch with previous
+ previous_track = self.jam_track.jam_track_tracks.where("position=?", self.position-1).first
+ if previous_track
+ JamTrack.transaction do
+ previous_track.position,self.position = self.position,previous_track.position
+ previous_track.save(validate:false)
+ self.save(validate:false)
+ end
+ end
+ end
+ end
+
+ def move_down
+ count=normalize_position
+ if self.position < count
+ # Switch with next:
+ next_track = self.jam_track.jam_track_tracks.where("position=?", self.position+1).first
+ if next_track
+ next_track.position,self.position = self.position,next_track.position
+ next_track.save(validate:false)
+ self.save(validate:false)
+ end
+ end
+ end
+
+ private
+ def normalize_position
+ parent = self.jam_track
+ position = 0
+ if parent
+ JamTrack.transaction do
+ parent.jam_track_tracks.each do |jtt|
+ position += 1
+ if jtt.position != position
+ jtt.position = position
+ jtt.save(validate:false)
+ end
+ end
+ end
+ end
+ position
+ end # normalize_position
+
+ end # class
+end # module
diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb
index e1cbea90e..ff4b2e3ea 100644
--- a/ruby/lib/jam_ruby/models/music_session.rb
+++ b/ruby/lib/jam_ruby/models/music_session.rb
@@ -286,12 +286,15 @@ module JamRuby
# keep unstarted sessions around for 12 hours after scheduled_start
session_not_started = "(music_sessions.scheduled_start > NOW() - '12 hour'::INTERVAL AND music_sessions.started_at IS NULL)"
+ # keep started sessions that are not finished yet
+ session_started_not_finished = "(music_sessions.started_at IS NOT NULL AND music_sessions.session_removed_at IS NULL)"
+
# let session be restarted for up to 2 hours after finishing
session_finished = "(music_sessions.session_removed_at > NOW() - '2 hour'::INTERVAL)"
query = MusicSession.where("music_sessions.canceled = FALSE")
query = query.where("music_sessions.user_id = '#{user.id}'")
- query = query.where("music_sessions.scheduled_start IS NULL OR #{session_not_started} OR #{session_finished}")
+ query = query.where("music_sessions.scheduled_start IS NULL OR #{session_not_started} OR #{session_finished} OR #{session_started_not_finished}")
query = query.where("music_sessions.create_type IS NULL OR music_sessions.create_type != '#{CREATE_TYPE_QUICK_START}'")
query = query.order("music_sessions.scheduled_start ASC")
diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb
index 79187c3a2..783f39fd2 100644
--- a/ruby/lib/jam_ruby/models/notification.rb
+++ b/ruby/lib/jam_ruby/models/notification.rb
@@ -13,6 +13,7 @@ module JamRuby
belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id"
belongs_to :music_session, :class_name => "JamRuby::MusicSession", :foreign_key => "music_session_id"
belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id"
+ belongs_to :jam_track_right, :class_name => "JamRuby::JamTrackRight", :foreign_key => "jam_track_right_id"
validates :target_user, :presence => true
validates :message, length: {minimum: 1, maximum: 400}, no_profanity: true, if: :text_message?
@@ -203,6 +204,9 @@ module JamRuby
when NotificationTypes::SCHEDULED_SESSION_COMMENT
return "New message about session."
+ when NotificationTypes::JAM_TRACK_SIGN_COMPLETE
+ return "Jam Track is ready for download."
+
# recording notifications
when NotificationTypes::MUSICIAN_RECORDING_SAVED
return "#{name} has made a new recording."
@@ -1186,6 +1190,19 @@ module JamRuby
end
end
+ def send_jam_track_sign_complete(jam_track_right)
+
+ notification = Notification.new
+ notification.jam_track_right_id = jam_track_right.id
+ notification.description = NotificationTypes::JAM_TRACK_SIGN_COMPLETE
+ notification.target_user_id = jam_track_right.user_id
+ notification.save!
+
+ msg = @@message_factory.jam_track_sign_complete(jam_track_right.user_id, jam_track_right.id)
+ @@mq_router.publish_to_user(jam_track_right.user_id, msg)
+ #@@mq_router.publish_to_all_clients(msg)
+ end
+
def send_client_update(product, version, uri, size)
msg = @@message_factory.client_update( product, version, uri, size)
diff --git a/ruby/lib/jam_ruby/models/shopping_cart.rb b/ruby/lib/jam_ruby/models/shopping_cart.rb
index ccad1b9d4..da1e567f6 100644
--- a/ruby/lib/jam_ruby/models/shopping_cart.rb
+++ b/ruby/lib/jam_ruby/models/shopping_cart.rb
@@ -13,7 +13,7 @@ module JamRuby
def product_info
product = self.cart_product
- {name: product.name, price: product.price} unless product.nil?
+ {name: product.name, price: product.price, product_id: cart_id} unless product.nil?
end
def cart_product
diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb
index 63890f2e7..c35db3dd2 100644
--- a/ruby/lib/jam_ruby/models/user.rb
+++ b/ruby/lib/jam_ruby/models/user.rb
@@ -1477,7 +1477,6 @@ module JamRuby
def self.stats
stats = {}
result = User.select('count(CASE WHEN musician THEN 1 ELSE null END) as musician_count, count(CASE WHEN musician = FALSE THEN 1 ELSE null END) as fan_count, count(first_downloaded_client_at) first_downloaded_client_at_count, count(first_ran_client_at) first_ran_client_at_count, count(first_certified_gear_at) first_certified_gear_at_count, count(first_music_session_at) as first_music_session_at_count, count(first_invited_at) first_invited_at_count, count(first_friended_at) as first_friended_at_count, count(first_social_promoted_at) first_social_promoted_at_count, avg(last_jam_audio_latency) last_jam_audio_latency_avg').first
- puts "result #{result['musician_count']}"
stats['musicians'] = result['musician_count'].to_i
stats['fans'] = result['fan_count'].to_i
stats['downloaded_client'] = result['first_downloaded_client_at_count'].to_i
diff --git a/ruby/lib/jam_ruby/resque/audiomixer.rb b/ruby/lib/jam_ruby/resque/audiomixer.rb
index 7ea7e7e55..cbe62d92e 100644
--- a/ruby/lib/jam_ruby/resque/audiomixer.rb
+++ b/ruby/lib/jam_ruby/resque/audiomixer.rb
@@ -20,13 +20,11 @@ module JamRuby
def self.perform(mix_id, postback_ogg_url, postback_mp3_url)
- JamWebEventMachine.run_wait_stop do
- audiomixer = AudioMixer.new()
- audiomixer.postback_ogg_url = postback_ogg_url
- audiomixer.postback_mp3_url = postback_mp3_url
- audiomixer.mix_id = mix_id
- audiomixer.run
- end
+ audiomixer = AudioMixer.new()
+ audiomixer.postback_ogg_url = postback_ogg_url
+ audiomixer.postback_mp3_url = postback_mp3_url
+ audiomixer.mix_id = mix_id
+ audiomixer.run
end
diff --git a/ruby/lib/jam_ruby/resque/batch_email_job.rb b/ruby/lib/jam_ruby/resque/batch_email_job.rb
index 6d82d420e..fbfcde05f 100644
--- a/ruby/lib/jam_ruby/resque/batch_email_job.rb
+++ b/ruby/lib/jam_ruby/resque/batch_email_job.rb
@@ -3,7 +3,7 @@ require 'resque-lonely_job'
module JamRuby
class BatchEmailJob
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@@log = Logging.logger[BatchEmailJob]
diff --git a/ruby/lib/jam_ruby/resque/icecast_config_writer.rb b/ruby/lib/jam_ruby/resque/icecast_config_writer.rb
index 05ce40a26..50b41f230 100644
--- a/ruby/lib/jam_ruby/resque/icecast_config_writer.rb
+++ b/ruby/lib/jam_ruby/resque/icecast_config_writer.rb
@@ -9,7 +9,7 @@ module JamRuby
# executes a mix of tracks, creating a final output mix
class IcecastConfigWriter
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@@log = Logging.logger[IcecastConfigWriter]
diff --git a/ruby/lib/jam_ruby/resque/jam_tracks_builder.rb b/ruby/lib/jam_ruby/resque/jam_tracks_builder.rb
new file mode 100644
index 000000000..cc782a5ec
--- /dev/null
+++ b/ruby/lib/jam_ruby/resque/jam_tracks_builder.rb
@@ -0,0 +1,38 @@
+require 'json'
+require 'resque'
+require 'resque-retry'
+require 'net/http'
+require 'digest/md5'
+
+module JamRuby
+ class JamTracksBuilder
+ extend JamRuby::ResqueStats
+
+ @queue = :jam_tracks_builder
+
+ def log
+ @log || Logging.logger[JamTracksBuilder]
+ end
+
+ attr_accessor :jam_track_right_id
+
+ def self.perform(jam_track_right_id)
+ jam_track_builder = JamTracksBuilder.new()
+ jam_track_builder.jam_track_right_id = jam_track_right_id
+ jam_track_builder.run
+ end
+
+ def run
+ log.info("jam_track_builder job starting. jam_track_right_id #{jam_track_right_id}")
+ @jam_track_right = JamTrackRight.find(jam_track_right_id)
+ JamRuby::JamTracksManager.save_jam_track_right_jkz(@jam_track_right)
+
+ length = @jam_track_right.url.size()
+ md5 = Digest::MD5.new
+
+ @jam_track_right.finish_sign(length, md5.to_s)
+
+ log.info "Signed jamtrack to #{@jam_track_right[:url]}"
+ end
+ end
+end
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/resque/quick_mixer.rb b/ruby/lib/jam_ruby/resque/quick_mixer.rb
index daddc1802..93704173f 100644
--- a/ruby/lib/jam_ruby/resque/quick_mixer.rb
+++ b/ruby/lib/jam_ruby/resque/quick_mixer.rb
@@ -19,12 +19,10 @@ module JamRuby
def self.perform(quick_mix_id, postback_mp3_url)
- JamWebEventMachine.run_wait_stop do
- audiomixer = QuickMixer.new
- audiomixer.postback_mp3_url = postback_mp3_url
- audiomixer.quick_mix_id = quick_mix_id
- audiomixer.run
- end
+ audiomixer = QuickMixer.new
+ audiomixer.postback_mp3_url = postback_mp3_url
+ audiomixer.quick_mix_id = quick_mix_id
+ audiomixer.run
end
diff --git a/ruby/lib/jam_ruby/resque/resque_hooks.rb b/ruby/lib/jam_ruby/resque/resque_hooks.rb
index 37685f0d8..fcd344bf6 100644
--- a/ruby/lib/jam_ruby/resque/resque_hooks.rb
+++ b/ruby/lib/jam_ruby/resque/resque_hooks.rb
@@ -1,26 +1,52 @@
require 'resque'
-# https://devcenter.heroku.com/articles/forked-pg-connections
-Resque.before_fork do
- defined?(ActiveRecord::Base) and
- ActiveRecord::Base.connection.disconnect!
+ENV['FORK_PER_JOB'] = 'false'
- JamRuby::Stats.destroy!
+def shutdown
+ puts "Cleaning up resources..."
+ Stats.destroy!
+ EventMachine.stop_event_loop
+ puts "Terminated!"
+ exit!
end
-Resque.after_fork do
- defined?(ActiveRecord::Base) and
- ActiveRecord::Base.establish_connection
+Resque.before_first_fork do
+ JamWebEventMachine.start
+ #ActiveRecord::Base.establish_connection
config = {
influxdb_database: APP_CONFIG.influxdb_database,
influxdb_username: APP_CONFIG.influxdb_username,
influxdb_password: APP_CONFIG.influxdb_password,
influxdb_hosts: APP_CONFIG.influxdb_hosts,
influxdb_port: APP_CONFIG.influxdb_port,
- influxdb_async: false # if we use async=true, the forked job will die before the stat is sent
+ influxdb_async: true # if we use async=true, the forked job will die before the stat is sent
}
+
+ # handle these events and force a shutdown. this is required I think due to influxdb-client.
+ Signal.trap("TERM") do
+ shutdown
+ end
+ Signal.trap("INT") do
+ shutdown
+ end
+
JamRuby::Stats.init(config)
+
+end
+# https://devcenter.heroku.com/articles/forked-pg-connections
+Resque.before_fork do
+
+ #defined?(ActiveRecord::Base) and
+ # ActiveRecord::Base.connection.disconnect!
+
+ #JamRuby::Stats.destroy!
+end
+
+Resque.after_fork do
+ #defined?(ActiveRecord::Base) and
+ # ActiveRecord::Base.establish_connection
+
end
# for jobs that do not extend lonely job, just extend this module and get stats
@@ -36,19 +62,21 @@ module JamRuby
end
end
+
+require 'resque-lonely_job'
+
# for jobs that extend lonely job, we override around_perform already implemented in LonelyJob, and call into it
module Resque
module Plugins
- module LonelyJob
+ module JamLonelyJob
def around_perform(*args)
Stats.timer('job.stats') do
- begin
- yield
- ensure
- unlock_queue(*args)
- end
+ super
end
end
end
end
end
+
+Resque::Plugins::JamLonelyJob.module_eval { include Resque::Plugins::LonelyJob }
+
diff --git a/ruby/lib/jam_ruby/resque/scheduled/active_music_session_cleaner.rb b/ruby/lib/jam_ruby/resque/scheduled/active_music_session_cleaner.rb
index e700a0427..899f3dd67 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/active_music_session_cleaner.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/active_music_session_cleaner.rb
@@ -7,7 +7,7 @@ require 'digest/md5'
module JamRuby
class ActiveMusicSessionCleaner
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
attr_accessor :interval
@@ -22,11 +22,9 @@ module JamRuby
def self.perform
@@log.debug("ActiveMusicSessionCleaner waking up")
- JamWebEventMachine.run_wait_stop do
- cleaner = ActiveMusicSessionCleaner.new
- cleaner.interval = "INTERVAL '1 minute'"
- cleaner.run
- end
+ cleaner = ActiveMusicSessionCleaner.new
+ cleaner.interval = "INTERVAL '1 minute'"
+ cleaner.run
@@log.debug("ActiveMusicSessionCleaner done")
end
diff --git a/ruby/lib/jam_ruby/resque/scheduled/audiomixer_retry.rb b/ruby/lib/jam_ruby/resque/scheduled/audiomixer_retry.rb
index 6c2d8fe90..782b8a8d9 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/audiomixer_retry.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/audiomixer_retry.rb
@@ -8,7 +8,7 @@ module JamRuby
# periodically scheduled to find jobs that need retrying, and cleanup activities
class AudioMixerRetry
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@queue = :scheduled_audiomixer_retry
diff --git a/ruby/lib/jam_ruby/resque/scheduled/cleanup_facebook_signup.rb b/ruby/lib/jam_ruby/resque/scheduled/cleanup_facebook_signup.rb
index 25b643187..c8f67120b 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/cleanup_facebook_signup.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/cleanup_facebook_signup.rb
@@ -2,9 +2,6 @@
module JamRuby
class CleanupFacebookSignup
-
-
-
@queue = :scheduled_cleanup_facebook_signup
@@log = Logging.logger[CleanupFacebookSignup]
diff --git a/ruby/lib/jam_ruby/resque/scheduled/daily_session_emailer.rb b/ruby/lib/jam_ruby/resque/scheduled/daily_session_emailer.rb
index e5faa2d51..6d233f165 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/daily_session_emailer.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/daily_session_emailer.rb
@@ -1,6 +1,6 @@
module JamRuby
class DailySessionEmailer
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@queue = :scheduled_daily_session_emailer
@@log = Logging.logger[DailySessionEmailer]
diff --git a/ruby/lib/jam_ruby/resque/scheduled/icecast_config_retry.rb b/ruby/lib/jam_ruby/resque/scheduled/icecast_config_retry.rb
index 1e2f6fcc3..7048c9be4 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/icecast_config_retry.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/icecast_config_retry.rb
@@ -8,7 +8,7 @@ module JamRuby
# periodically scheduled to find jobs that need retrying
class IcecastConfigRetry
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@queue = :scheduled_icecast_config_retry
diff --git a/ruby/lib/jam_ruby/resque/scheduled/icecast_source_check.rb b/ruby/lib/jam_ruby/resque/scheduled/icecast_source_check.rb
index b81c5b992..8accb3c43 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/icecast_source_check.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/icecast_source_check.rb
@@ -9,11 +9,13 @@ module JamRuby
# http://blog.bignerdranch.com/1643-never-use-resque-for-serial-jobs/
# periodically scheduled to find sources that need to be brought down, or alternatively, it seems the client failed to start sourcing
class IcecastSourceCheck
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@queue = :scheduled_icecast_source_check
- @@log = Logging.logger[IcecastSourceCheck]
+ def log
+ @log || Logging.logger[IcecastSourceCheck]
+ end
def self.lock_timeout
# this should be enough time to make sure the job has finished, but not so long that the system isn't recovering from a abandoned job
@@ -21,13 +23,7 @@ module JamRuby
end
def self.perform
- @@log.debug("waking up")
-
- JamWebEventMachine.run_wait_stop do
- IcecastSourceCheck.new.run
- end
-
- @@log.debug("done")
+ IcecastSourceCheck.new.run
end
@@ -37,6 +33,8 @@ module JamRuby
# ** listeners > 0 and sourced is DOWN (false)
# ** listeners == 0 and sourced is UP (true)
+ log.debug("waking up")
+
IcecastMount.find_each(lock: true, :conditions => "( (listeners > 0 AND sourced = FALSE) OR (listeners = 0 AND sourced = TRUE) ) AND ( sourced_needs_changing_at IS NULL OR sourced_needs_changing_at < (NOW() - interval '#{APP_CONFIG.icecast_max_sourced_changed} second') ) ", :batch_size => 100) do |mount|
if mount.music_session_id
mount.with_lock do
@@ -44,18 +42,20 @@ module JamRuby
end
end
end
+
+ log.debug("done")
end
def handle_notifications(mount)
if mount.listeners == 0 && mount.sourced
# if no listeners, but we are sourced, then ask it to stop sourcing
- @@log.debug("SOURCE_DOWN_REQUEST called on mount #{mount.name}")
+ log.debug("SOURCE_DOWN_REQUEST called on mount #{mount.name}")
mount.notify_source_down_requested
elsif mount.listeners > 0 && !mount.sourced
# if we have some listeners, and still are not sourced, then ask to start sourcing again
- @@log.debug("SOURCE_UP_REQUEST called on mount #{mount.name}")
+ log.debug("SOURCE_UP_REQUEST called on mount #{mount.name}")
mount.notify_source_up_requested
end
diff --git a/ruby/lib/jam_ruby/resque/scheduled/jam_tracks_cleaner.rb b/ruby/lib/jam_ruby/resque/scheduled/jam_tracks_cleaner.rb
new file mode 100644
index 000000000..cb14d7866
--- /dev/null
+++ b/ruby/lib/jam_ruby/resque/scheduled/jam_tracks_cleaner.rb
@@ -0,0 +1,34 @@
+require 'json'
+require 'resque'
+require 'resque-retry'
+require 'net/http'
+require 'digest/md5'
+
+module JamRuby
+
+ # periodically scheduled to find jam_tracks to cleanup
+ class JamTracksCleaner
+ extend Resque::Plugins::JamLonelyJob
+
+ @queue = :jam_tracks_cleaner
+
+ class << self
+ def log
+ @log || Logging.logger[JamTracksCleaner]
+ end
+
+ def lock_timeout
+ # this should be enough time to make sure the job has finished, but not so long that the system isn't recovering from a abandoned job
+ 1200
+ end
+
+ def perform
+ JamTrackRight.ready_to_clean.each do |jam_track_right|
+ log.debug("deleting files for jam_track_right #{jam_track_right.id}")
+ jam_track_right.delete_s3_files
+ end
+ end
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb b/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb
index cd706285a..2ed6b53fc 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb
@@ -6,7 +6,7 @@ require 'digest/md5'
module JamRuby
class MusicSessionScheduler
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@queue = :music_session_scheduler
@@ -19,9 +19,7 @@ module JamRuby
def self.perform
@@log.debug("MusicSessionScheduler waking up")
- JamWebEventMachine.run_wait_stop do
- MusicSessionScheduler.new.run
- end
+ MusicSessionScheduler.new.run
@@log.debug("MusicSessionScheduler done")
end
diff --git a/ruby/lib/jam_ruby/resque/scheduled/new_musician_emailer.rb b/ruby/lib/jam_ruby/resque/scheduled/new_musician_emailer.rb
index 2a2c861a4..8012e197a 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/new_musician_emailer.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/new_musician_emailer.rb
@@ -1,6 +1,6 @@
module JamRuby
class NewMusicianEmailer
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@queue = :scheduled_new_musician_emailer
@@log = Logging.logger[NewMusicianEmailer]
diff --git a/ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb b/ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb
index 1e4b41769..7903edc0a 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/recordings_cleaner.rb
@@ -8,7 +8,7 @@ module JamRuby
# periodically scheduled to find recordings to cleanup
class RecordingsCleaner
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@queue = :recordings_cleaner
diff --git a/ruby/lib/jam_ruby/resque/scheduled/scheduled_music_session_cleaner.rb b/ruby/lib/jam_ruby/resque/scheduled/scheduled_music_session_cleaner.rb
index 8dfeca8e0..8fddeccfb 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/scheduled_music_session_cleaner.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/scheduled_music_session_cleaner.rb
@@ -1,6 +1,6 @@
module JamRuby
class ScheduledMusicSessionCleaner
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@queue = :scheduled_music_session_cleaner
@@log = Logging.logger[ScheduledMusicSessionCleaner]
diff --git a/ruby/lib/jam_ruby/resque/scheduled/score_history_sweeper.rb b/ruby/lib/jam_ruby/resque/scheduled/score_history_sweeper.rb
index 76c759ef8..52819600b 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/score_history_sweeper.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/score_history_sweeper.rb
@@ -8,7 +8,7 @@ module JamRuby
# periodically scheduled to find jobs that need retrying
class ScoreHistorySweeper
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@queue = :score_history_sweeper
diff --git a/ruby/lib/jam_ruby/resque/scheduled/stats_maker.rb b/ruby/lib/jam_ruby/resque/scheduled/stats_maker.rb
index d84535b3e..f22dd4bad 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/stats_maker.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/stats_maker.rb
@@ -8,13 +8,25 @@ module JamRuby
# creates stats to send to influx periodically
class StatsMaker
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
- @queue = :stats_maker
+ @queue = :scheduled_db_metrics
+
+ def log
+ @log || Logging.logger[StatsMaker]
+ end
+
+ def self.lock_timeout
+ 120
+ end
- @@log = Logging.logger['StatsMaker']
def self.perform
+ StatsMaker.new.run
+ end
+
+ def run
+ log.debug("starting...")
Stats.write('connection', Connection.stats)
Stats.write('users', User.stats)
end
diff --git a/ruby/lib/jam_ruby/resque/scheduled/unused_music_notation_cleaner.rb b/ruby/lib/jam_ruby/resque/scheduled/unused_music_notation_cleaner.rb
index 9fb3e307f..3d3ca8e76 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/unused_music_notation_cleaner.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/unused_music_notation_cleaner.rb
@@ -6,7 +6,7 @@ require 'digest/md5'
module JamRuby
class UnusedMusicNotationCleaner
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@queue = :unused_music_notation_cleaner
@@ -20,9 +20,7 @@ module JamRuby
def self.perform
@@log.debug("waking up")
- JamWebEventMachine.run_wait_stop do
- UnusedMusicNotationCleaner.new.run
- end
+ UnusedMusicNotationCleaner.new.run
@@log.debug("done")
end
diff --git a/ruby/lib/jam_ruby/resque/scheduled/user_progress_emailer.rb b/ruby/lib/jam_ruby/resque/scheduled/user_progress_emailer.rb
index 99835146f..84e1c5081 100644
--- a/ruby/lib/jam_ruby/resque/scheduled/user_progress_emailer.rb
+++ b/ruby/lib/jam_ruby/resque/scheduled/user_progress_emailer.rb
@@ -1,6 +1,6 @@
module JamRuby
class UserProgressEmailer
- extend Resque::Plugins::LonelyJob
+ extend Resque::Plugins::JamLonelyJob
@queue = :scheduled_user_progress_emailer
@@log = Logging.logger[UserProgressEmailer]
diff --git a/ruby/spec/files/on.ogg b/ruby/spec/files/on.ogg
new file mode 100644
index 000000000..743d6e3aa
Binary files /dev/null and b/ruby/spec/files/on.ogg differ
diff --git a/ruby/spec/jam_ruby/models/connection_spec.rb b/ruby/spec/jam_ruby/models/connection_spec.rb
index 97784b838..f93e46bf7 100644
--- a/ruby/spec/jam_ruby/models/connection_spec.rb
+++ b/ruby/spec/jam_ruby/models/connection_spec.rb
@@ -193,5 +193,20 @@ describe JamRuby::Connection do
stats['udp_reachable'].should eq(0)
stats['networking_testing'].should eq(0)
end
+
+ it "1 connection" do
+ conn.touch
+
+ stats = Connection.stats
+ stats[Connection::TYPE_CLIENT].should eq(1)
+ stats[Connection::TYPE_BROWSER].should eq(0)
+ stats[Connection::TYPE_LATENCY_TESTER].should eq(0)
+ stats['count'].should eq(1)
+ stats['scoring_timeout'].should eq(0)
+ stats['in_session'].should eq(1)
+ stats['musicians'].should eq(1)
+ stats['udp_reachable'].should eq(1)
+ stats['networking_testing'].should eq(0)
+ end
end
end
diff --git a/ruby/spec/jam_ruby/models/jam_track_right_spec.rb b/ruby/spec/jam_ruby/models/jam_track_right_spec.rb
index 5d136d62a..2936fd8d4 100644
--- a/ruby/spec/jam_ruby/models/jam_track_right_spec.rb
+++ b/ruby/spec/jam_ruby/models/jam_track_right_spec.rb
@@ -1,7 +1,8 @@
require 'spec_helper'
describe JamTrackRight do
-
+ include UsesTempFiles
+ include CarrierWave::Test::Matchers
it "created" do
jam_track_right = FactoryGirl.create(:jam_track_right)
@@ -16,6 +17,14 @@ describe JamTrackRight do
end
+ it "lists" do
+ jam_track_right = FactoryGirl.create(:jam_track_right)
+ jam_tracks = JamTrack.list_downloads(jam_track_right.user)
+ jam_tracks.should have_key('downloads')
+ jam_tracks.should have_key('next')
+ jam_tracks['downloads'].should have(1).items
+ end
+
describe "validations" do
it "one purchase per user/jam_track combo" do
user = FactoryGirl.create(:user)
@@ -27,5 +36,92 @@ describe JamTrackRight do
right_2.errors[:user_id].should == ['has already been taken']
end
end
+
+ describe "JKZ" do
+ before(:all) do
+ original_storage = JamTrackTrackUploader.storage = :fog
+ original_storage = JamTrackRightUploader.storage = :fog
+ end
+
+ after(:all) do
+ JamTrackTrackUploader.storage = @original_storage
+ JamTrackRightUploader.storage = @original_storage
+ end
+
+ before(:each) do
+ #content_for_file('abc')
+ end
+
+ it "should fail if no tracks" do
+ user = FactoryGirl.create(:user)
+ jam_track = FactoryGirl.create(:jam_track)
+ right = JamTrackRight.create(:user=>user, :jam_track=>jam_track)
+ expect {
+ JamRuby::JamTracksManager.save_jam_track_jkz(user, jam_track)
+ }.to raise_error(ArgumentError)
+ end
+
+ it "should create" do
+ ogg_path = File.join('spec', 'files', 'on.ogg')
+ user = FactoryGirl.create(:user)
+ jam_track_track = FactoryGirl.create(:jam_track_track)
+ jam_track = jam_track_track.jam_track
+
+ uploader = JamTrackTrackUploader.new(jam_track_track, :url)
+ uploader.store!(File.open(ogg_path, 'rb'))
+ jam_track_track.save!
+
+ jam_track_track[:url].should == jam_track_track.store_dir + '/' + jam_track_track.filename
+
+ # verify it's on S3
+ s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
+ s3.exists?(jam_track_track[:url]).should be_true
+ s3.length(jam_track_track[:url]).should == File.size?(ogg_path)
+
+ jam_track_right = JamTrackRight.create(:user=>user, :jam_track=>jam_track)
+ #expect {
+ JamRuby::JamTracksManager.save_jam_track_jkz(user, jam_track)
+ #}.to_not raise_error(ArgumentError)
+ jam_track_right.reload
+ jam_track_right[:url].should == jam_track_right.store_dir + '/' + jam_track_right.filename
+
+ # verify it's on S3
+ url = jam_track_right[:url]
+ s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
+ s3.exists?(url).should be_true
+ s3.length(url).should > File.size?(ogg_path)
+
+ JamTrackRight.ready_to_clean.count.should == 0
+ jam_track_right.destroy
+ s3.exists?(url).should be_false
+ end
+ end
+
+ describe "list_keys" do
+ let(:user) {FactoryGirl.create(:user)}
+
+ it "empty" do
+ JamTrackRight.list_keys(user, nil).should eq([])
+ end
+
+ it "bogus key" do
+ JamTrackRight.list_keys(user, ['a']).should eq([])
+ end
+
+ it "valid track with no rights to it by querying user" do
+ jam_track = FactoryGirl.create(:jam_track)
+ keys = JamTrackRight.list_keys(user, [jam_track.id])
+ keys.length.should == 0
+ end
+
+ it "valid track with rights to it by querying user" do
+ jam_track_right = FactoryGirl.create(:jam_track_right, private_key: 'keyabc')
+ keys = JamTrackRight.list_keys(jam_track_right.user, [jam_track_right.jam_track.id])
+ keys.length.should == 1
+ keys[0].id.should == jam_track_right.jam_track.id
+ keys[0]['private_key'].should eq('keyabc')
+ end
+ end
+
end
diff --git a/ruby/spec/jam_ruby/models/jam_track_spec.rb b/ruby/spec/jam_ruby/models/jam_track_spec.rb
index 85cf9059c..1bddfc5ab 100644
--- a/ruby/spec/jam_ruby/models/jam_track_spec.rb
+++ b/ruby/spec/jam_ruby/models/jam_track_spec.rb
@@ -97,7 +97,7 @@ describe JamTrack do
end
describe "upload/download" do
- JKA_NAME = 'blah.jka'
+ JKA_NAME = 'blah.jkz'
in_directory_with_file(JKA_NAME)
diff --git a/ruby/spec/jam_ruby/resque/jam_tracks_cleaner_spec.rb b/ruby/spec/jam_ruby/resque/jam_tracks_cleaner_spec.rb
new file mode 100644
index 000000000..36cb5a067
--- /dev/null
+++ b/ruby/spec/jam_ruby/resque/jam_tracks_cleaner_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+
+describe JamTracksCleaner do
+ include UsesTempFiles
+ include CarrierWave::Test::Matchers
+ RIGHT_NAME = 'abc.jkz'
+ in_directory_with_file(RIGHT_NAME)
+
+ before (:all) do
+ @user = FactoryGirl.create(:user)
+ @jam_track = FactoryGirl.create(:jam_track)
+ original_storage = JamTrackRightUploader.storage = :fog
+ end
+
+ after(:all) do
+ JamTrackRightUploader.storage = @original_storage
+ end
+
+
+ before(:each) do
+ content_for_file('abc')
+ end
+
+ it "should clean" do
+ jam_track_right = JamTrackRight.create(:user=>@user, :jam_track=>@jam_track)
+ jam_track_right.signed=true
+ jam_track_right
+
+ jam_track_right.url.store!(File.open(RIGHT_NAME))
+ jam_track_right.downloaded_since_sign=true
+ jam_track_right.save!
+
+ jam_track_right[:url].should == jam_track_right.store_dir + '/' + jam_track_right.filename
+ jam_track_right.reload
+
+ # Should exist after uploading:
+ url = jam_track_right[:url]
+ s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
+
+ url.should_not be_nil
+ s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
+ s3.exists?(jam_track_right[:url]).should be_true
+
+ JamRuby::JamTracksCleaner.perform
+ s3.exists?(url).should be_true
+ s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
+ jam_track_right.update_attribute("updated_at", 6.minutes.ago)
+
+ # But not after running cleaner job:
+ JamRuby::JamTracksCleaner.perform
+ s3.exists?(url).should be_false
+ end
+end
\ No newline at end of file
diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb
index 73e9a3384..890a195c0 100644
--- a/ruby/spec/spec_helper.rb
+++ b/ruby/spec/spec_helper.rb
@@ -12,6 +12,10 @@ require 'uses_temp_files'
require 'resque_spec'
require 'resque_failed_job_mailer'
+# to prevent embedded resque code from forking
+ENV['FORK_PER_JOB'] = 'false'
+
+
# recreate test database and migrate it
SpecDb::recreate_database
diff --git a/ruby/spec/support/utilities.rb b/ruby/spec/support/utilities.rb
index 8dd934198..d2dbb4056 100644
--- a/ruby/spec/support/utilities.rb
+++ b/ruby/spec/support/utilities.rb
@@ -74,6 +74,10 @@ def app_config
0 # 0 seconds
end
+ def jamtracks_dir
+ ENV['JAMTRACKS_DIR'] || File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "..", "jamtracks"))
+ end
+
def rabbitmq_host
"localhost"
end
diff --git a/web/README.md b/web/README.md
index 03b796614..5aca8b9bf 100644
--- a/web/README.md
+++ b/web/README.md
@@ -1,4 +1,4 @@
-* TODO
+== TODO:
Jasmine Javascript Unit Tests
=============================
@@ -11,3 +11,4 @@ $ rake jasmine
Open browser to localhost:8888
+
diff --git a/web/app/assets/javascripts/JamServer.js b/web/app/assets/javascripts/JamServer.js
index f60ea07d0..73997c0a7 100644
--- a/web/app/assets/javascripts/JamServer.js
+++ b/web/app/assets/javascripts/JamServer.js
@@ -321,23 +321,15 @@
lastDisconnectedReason = 'WEBSOCKET_CLOSED_LOCALLY'
}
- rest.createDiagnostic({
- type: lastDisconnectedReason,
- data: {logs: logger.logCache, client_type: clientType, client_id: server.clientID, channel_id: channelId}
- })
- .always(function() {
- if ($currentDisplay.is('.no-websocket-connection')) {
- // this path is the 'not in session path'; so there is nothing else to do
- $currentDisplay.removeClass('active');
-
- // TODO: tell certain elements that we've reconnected
- }
- else {
- window.location.reload();
- }
- });
-
+ if ($currentDisplay.is('.no-websocket-connection')) {
+ // this path is the 'not in session path'; so there is nothing else to do
+ $currentDisplay.removeClass('active');
+ // TODO: tell certain elements that we've reconnected
+ }
+ else {
+ window.location.reload();
+ }
}
function buildOptions() {
diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js
index e0988bcf6..a5611ba60 100644
--- a/web/app/assets/javascripts/jam_rest.js
+++ b/web/app/assets/javascripts/jam_rest.js
@@ -1344,6 +1344,15 @@
})
}
+ function clearShoppingCart(options) {
+ return $.ajax({
+ type: "DELETE",
+ url: '/api/shopping_carts/clear_all',
+ dataType: "json",
+ contentType: 'application/json'
+ })
+ }
+
function getRecurlyAccount() {
return $.ajax({
type: "GET",
@@ -1374,15 +1383,16 @@
function updateBillingInfo(options) {
return $.ajax({
type: "PUT",
- url: '/api/recurly/update_billing_info?' + $.param(options),
+ url: '/api/recurly/update_billing_info?' + $.param({billing_info: options}),
dataType: "json",
+ //data: JSON.stringify({"billing_info": $.param(options)}),
contentType: 'application/json'
});
}
function placeOrder(options) {
return $.ajax({
- type: "PUT",
+ type: "POST",
url: '/api/recurly/place_order?' + $.param(options),
dataType: "json",
contentType: 'application/json'
@@ -1534,6 +1544,7 @@
this.addJamtrackToShoppingCart = addJamtrackToShoppingCart;
this.getShoppingCarts = getShoppingCarts;
this.removeShoppingCart = removeShoppingCart;
+ this.clearShoppingCart = clearShoppingCart;
this.getRecurlyAccount = getRecurlyAccount;
this.createRecurlyAccount = createRecurlyAccount;
this.getBillingInfo = getBillingInfo;
diff --git a/web/app/assets/javascripts/jamtrack.js b/web/app/assets/javascripts/jamtrack.js
index 6ae767514..a0771762a 100644
--- a/web/app/assets/javascripts/jamtrack.js
+++ b/web/app/assets/javascripts/jamtrack.js
@@ -156,7 +156,6 @@
e.preventDefault();
var params = {id: $(e.target).attr("data-jamtrack-id")};
-
rest.addJamtrackToShoppingCart(params)
.done(function(response) {
context.location = "/client#/shoppingCart";
diff --git a/web/app/assets/javascripts/jquery.listenbroadcast.js b/web/app/assets/javascripts/jquery.listenbroadcast.js
index 349305b57..95c806af2 100644
--- a/web/app/assets/javascripts/jquery.listenbroadcast.js
+++ b/web/app/assets/javascripts/jquery.listenbroadcast.js
@@ -33,6 +33,7 @@
context.JK.ListenBroadcast = function($parentElement, options){
var WAIT_FOR_BUFFER_TIMEOUT = 5000;
+ var WAIT_FOR_PLAYING_TIMEOUT = 7000;
var RETRY_ATTEMPTS = 5; // we try 4 times, so the user will wait up until RETRY_ATTEMPTS * WAIT_FOR_BUFFER_TIMEOUTS
var logger = context.JK.logger;
@@ -42,6 +43,7 @@
var audioDomElement = null;
var musicSessionId = null;
var waitForBufferingTimeout = null;
+ var waitForPlayingTimeout = null;
var fanAccess = null;
var audioSrc = null;
var audioType = null;
@@ -49,6 +51,7 @@
var self = this;
var mountInfo = null;
var $mountState = null;
+ var sessionInfo = null; // stored so we can access .mount, mostly
var lazyAudioInit = options && options.lazyAudioInit;
var hoverOptions = (options && options.hoverOptions) ? options.hoverOptions : {}
var $detailHelper = options && options.detailHelper;
@@ -77,21 +80,7 @@
e.stopPropagation();
}
- if(lazyAudioInit) {
- if($audio.length == 0) {
- $audio =
- $('')
- $parent.append($audio)
- audioDomElement = $audio.get(0);
- audioBind();
- }
- }
-
- if(destroyed) return;
-
- if(!audioDomElement) throw "no audio element supplied; the user should not be able to attempt a play"
+ //if(destroyed) return;
if(context.JK.ListenBroadcastCurrentlyPlaying) {
context.JK.ListenBroadcastCurrentlyPlaying.forcedPause();
@@ -102,20 +91,33 @@
checkServer()
.done(function(response) {
- if(!response.mount) {
+ if(!sessionInfo.mount) {
transition(PlayStateSessionOver);
destroy();
- }
+ }
else {
- audioDomElement.play();
+ recreateAudioElement();
- retryAttempts = 0;
+ audioDomElement.load();
- transition(PlayStateInitializing);
+ retryAttempts = 0;
+
+ transition(PlayStateInitializing);
+
+ // keep this after transition, because any transition clears this timer
+ waitForBufferingTimeout = setTimeout(noBuffer, WAIT_FOR_BUFFER_TIMEOUT);
+ logger.debug("setting buffering timeout");
+ rest.addPlayablePlay(musicSessionId, 'JamRuby::MusicSession', null, context.JK.currentUserId);
+
+ if(needsCanPlayGuard()) {
+ $audio.bind('canplay', function() {
+ audioDomElement.play();
+ })
+ }
+ else {
+ audioDomElement.play();
+ }
- // keep this after transition, because any transition clears this timer
- waitForBufferingTimeout = setTimeout(noBuffer, WAIT_FOR_BUFFER_TIMEOUT);
- rest.addPlayablePlay(musicSessionId, 'JamRuby::MusicSession', null, context.JK.currentUserId);
}
})
}
@@ -131,9 +133,7 @@
e.stopPropagation();
}
- if(destroyed) return;
-
- if(!lazyAudioInit && !audioDomElement) throw "no audio element supplied; the user should not be able to attempt a pause"
+ //if(destroyed) return;
transition(PlayStateNone);
@@ -141,12 +141,13 @@
}
function destroy() {
- if(!destroyed) {
- $audio.remove();
- $audio = null;
- audioDomElement = null;
- destroyed = true;
- }
+ // if(!destroyed) {
+ //$audio.remove();
+ //$audio = null;
+ //audioDomElement = null;
+ recreateAudioElement()
+ // destroyed = true;
+ //}
}
function onScreenChanged(e, data) {
@@ -155,30 +156,49 @@
}
}
+ function createAudioElementHtml() {
+ if (sessionInfo == null) throw "no session info";
+ if (sessionInfo.mount == null) throw "no session mount info";
+
+ $audio =
+ $('')
+ $parent.append($audio)
+ audioDomElement = $audio.get(0);
+ audioBind();
+
+ }
+
// this is the only way to make audio stop buffering after the user hits pause
function recreateAudioElement() {
// jeez: http://stackoverflow.com/questions/4071872/html5-video-force-abort-of-buffering/13302599#13302599
- var originalSource = $audio.html()
- audioDomElement.pause();
- audioDomElement.src = '';
- audioDomElement.load();
- var $parent = $audio.parent();
+ if(audioDomElement) {
+ audioDomElement.pause();
+ audioDomElement.src = '';
+ audioDomElement.load();
+ }
$audio.remove();
- $parent.append('');
- $audio = $('audio', $parent);
- $audio.append(originalSource);
- audioDomElement = $audio.get(0);
- audioBind();
+ createAudioElementHtml();
logger.log("recreated audio element ")
}
function clearBufferTimeout() {
if(waitForBufferingTimeout) {
+ logger.debug("clearing buffering timeout");
clearTimeout (waitForBufferingTimeout);
waitForBufferingTimeout = null;
}
}
+ function clearPlayingTimeout() {
+ if(waitForPlayingTimeout) {
+ logger.debug("clearing playing timeout");
+ clearTimeout (waitForPlayingTimeout);
+ waitForPlayingTimeout = null;
+ }
+ }
+
function transition(newState) {
logger.log("transitioning from " + playState + " to " + newState);
@@ -186,8 +206,15 @@
if(newState != PlayStateStalled) {
clearBufferTimeout();
+ clearPlayingTimeout();
}
+ if(newState == PlayStateBuffering) {
+ // give some time after buffering is seen to let play start
+ waitForPlayingTimeout = setTimeout(noPlay, WAIT_FOR_PLAYING_TIMEOUT)
+ }
+
+
if( playState == PlayStateNone ||
playState == PlayStateEnded ||
playState == PlayStateFailedStart ||
@@ -202,6 +229,9 @@
triggerStateChange();
}
+ function noPlay() {
+ noBuffer();
+ }
function noBuffer() {
waitForBufferingTimeout = null;
@@ -218,13 +248,16 @@
checkServer()
.done(function(response) {
- if(!response.mount) {
+ if(!sessionInfo.mount) {
transition(PlayStateSessionOver);
destroy();
}
else {
// tell audio to stop/start, in attempt to retry
//audioDomElement.pause();
+
+ recreateAudioElement();
+
audioDomElement.load();
if(needsCanPlayGuard()) {
$audio.bind('canplay', function() {
@@ -344,6 +377,10 @@
function checkServer() {
return rest.getSession(musicSessionId)
+ .done(function(response) {
+ console.log("assigning sessionInfo")
+ sessionInfo = response;
+ })
.fail(function(jqXHR) {
if(jqXHR.status == 404 || jqXHR.status == 403) {
transition(PlayStateSessionOver);
@@ -443,7 +480,12 @@
// we have cause to believe the session is done; check against the server
if(refresh) {
- checkServer();
+ checkServer()
+ .done(function(response) {
+ if(!sessionInfo.mount) {
+ transition(PlayStateSessionOver);
+ destroy();
+ }})
}
}
@@ -634,7 +676,8 @@
function openBubble() {
checkServer().done(function(response) {
- var mountId = response.mount ? response.mount.id : null
+
+ var mountId = sessionInfo.mount ? sessionInfo.mount.id : null
if(mountId) {
rest.getMount({id: mountId})
@@ -648,6 +691,10 @@
}
else {
mountInfo = null;
+
+ transition(PlayStateSessionOver);
+ destroy();
+
context.JK.app.layout.notify('This session can not currently broadcast')
}
})
@@ -679,8 +726,12 @@
})
}
}
- function initialize() {
+ function cacheBustedSrc(src) {
+ return src + '?cache-buster=' + new Date().getTime();
+ }
+
+ function initialize() {
musicSessionId = $parent.attr('data-music-session');
if(!musicSessionId) throw "data-music-session must be specified on $parentElement";
@@ -689,13 +740,6 @@
if(fanAccess === null) throw 'fan-access must be specified in $parentElement';
fanAccess = $parent.attr('fan-access') === 'true' // coerce to boolean
- if(lazyAudioInit) {
- audioSrc = $parent.attr('data-audio-src');
- if(audioSrc === null) throw 'data-audio-src must be specified in $parentElement';
- audioType = $parent.attr('data-audio-type');
- if(audioType === null) throw 'data-audio-type must be specified in $parentElement';
- }
-
bindHoverDetail();
$audio = $('audio', $parent);
@@ -708,11 +752,6 @@
throw "more than one