WIP - chrome
This commit is contained in:
parent
d11c71b5b7
commit
b4bb64eba3
27 changed files with 202 additions and 1735 deletions
|
|
@ -10,13 +10,11 @@ export default Controller.extend({
|
||||||
dimmerOn: false,
|
dimmerOn: false,
|
||||||
lightsIconsOn: true,
|
lightsIconsOn: true,
|
||||||
|
|
||||||
init(){
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
let storage = new window.Locally.Store({compress: true}),
|
let dimmerOn = chrome.storage.local.get('huegasm.dimmerOn'),
|
||||||
dimmerOn = storage.get('huegasm.dimmerOn'),
|
lightsIconsOn = chrome.storage.local.get('huegasm.lightsIconsOn');
|
||||||
lightsIconsOn = storage.get('huegasm.lightsIconsOn');
|
|
||||||
this.set('storage', storage);
|
|
||||||
|
|
||||||
if (!isEmpty(dimmerOn) && dimmerOn) {
|
if (!isEmpty(dimmerOn) && dimmerOn) {
|
||||||
this.send('toggleDimmer');
|
this.send('toggleDimmer');
|
||||||
|
|
@ -33,9 +31,9 @@ export default Controller.extend({
|
||||||
|
|
||||||
let lightsIconsOn = this.get('lightsIconsOn');
|
let lightsIconsOn = this.get('lightsIconsOn');
|
||||||
|
|
||||||
this.get('storage').set('huegasm.lightsIconsOn', lightsIconsOn);
|
chrome.storage.local.set('huegasm.lightsIconsOn', lightsIconsOn);
|
||||||
},
|
},
|
||||||
toggleDimmer(){
|
toggleDimmer() {
|
||||||
this.toggleProperty('dimmerOn');
|
this.toggleProperty('dimmerOn');
|
||||||
|
|
||||||
let dimmerOn = this.get('dimmerOn');
|
let dimmerOn = this.get('dimmerOn');
|
||||||
|
|
@ -48,7 +46,7 @@ export default Controller.extend({
|
||||||
$('html').removeClass('dimmerOn');
|
$('html').removeClass('dimmerOn');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.get('storage').set('huegasm.dimmerOn', dimmerOn);
|
chrome.storage.local.set('huegasm.dimmerOn', dimmerOn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1 @@
|
||||||
{{huegasm-app toggleLightsIcons="toggleLightsIcons" toggleDimmer="toggleDimmer" dimmerOn=dimmerOn lightsIconsOn=lightsIconsOn storage=storage}}
|
{{huegasm-app toggleLightsIcons="toggleLightsIcons" toggleDimmer="toggleDimmer" dimmerOn=dimmerOn lightsIconsOn=lightsIconsOn}}
|
||||||
|
|
||||||
{{huegasm-footer action="toggleDimmer" dimmerOn=dimmerOn storage=storage}}
|
|
||||||
|
|
@ -61,7 +61,7 @@ export default Component.extend({
|
||||||
|
|
||||||
if (status === 'success' && result.length === 1) {
|
if (status === 'success' && result.length === 1) {
|
||||||
this.set('bridgeIp', result[0].internalipaddress);
|
this.set('bridgeIp', result[0].internalipaddress);
|
||||||
this.get('storage').set('huegasm.bridgeIp', result[0].internalipaddress);
|
chrome.storage.local.set('huegasm.bridgeIp', result[0].internalipaddress);
|
||||||
bridgeFindStatus = 'success';
|
bridgeFindStatus = 'success';
|
||||||
} else if (result.length > 1) {
|
} else if (result.length > 1) {
|
||||||
let multipleBridgeIps = this.get('multipleBridgeIps');
|
let multipleBridgeIps = this.get('multipleBridgeIps');
|
||||||
|
|
@ -99,7 +99,7 @@ export default Component.extend({
|
||||||
|
|
||||||
if (status === 'success' && !result[0].error) {
|
if (status === 'success' && !result[0].error) {
|
||||||
this.clearBridgePingIntervalHandle();
|
this.clearBridgePingIntervalHandle();
|
||||||
this.get('storage').set('huegasm.bridgeUsername', result[0].success.username);
|
chrome.storage.local.set('huegasm.bridgeUsername', result[0].success.username);
|
||||||
this.set('bridgeUsername', result[0].success.username);
|
this.set('bridgeUsername', result[0].success.username);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +122,7 @@ export default Component.extend({
|
||||||
},
|
},
|
||||||
chooseBridge(bridge) {
|
chooseBridge(bridge) {
|
||||||
this.set('bridgeIp', bridge);
|
this.set('bridgeIp', bridge);
|
||||||
this.get('storage').set('huegasm.bridgeIp', bridge);
|
chrome.storage.local.set('huegasm.bridgeIp', bridge);
|
||||||
},
|
},
|
||||||
findBridgeByIp() {
|
findBridgeByIp() {
|
||||||
let manualBridgeIp = this.get('manualBridgeIp');
|
let manualBridgeIp = this.get('manualBridgeIp');
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,8 @@ export default Component.extend({
|
||||||
setInterval(this.updateLightData.bind(this), 2000);
|
setInterval(this.updateLightData.bind(this), 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isNone(this.get('storage').get('huegasm.selectedTab'))) {
|
if (!isNone(chrome.storage.local.get('huegasm.selectedTab'))) {
|
||||||
this.set('selectedTab', this.get('storage').get('huegasm.selectedTab'));
|
this.set('selectedTab', chrome.storage.local.get('huegasm.selectedTab'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -112,12 +112,11 @@ export default Component.extend({
|
||||||
changeTab(tabName) {
|
changeTab(tabName) {
|
||||||
let index = this.get('tabList').indexOf(tabName);
|
let index = this.get('tabList').indexOf(tabName);
|
||||||
this.set('selectedTab', index);
|
this.set('selectedTab', index);
|
||||||
this.get('storage').set('huegasm.selectedTab', index);
|
chrome.storage.local.set('huegasm.selectedTab', index);
|
||||||
},
|
},
|
||||||
clearBridge() {
|
clearBridge() {
|
||||||
let storage = this.get('storage');
|
chrome.storage.local.remove('huegasm.bridgeUsername');
|
||||||
storage.remove('huegasm.bridgeUsername');
|
chrome.storage.local.remove('huegasm.bridgeIp');
|
||||||
storage.remove('huegasm.bridgeIp');
|
|
||||||
location.reload();
|
location.reload();
|
||||||
},
|
},
|
||||||
toggleDimmer() {
|
toggleDimmer() {
|
||||||
|
|
@ -127,119 +126,8 @@ export default Component.extend({
|
||||||
this.sendAction('toggleLightsIcons');
|
this.sendAction('toggleLightsIcons');
|
||||||
},
|
},
|
||||||
clearAllSettings() {
|
clearAllSettings() {
|
||||||
this.get('storage').clear();
|
chrome.storage.local.clear();
|
||||||
location.reload();
|
location.reload();
|
||||||
},
|
|
||||||
startIntro() {
|
|
||||||
let intro = introJs(),
|
|
||||||
playerBottom = $('#player-bottom');
|
|
||||||
|
|
||||||
if (this.get('dimmerOn')) {
|
|
||||||
this.send('toggleDimmer');
|
|
||||||
}
|
|
||||||
|
|
||||||
intro.setOptions({
|
|
||||||
steps: [
|
|
||||||
{
|
|
||||||
intro: 'Welcome! This short tutorial will introduce you to Huegasm.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
element: '#music-tab',
|
|
||||||
intro: 'This is the music player. You\'ll use this to play music and synchronize it with your active lights.<br><br>' +
|
|
||||||
'<i><b>TIP</b>: Control which lights are active through the <b>Lights</b> tab.</i>'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
element: '#playlist',
|
|
||||||
intro: 'You can add and select music to play from your playlist here. You may listen to local audio files, stream music from soundcloud or stream directly from a connected microphone.<br><br>' +
|
|
||||||
'<i><b>TIP</b>: Songs added through Soundcloud will be saved for when you visit this page again.</i>'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
element: $('#playlist md-menu')[0],
|
|
||||||
intro: '<img src="/assets/images/soundcloudUrl.png" id="soundcloud-tutorial">You can add songs from SoundCloud by copy and pasting the URL shown here'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
element: '#player-area',
|
|
||||||
intro: 'The audio playback may be controlled with the controls here. Basic music visualization effects may be shown here by selecting them from the menu ( eyeball icon in the bottom right ).'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
element: '#beat-option-row',
|
|
||||||
intro: 'These are the settings for the music tab:<br>' +
|
|
||||||
'<b>Sensitivity</b> - The sensitivity of the beat detector ( more sensitivity results in more registered beats )<br>' +
|
|
||||||
'<b>Hue Range</b> - The hue range that the lights may change to on beat.<br>' +
|
|
||||||
'<b>Brightness Range</b> - The minimum ( off-beat ) and maximum ( on-beat ) brightness of the lights.<br>' +
|
|
||||||
'<b>Flashing Transitions</b> - Quickly flash the lights on beat<br>' +
|
|
||||||
'<b>Colorloop</b> - Slowly cycle the lights through all the colors while the music is playing<br>' +
|
|
||||||
'<i><b>TIP</b>: Your sensitivity settings are saved per song as indicated by the red star icon in the top left corner. These settings they will be restored if you ever listen to the same song again.</i>',
|
|
||||||
position: 'top'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
element: '#beat-container',
|
|
||||||
intro: 'An interactive speaker that will bump when a beat is registered. <br><br>' +
|
|
||||||
'<i><b>TIP</b>: Click on the center of the speaker to simulate a beat.</i>',
|
|
||||||
position: 'top'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
element: '#lights-tab',
|
|
||||||
intro: 'This is the lights tab. Here you\'ll be able to change various light properties:<br>' +
|
|
||||||
'<b>Power</b> - Turn the selected lights on/off<br>' +
|
|
||||||
'<b>Brightness</b> - The brightness level of the selected lights<br>' +
|
|
||||||
'<b>Color</b> - The color of the selected lights<br>' +
|
|
||||||
'<b>Strobe</b> - Selected lights will flash in sequential order<br>' +
|
|
||||||
'<b>Colorloop</b> - Selected lights will slowly cycle through all the colors<br>'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
element: '#active-lights',
|
|
||||||
intro: 'These icons represent the hue lights in your system. Active lights will be controlled by the application while the inactive lights will have a red X over them and will not be controlled.<br>' +
|
|
||||||
'You may toggle a light\'s state by clicking on it.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
element: $('#navigation .ember-basic-dropdown-trigger')[0],
|
|
||||||
intro: 'A few miscellaneous settings can be found here.<br><br>' +
|
|
||||||
'<b>WARNING</b>: clearing application settings will restore the application to its original state. This will even delete your playlist and any saved song beat preferences.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
intro: 'And that\'s it...Hope you enjoy the application. ;)'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
intro.onexit(() => {
|
|
||||||
$('body').velocity('scroll', { duration: 200 });
|
|
||||||
});
|
|
||||||
|
|
||||||
intro.onchange((element) => {
|
|
||||||
if (element.id === '' || element.id === 'music-tab' || element.id === 'playlist' || element.id === 'player-area' || element.id === 'beat-option-row' || element.id === 'beat-option-button-group' || element.id === 'beat-container' || element.id === 'using-mic-audio-tooltip' || element.nodeName === 'MD-MENU') {
|
|
||||||
$('.navigation-item').eq(1).click();
|
|
||||||
} else {
|
|
||||||
$('.navigation-item').eq(0).click();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.id === 'music-tab' || element.id === 'playlist' || element.id === 'player-area') {
|
|
||||||
playerBottom.hide();
|
|
||||||
} else if (element.id === 'beat-option-row' || element.id === 'beat-option-button-group' || element.id === 'beat-container') {
|
|
||||||
playerBottom.show();
|
|
||||||
} else if (element.id === 'dimmer') {
|
|
||||||
$(document).click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// skip hidden/missing elements
|
|
||||||
intro.onafterchange((element) => {
|
|
||||||
let elem = $(element);
|
|
||||||
if (elem.html() === '<!---->') {
|
|
||||||
$('.introjs-nextbutton').click();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.id === '') {
|
|
||||||
later(this, () => {
|
|
||||||
$('body').velocity('scroll');
|
|
||||||
}, 500);
|
|
||||||
} else {
|
|
||||||
later(this, () => {
|
|
||||||
$('.introjs-tooltip').velocity('scroll', { offset: -100 });
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -11,23 +11,19 @@
|
||||||
{{/paper-button}}
|
{{/paper-button}}
|
||||||
{{/menu.trigger}}
|
{{/menu.trigger}}
|
||||||
{{#menu.content width=3 as |content|}}
|
{{#menu.content width=3 as |content|}}
|
||||||
{{#content.menu-item onClick="toggleDimmer"}}
|
{{#content.menu-item onClick="toggleDimmer" }}
|
||||||
{{paper-icon "highlight" class=dimmerOnClass}} Dark Mode: <strong>{{if dimmerOn "On" "Off"}}</strong>
|
{{paper-icon "highlight" class=dimmerOnClass}} Dark Mode: <strong>{{if dimmerOn "On" "Off"}}</strong>
|
||||||
{{/content.menu-item}}
|
{{/content.menu-item}}
|
||||||
|
|
||||||
{{#content.menu-item onClick="toggleLightsIcons"}}
|
{{#content.menu-item onClick="toggleLightsIcons" }}
|
||||||
{{paper-icon "lightbulb outline" class=dimmerOnClass}} Active Lights: <strong>{{if lightsIconsOn "Icons" "Text"}}</strong>
|
{{paper-icon "lightbulb outline" class=dimmerOnClass}} Active Lights: <strong>{{if lightsIconsOn "Icons" "Text"}}</strong>
|
||||||
{{/content.menu-item}}
|
{{/content.menu-item}}
|
||||||
|
|
||||||
{{#content.menu-item onClick="clearBridge"}}
|
{{#content.menu-item onClick="clearBridge" }}
|
||||||
{{paper-icon "compare arrows" class=dimmerOnClass}} Switch bridge
|
{{paper-icon "compare arrows" class=dimmerOnClass}} Switch bridge
|
||||||
{{/content.menu-item}}
|
{{/content.menu-item}}
|
||||||
|
|
||||||
{{#content.menu-item onClick="startIntro"}}
|
{{#content.menu-item onClick="clearAllSettings" }}
|
||||||
{{paper-icon "cached" class=dimmerOnClass}} Restart tutorial
|
|
||||||
{{/content.menu-item}}
|
|
||||||
|
|
||||||
{{#content.menu-item onClick="clearAllSettings"}}
|
|
||||||
{{paper-icon "settings backup restore" class=dimmerOnClass}} Reset settings
|
{{paper-icon "settings backup restore" class=dimmerOnClass}} Reset settings
|
||||||
{{/content.menu-item}}
|
{{/content.menu-item}}
|
||||||
{{/menu.content}}
|
{{/menu.content}}
|
||||||
|
|
@ -35,12 +31,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{light-group lightsData=lightsData activeLights=activeLights syncLight=syncLight apiURL=apiURL dimmerOn=dimmerOn lightsIconsOn=lightsIconsOn storage=storage}}
|
{{light-group lightsData=lightsData activeLights=activeLights syncLight=syncLight apiURL=apiURL dimmerOn=dimmerOn lightsIconsOn=lightsIconsOn}}
|
||||||
|
|
||||||
<div id="huegasm-content" class="row">
|
<div id="huegasm-content" class="row">
|
||||||
{{lights-tab active=(eq selectedTab 0) apiURL=apiURL lightsData=lightsData activeLights=activeLights syncLight=syncLight trial=trial colorLoopOn=colorLoopOn dimmerOn=dimmerOn playing=playing pauseLightUpdates=pauseLightUpdates}}
|
{{lights-tab active=(eq selectedTab 0) apiURL=apiURL lightsData=lightsData activeLights=activeLights syncLight=syncLight
|
||||||
|
trial=trial colorLoopOn=colorLoopOn dimmerOn=dimmerOn playing=playing pauseLightUpdates=pauseLightUpdates}} {{music-tab
|
||||||
{{music-tab active=(eq selectedTab 1) apiURL=apiURL lightsData=lightsData activeLights=activeLights pauseLightUpdates=pauseLightUpdates dimmerOn=dimmerOn storage=storage colorLoopOn=colorLoopOn playing=playing action="startIntro"}}
|
active=(eq selectedTab 1) apiURL=apiURL lightsData=lightsData activeLights=activeLights pauseLightUpdates=pauseLightUpdates
|
||||||
|
dimmerOn=dimmerOn colorLoopOn=colorLoopOn playing=playing action="startIntro"}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{paper-progress-circular diameter=100}}
|
{{paper-progress-circular diameter=100}}
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,10 @@ export default Component.extend({
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
let storage = this.get('storage');
|
if (!isEmpty(chrome.storage.local.get('huegasm.bridgeIp')) && !isEmpty(chrome.storage.local.get('huegasm.bridgeUsername'))) {
|
||||||
|
|
||||||
if (!isEmpty(storage.get('huegasm.bridgeIp')) && !isEmpty(storage.get('huegasm.bridgeUsername'))) {
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
bridgeIp: storage.get('huegasm.bridgeIp'),
|
bridgeIp: chrome.storage.local.get('huegasm.bridgeIp'),
|
||||||
bridgeUsername: storage.get('huegasm.bridgeUsername')
|
bridgeUsername: chrome.storage.local.get('huegasm.bridgeUsername')
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{{#if bridgeUsername}}
|
{{#if bridgeUsername}}
|
||||||
{{hue-controls bridgeIp=bridgeIp bridgeUsername=bridgeUsername trial=trial dimmerOn=dimmerOn lightsIconsOn=lightsIconsOn
|
{{hue-controls bridgeIp=bridgeIp bridgeUsername=bridgeUsername trial=trial dimmerOn=dimmerOn lightsIconsOn=lightsIconsOn
|
||||||
storage=storage toggleDimmer="toggleDimmer" toggleLightsIcons="toggleLightsIcons"}}
|
toggleDimmer="toggleDimmer" toggleLightsIcons="toggleLightsIcons"}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{bridge-finder bridgeIp=bridgeIp bridgeUsername=bridgeUsername trial=trial storage=storage}}
|
{{bridge-finder bridgeIp=bridgeIp bridgeUsername=bridgeUsername trial=trial}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
const {
|
|
||||||
Component,
|
|
||||||
computed
|
|
||||||
} = Ember;
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
tagName: 'footer',
|
|
||||||
classNames: ['footer'],
|
|
||||||
|
|
||||||
year: computed(function(){
|
|
||||||
return new Date().getFullYear();
|
|
||||||
}),
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
toggleDimmer(){
|
|
||||||
this.sendAction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
<div class="logo" {{action "toggleDimmer"}}></div>
|
|
||||||
|
|
||||||
<div class="footer-text">
|
|
||||||
© {{year}}
|
|
||||||
|
|
||||||
<a href="http://www.egorphilippov.me" target="_blank" rel="noopener noreferrer">
|
|
||||||
Egor Philippov
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=com.hoboman313.huegasm" target="_blank" rel="noopener noreferrer">
|
|
||||||
<img src="assets/images/google-play-badge.png" alt="Get it on the Google Play Store">
|
|
||||||
</a>
|
|
||||||
|
|
@ -17,7 +17,7 @@ export default Component.extend({
|
||||||
activeLights: A(),
|
activeLights: A(),
|
||||||
|
|
||||||
// list of all the lights in the hue system
|
// list of all the lights in the hue system
|
||||||
lightsList: computed('lightsData', 'activeLights.[]', 'dimmerOn', function(){
|
lightsList: computed('lightsData', 'activeLights.[]', 'dimmerOn', function () {
|
||||||
let lightsData = this.get('lightsData'),
|
let lightsData = this.get('lightsData'),
|
||||||
activeLights = this.get('activeLights'),
|
activeLights = this.get('activeLights'),
|
||||||
dimmerOn = this.get('dimmerOn'),
|
dimmerOn = this.get('dimmerOn'),
|
||||||
|
|
@ -29,7 +29,7 @@ export default Component.extend({
|
||||||
activeClass = 'light-active';
|
activeClass = 'light-active';
|
||||||
|
|
||||||
if (lightsData.hasOwnProperty(key) && lightsData[key].state.reachable) {
|
if (lightsData.hasOwnProperty(key) && lightsData[key].state.reachable) {
|
||||||
switch(lightsData[key].modelid){
|
switch (lightsData[key].modelid) {
|
||||||
case 'LCT001':
|
case 'LCT001':
|
||||||
type = 'a19';
|
type = 'a19';
|
||||||
break;
|
break;
|
||||||
|
|
@ -61,7 +61,7 @@ export default Component.extend({
|
||||||
type = 'storylight';
|
type = 'storylight';
|
||||||
break;
|
break;
|
||||||
case 'LWB004':
|
case 'LWB004':
|
||||||
type ='a19';
|
type = 'a19';
|
||||||
break;
|
break;
|
||||||
case 'LLC020':
|
case 'LLC020':
|
||||||
type = 'huego';
|
type = 'huego';
|
||||||
|
|
@ -70,34 +70,34 @@ export default Component.extend({
|
||||||
type = 'a19';
|
type = 'a19';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(dimmerOn){
|
if (dimmerOn) {
|
||||||
type += 'w';
|
type += 'w';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!activeLights.includes(key)){
|
if (!activeLights.includes(key)) {
|
||||||
activeClass = 'light-inactive';
|
activeClass = 'light-inactive';
|
||||||
}
|
}
|
||||||
|
|
||||||
lightsList.push({type: type, name: lightsData[key].name, id: key, data: lightsData[key], activeClass: activeClass});
|
lightsList.push({ type: type, name: lightsData[key].name, id: key, data: lightsData[key], activeClass: activeClass });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return lightsList;
|
return lightsList;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
onActiveLightsChange: observer('activeLights.[]', function(){
|
onActiveLightsChange: observer('activeLights.[]', function () {
|
||||||
this.get('storage').set('huegasm.activeLights', this.get('activeLights'));
|
chrome.storage.local.set('huegasm.activeLights', this.get('activeLights'));
|
||||||
}),
|
}),
|
||||||
|
|
||||||
init(){
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
let lightsData = this.get('lightsData'),
|
let lightsData = this.get('lightsData'),
|
||||||
activeLights = this.get('activeLights'),
|
activeLights = this.get('activeLights'),
|
||||||
activeLightsCache = this.get('storage').get('huegasm.activeLights');
|
activeLightsCache = chrome.storage.local.get('huegasm.activeLights');
|
||||||
|
|
||||||
if(!isNone(activeLightsCache)){
|
if (!isNone(activeLightsCache)) {
|
||||||
activeLightsCache.forEach(function(i){
|
activeLightsCache.forEach(function (i) {
|
||||||
if (!isNone(lightsData) && lightsData.hasOwnProperty(i) && lightsData[i].state.reachable) {
|
if (!isNone(lightsData) && lightsData.hasOwnProperty(i) && lightsData[i].state.reachable) {
|
||||||
activeLights.pushObject(i);
|
activeLights.pushObject(i);
|
||||||
}
|
}
|
||||||
|
|
@ -112,26 +112,26 @@ export default Component.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
clickLight(id){
|
clickLight(id) {
|
||||||
let activeLights = this.get('activeLights'),
|
let activeLights = this.get('activeLights'),
|
||||||
lightId = activeLights.indexOf(id);
|
lightId = activeLights.indexOf(id);
|
||||||
|
|
||||||
if(lightId !== -1){
|
if (lightId !== -1) {
|
||||||
activeLights.removeObject(id);
|
activeLights.removeObject(id);
|
||||||
} else {
|
} else {
|
||||||
activeLights.pushObject(id);
|
activeLights.pushObject(id);
|
||||||
this.set('syncLight', id);
|
this.set('syncLight', id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lightStartHover(id){
|
lightStartHover(id) {
|
||||||
if(!window.matchMedia || (window.matchMedia("(min-width: 768px)").matches)){
|
if (!window.matchMedia || (window.matchMedia("(min-width: 768px)").matches)) {
|
||||||
let hoveredLight = this.get('lightsList').filter(function(light){
|
let hoveredLight = this.get('lightsList').filter(function (light) {
|
||||||
return light.activeClass !== 'unreachable' && light.id === id[0];
|
return light.activeClass !== 'unreachable' && light.id === id[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!isEmpty(hoveredLight) && this.get('noHover') !== true){
|
if (!isEmpty(hoveredLight) && this.get('noHover') !== true) {
|
||||||
$.ajax(this.get('apiURL') + '/lights/' + id + '/state', {
|
$.ajax(this.get('apiURL') + '/lights/' + id + '/state', {
|
||||||
data: JSON.stringify({"alert": "lselect"}),
|
data: JSON.stringify({ "alert": "lselect" }),
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
type: 'PUT'
|
type: 'PUT'
|
||||||
});
|
});
|
||||||
|
|
@ -140,15 +140,15 @@ export default Component.extend({
|
||||||
this.set('isHovering', true);
|
this.set('isHovering', true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lightStopHover(id){
|
lightStopHover(id) {
|
||||||
if(!window.matchMedia || (window.matchMedia("(min-width: 768px)").matches)){
|
if (!window.matchMedia || (window.matchMedia("(min-width: 768px)").matches)) {
|
||||||
let hoveredLight = this.get('lightsList').filter(function(light){
|
let hoveredLight = this.get('lightsList').filter(function (light) {
|
||||||
return light.activeClass !== 'unreachable' && light.id === id[0];
|
return light.activeClass !== 'unreachable' && light.id === id[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!isEmpty(hoveredLight) && this.get('noHover') !== true){
|
if (!isEmpty(hoveredLight) && this.get('noHover') !== true) {
|
||||||
$.ajax(this.get('apiURL') + '/lights/' + id + '/state', {
|
$.ajax(this.get('apiURL') + '/lights/' + id + '/state', {
|
||||||
data: JSON.stringify({"alert": "none"}),
|
data: JSON.stringify({ "alert": "none" }),
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
type: 'PUT'
|
type: 'PUT'
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
const {
|
|
||||||
Component,
|
|
||||||
observer,
|
|
||||||
computed,
|
|
||||||
isEmpty,
|
|
||||||
isNone,
|
|
||||||
run: { later },
|
|
||||||
$
|
|
||||||
} = Ember;
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
url: null,
|
|
||||||
|
|
||||||
onIsShowingModalChange: observer('isShowingModal', function(){
|
|
||||||
if(this.get('isShowingModal')){
|
|
||||||
this.set('url', null);
|
|
||||||
later(function(){
|
|
||||||
$('md-input-container input').focus();
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
}),
|
|
||||||
|
|
||||||
saveDisabled: computed('url', function(){
|
|
||||||
return isNone(this.get('url')) || isEmpty(this.get('url').trim());
|
|
||||||
}),
|
|
||||||
|
|
||||||
didInsertElement: function() {
|
|
||||||
$(document).keypress((event)=>{
|
|
||||||
if(!this.get('saveDisabled') && event.which === 13) {
|
|
||||||
this.send('add');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
close () {
|
|
||||||
this.sendAction();
|
|
||||||
},
|
|
||||||
add (){
|
|
||||||
this.sendAction('action', this.get('url'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
{{#if isShowingModal}}
|
|
||||||
{{#modal-dialog close="close" alignment="center" translucentOverlay=true attachment="center" targetAttachment="center"}}
|
|
||||||
|
|
||||||
<p>Enter a <a href="https://soundcloud.com" target="_blank" rel="noopener noreferrer">SoundCloud</a> track or playlist/set URL</p>
|
|
||||||
<p>( ex. https://soundcloud.com/mrsuicidesheep/tracks )</p>
|
|
||||||
|
|
||||||
{{paper-input label="SoundCloud URL" icon="search" value=url onChange=(action (mut url))}}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{{paper-button onClick=(action "close") label="Close"}}
|
|
||||||
{{paper-button class="pull-right" onClick=(action "add") disabled=saveDisabled primary=true label="Add Music"}}
|
|
||||||
</div>
|
|
||||||
{{/modal-dialog}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
@ -1,118 +1,128 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
import helperMixin from './mixins/helpers';
|
|
||||||
import visualizerMixin from './mixins/visualizer';
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
A,
|
||||||
Component,
|
Component,
|
||||||
observer,
|
observer,
|
||||||
isEmpty,
|
isEmpty,
|
||||||
isNone,
|
isNone,
|
||||||
$,
|
$,
|
||||||
|
inject: { service },
|
||||||
run: { later, next }
|
run: { later, next }
|
||||||
} = Ember;
|
} = Ember;
|
||||||
|
|
||||||
export default Component.extend(helperMixin, visualizerMixin, {
|
export default Component.extend({
|
||||||
updatePageTitle: observer('playQueuePointer', function () {
|
classNames: ['col-sm-10', 'col-sm-offset-1', 'col-xs-12'],
|
||||||
let title = 'Huegasm',
|
classNameBindings: ['active::hidden'],
|
||||||
playQueuePointer = this.get('playQueuePointer'),
|
elementId: 'music-tab',
|
||||||
playQueue = this.get('playQueue');
|
|
||||||
|
|
||||||
if (playQueuePointer !== -1) {
|
dancer: null,
|
||||||
let song = playQueue[playQueuePointer];
|
|
||||||
if (song.title) {
|
|
||||||
title = song.title;
|
|
||||||
|
|
||||||
if (song.artist) {
|
notify: service(),
|
||||||
title += (' - ' + song.artist);
|
|
||||||
}
|
beatOptions: {
|
||||||
|
threshold: {
|
||||||
|
range: { min: 0, max: 0.5 },
|
||||||
|
step: 0.01,
|
||||||
|
defaultValue: 0.3,
|
||||||
|
pips: {
|
||||||
|
mode: 'values',
|
||||||
|
values: [0, 0.25, 0.5],
|
||||||
|
density: 10,
|
||||||
|
format: {
|
||||||
|
to: function (value) {
|
||||||
|
if (value === 0) {
|
||||||
|
value = 'More';
|
||||||
|
} else if (value === 0.25) {
|
||||||
|
value = '';
|
||||||
} else {
|
} else {
|
||||||
title = song.fileName;
|
value = 'Less';
|
||||||
}
|
}
|
||||||
|
|
||||||
title += '- Huegasm';
|
return value;
|
||||||
|
},
|
||||||
|
from: function (value) { return value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hueRange: {
|
||||||
|
range: { min: 0, max: 65535 },
|
||||||
|
step: 1,
|
||||||
|
defaultValue: 0.3,
|
||||||
|
pips: {
|
||||||
|
mode: 'values',
|
||||||
|
values: [0, 25500, 46920, 65535],
|
||||||
|
density: 10,
|
||||||
|
format: {
|
||||||
|
to: function (value) {
|
||||||
|
if (value === 0 || value === 65535) {
|
||||||
|
value = 'Red';
|
||||||
|
} else if (value === 25500) {
|
||||||
|
value = 'Green';
|
||||||
|
} else {
|
||||||
|
value = 'Blue';
|
||||||
}
|
}
|
||||||
|
|
||||||
document.title = title;
|
return value;
|
||||||
}),
|
},
|
||||||
|
from: function (value) { return value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
brightnessRange: {
|
||||||
|
range: { min: 1, max: 254 },
|
||||||
|
step: 1,
|
||||||
|
defaultValue: 0,
|
||||||
|
pips: {
|
||||||
|
mode: 'values',
|
||||||
|
values: [1, 50, 100, 150, 200, 254],
|
||||||
|
density: 10,
|
||||||
|
format: {
|
||||||
|
to: function (value) { return value; },
|
||||||
|
from: function (value) { return value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
changePlayerControl(name, value, saveBeatPrefs) {
|
threshold: 0.3,
|
||||||
|
hueRange: [0, 65535],
|
||||||
|
brightnessRange: [1, 254],
|
||||||
|
oldThreshold: null,
|
||||||
|
|
||||||
|
lastLightBopIndex: 0,
|
||||||
|
|
||||||
|
playerBottomDisplayed: true,
|
||||||
|
audioStream: null,
|
||||||
|
dimmerOn: false,
|
||||||
|
|
||||||
|
colorloopMode: false,
|
||||||
|
flashingTransitions: false,
|
||||||
|
|
||||||
|
// 0 - no repeat, 1 - repeat all, 2 - repeat one
|
||||||
|
repeat: 0,
|
||||||
|
shuffle: false,
|
||||||
|
volumeMuted: false,
|
||||||
|
volume: 100,
|
||||||
|
// beat detection related pausing
|
||||||
|
paused: false,
|
||||||
|
songBeatPreferences: {},
|
||||||
|
usingBeatPreferences: false,
|
||||||
|
oldBeatPrefCache: null,
|
||||||
|
firstVisit: true,
|
||||||
|
|
||||||
|
// noUiSlider connection specification
|
||||||
|
filledConnect: [true, false],
|
||||||
|
hueRangeConnect: [false, true, false],
|
||||||
|
|
||||||
|
changePlayerControl(name, value) {
|
||||||
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) {
|
chrome.storage.local.set('huegasm.' + name, value);
|
||||||
this.saveSongBeatPreferences();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.get('storage').set('huegasm.' + name, value);
|
|
||||||
},
|
|
||||||
|
|
||||||
saveSongBeatPreferences() {
|
|
||||||
let song = this.get('playQueue')[this.get('playQueuePointer')];
|
|
||||||
if (song) {
|
|
||||||
let title = isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title,
|
|
||||||
songBeatPreferences = this.get('songBeatPreferences');
|
|
||||||
|
|
||||||
songBeatPreferences[title] = { threshold: this.get('threshold') };
|
|
||||||
|
|
||||||
this.set('usingBeatPreferences', true);
|
|
||||||
this.get('storage').set('huegasm.songBeatPreferences', songBeatPreferences);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
loadSongBeatPreferences() {
|
|
||||||
let song = this.get('playQueue')[this.get('playQueuePointer')],
|
|
||||||
title = isEmpty(song.artist) ? song.fileName : song.artist + '-' + song.title,
|
|
||||||
songBeatPreferences = this.get('songBeatPreferences'),
|
|
||||||
preference = songBeatPreferences[title],
|
|
||||||
oldBeatPrefCache = this.get('oldBeatPrefCache'),
|
|
||||||
newOldBeatPrefCache = null;
|
|
||||||
|
|
||||||
if (!isNone(preference)) { // load existing beat prefs
|
|
||||||
newOldBeatPrefCache = { threshold: this.get('threshold') };
|
|
||||||
|
|
||||||
this.changePlayerControl('threshold', preference.threshold);
|
|
||||||
this.set('usingBeatPreferences', true);
|
|
||||||
} else if (!isNone(oldBeatPrefCache)) { // revert to using beat prefs before the remembered song
|
|
||||||
this.changePlayerControl('threshold', oldBeatPrefCache.threshold);
|
|
||||||
this.set('usingBeatPreferences', false);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('oldBeatPrefCache', newOldBeatPrefCache);
|
|
||||||
},
|
|
||||||
|
|
||||||
clearCurrentAudio(resetPointer) {
|
|
||||||
let dancer = this.get('dancer');
|
|
||||||
|
|
||||||
if (dancer.audio.pause) {
|
|
||||||
dancer.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resetPointer) {
|
|
||||||
this.set('playQueuePointer', -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setProperties({
|
|
||||||
timeElapsed: 0,
|
|
||||||
timeTotal: 0,
|
|
||||||
playing: false
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
dragOver() {
|
|
||||||
let dragLeaveTimeoutHandle = this.get('dragLeaveTimeoutHandle');
|
|
||||||
this.set('dragging', true);
|
|
||||||
|
|
||||||
if (dragLeaveTimeoutHandle) {
|
|
||||||
clearTimeout(dragLeaveTimeoutHandle);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
dragLeave() {
|
|
||||||
// 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));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
simulateKick(/*mag, ratioKickMag*/) {
|
simulateKick(/*mag, ratioKickMag*/) {
|
||||||
|
|
@ -196,7 +206,6 @@ export default Component.extend(helperMixin, visualizerMixin, {
|
||||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||||
|
|
||||||
let dancer = new Dancer(),
|
let dancer = new Dancer(),
|
||||||
storage = this.get('storage'),
|
|
||||||
kick = dancer.createKick({
|
kick = dancer.createKick({
|
||||||
threshold: this.get('threshold'),
|
threshold: this.get('threshold'),
|
||||||
onKick: (mag, ratioKickMag) => {
|
onKick: (mag, ratioKickMag) => {
|
||||||
|
|
@ -213,9 +222,9 @@ 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) => {
|
['threshold', 'playerBottomDisplayed', 'flashingTransitions', 'colorloopMode', 'hueRange', 'brightnessRange'].forEach((item) => {
|
||||||
if (!isNone(storage.get('huegasm.' + item))) {
|
if (!isNone(chrome.storage.local.get('huegasm.' + item))) {
|
||||||
let itemVal = storage.get('huegasm.' + item);
|
let itemVal = chrome.storage.local.get('huegasm.' + item);
|
||||||
|
|
||||||
if (isNone(this.actions[item + 'Changed'])) {
|
if (isNone(this.actions[item + 'Changed'])) {
|
||||||
this.set(item, itemVal);
|
this.set(item, itemVal);
|
||||||
|
|
@ -224,12 +233,6 @@ export default Component.extend(helperMixin, visualizerMixin, {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.set('oldPlayQueueLength', this.get('playQueue.length'));
|
|
||||||
|
|
||||||
SC.initialize({
|
|
||||||
client_id: this.get('SC_CLIENT_ID')
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
|
|
@ -237,423 +240,23 @@ export default Component.extend(helperMixin, visualizerMixin, {
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
// file input code
|
|
||||||
$('#file-input').on('change', function () {
|
|
||||||
let files = this.files;
|
|
||||||
self.send('handleNewFiles', files);
|
|
||||||
this.value = null; // reset in case upload the second file again
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on('click', '.alert', (event) => {
|
|
||||||
$(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) => {
|
|
||||||
if (event.which === 32 && event.target.type !== 'text') {
|
|
||||||
this.send('play');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$().on('drop', '#play-list-area', (event) => {
|
|
||||||
this.send('dropFiles', event.dataTransfer.files);
|
|
||||||
});
|
|
||||||
|
|
||||||
// control the volume by scrolling up/down
|
|
||||||
$('#player-area').on('mousewheel', (event) => {
|
|
||||||
if (this.get('playQueueNotEmpty')) {
|
|
||||||
let scrollSize = 5;
|
|
||||||
|
|
||||||
if (event.deltaY < 0) {
|
|
||||||
scrollSize *= -1;
|
|
||||||
}
|
|
||||||
let newVolume = this.get('volume') + scrollSize;
|
|
||||||
|
|
||||||
this.send('volumeChanged', newVolume < 0 ? 0 : newVolume);
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// demo tracks
|
|
||||||
if (this.get('firstVisit')) {
|
|
||||||
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/mrsuicidesheep/vallis-alps-young-feki-remix');
|
|
||||||
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/andrew-luce-when-to-love-you-feat-chelsea-cutler');
|
|
||||||
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/ahh-ooh-carefree-with-me');
|
|
||||||
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/crywolf-slow-burn');
|
|
||||||
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/clozee-red-forest');
|
|
||||||
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/mrsuicidesheep/elo-method-subranger-solace');
|
|
||||||
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/itspapaya/sunny');
|
|
||||||
this.send('handleNewSoundCloudURL', 'https://soundcloud.com/stonesthrow/nxworries-anderson-paak-knxwledge-suede');
|
|
||||||
|
|
||||||
this.get('storage').set('huegasm.firstVisit', false);
|
|
||||||
|
|
||||||
this.sendAction();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.get('playerBottomDisplayed')) {
|
if (!this.get('playerBottomDisplayed')) {
|
||||||
$('#player-bottom').hide();
|
$('#player-bottom').hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
clearPlaylist() {
|
|
||||||
this.get('playQueue').clear();
|
|
||||||
},
|
|
||||||
setVisName(name) {
|
|
||||||
this.set('currentVisName', name);
|
|
||||||
},
|
|
||||||
hideTooltip() {
|
|
||||||
$('.bootstrap-tooltip').tooltip('hide');
|
|
||||||
},
|
|
||||||
gotoSCURL(URL) {
|
|
||||||
// need to pause the music since soundcloud is going to start playing this song anyways
|
|
||||||
if (this.get('playing')) {
|
|
||||||
this.send('play');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.send('gotoURL', URL);
|
|
||||||
},
|
|
||||||
gotoURL(URL) {
|
|
||||||
$('.tooltip').remove();
|
|
||||||
window.open(URL, '_blank');
|
|
||||||
},
|
|
||||||
handleNewSoundCloudURL(URL) {
|
|
||||||
if (URL) {
|
|
||||||
SC.resolve(URL).then((resultObj) => {
|
|
||||||
let processResult = (result) => {
|
|
||||||
if (result.kind === 'user') {
|
|
||||||
this.get('notify').alert({ html: this.get('scUserNotSupportedHtml') });
|
|
||||||
} else if (result.kind === 'track') {
|
|
||||||
if (result.streamable === true) {
|
|
||||||
let picture = null;
|
|
||||||
|
|
||||||
if (result.artwork_url) {
|
|
||||||
picture = result.artwork_url.replace('large', 't67x67');
|
|
||||||
} else if (result.user.avatar_url) {
|
|
||||||
picture = result.user.avatar_url;
|
|
||||||
}
|
|
||||||
|
|
||||||
$.get(picture)
|
|
||||||
.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 });
|
|
||||||
}).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 });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
failedSongs.push(result.title);
|
|
||||||
}
|
|
||||||
} else if (result.kind === 'playlist') {
|
|
||||||
if (result.streamable === true) {
|
|
||||||
result.tracks.forEach(processResult);
|
|
||||||
} else {
|
|
||||||
failedSongs.push(result.title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
failedSongs = [];
|
|
||||||
|
|
||||||
if (resultObj instanceof Array) {
|
|
||||||
resultObj.forEach(processResult);
|
|
||||||
} else {
|
|
||||||
processResult(resultObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (failedSongs.length > 0) {
|
|
||||||
this.get('notify').alert({ html: this.get('notStreamableHtml')(failedSongs) });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.get('playQueuePointer') === -1) {
|
|
||||||
if (this.get('firstVisit')) {
|
|
||||||
this.send('goToSong', 0);
|
|
||||||
} else {
|
|
||||||
this.send('next');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, () => {
|
|
||||||
this.get('notify').alert({ html: this.get('urlNotFoundHtml')(URL) });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('isShowingAddSoundCloudModal', false);
|
|
||||||
},
|
|
||||||
toggleIsShowingAddSoundCloudModal() {
|
|
||||||
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) {
|
|
||||||
let dancer = this.get('dancer'), playQueue = this.get('playQueue');
|
|
||||||
|
|
||||||
if (dancer.audio) {
|
|
||||||
this.clearCurrentAudio(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNone(playQueue[index])) {
|
|
||||||
let audio = new Audio();
|
|
||||||
audio.src = this.get('playQueue')[index].url;
|
|
||||||
|
|
||||||
audio.crossOrigin = "anonymous";
|
|
||||||
audio.oncanplay = () => {
|
|
||||||
this.set('timeTotal', Math.floor(audio.duration));
|
|
||||||
this.set('soundCloudFuckUps', 0);
|
|
||||||
};
|
|
||||||
audio.onerror = (event) => {
|
|
||||||
let playQueuePointer = this.get('playQueuePointer'),
|
|
||||||
song = this.get('playQueue')[playQueuePointer];
|
|
||||||
|
|
||||||
if (this.get('soundCloudFuckUps') >= this.get('maxSoundCloudFuckUps')) {
|
|
||||||
this.get('notify').alert({ html: this.get('tooManySoundCloudFuckUps') });
|
|
||||||
this.send('play');
|
|
||||||
this.set('soundCloudFuckUps', 0);
|
|
||||||
} else {
|
|
||||||
if (song.local) {
|
|
||||||
this.send('removeAudio', playQueuePointer);
|
|
||||||
} else {
|
|
||||||
this.send('next', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.target.error.code === 2) {
|
|
||||||
this.get('notify').alert({ html: this.get('failedToDecodeFileHtml')(song.fileName) });
|
|
||||||
} else {
|
|
||||||
this.get('notify').alert({ html: this.get('failedToPlayFileHtml')(song.fileName) });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('usingBeatPreferences', false);
|
|
||||||
this.incrementProperty('soundCloudFuckUps');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
audio.ontimeupdate = () => {
|
|
||||||
this.set('timeElapsed', Math.floor(audio.currentTime));
|
|
||||||
};
|
|
||||||
audio.onended = () => {
|
|
||||||
this.send('next');
|
|
||||||
};
|
|
||||||
|
|
||||||
dancer.load(audio, 1);
|
|
||||||
|
|
||||||
this.set('playQueuePointer', index);
|
|
||||||
|
|
||||||
this.loadSongBeatPreferences();
|
|
||||||
|
|
||||||
if (playSong) {
|
|
||||||
this.send('play');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scrollToSong) {
|
|
||||||
// this is just a bad workaround to make sure that the track has been rendered to the playlist
|
|
||||||
next(this, () => {
|
|
||||||
$('.track' + index).velocity('scroll', { container: $('#play-list-area'), duration: 200 });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
removeAudio(index) {
|
|
||||||
this.get('playQueue').removeAt(index);
|
|
||||||
|
|
||||||
// need to manually remove the tooltip
|
|
||||||
$('body .tooltip').remove();
|
|
||||||
|
|
||||||
if (index === this.get('playQueuePointer')) {
|
|
||||||
this.send('goToSong', index, true, true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
playerAreaPlay() {
|
|
||||||
if (isEmpty($('#player-controls:hover')) && this.get('playQueuePointer') !== -1) {
|
|
||||||
this.send('play');
|
|
||||||
|
|
||||||
$('#play-notification').velocity({ opacity: 0.8, scale: 1 }, 0).velocity({ opacity: 0, scale: 3 }, 500);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
play(replayPause) {
|
|
||||||
let dancer = this.get('dancer'),
|
|
||||||
playQueuePointer = this.get('playQueuePointer'),
|
|
||||||
playing = this.get('playing'),
|
|
||||||
lightsData = this.get('lightsData');
|
|
||||||
|
|
||||||
if (playQueuePointer !== -1) {
|
|
||||||
if (playing) {
|
|
||||||
dancer.pause();
|
|
||||||
|
|
||||||
let preMusicLightsDataCache = this.get('preMusicLightsDataCache'),
|
|
||||||
updateLight = (lightIndex) => {
|
|
||||||
$.ajax(this.get('apiURL') + '/lights/' + lightIndex + '/state', {
|
|
||||||
data: JSON.stringify({
|
|
||||||
'on': preMusicLightsDataCache[lightIndex].state.on,
|
|
||||||
'hue': preMusicLightsDataCache[lightIndex].state.hue,
|
|
||||||
'bri': preMusicLightsDataCache[lightIndex].state.bri
|
|
||||||
}),
|
|
||||||
contentType: 'application/json',
|
|
||||||
type: 'PUT'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let key in lightsData) {
|
|
||||||
if (lightsData.hasOwnProperty(key)) {
|
|
||||||
later(this, updateLight, key, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!replayPause) {
|
|
||||||
this.set('timeElapsed', Math.floor(dancer.getTime()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let timeTotal = this.get('timeTotal');
|
|
||||||
|
|
||||||
if (this.get('volumeMuted')) {
|
|
||||||
dancer.setVolume(0);
|
|
||||||
} else {
|
|
||||||
dancer.setVolume(this.get('volume') / 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// replay song
|
|
||||||
if (this.get('timeElapsed') === timeTotal && timeTotal !== 0) {
|
|
||||||
this.send('next', true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$(window).trigger('resize'); // workaround to redraw the canvas for the vitualizer
|
|
||||||
|
|
||||||
this.set('preMusicLightsDataCache', lightsData);
|
|
||||||
dancer.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('pauseLightUpdates', !playing);
|
|
||||||
this.onColorloopModeChange();
|
|
||||||
this.toggleProperty('playing');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
volumeChanged(value) {
|
|
||||||
this.changePlayerControl('volume', value);
|
|
||||||
if (this.get('playing')) {
|
|
||||||
this.get('dancer').setVolume(value / 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.get('volume') > 0 && this.get('volumeMuted')) {
|
|
||||||
this.changePlayerControl('volumeMuted', false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
next(repeatAll) {
|
|
||||||
let playQueuePointer = this.get('playQueuePointer'),
|
|
||||||
playQueue = this.get('playQueue'),
|
|
||||||
nextSong = (playQueuePointer + 1),
|
|
||||||
repeat = this.get('repeat'),
|
|
||||||
shuffle = this.get('shuffle');
|
|
||||||
|
|
||||||
if (repeat === 2) { // repeating one song takes precedence over shuffling
|
|
||||||
if (playQueuePointer === -1 && playQueue.length > 0) {
|
|
||||||
nextSong = 0;
|
|
||||||
} else {
|
|
||||||
nextSong = playQueuePointer;
|
|
||||||
}
|
|
||||||
} else if (shuffle) { // next shuffle song
|
|
||||||
let shufflePlayed = this.get('shufflePlayed');
|
|
||||||
|
|
||||||
// played all the song in shuffle mode
|
|
||||||
if (shufflePlayed.length === playQueue.length) {
|
|
||||||
shufflePlayed.clear();
|
|
||||||
this.send('play', true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we're going to assume that the song URL is the id
|
|
||||||
do {
|
|
||||||
nextSong = Math.floor(Math.random() * playQueue.length);
|
|
||||||
} while (shufflePlayed.includes(playQueue[nextSong].url));
|
|
||||||
|
|
||||||
shufflePlayed.pushObject(playQueue[nextSong].url);
|
|
||||||
} else if (nextSong > playQueue.length - 1) {
|
|
||||||
if (repeat === 1 || repeatAll) {
|
|
||||||
nextSong = nextSong % playQueue.length;
|
|
||||||
} else {
|
|
||||||
this.send('play', true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.send('goToSong', nextSong, true, true);
|
|
||||||
},
|
|
||||||
previous() {
|
|
||||||
if (this.get('timeElapsed') > 5) {
|
|
||||||
this.send('seekChanged', 0);
|
|
||||||
} else {
|
|
||||||
let nextSong = this.get('playQueuePointer'),
|
|
||||||
playQueue = this.get('playQueue');
|
|
||||||
|
|
||||||
if (this.get('shuffle') && !isNone(playQueue[nextSong])) { // go to the previously shuffled song
|
|
||||||
let shufflePlayed = this.get('shufflePlayed'),
|
|
||||||
shuffledSongIndx = this.get('shufflePlayed').indexOf(playQueue[nextSong].url),
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
if (shufflePlayed.length > 0 && shuffledSongIndx !== -1) { // only if there was one
|
|
||||||
nextSong = shuffledSongIndx - 1;
|
|
||||||
|
|
||||||
if (nextSong < 0) {
|
|
||||||
nextSong = shufflePlayed.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
playQueue.some(function (item) { // try to find the previous song id
|
|
||||||
if (item.url === shufflePlayed[nextSong]) {
|
|
||||||
nextSong = i;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nextSong--;
|
|
||||||
|
|
||||||
if (nextSong < 0) {
|
|
||||||
nextSong = playQueue.length - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.send('goToSong', nextSong, true, true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
seekChanged(position) {
|
|
||||||
let dancer = this.get('dancer');
|
|
||||||
|
|
||||||
if (dancer.audio) {
|
|
||||||
dancer.audio.currentTime = Math.floor(this.get('timeTotal') * position / 100);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
volumeMutedChanged(value) {
|
|
||||||
let dancer = this.get('dancer'),
|
|
||||||
volumeMuted = isNone(value) ? !this.get('volumeMuted') : value;
|
|
||||||
|
|
||||||
this.changePlayerControl('volumeMuted', volumeMuted);
|
|
||||||
|
|
||||||
if (this.get('playing')) {
|
|
||||||
if (volumeMuted) {
|
|
||||||
dancer.setVolume(0);
|
|
||||||
} else {
|
|
||||||
dancer.setVolume(this.get('volume') / 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
addLocalAudio: function () {
|
|
||||||
$('#file-input').click();
|
|
||||||
},
|
|
||||||
shuffleChanged(value) {
|
|
||||||
this.changePlayerControl('shuffle', isNone(value) ? !this.get('shuffle') : value);
|
|
||||||
},
|
|
||||||
repeatChanged(value) {
|
|
||||||
this.changePlayerControl('repeat', isNone(value) ? (this.get('repeat') + 1) % 3 : value);
|
|
||||||
},
|
|
||||||
playerBottomDisplayedChanged(value) {
|
playerBottomDisplayedChanged(value) {
|
||||||
this.changePlayerControl('playerBottomDisplayed', value);
|
this.changePlayerControl('playerBottomDisplayed', value);
|
||||||
},
|
},
|
||||||
|
|
@ -666,70 +269,9 @@ export default Component.extend(helperMixin, visualizerMixin, {
|
||||||
hueRangeChanged(value) {
|
hueRangeChanged(value) {
|
||||||
this.changePlayerControl('hueRange', value);
|
this.changePlayerControl('hueRange', value);
|
||||||
},
|
},
|
||||||
playQueuePointerChanged(value) {
|
|
||||||
this.send('goToSong', value, false, true);
|
|
||||||
},
|
|
||||||
clickSpeaker() {
|
clickSpeaker() {
|
||||||
this.simulateKick(1);
|
this.simulateKick(1);
|
||||||
},
|
},
|
||||||
dropFiles(files) {
|
|
||||||
this.setProperties({
|
|
||||||
dragging: false,
|
|
||||||
draggingOverPlayListArea: false
|
|
||||||
});
|
|
||||||
this.send('handleNewFiles', files);
|
|
||||||
},
|
|
||||||
playerListAreaDragOver() {
|
|
||||||
this.set('draggingOverPlayListArea', true);
|
|
||||||
},
|
|
||||||
playerListAreaDragLeave() {
|
|
||||||
this.set('draggingOverPlayListArea', false);
|
|
||||||
},
|
|
||||||
handleNewFiles(files) {
|
|
||||||
let self = this,
|
|
||||||
playQueue = this.get('playQueue'),
|
|
||||||
updatePlayQueue = function () {
|
|
||||||
let tags = ID3.getAllTags("local"),
|
|
||||||
picture = null;
|
|
||||||
|
|
||||||
if (tags.picture) {
|
|
||||||
let base64String = "";
|
|
||||||
for (let i = 0; i < tags.picture.data.length; i++) {
|
|
||||||
base64String += String.fromCharCode(tags.picture.data[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
picture = "data:" + tags.picture.format + ";base64," + window.btoa(base64String);
|
|
||||||
}
|
|
||||||
|
|
||||||
playQueue.pushObject({
|
|
||||||
fileName: this.name.replace(/\.[^/.]+$/, ""),
|
|
||||||
url: URL.createObjectURL(this),
|
|
||||||
artist: tags.artist,
|
|
||||||
title: tags.title,
|
|
||||||
picture: picture,
|
|
||||||
local: true
|
|
||||||
});
|
|
||||||
|
|
||||||
ID3.clearAll();
|
|
||||||
|
|
||||||
if (self.get('playQueuePointer') === -1) {
|
|
||||||
self.send('next');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let key in files) {
|
|
||||||
if (files.hasOwnProperty(key)) {
|
|
||||||
let file = files[key];
|
|
||||||
|
|
||||||
if (file.type.startsWith('audio')) {
|
|
||||||
ID3.loadTags("local", updatePlayQueue.bind(file), {
|
|
||||||
dataReader: new FileAPIReader(file),
|
|
||||||
tags: ['title', 'artist', 'album', 'track', 'picture']
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggleDimmer() {
|
toggleDimmer() {
|
||||||
this.sendAction('toggleDimmer');
|
this.sendAction('toggleDimmer');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,412 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
const {
|
|
||||||
Mixin,
|
|
||||||
observer,
|
|
||||||
computed,
|
|
||||||
isNone,
|
|
||||||
run,
|
|
||||||
$,
|
|
||||||
inject,
|
|
||||||
on,
|
|
||||||
A
|
|
||||||
} = Ember;
|
|
||||||
|
|
||||||
export default Mixin.create({
|
|
||||||
classNames: ['col-sm-10', 'col-sm-offset-1', 'col-xs-12'],
|
|
||||||
classNameBindings: ['active::hidden'],
|
|
||||||
elementId: 'music-tab',
|
|
||||||
|
|
||||||
dancer: null,
|
|
||||||
|
|
||||||
notify: inject.service(),
|
|
||||||
|
|
||||||
beatOptions: {
|
|
||||||
threshold: {
|
|
||||||
range: {min: 0, max: 0.5},
|
|
||||||
step: 0.01,
|
|
||||||
defaultValue: 0.3,
|
|
||||||
pips: {
|
|
||||||
mode: 'values',
|
|
||||||
values: [0, 0.25, 0.5],
|
|
||||||
density: 10,
|
|
||||||
format: {
|
|
||||||
to: function ( value ) {
|
|
||||||
if(value === 0) {
|
|
||||||
value = 'More';
|
|
||||||
} else if(value === 0.25) {
|
|
||||||
value = '';
|
|
||||||
} else {
|
|
||||||
value = 'Less';
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
from: function ( value ) { return value; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hueRange: {
|
|
||||||
range: {min: 0, max: 65535},
|
|
||||||
step: 1,
|
|
||||||
defaultValue: 0.3,
|
|
||||||
pips: {
|
|
||||||
mode: 'values',
|
|
||||||
values: [0, 25500, 46920, 65535],
|
|
||||||
density: 10,
|
|
||||||
format: {
|
|
||||||
to: function ( value ) {
|
|
||||||
if(value === 0 || value === 65535) {
|
|
||||||
value = 'Red';
|
|
||||||
} else if(value === 25500 ) {
|
|
||||||
value = 'Green';
|
|
||||||
} else {
|
|
||||||
value = 'Blue';
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
from: function ( value ) { return value; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
brightnessRange: {
|
|
||||||
range: {min: 1, max: 254},
|
|
||||||
step: 1,
|
|
||||||
defaultValue: 0,
|
|
||||||
pips: {
|
|
||||||
mode: 'values',
|
|
||||||
values: [1, 50, 100, 150, 200, 254],
|
|
||||||
density: 10,
|
|
||||||
format: {
|
|
||||||
to: function ( value ) { return value; },
|
|
||||||
from: function ( value ) { return value; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
threshold: 0.3,
|
|
||||||
hueRange: [0, 65535],
|
|
||||||
brightnessRange: [1, 254],
|
|
||||||
oldThreshold: null,
|
|
||||||
|
|
||||||
playQueuePointer: -1,
|
|
||||||
playQueue: A(),
|
|
||||||
timeElapsed: 0,
|
|
||||||
timeTotal: 0,
|
|
||||||
lastLightBopIndex: 0,
|
|
||||||
|
|
||||||
playerBottomDisplayed: true,
|
|
||||||
dragging: false,
|
|
||||||
draggingOverPlayListArea: false,
|
|
||||||
dragLeaveTimeoutHandle: null,
|
|
||||||
audioStream: null,
|
|
||||||
dimmerOn: false,
|
|
||||||
isShowingAddSoundCloudModal: false,
|
|
||||||
|
|
||||||
colorloopMode: false,
|
|
||||||
flashingTransitions: false,
|
|
||||||
|
|
||||||
// 0 - no repeat, 1 - repeat all, 2 - repeat one
|
|
||||||
repeat: 0,
|
|
||||||
shuffle: false,
|
|
||||||
volumeMuted: false,
|
|
||||||
volume: 100,
|
|
||||||
// beat detection related pausing
|
|
||||||
paused: false,
|
|
||||||
// audio: playing or paused
|
|
||||||
playing: false,
|
|
||||||
songBeatPreferences: {},
|
|
||||||
usingBeatPreferences: false,
|
|
||||||
oldBeatPrefCache: null,
|
|
||||||
storage: null,
|
|
||||||
firstVisit: true,
|
|
||||||
|
|
||||||
soundCloudFuckUps: 0,
|
|
||||||
maxSoundCloudFuckUps: 3,
|
|
||||||
|
|
||||||
// used to insure that we don't replay the same thing multiple times in shuffle mode
|
|
||||||
shufflePlayed: [],
|
|
||||||
|
|
||||||
// noUiSlider connection specification
|
|
||||||
filledConnect: [true, false],
|
|
||||||
hueRangeConnect: [false, true, false],
|
|
||||||
|
|
||||||
SC_CLIENT_ID: 'aeec0034f58ecd85c2bd1deaecc41594',
|
|
||||||
scUserNotSupportedHtml: '<div class="alert alert-danger" role="alert">SoundCloud user URLs are not supported.</div>',
|
|
||||||
tooManySoundCloudFuckUps: '<div class="alert alert-danger" role="alert">The SoundCloud API is not seving the audio properly. More details <a href="https://www.soundcloudcommunity.com/soundcloud/topics/some-soundcloud-cdn-hosted-tracks-dont-have-access-control-allow-origin-header" target="_blank" rel="noopener noreferrer">HERE</a>.</div>',
|
|
||||||
notStreamableHtml(fileNames){
|
|
||||||
let html = '<div class="alert alert-danger" role="alert">The following file(s) could not be added because they are not allowed to be streamed:<br>' + fileNames.toString().replace(/,/g, '<br>') + '</div>';
|
|
||||||
|
|
||||||
return html;
|
|
||||||
},
|
|
||||||
urlNotFoundHtml(url){
|
|
||||||
return '<div class="alert alert-danger" role="alert">The URL ( ' + url + ' ) could not be resolved.</div>';
|
|
||||||
},
|
|
||||||
failedToPlayFileHtml(fileName){
|
|
||||||
return '<div class="alert alert-danger" role="alert">Failed to play file ( ' + fileName + ' ).</div>';
|
|
||||||
},
|
|
||||||
failedToDecodeFileHtml(fileName){
|
|
||||||
return '<div class="alert alert-danger" role="alert">Failed to decode file ( ' + fileName + ' ).</div>';
|
|
||||||
},
|
|
||||||
|
|
||||||
scUrl: computed('playQueuePointer', 'playQueue.[]', function(){
|
|
||||||
let rtn = null,
|
|
||||||
currentSong = this.get('playQueue')[this.get('playQueuePointer')];
|
|
||||||
|
|
||||||
if(currentSong && currentSong.scUrl){
|
|
||||||
rtn = currentSong.scUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rtn;
|
|
||||||
}),
|
|
||||||
|
|
||||||
playQueueEmpty: computed.empty('playQueue'),
|
|
||||||
playQueueNotEmpty: computed.notEmpty('playQueue'),
|
|
||||||
playQueueMultiple: computed('playQueue.[]', function(){
|
|
||||||
return this.get('playQueue').length > 1;
|
|
||||||
}),
|
|
||||||
|
|
||||||
seekPosition: computed('timeElapsed', 'timeTotal', function(){
|
|
||||||
let timeTotal = this.get('timeTotal'),
|
|
||||||
timeElapsed = this.get('timeElapsed');
|
|
||||||
|
|
||||||
if (timeTotal === 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return timeElapsed/timeTotal*100;
|
|
||||||
}),
|
|
||||||
|
|
||||||
largeArtworkPic: computed('playQueuePointer', 'currentVisName', function(){
|
|
||||||
let pic = '',
|
|
||||||
currentVisName = this.get('currentVisName'),
|
|
||||||
playQueuePointer = this.get('playQueuePointer'),
|
|
||||||
playQueue = this.get('playQueue');
|
|
||||||
|
|
||||||
if(playQueuePointer !== -1 && currentVisName === 'None'){
|
|
||||||
let song = playQueue[playQueuePointer];
|
|
||||||
if(!isNone(song.picture)){
|
|
||||||
pic = song.picture;
|
|
||||||
|
|
||||||
if(song.scUrl){
|
|
||||||
pic = pic.replace('67x67', '500x500');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pic;
|
|
||||||
}),
|
|
||||||
|
|
||||||
repeatIcon: computed('repeat', function() {
|
|
||||||
if(this.get('repeat') === 2) {
|
|
||||||
return 'repeat-one';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'repeat';
|
|
||||||
}),
|
|
||||||
|
|
||||||
playingIcon: computed('playing', function() {
|
|
||||||
if(this.get('playing')){
|
|
||||||
return 'pause';
|
|
||||||
} else if(this.get('timeElapsed') === this.get('timeTotal') && this.get('timeTotal') !== 0){
|
|
||||||
return 'replay';
|
|
||||||
} else {
|
|
||||||
return 'play-arrow';
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
playerAreaClickIcon: computed('playing', function() {
|
|
||||||
if(this.get('playing')){
|
|
||||||
return 'play-arrow';
|
|
||||||
} else {
|
|
||||||
return 'pause';
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
playListAreaClass: computed('dragging', 'draggingOverPlayListArea', 'dimmerOn', function(){
|
|
||||||
let classes = 'pointer';
|
|
||||||
|
|
||||||
if(this.get('dragging')){
|
|
||||||
classes += ' drag-here-highlight';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.get('draggingOverPlayListArea')){
|
|
||||||
classes += ' dragging-over';
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.get('dimmerOn')){
|
|
||||||
classes += ' dimmerOn';
|
|
||||||
}
|
|
||||||
|
|
||||||
return classes;
|
|
||||||
}),
|
|
||||||
|
|
||||||
dimmerOnClass: computed('dimmerOn', function(){
|
|
||||||
return this.get('dimmerOn') ? 'dimmerOn' : null;
|
|
||||||
}),
|
|
||||||
|
|
||||||
volumeMutedClass: computed('volumeMuted', function(){
|
|
||||||
let classes = 'player-control-icon volumeButton';
|
|
||||||
|
|
||||||
if(this.get('volumeMuted')){
|
|
||||||
classes += ' active';
|
|
||||||
}
|
|
||||||
|
|
||||||
return classes;
|
|
||||||
}),
|
|
||||||
|
|
||||||
repeatClass: computed('repeat', function(){
|
|
||||||
return this.get('repeat') !== 0 ? 'player-control-icon active' : 'player-control-icon';
|
|
||||||
}),
|
|
||||||
|
|
||||||
shuffleClass: computed('shuffle', function(){
|
|
||||||
return this.get('shuffle') ? 'player-control-icon active' : 'player-control-icon';
|
|
||||||
}),
|
|
||||||
|
|
||||||
volumeIcon: computed('volumeMuted', 'volume', function() {
|
|
||||||
let volume = this.get('volume');
|
|
||||||
|
|
||||||
if (this.get('volumeMuted')) {
|
|
||||||
return "volume-off";
|
|
||||||
} else if (volume >= 70) {
|
|
||||||
return "volume-up";
|
|
||||||
} else if (volume > 10) {
|
|
||||||
return "volume-down";
|
|
||||||
} else {
|
|
||||||
return 'volume-mute';
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
beatDetectionAreaArrowIcon: computed('playerBottomDisplayed', function(){
|
|
||||||
if(!this.get('playerBottomDisplayed')){
|
|
||||||
return 'keyboard-arrow-down';
|
|
||||||
} else {
|
|
||||||
return 'keyboard-arrow-up';
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
timeElapsedTxt: computed('timeElapsed', function(){
|
|
||||||
return this.formatTime(this.get('timeElapsed'));
|
|
||||||
}),
|
|
||||||
|
|
||||||
timeTotalTxt: computed('timeTotal', function() {
|
|
||||||
return this.formatTime(this.get('timeTotal'));
|
|
||||||
}),
|
|
||||||
|
|
||||||
onPlayQueueChange: observer('playQueue.length', function(){
|
|
||||||
let playQueueLength = this.get('playQueue.length');
|
|
||||||
|
|
||||||
if(playQueueLength > this.get('oldPlayQueueLength')){
|
|
||||||
run.once(this, ()=>{
|
|
||||||
run.next(this, function() {
|
|
||||||
$(`.track${playQueueLength-1}`).velocity('scroll', { container: $('#play-list-area'), duration: 200 });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('oldPlayQueueLength', playQueueLength);
|
|
||||||
}),
|
|
||||||
|
|
||||||
onColorloopModeChange: observer('colorloopMode', 'playing', function(){
|
|
||||||
this.set('colorLoopOn', this.get('playing') && this.get('colorloopMode'));
|
|
||||||
}),
|
|
||||||
|
|
||||||
onOptionChange: observer('flashingTransitions', 'playQueue.[]', 'playQueuePointer', 'colorloopMode', function(self, option){
|
|
||||||
option = option.replace('.[]', '');
|
|
||||||
let value = this.get(option);
|
|
||||||
|
|
||||||
// can't really save local music
|
|
||||||
if(option === 'playQueue'){
|
|
||||||
value = value.filter((song)=>{
|
|
||||||
return !song.url.startsWith('blob:');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.get('storage').set('huegasm.' + option, value);
|
|
||||||
}),
|
|
||||||
|
|
||||||
onRepeatChange: on('init', observer('repeat', function () {
|
|
||||||
let tooltipTxt = 'Repeat all', type = 'repeat';
|
|
||||||
|
|
||||||
if (this.get(type) === 1) {
|
|
||||||
tooltipTxt = 'Repeat one';
|
|
||||||
} else if (this.get(type) === 2) {
|
|
||||||
tooltipTxt = 'Repeat off';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.changeTooltipText(type, tooltipTxt);
|
|
||||||
})),
|
|
||||||
|
|
||||||
onShuffleChange: on('init', observer('shuffle', function () {
|
|
||||||
let tooltipTxt = 'Shuffle', type = 'shuffle';
|
|
||||||
|
|
||||||
if (this.get(type)) {
|
|
||||||
this.get('shufflePlayed').clear();
|
|
||||||
tooltipTxt = 'Unshuffle';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.changeTooltipText(type, tooltipTxt);
|
|
||||||
})),
|
|
||||||
|
|
||||||
onVolumeMutedChange: on('init', observer('volumeMuted', function() {
|
|
||||||
let tooltipTxt = 'Mute', type = 'volumeMuted',
|
|
||||||
volumeMuted = this.get(type), dancer = this.get('dancer'),
|
|
||||||
volume=0;
|
|
||||||
|
|
||||||
if (volumeMuted) {
|
|
||||||
tooltipTxt = 'Unmute';
|
|
||||||
volume = 0;
|
|
||||||
} else {
|
|
||||||
volume = this.get('volume')/100;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.get('playing')){
|
|
||||||
dancer.setVolume(volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.changeTooltipText(type, tooltipTxt);
|
|
||||||
})),
|
|
||||||
|
|
||||||
onPrevChange: on('init', observer('timeElapsed', 'playQueueNotEmpty', 'playQueue.[]', function() {
|
|
||||||
if(this.get('playQueueNotEmpty')){
|
|
||||||
let tooltipTxt = 'Previous', type = 'prev';
|
|
||||||
|
|
||||||
if(this.get('timeElapsed') > 5 || this.get('playQueue').length === 1) {
|
|
||||||
tooltipTxt = 'Replay';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.changeTooltipText(type, tooltipTxt);
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
|
|
||||||
onPlayingChange: on('init', observer('playing', function () {
|
|
||||||
let tooltipTxt = 'Play', type = 'playing';
|
|
||||||
|
|
||||||
if (this.get(type)) {
|
|
||||||
tooltipTxt = 'Pause';
|
|
||||||
} else if(this.get('timeElapsed') === this.get('timeTotal') && this.get('timeTotal') !== 0){
|
|
||||||
tooltipTxt = 'Replay';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.changeTooltipText(type, tooltipTxt);
|
|
||||||
})),
|
|
||||||
|
|
||||||
changeTooltipText(type, text) {
|
|
||||||
// change the tooltip text if it's already visible
|
|
||||||
$('#' + type + 'Tooltip + .tooltip .tooltip-inner').html(text);
|
|
||||||
//change the tooltip text for hover
|
|
||||||
$('#' + type + 'Tooltip').attr('data-original-title', text);
|
|
||||||
|
|
||||||
if(isNone(this.get(type + 'TooltipTxt'))) {
|
|
||||||
this.set(type + 'TooltipTxt', text);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
formatTime(time){
|
|
||||||
return this.pad(Math.floor(time/60), 2) + ':' + this.pad(time%60, 2);
|
|
||||||
},
|
|
||||||
|
|
||||||
pad(num, size){ return ('000000000' + num).substr(-size); }
|
|
||||||
});
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
const {
|
|
||||||
Mixin,
|
|
||||||
observer,
|
|
||||||
$
|
|
||||||
} = Ember;
|
|
||||||
|
|
||||||
export default Mixin.create({
|
|
||||||
currentVisName: 'None',
|
|
||||||
|
|
||||||
visNames: ['None', 'Bars', 'Wave'],
|
|
||||||
|
|
||||||
onCurrentVisNameChange: observer('currentVisName', function () {
|
|
||||||
let currentVisName = this.get('currentVisName');
|
|
||||||
|
|
||||||
if(currentVisName === 'None'){
|
|
||||||
let canvasEl = $('#visualization')[0],
|
|
||||||
ctx = canvasEl.getContext('2d');
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.get('storage').set('huegasm.currentVisName', currentVisName);
|
|
||||||
}),
|
|
||||||
|
|
||||||
didInsertElement(){
|
|
||||||
let dancer = this.get('dancer'),
|
|
||||||
canvas = $('#visualization')[0],
|
|
||||||
playerArea = $('#player-area'),
|
|
||||||
ctx = canvas.getContext('2d'),
|
|
||||||
spacing = 2,
|
|
||||||
h = playerArea.height(), w;
|
|
||||||
|
|
||||||
canvas.height = h;
|
|
||||||
|
|
||||||
// must be done to preserver resolution so that things don't appear blurry
|
|
||||||
// note that the height is set to 400px via css so it doesn't need to be recalculated
|
|
||||||
let syncCanvasHeight = ()=>{
|
|
||||||
w = playerArea.width();
|
|
||||||
canvas.width = w;
|
|
||||||
};
|
|
||||||
|
|
||||||
syncCanvasHeight();
|
|
||||||
|
|
||||||
$(window).on('resize', syncCanvasHeight);
|
|
||||||
|
|
||||||
dancer.bind('update', () => {
|
|
||||||
let currentVisName = this.get('currentVisName'),
|
|
||||||
gradient = ctx.createLinearGradient(0, 0, 0, h),
|
|
||||||
pageHidden = document.hidden || document.msHidden || document.webkitHidden || document.mozHidden;
|
|
||||||
|
|
||||||
// dont do anything if the page is hidden or no visualization
|
|
||||||
if(currentVisName === 'None' || pageHidden || !this.get('active')){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.clearRect(0, 0, w, h);
|
|
||||||
|
|
||||||
if (currentVisName === 'Wave') {
|
|
||||||
let width = 3,
|
|
||||||
count = 1024;
|
|
||||||
|
|
||||||
gradient.addColorStop(0.6, 'white');
|
|
||||||
gradient.addColorStop(0, '#0036FA');
|
|
||||||
|
|
||||||
ctx.lineWidth = 1;
|
|
||||||
ctx.strokeStyle = gradient;
|
|
||||||
let waveform = dancer.getWaveform();
|
|
||||||
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(0, h / 2);
|
|
||||||
for (let i = 0, l = waveform.length; i < l && i < count; i++) {
|
|
||||||
ctx.lineTo(i * ( spacing + width ), ( h / 2 ) + waveform[i] * ( h / 2 ));
|
|
||||||
}
|
|
||||||
ctx.stroke();
|
|
||||||
ctx.closePath();
|
|
||||||
} else if (currentVisName === 'Bars') {
|
|
||||||
let width = 4,
|
|
||||||
count = 128;
|
|
||||||
|
|
||||||
gradient.addColorStop(1, '#0f0');
|
|
||||||
gradient.addColorStop(0.6, '#ff0');
|
|
||||||
gradient.addColorStop(0.2, '#F12B24');
|
|
||||||
|
|
||||||
ctx.fillStyle = gradient;
|
|
||||||
let spectrum = dancer.getSpectrum();
|
|
||||||
for (let i = 0, l = spectrum.length; i < l && i < count; i++) {
|
|
||||||
ctx.fillRect(i * ( spacing + width ), h, width, -spectrum[i] * h - 60);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,127 +1,3 @@
|
||||||
<div class="row" id="step1">
|
|
||||||
<div id="player-area" class="col-sm-8 col-xs-12 {{if (eq "None" currentVisName) "display-icon"}}" {{action "playerAreaPlay"}}>
|
|
||||||
<canvas id="visualization"></canvas>
|
|
||||||
|
|
||||||
<div id="artwork">
|
|
||||||
<img src={{largeArtworkPic}}>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{paper-icon playerAreaClickIcon id="play-notification"}}
|
|
||||||
|
|
||||||
<div id="player-controls">
|
|
||||||
{{range-slider start=seekPosition min=0 max=100 connect=filledConnect id="seek-slider" on-change="seekChanged"}}
|
|
||||||
|
|
||||||
{{#if playQueueNotEmpty}}
|
|
||||||
<span data-toggle="tooltip" data-placement="top" class="bootstrap-tooltip" id="prevTooltip"
|
|
||||||
data-title={{prevTooltipTxt}} {{action "previous"}}>{{paper-icon "skip-previous" class="player-control-icon"}}</span><!--
|
|
||||||
-->{{/if}}<!--
|
|
||||||
--><span data-toggle="tooltip" data-placement="top" id="playingTooltip" class="bootstrap-tooltip"
|
|
||||||
data-title={{playingTooltipTxt}} {{action "play"}}>{{paper-icon playingIcon class="player-control-icon"}}</span><!--
|
|
||||||
-->{{#if playQueueMultiple}}<!--
|
|
||||||
--><span data-toggle="tooltip" data-placement="top" class="bootstrap-tooltip"
|
|
||||||
data-title="Next" {{action "next" true}}>{{paper-icon "skip-next" action="" class="player-control-icon"}}</span><!--
|
|
||||||
-->{{/if}}<!--
|
|
||||||
--><span data-toggle="tooltip" data-placement="top" class="bootstrap-tooltip" id="volumeMutedTooltip"
|
|
||||||
data-title={{volumeMutedTooltipTxt}} {{action "volumeMutedChanged"}}>{{paper-icon icon=volumeIcon class=volumeMutedClass}}</span><!--
|
|
||||||
-->{{range-slider start=volume min=0 max=100 connect=filledConnect on-change="volumeChanged" id="volume-bar" class="hidden-xs"}}
|
|
||||||
|
|
||||||
<div id="player-time-controls">{{timeElapsedTxt}} / {{timeTotalTxt}}</div>
|
|
||||||
|
|
||||||
{{#paper-menu as |menu|}}
|
|
||||||
{{#menu.trigger}}
|
|
||||||
{{#paper-button iconButton=true}}
|
|
||||||
{{paper-icon "remove-red-eye" class="player-control-icon"}}
|
|
||||||
{{/paper-button}}
|
|
||||||
{{/menu.trigger}}
|
|
||||||
{{#menu.content width=2 as |content|}}
|
|
||||||
{{#each visNames as |name|}}
|
|
||||||
{{#content.menu-item onClick=(action "setVisName" name)}}
|
|
||||||
{{name}}
|
|
||||||
|
|
||||||
{{#if (eq currentVisName name)}}
|
|
||||||
{{paper-icon "check" classNames=dimmerOnClass}}
|
|
||||||
{{/if}}
|
|
||||||
{{/content.menu-item}}
|
|
||||||
{{/each}}
|
|
||||||
{{/menu.content}}
|
|
||||||
{{/paper-menu}}
|
|
||||||
|
|
||||||
{{#if scUrl}}
|
|
||||||
<a href={{scUrl}} class="sound-cloud-link"{{action "gotoSCURL" scUrl}}>
|
|
||||||
<img src="assets/images/sc-white.png" id="soundcloud-logo" />
|
|
||||||
<img src="assets/images/sc-white-sm.png" id="soundcloud-logo-small" />
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="playlist" class="col-sm-4 col-xs-12">
|
|
||||||
<input id="file-input" type="file" accept="audio/*" multiple="true"/>
|
|
||||||
|
|
||||||
<div id="play-list-controls">
|
|
||||||
{{#paper-menu as |menu|}}
|
|
||||||
{{#menu.trigger}}
|
|
||||||
{{#paper-button iconButton=false}}
|
|
||||||
{{paper-icon "playlist add" class="player-control-icon"}} <span id="add-new-music-label">Add new music</span>
|
|
||||||
{{/paper-button}}
|
|
||||||
{{/menu.trigger}}
|
|
||||||
{{#menu.content width=3 as |content|}}
|
|
||||||
{{#content.menu-item onClick="addLocalAudio"}}
|
|
||||||
{{paper-icon "attachment" class=shuffleClass}} Local file
|
|
||||||
{{/content.menu-item}}
|
|
||||||
{{#content.menu-item onClick="toggleIsShowingAddSoundCloudModal"}}
|
|
||||||
{{paper-icon "cloud" class=shuffleClass}} SoundCloud
|
|
||||||
{{/content.menu-item}}
|
|
||||||
{{/menu.content}}
|
|
||||||
{{/paper-menu}}
|
|
||||||
|
|
||||||
<span data-toggle="tooltip" data-placement="top auto" class="bootstrap-tooltip" id="shuffleTooltip" data-title={{shuffleTooltipTxt}} {{action "shuffleChanged"}}>{{paper-icon "shuffle" class=shuffleClass}}</span>
|
|
||||||
<span data-toggle="tooltip" data-placement="top auto" class="bootstrap-tooltip" id="repeatTooltip" data-title={{repeatTooltipTxt}} {{action "repeatChanged"}}>{{paper-icon repeatIcon class=repeatClass}}</span>
|
|
||||||
<span data-toggle="tooltip" data-placement="top auto" class="bootstrap-tooltip hidden-xs" data-title="Clear playlist" {{action "clearPlaylist"}}>{{paper-icon "clear-all" class="player-control-icon"}}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="play-list-area" class={{playListAreaClass}} {{action "addLocalAudio"}} {{action "playerListAreaDragOver" on="dragOver"}} {{action "playerListAreaDragLeave" on="dragLeave"}} {{action "dropFiles" on="drop"}}>
|
|
||||||
{{#if (or playQueueEmpty dragging)}}
|
|
||||||
<div id="dragHere">
|
|
||||||
{{#if dragging}}
|
|
||||||
Drag your music files here
|
|
||||||
{{else}}
|
|
||||||
Add your music files here
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{paper-icon "library-music" class=dimmerOnClass}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#each playQueue as |item index|}}
|
|
||||||
<div class="playlist-item pointer track{{index}} {{if (eq index playQueuePointer) "active"}} {{if dragging "hidden"}}" {{action "goToSong" index true bubbles=false}}>
|
|
||||||
{{#if item.picture}}
|
|
||||||
<img class="album-art" src={{item.picture}}>
|
|
||||||
{{else}}
|
|
||||||
<img class="album-art" src="assets/images/missingArtwork.png">
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="song-info">
|
|
||||||
{{#if item.title}}
|
|
||||||
<div class="song-title">{{item.title}}</div>
|
|
||||||
<div class="song-artist">
|
|
||||||
{{#if item.artistUrl}}
|
|
||||||
<a href="#" {{action "gotoURL" item.artistUrl bubbles=false}}>{{item.artist}}</a>
|
|
||||||
{{else}}
|
|
||||||
{{item.artist}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
{{item.fileName}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span data-toggle="tooltip" data-placement="top auto" data-title="Remove" data-container="body" class="audio-remove-button pointer bootstrap-tooltip" {{action "removeAudio" index bubbles=false}}>{{paper-icon "close" classNames="close"}}</span>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="slide-toggle" class="text-center pointer row" {{action "slideTogglePlayerBottom"}}>
|
<div id="slide-toggle" class="text-center pointer row" {{action "slideTogglePlayerBottom"}}>
|
||||||
<div class="col-xs-offset-5 col-xs-2">
|
<div class="col-xs-offset-5 col-xs-2">
|
||||||
{{paper-icon beatDetectionAreaArrowIcon id="beat-detection-area-arrow-icon"}}
|
{{paper-icon beatDetectionAreaArrowIcon id="beat-detection-area-arrow-icon"}}
|
||||||
|
|
@ -130,43 +6,45 @@
|
||||||
|
|
||||||
<div id="player-bottom" class="row {{if dimmerOn "dimmerOn"}} {{if playerBottomDisplayed "display-flex"}}">
|
<div id="player-bottom" class="row {{if dimmerOn "dimmerOn"}} {{if playerBottomDisplayed "display-flex"}}">
|
||||||
<div id="beat-area" class="col-sm-7 col-xs-12">
|
<div id="beat-area" class="col-sm-7 col-xs-12">
|
||||||
{{#if usingBeatPreferences}}
|
|
||||||
<span data-toggle="tooltip" data-placement="top" data-title="Using the saved sensitivity preference from the last time you listened to this song" class="bootstrap-tooltip" id="save-beat-preferences-star">
|
|
||||||
{{paper-icon "star" class=dimmerOnClass}}
|
|
||||||
</span>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="row" id="beat-option-row">
|
<div class="row" id="beat-option-row">
|
||||||
<div class="beat-option col-xs-4">
|
<div class="beat-option col-xs-4">
|
||||||
<span data-toggle="tooltip" data-placement="top" data-title="The range of hues ( colors ) that the lights may change to on beat." class="option-description bootstrap-tooltip">
|
<span data-toggle="tooltip" data-placement="top" data-title="The range of hues ( colors ) that the lights may change to on beat."
|
||||||
|
class="option-description bootstrap-tooltip">
|
||||||
Hue Range
|
Hue Range
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{{range-slider start=hueRange orientation="vertical" step=beatOptions.hueRange.step range=beatOptions.hueRange.range connect=hueRangeConnect on-change="hueRangeChanged" pips=beatOptions.hueRange.pips}}
|
{{range-slider start=hueRange orientation="vertical" step=beatOptions.hueRange.step range=beatOptions.hueRange.range connect=hueRangeConnect
|
||||||
|
on-change="hueRangeChanged" pips=beatOptions.hueRange.pips}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="beat-option col-xs-4">
|
<div class="beat-option col-xs-4">
|
||||||
<span data-toggle="tooltip" data-placement="top" data-title="The minimum ( off-beat ) and maximum ( on-beat ) brightness of the lights" class="option-description bootstrap-tooltip">
|
<span data-toggle="tooltip" data-placement="top" data-title="The minimum ( off-beat ) and maximum ( on-beat ) brightness of the lights"
|
||||||
|
class="option-description bootstrap-tooltip">
|
||||||
Brightness Range
|
Brightness Range
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{{range-slider start=brightnessRange orientation="vertical" step=beatOptions.brightnessRange.step range=beatOptions.brightnessRange.range on-change="brightnessRangeChanged" pips=beatOptions.brightnessRange.pips}}
|
{{range-slider start=brightnessRange orientation="vertical" step=beatOptions.brightnessRange.step range=beatOptions.brightnessRange.range
|
||||||
|
on-change="brightnessRangeChanged" pips=beatOptions.brightnessRange.pips}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="sensitivity-settings" class="beat-option col-xs-4">
|
<div id="sensitivity-settings" class="beat-option col-xs-4">
|
||||||
<span data-toggle="tooltip" data-placement="top" data-title="The sensitivity of the beat detector ( more sensitivity results in more registered beats )" class="option-description bootstrap-tooltip">
|
<span data-toggle="tooltip" data-placement="top" data-title="The sensitivity of the beat detector ( more sensitivity results in more registered beats )"
|
||||||
|
class="option-description bootstrap-tooltip">
|
||||||
Sensitivity
|
Sensitivity
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{{range-slider start=threshold orientation="vertical" step=beatOptions.threshold.step range=beatOptions.threshold.range on-change="thresholdChanged" pips=beatOptions.threshold.pips}}
|
{{range-slider start=threshold orientation="vertical" step=beatOptions.threshold.step range=beatOptions.threshold.range on-change="thresholdChanged"
|
||||||
|
pips=beatOptions.threshold.pips}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="light-option" class="col-xs-12">
|
<div id="light-option" class="col-xs-12">
|
||||||
<span data-toggle="tooltip" data-placement="top auto" data-title="Quickly flash the lights on beat" class="bootstrap-tooltip" {{action "hideTooltip" on="mouseLeave"}}>
|
<span data-toggle="tooltip" data-placement="top auto" data-title="Quickly flash the lights on beat" class="bootstrap-tooltip"
|
||||||
|
{{action "hideTooltip" on="mouseLeave"}}>
|
||||||
{{paper-checkbox value=flashingTransitions onChange=(action (mut flashingTransitions)) label="Flashing Transitions"}}
|
{{paper-checkbox value=flashingTransitions onChange=(action (mut flashingTransitions)) label="Flashing Transitions"}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span data-toggle="tooltip" data-placement="top auto" data-title="Slowly cycle the lights through all the colors" class="bootstrap-tooltip" {{action "hideTooltip" on="mouseLeave"}}>
|
<span data-toggle="tooltip" data-placement="top auto" data-title="Slowly cycle the lights through all the colors" class="bootstrap-tooltip"
|
||||||
|
{{action "hideTooltip" on="mouseLeave"}}>
|
||||||
{{paper-checkbox value=colorloopMode onChange=(action (mut colorloopMode)) label="Colorloop"}}
|
{{paper-checkbox value=colorloopMode onChange=(action (mut colorloopMode)) label="Colorloop"}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -190,5 +68,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{music-tab/add-soundcloud-sound-modal action="handleNewSoundCloudURL" isShowingModal=isShowingAddSoundCloudModal}}
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
@import 'common';
|
@import 'common';
|
||||||
@import 'dimmer';
|
@import 'dimmer';
|
||||||
@import 'fancy-speaker';
|
@import 'fancy-speaker';
|
||||||
@import 'introjs';
|
|
||||||
@import 'hue-controls';
|
@import 'hue-controls';
|
||||||
@import 'light-group';
|
@import 'light-group';
|
||||||
@import 'music-tab';
|
@import 'music-tab';
|
||||||
|
|
@ -38,25 +37,6 @@ body, button {
|
||||||
padding-bottom: 50px;
|
padding-bottom: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
|
||||||
margin: 0 auto 10px auto;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 800px;
|
|
||||||
text-align: center;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-around;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-text {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 18px;
|
|
||||||
a {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.alert {
|
.alert {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
#settings.introjs-fixParent {
|
|
||||||
position: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.introjs-tooltip {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.introjs-skipbutton {
|
|
||||||
color: $secondaryThemeColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.introjs-bullets ul li a.active {
|
|
||||||
position: relative;
|
|
||||||
height: 10px;
|
|
||||||
width: 10px;
|
|
||||||
top: -2px;
|
|
||||||
}
|
|
||||||
|
|
@ -23,70 +23,6 @@
|
||||||
color: lighten($playerDefaultIconColor, 30%) !important;
|
color: lighten($playerDefaultIconColor, 30%) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#player-controls {
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
padding: 15px 10px;
|
|
||||||
width: 100%;
|
|
||||||
color: white !important;
|
|
||||||
z-index: 20;
|
|
||||||
cursor: default;
|
|
||||||
background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));
|
|
||||||
.ember-basic-dropdown-trigger {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
bottom: 13px;
|
|
||||||
}
|
|
||||||
.tooltip.top {
|
|
||||||
margin-top: -17px;
|
|
||||||
}
|
|
||||||
.tooltip-arrow {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
md-menu-item>.md-button md-icon {
|
|
||||||
margin: auto 0 5px 10px;
|
|
||||||
}
|
|
||||||
.play-arrow,
|
|
||||||
.pause,
|
|
||||||
.replay {
|
|
||||||
font-size: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#player-time-controls {
|
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.player-control-icon {
|
|
||||||
color: $playerDefaultIconColor !important;
|
|
||||||
transition-duration: 0.1s;
|
|
||||||
margin-right: 10px;
|
|
||||||
margin-top: 4px;
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.player-control-icon.active {
|
|
||||||
color: $secondaryThemeColor !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.player-control-icon:hover {
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#play-notification {
|
|
||||||
position: relative;
|
|
||||||
color: white !important;
|
|
||||||
background: black;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
opacity: 0;
|
|
||||||
border-radius: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#player-area {
|
#player-area {
|
||||||
height: $playerHeight;
|
height: $playerHeight;
|
||||||
|
|
@ -153,129 +89,11 @@
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#play-list-controls {
|
|
||||||
min-height: 40px;
|
|
||||||
margin-top: 5px;
|
|
||||||
border-bottom: 1px solid #3a3a3a;
|
|
||||||
position: relative;
|
|
||||||
button .player-control-icon {
|
|
||||||
margin: 0 5px 1px 3px;
|
|
||||||
}
|
|
||||||
.ember-basic-dropdown-trigger {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
color: $whitish;
|
|
||||||
.paper-button {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#play-list-area {
|
|
||||||
background-color: white;
|
|
||||||
width: 100%;
|
|
||||||
height: 350px;
|
|
||||||
margin: 0 auto;
|
|
||||||
border-radius: 5px;
|
|
||||||
transition: 0.1s all ease-in-out;
|
|
||||||
position: relative;
|
|
||||||
overflow: auto;
|
|
||||||
#dragHere {
|
|
||||||
position: absolute;
|
|
||||||
top: 27%;
|
|
||||||
font-size: 20px;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
[md-font-icon="library-music"] {
|
|
||||||
position: absolute;
|
|
||||||
top: 40%;
|
|
||||||
font-size: 100px;
|
|
||||||
opacity: 0.5;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.song-artist {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
#play-list-area.drag-here-highlight {
|
|
||||||
background-color: white;
|
|
||||||
border: 5px dotted #5383ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
#play-list-area.dragging-over {
|
|
||||||
background-color: darken(white, 5%);
|
|
||||||
box-shadow: inset 0 0 20px 0 rgba(0, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#file-input {
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playlist-item {
|
|
||||||
border-bottom: 1px solid rgba(128, 128, 128, 0.3);
|
|
||||||
border-top: 1px solid rgba(128, 128, 128, 0.3);
|
|
||||||
height: 62px;
|
|
||||||
font-family: 'Open Sans', sans-serif;
|
|
||||||
padding: 0 20px 0 5px;
|
|
||||||
position: relative;
|
|
||||||
color: $blackish;
|
|
||||||
background: darken(white, 5%);
|
|
||||||
.close {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
.album-art {
|
|
||||||
height: 60px;
|
|
||||||
float: left;
|
|
||||||
margin-right: 5px;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
.song-info {
|
|
||||||
.song-title {
|
|
||||||
max-height: 40px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.song-artist {
|
|
||||||
max-height: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.audio-remove-button {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 0;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.playlist-item.active {
|
|
||||||
background: darken(white, 15%) !important;
|
|
||||||
border-top: 1px solid $secondaryThemeColor;
|
|
||||||
border-bottom: 1px solid $secondaryThemeColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.playlist-item:hover {
|
|
||||||
background: darken(white, 10%);
|
|
||||||
.close {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#beat-area {
|
#beat-area {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.star {
|
|
||||||
cursor: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#beat-option-button-group {
|
#beat-option-button-group {
|
||||||
margin: 20px 0 10px 0;
|
margin: 20px 0 10px 0;
|
||||||
}
|
}
|
||||||
|
|
@ -363,11 +181,7 @@
|
||||||
bottom: 22px;
|
bottom: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#visualization {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#save-beat-preferences-star {
|
#save-beat-preferences-star {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
@ -390,17 +204,6 @@
|
||||||
background-size: 80px 80px;
|
background-size: 80px 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#artwork {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
img {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
max-height: 400px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.keyboard-arrow-down {
|
.keyboard-arrow-down {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "huegasm",
|
"name": "huegasm",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"JavaScript-ID3-Reader": "https://github.com/aadsm/JavaScript-ID3-Reader.git",
|
|
||||||
"bootstrap-sass": "^3.3.5",
|
"bootstrap-sass": "^3.3.5",
|
||||||
"hammer.js": "^2.0.8",
|
"hammer.js": "^2.0.8",
|
||||||
"intro.js": "^2.1.0",
|
|
||||||
"jquery-mousewheel": "^3.1.13",
|
|
||||||
"locallyjs": "^0.3.2",
|
|
||||||
"matchMedia": "^0.3.0",
|
"matchMedia": "^0.3.0",
|
||||||
"nouislider": "^9.0.0",
|
"nouislider": "^9.0.0",
|
||||||
"velocity": "^1.3.1"
|
"velocity": "^1.3.1"
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,6 @@ module.exports = function (defaults) {
|
||||||
app.import('vendor/dancer.js');
|
app.import('vendor/dancer.js');
|
||||||
|
|
||||||
app.import('bower_components/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js');
|
app.import('bower_components/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js');
|
||||||
app.import('bower_components/intro.js/intro.js');
|
|
||||||
app.import('bower_components/intro.js/introjs.css');
|
|
||||||
app.import('bower_components/intro.js/themes/introjs-nassim.css');
|
|
||||||
app.import('bower_components/JavaScript-ID3-Reader/dist/id3-minimized.js');
|
|
||||||
app.import('bower_components/jquery-mousewheel/jquery.mousewheel.js');
|
|
||||||
app.import('bower_components/locallyjs/dist/locally.min.js');
|
|
||||||
app.import('bower_components/velocity/velocity.js');
|
app.import('bower_components/velocity/velocity.js');
|
||||||
|
|
||||||
return app.toTree(extraAssets);
|
return app.toTree(extraAssets);
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 292 B |
Binary file not shown.
|
Before Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 55 KiB |
11
chrome/public/background.js
Normal file
11
chrome/public/background.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
|
||||||
|
if (request.action === 'start-listen') {
|
||||||
|
chrome.tabCapture.capture({
|
||||||
|
audio: true,
|
||||||
|
video: false
|
||||||
|
}, function (stream) {
|
||||||
|
console.log('stream', stream);
|
||||||
|
//I can attach all my filter here...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -3,14 +3,17 @@
|
||||||
"name": "Huegasm",
|
"name": "Huegasm",
|
||||||
"description": "Huegasm is a free web application for managing and synchronizing your Philips Hue lights with the beat of your music.",
|
"description": "Huegasm is a free web application for managing and synchronizing your Philips Hue lights with the beat of your music.",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"content_security_policy": "script-src https://connect.soundcloud.com 'self'; object-src 'self'",
|
|
||||||
"icons": {
|
"icons": {
|
||||||
"16": "16x16.png",
|
"16": "16x16.png",
|
||||||
"48": "48x48.png",
|
"48": "48x48.png",
|
||||||
"128": "128x128.png"
|
"128": "128x128.png"
|
||||||
},
|
},
|
||||||
"background": {
|
"background": {
|
||||||
"page": "index.html"
|
"background": {
|
||||||
|
"scripts": [
|
||||||
|
"background.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"browser_action": {
|
"browser_action": {
|
||||||
"default_icon": {
|
"default_icon": {
|
||||||
|
|
@ -22,7 +25,8 @@
|
||||||
},
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"activeTab",
|
"activeTab",
|
||||||
"background",
|
"tabCapture",
|
||||||
|
"storage",
|
||||||
"https://ajax.googleapis.com/"
|
"https://ajax.googleapis.com/"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in a new issue