@@ -159,6 +159,9 @@
margin-right: 10px;
/* Change this to whatever the width of your left column is*/
}
#right {
padding-bottom: 165px;
}
#right header {
padding: 15px 20px;
margin-top: 10px;
@@ -211,7 +214,7 @@
#right header table .box label {
display: inline-block;
}
#right > table {
#right > div > table {
width: 100%;
/*font-size: 170%;*/
border-spacing: 1px;
@@ -222,81 +225,81 @@
background-color: white;
color: black;
}
#right > table tr {
#right > div > table tr {
/*background-image:
linear-gradient(
to bottom,
white,
gold
);*/
}
#right > table tr.nowplaying {
#right > div > table tr.nowplaying {
background-color: rgba(171, 238, 144, 0.57);
font-weight: bold;
}
#right > table th:nth-child(1),
#right > table td:nth-child(1),
#right > table th:nth-child(4),
#right > table td:nth-child(4) {
#right > div > table th:nth-child(1),
#right > div > table td:nth-child(1),
#right > div > table th:nth-child(4),
#right > div > table td:nth-child(4) {
width: 40px;
text-align: center;
}
#right > table th:nth-child(4),
#right > table td:nth-child(4) {
#right > div > table th:nth-child(4),
#right > div > table td:nth-child(4) {
width: 160px;
vertical-align: middle;
}
#right > table td:nth-child(2) img {
#right > div > table td:nth-child(2) img {
border: 3px solid transparent;
cursor: pointer;
}
#right > table td:nth-child(2) img.justrequested {
#right > div > table td:nth-child(2) img.justrequested {
border: 3px solid orange;
cursor: wait;
}
#right > table tr td:nth-child(2) img.waitingForDl {
#right > div > table tr td:nth-child(2) img.waitingForDl {
cursor: wait;
}
#right > table tr.nowplaying td:nth-child(2) img {
#right > div > table tr.nowplaying td:nth-child(2) img {
border: 3px solid lime;
}

#right > table td:nth-child(4) img {
#right > div > table td:nth-child(4) img {
padding: 10px;
border-radius: 7px;
/*border: 2px solid transparent;*/
}
#right > table td:nth-child(4) img:hover {
#right > div > table td:nth-child(4) img:hover {
/*border: 2px solid gold;*/
background-color: rgba(0, 255, 255, 0.25);
}
#right > table th {
#right > div > table th {
text-align: left;
background-color: gold;
padding: 5px;
}
#right > table td {
#right > div > table td {
vertical-align: bottom;
padding: 5px;
}
#right > table th:nth-child(2) {
#right > div > table th:nth-child(2) {
width: 30px;
}
#right > table th:nth-child(2) {
#right > div > table th:nth-child(2) {
width: 30px;
}
#right > table th:nth-child(2) {
#right > div > table th:nth-child(2) {
width: 30px;
}
#right > table td a, #right > table td a:visited {
#right > div > table td a, #right > div > table td a:visited {
font-size: 60%;
color: blue;
}
#right > table td a:hover {
#right > div > table td a:hover {
color: orange;
}
.container:not(.mp3enabled) #right > table td:nth-child(4),
.container:not(.mp3enabled) #right > table th:nth-child(4) {
.container:not(.mp3enabled) #right > div > table td:nth-child(4),
.container:not(.mp3enabled) #right > div > table th:nth-child(4) {
display: none;
}
#right .notracks {
@@ -573,6 +576,7 @@
width: 220px;
height: 167px;
z-index: 99;
display: none;
}
</style>

