Merge branch 'develop' of bitbucket.org:jamkazam/jam-cloud into develop

This commit is contained in:
jam 2014-01-08 22:42:24 +00:00
commit e827f274b2
37 changed files with 604 additions and 301 deletions

View File

@ -42,6 +42,7 @@ message ClientMessage {
RECORDING_STARTED = 210;
RECORDING_ENDED = 215;
RECORDING_MASTER_MIX_COMPLETE = 220;
DOWNLOAD_AVAILABLE = 221;
// band notifications
BAND_INVITATION = 225;
@ -109,6 +110,7 @@ message ClientMessage {
optional RecordingStarted recording_started = 210;
optional RecordingEnded recording_ended = 215;
optional RecordingMasterMixComplete recording_master_mix_complete = 220;
optional DownloadAvailable download_available = 221;
// band notifications
optional BandInvitation band_invitation = 225;
@ -330,6 +332,10 @@ message RecordingMasterMixComplete {
optional string created_at = 4;
}
message DownloadAvailable {
}
message BandInvitation {
optional string band_invitation_id = 1;
optional string band_id = 2;

2
ruby/config/resque.yml Normal file
View File

@ -0,0 +1,2 @@
test:
redis: localhost:6379

View File

@ -27,6 +27,7 @@ require "jam_ruby/lib/module_overrides"
require "jam_ruby/lib/s3_util"
require "jam_ruby/lib/s3_manager"
require "jam_ruby/lib/profanity"
require "jam_ruby/resque/audiomixer"
require "jam_ruby/mq_router"
require "jam_ruby/base_manager"
require "jam_ruby/connection_manager"

View File

@ -1,21 +0,0 @@
require 'json'
require 'resque'
module JamRuby
@queue = :audiomixer
class AudioMixer
def self.perform(manifest)
tmp = Dir::Tmpname.make_tmpname "/var/tmp/audiomixer/manifest-#{manifest['recordingId']}", nil
File.open(tmp,"w") do |f|
f.write(manifest.to_json)
end
system("tar zxvf some_big_tarball.tar.gz"))
end
end
end

View File

@ -19,8 +19,8 @@ module JamRuby
"s3://#{@aws_bucket}/#{filename}"
end
def url(filename)
"https://s3.amazonaws.com/#{@aws_bucket}/#{filename}"
def url(filename, options = @@def_opts)
"http#{options[:secure] ? "s" : ""}://s3.amazonaws.com/#{@aws_bucket}/#{filename}"
end
def upload_sign(filename, content_md5, part_number, upload_id)

View File

@ -70,6 +70,16 @@ module JamRuby
)
end
def download_available
download_available = Jampb::DownloadAvailable.new
return Jampb::ClientMessage.new(
:type => ClientMessage::Type::DOWNLOAD_AVAILABLE,
:route_to => CLIENT_TARGET,
:download_available => download_available
)
end
# create a music session login message
def login_music_session(music_session)
login_music_session = Jampb::LoginMusicSession.new(:music_session => music_session)

View File

@ -740,6 +740,13 @@ module JamRuby
@@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id})
end
def send_download_available(user_id)
msg = @@message_factory.download_available
@@mq_router.publish_to_user(user_id, msg)
end
end
end
end

View File

@ -86,7 +86,7 @@ module JamRuby
recorded_track.next_part_to_upload = 0
recorded_track.file_offset = 0
recorded_track.save
recorded_track.url = construct_filename(recording.id, track.id)
recorded_track.url = construct_filename(recording.id, track.client_track_id)
recorded_track.save
recorded_track
end
@ -160,12 +160,12 @@ module JamRuby
def filename
# construct a path for s3
RecordedTrack.construct_filename(self.recording.id, self.track_id)
RecordedTrack.construct_filename(self.recording.id, self.client_track_id)
end
def self.construct_filename(recording_id, track_id)
raise "unknown ID" unless track_id
"recordings/#{recording_id}/track-#{track_id}.ogg"
def self.construct_filename(recording_id, client_track_id)
raise "unknown ID" unless client_track_id
"recordings/#{recording_id}/track-#{client_track_id}.ogg"
end
end
end

View File

@ -5,6 +5,8 @@ module JamRuby
observe JamRuby::RecordedTrack
def before_validation(recorded_track)
# if we see that a part was just uploaded entirely, validate that we can find the part that was just uploaded
if recorded_track.is_part_uploading_was && !recorded_track.is_part_uploading
begin
aws_part = recorded_track.s3_manager.multiple_upload_find_part(recorded_track.url, recorded_track.upload_id, recorded_track.next_part_to_upload - 1)
@ -22,10 +24,13 @@ module JamRuby
end
# if we detect that this just became fully uploaded
# if we detect that this just became fully uploaded -- if so, tell s3 to put the parts together
if !recorded_track.fully_uploaded_was && recorded_track.fully_uploaded
multipart_success = false
begin
recorded_track.s3_manager.multipart_upload_complete(recorded_track.url, recorded_track.upload_id)
multipart_success = true
rescue SocketError => e
raise # this should cause a 500 error, which is what we want. The client will retry later.
rescue Exception => e
@ -34,6 +39,11 @@ module JamRuby
recorded_track.errors.add(:upload_id, ValidationMessages::BAD_UPLOAD)
end
# tell all users that a download is available, except for the user who just uploaded
recorded_track.recording.users.each do |user|
Notification.send_download_available(recorded_track.user_id) unless user == recorded_track.user
end
end
end
@ -41,6 +51,7 @@ module JamRuby
end
# here we tick upload failure counts, or revert the state of the model, as needed
def after_rollback(recorded_track)
# if fully uploaded, don't increment failures
if recorded_track.fully_uploaded

