-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Midi Download from Form Submission
- Loading branch information
1 parent
3e11004
commit ca98d4a
Showing
5 changed files
with
342 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
# misc | ||
.DS_Store | ||
*.pem | ||
backlog.txt | ||
|
||
# debug | ||
npm-debug.log* | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
"use client"; | ||
|
||
import React, { useState } from "react"; | ||
import { zodResolver } from "@hookform/resolvers/zod"; | ||
import { useForm } from "react-hook-form"; | ||
import { z } from "zod"; | ||
import { Input } from "@/components/ui/input"; | ||
import { Button } from "@/components/ui/button"; | ||
import { | ||
Form, | ||
FormControl, | ||
FormDescription, | ||
FormField, | ||
FormItem, | ||
FormLabel, | ||
FormMessage, | ||
} from "@/components/ui/form"; | ||
|
||
const validMimeTypes = ["audio/mpeg", "audio/wav", "audio/ogg, audio/flac"]; | ||
const apiBaseUrl = "http://localhost:8000"; | ||
|
||
const formSchema = z.object({ | ||
audio_file: z | ||
.any() | ||
.refine((fileList) => fileList && fileList.length > 0, { | ||
message: "Please upload a valid file.", | ||
}) | ||
.refine( | ||
(fileList) => { | ||
if (!fileList || fileList.length === 0) return false; | ||
const file = fileList[0]; | ||
return validMimeTypes.includes(file.type); | ||
}, | ||
{ | ||
message: | ||
"Invalid file type. Only MP3, WAV, OGG, or FLAC files are allowed.", | ||
}, | ||
), | ||
}); | ||
|
||
export default function AudioToMidiForm() { | ||
const [isSubmitting, setIsSubmitting] = useState(false); | ||
const form = useForm<z.infer<typeof formSchema>>({ | ||
resolver: zodResolver(formSchema), | ||
defaultValues: { | ||
audio_file: null, | ||
}, | ||
}); | ||
|
||
async function onSubmit(values: z.infer<typeof formSchema>) { | ||
const fileList = values.audio_file; | ||
const file = fileList[0]; | ||
if (!file) return; | ||
|
||
const formData = new FormData(); | ||
formData.append("audio_file", file); | ||
|
||
try { | ||
setIsSubmitting(true); | ||
const response = await fetch(`${apiBaseUrl}/audio-to-midi`, { | ||
method: "POST", | ||
mode: "cors", | ||
body: formData, | ||
}); | ||
|
||
if (!response.ok) { | ||
throw new Error("Failed to convert audio"); | ||
} | ||
|
||
const midiBlob = await response.blob(); | ||
const midiUrl = URL.createObjectURL(midiBlob); | ||
|
||
// Trigger the download of the MIDI file | ||
const link = document.createElement("a"); | ||
link.href = midiUrl; | ||
link.download = "converted.mid"; // Specify the filename | ||
document.body.appendChild(link); | ||
link.click(); | ||
document.body.removeChild(link); | ||
|
||
// Optionally revoke the object URL after download | ||
URL.revokeObjectURL(midiUrl); | ||
} catch (error) { | ||
console.error(error); | ||
alert("Error converting audio to MIDI"); | ||
} finally { | ||
setIsSubmitting(false); | ||
} | ||
} | ||
|
||
return ( | ||
<div className="flex w-full justify-around"> | ||
<Form {...form}> | ||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8"> | ||
<FormField | ||
control={form.control} | ||
name="audio_file" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>Upload Audio File</FormLabel> | ||
<FormControl> | ||
<Input | ||
type="file" | ||
accept=".mp3, .wav, .ogg, .flac" | ||
onChange={(e) => { | ||
field.onChange(e.target.files); | ||
}} | ||
/> | ||
</FormControl> | ||
<FormDescription> | ||
Upload an audio file to convert to MIDI. | ||
</FormDescription> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<Button type="submit" disabled={isSubmitting}> | ||
{isSubmitting ? "Converting..." : "Submit"} | ||
</Button> | ||
</form> | ||
</Form> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
"use client" | ||
|
||
import * as React from "react" | ||
import * as LabelPrimitive from "@radix-ui/react-label" | ||
import { Slot } from "@radix-ui/react-slot" | ||
import { | ||
Controller, | ||
ControllerProps, | ||
FieldPath, | ||
FieldValues, | ||
FormProvider, | ||
useFormContext, | ||
} from "react-hook-form" | ||
|
||
import { cn } from "@/lib/utils" | ||
import { Label } from "@/components/ui/label" | ||
|
||
const Form = FormProvider | ||
|
||
type FormFieldContextValue< | ||
TFieldValues extends FieldValues = FieldValues, | ||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> | ||
> = { | ||
name: TName | ||
} | ||
|
||
const FormFieldContext = React.createContext<FormFieldContextValue>( | ||
{} as FormFieldContextValue | ||
) | ||
|
||
const FormField = < | ||
TFieldValues extends FieldValues = FieldValues, | ||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues> | ||
>({ | ||
...props | ||
}: ControllerProps<TFieldValues, TName>) => { | ||
return ( | ||
<FormFieldContext.Provider value={{ name: props.name }}> | ||
<Controller {...props} /> | ||
</FormFieldContext.Provider> | ||
) | ||
} | ||
|
||
const useFormField = () => { | ||
const fieldContext = React.useContext(FormFieldContext) | ||
const itemContext = React.useContext(FormItemContext) | ||
const { getFieldState, formState } = useFormContext() | ||
|
||
const fieldState = getFieldState(fieldContext.name, formState) | ||
|
||
if (!fieldContext) { | ||
throw new Error("useFormField should be used within <FormField>") | ||
} | ||
|
||
const { id } = itemContext | ||
|
||
return { | ||
id, | ||
name: fieldContext.name, | ||
formItemId: `${id}-form-item`, | ||
formDescriptionId: `${id}-form-item-description`, | ||
formMessageId: `${id}-form-item-message`, | ||
...fieldState, | ||
} | ||
} | ||
|
||
type FormItemContextValue = { | ||
id: string | ||
} | ||
|
||
const FormItemContext = React.createContext<FormItemContextValue>( | ||
{} as FormItemContextValue | ||
) | ||
|
||
const FormItem = React.forwardRef< | ||
HTMLDivElement, | ||
React.HTMLAttributes<HTMLDivElement> | ||
>(({ className, ...props }, ref) => { | ||
const id = React.useId() | ||
|
||
return ( | ||
<FormItemContext.Provider value={{ id }}> | ||
<div ref={ref} className={cn("space-y-2", className)} {...props} /> | ||
</FormItemContext.Provider> | ||
) | ||
}) | ||
FormItem.displayName = "FormItem" | ||
|
||
const FormLabel = React.forwardRef< | ||
React.ElementRef<typeof LabelPrimitive.Root>, | ||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> | ||
>(({ className, ...props }, ref) => { | ||
const { error, formItemId } = useFormField() | ||
|
||
return ( | ||
<Label | ||
ref={ref} | ||
className={cn(error && "text-destructive", className)} | ||
htmlFor={formItemId} | ||
{...props} | ||
/> | ||
) | ||
}) | ||
FormLabel.displayName = "FormLabel" | ||
|
||
const FormControl = React.forwardRef< | ||
React.ElementRef<typeof Slot>, | ||
React.ComponentPropsWithoutRef<typeof Slot> | ||
>(({ ...props }, ref) => { | ||
const { error, formItemId, formDescriptionId, formMessageId } = useFormField() | ||
|
||
return ( | ||
<Slot | ||
ref={ref} | ||
id={formItemId} | ||
aria-describedby={ | ||
!error | ||
? `${formDescriptionId}` | ||
: `${formDescriptionId} ${formMessageId}` | ||
} | ||
aria-invalid={!!error} | ||
{...props} | ||
/> | ||
) | ||
}) | ||
FormControl.displayName = "FormControl" | ||
|
||
const FormDescription = React.forwardRef< | ||
HTMLParagraphElement, | ||
React.HTMLAttributes<HTMLParagraphElement> | ||
>(({ className, ...props }, ref) => { | ||
const { formDescriptionId } = useFormField() | ||
|
||
return ( | ||
<p | ||
ref={ref} | ||
id={formDescriptionId} | ||
className={cn("text-sm text-muted-foreground", className)} | ||
{...props} | ||
/> | ||
) | ||
}) | ||
FormDescription.displayName = "FormDescription" | ||
|
||
const FormMessage = React.forwardRef< | ||
HTMLParagraphElement, | ||
React.HTMLAttributes<HTMLParagraphElement> | ||
>(({ className, children, ...props }, ref) => { | ||
const { error, formMessageId } = useFormField() | ||
const body = error ? String(error?.message) : children | ||
|
||
if (!body) { | ||
return null | ||
} | ||
|
||
return ( | ||
<p | ||
ref={ref} | ||
id={formMessageId} | ||
className={cn("text-sm font-medium text-destructive", className)} | ||
{...props} | ||
> | ||
{body} | ||
</p> | ||
) | ||
}) | ||
FormMessage.displayName = "FormMessage" | ||
|
||
export { | ||
useFormField, | ||
Form, | ||
FormItem, | ||
FormLabel, | ||
FormControl, | ||
FormDescription, | ||
FormMessage, | ||
FormField, | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.