mirror of
https://github.com/maciejpedzich/spotifyplaylistarchive.com.git
synced 2024-09-19 18:16:19 +02:00
Implement playlist search typeahead
This commit is contained in:
parent
2a8207868a
commit
8faaf6bed1
4
models/search-result.ts
Normal file
4
models/search-result.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface SearchResult {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
}
|
106
pages/index.vue
106
pages/index.vue
@ -1,25 +1,63 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import InputText from 'primevue/inputtext';
|
import { $fetch } from 'ohmyfetch';
|
||||||
import Button from 'primevue/button';
|
import AutoComplete from 'primevue/autocomplete';
|
||||||
|
|
||||||
|
import { SearchResult } from '~~/models/search-result';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const playlistLink = ref('');
|
|
||||||
|
|
||||||
const openSnapshotCalendar = async () => {
|
const searchText = ref('');
|
||||||
const urlObject = new URL(playlistLink.value);
|
const suggestions = ref([]);
|
||||||
const params = urlObject.pathname.split('/').filter((p) => p);
|
|
||||||
const [collectionName, itemId] = params;
|
|
||||||
|
|
||||||
if (
|
const findPlaylists = async () => {
|
||||||
urlObject.hostname === 'open.spotify.com' &&
|
try {
|
||||||
collectionName === 'playlist' &&
|
const urlObject = new URL(searchText.value);
|
||||||
itemId
|
const [collectionName, playlistId] = urlObject.pathname
|
||||||
) {
|
.split('/')
|
||||||
await router.push(`/playlists/${itemId}/snapshots`);
|
.filter(Boolean);
|
||||||
} else {
|
|
||||||
throw new Error('This is not a valid playlist link');
|
if (
|
||||||
|
urlObject.hostname === 'open.spotify.com' &&
|
||||||
|
collectionName === 'playlist' &&
|
||||||
|
playlistId
|
||||||
|
) {
|
||||||
|
const searchResults = await $fetch(
|
||||||
|
`https://raw.githubusercontent.com/mackorone/spotify-playlist-archive/main/playlists/pretty/${playlistId}.json`,
|
||||||
|
{
|
||||||
|
parseResponse: (body) =>
|
||||||
|
body === '404: Not Found'
|
||||||
|
? []
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
id: playlistId,
|
||||||
|
title: JSON.parse(body).original_name
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
).catch((e) => e.data);
|
||||||
|
|
||||||
|
suggestions.value = searchResults;
|
||||||
|
} else {
|
||||||
|
throw new Error('This is not a valid playlist link');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message === 'This is not a valid playlist link') return [];
|
||||||
|
|
||||||
|
const searchResults = await $fetch<SearchResult[]>(
|
||||||
|
`/api/playlists/search?title=${searchText.value}`
|
||||||
|
);
|
||||||
|
|
||||||
|
suggestions.value = searchResults;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openSnapshotsCalendar = async ({
|
||||||
|
value: entry
|
||||||
|
}: {
|
||||||
|
value: SearchResult;
|
||||||
|
}) => {
|
||||||
|
await router.push(`/playlists/${entry.id}/snapshots`);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -29,23 +67,29 @@ const openSnapshotCalendar = async () => {
|
|||||||
<p class="text-xl text-gray-300">
|
<p class="text-xl text-gray-300">
|
||||||
Browse past versions of thousands of playlists saved over time
|
Browse past versions of thousands of playlists saved over time
|
||||||
</p>
|
</p>
|
||||||
<form
|
<div class="w-full">
|
||||||
class="w-full"
|
<AutoComplete
|
||||||
@submit.prevent="openSnapshotCalendar"
|
v-model="searchText"
|
||||||
@keypress.enter="openSnapshotCalendar"
|
:suggestions="suggestions"
|
||||||
>
|
class="w-full"
|
||||||
<InputText
|
placeholder="Type in at least 3 characters, or paste a playlist link"
|
||||||
v-model="playlistLink"
|
field="title"
|
||||||
type="url"
|
:min-length="3"
|
||||||
class="w-full text-center mt-1 mb-3"
|
@complete="findPlaylists"
|
||||||
placeholder="Enter a playlist link"
|
@item-select="openSnapshotsCalendar"
|
||||||
/>
|
/>
|
||||||
<Button
|
</div>
|
||||||
type="submit"
|
|
||||||
label="Browse snapshots"
|
|
||||||
:disabled="playlistLink.length === 0"
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
:deep(.p-autocomplete-input) {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.p-autocomplete-input::placeholder) {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
24
server/api/playlists/search.ts
Normal file
24
server/api/playlists/search.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { $fetch } from 'ohmyfetch';
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const query = useQuery(event);
|
||||||
|
|
||||||
|
const readmeFileContent = await $fetch<string>(
|
||||||
|
'https://raw.githubusercontent.com/mackorone/spotify-playlist-archive/main/README.md'
|
||||||
|
);
|
||||||
|
const [, playlistLinksMdList] = readmeFileContent.split('## Playlists\n\n');
|
||||||
|
const archiveEntries = playlistLinksMdList
|
||||||
|
.replaceAll('- [', '')
|
||||||
|
.replaceAll('\\', '')
|
||||||
|
.replaceAll('](', ' ')
|
||||||
|
.replaceAll('.md)', '')
|
||||||
|
.split('\n')
|
||||||
|
.map((textEntry) => {
|
||||||
|
const [title, id] = textEntry.split(' /playlists/pretty/');
|
||||||
|
|
||||||
|
return { title, id };
|
||||||
|
})
|
||||||
|
.filter((entry) => entry.title.includes(query.title as string));
|
||||||
|
|
||||||
|
return archiveEntries;
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user