SongThumbnail.vue 2.7 KB

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