Initial release v1.0.0

This commit is contained in:
Kevin O'Neill
2025-12-25 18:58:06 -06:00
commit 021aec7a63
439 changed files with 116588 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
import { api } from "@/lib/api";
import { useAudio } from "@/lib/audio-context";
import { Track } from "../types";
// Helper to convert library Track to audio context Track format
const formatTrackForAudio = (track: Track) => ({
id: track.id,
title: track.title,
duration: track.duration,
artist: {
id: track.album?.artist?.id,
name: track.album?.artist?.name || "Unknown Artist",
},
album: {
id: track.album?.id,
title: track.album?.title || "Unknown Album",
coverArt: track.album?.coverArt,
},
});
export function useLibraryActions() {
const { playTrack, playTracks, addToQueue } = useAudio();
const playArtist = async (artistId: string) => {
try {
const albumsData = await api.getAlbums({ artistId });
if (!albumsData.albums || albumsData.albums.length === 0) {
return;
}
const firstAlbum = await api.getAlbum(albumsData.albums[0].id);
if (
!firstAlbum ||
!firstAlbum.tracks ||
firstAlbum.tracks.length === 0
) {
return;
}
const tracksWithAlbum = firstAlbum.tracks.map((track: any) => ({
...track,
album: {
id: firstAlbum.id,
title: firstAlbum.title,
coverArt: firstAlbum.coverArt || firstAlbum.coverUrl,
},
artist: {
id: firstAlbum.artist?.id,
name: firstAlbum.artist?.name,
},
}));
playTracks(tracksWithAlbum, 0);
} catch (error) {
console.error("Error playing artist:", error);
}
};
const playAlbum = async (albumId: string) => {
try {
const album = await api.getAlbum(albumId);
if (!album || !album.tracks || album.tracks.length === 0) {
return;
}
const tracksWithAlbum = album.tracks.map((track: any) => ({
...track,
album: {
id: album.id,
title: album.title,
coverArt: album.coverArt || album.coverUrl,
},
artist: {
id: album.artist?.id,
name: album.artist?.name,
},
}));
playTracks(tracksWithAlbum, 0);
} catch (error) {
console.error("Error playing album:", error);
}
};
const playTrackAction = (track: Track) => {
try {
playTrack(formatTrackForAudio(track));
} catch (error) {
console.error("Error playing track:", error);
}
};
const addTrackToQueue = (track: Track) => {
try {
addToQueue(formatTrackForAudio(track));
} catch (error) {
console.error("Error adding track to queue:", error);
}
};
const addTrackToPlaylist = async (playlistId: string, trackId: string) => {
try {
await api.addTrackToPlaylist(playlistId, trackId);
} catch (error) {
console.error("Error adding track to playlist:", error);
}
};
const deleteTrack = async (id: string): Promise<void> => {
try {
await api.deleteTrack(id);
} catch (error) {
console.error("Error deleting track:", error);
throw error;
}
};
const deleteAlbum = async (id: string): Promise<void> => {
try {
await api.deleteAlbum(id);
} catch (error) {
console.error("Error deleting album:", error);
throw error;
}
};
const deleteArtist = async (id: string): Promise<void> => {
try {
await api.deleteArtist(id);
} catch (error) {
console.error("Error deleting artist:", error);
throw error;
}
};
return {
playArtist,
playAlbum,
playTrack: playTrackAction,
addTrackToQueue,
addTrackToPlaylist,
deleteTrack,
deleteAlbum,
deleteArtist,
};
}

View File

@@ -0,0 +1,64 @@
import { useEffect, useState, useCallback } from "react";
import { Artist, Album, Track, Tab } from "../types";
import { api } from "@/lib/api";
import { useAuth } from "@/lib/auth-context";
export type LibraryFilter = "owned" | "discovery" | "all";
interface UseLibraryDataProps {
activeTab: Tab;
filter?: LibraryFilter;
}
export function useLibraryData({
activeTab,
filter = "owned",
}: UseLibraryDataProps) {
const [artists, setArtists] = useState<Artist[]>([]);
const [albums, setAlbums] = useState<Album[]>([]);
const [tracks, setTracks] = useState<Track[]>([]);
const [isLoading, setIsLoading] = useState(false);
const { isAuthenticated } = useAuth();
const loadData = useCallback(async () => {
if (!isAuthenticated) return;
setIsLoading(true);
try {
if (activeTab === "artists") {
const { artists } = await api.getArtists({
limit: 500,
filter,
});
setArtists(artists);
} else if (activeTab === "albums") {
const { albums } = await api.getAlbums({ limit: 500, filter });
setAlbums(albums);
} else if (activeTab === "tracks") {
// Tracks filter could be added later if needed
const { tracks } = await api.getTracks({ limit: 500 });
setTracks(tracks);
}
} catch (error) {
console.error("Failed to load library data:", error);
} finally {
setIsLoading(false);
}
}, [activeTab, filter, isAuthenticated]);
useEffect(() => {
loadData();
}, [loadData]);
const reloadData = () => {
loadData();
};
return {
artists,
albums,
tracks,
isLoading,
reloadData,
};
}