showing brightness value in slider, remember bridge ip when picking from multiple bridges, fix light strobe, remove error handling on bridge finding ( TRIAL )

This commit is contained in:
Egor 2017-02-13 00:02:00 -08:00
parent 7db49d310c
commit d12e599814
20 changed files with 521 additions and 528 deletions

View file

@ -28,12 +28,11 @@ export default Component.extend({
manualBridgeIp: null, manualBridgeIp: null,
manualBridgeIpNotFound: false, manualBridgeIpNotFound: false,
multipleBridgeIps: [], multipleBridgeIps: [],
error: false,
isAuthenticating: computed.notEmpty('bridgePingIntervalHandle'), isAuthenticating: computed.notEmpty('bridgePingIntervalHandle'),
// try to authenticate against the bridge here // try to authenticate against the bridge here
onBridgeIpChange: on('init', observer('bridgeIp', function(){ onBridgeIpChange: on('init', observer('bridgeIp', function () {
if(!this.get('trial') && !this.get('isAuthenticating')) { if (!this.get('trial') && !this.get('isAuthenticating')) {
this.setProperties({ this.setProperties({
bridgePingIntervalHandle: setInterval(this.pingBridgeUser.bind(this), this.get('bridgeUsernamePingIntervalTime')), bridgePingIntervalHandle: setInterval(this.pingBridgeUser.bind(this), this.get('bridgeUsernamePingIntervalTime')),
bridgeUserNamePingIntervalProgress: 0 bridgeUserNamePingIntervalProgress: 0
@ -42,14 +41,14 @@ export default Component.extend({
})), })),
didInsertElement() { didInsertElement() {
$(document).keypress((event)=>{ $(document).keypress((event) => {
if(!isNone(this.get('manualBridgeIp')) && event.which === 13) { if (!isNone(this.get('manualBridgeIp')) && event.which === 13) {
this.send('findBridgeByIp'); this.send('findBridgeByIp');
} }
}); });
document.addEventListener('resume', () => { document.addEventListener('resume', () => {
if(this.get('error') || this.get('trial') || this.get('bridgeFindFail')) { if (this.get('trial') || this.get('bridgeFindFail')) {
this.send('tryAgain'); this.send('tryAgain');
} }
}, false); }, false);
@ -59,11 +58,11 @@ export default Component.extend({
init() { init() {
this._super(...arguments); this._super(...arguments);
if(this.get('bridgeIp') === null) { if (this.get('bridgeIp') === null) {
$.ajax('https://www.meethue.com/api/nupnp', { $.ajax('https://www.meethue.com/api/nupnp', {
timeout: 30000 timeout: 30000
}) })
.done((result, status)=> { .done((result, status) => {
let bridgeFindStatus = 'fail'; let bridgeFindStatus = 'fail';
if (status === 'success' && result.length === 1) { if (status === 'success' && result.length === 1) {
@ -84,9 +83,9 @@ export default Component.extend({
this.set('bridgeFindStatus', bridgeFindStatus); this.set('bridgeFindStatus', bridgeFindStatus);
}) })
.fail(()=>{ .fail(() => {
this.set('bridgeFindStatus', 'fail'); this.set('bridgeFindStatus', 'fail');
}); });
} }
}, },
@ -97,11 +96,11 @@ export default Component.extend({
if (bridgeIp !== null && bridgeUserNamePingIntervalProgress < 100) { if (bridgeIp !== null && bridgeUserNamePingIntervalProgress < 100) {
$.ajax('http://' + bridgeIp + '/api', { $.ajax('http://' + bridgeIp + '/api', {
data: JSON.stringify({"devicetype": "huegasm"}), data: JSON.stringify({ "devicetype": "huegasm" }),
contentType: 'application/json', contentType: 'application/json',
type: 'POST' type: 'POST'
}).done((result, status)=>{ }).done((result, status) => {
if(!this.isDestroyed){ if (!this.isDestroyed) {
this.set('bridgeAuthenticateReachedStatus', status); this.set('bridgeAuthenticateReachedStatus', status);
if (status === 'success' && !result[0].error) { if (status === 'success' && !result[0].error) {
@ -110,18 +109,15 @@ export default Component.extend({
this.set('bridgeUsername', result[0].success.username); this.set('bridgeUsername', result[0].success.username);
} }
} }
}).fail(()=>{
this.clearBridgePingIntervalHandle();
this.set('error', true);
}); });
this.incrementProperty('bridgeUserNamePingIntervalProgress', this.get('bridgeUsernamePingIntervalTime')/bridgeUsernamePingMaxTime*100); this.incrementProperty('bridgeUserNamePingIntervalProgress', this.get('bridgeUsernamePingIntervalTime') / bridgeUsernamePingMaxTime * 100);
} else { } else {
this.clearBridgePingIntervalHandle(); this.clearBridgePingIntervalHandle();
} }
}, },
clearBridgePingIntervalHandle(){ clearBridgePingIntervalHandle() {
clearInterval(this.get('bridgePingIntervalHandle')); clearInterval(this.get('bridgePingIntervalHandle'));
this.set('bridgePingIntervalHandle', null); this.set('bridgePingIntervalHandle', null);
}, },
@ -131,11 +127,12 @@ export default Component.extend({
this.get('storage').clear(); this.get('storage').clear();
location.reload(); location.reload();
}, },
retry(){ retry() {
this.onBridgeIpChange(); this.onBridgeIpChange();
}, },
chooseBridge(bridge){ chooseBridge(bridge) {
this.set('bridgeIp', bridge); this.set('bridgeIp', bridge);
this.get('storage').set('huegasm.bridgeIp', bridge);
}, },
findBridgeByIp() { findBridgeByIp() {
let manualBridgeIp = this.get('manualBridgeIp'); let manualBridgeIp = this.get('manualBridgeIp');
@ -148,12 +145,12 @@ export default Component.extend({
}); });
} else { } else {
$.ajax('http://' + manualBridgeIp + '/api', { $.ajax('http://' + manualBridgeIp + '/api', {
data: JSON.stringify({"devicetype": "huegasm"}), data: JSON.stringify({ "devicetype": "huegasm" }),
contentType: 'application/json', contentType: 'application/json',
type: 'POST' type: 'POST'
}).fail(() => { }).fail(() => {
this.set('manualBridgeIpNotFound', true); this.set('manualBridgeIpNotFound', true);
later(this, function() { later(this, function () {
this.set('manualBridgeIpNotFound', false); this.set('manualBridgeIpNotFound', false);
}, 5000); }, 5000);
}).then(() => { }).then(() => {

View file

@ -1,23 +1,15 @@
<div class="title"><img src="assets/images/logo.png" alt="Huegasm"></div> <div class="title"><img src="assets/images/logo.png" alt="Huegasm"></div>
{{#unless bridgeUsername}} {{#unless bridgeUsername}}
{{#if bridgeIp}} {{#if bridgeIp}}
{{#if error}} <img src="assets/images/pressButtonBridge.png" id="press-bridge-button-img"> {{paper-progress-linear warn=true value=bridgeUserNamePingIntervalProgress}}
<p>Huegasm encountered a critical error while trying to connect to your bridge.<br><br> <p class="bridge-finder-bottom">
<a href="#" {{action "tryAgain"}}>TRY AGAIN</a> {{#if isAuthenticating}}
</p> Your bridge IP is <b>{{bridgeIp}}</b>
{{else}} <br> Press the button on your bridge to authenticate this application.
<img src="assets/images/pressButtonBridge.png" id="press-bridge-button-img"> {{else}}
{{paper-progress-linear warn=true value=bridgeUserNamePingIntervalProgress}} You failed to press the button in time. <a class="no-text-decoration" href="#" {{action 'retry'}}>RETRY</a>
<p class="bridge-finder-bottom"> {{/if}}
{{#if isAuthenticating}} </p>
Your bridge IP is <b>{{bridgeIp}}</b>
<br>
Press the button on your bridge to authenticate this application.
{{else}}
You failed to press the button in time. <a class="no-text-decoration" href="#" {{action 'retry'}}>RETRY</a>
{{/if}}
</p>
{{/if}}
{{else}} {{else}}
{{#unless bridgeFindStatus}} {{#unless bridgeFindStatus}}
{{paper-progress-circular diameter=100}} {{paper-progress-circular diameter=100}}
@ -25,8 +17,7 @@
{{/unless}} {{/unless}}
{{#if bridgeFindMultiple}} {{#if bridgeFindMultiple}}
<p>Found multiple hue bridges. <br> <p>Huegasm found multiple hue bridges. <br> Please select the one you want to use for this application.</p>
Please select the one you want to use for this application.</p>
<div id="bridge-button-group"> <div id="bridge-button-group">
{{#each multipleBridgeIps as |bridge|}} {{#each multipleBridgeIps as |bridge|}}
@ -35,9 +26,7 @@
</div> </div>
{{else}} {{else}}
{{#if bridgeFindFail}} {{#if bridgeFindFail}}
<p>A hue bridge could not be automatically found on your network. <br> <p>A hue bridge could not be automatically found on your network. <br> Enter one manually? <br><br> ( or type <b>offline</b> to look around )
Enter one manually? <br><br>
( or type <b>offline</b> to look around )
</p> </p>
<span id="bridge-input"> <span id="bridge-input">

View file

@ -11,10 +11,10 @@ export default Component.extend({
trial: false, trial: false,
storage: null, storage: null,
init(){ init() {
this._super(...arguments); this._super(...arguments);
let storage = new window.Locally.Store({compress: true}); let storage = new window.Locally.Store({ compress: true });
this.set('storage', storage); this.set('storage', storage);
if (!isEmpty(storage.get('huegasm.bridgeIp')) && !isEmpty(storage.get('huegasm.bridgeUsername'))) { if (!isEmpty(storage.get('huegasm.bridgeIp')) && !isEmpty(storage.get('huegasm.bridgeUsername'))) {

View file

@ -5,7 +5,7 @@ const {
observer, observer,
computed, computed,
on, on,
run: { later }, run: { later, once },
$ $
} = Ember; } = Ember;
@ -20,45 +20,45 @@ export default Component.extend({
colorLoopOn: false, colorLoopOn: false,
lightsOnTxt: computed('lightsOn', function(){ lightsOnTxt: computed('lightsOn', function () {
return this.get('lightsOn') ? 'On' : 'Off'; return this.get('lightsOn') ? 'On' : 'Off';
}), }),
colorloopOnTxt: computed('colorLoopOn', function(){ colorloopOnTxt: computed('colorLoopOn', function () {
return this.get('colorLoopOn') ? 'On' : 'Off'; return this.get('colorLoopOn') ? 'On' : 'Off';
}), }),
// determines the average brightness of the hue system for the brightness slider // determines the average brightness of the hue system for the brightness slider
lightsBrightness: computed('lightsData', function(){ lightsBrightness: computed('lightsData', function () {
let lightsData = this.get('lightsData'), let lightsData = this.get('lightsData'),
activeLights = this.get('activeLights'), activeLights = this.get('activeLights'),
lightsBrightness = 0; lightsBrightness = 0;
activeLights.forEach(function(light){ activeLights.forEach(function (light) {
lightsBrightness += lightsData[light].state.bri; lightsBrightness += lightsData[light].state.bri;
}); });
return lightsBrightness/activeLights.length; return lightsBrightness / activeLights.length;
}), }),
brightnessControlDisabled: computed.not('lightsOn'), brightnessControlDisabled: computed.not('lightsOn'),
onColorLoopOnChange: observer('colorLoopOn', function(){ onColorLoopOnChange: observer('colorLoopOn', function () {
let lightsData = this.get('lightsData'), let lightsData = this.get('lightsData'),
activeLights = this.get('activeLights'), activeLights = this.get('activeLights'),
colorLoopsOn = this.get('colorLoopOn'), colorLoopsOn = this.get('colorLoopOn'),
effect = colorLoopsOn ? 'colorloop' : 'none'; effect = colorLoopsOn ? 'colorloop' : 'none';
let colorLoopsOnSystem = activeLights.some(function(light) { let colorLoopsOnSystem = activeLights.some(function (light) {
return lightsData[light].state.effect === 'colorloop'; return lightsData[light].state.effect === 'colorloop';
}); });
// if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state // if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state
if(colorLoopsOn !== colorLoopsOnSystem){ if (colorLoopsOn !== colorLoopsOnSystem) {
activeLights.forEach((light)=>{ activeLights.forEach((light) => {
if(this.get('lightsData')[light].state.effect !== effect) { if (this.get('lightsData')[light].state.effect !== effect) {
$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { $.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({'effect': effect }), data: JSON.stringify({ 'effect': effect }),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
@ -67,7 +67,7 @@ export default Component.extend({
} }
}), }),
rgbPreview: observer('rgb', function() { rgbPreview: observer('rgb', function () {
let rgb = this.get('rgb'), let rgb = this.get('rgb'),
xy = this.rgbToXy(rgb[0], rgb[1], rgb[2]); xy = this.rgbToXy(rgb[0], rgb[1], rgb[2]);
@ -75,7 +75,7 @@ export default Component.extend({
this.get('activeLights').forEach((light) => { this.get('activeLights').forEach((light) => {
$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { $.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({"xy": xy}), data: JSON.stringify({ "xy": xy }),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
@ -86,9 +86,9 @@ export default Component.extend({
}), }),
// determines whether the lights are on/off for the lights switch // determines whether the lights are on/off for the lights switch
lightsOnChange: on('init', observer('lightsData.@each.state.on', 'activeLights.[]', function(){ lightsOnChange: on('init', observer('lightsData.@each.state.on', 'activeLights.[]', function () {
if(!this.get('strobeOn')){ if (!this.get('strobeOn')) {
let lightsData = this.get('lightsData'), lightsOn = this.get('activeLights').some(function(light) { let lightsData = this.get('lightsData'), lightsOn = this.get('activeLights').some(function (light) {
return lightsData[light].state.on === true; return lightsData[light].state.on === true;
}); });
@ -96,20 +96,20 @@ export default Component.extend({
} }
})), })),
onLightsOnChange: observer('lightsOn', function(){ onLightsOnChange: observer('lightsOn', function () {
let lightsData = this.get('lightsData'), let lightsData = this.get('lightsData'),
activeLights = this.get('activeLights'), activeLights = this.get('activeLights'),
lightsOn = this.get('lightsOn'); lightsOn = this.get('lightsOn');
let lightsOnSystem = activeLights.some(function(light) { let lightsOnSystem = activeLights.some(function (light) {
return lightsData[light].state.on === true; return lightsData[light].state.on === true;
}); });
// if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state // if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state
if(lightsOn !== lightsOnSystem){ if (lightsOn !== lightsOnSystem) {
activeLights.forEach((light)=>{ activeLights.forEach((light) => {
$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { $.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({"on": lightsOn}), data: JSON.stringify({ "on": lightsOn }),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
@ -117,32 +117,34 @@ export default Component.extend({
} }
}), }),
onBrightnessChanged: observer('lightsBrightness', function(){ onBrightnessChanged: observer('lightsBrightness', function () {
let lightsData = this.get('lightsData'), once(this, function () {
lightsBrightnessSystem = false, let lightsData = this.get('lightsData'),
lightsBrightness = this.get('lightsBrightness'), lightsBrightnessSystem = false,
activeLights = this.get('activeLights'); lightsBrightness = this.get('lightsBrightness'),
activeLights = this.get('activeLights');
activeLights.forEach(function(light){ activeLights.forEach(function (light) {
lightsBrightnessSystem += lightsData[light].state.bri; lightsBrightnessSystem += lightsData[light].state.bri;
});
lightsBrightnessSystem /= activeLights.length;
// if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state
if(lightsBrightness !== lightsBrightnessSystem){
activeLights.forEach((light)=>{
$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({"bri": lightsBrightness}),
contentType: 'application/json',
type: 'PUT'
});
}); });
}
lightsBrightnessSystem /= activeLights.length;
// if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state
if (lightsBrightness !== lightsBrightnessSystem) {
activeLights.forEach((light) => {
$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({ "bri": lightsBrightness }),
contentType: 'application/json',
type: 'PUT'
});
});
}
});
}), }),
// sync the current light settings to the newly added light // sync the current light settings to the newly added light
onaActiveLightsChange: observer('syncLight', function(){ onaActiveLightsChange: observer('syncLight', function () {
let options = { let options = {
on: this.get('lightsOn'), on: this.get('lightsOn'),
bri: this.get('lightsBrightness'), bri: this.get('lightsBrightness'),
@ -150,7 +152,7 @@ export default Component.extend({
}, rgb = this.get('rgb'), }, rgb = this.get('rgb'),
syncLight = this.get('syncLight'); syncLight = this.get('syncLight');
if(rgb[0] !== 255 && rgb[1] !== 255 && rgb[2] !== 255) { if (rgb[0] !== 255 && rgb[1] !== 255 && rgb[2] !== 255) {
options['xy'] = this.rgbToXy(rgb[0], rgb[1], rgb[2]); options['xy'] = this.rgbToXy(rgb[0], rgb[1], rgb[2]);
} }
@ -167,9 +169,8 @@ export default Component.extend({
strobeOn: false, strobeOn: false,
strobeOnInervalHandle: null, strobeOnInervalHandle: null,
strobeSat: 0,
preStrobeOnLightsDataCache: null, preStrobeOnLightsDataCache: null,
lastStrobeLight: 0, nextLightIdx: 0,
onStrobeOnChange: observer('strobeOn', function () { onStrobeOnChange: observer('strobeOn', function () {
let lightsData = this.get('lightsData'), let lightsData = this.get('lightsData'),
@ -177,7 +178,7 @@ export default Component.extend({
if (strobeOn) { if (strobeOn) {
this.set('preStrobeOnLightsDataCache', lightsData); this.set('preStrobeOnLightsDataCache', lightsData);
let stobeInitRequestData = {'sat': this.get('strobeSat'), 'transitiontime': 0}; let stobeInitRequestData = { 'transitiontime': 0 };
for (let key in lightsData) { for (let key in lightsData) {
if (lightsData.hasOwnProperty(key)) { if (lightsData.hasOwnProperty(key)) {
@ -195,7 +196,7 @@ export default Component.extend({
this.set('strobeOnInervalHandle', setInterval(this.strobeStep.bind(this), 500)); this.set('strobeOnInervalHandle', setInterval(this.strobeStep.bind(this), 500));
} else { // revert the light system to pre-strobe } else { // revert the light system to pre-strobe
let preStrobeOnLightsDataCache = this.get('preStrobeOnLightsDataCache'), updateLight = (lightIndex)=> { let preStrobeOnLightsDataCache = this.get('preStrobeOnLightsDataCache'), updateLight = (lightIndex) => {
$.ajax(this.get('apiURL') + '/lights/' + lightIndex + '/state', { $.ajax(this.get('apiURL') + '/lights/' + lightIndex + '/state', {
data: JSON.stringify({ data: JSON.stringify({
'on': preStrobeOnLightsDataCache[lightIndex].state.on, 'on': preStrobeOnLightsDataCache[lightIndex].state.on,
@ -220,45 +221,46 @@ export default Component.extend({
}), }),
strobeStep() { strobeStep() {
let lastStrobeLight = (this.get('lastStrobeLight') + 1) % (this.get('activeLights').length + 1), let nextLightIdx = this.get('nextLightIdx') % this.get('activeLights').length,
turnOnOptions = {'on': true, 'transitiontime': 0, 'alert': 'select'}; nextStrobeLight = this.get('activeLights')[nextLightIdx],
turnOnOptions = { on: true, transitiontime: 0, alert: 'select' };
// random light if in cololoop mode // random light if in cololoop mode
if(this.get('colorLoopOn')) { if (this.get('colorLoopOn')) {
turnOnOptions.hue = Math.floor(Math.random() * 65535); turnOnOptions.hue = Math.floor(Math.random() * 65535);
} }
$.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', { $.ajax(this.get('apiURL') + '/lights/' + nextStrobeLight + '/state', {
data: JSON.stringify(turnOnOptions), data: JSON.stringify(turnOnOptions),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
$.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', { $.ajax(this.get('apiURL') + '/lights/' + nextStrobeLight + '/state', {
data: JSON.stringify({'on': false, 'transitiontime': 0}), data: JSON.stringify({ 'on': false, 'transitiontime': 0 }),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
this.set('lastStrobeLight', lastStrobeLight); this.set('nextLightIdx', ++nextLightIdx);
}, },
strobeOnTxt: computed('strobeOn', function () { strobeOnTxt: computed('strobeOn', function () {
return this.get('strobeOn') ? 'On' : 'Off'; return this.get('strobeOn') ? 'On' : 'Off';
}), }),
dimmerOnClass: computed('dimmerOn', function(){ dimmerOnClass: computed('dimmerOn', function () {
return this.get('dimmerOn') ? 'dimmerOn' : null; return this.get('dimmerOn') ? 'dimmerOn' : null;
}), }),
// **************** STROBE LIGHT FINISH **************** // **************** STROBE LIGHT FINISH ****************
// http://www.developers.meethue.com/documentation/color-conversions-rgb-xy // http://www.developers.meethue.com/documentation/color-conversions-rgb-xy
rgbToXy(red, green, blue){ rgbToXy(red, green, blue) {
let X, Y, Z, x, y; let X, Y, Z, x, y;
// normalize // normalize
red = Number((red/255)); red = Number((red / 255));
green = Number((green/255)); green = Number((green / 255));
blue = Number((blue/255)); blue = Number((blue / 255));
// gamma correction // gamma correction
red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92); red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92);
@ -273,10 +275,10 @@ export default Component.extend({
x = X / (X + Y + Z); x = X / (X + Y + Z);
y = Y / (X + Y + Z); y = Y / (X + Y + Z);
return [x,y]; return [x, y];
}, },
xyToRgb(x, y){ xyToRgb(x, y) {
let r, g, b, X, Y = 1.0, Z; let r, g, b, X, Y = 1.0, Z;
X = (Y / y) * x; X = (Y / y) * x;

View file

@ -7,11 +7,11 @@
{{#paper-item}} {{#paper-item}}
{{paper-icon "brightness-4" class=dimmerOnClass}} {{paper-icon "brightness-4" class=dimmerOnClass}}
<p>Brightness</p> <p class="layout flex-60">Brightness</p>
{{paper-slider flex=true min=1 max=254 value=lightsBrightness disabled=brightnessControlDisabled}} {{paper-slider class="flex" discrete=true step="10" min="1" max="254" value=lightsBrightness disabled=brightnessControlDisabled}}
{{/paper-item}} {{/paper-item}}
{{#paper-item class="color-row"}} {{#paper-item class="color-row" }}
{{paper-icon "color-lens" class=dimmerOnClass}} {{paper-icon "color-lens" class=dimmerOnClass}}
<p>Color</p> <p>Color</p>
{{#paper-menu offset="0 -100" as |menu|}} {{#paper-menu offset="0 -100" as |menu|}}

View file

@ -12,17 +12,17 @@ const {
} = Ember; } = Ember;
export default Component.extend(helperMixin, visualizerMixin, { export default Component.extend(helperMixin, visualizerMixin, {
updatePageTitle: observer('playQueuePointer', function(){ updatePageTitle: observer('playQueuePointer', function () {
let title = 'Huegasm', let title = 'Huegasm',
playQueuePointer = this.get('playQueuePointer'), playQueuePointer = this.get('playQueuePointer'),
playQueue = this.get('playQueue'); playQueue = this.get('playQueue');
if(playQueuePointer !== -1){ if (playQueuePointer !== -1) {
let song = playQueue[playQueuePointer]; let song = playQueue[playQueuePointer];
if(song.title){ if (song.title) {
title = song.title; title = song.title;
if(song.artist){ if (song.artist) {
title += (' - ' + song.artist); title += (' - ' + song.artist);
} }
} else { } else {
@ -35,14 +35,14 @@ export default Component.extend(helperMixin, visualizerMixin, {
document.title = title; document.title = title;
}), }),
changePlayerControl(name, value, saveBeatPrefs){ changePlayerControl(name, value, saveBeatPrefs) {
this.set(name, value); this.set(name, value);
if(name === 'threshold'){ if (name === 'threshold') {
this.get('kick').set({threshold: value}); this.get('kick').set({ threshold: value });
} }
if(saveBeatPrefs && this.get('playQueuePointer') !== -1){ if (saveBeatPrefs && this.get('playQueuePointer') !== -1) {
this.saveSongBeatPreferences(); this.saveSongBeatPreferences();
} }
@ -51,11 +51,11 @@ export default Component.extend(helperMixin, visualizerMixin, {
saveSongBeatPreferences() { saveSongBeatPreferences() {
let song = this.get('playQueue')[this.get('playQueuePointer')]; let song = this.get('playQueue')[this.get('playQueuePointer')];
if(song) { if (song) {
let title = isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title, let title = isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title,
songBeatPreferences = this.get('songBeatPreferences'); songBeatPreferences = this.get('songBeatPreferences');
songBeatPreferences[title] = {threshold: this.get('threshold')}; songBeatPreferences[title] = { threshold: this.get('threshold') };
this.set('usingBeatPreferences', true); this.set('usingBeatPreferences', true);
this.get('storage').set('huegasm.songBeatPreferences', songBeatPreferences); this.get('storage').set('huegasm.songBeatPreferences', songBeatPreferences);
@ -70,12 +70,12 @@ export default Component.extend(helperMixin, visualizerMixin, {
oldBeatPrefCache = this.get('oldBeatPrefCache'), oldBeatPrefCache = this.get('oldBeatPrefCache'),
newOldBeatPrefCache = null; newOldBeatPrefCache = null;
if(!isNone(preference)) { // load existing beat prefs if (!isNone(preference)) { // load existing beat prefs
newOldBeatPrefCache = {threshold: this.get('threshold')}; newOldBeatPrefCache = { threshold: this.get('threshold') };
this.changePlayerControl('threshold', preference.threshold); this.changePlayerControl('threshold', preference.threshold);
this.set('usingBeatPreferences', true); this.set('usingBeatPreferences', true);
} else if(!isNone(oldBeatPrefCache)) { // revert to using beat prefs before the remembered song } else if (!isNone(oldBeatPrefCache)) { // revert to using beat prefs before the remembered song
this.changePlayerControl('threshold', oldBeatPrefCache.threshold); this.changePlayerControl('threshold', oldBeatPrefCache.threshold);
this.set('usingBeatPreferences', false); this.set('usingBeatPreferences', false);
} }
@ -86,11 +86,11 @@ export default Component.extend(helperMixin, visualizerMixin, {
clearCurrentAudio(resetPointer) { clearCurrentAudio(resetPointer) {
let dancer = this.get('dancer'); let dancer = this.get('dancer');
if(dancer.audio.pause) { if (dancer.audio.pause) {
dancer.pause(); dancer.pause();
} }
if(resetPointer){ if (resetPointer) {
this.set('playQueuePointer', -1); this.set('playQueuePointer', -1);
} }
@ -108,19 +108,19 @@ export default Component.extend(helperMixin, visualizerMixin, {
transitiontime = this.get('flashingTransitions'), transitiontime = this.get('flashingTransitions'),
brightnessRange = this.get('brightnessRange'), brightnessRange = this.get('brightnessRange'),
stimulateLight = (light, brightness, hue) => { stimulateLight = (light, brightness, hue) => {
let options = {'bri': brightness}; let options = { 'bri': brightness };
if(transitiontime) { if (transitiontime) {
options['transitiontime'] = 0; options['transitiontime'] = 0;
} else { } else {
options['transitiontime'] = 1; options['transitiontime'] = 1;
} }
if(!isNone(hue)) { if (!isNone(hue)) {
options.hue = hue; options.hue = hue;
} }
if(lightsData[light].state.on === false){ if (lightsData[light].state.on === false) {
options.on = true; options.on = true;
} }
@ -132,7 +132,7 @@ export default Component.extend(helperMixin, visualizerMixin, {
}, },
timeToBriOff = 100; timeToBriOff = 100;
if(activeLights.length > 0){ if (activeLights.length > 0) {
let lastLightBopIndex = this.get('lastLightBopIndex'), let lastLightBopIndex = this.get('lastLightBopIndex'),
lightBopIndex, lightBopIndex,
light; light;
@ -140,8 +140,8 @@ export default Component.extend(helperMixin, visualizerMixin, {
lightBopIndex = Math.floor(Math.random() * activeLights.length); lightBopIndex = Math.floor(Math.random() * activeLights.length);
// let's try not to select the same light twice in a row // let's try not to select the same light twice in a row
if(activeLights.length > 1) { if (activeLights.length > 1) {
while(lightBopIndex === lastLightBopIndex) { while (lightBopIndex === lastLightBopIndex) {
lightBopIndex = Math.floor(Math.random() * activeLights.length); lightBopIndex = Math.floor(Math.random() * activeLights.length);
} }
} }
@ -149,13 +149,13 @@ export default Component.extend(helperMixin, visualizerMixin, {
light = activeLights[lightBopIndex]; light = activeLights[lightBopIndex];
this.set('lastLightBopIndex', lightBopIndex); this.set('lastLightBopIndex', lightBopIndex);
if(!this.get('colorloopMode')) { if (!this.get('colorloopMode')) {
let hueRange = this.get('hueRange'); let hueRange = this.get('hueRange');
color = Math.floor(Math.random()*(hueRange[1] - hueRange[0] + 1)+hueRange[0]); color = Math.floor(Math.random() * (hueRange[1] - hueRange[0] + 1) + hueRange[0]);
} }
if(transitiontime){ if (transitiontime) {
timeToBriOff = 80; timeToBriOff = 80;
} }
@ -164,13 +164,15 @@ export default Component.extend(helperMixin, visualizerMixin, {
} }
this.set('paused', true); this.set('paused', true);
later(this, function(){{ later(this, function () {
this.set('paused', false); {
}}, 150); this.set('paused', false);
}
}, 150);
//work the music beat area - simulate the speaker vibration by running a CSS animation on it //work the music beat area - simulate the speaker vibration by running a CSS animation on it
$('#beat-speaker-center-outer').velocity({blur: 3}, 100).velocity({blur: 0}, 100); $('#beat-speaker-center-outer').velocity({ blur: 3 }, 100).velocity({ blur: 0 }, 100);
$('#beat-speaker-center-inner').velocity({scale: 1.05}, 100).velocity({scale: 1}, 100); $('#beat-speaker-center-inner').velocity({ scale: 1.05 }, 100).velocity({ scale: 1 }, 100);
}, },
init() { init() {
@ -198,11 +200,11 @@ export default Component.extend(helperMixin, visualizerMixin, {
kick: kick kick: kick
}); });
['shuffle', 'repeat', 'threshold', 'playerBottomDisplayed', 'audioMode', 'songBeatPreferences', 'firstVisit', 'currentVisName', 'playQueue', 'playQueuePointer', 'flashingTransitions', 'colorloopMode', 'hueRange', 'brightnessRange'].forEach((item)=>{ ['shuffle', 'repeat', 'threshold', 'playerBottomDisplayed', 'audioMode', 'songBeatPreferences', 'firstVisit', 'currentVisName', 'playQueue', 'playQueuePointer', 'flashingTransitions', 'colorloopMode', 'hueRange', 'brightnessRange'].forEach((item) => {
if (!isNone(storage.get('huegasm.' + item))) { if (!isNone(storage.get('huegasm.' + item))) {
let itemVal = storage.get('huegasm.' + item); let itemVal = storage.get('huegasm.' + item);
if(isNone(this.actions[item+'Changed'])){ if (isNone(this.actions[item + 'Changed'])) {
this.set(item, itemVal); this.set(item, itemVal);
} else { } else {
this.send(item + 'Changed', itemVal); this.send(item + 'Changed', itemVal);
@ -217,7 +219,7 @@ export default Component.extend(helperMixin, visualizerMixin, {
this.set('oldPlayQueueLength', this.get('playQueue.length')); this.set('oldPlayQueueLength', this.get('playQueue.length'));
document.addEventListener('stop', () => { document.addEventListener('stop', () => {
if(this.get('playing')){ if (this.get('playing')) {
this.send('play'); this.send('play');
} }
}, false); }, false);
@ -234,29 +236,29 @@ export default Component.extend(helperMixin, visualizerMixin, {
}); });
// file input code // file input code
$('#file-input').on('change', function() { $('#file-input').on('change', function () {
let files = this.files; let files = this.files;
self.send('handleNewFiles', files); self.send('handleNewFiles', files);
this.value = null; // reset in case upload the second file again this.value = null; // reset in case upload the second file again
}); });
$(document).on('click', '.alert', (event)=>{ $(document).on('click', '.alert', (event) => {
$(event.target).addClass('removed'); $(event.target).addClass('removed');
}); });
// prevent space/text selection when the user repeatedly clicks on the center // prevent space/text selection when the user repeatedly clicks on the center
$('#beat-container').on('mousedown', '#beat-speaker-center-inner', function(event) { $('#beat-container').on('mousedown', '#beat-speaker-center-inner', function (event) {
event.preventDefault(); event.preventDefault();
}); });
$(document).keypress((event) => { $(document).keypress((event) => {
if(event.which === 32 && event.target.type !== 'text'){ if (event.which === 32 && event.target.type !== 'text') {
this.send('play'); this.send('play');
} }
}); });
// demo tracks // demo tracks
if(this.get('firstVisit')){ if (this.get('firstVisit')) {
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/candyland-speechless-feat-rkcb'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/candyland-speechless-feat-rkcb');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/dillistone/dillistone-lili-n-rude'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/dillistone/dillistone-lili-n-rude');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/vallis-alps-young-feki-remix'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/vallis-alps-young-feki-remix');
@ -268,86 +270,87 @@ export default Component.extend(helperMixin, visualizerMixin, {
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/90-pounds-of-pete-waited-too-long-feat-devon-baldwin'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/90-pounds-of-pete-waited-too-long-feat-devon-baldwin');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/draper-eyes-open'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/draper-eyes-open');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/itspapaya/sunny'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/itspapaya/sunny');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/stonesthrow/nxworries-anderson-paak-knxwledge-suede');
this.get('storage').set('huegasm.firstVisit', false); this.get('storage').set('huegasm.firstVisit', false);
this.sendAction(); this.sendAction();
} }
if(!this.get('playerBottomDisplayed')) { if (!this.get('playerBottomDisplayed')) {
$('#player-bottom').hide(); $('#player-bottom').hide();
} }
}, },
actions: { actions: {
setVisName(name){ setVisName(name) {
this.set('currentVisName', name); this.set('currentVisName', name);
}, },
gotoSCURL(URL){ gotoSCURL(URL) {
// need to pause the music since soundcloud is going to start playing this song anyways // need to pause the music since soundcloud is going to start playing this song anyways
if(this.get('playing')){ if (this.get('playing')) {
this.send('play'); this.send('play');
} }
this.send('gotoURL', URL); this.send('gotoURL', URL);
}, },
gotoURL(URL){ gotoURL(URL) {
window.open(URL, '_blank'); window.open(URL, '_blank');
}, },
handleNewSoundCloudURL(URL){ handleNewSoundCloudURL(URL) {
if(URL) { if (URL) {
SC.resolve(URL).then((resultObj)=>{ SC.resolve(URL).then((resultObj) => {
let processResult = (result)=>{ let processResult = (result) => {
if(result.kind === 'user'){ if (result.kind === 'user') {
this.get('notify').alert({html: this.get('scUserNotSupportedHtml')}); this.get('notify').alert({ html: this.get('scUserNotSupportedHtml') });
} else if(result.kind === 'track') { } else if (result.kind === 'track') {
if(result.streamable === true){ if (result.streamable === true) {
let picture = null; let picture = null;
if(result.artwork_url){ if (result.artwork_url) {
picture = result.artwork_url.replace('large', 't67x67'); picture = result.artwork_url.replace('large', 't67x67');
} else if(result.user.avatar_url){ } else if (result.user.avatar_url) {
picture = result.user.avatar_url; picture = result.user.avatar_url;
} }
$.get(picture) $.get(picture)
.done(()=>{ .done(() => {
this.get('playQueue').pushObject({url: result.stream_url + '?client_id=' + this.get('SC_CLIENT_ID'), fileName: result.title + ' - ' + result.user.username, artist: result.user.username, scUrl: result.permalink_url, title: result.title, picture: picture }); this.get('playQueue').pushObject({ url: result.stream_url + '?client_id=' + this.get('SC_CLIENT_ID'), fileName: result.title + ' - ' + result.user.username, artist: result.user.username, scUrl: result.permalink_url, title: result.title, picture: picture });
}).fail(()=>{ // no picture }).fail(() => { // no picture
this.get('playQueue').pushObject({url: result.stream_url + '?client_id=' + this.get('SC_CLIENT_ID'), fileName: result.title + ' - ' + result.user.username, artist: result.user.username, scUrl: result.permalink_url, title: result.title }); this.get('playQueue').pushObject({ url: result.stream_url + '?client_id=' + this.get('SC_CLIENT_ID'), fileName: result.title + ' - ' + result.user.username, artist: result.user.username, scUrl: result.permalink_url, title: result.title });
}); });
} else { } else {
failedSongs.push(result.title); failedSongs.push(result.title);
}
} else if(result.kind === 'playlist'){
if(result.streamable === true){
result.tracks.forEach(processResult);
} else {
failedSongs.push(result.title);
}
} }
}, } else if (result.kind === 'playlist') {
if (result.streamable === true) {
result.tracks.forEach(processResult);
} else {
failedSongs.push(result.title);
}
}
},
failedSongs = []; failedSongs = [];
if(resultObj instanceof Array){ if (resultObj instanceof Array) {
resultObj.forEach(processResult); resultObj.forEach(processResult);
} else { } else {
processResult(resultObj); processResult(resultObj);
} }
if(failedSongs.length > 0) { if (failedSongs.length > 0) {
this.get('notify').alert({html: this.get('notStreamableHtml')(failedSongs)}); this.get('notify').alert({ html: this.get('notStreamableHtml')(failedSongs) });
} }
if(this.get('playQueuePointer') === -1){ if (this.get('playQueuePointer') === -1) {
if(this.get('firstVisit')){ if (this.get('firstVisit')) {
this.send('goToSong', 0); this.send('goToSong', 0);
} else { } else {
this.send('next'); this.send('next');
} }
} }
}, () => { }, () => {
this.get('notify').alert({html: this.get('urlNotFoundHtml')(URL)}); this.get('notify').alert({ html: this.get('urlNotFoundHtml')(URL) });
}); });
} }
@ -356,57 +359,57 @@ export default Component.extend(helperMixin, visualizerMixin, {
toggleIsShowingAddSoundCloudModal() { toggleIsShowingAddSoundCloudModal() {
this.toggleProperty('isShowingAddSoundCloudModal'); this.toggleProperty('isShowingAddSoundCloudModal');
}, },
slideTogglePlayerBottom(){ slideTogglePlayerBottom() {
let elem = this.$('#player-bottom'); let elem = this.$('#player-bottom');
elem.velocity(elem.is(':visible') ? 'slideUp' : 'slideDown', { duration: 300 }); elem.velocity(elem.is(':visible') ? 'slideUp' : 'slideDown', { duration: 300 });
this.changePlayerControl('playerBottomDisplayed', !this.get('playerBottomDisplayed')); this.changePlayerControl('playerBottomDisplayed', !this.get('playerBottomDisplayed'));
}, },
goToSong(index, playSong, scrollToSong){ goToSong(index, playSong, scrollToSong) {
let dancer = this.get('dancer'), playQueue = this.get('playQueue'); let dancer = this.get('dancer'), playQueue = this.get('playQueue');
if(dancer.audio) { if (dancer.audio) {
this.clearCurrentAudio(true); this.clearCurrentAudio(true);
} }
if(!isNone(playQueue[index])) { if (!isNone(playQueue[index])) {
let audio = new Audio(); let audio = new Audio();
audio.src = this.get('playQueue')[index].url; audio.src = this.get('playQueue')[index].url;
audio.crossOrigin = "anonymous"; audio.crossOrigin = "anonymous";
audio.oncanplay = ()=>{ audio.oncanplay = () => {
this.set('timeTotal', Math.floor(audio.duration)); this.set('timeTotal', Math.floor(audio.duration));
this.set('soundCloudFuckUps', 0); this.set('soundCloudFuckUps', 0);
}; };
audio.onerror = (event)=>{ audio.onerror = (event) => {
let playQueuePointer =this.get('playQueuePointer'), let playQueuePointer = this.get('playQueuePointer'),
song = this.get('playQueue')[playQueuePointer]; song = this.get('playQueue')[playQueuePointer];
if(this.get('soundCloudFuckUps') >= this.get('maxSoundCloudFuckUps')) { if (this.get('soundCloudFuckUps') >= this.get('maxSoundCloudFuckUps')) {
this.get('notify').alert({html: this.get('tooManySoundCloudFuckUps')}); this.get('notify').alert({ html: this.get('tooManySoundCloudFuckUps') });
this.send('play'); this.send('play');
this.set('soundCloudFuckUps', 0); this.set('soundCloudFuckUps', 0);
} else { } else {
if(song.local){ if (song.local) {
this.send('removeAudio', playQueuePointer); this.send('removeAudio', playQueuePointer);
} else { } else {
this.send('next', true); this.send('next', true);
} }
if(event.target.error.code === 2){ if (event.target.error.code === 2) {
this.get('notify').alert({html: this.get('failedToDecodeFileHtml')(song.fileName)}); this.get('notify').alert({ html: this.get('failedToDecodeFileHtml')(song.fileName) });
} else { } else {
this.get('notify').alert({html: this.get('failedToPlayFileHtml')(song.fileName)}); this.get('notify').alert({ html: this.get('failedToPlayFileHtml')(song.fileName) });
} }
this.set('usingBeatPreferences', false); this.set('usingBeatPreferences', false);
this.incrementProperty('soundCloudFuckUps'); this.incrementProperty('soundCloudFuckUps');
} }
}; };
audio.ontimeupdate = ()=>{ audio.ontimeupdate = () => {
this.set('timeElapsed', Math.floor(audio.currentTime)); this.set('timeElapsed', Math.floor(audio.currentTime));
}; };
audio.onended = ()=> { audio.onended = () => {
this.send('next'); this.send('next');
}; };
@ -416,29 +419,29 @@ export default Component.extend(helperMixin, visualizerMixin, {
this.loadSongBeatPreferences(); this.loadSongBeatPreferences();
if(playSong){ if (playSong) {
this.send('play'); this.send('play');
} }
if(scrollToSong){ if (scrollToSong) {
next(this, ()=>{ next(this, () => {
$('.track'+index).velocity('scroll', { container: $('#play-list-area'), duration: 200 }); $('.track' + index).velocity('scroll', { container: $('#play-list-area'), duration: 200 });
}); });
} }
} }
}, },
removeAudio(index){ removeAudio(index) {
this.get('playQueue').removeAt(index); this.get('playQueue').removeAt(index);
if(index === this.get('playQueuePointer')) { if (index === this.get('playQueuePointer')) {
this.send('goToSong', index, true, true); this.send('goToSong', index, true, true);
} }
}, },
playerAreaPlay(){ playerAreaPlay() {
if(isEmpty($('#player-controls:hover')) && this.get('playQueuePointer') !== -1 ){ if (isEmpty($('#player-controls:hover')) && this.get('playQueuePointer') !== -1) {
this.send('play'); this.send('play');
$('#play-notification').velocity({opacity: 1, scale: 1}, 0).velocity({opacity: 0, scale: 3}, 500); $('#play-notification').velocity({ opacity: 1, scale: 1 }, 0).velocity({ opacity: 0, scale: 3 }, 500);
} }
}, },
play(replayPause) { play(replayPause) {
@ -447,22 +450,22 @@ export default Component.extend(helperMixin, visualizerMixin, {
playing = this.get('playing'), playing = this.get('playing'),
lightsData = this.get('lightsData'); lightsData = this.get('lightsData');
if(playQueuePointer !== -1 ) { if (playQueuePointer !== -1) {
if (playing) { if (playing) {
dancer.pause(); dancer.pause();
let preMusicLightsDataCache = this.get('preMusicLightsDataCache'), let preMusicLightsDataCache = this.get('preMusicLightsDataCache'),
updateLight = (lightIndex)=> { updateLight = (lightIndex) => {
$.ajax(this.get('apiURL') + '/lights/' + lightIndex + '/state', { $.ajax(this.get('apiURL') + '/lights/' + lightIndex + '/state', {
data: JSON.stringify({ data: JSON.stringify({
'on': preMusicLightsDataCache[lightIndex].state.on, 'on': preMusicLightsDataCache[lightIndex].state.on,
'hue': preMusicLightsDataCache[lightIndex].state.hue, 'hue': preMusicLightsDataCache[lightIndex].state.hue,
'bri': preMusicLightsDataCache[lightIndex].state.bri 'bri': preMusicLightsDataCache[lightIndex].state.bri
}), }),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
}; };
for (let key in lightsData) { for (let key in lightsData) {
if (lightsData.hasOwnProperty(key)) { if (lightsData.hasOwnProperty(key)) {
@ -470,14 +473,14 @@ export default Component.extend(helperMixin, visualizerMixin, {
} }
} }
if(!replayPause){ if (!replayPause) {
this.set('timeElapsed', Math.floor(dancer.getTime())); this.set('timeElapsed', Math.floor(dancer.getTime()));
} }
} else { } else {
let timeTotal = this.get('timeTotal'); let timeTotal = this.get('timeTotal');
// replay song // replay song
if(this.get('timeElapsed') === timeTotal && timeTotal !== 0){ if (this.get('timeElapsed') === timeTotal && timeTotal !== 0) {
this.send('next', true); this.send('next', true);
return; return;
} }
@ -500,17 +503,17 @@ export default Component.extend(helperMixin, visualizerMixin, {
repeat = this.get('repeat'), repeat = this.get('repeat'),
shuffle = this.get('shuffle'); shuffle = this.get('shuffle');
if(repeat === 2){ // repeating one song takes precedence over shuffling if (repeat === 2) { // repeating one song takes precedence over shuffling
if(playQueuePointer === -1 && playQueue.length > 0) { if (playQueuePointer === -1 && playQueue.length > 0) {
nextSong = 0; nextSong = 0;
} else { } else {
nextSong = playQueuePointer; nextSong = playQueuePointer;
} }
} else if(shuffle){ // next shuffle song } else if (shuffle) { // next shuffle song
let shufflePlayed = this.get('shufflePlayed'); let shufflePlayed = this.get('shufflePlayed');
// played all the song in shuffle mode // played all the song in shuffle mode
if(shufflePlayed.length === playQueue.length){ if (shufflePlayed.length === playQueue.length) {
shufflePlayed.clear(); shufflePlayed.clear();
this.send('play', true); this.send('play', true);
return; return;
@ -519,11 +522,11 @@ export default Component.extend(helperMixin, visualizerMixin, {
// we're going to assume that the song URL is the id // we're going to assume that the song URL is the id
do { do {
nextSong = Math.floor(Math.random() * playQueue.length); nextSong = Math.floor(Math.random() * playQueue.length);
} while(shufflePlayed.includes(playQueue[nextSong].url)); } while (shufflePlayed.includes(playQueue[nextSong].url));
shufflePlayed.pushObject(playQueue[nextSong].url); shufflePlayed.pushObject(playQueue[nextSong].url);
} else if(nextSong > playQueue.length-1){ } else if (nextSong > playQueue.length - 1) {
if(repeat === 1 || repeatAll){ if (repeat === 1 || repeatAll) {
nextSong = nextSong % playQueue.length; nextSong = nextSong % playQueue.length;
} else { } else {
this.send('play', true); this.send('play', true);
@ -534,26 +537,26 @@ export default Component.extend(helperMixin, visualizerMixin, {
this.send('goToSong', nextSong, true, true); this.send('goToSong', nextSong, true, true);
}, },
previous() { previous() {
if(this.get('timeElapsed') > 5) { if (this.get('timeElapsed') > 5) {
this.send('seekChanged', 0); this.send('seekChanged', 0);
} else { } else {
let nextSong = this.get('playQueuePointer'), let nextSong = this.get('playQueuePointer'),
playQueue = this.get('playQueue'); playQueue = this.get('playQueue');
if(this.get('shuffle') && !isNone(playQueue[nextSong])) { // go to the previously shuffled song if (this.get('shuffle') && !isNone(playQueue[nextSong])) { // go to the previously shuffled song
let shufflePlayed = this.get('shufflePlayed'), let shufflePlayed = this.get('shufflePlayed'),
shuffledSongIndx = this.get('shufflePlayed').indexOf(playQueue[nextSong].url), shuffledSongIndx = this.get('shufflePlayed').indexOf(playQueue[nextSong].url),
i = 0; i = 0;
if(shufflePlayed.length > 0 && shuffledSongIndx !== -1){ // only if there was one if (shufflePlayed.length > 0 && shuffledSongIndx !== -1) { // only if there was one
nextSong = shuffledSongIndx - 1; nextSong = shuffledSongIndx - 1;
if(nextSong < 0){ if (nextSong < 0) {
nextSong = shufflePlayed.length - 1; nextSong = shufflePlayed.length - 1;
} }
playQueue.some(function(item){ // try to find the previous song id playQueue.some(function (item) { // try to find the previous song id
if(item.url === shufflePlayed[nextSong]){ if (item.url === shufflePlayed[nextSong]) {
nextSong = i; nextSong = i;
return true; return true;
} }
@ -565,7 +568,7 @@ export default Component.extend(helperMixin, visualizerMixin, {
} else { } else {
nextSong--; nextSong--;
if(nextSong < 0) { if (nextSong < 0) {
nextSong = playQueue.length - 1; nextSong = playQueue.length - 1;
} }
} }
@ -576,7 +579,7 @@ export default Component.extend(helperMixin, visualizerMixin, {
seekChanged(position) { seekChanged(position) {
let dancer = this.get('dancer'); let dancer = this.get('dancer');
if(dancer.audio){ if (dancer.audio) {
dancer.audio.currentTime = Math.floor(this.get('timeTotal') * position / 100); dancer.audio.currentTime = Math.floor(this.get('timeTotal') * position / 100);
} }
}, },
@ -601,20 +604,20 @@ export default Component.extend(helperMixin, visualizerMixin, {
hueRangeChanged(value) { hueRangeChanged(value) {
this.changePlayerControl('hueRange', value); this.changePlayerControl('hueRange', value);
}, },
playQueuePointerChanged(value){ playQueuePointerChanged(value) {
this.send('goToSong', value, false, true); this.send('goToSong', value, false, true);
}, },
clickSpeaker(){ clickSpeaker() {
this.simulateKick(1); this.simulateKick(1);
}, },
handleNewFiles(files){ handleNewFiles(files) {
let self = this, let self = this,
playQueue = this.get('playQueue'), playQueue = this.get('playQueue'),
updatePlayQueue = function(){ updatePlayQueue = function () {
let tags = ID3.getAllTags("local"), let tags = ID3.getAllTags("local"),
picture = null; picture = null;
if(tags.picture){ if (tags.picture) {
let base64String = ""; let base64String = "";
for (let i = 0; i < tags.picture.data.length; i++) { for (let i = 0; i < tags.picture.data.length; i++) {
base64String += String.fromCharCode(tags.picture.data[i]); base64String += String.fromCharCode(tags.picture.data[i]);
@ -634,7 +637,7 @@ export default Component.extend(helperMixin, visualizerMixin, {
ID3.clearAll(); ID3.clearAll();
if(self.get('playQueuePointer') === -1){ if (self.get('playQueuePointer') === -1) {
self.send('next'); self.send('next');
} }
}; };
@ -643,8 +646,8 @@ export default Component.extend(helperMixin, visualizerMixin, {
if (files.hasOwnProperty(key)) { if (files.hasOwnProperty(key)) {
let file = files[key]; let file = files[key];
if(file.type.startsWith('audio')) { if (file.type.startsWith('audio')) {
ID3.loadTags("local", updatePlayQueue.bind(file),{ ID3.loadTags("local", updatePlayQueue.bind(file), {
dataReader: new FileAPIReader(file), dataReader: new FileAPIReader(file),
tags: ['title', 'artist', 'album', 'track', 'picture'] tags: ['title', 'artist', 'album', 'track', 'picture']
}); });

View file

@ -4,9 +4,6 @@
.paper-icon { .paper-icon {
line-height: 0.8 !important; line-height: 0.8 !important;
} }
md-slider {
width: 50%;
}
} }
#hue-controls { #hue-controls {

View file

@ -244,10 +244,10 @@
margin: 0; margin: 0;
} }
.option-description { .option-description {
font-size: 18px; display: inline-flex;
white-space: nowrap; font-size: 20px;
overflow: hidden; justify-content: center;
text-overflow: ellipsis; flex-direction: column;
} }
button { button {
margin-top: 0; margin-top: 0;
@ -359,4 +359,7 @@
#sensitivity-settings .noUi-value-vertical { #sensitivity-settings .noUi-value-vertical {
display: none; display: none;
} }
.option-description {
height: 55px;
}
} }

View file

@ -48,3 +48,7 @@ md-switch.md-default-theme.md-checked .md-thumb {
md-list-item { md-list-item {
margin-bottom: 2vh; margin-bottom: 2vh;
} }
.md-thumb-text {
user-select: none;
}

View file

@ -31,8 +31,8 @@ export default Component.extend({
isAuthenticating: computed.notEmpty('bridgePingIntervalHandle'), isAuthenticating: computed.notEmpty('bridgePingIntervalHandle'),
// try to authenticate against the bridge here // try to authenticate against the bridge here
onBridgeIpChange: on('init', observer('bridgeIp', function(){ onBridgeIpChange: on('init', observer('bridgeIp', function () {
if(!this.get('trial') && !this.get('isAuthenticating')) { if (!this.get('trial') && !this.get('isAuthenticating')) {
this.setProperties({ this.setProperties({
bridgePingIntervalHandle: setInterval(this.pingBridgeUser.bind(this), this.get('bridgeUsernamePingIntervalTime')), bridgePingIntervalHandle: setInterval(this.pingBridgeUser.bind(this), this.get('bridgeUsernamePingIntervalTime')),
bridgeUserNamePingIntervalProgress: 0 bridgeUserNamePingIntervalProgress: 0
@ -41,8 +41,8 @@ export default Component.extend({
})), })),
didInsertElement() { didInsertElement() {
$(document).keypress((event)=>{ $(document).keypress((event) => {
if(!isNone(this.get('manualBridgeIp')) && event.which === 13) { if (!isNone(this.get('manualBridgeIp')) && event.which === 13) {
this.send('findBridgeByIp'); this.send('findBridgeByIp');
} }
}); });
@ -52,11 +52,11 @@ export default Component.extend({
init() { init() {
this._super(...arguments); this._super(...arguments);
if(this.get('bridgeIp') === null) { if (this.get('bridgeIp') === null) {
$.ajax('https://www.meethue.com/api/nupnp', { $.ajax('https://www.meethue.com/api/nupnp', {
timeout: 30000 timeout: 30000
}) })
.done((result, status)=> { .done((result, status) => {
let bridgeFindStatus = 'fail'; let bridgeFindStatus = 'fail';
if (status === 'success' && result.length === 1) { if (status === 'success' && result.length === 1) {
@ -77,9 +77,9 @@ export default Component.extend({
this.set('bridgeFindStatus', bridgeFindStatus); this.set('bridgeFindStatus', bridgeFindStatus);
}) })
.fail(()=>{ .fail(() => {
this.set('bridgeFindStatus', 'fail'); this.set('bridgeFindStatus', 'fail');
}); });
} }
}, },
@ -90,11 +90,11 @@ export default Component.extend({
if (bridgeIp !== null && bridgeUserNamePingIntervalProgress < 100) { if (bridgeIp !== null && bridgeUserNamePingIntervalProgress < 100) {
$.ajax('http://' + bridgeIp + '/api', { $.ajax('http://' + bridgeIp + '/api', {
data: JSON.stringify({"devicetype": "huegasm"}), data: JSON.stringify({ "devicetype": "huegasm" }),
contentType: 'application/json', contentType: 'application/json',
type: 'POST' type: 'POST'
}).done((result, status)=>{ }).done((result, status) => {
if(!this.isDestroyed){ if (!this.isDestroyed) {
this.set('bridgeAuthenticateReachedStatus', status); this.set('bridgeAuthenticateReachedStatus', status);
if (status === 'success' && !result[0].error) { if (status === 'success' && !result[0].error) {
@ -105,23 +105,24 @@ export default Component.extend({
} }
}); });
this.incrementProperty('bridgeUserNamePingIntervalProgress', this.get('bridgeUsernamePingIntervalTime')/bridgeUsernamePingMaxTime*100); this.incrementProperty('bridgeUserNamePingIntervalProgress', this.get('bridgeUsernamePingIntervalTime') / bridgeUsernamePingMaxTime * 100);
} else { } else {
this.clearBridgePingIntervalHandle(); this.clearBridgePingIntervalHandle();
} }
}, },
clearBridgePingIntervalHandle(){ clearBridgePingIntervalHandle() {
clearInterval(this.get('bridgePingIntervalHandle')); clearInterval(this.get('bridgePingIntervalHandle'));
this.set('bridgePingIntervalHandle', null); this.set('bridgePingIntervalHandle', null);
}, },
actions: { actions: {
retry(){ retry() {
this.onBridgeIpChange(); this.onBridgeIpChange();
}, },
chooseBridge(bridge){ chooseBridge(bridge) {
this.set('bridgeIp', bridge); this.set('bridgeIp', bridge);
this.get('storage').set('huegasm.bridgeIp', bridge);
}, },
findBridgeByIp() { findBridgeByIp() {
let manualBridgeIp = this.get('manualBridgeIp'); let manualBridgeIp = this.get('manualBridgeIp');
@ -134,12 +135,12 @@ export default Component.extend({
}); });
} else { } else {
$.ajax('http://' + manualBridgeIp + '/api', { $.ajax('http://' + manualBridgeIp + '/api', {
data: JSON.stringify({"devicetype": "huegasm"}), data: JSON.stringify({ "devicetype": "huegasm" }),
contentType: 'application/json', contentType: 'application/json',
type: 'POST' type: 'POST'
}).fail(() => { }).fail(() => {
this.set('manualBridgeIpNotFound', true); this.set('manualBridgeIpNotFound', true);
later(this, function(){ later(this, function () {
this.set('manualBridgeIpNotFound', false); this.set('manualBridgeIpNotFound', false);
}, 5000); }, 5000);
}).then(() => { }).then(() => {

View file

@ -1,17 +1,15 @@
<div class="title"><img src="assets/images/logo.png" alt="Huegasm"></div> <div class="title"><img src="assets/images/logo.png" alt="Huegasm"></div>
{{#unless bridgeUsername}} {{#unless bridgeUsername}}
{{#if bridgeIp}} {{#if bridgeIp}}
<img src="assets/images/pressButtonBridge.png" id="press-bridge-button-img"> <img src="assets/images/pressButtonBridge.png" id="press-bridge-button-img"> {{paper-progress-linear warn=true value=bridgeUserNamePingIntervalProgress}}
{{paper-progress-linear warn=true value=bridgeUserNamePingIntervalProgress}}
{{#if isAuthenticating}} {{#if isAuthenticating}}
<p> <p>
Your bridge IP is <b>{{bridgeIp}}</b> Your bridge IP is <b>{{bridgeIp}}</b>
<br> <br> Press the button on your bridge to authenticate this application.
Press the button on your bridge to authenticate this application.
</p> </p>
{{else}} {{else}}
<p>You failed to press the button in time. <a class="no-text-decoration" href="#" {{action 'retry'}}>RETRY</a></p> <p>You failed to press the button in time. <a class="no-text-decoration" href="#" {{action 'retry'}}>RETRY</a></p>
{{/if}} {{/if}}
{{else}} {{else}}
{{#unless bridgeFindStatus}} {{#unless bridgeFindStatus}}
@ -20,8 +18,7 @@
{{/unless}} {{/unless}}
{{#if bridgeFindMultiple}} {{#if bridgeFindMultiple}}
<p>Found multiple hue bridges. <br> <p>Huegasm found multiple hue bridges. <br> Please select the one you want to use for this application.</p>
Please select the one you want to use for this application.</p>
<div id="bridge-button-group"> <div id="bridge-button-group">
{{#each multipleBridgeIps as |bridge|}} {{#each multipleBridgeIps as |bridge|}}
@ -30,9 +27,7 @@
</div> </div>
{{else}} {{else}}
{{#if bridgeFindFail}} {{#if bridgeFindFail}}
<p>A hue bridge could not be automatically found on your network. <br> <p>A hue bridge could not be automatically found on your network. <br> Enter one manually? <br><br> ( or type <b>offline</b> to look around )
Enter one manually? <br><br>
( or type <b>offline</b> to look around )
</p> </p>
<span id="bridge-input"> <span id="bridge-input">
@ -41,7 +36,7 @@
<div> <div>
{{paper-button onClick=(action "findBridgeByIp") raised=true primary=true label="Find"}} {{paper-button onClick=(action "findBridgeByIp") raised=true primary=true label="Find"}}
</div> </div>
</span> </span>
{{#if manualBridgeIpNotFound}} {{#if manualBridgeIpNotFound}}
<p class="bg-danger"> <p class="bg-danger">

View file

@ -13,7 +13,7 @@ export default Component.extend({
ready: false, ready: false,
elementId: 'huegasm', elementId: 'huegasm',
init(){ init() {
this._super(...arguments); this._super(...arguments);
let storage = this.get('storage'); let storage = this.get('storage');
@ -27,15 +27,15 @@ export default Component.extend({
}, },
actions: { actions: {
toggleDimmer(){ toggleDimmer() {
this.sendAction('toggleDimmer'); this.sendAction('toggleDimmer');
}, },
toggleLightsIcons(){ toggleLightsIcons() {
this.sendAction('toggleLightsIcons'); this.sendAction('toggleLightsIcons');
}, },
isReady(){ isReady() {
this.set('ready', true); this.set('ready', true);
$('html, body').velocity('scroll'); $('html, body').velocity('scroll');
} }

View file

@ -1,5 +1,6 @@
{{#if bridgeUsername}} {{#if bridgeUsername}}
{{hue-controls bridgeIp=bridgeIp bridgeUsername=bridgeUsername trial=trial dimmerOn=dimmerOn lightsIconsOn=lightsIconsOn storage=storage toggleDimmer="toggleDimmer" toggleLightsIcons="toggleLightsIcons"}} {{hue-controls bridgeIp=bridgeIp bridgeUsername=bridgeUsername trial=trial dimmerOn=dimmerOn lightsIconsOn=lightsIconsOn
storage=storage toggleDimmer="toggleDimmer" toggleLightsIcons="toggleLightsIcons"}}
{{else}} {{else}}
{{#if ready}} {{#if ready}}
{{bridge-finder bridgeIp=bridgeIp bridgeUsername=bridgeUsername trial=trial storage=storage}} {{bridge-finder bridgeIp=bridgeIp bridgeUsername=bridgeUsername trial=trial storage=storage}}
@ -13,7 +14,7 @@
Your lights meet your music. Your lights meet your music.
</p> </p>
<p id="intro-paragraph"> <p id="intro-paragraph">
Huegasm is a free web application for managing and synchronizing your <a target="_blank" href="http://www2.meethue.com">Philips Hue lights</a> with the beat of your music. Huegasm is a free web application for managing and synchronizing your <a target="_blank" href="http://www2.meethue.com">Philips Hue lights</a> with the beat of your music.
</p> </p>
<div class="embed-container-wrapper"> <div class="embed-container-wrapper">
@ -21,7 +22,7 @@
<iframe width="560" height="315" src="https://www.youtube.com/embed/zi9J6Qg-MPw" frameborder="0" allowfullscreen></iframe> <iframe width="560" height="315" src="https://www.youtube.com/embed/zi9J6Qg-MPw" frameborder="0" allowfullscreen></iframe>
</div> </div>
</div> </div>
{{paper-button raised=true primary=true onClick=(action "isReady") class="go-button center-block" label="Go!"}} {{paper-button raised=true primary=true warn=true onClick=(action "isReady") class="go-button center-block" label="START"}}
</div> </div>
{{/if}} {{/if}}
{{/if}} {{/if}}

View file

@ -5,7 +5,7 @@ const {
observer, observer,
computed, computed,
on, on,
run: { later }, run: { later, once },
$ $
} = Ember; } = Ember;
@ -21,45 +21,45 @@ export default Component.extend({
// COLOR LOOP related stuff // COLOR LOOP related stuff
colorLoopOn: false, colorLoopOn: false,
lightsOnTxt: computed('lightsOn', function(){ lightsOnTxt: computed('lightsOn', function () {
return this.get('lightsOn') ? 'On' : 'Off'; return this.get('lightsOn') ? 'On' : 'Off';
}), }),
colorloopOnTxt: computed('colorLoopOn', function(){ colorloopOnTxt: computed('colorLoopOn', function () {
return this.get('colorLoopOn') ? 'On' : 'Off'; return this.get('colorLoopOn') ? 'On' : 'Off';
}), }),
// determines the average brightness of the hue system for the brightness slider // determines the average brightness of the hue system for the brightness slider
lightsBrightness: computed('lightsData', function(){ lightsBrightness: computed('lightsData', function () {
let lightsData = this.get('lightsData'), let lightsData = this.get('lightsData'),
activeLights = this.get('activeLights'), activeLights = this.get('activeLights'),
lightsBrightness = 0; lightsBrightness = 0;
activeLights.forEach(function(light){ activeLights.forEach(function (light) {
lightsBrightness += lightsData[light].state.bri; lightsBrightness += lightsData[light].state.bri;
}); });
return lightsBrightness/activeLights.length; return lightsBrightness / activeLights.length;
}), }),
brightnessControlDisabled: computed.not('lightsOn'), brightnessControlDisabled: computed.not('lightsOn'),
onColorLoopOnChange: observer('colorLoopOn', function(){ onColorLoopOnChange: observer('colorLoopOn', function () {
let lightsData = this.get('lightsData'), let lightsData = this.get('lightsData'),
activeLights = this.get('activeLights'), activeLights = this.get('activeLights'),
colorLoopsOn = this.get('colorLoopOn'), colorLoopsOn = this.get('colorLoopOn'),
effect = colorLoopsOn ? 'colorloop' : 'none'; effect = colorLoopsOn ? 'colorloop' : 'none';
let colorLoopsOnSystem = activeLights.some(function(light) { let colorLoopsOnSystem = activeLights.some(function (light) {
return lightsData[light].state.effect === 'colorloop'; return lightsData[light].state.effect === 'colorloop';
}); });
// if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state // if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state
if(colorLoopsOn !== colorLoopsOnSystem){ if (colorLoopsOn !== colorLoopsOnSystem) {
activeLights.forEach((light)=>{ activeLights.forEach((light) => {
if(this.get('lightsData')[light].state.effect !== effect) { if (this.get('lightsData')[light].state.effect !== effect) {
$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { $.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({'effect': effect }), data: JSON.stringify({ 'effect': effect }),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
@ -68,7 +68,7 @@ export default Component.extend({
} }
}), }),
rgbPreview: observer('rgb', function() { rgbPreview: observer('rgb', function () {
let rgb = this.get('rgb'), let rgb = this.get('rgb'),
xy = this.rgbToXy(rgb[0], rgb[1], rgb[2]); xy = this.rgbToXy(rgb[0], rgb[1], rgb[2]);
@ -76,7 +76,7 @@ export default Component.extend({
this.get('activeLights').forEach((light) => { this.get('activeLights').forEach((light) => {
$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { $.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({"xy": xy}), data: JSON.stringify({ "xy": xy }),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
@ -87,9 +87,9 @@ export default Component.extend({
}), }),
// determines whether the lights are on/off for the lights switch // determines whether the lights are on/off for the lights switch
lightsOnChange: on('init', observer('lightsData.@each.state.on', 'activeLights.[]', function(){ lightsOnChange: on('init', observer('lightsData.@each.state.on', 'activeLights.[]', function () {
if(!this.get('strobeOn')){ if (!this.get('strobeOn')) {
let lightsData = this.get('lightsData'), lightsOn = this.get('activeLights').some(function(light) { let lightsData = this.get('lightsData'), lightsOn = this.get('activeLights').some(function (light) {
return lightsData[light].state.on === true; return lightsData[light].state.on === true;
}); });
@ -97,20 +97,20 @@ export default Component.extend({
} }
})), })),
onLightsOnChange: observer('lightsOn', function(){ onLightsOnChange: observer('lightsOn', function () {
let lightsData = this.get('lightsData'), let lightsData = this.get('lightsData'),
activeLights = this.get('activeLights'), activeLights = this.get('activeLights'),
lightsOn = this.get('lightsOn'); lightsOn = this.get('lightsOn');
let lightsOnSystem = activeLights.some(function(light) { let lightsOnSystem = activeLights.some(function (light) {
return lightsData[light].state.on === true; return lightsData[light].state.on === true;
}); });
// if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state // if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state
if(lightsOn !== lightsOnSystem){ if (lightsOn !== lightsOnSystem) {
activeLights.forEach((light)=>{ activeLights.forEach((light) => {
$.ajax(this.get('apiURL') + '/lights/' + light + '/state', { $.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({"on": lightsOn}), data: JSON.stringify({ "on": lightsOn }),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
@ -118,32 +118,34 @@ export default Component.extend({
} }
}), }),
onBrightnessChanged: observer('lightsBrightness', function(){ onBrightnessChanged: observer('lightsBrightness', function () {
let lightsData = this.get('lightsData'), once(this, function () {
lightsBrightnessSystem = false, let lightsData = this.get('lightsData'),
lightsBrightness = this.get('lightsBrightness'), lightsBrightnessSystem = false,
activeLights = this.get('activeLights'); lightsBrightness = this.get('lightsBrightness'),
activeLights = this.get('activeLights');
activeLights.forEach(function(light){ activeLights.forEach(function (light) {
lightsBrightnessSystem += lightsData[light].state.bri; lightsBrightnessSystem += lightsData[light].state.bri;
});
lightsBrightnessSystem /= activeLights.length;
// if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state
if(lightsBrightness !== lightsBrightnessSystem){
activeLights.forEach((light)=>{
$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({"bri": lightsBrightness}),
contentType: 'application/json',
type: 'PUT'
});
}); });
}
lightsBrightnessSystem /= activeLights.length;
// if the internal lights state is different than the one from lightsData ( user manually toggled the switch ), send the request to change the bulbs state
if (lightsBrightness !== lightsBrightnessSystem) {
activeLights.forEach((light) => {
$.ajax(this.get('apiURL') + '/lights/' + light + '/state', {
data: JSON.stringify({ "bri": lightsBrightness }),
contentType: 'application/json',
type: 'PUT'
});
});
}
});
}), }),
// sync the current light settings to the newly added light // sync the current light settings to the newly added light
onaActiveLightsChange: observer('syncLight', function(){ onaActiveLightsChange: observer('syncLight', function () {
let options = { let options = {
on: this.get('lightsOn'), on: this.get('lightsOn'),
bri: this.get('lightsBrightness'), bri: this.get('lightsBrightness'),
@ -151,7 +153,7 @@ export default Component.extend({
}, rgb = this.get('rgb'), }, rgb = this.get('rgb'),
syncLight = this.get('syncLight'); syncLight = this.get('syncLight');
if(rgb[0] !== 255 && rgb[1] !== 255 && rgb[2] !== 255) { if (rgb[0] !== 255 && rgb[1] !== 255 && rgb[2] !== 255) {
options['xy'] = this.rgbToXy(rgb[0], rgb[1], rgb[2]); options['xy'] = this.rgbToXy(rgb[0], rgb[1], rgb[2]);
} }
@ -168,9 +170,8 @@ export default Component.extend({
strobeOn: false, strobeOn: false,
strobeOnInervalHandle: null, strobeOnInervalHandle: null,
strobeSat: 0,
preStrobeOnLightsDataCache: null, preStrobeOnLightsDataCache: null,
lastStrobeLight: 0, nextLightIdx: 0,
onStrobeOnChange: observer('strobeOn', function () { onStrobeOnChange: observer('strobeOn', function () {
let lightsData = this.get('lightsData'), let lightsData = this.get('lightsData'),
@ -178,7 +179,7 @@ export default Component.extend({
if (strobeOn) { if (strobeOn) {
this.set('preStrobeOnLightsDataCache', lightsData); this.set('preStrobeOnLightsDataCache', lightsData);
let stobeInitRequestData = {'sat': this.get('strobeSat'), 'transitiontime': 0}; let stobeInitRequestData = { transitiontime: 0 };
for (let key in lightsData) { for (let key in lightsData) {
if (lightsData.hasOwnProperty(key)) { if (lightsData.hasOwnProperty(key)) {
@ -196,11 +197,11 @@ export default Component.extend({
this.set('strobeOnInervalHandle', setInterval(this.strobeStep.bind(this), 500)); this.set('strobeOnInervalHandle', setInterval(this.strobeStep.bind(this), 500));
} else { // revert the light system to pre-strobe } else { // revert the light system to pre-strobe
let preStrobeOnLightsDataCache = this.get('preStrobeOnLightsDataCache'), updateLight = (lightIndex)=> { let preStrobeOnLightsDataCache = this.get('preStrobeOnLightsDataCache'), updateLight = (lightIndex) => {
$.ajax(this.get('apiURL') + '/lights/' + lightIndex + '/state', { $.ajax(this.get('apiURL') + '/lights/' + lightIndex + '/state', {
data: JSON.stringify({ data: JSON.stringify({
'on': preStrobeOnLightsDataCache[lightIndex].state.on, on: preStrobeOnLightsDataCache[lightIndex].state.on,
'sat': preStrobeOnLightsDataCache[lightIndex].state.sat sat: preStrobeOnLightsDataCache[lightIndex].state.sat
}), }),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
@ -221,51 +222,52 @@ export default Component.extend({
}), }),
strobeStep() { strobeStep() {
let lastStrobeLight = (this.get('lastStrobeLight') + 1) % (this.get('activeLights').length + 1), let nextLightIdx = this.get('nextLightIdx') % this.get('activeLights').length,
turnOnOptions = {'on': true, 'transitiontime': 0, 'alert': 'select'}; nextStrobeLight = this.get('activeLights')[nextLightIdx],
turnOnOptions = { on: true, transitiontime: 0, alert: 'select' };
// random light if in cololoop mode // random light if in cololoop mode
if(this.get('colorLoopOn')) { if (this.get('colorLoopOn')) {
turnOnOptions.hue = Math.floor(Math.random() * 65535); turnOnOptions.hue = Math.floor(Math.random() * 65535);
} }
$.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', { $.ajax(this.get('apiURL') + '/lights/' + nextStrobeLight + '/state', {
data: JSON.stringify(turnOnOptions), data: JSON.stringify(turnOnOptions),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
$.ajax(this.get('apiURL') + '/lights/' + lastStrobeLight + '/state', { $.ajax(this.get('apiURL') + '/lights/' + nextStrobeLight + '/state', {
data: JSON.stringify({'on': false, 'transitiontime': 0}), data: JSON.stringify({ 'on': false, 'transitiontime': 0 }),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
this.set('lastStrobeLight', lastStrobeLight); this.set('nextLightIdx', ++nextLightIdx);
}, },
strobeOnTxt: computed('strobeOn', function () { strobeOnTxt: computed('strobeOn', function () {
return this.get('strobeOn') ? 'On' : 'Off'; return this.get('strobeOn') ? 'On' : 'Off';
}), }),
dimmerOnClass: computed('dimmerOn', function(){ dimmerOnClass: computed('dimmerOn', function () {
return this.get('dimmerOn') ? 'dimmerOn' : null; return this.get('dimmerOn') ? 'dimmerOn' : null;
}), }),
actions: { actions: {
toggleDimmer(){ toggleDimmer() {
this.sendAction('toggleDimmer'); this.sendAction('toggleDimmer');
} }
}, },
// **************** STROBE LIGHT FINISH **************** // **************** STROBE LIGHT FINISH ****************
// http://www.developers.meethue.com/documentation/color-conversions-rgb-xy // http://www.developers.meethue.com/documentation/color-conversions-rgb-xy
rgbToXy(red, green, blue){ rgbToXy(red, green, blue) {
let X, Y, Z, x, y; let X, Y, Z, x, y;
// normalize // normalize
red = Number((red/255)); red = Number((red / 255));
green = Number((green/255)); green = Number((green / 255));
blue = Number((blue/255)); blue = Number((blue / 255));
// gamma correction // gamma correction
red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92); red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92);
@ -280,10 +282,10 @@ export default Component.extend({
x = X / (X + Y + Z); x = X / (X + Y + Z);
y = Y / (X + Y + Z); y = Y / (X + Y + Z);
return [x,y]; return [x, y];
}, },
xyToRgb(x, y){ xyToRgb(x, y) {
let r, g, b, X, Y = 1.0, Z; let r, g, b, X, Y = 1.0, Z;
X = (Y / y) * x; X = (Y / y) * x;

View file

@ -7,11 +7,11 @@
{{#paper-item}} {{#paper-item}}
{{paper-icon "brightness-4" class=dimmerOnClass}} {{paper-icon "brightness-4" class=dimmerOnClass}}
<p data-toggle="tooltip" data-placement="top auto" class="bootstrap-tooltip lights-control-tooltip" data-title="The brightness level of the selected lights">Brightness</p> <p data-toggle="tooltip" data-placement="top auto" class="layout flex-60 bootstrap-tooltip lights-control-tooltip" data-title="The brightness level of the selected lights">Brightness</p>
{{paper-slider flex=true min=1 max=254 value=lightsBrightness disabled=brightnessControlDisabled}} {{paper-slider class="flex" discrete=true step="10" min="1" max="254" value=lightsBrightness disabled=brightnessControlDisabled}}
{{/paper-item}} {{/paper-item}}
{{#paper-item elementId="color-row"}} {{#paper-item elementId="color-row" }}
{{paper-icon "color-lens" class=dimmerOnClass}} {{paper-icon "color-lens" class=dimmerOnClass}}
<p data-toggle="tooltip" data-placement="top auto" class="bootstrap-tooltip lights-control-tooltip" data-title="The color of the selected lights">Color</p> <p data-toggle="tooltip" data-placement="top auto" class="bootstrap-tooltip lights-control-tooltip" data-title="The color of the selected lights">Color</p>

View file

@ -12,17 +12,17 @@ const {
} = Ember; } = Ember;
export default Component.extend(helperMixin, visualizerMixin, { export default Component.extend(helperMixin, visualizerMixin, {
updatePageTitle: observer('playQueuePointer', function(){ updatePageTitle: observer('playQueuePointer', function () {
let title = 'Huegasm', let title = 'Huegasm',
playQueuePointer = this.get('playQueuePointer'), playQueuePointer = this.get('playQueuePointer'),
playQueue = this.get('playQueue'); playQueue = this.get('playQueue');
if(playQueuePointer !== -1){ if (playQueuePointer !== -1) {
let song = playQueue[playQueuePointer]; let song = playQueue[playQueuePointer];
if(song.title){ if (song.title) {
title = song.title; title = song.title;
if(song.artist){ if (song.artist) {
title += (' - ' + song.artist); title += (' - ' + song.artist);
} }
} else { } else {
@ -35,14 +35,14 @@ export default Component.extend(helperMixin, visualizerMixin, {
document.title = title; document.title = title;
}), }),
changePlayerControl(name, value, saveBeatPrefs){ changePlayerControl(name, value, saveBeatPrefs) {
this.set(name, value); this.set(name, value);
if(name === 'threshold'){ if (name === 'threshold') {
this.get('kick').set({threshold: value}); this.get('kick').set({ threshold: value });
} }
if(saveBeatPrefs && this.get('playQueuePointer') !== -1){ if (saveBeatPrefs && this.get('playQueuePointer') !== -1) {
this.saveSongBeatPreferences(); this.saveSongBeatPreferences();
} }
@ -51,11 +51,11 @@ export default Component.extend(helperMixin, visualizerMixin, {
saveSongBeatPreferences() { saveSongBeatPreferences() {
let song = this.get('playQueue')[this.get('playQueuePointer')]; let song = this.get('playQueue')[this.get('playQueuePointer')];
if(song) { if (song) {
let title = isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title, let title = isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title,
songBeatPreferences = this.get('songBeatPreferences'); songBeatPreferences = this.get('songBeatPreferences');
songBeatPreferences[title] = {threshold: this.get('threshold')}; songBeatPreferences[title] = { threshold: this.get('threshold') };
this.set('usingBeatPreferences', true); this.set('usingBeatPreferences', true);
this.get('storage').set('huegasm.songBeatPreferences', songBeatPreferences); this.get('storage').set('huegasm.songBeatPreferences', songBeatPreferences);
@ -70,12 +70,12 @@ export default Component.extend(helperMixin, visualizerMixin, {
oldBeatPrefCache = this.get('oldBeatPrefCache'), oldBeatPrefCache = this.get('oldBeatPrefCache'),
newOldBeatPrefCache = null; newOldBeatPrefCache = null;
if(!isNone(preference)) { // load existing beat prefs if (!isNone(preference)) { // load existing beat prefs
newOldBeatPrefCache = {threshold: this.get('threshold')}; newOldBeatPrefCache = { threshold: this.get('threshold') };
this.changePlayerControl('threshold', preference.threshold); this.changePlayerControl('threshold', preference.threshold);
this.set('usingBeatPreferences', true); this.set('usingBeatPreferences', true);
} else if(!isNone(oldBeatPrefCache)) { // revert to using beat prefs before the remembered song } else if (!isNone(oldBeatPrefCache)) { // revert to using beat prefs before the remembered song
this.changePlayerControl('threshold', oldBeatPrefCache.threshold); this.changePlayerControl('threshold', oldBeatPrefCache.threshold);
this.set('usingBeatPreferences', false); this.set('usingBeatPreferences', false);
} }
@ -86,11 +86,11 @@ export default Component.extend(helperMixin, visualizerMixin, {
clearCurrentAudio(resetPointer) { clearCurrentAudio(resetPointer) {
let dancer = this.get('dancer'); let dancer = this.get('dancer');
if(dancer.audio.pause) { if (dancer.audio.pause) {
dancer.pause(); dancer.pause();
} }
if(resetPointer){ if (resetPointer) {
this.set('playQueuePointer', -1); this.set('playQueuePointer', -1);
} }
@ -110,31 +110,31 @@ export default Component.extend(helperMixin, visualizerMixin, {
} }
}, },
dragLeave(){ dragLeave() {
// need to delay the dragLeave notification to avoid flickering ( hovering over some page elements causes this event to be sent ) // need to delay the dragLeave notification to avoid flickering ( hovering over some page elements causes this event to be sent )
this.set('dragLeaveTimeoutHandle', setTimeout(()=>{ this.set('dragging', false); }, 500)); this.set('dragLeaveTimeoutHandle', setTimeout(() => { this.set('dragging', false); }, 500));
}, },
simulateKick(/*mag, ratioKickMag*/) { simulateKick(/*mag, ratioKickMag*/) {
let activeLights = this.get('activeLights'), let activeLights = this.get('activeLights'),
lightsData = this.get('lightsData'), lightsData = this.get('lightsData'),
color = null, color = null,
transitiontime = this.get('flashingTransitions'), transitiontime = this.get('flashingTransitions'),
stimulateLight = (light, brightness, hue) => { stimulateLight = (light, brightness, hue) => {
let options = {'bri': brightness}; let options = { 'bri': brightness };
if(transitiontime) { if (transitiontime) {
options['transitiontime'] = 0; options['transitiontime'] = 0;
} else { } else {
options['transitiontime'] = 1; options['transitiontime'] = 1;
} }
if(!isNone(hue)) { if (!isNone(hue)) {
options.hue = hue; options.hue = hue;
} }
if(lightsData[light].state.on === false){ if (lightsData[light].state.on === false) {
options.on = true; options.on = true;
} }
@ -146,7 +146,7 @@ export default Component.extend(helperMixin, visualizerMixin, {
}, },
timeToBriOff = 100; timeToBriOff = 100;
if(activeLights.length > 0){ if (activeLights.length > 0) {
let lastLightBopIndex = this.get('lastLightBopIndex'), let lastLightBopIndex = this.get('lastLightBopIndex'),
lightBopIndex, lightBopIndex,
brightnessRange = this.get('brightnessRange'), brightnessRange = this.get('brightnessRange'),
@ -155,8 +155,8 @@ export default Component.extend(helperMixin, visualizerMixin, {
lightBopIndex = Math.floor(Math.random() * activeLights.length); lightBopIndex = Math.floor(Math.random() * activeLights.length);
// let's try not to select the same light twice in a row // let's try not to select the same light twice in a row
if(activeLights.length > 1) { if (activeLights.length > 1) {
while(lightBopIndex === lastLightBopIndex) { while (lightBopIndex === lastLightBopIndex) {
lightBopIndex = Math.floor(Math.random() * activeLights.length); lightBopIndex = Math.floor(Math.random() * activeLights.length);
} }
} }
@ -164,13 +164,13 @@ export default Component.extend(helperMixin, visualizerMixin, {
light = activeLights[lightBopIndex]; light = activeLights[lightBopIndex];
this.set('lastLightBopIndex', lightBopIndex); this.set('lastLightBopIndex', lightBopIndex);
if(!this.get('colorloopMode')) { if (!this.get('colorloopMode')) {
let hueRange = this.get('hueRange'); let hueRange = this.get('hueRange');
color = Math.floor(Math.random()*(hueRange[1] - hueRange[0] + 1)+hueRange[0]); color = Math.floor(Math.random() * (hueRange[1] - hueRange[0] + 1) + hueRange[0]);
} }
if(transitiontime){ if (transitiontime) {
timeToBriOff = 80; timeToBriOff = 80;
} }
@ -179,13 +179,13 @@ export default Component.extend(helperMixin, visualizerMixin, {
} }
this.set('paused', true); this.set('paused', true);
later(this, function(){ later(this, function () {
this.set('paused', false); this.set('paused', false);
}, 150); }, 150);
//work the music beat area - simulate the speaker vibration by running a CSS animation on it //work the music beat area - simulate the speaker vibration by running a CSS animation on it
$('#beat-speaker-center-outer').velocity({blur: 3}, 100).velocity({blur: 0}, 100); $('#beat-speaker-center-outer').velocity({ blur: 3 }, 100).velocity({ blur: 0 }, 100);
$('#beat-speaker-center-inner').velocity({scale: 1.05}, 100).velocity({scale: 1}, 100); $('#beat-speaker-center-inner').velocity({ scale: 1.05 }, 100).velocity({ scale: 1 }, 100);
}, },
init() { init() {
@ -213,11 +213,11 @@ export default Component.extend(helperMixin, visualizerMixin, {
kick: kick kick: kick
}); });
['volume', 'shuffle', 'repeat', 'volumeMuted', 'threshold', 'playerBottomDisplayed', 'songBeatPreferences', 'firstVisit', 'currentVisName', 'playQueue', 'playQueuePointer', 'flashingTransitions', 'colorloopMode', 'hueRange', 'brightnessRange'].forEach((item)=>{ ['volume', 'shuffle', 'repeat', 'volumeMuted', 'threshold', 'playerBottomDisplayed', 'songBeatPreferences', 'firstVisit', 'currentVisName', 'playQueue', 'playQueuePointer', 'flashingTransitions', 'colorloopMode', 'hueRange', 'brightnessRange'].forEach((item) => {
if (!isNone(storage.get('huegasm.' + item))) { if (!isNone(storage.get('huegasm.' + item))) {
let itemVal = storage.get('huegasm.' + item); let itemVal = storage.get('huegasm.' + item);
if(isNone(this.actions[item+'Changed'])){ if (isNone(this.actions[item + 'Changed'])) {
this.set(item, itemVal); this.set(item, itemVal);
} else { } else {
this.send(item + 'Changed', itemVal); this.send(item + 'Changed', itemVal);
@ -238,37 +238,37 @@ export default Component.extend(helperMixin, visualizerMixin, {
let self = this; let self = this;
// file input code // file input code
$('#file-input').on('change', function() { $('#file-input').on('change', function () {
let files = this.files; let files = this.files;
self.send('handleNewFiles', files); self.send('handleNewFiles', files);
this.value = null; // reset in case upload the second file again this.value = null; // reset in case upload the second file again
}); });
$(document).on('click', '.alert', (event)=>{ $(document).on('click', '.alert', (event) => {
$(event.target).addClass('removed'); $(event.target).addClass('removed');
}); });
// prevent space/text selection when the user repeatedly clicks on the center // prevent space/text selection when the user repeatedly clicks on the center
$('#beat-container').on('mousedown', '#beat-speaker-center-inner', function(event) { $('#beat-container').on('mousedown', '#beat-speaker-center-inner', function (event) {
event.preventDefault(); event.preventDefault();
}); });
$(document).keypress((event) => { $(document).keypress((event) => {
if(event.which === 32 && event.target.type !== 'text'){ if (event.which === 32 && event.target.type !== 'text') {
this.send('play'); this.send('play');
} }
}); });
this.$().on('drop', '#play-list-area', (event)=>{ this.$().on('drop', '#play-list-area', (event) => {
this.send('dropFiles', event.dataTransfer.files); this.send('dropFiles', event.dataTransfer.files);
}); });
// control the volume by scrolling up/down // control the volume by scrolling up/down
$('#player-area').on('mousewheel', (event)=>{ $('#player-area').on('mousewheel', (event) => {
if(this.get('playQueueNotEmpty')) { if (this.get('playQueueNotEmpty')) {
let scrollSize = 5; let scrollSize = 5;
if(event.deltaY < 0) { if (event.deltaY < 0) {
scrollSize *= -1; scrollSize *= -1;
} }
let newVolume = this.get('volume') + scrollSize; let newVolume = this.get('volume') + scrollSize;
@ -278,8 +278,8 @@ export default Component.extend(helperMixin, visualizerMixin, {
} }
}); });
// demo tracks // demo tracks
if(this.get('firstVisit')){ if (this.get('firstVisit')) {
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/candyland-speechless-feat-rkcb'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/candyland-speechless-feat-rkcb');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/dillistone/dillistone-lili-n-rude'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/dillistone/dillistone-lili-n-rude');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/vallis-alps-young-feki-remix'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/vallis-alps-young-feki-remix');
@ -291,93 +291,94 @@ export default Component.extend(helperMixin, visualizerMixin, {
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/90-pounds-of-pete-waited-too-long-feat-devon-baldwin'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/90-pounds-of-pete-waited-too-long-feat-devon-baldwin');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/draper-eyes-open'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/draper-eyes-open');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/itspapaya/sunny'); this.send('handleNewSoundCloudURL', 'https://soundcloud.com/itspapaya/sunny');
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/stonesthrow/nxworries-anderson-paak-knxwledge-suede');
this.get('storage').set('huegasm.firstVisit', false); this.get('storage').set('huegasm.firstVisit', false);
this.sendAction(); this.sendAction();
} }
if(!this.get('playerBottomDisplayed')) { if (!this.get('playerBottomDisplayed')) {
$('#player-bottom').hide(); $('#player-bottom').hide();
} }
}, },
actions: { actions: {
clearPlaylist(){ clearPlaylist() {
this.get('playQueue').clear(); this.get('playQueue').clear();
}, },
setVisName(name){ setVisName(name) {
this.set('currentVisName', name); this.set('currentVisName', name);
}, },
hideTooltip(){ hideTooltip() {
$('.bootstrap-tooltip').tooltip('hide'); $('.bootstrap-tooltip').tooltip('hide');
}, },
gotoSCURL(URL){ gotoSCURL(URL) {
// need to pause the music since soundcloud is going to start playing this song anyways // need to pause the music since soundcloud is going to start playing this song anyways
if(this.get('playing')){ if (this.get('playing')) {
this.send('play'); this.send('play');
} }
this.send('gotoURL', URL); this.send('gotoURL', URL);
}, },
gotoURL(URL){ gotoURL(URL) {
$('.tooltip').remove(); $('.tooltip').remove();
window.open(URL, '_blank'); window.open(URL, '_blank');
}, },
handleNewSoundCloudURL(URL){ handleNewSoundCloudURL(URL) {
if(URL) { if (URL) {
SC.resolve(URL).then((resultObj)=>{ SC.resolve(URL).then((resultObj) => {
let processResult = (result)=>{ let processResult = (result) => {
if(result.kind === 'user'){ if (result.kind === 'user') {
this.get('notify').alert({html: this.get('scUserNotSupportedHtml')}); this.get('notify').alert({ html: this.get('scUserNotSupportedHtml') });
} else if(result.kind === 'track') { } else if (result.kind === 'track') {
if(result.streamable === true){ if (result.streamable === true) {
let picture = null; let picture = null;
if(result.artwork_url){ if (result.artwork_url) {
picture = result.artwork_url.replace('large', 't67x67'); picture = result.artwork_url.replace('large', 't67x67');
} else if(result.user.avatar_url){ } else if (result.user.avatar_url) {
picture = result.user.avatar_url; picture = result.user.avatar_url;
} }
$.get(picture) $.get(picture)
.done(()=>{ .done(() => {
this.get('playQueue').pushObject({url: result.stream_url + '?client_id=' + this.get('SC_CLIENT_ID'), fileName: result.title + ' - ' + result.user.username, artist: result.user.username, scUrl: result.permalink_url, title: result.title, picture: picture }); this.get('playQueue').pushObject({ url: result.stream_url + '?client_id=' + this.get('SC_CLIENT_ID'), fileName: result.title + ' - ' + result.user.username, artist: result.user.username, scUrl: result.permalink_url, title: result.title, picture: picture });
}).fail(()=>{ // no picture }).fail(() => { // no picture
this.get('playQueue').pushObject({url: result.stream_url + '?client_id=' + this.get('SC_CLIENT_ID'), fileName: result.title + ' - ' + result.user.username, artist: result.user.username, scUrl: result.permalink_url, title: result.title }); this.get('playQueue').pushObject({ url: result.stream_url + '?client_id=' + this.get('SC_CLIENT_ID'), fileName: result.title + ' - ' + result.user.username, artist: result.user.username, scUrl: result.permalink_url, title: result.title });
}); });
} else { } else {
failedSongs.push(result.title); failedSongs.push(result.title);
}
} else if(result.kind === 'playlist'){
if(result.streamable === true){
result.tracks.forEach(processResult);
} else {
failedSongs.push(result.title);
}
} }
}, } else if (result.kind === 'playlist') {
if (result.streamable === true) {
result.tracks.forEach(processResult);
} else {
failedSongs.push(result.title);
}
}
},
failedSongs = []; failedSongs = [];
if(resultObj instanceof Array){ if (resultObj instanceof Array) {
resultObj.forEach(processResult); resultObj.forEach(processResult);
} else { } else {
processResult(resultObj); processResult(resultObj);
} }
if(failedSongs.length > 0) { if (failedSongs.length > 0) {
this.get('notify').alert({html: this.get('notStreamableHtml')(failedSongs)}); this.get('notify').alert({ html: this.get('notStreamableHtml')(failedSongs) });
} }
if(this.get('playQueuePointer') === -1){ if (this.get('playQueuePointer') === -1) {
if(this.get('firstVisit')){ if (this.get('firstVisit')) {
this.send('goToSong', 0); this.send('goToSong', 0);
} else { } else {
this.send('next'); this.send('next');
} }
} }
}, () => { }, () => {
this.get('notify').alert({html: this.get('urlNotFoundHtml')(URL)}); this.get('notify').alert({ html: this.get('urlNotFoundHtml')(URL) });
}); });
} }
@ -386,57 +387,57 @@ export default Component.extend(helperMixin, visualizerMixin, {
toggleIsShowingAddSoundCloudModal() { toggleIsShowingAddSoundCloudModal() {
this.toggleProperty('isShowingAddSoundCloudModal'); this.toggleProperty('isShowingAddSoundCloudModal');
}, },
slideTogglePlayerBottom(){ slideTogglePlayerBottom() {
let elem = this.$('#player-bottom'); let elem = this.$('#player-bottom');
elem.velocity(elem.is(':visible') ? 'slideUp' : 'slideDown', { duration: 300 }); elem.velocity(elem.is(':visible') ? 'slideUp' : 'slideDown', { duration: 300 });
this.changePlayerControl('playerBottomDisplayed', !this.get('playerBottomDisplayed')); this.changePlayerControl('playerBottomDisplayed', !this.get('playerBottomDisplayed'));
}, },
goToSong(index, playSong, scrollToSong){ goToSong(index, playSong, scrollToSong) {
let dancer = this.get('dancer'), playQueue = this.get('playQueue'); let dancer = this.get('dancer'), playQueue = this.get('playQueue');
if(dancer.audio) { if (dancer.audio) {
this.clearCurrentAudio(true); this.clearCurrentAudio(true);
} }
if(!isNone(playQueue[index])) { if (!isNone(playQueue[index])) {
let audio = new Audio(); let audio = new Audio();
audio.src = this.get('playQueue')[index].url; audio.src = this.get('playQueue')[index].url;
audio.crossOrigin = "anonymous"; audio.crossOrigin = "anonymous";
audio.oncanplay = ()=>{ audio.oncanplay = () => {
this.set('timeTotal', Math.floor(audio.duration)); this.set('timeTotal', Math.floor(audio.duration));
this.set('soundCloudFuckUps', 0); this.set('soundCloudFuckUps', 0);
}; };
audio.onerror = (event)=>{ audio.onerror = (event) => {
let playQueuePointer =this.get('playQueuePointer'), let playQueuePointer = this.get('playQueuePointer'),
song = this.get('playQueue')[playQueuePointer]; song = this.get('playQueue')[playQueuePointer];
if(this.get('soundCloudFuckUps') >= this.get('maxSoundCloudFuckUps')) { if (this.get('soundCloudFuckUps') >= this.get('maxSoundCloudFuckUps')) {
this.get('notify').alert({html: this.get('tooManySoundCloudFuckUps')}); this.get('notify').alert({ html: this.get('tooManySoundCloudFuckUps') });
this.send('play'); this.send('play');
this.set('soundCloudFuckUps', 0); this.set('soundCloudFuckUps', 0);
} else { } else {
if(song.local){ if (song.local) {
this.send('removeAudio', playQueuePointer); this.send('removeAudio', playQueuePointer);
} else { } else {
this.send('next', true); this.send('next', true);
} }
if(event.target.error.code === 2){ if (event.target.error.code === 2) {
this.get('notify').alert({html: this.get('failedToDecodeFileHtml')(song.fileName)}); this.get('notify').alert({ html: this.get('failedToDecodeFileHtml')(song.fileName) });
} else { } else {
this.get('notify').alert({html: this.get('failedToPlayFileHtml')(song.fileName)}); this.get('notify').alert({ html: this.get('failedToPlayFileHtml')(song.fileName) });
} }
this.set('usingBeatPreferences', false); this.set('usingBeatPreferences', false);
this.incrementProperty('soundCloudFuckUps'); this.incrementProperty('soundCloudFuckUps');
} }
}; };
audio.ontimeupdate = ()=>{ audio.ontimeupdate = () => {
this.set('timeElapsed', Math.floor(audio.currentTime)); this.set('timeElapsed', Math.floor(audio.currentTime));
}; };
audio.onended = ()=> { audio.onended = () => {
this.send('next'); this.send('next');
}; };
@ -446,33 +447,33 @@ export default Component.extend(helperMixin, visualizerMixin, {
this.loadSongBeatPreferences(); this.loadSongBeatPreferences();
if(playSong){ if (playSong) {
this.send('play'); this.send('play');
} }
if(scrollToSong){ if (scrollToSong) {
// this is just a bad workaround to make sure that the track has been rendered to the playlist // this is just a bad workaround to make sure that the track has been rendered to the playlist
next(this, ()=>{ next(this, () => {
$('.track'+index).velocity('scroll', { container: $('#play-list-area'), duration: 200 }); $('.track' + index).velocity('scroll', { container: $('#play-list-area'), duration: 200 });
}); });
} }
} }
}, },
removeAudio(index){ removeAudio(index) {
this.get('playQueue').removeAt(index); this.get('playQueue').removeAt(index);
// need to manually remove the tooltip // need to manually remove the tooltip
$('body .tooltip').remove(); $('body .tooltip').remove();
if(index === this.get('playQueuePointer')) { if (index === this.get('playQueuePointer')) {
this.send('goToSong', index, true, true); this.send('goToSong', index, true, true);
} }
}, },
playerAreaPlay(){ playerAreaPlay() {
if(isEmpty($('#player-controls:hover')) && this.get('playQueuePointer') !== -1 ){ if (isEmpty($('#player-controls:hover')) && this.get('playQueuePointer') !== -1) {
this.send('play'); this.send('play');
$('#play-notification').velocity({opacity: 0.8, scale: 1}, 0).velocity({opacity: 0, scale: 3}, 500); $('#play-notification').velocity({ opacity: 0.8, scale: 1 }, 0).velocity({ opacity: 0, scale: 3 }, 500);
} }
}, },
play(replayPause) { play(replayPause) {
@ -481,22 +482,22 @@ export default Component.extend(helperMixin, visualizerMixin, {
playing = this.get('playing'), playing = this.get('playing'),
lightsData = this.get('lightsData'); lightsData = this.get('lightsData');
if(playQueuePointer !== -1 ) { if (playQueuePointer !== -1) {
if (playing) { if (playing) {
dancer.pause(); dancer.pause();
let preMusicLightsDataCache = this.get('preMusicLightsDataCache'), let preMusicLightsDataCache = this.get('preMusicLightsDataCache'),
updateLight = (lightIndex)=> { updateLight = (lightIndex) => {
$.ajax(this.get('apiURL') + '/lights/' + lightIndex + '/state', { $.ajax(this.get('apiURL') + '/lights/' + lightIndex + '/state', {
data: JSON.stringify({ data: JSON.stringify({
'on': preMusicLightsDataCache[lightIndex].state.on, 'on': preMusicLightsDataCache[lightIndex].state.on,
'hue': preMusicLightsDataCache[lightIndex].state.hue, 'hue': preMusicLightsDataCache[lightIndex].state.hue,
'bri': preMusicLightsDataCache[lightIndex].state.bri 'bri': preMusicLightsDataCache[lightIndex].state.bri
}), }),
contentType: 'application/json', contentType: 'application/json',
type: 'PUT' type: 'PUT'
}); });
}; };
for (let key in lightsData) { for (let key in lightsData) {
if (lightsData.hasOwnProperty(key)) { if (lightsData.hasOwnProperty(key)) {
@ -504,20 +505,20 @@ export default Component.extend(helperMixin, visualizerMixin, {
} }
} }
if(!replayPause){ if (!replayPause) {
this.set('timeElapsed', Math.floor(dancer.getTime())); this.set('timeElapsed', Math.floor(dancer.getTime()));
} }
} else { } else {
let timeTotal = this.get('timeTotal'); let timeTotal = this.get('timeTotal');
if(this.get('volumeMuted')) { if (this.get('volumeMuted')) {
dancer.setVolume(0); dancer.setVolume(0);
} else { } else {
dancer.setVolume(this.get('volume')/100); dancer.setVolume(this.get('volume') / 100);
} }
// replay song // replay song
if(this.get('timeElapsed') === timeTotal && timeTotal !== 0){ if (this.get('timeElapsed') === timeTotal && timeTotal !== 0) {
this.send('next', true); this.send('next', true);
return; return;
} }
@ -535,11 +536,11 @@ export default Component.extend(helperMixin, visualizerMixin, {
}, },
volumeChanged(value) { volumeChanged(value) {
this.changePlayerControl('volume', value); this.changePlayerControl('volume', value);
if(this.get('playing')) { if (this.get('playing')) {
this.get('dancer').setVolume(value/100); this.get('dancer').setVolume(value / 100);
} }
if(this.get('volume') > 0 && this.get('volumeMuted')){ if (this.get('volume') > 0 && this.get('volumeMuted')) {
this.changePlayerControl('volumeMuted', false); this.changePlayerControl('volumeMuted', false);
} }
}, },
@ -550,17 +551,17 @@ export default Component.extend(helperMixin, visualizerMixin, {
repeat = this.get('repeat'), repeat = this.get('repeat'),
shuffle = this.get('shuffle'); shuffle = this.get('shuffle');
if(repeat === 2){ // repeating one song takes precedence over shuffling if (repeat === 2) { // repeating one song takes precedence over shuffling
if(playQueuePointer === -1 && playQueue.length > 0) { if (playQueuePointer === -1 && playQueue.length > 0) {
nextSong = 0; nextSong = 0;
} else { } else {
nextSong = playQueuePointer; nextSong = playQueuePointer;
} }
} else if(shuffle){ // next shuffle song } else if (shuffle) { // next shuffle song
let shufflePlayed = this.get('shufflePlayed'); let shufflePlayed = this.get('shufflePlayed');
// played all the song in shuffle mode // played all the song in shuffle mode
if(shufflePlayed.length === playQueue.length){ if (shufflePlayed.length === playQueue.length) {
shufflePlayed.clear(); shufflePlayed.clear();
this.send('play', true); this.send('play', true);
return; return;
@ -569,11 +570,11 @@ export default Component.extend(helperMixin, visualizerMixin, {
// we're going to assume that the song URL is the id // we're going to assume that the song URL is the id
do { do {
nextSong = Math.floor(Math.random() * playQueue.length); nextSong = Math.floor(Math.random() * playQueue.length);
} while(shufflePlayed.includes(playQueue[nextSong].url)); } while (shufflePlayed.includes(playQueue[nextSong].url));
shufflePlayed.pushObject(playQueue[nextSong].url); shufflePlayed.pushObject(playQueue[nextSong].url);
} else if(nextSong > playQueue.length-1){ } else if (nextSong > playQueue.length - 1) {
if(repeat === 1 || repeatAll){ if (repeat === 1 || repeatAll) {
nextSong = nextSong % playQueue.length; nextSong = nextSong % playQueue.length;
} else { } else {
this.send('play', true); this.send('play', true);
@ -584,26 +585,26 @@ export default Component.extend(helperMixin, visualizerMixin, {
this.send('goToSong', nextSong, true, true); this.send('goToSong', nextSong, true, true);
}, },
previous() { previous() {
if(this.get('timeElapsed') > 5) { if (this.get('timeElapsed') > 5) {
this.send('seekChanged', 0); this.send('seekChanged', 0);
} else { } else {
let nextSong = this.get('playQueuePointer'), let nextSong = this.get('playQueuePointer'),
playQueue = this.get('playQueue'); playQueue = this.get('playQueue');
if(this.get('shuffle') && !isNone(playQueue[nextSong])) { // go to the previously shuffled song if (this.get('shuffle') && !isNone(playQueue[nextSong])) { // go to the previously shuffled song
let shufflePlayed = this.get('shufflePlayed'), let shufflePlayed = this.get('shufflePlayed'),
shuffledSongIndx = this.get('shufflePlayed').indexOf(playQueue[nextSong].url), shuffledSongIndx = this.get('shufflePlayed').indexOf(playQueue[nextSong].url),
i = 0; i = 0;
if(shufflePlayed.length > 0 && shuffledSongIndx !== -1){ // only if there was one if (shufflePlayed.length > 0 && shuffledSongIndx !== -1) { // only if there was one
nextSong = shuffledSongIndx - 1; nextSong = shuffledSongIndx - 1;
if(nextSong < 0){ if (nextSong < 0) {
nextSong = shufflePlayed.length - 1; nextSong = shufflePlayed.length - 1;
} }
playQueue.some(function(item){ // try to find the previous song id playQueue.some(function (item) { // try to find the previous song id
if(item.url === shufflePlayed[nextSong]){ if (item.url === shufflePlayed[nextSong]) {
nextSong = i; nextSong = i;
return true; return true;
} }
@ -615,7 +616,7 @@ export default Component.extend(helperMixin, visualizerMixin, {
} else { } else {
nextSong--; nextSong--;
if(nextSong < 0) { if (nextSong < 0) {
nextSong = playQueue.length - 1; nextSong = playQueue.length - 1;
} }
} }
@ -626,7 +627,7 @@ export default Component.extend(helperMixin, visualizerMixin, {
seekChanged(position) { seekChanged(position) {
let dancer = this.get('dancer'); let dancer = this.get('dancer');
if(dancer.audio){ if (dancer.audio) {
dancer.audio.currentTime = Math.floor(this.get('timeTotal') * position / 100); dancer.audio.currentTime = Math.floor(this.get('timeTotal') * position / 100);
} }
}, },
@ -636,11 +637,11 @@ export default Component.extend(helperMixin, visualizerMixin, {
this.changePlayerControl('volumeMuted', volumeMuted); this.changePlayerControl('volumeMuted', volumeMuted);
if(this.get('playing')){ if (this.get('playing')) {
if(volumeMuted){ if (volumeMuted) {
dancer.setVolume(0); dancer.setVolume(0);
} else { } else {
dancer.setVolume(this.get('volume')/100); dancer.setVolume(this.get('volume') / 100);
} }
} }
}, },
@ -665,33 +666,33 @@ export default Component.extend(helperMixin, visualizerMixin, {
hueRangeChanged(value) { hueRangeChanged(value) {
this.changePlayerControl('hueRange', value); this.changePlayerControl('hueRange', value);
}, },
playQueuePointerChanged(value){ playQueuePointerChanged(value) {
this.send('goToSong', value, false, true); this.send('goToSong', value, false, true);
}, },
clickSpeaker(){ clickSpeaker() {
this.simulateKick(1); this.simulateKick(1);
}, },
dropFiles(files){ dropFiles(files) {
this.setProperties({ this.setProperties({
dragging: false, dragging: false,
draggingOverPlayListArea: false draggingOverPlayListArea: false
}); });
this.send('handleNewFiles', files); this.send('handleNewFiles', files);
}, },
playerListAreaDragOver(){ playerListAreaDragOver() {
this.set('draggingOverPlayListArea', true); this.set('draggingOverPlayListArea', true);
}, },
playerListAreaDragLeave(){ playerListAreaDragLeave() {
this.set('draggingOverPlayListArea', false); this.set('draggingOverPlayListArea', false);
}, },
handleNewFiles(files){ handleNewFiles(files) {
let self = this, let self = this,
playQueue = this.get('playQueue'), playQueue = this.get('playQueue'),
updatePlayQueue = function(){ updatePlayQueue = function () {
let tags = ID3.getAllTags("local"), let tags = ID3.getAllTags("local"),
picture = null; picture = null;
if(tags.picture){ if (tags.picture) {
let base64String = ""; let base64String = "";
for (let i = 0; i < tags.picture.data.length; i++) { for (let i = 0; i < tags.picture.data.length; i++) {
base64String += String.fromCharCode(tags.picture.data[i]); base64String += String.fromCharCode(tags.picture.data[i]);
@ -711,7 +712,7 @@ export default Component.extend(helperMixin, visualizerMixin, {
ID3.clearAll(); ID3.clearAll();
if(self.get('playQueuePointer') === -1){ if (self.get('playQueuePointer') === -1) {
self.send('next'); self.send('next');
} }
}; };
@ -720,8 +721,8 @@ export default Component.extend(helperMixin, visualizerMixin, {
if (files.hasOwnProperty(key)) { if (files.hasOwnProperty(key)) {
let file = files[key]; let file = files[key];
if(file.type.startsWith('audio')) { if (file.type.startsWith('audio')) {
ID3.loadTags("local", updatePlayQueue.bind(file),{ ID3.loadTags("local", updatePlayQueue.bind(file), {
dataReader: new FileAPIReader(file), dataReader: new FileAPIReader(file),
tags: ['title', 'artist', 'album', 'track', 'picture'] tags: ['title', 'artist', 'album', 'track', 'picture']
}); });
@ -729,7 +730,7 @@ export default Component.extend(helperMixin, visualizerMixin, {
} }
} }
}, },
toggleDimmer(){ toggleDimmer() {
this.sendAction('toggleDimmer'); this.sendAction('toggleDimmer');
} }
} }

View file

@ -70,7 +70,4 @@
width: 100px; width: 100px;
height: 100px; height: 100px;
font-size: 28px; font-size: 28px;
&:hover {
background: darken(#3f51b5, 10%) !important;
}
} }

View file

@ -4,9 +4,6 @@
.paper-icon { .paper-icon {
line-height: 0.8 !important; line-height: 0.8 !important;
} }
md-slider {
width: 50%;
}
} }
.lights-control-tooltip + .tooltip { .lights-control-tooltip + .tooltip {

View file

@ -40,6 +40,10 @@ md-slider {
cursor: pointer; cursor: pointer;
} }
.md-thumb-text {
user-select: none;
}
md-slider.md-default-theme .md-thumb:after { md-slider.md-default-theme .md-thumb:after {
border-color: $secondaryThemeColor; border-color: $secondaryThemeColor;
background-color: $secondaryThemeColor; background-color: $secondaryThemeColor;

View file

@ -44,4 +44,4 @@
"ember-source": "^2.11.0", "ember-source": "^2.11.0",
"loader.js": "^4.0.7" "loader.js": "^4.0.7"
} }
} }