Merge branch 'develop' into feature/musician_profile_enhancements

This commit is contained in:
Brian Smith 2015-04-01 23:09:51 -04:00
commit a87c08afe3
55 changed files with 3331 additions and 46 deletions

View File

@ -24,7 +24,7 @@ ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do
column :original_artist
column :name
column :flags do |jam_track| jam_track.duplicate_positions? ? 'DUP POSITIONS' : '' end
column :onboarding_flags do |jam_track| jam_track.onboard_warnings end
column :status
column :master_track do |jam_track| jam_track.master_track.nil? ? 'None' : (link_to "Download", jam_track.master_track.url_by_sample_rate(44)) end
column :licensor

View File

@ -13,6 +13,7 @@
= f.input :publisher, :input_html => { :rows=>1, :maxlength=>1000 }
= f.input :licensor, collection: JamRuby::JamTrackLicensor.all, include_blank: true
= f.input :genre, collection: JamRuby::Genre.all, include_blank: false
= f.input :duration, hint: 'this should rarely need editing because it comes from the import process'
= f.input :sales_region, collection: JamRuby::JamTrack::SALES_REGION, include_blank: false
= f.input :price, :required => true, :input_html => {type: 'numeric'}
= f.input :pro_ascap, :label => 'ASCAP royalties due?'

View File

@ -5,11 +5,14 @@
= 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
= f.input :preview_start_time_raw, :label => 'Preview Start Time', :hint => 'MM:SS:MLS', :as => :string
- if !f.object.nil? && f.object.track_type != 'Master'
= f.input :preview_start_time_raw, :label => 'Preview Start Time', :hint => 'MM:SS:MLS', :as => :string
- unless f.object.nil? || f.object[:preview_url].nil?
.current_file_holder style='margin-bottom:10px'
a href=f.object.preview_sign_url(3600) style='padding:0 0 0 20px'
| Download Preview
a href=f.object.preview_public_url('ogg') style='padding:0 0 0 20px'
| Download Preview (ogg)
a href=f.object.preview_public_url('mp3') style='padding:0 0 0 20px'
| Download Preview (mp3)
// temporarily disable
- if f.object.new_record?

View File

@ -13,7 +13,6 @@ class JamRuby::JamTrackTrack
end
# this is used by active admin/jam-admin
def preview_start_time_raw
if self.preview_start_time.nil? || self.preview_start_time.nil?
@ -60,6 +59,7 @@ class JamRuby::JamTrackTrack
input = File.join(tmp_dir, 'in.ogg')
output = File.join(tmp_dir, 'out.ogg')
output_mp3 = File.join(tmp_dir, 'out.mp3')
start = self.preview_start_time.to_f / 1000
stop = start + 20
@ -80,16 +80,52 @@ class JamRuby::JamTrackTrack
@@log.debug("fail #{result_code}")
@preview_generate_error = "unable to execute cut command #{sox_output}"
else
@@log.debug("uploading preview to #{self.preview_filename}")
# now create mp3 off of ogg preview
s3_manager.upload(self.preview_filename, output)
convert_mp3_cmd = "#{APP_CONFIG.ffmpeg_path} -i \"#{output}\" -ab 192k \"#{output_mp3}\""
self.skip_uploader = true
# and finally update the JamTrackTrack with the new info
self["preview_url"] = self.preview_filename
self["preview_md5"] = ::Digest::MD5.file(output).hexdigest
self["preview_length"] = File.new(output).size
self.save!
@@log.debug("converting to mp3 using: " + convert_mp3_cmd)
convert_output = `#{convert_mp3_cmd}`
result_code = $?.to_i
if result_code != 0
@@log.debug("fail #{result_code}")
@preview_generate_error = "unable to execute mp3 convert command #{convert_output}"
else
ogg_digest = ::Digest::MD5.file(output)
mp3_digest = ::Digest::MD5.file(output_mp3)
self["preview_md5"] = ogg_md5 = ogg_digest.hexdigest
self["preview_mp3_md5"] = mp3_md5 = mp3_digest.hexdigest
@@log.debug("uploading ogg preview to #{self.preview_filename('ogg')}")
s3_public_manager.upload(self.preview_filename(ogg_md5, 'ogg'), output, content_type: 'audio/ogg', content_md5: ogg_digest.base64digest)
@@log.debug("uploading mp3 preview to #{self.preview_filename('mp3')}")
s3_public_manager.upload(self.preview_filename(mp3_md5, 'mp3'), output_mp3, content_type: 'audio/mpeg', content_md5: mp3_digest.base64)
self.skip_uploader = true
original_ogg_preview_url = self["preview_url"]
original_mp3_preview_url = self["preview_mp3_url"]
# and finally update the JamTrackTrack with the new info
self["preview_url"] = self.preview_filename(ogg_md5, 'ogg')
self["preview_length"] = File.new(output).size
# and finally update the JamTrackTrack with the new info
self["preview_mp3_url"] = self.preview_filename(mp3_md5, 'mp3')
self["preview_mp3_length"] = File.new(output_mp3).size
self.save!
# if all that worked, now delete old previews, if present
begin
s3_public_manager.delete(original_ogg_preview_url) if original_ogg_preview_url && original_ogg_preview_url != self["preview_url"]
s3_public_manager.delete(original_mp3_preview_url) if original_mp3_preview_url && original_mp3_preview_url != track["preview_mp3_url"]
rescue
puts "UNABLE TO CLEANUP OLD PREVIEW URL"
end
end
end
end
rescue Exception => e

View File

@ -275,4 +275,6 @@ shopping_cart_anonymous.sql
user_reuse_card_and_reedem.sql
jam_track_id_to_varchar.sql
drop_position_unique_jam_track.sql
recording_client_metadata.sql
recording_client_metadata.sql
preview_support_mp3.sql
jam_track_duration.sql

View File

@ -0,0 +1 @@
ALTER TABLE jam_tracks ADD COLUMN duration INTEGER;

View File

@ -0,0 +1,4 @@
ALTER TABLE jam_track_tracks ADD COLUMN preview_mp3_url VARCHAR;
ALTER TABLE jam_track_tracks ADD COLUMN preview_mp3_md5 VARCHAR;
ALTER TABLE jam_track_tracks ADD COLUMN preview_mp3_length BIGINT;
UPDATE jam_track_tracks SET preview_url = NULL where track_type = 'Master';

View File

@ -30,6 +30,7 @@ require "jam_ruby/errors/jam_argument_error"
require "jam_ruby/errors/conflict_error"
require "jam_ruby/lib/app_config"
require "jam_ruby/lib/s3_manager_mixin"
require "jam_ruby/lib/s3_public_manager_mixin"
require "jam_ruby/lib/module_overrides"
require "jam_ruby/lib/s3_util"
require "jam_ruby/lib/s3_manager"

View File