View File

@ -163,53 +163,45 @@ module JamRuby
# ":recordings =>" part, you'll just get the recorded_tracks that I played. Very different!
# we also only allow you to be told about downloads if you have claimed the recording
User.joins(:recordings).joins(:recordings => :recorded_tracks).joins(:recordings => :claimed_recordings)
.order(%Q{ recorded_tracks.id })
.where(%Q{ recorded_tracks.fully_uploaded = TRUE })
#User.joins(:recordings).joins(:recordings => :recorded_tracks).joins(:recordings => :claimed_recordings)
RecordedTrack.joins(:recording).joins(:recording => :claimed_recordings)
.order('recorded_tracks.id')
.where('recorded_tracks.fully_uploaded = TRUE')
.where('recorded_tracks.id > ?', since)
.where('claimed_recordings.user_id = ?', user)
.where(:id => user.id).limit(limit).each do |theuser|
theuser.recordings.each do |recording|
recording.recorded_tracks.each do |recorded_track|
downloads.push(
{
:type => "recorded_track",
:id => recorded_track.client_track_id,
:recording_id => recording.id,
:length => recorded_track.length,
:md5 => recorded_track.md5,
:url => recorded_track.url,
:next => recorded_track.id
}
)
end
end
.where('claimed_recordings.user_id = ?', user).limit(limit).each do |recorded_track|
downloads.push(
{
:type => "recorded_track",
:id => recorded_track.client_track_id,
:recording_id => recorded_track.recording_id,
:length => recorded_track.length,
:md5 => recorded_track.md5,
:url => recorded_track.url,
:next => recorded_track.id
}
)
end
latest_recorded_track = downloads[-1][:next] if downloads.length > 0
User.joins(:recordings).joins(:recordings => :mixes)
Mix.joins(:recording).joins(:recording => :claimed_recordings)
.order('mixes.id')
.where('mixes.completed_at IS NOT NULL')
.where('mixes.id > ?', since)
.where(:id => user.id)
.limit(limit).each do |theuser|
theuser.recordings.each do |recording|
recording.mixes.each do |mix|
downloads.push(
{
:type => "mix",
:id => mix.id,
:recording_id => recording.id,
:length => mix.length,
:md5 => mix.md5,
:url => mix.url,
:created_at => mix.created_at,
:next => mix.id
}
)
end
end
.where('claimed_recordings.user_id = ?', user)
.limit(limit).each do |mix|
downloads.push(
{
:type => "mix",
:id => mix.id,
:recording_id => mix.recording_id,
:length => mix.length,
:md5 => mix.md5,
:url => mix.url,
:created_at => mix.created_at,
:next => mix.id
}
)
end
latest_mix = downloads[-1][:next] if downloads.length > 0

View File

@ -0,0 +1,32 @@
require 'json'
require 'resque'
module JamRuby
@queue = :audiomixer
class AudioMixer
def self.perform(manifest)
audiomixer = AudioMixer.new
audiomixer.run(manifest)
end
def run(manifest)
manifest_file = Dir::Tmpname.make_tmpname "/var/tmp/audiomixer/manifest-#{manifest['recordingId']}", nil
File.open(manifest_file,"w") do |f|
f.write(manifest.to_json)
end
#{"files": [{"codec": "vorbis", "offset": 0, "filename": "TPD - bass.flac-stereo.ogg"}, {"codec": "vorbis", "offset": 0, "filename": "TPD - bg vox.flac-stereo.ogg"}, {"codec": "vorbis", "offset": 0, "filename": "TPD - drums.flac-stereo.ogg"}, {"codec": "vorbis", "offset": 0, "filename": "TPD - guitars.flac-stereo.ogg"}, {"codec": "vorbis", "offset": 0, "filename": "TPD - lead vox.flac-stereo.ogg"}], "output": {"codec": "vorbis", "filename": "mix.ogg"}, "timeline": [{"timestamp": 0, "mix": [{"balance": 0, "level": 100}, {"balance": 0, "level": 100}, {"balance": 0, "level": 100}, {"balance": 0, "level": 100}, {"balance": 0, "level": 100}]}]}
path = "/var/lib/audiomixer/audiomixer/audiomixerapp"
system("tar zxvf some_big_tarball.tar.gz")
end
end
end

View File

@ -1,13 +0,0 @@
require 'spec_helper'
describe S3Util do
describe "sign_url" do
pending
it "returns something" do
S3Util.sign_url("jamkazam-dev", "avatar-tmp/user/image.png").should_not be_nil
end
end
end

View File

