mirror of
https://github.com/FranP-code/spooky-spotify-showcase.git
synced 2025-10-13 00:02:36 +00:00
Updated prompt and added queue system
This commit is contained in:
@@ -6,7 +6,9 @@ import { TrackByAlbum } from "./spotify-data";
|
|||||||
import { api } from "@/trpc/react";
|
import { api } from "@/trpc/react";
|
||||||
import { quantum } from "ldrs";
|
import { quantum } from "ldrs";
|
||||||
import { ring2 } from "ldrs";
|
import { ring2 } from "ldrs";
|
||||||
|
import { pulsar } from "ldrs";
|
||||||
|
|
||||||
|
pulsar.register();
|
||||||
ring2.register();
|
ring2.register();
|
||||||
quantum.register();
|
quantum.register();
|
||||||
|
|
||||||
@@ -15,7 +17,15 @@ export default function AlbumShowcase({
|
|||||||
album,
|
album,
|
||||||
position,
|
position,
|
||||||
tracks,
|
tracks,
|
||||||
}: TrackByAlbum & { spookify: boolean }) {
|
place,
|
||||||
|
lastSpookyImageLoaded,
|
||||||
|
setLastSpookyImageLoaded,
|
||||||
|
}: TrackByAlbum & {
|
||||||
|
spookify: boolean;
|
||||||
|
place: number;
|
||||||
|
lastSpookyImageLoaded: number;
|
||||||
|
setLastSpookyImageLoaded: any;
|
||||||
|
}) {
|
||||||
const imageSource = album.images[0]
|
const imageSource = album.images[0]
|
||||||
? album.images[0].url
|
? album.images[0].url
|
||||||
: "https://via.placeholder.com/150";
|
: "https://via.placeholder.com/150";
|
||||||
@@ -77,7 +87,15 @@ export default function AlbumShowcase({
|
|||||||
)?.match(/https:\/\/res.cloudinary.com\/[^"]+/);
|
)?.match(/https:\/\/res.cloudinary.com\/[^"]+/);
|
||||||
|
|
||||||
const spookyImageSource = spookyImageMatch ? spookyImageMatch[0] : null;
|
const spookyImageSource = spookyImageMatch ? spookyImageMatch[0] : null;
|
||||||
console.log(spookyImageSource);
|
|
||||||
|
const onImageLoad = () => {
|
||||||
|
if (!spookyImageLoaded) {
|
||||||
|
setSpookyImageLoaded(true);
|
||||||
|
setLastSpookyImageLoaded((state: number) =>
|
||||||
|
state > place ? state : place,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -97,40 +115,65 @@ export default function AlbumShowcase({
|
|||||||
src={imageSource}
|
src={imageSource}
|
||||||
alt={album.name}
|
alt={album.name}
|
||||||
/>
|
/>
|
||||||
{!!spookyImageSource && (
|
{spookyImageSource &&
|
||||||
<img
|
(generateSpookyImage.data
|
||||||
className="h-36 w-36 cursor-pointer rounded"
|
? lastSpookyImageLoaded >= place ||
|
||||||
style={{
|
lastSpookyImageLoaded + 1 === place
|
||||||
display:
|
: true) && (
|
||||||
showSpookyImage && spookyImageLoaded ? "block" : "none",
|
<img
|
||||||
}}
|
className="h-36 w-36 cursor-pointer rounded"
|
||||||
src={spookyImageSource}
|
style={{
|
||||||
alt={album.name}
|
display:
|
||||||
onLoad={() => {
|
showSpookyImage && spookyImageLoaded ? "block" : "none",
|
||||||
if (!spookyImageLoaded) {
|
}}
|
||||||
setSpookyImageLoaded(true);
|
src={spookyImageSource}
|
||||||
}
|
alt={album.name}
|
||||||
}}
|
onLoad={onImageLoad}
|
||||||
/>
|
onError={() => {
|
||||||
)}
|
onImageLoad();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{showSpookyImage && !spookyImageLoaded && (
|
{showSpookyImage && !spookyImageLoaded && (
|
||||||
<div className="flex h-36 w-36 items-center justify-center rounded bg-slate-300 bg-opacity-10">
|
<div className="flex h-36 w-36 items-center justify-center rounded bg-slate-300 bg-opacity-10">
|
||||||
<div>
|
<div>
|
||||||
{generateSpookyImage.data ? (
|
{(() => {
|
||||||
<>
|
if (generateSpookyImage.data) {
|
||||||
<l-quantum
|
if (lastSpookyImageLoaded < place - 1) {
|
||||||
size="100"
|
return (
|
||||||
speed="1.75"
|
<>
|
||||||
color="white"
|
<l-pulsar
|
||||||
></l-quantum>
|
size="100"
|
||||||
<p className="text-center">Generating...</p>
|
speed="1.75"
|
||||||
</>
|
color="white"
|
||||||
) : (
|
></l-pulsar>{" "}
|
||||||
<>
|
<p className="text-center">On queue...</p>
|
||||||
<l-ring-2 size="100" speed="1.75" color="white"></l-ring-2>
|
</>
|
||||||
<p className="text-center">Getting image...</p>
|
);
|
||||||
</>
|
}
|
||||||
)}
|
return (
|
||||||
|
<>
|
||||||
|
<l-quantum
|
||||||
|
size="100"
|
||||||
|
speed="1.75"
|
||||||
|
color="white"
|
||||||
|
></l-quantum>
|
||||||
|
<p className="text-center">Generating...</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<l-ring-2
|
||||||
|
size="100"
|
||||||
|
speed="1.75"
|
||||||
|
color="white"
|
||||||
|
></l-ring-2>
|
||||||
|
<p className="text-center">Getting image...</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -144,7 +187,6 @@ export default function AlbumShowcase({
|
|||||||
{tracks.map((track) => (
|
{tracks.map((track) => (
|
||||||
<li key={track.id}>{track.name}</li>
|
<li key={track.id}>{track.name}</li>
|
||||||
))}
|
))}
|
||||||
{JSON.stringify(generateSpookyImage.data)}
|
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,11 +14,17 @@ export default function ArtistShowcase({
|
|||||||
images,
|
images,
|
||||||
name,
|
name,
|
||||||
id,
|
id,
|
||||||
|
place,
|
||||||
|
lastSpookyImageLoaded,
|
||||||
|
setLastSpookyImageLoaded,
|
||||||
}: {
|
}: {
|
||||||
spookify: boolean;
|
spookify: boolean;
|
||||||
images: { url: string }[];
|
images: { url: string }[];
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
place: number;
|
||||||
|
lastSpookyImageLoaded: number;
|
||||||
|
setLastSpookyImageLoaded: any;
|
||||||
}) {
|
}) {
|
||||||
const [showSpookyImage, setShowSpookyImage] = useState(spookify);
|
const [showSpookyImage, setShowSpookyImage] = useState(spookify);
|
||||||
const [spookyImageLoaded, setSpookyImageLoaded] = useState(false);
|
const [spookyImageLoaded, setSpookyImageLoaded] = useState(false);
|
||||||
@@ -79,6 +85,15 @@ export default function ArtistShowcase({
|
|||||||
)?.match(/https:\/\/res.cloudinary.com\/[^"]+/);
|
)?.match(/https:\/\/res.cloudinary.com\/[^"]+/);
|
||||||
const spookyImageSource = spookyImageMatch ? spookyImageMatch[0] : null;
|
const spookyImageSource = spookyImageMatch ? spookyImageMatch[0] : null;
|
||||||
|
|
||||||
|
const onImageLoad = () => {
|
||||||
|
if (!spookyImageLoaded) {
|
||||||
|
setSpookyImageLoaded(true);
|
||||||
|
setLastSpookyImageLoaded((state: number) =>
|
||||||
|
state > place ? state : place,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tilt tiltMaxAngleX={10} tiltMaxAngleY={10} transitionSpeed={200}>
|
<Tilt tiltMaxAngleX={10} tiltMaxAngleY={10} transitionSpeed={200}>
|
||||||
<div
|
<div
|
||||||
@@ -93,25 +108,29 @@ export default function ArtistShowcase({
|
|||||||
src={imageSource}
|
src={imageSource}
|
||||||
alt={name}
|
alt={name}
|
||||||
/>
|
/>
|
||||||
{!!spookyImageSource && (
|
{spookyImageSource &&
|
||||||
<img
|
(generateSpookyImage.data
|
||||||
style={{
|
? lastSpookyImageLoaded >= place ||
|
||||||
display: showSpookyImage && spookyImageLoaded ? "block" : "none",
|
lastSpookyImageLoaded + 1 === place
|
||||||
}}
|
: true) && (
|
||||||
className="h-56 w-56 object-fill"
|
<img
|
||||||
onLoad={() => {
|
style={{
|
||||||
if (!spookyImageLoaded) {
|
display:
|
||||||
setSpookyImageLoaded(true);
|
showSpookyImage && spookyImageLoaded ? "block" : "none",
|
||||||
}
|
}}
|
||||||
}}
|
className="h-56 w-56 object-fill"
|
||||||
src={spookyImageSource}
|
onLoad={onImageLoad}
|
||||||
alt={name}
|
onError={() => {
|
||||||
/>
|
onImageLoad();
|
||||||
)}
|
}}
|
||||||
|
src={spookyImageSource}
|
||||||
|
alt={name}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{showSpookyImage && !spookyImageLoaded && (
|
{showSpookyImage && !spookyImageLoaded && (
|
||||||
<div className="flex h-56 w-56 items-center justify-center">
|
<div className="flex h-56 w-56 items-center justify-center">
|
||||||
<div>
|
<div>
|
||||||
{generateSpookyImage.data ? (
|
{/* {generateSpookyImage.data ? (
|
||||||
<>
|
<>
|
||||||
<l-quantum size="165" speed="1.75" color="white"></l-quantum>
|
<l-quantum size="165" speed="1.75" color="white"></l-quantum>
|
||||||
<p className="text-center">Generating...</p>
|
<p className="text-center">Generating...</p>
|
||||||
@@ -128,7 +147,39 @@ export default function ArtistShowcase({
|
|||||||
></l-ring-2>
|
></l-ring-2>
|
||||||
<p className="text-center">Getting image...</p>
|
<p className="text-center">Getting image...</p>
|
||||||
</>
|
</>
|
||||||
)}
|
)} */}
|
||||||
|
{(() => {
|
||||||
|
if (generateSpookyImage.data) {
|
||||||
|
if (lastSpookyImageLoaded < place - 1) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<l-pulsar
|
||||||
|
size="165"
|
||||||
|
speed="1.75"
|
||||||
|
color="white"
|
||||||
|
></l-pulsar>
|
||||||
|
<p className="text-center">On queue...</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<l-quantum
|
||||||
|
size="165"
|
||||||
|
speed="1.75"
|
||||||
|
color="white"
|
||||||
|
></l-quantum>
|
||||||
|
<p className="text-center">Generating...</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<l-ring-2 size="165" speed="1.75" color="white"></l-ring-2>
|
||||||
|
<p className="text-center">Getting image...</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ export function Showcase({
|
|||||||
tracksByAlbum: Record<string, any>;
|
tracksByAlbum: Record<string, any>;
|
||||||
artists: any[];
|
artists: any[];
|
||||||
}) {
|
}) {
|
||||||
const [spookify, setSpookify] = useState(false);
|
const [spookify, setSpookify] = useState(true);
|
||||||
|
const [lastSpookyImageLoaded, setLastSpookyImageLoaded] = useState(0);
|
||||||
|
|
||||||
|
const albumsQuantity = Object.values(tracksByAlbum).length;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex gap-2 self-start">
|
<div className="flex gap-2 self-start">
|
||||||
@@ -24,10 +27,16 @@ export function Showcase({
|
|||||||
<h3>Tracks by album</h3>
|
<h3>Tracks by album</h3>
|
||||||
{Object.values(tracksByAlbum)
|
{Object.values(tracksByAlbum)
|
||||||
.sort((a, b) => b.position - a.position)
|
.sort((a, b) => b.position - a.position)
|
||||||
.map(
|
.map((album, index) => (
|
||||||
(album, index) =>
|
<AlbumShowcase
|
||||||
!index && <AlbumShowcase spookify={spookify} {...album} />,
|
key={album.id}
|
||||||
)}
|
place={index}
|
||||||
|
lastSpookyImageLoaded={lastSpookyImageLoaded}
|
||||||
|
setLastSpookyImageLoaded={setLastSpookyImageLoaded}
|
||||||
|
spookify={spookify}
|
||||||
|
{...album}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
<h3>Artists images</h3>
|
<h3>Artists images</h3>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -36,9 +45,16 @@ export function Showcase({
|
|||||||
justifyContent: "space-evenly",
|
justifyContent: "space-evenly",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{[artists[0]].map((artist) => {
|
{artists.map((artist, index) => {
|
||||||
return (
|
return (
|
||||||
<ArtistShowcase spookify={spookify} key={artist.id} {...artist} />
|
<ArtistShowcase
|
||||||
|
place={index + albumsQuantity}
|
||||||
|
lastSpookyImageLoaded={lastSpookyImageLoaded}
|
||||||
|
setLastSpookyImageLoaded={setLastSpookyImageLoaded}
|
||||||
|
spookify={spookify}
|
||||||
|
key={artist.id}
|
||||||
|
{...artist}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import SpotifyWebApi from "spotify-web-api-node";
|
import SpotifyWebApi from "spotify-web-api-node";
|
||||||
import Showcase from "./showcase";
|
import Showcase from "./showcase";
|
||||||
|
import { FETCH_ARTISTS_LIMIT, FETCH_TRACKS_LIMIT } from "../utils/contants";
|
||||||
const TRACKS_LIMIT = 20;
|
|
||||||
const ARTISTS_LIMIT = 21;
|
|
||||||
|
|
||||||
export type TrackByAlbum = {
|
export type TrackByAlbum = {
|
||||||
album: {
|
album: {
|
||||||
@@ -35,13 +33,13 @@ export default async function SpotifyData({
|
|||||||
const albums = albumsData.body.items;
|
const albums = albumsData.body.items;
|
||||||
|
|
||||||
const artistsData = await spotifyApi.getMyTopArtists({
|
const artistsData = await spotifyApi.getMyTopArtists({
|
||||||
limit: ARTISTS_LIMIT,
|
limit: FETCH_ARTISTS_LIMIT,
|
||||||
time_range: "short_term",
|
time_range: "short_term",
|
||||||
});
|
});
|
||||||
const artists = artistsData.body.items;
|
const artists = artistsData.body.items;
|
||||||
|
|
||||||
const tracksData = await spotifyApi.getMyTopTracks({
|
const tracksData = await spotifyApi.getMyTopTracks({
|
||||||
limit: TRACKS_LIMIT,
|
limit: FETCH_TRACKS_LIMIT,
|
||||||
time_range: "short_term",
|
time_range: "short_term",
|
||||||
});
|
});
|
||||||
const tracks = tracksData.body.items.map((track, i) => ({
|
const tracks = tracksData.body.items.map((track, i) => ({
|
||||||
@@ -59,7 +57,7 @@ export default async function SpotifyData({
|
|||||||
album: track.album,
|
album: track.album,
|
||||||
position:
|
position:
|
||||||
tracksWithAlbum.reduce(
|
tracksWithAlbum.reduce(
|
||||||
(acc, _track) => TRACKS_LIMIT / _track.position + acc,
|
(acc, _track) => FETCH_TRACKS_LIMIT / _track.position + acc,
|
||||||
0,
|
0,
|
||||||
) / tracksWithAlbum.length,
|
) / tracksWithAlbum.length,
|
||||||
tracks: [],
|
tracks: [],
|
||||||
|
|||||||
2
src/app/utils/contants.ts
Normal file
2
src/app/utils/contants.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export const FETCH_TRACKS_LIMIT = 20;
|
||||||
|
export const FETCH_ARTISTS_LIMIT = 21;
|
||||||
@@ -25,7 +25,10 @@ export const uploadImage = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const makeImageSpooky = (publicId: string) => {
|
export const makeImageSpooky = (publicId: string) => {
|
||||||
const options = { effect: "gen_background_replace" };
|
const options = {
|
||||||
|
effect:
|
||||||
|
"gen_background_replace:prompt_a bizarre and super creepy background acording with main object theme-max creativity",
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = cloudinary.v2.image(publicId, { ...options });
|
const result = cloudinary.v2.image(publicId, { ...options });
|
||||||
|
|||||||
Reference in New Issue
Block a user