ViewMedia.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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. import { SoundcloudTrack } from "@/components/SoundcloudTrackInfo.vue";
  16. const YoutubeVideoInfo = defineAsyncComponent(
  17. () => import("@/components/YoutubeVideoInfo.vue")
  18. );
  19. const YoutubePlayer = defineAsyncComponent(
  20. () => import("@/components/YoutubePlayer.vue")
  21. );
  22. const SoundcloudTrackInfo = defineAsyncComponent(
  23. () => import("@/components/SoundcloudTrackInfo.vue")
  24. );
  25. const props = defineProps({
  26. modalUuid: { type: String, required: true },
  27. mediaSource: { type: String, default: null }
  28. });
  29. const youtubeVideo = ref<YoutubeVideo>({
  30. _id: null,
  31. youtubeId: null,
  32. title: null,
  33. author: null,
  34. duration: 0
  35. });
  36. const soundcloudTrack = ref<SoundcloudTrack>({
  37. _id: null,
  38. trackId: null,
  39. userPermalink: null,
  40. permalink: null,
  41. title: null,
  42. username: null,
  43. duration: 0,
  44. soundcloudCreatedAt: null,
  45. artworkUrl: null
  46. });
  47. const currentSongMediaType = computed(() => props.mediaSource.split(":")[0]);
  48. const currentSongMediaValue = computed(() => props.mediaSource.split(":")[1]);
  49. const youtubeSongNormalized = computed(() => ({
  50. mediaSource: `youtube:${youtubeVideo.value.youtubeId}`,
  51. title: youtubeVideo.value.title,
  52. artists: [youtubeVideo.value.author],
  53. duration: youtubeVideo.value.duration
  54. }));
  55. const songNormalized = computed(() => {
  56. if (currentSongMediaType.value === "youtube")
  57. return youtubeSongNormalized.value;
  58. return {};
  59. });
  60. const loaded = ref(false);
  61. const { openModal, closeCurrentModal } = useModalsStore();
  62. const { socket } = useWebsocketsStore();
  63. const userAuthStore = useUserAuthStore();
  64. const { hasPermission } = userAuthStore;
  65. const youtubeRemove = () => {
  66. socket.dispatch("youtube.removeVideos", youtubeVideo.value._id, res => {
  67. if (res.status === "success") {
  68. new Toast("YouTube video successfully removed.");
  69. closeCurrentModal();
  70. } else {
  71. new Toast("Youtube video with that ID not found.");
  72. }
  73. });
  74. };
  75. const remove = () => {
  76. if (currentSongMediaType.value === "youtube") youtubeRemove();
  77. else throw new Error("Not implemented.");
  78. };
  79. onMounted(() => {
  80. socket.onConnect(() => {
  81. loaded.value = false;
  82. if (currentSongMediaType.value === "youtube") {
  83. socket.dispatch(
  84. "youtube.getVideo",
  85. currentSongMediaValue.value,
  86. true,
  87. res => {
  88. if (res.status === "success") {
  89. youtubeVideo.value = res.data;
  90. loaded.value = true;
  91. socket.dispatch(
  92. "apis.joinRoom",
  93. `view-media.${props.mediaSource}`
  94. );
  95. } else {
  96. new Toast("YouTube video with that ID not found");
  97. closeCurrentModal();
  98. }
  99. }
  100. );
  101. socket.on(
  102. "event:youtubeVideo.removed",
  103. () => {
  104. new Toast("This YouTube video was removed.");
  105. closeCurrentModal();
  106. },
  107. { modalUuid: props.modalUuid }
  108. );
  109. } else if (currentSongMediaType.value === "soundcloud") {
  110. socket.dispatch(
  111. "soundcloud.getTrack",
  112. currentSongMediaValue.value,
  113. true,
  114. res => {
  115. if (res.status === "success") {
  116. soundcloudTrack.value = res.data;
  117. loaded.value = true;
  118. socket.dispatch(
  119. "apis.joinRoom",
  120. `view-media.${props.mediaSource}`
  121. );
  122. } else {
  123. new Toast("SoundCloud track with that ID not found");
  124. closeCurrentModal();
  125. }
  126. }
  127. );
  128. }
  129. });
  130. });
  131. onBeforeUnmount(() => {
  132. loaded.value = false;
  133. socket.dispatch(
  134. "apis.leaveRoom",
  135. `view-media.${props.mediaSource}`,
  136. () => {}
  137. );
  138. });
  139. </script>
  140. <template>
  141. <modal title="View Media">
  142. <template #body>
  143. <template v-if="loaded">
  144. <template v-if="currentSongMediaType === 'youtube'">
  145. <youtube-video-info :video="youtubeVideo" />
  146. <youtube-player :song="youtubeSongNormalized" />
  147. </template>
  148. <template v-else-if="currentSongMediaType === 'soundcloud'">
  149. <soundcloud-track-info :track="soundcloudTrack" />
  150. <!-- <youtube-player :song="youtubeSongNormalized" /> -->
  151. </template>
  152. </template>
  153. <div v-else class="vertical-padding">
  154. <p>Media hasn't loaded yet</p>
  155. </div>
  156. </template>
  157. <template #footer>
  158. <button
  159. v-if="
  160. hasPermission('songs.create') ||
  161. hasPermission('songs.update')
  162. "
  163. class="button is-primary icon-with-button material-icons"
  164. @click.prevent="
  165. openModal({
  166. modal: 'editSong',
  167. props: {
  168. song: {
  169. mediaSource,
  170. ...songNormalized
  171. }
  172. }
  173. })
  174. "
  175. content="Create/edit song from video"
  176. v-tippy
  177. >
  178. music_note
  179. </button>
  180. <div class="right">
  181. <button
  182. v-if="
  183. currentSongMediaType === 'youtube' &&
  184. hasPermission('youtube.removeVideos')
  185. "
  186. class="button is-danger icon-with-button material-icons"
  187. @click.prevent="
  188. openModal({
  189. modal: 'confirm',
  190. props: {
  191. message:
  192. 'Removing this video will remove it from all playlists and cause a ratings recalculation.',
  193. onCompleted: remove
  194. }
  195. })
  196. "
  197. content="Delete Video"
  198. v-tippy
  199. >
  200. delete_forever
  201. </button>
  202. </div>
  203. </template>
  204. </modal>
  205. </template>
  206. <style lang="less" scoped></style>