AddSongToQueue.vue 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. <template>
  2. <modal title="Add Song To Queue">
  3. <div slot="body">
  4. <div class="vertical-padding">
  5. <h4 class="modal-section-title">Choose a song</h4>
  6. <p class="modal-section-description">
  7. Choose a song by searching or using a link from YouTube.
  8. </p>
  9. <br />
  10. <div class="control is-grouped" id="youtube-search-input">
  11. <p class="control is-expanded">
  12. <input
  13. class="input"
  14. type="text"
  15. placeholder="Enter your YouTube query here..."
  16. v-model="querySearch"
  17. autofocus
  18. @keyup.enter="submitQuery()"
  19. />
  20. </p>
  21. <p class="control">
  22. <a
  23. class="button is-info"
  24. v-on:click="submitQuery()"
  25. href="#"
  26. ><i class="material-icons icon-with-button"
  27. >search</i
  28. >Search</a
  29. >
  30. </p>
  31. </div>
  32. <div
  33. class="control is-grouped"
  34. v-if="station.type === 'official'"
  35. >
  36. <p class="control is-expanded">
  37. <input
  38. class="input"
  39. type="text"
  40. placeholder="YouTube Playlist URL"
  41. v-model="importQuery"
  42. @keyup.enter="importPlaylist()"
  43. />
  44. </p>
  45. <p class="control">
  46. <a
  47. class="button is-info"
  48. v-on:click="importPlaylist()"
  49. href="#"
  50. >Import</a
  51. >
  52. </p>
  53. </div>
  54. <table
  55. class="table"
  56. style="margin-top: 20px;"
  57. v-if="queryResults.length > 0"
  58. >
  59. <tbody>
  60. <tr
  61. v-for="(result, index) in queryResults"
  62. :key="index"
  63. >
  64. <td class="song-thumbnail">
  65. <div
  66. :style="
  67. `background-image: url('${result.thumbnail}'`
  68. "
  69. ></div>
  70. </td>
  71. <td><strong v-html="result.title"></strong></td>
  72. <td class="song-actions">
  73. <a
  74. class="button is-success"
  75. v-on:click="addSongToQueue(result.id)"
  76. href="#"
  77. ><i class="material-icons icon-with-button"
  78. >add</i
  79. >Add to queue
  80. </a>
  81. </td>
  82. </tr>
  83. </tbody>
  84. </table>
  85. <hr style="margin: 30px 0;" />
  86. <aside
  87. id="playlist-to-queue-selection"
  88. v-if="
  89. loggedIn &&
  90. station.type === 'community' &&
  91. playlists.length > 0
  92. "
  93. >
  94. <h4 class="modal-section-title">Choose a playlist</h4>
  95. <p class="modal-section-description">
  96. Choose one of your playlists to add to the queue.
  97. </p>
  98. <br />
  99. <div id="playlists">
  100. <div
  101. class="playlist"
  102. v-for="(playlist, index) in playlists"
  103. :key="index"
  104. >
  105. <playlist-item :playlist="playlist">
  106. <div slot="actions">
  107. <a
  108. class="button is-danger"
  109. v-on:click="addSongToQueue(result.id)"
  110. href="#"
  111. @click="
  112. togglePlaylistSelection(
  113. playlist._id
  114. )
  115. "
  116. v-if="isPlaylistSelected(playlist._id)"
  117. >
  118. <i
  119. class="material-icons icon-with-button"
  120. >stop</i
  121. >
  122. Stop playing
  123. </a>
  124. <a
  125. class="button is-success"
  126. v-on:click="addSongToQueue(result.id)"
  127. href="#"
  128. @click="
  129. togglePlaylistSelection(
  130. playlist._id
  131. )
  132. "
  133. v-else
  134. ><i
  135. class="material-icons icon-with-button"
  136. >play_arrow</i
  137. >Play in queue
  138. </a>
  139. </div>
  140. </playlist-item>
  141. </div>
  142. </div>
  143. </aside>
  144. </div>
  145. </div>
  146. </modal>
  147. </template>
  148. <script>
  149. import { mapState, mapActions } from "vuex";
  150. import Toast from "toasters";
  151. import PlaylistItem from "../PlaylistItem.vue";
  152. import Modal from "./Modal.vue";
  153. import io from "../../io";
  154. export default {
  155. data() {
  156. return {
  157. querySearch: "",
  158. queryResults: [],
  159. playlists: [],
  160. importQuery: ""
  161. };
  162. },
  163. computed: mapState({
  164. loggedIn: state => state.user.auth.loggedIn,
  165. station: state => state.station.station,
  166. privatePlaylistQueueSelected: state =>
  167. state.station.privatePlaylistQueueSelected
  168. }),
  169. methods: {
  170. isPlaylistSelected(playlistId) {
  171. return this.privatePlaylistQueueSelected === playlistId;
  172. },
  173. togglePlaylistSelection(playlistId) {
  174. console.log(this.isPlaylistSelected(playlistId), "sleect toggle");
  175. if (this.station.type === "community") {
  176. if (this.isPlaylistSelected(playlistId)) {
  177. this.updatePrivatePlaylistQueueSelected(null);
  178. } else {
  179. console.log("1");
  180. this.updatePrivatePlaylistQueueSelected(playlistId);
  181. this.$parent.addFirstPrivatePlaylistSongToQueue();
  182. console.log(this.isPlaylistSelected(playlistId));
  183. }
  184. }
  185. },
  186. addSongToQueue(songId) {
  187. console.log(this.station.type);
  188. if (this.station.type === "community") {
  189. this.socket.emit(
  190. "stations.addToQueue",
  191. this.station._id,
  192. songId,
  193. data => {
  194. if (data.status !== "success")
  195. new Toast({
  196. content: `Error: ${data.message}`,
  197. timeout: 8000
  198. });
  199. else
  200. new Toast({
  201. content: `${data.message}`,
  202. timeout: 4000
  203. });
  204. }
  205. );
  206. } else {
  207. this.socket.emit("queueSongs.add", songId, data => {
  208. if (data.status !== "success")
  209. new Toast({
  210. content: `Error: ${data.message}`,
  211. timeout: 8000
  212. });
  213. else
  214. new Toast({
  215. content: `${data.message}`,
  216. timeout: 4000
  217. });
  218. });
  219. }
  220. },
  221. importPlaylist() {
  222. new Toast({
  223. content:
  224. "Starting to import your playlist. This can take some time to do.",
  225. timeout: 4000
  226. });
  227. this.socket.emit(
  228. "queueSongs.addSetToQueue",
  229. this.importQuery,
  230. res => {
  231. return new Toast({ content: res.message, timeout: 4000 });
  232. }
  233. );
  234. },
  235. submitQuery() {
  236. let query = this.querySearch;
  237. if (!this.querySearch)
  238. return new Toast({
  239. content: "Please input a search query or a YouTube link",
  240. timeout: 4000
  241. });
  242. if (query.indexOf("&index=") !== -1) {
  243. query = query.split("&index=");
  244. query.pop();
  245. query = query.join("");
  246. }
  247. if (query.indexOf("&list=") !== -1) {
  248. query = query.split("&list=");
  249. query.pop();
  250. query = query.join("");
  251. }
  252. return this.socket.emit("apis.searchYoutube", query, res => {
  253. if (res.status === "failure")
  254. return new Toast({
  255. content: "Error searching on YouTube",
  256. timeout: 4000
  257. });
  258. const { data } = res;
  259. this.queryResults = [];
  260. console.log(res.data);
  261. for (let i = 0; i < data.items.length; i += 1) {
  262. this.queryResults.push({
  263. id: data.items[i].id.videoId,
  264. url: `https://www.youtube.com/watch?v=${this.id}`,
  265. title: data.items[i].snippet.title,
  266. thumbnail: data.items[i].snippet.thumbnails.default.url
  267. });
  268. }
  269. return this.queryResults;
  270. });
  271. },
  272. ...mapActions("station", ["updatePrivatePlaylistQueueSelected"]),
  273. ...mapActions("user/playlists", ["editPlaylist"])
  274. },
  275. mounted() {
  276. io.getSocket(socket => {
  277. this.socket = socket;
  278. this.socket.emit("playlists.indexForUser", res => {
  279. if (res.status === "success") this.playlists = res.data;
  280. });
  281. });
  282. },
  283. components: { Modal, PlaylistItem }
  284. };
  285. </script>
  286. <style lang="scss" scoped>
  287. @import "styles/global.scss";
  288. tr td {
  289. vertical-align: middle;
  290. }
  291. .song-thumbnail {
  292. padding-left: 0;
  293. }
  294. .song-actions {
  295. padding-right: 0;
  296. .button {
  297. height: 36px;
  298. }
  299. }
  300. .song-thumbnail div {
  301. width: 96px;
  302. height: 54px;
  303. background-position: center;
  304. background-repeat: no-repeat;
  305. }
  306. .table {
  307. margin-bottom: 0;
  308. }
  309. .night-mode {
  310. .modal-section {
  311. color: #000;
  312. }
  313. div {
  314. color: #4d4d4d;
  315. }
  316. }
  317. .modal-section {
  318. margin-top: 20px;
  319. padding: 10px 20px;
  320. background-color: #f5f5f5;
  321. }
  322. .modal-section-title {
  323. font-size: 26px;
  324. margin: 0px;
  325. }
  326. .modal-section-description {
  327. margin-bottom: 5px;
  328. }
  329. #playlist-to-queue-selection {
  330. margin-top: 0;
  331. #playlists {
  332. font-size: 18px;
  333. .radio {
  334. display: flex;
  335. flex-direction: row;
  336. align-items: center;
  337. input {
  338. transform: scale(1.25);
  339. }
  340. }
  341. }
  342. }
  343. #youtube-search-input {
  344. .control {
  345. margin-right: 0px;
  346. }
  347. input {
  348. height: 36px;
  349. border-radius: 3px 0 3px 0;
  350. }
  351. .button {
  352. height: 36px;
  353. border-radius: 0 3px 3px 0;
  354. }
  355. }
  356. .vertical-padding {
  357. padding: 20px;
  358. }
  359. #playlists {
  360. .playlist {
  361. .button {
  362. width: 146px;
  363. }
  364. }
  365. }
  366. </style>