@@ -589,7 +593,7 @@
</script>
<script src="../api/getAudio.js"></script>
<script src="../api/openItem.js"></script>
<script src="../api/getAlbumTracks.js"></script>
<script src="../api/last.js"></script>
<script src="bundle.js"></script>
<audio id="player" controls>
<source src="" type="audio/mpeg">
@@ -27,8 +27,11 @@ app({
},
route: (state, actions, data) => {
console.log('route', state, actions, data);
if (data.params && data.params.id) {
const isRoute = route => data.match.indexOf(route) !== -1 && data.params && data.params.id;
if (isRoute('playlist')) {
actions.playlistRoute(data.params.id);
} else if (isRoute('fetch')) {
actions.setCurrentPlaylist(state.fetches[data.params.id], true); // dont update cache bro
}
actions.setRouteMatch(data.match);
}
@@ -0,0 +1,18 @@

const FetchManager = () => ({
state: {
fetches: JSON.parse(localStorage.getItem('fetches')) || {},
},
actions: {
newFetch: (state, actions, fetch) => {
const newFetches = state.fetches;
newFetches[fetchid] = fetch;
localStorage.setItem('fetches', JSON.stringify(newFetches));
return {
fetches: newFetches
};
},
}
});

module.exports = FetchManager;
@@ -0,0 +1,84 @@
import { h } from 'hyperapp';
import Layout from '../components/layout';
import YoutubeSearcher from '../components/youtubeSearcher';
import PlaylistViewer from '../components/playlistViewer';

const fetches = (state, actions) => {

const isInSavedPlaylists = state.playlists.some(pl => {
return pl.playlistid === state.currentPlaylist.playlistid;
});

const addToSavedPls = () => {
if (isInSavedPlaylists) return;
actions.addPlaylist({
playlistid: state.currentPlaylist.playlistid,
title: state.currentPlaylist.title
});
};

const downloadAll = () => {
actions.downloadAll();
};

const admin = state.authedKeys.indexOf(state.currentPlaylist.playlistid) !== -1;

const showSubmitKeysModal = () => {
actions.showModal('submitkeys');
document.getElementById('submittingKey').focus();
};

const hasDl = state.currentPlaylist.tracks && state.currentPlaylist.tracks.some(track => !state.fileDirectory[track.id] && state.activeDownloads.indexOf(track.id) === -1);

return (
<Layout
actions={actions}
state={state}>

{state.activeFetch && (
<div id='activeFetchInfo'>
fetching "{state.activeFetch.title}" by {state.activeFetch.artist}<br/>
currently on track #{state.activeFetchTrackNum + 1}: "{state.activeFetch.tracks[state.activeFetchTrackNum]}"
</div>
)}

{
admin
&& (
<YoutubeSearcher
actions={actions}
state={state} />
)
}


<header>
<h3>album fetch{state.currentPlaylist.artist}</h3>
<table>
<tr>
<td>
{state.settings.enableMP3s && (
<input
type="button"
value="| Download all"
class={(!hasDl || state.activeFetch) ? 'disabled' : ''}
onclick={downloadAll}
disabled={!hasDl || state.activeFetch}/>
)}

</td>
<td>
playlistid: {state.currentPlaylist.playlistid}
</td>
</tr>
</table>
</header>

<PlaylistViewer state={state} actions={actions} />

</Layout>
)

};

module.exports = fetches;
@@ -2,6 +2,7 @@ import { h } from 'hyperapp';
import home from './home';
import leaderboard from './leaderboard';
import playlistEditor from './playlistEditor';
import fetches from './fetches';
import settings from './settings';

// const prependRoutes = location.pathname.substring(0, location.pathname.indexOf('/dist/') + 6);
@@ -11,6 +12,7 @@ const views = module.exports = [
['/dist/index.html', home],
['/leaderboard', leaderboard],
['/playlist/:id', playlistEditor],
['/fetch/:id', fetches],
['/settings', settings],
['*', (state, actions) => {
return (
@@ -12,7 +12,8 @@ const home = (state, actions) => {

<Leaderboard
leaderboard={state.leaderboard}
goToPlaylist={(playlistid) => actions.router.go('/playlist/' + playlistid) }/>
goToPlaylist={(playlistid) => actions.router.go('/playlist/' + playlistid) }
thumbnailClick={actions.thumbnailClick} />

</Layout>
)
@@ -1,6 +1,7 @@
import { h } from 'hyperapp';
import Layout from '../components/layout';
import YoutubeSearcher from '../components/youtubeSearcher';
import PlaylistViewer from '../components/playlistViewer';

const playlistEditor = (state, actions) => {

@@ -27,28 +28,6 @@ const playlistEditor = (state, actions) => {
document.getElementById('submittingKey').focus();
};

const handleThumbnailClick = (track) => {
if (state.nowPlaying === track.id) {
return actions.songDone();
} else if (state.settings.enableMP3s && state.fileDirectory[track.id]) {

return actions.playMP3({
id: track.id,
file: state.fileDirectory[track.id]
});

} else {

actions.playYoutube({
id: track.id,
file: state.fileDirectory[track.id]
});

}


};

const hasDl = state.currentPlaylist.tracks && state.currentPlaylist.tracks.some(track => !state.fileDirectory[track.id] && state.activeDownloads.indexOf(track.id) === -1);

return (
@@ -116,54 +95,7 @@ const playlistEditor = (state, actions) => {
</table>
</header>

{state.currentPlaylist.tracks && (
<table>
<thead>
<th>#</th>
<th>Thumbnail</th>
<th>Song</th>
<th>DL</th>
</thead>
<tbody>
{ state.currentPlaylist.tracks &&
(state.currentPlaylist.tracks.map) &&
state.currentPlaylist.tracks.map((track, i) => (
<tr class={(state.nowPlaying === track.id) ? 'nowplaying' : ''}>
<td>{i + 1}</td>
<td>
<img
src={track.thumbnail}
onmousedown={() => handleThumbnailClick(track) }
class={(state.activeDownloads.indexOf(track.id) !== -1) ? 'waitingForDl' : ''}/>
</td>
<td>
{track.title}<br/>
<a href={track.url} target='_blank'>{track.url}</a>
</td>
<td>
{
(state.fileDirectory[track.id]) ? (
<a onclick={() => { window.openItem('/' + state.fileDirectory[track.id]) }} >
<img width='50px' src='/dist/locateOnHdd.png' />
</a>
) : (state.activeDownloads.indexOf(track.id) === -1) ? (
<a onclick={() => { actions.downloadAudio(track); }} >
<img width='50px' src='/dist/download.gif' />
</a>
) : (
<div class="loader"/>
)
}
</td>
</tr>
))}
</tbody>
</table>
)}

{(!state.currentPlaylist.tracks || !state.currentPlaylist.tracks.length) && (
<b class='notracks'>playlist has no tracks yet. add some!</b>
)}
<PlaylistViewer state={state} actions={actions} />

</Layout>
)
@@ -0,0 +1,42 @@
import { h } from 'hyperapp';
import Layout from '../components/layout';

const Settings = (state, actions) => {

const settings = [
{
setting: 'enableMP3s',
label: 'Enable MP3s'
},
{
setting: 'enableAlbumFetchs',
label: 'Enable album-fetch\'s'
}
];

return (
<Layout
actions={actions}
state={state}>


{
settings.map(settingObj => (
<p>
<label>
<input
type="checkbox"
checked={state.settings[settingObj.setting]}
onchange={() => actions.toggleSetting(settingObj.setting)}/>
{settingObj.label}
</label>
</p>
))
}

</Layout>
)

};

module.exports = Settings;
@@ -0,0 +1,62 @@
var TableInterface = require('../johnsutils/TableInterface');
var EscapeTicksInArrOfObjs = require('../johnsutils/EscapeTicksInArrOfObjs');
var shortid = require('shortid');
var uniqid = require('uniqid');

var Fetches = new TableInterface('fetches', {
tableid: ['serial', 'primary key'],
fetchid: ['varchar(20)', 'not null'],
artist: ['varchar(70)', 'not null'],
release: ['varchar(70)', 'not null'],
fetchcount: ['integer', 'DEFAULT 1']
}, function() {
this.getAll = (cb) => {
return this.select({}, cb);
};
this.checkIfExists = (artist, release, cb) => {
return this.select({
where: {
artist,
release
}
}, (res) => {
if (!res) return cb(null);
res = res[0];
return cb(res || {});
});
};
this.createFetch = (artist, release, cb) => {
return this.insert({
artist,
release,
fetchid: shortid.generate()
}, (fetch) => {
console.log('created ', fetch);
cb(fetch);
});
};
this.incrementFetchCount = (fetchid, cb) => {
console.log('incrementing ' + playlistid);
return this.update({
data: {
fetchcount: 'fetchcount + 1'
},
where: {
fetchid
}
}, res => {
console.log('res', JSON.stringify(res))
return cb && cb(!!res);
});
};
this.getTopFetches = (cb) => {
return this.executeQuery('SELECT * FROM fetches ORDER BY fetchcount desc LIMIT 3', res => {
return cb(res.map(fetch => {
delete fetch.tableid;
return fetch;
}));
});
}
});

module.exports = Playlists;
@@ -10,6 +10,7 @@ var socketManager = require('./socketManager');

var Playlists = require('./models/playlists');
var Songs = require('./models/songs');
var Fetches = require('./models/fetches');

var port = process.env.PORT || 2222;
console.log(process.env.DATABASE_URL);
@@ -13,6 +13,7 @@ var fs = require('fs');
// db
var Playlists = require('./models/playlists');
var Songs = require('./models/songs');
var Fetches = require('./models/fetches');

// module
var leaderboard = require('./leaderboard');
@@ -88,6 +89,17 @@ var socketManager = (io) => (socket) => {
});
});

socket.on('newFetch', ({ artist, release }, cb) => {
console.log('new fetch: ', artist, ' ', release);
Fetches.checkIfExists(artist, release, res => {
if (res) {
Fetches.incrementFetchCount(res.trackid, cb);
} else {
Fetches.createFetch(artist, release, cb);
}
});
});

socket.on('downloadRequest', (song, playlistid) => {

console.log('getting song')