├── .env
├── .gitattributes
├── public
├── cover.png
├── spot.png
├── preview-image.png
├── publi-cover-end.png
├── publi-cover-mid.png
├── robots.txt
├── sitemap.xml
├── site.webmanifest
└── vite.svg
├── postcss.config.js
├── src
├── assets
│ ├── images
│ │ ├── M4.jpg
│ │ ├── m2.jpg
│ │ ├── m3.jpg
│ │ ├── music.jpg
│ │ ├── spot.png
│ │ └── skullemoji.jpg
│ ├── fonts
│ │ ├── GothamBold.ttf
│ │ ├── GothamBook.ttf
│ │ ├── GothamMedium.ttf
│ │ ├── azonix
│ │ │ └── Azonix.otf
│ │ ├── babell
│ │ │ └── BabellBold.ttf
│ │ ├── creato
│ │ │ ├── CreatoDisplay-Black.otf
│ │ │ ├── CreatoDisplay-Bold.otf
│ │ │ ├── CreatoDisplay-Light.otf
│ │ │ ├── CreatoDisplay-Thin.otf
│ │ │ ├── CreatoDisplay-Medium.otf
│ │ │ ├── CreatoDisplay-Regular.otf
│ │ │ ├── CreatoDisplay-BoldItalic.otf
│ │ │ ├── CreatoDisplay-ExtraBold.otf
│ │ │ ├── CreatoDisplay-ThinItalic.otf
│ │ │ ├── CreatoDisplay-BlackItalic.otf
│ │ │ ├── CreatoDisplay-LightItalic.otf
│ │ │ ├── CreatoDisplay-MediumItalic.otf
│ │ │ ├── CreatoDisplay-ExtraBoldItalic.otf
│ │ │ └── CreatoDisplay-RegularItalic.otf
│ │ └── cocosharp
│ │ │ ├── Coco-Sharp-Bold-trial.ttf
│ │ │ ├── Coco-Sharp-Heavy-trial.ttf
│ │ │ ├── Coco-Sharp-Italic-trial.ttf
│ │ │ ├── Coco-Sharp-Light-trial.ttf
│ │ │ ├── Coco-Sharp-by-zetafonts.png
│ │ │ ├── Coco-Sharp-Extrabold-trial.ttf
│ │ │ ├── Coco-Sharp-Regular-trial.ttf
│ │ │ ├── Coco-Sharp-Bold-Italic-trial.ttf
│ │ │ ├── Coco-Sharp-Extralight-trial.ttf
│ │ │ ├── Coco-Sharp-Heavy-Italic-trial.ttf
│ │ │ ├── Coco-Sharp-Light-Italic-trial.ttf
│ │ │ ├── Coco-Sharp-Extrabold-Italic-trial.ttf
│ │ │ ├── Coco-Sharp-Extralight-Italic-trial.ttf
│ │ │ └── Coco-Sharp-Family-CC-BY-NCLicensepdf.pdf
│ ├── index.js
│ └── react.svg
├── lib
│ └── utils.ts
├── main.jsx
├── App.css
├── components
│ ├── GridCard.jsx
│ ├── UserProfile.jsx
│ └── ui
│ │ ├── aurora-background.tsx
│ │ ├── following-pointer.tsx
│ │ ├── lamp.tsx
│ │ ├── glowing-effect.tsx
│ │ ├── background-beams.tsx
│ │ └── sparkles.tsx
├── utils
│ └── spotifyApi.js
├── index.css
└── App.jsx
├── tsconfig.json
├── .gitignore
├── vite.config.js
├── components.json
├── generate-sitemap.js
├── eslint.config.js
├── package.json
├── tailwind.config.js
├── README.md
└── index.html
/.env:
--------------------------------------------------------------------------------
1 | REACT_APP_API_URL=https://authback-jxx5.onrender.com/api/users
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/public/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/public/cover.png
--------------------------------------------------------------------------------
/public/spot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/public/spot.png
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/preview-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/public/preview-image.png
--------------------------------------------------------------------------------
/src/assets/images/M4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/images/M4.jpg
--------------------------------------------------------------------------------
/src/assets/images/m2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/images/m2.jpg
--------------------------------------------------------------------------------
/src/assets/images/m3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/images/m3.jpg
--------------------------------------------------------------------------------
/public/publi-cover-end.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/public/publi-cover-end.png
--------------------------------------------------------------------------------
/public/publi-cover-mid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/public/publi-cover-mid.png
--------------------------------------------------------------------------------
/src/assets/images/music.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/images/music.jpg
--------------------------------------------------------------------------------
/src/assets/images/spot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/images/spot.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
3 |
4 | Sitemap: https://thinakaranmanokaran.github.io/spotmix-downloader/sitemap.xml
5 |
--------------------------------------------------------------------------------
/src/assets/fonts/GothamBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/GothamBold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/GothamBook.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/GothamBook.ttf
--------------------------------------------------------------------------------
/src/assets/images/skullemoji.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/images/skullemoji.jpg
--------------------------------------------------------------------------------
/src/assets/fonts/GothamMedium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/GothamMedium.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/azonix/Azonix.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/azonix/Azonix.otf
--------------------------------------------------------------------------------
/src/assets/fonts/babell/BabellBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/babell/BabellBold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-Black.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-Black.otf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-Bold.otf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-Light.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-Light.otf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-Thin.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-Thin.otf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-Medium.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-Medium.otf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-Regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-Regular.otf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Bold-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Bold-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-BoldItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-BoldItalic.otf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-ExtraBold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-ExtraBold.otf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-ThinItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-ThinItalic.otf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Heavy-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Heavy-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Italic-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Italic-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Light-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Light-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-by-zetafonts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-by-zetafonts.png
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-BlackItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-BlackItalic.otf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-LightItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-LightItalic.otf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-MediumItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-MediumItalic.otf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Extrabold-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Extrabold-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Regular-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Regular-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-ExtraBoldItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-ExtraBoldItalic.otf
--------------------------------------------------------------------------------
/src/assets/fonts/creato/CreatoDisplay-RegularItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/creato/CreatoDisplay-RegularItalic.otf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Bold-Italic-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Bold-Italic-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Extralight-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Extralight-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Heavy-Italic-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Heavy-Italic-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Light-Italic-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Light-Italic-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Extrabold-Italic-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Extrabold-Italic-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Extralight-Italic-trial.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Extralight-Italic-trial.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/cocosharp/Coco-Sharp-Family-CC-BY-NCLicensepdf.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thinakaranmanokaran/spotmix-downloader/HEAD/src/assets/fonts/cocosharp/Coco-Sharp-Family-CC-BY-NCLicensepdf.pdf
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "@/*": [
6 | "src/*"
7 | ]
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react'
2 | import { createRoot } from 'react-dom/client'
3 | import App from './App.jsx'
4 | import './index.css'
5 |
6 | createRoot(document.getElementById('root')).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/src/assets/index.js:
--------------------------------------------------------------------------------
1 | import MusicDoodle1 from "./images/music.jpg"
2 | import MusicDoodle2 from "./images/m2.jpg"
3 | import MusicDoodle3 from "./images/m3.jpg"
4 | import MusicDoodle4 from "./images/M4.jpg"
5 |
6 | export default {
7 | MusicDoodle1,
8 | MusicDoodle2,
9 | MusicDoodle3,
10 | MusicDoodle4,
11 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 | .vite
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/public/sitemap.xml:
--------------------------------------------------------------------------------
1 | https://thinakaranmanokaran.github.io/ 2025-08-14T00:00:00.000Z weekly 1.0
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | // vite.config.ts
2 | import { defineConfig } from "vite";
3 | import react from "@vitejs/plugin-react";
4 | import { webcrypto } from "node:crypto"; // ✅ ESM import
5 |
6 | // Polyfill for Node build environment
7 | if (!globalThis.crypto) {
8 | globalThis.crypto = webcrypto;
9 | }
10 |
11 | // https://vitejs.dev/config/
12 | export default defineConfig({
13 | plugins: [react()],
14 | base: "/spotmix-downloader/",
15 | build: {
16 | outDir: 'dist', // Ensure correct output folder
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "new-york",
4 | "rsc": false,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.js",
8 | "css": "src/index.css",
9 | "baseColor": "gray",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils",
16 | "ui": "@/components/ui",
17 | "lib": "@/lib",
18 | "hooks": "@/hooks"
19 | },
20 | "iconLibrary": "lucide"
21 | }
--------------------------------------------------------------------------------
/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Spotify Song Downloader",
3 | "short_name": "Spotify Downloader",
4 | "description": "Download any Spotify song, playlist, or album as high-quality MP3 instantly by pasting the Spotify URL. 100% free, no premium needed.",
5 | "start_url": "/",
6 | "display": "standalone",
7 | "background_color": "#000000",
8 | "theme_color": "#1db954",
9 | "icons": [
10 | {
11 | "src": "/spot.png",
12 | "sizes": "192x192",
13 | "type": "image/png"
14 | },
15 | {
16 | "src": "/spot.png",
17 | "sizes": "512x512",
18 | "type": "image/png"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/generate-sitemap.js:
--------------------------------------------------------------------------------
1 | // generate-sitemap.js
2 | import { writeFileSync } from 'fs';
3 | import { SitemapStream, streamToPromise } from 'sitemap';
4 | import { resolve } from 'path';
5 |
6 | const baseUrl = 'https://thinakaranmanokaran.github.io/spotmix-downloader/';
7 |
8 | // List of site pages (you can add more later)
9 | const pages = [
10 | { url: '/', changefreq: 'weekly', priority: 1.0 },
11 | ];
12 |
13 | (async () => {
14 | const sitemapStream = new SitemapStream({ hostname: baseUrl });
15 |
16 | pages.forEach(page => {
17 | sitemapStream.write({
18 | url: page.url,
19 | changefreq: page.changefreq,
20 | priority: page.priority,
21 | lastmod: new Date().toISOString().split('T')[0], // auto-update
22 | });
23 | });
24 |
25 | sitemapStream.end();
26 |
27 | const sitemapData = await streamToPromise(sitemapStream);
28 | writeFileSync(resolve('./public/sitemap.xml'), sitemapData.toString());
29 |
30 | console.log('✅ Sitemap generated at public/sitemap.xml');
31 | })();
32 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js'
2 | import globals from 'globals'
3 | import react from 'eslint-plugin-react'
4 | import reactHooks from 'eslint-plugin-react-hooks'
5 | import reactRefresh from 'eslint-plugin-react-refresh'
6 |
7 | export default [
8 | { ignores: ['dist'] },
9 | {
10 | files: ['**/*.{js,jsx}'],
11 | languageOptions: {
12 | ecmaVersion: 2020,
13 | globals: globals.browser,
14 | parserOptions: {
15 | ecmaVersion: 'latest',
16 | ecmaFeatures: { jsx: true },
17 | sourceType: 'module',
18 | },
19 | },
20 | settings: { react: { version: '18.3' } },
21 | plugins: {
22 | react,
23 | 'react-hooks': reactHooks,
24 | 'react-refresh': reactRefresh,
25 | },
26 | rules: {
27 | ...js.configs.recommended.rules,
28 | ...react.configs.recommended.rules,
29 | ...react.configs['jsx-runtime'].rules,
30 | ...reactHooks.configs.recommended.rules,
31 | 'react/jsx-no-target-blank': 'off',
32 | 'react-refresh/only-export-components': [
33 | 'warn',
34 | { allowConstantExport: true },
35 | ],
36 | },
37 | },
38 | ]
39 |
--------------------------------------------------------------------------------
/src/components/GridCard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { motion } from "motion/react";
3 |
4 | const GridCard = ({ number, title, description, className }) => {
5 | return (
6 |
13 | +
14 | +
15 | +
16 | +
17 | {number}
18 | {title}
19 | {description}
20 |
21 | )
22 | }
23 |
24 | export default GridCard
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "spot-mix",
3 | "private": true,
4 | "version": "2.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "node generate-sitemap.js && vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview",
11 | "deploy": "gh-pages -d dist"
12 | },
13 | "dependencies": {
14 | "@tsparticles/engine": "^3.9.1",
15 | "@tsparticles/react": "^3.0.0",
16 | "@tsparticles/slim": "^3.9.1",
17 | "axios": "^1.7.9",
18 | "class-variance-authority": "^0.7.1",
19 | "clsx": "^2.1.1",
20 | "jwt-decode": "^4.0.0",
21 | "lucide-react": "^0.539.0",
22 | "motion": "^12.23.12",
23 | "react": "^18.3.1",
24 | "react-dom": "^18.3.1",
25 | "tailwind-merge": "^3.3.1",
26 | "tailwindcss-animate": "^1.0.7"
27 | },
28 | "devDependencies": {
29 | "@eslint/js": "^9.11.1",
30 | "@types/react": "^18.3.10",
31 | "@types/react-dom": "^18.3.0",
32 | "@vitejs/plugin-react": "^4.3.2",
33 | "autoprefixer": "^10.4.20",
34 | "eslint": "^9.11.1",
35 | "eslint-plugin-react": "^7.37.0",
36 | "eslint-plugin-react-hooks": "^5.1.0-rc.0",
37 | "eslint-plugin-react-refresh": "^0.4.12",
38 | "gh-pages": "^6.3.0",
39 | "globals": "^15.9.0",
40 | "postcss": "^8.4.47",
41 | "sitemap": "^8.0.0",
42 | "tailwindcss": "^3.4.13",
43 | "vite": "^7.1.2"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/utils/spotifyApi.js:
--------------------------------------------------------------------------------
1 | // spotifyApi.js
2 | export async function getAccessToken() {
3 | const clientId = import.meta.env.VITE_SPOTIFY_CLIENT;
4 | const clientSecret = import.meta.env.VITE_SPOTIFY_SECRET;
5 |
6 | console.log('Client ID:', import.meta.env.VITE_SPOTIFY_CLIENT);
7 | console.log('Client Secret exists:', Boolean(import.meta.env.VITE_SPOTIFY_SECRET));
8 |
9 | // Basic check if credentials exist
10 | if (!clientId || !clientSecret) {
11 | throw new Error('Spotify client credentials not configured');
12 | }
13 |
14 | try {
15 | // Create the authorization header
16 | const authHeader = 'Basic ' + btoa(`${clientId}:${clientSecret}`);
17 |
18 | const response = await fetch("https://accounts.spotify.com/api/token", {
19 | method: "POST",
20 | headers: {
21 | "Content-Type": "application/x-www-form-urlencoded",
22 | "Authorization": authHeader,
23 | },
24 | body: new URLSearchParams({
25 | grant_type: 'client_credentials'
26 | })
27 | });
28 |
29 | if (!response.ok) {
30 | const errorData = await response.json();
31 | throw new Error(`Spotify API error: ${errorData.error_description || response.statusText}`);
32 | }
33 |
34 | const data = await response.json();
35 | return data.access_token;
36 | } catch (error) {
37 | console.error('Error getting access token:', error);
38 | throw error;
39 | }
40 | }
--------------------------------------------------------------------------------
/src/components/UserProfile.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import axios from 'axios';
3 | import Skull from './../assets/images/skullemoji.jpg'
4 |
5 |
6 | const UserProfile = ({ currentUser }) => {
7 |
8 | const [userProfile, setUserProfile] = useState(false);
9 |
10 | function showProfile() {
11 | setUserProfile(!userProfile);
12 | }
13 |
14 | const UserDetails = ({ currentUser }) => {
15 | return(
16 |
17 |
18 |
19 | {currentUser.email}
20 |
21 |
22 |
23 | )
24 | }
25 |
26 | return (
27 |
28 |
29 | {
30 | currentUser ? (
31 |
32 |
33 |
34 |
35 | { userProfile &&
}
36 |
37 | ) : (
)
38 | }
39 |
40 |
41 | )
42 | }
43 |
44 | export default UserProfile
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | darkMode: ["class"],
4 | content: [
5 | "./index.html",
6 | "./src/**/*.{js,ts,jsx,tsx}",
7 | ],
8 | theme: {
9 | extend: {
10 | fontFamily: {
11 | spotify: [
12 | 'spotify',
13 | 'sans-serif'
14 | ],
15 | bigfont: [
16 | 'BigFont',
17 | 'sans-serif'
18 | ],
19 | spotifyMed: [
20 | 'spotifyMed',
21 | 'sans-serif'
22 | ],
23 | number: [
24 | 'number',
25 | 'sans-serif'
26 | ],
27 | para: [
28 | 'Para',
29 | 'sans-serif'
30 | ],
31 | title: [
32 | 'Title',
33 | 'sans-serif'
34 | ]
35 | },
36 | borderRadius: {
37 | lg: 'var(--radius)',
38 | md: 'calc(var(--radius) - 2px)',
39 | sm: 'calc(var(--radius) - 4px)'
40 | },
41 | colors: {
42 | background: 'hsl(var(--background))',
43 | foreground: 'hsl(var(--foreground))',
44 | card: {
45 | DEFAULT: 'hsl(var(--card))',
46 | foreground: 'hsl(var(--card-foreground))'
47 | },
48 | popover: {
49 | DEFAULT: 'hsl(var(--popover))',
50 | foreground: 'hsl(var(--popover-foreground))'
51 | },
52 | primary: {
53 | DEFAULT: 'hsl(var(--primary))',
54 | foreground: 'hsl(var(--primary-foreground))'
55 | },
56 | secondary: {
57 | DEFAULT: 'hsl(var(--secondary))',
58 | foreground: 'hsl(var(--secondary-foreground))'
59 | },
60 | muted: {
61 | DEFAULT: 'hsl(var(--muted))',
62 | foreground: 'hsl(var(--muted-foreground))'
63 | },
64 | accent: {
65 | DEFAULT: 'hsl(var(--accent))',
66 | foreground: 'hsl(var(--accent-foreground))'
67 | },
68 | destructive: {
69 | DEFAULT: 'hsl(var(--destructive))',
70 | foreground: 'hsl(var(--destructive-foreground))'
71 | },
72 | border: 'hsl(var(--border))',
73 | input: 'hsl(var(--input))',
74 | ring: 'hsl(var(--ring))',
75 | chart: {
76 | '1': 'hsl(var(--chart-1))',
77 | '2': 'hsl(var(--chart-2))',
78 | '3': 'hsl(var(--chart-3))',
79 | '4': 'hsl(var(--chart-4))',
80 | '5': 'hsl(var(--chart-5))'
81 | }
82 | }
83 | }
84 | },
85 | plugins: [require("tailwindcss-animate")],
86 | }
87 |
--------------------------------------------------------------------------------
/src/components/ui/aurora-background.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { cn } from "../../lib/utils";
3 | import React, { ReactNode } from "react";
4 |
5 | interface AuroraBackgroundProps extends React.HTMLProps {
6 | children: ReactNode;
7 | showRadialGradient?: boolean;
8 | }
9 |
10 | export const AuroraBackground = ({
11 | className,
12 | children,
13 | showRadialGradient = true,
14 | ...props
15 | }: AuroraBackgroundProps) => {
16 | return (
17 |
18 |
25 |
57 | {children}
58 |
59 |
60 | );
61 | };
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # 🎵 Spotify Downloader – Download Songs, Playlists & Albums in MP3
4 |
5 | **Spotify Downloader** is a blazing-fast React web application that lets you **download Spotify songs, playlists, and albums** directly in **high-quality MP3, AAC, or FLAC formats**.
6 | No Spotify Premium required. Preserve **album artwork, metadata, and original audio quality** – all for free.
7 |
8 |
9 | ## 🖼 Screenshot
10 |
11 | 
12 |
13 | ---
14 |
15 | ## 🚀 Features
16 |
17 | - **Download Any Spotify Song** – Save your favorite tracks instantly.
18 | - **Playlist & Album Support** – Batch download entire collections.
19 | - **High-Quality Audio** – Get up to **320kbps MP3** or **lossless formats**.
20 | - **No Premium Required** – Works with a free Spotify account.
21 | - **Lightning-Fast** – Optimized servers for ultra-quick conversions.
22 | - **Full Metadata** – Keep song title, artist, release year, and album cover.
23 |
24 | ---
25 |
26 | ## 🌍 Live Demo
27 |
28 | Try the live version here:
29 | [🔗 Spotify Downloader – Live](https://thinakaranmanokaran.github.io/spotmix-downloader/)
30 |
31 | ---
32 |
33 |
34 | ## 📦 Installation & Setup
35 |
36 | ### Prerequisites
37 | - **Node.js** (LTS recommended)
38 | - **npm** or **yarn**
39 |
40 | ### Steps
41 |
42 | ```bash
43 | # 1. Clone the repository
44 | git clone https://github.com/thinakaranmanokaran/spotify-downloader.git
45 | cd spotify-downloader
46 |
47 | # 2. Install dependencies
48 | npm install # or yarn install
49 |
50 | # 3. Start the development server
51 | npm start # or yarn start
52 | ````
53 |
54 | Visit **[http://localhost:3000](http://localhost:3000)** in your browser.
55 |
56 | ---
57 |
58 | ## 📥 How to Use
59 |
60 | 1. **Copy** any Spotify track, playlist, or album URL.
61 | 2. **Paste** the link into the input field on the website.
62 | 3. **Verify** to fetch details like album name, cover image, artist, and release date.
63 | 4. **Download** the song(s) instantly in your preferred format.
64 |
65 | ---
66 |
67 | ## 🚀 Deployment (GitHub Pages)
68 |
69 | 1. In `package.json`, set the `homepage`:
70 |
71 | ```json
72 | "homepage": "https://thinakaranmanokaran.github.io/spotmix-downloader/"
73 | ```
74 | 2. Run:
75 |
76 | ```bash
77 | npm run deploy
78 | ```
79 | 3. Your site will be live on GitHub Pages.
80 |
81 | ---
82 |
83 | ## 🛠 Tech Stack
84 |
85 | * **React.js** – Frontend framework
86 | * **Tailwind CSS** – Styling
87 | * **Spotify Downloader API** – Fetches song details and download links
88 | * **GitHub Pages** – Deployment
89 |
90 | ---
91 |
92 | ## 🤝 Contributing
93 |
94 | Contributions are welcome!
95 | Follow these steps:
96 |
97 | ```bash
98 | # 1. Fork the repo
99 | # 2. Create a branch
100 | git checkout -b feature-branch
101 |
102 | # 3. Commit your changes
103 | git commit -m "Add new feature"
104 |
105 | # 4. Push and submit PR
106 | git push origin feature-branch
107 | ```
108 |
109 | ---
110 |
111 | ## 📜 License
112 |
113 | This project is licensed under the **MIT License** – see the [LICENSE](LICENSE) file.
114 |
115 | ---
116 |
117 | ## 😇 Author
118 |
119 | [Thinakaran Manokaran](https://thinakaran.dev)
120 |
121 | ---
122 |
123 | ### 📈 SEO Keywords
124 |
125 |
126 | ```
127 |
128 | Spotify downloader, Spotify song download, download Spotify playlist MP3, Spotify to MP3, free Spotify music downloader, download Spotify album, Spotify MP3 converter, download Spotify without premium
129 |
130 | ```
131 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @font-face {
6 | font-family: spotify;
7 | src: url('./assets/fonts/GothamBold.ttf');
8 | }
9 |
10 | @font-face {
11 | font-family: spotifyMed;
12 | src: url('./assets/fonts/GothamBook.ttf');
13 | }
14 |
15 | body{
16 | background-color: black;
17 | }
18 |
19 | body::-webkit-scrollbar {
20 | display: none;
21 | }
22 |
23 | @font-face {
24 | font-family: BigFont;
25 | src: url('./assets/fonts/babell/BabellBold.ttf');
26 | }
27 |
28 | @font-face {
29 | font-family: number;
30 | src: url(assets/fonts/azonix/Azonix.otf);
31 | }
32 |
33 | @font-face {
34 | font-family: Para;
35 | src: url('./assets/fonts/creato/CreatoDisplay-Light.otf');
36 | }
37 |
38 | @font-face {
39 | font-family: Title;
40 | src: url(assets/fonts/cocosharp/Coco-Sharp-Bold-trial.ttf);
41 | }
42 |
43 | @theme inline {
44 | --animate-aurora: aurora 60s linear infinite;
45 |
46 | @keyframes aurora {
47 | from {
48 | background-position:
49 | 50% 50%,
50 | 50% 50%;
51 | }
52 |
53 | to {
54 | background-position:
55 | 350% 50%,
56 | 350% 50%;
57 | }
58 | }
59 | }
60 |
61 | @layer base {
62 | :root {
63 | --background: 0 0% 100%;
64 | --foreground: 224 71.4% 4.1%;
65 | --card: 0 0% 100%;
66 | --card-foreground: 224 71.4% 4.1%;
67 | --popover: 0 0% 100%;
68 | --popover-foreground: 224 71.4% 4.1%;
69 | --primary: 220.9 39.3% 11%;
70 | --primary-foreground: 210 20% 98%;
71 | --secondary: 220 14.3% 95.9%;
72 | --secondary-foreground: 220.9 39.3% 11%;
73 | --muted: 220 14.3% 95.9%;
74 | --muted-foreground: 220 8.9% 46.1%;
75 | --accent: 220 14.3% 95.9%;
76 | --accent-foreground: 220.9 39.3% 11%;
77 | --destructive: 0 84.2% 60.2%;
78 | --destructive-foreground: 210 20% 98%;
79 | --border: 220 13% 91%;
80 | --input: 220 13% 91%;
81 | --ring: 224 71.4% 4.1%;
82 | --chart-1: 12 76% 61%;
83 | --chart-2: 173 58% 39%;
84 | --chart-3: 197 37% 24%;
85 | --chart-4: 43 74% 66%;
86 | --chart-5: 27 87% 67%;
87 | --radius: 0.5rem;
88 | }
89 | .dark {
90 | --background: 224 71.4% 4.1%;
91 | --foreground: 210 20% 98%;
92 | --card: 224 71.4% 4.1%;
93 | --card-foreground: 210 20% 98%;
94 | --popover: 224 71.4% 4.1%;
95 | --popover-foreground: 210 20% 98%;
96 | --primary: 210 20% 98%;
97 | --primary-foreground: 220.9 39.3% 11%;
98 | --secondary: 215 27.9% 16.9%;
99 | --secondary-foreground: 210 20% 98%;
100 | --muted: 215 27.9% 16.9%;
101 | --muted-foreground: 217.9 10.6% 64.9%;
102 | --accent: 215 27.9% 16.9%;
103 | --accent-foreground: 210 20% 98%;
104 | --destructive: 0 62.8% 30.6%;
105 | --destructive-foreground: 210 20% 98%;
106 | --border: 215 27.9% 16.9%;
107 | --input: 215 27.9% 16.9%;
108 | --ring: 216 12.2% 83.9%;
109 | --chart-1: 220 70% 50%;
110 | --chart-2: 160 60% 45%;
111 | --chart-3: 30 80% 55%;
112 | --chart-4: 280 65% 60%;
113 | --chart-5: 340 75% 55%;
114 | }
115 | }
116 |
117 | @layer base {
118 | * {
119 | @apply border-border;
120 | }
121 | body {
122 | @apply bg-background text-foreground;
123 | }
124 | }
--------------------------------------------------------------------------------
/src/components/ui/following-pointer.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 |
3 | import { motion, AnimatePresence, useMotionValue } from "motion/react";
4 | import { cn } from "../../lib/utils";
5 |
6 | export const FollowerPointerCard = ({
7 | children,
8 | className,
9 | title,
10 | }: {
11 | children: React.ReactNode;
12 | className?: string;
13 | title?: string | React.ReactNode;
14 | }) => {
15 | const x = useMotionValue(0);
16 | const y = useMotionValue(0);
17 | const ref = React.useRef(null);
18 | const [rect, setRect] = useState(null);
19 | const [isInside, setIsInside] = useState(false); // Add this line
20 |
21 | useEffect(() => {
22 | if (ref.current) {
23 | setRect(ref.current.getBoundingClientRect());
24 | }
25 | }, []);
26 |
27 | const handleMouseMove = (e: React.MouseEvent) => {
28 | if (rect) {
29 | const scrollX = window.scrollX;
30 | const scrollY = window.scrollY;
31 | x.set(e.clientX - rect.left + scrollX);
32 | y.set(e.clientY - rect.top + scrollY);
33 | }
34 | };
35 | const handleMouseLeave = () => {
36 | setIsInside(false);
37 | };
38 |
39 | const handleMouseEnter = () => {
40 | setIsInside(true);
41 | };
42 | return (
43 |
53 |
54 | {isInside && }
55 |
56 | {children}
57 |
58 | );
59 | };
60 |
61 | export const FollowPointer = ({
62 | x,
63 | y,
64 | title,
65 | }: {
66 | x: any;
67 | y: any;
68 | title?: string | React.ReactNode;
69 | }) => {
70 | const colors = [
71 | "#0ea5e9",
72 | "#737373",
73 | "#14b8a6",
74 | "#22c55e",
75 | "#3b82f6",
76 | "#ef4444",
77 | "#eab308",
78 | ];
79 | return (
80 |
100 |
110 |
111 |
112 |
132 | {title || `William Shakespeare`}
133 |
134 |
135 | );
136 | };
137 |
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Spotify Song Downloader - Free Spotify to MP3 (320kbps)
9 |
10 |
11 |
13 |
14 |
15 |
17 |
18 |
19 |
20 |
22 |
23 |
24 |
25 |
26 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
85 |
86 |
87 |
88 | Spotify Song Downloader - Convert Spotify Songs, Playlists & Albums to MP3
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/src/components/ui/lamp.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import { motion } from "motion/react";
4 | import { cn } from "../../lib/utils";
5 |
6 | export default function LampDemo() {
7 | return (
8 |
9 |
19 | Build lamps the right way
20 |
21 |
22 | );
23 | }
24 |
25 | export const LampContainer = ({
26 | children,
27 | className,
28 | }: {
29 | children: React.ReactNode;
30 | className?: string;
31 | }) => {
32 | return (
33 |
39 |
40 |
53 |
54 |
55 |
56 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
85 | {/*
*/}
95 |
96 |
97 |
98 |
99 |
100 | {children}
101 |
102 |
103 | );
104 | };
105 |
--------------------------------------------------------------------------------
/src/components/ui/glowing-effect.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { memo, useCallback, useEffect, useRef } from "react";
4 | import { cn } from "../../lib/utils";
5 | import { animate } from "motion/react";
6 |
7 | interface GlowingEffectProps {
8 | blur?: number;
9 | inactiveZone?: number;
10 | proximity?: number;
11 | spread?: number;
12 | variant?: "default" | "white";
13 | glow?: boolean;
14 | className?: string;
15 | disabled?: boolean;
16 | movementDuration?: number;
17 | borderWidth?: number;
18 | }
19 | const GlowingEffect = memo(
20 | ({
21 | blur = 0,
22 | inactiveZone = 0.7,
23 | proximity = 0,
24 | spread = 20,
25 | variant = "default",
26 | glow = false,
27 | className,
28 | movementDuration = 2,
29 | borderWidth = 1,
30 | disabled = true,
31 | }: GlowingEffectProps) => {
32 | const containerRef = useRef(null);
33 | const lastPosition = useRef({ x: 0, y: 0 });
34 | const animationFrameRef = useRef(0);
35 |
36 | const handleMove = useCallback(
37 | (e?: MouseEvent | { x: number; y: number }) => {
38 | if (!containerRef.current) return;
39 |
40 | if (animationFrameRef.current) {
41 | cancelAnimationFrame(animationFrameRef.current);
42 | }
43 |
44 | animationFrameRef.current = requestAnimationFrame(() => {
45 | const element = containerRef.current;
46 | if (!element) return;
47 |
48 | const { left, top, width, height } = element.getBoundingClientRect();
49 | const mouseX = e?.x ?? lastPosition.current.x;
50 | const mouseY = e?.y ?? lastPosition.current.y;
51 |
52 | if (e) {
53 | lastPosition.current = { x: mouseX, y: mouseY };
54 | }
55 |
56 | const center = [left + width * 0.5, top + height * 0.5];
57 | const distanceFromCenter = Math.hypot(
58 | mouseX - center[0],
59 | mouseY - center[1]
60 | );
61 | const inactiveRadius = 0.5 * Math.min(width, height) * inactiveZone;
62 |
63 | if (distanceFromCenter < inactiveRadius) {
64 | element.style.setProperty("--active", "0");
65 | return;
66 | }
67 |
68 | const isActive =
69 | mouseX > left - proximity &&
70 | mouseX < left + width + proximity &&
71 | mouseY > top - proximity &&
72 | mouseY < top + height + proximity;
73 |
74 | element.style.setProperty("--active", isActive ? "1" : "0");
75 |
76 | if (!isActive) return;
77 |
78 | const currentAngle =
79 | parseFloat(element.style.getPropertyValue("--start")) || 0;
80 | let targetAngle =
81 | (180 * Math.atan2(mouseY - center[1], mouseX - center[0])) /
82 | Math.PI +
83 | 90;
84 |
85 | const angleDiff = ((targetAngle - currentAngle + 180) % 360) - 180;
86 | const newAngle = currentAngle + angleDiff;
87 |
88 | animate(currentAngle, newAngle, {
89 | duration: movementDuration,
90 | ease: [0.16, 1, 0.3, 1],
91 | onUpdate: (value) => {
92 | element.style.setProperty("--start", String(value));
93 | },
94 | });
95 | });
96 | },
97 | [inactiveZone, proximity, movementDuration]
98 | );
99 |
100 | useEffect(() => {
101 | if (disabled) return;
102 |
103 | const handleScroll = () => handleMove();
104 | const handlePointerMove = (e: PointerEvent) => handleMove(e);
105 |
106 | window.addEventListener("scroll", handleScroll, { passive: true });
107 | document.body.addEventListener("pointermove", handlePointerMove, {
108 | passive: true,
109 | });
110 |
111 | return () => {
112 | if (animationFrameRef.current) {
113 | cancelAnimationFrame(animationFrameRef.current);
114 | }
115 | window.removeEventListener("scroll", handleScroll);
116 | document.body.removeEventListener("pointermove", handlePointerMove);
117 | };
118 | }, [handleMove, disabled]);
119 |
120 | return (
121 | <>
122 |
130 | 0 && "blur-[var(--blur)] ",
165 | className,
166 | disabled && "!hidden"
167 | )}
168 | >
169 |
182 |
183 | >
184 | );
185 | }
186 | );
187 |
188 | GlowingEffect.displayName = "GlowingEffect";
189 |
190 | export { GlowingEffect };
191 |
--------------------------------------------------------------------------------
/src/components/ui/background-beams.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import { motion } from "motion/react";
4 | import { cn } from "../../lib/utils";
5 |
6 | export const BackgroundBeams = React.memo(
7 | ({ className }: { className?: string }) => {
8 | const paths = [
9 | "M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875",
10 | "M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867",
11 | "M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859",
12 | "M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851",
13 | "M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843",
14 | "M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835",
15 | "M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827",
16 | "M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819",
17 | "M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811",
18 | "M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803",
19 | "M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795",
20 | "M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787",
21 | "M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779",
22 | "M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771",
23 | "M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763",
24 | "M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755",
25 | "M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747",
26 | "M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739",
27 | "M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731",
28 | "M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723",
29 | "M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715",
30 | "M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707",
31 | "M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699",
32 | "M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691",
33 | "M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683",
34 | "M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675",
35 | "M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667",
36 | "M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659",
37 | "M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651",
38 | "M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643",
39 | "M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635",
40 | "M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627",
41 | "M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619",
42 | "M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611",
43 | "M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603",
44 | "M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595",
45 | "M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587",
46 | "M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579",
47 | "M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571",
48 | "M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563",
49 | "M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555",
50 | "M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547",
51 | "M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539",
52 | "M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531",
53 | "M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523",
54 | "M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515",
55 | "M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507",
56 | "M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499",
57 | "M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491",
58 | "M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483",
59 | ];
60 | return (
61 |
67 |
75 |
81 |
82 | {paths.map((path, index) => (
83 |
90 | ))}
91 |
92 | {paths.map((path, index) => (
93 |
115 |
116 |
117 |
118 |
119 |
120 | ))}
121 |
122 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | );
138 | },
139 | );
140 |
141 | BackgroundBeams.displayName = "BackgroundBeams";
142 |
--------------------------------------------------------------------------------
/src/components/ui/sparkles.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useId, useMemo } from "react";
3 | import { useEffect, useState } from "react";
4 | import Particles, { initParticlesEngine } from "@tsparticles/react";
5 | import type { Container, SingleOrMultiple } from "@tsparticles/engine";
6 | import { loadSlim } from "@tsparticles/slim";
7 | import { cn } from "../../lib/utils";
8 | import { motion, useAnimation } from "motion/react";
9 |
10 | type ParticlesProps = {
11 | id?: string;
12 | className?: string;
13 | background?: string;
14 | particleSize?: number;
15 | minSize?: number;
16 | maxSize?: number;
17 | speed?: number;
18 | particleColor?: string;
19 | particleDensity?: number;
20 | };
21 | export const SparklesCore = (props: ParticlesProps) => {
22 | const {
23 | id,
24 | className,
25 | background,
26 | minSize,
27 | maxSize,
28 | speed,
29 | particleColor,
30 | particleDensity,
31 | } = props;
32 | const [init, setInit] = useState(false);
33 | useEffect(() => {
34 | initParticlesEngine(async (engine) => {
35 | await loadSlim(engine);
36 | }).then(() => {
37 | setInit(true);
38 | });
39 | }, []);
40 | const controls = useAnimation();
41 |
42 | const particlesLoaded = async (container?: Container) => {
43 | if (container) {
44 | controls.start({
45 | opacity: 1,
46 | transition: {
47 | duration: 1,
48 | },
49 | });
50 | }
51 | };
52 |
53 | const generatedId = useId();
54 | return (
55 |
56 | {init && (
57 | | undefined,
161 | },
162 | groups: {},
163 | move: {
164 | angle: {
165 | offset: 0,
166 | value: 90,
167 | },
168 | attract: {
169 | distance: 200,
170 | enable: false,
171 | rotate: {
172 | x: 3000,
173 | y: 3000,
174 | },
175 | },
176 | center: {
177 | x: 50,
178 | y: 50,
179 | mode: "percent",
180 | radius: 0,
181 | },
182 | decay: 0,
183 | distance: {},
184 | direction: "none",
185 | drift: 0,
186 | enable: true,
187 | gravity: {
188 | acceleration: 9.81,
189 | enable: false,
190 | inverse: false,
191 | maxSpeed: 50,
192 | },
193 | path: {
194 | clamp: true,
195 | delay: {
196 | value: 0,
197 | },
198 | enable: false,
199 | options: {},
200 | },
201 | outModes: {
202 | default: "out",
203 | },
204 | random: false,
205 | size: false,
206 | speed: {
207 | min: 0.1,
208 | max: 1,
209 | },
210 | spin: {
211 | acceleration: 0,
212 | enable: false,
213 | },
214 | straight: false,
215 | trail: {
216 | enable: false,
217 | length: 10,
218 | fill: {},
219 | },
220 | vibrate: false,
221 | warp: false,
222 | },
223 | number: {
224 | density: {
225 | enable: true,
226 | width: 400,
227 | height: 400,
228 | },
229 | limit: {
230 | mode: "delete",
231 | value: 0,
232 | },
233 | value: particleDensity || 120,
234 | },
235 | opacity: {
236 | value: {
237 | min: 0.1,
238 | max: 1,
239 | },
240 | animation: {
241 | count: 0,
242 | enable: true,
243 | speed: speed || 4,
244 | decay: 0,
245 | delay: 0,
246 | sync: false,
247 | mode: "auto",
248 | startValue: "random",
249 | destroy: "none",
250 | },
251 | },
252 | reduceDuplicates: false,
253 | shadow: {
254 | blur: 0,
255 | color: {
256 | value: "#000",
257 | },
258 | enable: false,
259 | offset: {
260 | x: 0,
261 | y: 0,
262 | },
263 | },
264 | shape: {
265 | close: true,
266 | fill: true,
267 | options: {},
268 | type: "circle",
269 | },
270 | size: {
271 | value: {
272 | min: minSize || 1,
273 | max: maxSize || 3,
274 | },
275 | animation: {
276 | count: 0,
277 | enable: false,
278 | speed: 5,
279 | decay: 0,
280 | delay: 0,
281 | sync: false,
282 | mode: "auto",
283 | startValue: "random",
284 | destroy: "none",
285 | },
286 | },
287 | stroke: {
288 | width: 0,
289 | },
290 | zIndex: {
291 | value: 0,
292 | opacityRate: 1,
293 | sizeRate: 1,
294 | velocityRate: 1,
295 | },
296 | destroy: {
297 | bounds: {},
298 | mode: "none",
299 | split: {
300 | count: 1,
301 | factor: {
302 | value: 3,
303 | },
304 | rate: {
305 | value: {
306 | min: 4,
307 | max: 9,
308 | },
309 | },
310 | sizeOffset: true,
311 | },
312 | },
313 | roll: {
314 | darken: {
315 | enable: false,
316 | value: 0,
317 | },
318 | enable: false,
319 | enlighten: {
320 | enable: false,
321 | value: 0,
322 | },
323 | mode: "vertical",
324 | speed: 25,
325 | },
326 | tilt: {
327 | value: 0,
328 | animation: {
329 | enable: false,
330 | speed: 0,
331 | decay: 0,
332 | sync: false,
333 | },
334 | direction: "clockwise",
335 | enable: false,
336 | },
337 | twinkle: {
338 | lines: {
339 | enable: false,
340 | frequency: 0.05,
341 | opacity: 1,
342 | },
343 | particles: {
344 | enable: false,
345 | frequency: 0.05,
346 | opacity: 1,
347 | },
348 | },
349 | wobble: {
350 | distance: 5,
351 | enable: false,
352 | speed: {
353 | angle: 50,
354 | move: 10,
355 | },
356 | },
357 | life: {
358 | count: 0,
359 | delay: {
360 | value: 0,
361 | sync: false,
362 | },
363 | duration: {
364 | value: 0,
365 | sync: false,
366 | },
367 | },
368 | rotate: {
369 | value: 0,
370 | animation: {
371 | enable: false,
372 | speed: 0,
373 | decay: 0,
374 | sync: false,
375 | },
376 | direction: "clockwise",
377 | path: false,
378 | },
379 | orbit: {
380 | animation: {
381 | count: 0,
382 | enable: false,
383 | speed: 1,
384 | decay: 0,
385 | delay: 0,
386 | sync: false,
387 | },
388 | enable: false,
389 | opacity: 1,
390 | rotation: {
391 | value: 45,
392 | },
393 | width: 1,
394 | },
395 | links: {
396 | blink: false,
397 | color: {
398 | value: "#fff",
399 | },
400 | consent: false,
401 | distance: 100,
402 | enable: false,
403 | frequency: 1,
404 | opacity: 1,
405 | shadow: {
406 | blur: 5,
407 | color: {
408 | value: "#000",
409 | },
410 | enable: false,
411 | },
412 | triangles: {
413 | enable: false,
414 | frequency: 1,
415 | },
416 | width: 1,
417 | warp: false,
418 | },
419 | repulse: {
420 | value: 0,
421 | enabled: false,
422 | distance: 1,
423 | duration: 1,
424 | factor: 1,
425 | speed: 1,
426 | },
427 | },
428 | detectRetina: true,
429 | }}
430 | />
431 | )}
432 |
433 | );
434 | };
435 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useRef } from 'react';
2 | import { LampContainer } from "./components/ui/lamp";
3 | import { motion } from "motion/react";
4 | import { SparklesCore } from "./components/ui/sparkles";
5 | import './index.css';
6 | import GridCard from './components/GridCard';
7 |
8 | const App = () => {
9 | const [spotifyTrackUrl, setSpotifyTrackUrl] = useState('');
10 | const [spotifyTrackId, setSpotifyTrackId] = useState('');
11 | const [isLoading, setIsLoading] = useState(false);
12 | const [fetchError, setFetchError] = useState(null);
13 | const [downloadDetails, setDownloadDetails] = useState(null);
14 | const [isVerified, setIsVerified] = useState(false);
15 | const [isDownloading, setDownloading] = useState(false);
16 | const inputSectionRef = useRef(null);
17 |
18 | // Extract track ID from Spotify URL
19 | const extractTrackId = (url) => {
20 | const match = url.match(/track\/([a-zA-Z0-9]+)/);
21 | return match ? match[1] : '';
22 | };
23 |
24 | // Verify song details from API
25 | const verifySongData = async () => {
26 | if (!spotifyTrackId) {
27 | setFetchError("Please enter a valid Spotify track URL.");
28 | return;
29 | }
30 |
31 | setIsLoading(true);
32 | setFetchError(null);
33 | setDownloadDetails(null);
34 | setIsVerified(false);
35 |
36 | const apiUrl = `https://spotify-downloader9.p.rapidapi.com/downloadSong?songId=${spotifyTrackId}`;
37 | const headers = {
38 | 'x-rapidapi-key': 'b956d6a526msh7b1a2a3662d09cdp11fa3djsn5b6cad23f10f',
39 | 'x-rapidapi-host': 'spotify-downloader9.p.rapidapi.com',
40 | };
41 |
42 | try {
43 | const response = await fetch(apiUrl, { method: 'GET', headers });
44 | if (!response.ok) throw new Error(`API Error: ${response.status}`);
45 |
46 | const data = await response.json();
47 | if (data.success && data.data) {
48 | setDownloadDetails(data.data);
49 | console.log(data.data);
50 | setIsVerified(true);
51 | } else {
52 | throw new Error('Invalid song data received from API.');
53 | }
54 | } catch (err) {
55 | setFetchError(err.message || 'Failed to fetch song data.');
56 | } finally {
57 | setIsLoading(false);
58 | }
59 | };
60 |
61 | // Download after verification
62 | const handleDownload = async () => {
63 | if (!downloadDetails?.downloadLink) {
64 | setFetchError("Download link not available. Please verify again.");
65 | setIsVerified(false);
66 | return;
67 | }
68 |
69 | setDownloading(true);
70 | setFetchError(null);
71 |
72 | try {
73 | const response = await fetch(downloadDetails.downloadLink);
74 | if (!response.ok) throw new Error("Failed to download file");
75 |
76 | const blob = await response.blob();
77 | const url = window.URL.createObjectURL(blob);
78 |
79 | const link = document.createElement('a');
80 | link.href = url;
81 | link.setAttribute(
82 | 'download',
83 | `${downloadDetails.title || 'spotify_song'}.mp3`
84 | );
85 | document.body.appendChild(link);
86 | link.click();
87 | document.body.removeChild(link);
88 |
89 | // Cleanup the blob URL
90 | window.URL.revokeObjectURL(url);
91 | } catch (err) {
92 | setFetchError(err.message || "Download failed");
93 | } finally {
94 | setDownloading(false);
95 | }
96 | };
97 |
98 | // Handle input
99 | const handleSpotifyUrlChange = (e) => {
100 | const url = e.target.value.trim();
101 | setSpotifyTrackUrl(url);
102 | setSpotifyTrackId(extractTrackId(url));
103 | setIsVerified(false);
104 | setDownloadDetails(null);
105 | setFetchError(null);
106 | };
107 |
108 | const scrollToInputSection = () => {
109 | inputSectionRef.current?.scrollIntoView({ behavior: "smooth" });
110 | };
111 |
112 | return (
113 |
114 | {/* Background sparkles */}
115 |
124 |
125 | {/* Foreground */}
126 |
127 |
128 |
134 | Download Spotify Songs,
135 |
136 | Playlists & Albums in High Quality MP3
137 |
138 |
139 |
140 |
146 | Explore now
147 |
148 |
149 |
150 | {/* Features */}
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | {/* URL Input */}
160 |
161 |
167 | Paste the URL Below
168 |
169 |
170 |
177 |
184 |
185 |
190 | {isLoading ? 'Verifying...' : isVerified ? (isDownloading ? "Downloading..." : 'Download') : 'Verify'}
191 |
192 |
193 | {/* Download Details Section */}
194 | {downloadDetails && (
195 |
201 |
202 |
203 |
208 |
209 |
{downloadDetails.title}
210 |
{downloadDetails.album}
211 |
{downloadDetails.artist}
212 |
{downloadDetails.releaseDate}
213 |
214 |
215 |
216 |
217 | )}
218 | {fetchError &&
{fetchError}
}
219 |
220 |
221 |
222 |
Spotify Downloader - Free MP3 Download of Songs, Playlists & Albums
223 |
How to Download Spotify Songs as MP3
224 |
Our Spotify downloader makes it easy to save any Spotify track, playlist or album as high-quality MP3 files. Simply paste the Spotify song URL, click download, and get your MP3 instantly. No registration or premium account required.
225 |
226 |
Features of Our Spotify to MP3 Converter
227 |
228 | Download individual Spotify songs or entire playlists
229 | Convert Spotify to MP3 with 320kbps audio quality
230 | Preserve all metadata including artist, album and cover art
231 | Works without Spotify Premium subscription
232 | Fast downloads with no speed limits
233 | How to save Spotify songs offline free
234 | Download Spotify music to phone
235 | Best Spotify to MP3 converter online
236 | No software installation - works directly in your browser
237 |
238 |
239 |
Why Choose Our Spotify Music Downloader?
240 |
Among all Spotify downloaders available online, our tool stands out because:
241 |
242 | 100% free with no hidden costs
243 | No watermarks on downloaded files
244 | Supports all Spotify regions and languages
245 | Regular updates to ensure compatibility
246 | Secure connection protects your privacy
247 |
248 |
249 |
Spotify Downloader FAQ
250 |
Is it legal to download songs from Spotify?
251 | Our tool is for personal use only. Please only download music you have rights to access.
252 |
253 |
What audio quality can I expect?
254 | Downloads are available in 128kbps, 256kbps and 320kbps MP3 quality.
255 |
256 |
Does this work on mobile devices?
257 | Yes! Our Spotify downloader works on iPhone, Android, tablets and computers.
258 |
259 |
260 | );
261 | };
262 |
263 | export default App;
--------------------------------------------------------------------------------