diff --git a/README.md b/README.md index 7fa0451..56c9820 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,18 @@ Self-hosted YouTube music streaming application built with Next.js. - Toast notifications for user feedback - Simple JSON file storage - no database setup required - Easy Docker deployment +- **Smart search**: Search YouTube directly from the app or paste URLs + ## Recent Updates -### v1.1.0 - Latest Improvements +### v1.2.0 - Latest Improvements + +- ✅ **YouTube search integration**: Search for songs directly in the app without needing URLs +- ✅ **Smart input detection**: Automatically detects if input is a URL or search query +- ✅ **Live search results**: Real-time search with thumbnails, duration, and channel info +- ✅ **Progress bar fix**: Fixed playbar not advancing for single songs + +### v1.1.0 - Previous Updates - ✅ **Direct song deletion**: Removed confirmation dialogs when deleting songs from home page for faster workflow - ✅ **Bulk playlist creation**: Add entire playlists directly from YouTube playlist URLs @@ -137,12 +146,13 @@ If you prefer to build the Docker image yourself: ## Usage -1. **Add a song**: Paste a YouTube URL in the input field and click "Add" -2. **Play a song**: Click the play button on any song card -3. **Control playback**: Use the player controls at the bottom of the page -4. **Create playlists**: Navigate to Playlists and create your custom collections -5. **Shuffle mode**: Toggle shuffle to randomize playback order -6. **Navigate freely**: Music continues playing as you browse different pages +1. **Add a song**: Search for songs by artist/title or paste a YouTube URL in the input field +2. **Search**: Type any song name or artist and select from the results dropdown +3. **Play a song**: Click the play button on any song card +4. **Control playback**: Use the player controls at the bottom of the page +5. **Create playlists**: Navigate to Playlists and create your custom collections +6. **Shuffle mode**: Toggle shuffle to randomize playback order +7. **Navigate freely**: Music continues playing as you browse different pages ## Data Storage diff --git a/app/api/search/route.ts b/app/api/search/route.ts new file mode 100644 index 0000000..b49c2bf --- /dev/null +++ b/app/api/search/route.ts @@ -0,0 +1,19 @@ +import { NextRequest, NextResponse } from 'next/server' +import { searchYoutube } from '@/lib/ytdlp' + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url) + const query = searchParams.get('q') + + if (!query) { + return NextResponse.json({ error: 'Query parameter is required' }, { status: 400 }) + } + + const results = await searchYoutube(query, 10) + return NextResponse.json(results) + } catch (error) { + console.error('Search error:', error) + return NextResponse.json({ error: 'Failed to search' }, { status: 500 }) + } +} diff --git a/components/Header.tsx b/components/Header.tsx index b7bcd2c..d3fe340 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -1,46 +1,214 @@ 'use client' -import { useState } from 'react' -import { Plus } from 'lucide-react' +import { useState, useRef, useEffect } from 'react' +import { Plus, Search, Loader2 } from 'lucide-react' +import Image from 'next/image' interface HeaderProps { onAddSong: (url: string) => void isLoading?: boolean } +interface SearchResult { + id: string + title: string + channel: string + thumbnail: string + duration: number +} + export default function Header({ onAddSong, isLoading }: HeaderProps) { - const [url, setUrl] = useState('') + const [input, setInput] = useState('') + const [showResults, setShowResults] = useState(false) + const [searchResults, setSearchResults] = useState([]) + const [isSearching, setIsSearching] = useState(false) + const [searchError, setSearchError] = useState('') + const resultsRef = useRef(null) + const inputRef = useRef(null) + + // Close results when clicking outside + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (resultsRef.current && !resultsRef.current.contains(event.target as Node) && + inputRef.current && !inputRef.current.contains(event.target as Node)) { + setShowResults(false) + } + } + + document.addEventListener('mousedown', handleClickOutside) + return () => document.removeEventListener('mousedown', handleClickOutside) + }, []) + + const isYoutubeUrl = (str: string) => { + return str.includes('youtube.com/') || str.includes('youtu.be/') + } + + const handleSearch = async (query: string) => { + if (!query.trim() || isYoutubeUrl(query)) { + setShowResults(false) + return + } + + setIsSearching(true) + setSearchError('') + + try { + const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`) + if (response.ok) { + const results = await response.json() + setSearchResults(results) + setShowResults(true) + } else { + setSearchError('Search failed') + setShowResults(false) + } + } catch (error) { + console.error('Search error:', error) + setSearchError('Search error') + setShowResults(false) + } finally { + setIsSearching(false) + } + } + + const handleInputChange = (e: React.ChangeEvent) => { + const value = e.target.value + setInput(value) + + // Auto-search if not a URL + if (value.trim() && !isYoutubeUrl(value)) { + // Debounce search + const timeoutId = setTimeout(() => { + handleSearch(value) + }, 500) + return () => clearTimeout(timeoutId) + } else { + setShowResults(false) + } + } const handleSubmit = (e: React.FormEvent) => { e.preventDefault() - if (url.trim()) { - onAddSong(url) - setUrl('') + if (input.trim()) { + if (isYoutubeUrl(input)) { + onAddSong(input) + setInput('') + setShowResults(false) + } else { + handleSearch(input) + } } } + const handleSelectResult = (result: SearchResult) => { + const youtubeUrl = `https://www.youtube.com/watch?v=${result.id}` + onAddSong(youtubeUrl) + setInput('') + setShowResults(false) + setSearchResults([]) + } + + const formatDuration = (seconds: number): string => { + const mins = Math.floor(seconds / 60) + const secs = Math.floor(seconds % 60) + return `${mins}:${secs.toString().padStart(2, '0')}` + } + return (
-
+
setUrl(e.target.value)} - placeholder="Paste YouTube URL here..." - className="w-full bg-gray-800 text-white px-3 py-2 sm:px-4 sm:py-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 placeholder-gray-500 text-sm sm:text-base" + value={input} + onChange={handleInputChange} + placeholder="Search for songs or paste YouTube URL..." + className="w-full bg-gray-800 text-white px-3 py-2 sm:px-4 sm:py-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 placeholder-gray-500 text-sm sm:text-base pr-10" disabled={isLoading} /> + {isSearching && ( +
+ +
+ )}
+ + {/* Search Results Dropdown */} + {showResults && searchResults.length > 0 && ( +
+ {searchResults.map((result) => ( + + ))} +
+ )} + + {showResults && searchResults.length === 0 && !isSearching && ( +
+

No results found

+
+ )} + + {searchError && ( +
+

{searchError}

+
+ )}
) diff --git a/components/PlayerBar.tsx b/components/PlayerBar.tsx index e09cd95..01b8cbf 100644 --- a/components/PlayerBar.tsx +++ b/components/PlayerBar.tsx @@ -125,7 +125,7 @@ export default function PlayerBar({ audio.removeEventListener('canplay', handleCanPlay) audio.removeEventListener('ended', handleEnded) } - }, [onNext]) + }, [onNext, currentSong]) const togglePlay = () => { if (audioRef.current) { diff --git a/data/db.json b/data/db.json index bfa3e1e..92acd05 100644 --- a/data/db.json +++ b/data/db.json @@ -1,1099 +1,5 @@ { - "songs": [ - { - "youtubeId": "JhOePB3WiuY", - "title": "ROSALÍA - La Perla (ft. Yahritza Y Su Esencia) (Letra/Lyrics)", - "channelName": "tivs14", - "thumbnail": "https://i.ytimg.com/vi/JhOePB3WiuY/maxresdefault.jpg", - "duration": 196, - "id": "f901c2c0b1f35932587f468cdabff364", - "isInLibrary": false, - "createdAt": 1766661870677 - }, - { - "youtubeId": "F1_aOX0acbY", - "title": "W Sound 05 \"LA PLENA\" - Beéle, Westcol, Ovy On The Drums", - "channelName": "W Sound", - "thumbnail": "https://i.ytimg.com/vi/F1_aOX0acbY/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBGmo_GBWDwvml-jUvsAjcLdFPFiA", - "duration": 150, - "id": "9c582292a947441c2462af6a754815c7", - "isInLibrary": true, - "createdAt": 1766662249255 - }, - { - "youtubeId": "QCZZwZQ4qNs", - "title": "KAROL G - Si Antes Te Hubiera Conocido | Coke Studio", - "channelName": "KAROL G", - "thumbnail": "https://i.ytimg.com/vi/QCZZwZQ4qNs/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAXTyYq6dgEDCi1IMq4tAylZWWdhQ", - "duration": 196, - "id": "eadd4ff80f612fd0e38854d1d187ffe8", - "isInLibrary": true, - "createdAt": 1766662249260 - }, - { - "youtubeId": "J8U9czVT7tM", - "title": "Rauw Alejandro, Romeo Santos - Khé? (Official Video)", - "channelName": "Rauw Alejandro", - "thumbnail": "https://i.ytimg.com/vi/J8U9czVT7tM/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBcEVOapWWI0u1PRxxBlJdr2Kb-6g", - "duration": 248, - "id": "e86e0adf843cf4d8b223aeeb6860a0fb", - "isInLibrary": true, - "createdAt": 1766662249265 - }, - { - "youtubeId": "kE1-O0tU-UU", - "title": "Xavi, Manuel Turizo - En Privado (Official Video)", - "channelName": "Xavi Oficial", - "thumbnail": "https://i.ytimg.com/vi/kE1-O0tU-UU/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDs472g0TXUDnrxhFU39ZT6jYDa3g", - "duration": 199, - "id": "1ccb34504cc701b32cd56c9a299425d8", - "isInLibrary": true, - "createdAt": 1766662249269 - }, - { - "youtubeId": "cMhT1SqxqHY", - "title": "Alleh & Yorghaki - capaz (merengueton) (visualizer)", - "channelName": "Alleh", - "thumbnail": "https://i.ytimg.com/vi/cMhT1SqxqHY/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBHWRiDvfaQx6Eecy-wxBt3SEpH3A", - "duration": 180, - "id": "7ad4ad43e9eb8e43ca6dfb0d7e73f5df", - "isInLibrary": true, - "createdAt": 1766662249273 - }, - { - "youtubeId": "qNw8ejrI0nM", - "title": "DADDY YANKEE || BZRP Music Sessions #0/66", - "channelName": "Bizarrap and Daddy Yankee", - "thumbnail": "https://i.ytimg.com/vi/qNw8ejrI0nM/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCTXtn0DUP9xRQul5Gaaq3w5tamXQ", - "duration": 160, - "id": "d102378ad3e7f64973a79b485a7e1941", - "isInLibrary": true, - "createdAt": 1766662249278 - }, - { - "youtubeId": "TiM_TFpT_DE", - "title": "La Bachata - MTZ Manuel Turizo | Video Oficial", - "channelName": "Manuel Turizo", - "thumbnail": "https://i.ytimg.com/vi/TiM_TFpT_DE/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDCzrrxwiNOta8uAubehwhHmhfNhQ", - "duration": 195, - "id": "d6a590ac2126ccb91dde9d1204adc668", - "isInLibrary": true, - "createdAt": 1766662249283 - }, - { - "youtubeId": "yNEpyU3PnDI", - "title": "BAD BUNNY ft. Dei V, Omar Courtz - VeLDÁ (Visualizer) | DeBÍ TiRAR MáS FOToS", - "channelName": "Bad Bunny", - "thumbnail": "https://i.ytimg.com/vi/yNEpyU3PnDI/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCHb6N6-PaIx_-9dK37XNZNdyeYvw", - "duration": 236, - "id": "421099682a39c2a93f46a24eae7c2d24", - "isInLibrary": true, - "createdAt": 1766662249289 - }, - { - "youtubeId": "xU3OvYoE1rc", - "title": "Becky G, Manuel Turizo - QUE HACES (Official Video)", - "channelName": "Becky G", - "thumbnail": "https://i.ytimg.com/vi/xU3OvYoE1rc/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLB_si2LxFgV77ZKnzGjuE1dZmkocQ", - "duration": 193, - "id": "b3028951cd727989c2e6856d1887779a", - "isInLibrary": true, - "createdAt": 1766662249293 - }, - { - "youtubeId": "a42lMLKItUc", - "title": "Bomba Estéreo, Rawayana, ASTROPICAL - Me Pasa (Piscis) (Official Video)", - "channelName": "Bomba Estéreo", - "thumbnail": "https://i.ytimg.com/vi/a42lMLKItUc/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAu9GodG7tUc59E9Kz3B4_U5uAHug", - "duration": 214, - "id": "9348b58babc37a14d6d8ccbd4c217492", - "isInLibrary": true, - "createdAt": 1766662249298 - }, - { - "youtubeId": "jZGpkLElSu8", - "title": "KAROL G, Shakira - TQG (Official Video)", - "channelName": "KAROL G", - "thumbnail": "https://i.ytimg.com/vi/jZGpkLElSu8/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCI9zuH0JHt3mLpUwAYFQZms1Uhqg", - "duration": 217, - "id": "b47fc29d665d12e717331632d81a243c", - "isInLibrary": true, - "createdAt": 1766662249302 - }, - { - "youtubeId": "3mchJ-EW9rM", - "title": "Yng Lvcas & Peso Pluma - La Bebe (Remix) [Video Oficial]", - "channelName": "Yng Lvcas", - "thumbnail": "https://i.ytimg.com/vi/3mchJ-EW9rM/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCmRjghxI0S5aL2EjUJMFg21N30vA", - "duration": 245, - "id": "06c3a9f1771fd2c5f1bedd5edede2ef0", - "isInLibrary": true, - "createdAt": 1766662249305 - }, - { - "youtubeId": "-r687V8yqKY", - "title": "FloyyMenor, Cris MJ - Gata Only", - "channelName": "FloyyMenor", - "thumbnail": "https://i.ytimg.com/vi/-r687V8yqKY/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDE8oAz3QZtyd_vEU4WxJp3cmfAMA", - "duration": 216, - "id": "1c3abf88472606a9e53f75312a66fe98", - "isInLibrary": true, - "createdAt": 1766662249309 - }, - { - "youtubeId": "WcIcVapfqXw", - "title": "Rema, Selena Gomez - Calm Down (Official Music Video)", - "channelName": "Selena Gomez", - "thumbnail": "https://i.ytimg.com/vi/WcIcVapfqXw/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAZvELFCumAsrKCaqklc9gyxwRPyg", - "duration": 240, - "id": "25fc470fe2ef68d46d27289f1e193886", - "isInLibrary": true, - "createdAt": 1766662249314 - }, - { - "youtubeId": "3inw26U-os4", - "title": "Grupo Frontera x Bad Bunny - UN X100TO (Video Oficial)", - "channelName": "Grupo Frontera", - "thumbnail": "https://i.ytimg.com/vi/3inw26U-os4/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLB_B-t8CCW-6jsdNwF3hSWIlSPqlg", - "duration": 195, - "id": "149a26514df5060037004e1c15e79275", - "isInLibrary": true, - "createdAt": 1766662249318 - }, - { - "youtubeId": "ca48oMV59LU", - "title": "KAROL G - Provenza (Official Video)", - "channelName": "KAROL G", - "thumbnail": "https://i.ytimg.com/vi/ca48oMV59LU/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLB2fFaba4P9-m_i2RpUt0DaHC6fUA", - "duration": 227, - "id": "5aaf02aa74acd38a6ff9affa10d3a5af", - "isInLibrary": true, - "createdAt": 1766662249321 - }, - { - "youtubeId": "HfzbN5ky5Co", - "title": "Xavi - La Diabla (Official Video)", - "channelName": "Xavi Oficial", - "thumbnail": "https://i.ytimg.com/vi/HfzbN5ky5Co/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBFwsaifQa2TbPW8it5KOzwHd7bTA", - "duration": 173, - "id": "974e3a7dd6ebd938af9f6e17105efe5b", - "isInLibrary": true, - "createdAt": 1766662249325 - }, - { - "youtubeId": "fOT0BUpITw8", - "title": "BELLAKEO (Video Oficial) - Peso Pluma, Anitta", - "channelName": "Peso Pluma", - "thumbnail": "https://i.ytimg.com/vi/fOT0BUpITw8/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDIUMo6wmNwCk7_4gaFgstLo8XRVQ", - "duration": 235, - "id": "a3256a6c1d8ea8c9232891547ad5c04c", - "isInLibrary": true, - "createdAt": 1766662249329 - }, - { - "youtubeId": "saGYMhApaH8", - "title": "Bad Bunny (ft. Chencho Corleone) - Me Porto Bonito (Official Video) | Un Verano Sin Ti", - "channelName": "Bad Bunny", - "thumbnail": "https://i.ytimg.com/vi/saGYMhApaH8/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLA1pbGC6EKmN9eFdYyXjeEmqvOqYA", - "duration": 192, - "id": "ef1cbdd0b3d51a6163483c183f874ac6", - "isInLibrary": true, - "createdAt": 1766662249334 - }, - { - "youtubeId": "CocEMWdc7Ck", - "title": "SHAKIRA || BZRP Music Sessions #53/66", - "channelName": "Bizarrap", - "thumbnail": "https://i.ytimg.com/vi/CocEMWdc7Ck/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCLhxzkd8fNlNHYOLMKzo4SdQ3FWQ", - "duration": 218, - "id": "b4c10fdd2206766597858a8727aab39a", - "isInLibrary": true, - "createdAt": 1766662249339 - }, - { - "youtubeId": "LWdAMW_4Yq0", - "title": "MESITA, NICKI NICOLE, EMILIA, TIAGO PZK - UNA FOTO REMIX (Video Oficial)", - "channelName": "Mesita", - "thumbnail": "https://i.ytimg.com/vi/LWdAMW_4Yq0/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCzLGQmIZoYGiDwVrA1LimXvJ1MUw", - "duration": 245, - "id": "3469e6e91767be972331f48f6eca8d17", - "isInLibrary": true, - "createdAt": 1766662249343 - }, - { - "youtubeId": "Cr8K88UcO0s", - "title": "Bad Bunny - Tití Me Preguntó (Official Video) | Un Verano Sin Ti", - "channelName": "Bad Bunny", - "thumbnail": "https://i.ytimg.com/vi/Cr8K88UcO0s/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLC3nJQX0r-Dp1ySRiz-E1oizetJ7g", - "duration": 291, - "id": "b69a20b6625246c875ed44c8ee30a047", - "isInLibrary": true, - "createdAt": 1766662249348 - }, - { - "youtubeId": "BVdngsy95mY", - "title": "Myke Towers - Lala (Video Oficial)", - "channelName": "Myke Towers", - "thumbnail": "https://i.ytimg.com/vi/BVdngsy95mY/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBxRP9dmzMD_Mc5P9w40yKy037Frg", - "duration": 198, - "id": "c35eea92f1c3e8597b39f276ae83536b", - "isInLibrary": true, - "createdAt": 1766662249352 - }, - { - "youtubeId": "spbagny8OjM", - "title": "UWAIE - Kapo (Video Oficial)", - "channelName": "Kapo", - "thumbnail": "https://i.ytimg.com/vi/spbagny8OjM/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLC0kuEFtTNPqPBE81wyzd0C209FAg", - "duration": 172, - "id": "6309cb2d181607e6b2152b510dbe2f37", - "isInLibrary": true, - "createdAt": 1766662249356 - }, - { - "youtubeId": "YpoUzo-CflQ", - "title": "Yailin La Mas Viral - Bing Bong (Video Oficial)", - "channelName": "Yailin La Mas Viral", - "thumbnail": "https://i.ytimg.com/vi/YpoUzo-CflQ/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBl00GeBELTJqi-UiYHznB-lN1eew", - "duration": 124, - "id": "4f7ca9ec897044676336607c7c8dd6a5", - "isInLibrary": true, - "createdAt": 1766662249363 - }, - { - "youtubeId": "oBofuVYDoG4", - "title": "Shakira - Soltera (Official Video)", - "channelName": "Shakira", - "thumbnail": "https://i.ytimg.com/vi/oBofuVYDoG4/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCF3tlsdsijzSbI55RSQ6_sdwl2eA", - "duration": 249, - "id": "743682c3a097c128d9f8ae251c44e894", - "isInLibrary": true, - "createdAt": 1766662249367 - }, - { - "youtubeId": "cD5T1Y4b7wA", - "title": "Feid, Young Miko - Classy 101 (Official Video)", - "channelName": "Feid", - "thumbnail": "https://i.ytimg.com/vi/cD5T1Y4b7wA/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLALGKeIKrrJanOVitwOcxYPJmmxCQ", - "duration": 196, - "id": "55cadbb57d45d06dd01d25ec66df1316", - "isInLibrary": true, - "createdAt": 1766662249371 - }, - { - "youtubeId": "x2oUajHp8pg", - "title": "Feid, ATL Jacob - LUNA (Official Video)", - "channelName": "Feid", - "thumbnail": "https://i.ytimg.com/vi/x2oUajHp8pg/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDdmqi2M6_ENOhAJf9tiYPZdTFMhA", - "duration": 200, - "id": "27b74bcbc73b65071daf8aa2e6155d18", - "isInLibrary": true, - "createdAt": 1766662249376 - }, - { - "youtubeId": "lmDfmilZ4Xo", - "title": "OHNANA - Kapo (Video Oficial)", - "channelName": "Kapo", - "thumbnail": "https://i.ytimg.com/vi/lmDfmilZ4Xo/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLC7UPrtrqM_bDurzZ0eCyQazN4klg", - "duration": 157, - "id": "ad9eaae0f1a5ce216657d26a89b7d483", - "isInLibrary": true, - "createdAt": 1766662249380 - }, - { - "youtubeId": "A_g3lMcWVy0", - "title": "QUEVEDO || BZRP Music Sessions #52/66", - "channelName": "Bizarrap", - "thumbnail": "https://i.ytimg.com/vi/A_g3lMcWVy0/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAH7jmjcFlg3mAzLhvxZydUTPAIdg", - "duration": 204, - "id": "a0e5714f44645120d4ee530328936d19", - "isInLibrary": true, - "createdAt": 1766662249384 - }, - { - "youtubeId": "5g2hT4GmAGU", - "title": "ROSALÍA - DESPECHÁ (Official Video)", - "channelName": "ROSALÍA", - "thumbnail": "https://i.ytimg.com/vi/5g2hT4GmAGU/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAzVCJVnhfQ8hFMM3BkoNv1sY2P_w", - "duration": 159, - "id": "1031c7588047f106941914203aa35296", - "isInLibrary": true, - "createdAt": 1766662249388 - }, - { - "youtubeId": "CFPLIaMpGrY", - "title": "Rauw Alejandro - Todo De Ti (Official Video)", - "channelName": "Rauw Alejandro", - "thumbnail": "https://i.ytimg.com/vi/CFPLIaMpGrY/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDwSw6VE-pkuUrDcXdUwkWgimAqFg", - "duration": 212, - "id": "c55abffc6391ba590b24d575396eb94c", - "isInLibrary": true, - "createdAt": 1766662249392 - }, - { - "youtubeId": "9V_HpS9p4QY", - "title": "Rvssian, Rauw Alejandro, Ayra Starr - Santa (Official Video)", - "channelName": "Rvssian", - "thumbnail": "https://i.ytimg.com/vi/9V_HpS9p4QY/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBCttCdX3KsBlq7xj6EetuGPTN2Dw", - "duration": 199, - "id": "e74abcc23686a1f28bc6a8f8e45b6469", - "isInLibrary": true, - "createdAt": 1766662249397 - }, - { - "youtubeId": "fEWhm_TVvUw", - "title": "KAROL G - Amargura (Visualizer)", - "channelName": "KAROL G", - "thumbnail": "https://i.ytimg.com/vi/fEWhm_TVvUw/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBP4MjC9T9Mbiq_x6oKmwtGLn3b4w", - "duration": 170, - "id": "64622e955545503301883aeb00a6672c", - "isInLibrary": true, - "createdAt": 1766662249401 - }, - { - "youtubeId": "fAFiHTyCioI", - "title": "Boza, ELENA ROSE - orióN (Official Video)", - "channelName": "Boza Oficial", - "thumbnail": "https://i.ytimg.com/vi/fAFiHTyCioI/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAFHMj3hXyCxZNVG9z6KLLDPTTE0w", - "duration": 246, - "id": "4fd50399aa2bfdae7b99ca02ff824a22", - "isInLibrary": true, - "createdAt": 1766662249406 - }, - { - "youtubeId": "25vNYV0qdgA", - "title": "Marshmello, Manuel Turizo - El Merengue (Official Video)", - "channelName": "Marshmello", - "thumbnail": "https://i.ytimg.com/vi/25vNYV0qdgA/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDytVOa78DlVibzvRHft4gDN7ObbA", - "duration": 219, - "id": "90d3af738a5166d2c8f1ca3294d90c74", - "isInLibrary": true, - "createdAt": 1766662249410 - }, - { - "youtubeId": "D6XR9GfBfwY", - "title": "Romeo Santos & Prince Royce - Dardos (Lyric Video)", - "channelName": "Romeo Santos and Prince Royce", - "thumbnail": "https://i.ytimg.com/vi/D6XR9GfBfwY/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCmDIAnPoeA6-Ked0rVrTiJxIeihg", - "duration": 244, - "id": "884d370a6e4482f5abf03bfa4ece9630", - "isInLibrary": true, - "createdAt": 1766662249414 - }, - { - "youtubeId": "2p6O7EmroEs", - "title": "Feid, Yandel - Brickell (Lyric Video)", - "channelName": "Feid", - "thumbnail": "https://i.ytimg.com/vi/2p6O7EmroEs/hqdefault.jpg?sqp=-oaymwE2CNACELwBSFXyq4qpAygIARUAAIhCGAFwAcABBvABAfgB_gmAAtAFigIMCAAQARh_IEooHzAP&rs=AOn4CLC9tGcVYUT4wQuaxKieQRSpLBdGUQ", - "duration": 181, - "id": "11eadd066989f84ae8e776a48b0f54a3", - "isInLibrary": true, - "createdAt": 1766662249418 - }, - { - "youtubeId": "XxIF0bJba8k", - "title": "KEY-KEY FT OZUNA - TENGO UN PLAN REMIX (VÍDEO OFICIAL)", - "channelName": "Keykey Oficial", - "thumbnail": "https://i.ytimg.com/vi/XxIF0bJba8k/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBP_ph4uXBn94SZ0HhMGqw0zPvRLA", - "duration": 221, - "id": "2d0b9905254a57226d75b514b41884bf", - "isInLibrary": true, - "createdAt": 1766662249422 - }, - { - "youtubeId": "0J1hIERZ1yA", - "title": "Sebastián Yatra, Manuel Turizo, Beéle - VAGABUNDO (Official Video)", - "channelName": "Sebastián Yatra", - "thumbnail": "https://i.ytimg.com/vi/0J1hIERZ1yA/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLA-ScOhmZ9476Hu3wa5wsSxH2A2-w", - "duration": 219, - "id": "539cbbf5e27f1171ecd253e373406294", - "isInLibrary": true, - "createdAt": 1766662249426 - }, - { - "youtubeId": "ubbE6gyBf8k", - "title": "Arcangel, Bad Bunny - La Jumpa (Video Oficial) | SR. SANTOS", - "channelName": "Arcangel", - "thumbnail": "https://i.ytimg.com/vi/ubbE6gyBf8k/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBVDfBtWhSMZJlH_iMIByR10dqlDQ", - "duration": 253, - "id": "fb281ed8edc7490f6b7900382d6e0b06", - "isInLibrary": true, - "createdAt": 1766662249430 - }, - { - "youtubeId": "bef8QLNHubw", - "title": "Bad Bunny - WHERE SHE GOES (Official Video)", - "channelName": "Bad Bunny", - "thumbnail": "https://i.ytimg.com/vi/bef8QLNHubw/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLB-rJ8_KcGm5SyH6XosQtfjgykSeQ", - "duration": 272, - "id": "e31cffad20778838000535d37be6bc96", - "isInLibrary": true, - "createdAt": 1766662249435 - }, - { - "youtubeId": "XEFjBktAH6Y", - "title": "El Alfa \"El Jefe\" x Nfasis - ESTE (Video Oficial)", - "channelName": "ElAlfaElJefeTV", - "thumbnail": "https://i.ytimg.com/vi/XEFjBktAH6Y/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAickb7Fnb-Wgi7D2-4VSZBy7YixQ", - "duration": 162, - "id": "eed9fae36231c0e2c3736b41a2a59218", - "isInLibrary": true, - "createdAt": 1766662249441 - }, - { - "youtubeId": "YR1t_MUN8I4", - "title": "Kali Uchis, Peso Pluma - Igual Que Un Ángel", - "channelName": "KALI UCHIS", - "thumbnail": "https://i.ytimg.com/vi/YR1t_MUN8I4/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAn5-x6otDKy4EiAiQdZ3utPdLkhw", - "duration": 263, - "id": "3c96fb1a8c74d06f1cd504506a776337", - "isInLibrary": true, - "createdAt": 1766662249446 - }, - { - "youtubeId": "dbmNWN_dEkI", - "title": "TOMMY & PAMELA (Video Oficial) - Peso Pluma, Kenia Os", - "channelName": "Peso Pluma", - "thumbnail": "https://i.ytimg.com/vi/dbmNWN_dEkI/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDUPuFnM_9G4vV8Xxa4RKTtCCL91Q", - "duration": 214, - "id": "8c5a9ad23a3efa4e7d3358cfb2839f23", - "isInLibrary": true, - "createdAt": 1766662249450 - }, - { - "youtubeId": "vK9AG0gVvnQ", - "title": "Maria Becerra, Chencho Corleone, Ovy On The Drums - PISCINA (Official Video)", - "channelName": "Maria Becerra", - "thumbnail": "https://i.ytimg.com/vi/vK9AG0gVvnQ/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLA7GnaeALfr0l9WRNhNL-uSxYrpww", - "duration": 232, - "id": "6a1b72dbee6e9574588ec7b7a507673e", - "isInLibrary": true, - "createdAt": 1766662249455 - }, - { - "youtubeId": "NCmp7AV8O9s", - "title": "KAROL G, Tiësto - CONTIGO (Official Video)", - "channelName": "KAROL G", - "thumbnail": "https://i.ytimg.com/vi/NCmp7AV8O9s/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAh0TWjfFBT7tuvdyE8OYVK8izmTQ", - "duration": 267, - "id": "6eb1ddd29977357467deccede6506fb0", - "isInLibrary": true, - "createdAt": 1766662249459 - }, - { - "youtubeId": "NaoNcERVF78", - "title": "YOUNG MIKO || BZRP Music Sessions #58/66", - "channelName": "Bizarrap", - "thumbnail": "https://i.ytimg.com/vi/NaoNcERVF78/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBHtdnPXTd3ihpUBWpNiuozCzplmA", - "duration": 189, - "id": "0389a7f1d4fcb854a59c99054f9e0bd3", - "isInLibrary": true, - "createdAt": 1766662249464 - }, - { - "youtubeId": "jg4mZak7wzQ", - "title": "Myke Towers & benny blanco - DEGENERE (Official Music Video)", - "channelName": "Myke Towers", - "thumbnail": "https://i.ytimg.com/vi/jg4mZak7wzQ/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAd0ZnWupXS-CgWpkhrw9sc2Ys5hQ", - "duration": 133, - "id": "5e3b8a84c5916c5d9b1c608cb4c01923", - "isInLibrary": true, - "createdAt": 1766662249469 - }, - { - "youtubeId": "vmbyVU9w47Y", - "title": "BAD BUNNY ft. Chuwi - WELTiTA (Video Oficial) | DeBÍ TiRAR MáS FOToS", - "channelName": "Bad Bunny", - "thumbnail": "https://i.ytimg.com/vi/vmbyVU9w47Y/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD60V44oGLtlu7Qc9DAL2nH31LFXw", - "duration": 202, - "id": "7d58835cefde450eae76536a34c823d1", - "isInLibrary": true, - "createdAt": 1766662249474 - }, - { - "youtubeId": "pyZtr3_dN3E", - "title": "Myke Towers - LA FALDA (Video Oficial)", - "channelName": "Myke Towers", - "thumbnail": "https://i.ytimg.com/vi/pyZtr3_dN3E/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDKCYNyeHuzN3JMoouCqL8usnyi6Q", - "duration": 228, - "id": "53b01c3c5130d7fdf0da70625d658336", - "isInLibrary": true, - "createdAt": 1766662249480 - }, - { - "youtubeId": "IEvX1i-TDc4", - "title": "Feid, Rema - Bubalu (Official Video)", - "channelName": "Feid", - "thumbnail": "https://i.ytimg.com/vi/IEvX1i-TDc4/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAW7UCxJrJlG90OsRV23SR727cbiQ", - "duration": 252, - "id": "441a2f444537a887a45b6391346f4180", - "isInLibrary": true, - "createdAt": 1766662249486 - }, - { - "youtubeId": "hGFfAQsFnDQ", - "title": "ILY - Kapo x Myke Towers (Video Oficial)", - "channelName": "Kapo", - "thumbnail": "https://i.ytimg.com/vi/hGFfAQsFnDQ/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDzNdo-vp5ofnpNWAzV2IZyjuu7SQ", - "duration": 224, - "id": "97176481b11bf6001d213998917a03e0", - "isInLibrary": false, - "createdAt": 1766662249490 - }, - { - "youtubeId": "z8WnSzBe_BM", - "title": "Luck Ra feat Maluma, Khea - HOLA PERDIDA (Remix)", - "channelName": "Luck Ra", - "thumbnail": "https://i.ytimg.com/vi/z8WnSzBe_BM/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD-KSdqtaxCLDihsWoUXykTpQ81Sw", - "duration": 201, - "id": "2386f871aeace45a15186aad32dfd95f", - "isInLibrary": false, - "createdAt": 1766662249495 - }, - { - "youtubeId": "3Z_bUjdKKQU", - "title": "KAROL G, Peso Pluma - QLONA (Letra/Lyrics)", - "channelName": "KAROL G", - "thumbnail": "https://i.ytimg.com/vi/3Z_bUjdKKQU/hqdefault.jpg?sqp=-oaymwE2CNACELwBSFXyq4qpAygIARUAAIhCGAFwAcABBvABAfgB_gmAAtAFigIMCAAQARhoIGgoaDAP&rs=AOn4CLA1egHJTt18MTHcl9KWzIyIZ3PhUQ", - "duration": 176, - "id": "836ceae0c94cc9065ed8f08a5da7325f", - "isInLibrary": false, - "createdAt": 1766662249501 - }, - { - "youtubeId": "IIKlkqbeYbI", - "title": "Fuerza Regida X Marshmello - HARLEY QUINN (Oficial Video)", - "channelName": "FUERZA REGIDA", - "thumbnail": "https://i.ytimg.com/vi/IIKlkqbeYbI/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAegd2WDOTRINNs4HGlxBONC9_F3A", - "duration": 164, - "id": "d190d839f131808a2a3f45c07cc72a7d", - "isInLibrary": false, - "createdAt": 1766662249507 - }, - { - "youtubeId": "UUzmHevEneY", - "title": "Rauw Alejandro - Déjame Entrar (Official Video)", - "channelName": "Rauw Alejandro", - "thumbnail": "https://i.ytimg.com/vi/UUzmHevEneY/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBe3ISYcePRL_AnwSB9vBHdxkqSsg", - "duration": 313, - "id": "6e5b73633c03382b8edc9dcf1f0812bb", - "isInLibrary": false, - "createdAt": 1766662249513 - }, - { - "youtubeId": "XJmMJXpTz7g", - "title": "LOKiTA", - "channelName": "FUERZA REGIDA", - "thumbnail": "https://i.ytimg.com/vi/XJmMJXpTz7g/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLALpxztV33_sUyvBNiJx2oe6srlfw", - "duration": 210, - "id": "b81556207fb1a0dc79d09c7abb936f5c", - "isInLibrary": false, - "createdAt": 1766662249519 - }, - { - "youtubeId": "KA9HgG9rnRg", - "title": "RIDE OR DIE PT. 2 FT. TOKISCHA & VILLANO ANTILLANO (VÍDEO CON LETRAS)", - "channelName": "Sevdaliza", - "thumbnail": "https://i.ytimg.com/vi/KA9HgG9rnRg/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLA3TYhNzKzBED8mLf1-vGX2U7WBCA", - "duration": 159, - "id": "4aaa6f80bfe4d5388119994168d4423c", - "isInLibrary": false, - "createdAt": 1766662249525 - }, - { - "youtubeId": "P-uP_SZNfDw", - "title": "Comernos", - "channelName": "Omar Courtz ", - "thumbnail": "https://i.ytimg.com/vi/P-uP_SZNfDw/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCTqYNK-3P0MfrYVqz5pzs6PAERBA", - "duration": 222, - "id": "0f84e235d11d49c424e21e04dd08b501", - "isInLibrary": false, - "createdAt": 1766662249530 - }, - { - "youtubeId": "flL8ZMqIWGA", - "title": "Becky G, KAROL G - MAMIII (Official Video)", - "channelName": "Becky G", - "thumbnail": "https://i.ytimg.com/vi/flL8ZMqIWGA/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCIMQxqIVBE4MWLG62UWT4DPhQWiA", - "duration": 275, - "id": "6d116fb3268466e0d2876dd6de9bb170", - "isInLibrary": false, - "createdAt": 1766662249537 - }, - { - "youtubeId": "SDBQYJx39o8", - "title": "Olvidarte", - "channelName": "GORDO", - "thumbnail": "https://i.ytimg.com/vi/SDBQYJx39o8/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCHJbf8nN1aRjvPjchxQdWeGMsNuA", - "duration": 158, - "id": "1582166ea555052e445f73d396cc3b55", - "isInLibrary": false, - "createdAt": 1766662249546 - }, - { - "youtubeId": "cZ5LCIEGThY", - "title": "Apaga la luz", - "channelName": "RØZ - Topic", - "thumbnail": "https://i.ytimg.com/vi/cZ5LCIEGThY/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLB0AjMhd-saRflTGZ2MUj8IBroV_Q", - "duration": 204, - "id": "890d6bdafc99fa0d90ae4b72ed9fd966", - "isInLibrary": false, - "createdAt": 1766662249552 - } - ], - "playlists": [ - { - "id": "2cb3d2790ecd557efbc634a28a562e4b", - "name": "mezcla", - "createdAt": 1766662242528 - }, - { - "id": "aa501128b9906d62b64901e7ea5ebd4b", - "name": "test", - "createdAt": 1766662448858 - } - ], - "playlistSongs": [ - { - "id": "8e349fe6cb3c962b207fa30de786a410", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "eadd4ff80f612fd0e38854d1d187ffe8", - "position": 1, - "addedAt": 1766662249262 - }, - { - "id": "9f2efbc3c8db7cf3f8a48686769d6249", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "e86e0adf843cf4d8b223aeeb6860a0fb", - "position": 2, - "addedAt": 1766662249267 - }, - { - "id": "87109ad362c587547fd1d91d6b8ab55d", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "1ccb34504cc701b32cd56c9a299425d8", - "position": 3, - "addedAt": 1766662249271 - }, - { - "id": "bebbf2e3f3f8f85a9e02c1f0248976d6", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "7ad4ad43e9eb8e43ca6dfb0d7e73f5df", - "position": 4, - "addedAt": 1766662249275 - }, - { - "id": "532480bd67a9a002ee5460e45e88e5f7", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "d102378ad3e7f64973a79b485a7e1941", - "position": 5, - "addedAt": 1766662249280 - }, - { - "id": "f2efbb81fff8b23210754cd54633a6d9", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "d6a590ac2126ccb91dde9d1204adc668", - "position": 6, - "addedAt": 1766662249286 - }, - { - "id": "1dc309c5b436b6eb0b8f3d68f9925fa3", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "421099682a39c2a93f46a24eae7c2d24", - "position": 7, - "addedAt": 1766662249291 - }, - { - "id": "81eac0be1e2ab08c35a197532d8b6df6", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "b3028951cd727989c2e6856d1887779a", - "position": 8, - "addedAt": 1766662249296 - }, - { - "id": "85c1af75f97743b9a1b62b7427c07c18", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "9348b58babc37a14d6d8ccbd4c217492", - "position": 9, - "addedAt": 1766662249299 - }, - { - "id": "cff75ee9a64bae8bb77299f7edb26601", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "b47fc29d665d12e717331632d81a243c", - "position": 10, - "addedAt": 1766662249303 - }, - { - "id": "756874c1bcac8cf7e2756ce588ed3195", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "06c3a9f1771fd2c5f1bedd5edede2ef0", - "position": 11, - "addedAt": 1766662249307 - }, - { - "id": "787731fd0f849d9cb8ff7f48b7cd3e7f", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "1c3abf88472606a9e53f75312a66fe98", - "position": 12, - "addedAt": 1766662249312 - }, - { - "id": "c3b0587c1e807367114a530f386d332e", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "25fc470fe2ef68d46d27289f1e193886", - "position": 13, - "addedAt": 1766662249316 - }, - { - "id": "d64ff5f5a5caef7f5c5cd33d9f403f78", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "149a26514df5060037004e1c15e79275", - "position": 14, - "addedAt": 1766662249320 - }, - { - "id": "9d5280a0297b94dc21a5f4d8478c395e", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "5aaf02aa74acd38a6ff9affa10d3a5af", - "position": 15, - "addedAt": 1766662249323 - }, - { - "id": "aaa4947e4fb60e884bf5ac3ecdefeedf", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "974e3a7dd6ebd938af9f6e17105efe5b", - "position": 16, - "addedAt": 1766662249327 - }, - { - "id": "5725e4da65f65b3670c5ec20f3e79bc2", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "a3256a6c1d8ea8c9232891547ad5c04c", - "position": 17, - "addedAt": 1766662249331 - }, - { - "id": "50169fccd0aa579eba93e1a1d848016f", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "ef1cbdd0b3d51a6163483c183f874ac6", - "position": 18, - "addedAt": 1766662249336 - }, - { - "id": "31372fa4babca9f0029c2dd8a93ccde9", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "b4c10fdd2206766597858a8727aab39a", - "position": 19, - "addedAt": 1766662249340 - }, - { - "id": "c74990091ab63430209ba1c1a04a47d2", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "3469e6e91767be972331f48f6eca8d17", - "position": 20, - "addedAt": 1766662249345 - }, - { - "id": "897870bc04851d05200c062c4ee3ca44", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "b69a20b6625246c875ed44c8ee30a047", - "position": 21, - "addedAt": 1766662249349 - }, - { - "id": "72499ed9bed5a1f5d853a267174e2685", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "c35eea92f1c3e8597b39f276ae83536b", - "position": 22, - "addedAt": 1766662249353 - }, - { - "id": "13a737628c2169044702fee2ce456de2", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "6309cb2d181607e6b2152b510dbe2f37", - "position": 23, - "addedAt": 1766662249358 - }, - { - "id": "63025c9a67648ceac3e2338bc7189eb8", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "4f7ca9ec897044676336607c7c8dd6a5", - "position": 24, - "addedAt": 1766662249365 - }, - { - "id": "cd1f7bb34657e383bbbd799f2b241935", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "743682c3a097c128d9f8ae251c44e894", - "position": 25, - "addedAt": 1766662249369 - }, - { - "id": "f134f5ad54457febc83c0bc3399829b3", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "55cadbb57d45d06dd01d25ec66df1316", - "position": 26, - "addedAt": 1766662249373 - }, - { - "id": "fc78fab03ae2d373b2399ce34e212d0c", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "27b74bcbc73b65071daf8aa2e6155d18", - "position": 27, - "addedAt": 1766662249378 - }, - { - "id": "d1fe6d5e1504b9fd4f80903493a1426f", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "ad9eaae0f1a5ce216657d26a89b7d483", - "position": 28, - "addedAt": 1766662249382 - }, - { - "id": "d7eea227daaf7384d1082dfd2ab0581b", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "a0e5714f44645120d4ee530328936d19", - "position": 29, - "addedAt": 1766662249386 - }, - { - "id": "a1209f63113c1a06ac7edfcc353a5e90", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "1031c7588047f106941914203aa35296", - "position": 30, - "addedAt": 1766662249390 - }, - { - "id": "32020704e2feb61aca36c1fe8a2f3b32", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "c55abffc6391ba590b24d575396eb94c", - "position": 31, - "addedAt": 1766662249394 - }, - { - "id": "032752e18845040e04d3a426ac0053b4", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "e74abcc23686a1f28bc6a8f8e45b6469", - "position": 32, - "addedAt": 1766662249398 - }, - { - "id": "262331b63e9e0b8a0816e993359e9102", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "64622e955545503301883aeb00a6672c", - "position": 33, - "addedAt": 1766662249403 - }, - { - "id": "8ca43618604897d8091260b5115a6900", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "4fd50399aa2bfdae7b99ca02ff824a22", - "position": 34, - "addedAt": 1766662249408 - }, - { - "id": "39c69028fcaeb3ca3624995fe4147894", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "90d3af738a5166d2c8f1ca3294d90c74", - "position": 35, - "addedAt": 1766662249412 - }, - { - "id": "c7f9bd374471b0f26a611acc4b080188", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "884d370a6e4482f5abf03bfa4ece9630", - "position": 36, - "addedAt": 1766662249416 - }, - { - "id": "af20bc65367732a17788ed7b9670b833", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "11eadd066989f84ae8e776a48b0f54a3", - "position": 37, - "addedAt": 1766662249420 - }, - { - "id": "9e7eb4e2930aebdaa1f0b4d14b5b8c1e", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "2d0b9905254a57226d75b514b41884bf", - "position": 38, - "addedAt": 1766662249424 - }, - { - "id": "c01ffc0265a435e53bf04c8aae8c2bfe", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "539cbbf5e27f1171ecd253e373406294", - "position": 39, - "addedAt": 1766662249428 - }, - { - "id": "1641a9ae9e1f4938d15ea9260a596461", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "fb281ed8edc7490f6b7900382d6e0b06", - "position": 40, - "addedAt": 1766662249432 - }, - { - "id": "24157f36b6dd4c9cb1cb615a573cb456", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "e31cffad20778838000535d37be6bc96", - "position": 41, - "addedAt": 1766662249438 - }, - { - "id": "3cd6ce19b6dbe2b6c839f372d888d509", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "eed9fae36231c0e2c3736b41a2a59218", - "position": 42, - "addedAt": 1766662249443 - }, - { - "id": "b7bacb555a0c7bc417d7552e4caa6723", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "3c96fb1a8c74d06f1cd504506a776337", - "position": 43, - "addedAt": 1766662249447 - }, - { - "id": "3f333aaca2c13ab569ef71ab57e7e14c", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "8c5a9ad23a3efa4e7d3358cfb2839f23", - "position": 44, - "addedAt": 1766662249452 - }, - { - "id": "f7d9977b1b79cab8cd3a369d8d373847", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "6a1b72dbee6e9574588ec7b7a507673e", - "position": 45, - "addedAt": 1766662249457 - }, - { - "id": "cf52864ce36dd8a8b19fe96ca28089bc", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "6eb1ddd29977357467deccede6506fb0", - "position": 46, - "addedAt": 1766662249461 - }, - { - "id": "73da1cf70a0936e3b94c494b24973b24", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "0389a7f1d4fcb854a59c99054f9e0bd3", - "position": 47, - "addedAt": 1766662249466 - }, - { - "id": "3b47b4d30d40c010eb2ef80cd3cc72b6", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "5e3b8a84c5916c5d9b1c608cb4c01923", - "position": 48, - "addedAt": 1766662249471 - }, - { - "id": "869cb92f6c186888d22107dc6dbc6685", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "7d58835cefde450eae76536a34c823d1", - "position": 49, - "addedAt": 1766662249476 - }, - { - "id": "265864a54724b95530163596571096f2", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "53b01c3c5130d7fdf0da70625d658336", - "position": 50, - "addedAt": 1766662249482 - }, - { - "id": "d19bc66c7d92664ed7be5554869e9153", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "441a2f444537a887a45b6391346f4180", - "position": 51, - "addedAt": 1766662249487 - }, - { - "id": "d3abfdde42f3ae89bfedf3bb2eefc2f1", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "97176481b11bf6001d213998917a03e0", - "position": 52, - "addedAt": 1766662249492 - }, - { - "id": "6c0c4af223dfa2784f3f79ea34152d1c", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "2386f871aeace45a15186aad32dfd95f", - "position": 53, - "addedAt": 1766662249498 - }, - { - "id": "fe51dfaf1eab9f29c55c72013b264234", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "836ceae0c94cc9065ed8f08a5da7325f", - "position": 54, - "addedAt": 1766662249503 - }, - { - "id": "b61486e72e7b2b8dbc5b3c5d8228a299", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "d190d839f131808a2a3f45c07cc72a7d", - "position": 55, - "addedAt": 1766662249509 - }, - { - "id": "37ce2135a21dc49e87bea81c9383f7b3", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "6e5b73633c03382b8edc9dcf1f0812bb", - "position": 56, - "addedAt": 1766662249515 - }, - { - "id": "1e6addedf96647d644d0aee8295f7476", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "b81556207fb1a0dc79d09c7abb936f5c", - "position": 57, - "addedAt": 1766662249521 - }, - { - "id": "05a8033c31cf6abcf58d292b54459165", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "4aaa6f80bfe4d5388119994168d4423c", - "position": 58, - "addedAt": 1766662249527 - }, - { - "id": "cfe6edf99832b8e47ae5ebb844687bb2", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "0f84e235d11d49c424e21e04dd08b501", - "position": 59, - "addedAt": 1766662249533 - }, - { - "id": "f428ad1ccc51099d0f3d64f4d32b5f6a", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "6d116fb3268466e0d2876dd6de9bb170", - "position": 60, - "addedAt": 1766662249542 - }, - { - "id": "6d0fc3a9160e768a8dd41f787e5a8b90", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "1582166ea555052e445f73d396cc3b55", - "position": 61, - "addedAt": 1766662249549 - }, - { - "id": "ba03c550fd40af73d2e5bfd249b40099", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "890d6bdafc99fa0d90ae4b72ed9fd966", - "position": 62, - "addedAt": 1766662249555 - }, - { - "id": "6996608fb7783337ac7a582ffb765195", - "playlistId": "2cb3d2790ecd557efbc634a28a562e4b", - "songId": "9c582292a947441c2462af6a754815c7", - "position": 63, - "addedAt": 1766662515545 - } - ] + "songs": [], + "playlists": [], + "playlistSongs": [] } \ No newline at end of file diff --git a/lib/ytdlp.ts b/lib/ytdlp.ts index ccbc66f..8adfdb2 100644 --- a/lib/ytdlp.ts +++ b/lib/ytdlp.ts @@ -203,3 +203,65 @@ export async function getYoutubePlaylistMetadata(url: string): Promise> { + try { + console.log('[searchYoutube] Searching for:', query) + const stdout = await executeYtDlp([ + '--dump-json', + '--no-warnings', + '--flat-playlist', + '--no-playlist', + `ytsearch${maxResults}:${query}` + ], 60000) // 60 second timeout + + // Split by newlines and parse each JSON object + const lines = stdout.trim().split('\n').filter(line => line.trim()) + const videos = lines.map(line => { + try { + return JSON.parse(line) + } catch (e) { + return null + } + }).filter((data: any) => data && data.id) + + console.log('[searchYoutube] Found', videos.length, 'results') + + return videos.map((data: any) => { + // Get best thumbnail URL + let thumbnail = '' + if (data.thumbnails && Array.isArray(data.thumbnails) && data.thumbnails.length > 0) { + const bestThumbnail = data.thumbnails + .filter((t: any) => t.url) + .sort((a: any, b: any) => { + const widthA = a.width || 0 + const widthB = b.width || 0 + return widthB - widthA + })[0] + thumbnail = bestThumbnail?.url || data.thumbnails[0]?.url || '' + } + + // Fallback to standard YouTube thumbnail URLs + if (!thumbnail && data.id) { + thumbnail = `https://i.ytimg.com/vi/${data.id}/hqdefault.jpg` + } + + return { + id: data.id, + title: data.title, + channel: data.channel || data.uploader || 'Unknown', + thumbnail, + duration: data.duration || 0, + } + }) + } catch (error) { + console.error('[searchYoutube] Error:', error) + throw new Error(`Failed to search YouTube: ${error}`) + } +} +