@ -11,7 +11,8 @@ describe Mix do
@music_session.save
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.claim(@user, "name", "description", Genre.first, true, true)
@recording.claim(@user, "name", "description", Genre.first, true, true)
@recording.errors.any?.should be_false
@mix = Mix.schedule(@recording, "{}")
end

View File

@ -42,7 +42,7 @@ describe RecordedTrack do
it "gets a url for the track" do
@recorded_track = RecordedTrack.create_from_track(@track, @recording)
@recorded_track.save.should be_true
@recorded_track.url.should == "recordings/#{@recording.id}/track-#{@track.id}.ogg"
@recorded_track.url.should == "recordings/#{@recording.id}/track-#{@track.client_track_id}.ogg"
end
it "signs url" do

View File

@ -230,6 +230,7 @@ describe Recording do
downloads["downloads"].length.should == 1
end
it "should return a file list for a user properly" do
pending
stub_const("APP_CONFIG", app_config)

View File

@ -23,9 +23,7 @@ def wipe_s3_test_bucket
s3 = AWS::S3.new(:access_key_id => test_config.aws_access_key_id,
:secret_access_key => test_config.aws_secret_access_key)
test_bucket = s3.buckets[JAMKAZAM_TESTING_BUCKET]
if test_bucket.name == JAMKAZAM_TESTING_BUCKET
puts "Ruby - Pretending to delete all contents of #{test_bucket.name}"
#test_bucket.delete_all
#test_bucket.clear!
end
end

View File

@ -42,6 +42,7 @@
RECORDING_STARTED : "RECORDING_STARTED",
RECORDING_ENDED : "RECORDING_ENDED",
RECORDING_MASTER_MIX_COMPLETE : "RECORDING_MASTER_MIX_COMPLETE",
DOWNLOAD_AVAILABLE : "DOWNLOAD_AVAILABLE",
// band notifications
BAND_INVITATION : "BAND_INVITATION",

View File

@ -283,6 +283,10 @@
];
}
function RegisterRecordingManagerCallbacks(commandStart, commandProgress, commandStop, commandsChanged) {
}
function RegisterRecordingCallbacks(startRecordingCallbackName, stopRecordingCallbackName, startedRecordingCallbackName, stoppedRecordingCallbackName, abortedRecordingCallbackName) {
fakeJamClientRecordings.RegisterRecordingCallbacks(startRecordingCallbackName, stopRecordingCallbackName, startedRecordingCallbackName,stoppedRecordingCallbackName, abortedRecordingCallbackName);
}
@ -565,6 +569,7 @@
return {success: true}
}
function CloseRecording() {}
function OnDownloadAvailable() {}
// Javascript Bridge seems to camel-case
@ -633,6 +638,7 @@
this.SessionAddTrack = SessionAddTrack;
this.SessionGetControlState = SessionGetControlState;
this.SessionGetIDs = SessionGetIDs;
this.RegisterRecordingManagerCallbacks = RegisterRecordingManagerCallbacks;
this.RegisterRecordingCallbacks = RegisterRecordingCallbacks;
this.SessionRegisterCallback = SessionRegisterCallback;
this.SessionSetAlertCallback = SessionSetAlertCallback;
@ -695,6 +701,7 @@
this.GetLocalRecordingState = GetLocalRecordingState;
this.OpenRecording = OpenRecording;
this.CloseRecording = CloseRecording;
this.OnDownloadAvailable = OnDownloadAvailable;
// fake calls; not a part of the actual jam client
this.RegisterP2PMessageCallbacks = RegisterP2PMessageCallbacks;

View File