@ -19,6 +19,10 @@ module JamRuby
@s3_manager ||= S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
end
def public_jamkazam_s3_manager
@public_s3_manager ||= S3Manager.new(APP_CONFIG.aws_bucket_public, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
end
def finish(reason, detail)
self.reason = reason
self.detail = detail
@ -93,6 +97,7 @@ module JamRuby
self.name = metadata["name"] || name
if jam_track.new_record?
jam_track.id = "#{JamTrack.count + 1}" # default is UUID, but the initial import was based on auto-increment ID, so we'll maintain that
jam_track.status = 'Staging'
jam_track.metalocation = metalocation
jam_track.original_artist = metadata["original_artist"] || original_artist
@ -228,7 +233,7 @@ module JamRuby
part = potential_instrument_original if !part
{instrument: instrument,
part: part}
part: part}
end
@ -259,7 +264,7 @@ module JamRuby
if stem_location
bits = filename_no_ext[stem_location..-1].split('-')
bits.collect! {|bit| bit.strip}
bits.collect! { |bit| bit.strip }
possible_instrument = nil
possible_part = nil
@ -424,6 +429,15 @@ module JamRuby
track["url_48"] = ogg_48000_s3_path
track["md5_48"] = 'md5'
track["length_48"] = 1
# we can't fake the preview as easily because we don't know the MD5 of the current item
#track["preview_md5"] = 'md5'
#track["preview_mp3_md5"] = 'md5'
#track["preview_url"] = track.preview_filename('md5', 'ogg')
#track["preview_length"] = 1
#track["preview_mp3_url"] = track.preview_filename('md5', 'mp3')
#track["preview_mp3_length"] = 1
#track["preview_start_time"] = 0
else
wav_file = File.join(tmp_dir, basename)
@ -456,15 +470,26 @@ module JamRuby
jamkazam_s3_manager.upload(ogg_48000_s3_path, ogg_48000)
ogg_44100_digest = ::Digest::MD5.file(ogg_44100)
# and finally update the JamTrackTrack with the new info
track["url_44"] = ogg_44100_s3_path
track["md5_44"] = ::Digest::MD5.file(ogg_44100).hexdigest
track["md5_44"] = ogg_44100_digest.hexdigest
track["length_44"] = File.new(ogg_44100).size
track["url_48"] = ogg_48000_s3_path
track["md5_48"] = ::Digest::MD5.file(ogg_48000).hexdigest
track["length_48"] = File.new(ogg_48000).size
synchronize_duration(jam_track, ogg_44100)
# convert entire master ogg file to mp3, and push both to public destination
preview_succeeded = synchronize_master_preview(track, tmp_dir, ogg_44100, ogg_44100_digest) if track.track_type == 'Master'
if !preview_succeeded
return false
end
end
track.save!
@ -478,6 +503,77 @@ module JamRuby
return true
end
def synchronize_duration(jam_track, ogg_44100)
duration_command = "soxi -D \"#{ogg_44100}\""
output = `#{duration_command}`
result_code = $?.to_i
if result_code == 0
duration = output.to_f.round
jam_track.duration = duration
else
@@log.warn("unable to determine duration for jam_track #{jam_track.name}. output #{output}")
end
true
end
def synchronize_master_preview(track, tmp_dir, ogg_44100, ogg_digest)
begin
mp3_44100 = File.join(tmp_dir, 'output-preview-44100.mp3')
convert_mp3_cmd = "#{APP_CONFIG.ffmpeg_path} -i \"#{ogg_44100}\" -ab 192k \"#{mp3_44100}\""
@@log.debug("converting to mp3 using: " + convert_mp3_cmd)
convert_output = `#{convert_mp3_cmd}`
mp3_digest = ::Digest::MD5.file(mp3_44100)
track["preview_md5"] = ogg_md5 = ogg_digest.hexdigest
track["preview_mp3_md5"] = mp3_md5 = mp3_digest.hexdigest
# upload 44100 ogg and mp3 to public location as well
@@log.debug("uploading ogg preview to #{track.preview_filename('ogg')}")
public_jamkazam_s3_manager.upload(track.preview_filename(ogg_digest.hexdigest, 'ogg'), ogg_44100, content_type: 'audio/ogg', content_md5: ogg_digest.base64digest)
@@log.debug("uploading mp3 preview to #{track.preview_filename('mp3')}")
public_jamkazam_s3_manager.upload(track.preview_filename(mp3_digest.hexdigest, 'mp3'), mp3_44100, content_type: 'audio/mpeg', content_md5: mp3_digest.base64digest)
track.skip_uploader = true
original_ogg_preview_url = track["preview_url"]
original_mp3_preview_url = track["preview_mp3_url"]
# and finally update the JamTrackTrack with the new info
track["preview_url"] = track.preview_filename(ogg_md5, 'ogg')
track["preview_length"] = File.new(ogg_44100).size
# and finally update the JamTrackTrack with the new info
track["preview_mp3_url"] = track.preview_filename(mp3_md5, 'mp3')
track["preview_mp3_length"] = File.new(mp3_44100).size
track["preview_start_time"] = 0
if !track.save
finish("save_master_preview", track.errors.to_s)
return false
end
# if all that worked, now delete old previews, if present
begin
public_jamkazam_s3_manager.delete(original_ogg_preview_url) if original_ogg_preview_url && original_ogg_preview_url != track["preview_url"]
public_jamkazam_s3_manager.delete(original_mp3_preview_url) if original_mp3_preview_url && original_mp3_preview_url != track["preview_mp3_url"]
rescue
puts "UNABLE TO CLEANUP OLD PREVIEW URL"
end
rescue Exception => e
finish("sync_master_preview_exception", e.to_s)
return false
end
return true
end
def fetch_all_files(s3_path)
JamTrackImporter::s3_manager.list_files(s3_path)
end
@ -533,6 +629,10 @@ module JamRuby
@s3_manager ||= S3Manager.new(APP_CONFIG.aws_bucket_jamtracks, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
end
def private_s3_manager
@s3_manager ||= S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
end
def dry_run
s3_manager.list_directories('audio').each do |original_artist|
@ -554,6 +654,139 @@ module JamRuby
end
def synchronize_jamtrack_master_preview(jam_track)
importer = JamTrackImporter.new
importer.name = jam_track.name
master_track = jam_track.master_track
if master_track
Dir.mktmpdir do |tmp_dir|
ogg_44100 = File.join(tmp_dir, 'input.ogg')
private_s3_manager.download(master_track.url_by_sample_rate(44), ogg_44100)
ogg_44100_digest = ::Digest::MD5.file(ogg_44100)
if importer.synchronize_master_preview(master_track, tmp_dir, ogg_44100, ogg_44100_digest)
importer.finish("success", nil)
end
end
else
importer.finish('no_master_track', nil)
end
importer
end
def synchronize_jamtrack_master_previews
importers = []
JamTrack.all.each do |jam_track|
importers << synchronize_jamtrack_master_preview(jam_track)
end
@@log.info("SUMMARY")
@@log.info("-------")
importers.each do |importer|
if importer
if importer.reason == "success" || importer.reason == "jam_track_exists"
@@log.info("#{importer.name} #{importer.reason}")
else
@@log.error("#{importer.name} failed to import.")
@@log.error("#{importer.name} reason=#{importer.reason}")
@@log.error("#{importer.name} detail=#{importer.detail}")
end
else
@@log.error("NULL IMPORTER")
end
end
end
def synchronize_duration(jam_track)
importer = JamTrackImporter.new
importer.name = jam_track.name
master_track = jam_track.master_track
if master_track
Dir.mktmpdir do |tmp_dir|
ogg_44100 = File.join(tmp_dir, 'input.ogg')
private_s3_manager.download(master_track.url_by_sample_rate(44), ogg_44100)
if importer.synchronize_duration(jam_track, ogg_44100)
jam_track.save!
importer.finish("success", nil)
end
end
else
importer.finish('no_duration', nil)
end
importer
end
def synchronize_durations
importers = []
JamTrack.all.each do |jam_track|
importers << synchronize_duration(jam_track)
end
@@log.info("SUMMARY")
@@log.info("-------")
importers.each do |importer|
if importer
if importer.reason == "success" || importer.reason == "jam_track_exists"
@@log.info("#{importer.name} #{importer.reason}")
else
@@log.error("#{importer.name} failed to import.")
@@log.error("#{importer.name} reason=#{importer.reason}")
@@log.error("#{importer.name} detail=#{importer.detail}")
end
else
@@log.error("NULL IMPORTER")
end
end
end
def download_master(jam_track)
importer = JamTrackImporter.new
importer.name = jam_track.name
Dir.mkdir('tmp') unless Dir.exists?('tmp')
Dir.mkdir('tmp/jam_track_masters') unless Dir.exists?('tmp/jam_track_masters')
master_track = jam_track.master_track
if master_track
ogg_44100 = File.join('tmp/jam_track_masters', "#{jam_track.original_artist} - #{jam_track.name}.ogg")
private_s3_manager.download(master_track.url_by_sample_rate(44), ogg_44100)
end
importer
end
def download_masters
importers = []
JamTrack.all.each do |jam_track|
importers << download_master(jam_track)
end
@@log.info("SUMMARY")
@@log.info("-------")
importers.each do |importer|
if importer
if importer.reason == "success"
@@log.info("#{importer.name} #{importer.reason}")
else
@@log.error("#{importer.name} failed to download.")
@@log.error("#{importer.name} reason=#{importer.reason}")
@@log.error("#{importer.name} detail=#{importer.detail}")
end
else
@@log.error("NULL IMPORTER")
end
end
end
def synchronize_all(options)
importers = []
@ -607,14 +840,14 @@ module JamRuby
def load_metalocation(metalocation)
begin
data = s3_manager.read_all(metalocation)
data = s3_manager.read_all(metalocation)
return YAML.load(data)
rescue AWS::S3::Errors::NoSuchKey
return nil
end
end
def create_from_metalocation(meta, metalocation, options = {skip_audio_upload:false})
def create_from_metalocation(meta, metalocation, options = {skip_audio_upload: false})
jam_track = JamTrack.new
sync_from_metadata(jam_track, meta, metalocation, options)
end
@ -628,7 +861,7 @@ module JamRuby
JamTrack.transaction do
#begin
jam_track_importer.synchronize(jam_track, meta, metalocation, options)
jam_track_importer.synchronize(jam_track, meta, metalocation, options)
#rescue Exception => e
# jam_track_importer.finish("unhandled_exception", e.to_s)
#end

View File

@ -44,6 +44,10 @@ module JamRuby
s3_bucket.objects[key].url_for(operation, options).to_s
end
def public_url(key, options = @@def_opts)
s3_bucket.objects[key].public_url(options).to_s
end
def presigned_post(key, options = @@def_opts)
s3_bucket.objects[key].presigned_post(options)
end
@ -72,8 +76,15 @@ module JamRuby
s3_bucket.objects[filename].delete
end
def upload(key, filename)
s3_bucket.objects[key].write(:file => filename)
def upload(key, filename, options={})
options[:file] = filename
s3_bucket.objects[key].write(options)
end
def cached_upload(key, filename, options={})
options[:file] = filename
options.merge({expires: 5.years.from_now})
s3_bucket.objects[key].write(filename, options)
end
def delete_folder(folder)

View File

@ -0,0 +1,17 @@
module JamRuby
module S3PublicManagerMixin
extend ActiveSupport::Concern
include AppConfig
included do
end
module ClassMethods
end
def s3_public_manager()
@s3_public_manager ||= S3Manager.new(app_config.aws_bucket_public, app_config.aws_access_key_id, app_config.aws_secret_access_key)
end
end
end

View File

@ -37,6 +37,7 @@ module JamRuby
validates :public_performance_royalty, inclusion: {in: [nil, true, false]}
validates :reproduction_royalty, inclusion: {in: [nil, true, false]}
validates :public_performance_royalty, inclusion: {in: [nil, true, false]}
validates :duration, numericality: {only_integer: true}, :allow_nil => true
validates_format_of :reproduction_royalty_amount, with: /^\d+\.*\d{0,3}$/
validates_format_of :licensor_royalty_amount, with: /^\d+\.*\d{0,3}$/
@ -44,7 +45,7 @@ module JamRuby
belongs_to :genre, class_name: "JamRuby::Genre"
belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id'
has_many :jam_track_tracks, :class_name => "JamRuby::JamTrackTrack", order: 'position ASC, part ASC, instrument_id ASC'
has_many :jam_track_tracks, :class_name => "JamRuby::JamTrackTrack", order: 'track_type ASC, position ASC, part ASC, instrument_id ASC'
has_many :jam_track_tap_ins, :class_name => "JamRuby::JamTrackTapIn", order: 'offset_time ASC'
has_many :jam_track_rights, :class_name => "JamRuby::JamTrackRight" #, inverse_of: 'jam_track', :foreign_key => "jam_track_id" # '
@ -70,7 +71,6 @@ module JamRuby
if count.nil?
count = 0
end
puts "count #{count}"
counter[track.position] = count + 1
end
@ -84,6 +84,29 @@ module JamRuby
duplicate
end
def missing_previews?
missing_preview = false
self.jam_track_tracks.each do |track|
unless track.has_preview?
missing_preview = true
break
end
end
missing_preview
end
def onboard_warnings
warnings = []
warnings << 'POSITIONS' if duplicate_positions?
warnings << 'PREVIEWS'if missing_previews?
warnings << 'DURATION' if duration.nil?
warnings.join(',')
end
def band_jam_track_count
JamTrack.where(original_artist: original_artist).count
end
class << self
# @return array[artist_name(string)]
def all_artists

View File

@ -3,6 +3,7 @@ module JamRuby
# describes an audio track (like the drums, or guitar) that comprises a JamTrack
class JamTrackTrack < ActiveRecord::Base
include JamRuby::S3ManagerMixin
include JamRuby::S3PublicManagerMixin
# there should only be one Master per JamTrack, but there can be N Track per JamTrack
TRACK_TYPE = %w{Track Master}
@ -41,24 +42,27 @@ module JamRuby
"#{store_dir}/#{jam_track.original_artist}/#{jam_track.name}/#{original_name}"
end
# create name of the file
def preview_filename
filename("#{File.basename(self["url_44"], ".ogg")}-preview.ogg")
# create name of the preview file.
# md5-'ed because we cache forever
def preview_filename(md5, ext='ogg')
original_name = "#{File.basename(self["url_44"], ".ogg")}-preview-#{md5}.#{ext}"
"jam_track_previews/#{jam_track.original_artist}/#{jam_track.name}/#{original_name}"
end
def has_preview?
!self["preview_url"].nil?
!self["preview_url"].nil? && !self['preview_mp3_url'].nil?
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 preview_sign_url(expiration_time = 120)
s3_manager.sign_url(self[:preview_url], {:expires => expiration_time, :response_content_type => 'audio/ogg', :secure => false})
# generates a URL that points to a public version of the preview
def preview_public_url(media_type='ogg')
url = media_type == 'ogg' ? self[:preview_url] : self[:preview_mp3_url]
if url
s3_public_manager.public_url(url,{ :secure => false})
else
nil
end
end
def manually_uploaded_filename(mounted_as)
if track_type == 'Master'
filename("Master Mix-#{mounted_as == :url_48 ? '48000' : '44100'}.ogg")
@ -67,6 +71,10 @@ module JamRuby
end
end
def master?
track_type == 'Master'
end
def url_by_sample_rate(sample_rate=48)
field_name = (sample_rate==48) ? "url_48" : "url_44"
self[field_name]

View File

@ -899,6 +899,11 @@ module JamRuby
user.email = user.update_email
user.update_email_token = nil
user.save
begin
RecurlyClient.new.update_account(user)
rescue Recurly::Error
@@log.debug("No recurly account found; continuing")
end
return user
end

View File

@ -22,6 +22,11 @@ module JamRuby
account
end
def has_account?(current_user)
account = get_account(current_user)
!!account
end
def delete_account(current_user)
account = get_account(current_user)
if (account)

View File

@ -1,4 +1,5 @@
require 'spec_helper'
require 'jam_ruby/recurly_client'
RESET_PASSWORD_URL = "/reset_token"
@ -9,6 +10,7 @@ describe User do
@user = User.new(first_name: "Example", last_name: "User", email: "user@example.com",
password: "foobar", password_confirmation: "foobar", city: "Apex", state: "NC", country: "US", terms_of_service: true, musician: true)
@user.musician_instruments << FactoryGirl.build(:musician_instrument, user: @user)
@recurly = RecurlyClient.new
end
subject { @user }
@ -434,6 +436,8 @@ describe User do
describe "finalize email update" do
before do
@recurly.has_account?(@user).should == false
@user.begin_update_email("somenewemail@blah.com", "foobar", "http://www.jamkazam.com/confirm_email_update?token=")
UserMailer.deliveries.clear
end
@ -464,6 +468,36 @@ describe User do
end
end
describe "finalize email updates recurly" do
before do
@user.begin_update_email("somenewemail@blah.com", "foobar", "http://www.jamkazam.com/confirm_email_update?token=")
UserMailer.deliveries.clear
billing_info = {
first_name: @user.first_name,
last_name: @user.last_name,
address1: 'Test Address 1',
address2: 'Test Address 2',
city: @user.city,
state: @user.state,
country: @user.country,
zip: '12345',
number: '4111-1111-1111-1111',
month: '08',
year: '2017',
verification_value: '111'
}
@recurly.find_or_create_account(@user, billing_info)
end
it "should update recurly" do
@recurly.has_account?(@user).should == true
@recurly.get_account(@user).email.should_not == "somenewemail@blah.com"
@finalized = User.finalize_update_email(@user.update_email_token)
@recurly.get_account(@user).email.should == "somenewemail@blah.com"
end
end
describe "user_authorizations" do
it "can create" do

View File

@ -36,6 +36,7 @@
//= require jquery.custom-protocol
//= require jquery.exists
//= require jquery.payment
//= require howler.core.js
//= require jstz
//= require class
//= require AAC_underscore

View File

@ -1448,6 +1448,24 @@
});
}
function getJamTrack(options) {
return $.ajax({
type: "GET",
url: '/api/jamtracks/' + options['plan_code'] + '?' + $.param(options),
dataType: "json",
contentType: 'application/json'
});
}
function getJamTrackWithArtistInfo(options) {
return $.ajax({
type: "GET",
url: '/api/jamtracks/band/' + options['plan_code'] + '?' + $.param(options),
dataType: "json",
contentType: 'application/json'
});
}
function getJamtracks(options) {
return $.ajax({
type: "GET",
@ -1757,6 +1775,8 @@
this.createDiagnostic = createDiagnostic;
this.getLatencyTester = getLatencyTester;
this.updateAudioLatency = updateAudioLatency;
this.getJamTrack = getJamTrack;
this.getJamTrackWithArtistInfo = getJamTrackWithArtistInfo;
this.getJamtracks = getJamtracks;
this.getPurchasedJamTracks = getPurchasedJamTracks;
this.getPaymentHistory = getPaymentHistory;

View File

@ -0,0 +1,125 @@
$ = jQuery
context = window
context.JK ||= {};
context.JK.JamTrackPreview = {}
context.JK.JamTrackPreview = class JamTrackPreview
constructor: (app, $root, jamTrack, jamTrackTrack, options) ->
@EVENTS = context.JK.EVENTS
@rest = context.JK.Rest()
@logger = context.JK.logger
@options = options || {master_shows_duration: false}
@app = app
@jamTrack = jamTrack
@jamTrackTrack = jamTrackTrack
@root = $root
@playButton = null
@stopButton = null
@instrumentIcon = null
@instrumentName = null
@part = null
template = $('#template-jam-track-preview')
throw "no jam track preview template" if not template.exists()
@root.html($(template.html()))
@playButton = @root.find('.play-button')
@stopButton = @root.find('.stop-button')
@instrumentIcon = @root.find('.instrument-icon')
@instrumentName = @root.find('.instrument-name')
@part = @root.find('.part')
@playButton.on('click', @play)
@stopButton.on('click', @stop)
@root.attr('data-track-type', @jamTrackTrack.track_type).attr('data-id', @jamTrackTrack.id)
instrumentId = null
instrumentDescription = '?'
if @jamTrackTrack.track_type == 'Track'
if @jamTrackTrack.instrument
instrumentId = @jamTrackTrack.instrument.id
instrumentDescription = @jamTrackTrack.instrument.description
else
instrumentId = 'other'
instrumentDescription= 'Master Mix'
instrument_src = context.JK.getInstrumentIcon24(instrumentId)
@instrumentIcon.attr('data-instrument-id', instrumentId).attr('src', instrument_src)
@instrumentName.text(instrumentDescription)
#context.JK.bindInstrumentHover(@root)
part = ''
if @jamTrackTrack.track_type == 'Track'
part = "#{@jamTrackTrack.part}" if @jamTrackTrack.part? && @jamTrackTrack.part != instrumentDescription
else
if @options.master_shows_duration
duration = 'entire song'
if @jamTrack.duration
duration = "0:00 - #{context.JK.prettyPrintSeconds(@jamTrack.duration)}"
part = duration
else
part = @jamTrack.name + ' by ' + @jamTrack.original_artist
@part.text("(#{part})") if part != ''
if @jamTrackTrack.preview_mp3_url?
urls = [@jamTrackTrack.preview_mp3_url]
if @jamTrackTrack.preview_ogg_url?
urls.push(@jamTrackTrack.preview_ogg_url)
@no_audio = false
@sound = new Howl({
src: urls,
autoplay: false,
loop: false,
volume: 1.0,
onend: @onHowlerEnd})
else
@no_audio = true
if @no_audio
@playButton.addClass('disabled')
@stopButton.addClass('disabled')
onHowlerEnd: () =>
@logger.debug("on end $(this)", $(this))
@stopButton.addClass('hidden')
@playButton.removeClass('hidden')
play: (e) =>
if e?
e.stopPropagation()
if @no_audio
context.JK.prodBubble(@playButton, 'There is no preview available for this track.', {}, {duration:2000})
else
logger.debug("play issued for jam track preview")
@sound.play()
@playButton.addClass('hidden')
@stopButton.removeClass('hidden')
return false
stop: (e) =>
if e?
e.stopPropagation()
if @no_audio
context.JK.helpBubble(@playButton, 'There is no preview available for this track.', {}, {duration:2000})
else
logger.debug("stop issued for jam track preview")
@sound.stop()
@stopButton.addClass('hidden')
@playButton.removeClass('hidden')
return false

View File

@ -2740,6 +2740,12 @@
logger.debug("Unstable clocks: ", names, unstable)
context.JK.Banner.showAlert("Couldn't open metronome", context._.template($('#template-help-metronome-unstable').html(), {names: names}, { variable: 'data' }));
} else {
var data = {
value: 1,
session_size: sessionModel.participants().length,
user_id: context.JK.currentUserId,
user_name: context.JK.currentUserName }
context.stats.write('web.metronome.open', data)
var bpm = 120;
logger.debug("opening the metronome with bpm: " + bpm + ", sound:" + metroSound)
rest.openMetronome({id: sessionModel.id()})

View File

@ -133,12 +133,17 @@
}
}
else {
var $template = $('#template-help-' + templateName)
if($template.length == 0) {
var helpText = templateName;
try {
var $template = $('#template-help-' + templateName)
if ($template.length == 0) {
var helpText = templateName;
}
else {
var helpText = context._.template($template.html(), data, { variable: 'data' });
}
}
else {
var helpText = context._.template($template.html(), data, { variable: 'data' });
catch(e) {
var helpText = templateName;
}
holder = $('<div class="hover-bubble help-bubble"></div>');

View File

@ -0,0 +1,62 @@
(function (context, $) {
"use strict";
context.JK = context.JK || {};
context.JK.IndividualJamTrack = function (app) {
var rest = context.JK.Rest();
var logger = context.JK.logger;
var $page = null;
var $jamtrack_name = null;
var $previews = null;
var $jamTracksButton = null;
var $genericHeader = null;
var $individualizedHeader = null;
function fetchJamTrack() {
rest.getJamTrack({plan_code: gon.jam_track_plan_code})
.done(function (jam_track) {
logger.debug("jam_track", jam_track)
if(!gon.just_previews) {
if (gon.generic) {
$genericHeader.removeClass('hidden');
}
else {
$individualizedHeader.removeClass('hidden')
$jamtrack_name.text(jam_track.name);
$jamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrack')
}
}
context._.each(jam_track.tracks, function (track) {
var $element = $('<div class="jam-track-preview-holder"></div>')
$previews.append($element);
new context.JK.JamTrackPreview(app, $element, jam_track, track, {master_shows_duration: false})
})
$previews.append('<br clear = "all" />')
})
.fail(function () {
app.notify({title: 'Unable to fetch JamTrack', text: "Please refresh the page or try again later."})
})
}
function initialize() {
$page = $('body')
$jamtrack_name = $page.find('.jamtrack_name')
$previews = $page.find('.previews')
$jamTracksButton = $page.find('.browse-jamtracks-wrapper .white-bordered-button')
$genericHeader = $page.find('h1.generic')
$individualizedHeader = $page.find('h1.individualized')
fetchJamTrack();
}
this.initialize = initialize;
}
})(window, jQuery);

View File

@ -0,0 +1,60 @@
(function (context, $) {
"use strict";
context.JK = context.JK || {};
context.JK.IndividualJamTrackBand = function (app) {
var rest = context.JK.Rest();
var logger = context.JK.logger;
var $page = null;
var $jamTrackBandInfo = null;
var $jamTrackNoun = null;
var $previews = null;
var $jamTracksButton = null;
var $checkItOut = null;
function fetchJamTrack() {
rest.getJamTrackWithArtistInfo({plan_code: gon.jam_track_plan_code})
.done(function (jam_track) {
logger.debug("jam_track", jam_track)
$jamTrackBandInfo.text(jam_track.band_jam_track_count + ' ' + jam_track.original_artist);
$jamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrack')
if(jam_track.band_jam_track_count == 1) {
$jamTrackNoun.text('JamTrack')
$checkItOut.text(', Check It Out!')
}
context._.each(jam_track.tracks, function (track) {
var $element = $('<div class="jam-track-preview-holder"></div>')
$previews.append($element);
new context.JK.JamTrackPreview(app, $element, jam_track, track, {master_shows_duration: false})
})
$previews.append('<br clear = "all" />')
})
.fail(function () {
app.notify({title: 'Unable to fetch JamTrack', text: "Please refresh the page or try again later."})
})
}
function initialize() {
$page = $('body')
$jamTrackBandInfo = $page.find('.jamtrack_band_info')
$previews = $page.find('.previews')
$jamTracksButton = $page.find('.browse-jamtracks-wrapper .white-bordered-button')
$jamTrackNoun = $page.find('.jamtrack_noun')
$checkItOut = $page.find('.check-it-out')
fetchJamTrack();
}
this.initialize = initialize;
}
})(window, jQuery);

View File

@ -20,6 +20,7 @@
//= require jquery.icheck
//= require jquery.bt
//= require jquery.exists
//= require howler.core.js
//= require AAA_Log
//= require AAC_underscore
//= require alert
@ -52,6 +53,7 @@
//= require recording_utils
//= require helpBubbleHelper
//= require facebook_rest
//= require jam_track_preview
//= require landing/init
//= require landing/signup
//= require web/downloads
@ -60,6 +62,8 @@
//= require web/session_info
//= require web/recordings
//= require web/welcome
//= require web/individual_jamtrack
//= require web/individual_jamtrack_band
//= require fakeJamClient
//= require fakeJamClientMessages
//= require fakeJamClientRecordings

View File

@ -73,4 +73,5 @@
*= require icheck/minimal/minimal
*= require users/syncViewer
*= require ./downloadJamTrack
*= require ./jamTrackPreview
*/

View File

@ -318,3 +318,15 @@ $fair: #cc9900;
}
}
.white-bordered-button {
font-size:20px;
font-weight:bold;
background-color:white;
color:$ColorScreenPrimary;
border:3px solid $ColorScreenPrimary;
padding:18px;
-webkit-border-radius:8px;
-moz-border-radius:8px;
border-radius:8px;
}

View File

@ -0,0 +1,35 @@
@import "client/common";
.jam-track-preview {
display:inline-block;
line-height:24px;
vertical-align: middle;
@include border_box_sizing;
color:$ColorTextTypical;
font-size:14px;
.actions {
display:inline;
vertical-align: middle;
}
img.instrument-icon {
display:inline;
vertical-align: middle;
margin-left:10px;
}
.instrument-name {
display:inline;
vertical-align: middle;
margin-left:10px;
}
.part {
display:inline;
vertical-align: middle;
margin-left:4px;
}
}

View File

@ -134,7 +134,7 @@ input[type="button"] {
}
.hidden {
display:none;
display:none !important;
}
.small {

View File

@ -0,0 +1,32 @@
body.web.landing_jamtrack.individual_jamtrack {
.previews {
margin-top:10px;
}
.jamtrack-reasons {
margin: 10px 0 0 20px;
}
.white-bordered-button {
margin-top: 20px;
}
.browse-jamtracks-wrapper {
text-align:center;
width:90%;
}
.jam-track-preview-holder {
margin-bottom: 7px;
float: left;
&[data-track-type="Master"] {
width: 100%;
}
&[data-track-type="Track"] {
width: 50%;
}
}
}

View File

@ -0,0 +1,32 @@
body.web.landing_jamtrack.individual_jamtrack_band {
.previews {
margin-top:10px;
}
.jamtrack-reasons {
margin: 10px 0 0 20px;
}
.white-bordered-button {
margin-top: 20px;
}
.browse-jamtracks-wrapper {
text-align:center;
width:90%;
}
.jam-track-preview-holder {
margin-bottom: 7px;
float: left;
&[data-track-type="Master"] {
width: 100%;
}
&[data-track-type="Track"] {
width: 50%;
}
}
}

View File

@ -0,0 +1,52 @@
@import "client/common.css.scss";
body.web.landing_page {
.two_by_two {
h1 {
margin:0 0 5px;
padding:7px 0;
display:inline-block;
}
.row {
@include border_box_sizing;
&:nth-of-type(1) {
padding:20px 0 0 0;
}
.column {
width:50%;
float:left;
@include border_box_sizing;
}
}
}
&.landing_jamtrack, &.landing_product {
.landing-tag {
left:50%;
text-align:center;
}
p, ul, li {
font-size:14px;
line-height:125%;
color:$ColorTextTypical;
}
p {
}
ul {
list-style-type: disc;
}
li {
margin-left:20px;
}
.video-container {
margin-top:0;
}
}
}

View File

@ -0,0 +1,53 @@
@import "client/common.css.scss";
body.web.landing_product {
h1.product-headline {
color:white;
background-color:$ColorScreenPrimary;
margin-bottom:20px;
padding:7px;
border-radius:4px;
}
.product-description {
margin-bottom:20px;
}
.white-bordered-button {
margin-top: 20px;
}
.cta-big-button {
text-align:center;
width:90%;
}
.linked-video-holder {
text-align:center;
width:90%;
margin-top:20px;
}
.previews {
margin-top:10px;
}
.jamtrack-reasons {
margin-top:20px;
}
.jam-track-preview-holder {
margin-bottom: 7px;
float: left;
&[data-track-type="Master"] {
width: 100%;
}
&[data-track-type="Track"] {
width: 50%;
}
}
}

View File

@ -2,6 +2,7 @@
*= require client/jamServer
*= require client/ie
*= require client/jamkazam
*= require jquery.bt
*= require easydropdown
*= require easydropdown_jk
*= require client/screen_common
@ -14,6 +15,7 @@
*= require client/help
*= require client/listenBroadcast
*= require client/flash
*= require client/jamTrackPreview
*= require web/main
*= require web/footer
*= require web/recordings
@ -24,6 +26,7 @@
*= require web/downloads
*= require users/signinCommon
*= require dialogs/dialog
*= require landings/landing_page
*= require client/help
*= require_directory ../landings
*= require icheck/minimal/minimal
*/

View File

@ -1,12 +1,20 @@
class ApiJamTracksController < ApiController
# have to be signed in currently to see this screen
before_filter :api_signed_in_user, :except => [:index]
before_filter :api_any_user, :only => [:index]
before_filter :api_signed_in_user, :except => [:index, :show, :show_with_artist_info]
before_filter :api_any_user, :only => [:index, :show, :show_with_artist_info]
before_filter :lookup_jam_track_right, :only => [:download,:enqueue, :show_jam_track_right]
respond_to :json
def show
@jam_track = JamTrack.find_by_plan_code!(params[:plan_code])
end
def show_with_artist_info
@jam_track = JamTrack.find_by_plan_code!(params[:plan_code])
end
def index
data = JamTrack.index(params, any_user)
@jam_tracks, @next = data[0], data[1]

View File

@ -65,5 +65,37 @@ class LandingsController < ApplicationController
def watch_overview_tight
render 'watch_overview_tight', layout: 'web'
end
def individual_jamtrack
gon.jam_track_plan_code = params[:plan_code] ? "jamtrack-" + params[:plan_code] : nil
gon.generic = params[:generic]
render 'individual_jamtrack', layout: 'web'
end
def individual_jamtrack_band
gon.jam_track_plan_code = params[:plan_code] ? "jamtrack-" + params[:plan_code] : nil
render 'individual_jamtrack_band', layout: 'web'
end
def product_jamblaster
render 'product_jamblaster', layout: 'web'
end
def product_platform
render 'product_platform', layout: 'web'
end
def product_jamtracks
gon.generic = true
gon.just_previews = true
jam_track = JamTrack.select('plan_code').where(plan_code: Rails.application.config.nominated_jam_track).first
unless jam_track
jam_track = JamTrack.first
end
gon.jam_track_plan_code = jam_track.plan_code
render 'product_jamtracks', layout: 'web'
end
end

View File

@ -53,6 +53,13 @@ class SpikesController < ApplicationController
render :layout => 'web'
end
def jam_track_preview
gon.jamTrackPlanCode = params[:plan_code]
render :layout => 'web'
end
def site_validate
render :layout => 'web'
end

View File

@ -16,6 +16,13 @@ end
child(:jam_track_tracks => :tracks) {
attributes :id, :part, :instrument, :track_type
node do |track|
{
preview_mp3_url: track.preview_public_url('mp3'),
preview_ogg_url: track.preview_public_url('ogg')
}
end
}
child(:licensor => :licensor) {

View File

@ -0,0 +1,7 @@
object @jam_track
attributes :band_jam_track_count
node do |jam_track|
partial "api_jam_tracks/show", object: @jam_track
end

View File

@ -0,0 +1,10 @@
script type="text/template" id='template-jam-track-preview'
.jam-track-preview
.actions
a.play-button href="#"
| Play
a.stop-button.hidden href="#"
| Stop
img.instrument-icon hoveraction="instrument" data-instrument-id="" width="24" height="24"
.instrument-name
.part

View File

@ -1,4 +1,4 @@
#jamtrackLanding.screen.secondary layout='screen' layout-id='jamtrackLanding'
#jamtrackLanding.screen.secondary.no-login-required layout='screen' layout-id='jamtrackLanding'
.content
.content-head
.content-icon=image_tag("content/icon_jamtracks.png", height:19, width:19)

View File

@ -73,6 +73,7 @@
<%= render "listenBroadcast" %>
<%= render "sync_viewer_templates" %>
<%= render "download_jamtrack_templates" %>
<%= render "jam_track_preview" %>
<%= render "help" %>
<%= render 'dialogs/dialogs' %>
<div id="fb-root"></div>

View File

@ -0,0 +1,48 @@
- provide(:page_name, 'landing_page full landing_jamtrack individual_jamtrack')
.two_by_two
.row
.column
h1.hidden.individualized
| Check Out Our&nbsp;
strong.jamtrack_name
| &nbsp;JamTrack
h1.hidden.generic
| We Have 100+ Amazing JamTracks, Check One Out!
p Click the play buttons below to hear the master mix and each fully isolated track. All are included in each single JamTrack.
.previews
.column
h1 See What You Can Do With JamTracks
.video-wrapper
.video-container
iframe src="//www.youtube.com/embed/gAJAIHMyois" frameborder="0" allowfullscreen
br clear="all"
.row
.column
h1
| Get Your First JamTrack Free Now!
p Click the GET A JAMTRACK FREE button below. Browse to find the one you want. Click Add to cart, and we'll apply a credit during checkout to make this first one free! We're confident you'll be back for more.
.browse-jamtracks-wrapper
a.white-bordered-button href="/client#/jamtrack" GET A JAMTRACK FREE!
.column
h1 Why Are JamTracks Different & Better?
p
| JamTracks are the best way to play with your favorite music.&nbsp;
| Unlike traditional backing tracks, JamTracks are complete multitrack recordings,&nbsp;
| with fully isolated tracks for each part. Used with the free JamKazam app/service, you can:
ul.jamtrack-reasons
li Solo just the individual track you want to play to hear and learn it
li Mute just the track you want to play, and play along with the rest
li Make audio recordings and share them via Facebook or URL
li Make video recordings and share them via YouTube or URL
li And even go online to play JamTracks with others in real time!
br clear="all"
br clear="all"
javascript:
$(document).on('JAMKAZAM_READY', function(e, data) {
var song = new JK.IndividualJamTrack(data.app);
song.initialize();
})

View File

@ -0,0 +1,46 @@
- provide(:page_name, 'landing_page full landing_jamtrack individual_jamtrack_band')
.two_by_two
.row
.column
h1
| We Have&nbsp;
span.jamtrack_band_info
| &nbsp;
span.jamtrack_noun JamTracks
span.check-it-out , Check One Out!
p Click the play buttons below to hear the master mix and each fully isolated track. All are included in each single JamTrack.
.previews
.column
h1 See What You Can Do With JamTracks
.video-wrapper
.video-container
iframe src="//www.youtube.com/embed/gAJAIHMyois" frameborder="0" allowfullscreen
br clear="all"
.row
.column
h1
| Get Your First JamTrack Free Now!
p Click the GET A JAMTRACK FREE button below. Browse to find the one you want. Click Add to cart, and we'll apply a credit during checkout to make this first one free! We're confident you'll be back for more.
.browse-jamtracks-wrapper
a.white-bordered-button href="/client#/jamtrack" GET A JAMTRACK FREE!
.column
h1 Why Are JamTracks Different & Better?
p
| JamTracks are the best way to play with your favorite music.&nbsp;
| Unlike traditional backing tracks, JamTracks are complete multitrack recordings,&nbsp;
| with fully isolated tracks for each part. Used with the free JamKazam app/service, you can:
ul.jamtrack-reasons
li Solo just the individual track you want to play to hear and learn it
li Mute just the track you want to play, and play along with the rest
li Make audio recordings and share them via Facebook or URL
li Make video recordings and share them via YouTube or URL
li And even go online to play JamTracks with others in real time!
br clear="all"
br clear="all"
javascript:
$(document).on('JAMKAZAM_READY', function(e, data) {
var song = new JK.IndividualJamTrackBand(data.app);
song.initialize();
})

View File

@ -0,0 +1,37 @@
- provide(:page_name, 'landing_page full landing_product product_jamblaster')
.two_by_two
.row
.column
h1.product-headline
| The JamBlaster by JamKazam
p.product-description
| The JamBlaster is a device designed from the ground up to meet the unique requirements of real-time, online, distributed music performance. This device vastly extends the range/distance over which musicians can play together across the Internet.
ul
li Radically reduces audio processing latency compared to today's industry standard computers and audio interfaces.
li Delivers plug-and-play ease of use, with no worries about hardware and software incompatibilities, driver problems, and arcane configurations.
li Combines both a computer and an audio interface into a single elegant device.
li Works with computers (even old crappy ones), tablets or smartphones.
li Works with your favorite recording software applications like Garage Band, Reaper, Pro Tools, etc.
.column
h1 See What You Can Do With The JamBlaster
.video-wrapper
.video-container
iframe src="//www.youtube.com/embed/2Zk7-04IAx4" frameborder="0" allowfullscreen
br clear="all"
.row
.column
h1
| Want a JamBlaster? Need One?
p If you are a registered member of the JamKazam community, and if you "know" you will buy a JamBlaster for $199 as soon as they become available, then click the button below to add yourself to our wait list. When we get enough "virtual orders", we'll reach back out to all signups to take real orders.
.cta-big-button
a.white-bordered-button href="#" SIGN UP TO BUY A JAMBLASTER
.column
h1 Want To Know More About Latency?
p
| How is it possible that someone hundreds of miles away could feel like they are 20 feet away from you? Check out this video on latency to understand more:
.linked-video-holder
a href="https://www.youtube.com/watch?v=mB3_KMse-J4" rel="external" Watch Video About Latency - It's Pretty Fascinating
br clear="all"
br clear="all"

View File

@ -0,0 +1,42 @@
- provide(:page_name, 'landing_page full landing_product product_jamtracks')
.two_by_two
.row
.column
h1.product-headline
| JamTracks by JamKazam
p.product-description
| We have 100+ amazing JamTracks. Click the play buttons to hear the master mix and fully isolated track. All are included in each JamTrack.
.previews
.column
h1 See What You Can With JamTracks
.video-wrapper
.video-container
iframe src="//www.youtube.com/embed/2Zk7-04IAx4" frameborder="0" allowfullscreen
br clear="all"
.row
.column
h1
| Get Your First JamTrack Free Now!
p Click the GET A JAMTRACK FREE button below. Browse to find the one you want, click the Add to cart, and we'll apply a credit during checkout to make the first one free! We're confident you'll be back for more.
.cta-big-button
a.white-bordered-button href="/client#/jamtrack" GET A JAMTRACK FREE!
.column
h1 Why are JamTracks Better than Backing Tracks?
p
| JamTracks are the best way to play with your favorite music. Unlike traditional backing tracks, JamTracks are complete multitrack recordings, with fully isolated tracks for each part. Used with the free JamKazam app/service, you can:
ul.jamtrack-reasons
li Solo just the individual track you want to play to hear and learn it
li Mute just the track you want to play, and play along with the rest
li Make audio recordings and share them via Facebook or URL
li Make video recordings and share them via YouTube or URL
li And even go online to play JamTracks with others in real time!
br clear="all"
br clear="all"
javascript:
$(document).on('JAMKAZAM_READY', function (e, data) {
var song = new JK.IndividualJamTrack(data.app);
song.initialize();
})

View File

@ -0,0 +1,40 @@
- provide(:page_name, 'landing_page full landing_product product_platform')
.two_by_two
.row
.column
h1.product-headline
| The JamKazam Platform
p.product-description
| JamKazam is an innovative live music platform and social network, enabling musicians to play music together in real time from different locations over the internet as if they are sitting in the same room. The core platform is free to use and delivers immense value:
ul
li Play music from home with your friends and bandmates without packing and transporting gear, and without needing a rehearsal space
li Connect with new musician friends from our community of thousands of musicians to play more often, explore new styles, learn from others
li Find musicians to join your band, or find a band to join, either virtual on JamKazam, or real world to meet and play in person
li Schedule sessions or jump into ad hoc jams
li Make and share recordings or session performances via Facebook or URL
li Live broadcast sessions to family, friends, and fans
li List your band for hire to play gigs at clubs and events
li List yourself for hire to play studio sessions or lay down recorded tracks remotely
.column
h1 See What You Can Do With JamKazam
.video-wrapper
.video-container
iframe src="//www.youtube.com/embed/ylYcvTY9CVo" frameborder="0" allowfullscreen
br clear="all"
.row
.column
h1
| Sign Up for JamKazam Now, It's Free!
p Yep, seriously. Sign up and start playing music online in real time with your friends - or make new ones from our community of thousands of musicians. It's free to play with others as much as you want.
.cta-big-button
a.white-bordered-button href="/signup" SIGN UP NOW FOR YOUR FREE ACCOUNT
.column
h1 Does This Really Work?
p
| Feeling skeptical about whether this can actually work? That's natural. We'd encourage you to watch a video of endorsements and kudos from just a few of the musicians who use JamKazam.
.linked-video-holder
a href="https://www.youtube.com/watch?v=_7qj5RXyHCo" rel="external" Check Out Endorsements Of Real Users
br clear="all"
br clear="all"

View File

@ -85,6 +85,7 @@
<%= render "clients/help" %>
<%= render "clients/listenBroadcast" %>
<%= render "clients/flash" %>
<%= render "clients/jam_track_preview" %>
<%= render 'dialogs/dialogs' %>

View File

@ -0,0 +1,40 @@
- provide(:title, 'Jam Track Preview')
.content-wrapper
h2 Jam Track Preview
#players
javascript:
var initialized = false;
$(document).on('JAMKAZAM_READY', function(e, data) {
var rest = JK.Rest();
if(gon.jamTrackPlanCode) {
rest.getJamTrack({plan_code: gon.jamTrackPlanCode})
.done(function(jamTrack) {
var $players = $('#players')
_.each(jamTrack.tracks, function(track) {
var $element = $('<div class="meh"></div>')
$players.append($element);
new JK.JamTrackPreview(data.app, $element, jamTrack, track, {master_shows_duration: true})
})
})
.fail(function() {
alert("couldn't fetch jam track")
})
}
else {
alert("You need to add ?jam_track_plan_code=jamtracks-acdc-backinblack for this to work (or any jamtrack 'plancode')")
}
})

View File

@ -318,5 +318,7 @@ if defined?(Bundler)
config.metronome_available = true
config.backing_tracks_available = true
config.one_free_jamtrack_per_user = true
config.nominated_jam_track = 'jamtrack-pearljam-alive'
end
end

View File

@ -30,6 +30,13 @@ SampleApp::Application.routes.draw do
match '/landing/kick2', to: 'landings#watch_overview_kick2', via: :get, as: 'landing_kick2'
match '/landing/kick3', to: 'landings#watch_overview_kick3', via: :get, as: 'landing_kick3'
match '/landing/kick4', to: 'landings#watch_overview_kick4', via: :get, as: 'landing_kick4'
match '/landing/jamtracks/:plan_code', to: 'landings#individual_jamtrack', via: :get, as: 'individual_jamtrack'
match '/landing/jamtracks/band/:plan_code', to: 'landings#individual_jamtrack_band', via: :get, as: 'individual_jamtrack_band'
# product pages
match '/products/jamblaster', to: 'landings#product_jamblaster', via: :get, as: 'product_jamblaster'
match '/products/platform', to: 'landings#product_platform', via: :get, as: 'product_platform'
match '/products/jamtracks', to: 'landings#product_jamtracks', via: :get, as: 'product_jamtracks'
# oauth
match '/auth/:provider/callback', :to => 'sessions#oauth_callback'
@ -94,7 +101,8 @@ SampleApp::Application.routes.draw do
match '/launch_app', to: 'spikes#launch_app'
match '/websocket', to: 'spikes#websocket'
match '/test_subscription', to: 'spikes#subscription'
match '/widgets/download_jam_track', to: 'spikes#download_jam_track'
match '/widgets/download_jam_track', to: 'spikes #download_jam_track'
match '/widgets/jam_track_preview', to: 'spikes#jam_track_preview'
match '/site_validate', to: 'spikes#site_validate'
match '/recording_source', to: 'spikes#recording_source'
@ -204,6 +212,8 @@ SampleApp::Application.routes.draw do
match '/backing_tracks' => 'api_backing_tracks#index', :via => :get, :as => 'api_backing_tracks_list'
# Jamtracks
match '/jamtracks/:plan_code' => 'api_jam_tracks#show', :via => :get, :as => 'api_jam_tracks_show'
match '/jamtracks/band/:plan_code' => 'api_jam_tracks#show_with_artist_info', :via => :get, :as => 'api_jam_tracks_show_with_artist_info'
match '/jamtracks' => 'api_jam_tracks#index', :via => :get, :as => 'api_jam_tracks_list'
match '/jamtracks/purchased' => 'api_jam_tracks#purchased', :via => :get, :as => 'api_jam_tracks_purchased'
match '/jamtracks/download/:id' => 'api_jam_tracks#download', :via => :get, :as => 'api_jam_tracks_download'

View File

@ -24,4 +24,60 @@ namespace :jam_tracks do
JamTrackImporter.synchronize_all(skip_audio_upload:true)
end
task sync_master_preview_all: :environment do |task, args|
importer = JamTrackImporter.synchronize_jamtrack_master_previews
end
# syncs just one master track for a give JamTrack
task sync_master_preview: :environment do |task, args|
plan_code = ENV['PLAN_CODE']
if !plan_code
puts "PLAN_CODE must be set to something like jamtrack-acdc-backinblack"
exit(1)
end
jam_track = JamTrack.find_by_plan_code!(plan_code)
importer = JamTrackImporter.synchronize_jamtrack_master_preview(jam_track)
if importer.reason.nil? || importer.reason == "success" || importer.reason == "jam_track_exists"
puts("#{importer.name} #{importer.reason}")
else
puts("#{importer.name} failed to import.")
puts("#{importer.name} reason=#{importer.reason}")
puts("#{importer.name} detail=#{importer.detail}")
end
end
task sync_duration_all: :environment do |task, args|
importer = JamTrackImporter.synchronize_durations
end
# syncs just one master track for a give JamTrack
task sync_duration: :environment do |task, args|
plan_code = ENV['PLAN_CODE']
if !plan_code
puts "PLAN_CODE must be set to something like jamtrack-acdc-backinblack"
exit(1)
end
jam_track = JamTrack.find_by_plan_code!(plan_code)
importer = JamTrackImporter.synchronize_duration(jam_track)
if importer.reason.nil? || importer.reason == "success" || importer.reason == "jam_track_exists"
puts("#{importer.name} #{importer.reason}")
else
puts("#{importer.name} failed to import.")
puts("#{importer.name} reason=#{importer.reason}")
puts("#{importer.name} detail=#{importer.detail}")
end
end
task download_masters: :environment do |task, arg|
JamTrackImporter.download_masters
end
end

View File

@ -0,0 +1,106 @@
require 'spec_helper'
describe "Individual JamTrack Band", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
before(:all) do
ShoppingCart.delete_all
JamTrackRight.delete_all
JamTrack.delete_all
JamTrackTrack.delete_all
JamTrackLicensor.delete_all
end
let(:user) { FactoryGirl.create(:user) }
let(:jamtrack_acdc_backinblack) { @jamtrack_acdc_backinblack }
let(:billing_info) {
{
first_name: 'Seth',
last_name: 'Call',
address1: '10704 Buckthorn Drive',
city: 'Austin',
state: 'Texas',
country: 'US',
zip: '78759',
number: '4111111111111111',
month: '08',
year: '2017',
verification_value: '012'
}
}
def create_account(user, billing_info)
@recurlyClient.create_account(user, billing_info)
@created_accounts << user
end
before(:all) do
@recurlyClient = RecurlyClient.new
@created_accounts = []
@jamtrack_acdc_backinblack = FactoryGirl.create(:jam_track, name: 'Back in Black', original_artist: 'AC/DC', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-acdc-backinblack')
# make sure plans are there
@recurlyClient.create_jam_track_plan(@jamtrack_acdc_backinblack) unless @recurlyClient.find_jam_track_plan(@jamtrack_acdc_backinblack)
end
after(:each) do
@created_accounts.each do |user|
if user.recurly_code
begin
@account = Recurly::Account.find(user.recurly_code)
if @account.present?
@account.destroy
end
rescue
end
end
end
end
describe "AC/DC Back in Black" do
it "logged out" do
visit "/landing/jamtracks/band/acdc-backinblack"
find('h1', text: "We Have 1 #{@jamtrack_acdc_backinblack.original_artist} JamTrack, Check It Out!")
jamtrack_acdc_backinblack.jam_track_tracks.each do |track|
if track.master?
find('.jam-track-preview-holder[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="other"]')
find('.jam-track-preview-holder[data-id="' + track.id + '"] .instrument-name', text:'Master Mix')
else
find('.jam-track-preview-holder[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="' + track.instrument.id + '"]')
find('.jam-track-preview-holder[data-id="' + track.id + '"] .instrument-name', text:track.instrument.description)
end
end
find('a.white-bordered-button')['href'].should eq("/client?artist=#{jamtrack_acdc_backinblack.original_artist}#/jamtrack")
find('a.white-bordered-button').trigger(:click)
find('h1', text: 'jamtracks')
end
it "logged in" do
fast_signin(user, "/landing/jamtracks/band/acdc-backinblack")
find('h1', text: "We Have 1 #{@jamtrack_acdc_backinblack.original_artist} JamTrack, Check It Out!")
jamtrack_acdc_backinblack.jam_track_tracks.each do |track|
if track.master?
find('.jam-track-preview-holder[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="other"]')
find('.jam-track-preview-holder[data-id="' + track.id + '"] .instrument-name', text:'Master Mix')
else
find('.jam-track-preview-holder[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="' + track.instrument.id + '"]')
find('.jam-track-preview-holder[data-id="' + track.id + '"] .instrument-name', text:track.instrument.description)
end
end
find('a.white-bordered-button')['href'].should eq("/client?artist=#{jamtrack_acdc_backinblack.original_artist}#/jamtrack")
find('a.white-bordered-button').trigger(:click)
find('h1', text: 'jamtracks')
end
end
end

View File

@ -0,0 +1,125 @@
require 'spec_helper'
describe "Individual JamTrack", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
before(:all) do
ShoppingCart.delete_all
JamTrackRight.delete_all
JamTrack.delete_all
JamTrackTrack.delete_all
JamTrackLicensor.delete_all
end
let(:user) { FactoryGirl.create(:user) }
let(:jamtrack_acdc_backinblack) { @jamtrack_acdc_backinblack }
let(:billing_info) {
{
first_name: 'Seth',
last_name: 'Call',
address1: '10704 Buckthorn Drive',
city: 'Austin',
state: 'Texas',
country: 'US',
zip: '78759',
number: '4111111111111111',
month: '08',
year: '2017',
verification_value: '012'
}
}
def create_account(user, billing_info)
@recurlyClient.create_account(user, billing_info)
@created_accounts << user
end
before(:all) do
@recurlyClient = RecurlyClient.new
@created_accounts = []
@jamtrack_acdc_backinblack = FactoryGirl.create(:jam_track, name: 'Back in Black', original_artist: 'AC/DC', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-acdc-backinblack')
# make sure plans are there
@recurlyClient.create_jam_track_plan(@jamtrack_acdc_backinblack) unless @recurlyClient.find_jam_track_plan(@jamtrack_acdc_backinblack)
end
after(:each) do
@created_accounts.each do |user|
if user.recurly_code
begin
@account = Recurly::Account.find(user.recurly_code)
if @account.present?
@account.destroy
end
rescue
end
end
end
end
describe "AC/DC Back in Black" do
it "logged out" do
visit "/landing/jamtracks/acdc-backinblack"
find('h1', text: 'Check Out Our Back in Black JamTrack')
jamtrack_acdc_backinblack.jam_track_tracks.each do |track|
if track.master?
find('.jam-track-preview-holder[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="other"]')
find('.jam-track-preview-holder[data-id="' + track.id + '"] .instrument-name', text:'Master Mix')
else
find('.jam-track-preview-holder[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="' + track.instrument.id + '"]')
find('.jam-track-preview-holder[data-id="' + track.id + '"] .instrument-name', text:track.instrument.description)
end
end
find('a.white-bordered-button')['href'].should eq("/client?artist=#{jamtrack_acdc_backinblack.original_artist}#/jamtrack")
find('a.white-bordered-button').trigger(:click)
find('h1', text: 'jamtracks')
end
it "logged in" do
fast_signin(user, "/landing/jamtracks/acdc-backinblack")
find('h1', text: 'Check Out Our Back in Black JamTrack')
jamtrack_acdc_backinblack.jam_track_tracks.each do |track|
if track.master?
find('.jam-track-preview-holder[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="other"]')
find('.jam-track-preview-holder[data-id="' + track.id + '"] .instrument-name', text:'Master Mix')
else
find('.jam-track-preview-holder[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="' + track.instrument.id + '"]')
find('.jam-track-preview-holder[data-id="' + track.id + '"] .instrument-name', text:track.instrument.description)
end
end
find('a.white-bordered-button')['href'].should eq("/client?artist=#{jamtrack_acdc_backinblack.original_artist}#/jamtrack")
find('a.white-bordered-button').trigger(:click)
find('h1', text: 'jamtracks')
end
it "generic version" do
visit "/landing/jamtracks/acdc-backinblack?generic=true"
find('h1', text: 'We Have 100+ Amazing JamTracks, Check One Out!')
jamtrack_acdc_backinblack.jam_track_tracks.each do |track|
if track.master?
find('.jam-track-preview-holder[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="other"]')
find('.jam-track-preview-holder[data-id="' + track.id + '"] .instrument-name', text:'Master Mix')
else
find('.jam-track-preview-holder[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="' + track.instrument.id + '"]')
find('.jam-track-preview-holder[data-id="' + track.id + '"] .instrument-name', text:track.instrument.description)
end
end
find('a.white-bordered-button')['href'].should eq("/client#/jamtrack")
find('a.white-bordered-button').trigger(:click)
find('h1', text: 'jamtracks')
end
end
end

View File

@ -0,0 +1,122 @@
require 'spec_helper'
describe "Product Pages", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
before(:all) do
ShoppingCart.delete_all
JamTrackRight.delete_all
JamTrack.delete_all
JamTrackTrack.delete_all
JamTrackLicensor.delete_all
end
let(:user) { FactoryGirl.create(:user) }
let(:jamtrack_acdc_backinblack) { @jamtrack_acdc_backinblack }
let(:billing_info) {
{
first_name: 'Seth',
last_name: 'Call',
address1: '10704 Buckthorn Drive',
city: 'Austin',
state: 'Texas',
country: 'US',
zip: '78759',
number: '4111111111111111',
month: '08',
year: '2017',
verification_value: '012'
}
}
def create_account(user, billing_info)
@recurlyClient.create_account(user, billing_info)
@created_accounts << user
end
before(:all) do
@recurlyClient = RecurlyClient.new
@created_accounts = []
@jamtrack_acdc_backinblack = FactoryGirl.create(:jam_track, name: 'Back in Black', original_artist: 'AC/DC', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-acdc-backinblack')
# make sure plans are there
@recurlyClient.create_jam_track_plan(@jamtrack_acdc_backinblack) unless @recurlyClient.find_jam_track_plan(@jamtrack_acdc_backinblack)
end
after(:each) do
@created_accounts.each do |user|
if user.recurly_code
begin
@account = Recurly::Account.find(user.recurly_code)
if @account.present?
@account.destroy
end
rescue
end
end
end
end
describe "JamBlaster" do
it "logged out" do
visit "/products/jamblaster"
find('h1', text: 'The JamBlaster by JamKazam')
find('a.white-bordered-button')['href'].should eq("#") # nowhere to go yet
end
it "logged in" do
fast_signin(user, "/products/jamblaster")
find('h1', text: 'The JamBlaster by JamKazam')
find('a.white-bordered-button')['href'].should eq("#") # nowhere to go yet
end
end
describe "Platform" do
it "logged out" do
visit "/products/platform"
find('h1', text: 'The JamKazam Platform')
find('a.white-bordered-button').trigger(:click)
find('h2', text: 'Create your free JamKazam account')
end
it "logged in" do
fast_signin(user, "/products/platform")
find('h1', text: 'The JamKazam Platform')
find('a.white-bordered-button').trigger(:click)
# clicking /signup just redirects you to the client
find('h2', text: 'create session')
end
end
describe "JamTracks" do
it "logged out" do
visit "/products/jamtracks"
find('h1', text: 'JamTracks by JamKazam')
find('a.white-bordered-button').trigger(:click)
find('h1', text: 'jamtracks')
end
it "logged in" do
fast_signin(user, "/products/jamtracks")
find('h1', text: 'JamTracks by JamKazam')
find('a.white-bordered-button').trigger(:click)
find('h1', text: 'jamtracks')
end
end
end

File diff suppressed because it is too large Load Diff