Skip to content

Commit

Permalink
feat: more features
Browse files Browse the repository at this point in the history
  • Loading branch information
sandipndev committed Sep 29, 2024
1 parent 8a0f00c commit f2f1c38
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 58 deletions.
45 changes: 23 additions & 22 deletions frontend/app/track/[track-id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client"

import { useTrackQuery } from "@/lib/graphql/generated"
import { Track } from "@/components/track"
import { SemitoneStatus, useTrackQuery } from "@/lib/graphql/generated"
import { useEffect, useState, useRef } from "react"

type Props = {
Expand All @@ -12,14 +13,14 @@ type Props = {
const TrackDetail: React.FC<Props> = ({ params }) => {
const trackId = params["track-id"]

const { data, loading: processLoading } = useTrackQuery({
const { data, loading: trackLoading } = useTrackQuery({
variables: {
trackId,
},
pollInterval: 1000,
})
const [preloadingAudio, setPreloadingAudio] = useState(true)
const loading = processLoading && preloadingAudio
const loading = trackLoading && preloadingAudio

const [, setRenderCount] = useState(0)

Expand All @@ -30,9 +31,13 @@ const TrackDetail: React.FC<Props> = ({ params }) => {
const [isSeeking, setIsSeeking] = useState(false)
const audioRefs = useRef<{ [key: number]: HTMLAudioElement }>({})

const allSemitoneConversionsComplete = data?.track.semitones.every(
({ status }) => status === SemitoneStatus.Completed,
)

// Preload all audio files once data is available
useEffect(() => {
if (data?.getProcess.status === "DONE") {
if (allSemitoneConversionsComplete) {
const semitoneShifts = []
for (let i = -10; i <= 10; i++) {
semitoneShifts.push(i)
Expand All @@ -41,7 +46,7 @@ const TrackDetail: React.FC<Props> = ({ params }) => {
semitoneShifts.forEach((semitone) => {
const semitoneLabel = semitone >= 0 ? `+${semitone}` : `${semitone}`
const fileName =
semitone === 0 ? `${processId}.mp3` : `${processId}_${semitoneLabel}_ST.mp3`
semitone === 0 ? `${trackId}.mp3` : `${trackId}_${semitoneLabel}_ST.mp3`
const fileUrl = `/media/${fileName}`

const audio = new Audio(fileUrl)
Expand All @@ -50,7 +55,7 @@ const TrackDetail: React.FC<Props> = ({ params }) => {

setPreloadingAudio(false)
}
}, [data, processId])
}, [allSemitoneConversionsComplete, data, trackId])

// Update currentTime when the audio is playing
useEffect(() => {
Expand Down Expand Up @@ -145,13 +150,13 @@ const TrackDetail: React.FC<Props> = ({ params }) => {
const semitoneLabel =
currentSemitone >= 0 ? `+${currentSemitone}` : `${currentSemitone}`
const fileName =
currentSemitone === 0 ? `${processId}.mp3` : `${processId}_${semitoneLabel}_ST.mp3`
currentSemitone === 0 ? `${trackId}.mp3` : `${trackId}_${semitoneLabel}_ST.mp3`
const fileUrl = `/media/${fileName}`

// Create a temporary anchor element to initiate download
const link = document.createElement("a")
link.href = fileUrl
link.download = `${data?.getProcess.name}_${semitoneLabel}.mp3`
link.download = `${data?.track.name}_${semitoneLabel}.mp3`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
Expand All @@ -166,25 +171,21 @@ const TrackDetail: React.FC<Props> = ({ params }) => {

if (loading) return <div>Loading...</div>

if (data?.getProcess.status !== "DONE")
return (
<>
<div className="">{data?.getProcess.name}</div>
<div>
Still Processing...
<br /> Current State: <span>{data?.getProcess.status}</span>
</div>
</>
)
if (!allSemitoneConversionsComplete && data?.track) return <Track track={data?.track} />

const playingAudio = audioRefs.current[currentSemitone]
const playTime = (playingAudio && playingAudio.currentTime) || seekTime
const duration = (playingAudio && playingAudio.duration) || 0

const minSemitoneAvailable =
data?.track.semitones.reduce((min, { shift }) => Math.min(min, shift), 0) || -6
const maxSemitoneAvailable =
data?.track.semitones.reduce((max, { shift }) => Math.max(max, shift), 0) || 4

return (
<div>
<div className="text-xl font-bold">{data?.getProcess.name}</div>
<div className="text-sm text-cyan-500">{data?.getProcess.youtubeUrl}</div>
<div className="text-xl font-bold">{data?.track.name}</div>
<div className="text-sm text-cyan-500">{data?.track.youtubeUrl}</div>
<hr className="mt-6 border-zinc-700" />
<div className="mt-4">
<div>
Expand All @@ -196,14 +197,14 @@ const TrackDetail: React.FC<Props> = ({ params }) => {
<div className="my-4">
<button
onClick={() => changeSemitone(-1)}
disabled={currentSemitone <= -10}
disabled={currentSemitone <= minSemitoneAvailable}
className="px-4 py-2 mr-2 bg-blue-500 hover:bg-blue-600 text-white rounded disabled:opacity-50"
>
-
</button>
<button
onClick={() => changeSemitone(1)}
disabled={currentSemitone >= 10}
disabled={currentSemitone >= maxSemitoneAvailable}
className="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded disabled:opacity-50"
>
+
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/all-tracks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const AllTracks = () => {
<td className="px-6 py-4">{track.name}</td>
<td className="px-6 py-4">
{formatStatus(
track.semitones.every(({ status }) => status === "DONE")
track.semitones.every(({ status }) => status === "COMPLETED")
? "Done"
: "Processing",
)}
Expand Down
32 changes: 15 additions & 17 deletions frontend/components/submit-track.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
/* eslint-disable @typescript-eslint/no-unused-expressions */

"use client"

import { useState } from "react"
import Link from "next/link"

import { useCreateTrackMutation, useTrackLazyQuery } from "@/lib/graphql/generated"
import { gql } from "@apollo/client"
import { formatStatus } from "@/lib/utils"

import { Track } from "./track"
import { Loading } from "./loading"
import Link from "next/link"

gql`
mutation CreateTrack($youtubeUrl: YoutubeUrl!) {
Expand Down Expand Up @@ -41,15 +43,14 @@ export const SubmitTrack = () => {
const [youtubeUrl, setYoutubeUrl] = useState("")

const handleSubmit = async () => {
const { data } = await createTrack({ variables: { youtubeUrl } })
const trackId = data?.createTrack
const url = new URL(youtubeUrl)
url.searchParams.delete("list")
const { data } = await createTrack({ variables: { youtubeUrl: url.toString() } })
const trackId = data?.createTrack.id
await track({ variables: { trackId } })
setYoutubeUrl("")
}

const allDone = trackData?.track.semitones.every(({ status }) => status === "DONE")
const isProcessing = loading || (Boolean(trackData?.track) && !allDone)
const done = Boolean(trackData?.track) && allDone

return (
<div className="mt-6">
<label htmlFor="link-input" className="block mb-2 text-white text-xl">
Expand All @@ -64,7 +65,7 @@ export const SubmitTrack = () => {
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
/>
<div className="mt-4">
{done ? (
{trackData?.track ? (
<Link
href={`/track/${trackData?.track.id}`}
className="bg-green-500 hover:bg-green-600 text-white text-sm font-semibold rounded-lg px-4 py-2.5"
Expand All @@ -74,11 +75,11 @@ export const SubmitTrack = () => {
) : (
<button
onClick={handleSubmit}
disabled={isProcessing}
disabled={loading}
type="button"
className="bg-blue-500 hover:bg-blue-600 text-white text-sm font-semibold rounded-lg px-4 py-2.5"
>
{isProcessing ? (
{loading ? (
<div className="flex space-x-2 items-center">
<Loading />
<div>Processing</div>
Expand All @@ -89,15 +90,12 @@ export const SubmitTrack = () => {
</button>
)}
</div>

{error && (
<div className="mt-4 text-red-500 text-sm font-semibold">{error.message}</div>
)}
{trackData && trackData.track && (
<div className="mt-4 text-green-500 text-xl font-semibold capitalize">
<span className="mr-4">Current Status: </span>
{formatStatus(isProcessing ? "Processing" : "Done")}
</div>
)}

{trackData && trackData.track && <Track track={trackData.track} />}
</div>
)
}
27 changes: 27 additions & 0 deletions frontend/components/track.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { SemitoneStatus, TrackQuery } from "@/lib/graphql/generated"
import { formatStatus } from "@/lib/utils"

type TrackProps = {
track: NonNullable<TrackQuery["track"]>
}

export const Track: React.FC<TrackProps> = ({ track }) => {
return (
<>
<div className="mt-4 text-green-500 font-semibold capitalize flex space-x-4 items-center">
<span>Name:</span>
<span>{track.name}</span>
</div>
<div className="text-green-500 font-semibold capitalize flex space-x-4 items-center">
<span>Current Status:</span>
<span>
{formatStatus(
track.semitones.every(({ status }) => status === SemitoneStatus.Completed)
? "Done"
: "Processing",
)}
</span>
</div>
</>
)
}
2 changes: 1 addition & 1 deletion frontend/lib/graphql/generated/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export type Semitone = {
};

export enum SemitoneStatus {
Done = 'DONE',
Completed = 'COMPLETED',
Pending = 'PENDING',
Processing = 'PROCESSING'
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

This file was deleted.

1 change: 1 addition & 0 deletions server/src/commands/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub async fn shift_pitch_of_track_by(
shift.to_string()
}
);

let status = Command::new("ffmpeg")
.arg("-y") // Overwrite output files without asking
.arg("-i")
Expand Down
2 changes: 1 addition & 1 deletion server/src/commands/download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub async fn download_track(
let download_file_at = format!("{}/{}.mp3", home_absolute_path, track_id);

let status = Command::new("yt-dlp")
.arg("-x")
.arg("-xk")
.arg("--audio-format")
.arg("mp3")
.arg("-o")
Expand Down
1 change: 1 addition & 0 deletions server/src/tracks/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ impl TracksRepo {
r#"
SELECT id, name, youtube_url, created_at
FROM tracks
ORDER BY created_at DESC
"#,
)
.fetch_all(&self.pool)
Expand Down

0 comments on commit f2f1c38

Please sign in to comment.