mirror of
https://github.com/wahyd4/you-music.git
synced 2026-08-08 20:59:47 +10:00
Update download from search
This commit is contained in:
@@ -18,7 +18,7 @@ router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def process_download_job(job_id: int, db: AsyncSession):
|
||||
async def process_download_job(job_id: int, db: AsyncSession, direct_url: str = None):
|
||||
"""Background task to process a download job"""
|
||||
try:
|
||||
# Get job
|
||||
@@ -27,6 +27,69 @@ async def process_download_job(job_id: int, db: AsyncSession):
|
||||
if not job:
|
||||
return
|
||||
|
||||
# Check if this is a direct URL download (skip search)
|
||||
if direct_url:
|
||||
logger.info(f"Direct URL download job {job_id}: url='{direct_url}', song_name='{job.song_name}'")
|
||||
|
||||
# Skip search, go directly to download
|
||||
job.status = "downloading"
|
||||
await db.commit()
|
||||
|
||||
# Format output filename
|
||||
output_filename = job.song_name # Already formatted as "Artist - Title"
|
||||
|
||||
# Download the music
|
||||
success, file_path, error = await music_downloader.download_music(
|
||||
direct_url,
|
||||
output_filename
|
||||
)
|
||||
|
||||
if success and file_path:
|
||||
# Extract metadata
|
||||
metadata = await music_downloader.get_music_metadata(file_path)
|
||||
|
||||
# Determine source type
|
||||
source_type = "youtube" if music_downloader.is_youtube_url(direct_url) else \
|
||||
"bilibili" if music_downloader.is_bilibili_url(direct_url) else "other"
|
||||
|
||||
# Get relative path and file format
|
||||
relative_path = os.path.relpath(file_path, settings.MUSIC_DIR)
|
||||
file_extension = os.path.splitext(file_path)[1][1:]
|
||||
|
||||
# Create database record
|
||||
db_music = Music(
|
||||
title=metadata.get("title", job.song_name or "Unknown"),
|
||||
artist=metadata.get("artist", "Unknown"),
|
||||
album=metadata.get("album", ""),
|
||||
duration=metadata.get("duration", 0),
|
||||
file_path=relative_path,
|
||||
file_size=os.path.getsize(file_path),
|
||||
file_format=file_extension,
|
||||
file_location=file_path,
|
||||
file_exists=True,
|
||||
source_url=direct_url,
|
||||
source_type=source_type,
|
||||
thumbnail=metadata.get("thumbnail")
|
||||
)
|
||||
|
||||
db.add(db_music)
|
||||
await db.commit()
|
||||
await db.refresh(db_music)
|
||||
|
||||
job.status = "completed"
|
||||
job.music_id = db_music.id
|
||||
await db.commit()
|
||||
|
||||
logger.info(f"Direct download job {job_id} completed: music_id={db_music.id}")
|
||||
else:
|
||||
job.status = "failed"
|
||||
job.error_message = error or "Download failed"
|
||||
await db.commit()
|
||||
logger.error(f"Direct download job {job_id} failed: {error}")
|
||||
|
||||
return
|
||||
|
||||
# Original auto-download flow (with search)
|
||||
# Update status to searching
|
||||
job.status = "searching"
|
||||
await db.commit()
|
||||
@@ -159,11 +222,17 @@ async def create_download_job(
|
||||
# Format song_name to include artist if provided
|
||||
# This ensures better search results
|
||||
song_name = request.song_name
|
||||
if request.artist:
|
||||
# Format as "Artist - Song" for better search accuracy
|
||||
if request.artist and not request.direct_url:
|
||||
# Format as "Artist - Song" for better search accuracy (only if not direct URL)
|
||||
song_name = f"{request.artist} - {request.song_name}"
|
||||
elif request.artist and request.direct_url:
|
||||
# For direct URL, still format the output filename
|
||||
song_name = f"{request.artist} - {request.song_name}"
|
||||
|
||||
logger.info(f"Creating auto-download job: song_name='{song_name}' (original: '{request.song_name}', artist: '{request.artist}')")
|
||||
if request.direct_url:
|
||||
logger.info(f"Creating direct download job: url='{request.direct_url}', filename='{song_name}'")
|
||||
else:
|
||||
logger.info(f"Creating auto-download job: song_name='{song_name}' (original: '{request.song_name}', artist: '{request.artist}')")
|
||||
|
||||
# Create job
|
||||
job = DownloadJob(
|
||||
@@ -176,7 +245,8 @@ async def create_download_job(
|
||||
await db.refresh(job)
|
||||
|
||||
# Start processing in background
|
||||
background_tasks.add_task(process_download_job, job.id, db)
|
||||
# Pass direct_url if provided to skip search
|
||||
background_tasks.add_task(process_download_job, job.id, db, request.direct_url)
|
||||
|
||||
return job
|
||||
|
||||
|
||||
@@ -148,6 +148,7 @@ class MusicDetailInfo(BaseModel):
|
||||
class DownloadJobCreate(BaseModel):
|
||||
song_name: str
|
||||
artist: Optional[str] = None # Artist name to improve search accuracy
|
||||
direct_url: Optional[str] = None # Direct URL to download (skips search)
|
||||
|
||||
|
||||
class DownloadJobResponse(BaseModel):
|
||||
|
||||
@@ -100,8 +100,8 @@ export const settingsApi = {
|
||||
|
||||
// Auto Download API
|
||||
export const autoDownloadApi = {
|
||||
createJob: (songName: string, artist?: string) =>
|
||||
api.post('/auto-download/job', { song_name: songName, artist }),
|
||||
createJob: (songName: string, artist?: string, directUrl?: string) =>
|
||||
api.post('/auto-download/job', { song_name: songName, artist, direct_url: directUrl }),
|
||||
getJobs: (status?: string) =>
|
||||
api.get('/auto-download/jobs', { params: { status } }),
|
||||
getJob: (jobId: number) =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from 'react'
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { searchApi, downloadApi, musicApi } from '@/api/client'
|
||||
import { useQuery, useMutation } from '@tanstack/react-query'
|
||||
import { searchApi, autoDownloadApi, musicApi } from '@/api/client'
|
||||
import { Music } from '@/types'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
@@ -14,7 +14,6 @@ interface SearchPageProps {
|
||||
export default function SearchPage({ onPlayMusic }: SearchPageProps) {
|
||||
const [query, setQuery] = useState('')
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const { data: localResults, isLoading: isLoadingLocal } = useQuery({
|
||||
queryKey: ['music', 'search', searchQuery],
|
||||
@@ -37,14 +36,13 @@ export default function SearchPage({ onPlayMusic }: SearchPageProps) {
|
||||
})
|
||||
|
||||
const downloadMutation = useMutation({
|
||||
mutationFn: ({ url, title, thumbnail, artist }: { url: string; title: string; thumbnail?: string; artist?: string }) =>
|
||||
downloadApi.downloadMusic({ url, title, thumbnail, artist }),
|
||||
mutationFn: ({ title, artist, url }: { title: string; artist?: string; url?: string }) =>
|
||||
autoDownloadApi.createJob(title, artist, url),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['download-status'] })
|
||||
toast.success('Download started! Check Download Center for progress.')
|
||||
toast.success('Download job created! Check Download Center for progress.')
|
||||
},
|
||||
onError: () => {
|
||||
toast.error('Download failed')
|
||||
toast.error('Failed to create download job')
|
||||
},
|
||||
})
|
||||
|
||||
@@ -53,14 +51,14 @@ export default function SearchPage({ onPlayMusic }: SearchPageProps) {
|
||||
setSearchQuery(query)
|
||||
}
|
||||
|
||||
const handleDownload = (url: string, title: string, thumbnail?: string, artist?: string) => {
|
||||
downloadMutation.mutate({ url, title, thumbnail, artist })
|
||||
const handleDownload = (title: string, artist?: string, url?: string) => {
|
||||
downloadMutation.mutate({ title, artist, url })
|
||||
}
|
||||
|
||||
const handlePlayAndDownload = async (url: string, title: string, thumbnail?: string, artist?: string) => {
|
||||
// Start download
|
||||
downloadMutation.mutate({ url, title, thumbnail, artist })
|
||||
|
||||
// Start download job with direct URL
|
||||
downloadMutation.mutate({ title, artist, url })
|
||||
|
||||
// Create temporary music object for streaming playback
|
||||
const streamUrl = `/api/stream?url=${encodeURIComponent(url)}`
|
||||
const tempMusic: Music = {
|
||||
@@ -83,7 +81,7 @@ export default function SearchPage({ onPlayMusic }: SearchPageProps) {
|
||||
file_exists: true,
|
||||
last_scanned_at: null,
|
||||
}
|
||||
|
||||
|
||||
onPlayMusic(tempMusic)
|
||||
toast.success('Playing while downloading...')
|
||||
}
|
||||
@@ -187,7 +185,7 @@ export default function SearchPage({ onPlayMusic }: SearchPageProps) {
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={() => handleDownload(result.url, result.title, result.thumbnail, result.artist)}
|
||||
onClick={() => handleDownload(result.title, result.artist, result.url)}
|
||||
disabled={downloadMutation.isPending}
|
||||
>
|
||||
<Download className="h-5 w-5" />
|
||||
@@ -231,7 +229,7 @@ export default function SearchPage({ onPlayMusic }: SearchPageProps) {
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={() => handleDownload(result.url, result.title, result.thumbnail, result.artist)}
|
||||
onClick={() => handleDownload(result.title, result.artist, result.url)}
|
||||
disabled={downloadMutation.isPending}
|
||||
>
|
||||
<Download className="h-5 w-5" />
|
||||
|
||||
Reference in New Issue
Block a user