SongThumbnail.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. <script setup lang="ts">
  2. import { ref, computed, watch } from "vue";
  3. const props = defineProps({
  4. song: { type: Object, default: () => {} },
  5. fallback: { type: Boolean, default: true }
  6. });
  7. const emit = defineEmits(["loadError"]);
  8. const loadError = ref(0);
  9. const isYoutubeThumbnail = computed(
  10. () =>
  11. ((props.song.mediaSource &&
  12. props.song.mediaSource.startsWith("youtube:")) ||
  13. props.song.youtubeId) &&
  14. ((props.song.thumbnail &&
  15. (props.song.thumbnail.lastIndexOf("i.ytimg.com") !== -1 ||
  16. props.song.thumbnail.lastIndexOf("img.youtube.com") !== -1)) ||
  17. (props.fallback &&
  18. (!props.song.thumbnail ||
  19. (props.song.thumbnail &&
  20. (props.song.thumbnail.lastIndexOf(
  21. "notes-transparent"
  22. ) !== -1 ||
  23. props.song.thumbnail.lastIndexOf(
  24. "/assets/notes.png"
  25. ) !== -1 ||
  26. props.song.thumbnail === "empty")) ||
  27. loadError.value === 1)))
  28. );
  29. const onLoadError = () => {
  30. // Error codes
  31. // -1 - Error occured, fallback disabled
  32. // 0 - No errors
  33. // 1 - Error occured with thumbnail, fallback enabled
  34. // 2 - Error occured with youtube thumbnail, fallback enabled
  35. if (!props.fallback) loadError.value = -1;
  36. else if (loadError.value === 0 && !isYoutubeThumbnail.value)
  37. loadError.value = 1;
  38. else loadError.value = 2;
  39. emit("loadError", loadError.value);
  40. };
  41. watch(
  42. () => props.song,
  43. () => {
  44. loadError.value = 0;
  45. emit("loadError", loadError.value);
  46. }
  47. );
  48. </script>
  49. <template>
  50. <div
  51. :class="{
  52. thumbnail: true,
  53. 'youtube-thumbnail': isYoutubeThumbnail
  54. }"
  55. >
  56. <slot name="icon" />
  57. <div
  58. v-if="-1 < loadError && loadError < 2 && isYoutubeThumbnail"
  59. class="thumbnail-bg"
  60. :style="{
  61. 'background-image':
  62. 'url(' +
  63. `https://img.youtube.com/vi/${
  64. song.mediaSource
  65. ? song.mediaSource.split(':')[1]
  66. : song.youtubeId
  67. }/mqdefault.jpg` +
  68. ')'
  69. }"
  70. ></div>
  71. <div
  72. v-if="-1 < loadError && loadError < 2 && !isYoutubeThumbnail"
  73. class="thumbnail-bg"
  74. :style="{
  75. 'background-image': `url(${song.thumbnail})`
  76. }"
  77. ></div>
  78. <img
  79. v-if="-1 < loadError && loadError < 2 && isYoutubeThumbnail"
  80. loading="lazy"
  81. :src="`https://img.youtube.com/vi/${
  82. song.mediaSource
  83. ? song.mediaSource.split(':')[1]
  84. : song.youtubeId
  85. }/mqdefault.jpg`"
  86. @error="onLoadError"
  87. />
  88. <img
  89. v-else-if="loadError <= 0"
  90. loading="lazy"
  91. :src="song.thumbnail"
  92. @error="onLoadError"
  93. />
  94. <img v-else loading="lazy" src="/assets/notes-transparent.png" />
  95. </div>
  96. </template>
  97. <style lang="less">
  98. .thumbnail {
  99. min-width: 130px;
  100. height: 130px;
  101. position: relative;
  102. margin-top: -15px;
  103. margin-bottom: -15px;
  104. margin-left: -10px;
  105. // .yt-thumbnail-bg {
  106. // display: none;
  107. // }
  108. img {
  109. // height: 100%;
  110. width: 100%;
  111. margin-top: auto;
  112. margin-bottom: auto;
  113. z-index: 1;
  114. position: absolute;
  115. top: 0;
  116. bottom: 0;
  117. left: 0;
  118. right: 0;
  119. }
  120. .thumbnail-bg {
  121. height: 100%;
  122. width: 100%;
  123. display: block;
  124. position: absolute;
  125. top: 0;
  126. filter: blur(2px);
  127. background: url("/assets/notes-transparent.png") no-repeat center center;
  128. background-size: cover;
  129. }
  130. // &.youtube-thumbnail {
  131. // img {
  132. // height: auto;
  133. // }
  134. // }
  135. }
  136. </style>