@ -1,15 +1,15 @@
(function(context,$) {
"use strict";
"use strict";
context.JK = context.JK || {};
context.JK.FindMusicianScreen = function(app) {
context.JK = context.JK || {};
context.JK.FindMusicianScreen = function(app) {
var logger = context.JK.logger;
var musicians = {};
var musicianList;
var instrument_logo_map = context.JK.getInstrumentIconMap24();
var did_show_musician_page = false;
var page_num=1, page_count=0;
var did_show_musician_page = false;
var page_num=1, page_count=0;
function loadMusicians(queryString) {
// squelch nulls and undefines
@ -24,157 +24,165 @@
}
function search() {
did_show_musician_page = true;
did_show_musician_page = true;
var queryString = 'srch_m=1&page='+page_num+'&';
// order by
var orderby = $('#musician_order_by').val();
var orderby = $('#musician_order_by').val();
if (typeof orderby != 'undefined' && orderby.length > 0) {
queryString += "orderby=" + orderby + '&';
}
// instrument filter
var instrument = $('#musician_instrument').val();
var instrument = $('#musician_instrument').val();
if (typeof instrument != 'undefined' && !(instrument === '')) {
queryString += "instrument=" + instrument + '&';
}
// distance filter
var query_param = $('#musician_query_distance').val();
if (query_param !== null && query_param.length > 0) {
var matches = query_param.match(/(\d+)/);
if (0 < matches.length) {
var distance = matches[0];
queryString += "distance=" + distance + '&';
}
var matches = query_param.match(/(\d+)/);
if (0 < matches.length) {
var distance = matches[0];
queryString += "distance=" + distance + '&';
}
}
loadMusicians(queryString);
}
function refreshDisplay() {
clearResults();
search();
clearResults();
search();
}
function afterLoadMusicians(mList) {
// display the 'no musicians' banner if appropriate
var $noMusiciansFound = $('#musicians-none-found');
musicianList = mList;
musicianList = mList;
if(musicianList.length == 0) {
$noMusiciansFound.show();
musicians = [];
} else {
musicians = [];
}
else {
$noMusiciansFound.hide();
musicians = musicianList['musicians'];
if (!(typeof musicians === 'undefined')) {
$('#musician-filter-city').text(musicianList['city']);
if (0 == page_count) {
page_count = musicianList['page_count'];
}
renderMusicians();
}
musicians = musicianList['musicians'];
if (!(typeof musicians === 'undefined')) {
$('#musician-filter-city').text(musicianList['city']);
if (0 == page_count) {
page_count = musicianList['page_count'];
}
renderMusicians();
}
}
}
/**
* Render a list of musicians
*/
function renderMusicians() {
var ii, len;
var mTemplate = $('#template-find-musician-row').html();
var fTemplate = $('#template-musician-follow-info').html();
var aTemplate = $('#template-musician-action-btns').html();
var mVals, mm, renderings='';
var instr_logos, instr;
var follows, followVals, aFollow;
for (ii=0, len=musicians.length; ii < len; ii++) {
mm = musicians[ii];
instr_logos = '';
for (var jj=0, ilen=mm['instruments'].length; jj<ilen; jj++) {
var mTemplate = $('#template-find-musician-row').html();
var fTemplate = $('#template-musician-follow-info').html();
var aTemplate = $('#template-musician-action-btns').html();
var mVals, mm, renderings='';
var instr_logos, instr;
var follows, followVals, aFollow;
for (ii=0, len=musicians.length; ii < len; ii++) {
mm = musicians[ii];
instr_logos = '';
for (var jj=0, ilen=mm['instruments'].length; jj<ilen; jj++) {
if (mm['instruments'][jj].instrument_id in instrument_logo_map) {
instr = instrument_logo_map[mm['instruments'][jj].instrument_id];
instr = instrument_logo_map[mm['instruments'][jj].instrument_id];
}
instr_logos += '<img src="' + instr + '" width="24" height="24" />&nbsp;';
}
follows = '';
followVals = {};
for (var jj=0, ilen=mm['followings'].length; jj<ilen; jj++) {
aFollow = mm['followings'][jj];
followVals = {
musician_name: aFollow.name,
profile_url: '/#/profile/' + aFollow.user_id,
avatar_url: context.JK.resolveAvatarUrl(aFollow.photo_url),
};
}
follows = '';
followVals = {};
for (var jj=0, ilen=mm['followings'].length; jj<ilen; jj++) {
aFollow = mm['followings'][jj];
followVals = {
musician_name: aFollow.name,
profile_url: '/#/profile/' + aFollow.user_id,
avatar_url: context.JK.resolveAvatarUrl(aFollow.photo_url),
};
follows += context.JK.fillTemplate(fTemplate, followVals);
if (2 == jj) break;
}
var actionVals = {
profile_url: "/#/profile/" + mm.id,
button_friend: mm['is_friend'] ? '' : 'button-orange',
button_follow: mm['is_following'] ? '' : 'button-orange',
button_message: 'button-orange'
};
if (2 == jj) break;
}
var actionVals = {
profile_url: "/#/profile/" + mm.id,
button_friend: mm['is_friend'] ? '' : 'button-orange',
button_follow: mm['is_following'] ? '' : 'button-orange',
button_message: 'button-orange'
};
var musician_actions = context.JK.fillTemplate(aTemplate, actionVals);
mVals = {
avatar_url: context.JK.resolveAvatarUrl(mm.photo_url),
profile_url: "/#/profile/" + mm.id,
musician_name: mm.name,
musician_location: mm.city + ', ' + mm.state,
instruments: instr_logos,
biography: mm['biography'],
follow_count: mm['follow_count'],
friend_count: mm['friend_count'],
recording_count: mm['recording_count'],
session_count: mm['session_count'],
musician_id: mm['id'],
musician_follow_template: follows,
musician_action_template: musician_actions
};
var musician_row = context.JK.fillTemplate(mTemplate, mVals);
mVals = {
avatar_url: context.JK.resolveAvatarUrl(mm.photo_url),
profile_url: "/#/profile/" + mm.id,
musician_name: mm.name,
musician_location: mm.city + ', ' + mm.state,
instruments: instr_logos,
biography: mm['biography'],
follow_count: mm['follow_count'],
friend_count: mm['friend_count'],
recording_count: mm['recording_count'],
session_count: mm['session_count'],
musician_id: mm['id'],
musician_follow_template: follows,
musician_action_template: musician_actions
};
var musician_row = context.JK.fillTemplate(mTemplate, mVals);
renderings += musician_row;
}
$('#musician-filter-results').append(renderings);
}
$('#musician-filter-results').append(renderings);
$('.search-m-friend').on('click', friendMusician);
$('.search-m-follow').on('click', followMusician);
$('.search-m-friend').on('click', friendMusician);
$('.search-m-follow').on('click', followMusician);
}
function beforeShow(data) {
}
function afterShow(data) {
if (!did_show_musician_page) {
refreshDisplay();
}
if (!did_show_musician_page) {
refreshDisplay();
}
}
function clearResults() {
musicians = {};
$('#musician-filter-results').empty();
page_num = 1;
page_count = 0;
$('#musician-filter-results').empty();
page_num = 1;
page_count = 0;
}
function friendMusician(evt) {
// if the musician is already a friend, remove the button-orange class, and prevent
// the link from working
if (0 == $(this).closest('.button-orange').size()) return false;
$(this).click(function(ee) {ee.preventDefault();});
function friendMusician(evt) {
// if the musician is already a friend, remove the button-orange class, and prevent
// the link from working
if (0 === $(this).closest('.button-orange').size()) {
return false;
}
$(this).click(function(ee) {ee.preventDefault();});
evt.stopPropagation();
var uid = $(this).parent().data('musician-id');
var uid = $(this).parent().data('musician-id');
context.JK.sendFriendRequest(app, uid, friendRequestCallback);
}
function friendRequestCallback(user_id) {
// remove the orange look to indicate it's not selectable
$('div[data-musician-id='+user_id+'] .search-m-friend').removeClass('button-orange');
}
function followMusician(evt) {
// if the musician is already followed, remove the button-orange class, and prevent
// the link from working
if (0 == $(this).closest('.button-orange').size()) return false;
$(this).click(function(ee) {ee.preventDefault();});
function friendRequestCallback(user_id) {
// remove the orange look to indicate it's not selectable
$('div[data-musician-id='+user_id+'] .search-m-friend').removeClass('button-orange');
}
function followMusician(evt) {
// if the musician is already followed, remove the button-orange class, and prevent
// the link from working
if (0 === $(this).closest('.button-orange').size()) {
return false;
}
$(this).click(function(ee) {ee.preventDefault();});
evt.stopPropagation();
var newFollowing = {};
@ -188,31 +196,28 @@
data: JSON.stringify(newFollowing),
processData: false,
success: function(response) {
// remove the orange look to indicate it's not selectable
$('div[data-musician-id='+newFollowing.user_id+'] .search-m-follow').removeClass('button-orange');
// remove the orange look to indicate it's not selectable
$('div[data-musician-id='+newFollowing.user_id+'] .search-m-follow').removeClass('button-orange');
},
error: app.ajaxError
});
}
}
function events() {
$('#musician_query_distance').change(refreshDisplay);
$('#musician_instrument').change(refreshDisplay);
$('#musician_order_by').change(refreshDisplay);
$('#musician-filter-results').bind('scroll', function() {
$('#musician-filter-results').bind('scroll', function() {
if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
if (page_num < page_count) {
page_num += 1;
search();
}
if (page_num < page_count) {
page_num += 1;
search();
}
}
});
}
/**
* Initialize,
*/
function initialize() {
var screenBindings = {
'beforeShow': beforeShow,
@ -231,5 +236,4 @@
return this;
};
})(window,jQuery);

View File

@ -130,6 +130,13 @@
}
}
/**
* This occurs when a new download from a recording has become available
*/
function downloadAvailable() {
context.jamClient.OnDownloadAvailable();
}
/**
* Called whenever the websocket closes; this gives us a chance to cleanup things that should be stopped/cleared
* @param in_error did the socket close abnormally?
@ -172,6 +179,12 @@
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SERVER_BAD_STATE_RECOVERED, serverBadStateRecovered);
}
function registerDownloadAvailable() {
logger.debug("register for download_available");
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.DOWNLOAD_AVAILABLE, downloadAvailable);
}
function registerSocketClosed() {
logger.debug("register for socket closed");
context.JK.JamServer.registerOnSocketClosed(socketClosed);
@ -346,6 +359,7 @@
registerBadStateRecovered();
registerBadStateError();
registerSocketClosed();
registerDownloadAvailable();
context.JK.FaderHelpers.initialize();
context.window.onunload = this.unloadFunction;
}

View File

@ -1,5 +1,5 @@
/**
* Static functions for creating pagination
* Playback widget (play, pause , etc)
*/
(function(context, $) {

View File

@ -375,12 +375,6 @@
$('.profile-nav a.active').removeClass('active');
$('.profile-nav a.#profile-social-link').addClass('active');
/*if (isMusician()) {
$('.profile-social-left').show();
} else {
$('.profile-social-left').hide();
}*/
bindSocial();
}

View File

@ -0,0 +1,123 @@
/**
* Recording Manager viewer
* Although multiple instances could be made, only one should be
*/
(function(context, $) {
"use strict";
context.JK = context.JK || {};
context.JK.RecordingManager = function(){
var $parentElement = $('#recording-manager-viewer');
var logger = context.JK.logger;
if($parentElement.length == 0) {
logger.debug("no $parentElement specified in RecordingManager");
}
var $downloadCommand = $('#recording-manager-download', $parentElement);
var $downloadPercent = $('#recording-manager-download .percent', $parentElement);
var $uploadCommand = $('#recording-manager-upload', $parentElement);
var $uploadPercent = $('#recording-manager-upload .percent', $parentElement);
var $convertCommand = $('#recording-manager-convert', $parentElement);
var $convertPercent = $('#recording-manager-convert .percent', $parentElement);
// keys come from backend
var lookup = {
SyncDownload: { command: $downloadCommand, percent: $downloadPercent },
SyncUpload: { command: $uploadCommand, percent: $uploadPercent },
SyncConvert: { command: $convertCommand, percent: $convertPercent }
}
var $self = $(this);
function renderStartCommand($command) {
$command.css('visibility', 'visible');
}
function renderEndCommand($command) {
$command.css('visibility', 'hidden');
}
function renderPercentage($percent, value) {
$percent.text(Math.round(value * 100));
}
function onStartCommand(id, type) {
var command = lookup[type];
if(!command) { return }
var existingCommandId = command.command.data('command-id');
if(existingCommandId && existingCommandId != id) {
renderEndCommand(command.command);
}
command.command.data('command-id', id);
renderStartCommand(command.command);
renderPercentage(command.percent, 0);
}
function onStopCommand(id, type, success, reason, detail) {
var command = lookup[type];
if(!command) { return }
var existingCommandId = command.command.data('command-id');
if(!existingCommandId) {
command.command.data('command-id', id);
renderStartCommand(command.command);
}
else if(existingCommandId && existingCommandId != id) {
renderEndCommand(command.command);
command.command.data('command-id', id);
renderStartCommand(command.command);
}
renderPercentage(command.percent, 1);
renderEndCommand(command.command);
command.command.data('command-id', null);
}
function onCommandProgress(id, type, progress) {
var command = lookup[type];
if(!command) { return }
var existingCommandId = command.command.data('command-id');
if(!existingCommandId) {
command.command.data('command-id', id);
renderStartCommand(command.command);
}
else if(existingCommandId && existingCommandId != id) {
renderEndCommand(command.command);
command.command.data('command-id', id);
renderStartCommand(command.command);
}
renderPercentage(command.percent, progress);
}
function onCommandsChanged(id, type) {
}
context.JK.RecordingManagerCommandStart = onStartCommand;
context.JK.RecordingManagerCommandStop = onStopCommand;
context.JK.RecordingManagerCommandProgress = onCommandProgress;
context.JK.RecordingManagerCommandsChanged = onCommandsChanged;
context.jamClient.RegisterRecordingManagerCallbacks(
"JK.RecordingManagerCommandStart",
"JK.RecordingManagerCommandProgress",
"JK.RecordingManagerCommandStop",
"JK.RecordingManagerCommandsChanged"
)
return this;
}
})(window, jQuery);

View File

@ -590,7 +590,7 @@
var recordedTracks = sessionModel.recordedTracks();
console.log("recorded tracks=%o local_media_mixers=%o", recordedTracks, localMediaMixers);
console.log("recorded tracks=%o local_media=%o", recordedTracks, localMediaMixers);
if(recordedTracks && localMediaMixers.length == 0) {
// if we are the creator, then rather than raise an error, tell the server the recording is over.

View File

@ -18,6 +18,7 @@
*= require ./header
#= require ./user_dropdown
*= require ./footer
*= require ./recordingManager
*= require ./screen_common
*= require ./notify
*= require ./dialog

View File

@ -1,34 +1,35 @@
#client_update {
display:none;
.progress-bar {
width:100%;
background-color:#000;
border: solid 1px #ED3618;
height:22px;
}
#progress-bar {
width:0%;
}
.progress-bar-progress {
background-color:#ED3618;
border:solid 1px #000;
height:20px;
display:block;
}
h2 {
font-weight:bold;
font-size:x-large;
}
#client-updater-updating #update-steps {
margin-top:20px;
}
#client-updater-updating span.status {
color:white;
}
}
.progress-bar {
width:100%;
background-color:#000;
border: solid 1px #ED3618;
height:22px;
}
#progress-bar {
width:0%;
}
.progress-bar-progress {
background-color:#ED3618;
border:solid 1px #000;
height:20px;
display:block;
}
#client_update h2 {
font-weight:bold;
font-size:x-large;
}
#client-updater-updating #update-steps {
margin-top:20px;
}
#client-updater-updating span.status {
color:white;
}

