Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"projects": {
|
|
||||||
"default": "offbrandspotifydb"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BIN
4xnored.png
BIN
4xnored.png
Binary file not shown.
|
Before Width: | Height: | Size: 72 KiB |
@@ -1,151 +0,0 @@
|
|||||||
# Navidrome Integration Migration
|
|
||||||
|
|
||||||
This project has been migrated from a Firebase-based system with static data to use **Navidrome/Subsonic** as the backend music server.
|
|
||||||
|
|
||||||
## What Changed
|
|
||||||
|
|
||||||
### Removed:
|
|
||||||
- Firebase authentication and database
|
|
||||||
- Static album/artist data files
|
|
||||||
- Custom database URLs and tracklist JSON files
|
|
||||||
|
|
||||||
### Added:
|
|
||||||
- Navidrome/Subsonic API integration
|
|
||||||
- Real-time music streaming
|
|
||||||
- Dynamic music library loading
|
|
||||||
- Album cover art from Navidrome
|
|
||||||
- Playlist management through Navidrome
|
|
||||||
- Star/favorite functionality
|
|
||||||
- Scrobbling support
|
|
||||||
|
|
||||||
## Setup Instructions
|
|
||||||
|
|
||||||
### 1. Install Navidrome
|
|
||||||
|
|
||||||
First, you need to set up a Navidrome server. You can:
|
|
||||||
|
|
||||||
- **Self-host**: Follow the [Navidrome installation guide](https://www.navidrome.org/docs/installation/)
|
|
||||||
- **Docker**: Use the official Docker image
|
|
||||||
- **Pre-built binaries**: Download from GitHub releases
|
|
||||||
|
|
||||||
### 2. Configure Environment Variables
|
|
||||||
|
|
||||||
Copy `.env.example` to `.env.local` and configure your Navidrome server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp .env.example .env.local
|
|
||||||
```
|
|
||||||
|
|
||||||
Edit `.env.local`:
|
|
||||||
```env
|
|
||||||
NEXT_PUBLIC_NAVIDROME_URL=http://localhost:4533
|
|
||||||
NEXT_PUBLIC_NAVIDROME_USERNAME=your_username
|
|
||||||
NEXT_PUBLIC_NAVIDROME_PASSWORD=your_password
|
|
||||||
```
|
|
||||||
|
|
||||||
For production, use your actual Navidrome server URL:
|
|
||||||
```env
|
|
||||||
NEXT_PUBLIC_NAVIDROME_URL=https://your-navidrome-server.com
|
|
||||||
NEXT_PUBLIC_NAVIDROME_USERNAME=your_username
|
|
||||||
NEXT_PUBLIC_NAVIDROME_PASSWORD=your_password
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Install Dependencies
|
|
||||||
|
|
||||||
Remove Firebase dependencies and install:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm install
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Run the Application
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### Music Library
|
|
||||||
- **Albums**: Browse all albums in your Navidrome library
|
|
||||||
- **Artists**: Browse all artists with album counts
|
|
||||||
- **Songs**: Play individual tracks with streaming
|
|
||||||
- **Search**: Search across artists, albums, and songs
|
|
||||||
- **Playlists**: Create and manage playlists
|
|
||||||
|
|
||||||
### Audio Player
|
|
||||||
- **Streaming**: Direct streaming from Navidrome server
|
|
||||||
- **Queue Management**: Add albums/artists to queue
|
|
||||||
- **Scrobbling**: Track listening history
|
|
||||||
- **Controls**: Play, pause, skip, volume control
|
|
||||||
|
|
||||||
### User Features
|
|
||||||
- **Favorites**: Star/unstar albums, artists, and songs
|
|
||||||
- **Playlists**: Create, edit, and delete playlists
|
|
||||||
- **Recently Added**: See newest additions to your library
|
|
||||||
- **Album Artwork**: High quality cover art from Navidrome
|
|
||||||
|
|
||||||
## API Integration
|
|
||||||
|
|
||||||
The app uses the Subsonic API (compatible with Navidrome) with these endpoints:
|
|
||||||
|
|
||||||
- `ping` - Test server connection
|
|
||||||
- `getArtists` - Get all artists
|
|
||||||
- `getAlbums` - Get albums (newest, recent, etc.)
|
|
||||||
- `getAlbum` - Get album details and tracks
|
|
||||||
- `search3` - Search music library
|
|
||||||
- `getPlaylists` - Get user playlists
|
|
||||||
- `stream` - Stream audio files
|
|
||||||
- `getCoverArt` - Get album/artist artwork
|
|
||||||
- `star/unstar` - Favorite items
|
|
||||||
- `scrobble` - Track listening
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
lib/
|
|
||||||
navidrome.ts # Navidrome API client
|
|
||||||
app/
|
|
||||||
components/
|
|
||||||
NavidromeContext.tsx # React context for Navidrome data
|
|
||||||
AudioPlayerContext.tsx # Updated for Navidrome streaming
|
|
||||||
album-artwork.tsx # Updated for Navidrome albums
|
|
||||||
artist-icon.tsx # Updated for Navidrome artists
|
|
||||||
AudioPlayer.tsx # Updated for streaming
|
|
||||||
```
|
|
||||||
|
|
||||||
## Migration Notes
|
|
||||||
|
|
||||||
- **Authentication**: Removed Firebase auth (Navidrome handles users)
|
|
||||||
- **Data Source**: Now uses live music library instead of static JSON
|
|
||||||
- **Streaming**: Direct audio streaming instead of static file URLs
|
|
||||||
- **Cover Art**: Dynamic cover art from Navidrome instead of static images
|
|
||||||
- **Playlists**: Managed through Navidrome instead of static data
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Connection Issues
|
|
||||||
1. Verify Navidrome server is running
|
|
||||||
2. Check URL, username, and password in `.env.local`
|
|
||||||
3. Ensure CORS is properly configured in Navidrome
|
|
||||||
4. Check network connectivity
|
|
||||||
|
|
||||||
### Audio Issues
|
|
||||||
1. Verify audio files are properly imported in Navidrome
|
|
||||||
2. Check browser audio permissions
|
|
||||||
3. Ensure audio codecs are supported
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
1. Navidrome server performance affects loading times
|
|
||||||
2. Consider server location for streaming quality
|
|
||||||
3. Check network bandwidth for audio streaming
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
The app now uses TypeScript interfaces that match the Subsonic API responses. All components have been updated to work with the new data structure and real-time streaming.
|
|
||||||
|
|
||||||
Key changes:
|
|
||||||
- Album interface now includes Navidrome-specific fields
|
|
||||||
- Artist interface includes album counts and cover art
|
|
||||||
- Song interface includes streaming URLs and metadata
|
|
||||||
- Playlist interface matches Navidrome playlist structure
|
|
||||||
@@ -37,7 +37,7 @@ const Ihateserverside: React.FC<IhateserversideProps> = ({ children }) => {
|
|||||||
{/* Main Content Area */}
|
{/* Main Content Area */}
|
||||||
<div className="flex-1 flex overflow-hidden">
|
<div className="flex-1 flex overflow-hidden">
|
||||||
{isSidebarVisible && (
|
{isSidebarVisible && (
|
||||||
<div className="w-64 flex-shrink-0">
|
<div className="w-64 flex-shrink-0 border-r">
|
||||||
<Sidebar
|
<Sidebar
|
||||||
playlists={playlists}
|
playlists={playlists}
|
||||||
className="h-full overflow-y-auto"
|
className="h-full overflow-y-auto"
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export function Menu({ toggleSidebar, isSidebarVisible, toggleStatusBar, isStatu
|
|||||||
<>
|
<>
|
||||||
<Menubar className="rounded-none border-b border-none px-2 lg:px-4">
|
<Menubar className="rounded-none border-b border-none px-2 lg:px-4">
|
||||||
<MenubarMenu>
|
<MenubarMenu>
|
||||||
<MenubarTrigger className="font-bold">offbrand spotify</MenubarTrigger>
|
<MenubarTrigger className="font-bold">mice</MenubarTrigger>
|
||||||
<MenubarContent>
|
<MenubarContent>
|
||||||
<MenubarItem onClick={() => setOpen(true)}>About Music</MenubarItem>
|
<MenubarItem onClick={() => setOpen(true)}>About Music</MenubarItem>
|
||||||
<MenubarSeparator />
|
<MenubarSeparator />
|
||||||
|
|||||||
@@ -2,48 +2,6 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
@layer utilities {
|
|
||||||
.text-balance {
|
|
||||||
text-wrap: balance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@layer base {
|
|
||||||
:root {
|
|
||||||
--background: 0 0% 100%;
|
|
||||||
--foreground: 240 10% 3.9%;
|
|
||||||
--card: 0 0% 100%;
|
|
||||||
--card-foreground: 240 10% 3.9%;
|
|
||||||
--popover: 0 0% 100%;
|
|
||||||
--popover-foreground: 240 10% 3.9%;
|
|
||||||
--primary: 221.2 83.2% 53.3%;
|
|
||||||
--primary-foreground: 0 0% 98%;
|
|
||||||
--secondary: 240 4.8% 95.9%;
|
|
||||||
--secondary-foreground: 240 5.9% 10%;
|
|
||||||
--muted: 240 4.8% 95.9%;
|
|
||||||
--muted-foreground: 240 3.8% 46.1%;
|
|
||||||
--accent: 240 4.8% 95.9%;
|
|
||||||
--accent-foreground: 240 5.9% 10%;
|
|
||||||
--destructive: 0 84.2% 60.2%;
|
|
||||||
--destructive-foreground: 0 0% 98%;
|
|
||||||
--border: 240 5.9% 90%;
|
|
||||||
--input: 240 5.9% 90%;
|
|
||||||
--ring: 240 5.9% 10%;
|
|
||||||
--radius: 0.5rem;
|
|
||||||
--chart-1: 12 76% 61%;
|
|
||||||
--chart-2: 173 58% 39%;
|
|
||||||
--chart-3: 197 37% 24%;
|
|
||||||
--chart-4: 43 74% 66%;
|
|
||||||
--chart-5: 27 87% 67%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
@@ -82,9 +40,9 @@ body {
|
|||||||
--chart-4: 280 65% 60%;
|
--chart-4: 280 65% 60%;
|
||||||
--chart-5: 340 75% 55%;
|
--chart-5: 340 75% 55%;
|
||||||
--hover: 240 27% 11%;
|
--hover: 240 27% 11%;
|
||||||
|
--radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Blue Theme Dark */
|
/* Blue Theme Dark */
|
||||||
.theme-blue.dark {
|
.theme-blue.dark {
|
||||||
--background: 240 10% 3.9%;
|
--background: 240 10% 3.9%;
|
||||||
@@ -107,6 +65,7 @@ body {
|
|||||||
--input: 217.2 32.6% 17.5%;
|
--input: 217.2 32.6% 17.5%;
|
||||||
--ring: 224.3 76.3% 48%;
|
--ring: 224.3 76.3% 48%;
|
||||||
--hover: 240 27% 11%;
|
--hover: 240 27% 11%;
|
||||||
|
--radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Violet Theme Dark */
|
/* Violet Theme Dark */
|
||||||
@@ -130,36 +89,10 @@ body {
|
|||||||
--border: 215 27.9% 16.9%;
|
--border: 215 27.9% 16.9%;
|
||||||
--input: 215 27.9% 16.9%;
|
--input: 215 27.9% 16.9%;
|
||||||
--ring: 263.4 70% 50.4%;
|
--ring: 263.4 70% 50.4%;
|
||||||
|
--radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Default dark mode (fallback) */
|
|
||||||
.dark {
|
|
||||||
--background: 240 10% 3.9%;
|
|
||||||
--foreground: 0 0% 98%;
|
|
||||||
--card: 240 10% 3.9%;
|
|
||||||
--card-foreground: 0 0% 98%;
|
|
||||||
--popover: 240 10% 3.9%;
|
|
||||||
--popover-foreground: 0 0% 98%;
|
|
||||||
--primary: 217.2 91.2% 59.8%;
|
|
||||||
--primary-foreground: 222.2 47.4% 11.2%;
|
|
||||||
--secondary: 217.2 32.6% 17.5%;
|
|
||||||
--secondary-foreground: 0 0% 98%;
|
|
||||||
--muted: 217.2 32.6% 17.5%;
|
|
||||||
--muted-foreground: 215 20.2% 65.1%;
|
|
||||||
--accent: 217.2 32.6% 17.5%;
|
|
||||||
--accent-foreground: 0 0% 98%;
|
|
||||||
--destructive: 0 62.8% 30.6%;
|
|
||||||
--destructive-foreground: 0 0% 98%;
|
|
||||||
--border: 217.2 32.6% 17.5%;
|
|
||||||
--input: 217.2 32.6% 17.5%;
|
|
||||||
--ring: 224.3 76.3% 48%;
|
|
||||||
--chart-1: 220 70% 50%;
|
|
||||||
--chart-2: 160 60% 45%;
|
|
||||||
--chart-3: 30 80% 55%;
|
|
||||||
--chart-4: 280 65% 60%;
|
|
||||||
--chart-5: 340 75% 55%;
|
|
||||||
--hover: 240 27% 11%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.dark {
|
.dark {
|
||||||
--background: 240 10% 3.9%;
|
--background: 240 10% 3.9%;
|
||||||
@@ -187,8 +120,9 @@ body {
|
|||||||
--chart-4: 280 65% 60%;
|
--chart-4: 280 65% 60%;
|
||||||
--chart-5: 340 75% 55%;
|
--chart-5: 340 75% 55%;
|
||||||
--hover: 240 27% 11%;
|
--hover: 240 27% 11%;
|
||||||
|
--radius: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ export const viewport: Viewport = {
|
|||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: {
|
title: {
|
||||||
template: 'offbrand spotify | %s',
|
template: 'mice | %s',
|
||||||
default: 'offbrand spotify',
|
default: 'mice',
|
||||||
},
|
},
|
||||||
description: 'a very awesome music streaming service',
|
description: 'a very awesome music streaming service',
|
||||||
robots: {
|
robots: {
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import type { MetadataRoute } from 'next'
|
|||||||
|
|
||||||
export default function manifest(): MetadataRoute.Manifest {
|
export default function manifest(): MetadataRoute.Manifest {
|
||||||
return {
|
return {
|
||||||
name: 'Offbrand Spotify',
|
name: 'mice',
|
||||||
short_name: 'Offbrand',
|
short_name: 'Offbrand',
|
||||||
description: 'a very offbrand spotify clone',
|
description: 'a very mice clone',
|
||||||
start_url: '/',
|
start_url: '/',
|
||||||
categories: ["music", "entertainment"],
|
categories: ["music", "entertainment"],
|
||||||
display_override: ['window-controls-overlay'],
|
display_override: ['window-controls-overlay'],
|
||||||
|
|||||||
12
colors.txt
12
colors.txt
@@ -1,12 +0,0 @@
|
|||||||
border is located at *
|
|
||||||
hsl(214.3deg 3.81% 25%)
|
|
||||||
|
|
||||||
background color in sticky top-0
|
|
||||||
hsl(0deg 0% 5.86%)
|
|
||||||
|
|
||||||
.text muted foreground
|
|
||||||
hsl(0deg 0% 58.47%)
|
|
||||||
|
|
||||||
change in body color
|
|
||||||
hsl(0 0% 100%)
|
|
||||||
|
|
||||||
BIN
preview.png
BIN
preview.png
Binary file not shown.
|
Before Width: | Height: | Size: 483 KiB |
BIN
public/Screenshot 2025-06-19 10.54.18 AM.png
Normal file
BIN
public/Screenshot 2025-06-19 10.54.18 AM.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 370 KiB |
BIN
public/buh.mp3
BIN
public/buh.mp3
Binary file not shown.
Reference in New Issue
Block a user