diff --git a/README.md b/README.md index cfdddd8d..2dc76798 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,11 @@ It also uses https://github.com/fief-dev for authentication, and Vercel for depl - [Back-End](#back-end) - [Installation](#installation-1) - [Start the API/Backend](#start-the-apibackend) + - [Redis (Mac)](#redis-mac) + - [Redis (Windows)](#redis-windows) + - [Update the database schema (run on first install, and after each pull containing a migration)](#update-the-database-schema-run-on-first-install-and-after-each-pull-containing-a-migration) + - [Main Server](#main-server) + - [Crontab (optional)](#crontab-optional) - [Using docker](#using-docker) - [Using local GPT4All](#using-local-gpt4all) - [Using local files](#using-local-files) @@ -152,7 +157,7 @@ redis-server ## Update the database schema (run on first install, and after each pull containing a migration) ```bash -poetry run python alembic head +poetry run alembic heads ``` ## Main Server diff --git a/www/app/[domain]/browse/page.tsx b/www/app/[domain]/browse/page.tsx index b2359b9e..46a81295 100644 --- a/www/app/[domain]/browse/page.tsx +++ b/www/app/[domain]/browse/page.tsx @@ -72,18 +72,18 @@ export default function TranscriptBrowser() { <> )} - {item.sourceLanguage ? ( + {item.source_language ? (
- {item.sourceLanguage} + {item.source_language}
) : ( <> )}
- {new Date(item.createdAt).toLocaleDateString("en-US")} + {new Date(item.created_at).toLocaleDateString("en-US")}
-
{item.shortSummary}
+
{item.short_summary}
))} diff --git a/www/app/[domain]/layout.tsx b/www/app/[domain]/layout.tsx index 73cc4841..ebdfbe5d 100644 --- a/www/app/[domain]/layout.tsx +++ b/www/app/[domain]/layout.tsx @@ -1,6 +1,6 @@ import "../styles/globals.scss"; import { Poppins } from "next/font/google"; -import { Metadata } from "next"; +import { Metadata, Viewport } from "next"; import FiefWrapper from "../(auth)/fiefWrapper"; import UserInfo from "../(auth)/userInfo"; import { ErrorProvider } from "../(errors)/errorContext"; @@ -17,7 +17,15 @@ import { SESSION_COOKIE_NAME } from "../lib/fief"; const poppins = Poppins({ subsets: ["latin"], weight: ["200", "400", "600"] }); +export const viewport: Viewport = { + themeColor: "black", + width: "device-width", + initialScale: 1, + maximumScale: 1, +}; + export const metadata: Metadata = { + metadataBase: new URL(process.env.DEV_URL || "https://reflector.media"), title: { template: "%s – Reflector", default: "Reflector - AI-Powered Meeting Transcriptions by Monadical", @@ -54,12 +62,6 @@ export const metadata: Metadata = { shortcut: "/r-icon.png", apple: "/r-icon.png", }, - viewport: { - width: "device-width", - initialScale: 1, - maximumScale: 1, - }, - robots: { index: false, follow: false, noarchive: true, noimageindex: true }, }; diff --git a/www/app/[domain]/transcripts/[transcriptId]/page.tsx b/www/app/[domain]/transcripts/[transcriptId]/page.tsx index 18debf69..1e1eaf8f 100644 --- a/www/app/[domain]/transcripts/[transcriptId]/page.tsx +++ b/www/app/[domain]/transcripts/[transcriptId]/page.tsx @@ -16,9 +16,8 @@ import ShareModal from "./shareModal"; import Player from "../player"; import WaveformLoading from "../waveformLoading"; import { useRouter } from "next/navigation"; -import { faSpinner } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { featureEnabled } from "../../domainContext"; +import { toShareMode } from "../../../lib/shareMode"; type TranscriptDetails = { params: { @@ -91,7 +90,7 @@ export default function TranscriptDetails(details: TranscriptDetails) { @@ -110,10 +109,10 @@ export default function TranscriptDetails(details: TranscriptDetails) {
- {transcript.response.longSummary ? ( + {transcript.response.long_summary ? ( setShowModal(true)} /> @@ -142,8 +141,8 @@ export default function TranscriptDetails(details: TranscriptDetails) {
diff --git a/www/app/[domain]/transcripts/[transcriptId]/record/page.tsx b/www/app/[domain]/transcripts/[transcriptId]/record/page.tsx index 8615a4b1..d4bb6b3e 100644 --- a/www/app/[domain]/transcripts/[transcriptId]/record/page.tsx +++ b/www/app/[domain]/transcripts/[transcriptId]/record/page.tsx @@ -75,10 +75,8 @@ const TranscriptRecord = (details: TranscriptDetails) => { }, [webSockets.status.value, transcript.response?.status]); useEffect(() => { - if (webSockets.duration) { - mp3.getNow(); - } - }, [webSockets.duration]); + if (transcript.response?.status === "ended") mp3.getNow(); + }, [transcript.response]); useEffect(() => { lockWakeState(); @@ -112,6 +110,7 @@ const TranscriptRecord = (details: TranscriptDetails) => { }} getAudioStream={getAudioStream} audioDevices={audioDevices} + transcriptId={details.params.transcriptId} /> )} diff --git a/www/app/[domain]/transcripts/createTranscript.ts b/www/app/[domain]/transcripts/createTranscript.ts index 9ad1abe0..8435e6c2 100644 --- a/www/app/[domain]/transcripts/createTranscript.ts +++ b/www/app/[domain]/transcripts/createTranscript.ts @@ -1,48 +1,32 @@ -import { useEffect, useState } from "react"; -import { - DefaultApi, - V1TranscriptsCreateRequest, -} from "../../api/apis/DefaultApi"; -import { GetTranscript } from "../../api"; +import { useState } from "react"; import { useError } from "../../(errors)/errorContext"; -import getApi from "../../lib/getApi"; +import { GetTranscript, CreateTranscript } from "../../api"; +import useApi from "../../lib/useApi"; -type CreateTranscript = { - response: GetTranscript | null; +type UseTranscript = { + transcript: GetTranscript | null; loading: boolean; error: Error | null; - create: (params: V1TranscriptsCreateRequest["createTranscript"]) => void; + create: (transcriptCreationDetails: CreateTranscript) => void; }; -const useCreateTranscript = (): CreateTranscript => { - const [response, setResponse] = useState(null); +const useCreateTranscript = (): UseTranscript => { + const [transcript, setTranscript] = useState(null); const [loading, setLoading] = useState(false); const [error, setErrorState] = useState(null); const { setError } = useError(); - const api = getApi(); + const api = useApi(); - const create = (params: V1TranscriptsCreateRequest["createTranscript"]) => { + const create = (transcriptCreationDetails: CreateTranscript) => { if (loading || !api) return; setLoading(true); - const requestParameters: V1TranscriptsCreateRequest = { - createTranscript: { - name: params.name || "Unnamed Transcript", // Default - targetLanguage: params.targetLanguage || "en", // Default - }, - }; - - console.debug( - "POST - /v1/transcripts/ - Requesting new transcription creation", - requestParameters, - ); api - .v1TranscriptsCreate(requestParameters) - .then((result) => { - setResponse(result); + .v1TranscriptsCreate(transcriptCreationDetails) + .then((transcript) => { + setTranscript(transcript); setLoading(false); - console.debug("New transcript created:", result); }) .catch((err) => { setError( @@ -54,7 +38,7 @@ const useCreateTranscript = (): CreateTranscript => { }); }; - return { response, loading, error, create }; + return { transcript, loading, error, create }; }; export default useCreateTranscript; diff --git a/www/app/[domain]/transcripts/fileUploadButton.tsx b/www/app/[domain]/transcripts/fileUploadButton.tsx new file mode 100644 index 00000000..7cfaf19e --- /dev/null +++ b/www/app/[domain]/transcripts/fileUploadButton.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import useApi from "../../lib/useApi"; +import { Body_transcript_record_upload_v1_transcripts__transcript_id__record_upload_post } from "../../api"; + +type FileUploadButton = { + transcriptId: string; +}; + +export default function FileUploadButton(props: FileUploadButton) { + const fileInputRef = React.useRef(null); + const api = useApi(); + + const triggerFileUpload = () => { + fileInputRef.current?.click(); + }; + + const handleFileUpload = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + + if (file) { + console.log("Calling api.v1TranscriptRecordUpload()..."); + + // Create an object of the expected type + const uploadData = { + file: file, + // Add other properties if required by the type definition + }; + + api?.v1TranscriptRecordUpload(props.transcriptId, uploadData); + } + }; + + return ( + <> + + + + + ); +} diff --git a/www/app/[domain]/transcripts/finalSummary.tsx b/www/app/[domain]/transcripts/finalSummary.tsx index f7d7ac23..6ad90b14 100644 --- a/www/app/[domain]/transcripts/finalSummary.tsx +++ b/www/app/[domain]/transcripts/finalSummary.tsx @@ -2,8 +2,9 @@ import { useRef, useState } from "react"; import React from "react"; import Markdown from "react-markdown"; import "../../styles/markdown.css"; -import getApi from "../../lib/getApi"; import { featureEnabled } from "../domainContext"; +import { UpdateTranscript } from "../../api"; +import useApi from "../../lib/useApi"; type FinalSummaryProps = { summary: string; @@ -19,17 +20,17 @@ export default function FinalSummary(props: FinalSummaryProps) { const [isEditMode, setIsEditMode] = useState(false); const [preEditSummary, setPreEditSummary] = useState(props.summary); const [editedSummary, setEditedSummary] = useState(props.summary); - const api = getApi(); const updateSummary = async (newSummary: string, transcriptId: string) => { - if (!api) return; try { - const updatedTranscript = await api.v1TranscriptUpdate({ + const api = useApi(); + const requestBody: UpdateTranscript = { + long_summary: newSummary, + }; + const updatedTranscript = await api?.v1TranscriptUpdate( transcriptId, - updateTranscript: { - longSummary: newSummary, - }, - }); + requestBody, + ); console.log("Updated long summary:", updatedTranscript); } catch (err) { console.error("Failed to update long summary:", err); diff --git a/www/app/[domain]/transcripts/new/page.tsx b/www/app/[domain]/transcripts/new/page.tsx index 045c2db5..0086573c 100644 --- a/www/app/[domain]/transcripts/new/page.tsx +++ b/www/app/[domain]/transcripts/new/page.tsx @@ -18,7 +18,7 @@ const TranscriptCreate = () => { const isAuthenticated = useFiefIsAuthenticated(); const requireLogin = featureEnabled("requireLogin"); - const [name, setName] = useState(); + const [name, setName] = useState(""); const nameChange = (event: React.ChangeEvent) => { setName(event.target.value); }; @@ -35,13 +35,13 @@ const TranscriptCreate = () => { const send = () => { if (loadingSend || createTranscript.loading || permissionDenied) return; setLoadingSend(true); - createTranscript.create({ name, targetLanguage }); + createTranscript.create({ name, target_language: targetLanguage }); }; useEffect(() => { - createTranscript.response && - router.push(`/transcripts/${createTranscript.response.id}/record`); - }, [createTranscript.response]); + createTranscript.transcript && + router.push(`/transcripts/${createTranscript.transcript.id}/record`); + }, [createTranscript.transcript]); useEffect(() => { if (createTranscript.error) setLoadingSend(false); @@ -59,6 +59,7 @@ const TranscriptCreate = () => {

Welcome to reflector.media

+

Reflector is a transcription and summarization pipeline that transforms audio into knowledge. diff --git a/www/app/[domain]/transcripts/player.tsx b/www/app/[domain]/transcripts/player.tsx index 02151a68..632dfd8a 100644 --- a/www/app/[domain]/transcripts/player.tsx +++ b/www/app/[domain]/transcripts/player.tsx @@ -14,7 +14,7 @@ type PlayerProps = { Topic | null, React.Dispatch>, ]; - waveform: AudioWaveform["data"]; + waveform: AudioWaveform; media: HTMLMediaElement; mediaDuration: number; }; diff --git a/www/app/[domain]/transcripts/recorder.tsx b/www/app/[domain]/transcripts/recorder.tsx index e7c016a7..562f6a76 100644 --- a/www/app/[domain]/transcripts/recorder.tsx +++ b/www/app/[domain]/transcripts/recorder.tsx @@ -12,6 +12,7 @@ import AudioInputsDropdown from "./audioInputsDropdown"; import { Option } from "react-dropdown"; import { waveSurferStyles } from "../../styles/recorder"; import { useError } from "../../(errors)/errorContext"; +import FileUploadButton from "./fileUploadButton"; type RecorderProps = { setStream: React.Dispatch>; @@ -19,6 +20,7 @@ type RecorderProps = { onRecord?: () => void; getAudioStream: (deviceId) => Promise; audioDevices: Option[]; + transcriptId: string; }; export default function Recorder(props: RecorderProps) { @@ -307,6 +309,11 @@ export default function Recorder(props: RecorderProps) { > {isRecording ? "Stop" : "Record"} + + + {!isRecording && (