View File

@ -99,6 +99,55 @@
box-sizing: border-box;
padding-top: 49px;
}
.profile-head {
position: absolute;
box-sizing: border-box;
width:100%;
}
.profile-body {
height:100%;
width:100%;
box-sizing: border-box;
padding-top: 157px;
> * {
box-sizing:border-box;
}
}
.profile-body-content {
height: inherit;
overflow:auto;
&.outer {
overflow:hidden;
}
&.padded {
padding:10px 25px;
}
}
.profile-social-head {
position: absolute;
width:100%;
padding:0 25px;
line-height:25px;
box-sizing:border-box;
+.profile-wrapper {
padding-top: 0;
}
}
.profile-social-body {
height:100%;
width:100%;
box-sizing:border-box;
padding-top:25px;
.profile-social-body-wrapper {
height:inherit;
}
.profile-social-content {
padding:0 25px;
}
}
}
.result-list-button-wrapper {

View File

@ -0,0 +1,42 @@
#recording-manager-viewer {
color: #CCCCCC;
font-size: 11px;
margin: 0 auto;
position: absolute;
text-align: center;
left: 25%;
width: 50%;
.recording-manager-command {
box-sizing: border-box;
width:33%;
margin:5px 10px;
visibility: hidden;
.percent {
margin-left:3px;
}
.percent:after {
content:"%";
}
.progress-bar {
width:100%;
background-color:#000;
border: solid 1px #ED3618;
height:22px;
display:inline;
}
.progress-bar-progress {
background-color:#ED3618;
border:solid 1px #000;
height:20px;
display:block;
width:0%;
display:inline;
}
}
}

