ViewSong.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. <script setup lang="ts">
  2. import {
  3. defineAsyncComponent,
  4. onMounted,
  5. onBeforeUnmount,
  6. ref,
  7. computed
  8. } from "vue";
  9. import Toast from "toasters";
  10. import { useWebsocketsStore } from "@/stores/websockets";
  11. import { useModalsStore } from "@/stores/modals";
  12. import { useUserAuthStore } from "@/stores/userAuth";
  13. import Modal from "@/components/Modal.vue";
  14. import { YoutubeVideo } from "@/components/YoutubeVideoInfo.vue";
  15. const YoutubeVideoInfo = defineAsyncComponent(
  16. () => import("@/components/YoutubeVideoInfo.vue")
  17. );
  18. const YoutubePlayer = defineAsyncComponent(
  19. () => import("@/components/YoutubePlayer.vue")
  20. );
  21. const props = defineProps({
  22. modalUuid: { type: String, required: true },
  23. mediaSource: { type: String, default: null }
  24. });
  25. const youtubeVideo = ref<YoutubeVideo>({
  26. _id: null,
  27. youtubeId: null,
  28. title: null,
  29. author: null,
  30. duration: 0
  31. });
  32. const currentSongMediaType = computed(() => props.mediaSource.split(":")[0]);
  33. const currentSongMediaValue = computed(() => props.mediaSource.split(":")[1]);
  34. const loaded = ref(false);
  35. const { openModal, closeCurrentModal } = useModalsStore();
  36. const { socket } = useWebsocketsStore();
  37. const userAuthStore = useUserAuthStore();
  38. const { hasPermission } = userAuthStore;
  39. const youtubeRemove = () => {
  40. socket.dispatch("youtube.removeVideos", youtubeVideo.value._id, res => {
  41. if (res.status === "success") {
  42. new Toast("YouTube video successfully removed.");
  43. closeCurrentModal();
  44. } else {
  45. new Toast("Youtube video with that ID not found.");
  46. }
  47. });
  48. };
  49. const remove = () => {
  50. if (currentSongMediaType.value === "youtube") youtubeRemove();
  51. else throw new Error("Not implemented.");
  52. };
  53. onMounted(() => {
  54. socket.onConnect(() => {
  55. loaded.value = false;
  56. socket.dispatch(
  57. "youtube.getVideo",
  58. currentSongMediaValue.value,
  59. true,
  60. res => {
  61. if (res.status === "success") {
  62. youtubeVideo.value = res.data;
  63. loaded.value = true;
  64. socket.dispatch(
  65. "apis.joinRoom",
  66. `view-youtube-video.${youtubeVideo.value._id}`
  67. );
  68. } else {
  69. new Toast("YouTube video with that ID not found");
  70. closeCurrentModal();
  71. }
  72. }
  73. );
  74. });
  75. socket.on(
  76. "event:youtubeVideo.removed",
  77. () => {
  78. new Toast("This YouTube video was removed.");
  79. closeCurrentModal();
  80. },
  81. { modalUuid: props.modalUuid }
  82. );
  83. });
  84. onBeforeUnmount(() => {
  85. loaded.value = false;
  86. socket.dispatch(
  87. "apis.leaveRoom",
  88. `view-song.${youtubeVideo.value._id}`,
  89. () => {}
  90. );
  91. });
  92. </script>
  93. <template>
  94. <modal title="View Song">
  95. <template #body>
  96. <youtube-video-info v-if="loaded" :video="youtubeVideo" />
  97. <youtube-player
  98. v-if="loaded"
  99. :song="{
  100. mediaSource: `youtube:${youtubeVideo.youtubeId}`,
  101. title: youtubeVideo.title,
  102. artists: [youtubeVideo.author],
  103. duration: youtubeVideo.duration
  104. }"
  105. />
  106. <div v-if="!loaded" class="vertical-padding">
  107. <p>Video hasn't loaded yet</p>
  108. </div>
  109. </template>
  110. <template #footer>
  111. <button
  112. v-if="
  113. hasPermission('songs.create') ||
  114. hasPermission('songs.update')
  115. "
  116. class="button is-primary icon-with-button material-icons"
  117. @click.prevent="
  118. openModal({
  119. modal: 'editSong',
  120. props: {
  121. song: {
  122. mediaSource: `youtube:${youtubeVideo.youtubeId}`,
  123. ...youtubeVideo
  124. }
  125. }
  126. })
  127. "
  128. content="Create/edit song from video"
  129. v-tippy
  130. >
  131. music_note
  132. </button>
  133. <div class="right">
  134. <button
  135. v-if="hasPermission('youtube.removeVideos')"
  136. class="button is-danger icon-with-button material-icons"
  137. @click.prevent="
  138. openModal({
  139. modal: 'confirm',
  140. props: {
  141. message:
  142. 'Removing this video will remove it from all playlists and cause a ratings recalculation.',
  143. onCompleted: remove
  144. }
  145. })
  146. "
  147. content="Delete Video"
  148. v-tippy
  149. >
  150. delete_forever
  151. </button>
  152. </div>
  153. </template>
  154. </modal>
  155. </template>
  156. <style lang="less" scoped></style>