Squashed commit of the following:
commit30965c6351Author: Seth Call <sethcall@gmail.com> Date: Tue Sep 15 05:23:27 2015 -0500 * allow jamblaster to fetch http commit5c8fb6b01eAuthor: Seth Call <sethcall@gmail.com> Date: Fri Sep 11 13:43:07 2015 -0500 * don't issue stop video in session end commit3e27680ea9Author: Seth Call <sethcall@gmail.com> Date: Fri Sep 11 13:40:34 2015 -0500 * decommision webcam_viewer in session page commitac1cc0c828Author: Seth Call <sethcall@gmail.com> Date: Thu Sep 10 07:24:42 2015 -0500 * VRFS-3541 - don't use HTML to store data sent to server for genre ID bug in profile commit004991119aAuthor: Seth Call <sethcall@gmail.com> Date: Wed Sep 9 15:10:51 2015 -0500 * set 'are you our user' cookie to do better job with ad tracking commit13a950e65fAuthor: Seth Call <sethcall@gmail.com> Date: Wed Sep 9 07:58:46 2015 -0500 * align disable vide obutton better commit9722c6cbc6Author: Seth Call <sethcall@gmail.com> Date: Wed Sep 9 07:45:18 2015 -0500 * whitesapce commit3976707b14Author: Seth Call <sethcall@gmail.com> Date: Wed Sep 9 07:13:51 2015 -0500 * check for video enabled better commitb483dd537fAuthor: Seth Call <sethcall@gmail.com> Date: Wed Sep 9 07:02:12 2015 -0500 * better text for video test commita4f465b6d1Author: Seth Call <sethcall@gmail.com> Date: Tue Sep 8 20:30:47 2015 -0500 * VRFS-3530, VRFS-3531 - allow user to test and disable video commitba99f88048Author: Seth Call <sethcall@gmail.com> Date: Tue Sep 8 10:05:26 2015 -0500 * VRFS-3534 - fix start recording API signature commit386ed8144cAuthor: Seth Call <sethcall@gmail.com> Date: Sun Sep 6 19:03:08 2015 -0500 * VRFS-3528 - make sure open jamtrack dialog passes 'show_purchased_only' commit6d010a561bAuthor: Seth Call <sethcall@gmail.com> Date: Fri Sep 4 20:43:15 2015 -0500 * deal with too-few tracks on landing page, and the 3rd CTA bubble clipping off text commit0076f0205aAuthor: Seth Call <sethcall@gmail.com> Date: Fri Sep 4 15:00:45 2015 -0500 * VRFS-352 - instrument-centric landing page commit3ee71634b3Author: Seth Call <sethcall@gmail.com> Date: Wed Sep 2 09:40:06 2015 -0500 * remove test stuff commitd07ac009bfAuthor: Seth Call <sethcall@gmail.com> Date: Tue Sep 1 08:11:35 2015 -0500 * VRFS-3509 - case where no device is configured handled commit9420cebad4Author: Seth Call <sethcall@gmail.com> Date: Sun Aug 30 05:00:00 2015 -0500 * VRFS-3494 - show popup when video window launches for the 1st time to offer guidance commitc3f81a4d23Author: Seth Call <sethcall@gmail.com> Date: Thu Aug 27 10:35:43 2015 -0500 * build bump commite782d5f9bbAuthor: Seth Call <sethcall@gmail.com> Date: Thu Aug 27 09:43:40 2015 -0500 * VRFS-3419 - check better for window opener commit36b6699cdeAuthor: Seth Call <sethcall@gmail.com> Date: Thu Aug 27 08:12:47 2015 -0500 * validate popup VRFS-3419 commit8948f0498fAuthor: Seth Call <sethcall@gmail.com> Date: Thu Aug 27 07:59:21 2015 -0500 * fix changed path commit2bce35d604Author: Seth Call <sethcall@gmail.com> Date: Wed Aug 26 20:38:34 2015 -0500 * fix jamtrack test commit63ef63c20dAuthor: Seth Call <sethcall@gmail.com> Date: Wed Aug 26 20:34:40 2015 -0500 * fix typo again in webcamViewer. need to go to bed commit8566cc5bc9Author: Seth Call <sethcall@gmail.com> Date: Wed Aug 26 20:31:34 2015 -0500 * fix typo added in webcamViewer commit22ea6e89fdAuthor: Seth Call <sethcall@gmail.com> Date: Wed Aug 26 20:26:39 2015 -0500 * VRFS-3488 - jamtrack search by artist and song need to pin to the match, not do a sloppy search commita4bd28e168Author: Seth Call <sethcall@gmail.com> Date: Wed Aug 26 16:43:34 2015 -0500 VRFS-3474 - watch for USB events and refresh video pages commitd2edfd22c5Author: Seth Call <sethcall@gmail.com> Date: Wed Aug 26 12:01:52 2015 -0500 * VRFS-3467 - previews are 20 seconds long indicator on jamtracks commitdefdfa8ce9Author: Seth Call <sethcall@gmail.com> Date: Wed Aug 26 06:04:53 2015 -0500 * VRFS-3473 - fix 'videoShared' state in webcamViewer commit090cfa17c0Merge:7560b34818596aAuthor: Seth Call <sethcall@gmail.com> Date: Tue Aug 25 14:53:35 2015 -0500 Merge branch 'develop' of bitbucket.org:jamkazam/jam-cloud into develop commit7560b340c7Author: Seth Call <sethcall@gmail.com> Date: Tue Aug 25 14:52:05 2015 -0500 * VRFS-3466 - updated frontend to pass in GUIDs commit1252dbe178Author: Seth Call <sethcall@gmail.com> Date: Tue Aug 25 05:28:15 2015 -0500 * use new bridge calls to handle current FPS and resolution VRFS-3428 commit818596ae36Author: Jonathan Kolyer <jonathan@jamkazam.com> Date: Tue Aug 25 08:23:52 2015 +0000 VRFS-3451 musician_search verifying instrument and genres inputs commit6918eaf095Author: Seth Call <sethcall@gmail.com> Date: Mon Aug 24 17:55:06 2015 -0500 more UI tweaks for video settup in account screen VRFS-3428 commitfc69242578Author: Seth Call <sethcall@gmail.com> Date: Mon Aug 24 16:18:31 2015 -0500 * VRFS-3427 - update FTUE to test video, not just audio commit729974013aAuthor: Seth Call <sethcall@gmail.com> Date: Mon Aug 24 16:17:53 2015 -0500 * VRFS-3428 - fix button text commitdb1f1d60d5Merge:04825d290c8d05Author: Seth Call <sethcall@gmail.com> Date: Mon Aug 24 15:56:42 2015 -0500 Merge branch 'feature/video_frontend' into develop commit04825d2659Author: Seth Call <sethcall@gmail.com> Date: Mon Aug 24 15:54:59 2015 -0500 * VRFS-3428 - update how we query backend for frame rates commit39d0731d74Author: Seth Call <sethcall@gmail.com> Date: Sat Aug 22 05:44:59 2015 -0500 * VRFS-3456 - remove special chars from search commit1874720ee8Author: Seth Call <sethcall@gmail.com> Date: Sat Aug 22 05:32:28 2015 -0500 * VRFS-3456 - protect special chars from tsquery commit29104ff09bAuthor: Seth Call <sethcall@gmail.com> Date: Fri Aug 21 05:02:48 2015 -0500 * VRFS-3446 - bug fix for no genre specified on join of session; also fix search bar in jamtrack dialog commit3b6d1febdbAuthor: Seth Call <sethcall@gmail.com> Date: Thu Aug 20 15:44:21 2015 -0500 * forget cta image commit6ac622853cAuthor: Seth Call <sethcall@gmail.com> Date: Thu Aug 20 15:02:55 2015 -0500 * VRFS-3449 - a little more tweaking of JamTrack landing page commitd7fcadcd0dAuthor: Seth Call <sethcall@gmail.com> Date: Thu Aug 20 14:49:07 2015 -0500 * VRFS-3450 - fix 'show all tracks' when pagination occurs by not doubleregistering commite7b50ca4a8Author: Seth Call <sethcall@gmail.com> Date: Thu Aug 20 14:19:07 2015 -0500 * VRFS-3449 - updates for direct landing pages commit0d075a9568Author: Seth Call <sethcall@gmail.com> Date: Thu Aug 20 09:19:17 2015 -0500 * fix spacing issue commit9c17d9a024Merge:98734500b67ef5Author: Seth Call <sethcall@gmail.com> Date: Thu Aug 20 09:06:48 2015 -0500 Merge branch 'develop' of bitbucket.org:jamkazam/jam-cloud into develop commit98734506dfAuthor: Seth Call <sethcall@gmail.com> Date: Thu Aug 20 09:06:36 2015 -0500 * VRFS-3448 - fix invisible downloader commit90c8d05d00Author: Seth Call <sethcall@gmail.com> Date: Wed Aug 19 14:17:10 2015 -0500 * wip commitbf4044d92eAuthor: Seth Call <sethcall@gmail.com> Date: Wed Aug 19 09:24:14 2015 -0500 * VRFS-3422 - don't die if the user has on sale_line_items commit87c62b4db2Author: Seth Call <sethcall@gmail.com> Date: Wed Aug 19 08:29:22 2015 -0500 * a fix for linux? hfa code commit3fa58715fcAuthor: Seth Call <sethcall@gmail.com> Date: Wed Aug 19 07:36:04 2015 -0500 * fix open jamtrack dialog for people with less than 10 jamtracks commitd045c94f54Author: Seth Call <sethcall@gmail.com> Date: Wed Aug 19 07:17:37 2015 -0500 * more HFA request polish commitdc343f10e3Author: Seth Call <sethcall@gmail.com> Date: Wed Aug 19 07:01:47 2015 -0500 * don't show free jamtrack notice on landing page if redeemed_jamtrack cookie is set commite6618da456Author: Seth Call <sethcall@gmail.com> Date: Tue Aug 18 21:29:15 2015 -0500 * fix a bug in figuring out if the user should be show GET IT FREE commit5ba03a2755Author: Seth Call <sethcall@gmail.com> Date: Tue Aug 18 20:41:37 2015 -0500 * VRFS-3431 - better response when creating HFA request commit37d6c3e57cAuthor: Seth Call <sethcall@gmail.com> Date: Tue Aug 18 15:19:40 2015 -0500 * add csv to dump released JamTracks commitf6101f3621Author: Seth Call <sethcall@gmail.com> Date: Tue Aug 18 14:26:41 2015 -0500 VRFS-3422, VRFS-3423, VRFS-3424, VRFS-3429 - JamTrack search/listing commit0b67ef5f52Author: Jonathan Kolyer <jonathan@jamkazam.com> Date: Sat Aug 15 15:03:00 2015 +0000 fixed test for instruments in musician search
This commit is contained in:
parent
900400f053
commit
2e4dfaa728
|
|
@ -0,0 +1,7 @@
|
|||
ActiveAdmin.register_page "CSVs" do
|
||||
menu :parent => 'Misc'
|
||||
|
||||
content do
|
||||
link_to('Released JamTracks', released_jamtracks_csv_path)
|
||||
end
|
||||
end
|
||||
|
|
@ -113,6 +113,21 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
|||
@user.delete_mod(User::MOD_GEAR, User::MOD_GEAR_FRAME_OPTIONS)
|
||||
end
|
||||
|
||||
|
||||
if params[:jam_ruby_user][:how_to_use_video_no_show].to_i == 1
|
||||
@user.mod_merge({User::MOD_NO_SHOW => {User::HOWTO_USE_VIDEO_NOSHOW => true}})
|
||||
else
|
||||
@user.delete_mod(User::MOD_NO_SHOW, User::HOWTO_USE_VIDEO_NOSHOW)
|
||||
end
|
||||
|
||||
|
||||
if params[:jam_ruby_user][:configure_video_no_show].to_i == 1
|
||||
@user.mod_merge({User::MOD_NO_SHOW => {User::CONFIGURE_VIDEO_NOSHOW=> true}})
|
||||
else
|
||||
@user.delete_mod(User::MOD_NO_SHOW, User::CONFIGURE_VIDEO_NOSHOW)
|
||||
end
|
||||
|
||||
|
||||
@user.save!
|
||||
|
||||
redirect_to edit_admin_user_path(@user)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
ActiveAdmin.register_page "Harry Fox Request" do
|
||||
menu :parent => 'JamTracks'
|
||||
|
||||
page_action :create_request, :method => :post do
|
||||
|
||||
name = params[:jam_ruby_jam_track_hfa_request][:name]
|
||||
|
||||
request = JamTrackHfaRequest.create(name)
|
||||
redirect_to admin_harry_fox_request_path, :notice => "Request created. Check Amazon S3 in the 'jamkazam' bucket; specifically #{request.request_csv_filename}"
|
||||
end
|
||||
|
||||
|
||||
content do
|
||||
semantic_form_for JamTrackHfaRequest.new, :url => admin_harry_fox_request_create_request_path, :builder => ActiveAdmin::FormBuilder do |f|
|
||||
f.inputs "New Harry Fox Licensing Request" do
|
||||
f.input :name, :hint => "Some sort of name to help us remember what this request was for"
|
||||
end
|
||||
f.actions
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -16,5 +16,4 @@ class EmailController < ApplicationController
|
|||
|
||||
@users = User.where(subscribe_email: true)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
require 'csv'
|
||||
|
||||
class JamTrackController < ApplicationController
|
||||
|
||||
respond_to :html
|
||||
|
||||
def dump_released
|
||||
headers['Content-Disposition'] = "attachment; filename=\"released-jam-tracks.csv\""
|
||||
headers['Content-Type'] ||= 'text/csv'
|
||||
|
||||
@jam_tracks = JamTrack.where(status: 'Production')
|
||||
render "jam_track/dump_released", :layout => nil
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -9,4 +9,7 @@
|
|||
= f.input :musician
|
||||
= f.inputs "Gear Mods" do
|
||||
= f.input :show_frame_options, as: :boolean
|
||||
= f.inputs "Do Not Shows" do
|
||||
= f.input :how_to_use_video_no_show, as: :boolean
|
||||
= f.input :configure_video_no_show, as: :boolean
|
||||
= f.actions
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
<%- headers = ['Artist Name', 'Song Name', 'Direct Landing', 'Generic Direct Landing', 'Band Landing'] -%>
|
||||
<%= CSV.generate_line headers %><%- @jam_tracks.each do |jam_track| -%><%= CSV.generate_line([jam_track.original_artist, jam_track.name,
|
||||
"https://www.jamkazam.com/landing/jamtracks/#{jam_track.slug}",
|
||||
"https://www.jamkazam.com/landing/jamtracks/#{jam_track.slug}?generic=1",
|
||||
"https://www.jamkazam.com/landing/jamtracks/band/#{jam_track.slug}"
|
||||
]) %><%- end -%>
|
||||
|
|
@ -27,4 +27,13 @@
|
|||
def show_frame_options
|
||||
self.get_gear_mod(MOD_GEAR_FRAME_OPTIONS)
|
||||
end
|
||||
|
||||
|
||||
def how_to_use_video_no_show
|
||||
self.get_no_show_mod(HOWTO_USE_VIDEO_NOSHOW)
|
||||
end
|
||||
|
||||
def configure_video_no_show
|
||||
self.get_no_show_mod(CONFIGURE_VIDEO_NOSHOW)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,9 +8,6 @@ JamAdmin::Application.routes.draw do
|
|||
|
||||
devise_for :users, :class_name => "JamRuby::User", :path_prefix => '/admin', :path => '', :path_names => {:sign_in => 'login', :sign_out => 'logout'}
|
||||
|
||||
|
||||
|
||||
|
||||
scope ENV['RAILS_RELATIVE_URL_ROOT'] || '/' do
|
||||
root :to => "admin/dashboard#index"
|
||||
|
||||
|
|
@ -28,13 +25,12 @@ JamAdmin::Application.routes.draw do
|
|||
|
||||
ActiveAdmin.routes(self)
|
||||
|
||||
|
||||
|
||||
match '/api/artifacts' => 'artifacts#update_artifacts', :via => :post
|
||||
match '/api/mix/:id/enqueue' => 'admin/mixes#mix_again', :via => :post
|
||||
match '/api/checks/latency_tester' => 'checks#check_latency_tester', :via => :get
|
||||
|
||||
match '/api/users/emailables/:code' => 'email#dump_emailables', :via => :get
|
||||
match '/api/jam_tracks/released' => 'jam_track#dump_released', :via => :get, as: 'released_jamtracks_csv'
|
||||
|
||||
mount Resque::Server.new, :at => "/resque"
|
||||
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ FactoryGirl.define do
|
|||
factory :jam_track, :class => JamRuby::JamTrack do
|
||||
sequence(:name) { |n| "jam-track-#{n}" }
|
||||
sequence(:description) { |n| "description-#{n}" }
|
||||
sequence(:slug) { |n| "slug-#{n}" }
|
||||
time_signature '4/4'
|
||||
status 'Production'
|
||||
recording_type 'Cover'
|
||||
|
|
|
|||
|
|
@ -303,3 +303,6 @@ jam_track_onboarding_enhancements.sql
|
|||
jam_track_name_drop_unique.sql
|
||||
populate_languages.sql
|
||||
populate_subjects.sql
|
||||
jam_track_searchability.sql
|
||||
harry_fox_agency.sql
|
||||
jam_track_slug.sql
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
ALTER TABLE jam_tracks ADD COLUMN server_fixation_date DATE DEFAULT NOW();
|
||||
ALTER TABLE jam_tracks ADD COLUMN hfa_license_status BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE jam_tracks ADD COLUMN hfa_license_desired BOOLEAN DEFAULT TRUE;
|
||||
ALTER TABLE jam_tracks ADD COLUMN alternative_license_status BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE jam_tracks ADD COLUMN hfa_license_number INTEGER;
|
||||
ALTER TABLE jam_tracks ADD COLUMN hfa_song_code VARCHAR;
|
||||
ALTER TABLE jam_tracks ADD COLUMN album_title VARCHAR;
|
||||
|
||||
CREATE TABLE jam_track_hfa_requests (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR NOT NULL,
|
||||
request_csv_filename VARCHAR,
|
||||
response_csv_filename VARCHAR,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
approved_at TIMESTAMP,
|
||||
received_at TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE jam_track_hfa_request_ids (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
jam_track_id VARCHAR(64) NOT NULL REFERENCES jam_tracks(id) ON DELETE SET NULL,
|
||||
jam_track_hfa_request_id INTEGER REFERENCES jam_track_hfa_requests(id) ON DELETE SET NULL,
|
||||
request_id INTEGER,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
ALTER TABLE jam_tracks ADD COLUMN search_tsv tsvector;
|
||||
ALTER TABLE jam_tracks ADD COLUMN artist_tsv tsvector;
|
||||
ALTER TABLE jam_tracks ADD COLUMN name_tsv tsvector;
|
||||
|
||||
CREATE FUNCTION jam_tracks_update_tsv() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
new.search_tsv = to_tsvector('public.jamenglish', COALESCE(NEW.original_artist, '') || ' ' || COALESCE(NEW.name, '') || ' ' || COALESCE(NEW.additional_info, ''));
|
||||
new.artist_tsv = to_tsvector('public.jamenglish', COALESCE(NEW.original_artist, ''));
|
||||
new.name_tsv = to_tsvector('public.jamenglish', COALESCE(NEW.name, ''));
|
||||
|
||||
RETURN NEW;
|
||||
END
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
|
||||
ON jam_tracks FOR EACH ROW EXECUTE PROCEDURE
|
||||
jam_tracks_update_tsv();
|
||||
|
||||
CREATE INDEX jam_tracks_search_tsv_index ON jam_tracks USING gin(search_tsv);
|
||||
CREATE INDEX jam_tracks_artist_tsv_index ON jam_tracks USING gin(artist_tsv);
|
||||
CREATE INDEX jam_tracks_name_tsv_index ON jam_tracks USING gin(name_tsv);
|
||||
|
||||
CREATE INDEX jam_tracks_name_key ON jam_tracks USING btree (name);
|
||||
CREATE INDEX jam_tracks_original_artist_key ON jam_tracks USING btree (original_artist);
|
||||
CREATE INDEX jam_tracks_status_key ON jam_tracks USING btree (status);
|
||||
|
||||
|
||||
UPDATE jam_tracks SET original_artist=original_artist, name=name, additional_info=additional_info;
|
||||
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE jam_tracks ADD COLUMN slug VARCHAR(2000) UNIQUE;
|
||||
|
|
@ -203,6 +203,8 @@ require "jam_ruby/models/email_batch_scheduled_sessions"
|
|||
require "jam_ruby/models/email_batch_set"
|
||||
require "jam_ruby/models/jam_track_licensor"
|
||||
require "jam_ruby/models/jam_track"
|
||||
require "jam_ruby/models/jam_track_hfa_request"
|
||||
require "jam_ruby/models/jam_track_hfa_request_id"
|
||||
require "jam_ruby/models/jam_track_track"
|
||||
require "jam_ruby/models/jam_track_right"
|
||||
require "jam_ruby/models/jam_track_tap_in"
|
||||
|
|
|
|||
|
|
@ -182,6 +182,25 @@ module JamRuby
|
|||
finish("success", nil)
|
||||
end
|
||||
|
||||
def add_licensor_metadata(vendor, metalocation)
|
||||
Dir.mktmpdir do |tmp_dir|
|
||||
@@log.debug("update vendor metadata")
|
||||
meta_yml = File.join(tmp_dir, 'meta.yml')
|
||||
if jamkazam_s3_manager.exists?(metalocation)
|
||||
jamkazam_s3_manager.download(metalocation, meta_yml)
|
||||
meta = YAML.load_file(meta_yml)
|
||||
else
|
||||
meta = {}
|
||||
end
|
||||
|
||||
meta[:licensor] = vendor
|
||||
|
||||
File.open(meta_yml, 'w') {|f| f.write meta.to_yaml }
|
||||
|
||||
jamkazam_s3_manager.upload(metalocation, meta_yml)
|
||||
end
|
||||
end
|
||||
|
||||
def is_tency_storage?
|
||||
assert_storage_set
|
||||
@storage_format == 'Tency'
|
||||
|
|
@ -421,10 +440,16 @@ module JamRuby
|
|||
jam_track.sales_region = 'Worldwide'
|
||||
jam_track.recording_type = 'Cover'
|
||||
jam_track.description = "This is a JamTrack audio file for use exclusively with the JamKazam service. This JamTrack is a high quality cover of the #{jam_track.original_artist} song \"#{jam_track.name}\"."
|
||||
jam_track.hfa_license_status = false
|
||||
jam_track.alternative_license_status = false
|
||||
jam_track.hfa_license_desired = true
|
||||
jam_track.server_fixation_date = Time.now
|
||||
jam_track.slug = metadata['slug'] || jam_track.generate_slug
|
||||
|
||||
if is_tency_storage?
|
||||
jam_track.vendor_id = metadata[:id]
|
||||
jam_track.licensor = JamTrackLicensor.find_by_name('Tency Music')
|
||||
#add_licensor_metadata('Tency Music', metalocation)
|
||||
end
|
||||
else
|
||||
if !options[:resync_audio]
|
||||
|
|
@ -1482,6 +1507,14 @@ module JamRuby
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
def add_tency_metadata
|
||||
JamTrackLicensor.find_by_name('Tency Music').jam_tracks.each do |jam_track|
|
||||
jam_track_importer = JamTrackImporter.new(@storage_format)
|
||||
jam_track_importer.add_licensor_metadata('Tency Music', jam_track.metalocation)
|
||||
break
|
||||
end
|
||||
end
|
||||
def create_masters
|
||||
iterate_song_storage do |metadata, metalocation|
|
||||
next if metadata.nil?
|
||||
|
|
@ -1711,11 +1744,20 @@ module JamRuby
|
|||
def remove_s3_special_chars(filename)
|
||||
filename.tr('/&@:,$=+?;\^`><{}[]#%~|', '')
|
||||
end
|
||||
|
||||
def generate_slugs
|
||||
JamTrack.all.each do |jam_track|
|
||||
jam_track.generate_slug
|
||||
jam_track.save!
|
||||
end
|
||||
end
|
||||
|
||||
def onboarding_exceptions
|
||||
JamTrack.all.each do |jam_track|
|
||||
jam_track.onboarding_exceptions
|
||||
end
|
||||
end
|
||||
|
||||
def synchronize_all(options)
|
||||
importers = []
|
||||
|
||||
|
|
@ -1732,7 +1774,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
if count > 500
|
||||
break
|
||||
#break
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ module JamRuby
|
|||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
def s3_manager(options={:bucket => nil, :public => false})
|
||||
@s3_manager ||= S3Manager.new(options[:bucket] ? options[:bucket] : (options[:public] ? APP_CONFIG.aws_bucket_public : APP_CONFIG.aws_bucket), APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
|
||||
end
|
||||
end
|
||||
|
||||
def s3_manager(options={:bucket => nil, :public => false})
|
||||
|
|
|
|||
|
|
@ -102,27 +102,39 @@ module JamRuby
|
|||
def self.search_target_class
|
||||
end
|
||||
|
||||
# FIXME: SQL INJECTION
|
||||
def _genres(rel, query_data=json)
|
||||
gids = query_data[KEY_GENRES]
|
||||
unless gids.blank?
|
||||
allgids = Genre.order(:id).pluck(:id)
|
||||
gids = gids.select { |gg| allgids.index(gg).present? }
|
||||
|
||||
unless gids.blank?
|
||||
gidsql = gids.join("','")
|
||||
gpsql = "SELECT player_id FROM genre_players WHERE (player_type = '#{self.class.search_target_class.name}' AND genre_id IN ('#{gidsql}'))"
|
||||
rel = rel.where("#{self.class.search_target_class.table_name}.id IN (#{gpsql})")
|
||||
end
|
||||
end
|
||||
rel
|
||||
end
|
||||
|
||||
# FIXME: SQL INJECTION
|
||||
def _instruments(rel, query_data=json)
|
||||
unless (instruments = query_data[KEY_INSTRUMENTS]).blank?
|
||||
instrids = Instrument.order(:id).pluck(:id)
|
||||
instruments = instruments.select { |ii| instrids.index(ii['instrument_id']).present? }
|
||||
|
||||
unless instruments.blank?
|
||||
instsql = "SELECT player_id FROM musicians_instruments WHERE (("
|
||||
instsql += instruments.collect do |inst|
|
||||
"instrument_id = '#{inst['instrument_id']}' AND proficiency_level = #{inst['proficiency_level']}"
|
||||
unless MusicianInstrument::PROFICIENCY_RANGE === (proflvl=inst['proficiency_level'].to_i)
|
||||
proflvl = MusicianInstrument::LEVEL_INTERMEDIATE
|
||||
end
|
||||
"instrument_id = '#{inst['instrument_id']}' AND proficiency_level = #{proflvl}"
|
||||
end.join(") OR (")
|
||||
instsql += "))"
|
||||
|
||||
rel = rel.where("#{self.class.search_target_class.table_name}.id IN (#{instsql})")
|
||||
end
|
||||
end
|
||||
rel
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ module JamRuby
|
|||
:original_artist, :songwriter, :publisher, :licensor, :licensor_id, :pro, :genres_jam_tracks_attributes, :sales_region, :price,
|
||||
:reproduction_royalty, :public_performance_royalty, :reproduction_royalty_amount,
|
||||
:licensor_royalty_amount, :pro_royalty_amount, :plan_code, :initial_play_silence, :jam_track_tracks_attributes,
|
||||
:jam_track_tap_ins_attributes, :genre_ids, :version, :jmep_json, :jmep_text, :pro_ascap, :pro_bmi, :pro_sesac, :duration, as: :admin
|
||||
:jam_track_tap_ins_attributes, :genre_ids, :version, :jmep_json, :jmep_text, :pro_ascap, :pro_bmi, :pro_sesac, :duration,
|
||||
:server_fixation_date, :hfa_license_status, :hfa_license_desired, :alternative_license_status, :hfa_license_number, :hfa_song_code, :album_title, as: :admin
|
||||
|
||||
validates :name, presence: true, length: {maximum: 200}
|
||||
validates :plan_code, presence: true, uniqueness: true, length: {maximum: 50 }
|
||||
|
|
@ -38,6 +39,13 @@ module JamRuby
|
|||
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 :hfa_license_status, inclusion: {in: [true, false]}
|
||||
validates :hfa_license_desired, inclusion: {in: [true, false]}
|
||||
validates :alternative_license_status, inclusion: {in: [true, false]}
|
||||
validates :hfa_license_number, numericality: {only_integer: true}, :allow_nil => true
|
||||
validates :hfa_song_code, length: {maximum: 200}
|
||||
validates :album_title, length: {maximum: 200}
|
||||
validates :slug, uniqueness: true
|
||||
|
||||
validates_format_of :reproduction_royalty_amount, with: /^\d+\.*\d{0,4}$/, :allow_blank => true
|
||||
validates_format_of :licensor_royalty_amount, with: /^\d+\.*\d{0,4}$/, :allow_blank => true
|
||||
|
|
@ -215,6 +223,28 @@ module JamRuby
|
|||
JamTrack.where("original_artist=?", artist_name).all
|
||||
end
|
||||
|
||||
# special case of index
|
||||
def autocomplete(options, user)
|
||||
|
||||
if options[:match].blank?
|
||||
return {artists: [], songs: []}
|
||||
end
|
||||
|
||||
options[:show_purchased_only] = options[:show_purchased_only]
|
||||
|
||||
options[:limit] = options[:limit] || 5
|
||||
|
||||
options[:artist_search] = options[:match]
|
||||
artists, pager = artist_index(options, user)
|
||||
|
||||
options.delete(:artist_search)
|
||||
options[:song_search] = options[:match]
|
||||
options[:sort_by] = 'jamtrack'
|
||||
songs, pager = index(options, user)
|
||||
|
||||
{artists: artists, songs:songs}
|
||||
end
|
||||
|
||||
def index(options, user)
|
||||
if options[:page]
|
||||
page = options[:page].to_i
|
||||
|
|
@ -252,10 +282,35 @@ module JamRuby
|
|||
query = query.where("jam_track_rights.user_id = ?", user.id)
|
||||
end
|
||||
|
||||
if options[:search]
|
||||
tsquery = Search.create_tsquery(options[:search])
|
||||
if tsquery
|
||||
query = query.where("(search_tsv @@ to_tsquery('jamenglish', ?))", tsquery)
|
||||
end
|
||||
end
|
||||
|
||||
if options[:artist_search]
|
||||
tsquery = Search.create_tsquery(options[:artist_search])
|
||||
if tsquery
|
||||
query = query.where("(artist_tsv @@ to_tsquery('jamenglish', ?))", tsquery)
|
||||
end
|
||||
end
|
||||
|
||||
if options[:song_search]
|
||||
tsquery = Search.create_tsquery(options[:song_search])
|
||||
if tsquery
|
||||
query = query.where("(name_tsv @@ to_tsquery('jamenglish', ?))", tsquery)
|
||||
end
|
||||
end
|
||||
|
||||
if options[:artist].present?
|
||||
query = query.where("original_artist=?", options[:artist])
|
||||
end
|
||||
|
||||
if options[:song].present?
|
||||
query = query.where("name=?", options[:song])
|
||||
end
|
||||
|
||||
if options[:id].present?
|
||||
query = query.where("jam_tracks.id=?", options[:id])
|
||||
end
|
||||
|
|
@ -266,10 +321,16 @@ module JamRuby
|
|||
query = query.order('jam_tracks.original_artist')
|
||||
else
|
||||
query = query.group("jam_tracks.id")
|
||||
if options[:sort_by] == 'jamtrack'
|
||||
query = query.order('jam_tracks.name')
|
||||
else
|
||||
query = query.order('jam_tracks.original_artist, jam_tracks.name')
|
||||
end
|
||||
|
||||
query = query.where("jam_tracks.status = ?", 'Production') unless user.admin
|
||||
|
||||
end
|
||||
|
||||
query = query.where("jam_tracks.status = ?", 'Production') unless user && user.admin
|
||||
|
||||
unless options[:genre].blank?
|
||||
query = query.joins(:genres)
|
||||
|
|
@ -279,13 +340,14 @@ module JamRuby
|
|||
query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}' and jam_track_tracks.track_type != 'Master'") unless options[:instrument].blank?
|
||||
query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?
|
||||
|
||||
count = query.total_entries
|
||||
|
||||
if query.length == 0
|
||||
[query, nil]
|
||||
if count == 0
|
||||
[query, nil, count]
|
||||
elsif query.length < limit
|
||||
[query, nil]
|
||||
[query, nil, count]
|
||||
else
|
||||
[query, start + limit]
|
||||
[query, start + limit, count]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -327,6 +389,19 @@ module JamRuby
|
|||
|
||||
query = query.where("jam_tracks.status = ?", 'Production') unless user.admin
|
||||
|
||||
if options[:show_purchased_only]
|
||||
query = query.joins(:jam_track_rights)
|
||||
query = query.where("jam_track_rights.user_id = ?", user.id)
|
||||
end
|
||||
|
||||
if options[:artist_search]
|
||||
tsquery = Search.create_tsquery(options[:artist_search])
|
||||
if tsquery
|
||||
query = query.where("(artist_tsv @@ to_tsquery('jamenglish', ?))", tsquery)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
unless options[:genre].blank?
|
||||
query = query.joins(:genres)
|
||||
query = query.where('genre_id = ? ', options[:genre])
|
||||
|
|
@ -368,5 +443,15 @@ module JamRuby
|
|||
plan_code[prefix.length..-1]
|
||||
end
|
||||
|
||||
# http://stackoverflow.com/questions/4308377/ruby-post-title-to-slug
|
||||
def sluggarize(field)
|
||||
field.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
|
||||
end
|
||||
|
||||
def generate_slug
|
||||
self.slug = sluggarize(original_artist) + '-' + sluggarize(name)
|
||||
puts "Self.slug #{self.slug}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
module JamRuby
|
||||
class JamTrackHfaRequest < ActiveRecord::Base
|
||||
include JamRuby::S3ManagerMixin
|
||||
|
||||
|
||||
@@log = Logging.logger[JamTrackHfaRequest]
|
||||
|
||||
attr_accessible :name, as: :admin
|
||||
|
||||
validates :name, presence: true, length: {maximum: 200}
|
||||
|
||||
# look through all jam_track requests, and find the highest one that is associated with an approved harry fox requests
|
||||
def self.find_max()
|
||||
max = JamTrackHfaRequestId.select('coalesce(max(request_id), 0) as max').joins('INNER JOIN jam_track_hfa_requests ON jam_track_hfa_requests.id = jam_track_hfa_request_id').where('received_at IS NOT NULL').first()['max']
|
||||
max.to_i
|
||||
end
|
||||
|
||||
def self.create(name)
|
||||
request = nil
|
||||
|
||||
transaction do
|
||||
|
||||
max = find_max()
|
||||
|
||||
start = max + 1
|
||||
|
||||
request = JamTrackHfaRequest.new
|
||||
request.name = name
|
||||
request.save!
|
||||
request.reload
|
||||
|
||||
requests = []
|
||||
JamTrack.where(hfa_license_status: false).where(hfa_license_desired: true).where(alternative_license_status: false).each do |jam_track|
|
||||
request_id = JamTrackHfaRequestId.new
|
||||
request_id.jam_track = jam_track
|
||||
request_id.jam_track_hfa_request = request
|
||||
request_id.request_id = start
|
||||
start += 1
|
||||
request_id.save
|
||||
request_id.reload # to get back the request_id attribute
|
||||
requests << request_id
|
||||
end
|
||||
|
||||
request_name = "JamKazam-#{request.id}-#{request.created_at.to_date.to_s}.csv"
|
||||
Dir.mktmpdir do |tmp_dir|
|
||||
out = File.join(tmp_dir, request_name)
|
||||
|
||||
# Field 1 - HFA Agreement Code - Hardcode to "SSA".
|
||||
# Field 2 - Manufacturer Number - Hardcode to "M18303".
|
||||
# Field 3 - Transaction Date - Populate this field with the date that we generate this tab-delimited file, in the format YYYYMMDD - e.g. "20150813".
|
||||
# Field 4 - License Request Number - This one is slightly more involved. Basically, according to HFA we need to generate a unique numeric ID for each JamTrack license request (as opposed to each unique JamTrack, as we might need to make more than one request per JamTrack if such requests were to fail in some cases). This unique numeric ID per request should start with the number 1, and increment by 1. So I guess this feature will need to remember which of these IDs are used on each run it makes so that it knows where to start on the next run.
|
||||
# Field 7 - Total Playing Time - Minutes - We already have a JamTrack field for the duration of the JamTrack in seconds. We should keep that field, and keep using it as is. We need to use that field to populate this Field 7 and the next Field 8. So if the duration of the JamTrack in seconds were 90 seconds, then we should set Field 7 to "1" and Field 8 to "30" to signify a length of 1:30.
|
||||
# Field 8 - Total Playing Time - Seconds - See note above on Field 7.
|
||||
# Field 9 - Artist Name - Populate this field from the Artist Name field in the JamTrack record - e.g. "AC/DC".
|
||||
# Field 10 - Song Title - Populate this field from the Song Name field in the JamTrack record - e.g. "Back In Black".
|
||||
# Field 21 - Configuration Code - Hardcode to "SP".
|
||||
# Field 22 - License Type - Hardcode to "G".
|
||||
# Field 23 - Server Fixation Date - Set this to the approximate date that the JamTrack was uploaded to our servers, and format as YYYYMMDD - e.g. "20150813". I'm suggesting we update each JamTrack record with this date, just so that we have a record of this piece of data we submitted to HFA - even though HFA didn't seem at all clear about how this data is used or why it matters.
|
||||
# Field 24 - Rate Code - Hardcode to "S".
|
||||
# Field 37 - User Defined - Populate this field with our internal JamKazam unique JamTrack ID. This field value is supposed to be passed back to us from HFA in the processed output file, and we'll need this to associate the HFA License Number with our internal JamTrack ID.
|
||||
# Field 38 - Track ID - Let's also populate this field with our internal JamKazam unique JamTrack ID, just like Field 37, just for fun.
|
||||
|
||||
|
||||
CSV.open(out, "wb") do |csv|
|
||||
requests.each do |request|
|
||||
line = {}
|
||||
line['1'] = 'SSA'
|
||||
line['2'] = 'M18303'
|
||||
line['3'] = Time.now.to_date.strftime('%Y%m%d')
|
||||
line['4'] = request.request_id
|
||||
line['7'] = request.jam_track.duration / 60
|
||||
line['8'] = request.jam_track.duration % 60
|
||||
line['9'] = request.jam_track.original_artist
|
||||
line['10'] = request.jam_track.name
|
||||
line['21'] = 'SP'
|
||||
line['22'] = 'G'
|
||||
line['23'] = request.jam_track.server_fixation_date.strftime('%Y%m%d')
|
||||
line['24'] = 'S'
|
||||
line['37'] = request.jam_track.id
|
||||
line['38'] = request.jam_track.id
|
||||
|
||||
entry = []
|
||||
38.times do |i|
|
||||
entry << line[(i + 1).to_s]
|
||||
end
|
||||
csv << entry
|
||||
end
|
||||
end
|
||||
|
||||
upload_path = "harry_fox_requests/#{request_name}"
|
||||
s3_manager.upload(upload_path, out, content_type: 'text/csv')
|
||||
|
||||
request.request_csv_filename = upload_path
|
||||
request.save!
|
||||
end
|
||||
|
||||
|
||||
request
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
module JamRuby
|
||||
class JamTrackHfaRequestId < ActiveRecord::Base
|
||||
include JamRuby::S3ManagerMixin
|
||||
|
||||
@@log = Logging.logger[JamTrackHfaRequestId]
|
||||
|
||||
attr_accessible :name, as: :admin
|
||||
|
||||
belongs_to :jam_track, class_name: "JamRuby::JamTrack"
|
||||
belongs_to :jam_track_hfa_request, class_name: "JamRuby::JamTrackHfaRequest"
|
||||
|
||||
validates :jam_track, presence: true
|
||||
validates :jam_track_hfa_request, presence:true
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -99,9 +99,9 @@ module JamRuby
|
|||
# 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, bitrate=48)
|
||||
def sign_url(expiration_time = 120, bitrate=48, secure=true)
|
||||
field_name = (bitrate==48) ? "url_48" : "url_44"
|
||||
s3_manager.sign_url(self[field_name], {:expires => expiration_time, :secure => true})
|
||||
s3_manager.sign_url(self[field_name], {:expires => expiration_time, :secure => secure})
|
||||
end
|
||||
|
||||
def delete_s3_files
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ module JamRuby
|
|||
belongs_to :player, polymorphic: true
|
||||
belongs_to :instrument, :class_name => "JamRuby::Instrument"
|
||||
|
||||
LEVEL_BEGIN = 1
|
||||
LEVEL_INTERMEDIATE = 2
|
||||
LEVEL_EXPERT = 3
|
||||
PROFICIENCY_RANGE = (LEVEL_BEGIN..LEVEL_EXPERT)
|
||||
|
||||
def description
|
||||
@description = self.instrument.description
|
||||
end
|
||||
|
|
|
|||
|
|
@ -151,6 +151,14 @@ module JamRuby
|
|||
sale.recurly_total_in_cents = 0
|
||||
sale.recurly_currency = 'USD'
|
||||
|
||||
if sale.sale_line_items.count == 0
|
||||
@@log.info("no sale line items associated with sale")
|
||||
# we must have ditched some of the sale items. let's just abort this sale
|
||||
sale.destroy
|
||||
sale = nil
|
||||
return sale
|
||||
end
|
||||
|
||||
sale_line_item = sale.sale_line_items[0]
|
||||
sale_line_item.recurly_tax_in_cents = 0
|
||||
sale_line_item.recurly_total_in_cents = 0
|
||||
|
|
|
|||
|
|
@ -130,15 +130,18 @@ module JamRuby
|
|||
|
||||
args = nil
|
||||
search_terms.each do |search_term|
|
||||
# remove ( ) ! : from query terms. parser blows up
|
||||
search_term.gsub!(/[\(\)!:]/, '')
|
||||
if args == nil
|
||||
args = search_term
|
||||
args = '"' + search_term + '"'
|
||||
else
|
||||
args = args + " & " + search_term
|
||||
args = args + " & " + '"' + search_term + '"'
|
||||
end
|
||||
end
|
||||
args = args + ":*"
|
||||
args
|
||||
end
|
||||
|
||||
def order_param(params, keys=M_ORDERING_KEYS)
|
||||
ordering = params[:orderby]
|
||||
ordering.blank? ? keys[0] : keys.detect { |oo| oo.to_s == ordering }
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ module JamRuby
|
|||
MOD_GEAR_FRAME_OPTIONS = "show_frame_options"
|
||||
|
||||
MOD_NO_SHOW = "no_show"
|
||||
HOWTO_USE_VIDEO_NOSHOW = 'how-to-use-video'
|
||||
CONFIGURE_VIDEO_NOSHOW = 'configure-video'
|
||||
|
||||
# MIN/MAX AUDIO LATENCY
|
||||
MINIMUM_AUDIO_LATENCY = 2
|
||||
|
|
|
|||
|
|
@ -736,6 +736,7 @@ FactoryGirl.define do
|
|||
factory :jam_track, :class => JamRuby::JamTrack do
|
||||
sequence(:name) { |n| "jam-track-#{n}" }
|
||||
sequence(:description) { |n| "description-#{n}" }
|
||||
sequence(:slug) { |n| "slug-#{n}" }
|
||||
time_signature '4/4'
|
||||
status 'Production'
|
||||
recording_type 'Cover'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe JamTrackHfaRequest do
|
||||
include CarrierWave::Test::Matchers
|
||||
include UsesTempFiles
|
||||
|
||||
let(:jamtrack1) {FactoryGirl.create(:jam_track, duration: 90, server_fixation_date: Time.now.to_date ) }
|
||||
|
||||
it "creates request" do
|
||||
|
||||
|
||||
JamTrackHfaRequest.find_max().should eq(0)
|
||||
jamtrack1.touch
|
||||
JamTrackHfaRequest.create('request1')
|
||||
|
||||
request = JamTrackHfaRequest.first
|
||||
request.request_csv_filename.should_not be_nil
|
||||
request.approved_at.should be_nil
|
||||
request.received_at.should be_nil
|
||||
|
||||
request_id = JamTrackHfaRequestId.first
|
||||
request_id.request_id.should_not be_nil
|
||||
|
||||
# as long as the previous request is not approved, we don't move on with the counter
|
||||
JamTrackHfaRequest.find_max().should eq(0)
|
||||
|
||||
request.received_at = Time.now
|
||||
request.save!
|
||||
|
||||
# but once it's approved, we move on
|
||||
JamTrackHfaRequest.find_max().should eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -159,8 +159,8 @@ describe JamTrack do
|
|||
|
||||
describe "index" do
|
||||
it "empty query" do
|
||||
query, pager = JamTrack.index({}, user)
|
||||
query.size.should == 0
|
||||
query, pager, count = JamTrack.index({}, user)
|
||||
count.should == 0
|
||||
end
|
||||
|
||||
it "sorts by name" do
|
||||
|
|
@ -190,24 +190,24 @@ describe JamTrack do
|
|||
jam_track1.save!
|
||||
jam_track2.save!
|
||||
|
||||
query, pager = JamTrack.index({genre: 'rock'}, user)
|
||||
query.size.should == 1
|
||||
query, pager, count = JamTrack.index({genre: 'rock'}, user)
|
||||
count.should == 1
|
||||
query[0].should eq(jam_track1)
|
||||
|
||||
query, pager = JamTrack.index({genre: 'asian'}, user)
|
||||
query.size.should == 1
|
||||
query, pager, count = JamTrack.index({genre: 'asian'}, user)
|
||||
count.should == 1
|
||||
query[0].should eq(jam_track2)
|
||||
|
||||
query, pager = JamTrack.index({genre: 'african'}, user)
|
||||
query.size.should == 0
|
||||
query, pager, count = JamTrack.index({genre: 'african'}, user)
|
||||
count.should == 0
|
||||
end
|
||||
|
||||
it "supports showing purchased only" do
|
||||
jam_track1 = FactoryGirl.create(:jam_track_with_tracks, name: 'a')
|
||||
|
||||
# no results yet
|
||||
query, pager = JamTrack.index({show_purchased_only:true}, user)
|
||||
query.size.should == 0
|
||||
query, pager, count = JamTrack.index({show_purchased_only:true}, user)
|
||||
count.should == 0
|
||||
|
||||
# but after the user buys it, it is returned
|
||||
FactoryGirl.create(:jam_track_right, jam_track: jam_track1, user: user)
|
||||
|
|
@ -215,6 +215,25 @@ describe JamTrack do
|
|||
query.size.should == 1
|
||||
query[0].should eq(jam_track1)
|
||||
end
|
||||
|
||||
it "full text search" do
|
||||
jam_track1 = FactoryGirl.create(:jam_track_with_tracks, name: 'Take a Chance On Me', original_artist: 'ABBA')
|
||||
jam_track2 = FactoryGirl.create(:jam_track_with_tracks, name: 'Nothing Chance', original_artist: 'ABBA')
|
||||
|
||||
query, pager = JamTrack.index({search: 'Take'}, user)
|
||||
query.size.should == 1
|
||||
query[0].should eq(jam_track1)
|
||||
|
||||
query, pager = JamTrack.index({search: 'ABB'}, user)
|
||||
query.size.should == 2
|
||||
|
||||
query, pager = JamTrack.index({search: 'Chance'}, user)
|
||||
query.size.should == 2
|
||||
|
||||
query, pager = JamTrack.index({search: 'Chan'}, user)
|
||||
query.size.should == 2
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
describe "validations" do
|
||||
|
|
|
|||
|
|
@ -172,8 +172,9 @@ describe 'Musician Search Model' do
|
|||
end
|
||||
|
||||
it "gets expected number of users" do
|
||||
instjson = [{ id: Instrument.first.id, level: 2 },
|
||||
{ id: Instrument.first(2)[1].id, level: 2 }
|
||||
instjson = [{ instrument_id: Instrument.first.id, proficiency_level: 2 },
|
||||
{ instrument_id: Instrument.first(2)[1].id, proficiency_level: 2 },
|
||||
{ instrument_id: 'foo', proficiency_level: 2 },
|
||||
]
|
||||
search.update_json_value(MusicianSearch::KEY_INSTRUMENTS, instjson)
|
||||
expect(search.do_search.count).to eq(3)
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ gem 'react-rails', '~> 1.0'
|
|||
source 'https://rails-assets.org' do
|
||||
gem 'rails-assets-reflux'
|
||||
gem 'rails-assets-classnames'
|
||||
gem 'rails-assets-react-select'
|
||||
end
|
||||
|
||||
#group :development, :production do
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
Jasmine Javascript Unit Tests
|
||||
=============================
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 802 B |
Binary file not shown.
|
After Width: | Height: | Size: 876 B |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
|
|
@ -47,7 +47,11 @@
|
|||
|
||||
if(gon.global.video_available && gon.global.video_available!="none" ) {
|
||||
var webcamName;
|
||||
var webcam = context.jamClient.FTUECurrentSelectedVideoDevice()
|
||||
var webcam = null;
|
||||
if (context.jamClient.FTUECurrentSelectedVideoDevice) {
|
||||
webcam = context.jamClient.FTUECurrentSelectedVideoDevice()
|
||||
}
|
||||
|
||||
if (webcam == null || typeof(webcam) == "undefined" || Object.keys(webcam).length == 0) {
|
||||
webcamName = "None Configured"
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -49,11 +49,13 @@ context.JK.AccountJamTracks = class AccountJamTracks
|
|||
#context.location="client#/createSession"
|
||||
jamRow = $(e.target).parents("tr")
|
||||
@createSession(jamRow.data(), true, jamRow.data('jamTrack'))
|
||||
return false;
|
||||
|
||||
groupSession:(e) =>
|
||||
#context.location="client#/createSession"
|
||||
jamRow = $(e.target).parents("tr")
|
||||
@createSession(jamRow.data(), false, jamRow.data('jamTrack'))
|
||||
return false;
|
||||
|
||||
createSession:(sessionData, solo, jamTrack) =>
|
||||
tracks = context.JK.TrackHelpers.getUserTracks(context.jamClient)
|
||||
|
|
@ -70,8 +72,8 @@ context.JK.AccountJamTracks = class AccountJamTracks
|
|||
data.musician_access = !solo
|
||||
data.fan_access = false
|
||||
data.fan_chat = false
|
||||
data.genre = [sessionData.genre]
|
||||
data.genres = [sessionData.genre]
|
||||
data.genre = $.map(sessionData.jamTrack.genres, (genre) -> genre.id)
|
||||
data.genres = $.map(sessionData.jamTrack.genres, (genre)-> genre.id)
|
||||
# data.genres = context.JK.GenreSelectorHelper.getSelectedGenres('#create-session-genre')
|
||||
# data.musician_access = if $('#musician-access option:selected').val() == 'true' then true else false
|
||||
# data.approval_required = if $('input[name=\'musician-access-option\']:checked').val() == 'true' then true else false
|
||||
|
|
|
|||
|
|
@ -121,19 +121,24 @@
|
|||
|
||||
// Column 2 - genres
|
||||
var genres = profileUtils.virtualBandGenreList(userDetail.genres)
|
||||
$virtualBandGenreList.html(genres && genres.length > 0 ? genres : NONE_SPECIFIED)
|
||||
var genreIds = profileUtils.getGenreIds(profileUtils.virtualBandGenres(userDetail.genres));
|
||||
$virtualBandGenreList.data('genres', genreIds).html(genres && genres.length > 0 ? genres : NONE_SPECIFIED)
|
||||
|
||||
genres = profileUtils.traditionalBandGenreList(userDetail.genres)
|
||||
$traditionalBandGenreList.html(genres && genres.length > 0 ? genres : NONE_SPECIFIED)
|
||||
var genreIds = profileUtils.getGenreIds(profileUtils.traditionalBandGenres(userDetail.genres));
|
||||
$traditionalBandGenreList.data('genres', genreIds).html(genres && genres.length > 0 ? genres : NONE_SPECIFIED)
|
||||
|
||||
genres = profileUtils.paidSessionGenreList(userDetail.genres)
|
||||
$paidSessionsGenreList.html(genres && genres.length > 0 ? genres : NONE_SPECIFIED)
|
||||
var genreIds = profileUtils.getGenreIds(profileUtils.paidSessionGenres(userDetail.genres));
|
||||
$paidSessionsGenreList.data('genres', genreIds).html(genres && genres.length > 0 ? genres : NONE_SPECIFIED)
|
||||
|
||||
genres = profileUtils.freeSessionGenreList(userDetail.genres)
|
||||
$freeSessionsGenreList.html(genres && genres.length > 0 ? genres : NONE_SPECIFIED)
|
||||
var genreIds = profileUtils.getGenreIds(profileUtils.freeSessionGenres(userDetail.genres));
|
||||
$freeSessionsGenreList.data('genres', genreIds).html(genres && genres.length > 0 ? genres : NONE_SPECIFIED)
|
||||
|
||||
genres = profileUtils.cowritingGenreList(userDetail.genres)
|
||||
$cowritingGenreList.html(genres && genres.length > 0 ? genres : NONE_SPECIFIED)
|
||||
var genreIds = profileUtils.getGenreIds(profileUtils.cowritingGenres(userDetail.genres));
|
||||
$cowritingGenreList.data('genres', genreIds).html(genres && genres.length > 0 ? genres : NONE_SPECIFIED)
|
||||
|
||||
// Column 3 - misc (play commitment, rates, cowriting purpose)
|
||||
$virtualBandCommitment.val(userDetail.virtual_band_commitment)
|
||||
|
|
@ -165,7 +170,7 @@
|
|||
}
|
||||
|
||||
ui.launchGenreSelectorDialog(type, genres, function(selectedGenres) {
|
||||
$genreList.html(selectedGenres && selectedGenres.length > 0 ? selectedGenres.join(GENRE_LIST_DELIMITER) : NONE_SPECIFIED)
|
||||
$genreList.data('genres', selectedGenres).html(selectedGenres && selectedGenres.length > 0 ? selectedGenres.join(GENRE_LIST_DELIMITER) : NONE_SPECIFIED)
|
||||
})
|
||||
|
||||
return false
|
||||
|
|
@ -278,24 +283,24 @@
|
|||
|
||||
api.updateUser({
|
||||
virtual_band: $screen.find('input[name=virtual_band]:checked').val(),
|
||||
virtual_band_genres: $virtualBandGenreList.html() === NONE_SPECIFIED ? [] : $virtualBandGenreList.html().split(GENRE_LIST_DELIMITER),
|
||||
virtual_band_genres: $virtualBandGenreList.data('genres'),
|
||||
virtual_band_commitment: $virtualBandCommitment.val(),
|
||||
|
||||
traditional_band: $screen.find('input[name=traditional_band]:checked').val(),
|
||||
traditional_band_genres: $traditionalBandGenreList.html() === NONE_SPECIFIED ? [] : $traditionalBandGenreList.html().split(GENRE_LIST_DELIMITER),
|
||||
traditional_band_genres: $traditionalBandGenreList.data('genres'),
|
||||
traditional_band_commitment: $traditionalBandCommitment.val(),
|
||||
traditional_band_touring: $traditionalTouringOption.val(),
|
||||
|
||||
paid_sessions: $screen.find('input[name=paid_sessions]:checked').val(),
|
||||
paid_session_genres: $paidSessionsGenreList.html() === NONE_SPECIFIED ? [] : $paidSessionsGenreList.html().split(GENRE_LIST_DELIMITER),
|
||||
paid_session_genres: $paidSessionsGenreList.data('genres'),
|
||||
paid_sessions_hourly_rate: profileUtils.normalizeMoneyForSubmit($hourlyRate.val()),
|
||||
paid_sessions_daily_rate: profileUtils.normalizeMoneyForSubmit($dailyRate.val()),
|
||||
|
||||
free_sessions: $screen.find('input[name=free_sessions]:checked').val(),
|
||||
free_session_genres: $freeSessionsGenreList.html() === NONE_SPECIFIED ? [] : $freeSessionsGenreList.html().split(GENRE_LIST_DELIMITER),
|
||||
free_session_genres: $freeSessionsGenreList.data('genres'),
|
||||
|
||||
cowriting: $screen.find('input[name=cowriting]:checked').val(),
|
||||
cowriting_genres: $cowritingGenreList.html() === NONE_SPECIFIED ? [] : $cowritingGenreList.html().split(GENRE_LIST_DELIMITER),
|
||||
cowriting_genres: $cowritingGenreList.data('genres'),
|
||||
cowriting_purpose: $cowritingPurpose.val()
|
||||
})
|
||||
.done(postUpdateProfileSuccess)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
var $templateOpenSlots = null;
|
||||
var $templateAccountPendingRsvp = null;
|
||||
var $templateAccountSessionDetail = null;
|
||||
var instrument_logo_map = context.JK.getInstrumentIconMap24();
|
||||
var invitationDialog = null;
|
||||
var inviteMusiciansUtil = null;
|
||||
var friendInput=null;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
context.JK = context.JK || {};
|
||||
context.JK.AccountVideoProfile = function (app) {
|
||||
var $webcamViewer = new context.JK.WebcamViewer()
|
||||
var webcamViewerReact = null;
|
||||
function initialize() {
|
||||
var screenBindings = {
|
||||
'beforeShow': beforeShow,
|
||||
|
|
@ -12,15 +12,18 @@
|
|||
};
|
||||
app.bindScreen('account/video', screenBindings);
|
||||
|
||||
$webcamViewer.init($("#account-video-profile .webcam-container"))
|
||||
var reactElement = React.createElement(window.WebcamViewer, {isVisible: false, show_header: true, show_disable: true});
|
||||
var reactDomNode = $("#account-video-profile .webcam-container").get(0)
|
||||
webcamViewerReact = React.render(reactElement, reactDomNode)
|
||||
}
|
||||
|
||||
|
||||
function beforeShow() {
|
||||
$webcamViewer.beforeShow()
|
||||
webcamViewerReact.beforeShow()
|
||||
}
|
||||
|
||||
function beforeHide() {
|
||||
$webcamViewer.setVideoOff()
|
||||
webcamViewerReact.beforeHide()
|
||||
}
|
||||
|
||||
this.beforeShow = beforeShow
|
||||
|
|
|
|||
|
|
@ -131,6 +131,12 @@
|
|||
// context.JK.CurrentSessionModel.onPlaybackStateChange(type, text);
|
||||
context.MediaPlaybackActions.playbackStateChange(text);
|
||||
}
|
||||
else if(type === ALERT_NAMES.VIDEO_WINDOW_OPENED) {
|
||||
context.VideoActions.videoWindowOpened()
|
||||
}
|
||||
else if(type === ALERT_NAMES.VIDEO_WINDOW_CLOSED) {
|
||||
context.VideoActions.videoWindowClosed()
|
||||
}
|
||||
else if((!context.JK.CurrentSessionModel || !context.JK.CurrentSessionModel.inSession()) &&
|
||||
(ALERT_NAMES.INPUT_IO_RATE == type || ALERT_NAMES.INPUT_IO_JTR == type || ALERT_NAMES.OUTPUT_IO_RATE == type || ALERT_NAMES.OUTPUT_IO_JTR== type)) {
|
||||
// squelch these events if not in session
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@
|
|||
}
|
||||
})
|
||||
.fail(function() {
|
||||
window.location = '/client#/jamtrackBrowse'
|
||||
window.location = '/client#/jamtrack/search'
|
||||
window.location.reload();
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class CheckoutUtils
|
|||
|
||||
@logger.debug("deleted preserve billing");
|
||||
|
||||
unless $.cookie(@cookie_name)?
|
||||
if $.cookie(@cookie_name)?
|
||||
@logger.error("after deleting the preserve billing cookie, it still exists!")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -39,4 +39,6 @@ context.JK.ClientInit = class ClientInit
|
|||
nativeClientInit: () =>
|
||||
@gearUtils.bootstrapDefaultPlaybackProfile();
|
||||
|
||||
window.VideoActions.checkPromptConfigureVideo()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,8 @@
|
|||
}
|
||||
|
||||
if(options.type == "yes_no") {
|
||||
$yesBtn.show().unbind('click').click(function() {
|
||||
var yesText = options.yes_text || 'YES'
|
||||
$yesBtn.text(yesText).show().unbind('click').click(function() {
|
||||
if(options.yes) {
|
||||
options.yes();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,14 @@
|
|||
checked = 'checked';
|
||||
}
|
||||
|
||||
$genres.append('<input type="checkbox" value="' + val.id + '" ' + checked + ' />' + val.description);
|
||||
var $input = $('<input type="checkbox" />')
|
||||
$input.val(val.id)
|
||||
if(checked == 'checked') {
|
||||
$input.attr('checked', 'checked')
|
||||
}
|
||||
|
||||
$genres.append($input);
|
||||
$genres.append(val.description);
|
||||
$genres.append('</li>');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
|
||||
$browserJamTrackBtn.click(function() {
|
||||
app.layout.closeDialog('getting-started')
|
||||
window.location = '/client#/jamtrackBrowse'
|
||||
window.location = '/client#/jamtrack/search'
|
||||
return false;
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,76 @@
|
|||
var $templateOpenJamTrackRow = null;
|
||||
var $downloadedTrackHelp = null;
|
||||
var $whatAreJamTracks = null;
|
||||
var $searchBtn = null;
|
||||
var sampleRate = null;
|
||||
var sampleRateForFilename = null;
|
||||
var searchQuery = null;
|
||||
var cookieName = 'jamtrack_session_search'
|
||||
|
||||
// called by react autocomplote component
|
||||
function search(searchType, searchData) {
|
||||
window.JamTrackSearchInput = searchData;
|
||||
searchQuery = {searchType: searchType, searchData: searchData}
|
||||
$.cookie(cookieName, JSON.stringify(searchQuery))
|
||||
doSearch(searchQuery);
|
||||
}
|
||||
|
||||
function userSearch(e) {
|
||||
e.preventDefault();
|
||||
searchQuery = {searchType: 'user-input', searchData: window.JamTrackSearchInput}
|
||||
$.cookie(cookieName, JSON.stringify(searchQuery))
|
||||
doSearch(searchQuery);
|
||||
}
|
||||
|
||||
function doSearch(query) {
|
||||
emptyList();
|
||||
resetPagination();
|
||||
|
||||
app.user().done(function(user) {
|
||||
|
||||
var showSearch = (user.purchased_jamtracks_count > perPage)
|
||||
|
||||
var $autocomplete = $dialog.find('[data-react-class="JamTrackAutoComplete"]')
|
||||
|
||||
if (showSearch) {
|
||||
$autocomplete.show()
|
||||
$searchBtn.show()
|
||||
|
||||
// if no query specified, look in a cookie for last query
|
||||
if (!query) {
|
||||
query = $.cookie(cookieName)
|
||||
|
||||
// and parse that cookie if defined
|
||||
if (query) {
|
||||
try {
|
||||
query = JSON.parse(query)
|
||||
}
|
||||
catch (e) {
|
||||
query = {searchType: 'user-input', searchData: ''}
|
||||
logger.error("unable to parse search query: " + e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if still no query (after checking cookie and what was specified in function, then default to anything
|
||||
if(!query){
|
||||
query = {searchType: 'user-input', searchData: ''}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$autocomplete.hide()
|
||||
$searchBtn.hide()
|
||||
}
|
||||
|
||||
getPurchasedJamTracks(0)
|
||||
.done(function (data, textStatus, jqXHR) {
|
||||
// initialize pagination
|
||||
var $paginator = context.JK.Paginator.create(parseInt(jqXHR.getResponseHeader('total-entries')), perPage, 0, onPageSelected, 20)
|
||||
$paginatorHolder.append($paginator);
|
||||
});
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
function emptyList() {
|
||||
$tbody.empty();
|
||||
|
|
@ -30,19 +97,13 @@
|
|||
}
|
||||
|
||||
function afterShow() {
|
||||
|
||||
$dialog.data('result', null)
|
||||
emptyList();
|
||||
resetPagination();
|
||||
showing = true;
|
||||
sampleRate = context.jamClient.GetSampleRate()
|
||||
sampleRateForFilename = sampleRate == 48 ? '48' : '44';
|
||||
doSearch();
|
||||
|
||||
getPurchasedJamTracks(0)
|
||||
.done(function(data, textStatus, jqXHR) {
|
||||
// initialize pagination
|
||||
var $paginator = context.JK.Paginator.create(parseInt(jqXHR.getResponseHeader('total-entries')), perPage, 0, onPageSelected)
|
||||
$paginatorHolder.append($paginator);
|
||||
});
|
||||
|
||||
}
|
||||
function afterHide() {
|
||||
|
|
@ -55,7 +116,21 @@
|
|||
}
|
||||
|
||||
function getPurchasedJamTracks(page) {
|
||||
return rest.getPurchasedJamTracks({page:page + 1, per_page:10})
|
||||
|
||||
var query = {page:page + 1, per_page:10}
|
||||
if (searchQuery && searchQuery.searchData && searchQuery.searchData.length > 0 && searchQuery.searchType && searchQuery.searchType.length > 0) {
|
||||
|
||||
if (searchQuery.searchType == 'user-input') {
|
||||
query.search = searchQuery.searchData
|
||||
}
|
||||
else if(searchQuery.searchType == 'artist-select') {
|
||||
query.artist_search = searchQuery.searchData
|
||||
}
|
||||
else if(searchQuery.searchType == 'song-select') {
|
||||
query.song_search = searchQuery.searchData
|
||||
}
|
||||
}
|
||||
return rest.getPurchasedJamTracks(query)
|
||||
.done(function(purchasedJamTracks) {
|
||||
|
||||
emptyList();
|
||||
|
|
@ -104,6 +179,8 @@
|
|||
|
||||
context.JK.helpBubble($whatAreJamTracks, 'no help yet for this topic', {}, {positions:['bottom'], offsetParent: $dialog})
|
||||
$whatAreJamTracks.on('click', false) // no help yet
|
||||
|
||||
$searchBtn.on('click', userSearch)
|
||||
}
|
||||
|
||||
function initialize(){
|
||||
|
|
@ -121,6 +198,7 @@
|
|||
$templateOpenJamTrackRow = $('#template-jam-track-row')
|
||||
$downloadedTrackHelp = $dialog.find('.downloaded-jamtrack-help')
|
||||
$whatAreJamTracks = $dialog.find('.what-are-jamtracks')
|
||||
$searchBtn = $dialog.find('.search-btn')
|
||||
|
||||
registerStaticEvents();
|
||||
};
|
||||
|
|
@ -128,6 +206,7 @@
|
|||
|
||||
this.initialize = initialize;
|
||||
this.isShowing = function isShowing() { return showing; }
|
||||
this.search = search; // called by react
|
||||
}
|
||||
|
||||
return this;
|
||||
|
|
|
|||
|
|
@ -243,6 +243,7 @@ context.JK.DownloadJamTrack = class DownloadJamTrack
|
|||
@logger.debug "downloadCheck"
|
||||
|
||||
retry: () =>
|
||||
@logger.debug "user initiated retry"
|
||||
@path = []
|
||||
@path.push('retry')
|
||||
this.clear()
|
||||
|
|
|
|||
|
|
@ -74,21 +74,30 @@
|
|||
|
||||
|
||||
function FTUESelectVideoCaptureDevice(device, settings) {
|
||||
|
||||
return true;
|
||||
}
|
||||
function FTUESetVideoEncodeResolution(resolution) {
|
||||
|
||||
}
|
||||
|
||||
function testVideoRender() {
|
||||
|
||||
}
|
||||
function FTUEGetVideoCaptureDeviceNames() {
|
||||
return ["Built-in Webcam HD"]
|
||||
return {"xy323ss": "Built-in Webcam HD"}
|
||||
}
|
||||
function FTUECurrentSelectedVideoDevice() {
|
||||
//return {}
|
||||
return {"xy323ss": "Built-in Webcam HD"}
|
||||
}
|
||||
function FTUEGetAvailableEncodeVideoResolutions() {
|
||||
return {
|
||||
1: "1024x768",
|
||||
2: "800x600"
|
||||
1 : "CIF (352X288)",
|
||||
2 : "VGA (640X480)",
|
||||
3 : "4CIF (704X576)",
|
||||
4 : "1/2WHD (640X360)",
|
||||
5 : "WHD (1280X720)",
|
||||
6 : "FHD (1920x1080)"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +105,30 @@
|
|||
return {}
|
||||
}
|
||||
|
||||
function FTUESetSendFrameRates(fps) {
|
||||
|
||||
}
|
||||
|
||||
function FTUEGetSendFrameRates() {
|
||||
return {20:20, 24:24, 30:30}
|
||||
}
|
||||
|
||||
function GetCurrentVideoResolution() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
function GetCurrentVideoFrameRate() {
|
||||
return 30;
|
||||
}
|
||||
|
||||
function FTUESetVideoShareEnable(){
|
||||
|
||||
}
|
||||
|
||||
function FTUEGetVideoShareEnable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isSessVideoShared() {
|
||||
return videoShared;
|
||||
}
|
||||
|
|
@ -336,8 +369,8 @@
|
|||
|
||||
function GetOS() { return 100000000; }
|
||||
function GetOSAsString() {
|
||||
return "Win32";
|
||||
//return "MacOSX";
|
||||
//return "Win32";
|
||||
return "MacOSX";
|
||||
}
|
||||
|
||||
function LatencyUpdated(map) { dbg('LatencyUpdated:' + JSON.stringify(map)); }
|
||||
|
|
@ -433,10 +466,19 @@
|
|||
|
||||
}
|
||||
|
||||
function SetVideoNetworkTestScore(numClients) {
|
||||
|
||||
}
|
||||
|
||||
function GetNetworkTestScore() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
|
||||
function GetVideoNetworkTestScore() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
function SetLatencyTestBlocked(blocked) {
|
||||
|
||||
}
|
||||
|
|
@ -1036,6 +1078,8 @@
|
|||
this.IsMyNetworkWireless = IsMyNetworkWireless;
|
||||
this.SetNetworkTestScore = SetNetworkTestScore;
|
||||
this.GetNetworkTestScore = GetNetworkTestScore;
|
||||
this.SetVideoNetworkTestScore = SetVideoNetworkTestScore;
|
||||
this.GetVideoNetworkTestScore = GetVideoNetworkTestScore;
|
||||
this.SetLatencyTestBlocked = SetLatencyTestBlocked;
|
||||
this.isLatencyTestBlocked = isLatencyTestBlocked;
|
||||
this.GetLastLatencyTestTimes = GetLastLatencyTestTimes;
|
||||
|
|
@ -1220,14 +1264,19 @@
|
|||
this.OnDownloadAvailable = OnDownloadAvailable;
|
||||
|
||||
// Video functionality:
|
||||
this.FTUESelectVideoCaptureDevice = FTUESelectVideoCaptureDevice
|
||||
this.testVideoRender = testVideoRender;
|
||||
this.FTUESelectVideoCaptureDevice = FTUESelectVideoCaptureDevice;
|
||||
this.FTUESetVideoEncodeResolution = FTUESetVideoEncodeResolution;
|
||||
this.FTUEGetVideoCaptureDeviceNames = FTUEGetVideoCaptureDeviceNames;
|
||||
this.FTUECurrentSelectedVideoDevice = FTUECurrentSelectedVideoDevice;
|
||||
this.FTUEGetAvailableEncodeVideoResolutions = FTUEGetAvailableEncodeVideoResolutions;
|
||||
this.FTUEGetVideoCaptureDeviceCapabilities = FTUEGetVideoCaptureDeviceCapabilities;
|
||||
|
||||
|
||||
this.FTUEGetSendFrameRates = FTUEGetSendFrameRates;
|
||||
this.FTUESetSendFrameRates = FTUESetSendFrameRates;
|
||||
this.GetCurrentVideoResolution = GetCurrentVideoResolution;
|
||||
this.GetCurrentVideoFrameRate = GetCurrentVideoFrameRate;
|
||||
this.FTUESetVideoShareEnable = FTUESetVideoShareEnable;
|
||||
this.FTUEGetVideoShareEnable = FTUEGetVideoShareEnable;
|
||||
this.isSessVideoShared = isSessVideoShared;
|
||||
this.SessStopVideoSharing = SessStopVideoSharing;
|
||||
this.SessStartVideoSharing = SessStartVideoSharing;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,23 @@
|
|||
context.JK.dropdown($('select', parentSelector));
|
||||
}
|
||||
|
||||
function render2($select, notSelectedString) {
|
||||
if(!notSelectedString) {
|
||||
notSelectedString = 'Any Genre'
|
||||
}
|
||||
$select.empty();
|
||||
$select.append('<option value="">' + notSelectedString + '</option>');
|
||||
var template = $('#template-genre-option').html();
|
||||
$.each(_genres, function(index, value) {
|
||||
// value will be a dictionary entry from _genres:
|
||||
// { value: xxx, label: yyy }
|
||||
var genreOptionHtml = context.JK.fillTemplate(template, value);
|
||||
$select.append(genreOptionHtml);
|
||||
});
|
||||
context.JK.dropdown($select);
|
||||
}
|
||||
|
||||
|
||||
function getSelectedGenres(parentSelector) {
|
||||
var selectedGenres = [];
|
||||
var selectedVal = $('select', parentSelector).val();
|
||||
|
|
@ -112,6 +129,10 @@
|
|||
render: function() {
|
||||
var _args = arguments;
|
||||
context.JK.GenreSelectorDeferred.done(function(){render.apply(self, _args)})
|
||||
},
|
||||
render2: function() {
|
||||
var _args = arguments;
|
||||
context.JK.GenreSelectorDeferred.done(function(){render2.apply(self, _args)})
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,16 @@
|
|||
SHOW_PREFERENCES : 39, // tell frontend to show preferences dialog
|
||||
USB_CONNECTED : 40, // tell frontend that a USB device was connected
|
||||
USB_DISCONNECTED : 41, // tell frontend that a USB device was disconnected
|
||||
LAST_ALERT : 42
|
||||
JAM_TRACK_SERVER_ERROR : 42, //error talking with server
|
||||
BAD_INTERVAL_RATE : 43, //the audio gear is calling back at rate that does not match the expected interval
|
||||
FIRST_AUDIO_PACKET : 44,// we are receiving audio from peer
|
||||
NETWORK_PORT_MANGLED : 45, // packet from peer indicates network port is being mangled
|
||||
NO_GLOBAL_CLOCK_SERVER : 46, //can't reach global clock NTP server
|
||||
GLOBAL_CLOCK_SYNCED : 47, //clock synced
|
||||
RECORDING_DONE :48, //the recording writer thread is done
|
||||
VIDEO_WINDOW_OPENED :49, //video window opened
|
||||
VIDEO_WINDOW_CLOSED :50,
|
||||
LAST_ALERT : 51
|
||||
}
|
||||
// recreate eThresholdType enum from MixerDialog.h
|
||||
context.JK.ALERT_TYPES = {
|
||||
|
|
@ -171,7 +180,16 @@
|
|||
39: {"title": "", "message": ""}, // SHOW_PREFERENCES, //show preferences dialog
|
||||
40: {"title": "", "message": ""}, // USB_CONNECTED
|
||||
41: {"title": "", "message": ""}, // USB_DISCONNECTED, // tell frontend that a USB device was disconnected
|
||||
42: {"title": "", "message": ""} // LAST_ALERT
|
||||
42: {"title": "", "message": ""}, // JAM_TRACK_SERVER_ERROR
|
||||
43: {"title": "", "message": ""}, // BAD_INTERVAL_RATE
|
||||
44: {"title": "", "message": ""}, // FIRST_AUDIO_PACKET
|
||||
45: {"title": "", "message": ""}, // NETWORK_PORT_MANGLED
|
||||
46: {"title": "", "message": ""}, // NO_GLOBAL_CLOCK_SERVER
|
||||
47: {"title": "", "message": ""}, // GLOBAL_CLOCK_SYNCED
|
||||
48: {"title": "", "message": ""}, // RECORDING_DONE
|
||||
49: {"title": "", "message": ""}, // VIDEO_WINDOW_OPENED
|
||||
50: {"title": "", "message": ""}, // VIDEO_WINDOW_CLOSED
|
||||
51: {"title": "", "message": ""} // LAST_ALERT
|
||||
};
|
||||
|
||||
// add the alert's name to the ALERT_TYPES structure
|
||||
|
|
@ -311,7 +329,9 @@
|
|||
|
||||
/** NAMED_MESSAGES means messages that we show to the user (dialogs/banners/whatever), that we have formally named */
|
||||
context.JK.NAMED_MESSAGES = {
|
||||
MASTER_VS_PERSONAL_MIX : 'master_vs_personal_mix'
|
||||
MASTER_VS_PERSONAL_MIX : 'master_vs_personal_mix',
|
||||
HOWTO_USE_VIDEO_NOSHOW : 'how-to-use-video',
|
||||
CONFIGURE_VIDEO_NOSHOW : 'configure-video'
|
||||
}
|
||||
|
||||
context.JK.ChannelGroupIds = {
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@
|
|||
|
||||
helpBubble.jamtrackLandingCta = function($element, $alternativeElement) {
|
||||
if (!$alternativeElement || $element.visible()) {
|
||||
context.JK.prodBubble($element, 'jamtrack-landing-cta', {}, bigHelpOptions({positions:['top', 'right'], width:240}))
|
||||
context.JK.prodBubble($element, 'jamtrack-landing-cta', {}, bigHelpOptions({positions:['top', 'right'], width:260}))
|
||||
}
|
||||
else if($alternativeElement) {
|
||||
context.JK.prodBubble($alternativeElement, 'jamtrack-landing-cta', {}, bigHelpOptions({positions:['right']}))
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
var logger = context.JK.logger;
|
||||
var rest = new context.JK.Rest();
|
||||
var _instruments = []; // will be list of structs: [ {label:xxx, value:yyy}, {...}, ... ]
|
||||
var _instrumentsSorted = [];
|
||||
var _rsvp = false;
|
||||
var _noICheck = false;
|
||||
if (typeof(_parentSelector)=="undefined") {_parentSelector=null}
|
||||
|
|
@ -35,6 +36,17 @@
|
|||
label: this.description
|
||||
});
|
||||
});
|
||||
|
||||
_instrumentsSorted = _instruments.slice().sort(sortAlpha)
|
||||
}
|
||||
|
||||
function sortAlpha(a, b) {
|
||||
if (a.value == b.value)
|
||||
return 0;
|
||||
if (a.value < b.value)
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
function render(parentSelector, userInstruments) {
|
||||
|
|
@ -85,6 +97,23 @@
|
|||
|
||||
}
|
||||
|
||||
function renderDropdown($select, notSelectedString) {
|
||||
if(!notSelectedString) {
|
||||
notSelectedString = 'Any Instrument'
|
||||
}
|
||||
$select.empty();
|
||||
$select.append('<option value="">' + notSelectedString + '</option>');
|
||||
var template = $('#template-instrument-option-simple').html();
|
||||
$.each(_instrumentsSorted, function(index, value) {
|
||||
// value will be a dictionary entry from _genres:
|
||||
// { value: xxx, label: yyy }
|
||||
var instrumentOptionHtml = context.JK.fillTemplate(template, value);
|
||||
$select.append(instrumentOptionHtml);
|
||||
});
|
||||
context.JK.dropdown($select);
|
||||
}
|
||||
|
||||
|
||||
function getSelectedInstruments() {
|
||||
var selectedInstruments = [];
|
||||
var $selectedVal = $('input[type="checkbox"]:checked', _parentSelector);
|
||||
|
|
@ -152,6 +181,10 @@
|
|||
var _args = arguments;
|
||||
context.JK.InstrumentSelectorDeferred.done(function(){render.apply(self, _args)})
|
||||
}
|
||||
this.renderDropdown = function() {
|
||||
var _args = arguments;
|
||||
context.JK.InstrumentSelectorDeferred.done(function(){renderDropdown.apply(self, _args)})
|
||||
}
|
||||
});
|
||||
|
||||
})(window,jQuery);
|
||||
|
|
@ -1642,6 +1642,15 @@
|
|||
});
|
||||
}
|
||||
|
||||
function autocompleteJamTracks(options) {
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
url: '/api/jamtracks/autocomplete?' + $.param(options),
|
||||
dataType: "json",
|
||||
contentType: 'application/json'
|
||||
});
|
||||
}
|
||||
|
||||
function getJamTrackArtists(options) {
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
|
|
@ -1715,14 +1724,15 @@
|
|||
type: "POST",
|
||||
url: '/api/shopping_carts/add_jamtrack?' + $.param(options),
|
||||
dataType: "json",
|
||||
contentType: 'applications/json'
|
||||
contentType: 'application/json'
|
||||
});
|
||||
}
|
||||
|
||||
function getShoppingCarts() {
|
||||
// the need for the time de-duplicator indicates we are doing something wrong on the server
|
||||
return $.ajax({
|
||||
type: "GET",
|
||||
url: '/api/shopping_carts',
|
||||
url: '/api/shopping_carts?time=' + new Date().getTime(),
|
||||
dataType: "json",
|
||||
contentType: 'application/json'
|
||||
});
|
||||
|
|
@ -2050,6 +2060,7 @@
|
|||
this.getJamTrack = getJamTrack;
|
||||
this.getJamTrackWithArtistInfo = getJamTrackWithArtistInfo;
|
||||
this.getJamTracks = getJamTracks;
|
||||
this.autocompleteJamTracks = autocompleteJamTracks;
|
||||
this.getJamTrackArtists = getJamTrackArtists;
|
||||
this.getPurchasedJamTracks = getPurchasedJamTracks;
|
||||
this.getPaymentHistory = getPaymentHistory;
|
||||
|
|
|
|||
|
|
@ -30,10 +30,12 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
|||
@playButton = @root.find('.play-button')
|
||||
@stopButton = @root.find('.stop-button')
|
||||
@instrumentIcon = @root.find('.instrument-icon')
|
||||
@instrumentPart = @root.find('.instrument-part')
|
||||
@instrumentName = @root.find('.instrument-name')
|
||||
@part = @root.find('.part')
|
||||
@loading = @root.find('.loading')
|
||||
@loadingText = @root.find('.loading-text')
|
||||
@loadingTextText = @root.find('.loading-text-text')
|
||||
|
||||
@playButton.on('click', @play)
|
||||
@stopButton.on('click', @stop)
|
||||
|
|
@ -42,10 +44,12 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
|||
instrumentId = null
|
||||
instrumentDescription = '?'
|
||||
if @jamTrackTrack.track_type == 'Track'
|
||||
@loadingTextText.text('20 second preview loading')
|
||||
if @jamTrackTrack.instrument
|
||||
instrumentId = @jamTrackTrack.instrument.id
|
||||
instrumentDescription = @jamTrackTrack.instrument.description
|
||||
else
|
||||
@loadingTextText.text('preview loading')
|
||||
instrumentId = 'other'
|
||||
instrumentDescription= 'Master Mix'
|
||||
|
||||
|
|
@ -64,7 +68,7 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
|||
if @options.master_adds_line_break
|
||||
part = '"' + @jamTrack.name + '"' + ' by ' + @jamTrack.original_artist
|
||||
|
||||
@part.html("#{part}") if part != ''
|
||||
@part.text("#{part}") if part != ''
|
||||
@part.addClass('adds-line-break')
|
||||
else
|
||||
|
||||
|
|
@ -79,6 +83,7 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
|||
@part.text("(#{part})") if part != ''
|
||||
|
||||
|
||||
@instrumentPart.text(@instrumentName.text() + ' ' + @part.text())
|
||||
|
||||
if @jamTrackTrack.preview_mp3_url?
|
||||
|
||||
|
|
@ -109,9 +114,7 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
|||
@sound.unload()
|
||||
|
||||
removeNowPlaying: () =>
|
||||
context.JK.JamTrackPreview.nowPlaying.splice(this)
|
||||
if context.JK.JamTrackPreview.nowPlaying.length > 0
|
||||
@logger.warn("multiple jamtrack previews playing")
|
||||
context.JamTrackPreviewActions.stoppedPlaying(this)
|
||||
|
||||
|
||||
onHowlerEnd: () =>
|
||||
|
|
@ -122,8 +125,8 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
|||
|
||||
onHowlerLoad: () =>
|
||||
@loaded = true
|
||||
@loading.fadeOut();
|
||||
@loadingText.fadeOut(); #addClass('hidden')
|
||||
@loading.fadeOut(2000);
|
||||
@loadingText.fadeOut(2000); #addClass('hidden')
|
||||
|
||||
play: (e) =>
|
||||
if e?
|
||||
|
|
@ -153,10 +156,7 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
|||
|
||||
@logger.debug("play issued for jam track preview")
|
||||
@sound.play()
|
||||
for playingSound in context.JK.JamTrackPreview.nowPlaying
|
||||
playingSound.issueStop()
|
||||
context.JK.JamTrackPreview.nowPlaying = []
|
||||
context.JK.JamTrackPreview.nowPlaying.push(this)
|
||||
context.JamTrackPreviewActions.startedPlaying(this)
|
||||
@playButton.addClass('hidden')
|
||||
@stopButton.removeClass('hidden')
|
||||
|
||||
|
|
@ -182,7 +182,4 @@ context.JK.JamTrackPreview = class JamTrackPreview
|
|||
return false
|
||||
|
||||
|
||||
context.JK.JamTrackPreview.nowPlaying = []
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -448,6 +448,7 @@ context.JK.JamTrackScreen=class JamTrackScreen
|
|||
this.handleExpanded(jamtrackRecord)
|
||||
|
||||
initialize:() =>
|
||||
|
||||
screenBindings =
|
||||
'beforeShow': this.beforeShow
|
||||
'afterShow': this.afterShow
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ context.JK.JamTrackLanding = class JamTrackLanding
|
|||
screenBindings =
|
||||
'beforeShow': @beforeShow
|
||||
'afterShow': @afterShow
|
||||
@app.bindScreen('jamtrackLanding', screenBindings)
|
||||
|
||||
#@app.bindScreen('jamtrackLanding', screenBindings)
|
||||
@screen = $('#jamtrackLanding')
|
||||
@noFreeJamTrack = @screen.find('.no-free-jamtrack')
|
||||
@freeJamTrack = @screen.find('.free-jamtrack')
|
||||
|
|
@ -64,14 +65,14 @@ context.JK.JamTrackLanding = class JamTrackLanding
|
|||
|
||||
# client#/jamtrack
|
||||
for artist in artists
|
||||
artistLink = "<a href='client?artist=#{encodeURIComponent(artist.original_artist)}#/jamtrackBrowse' class='artist-link' artist='#{artist.original_artist}'>#{artist.original_artist} (#{artist.song_count})</a>"
|
||||
artistLink = "<a href='client?artist=#{encodeURIComponent(artist.original_artist)}#/jamtrack/search' class='artist-link' artist='#{artist.original_artist}'>#{artist.original_artist} (#{artist.song_count})</a>"
|
||||
@bandList.append("<li>#{artistLink}</li>")
|
||||
|
||||
# We don't want to do a full page load if this is clicked on here:
|
||||
bindArtistLinks:() =>
|
||||
that=this
|
||||
@bandList.on "click", "a.artist-link", (event)->
|
||||
context.location="client#/jamtrackBrowse"
|
||||
context.location="client#/jamtrack/search"
|
||||
if window.history.replaceState # ie9 proofing
|
||||
window.history.replaceState({}, "", this.href)
|
||||
event.preventDefault()
|
||||
|
|
|
|||
|
|
@ -577,6 +577,8 @@
|
|||
|
||||
$(document).triggerHandler(EVENTS.SCREEN_CHANGED, {previousScreen: previousScreen, newScreen: currentScreen})
|
||||
|
||||
context.JamTrackPreviewActions.screenChange()
|
||||
|
||||
screenEvent(currentScreen, 'beforeShow', data);
|
||||
|
||||
// For now -- it seems we want it open always.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -18,7 +18,7 @@
|
|||
* @param onPageSelected when a new page is selected. receives one argument; the page number.
|
||||
* the function should return a deferred object (whats returned by $.ajax), and that response has to have a 'total-entries' header set
|
||||
*/
|
||||
create:function(totalEntries, perPage, currentPage, onPageSelected) {
|
||||
create:function(totalEntries, perPage, currentPage, onPageSelected, maxPages) {
|
||||
|
||||
if(this.$templatePaginator === null) {
|
||||
this.$templatePaginator = $('#template-paginator')
|
||||
|
|
@ -100,6 +100,13 @@
|
|||
|
||||
var pages = calculatePages(totalEntries, perPage);
|
||||
|
||||
|
||||
if(maxPages) {
|
||||
if((totalEntries / perPage) > maxPages) {
|
||||
pages = calculatePages(maxPages * perPage, perPage);
|
||||
}
|
||||
}
|
||||
|
||||
var options = { pages: pages,
|
||||
currentPage: currentPage };
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,17 @@
|
|||
return list;
|
||||
}
|
||||
|
||||
|
||||
profileUtils.getGenreIds = function(genres) {
|
||||
var list = []
|
||||
|
||||
for (var i=0; i < genres.length; i++) {
|
||||
list.push(genres[i].genre_id);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// the server stores money in cents; display it as such
|
||||
profileUtils.normalizeMoneyForDisplay = function(serverValue) {
|
||||
if (!serverValue || serverValue==="") {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
//= require react-input-autosize
|
||||
//= require react-select
|
||||
//= require_directory ./react-components/helpers
|
||||
//= require_directory ./react-components/actions
|
||||
//= require ./react-components/stores/AppStore
|
||||
//= require ./react-components/stores/RecordingStore
|
||||
//= require ./react-components/stores/VideoStore
|
||||
//= require ./react-components/stores/SessionStore
|
||||
//= require ./react-components/stores/MixerStore
|
||||
//= require ./react-components/stores/JamTrackStore
|
||||
|
|
@ -10,7 +13,9 @@
|
|||
//= require ./react-components/stores/SessionMyTracksStore
|
||||
//= require ./react-components/stores/SessionOtherTracksStore
|
||||
//= require ./react-components/stores/SessionMediaTracksStore
|
||||
//= require ./react-components/stores/PlatformStore
|
||||
//= require_directory ./react-components/stores
|
||||
//= require_directory ./react-components/mixins
|
||||
//= require_directory ./react-components
|
||||
|
||||
//= require_directory ./react-components/landing
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
|
||||
@JamTrackAutoComplete = React.createClass({
|
||||
|
||||
EVENTS: context.JK.EVENTS
|
||||
rest: context.JK.Rest()
|
||||
logger: context.JK.logger
|
||||
|
||||
|
||||
render: () ->
|
||||
|
||||
window.JamTrackSearchInput = '' unless window.JamTrackSearchInput? # can't pass null to react-select
|
||||
|
||||
searchValue = if @state.search == 'SEPARATOR' then '' else window.JamTrackSearchInput
|
||||
|
||||
`<Select
|
||||
placeholder="Search for JamTracks"
|
||||
name="search-field"
|
||||
asyncOptions={this.getOptions}
|
||||
autoload={false}
|
||||
value={searchValue}
|
||||
onChange={this.onSelectChange}
|
||||
onBlur={this.onSelectBlur}
|
||||
onFocus={this.onSelectFocus}
|
||||
className="autocompleter"
|
||||
cacheAsyncResults={false}
|
||||
filterOption={this.filterOption}
|
||||
clearable={false}
|
||||
/>`
|
||||
|
||||
getInitialState: () ->
|
||||
({search: ''})
|
||||
|
||||
filterOption:() ->
|
||||
true
|
||||
|
||||
onSelectChange: (val) ->
|
||||
#@logger.debug("CHANGE #{val}")
|
||||
|
||||
return false unless val?
|
||||
|
||||
if typeof @props.onSearch is 'string'
|
||||
searchFunction = eval(@props.onSearch)
|
||||
else
|
||||
searchFunction = @props.onSearch
|
||||
|
||||
search_type
|
||||
if val.indexOf('ARTIST=') == 0
|
||||
search_type = 'artist-select'
|
||||
artist = val['ARTIST='.length..-1]
|
||||
searchFunction(search_type, artist)
|
||||
else if val.indexOf('SONG=') == 0
|
||||
search_type = 'song-select'
|
||||
song = val['SONG='.length..-1]
|
||||
searchFunction(search_type, song)
|
||||
else
|
||||
@logger.debug("user selected separator")
|
||||
# this is to signal to the component that the separator was selected, and it has code in render to negate the selection
|
||||
setTimeout((() =>
|
||||
@setState({search:val})
|
||||
), 1)
|
||||
|
||||
return false
|
||||
|
||||
|
||||
onSelectFocus: (e) ->
|
||||
e.preventDefault()
|
||||
window.JamTrackSearchInput = ''
|
||||
@setState({search:''})
|
||||
|
||||
onSelectBlur: (e) ->
|
||||
|
||||
#@logger.debug("blur time")
|
||||
|
||||
#@search()
|
||||
|
||||
|
||||
getOptions: (input, callback) ->
|
||||
|
||||
#@logger.debug("getOptions input #{input}", this)
|
||||
|
||||
# sigh. ugly global
|
||||
window.JamTrackSearchInput = input
|
||||
|
||||
if !input? || input.length == 0
|
||||
callback(null, {options: [], complete: false})
|
||||
return
|
||||
|
||||
query = {match:input, limit:5}
|
||||
|
||||
if @props.show_purchased_only
|
||||
query.show_purchased_only = true
|
||||
|
||||
@rest.autocompleteJamTracks(query)
|
||||
.done((autocomplete) =>
|
||||
|
||||
options = []
|
||||
for artist in autocomplete.artists
|
||||
options.push { value: "ARTIST=#{artist.original_artist}", label: "Artist: #{artist.original_artist}" }
|
||||
|
||||
if options.length > 0 && autocomplete.songs.length > 0
|
||||
options.push { value: 'SEPARATOR', label: "---------------"}
|
||||
|
||||
for jamtrack in autocomplete.songs
|
||||
options.push { value: "SONG=#{jamtrack.name}", label: "Song: #{jamtrack.name}" }
|
||||
|
||||
callback(null, {options: options, complete: false})
|
||||
)
|
||||
})
|
||||
|
|
@ -0,0 +1,401 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
|
||||
@JamTrackFilterScreen = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
LIMIT: 20
|
||||
instrument_logo_map: context.JK.getInstrumentIconMap24()
|
||||
|
||||
computeWeight: (jam_track_track, instrument) ->
|
||||
weight = switch
|
||||
when jam_track_track.track_type == 'Master' then 0
|
||||
when jam_track_track.instrument?.id == instrument then 1 + jam_track_track.position
|
||||
else 10000 + jam_track_track.position
|
||||
|
||||
render: () ->
|
||||
|
||||
searchText = if @state.first_search then 'SEARCH' else 'SEARCH AGAIN'
|
||||
|
||||
uiJamTracks = []
|
||||
for jamtrack in @state.jamtracks
|
||||
trackRow = context._.clone(jamtrack)
|
||||
trackRow.track_cnt = jamtrack.tracks.length
|
||||
trackRow.tracks = []
|
||||
|
||||
# if an instrument is selected by the user, then re-order any jam tracks with a matching instrument to the top
|
||||
|
||||
instrument = @instrument.val() if @instrument?
|
||||
if instrument?
|
||||
jamtrack.tracks.sort((a, b) =>
|
||||
aWeight = @computeWeight(a, instrument)
|
||||
bWeight = @computeWeight(b, instrument)
|
||||
return aWeight - bWeight
|
||||
)
|
||||
|
||||
for track in jamtrack.tracks
|
||||
trackRow.tracks.push(track)
|
||||
if track.track_type=='Master'
|
||||
track.instrument_desc = "Master"
|
||||
else
|
||||
inst = '../assets/content/icon_instrument_default24.png'
|
||||
if track.instrument?
|
||||
if track.instrument.id in @instrument_logo_map
|
||||
inst = @instrument_logo_map[track.instrument.id].asset
|
||||
track.instrument_desc = track.instrument.description
|
||||
track.instrument_url = inst
|
||||
|
||||
if track.part != ''
|
||||
track.instrument_desc += ' (' + track.part + ')'
|
||||
|
||||
trackRow.free_state = if @state.is_free then 'free' else 'non-free'
|
||||
|
||||
trackRow.is_free = @state.is_free
|
||||
|
||||
uiJamTracks.push trackRow
|
||||
|
||||
|
||||
jamtracks = []
|
||||
|
||||
|
||||
for jamtrack in uiJamTracks
|
||||
|
||||
jamtrackPricesClasses = { "jamtrack-price" : true }
|
||||
jamtrackPricesClasses[jamtrack.free_state] = true
|
||||
jamtrackPricesClasses = classNames(jamtrackPricesClasses)
|
||||
|
||||
tracks = []
|
||||
for track in jamtrack.tracks
|
||||
tracks.push `<div className="jamtrack-track hidden" key={track.id} data-jamtrack-track-id={track.id}>
|
||||
<div className="jamtrack-preview">
|
||||
<JamTrackPreview jamTrack={jamtrack} jamTrackTrack={track} options={{master_shows_duration: true, color:'gray'}} />
|
||||
<div className="clearall" />
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
actionBtn = null
|
||||
if jamtrack.is_free
|
||||
actionBtn = `<a className="jamtrack-add-cart button-orange is_free" href="#" data-jamtrack-id={jamtrack.id}> GET IT FREE!</a>`
|
||||
else if jamtrack.purchased
|
||||
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="javascript:void(0)">PURCHASED</a>`
|
||||
else if jamtrack.added_cart
|
||||
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="client#/shoppingCart">ALREADY IN CART</a>`
|
||||
else
|
||||
actionBtn = `<a className="jamtrack-add-cart button-orange" href="#" data-jamtrack-id={jamtrack.id}>ADD TO CART</a>`
|
||||
|
||||
availabilityNotice = null
|
||||
if jamtrack.sales_region==context.JK.AVAILABILITY_US
|
||||
availabilityNotice =
|
||||
`<div className="jamtrack-license">
|
||||
This JamTrack available only to US customers.
|
||||
<a className="license-us-why" href="#">why?</a>
|
||||
</div>`
|
||||
|
||||
jamtracks.push `<tr className="jamtrack-record" key={jamtrack.id} data-jamtrack-id={jamtrack.id}>
|
||||
<td className="jamtrack-detail">
|
||||
<div className="jamtrack-name">"{jamtrack.name}"</div>
|
||||
<div className="jamtrack-original-artist">by {jamtrack.original_artist}</div>
|
||||
<br className="clearall"/>
|
||||
<div className="clearall detail-label extra hidden song-writer">Songwriters:</div>
|
||||
<div className="detail-value extra hidden">{jamtrack.songwriter}</div>
|
||||
<div className="clearall detail-label extra hidden">Publishers:</div>
|
||||
<div className="detail-value extra hidden">{jamtrack.publisher}</div>
|
||||
<div className="clearall detail-label extra hidden">Genres:</div>
|
||||
<div className="detail-value extra hidden">{jamtrack.genres.join(', ')}</div>
|
||||
<div className="clearall detail-label extra hidden">Version:</div>
|
||||
<div className="detail-value extra hidden">{jamtrack.recording_type}</div>
|
||||
</td>
|
||||
<td className="jamtrack-tracks">
|
||||
<div className="detail-arrow">
|
||||
<div className="jamtrack-detail-btn">
|
||||
show all tracks <a className="details-arrow arrow-down"/>
|
||||
</div>
|
||||
</div>
|
||||
{tracks}
|
||||
</td>
|
||||
<td className="jamtrack-action">
|
||||
<div className="jamtrack-action-container">
|
||||
<div className="jamtrack-actions">
|
||||
<div className={jamtrackPricesClasses}>$ {jamtrack.price}</div>
|
||||
{actionBtn}
|
||||
{availabilityNotice}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>`
|
||||
|
||||
if @state.searching
|
||||
jamtracksHeader = "searching..."
|
||||
else
|
||||
jamtracksHeader = "search results: #{@state.count} jamtracks"
|
||||
|
||||
jamTracksSection =
|
||||
`<div>
|
||||
<h2 className="jamtrack-results-header">{jamtracksHeader} <a className="back-to-jamtracks-home" href="/client#/jamtrack">back to jamtracks home</a></h2>
|
||||
<table className="generaltable jamtrack-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="jamtrack-detail">JAMTRACK</th>
|
||||
<th className="jamtrack-tracks">TRACKS INCLUDED / PREVIEW</th>
|
||||
<th className="jamtrack-shop">SHOP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="jamtrack-content">
|
||||
{jamtracks}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="end-of-jamtrack-list end-of-list">No more JamTracks</div>
|
||||
</div>`
|
||||
|
||||
options = {}
|
||||
|
||||
`<div className="JamTrackFilterScreen">
|
||||
<div className="content-body-scroller">
|
||||
{jamTracksSection}
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
|
||||
getInitialState: () ->
|
||||
{search: '', type: 'user-input', jamtracks:[], show_all_artists: false, currentPage: 0, next: null, searching: false, count: 0, is_free: context.JK.currentUserFreeJamTrack}
|
||||
|
||||
|
||||
clearResults:() ->
|
||||
#@content.empty()
|
||||
#@noMoreJamtracks.hide()
|
||||
@setState({currentPage: 0, next: null, jamtracks:[], type: 'user-input', searching:false, is_free: context.JK.currentUserFreeJamTrack})
|
||||
|
||||
|
||||
defaultQuery:(extra) ->
|
||||
query =
|
||||
per_page: @LIMIT
|
||||
page: @state.currentPage + 1
|
||||
if @state.next
|
||||
query.since = @state.next
|
||||
|
||||
instrument = @instrument.val()
|
||||
if instrument?
|
||||
query.instrument = instrument
|
||||
|
||||
genre = @genre.val()
|
||||
if genre?
|
||||
query.genre = genre
|
||||
|
||||
$.extend(query, extra)
|
||||
|
||||
userSearch: (e) ->
|
||||
e.preventDefault()
|
||||
@search()
|
||||
|
||||
setFilterState: (state) ->
|
||||
if state
|
||||
@genre.easyDropDown('enable').removeAttr('disabled')
|
||||
@instrument.easyDropDown('enable').removeAttr('disabled')
|
||||
else
|
||||
@genre.easyDropDown('disable').attr('disabled', 'disabled')
|
||||
@instrument.easyDropDown('disable').attr('disabled', 'disabled')
|
||||
|
||||
|
||||
search: (search_type, input) ->
|
||||
return if @state.searching
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
# disable scroll watching now that we've started a new search
|
||||
$root.find('.content-body-scroller').off('scroll')
|
||||
$root.find('.end-of-jamtrack-list').hide()
|
||||
|
||||
|
||||
# we have to make sure the query starts from page 1, and no 'next' from previous causes a 'since' to show up
|
||||
query = @defaultQuery({page: 1})
|
||||
delete query.since
|
||||
@rest.getJamTracks(query)
|
||||
.done((response) =>
|
||||
@setState({jamtracks: response.jamtracks, next: response.next, searching: false, first_search: false, currentPage: 1, count: response.count})
|
||||
)
|
||||
.fail(() =>
|
||||
@app.notifyServerError jqXHR, 'Search Unavailable'
|
||||
@setState({searching: false, first_search: false})
|
||||
)
|
||||
|
||||
|
||||
@setState({currentPage: 0, next: null, jamtracks:[], searching: true, count: 0})
|
||||
|
||||
componentDidMount: () ->
|
||||
$screen = $('#jamtrackFilter')
|
||||
@genre = $screen.find('#jamtrack_genre')
|
||||
@instrument = $screen.find('#jamtrack_instrument')
|
||||
|
||||
@genre.on 'change', this.userSearch
|
||||
@instrument.on 'change', this.userSearch
|
||||
|
||||
# increase dropdown size
|
||||
context.JK.dropdown(@genre, {cutOff:15})
|
||||
context.JK.dropdown(@instrument, {cutOff:15})
|
||||
|
||||
|
||||
componentDidUpdate: ( ) ->
|
||||
$root = $(this.getDOMNode())
|
||||
$scroller = $root.find('.content-body-scroller')
|
||||
|
||||
@setFilterState(!@state.searching)
|
||||
|
||||
for jamTrack in @state.jamtracks
|
||||
jamtrackElement = $root.find("tbody .jamtrack-record[data-jamtrack-id=\"#{jamTrack.id}\"]")
|
||||
alreadyRegistered = jamtrackElement.data('registered')
|
||||
|
||||
unless alreadyRegistered
|
||||
jamtrackElement.data('jamTrack', jamTrack)
|
||||
jamtrackElement.data('expanded', true)
|
||||
jamtrackElement.data('registered', true)
|
||||
|
||||
@handleExpanded(jamtrackElement)
|
||||
@registerEvents(jamtrackElement)
|
||||
|
||||
|
||||
if @state.next == null
|
||||
$scroller = $root.find('.content-body-scroller')
|
||||
# if we less results than asked for, end searching
|
||||
#$scroller.infinitescroll 'pause'
|
||||
$scroller.off('scroll')
|
||||
if @state.currentPage == 1 and @state.jamtracks.length == 0
|
||||
$root.find('.end-of-jamtrack-list').text('No JamTracks found matching your search').show()
|
||||
@logger.debug("JamTrackSearch: empty search")
|
||||
else if @state.currentPage > 0
|
||||
$noMoreJamtracks = $root.find('.end-of-jamtrack-list').text('No more JamTracks').show()
|
||||
# there are bugs with infinitescroll not removing the 'loading'.
|
||||
# it's most noticeable at the end of the list, so whack all such entries
|
||||
else
|
||||
@registerInfiniteScroll($scroller)
|
||||
|
||||
|
||||
|
||||
registerInfiniteScroll:($scroller) ->
|
||||
$scroller.off('scroll')
|
||||
$scroller.on('scroll', () =>
|
||||
|
||||
# be sure to not fire off many refreshes when user hits the bottom
|
||||
return if @refreshing
|
||||
|
||||
if $scroller.scrollTop() + $scroller.innerHeight() + 100 >= $scroller[0].scrollHeight
|
||||
$scroller.append('<div class="infinite-scroll-loader-2">... Loading more JamTracks ...</div>')
|
||||
@refreshing = true
|
||||
@logger.debug("refreshing more jamtracks for infinite scroll")
|
||||
@setState({searching:true})
|
||||
@rest.getJamTracks(@defaultQuery())
|
||||
.done((json) =>
|
||||
@setState({jamtracks: @state.jamtracks.concat(json.jamtracks), next: json.next, currentPage: @state.currentPage + 1, count: json.count})
|
||||
)
|
||||
.always(() =>
|
||||
$scroller.find('.infinite-scroll-loader-2').remove()
|
||||
@refreshing = false
|
||||
@setState({searching: false})
|
||||
)
|
||||
)
|
||||
|
||||
playJamtrack:(e) ->
|
||||
e.preventDefault()
|
||||
|
||||
addToCartJamtrack:(e) ->
|
||||
e.preventDefault()
|
||||
$target = $(e.target)
|
||||
params = id: $target.attr('data-jamtrack-id')
|
||||
isFree = $(e.target).is('.is_free')
|
||||
|
||||
@rest.addJamtrackToShoppingCart(params).done((response) =>
|
||||
if(isFree)
|
||||
if context.JK.currentUserId?
|
||||
context.JK.currentUserFreeJamTrack = true # make sure the user sees no more free notices
|
||||
context.location = '/client#/redeemComplete'
|
||||
else
|
||||
# now make a rest call to buy it
|
||||
context.location = '/client#/redeemSignup'
|
||||
|
||||
else
|
||||
context.location = '/client#/shoppingCart'
|
||||
|
||||
).fail(() => @app.ajaxError)
|
||||
|
||||
licenseUSWhy:(e) ->
|
||||
e.preventDefault()
|
||||
@app.layout.showDialog 'jamtrack-availability-dialog'
|
||||
|
||||
registerEvents:($parent) ->
|
||||
$parent.find('.play-button').on 'click', @playJamtrack
|
||||
$parent.find('.jamtrack-add-cart').on 'click', @addToCartJamtrack
|
||||
$parent.find('.license-us-why').on 'click', @licenseUSWhy
|
||||
$parent.find('.jamtrack-detail-btn').on 'click', @toggleExpanded
|
||||
|
||||
toggleExpanded:(e) ->
|
||||
e.preventDefault()
|
||||
jamtrackRecord = $(e.target).parents('.jamtrack-record')
|
||||
@handleExpanded(jamtrackRecord)
|
||||
|
||||
handleExpanded:(trackElement) ->
|
||||
jamTrack = trackElement.data('jamTrack')
|
||||
expanded = trackElement.data('expanded')
|
||||
expand = !expanded
|
||||
trackElement.data('expanded', expand)
|
||||
|
||||
detailArrow = trackElement.find('.jamtrack-detail-btn')
|
||||
|
||||
if expand
|
||||
trackElement.find('.extra').removeClass('hidden')
|
||||
detailArrow.html('hide tracks <a class="details-arrow arrow-up"></a>')
|
||||
for track in jamTrack.tracks
|
||||
trackElement.find("[data-jamtrack-track-id='#{track.id}']").removeClass('hidden')
|
||||
else
|
||||
trackElement.find('.extra').addClass('hidden')
|
||||
detailArrow.html('show all tracks <a class="details-arrow arrow-down"></a>')
|
||||
count = 0
|
||||
for track in jamTrack.tracks
|
||||
if count < 6
|
||||
trackElement.find("[data-jamtrack-track-id='#{track.id}']").removeClass('hidden')
|
||||
else
|
||||
trackElement.find("[data-jamtrack-track-id='#{track.id}']").addClass('hidden')
|
||||
count++
|
||||
|
||||
|
||||
afterShow: (data) ->
|
||||
@setFilterFromURL()
|
||||
|
||||
beforeShow: () ->
|
||||
@setState({is_free: context.JK.currentUserFreeJamTrack})
|
||||
|
||||
setFilterFromURL:() ->
|
||||
|
||||
performSearch = false
|
||||
if $.QueryString['genre']?
|
||||
performSearch = true
|
||||
@genre.easyDropDown('select', $.QueryString['genre'], true)
|
||||
if $.QueryString['instrument']?
|
||||
performSearch = true
|
||||
@instrument.easyDropDown('select', $.QueryString['instrument'], true)
|
||||
|
||||
unless performSearch
|
||||
search = context.JamTrackStore.checkRequestedFilter()
|
||||
if search?
|
||||
performSearch = true
|
||||
@genre.easyDropDown('select', search.genre, true)
|
||||
@instrument.easyDropDown('select', search.instrument, true)
|
||||
|
||||
if performSearch
|
||||
@search()
|
||||
if window.history.replaceState #ie9 proofing
|
||||
window.history.replaceState({}, "", "/client#/jamtrack/filter")
|
||||
|
||||
onAppInit: (@app) ->
|
||||
|
||||
window.JamTrackSearchInput = '' # need to be not null; otherwise react-select chokes
|
||||
@EVENTS = context.JK.EVENTS
|
||||
@rest = context.JK.Rest()
|
||||
@logger = context.JK.logger
|
||||
|
||||
screenBindings =
|
||||
'beforeShow': @beforeShow
|
||||
'afterShow': @afterShow
|
||||
|
||||
@app.bindScreen('jamtrack/filter', screenBindings)
|
||||
})
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
|
||||
@JamTrackLandingScreen = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
getInitialState: () ->
|
||||
{user: null}
|
||||
|
||||
render: () ->
|
||||
|
||||
howTo = null
|
||||
if @state.user?.free_jamtrack
|
||||
howTo =
|
||||
`<div className="free-jamtrack">
|
||||
<span>
|
||||
For a limited time, get one JamTrack free. Search JamTracks below, add one to your shopping cart, and we'll make it free during the checkout process.
|
||||
</span>
|
||||
</div>`
|
||||
else
|
||||
howTo = `<div className="no-free-jamtrack">
|
||||
<span>
|
||||
To play with your JamTracks, open a JamTrack while in a session in the JamKazam app. Or <a href="/client#/account/jamtracks">visit the JamTracks section of your account.</a>
|
||||
</span>
|
||||
</div>`
|
||||
|
||||
|
||||
`<div className="content-body-scroller">
|
||||
<div className="list-columns">
|
||||
<div className="browse">
|
||||
<h2>my jamtracks</h2>
|
||||
<div className="howto">
|
||||
{howTo}
|
||||
</div>
|
||||
<h2 className="browse-jamtracks">search jamtracks</h2>
|
||||
<div className="search-area">
|
||||
<div className="search-help para"> To search by the name of the original artist, band, or song name, enter your search words below:</div>
|
||||
<div className="search-controls">
|
||||
<JamTrackAutoComplete onSearch={this.search} /><button onClick={this.searchByString} className="search-by-string-btn button-orange ">SEARCH</button>
|
||||
</div>
|
||||
<div className="filter-help para">To search by genre and instrument, make your selections below:</div>
|
||||
<div className="search-controls">
|
||||
<select className="genre-list easydropdown" name="genres">
|
||||
<option value="">Any Genre</option>
|
||||
</select>
|
||||
<select className="instrument-list easydropdown" name="insruments">
|
||||
<option value="">Any Instrument</option>
|
||||
</select>
|
||||
<button className="search-by-filter-btn button-orange" onClick={this.searchByFilter}>SEARCH</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="about">
|
||||
<h2>what are jamtracks?</h2>
|
||||
<div className="what">
|
||||
<div className="details">
|
||||
JamTracks are the best way to play along with your favorite music! Unlike traditional backing tracks, JamTracks are professionally mastered, complete multitrack recordings, with fully isolated tracks for each part of the master mix. Used with the free JamKazam app & Internet service, you can:
|
||||
</div>
|
||||
<ul>
|
||||
<li>Solo just the part you want to play in order to hear and learn it</li>
|
||||
<li>Mute just the part you want to play and play along with the rest</li>
|
||||
<li>Slow down playback to practice without changing the pitch</li>
|
||||
<li>Change the song key by raising or lowering pitch in half steps</li>
|
||||
<li>Make audio recordings and share them via Facebook or URL</li>
|
||||
<li>Make video recordings and share them via YouTube</li>
|
||||
<li>And even go online to play with others live & in sync</li>
|
||||
</ul>
|
||||
<a className="video-thumbnail" href="https://www.youtube.com/watch?v=askHvcCoNfw" rel="external">
|
||||
<img className="play" src="/assets/content/icon_youtube_play.png" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
|
||||
componentDidMount: () ->
|
||||
$root = $(@getDOMNode())
|
||||
|
||||
search: (searchType, searchData) ->
|
||||
context.JamTrackActions.requestSearch(searchType, searchData)
|
||||
|
||||
searchByString: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
context.JamTrackActions.requestSearch('user-input', window.JamTrackSearchInput)
|
||||
|
||||
searchByFilter: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
genre = $root.find('select.genre-list').val()
|
||||
instrument = $root.find('select.instrument-list').val()
|
||||
context.JamTrackActions.requestFilter(genre, instrument)
|
||||
|
||||
afterShow: (data) ->
|
||||
|
||||
if context.JK.currentUserId
|
||||
@app.user().done(@onUser)
|
||||
else
|
||||
@onUser({free_jamtrack: context.JK.currentUserFreeJamTrack})
|
||||
|
||||
beforeShow: () ->
|
||||
@setState({user: null})
|
||||
|
||||
onUser:(user) ->
|
||||
@setState({user: user})
|
||||
|
||||
# Get artist names and build links
|
||||
#@rest.getJamTrackArtists({group_artist: true, per_page:100})
|
||||
#.done(this.buildArtistLinks)
|
||||
#.fail(this.handleFailure)
|
||||
|
||||
# Bind links to action that will open the jam_tracks list view filtered to given artist_name:
|
||||
# artist_name
|
||||
#@bindArtistLinks()
|
||||
|
||||
|
||||
onAppInit: (@app) ->
|
||||
@rest = context.JK.Rest()
|
||||
@client = context.jamClient
|
||||
@logger = context.JK.logger
|
||||
@screen = null
|
||||
@noFreeJamTrack = null
|
||||
@freeJamTrack = null
|
||||
@bandList = null
|
||||
@noBandsFound = null
|
||||
|
||||
screenBindings =
|
||||
'beforeShow': @beforeShow
|
||||
'afterShow': @afterShow
|
||||
|
||||
@app.bindScreen('jamtrack', screenBindings)
|
||||
|
||||
@screen = $('#jamtrackLanding')
|
||||
@noFreeJamTrack = @screen.find('.no-free-jamtrack')
|
||||
@freeJamTrack = @screen.find('.free-jamtrack')
|
||||
@bandList = @screen.find('#band_list')
|
||||
@noBandsFound = @screen.find('#no_bands_found')
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
context.JK.GenreSelectorHelper.render2($root.find('select.genre-list'))
|
||||
|
||||
@instrumentSelector = new context.JK.InstrumentSelector(@app)
|
||||
@instrumentSelector.initialize(false, true)
|
||||
@instrumentSelector.renderDropdown($root.find('select.instrument-list'))
|
||||
})
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
context = window
|
||||
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup
|
||||
|
||||
@JamTrackPreview = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore, "onAppInit")]
|
||||
EVENTS: context.JK.EVENTS
|
||||
logger: context.JK.logger
|
||||
propTypes: { options: React.PropTypes.object }
|
||||
|
||||
getDefaultProps: () ->
|
||||
{ options: {master_shows_duration: false, color: 'gray', add_line_break: false, preload_master: false}}
|
||||
|
||||
getInitialState: () ->
|
||||
{ loaded: false, loading: false, playing: false, no_audio: false }
|
||||
|
||||
render: () ->
|
||||
playButtonClasses = { "play-button": true, disabled: @state.no_audio}
|
||||
playButtonClasses[@props.options.color] = @props.options.color?
|
||||
playButtonClasses = classNames(playButtonClasses)
|
||||
|
||||
stopButtonClasses = { "stop-button": true, disabled: @state.no_audio }
|
||||
stopButtonClasses[@props.options.color] = @props.options.color?
|
||||
stopButtonClasses = classNames(stopButtonClasses)
|
||||
|
||||
partClasses = {part: true}
|
||||
partClasses['adds-line-break'] = true if @props.options.master_adds_line_break
|
||||
partClasses = classNames(partClasses)
|
||||
|
||||
if @state.playing
|
||||
activeButton = `<a className={stopButtonClasses} onClick={this.stop} />`
|
||||
else
|
||||
activeButton = `<a className={playButtonClasses} onClick={this.play} />`
|
||||
|
||||
loaders = []
|
||||
if @props.jamTrackTrack.track_type == 'Track'
|
||||
loading_text = '20 second preview loading'
|
||||
else
|
||||
loading_text = 'preview loading'
|
||||
|
||||
|
||||
if @state.loading
|
||||
loaders.push `<div key="text" className="loading-text">{loading_text}
|
||||
<div className="loading spinner-small"></div>
|
||||
</div>`
|
||||
|
||||
|
||||
`<div className="jam-track-preview" data-track-type={this.props.jamTrackTrack.track_type} data-id={this.props.jamTrackTrack.id}>
|
||||
<div className="actions">
|
||||
{activeButton}
|
||||
</div>
|
||||
<img className="instrument-icon" data-hoveraction="instrument" data-instrument-id={this.state.instrumentId} src={this.state.instrumentSrc} width="24" height="24" />
|
||||
<div className="instrument-name">{this.state.instrumentDescription}</div>
|
||||
<div className="part">{this.state.part}</div>
|
||||
<ReactCSSTransitionGroup transitionName="session-track-list" transitionAppear={true}>
|
||||
{loaders}
|
||||
</ReactCSSTransitionGroup>
|
||||
</div>`
|
||||
|
||||
|
||||
componentWillMount: () ->
|
||||
instrumentId = null
|
||||
instrumentDescription = '?'
|
||||
if @props.jamTrackTrack.track_type == 'Track'
|
||||
if @props.jamTrackTrack.instrument
|
||||
instrumentId = @props.jamTrackTrack.instrument.id
|
||||
instrumentDescription = @props.jamTrackTrack.instrument.description
|
||||
else
|
||||
instrumentId = 'other'
|
||||
instrumentDescription= 'Master Mix'
|
||||
|
||||
instrumentSrc = context.JK.getInstrumentIcon24(instrumentId)
|
||||
|
||||
part = ''
|
||||
|
||||
if @props.jamTrackTrack.track_type == 'Track'
|
||||
part = "(#{@props.jamTrackTrack.part})" if @props.jamTrackTrack.part? && @props.jamTrackTrack.part != instrumentDescription
|
||||
|
||||
else
|
||||
if @props.options.master_adds_line_break
|
||||
part = '"' + @props.jamTrack.name + '"' + ' by ' + @props.jamTrack.original_artist
|
||||
else
|
||||
if @props.options.master_shows_duration
|
||||
duration = 'entire song'
|
||||
if @props.jamTrack.duration
|
||||
duration = "#{context.JK.prettyPrintSeconds(@props.jamTrack.duration)}"
|
||||
part = duration
|
||||
else
|
||||
part = @props.jamTrack.name + ' by ' + @props.jamTrack.original_artist
|
||||
|
||||
part = "(#{part})" unless part?
|
||||
part = '' unless part?
|
||||
|
||||
urls = null
|
||||
no_audio = null
|
||||
|
||||
if @props.jamTrackTrack.preview_mp3_url?
|
||||
|
||||
urls = [@props.jamTrackTrack.preview_mp3_url]
|
||||
if @props.jamTrackTrack.preview_ogg_url?
|
||||
urls.push(@props.jamTrackTrack.preview_ogg_url)
|
||||
urls = urls
|
||||
|
||||
no_audio = false
|
||||
else
|
||||
no_audio = true
|
||||
|
||||
|
||||
@setState({
|
||||
instrumentId: instrumentId,
|
||||
instrumentDescription: instrumentDescription,
|
||||
instrumentSrc: instrumentSrc,
|
||||
part: part
|
||||
urls: urls,
|
||||
no_audio: no_audio})
|
||||
|
||||
componentDidMount: () ->
|
||||
$root = $(@getDOMNode());
|
||||
|
||||
if @props.options.preload_master && @props.jamTrackTrack.track_type == 'Master' && !@state.no_audio
|
||||
@sound = new Howl({
|
||||
src: @state.urls,
|
||||
autoplay: false,
|
||||
loop: false,
|
||||
volume: 1.0,
|
||||
preload: true,
|
||||
onload: @onHowlerLoad
|
||||
onend: @onHowlerEnd})
|
||||
|
||||
componentWillUnmount: () ->
|
||||
@sound.unload() if @sound?
|
||||
|
||||
removeNowPlaying: () ->
|
||||
context.JamTrackPreviewActions.stoppedPlaying(this)
|
||||
|
||||
onHowlerEnd: () ->
|
||||
@logger.debug("on end")
|
||||
@removeNowPlaying()
|
||||
@setState(playing: false)
|
||||
|
||||
onHowlerLoad: () ->
|
||||
@setState(loaded: true, loading: false)
|
||||
|
||||
play: (e) ->
|
||||
if e?
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
$root.triggerHandler(@EVENTS.PREVIEW_PLAYED)
|
||||
|
||||
$playButton = $root.find('.play-button')
|
||||
if @state.no_audio
|
||||
context.JK.prodBubble($playButton, 'There is no preview available for this track.', {}, {duration:2000})
|
||||
else
|
||||
unless @sound?
|
||||
|
||||
@sound = new Howl({
|
||||
src: @state.urls,
|
||||
autoplay: false,
|
||||
loop: false,
|
||||
volume: 1.0,
|
||||
preload: true,
|
||||
onload: @onHowlerLoad
|
||||
onend: @onHowlerEnd})
|
||||
|
||||
|
||||
@logger.debug("play issued for jam track preview")
|
||||
@sound.play()
|
||||
context.JamTrackPreviewActions.startedPlaying(this)
|
||||
@setState({playing: true, loading: !@state.loaded})
|
||||
|
||||
issueStop: () ->
|
||||
@logger.debug("pause issued for jam track preview")
|
||||
@sound.pause() if @sound? # stop does not actually stop in windows client
|
||||
@setState({playing: false})
|
||||
|
||||
stop: (e) ->
|
||||
if e?
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
|
||||
if @state.no_audio
|
||||
context.JK.helpBubble(@playButton, 'There is no preview available for this track.', {}, {duration:2000})
|
||||
else
|
||||
@issueStop()
|
||||
@removeNowPlaying()
|
||||
|
||||
return false
|
||||
|
||||
})
|
||||
|
|
@ -0,0 +1,539 @@
|
|||
context = window
|
||||
MIX_MODES = context.JK.MIX_MODES
|
||||
|
||||
|
||||
@JamTrackSearchScreen = React.createClass({
|
||||
|
||||
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
|
||||
|
||||
LIMIT: 10
|
||||
instrument_logo_map: context.JK.getInstrumentIconMap24()
|
||||
input: null
|
||||
MAX_ARTIST_SHOW: 3
|
||||
|
||||
filterOption:() ->
|
||||
true
|
||||
|
||||
render: () ->
|
||||
|
||||
searchText = if @state.first_search then 'SEARCH' else 'SEARCH AGAIN'
|
||||
|
||||
uiJamTracks = []
|
||||
for jamtrack in @state.jamtracks
|
||||
trackRow = context._.clone(jamtrack)
|
||||
trackRow.track_cnt = jamtrack.tracks.length
|
||||
trackRow.tracks = []
|
||||
|
||||
# if an instrument is selected by the user, then re-order any jam tracks with a matching instrument to the top
|
||||
|
||||
###instrument = @instrument.val()
|
||||
if instrument?
|
||||
jamtrack.tracks.sort((a, b) =>
|
||||
aWeight = @computeWeight(a, instrument)
|
||||
bWeight = @computeWeight(b, instrument)
|
||||
return aWeight - bWeight
|
||||
)
|
||||
###
|
||||
for track in jamtrack.tracks
|
||||
trackRow.tracks.push(track)
|
||||
if track.track_type=='Master'
|
||||
track.instrument_desc = "Master"
|
||||
else
|
||||
inst = '../assets/content/icon_instrument_default24.png'
|
||||
if track.instrument?
|
||||
if track.instrument.id in @instrument_logo_map
|
||||
inst = @instrument_logo_map[track.instrument.id].asset
|
||||
track.instrument_desc = track.instrument.description
|
||||
track.instrument_url = inst
|
||||
|
||||
if track.part != ''
|
||||
track.instrument_desc += ' (' + track.part + ')'
|
||||
|
||||
trackRow.free_state = if @state.is_free then 'free' else 'non-free'
|
||||
|
||||
trackRow.is_free = @state.is_free
|
||||
|
||||
uiJamTracks.push trackRow
|
||||
|
||||
artists = []
|
||||
artistsShown = 0
|
||||
for artist in @state.artists
|
||||
|
||||
if @state.show_all_artists || artistsShown < @MAX_ARTIST_SHOW
|
||||
artists.push `<div key={artist.original_artist}><a className="show-artist" onClick={this.artistNavSelected} data-artist={artist.original_artist}>{artist.original_artist}</a></div>`
|
||||
|
||||
artistsShown += 1
|
||||
|
||||
|
||||
artists.push `<div key="no-results" className="no-results">No matching artists</div>` if artists.length == 0
|
||||
|
||||
if !@state.show_all_artists && @state.artists.length > @MAX_ARTIST_SHOW
|
||||
artists.push `<div key="show-hide-artists"><a onClick={this.showAllArtists} className="show-hide-artists">show all <div className="details-arrow arrow-down" /></a></div>`
|
||||
else if @state.show_all_artists
|
||||
artists.push `<div key="show-hide-artists"><a onClick={this.hideExtraArtists} className="show-hide-artists">hide artists <div className="details-arrow arrow-up" /></a></div>`
|
||||
|
||||
jamtracks = []
|
||||
|
||||
|
||||
for jamtrack in uiJamTracks
|
||||
|
||||
jamtrackPricesClasses = { "jamtrack-price" : true }
|
||||
jamtrackPricesClasses[jamtrack.free_state] = true
|
||||
jamtrackPricesClasses = classNames(jamtrackPricesClasses)
|
||||
|
||||
tracks = []
|
||||
for track in jamtrack.tracks
|
||||
tracks.push `<div className="jamtrack-track hidden" key={track.id} data-jamtrack-track-id={track.id}>
|
||||
<div className="jamtrack-preview">
|
||||
<JamTrackPreview jamTrack={jamtrack} jamTrackTrack={track} options={{master_shows_duration: true, color:'gray'}} />
|
||||
<div className="clearall" />
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
actionBtn = null
|
||||
if jamtrack.is_free
|
||||
actionBtn = `<a className="jamtrack-add-cart button-orange is_free" href="#" data-jamtrack-id={jamtrack.id}> GET IT FREE!</a>`
|
||||
else if jamtrack.purchased
|
||||
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="javascript:void(0)">PURCHASED</a>`
|
||||
else if jamtrack.added_cart
|
||||
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="client#/shoppingCart">ALREADY IN CART</a>`
|
||||
else
|
||||
actionBtn = `<a className="jamtrack-add-cart button-orange" href="#" data-jamtrack-id={jamtrack.id}>ADD TO CART</a>`
|
||||
|
||||
availabilityNotice = null
|
||||
if jamtrack.sales_region==context.JK.AVAILABILITY_US
|
||||
availabilityNotice =
|
||||
`<div className="jamtrack-license">
|
||||
This JamTrack available only to US customers.
|
||||
<a className="license-us-why" href="#">why?</a>
|
||||
</div>`
|
||||
|
||||
jamtracks.push `<tr className="jamtrack-record" key={jamtrack.id} data-jamtrack-id={jamtrack.id}>
|
||||
<td className="jamtrack-detail">
|
||||
<div className="jamtrack-name">"{jamtrack.name}"</div>
|
||||
<div className="jamtrack-original-artist">by {jamtrack.original_artist}</div>
|
||||
<br className="clearall"/>
|
||||
<div className="clearall detail-label extra hidden song-writer">Songwriters:</div>
|
||||
<div className="detail-value extra hidden">{jamtrack.songwriter}</div>
|
||||
<div className="clearall detail-label extra hidden">Publishers:</div>
|
||||
<div className="detail-value extra hidden">{jamtrack.publisher}</div>
|
||||
<div className="clearall detail-label extra hidden">Genres:</div>
|
||||
<div className="detail-value extra hidden">{jamtrack.genres.join(', ')}</div>
|
||||
<div className="clearall detail-label extra hidden">Version:</div>
|
||||
<div className="detail-value extra hidden">{jamtrack.recording_type}</div>
|
||||
</td>
|
||||
<td className="jamtrack-tracks">
|
||||
<div className="detail-arrow">
|
||||
<div className="jamtrack-detail-btn">
|
||||
show all tracks <a className="details-arrow arrow-down"/>
|
||||
</div>
|
||||
</div>
|
||||
{tracks}
|
||||
</td>
|
||||
<td className="jamtrack-action">
|
||||
<div className="jamtrack-action-container">
|
||||
<div className="jamtrack-actions">
|
||||
<div className={jamtrackPricesClasses}>$ {jamtrack.price}</div>
|
||||
{actionBtn}
|
||||
{availabilityNotice}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>`
|
||||
|
||||
#jamtracks.push `<div className="no-results">No matching JamTracks</div>` if jamtracks.length == 0
|
||||
|
||||
searchClasses = classNames({
|
||||
"button-orange" : true,
|
||||
"search-btn" : true,
|
||||
"disabled" : @state.searching
|
||||
})
|
||||
|
||||
artistSection = null
|
||||
jamTracksSection = null
|
||||
|
||||
if @state.type == 'user-input'
|
||||
if @state.searching
|
||||
jamtracksHeader = "searching..."
|
||||
else
|
||||
jamtracksHeader = "search results: #{@state.count} jamtracks"
|
||||
|
||||
|
||||
|
||||
else if @state.type == 'artist-select'
|
||||
jamtracksHeader = "search results: jamtracks for artist \"#{@state.artist}\""
|
||||
else if @state.type == 'song-select'
|
||||
jamtracksHeader = "search results: jamtrack \"#{@state.song}\""
|
||||
else
|
||||
throw "unknown search type #{@state.type}"
|
||||
|
||||
if !@state.first_search
|
||||
|
||||
|
||||
# only show the artists links if the user typed the results
|
||||
if @state.type == 'user-input'
|
||||
artistSection =
|
||||
`<div>
|
||||
<h2>search results: artists</h2>
|
||||
<div className="artist-results">
|
||||
{artists}
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
jamTracksSection =
|
||||
`<div>
|
||||
<h2 className="jamtrack-results-header">{jamtracksHeader} <a className="back-to-jamtracks-home" href="/client#/jamtrack">back to jamtracks home</a></h2>
|
||||
|
||||
<table className="generaltable jamtrack-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="jamtrack-detail">JAMTRACK</th>
|
||||
<th className="jamtrack-tracks">TRACKS INCLUDED / PREVIEW</th>
|
||||
<th className="jamtrack-shop">SHOP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="jamtrack-content">
|
||||
{jamtracks}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="end-of-jamtrack-list end-of-list">No more JamTracks</div>
|
||||
</div>`
|
||||
|
||||
options = {}
|
||||
|
||||
|
||||
searchValue = if @state.search == 'SEPARATOR' then '' else window.JamTrackSearchInput
|
||||
|
||||
`<div className="JamTrackSearchScreen">
|
||||
<div className="controls">
|
||||
<JamTrackAutoComplete onSearch={this.search} />
|
||||
<button className={searchClasses} name="search" onClick={this.userSearch}>{searchText}</button>
|
||||
</div>
|
||||
<div className="content-body-scroller">
|
||||
{artistSection}
|
||||
{jamTracksSection}
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
|
||||
|
||||
clearResults:() ->
|
||||
@setState({currentPage: 0, next: null, show_all_artists: false, artists:[], jamtracks:[], type: 'user-input', searching:false, artist: null, song:null, is_free: context.JK.currentUserFreeJamTrack, first_search: true})
|
||||
|
||||
|
||||
getInitialState: () ->
|
||||
{search: '', type: 'user-input', artists:[], jamtracks:[], show_all_artists: false, currentPage: 0, next: null, searching: false, first_search: true, count: 0, is_free: context.JK.currentUserFreeJamTrack}
|
||||
|
||||
onSelectChange: (val) ->
|
||||
#@logger.debug("CHANGE #{val}")
|
||||
|
||||
return false unless val?
|
||||
|
||||
search_type
|
||||
if val.indexOf('ARTIST=') == 0
|
||||
search_type = 'artist-select'
|
||||
artist = val['ARTIST='.length..-1]
|
||||
@search(search_type, artist)
|
||||
else if val.indexOf('SONG=') == 0
|
||||
search_type = 'song-select'
|
||||
song = val['SONG='.length..-1]
|
||||
@search(search_type, song)
|
||||
else
|
||||
@logger.debug("user selected separator")
|
||||
# this is to signal to the component that the separator was selected, and it has code in render to negate the selection
|
||||
setTimeout((() =>
|
||||
@setState({search:val})
|
||||
), 1)
|
||||
|
||||
return false
|
||||
|
||||
|
||||
onSelectBlur: (e) ->
|
||||
|
||||
#@logger.debug("blur time")
|
||||
|
||||
#@search()
|
||||
|
||||
showAllArtists: () ->
|
||||
@setState({show_all_artists: true})
|
||||
|
||||
hideExtraArtists: () ->
|
||||
@setState({show_all_artists: false})
|
||||
|
||||
|
||||
defaultQuery:(extra) ->
|
||||
query =
|
||||
per_page: @LIMIT
|
||||
page: @state.currentPage + 1
|
||||
sort_by: 'jamtrack'
|
||||
if @state.next
|
||||
query.since = @state.next
|
||||
$.extend(query, extra)
|
||||
|
||||
|
||||
|
||||
userSearch: (e) ->
|
||||
e.preventDefault()
|
||||
@search('user-input', window.JamTrackSearchInput)
|
||||
|
||||
search: (search_type, input) ->
|
||||
return if @state.searching
|
||||
return unless input?
|
||||
|
||||
window.JamTrackSearchInput = input
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
# disable scroll watching now that we've started a new search
|
||||
#@logger.debug("disabling infinite scroll")
|
||||
$root.find('.content-body-scroller').off('scroll')
|
||||
$root.find('.end-of-jamtrack-list').hide()
|
||||
|
||||
artistSearch = {limit:100}
|
||||
if search_type == 'artist-select'
|
||||
# the user wants to see just artists matching thes exact name
|
||||
artistSearch.artist = input
|
||||
else
|
||||
# the user wants to see anything sort of matching input
|
||||
artistSearch.artist_search = input
|
||||
|
||||
if input?
|
||||
@rest.getJamTrackArtists(artistSearch)
|
||||
.done((response) =>
|
||||
@setState({artists:response.artists})
|
||||
|
||||
# we have to make sure the query starts from page 1, and no 'next' from previous causes a 'since' to show up
|
||||
query = @defaultQuery({page: 1})
|
||||
delete query.since
|
||||
|
||||
@logger.debug("Search type", search_type)
|
||||
if search_type == 'artist-select'
|
||||
query.artist = input # works like exact match
|
||||
else if search_type == 'song-select'
|
||||
query.song = input # works as exact match
|
||||
|
||||
else
|
||||
query.search = input # works with tsv
|
||||
@rest.getJamTracks(query)
|
||||
.done((response) =>
|
||||
@setState({jamtracks: response.jamtracks, next: response.next, searching: false, first_search: false, currentPage: 1, count: response.count})
|
||||
)
|
||||
.fail(() =>
|
||||
@app.notifyServerError jqXHR, 'Search Unavailable'
|
||||
@setState({searching: false, first_search: false})
|
||||
)
|
||||
)
|
||||
.fail(() =>
|
||||
@app.notifyServerError jqXHR, 'Search Unavailable'
|
||||
@setState({searching: false, first_search: false})
|
||||
)
|
||||
|
||||
@setState({currentPage: 0, next: null, artists: [], jamtracks:[], searching: true, artist: input, song: input, type: search_type, search:input, count:0})
|
||||
|
||||
getOptions: (input, callback) =>
|
||||
|
||||
#@logger.debug("getOptions input #{input}", this)
|
||||
|
||||
# sigh. ugly global
|
||||
window.JamTrackSearchInput = input
|
||||
|
||||
if !input? || input.length == 0
|
||||
callback(null, {options: [], complete: false})
|
||||
return
|
||||
|
||||
@rest.autocompleteJamTracks({match:input, limit:5})
|
||||
.done((autocomplete) =>
|
||||
|
||||
options = []
|
||||
for artist in autocomplete.artists
|
||||
options.push { value: "ARTIST=#{artist.original_artist}", label: "Artist: #{artist.original_artist}" }
|
||||
|
||||
if options.length > 0 && autocomplete.songs.length > 0
|
||||
options.push { value: 'SEPARATOR', label: "---------------"}
|
||||
|
||||
for jamtrack in autocomplete.songs
|
||||
options.push { value: "SONG=#{jamtrack.name}", label: "Song: #{jamtrack.name}" }
|
||||
|
||||
callback(null, {options: options, complete: false})
|
||||
)
|
||||
|
||||
artistNavSelected: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
@search('artist-select', $(e.target).attr('data-artist'))
|
||||
|
||||
componentDidMount: () ->
|
||||
#@logger.debug("componentDidMount")
|
||||
|
||||
componentDidUpdate: ( ) ->
|
||||
$root = $(this.getDOMNode())
|
||||
$scroller = $root.find('.content-body-scroller')
|
||||
|
||||
for jamTrack in @state.jamtracks
|
||||
jamtrackElement = $root.find("tbody .jamtrack-record[data-jamtrack-id=\"#{jamTrack.id}\"]")
|
||||
alreadyRegistered = jamtrackElement.data('registered')
|
||||
|
||||
unless alreadyRegistered
|
||||
jamtrackElement.data('jamTrack', jamTrack)
|
||||
jamtrackElement.data('registered', true)
|
||||
jamtrackElement.data('expanded', true)
|
||||
|
||||
@handleExpanded(jamtrackElement)
|
||||
@registerEvents(jamtrackElement)
|
||||
|
||||
|
||||
if @state.next == null
|
||||
$scroller = $root.find('.content-body-scroller')
|
||||
# if we less results than asked for, end searching
|
||||
#$scroller.infinitescroll 'pause'
|
||||
#@logger.debug("disabling infinite scroll")
|
||||
$scroller.off('scroll')
|
||||
if @state.currentPage == 1 and @state.jamtracks.length == 0
|
||||
$root.find('.end-of-jamtrack-list').text('No JamTracks found matching your search').show()
|
||||
@logger.debug("JamTrackSearch: empty search")
|
||||
else if @state.currentPage > 0
|
||||
@logger.debug("end of search")
|
||||
$noMoreJamtracks = $root.find('.end-of-jamtrack-list').text('No more JamTracks').show()
|
||||
# there are bugs with infinitescroll not removing the 'loading'.
|
||||
# it's most noticeable at the end of the list, so whack all such entries
|
||||
else
|
||||
@registerInfiniteScroll($scroller)
|
||||
|
||||
|
||||
|
||||
registerInfiniteScroll:($scroller) ->
|
||||
@logger.debug("registering infinite scroll")
|
||||
$scroller.off('scroll')
|
||||
$scroller.on('scroll', () =>
|
||||
|
||||
# be sure to not fire off many refreshes when user hits the bottom
|
||||
return if @refreshing
|
||||
|
||||
if $scroller.scrollTop() + $scroller.innerHeight() + 100 >= $scroller[0].scrollHeight
|
||||
$scroller.append('<div class="infinite-scroll-loader-2">... Loading more JamTracks ...</div>')
|
||||
@refreshing = true
|
||||
@setState({searching: true})
|
||||
@logger.debug("refreshing more jamtracks for infinite scroll")
|
||||
@rest.getJamTracks(@defaultQuery({search:@state.search}))
|
||||
.done((json) =>
|
||||
@setState({jamtracks: @state.jamtracks.concat(json.jamtracks), next: json.next, first_search: false, currentPage: @state.currentPage + 1, count: json.count})
|
||||
)
|
||||
.always(() =>
|
||||
$scroller.find('.infinite-scroll-loader-2').remove()
|
||||
@refreshing = false
|
||||
@setState({searching: false})
|
||||
)
|
||||
)
|
||||
|
||||
playJamtrack:(e) ->
|
||||
e.preventDefault()
|
||||
|
||||
addToCartJamtrack:(e) ->
|
||||
e.preventDefault()
|
||||
$target = $(e.target)
|
||||
params = id: $target.attr('data-jamtrack-id')
|
||||
isFree = $(e.target).is('.is_free')
|
||||
|
||||
@rest.addJamtrackToShoppingCart(params).done((response) =>
|
||||
if(isFree)
|
||||
if context.JK.currentUserId?
|
||||
context.JK.currentUserFreeJamTrack = true # make sure the user sees no more free notices
|
||||
context.location = '/client#/redeemComplete'
|
||||
else
|
||||
# now make a rest call to buy it
|
||||
context.location = '/client#/redeemSignup'
|
||||
|
||||
else
|
||||
context.location = '/client#/shoppingCart'
|
||||
|
||||
).fail(() => @app.ajaxError)
|
||||
|
||||
licenseUSWhy:(e) ->
|
||||
e.preventDefault()
|
||||
@app.layout.showDialog 'jamtrack-availability-dialog'
|
||||
|
||||
registerEvents:($parent) ->
|
||||
$parent.find('.play-button').on 'click', @playJamtrack
|
||||
$parent.find('.jamtrack-add-cart').on 'click', @addToCartJamtrack
|
||||
$parent.find('.license-us-why').on 'click', @licenseUSWhy
|
||||
$parent.find('.jamtrack-detail-btn').on 'click', @toggleExpanded
|
||||
|
||||
toggleExpanded:(e) ->
|
||||
e.preventDefault()
|
||||
jamtrackRecord = $(e.target).parents('.jamtrack-record')
|
||||
@handleExpanded(jamtrackRecord)
|
||||
|
||||
handleExpanded:(trackElement) ->
|
||||
jamTrack = trackElement.data('jamTrack')
|
||||
expanded = trackElement.data('expanded')
|
||||
expand = !expanded
|
||||
trackElement.data('expanded', expand)
|
||||
|
||||
detailArrow = trackElement.find('.jamtrack-detail-btn')
|
||||
|
||||
if expand
|
||||
trackElement.find('.extra').removeClass('hidden')
|
||||
detailArrow.html('hide tracks <a class="details-arrow arrow-up"></a>')
|
||||
for track in jamTrack.tracks
|
||||
trackElement.find("[data-jamtrack-track-id='#{track.id}']").removeClass('hidden')
|
||||
else
|
||||
trackElement.find('.extra').addClass('hidden')
|
||||
detailArrow.html('show all tracks <a class="details-arrow arrow-down"></a>')
|
||||
count = 0
|
||||
for track in jamTrack.tracks
|
||||
if count < 6
|
||||
trackElement.find("[data-jamtrack-track-id='#{track.id}']").removeClass('hidden')
|
||||
else
|
||||
trackElement.find("[data-jamtrack-track-id='#{track.id}']").addClass('hidden')
|
||||
count++
|
||||
|
||||
|
||||
afterShow: (data) ->
|
||||
|
||||
@setFilterFromURL()
|
||||
|
||||
setFilterFromURL:() ->
|
||||
|
||||
performSearch = false
|
||||
if $.QueryString['artist']?
|
||||
performSearch = true
|
||||
@search('artist-select', $.QueryString['artist'])
|
||||
else if $.QueryString['song']?
|
||||
performSearch = true
|
||||
@search('song-select', $.QueryString['song'])
|
||||
else if $.QueryString['search']?
|
||||
performSearch = true
|
||||
@search('user-input', $.QueryString['search'])
|
||||
else
|
||||
# check if someone has requested a search for us as we transition to this screen
|
||||
search = context.JamTrackStore.checkRequestedSearch()
|
||||
if search?
|
||||
performSearch = true
|
||||
@search(search.searchType, search.searchData)
|
||||
|
||||
if performSearch
|
||||
if window.history.replaceState #ie9 proofing
|
||||
window.history.replaceState({}, "", "/client#/jamtrack/search")
|
||||
|
||||
|
||||
beforeShow: () ->
|
||||
@setState({is_free: context.JK.currentUserFreeJamTrack})
|
||||
if !@state.first_search
|
||||
@search(@state.type, window.JamTrackSearchInput)
|
||||
|
||||
|
||||
|
||||
onAppInit: (@app) ->
|
||||
|
||||
window.JamTrackSearchInput = '' # need to be not null; otherwise react-select chokes
|
||||
@EVENTS = context.JK.EVENTS
|
||||
@rest = context.JK.Rest()
|
||||
@logger = context.JK.logger
|
||||
|
||||
|
||||
screenBindings =
|
||||
'beforeShow': @beforeShow
|
||||
'afterShow': @afterShow
|
||||
|
||||
@app.bindScreen('jamtrack/search', screenBindings)
|
||||
})
|
||||
|
|
@ -7,6 +7,12 @@ mixins = []
|
|||
|
||||
# this check ensures we attempt to listen if this component is created in a popup
|
||||
reactContext = if window.opener? then window.opener else window
|
||||
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
||||
if window.opener?
|
||||
try
|
||||
m = window.opener.MixerStore
|
||||
catch e
|
||||
reactContext = window
|
||||
|
||||
MixerStore = reactContext.MixerStore
|
||||
MixerActions = reactContext.MixerActions
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
mixins = []
|
||||
|
||||
|
||||
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
||||
|
||||
accessOpener = false
|
||||
if window.opener?
|
||||
try
|
||||
m = window.opener.MixerStore
|
||||
accessOpener = true
|
||||
catch e
|
||||
|
||||
|
||||
if accessOpener
|
||||
VideoActions = window.opener.VideoActions
|
||||
VideoStore = window.opener.VideoStore
|
||||
|
||||
#mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged'))
|
||||
|
||||
|
||||
@PopupConfigureVideoGear = React.createClass({
|
||||
|
||||
mixins: mixins
|
||||
logger: context.JK.logger
|
||||
|
||||
render: () ->
|
||||
`<div className="configure-video-geaor">
|
||||
<div className="popup-contents">
|
||||
<div classNmae="video-header">
|
||||
<h2 className="subcaption">video is not configured</h2>
|
||||
<div className="subcaption">
|
||||
If you might like to use video in sessions, please select a webcam to use, and a video resolution and frame rate to capture. Then click the TEST WEBCAM button to verify that you see video from your webcam properly. In sessions, you can choose to turn video on or off any time.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="webcam-container">
|
||||
<WebcamViewer isVisible={true} />
|
||||
</div>
|
||||
|
||||
<div className="important-note">
|
||||
<h5>
|
||||
Important Note
|
||||
</h5>
|
||||
<div className="contents">
|
||||
You can update your video configuration any time in your Account settings, or in the menus of the video window while in a session.
|
||||
</div>
|
||||
</div>
|
||||
<div className="clearall" />
|
||||
</div>
|
||||
|
||||
<div className="close-behavior">
|
||||
<span className="field">
|
||||
<input type="checkbox" name="dont_show" /><label htmlFor="dont_show">Don't show this again</label>
|
||||
</span>
|
||||
<a className="button-orange close-link" onClick={this.close}>CLOSE</a>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
close: () ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
VideoActions.configureVideoPopupClosed($dontShow.is(':checked'))
|
||||
window.close()
|
||||
|
||||
windowUnloaded: () ->
|
||||
|
||||
$root = jQuery(this.getDOMNode())
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
|
||||
VideoActions.howToUseVideoPopupClosed($dontShow.is(':checked'))
|
||||
|
||||
componentDidMount: () ->
|
||||
$(window).unload(@windowUnloaded)
|
||||
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
context.JK.checkbox($dontShow)
|
||||
|
||||
@resizeWindow()
|
||||
|
||||
# this is necessary due to whatever the client's rendering behavior is.
|
||||
setTimeout(@resizeWindow, 300)
|
||||
|
||||
componentDidUpdate: () ->
|
||||
@resizeWindow()
|
||||
|
||||
resizeWindow: () =>
|
||||
$container = $('#minimal-container')
|
||||
width = $container.width()
|
||||
height = $container.height()
|
||||
|
||||
# there is 20px or so of unused space at the top of the page. can't figure out why it's there. (above #minimal-container)
|
||||
mysteryTopMargin = 20
|
||||
|
||||
# deal with chrome in real browsers
|
||||
offset = (window.outerHeight - window.innerHeight) + mysteryTopMargin
|
||||
|
||||
# handle native client chrome that the above outer-inner doesn't catch
|
||||
#if navigator.userAgent.indexOf('JamKazam') > -1
|
||||
|
||||
#offset += 25
|
||||
|
||||
window.resizeTo(width, height + offset)
|
||||
})
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
mixins = []
|
||||
|
||||
|
||||
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
||||
|
||||
accessOpener = false
|
||||
if window.opener?
|
||||
try
|
||||
m = window.opener.MixerStore
|
||||
accessOpener = true
|
||||
catch e
|
||||
|
||||
|
||||
if accessOpener
|
||||
VideoActions = window.opener.VideoActions
|
||||
VideoStore = window.opener.VideoStore
|
||||
|
||||
#mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged'))
|
||||
|
||||
@PopupHowToUseVideo = React.createClass({
|
||||
|
||||
render: () ->
|
||||
`<div className="how-to-use-video">
|
||||
<div className="popup-contents">
|
||||
<div className="control-holder">
|
||||
<a className="control start-video" onClick={this.startVideo}>
|
||||
<span className="helper" />
|
||||
<img src="/assets/content/webcam-icon-gray.png" width="20" height="20" />
|
||||
<span id="recording-status">Start Webcam</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="important-note">
|
||||
<h5>
|
||||
Important Note
|
||||
</h5>
|
||||
<div className="contents">
|
||||
You can start and stop your webcam at any time by navigating to the Webcam menu of the video window and selecting Start/Stop Webcam.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<input type="checkbox" name="dont_show" /><label htmlFor="dont_show">Don't show this again</label>
|
||||
</div>
|
||||
|
||||
<div className="close-behavior">
|
||||
<a className="button-orange close-link" onClick={this.close}>CLOSE</a>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
close: () ->
|
||||
$root = jQuery(this.getDOMNode())
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
VideoActions.howToUseVideoPopupClosed($dontShow.is(':checked'))
|
||||
window.close()
|
||||
|
||||
startVideo: (e) ->
|
||||
e.preventDefault
|
||||
VideoActions.startVideo()
|
||||
|
||||
windowUnloaded: () ->
|
||||
|
||||
$root = jQuery(this.getDOMNode())
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
|
||||
VideoActions.howToUseVideoPopupClosed($dontShow.is(':checked'))
|
||||
|
||||
componentDidMount: () ->
|
||||
$(window).unload(@windowUnloaded)
|
||||
|
||||
$root = jQuery(this.getDOMNode())
|
||||
|
||||
$dontShow = $root.find('input[name="dont_show"]')
|
||||
context.JK.checkbox($dontShow)
|
||||
|
||||
@resizeWindow()
|
||||
|
||||
# this is necessary due to whatever the client's rendering behavior is.
|
||||
setTimeout(@resizeWindow, 300)
|
||||
|
||||
componentDidUpdate: () ->
|
||||
@resizeWindow()
|
||||
|
||||
resizeWindow: () =>
|
||||
$container = $('#minimal-container')
|
||||
width = $container.width()
|
||||
height = $container.height()
|
||||
|
||||
# there is 20px or so of unused space at the top of the page. can't figure out why it's there. (above #minimal-container)
|
||||
mysteryTopMargin = 20
|
||||
|
||||
# deal with chrome in real browsers
|
||||
offset = (window.outerHeight - window.innerHeight) + mysteryTopMargin
|
||||
|
||||
# handle native client chrome that the above outer-inner doesn't catch
|
||||
#if navigator.userAgent.indexOf('JamKazam') > -1
|
||||
|
||||
#offset += 25
|
||||
|
||||
window.resizeTo(width, height + offset)
|
||||
})
|
||||
|
|
@ -3,7 +3,18 @@ logger = context.JK.logger
|
|||
|
||||
mixins = []
|
||||
|
||||
|
||||
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
||||
|
||||
accessOpener = false
|
||||
if window.opener?
|
||||
try
|
||||
m = window.opener.MixerStore
|
||||
accessOpener = true
|
||||
catch e
|
||||
|
||||
|
||||
if accessOpener
|
||||
SessionActions = window.opener.SessionActions
|
||||
MediaPlaybackStore = window.opener.MediaPlaybackStore
|
||||
MixerActions = window.opener.MixerActions
|
||||
|
|
|
|||
|
|
@ -2,8 +2,17 @@ context = window
|
|||
|
||||
mixins = []
|
||||
|
||||
# this check ensures we attempt to listen if this component is created in a popup
|
||||
if window.opener
|
||||
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
||||
|
||||
accessOpener = false
|
||||
if window.opener?
|
||||
try
|
||||
m = window.opener.MixerStore
|
||||
accessOpener = true
|
||||
catch e
|
||||
|
||||
|
||||
if accessOpener
|
||||
mixins.push(Reflux.listenTo(window.opener.RecordingStore,"onRecordingStateChanged"))
|
||||
|
||||
@PopupRecordingStartStop = React.createClass({
|
||||
|
|
|
|||
|
|
@ -203,6 +203,8 @@ ChannelGroupIds = context.JK.ChannelGroupIds
|
|||
contents = null
|
||||
mediaTracks = []
|
||||
|
||||
mediaTracks.push `<div key="download-jamtrack-holder" className="download-jamtrack-holder"></div>`
|
||||
|
||||
if this.state.downloadJamTrack?
|
||||
closeOptions =
|
||||
`<div>
|
||||
|
|
@ -210,7 +212,6 @@ ChannelGroupIds = context.JK.ChannelGroupIds
|
|||
<img src="/assets/content/icon_close.png" width="18" height="20" />
|
||||
Close JamTrack
|
||||
</a>
|
||||
<div className="download-jamtrack-holder"></div>
|
||||
</div>`
|
||||
|
||||
contents = closeOptions
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
|||
<p className="notice">
|
||||
You have not set up any inputs for your instrument or vocals.
|
||||
If you want to hear yourself play through the JamKazam app,
|
||||
and let the app mix your live playing with JamTracks, or with other musicians in online sessions,
|
||||
<a href="#" className="open-ftue-no-tracks" onClick={this.goToFtue}>click here now.</a>
|
||||
and let the app mix your live playing with JamTracks, or with other musicians in online sessions, <a href="#" className="open-ftue-no-tracks" onClick={this.goToFtue}>click here now.</a>
|
||||
</p>
|
||||
</div>`
|
||||
|
||||
|
|
|
|||
|
|
@ -3,5 +3,7 @@ context = window
|
|||
@JamTrackActions = Reflux.createActions({
|
||||
open: {}
|
||||
close: {}
|
||||
requestSearch: {}
|
||||
requestFilter: {}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
context = window
|
||||
|
||||
@JamTrackPreviewActions = Reflux.createActions({
|
||||
startedPlaying: {}
|
||||
stoppedPlaying: {}
|
||||
screenChange: {}
|
||||
})
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
context = window
|
||||
|
||||
@VideoActions = Reflux.createActions({
|
||||
refresh: {}
|
||||
stopVideo: {}
|
||||
startVideo: {}
|
||||
setVideoEncodeResolution: {}
|
||||
setSendFrameRate: {}
|
||||
selectDevice: {}
|
||||
videoWindowOpened : {}
|
||||
videoWindowClosed : {}
|
||||
howToUseVideoPopupClosed: {}
|
||||
toggleVideo: {}
|
||||
testVideo: {}
|
||||
configureVideoPopupClosed: {}
|
||||
checkPromptConfigureVideo: {}
|
||||
setVideoEnabled: {}
|
||||
})
|
||||
|
|
@ -606,7 +606,7 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
|
||||
# sanity check
|
||||
if mixer && mixer.group_id != ChannelGroupIds.PeerAudioInputMusicGroup
|
||||
logger.error("found remote mixer that was not of groupID: PeerAudioInputMusicGroup", mixer)
|
||||
logger.warn("master: found remote mixer that was not of groupID: PeerAudioInputMusicGroup", client_id, track.client_track_id, mixer)
|
||||
|
||||
vuMixer = mixer
|
||||
muteMixer = mixer
|
||||
|
|
@ -618,7 +618,7 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
oppositeMixer = oppositeMixers[ChannelGroupIds.UserMusicInputGroup][0]
|
||||
|
||||
if !oppositeMixer
|
||||
logger.error("unable to find UserMusicInputGroup corresponding to PeerAudioInputMusicGroup mixer", mixer )
|
||||
logger.warn("unable to find UserMusicInputGroup corresponding to PeerAudioInputMusicGroup mixer", mixer )
|
||||
|
||||
when MIX_MODES.PERSONAL
|
||||
mixers = @groupedMixersForClientId(client_id, [ ChannelGroupIds.UserMusicInputGroup], {}, MIX_MODES.PERSONAL)
|
||||
|
|
@ -632,9 +632,9 @@ MIX_MODES = context.JK.MIX_MODES;
|
|||
# now grab the PeerAudioInputMusicGroup in master mode to satisfy the 'opposite' mixer
|
||||
oppositeMixer = @getMixerByTrackId(track.client_track_id, MIX_MODES.MASTER)
|
||||
if !oppositeMixer
|
||||
logger.debug("unable to find a PeerAudioInputMusicGroup master mixer matching a UserMusicInput", client_id, track.client_track_id)
|
||||
logger.debug("personal: unable to find a PeerAudioInputMusicGroup master mixer matching a UserMusicInput", client_id, track.client_track_id)
|
||||
else if oppositeMixer.group_id != ChannelGroupIds.PeerAudioInputMusicGroup
|
||||
logger.error("found remote mixer that was not of groupID: PeerAudioInputMusicGroup", mixer)
|
||||
logger.error("personaol: found remote mixer that was not of groupID: PeerAudioInputMusicGroup", client_id, track.client_track_id, mixer)
|
||||
|
||||
#vuMixer = oppositeMixer; # for personal mode, use the PeerAudioInputMusicGroup's VUs
|
||||
|
||||
|
|
|
|||
|
|
@ -4,17 +4,19 @@ context = window
|
|||
|
||||
watchVideo: (e) ->
|
||||
e.preventDefault()
|
||||
window.open("/popups/youtube/player?id=askHvcCoNfw", 'What Are JamTracks?', 'scrollbars=yes,toolbar=no,status=no,height=282,width=500')
|
||||
window.open("/popups/youtube/player?id=askHvcCoNfw", 'What Are JamTracks?', 'scrollbars=yes,toolbar=no,status=no,height=540,width=960')
|
||||
|
||||
render: () ->
|
||||
|
||||
header = null
|
||||
if @props.band
|
||||
if @props.instrument
|
||||
header = "We Have #{@props.instrument_count} JamTracks With #{@props.instrument} Parts - Play Along With Your Favorites!"
|
||||
else if @props.band
|
||||
header = "#{@props.jam_track.original_artist} Backing Tracks - Complete Multitracks"
|
||||
else if @props.generic?
|
||||
header = "Backing Tracks + Free Amazing App = Unmatched Experience"
|
||||
else
|
||||
header = "#{@props.jam_track.name} Backing Track by #{@props.jam_track.original_artist}"
|
||||
header = "\"#{@props.jam_track.name}\" Backing Track by #{@props.jam_track.original_artist}"
|
||||
|
||||
|
||||
`<div className="one_by_two">
|
||||
|
|
@ -39,22 +39,48 @@ rest = context.JK.Rest()
|
|||
{processing: false}
|
||||
|
||||
render: () ->
|
||||
bandBrowseUrl = "/client?artist=#{this.props.jam_track.original_artist}#/jamtrackBrowse"
|
||||
|
||||
|
||||
isFree = context.JK.currentUserFreeJamTrack
|
||||
|
||||
|
||||
if isFree
|
||||
img =`<img src="/assets/web/button_cta_jamtrack_free.png" />`
|
||||
else
|
||||
img =`<img src="/assets/web/buy-jamtrack-cta.png" />`
|
||||
|
||||
|
||||
if @props.instrument?
|
||||
getFreeText = "Get \"#{this.props.jam_track.name}\" JamTrack Free Now"
|
||||
instrumentBrowseUrl = "/client?instrument=#{this.props.instrument_id}#/jamtrack/filter"
|
||||
|
||||
`<div className="cta-holder instrument-selection">
|
||||
<div className="checkout">
|
||||
<a href="/client#/jamtrack/search" onClick={this.redeem} className="cta-free-jamtrack" alt="ClICK HERE TO PICK YOUR FIRST JAMTRACK FREE!">
|
||||
{getFreeText}
|
||||
</a>
|
||||
<div className="browse-instrument">
|
||||
<a href={instrumentBrowseUrl}>Or Browse All {this.props.instrument_count} JamTracks With {this.props.instrument} Parts<br/>And Get Your Favorite Free!</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
else
|
||||
bandBrowseUrl = "/client?artist=#{this.props.jam_track.original_artist}#/jamtrack/search"
|
||||
|
||||
`<div className="cta-holder">
|
||||
<div className="checkout">
|
||||
<a href="/client#/jamtrackBrowse" onClick={this.redeem} className="cta-free-jamtrack" alt="ClICK HERE TO PICK YOUR FIRST JAMTRACK FREE!">
|
||||
<img src="/assets/web/button_cta_jamtrack_free.png" />
|
||||
<a href="/client#/jamtrack/search" onClick={this.redeem} className="cta-free-jamtrack" alt="ClICK HERE TO PICK YOUR FIRST JAMTRACK FREE!">
|
||||
{img}
|
||||
</a>
|
||||
<span className="value-indicator">$1.99 value</span>
|
||||
</div>
|
||||
<br/>
|
||||
<div className="browse-band">
|
||||
<a href={bandBrowseUrl}>or browse all {this.props.band_track_count} AC/DC backing tracks</a>
|
||||
<a href={bandBrowseUrl}>or browse all {this.props.band_track_count} {this.props.jam_track.original_artist} backing tracks</a>
|
||||
</div>
|
||||
<br/>
|
||||
<div className="browse-all">
|
||||
<a href="/client#/jamtrackBrowse">or browse all {this.props.all_track_count} backing tracks!</a>
|
||||
<a href="/client?search=#/jamtrack/search">or browse all {this.props.all_track_count} backing tracks!</a>
|
||||
</div>
|
||||
</div>`
|
||||
})
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
rest = context.JK.Rest()
|
||||
EVENTS = context.JK.EVENTS
|
||||
|
||||
|
||||
JamTrackPreviewActions = @JamTrackPreviewActions
|
||||
|
||||
@JamTrackPreviewStore = Reflux.createStore(
|
||||
{
|
||||
listenables: JamTrackPreviewActions
|
||||
logger: context.JK.logger
|
||||
nowPlaying: []
|
||||
|
||||
init: ->
|
||||
# Register with the app store to get @app
|
||||
this.listenTo(context.AppStore, this.onAppInit)
|
||||
|
||||
onAppInit: (app) ->
|
||||
@app = app
|
||||
|
||||
onStartedPlaying: (preview) ->
|
||||
|
||||
for playingSound in @nowPlaying
|
||||
playingSound.issueStop()
|
||||
@nowPlaying = []
|
||||
@nowPlaying.push(preview)
|
||||
|
||||
onStoppedPlaying: (preview) ->
|
||||
@nowPlaying.splice(preview)
|
||||
if @nowPlaying.length > 0
|
||||
@logger.warn("multiple jamtrack previews playing")
|
||||
|
||||
onScreenChange: () ->
|
||||
for playingSound in @nowPlaying
|
||||
playingSound.issueStop()
|
||||
@nowPlaying = []
|
||||
}
|
||||
)
|
||||
|
|
@ -11,6 +11,8 @@ JamTrackActions = @JamTrackActions
|
|||
{
|
||||
listenables: JamTrackActions
|
||||
jamTrack: null
|
||||
requestedSearch: null
|
||||
requestedFilter: null
|
||||
|
||||
init: ->
|
||||
# Register with the app store to get @app
|
||||
|
|
@ -30,5 +32,26 @@ JamTrackActions = @JamTrackActions
|
|||
onClose: () ->
|
||||
@jamTrack = null
|
||||
this.trigger(@jamTrack)
|
||||
|
||||
onRequestSearch:(searchType, searchData) ->
|
||||
@requestedSearch = {searchType: searchType, searchData: searchData}
|
||||
window.location.href = '/client#/jamtrack/search'
|
||||
|
||||
# needed by the JamTrackSearchScreen
|
||||
checkRequestedSearch:() ->
|
||||
requested = @requestedSearch
|
||||
@requestedSearch = null
|
||||
requested
|
||||
|
||||
onRequestFilter:(genre, instrument) ->
|
||||
@requestedFilter = {genre: genre, instrument:instrument}
|
||||
window.location.href = '/client#/jamtrack/filter'
|
||||
|
||||
# needed by the JamTrackSearchScreen
|
||||
checkRequestedFilter:() ->
|
||||
requested = @requestedFilter
|
||||
@requestedFilter = null
|
||||
requested
|
||||
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
@PlatformStore = Reflux.createStore(
|
||||
{
|
||||
logger: context.JK.logger
|
||||
os: null
|
||||
|
||||
init: ->
|
||||
this.listenTo(context.AppStore, this.onAppInit)
|
||||
|
||||
onAppInit: (@app) ->
|
||||
@os = context.jamClient.GetOSAsString()
|
||||
this.trigger({os: @os, isWindows: @isWindows})
|
||||
|
||||
isWindows: ->
|
||||
@os == 'Win32'
|
||||
|
||||
}
|
||||
)
|
||||
|
|
@ -9,6 +9,7 @@ JamTrackActions = @JamTrackActions
|
|||
SessionActions = @SessionActions
|
||||
RecordingActions = @RecordingActions
|
||||
NotificationActions = @NotificationActions
|
||||
VideoActions = @VideoActions
|
||||
|
||||
@SessionStore = Reflux.createStore(
|
||||
{
|
||||
|
|
@ -42,6 +43,7 @@ NotificationActions = @NotificationActions
|
|||
# Register with the app store to get @app
|
||||
this.listenTo(context.AppStore, this.onAppInit)
|
||||
this.listenTo(context.RecordingStore, this.onRecordingChanged)
|
||||
this.listenTo(context.VideoStore, this.onVideoChanged)
|
||||
|
||||
|
||||
onAppInit: (@app) ->
|
||||
|
|
@ -51,10 +53,8 @@ NotificationActions = @NotificationActions
|
|||
RecordingActions.initModel(@recordingModel)
|
||||
@helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack)
|
||||
|
||||
if gon.global.video_available && gon.global.video_available!="none" && context.JK.WebcamViewer?
|
||||
@webcamViewer = new context.JK.WebcamViewer()
|
||||
@webcamViewer.init($("#create-session-layout"))
|
||||
@webcamViewer.setVideoOff()
|
||||
|
||||
onVideoChanged: (@videoState) ->
|
||||
|
||||
issueChange: () ->
|
||||
@helper = new context.SessionHelper(@app, @currentSession, @participantsEverSeen, @isRecording, @downloadingJamTrack)
|
||||
|
|
@ -181,8 +181,16 @@ NotificationActions = @NotificationActions
|
|||
@issueChange()
|
||||
|
||||
onToggleSessionVideo: () ->
|
||||
|
||||
if @videoState?.videoEnabled
|
||||
logger.debug("toggle session video")
|
||||
@webcamViewer.toggleWebcam() if @webcamViewer?
|
||||
VideoActions.toggleVideo()
|
||||
else
|
||||
context.JK.Banner.showAlert({
|
||||
title: "Video Is Disabled",
|
||||
html: "To re-enable video, you must go your video settings in your account settings and enable video.",
|
||||
})
|
||||
|
||||
|
||||
onAudioResync: () ->
|
||||
logger.debug("audio resyncing")
|
||||
|
|
@ -558,15 +566,14 @@ NotificationActions = @NotificationActions
|
|||
shareDialog.initialize(context.JK.FacebookHelperInstance);
|
||||
|
||||
# initialize webcamViewer
|
||||
if gon.global.video_available && gon.global.video_available != "none"
|
||||
@webcamViewer.beforeShow()
|
||||
VideoActions.stopVideo();
|
||||
|
||||
# double-check that we are connected to the server via websocket
|
||||
|
||||
return unless @ensureConnected()
|
||||
|
||||
# just make double sure a previous session state is cleared out
|
||||
@sessionEnded()
|
||||
@sessionEnded(true)
|
||||
|
||||
# update the session data to be empty
|
||||
@updateCurrentSession(null)
|
||||
|
|
@ -973,8 +980,7 @@ NotificationActions = @NotificationActions
|
|||
logger.warn("no location specified in leaveSession action", behavior)
|
||||
window.location = '/client#/home'
|
||||
|
||||
if gon.global.video_available && gon.global.video_available != "none"
|
||||
@webcamViewer.setVideoOff()
|
||||
#VideoActions.stopVideo()
|
||||
|
||||
@leaveSession()
|
||||
|
||||
|
|
@ -1019,7 +1025,7 @@ NotificationActions = @NotificationActions
|
|||
selfOpenedJamTracks: () ->
|
||||
@currentSession && (@currentSession.jam_track_initiator_id == context.JK.currentUserId)
|
||||
|
||||
sessionEnded: () ->
|
||||
sessionEnded: (onJoin) ->
|
||||
# cleanup
|
||||
|
||||
context.JK.JamServer.unregisterMessageCallback(context.JK.MessageType.SESSION_JOIN, @trackChanges);
|
||||
|
|
@ -1053,7 +1059,7 @@ NotificationActions = @NotificationActions
|
|||
@controlsLockedForJamTrackRecording = false
|
||||
@openBackingTrack = null
|
||||
@downloadingJamTrack = false
|
||||
@sessionUtils.setAutoOpenJamTrack(null)
|
||||
@sessionUtils.setAutoOpenJamTrack(null) unless onJoin
|
||||
|
||||
JamTrackActions.close()
|
||||
NotificationActions.sessionEnded()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,253 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
EVENTS = context.JK.EVENTS
|
||||
NAMED_MESSAGES = context.JK.NAMED_MESSAGES
|
||||
|
||||
VideoActions = @VideoActions
|
||||
|
||||
BackendToFrontend = {
|
||||
1 : "CIF (352x288)",
|
||||
2 : "VGA (640x480)",
|
||||
3 : "4CIF (704x576)",
|
||||
4 : "1/2 720p HD (640x360)",
|
||||
5 : "720p HD (1280x720)",
|
||||
6 : "1080p HD (1920x1080)"
|
||||
}
|
||||
|
||||
BackendToFrontendFPS = {
|
||||
|
||||
0: 30,
|
||||
1: 24,
|
||||
2: 20,
|
||||
3: 15,
|
||||
4: 10
|
||||
}
|
||||
|
||||
@VideoStore = Reflux.createStore(
|
||||
{
|
||||
listenables: VideoActions
|
||||
logger: context.JK.logger
|
||||
videoShared: false
|
||||
videoOpen : false
|
||||
state : null
|
||||
everDisabled : false
|
||||
|
||||
init: ->
|
||||
this.listenTo(context.AppStore, this.onAppInit)
|
||||
|
||||
onAppInit: (@app) ->
|
||||
|
||||
|
||||
|
||||
# someone has requested us to refresh our config
|
||||
onRefresh: ->
|
||||
|
||||
# don't do any check if this is a client with no video enabled
|
||||
return unless context.jamClient.FTUECurrentSelectedVideoDevice?
|
||||
|
||||
videoEnabled = context.jamClient.FTUEGetVideoShareEnable()
|
||||
|
||||
@videoEnabled = videoEnabled
|
||||
|
||||
if videoEnabled
|
||||
currentDevice = context.jamClient.FTUECurrentSelectedVideoDevice()
|
||||
deviceNames = context.jamClient.FTUEGetVideoCaptureDeviceNames()
|
||||
#deviceCaps = context.jamClient.FTUEGetVideoCaptureDeviceCapabilities()
|
||||
currentResolution = context.jamClient.GetCurrentVideoResolution()
|
||||
currentFrameRate = context.jamClient.GetCurrentVideoFrameRate()
|
||||
encodeResolutions = context.jamClient.FTUEGetAvailableEncodeVideoResolutions()
|
||||
frameRates = context.jamClient.FTUEGetSendFrameRates()
|
||||
|
||||
autoSelect = false
|
||||
if currentResolution == 0
|
||||
@logger.warn("current resolution not specified; defaulting to VGA")
|
||||
autoSelect = true
|
||||
currentResolution = 2
|
||||
|
||||
if currentFrameRate == 0
|
||||
autoSelect = true
|
||||
@logger.warn("current frame rate not specified; defaulting to 30")
|
||||
currentFrameRate = 30
|
||||
else
|
||||
# backend accepts 10,20,30 etc for FPS, but returns an indexed value (1, 2, 3).
|
||||
convertedFrameRate = BackendToFrontendFPS[currentFrameRate]
|
||||
@logger.debug("translating FPS: backend numeric #{currentFrameRate} to #{convertedFrameRate}")
|
||||
currentFrameRate = convertedFrameRate
|
||||
|
||||
# backend needs to be same as frontend
|
||||
if autoSelect
|
||||
context.jamClient.FTUESetVideoEncodeResolution(currentResolution)
|
||||
context.jamClient.FTUESetSendFrameRates(currentFrameRate)
|
||||
else
|
||||
@everDisabled = true
|
||||
# don't talk to the backend when video is disabled; avoiding crashes
|
||||
currentDevice = null
|
||||
deviceNames = {}
|
||||
currentResolution: 0
|
||||
currentFrameRate: 0
|
||||
encodeResolutions: {}
|
||||
frameRates: {}
|
||||
|
||||
|
||||
#deviceCaps: deviceCaps,
|
||||
|
||||
@state = {
|
||||
currentDevice: currentDevice,
|
||||
deviceNames: deviceNames,
|
||||
currentResolution: currentResolution,
|
||||
currentFrameRate: currentFrameRate,
|
||||
encodeResolutions: encodeResolutions,
|
||||
frameRates: frameRates,
|
||||
videoShared: @videoShared
|
||||
videoOpen: @videoOpen,
|
||||
videoEnabled: videoEnabled,
|
||||
everDisabled: @everDisabled
|
||||
}
|
||||
this.trigger(@state)
|
||||
|
||||
onSetVideoEnabled: (enable) ->
|
||||
|
||||
return unless context.jamClient.FTUESetVideoShareEnable?
|
||||
|
||||
context.jamClient.FTUESetVideoShareEnable(enable)
|
||||
|
||||
# keep state in sync
|
||||
@state.videoEnabled = enable
|
||||
@onRefresh()
|
||||
|
||||
onStartVideo: ->
|
||||
return unless context.jamClient.SessStartVideoSharing?
|
||||
|
||||
if @howtoWindow?
|
||||
@howtoWindow.close()
|
||||
@howtoWindow = null
|
||||
#else # TESTING
|
||||
# @howtoWindow = window.open("/popups/how-to-use-video", 'How to Use Video', 'scrollbars=yes,toolbar=no,status=no,height=315,width=320')
|
||||
|
||||
@logger.debug("SessStartVideoSharing()")
|
||||
context.jamClient.SessStartVideoSharing(0)
|
||||
@videoShared = true
|
||||
|
||||
@state.videoShared = @videoShared
|
||||
this.trigger(@state)
|
||||
|
||||
onStopVideo: ->
|
||||
if @videoShared
|
||||
@logger.debug("SessStopVideoSharing()")
|
||||
context.jamClient.SessStopVideoSharing()
|
||||
@videoShared = false
|
||||
@state.videoShared = @videoShared
|
||||
this.trigger(@state)
|
||||
|
||||
onTestVideo: () ->
|
||||
|
||||
return unless context.jamClient.testVideoRender?
|
||||
result = context.jamClient.testVideoRender()
|
||||
|
||||
if !result
|
||||
@app.layout.notify({title: 'Unable to initialize video window', text: "Please contact support@jamkazam.com"})
|
||||
|
||||
onToggleVideo: () ->
|
||||
if @videoShared
|
||||
@onStopVideo()
|
||||
else
|
||||
@onStartVideo()
|
||||
|
||||
onSetVideoEncodeResolution: (resolution) ->
|
||||
@logger.debug("set capture resolution: #{resolution}")
|
||||
context.jamClient.FTUESetVideoEncodeResolution(resolution)
|
||||
@state.currentResolution = resolution
|
||||
this.trigger(@state)
|
||||
|
||||
onSetSendFrameRate: (frameRates) ->
|
||||
@logger.debug("set capture frame rate: #{frameRates}")
|
||||
context.jamClient.FTUESetSendFrameRates(frameRates)
|
||||
@state.currentFrameRate = frameRates
|
||||
this.trigger(@state)
|
||||
|
||||
onSelectDevice: (device, caps) ->
|
||||
|
||||
# don't do anything if no video capabilities
|
||||
return unless context.jamClient.FTUESelectVideoCaptureDevice?
|
||||
|
||||
result = context.jamClient.FTUESelectVideoCaptureDevice(device, caps)
|
||||
if(!result)
|
||||
@logger.error("onSelectDevice failed with device #{device}")
|
||||
@app.layout.notify({title: 'Unable to select webcam', text: "Please try reconnecting webcam."})
|
||||
else
|
||||
@state.currentDevice = context.jamClient.FTUECurrentSelectedVideoDevice();
|
||||
this.trigger(@state)
|
||||
|
||||
onVideoWindowOpened: () ->
|
||||
@onRefresh() unless @state?
|
||||
|
||||
@logger.debug("in session? #{context.SessionStore.inSession()}, currentDevice? #{@state?.currentDevice?}, videoShared? #{@videoShared}")
|
||||
|
||||
if context.SessionStore.inSession() && @state.currentDevice? && Object.keys(@state.currentDevice).length > 0 && !@videoShared
|
||||
context.JK.ModUtils.shouldShow(NAMED_MESSAGES.HOWTO_USE_VIDEO_NOSHOW).done((shouldShow) =>
|
||||
@logger.debug("checking if user has 'should show' on video howto: #{shouldShow}")
|
||||
if shouldShow
|
||||
@howtoWindow = window.open("/popups/how-to-use-video", 'How to Use Video', 'scrollbars=yes,toolbar=no,status=no,height=315,width=320')
|
||||
)
|
||||
|
||||
#@howtoWindo.ParentRecordingStore = context.RecordingStore
|
||||
#@howtoWindo.ParentIsRecording = @recordingModel.isRecording()
|
||||
|
||||
@videoOpen = true
|
||||
@state.videoOpen = @videoOpen
|
||||
this.trigger(@state)
|
||||
|
||||
onVideoWindowClosed: () ->
|
||||
@onRefresh() unless @state?
|
||||
|
||||
if @howtoWindow?
|
||||
@howtoWindow.close()
|
||||
@howtoWindow = null
|
||||
|
||||
@videoOpen = false
|
||||
@state.videoOpen = @videoOpen
|
||||
@videoShared = false
|
||||
@state.videoShared = @videoShared
|
||||
this.trigger(@state)
|
||||
|
||||
onHowToUseVideoPopupClosed: (dontShow) ->
|
||||
if (dontShow)
|
||||
@logger.debug("requesting that user no longer see how-to-use-video")
|
||||
context.JK.ModUtils.updateNoShow(NAMED_MESSAGES.HOWTO_USE_VIDEO_NOSHOW);
|
||||
|
||||
logger.debug("how-to-use-video popup closed")
|
||||
@howtoWindow = null
|
||||
|
||||
onConfigureVideoPopupClosed: (dontShow) ->
|
||||
if (dontShow)
|
||||
@logger.debug("requesting that user no longer see configure-video")
|
||||
context.JK.ModUtils.updateNoShow(NAMED_MESSAGES.CONFIGURE_VIDEO_NOSHOW);
|
||||
|
||||
logger.debug("configure-video popup closed")
|
||||
@configureWindow = null
|
||||
|
||||
# if the user passes all the safeguards, let's see if we should get them to configure video
|
||||
onCheckPromptConfigureVideo: () ->
|
||||
# don't do any check if this is a client with no video enabled
|
||||
return unless context.jamClient.FTUECurrentSelectedVideoDevice?
|
||||
|
||||
@onRefresh() unless @state?
|
||||
|
||||
@logger.debug("checkPromptConfigureVideo", @state.currentDevice, @state.deviceNames)
|
||||
|
||||
# if no device configured and this is the native client and if you have at least 1 video
|
||||
# currentDevice, from the backend, is '{'':''}' in the case of no device configured. But we also check for an empty object, or null object.
|
||||
if (!@state.currentDevice? || Object.keys(@state.currentDevice).length == 0 || (Object.keys(@state.currentDevice).length == 1 && @state.currentDevice[''] == '')) && gon?.isNativeClient && Object.keys(@state.deviceNames).length > 0
|
||||
# and if they haven't said stop bothering me about this
|
||||
context.JK.ModUtils.shouldShow(NAMED_MESSAGES.CONFIGURE_VIDEO_NOSHOW).done((shouldShow) =>
|
||||
@logger.debug("checking if user has 'should show' on video config: #{shouldShow}")
|
||||
if shouldShow
|
||||
@configureWindow = window.open("/popups/configure-video", 'Configure Video', 'scrollbars=yes,toolbar=no,status=no,height=395,width=444')
|
||||
)
|
||||
|
||||
isVideoEnabled:() ->
|
||||
return @videoEnabled
|
||||
|
||||
}
|
||||
)
|
||||
|
|
@ -0,0 +1,405 @@
|
|||
context = window
|
||||
logger = context.JK.logger
|
||||
|
||||
reactContext = if window.opener? then window.opener else window
|
||||
# make sure this is actually us opening the window, not someone else (by checking for MixerStore)
|
||||
if window.opener?
|
||||
try
|
||||
m = window.opener.MixerStore
|
||||
catch e
|
||||
reactContext = window
|
||||
|
||||
VideoStore = reactContext.VideoStore
|
||||
VideoActions = reactContext.VideoActions
|
||||
PlatformStore = reactContext.PlatformStore
|
||||
|
||||
ALERT_NAMES = context.JK.ALERT_NAMES;
|
||||
|
||||
BackendToFrontend = {
|
||||
1 : "CIF (352x288)",
|
||||
2 : "VGA (640x480)",
|
||||
3 : "4CIF (704x576)",
|
||||
4 : "1/2 720p HD (640x360)",
|
||||
5 : "720p HD (1280x720)",
|
||||
6 : "1080p HD (1920x1080)"
|
||||
}
|
||||
|
||||
BackendNumericToBackendString = {
|
||||
1 : "CIF (352X288)",
|
||||
2 : "VGA (640X480)",
|
||||
3 : "4CIF (704X576)",
|
||||
4 : "1/2WHD (640X360)",
|
||||
5 : "WHD (1280X720)",
|
||||
6 : "FHD (1920x1080)"
|
||||
}
|
||||
|
||||
|
||||
BackendToFrontendFPS = {
|
||||
1: 30,
|
||||
2: 24,
|
||||
3: 20,
|
||||
4: 15,
|
||||
5: 10
|
||||
}
|
||||
FrontendToBackend = {}
|
||||
for key, value of BackendToFrontend
|
||||
FrontendToBackend[value] = key
|
||||
|
||||
mixins = []
|
||||
mixins.push(Reflux.listenTo(VideoStore, 'onVideoStateChanged'))
|
||||
|
||||
@WebcamViewer = React.createClass({
|
||||
|
||||
mixins: mixins
|
||||
logger: context.JK.logger
|
||||
visible: false
|
||||
|
||||
getInitialState: () ->
|
||||
{
|
||||
currentDevice: null
|
||||
deviceNames: {}
|
||||
deviceCaps: null
|
||||
currentResolution: 0
|
||||
currentFrameRate: 0
|
||||
encodeResolutions: {}
|
||||
frameRates: {}
|
||||
rescanning: false
|
||||
}
|
||||
|
||||
onVideoStateChanged: (changes) ->
|
||||
@setState(changes)
|
||||
|
||||
render: () ->
|
||||
|
||||
if @props.showBackBtn
|
||||
backBtn = `<a className="hidden button-grey back-btn" onClick={this.back}>BACK</a>`
|
||||
|
||||
selectedDevice = this.selectedDeviceName(@state)
|
||||
|
||||
# build list of webcams
|
||||
|
||||
webcams = []
|
||||
noneSelected = selectedDevice == null || selectedDevice.length == 0
|
||||
|
||||
# the backend does not allow setting no video camera. So if a webcam is selected, prevent un-selecting
|
||||
if noneSelected
|
||||
webcams.push `<option key="none" value="" selected={noneSelected}>None Selected</option>`
|
||||
|
||||
context._.each @state.deviceNames, (deviceName, deviceGuid) ->
|
||||
selected = deviceGuid == selectedDevice
|
||||
webcams.push `<option key={deviceGuid} value={deviceGuid} selected={selected}>{deviceName}</option>`
|
||||
|
||||
noWebcams = Object.keys(@state.deviceNames).length == 0
|
||||
|
||||
# build list of capture resolutions
|
||||
|
||||
captureResolutions = []
|
||||
# load current settings from backend
|
||||
currentResolution = @state.currentResolution
|
||||
currentFrameRate = @state.currentFrameRate
|
||||
|
||||
# protect against non-video clients pointed at video-enabled server from getting into a session
|
||||
resolutions = @state.encodeResolutions
|
||||
frames = @state.frameRates
|
||||
context._.each resolutions, (resolution, resolutionKey, obj) =>
|
||||
|
||||
#{1: "CIF (352X288)", 2: "VGA (640X480)", 3: "4CIF (704X576)", 4: "1/2WHD (640X360)", 5: "WHD (1280X720)", 6: "FHD (1920x1080)"}
|
||||
context._.each frames, (frame, key, obj) =>
|
||||
|
||||
frontendResolution = BackendToFrontend[resolutionKey]
|
||||
|
||||
@logger.error("unknown resolution! #{resolution}", BackendToFrontend) unless frontendResolution
|
||||
|
||||
value = "#{resolutionKey}|#{frame}"
|
||||
text = "#{frontendResolution} at #{frame} fps"
|
||||
|
||||
selected = currentResolution + '|' + currentFrameRate == value
|
||||
|
||||
captureResolutions.push `<option key={value} value={value} selected={selected}>{text}</option>`
|
||||
|
||||
|
||||
testBtnClassNames = {'button-orange' : true, 'webcam-test-btn' : true}
|
||||
if noWebcams
|
||||
if PlatformStore.isWindows()
|
||||
testBtnClassNames.disabled = !@state.videoEnabled
|
||||
testBtnClasses = classNames(testBtnClassNames)
|
||||
testBtn = `<a className={testBtnClasses} onClick={this.toggleWebcam}>TEST VIDEO</a>`
|
||||
else
|
||||
testBtn = null
|
||||
else if @state.videoShared
|
||||
testBtnClassNames.disabled = !@state.videoEnabled
|
||||
testBtnClasses = classNames(testBtnClassNames)
|
||||
testBtn = `<a className={testBtnClasses} onClick={this.toggleWebcam}>STOP WEBCAM</a>`
|
||||
else
|
||||
testBtnClassNames.disabled = !@state.videoEnabled || noneSelected
|
||||
testBtnClasses = classNames(testBtnClassNames)
|
||||
testBtn = `<a className={testBtnClasses} onClick={this.toggleWebcam}>TEST WEBCAM</a>`
|
||||
|
||||
if @state.rescanning
|
||||
rescanning =
|
||||
`<span className="rescanning-notice">
|
||||
<span className="spinner-small" />
|
||||
CHECKING GEAR
|
||||
</span>`
|
||||
|
||||
if @props.show_header
|
||||
if noWebcams
|
||||
if PlatformStore.isWindows()
|
||||
testVideoHelpText = `<span>The TEST VIDEO button will open the JamKazam video window to verify that receiving video works on your system.</span>`
|
||||
header = `<div className="video-header">
|
||||
<h2 className="subcaption">video gear:</h2>
|
||||
<div className="subcaption">
|
||||
JamKazam does not detect any webcams. You will not be able to send video, but you can still receive it from others. {testVideoHelpText}
|
||||
</div>
|
||||
</div>`
|
||||
else
|
||||
header =
|
||||
`<div className="video-header">
|
||||
<h2 className="subcaption">video gear:</h2>
|
||||
<div className="subcaption">
|
||||
Select webcam to use for video in sessions. Verify that you see video from webcam in the external application window (it may be behind this window).
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
if @state.videoEnabled
|
||||
disableVideoBtnText = "DISABLE VIDEO"
|
||||
else
|
||||
disableVideoBtnText = "ENABLE VIDEO"
|
||||
|
||||
if @props.show_disable || !@state.videoEnabled || @state.everDisabled
|
||||
|
||||
if @state.videoEnabled
|
||||
disableHelpBtn = `<a className="ftue-video-disable-help">[?]</a>`
|
||||
|
||||
disableBtnClasses = classNames({'button-grey' : true, 'disable-video' : true, 'disabled' : @state.videoShared})
|
||||
disableVideo =
|
||||
`<div className="webcam-select-container wizard_control">
|
||||
<a className={disableBtnClasses} onClick={this.disableVideo}>{disableVideoBtnText}</a>
|
||||
{disableHelpBtn}
|
||||
</div>`
|
||||
|
||||
`<div className="webcam-viewer">
|
||||
{header}
|
||||
<form className="video">
|
||||
<h2 className="sub-header select-webcam">select webcam:</h2>
|
||||
<div className="webcam-select-container wizard_control">
|
||||
<select onChange={this.selectWebcam} disabled={noWebcams || !this.state.videoEnabled}>
|
||||
{webcams}
|
||||
</select>
|
||||
</div>
|
||||
<h2 className="sub-header select-resolution">select video capture resolution & frame rate:</h2>
|
||||
<div className="webcam-resolution-select-container wizard_control">
|
||||
<select onChange={this.selectResolution} disabled={noWebcams || !this.state.videoEnabled}>
|
||||
{captureResolutions}
|
||||
</select>
|
||||
<a className="ftue-video-settings-help">[?]</a>
|
||||
</div>
|
||||
<div className="configure-webcam wizard_control">
|
||||
{backBtn}
|
||||
{testBtn}
|
||||
</div>
|
||||
{rescanning}
|
||||
</form>
|
||||
{disableVideo}
|
||||
</div>`
|
||||
|
||||
componentDidMount: () ->
|
||||
|
||||
if @props.isVisible
|
||||
@beforeShow()
|
||||
|
||||
$root = $(@getDOMNode())
|
||||
|
||||
$videoSettingsHelp = $root.find('.ftue-video-settings-help')
|
||||
context.JK.helpBubble($videoSettingsHelp, 'ftue-video-settings', {}, {width:300}) if $videoSettingsHelp.length > 0
|
||||
$videoSettingsHelp.click(false)
|
||||
|
||||
$videoDisableHelp = $root.find('.ftue-video-disable-help')
|
||||
context.JK.helpBubble($videoDisableHelp, 'ftue-video-disable', {}, {width:300}) if $videoDisableHelp.length > 0
|
||||
$videoDisableHelp.click(false)
|
||||
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
# protect against non-video clients pointed at video-enabled server from getting into a session
|
||||
|
||||
@logger.debug("webcam devices", nextState.deviceNames, @state.deviceNames)
|
||||
|
||||
if !@initialScan?
|
||||
@initialScan = true
|
||||
else if @visible
|
||||
@findChangedWebcams(nextState.deviceNames, @state.deviceNames)
|
||||
|
||||
componentWillReceiveProps:(nextProps) ->
|
||||
if nextProps.isVisible
|
||||
@beforeShow()
|
||||
else
|
||||
@beforeHide()
|
||||
|
||||
beforeShow:() ->
|
||||
|
||||
@visible = true
|
||||
VideoActions.refresh()
|
||||
VideoActions.stopVideo()
|
||||
|
||||
context.JK.onBackendEvent(ALERT_NAMES.USB_CONNECTED, 'webcam-viewer', @onUsbDeviceConnected);
|
||||
context.JK.onBackendEvent(ALERT_NAMES.USB_DISCONNECTED, 'webcam-viewer', @onUsbDeviceDisconnected);
|
||||
|
||||
beforeHide: () ->
|
||||
|
||||
@visible = false
|
||||
context.JK.offBackendEvent(ALERT_NAMES.USB_CONNECTED, 'webcam-viewer', @onUsbDeviceConnected);
|
||||
context.JK.offBackendEvent(ALERT_NAMES.USB_DISCONNECTED, 'webcam-viewer', @onUsbDeviceDisconnected);
|
||||
|
||||
if @rescanTimeout?
|
||||
clearTimeout(@rescanTimeout)
|
||||
@rescanTimeout = null
|
||||
|
||||
@setVideoOff()
|
||||
|
||||
|
||||
onUsbDeviceConnected: () ->
|
||||
# don't handle USB events when minimized
|
||||
#return if !context.jamClient.IsFrontendVisible()
|
||||
|
||||
logger.debug("USB device connected")
|
||||
|
||||
@scheduleRescanSystem(3000)
|
||||
|
||||
onUsbDeviceDisconnected:() ->
|
||||
# don't handle USB events when minimized
|
||||
#return if !context.jamClient.IsFrontendVisible()
|
||||
|
||||
logger.debug("USB device disconnected")
|
||||
|
||||
@scheduleRescanSystem(3000)
|
||||
|
||||
scheduleRescanSystem: (time) ->
|
||||
if @rescanTimeout?
|
||||
clearTimeout(@rescanTimeout)
|
||||
@rescanTimeout = null
|
||||
|
||||
@setState({rescanning: true})
|
||||
@rescanTimeout = setTimeout(() =>
|
||||
@setState({rescanning: false})
|
||||
VideoActions.refresh()
|
||||
, time)
|
||||
|
||||
selectWebcam:(e) ->
|
||||
e.preventDefault()
|
||||
|
||||
device = $(e.target).val()
|
||||
|
||||
VideoActions.selectDevice(device, {})
|
||||
|
||||
disableVideo: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
return if @state.videoShared
|
||||
|
||||
if @state.videoEnabled
|
||||
context.JK.Banner.showYesNo({
|
||||
title: "Disable Video?",
|
||||
html: "You will not be able to send or receive video.",
|
||||
yes: =>
|
||||
VideoActions.setVideoEnabled(false)
|
||||
})
|
||||
else
|
||||
VideoActions.setVideoEnabled(true)
|
||||
|
||||
|
||||
updateBackend: (selectedResolution, selectedFps) ->
|
||||
@logger.debug 'Selecting webcam resolution: ', selectedResolution
|
||||
@logger.debug 'Selecting webcam fps: ', selectedFps
|
||||
|
||||
VideoActions.setVideoEncodeResolution(selectedResolution)
|
||||
VideoActions.setSendFrameRate(selectedFps)
|
||||
|
||||
selectResolution:(e) ->
|
||||
e.preventDefault()
|
||||
|
||||
resolution = $(e.target).val()
|
||||
@logger.debug 'new capture resolution selected: ' + resolution
|
||||
|
||||
if resolution?
|
||||
|
||||
bits = resolution.split('|')
|
||||
selectedResolution = bits[0]
|
||||
selectedFps = bits[1]
|
||||
@updateBackend(selectedResolution, selectedFps)
|
||||
|
||||
setVideoOff:() ->
|
||||
VideoActions.stopVideo()
|
||||
|
||||
back: () =>
|
||||
window.location = '/client#/account'
|
||||
|
||||
toggleWebcam:(e) ->
|
||||
e.preventDefault()
|
||||
|
||||
return unless this.state.videoEnabled
|
||||
|
||||
$toggleBtn = $(e.target)
|
||||
|
||||
# we should only do this if no device is currently selected
|
||||
$root = $(@getDOMNode())
|
||||
$select = $root.find('.webcam-select-container select')
|
||||
if Object.keys(@state.deviceNames).length == 0
|
||||
|
||||
context.JK.Banner.showYesNo({
|
||||
yes_text: 'RUN TEST',
|
||||
title: "Run Video Test?",
|
||||
html: "A video window will show up with changing colors and shapes for 10 seconds. The test was successful if you were able to see the changing colors. Close the window once the colors and shapes stop changing.",
|
||||
yes: =>
|
||||
VideoActions.testVideo()
|
||||
})
|
||||
|
||||
else
|
||||
device = $select.val()
|
||||
#VideoActions.selectDevice(device, {})
|
||||
VideoActions.toggleVideo()
|
||||
|
||||
#if this.isVideoShared()
|
||||
# $toggleBtn.removeClass("selected")
|
||||
# VideoActions.stopVideo()
|
||||
# @setState({videoShared: false})
|
||||
#else
|
||||
# $toggleBtn.addClass("selected")
|
||||
# VideoActions.startVideo()
|
||||
# @setState({videoShared: true})
|
||||
|
||||
selectedDeviceName:(state) ->
|
||||
webcamName = null
|
||||
# protect against non-video clients pointed at video-enabled server from getting into a session
|
||||
webcam = state.currentDevice
|
||||
@logger.debug("currently selected video device", webcam)
|
||||
if (webcam? && Object.keys(webcam).length>0)
|
||||
webcamName = Object.keys(webcam)[0]
|
||||
|
||||
webcamName
|
||||
|
||||
findChangedWebcams: (newList, oldList) ->
|
||||
newKeys = Object.keys(newList)
|
||||
oldKeys = Object.keys(oldList)
|
||||
|
||||
webcamSelect = $(@getDOMNode()).find('.webcam-select-container select')
|
||||
|
||||
if newKeys.length > oldKeys.length
|
||||
for newKey in newKeys
|
||||
if oldKeys.indexOf(newKey) == -1
|
||||
newWebcam = newList[newKey]
|
||||
@logger.debug("new webcam found: " + newWebcam, newKey)
|
||||
context.JK.prodBubble(webcamSelect, 'new-webcam-found', {name: newWebcam}, {positions:['right']})
|
||||
break
|
||||
else if newKeys.length < oldKeys.length
|
||||
for oldKey in oldKeys
|
||||
if newKeys.indexOf(oldKey) == -1
|
||||
oldWebcam = oldList[oldKey]
|
||||
@logger.debug("webcam no longer found: " + oldWebcam)
|
||||
context.JK.prodBubble(webcamSelect, 'old-webcam-lost', {name: oldWebcam}, {positions:['right']})
|
||||
break
|
||||
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
|
||||
// ask the backend to start the session.
|
||||
var groupedTracks = groupTracksToClient(recording);
|
||||
jamClient.StartRecording(recording["id"], groupedTracks);
|
||||
jamClient.StartRecording(recording["id"], groupedTracks, 0, false, 0);
|
||||
})
|
||||
.fail(function(jqXHR) {
|
||||
var details = { clientId: app.clientId, reason: 'rest', detail: arguments, isRecording: false }
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
if(!checkoutUtils.hasOneFreeItemInShoppingCart(carts)) {
|
||||
// the user has multiple items in their shopping cart. They shouldn't be here.
|
||||
logger.error("invalid access of redeemComplete page")
|
||||
window.location = '/client#/jamtrackBrowse'
|
||||
window.location = '/client#/jamtrack/search'
|
||||
}
|
||||
else {
|
||||
// ok, we have one, free item. save it for
|
||||
|
|
@ -226,7 +226,7 @@
|
|||
$backBtn.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
context.location = '/client#/jamtrackBrowse'
|
||||
context.location = '/client#/jamtrack/search'
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@
|
|||
var $signinLink = null;
|
||||
|
||||
function beforeShow(data) {
|
||||
renderLoggedInState();
|
||||
|
||||
}
|
||||
|
||||
function afterShow(data) {
|
||||
|
||||
renderLoggedInState();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -67,13 +67,13 @@
|
|||
|
||||
if(carts.length == 0) {
|
||||
// nothing is in the user's shopping cart. They shouldn't be here.
|
||||
logger.error("invalid access of redeemJamTrack page")
|
||||
window.location = '/client#/jamtrackBrowse'
|
||||
logger.error("invalid access of redeemJamTrack page; none")
|
||||
window.location = '/client#/jamtrack/search'
|
||||
}
|
||||
else if(carts.length > 1) {
|
||||
// the user has multiple items in their shopping cart. They shouldn't be here.
|
||||
logger.error("invalid access of redeemJamTrack page")
|
||||
window.location = '/client#/jamtrackBrowse'
|
||||
logger.error("invalid access of redeemJamTrack page; multiple")
|
||||
window.location = '/client#/jamtrack/search'
|
||||
}
|
||||
else {
|
||||
var item = carts[0];
|
||||
|
|
@ -86,8 +86,8 @@
|
|||
}
|
||||
else {
|
||||
// the user has a non-free, single item in their basket. They shouldn't be here.
|
||||
logger.error("invalid access of redeemJamTrack page")
|
||||
window.location = '/client#/jamtrackBrowse'
|
||||
logger.error("invalid access of redeemJamTrack page, non-free/item")
|
||||
window.location = '/client#/jamtrack/search'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@
|
|||
var instrumentLogoHtml = '';
|
||||
if (instruments !== undefined) {
|
||||
for (var i=0; i < instruments.length; i++) {
|
||||
var inst = '../assets/content/icon_instrument_default24.png';
|
||||
var inst = '/assets/content/icon_instrument_default24.png';
|
||||
if (instruments[i].instrument_id in instrument_logo_map) {
|
||||
inst = instrument_logo_map[instruments[i].instrument_id].asset;
|
||||
instrumentLogoHtml += '<img src="' + inst + '" width="24" height="24" /> ';
|
||||
|
|
|
|||
|
|
@ -3294,8 +3294,8 @@
|
|||
$voiceChat = $screen.find('#voice-chat');
|
||||
$tracksHolder = $screen.find('#tracks')
|
||||
if(gon.global.video_available && gon.global.video_available!="none") {
|
||||
webcamViewer.init($("#create-session-layout .webcam-container"))
|
||||
webcamViewer.setVideoOff()
|
||||
//webcamViewer.init($("#create-session-layout .webcam-container"), false)
|
||||
//webcamViewer.setVideoOff()
|
||||
}
|
||||
events();
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
};
|
||||
|
||||
sessionUtils.setAutoOpenJamTrack = function(jamTrack) {
|
||||
logger.debug("setting auto-load jamtrack")
|
||||
logger.debug("setting auto-load jamtrack", jamTrack)
|
||||
autoOpenJamTrack = jamTrack;
|
||||
}
|
||||
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
sessionUtils.grabAutoOpenJamTrack = function() {
|
||||
var jamTrack = autoOpenJamTrack;
|
||||
autoOpenJamTrack = null;
|
||||
logger.debug("grabbing auto-load jamtrack", jamTrack)
|
||||
return jamTrack;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1081,8 +1081,11 @@
|
|||
});
|
||||
}
|
||||
|
||||
context.JK.dropdown = function ($select) {
|
||||
context.JK.dropdown = function ($select, options) {
|
||||
|
||||
var opts = options || {}
|
||||
|
||||
opts = $.extend({}, {nativeTouch: !(context.jamClient && context.jamClient.IsNativeClient()) && gon.global.env != "test", cutOff: 7}, opts)
|
||||
$select.each(function (index) {
|
||||
var $item = $(this);
|
||||
|
||||
|
|
@ -1090,7 +1093,7 @@
|
|||
// if this has already been initialized, re-init it so it picks up any new <options>
|
||||
$item.easyDropDown('destroy')
|
||||
}
|
||||
$item.easyDropDown({nativeTouch: !(context.jamClient && context.jamClient.IsNativeClient()) && gon.global.env != "test", cutOff: 7});
|
||||
$item.easyDropDown(opts);
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,10 +12,33 @@
|
|||
var $jamTracksButton = null;
|
||||
var $ctaJamTracksButton = null;
|
||||
|
||||
function computeWeight (jam_track_track, instrument) {
|
||||
var weight;
|
||||
|
||||
if (jam_track_track.track_type == 'Master') {
|
||||
weight = 0
|
||||
}
|
||||
else if (jam_track_track.instrument.id == instrument) {
|
||||
weight = 1 + jam_track_track.position
|
||||
}
|
||||
else {
|
||||
weight = 10000 + jam_track_track.position
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
|
||||
function fetchJamTrack() {
|
||||
rest.getJamTrackWithArtistInfo({plan_code: gon.jam_track_plan_code})
|
||||
.done(function (jam_track) {
|
||||
|
||||
if(gon.instrument_id) {
|
||||
jam_track.tracks.sort(function(a, b) {
|
||||
var aWeight = computeWeight(a, gon.instrument_id)
|
||||
var bWeight = computeWeight(b, gon.instrument_id)
|
||||
return aWeight - bWeight
|
||||
})
|
||||
}
|
||||
|
||||
context._.each(jam_track.tracks, function (track) {
|
||||
|
||||
var $element = $('<div class="jam-track-preview-holder"></div>')
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@
|
|||
logger.debug("jam_track", jam_track)
|
||||
|
||||
$jamtrack_band.text(jam_track.original_artist)
|
||||
$jamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrackBrowse')
|
||||
$jamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrack/search')
|
||||
$jamTracksButton.removeClass('hidden').text("Preview all " + jam_track.band_jam_track_count + " of our " + jam_track.original_artist + " JamTracks")
|
||||
$ctaJamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrackBrowse')
|
||||
$ctaJamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrack/searche')
|
||||
|
||||
|
||||
context._.each(jam_track.tracks, function (track) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
if(!gon.just_previews) {
|
||||
if (gon.generic) {
|
||||
$genericHeader.removeClass('hidden');
|
||||
$jamTracksButton.attr('href', '/client#/jamtrackBrowse')
|
||||
$jamTracksButton.attr('href', '/client#/jamtrack/search')
|
||||
$jamTracksButton.removeClass('hidden').text("Check out all 100+ JamTracks")
|
||||
|
||||
}
|
||||
|
|
@ -32,9 +32,9 @@
|
|||
$individualizedHeader.removeClass('hidden')
|
||||
$jamtrack_name.text('"' + jam_track.name + '"');
|
||||
$jamtrack_band.text(jam_track.original_artist)
|
||||
$jamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrackBrowse')
|
||||
$jamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrack/search')
|
||||
$jamTracksButton.removeClass('hidden').text("Preview all " + jam_track.band_jam_track_count + " of our " + jam_track.original_artist + " JamTracks")
|
||||
$ctaJamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrackBrowse')
|
||||
$ctaJamTracksButton.attr('href', '/client?artist=' + jam_track.original_artist + '#/jamtrack/search')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ class Tracking
|
|||
@rest = new context.JK.Rest();
|
||||
|
||||
adTrack: (app) =>
|
||||
utmSource = $.QueryString['utm_source']
|
||||
if utmSource == 'facebook-ads' || utmSource == 'google-ads' || utmSource == 'twitter-ads' || utmSource == 'affiliate' || utmSource == 'pr'
|
||||
if !context.jamClient.IsNativeClient()
|
||||
if context.JK.currentUserId?
|
||||
app.user().done( (user) =>
|
||||
|
|
@ -23,12 +21,12 @@ class Tracking
|
|||
@logger.debug("new user recorded")
|
||||
context.JK.GA.virtualPageView('/landing/jamtracks/new-user/')
|
||||
)
|
||||
else if $.cookie('jamkazam_user')?
|
||||
@logger.debug("existing/logged out user")
|
||||
context.JK.GA.virtualPageView('/landing/jamtracks/existing-user/')
|
||||
else
|
||||
@logger.debug("new user recorded")
|
||||
context.JK.GA.virtualPageView('/landing/jamtracks/new-user/')
|
||||
else
|
||||
@logger.debug("existing user recorded")
|
||||
context.JK.GA.virtualPageView('/landing/jamtracks/existing-user/');
|
||||
|
||||
jamtrackBrowseTrack: (app) =>
|
||||
if context.JK.currentUserId?
|
||||
|
|
|
|||
|
|
@ -2,6 +2,40 @@ $ = jQuery
|
|||
context = window
|
||||
context.JK ||= {};
|
||||
|
||||
|
||||
ALERT_NAMES = context.JK.ALERT_NAMES;
|
||||
|
||||
BackendToFrontend = {
|
||||
1 : "CIF (352x288)",
|
||||
2 : "VGA (640x480)",
|
||||
3 : "4CIF (704x576)",
|
||||
4 : "1/2 720p HD (640x360)",
|
||||
5 : "720p HD (1280x720)",
|
||||
6 : "1080p HD (1920x1080)"
|
||||
}
|
||||
|
||||
BackendNumericToBackendString = {
|
||||
1 : "CIF (352X288)",
|
||||
2 : "VGA (640X480)",
|
||||
3 : "4CIF (704X576)",
|
||||
4 : "1/2WHD (640X360)",
|
||||
5 : "WHD (1280X720)",
|
||||
6 : "FHD (1920x1080)"
|
||||
}
|
||||
|
||||
|
||||
BackendToFrontendFPS = {
|
||||
|
||||
1: 30,
|
||||
2: 24,
|
||||
3: 20,
|
||||
4: 15,
|
||||
5: 10
|
||||
}
|
||||
FrontendToBackend = {}
|
||||
for key, value of BackendToFrontend
|
||||
FrontendToBackend[value] = key
|
||||
|
||||
context.JK.WebcamViewer = class WebcamViewer
|
||||
constructor: (@root) ->
|
||||
@client = context.jamClient
|
||||
|
|
@ -10,23 +44,34 @@ context.JK.WebcamViewer = class WebcamViewer
|
|||
@toggleBtn = null
|
||||
@webcamSelect = null
|
||||
@resolutionSelect = null
|
||||
@videoShared=false
|
||||
@resolution=null
|
||||
@videoShared = false
|
||||
@resolution = null
|
||||
@videoSettingsHelp = null
|
||||
@showBackBtn = false
|
||||
@rescanTimeout = null
|
||||
@lastDeviceList = null
|
||||
|
||||
init: (root) =>
|
||||
# the session usage of webcamViewer does not actually pass in anything
|
||||
root = $() unless root?
|
||||
|
||||
@root = root
|
||||
init: (@root, @showBackButton) =>
|
||||
@toggleBtn = @root.find(".webcam-test-btn")
|
||||
@webcamSelect = @root.find(".webcam-select-container select")
|
||||
@resolutionSelect = @root.find(".webcam-resolution-select-container select")
|
||||
@webcamSelect.on("change", this.selectWebcam)
|
||||
@videoSettingsHelp = @root.find('.ftue-video-settings-help')
|
||||
@rescanningNotice = @root.find('.rescanning-notice')
|
||||
@backBtn = @root.find('.back-btn')
|
||||
@webcamSelect.on("change", @selectWebcam)
|
||||
@toggleBtn.on('click', @toggleWebcam)
|
||||
@resolutionSelect.on("change", this.selectResolution)
|
||||
@resolutionSelect.on("change", @selectResolution)
|
||||
@backBtn.on('click', @back)
|
||||
@backBtn.show() if @showBackBtn
|
||||
#logger.debug("Initialed with (unique) select",@webcamSelect)
|
||||
|
||||
|
||||
context.JK.helpBubble(@videoSettingsHelp, 'ftue-video-settings', {}, {width:300}) if @videoSettingsHelp.length > 0
|
||||
@videoSettingsHelp.click(false)
|
||||
|
||||
beforeShow:() =>
|
||||
|
||||
@videoShared = false # video can be assumed to be closed before htis is reached
|
||||
this.loadWebCams()
|
||||
this.selectWebcam()
|
||||
this.loadResolutions()
|
||||
|
|
@ -35,30 +80,78 @@ context.JK.WebcamViewer = class WebcamViewer
|
|||
# protect against non-video clients pointed at video-enabled server from getting into a session
|
||||
if @client.SessStopVideoSharing
|
||||
@client.SessStopVideoSharing()
|
||||
context.JK.onBackendEvent(ALERT_NAMES.USB_CONNECTED, 'webcam-viewer', @onUsbDeviceConnected);
|
||||
context.JK.onBackendEvent(ALERT_NAMES.USB_DISCONNECTED, 'webcam-viewer', @onUsbDeviceDisconnected);
|
||||
#client.SessSetInsetPosition(5)
|
||||
#client.SessSetInsetSize(1)
|
||||
#client.FTUESetAutoSelectVideoLayout(false)
|
||||
#client.SessSelectVideoDisplayLayoutGroup(1)
|
||||
|
||||
|
||||
onUsbDeviceConnected: () =>
|
||||
# don't handle USB events when minimized
|
||||
return if !context.jamClient.IsFrontendVisible()
|
||||
|
||||
logger.debug("USB device connected");
|
||||
|
||||
@scheduleRescanSystem(3000);
|
||||
|
||||
onUsbDeviceDisconnected:() =>
|
||||
# don't handle USB events when minimized
|
||||
return if !context.jamClient.IsFrontendVisible()
|
||||
|
||||
logger.debug("USB device disconnected");
|
||||
|
||||
@scheduleRescanSystem(3000);
|
||||
|
||||
scheduleRescanSystem: (time) =>
|
||||
if @rescanTimeout?
|
||||
clearTimeout(@rescanTimeout)
|
||||
@rescanTimeout = null
|
||||
|
||||
@rescanningNotice.show()
|
||||
@rescanTimeout = setTimeout(() =>
|
||||
@rescanningNotice.hide()
|
||||
@loadWebCams()
|
||||
, time)
|
||||
|
||||
selectWebcam:(e, data) =>
|
||||
device = @webcamSelect.val()
|
||||
if device?
|
||||
caps = @client.FTUEGetVideoCaptureDeviceCapabilities(device)
|
||||
@logger.debug("Got capabilities from device", caps, device)
|
||||
@client.FTUESelectVideoCaptureDevice(device, caps)
|
||||
result = @client.FTUESelectVideoCaptureDevice(device, caps)
|
||||
@logger.debug("FTUESelectVideoCaptureDevice result: ", result)
|
||||
|
||||
updateBackend: (selectedResolution, selectedFps) =>
|
||||
@logger.debug 'Selecting webcam resolution: ', selectedResolution
|
||||
@logger.debug 'Selecting webcam fps: ', selectedFps
|
||||
|
||||
@client.FTUESetVideoEncodeResolution selectedResolution
|
||||
@client.FTUESetSendFrameRates selectedFps
|
||||
|
||||
selectResolution:() =>
|
||||
@logger.debug 'Selecting from res control: ', @resolutionSelect
|
||||
@resolution = @resolutionSelect.val()
|
||||
if @resolution?
|
||||
@logger.debug 'Selecting webcam resolution: ', @resolution
|
||||
@client.FTUESetVideoEncodeResolution @resolution
|
||||
bits = @resolution.split('|')
|
||||
selectedResolution = bits[0]
|
||||
selectedFps = bits[1]
|
||||
@updateBackend(selectedResolution, selectedFps)
|
||||
|
||||
# if @isVideoShared
|
||||
# this.setVideoOff()
|
||||
# this.toggleWebcam()
|
||||
|
||||
beforeHide: () =>
|
||||
if @rescanTimeout?
|
||||
clearTimeout(@rescanTimeout)
|
||||
@rescanTimeout = null
|
||||
|
||||
@setVideoOff()
|
||||
|
||||
setVideoOff:() =>
|
||||
|
||||
if this.isVideoShared()
|
||||
@client.SessStopVideoSharing()
|
||||
|
||||
|
|
@ -71,6 +164,9 @@ context.JK.WebcamViewer = class WebcamViewer
|
|||
@toggleBtn.prop 'disabled', true
|
||||
@toggleBtn.prop 'disabled', !available
|
||||
|
||||
back: () =>
|
||||
window.location = '/client#/account'
|
||||
|
||||
toggleWebcam:() =>
|
||||
@logger.debug 'Toggling webcam from: ', this.isVideoShared(), @toggleBtn
|
||||
if this.isVideoShared()
|
||||
|
|
@ -79,6 +175,7 @@ context.JK.WebcamViewer = class WebcamViewer
|
|||
@videoShared = false
|
||||
else
|
||||
@toggleBtn.addClass("selected")
|
||||
alert("HERE?")
|
||||
@client.SessStartVideoSharing 0
|
||||
@videoShared = true
|
||||
|
||||
|
|
@ -86,23 +183,31 @@ context.JK.WebcamViewer = class WebcamViewer
|
|||
webcamName="None Configured"
|
||||
# protect against non-video clients pointed at video-enabled server from getting into a session
|
||||
webcam = if @client.FTUECurrentSelectedVideoDevice? then @client.FTUECurrentSelectedVideoDevice() else null
|
||||
logger.debug("currently selected video device", webcam)
|
||||
if (webcam? && Object.keys(webcam).length>0)
|
||||
webcamName = _.values(webcam)[0]
|
||||
webcamName = Object.keys(webcam)[0]
|
||||
|
||||
webcamName
|
||||
|
||||
loadWebCams:() =>
|
||||
# protect against non-video clients pointed at video-enabled server from getting into a session
|
||||
devices = if @client.FTUEGetVideoCaptureDeviceNames? then @client.FTUEGetVideoCaptureDeviceNames() else []
|
||||
devices = if @client.FTUEGetVideoCaptureDeviceNames? then @client.FTUEGetVideoCaptureDeviceNames() else {}
|
||||
selectedDevice = this.selectedDeviceName()
|
||||
@logger.debug("webcam devices", devices, selectedDevice)
|
||||
selectControl = @webcamSelect
|
||||
context._.each devices, (device) ->
|
||||
selected = device == selectedDevice
|
||||
selectControl.empty()
|
||||
newDeviceList = []
|
||||
context._.each devices, (deviceName, deviceGuid) ->
|
||||
selected = deviceName == selectedDevice
|
||||
option = $('<option/>',
|
||||
id: device
|
||||
value: device
|
||||
text: device)
|
||||
id: deviceGuid
|
||||
value: deviceGuid
|
||||
text: deviceName)
|
||||
selectControl.append option
|
||||
|
||||
@findChangedWebcams(devices, @lastDeviceList) if @lastDeviceList?
|
||||
|
||||
@lastDeviceList = devices
|
||||
selectControl.val selectedDevice
|
||||
|
||||
if devices.length == 0
|
||||
|
|
@ -110,16 +215,76 @@ context.JK.WebcamViewer = class WebcamViewer
|
|||
else
|
||||
@root.find('.no-webcam-msg').addClass 'hidden'
|
||||
|
||||
findChangedWebcams: (newList, oldList) =>
|
||||
newKeys = Object.keys(newList)
|
||||
oldKeys = Object.keys(oldList)
|
||||
|
||||
@logger.debug("change webcam check", newKeys, oldKeys)
|
||||
if newKeys.length > oldKeys.length
|
||||
for newKey in newKeys
|
||||
if oldKeys.indexOf(newKey) == -1
|
||||
newWebcam = newList[newKey]
|
||||
@logger.debug("new webcam found: " + newWebcam, newKey)
|
||||
context.JK.prodBubble(@webcamSelect, 'new-webcam-found', {name: newWebcam}, {positions:['right']})
|
||||
break
|
||||
else if newKeys.length < oldKeys.length
|
||||
for oldKey in oldKeys
|
||||
if newKeys.indexOf(oldKey) == -1
|
||||
oldWebcam = oldList[oldKey]
|
||||
@logger.debug("webcam no longer found: " + oldWebcam)
|
||||
context.JK.prodBubble(@webcamSelect, 'old-webcam-lost', {name: oldWebcam}, {positions:['right']})
|
||||
break
|
||||
|
||||
|
||||
|
||||
loadResolutions:() =>
|
||||
# protect against non-video clients pointed at video-enabled server from getting into a session
|
||||
resolutions = if @client.FTUEGetAvailableEncodeVideoResolutions? then @client.FTUEGetAvailableEncodeVideoResolutions() else {}
|
||||
frames = if @client.FTUEGetSendFrameRates? then @client.FTUEGetSendFrameRates() else {}
|
||||
selectControl = @resolutionSelect
|
||||
@logger.debug 'FOUND THESE RESOLUTIONS', resolutions, selectControl
|
||||
context._.each resolutions, (value, key, obj) ->
|
||||
@logger.debug 'FOUND THESE RESOLUTIONS', resolutions
|
||||
@logger.debug 'FOUND THESE FPS', frames
|
||||
context._.each resolutions, (resolution, resolutionKey, obj) ->
|
||||
|
||||
#{1: "CIF (352X288)", 2: "VGA (640X480)", 3: "4CIF (704X576)", 4: "1/2WHD (640X360)", 5: "WHD (1280X720)", 6: "FHD (1920x1080)"}
|
||||
context._.each frames, (frame, key, obj) ->
|
||||
|
||||
frontendResolution = BackendToFrontend[resolutionKey]
|
||||
|
||||
@logger.error("unknown resolution! #{resolution}") unless frontendResolution
|
||||
|
||||
value = "#{resolutionKey}|#{frame}"
|
||||
text = "#{frontendResolution} at #{frame} fps"
|
||||
|
||||
option = $('<option/>',
|
||||
value: value
|
||||
text: value)
|
||||
text: text)
|
||||
selectControl.append option
|
||||
|
||||
if @resolution != null and @resolution != ''
|
||||
selectControl.val(@resolution)
|
||||
# load current settings from backend
|
||||
currentResolution = @client.GetCurrentVideoResolution()
|
||||
currentFrameRate = @client.GetCurrentVideoFrameRate()
|
||||
|
||||
autoSelect = false
|
||||
if currentResolution == 0
|
||||
@logger.warn("current resolution not specified; defaulting to VGA")
|
||||
autoSelect = true
|
||||
currentResolution = 2
|
||||
if currentFrameRate == 0
|
||||
autoSelect = true
|
||||
@logger.warn("current frame rate not specified; defaulting to 30")
|
||||
currentFrameRate = 30
|
||||
else
|
||||
convertedFrameRate = BackendToFrontendFPS[currentFrameRate]
|
||||
@logger.debug("translating FPS: backend numeric #{currentFrameRate} to #{convertedFrameRate}")
|
||||
currentFrameRate = convertedFrameRate
|
||||
|
||||
selected = currentResolution + '|' + currentFrameRate
|
||||
|
||||
# backend needs to be same as frontend
|
||||
if autoSelect
|
||||
@updateBackend(currentResolution, currentFrameRate)
|
||||
|
||||
@logger.debug("setting current value of video settings to: " + selected)
|
||||
|
||||
selectControl.val(selected)
|
||||
|
|
|
|||
|
|
@ -5,18 +5,49 @@
|
|||
context.JK = context.JK || {}
|
||||
context.JK.StepVideoGear = function (app, $dialog) {
|
||||
var $step = null
|
||||
var $webcamViewer = new context.JK.WebcamViewer()
|
||||
var webcamViewerReact = null;
|
||||
var $instructions = null;
|
||||
var $noWebcamList = null;
|
||||
function initialize(_$step) {
|
||||
$step = _$step
|
||||
$webcamViewer.init($step)
|
||||
var reactElement = React.createElement(window.WebcamViewer, {isVisible: false});
|
||||
var reactDomNode = $step.find(".webcam-container").get(0)
|
||||
webcamViewerReact = React.render(reactElement, reactDomNode)
|
||||
|
||||
$instructions = $step.find('.instructions')
|
||||
$noWebcamList = $step.find('ul.no-webcam')
|
||||
window.VideoStore.listen(onVideoStoreUpdated);
|
||||
}
|
||||
|
||||
function onVideoStoreUpdated(videoState) {
|
||||
var noWebcams = Object.keys(videoState.deviceNames).length == 0
|
||||
var isWindows = window.PlatformStore.isWindows();
|
||||
|
||||
$instructions.removeClass('has-webcam no-webcam')
|
||||
if(noWebcams) {
|
||||
$instructions.addClass('no-webcam')
|
||||
}
|
||||
else {
|
||||
$instructions.addClass('has-webcam')
|
||||
}
|
||||
|
||||
$noWebcamList.removeClass('is-windows is-not-windows')
|
||||
if(isWindows) {
|
||||
$noWebcamList.addClass('is-windows')
|
||||
}
|
||||
else {
|
||||
$noWebcamList.addClass('is-not-windows')
|
||||
}
|
||||
}
|
||||
|
||||
function beforeShow() {
|
||||
$webcamViewer.beforeShow()
|
||||
$dialog.getWizard().getDialog().find('h1.top-header').text('video gear setup')
|
||||
webcamViewerReact.beforeShow()
|
||||
}
|
||||
|
||||
function beforeHide() {
|
||||
$webcamViewer.setVideoOff()
|
||||
$dialog.getWizard().getDialog().find('h1.top-header').text('audio gear setup')
|
||||
webcamViewerReact.beforeHide()
|
||||
}
|
||||
|
||||
this.beforeShow = beforeShow
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue