123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896 |
- <script setup lang="ts">
- /* eslint vue/no-unused-vars: 1 */
- /* eslint @typescript-eslint/no-unused-vars: 1 */
- import Toast from "toasters";
- import {
- defineAsyncComponent,
- ref,
- reactive,
- computed,
- onMounted,
- watch
- } from "vue";
- import { GenericResponse } from "@musare_types/actions/GenericActions";
- import VueJsonPretty from "vue-json-pretty";
- import { storeToRefs } from "pinia";
- import { useForm } from "@/composables/useForm";
- import { useWebsocketsStore } from "@/stores/websockets";
- import { useModalsStore } from "@/stores/modals";
- import { useLongJobsStore } from "@/stores/longJobs";
- import { useImportArtistStore } from "@/stores/importArtist";
- import "vue-json-pretty/lib/styles.css";
- import utils from "@/utils";
- // import { TempDraggableList } from "vue-draggable-list";
- const TempDraggableList = defineAsyncComponent(
- () => import("@/components/TempDraggableList.vue")
- );
- const TempYoutubeChannelCard = defineAsyncComponent(
- () => import("@/components/TempYoutubeChannelCard.vue")
- );
- const TempYoutubeVideoCard = defineAsyncComponent(
- () => import("@/components/TempYoutubeVideoCard.vue")
- );
- const Modal = defineAsyncComponent(() => import("@/components/Modal.vue"));
- const SaveButton = defineAsyncComponent(
- () => import("@/components/SaveButton.vue")
- );
- const props = defineProps({
- modalUuid: { type: String, required: true },
- artistId: { type: String, default: null }
- });
- const importArtistStore = useImportArtistStore({
- modalUuid: props.modalUuid
- })();
- const {
- artist,
- youtubeChannels,
- hideYoutubeChannel,
- youtubeVideoMap,
- youtubeVideoIds,
- linkedVideos,
- recordingsReleasesReleaseGroups,
- filterYoutubeVideos,
- recordingFilters,
- recordingSort,
- youtubeVideosSort,
- youtubeVideoTitleChanges,
- youtubeVideoFilters,
- hideYoutubeInfo,
- hideMusicbrainzInfo,
- youtubeChannelIds,
- // getters
- recordings,
- youtubeVideoMapAdjusted,
- filteredRecordings,
- filteredYoutubeVideoIds,
- filteredYoutubeVideosIdsLength
- } = storeToRefs(importArtistStore);
- const {
- setArtist,
- setYoutubeChannels,
- setYoutubeVideos,
- setMusicBrainzRecordingsReleasesReleaseGroups,
- linkVideos
- } = importArtistStore;
- const { socket } = useWebsocketsStore();
- const { closeCurrentModal } = useModalsStore();
- onMounted(() => {
- socket.onConnect(() => {
- if (props.artistId) {
- socket.dispatch(`artists.getArtistFromId`, props.artistId, res => {
- // res: GetArtistResponse
- if (res.status === "success") {
- setArtist(res.data.artist);
- socket.dispatch(
- `youtube.getChannelsById`,
- youtubeChannelIds.value,
- res => {
- // TODO handle fail
- const { data } = res;
- setYoutubeChannels(data);
- }
- );
- socket.dispatch(
- `youtube.getVideosForChannelIds`,
- youtubeChannelIds.value,
- res => {
- console.log(333222111, res);
- const { data } = res;
- setYoutubeVideos(data);
- }
- );
- socket.dispatch(
- `albums.getMusicBrainzRecordingsReleasesReleaseGroups`,
- artist.value.musicbrainzIdentifier,
- res => {
- const { data } = res;
- setMusicBrainzRecordingsReleasesReleaseGroups(
- data.releases
- );
- }
- );
- } else {
- new Toast("Artist with that ID not found.");
- closeCurrentModal();
- }
- });
- }
- });
- });
- const drag = ref(false);
- const repositionYoutubeVideo = () => {
- console.log("Reposition youtube video");
- };
- </script>
- <template>
- <modal
- class="import-artist-mb-modal"
- title="Import Artist MB"
- :size="'wide'"
- :split="true"
- >
- <template #body>
- <div class="columns flex flex-row w-full">
- <div class="column-left flex flex-column">
- <div class="card artist-info-card flex flex-row">
- <img src="/assets/notes.png" alt="Temp" />
- <div>
- <p>{{ artist.name }}</p>
- <p>
- MB:
- <a
- :href="`https://musicbrainz.org/artist/${artist.musicbrainzIdentifier}`"
- >{{ artist.musicbrainzIdentifier }}</a
- >
- </p>
- </div>
- </div>
- <div class="card recordings-card flex flex-column">
- <div
- class="flex flex-row"
- style="justify-content: space-between"
- >
- <p class="card-title">Recordings to display</p>
- <button class="button temp-button">
- <i class="material-icons">filter_alt</i>
- </button>
- </div>
- <div>
- Filter
- <div>
- <input
- type="checkbox"
- name="hideNullLength"
- id="hideNullLength"
- v-model="recordingFilters.hideNullLength"
- />
- <label for="hideNullLength"
- >Hide null length</label
- >
- </div>
- <div>
- <input
- type="checkbox"
- name="hidePartOf"
- id="hidePartOf"
- v-model="recordingFilters.hidePartOf"
- />
- <label for="hidePartOf">Hide part of</label>
- </div>
- </div>
- <div>
- Sort
- <div>
- <select
- name="recordingSort"
- id="recordingSort"
- v-model="recordingSort"
- >
- <option value="title">Title</option>
- <option value="length">Length</option>
- </select>
- </div>
- </div>
- <div>
- Hide info
- <div>
- <input
- type="checkbox"
- name="hideMusicbrainzInfo"
- id="hideMusicbrainzInfo"
- v-model="hideMusicbrainzInfo"
- />
- <label for="hideMusicbrainzInfo"
- >Hide MusicBrainz info</label
- >
- </div>
- </div>
- <div class="flex flex-column recordings">
- <div
- v-for="recording in filteredRecordings"
- :key="recording.id"
- class="recording flex flex-row"
- :class="recording.hide ? 'recording-hide' : ''"
- >
- <p>
- <span>{{ recording.title }}</span>
- <span v-if="recording.disambiguation"
- >({{ recording.disambiguation }})</span
- >
- </p>
- <span>{{
- utils.formatTime(recording.length / 1000)
- }}</span>
- <div class="icons">
- <tippy
- theme="info"
- v-if="!hideMusicbrainzInfo"
- >
- <i class="material-icons">info</i>
- <template #content>
- <div>
- <ul>
- <li>
- <p>
- Title:
- {{
- recording.title
- }}
- </p>
- </li>
- <li>
- <p>
- Video:
- {{
- recording.video
- ? "true"
- : "false"
- }}
- </p>
- </li>
- <li>
- <p>
- ID:
- {{ recording.id }}
- </p>
- </li>
- <li>
- <p>
- Length:
- {{
- recording.length
- }}
- </p>
- </li>
- <li
- v-if="
- recording.disambiguation
- "
- >
- <p>
- Disambiguation:
- {{
- recording.disambiguation
- }}
- </p>
- </li>
- </ul>
- </div>
- </template>
- </tippy>
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="column-center flex flex-column">
- <div class="card linking-card flex flex-column">
- <div
- class="flex flex-row"
- style="justify-content: space-between"
- >
- <p class="card-title">Linking</p>
- <div class="button-icons flex flex-row">
- <i class="material-icons">settings</i>
- <i class="material-icons">lock_open</i>
- </div>
- </div>
- <div>
- <button
- class="button is-primary"
- @click="linkVideos"
- >
- Link
- </button>
- </div>
- <div class="recordings flex flex-column">
- <div
- class="recording flex flex-column"
- v-for="recording in filteredRecordings"
- :key="recording.id"
- v-show="!recording.hide"
- >
- <div class="flex flex-row">
- <p>
- {{ recording.title }}
- <span v-if="recording.disambiguation"
- >({{
- recording.disambiguation
- }})</span
- >
- </p>
- <span>{{
- utils.formatTime(
- recording.length / 1000
- )
- }}</span>
- <div class="button-icons flex flex-row">
- <tippy
- theme="info"
- v-if="!hideMusicbrainzInfo"
- >
- <i class="material-icons">info</i>
- <template #content>
- <div>
- <ul>
- <li>
- <p>
- Title:
- {{
- recording.title
- }}
- </p>
- </li>
- <li>
- <p>
- Video:
- {{
- recording.video
- ? "true"
- : "false"
- }}
- </p>
- </li>
- <li>
- <p>
- ID:
- {{
- recording.id
- }}
- </p>
- </li>
- <li>
- <p>
- Length:
- {{
- recording.length
- }}
- </p>
- </li>
- <li
- v-if="
- recording.disambiguation
- "
- >
- <p>
- Disambiguation:
- {{
- recording.disambiguation
- }}
- </p>
- </li>
- </ul>
- </div>
- </template>
- </tippy>
- <button class="button temp-button">
- <i class="material-icons"
- >lock_open</i
- >
- </button>
- </div>
- </div>
- <div class="youtube-videos flex flex-column">
- <!-- <div class="youtube-video no-youtube-video" v-if="!linkedVideos[recording.id] || linkedVideos[recording.id].length === 0">
- <p>No videos linked</p>
- </div> -->
- <temp-draggable-list
- v-if="linkedVideos[recording.id]"
- v-model:list="
- linkedVideos[recording.id]
- "
- @start="drag = true"
- @end="drag = false"
- @update="repositionYoutubeVideo"
- group="mb-youtube-videos"
- :unique="true"
- debug-name="linked-videos"
- >
- <template
- #item="{
- element: youtubeVideoId,
- index
- }"
- >
- <temp-youtube-video-card
- :youtube-video="
- youtubeVideoMapAdjusted[
- youtubeVideoId
- ]
- "
- :hide-youtube-info="
- hideYoutubeInfo
- "
- :key="youtubeVideoId"
- ></temp-youtube-video-card>
- </template>
- <template #empty-list-placeholder>
- <div
- class="youtube-video no-youtube-video"
- >
- <p>No videos linked</p>
- </div>
- </template>
- </temp-draggable-list>
- </div>
- </div>
- </div>
- </div>
- </div>
- <div class="column-right flex flex-column">
- <div class="card youtube-channels-card">
- <p class="card-title">
- YouTube channels / playlists card
- </p>
- <div class="youtube-channels flex flex-column">
- <temp-youtube-channel-card
- v-for="youtubeChannel in youtubeChannels"
- :youtube-channel="youtubeChannel"
- :key="youtubeChannel.channelId"
- >
- </temp-youtube-channel-card>
- </div>
- </div>
- <div class="card youtube-videos-card">
- <p class="card-title">
- YouTube videos card ({{
- filteredYoutubeVideosIdsLength
- }}
- / {{ youtubeVideoIds.length }})
- </p>
- <div>
- Filter
- <div>
- <input
- type="checkbox"
- name="teaser"
- v-model="youtubeVideoFilters.teaser"
- />
- <label for="teaser">Teaser</label>
- </div>
- <div>
- <input
- type="checkbox"
- name="under45"
- v-model="youtubeVideoFilters.under45"
- />
- <label for="under45">Under 45s</label>
- </div>
- <div>
- <input
- type="checkbox"
- name="live"
- v-model="youtubeVideoFilters.live"
- />
- <label for="live">Live</label>
- </div>
- <div>
- <input
- type="checkbox"
- name="tour"
- v-model="youtubeVideoFilters.tour"
- />
- <label for="tour">Tour</label>
- </div>
- </div>
- <div>
- Name
- <div>
- <input
- type="checkbox"
- name="artistDash"
- v-model="
- youtubeVideoTitleChanges.artistDash
- "
- />
- <label for="artistDash">artistDash</label>
- </div>
- <div>
- <input
- type="checkbox"
- name="parantheses"
- v-model="
- youtubeVideoTitleChanges.parantheses
- "
- />
- <label for="parantheses">parantheses</label>
- </div>
- <div>
- <input
- type="checkbox"
- name="brackets"
- v-model="youtubeVideoTitleChanges.brackets"
- />
- <label for="brackets">brackets</label>
- </div>
- <div>
- <input
- type="checkbox"
- name="commonPhrases"
- v-model="
- youtubeVideoTitleChanges.commonPhrases
- "
- />
- <label for="commonPhrases"
- >common phrases (official mv, official
- audio, official video)</label
- >
- </div>
- </div>
- <div>
- Sort
- <div>
- <select
- name="youtubeVideosSort"
- id="youtubeVideosSort"
- v-model="youtubeVideosSort"
- >
- <option value="title">Title</option>
- <option value="length">Length</option>
- </select>
- </div>
- </div>
- <div>
- Hide info
- <div>
- <input
- type="checkbox"
- name="hideYoutubeInfo"
- id="hideYoutubeInfo"
- v-model="hideYoutubeInfo"
- />
- <label for="hideYoutubeInfo"
- >Hide YouTube info</label
- >
- </div>
- </div>
- <div>
- Hide YouTube channels
- <div
- v-for="youtubeChannel in youtubeChannels"
- :key="youtubeChannel.rawData.id"
- >
- <input
- type="checkbox"
- :name="youtubeChannel.rawData.id"
- :id="youtubeChannel.rawData.id"
- v-model="
- hideYoutubeChannel[
- youtubeChannel.rawData.id
- ]
- "
- />
- <label :for="youtubeChannel.rawData.id"
- >Hide {{ youtubeChannel.title }}</label
- >
- </div>
- </div>
- <div>
- Filter
- <input type="text" v-model="filterYoutubeVideos" />
- </div>
- <div class="flex flex-column youtube-videos">
- <temp-draggable-list
- v-if="filteredYoutubeVideoIds.length > 0"
- v-model:list="filteredYoutubeVideoIds"
- @start="drag = true"
- @end="drag = false"
- @update="repositionYoutubeVideo"
- :read-only="true"
- group="mb-youtube-videos"
- debug-name="youtube-videos"
- >
- <template
- #item="{ element: youtubeVideoId, index }"
- >
- <temp-youtube-video-card
- :youtube-video="
- youtubeVideoMapAdjusted[
- youtubeVideoId
- ]
- "
- :hide-youtube-info="hideYoutubeInfo"
- :key="youtubeVideoId"
- ></temp-youtube-video-card>
- </template>
- </temp-draggable-list>
- </div>
- </div>
- </div>
- </div>
- </template>
- <template #footer>
- <div>
- <!-- <save-button
- v-if="!importMode"
- :default-message="`${createArtist ? 'Create' : 'Update'} Artist`"
- @clicked="saveArtist()"
- />
- <save-button
- v-if="!importMode"
- :default-message="`${createArtist ? 'Create' : 'Update'} and close`"
- @clicked="saveArtist(true)"
- />
- <button
- v-if="!createArtist"
- class="button is-primary"
- @click="toggleImportMode()"
- >
- <span v-if="importMode">Edit Mode</span>
- <span v-else>Import Mode</span>
- </button> -->
- </div>
- </template>
- </modal>
- </template>
- <style lang="less">
- // .night-mode {
- // .edit-artist-modal {
- // .vjs-tree-node.is-highlight,
- // .vjs-tree-node:hover {
- // background: black;
- // }
- // }
- // }
- .import-artist-mb-modal {
- .modal-card {
- width: 100% !important;
- // height: 100%;
- }
- }
- </style>
- <style lang="less" scoped>
- .flex {
- display: flex;
- }
- .flex-column {
- flex-direction: column;
- }
- .flex-row {
- flex-direction: row;
- }
- .w-full {
- width: 100%;
- }
- .card {
- background-color: var(--dark-grey-3);
- border-radius: @border-radius;
- }
- .columns {
- gap: 16px;
- max-height: 100%;
- }
- .column-left,
- .column-right {
- width: 25%;
- max-height: 100%;
- overflow: auto;
- }
- .column-center {
- flex: 1;
- }
- .column-left,
- .column-center,
- .column-right {
- gap: 16px;
- }
- .artist-info-card {
- padding: 8px;
- gap: 8px;
- // Temp
- img {
- aspect-ratio: 1/1;
- max-width: 100px;
- }
- p:first-of-type {
- font-size: 20px;
- }
- p:last-of-type {
- font-size: 14px;
- }
- }
- .card-title {
- font-size: 20px;
- line-height: 24px;
- }
- .recordings-card {
- padding: 8px;
- height: 100%;
- gap: 16px;
- // font-size: 20px;
- .recordings {
- gap: 8px;
- .recording {
- background-color: var(--dark-grey-2);
- border-radius: @border-radius;
- padding: 8px;
- font-size: 16px;
- gap: 8px;
- align-items: center;
- > p {
- flex: 1;
- line-height: 24px;
- display: flex;
- flex-direction: column;
- // gap: 4px;
- span:nth-child(2) {
- font-size: 12px;
- line-height: 16px;
- }
- }
- .icons {
- height: 24px;
- }
- &.recording-hide {
- opacity: 0.5;
- }
- }
- }
- }
- .linking-card {
- padding: 8px;
- height: 100%;
- gap: 16px;
- > div > p {
- // font-size: 20px;
- // line-height: 24px;
- .button-icons {
- gap: 8px;
- padding: 8px;
- }
- }
- .recordings {
- gap: 8px;
- overflow: auto;
- max-height: 100%;
- .recording {
- background-color: var(--dark-grey-2);
- border-radius: @border-radius;
- padding: 8px;
- gap: 8px;
- > div:first-child {
- gap: 8px;
- align-items: center;
- p {
- font-size: 16px;
- line-height: 20px;
- flex: 1;
- span {
- font-size: 12px;
- line-height: 20px;
- }
- }
- > span {
- font-size: 14px;
- line-height: 20px;
- }
- .button-icons {
- }
- }
- .youtube-videos {
- gap: 8px;
- .youtube-video {
- background-color: var(--dark-grey);
- border-radius: @border-radius;
- gap: 8px;
- min-width: 350px;
- }
- .no-youtube-video {
- padding: 8px;
- }
- }
- }
- }
- }
- .youtube-channels-card {
- padding: 8px;
- .youtube-channels {
- gap: 8px;
- margin-top: 16px;
- }
- }
- .youtube-videos-card {
- padding: 8px;
- .youtube-videos {
- gap: 8px;
- margin-top: 16px;
- .youtube-video {
- background-color: var(--dark-grey);
- border-radius: @border-radius;
- gap: 8px;
- min-width: 350px;
- }
- }
- }
- .temp-button {
- padding: 0;
- margin: 0;
- background: none;
- color: white;
- outline: none;
- border: none;
- }
- </style>
|