mirror of
https://github.com/maciejpedzich/spotifyplaylistarchive.com.git
synced 2024-09-20 02:26:20 +02:00
Implement copying track URLs and JSON export
This commit is contained in:
parent
db14f68aa1
commit
7260c771a2
36
src/components/ExportToJsonLink.astro
Normal file
36
src/components/ExportToJsonLink.astro
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
import type { PlaylistSnapshot } from '@/models/playlist-snapshot';
|
||||
|
||||
interface Props {
|
||||
snapshot: PlaylistSnapshot;
|
||||
}
|
||||
|
||||
const { snapshot } = Astro.props;
|
||||
---
|
||||
|
||||
<script>
|
||||
import type { PlaylistSnapshot } from '@/models/playlist-snapshot';
|
||||
|
||||
class ExportToJsonLink extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
const snapshot = JSON.parse(this.dataset.snapshot!) as PlaylistSnapshot;
|
||||
const anchor = this.querySelector('a')!;
|
||||
|
||||
anchor.href = URL.createObjectURL(
|
||||
new Blob([JSON.stringify(snapshot, null, '\t')])
|
||||
);
|
||||
anchor.download = `${snapshot.original_name}.json`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('export-to-json-link', ExportToJsonLink);
|
||||
</script>
|
||||
|
||||
<export-to-json-link data-snapshot={JSON.stringify(snapshot)}>
|
||||
<a class="btn btn-ghost text-base normal-case">
|
||||
<i class="fa-solid fa-file-arrow-down mr-3"></i>
|
||||
Export to JSON
|
||||
</a>
|
||||
</export-to-json-link>
|
69
src/components/vue/CopyTrackUrlsButton.vue
Normal file
69
src/components/vue/CopyTrackUrlsButton.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
const props = defineProps<{ urls: string[] }>();
|
||||
|
||||
const textToCopy = ref('');
|
||||
const copySuccess = ref<boolean | null>(null);
|
||||
|
||||
const copyTrackUrls = async () => {
|
||||
try {
|
||||
copySuccess.value = null;
|
||||
textToCopy.value = props.urls.join('\n');
|
||||
|
||||
if (!navigator.clipboard) {
|
||||
throw new Error('Clipboard API is not supported');
|
||||
}
|
||||
|
||||
const { state: clipboardWritePerm } = await navigator.permissions.query({
|
||||
name: 'clipboard-write' as PermissionName
|
||||
});
|
||||
|
||||
if (clipboardWritePerm === 'denied') {
|
||||
throw new Error('Permission to write to clipboard has been denied');
|
||||
}
|
||||
|
||||
await navigator.clipboard.writeText(textToCopy.value);
|
||||
|
||||
copySuccess.value = true;
|
||||
setTimeout(() => (copySuccess.value = null), 5000);
|
||||
} catch (error) {
|
||||
copySuccess.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="btn btn-ghost hover:text-primary-focus text-base normal-case"
|
||||
@click="copyTrackUrls()"
|
||||
>
|
||||
<i class="fa-solid fa-clipboard-list mr-3"></i>
|
||||
Copy Track URLs
|
||||
</button>
|
||||
<Teleport to="body">
|
||||
<template v-if="copySuccess !== null">
|
||||
<div v-if="copySuccess === true" class="toast toast-center">
|
||||
<div class="alert alert-success min-w-max">
|
||||
<div>
|
||||
<span>URLs have been copied!</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="modal modal-open">
|
||||
<div class="modal-box md:max-w-xl max-w-xs text-center">
|
||||
<h3 class="font-bold text-2xl">Error</h3>
|
||||
<p class="py-4 text-md">
|
||||
Failed to copy track URLs! Select and manually copy the text below:
|
||||
</p>
|
||||
<div class="w-full max-h-72 overflow-y-auto">
|
||||
<pre>{{ textToCopy }}</pre>
|
||||
</div>
|
||||
<div class="modal-action justify-center">
|
||||
<button class="btn" @click="copySuccess = null">Done</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Teleport>
|
||||
</template>
|
@ -6,11 +6,15 @@ import PlaylistPageTab from '@/layouts/PlaylistPageTab.astro';
|
||||
import { getPlaylistLayoutProps } from '@/utils/getPlaylistLayoutProps';
|
||||
import { formatDate } from '@/utils/formatDate';
|
||||
|
||||
import CopyTrackUrlsButton from '@/components/vue/CopyTrackUrlsButton.vue';
|
||||
import ExportToJsonLink from '@/components/ExportToJsonLink.astro';
|
||||
|
||||
import type { PlaylistSnapshot } from '@/models/playlist-snapshot';
|
||||
|
||||
const layoutProps = await getPlaylistLayoutProps(Astro);
|
||||
|
||||
let snapshot: PlaylistSnapshot | null = null;
|
||||
let trackUrls: string[] = [];
|
||||
|
||||
if (layoutProps.playlist) {
|
||||
try {
|
||||
@ -24,6 +28,7 @@ if (layoutProps.playlist) {
|
||||
}
|
||||
|
||||
snapshot = (await githubResponse.json()) as PlaylistSnapshot;
|
||||
trackUrls = snapshot.tracks.map(({ url }) => url);
|
||||
layoutProps.description = decode(snapshot.description, { level: 'html5' });
|
||||
} catch (error) {
|
||||
const isNotFoundError = (error as Error).message === 'GitHub 404';
|
||||
@ -42,9 +47,13 @@ if (layoutProps.playlist) {
|
||||
|
||||
<PlaylistPageTab {...layoutProps}>
|
||||
<p class="text-lg">{layoutProps.description}</p>
|
||||
<div class="w-full flex justify-center content-center gap-1">
|
||||
<CopyTrackUrlsButton urls={trackUrls} client:only="vue" />
|
||||
<ExportToJsonLink snapshot={snapshot!} />
|
||||
</div>
|
||||
<div class="lg:max-w-full md:max-w-2xl max-w-sm px-5 overflow-x-auto">
|
||||
<table
|
||||
class="table datatable mt-4 mb-8 border-[1px] border-b-0 border-r-0 border-base-content border-opacity-20"
|
||||
class="table datatable mb-10 border-[1px] border-b-0 border-r-0 border-base-content border-opacity-20"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
|
Loading…
Reference in New Issue
Block a user