View File

@ -12,6 +12,10 @@
padding-top: 10px;
margin: 30px 30px 0;
border-top:solid 1px #444;
#recording-manager-viewer {
display:none;
}
}
#copyright {

View File

@ -10,6 +10,11 @@
#footer {
padding-top: 10px;
border-top:solid 1px #444;
#recording-manager-viewer {
display:none;
}
}
#copyright {

View File

@ -363,4 +363,4 @@ strong {
fieldset.login-error .login-error-msg {
display:block;
}
}
}

View File

@ -1,11 +1,12 @@
<!-- start footer -->
<div id="footer">
<%= render "clients/recordingManager" %>
<!-- copyright -->
<div id="copyright">Copyright &copy; 2013 JamKazam, Inc. All Rights Reserved</div>
<!-- footer links -->
<div id="footer-links">
<%= link_to "about", corp_about_path , :rel=>"external" %>&nbsp;&nbsp;|&nbsp;&nbsp;<%= link_to "news", corp_news_path , :rel=>"external" %>&nbsp;&nbsp;|&nbsp;&nbsp;<%= link_to "media", corp_media_center_path , :rel=>"external" %>&nbsp;&nbsp;|&nbsp;&nbsp;<%= link_to "contact", corp_contact_path , :rel=>"external" %>&nbsp;&nbsp;|&nbsp;&nbsp;<%= link_to "privacy", corp_privacy_path, :rel=>"external" %>&nbsp;&nbsp;|&nbsp;&nbsp;<%= link_to "terms of service", corp_terms_path , :rel=>"external" %>&nbsp;&nbsp;|&nbsp;<%= link_to "help", corp_help_path , :rel=>"external" %>
</div>

