app.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. History = new Mongo.Collection("history");
  2. Playlists = new Mongo.Collection("playlists");
  3. Rooms = new Mongo.Collection("rooms");
  4. if (Meteor.isClient) {
  5. Meteor.startup(function() {
  6. reCAPTCHA.config({
  7. publickey: '6LcVxg0TAAAAAE18vBiH00UAyaJggsmLm890SjZl'
  8. });
  9. });
  10. var hpSound = undefined;
  11. var songsArr = [];
  12. var ytArr = [];
  13. var _sound = undefined;
  14. var parts = location.href.split('/');
  15. var id = parts.pop();
  16. var type = id.toLowerCase();
  17. function getSpotifyInfo(title, cb) {
  18. $.ajax({
  19. type: "GET",
  20. url: 'https://api.spotify.com/v1/search?q=' + encodeURIComponent(title.toLowerCase()) + '&type=track',
  21. applicationType: "application/json",
  22. contentType: "json",
  23. success: function (data) {
  24. cb(data);
  25. }
  26. });
  27. }
  28. Template.register.events({
  29. "submit form": function(e){
  30. e.preventDefault();
  31. var username = e.target.registerUsername.value;
  32. var email = e.target.registerEmail.value;
  33. var password = e.target.registerPassword.value;
  34. var captchaData = grecaptcha.getResponse();
  35. Meteor.call("createUserMethod", {username: username, email: email, password: password}, captchaData, function(err, res) {
  36. grecaptcha.reset();
  37. if (err) {
  38. console.log(err);
  39. } else {
  40. Meteor.loginWithPassword(username, password);
  41. }
  42. });
  43. },
  44. "click #facebook-login": function(){
  45. Meteor.loginWithFacebook()
  46. },
  47. "click #github-login": function(){
  48. Meteor.loginWithGithub()
  49. },
  50. "click #login": function(){
  51. $("#register-view").hide();
  52. $("#login-view").show();
  53. }
  54. });
  55. Template.login.events({
  56. "submit form": function(e){
  57. e.preventDefault();
  58. var username = e.target.loginUsername.value;
  59. var password = e.target.loginPassword.value;
  60. Meteor.loginWithPassword(username, password);
  61. Accounts.onLoginFailure(function(){
  62. $("input").css("background-color","indianred").addClass("animated shake");
  63. $("input").on("click",function(){
  64. $("input").css({
  65. "background-color": "transparent",
  66. "width": "250px"
  67. });
  68. })
  69. });
  70. },
  71. "click #facebook-login": function(){
  72. Meteor.loginWithFacebook()
  73. },
  74. "click #github-login": function(){
  75. Meteor.loginWithGithub()
  76. },
  77. "click #register": function(){
  78. $("#login-view").hide();
  79. $("#register-view").show();
  80. }
  81. });
  82. Template.dashboard.events({
  83. "click .logout": function(e){
  84. e.preventDefault();
  85. Meteor.logout();
  86. if (hpSound !== undefined) {
  87. hpSound.stop();
  88. }
  89. },
  90. "click #croom_create": function() {
  91. Meteor.call("createRoom", $("#croom").val(), function (err, res) {
  92. if (err) {
  93. alert("Error " + err.error + ": " + err.reason);
  94. } else {
  95. window.location = "/" + $("#croom").val();
  96. }
  97. });
  98. }
  99. });
  100. Template.dashboard.helpers({
  101. rooms: function() {
  102. return Rooms.find({});
  103. }
  104. })
  105. Template.room.events({
  106. "click #search-song": function(){
  107. $("#song-results").empty()
  108. $.ajax({
  109. type: "GET",
  110. url: "https://www.googleapis.com/youtube/v3/search?part=snippet&q=" + $("#song-input").val() + "&key=AIzaSyAgBdacEWrHCHVPPM4k-AFM7uXg-Q__YXY",
  111. applicationType: "application/json",
  112. contentType: "json",
  113. success: function(data){
  114. console.log(data);
  115. for(var i in data.items){
  116. $("#song-results").append("<p>" + data.items[i].snippet.title + "</p>");
  117. ytArr.push({title: data.items[i].snippet.title, id: data.items[i].id.videoId});
  118. }
  119. console.log(ytArr);
  120. $("#song-results p").click(function(){
  121. var title = $(this).text();
  122. for(var i in ytArr){
  123. if(ytArr[i].title === title){
  124. var songObj = {
  125. id: ytArr[i].id,
  126. title: ytArr[i].title,
  127. type: "youtube"
  128. };
  129. console.log(ytArr[i].title);
  130. console.log(ytArr[i].id);
  131. // Set title field
  132. $("#title").val(songObj.title);
  133. // Set ID field
  134. $("#id").val(songObj.id);
  135. getSpotifyInfo(songObj.title.replace(/\[.*\]/g, ""), function(data) {
  136. console.log(data);
  137. if (data.tracks.items.length > 0) {
  138. $("#title").val(data.tracks.items[0].name);
  139. var artists = [];
  140. data.tracks.items[0].artists.forEach(function(artist) {
  141. artists.push(artist.name);
  142. });
  143. console.log(artists);
  144. console.log(artists.join(", "));
  145. $("#artist").val(artists.join(", "));
  146. }
  147. // Set title field again if possible
  148. // Set artist if possible
  149. });
  150. }
  151. }
  152. })
  153. }
  154. })
  155. // SC.get('/tracks', { q: $("#song-input").val()}, function(tracks) {
  156. // console.log(tracks);
  157. // for(var i in tracks){
  158. // $("#song-results").append("<p>" + tracks[i].title + "</p>")
  159. // songsArr.push({title: tracks[i].title, id: tracks[i].id, duration: tracks[i].duration / 1000});
  160. // }
  161. // $("#song-results p").click(function(){
  162. // var title = $(this).text();
  163. // for(var i in songsArr){
  164. // if(songsArr[i].title === title){
  165. // var id = songsArr[i].id;
  166. // var duration = songsArr[i].duration;
  167. // var songObj = {
  168. // title: songsArr[i].title,
  169. // id: id,
  170. // duration: duration,
  171. // type: "soundcloud"
  172. // }
  173. // }
  174. // }
  175. // console.log(id);
  176. // })
  177. // });
  178. },
  179. "click #add-songs": function(){
  180. $("#add-songs-modal").show();
  181. }
  182. });
  183. Template.room.helpers({
  184. type: function() {
  185. var parts = location.href.split('/');
  186. var id = parts.pop();
  187. return id.toUpperCase();
  188. },
  189. title: function(){
  190. return Session.get("title");
  191. },
  192. artist: function(){
  193. return Session.get("artist");
  194. },
  195. loaded: function() {
  196. return Session.get("loaded");
  197. }
  198. });
  199. Template.admin.helpers({
  200. rooms: function() {
  201. return Rooms.find({});
  202. }
  203. });
  204. Template.playlist.helpers({
  205. playlist_songs: function() {
  206. var data = Playlists.find({type: type}).fetch();
  207. if (data !== undefined && data.length > 0) {
  208. return data[0].songs;
  209. } else {
  210. return [];
  211. }
  212. }
  213. });
  214. Meteor.subscribe("rooms");
  215. Template.room.onCreated(function () {
  216. var tag = document.createElement("script");
  217. tag.src = "https://www.youtube.com/iframe_api";
  218. var firstScriptTag = document.getElementsByTagName('script')[0];
  219. firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  220. var currentSong = undefined;
  221. var _sound = undefined;
  222. var yt_player = undefined;
  223. var size = 0;
  224. var artistStr;
  225. var temp = "";
  226. var currentArt;
  227. function getTimeElapsed() {
  228. if (currentSong !== undefined) {
  229. return Date.now() - currentSong.started;
  230. }
  231. return 0;
  232. }
  233. function getSongInfo(query, platform){
  234. var search = query;
  235. var titles = [];
  236. getSpotifyInfo(query, function(data) {
  237. console.log(data);
  238. for(var i in data){
  239. for(var j in data[i].items){
  240. if(search.indexOf(data[i].items[j].name) !== -1){
  241. console.log(data[i].items[j].name);
  242. var info = data[i].items[j];
  243. Session.set("title", data[i].items[j].name);
  244. console.log("Info: " + info);
  245. if(platform === "youtube"){
  246. Session.set("duration", data[i].items[j].duration_ms / 1000)
  247. console.log(Session.get("duration"));
  248. }
  249. temp = "";
  250. if(data[i].items[j].artists.length >= 2){
  251. for(var k in data[i].items[j].artists){
  252. temp = temp + data[i].items[j].artists[k].name + ", ";
  253. }
  254. } else{
  255. for(var k in data[i].items[j].artists){
  256. temp = temp + data[i].items[j].artists[k].name;
  257. }
  258. }
  259. if(temp[temp.length-2] === ","){
  260. artistStr = temp.substr(0,temp.length-2);
  261. } else{
  262. artistStr = temp;
  263. }
  264. Session.set("artist", artistStr);
  265. $(".current").remove();
  266. $(".room-title").before("<img class='current' src='" + data[i].items[j].album.images[1].url + "' />");
  267. return true;
  268. }
  269. }
  270. //---------------------------------------------------------------//
  271. }
  272. });
  273. }
  274. function resizeSeekerbar() {
  275. $("#seeker-bar").width(((getTimeElapsed() / 1000) / Session.get("duration") * 100) + "%");
  276. }
  277. function startSong() {
  278. if (currentSong !== undefined) {
  279. if (_sound !== undefined) _sound.stop();
  280. if (yt_player !== undefined && yt_player.stopVideo !== undefined) yt_player.stopVideo();
  281. if (currentSong.song.type === "soundcloud") {
  282. $("#player").attr("src", "")
  283. getSongInfo(currentSong.song.title);
  284. SC.stream("/tracks/" + currentSong.song.id + "#t=20s", function(sound){
  285. console.log(sound);
  286. _sound = sound;
  287. sound._player._volume = 0.3;
  288. sound.play();
  289. console.log(getTimeElapsed());
  290. var interval = setInterval(function() {
  291. if (sound.getState() === "playing") {
  292. sound.seek(getTimeElapsed());
  293. window.clearInterval(interval);
  294. }
  295. }, 200);
  296. // Session.set("title", currentSong.song.title || "Title");
  297. // Session.set("artist", currentSong.song.artist || "Artist");
  298. Session.set("duration", currentSong.song.duration);
  299. resizeSeekerbar();
  300. });
  301. } else {
  302. if (yt_player === undefined) {
  303. yt_player = new YT.Player("player", {
  304. height: 540,
  305. width: 960,
  306. videoId: currentSong.song.id,
  307. events: {
  308. 'onReady': function(event) {
  309. event.target.seekTo(getTimeElapsed() / 1000);
  310. event.target.playVideo();
  311. resizeSeekerbar();
  312. },
  313. 'onStateChange': function(event){
  314. if (event.data == YT.PlayerState.PAUSED) {
  315. event.target.seekTo(getTimeElapsed() / 1000);
  316. event.target.playVideo();
  317. }
  318. }
  319. }
  320. });
  321. } else {
  322. yt_player.loadVideoById(currentSong.song.id);
  323. }
  324. // Session.set("title", currentSong.song.title || "Title");
  325. // Session.set("artist", currentSong.song.artist || "Artist");
  326. getSongInfo(currentSong.song.title, "youtube");
  327. //Session.set("duration", currentSong.song.duration);
  328. }
  329. }
  330. }
  331. Meteor.subscribe("history");
  332. Meteor.subscribe("playlists");
  333. Session.set("loaded", false);
  334. Meteor.subscribe("rooms", function() {
  335. var parts = location.href.split('/');
  336. var id = parts.pop();
  337. var type = id.toLowerCase();
  338. console.log(Rooms.find({type: type}).fetch().length);
  339. if (Rooms.find({type: type}).count() !== 1) {
  340. window.location = "/";
  341. } else {
  342. Session.set("loaded", true);
  343. Meteor.setInterval(function () {
  344. var data = undefined;
  345. var dataCursor = History.find({type: type});
  346. dataCursor.map(function (doc) {
  347. if (data === undefined) {
  348. data = doc;
  349. }
  350. });
  351. if (data !== undefined && data.history.length > size) {
  352. currentSong = data.history[data.history.length - 1];
  353. size = data.history.length;
  354. startSong();
  355. }
  356. }, 1000);
  357. Meteor.setInterval(function () {
  358. resizeSeekerbar();
  359. }, 50);
  360. }
  361. });
  362. });
  363. Template.admin.events({
  364. "submit form": function(e){
  365. e.preventDefault();
  366. var genre = e.target.genre.value;
  367. var type = e.target.type.value;
  368. var id = e.target.id.value;
  369. var title = e.target.title.value;
  370. var artist = e.target.artist.value;
  371. var songData = {type: type, id: id, title: title, artist: artist};
  372. Meteor.call("addPlaylistSong", genre, songData, function(err, res) {
  373. console.log(err, res);
  374. });
  375. }
  376. });
  377. }
  378. if (Meteor.isServer) {
  379. Meteor.startup(function() {
  380. reCAPTCHA.config({
  381. privatekey: '6LcVxg0TAAAAAI2fgIEEWHFxwNXeVIs8mzq5cfRM'
  382. });
  383. });
  384. Meteor.users.deny({update: function () { return true; }});
  385. Meteor.users.deny({insert: function () { return true; }});
  386. Meteor.users.deny({remove: function () { return true; }});
  387. function getSongDuration(query){
  388. var duration;
  389. var search = query;
  390. query = query.toLowerCase().split(" ").join("%20");
  391. var res = Meteor.http.get('https://api.spotify.com/v1/search?q=' + query + '&type=track');
  392. for(var i in res.data){
  393. for(var j in res.data[i].items){
  394. if(search.indexOf(res.data[i].items[j].name) !== -1){
  395. duration = res.data[i].items[j].duration_ms / 1000;
  396. console.log(duration);
  397. return duration;
  398. }
  399. }
  400. }
  401. }
  402. function getSongAlbumArt(query){
  403. var albumart;
  404. var search = query;
  405. query = query.toLowerCase().split(" ").join("%20");
  406. var res = Meteor.http.get('https://api.spotify.com/v1/search?q=' + query + '&type=track');
  407. for(var i in res.data){
  408. for(var j in res.data[i].items){
  409. if(search.indexOf(res.data[i].items[j].name) !== -1){
  410. albumart = res.data[i].items[j].album.images[1].url
  411. return albumart;
  412. }
  413. }
  414. }
  415. }
  416. //var room_types = ["edm", "nightcore"];
  417. var songsArr = [];
  418. function getSongsByType(type) {
  419. if (type === "edm") {
  420. return [
  421. {id: "aE2GCa-_nyU", title: "Radioactive - Lindsey Stirling and Pentatonix", duration: getSongDuration("Radioactive - Lindsey Stirling and Pentatonix"), albumart: getSongAlbumArt("Radioactive - Lindsey Stirling and Pentatonix"), type: "youtube"},
  422. {id: "aHjpOzsQ9YI", title: "Crystallize", artist: "Linsdey Stirling", duration: getSongDuration("Crystallize"), albumart: getSongAlbumArt("Crystallize"), type: "youtube"}
  423. ];
  424. } else if (type === "nightcore") {
  425. return [{id: "f7RKOP87tt4", title: "Monster (DotEXE Remix)", duration: getSongDuration("Monster (DotEXE Remix)"), albumart: getSongAlbumArt("Monster (DotEXE Remix)"), type: "youtube"}];
  426. } else {
  427. return [{id: "dQw4w9WgXcQ", title: "Never Gonna Give You Up", duration: getSongDuration("Never Gonna Give You Up"), albumart: getSongAlbumArt("Never Gonna Give You Up"), type: "youtube"}];
  428. }
  429. }
  430. Rooms.find({}).fetch().forEach(function(room) {
  431. var type = room.type;
  432. if (Playlists.find({type: type}).count() === 0) {
  433. if (type === "edm") {
  434. Playlists.insert({type: type, songs: getSongsByType(type)});
  435. } else if (type === "nightcore") {
  436. Playlists.insert({type: type, songs: getSongsByType(type)});
  437. } else {
  438. Playlists.insert({type: type, songs: getSongsByType(type)});
  439. }
  440. }
  441. if (History.find({type: type}).count() === 0) {
  442. History.insert({type: type, history: []});
  443. }
  444. if (Playlists.find({type: type}).fetch()[0].songs.length === 0) {
  445. // Add a global video to Playlist so it can proceed
  446. } else {
  447. var startedAt = Date.now();
  448. var songs = Playlists.find({type: type}).fetch()[0].songs;
  449. var currentSong = 0;
  450. addToHistory(songs[currentSong], startedAt);
  451. function addToHistory(song, startedAt) {
  452. History.update({type: type}, {$push: {history: {song: song, started: startedAt}}});
  453. }
  454. function skipSong() {
  455. songs = Playlists.find({type: type}).fetch()[0].songs;
  456. if (currentSong < (songs.length - 1)) {
  457. currentSong++;
  458. } else currentSong = 0;
  459. songTimer();
  460. addToHistory(songs[currentSong], startedAt);
  461. }
  462. function songTimer() {
  463. startedAt = Date.now();
  464. Meteor.setTimeout(function() {
  465. skipSong();
  466. }, songs[currentSong].duration * 1000);
  467. }
  468. songTimer();
  469. }
  470. });
  471. ServiceConfiguration.configurations.remove({
  472. service: "facebook"
  473. });
  474. ServiceConfiguration.configurations.insert({
  475. service: "facebook",
  476. appId: "1496014310695890",
  477. secret: "9a039f254a08a1488c08bb0737dbd2a6"
  478. });
  479. ServiceConfiguration.configurations.remove({
  480. service: "github"
  481. });
  482. ServiceConfiguration.configurations.insert({
  483. service: "github",
  484. clientId: "dcecd720f47c0e4001f7",
  485. secret: "375939d001ef1a0ca67c11dbf8fb9aeaa551e01b"
  486. });
  487. Meteor.publish("history", function() {
  488. return History.find({})
  489. });
  490. Meteor.publish("playlists", function() {
  491. return Playlists.find({})
  492. });
  493. Meteor.publish("rooms", function() {
  494. return Rooms.find()
  495. });
  496. Meteor.methods({
  497. createUserMethod: function(formData, captchaData) {
  498. var verifyCaptchaResponse = reCAPTCHA.verifyCaptcha(this.connection.clientAddress, captchaData);
  499. if (!verifyCaptchaResponse.success) {
  500. console.log('reCAPTCHA check failed!', verifyCaptchaResponse);
  501. throw new Meteor.Error(422, 'reCAPTCHA Failed: ' + verifyCaptchaResponse.error);
  502. } else {
  503. console.log('reCAPTCHA verification passed!');
  504. Accounts.createUser({
  505. username: formData.username,
  506. email: formData.email,
  507. password: formData.password
  508. });
  509. }
  510. return true;
  511. },
  512. addPlaylistSong: function(type, songData) {
  513. type = type.toLowerCase();
  514. if (Rooms.find({type: type}).count() === 1) {
  515. if (songData !== undefined && Object.keys(songData).length === 4 && songData.type !== undefined && songData.title !== undefined && songData.title !== undefined && songData.artist !== undefined) {
  516. songData.duration = getSongDuration(songData.title);
  517. Playlists.update({type: type}, {$push: {songs: {id: songData.id, title: songData.title, artist: songData.artist, duration: songData.duration, type: songData.type}}});
  518. return true;
  519. } else {
  520. throw new Meteor.error(403, "Invalid data.");
  521. }
  522. } else {
  523. throw new Meteor.error(403, "Invalid genre.");
  524. }
  525. },
  526. createRoom: function(type) {
  527. if (Rooms.find({type: type}).count() === 0) {
  528. Rooms.insert({type: type}, function(err) {
  529. if (err) {
  530. throw err;
  531. } else {
  532. if (Playlists.find({type: type}).count() === 1) {
  533. if (History.find({type: type}).count() === 0) {
  534. History.insert({type: type, history: []}, function(err3) {
  535. if (err3) {
  536. throw err3;
  537. } else {
  538. startStation();
  539. return true;
  540. }
  541. });
  542. } else {
  543. startStation();
  544. return true;
  545. }
  546. } else {
  547. Playlists.insert({type: type, songs: getSongsByType(type)}, function (err2) {
  548. if (err2) {
  549. throw err2;
  550. } else {
  551. if (History.find({type: type}).count() === 0) {
  552. History.insert({type: type, history: []}, function(err3) {
  553. if (err3) {
  554. throw err3;
  555. } else {
  556. startStation();
  557. return true;
  558. }
  559. });
  560. } else {
  561. startStation();
  562. return true;
  563. }
  564. }
  565. });
  566. }
  567. }
  568. });
  569. } else {
  570. throw "Room already exists";
  571. }
  572. function startStation() {
  573. var startedAt = Date.now();
  574. var songs = Playlists.find({type: type}).fetch()[0].songs;
  575. var currentSong = 0;
  576. addToHistory(songs[currentSong], startedAt);
  577. function addToHistory(song, startedAt) {
  578. History.update({type: type}, {$push: {history: {song: song, started: startedAt}}});
  579. }
  580. function skipSong() {
  581. songs = Playlists.find({type: type}).fetch()[0].songs;
  582. if (currentSong < (songs.length - 1)) {
  583. currentSong++;
  584. } else currentSong = 0;
  585. songTimer();
  586. addToHistory(songs[currentSong], startedAt);
  587. }
  588. function songTimer() {
  589. startedAt = Date.now();
  590. Meteor.setTimeout(function() {
  591. skipSong();
  592. }, songs[currentSong].duration * 1000);
  593. }
  594. songTimer();
  595. }
  596. }
  597. });
  598. }
  599. Router.route("/", {
  600. template: "home"
  601. });
  602. Router.route("/terms", {
  603. template: "terms"
  604. });
  605. Router.route("/privacy", {
  606. template: "privacy"
  607. });
  608. Router.route("/admin", {
  609. template: "admin"
  610. });
  611. Router.route("/:type", {
  612. template: "room"
  613. });