View File

@ -10,9 +10,8 @@
<%= render "screen_navigation" %>
</div>
<div class="content-body">
<div class="content-body-scroller">
<form id="profile-form">
<div class="profile-header">
<form id="profile-form" class="inner-content">
<div class="profile-header profile-head">
<!-- profile name -->
<h2 id="profile-username"></h2>
@ -45,57 +44,83 @@
</div>
<div class="clearall"></div>
</div>
<div class="content-scroller">
<div id="profile-about" class="profile-wrapper">
<div class="profile-body">
<div id="profile-about" class="profile-body-content">
<!-- stats & location -->
<div class="profile-about-left">
<h3>Location:</h3><br />
<span id="profile-location"></span><br /><br /><br />
<h3>Stats:</h3><br />
<span id="profile-friend-stats"></span><br />
<span id="profile-follower-stats"></span><br />
<span id="profile-following-stats"></span><br />
<span id="profile-favorite-stats"></span><br />
<span id="profile-session-stats"></span><br />
<span id="profile-recording-stats"></span><br />
</div>
<div class="profile-about-right">
<p id="profile-biography"></p><br />
<div id="profile-instruments">
<div class="profile-wrapper">
<div class="profile-about-left">
<h3>Location:</h3><br />
<span id="profile-location"></span><br /><br /><br />
<h3>Stats:</h3><br />
<span id="profile-friend-stats"></span><br />
<span id="profile-follower-stats"></span><br />
<span id="profile-following-stats"></span><br />
<span id="profile-favorite-stats"></span><br />
<span id="profile-session-stats"></span><br />
<span id="profile-recording-stats"></span><br />
</div>
<div class="profile-about-right">
<p id="profile-biography"></p><br />
<div id="profile-instruments"></div>
</div>
<br clear="all" />
</div>
<br clear="all" />
</div>
<div id="profile-history" class="profile-wrapper">
<div class="content-body-scroller">
<br clear="all" />
</div>
</div>
<div id="profile-bands" class="profile-wrapper profile-body-content">
<br clear="all" />
</div>
<div id="profile-bands" class="profile-wrapper">
<br clear="all" />
</div>
<div id="profile-social" class="profile-wrapper">
<div class="profile-social-left">
<h2>Friends</h2>
<div id="profile-social-friends">
<div id="profile-social" class="profile-body-content outer">
<div class="profile-social-head">
<div class="profile-social-left">
<h2>Friends</h2>
</div>
<div class="profile-social-mid">
<h2>Following</h2>
</div>
<div class="profile-social-right">
<h2>Followers</h2>
</div>
<div class="clearall"></div>
</div>
<div class="profile-social-body">
<div class="profile-social-body-wrapper">
<div class="content-body-scroller">
<!-- @FIXME: seems like too many divs -->
<div class="profile-social-content">
<div class="profile-social-left">
<div id="profile-social-friends">
</div>
</div>
<div class="profile-social-mid">
<div id="profile-social-followings">
</div>
</div>
<div class="profile-social-right">
<div id="profile-social-followers">
</div>
</div>
<div class="clearall"></div>
</div>
<!-- @FIXME: end -->
</div>
</div>
</div>
<div class="profile-social-mid">
<h2>Following</h2>
<div id="profile-social-followings">
</div>
<div id="profile-favorites" class="profile-body-content">
<div class="profile-wrapper">
<div class="content-body-scroller">
</div>
</div>
<div class="profile-social-right">
<h2>Followers</h2>
<div id="profile-social-followers">
</div>
</div>
<br clear="all" />
</div>
<div id="profile-favorites" class="profile-wrapper">
<br clear="all" />
</div>
<!-- </div> -->
</div>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,11 @@
<span id="recording-manager-viewer">
<span id="recording-manager-convert" class="recording-manager-command">
<span>converting</span><span class="percent">0</span>
</span>
<span id="recording-manager-upload" class="recording-manager-command">
<span>uploading</span><span class="percent">0</span>
</span>
<span id="recording-manager-download" class="recording-manager-command">
<span>downloading</span><span class="percent">0</span>
</span>
</span>

View File

@ -91,6 +91,8 @@
// Some things can't be initialized until we're connected. Put them here.
function _initAfterConnect() {
var recordingManager = new JK.RecordingManager();
var invitationDialog = new JK.InvitationDialog(JK.app);
invitationDialog.initialize();

View File

@ -12,6 +12,7 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature
let(:creator) { FactoryGirl.create(:user) }
let(:joiner1) { FactoryGirl.create(:user) }
let(:joiner2) { FactoryGirl.create(:user) }
let(:some_genre) { random_genre }
before(:each) do
@ -21,18 +22,14 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature
# creates a recording, and stops it, and confirms the 'Finished Recording' dialog shows for both
it "creator start/stop" do
start_recording_with(creator, [joiner1])
in_client(creator) { stop_recording }
check_recording_finished_for([creator, joiner1])
end
# confirms that anyone can start/stop a recording
it "creator starts and other stops" do
start_recording_with(creator, [joiner1])
in_client(joiner1) { stop_recording }
check_recording_finished_for([creator, joiner1])
end
@ -64,8 +61,6 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature
end
it "creator starts/stops, with 3 total participants" do
joiner2 = FactoryGirl.create(:user)
start_recording_with(creator, [joiner1, joiner2])
in_client(creator) do
@ -76,7 +71,6 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature
end
it "creator starts with session leave to stop, with 3 total participants" do
joiner2 = FactoryGirl.create(:user)
start_recording_with(creator, [joiner1, joiner2])
in_client(creator) do
@ -90,7 +84,6 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature
# confirms that if someone leaves 'ugly' (without calling 'Leave' REST API), that the recording is junked with 3 participants
it "creator starts and then abruptly leave with 3 participants" do
joiner2 = FactoryGirl.create(:user)
start_recording_with(creator, [joiner1, joiner2])
in_client(creator) do
@ -160,12 +153,12 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature
claim_recording(name, desc)
music_session = MusicSession.first()
recording = music_session.recordings.first()
claimed_recording = ClaimedRecording.find_by_user_id(user.id)
claimed_recording.name.should == name
claimed_recording.description.should == desc
claimed_recording.is_public.should be_true
claimed_recording.is_downloadable.should be_true
claimed_recording.genre = music_session.genres[0]
claimed_recording = recording.claimed_recordings.where(:user_id => user.id).first
expect(claimed_recording.name).to eq(name)
expect(claimed_recording.description).to eq(desc)
expect(claimed_recording.is_public).to be_true
expect(claimed_recording.is_downloadable).to be_true
expect(claimed_recording.genre).to eq(music_session.genres[0])
end
end
end
@ -189,12 +182,12 @@ describe "Session Recordings", :js => true, :type => :feature, :capybara_feature
claim_recording("my recording", '')
music_session = MusicSession.first()
recording = music_session.recordings.first()
claimed_recording = ClaimedRecording.find_by_user_id(user.id)
claimed_recording.name.should == "my recording"
claimed_recording.description.should == ''
claimed_recording.is_public.should be_true
claimed_recording.is_downloadable.should be_true
claimed_recording.genre = music_session.genres[0]
claimed_recording = recording.claimed_recordings.where(:user_id => user.id).first
expect(claimed_recording.name).to eq("my recording")
expect(claimed_recording.description).to eq('')
expect(claimed_recording.is_public).to be_true
expect(claimed_recording.is_downloadable).to be_true
expect(claimed_recording.genre).to eq(music_session.genres[0])
end
end
end

View File

@ -37,8 +37,7 @@ def wipe_s3_test_bucket
:secret_access_key => Rails.application.config.aws_secret_access_key)
test_bucket = s3.buckets[JAMKAZAM_TESTING_BUCKET]
if test_bucket.name == JAMKAZAM_TESTING_BUCKET
puts "Web - Pretending to delete all contents of #{test_bucket.name}"
#test_bucket.delete_all
#test_bucket.clear!
end
end