Bug fixes and readme update
This commit is contained in:
parent
5edb990324
commit
fc780746aa
4 changed files with 43 additions and 38 deletions
47
README.md
47
README.md
|
|
@ -6,45 +6,47 @@
|
|||
[](../../releases)
|
||||
|
||||
[](../../discussions)
|
||||
[](https://bsky.app/profile/richardr.dev)
|
||||
|
||||
|
||||
# OpenReader WebUI 📄🔊
|
||||
|
||||
OpenReader WebUI is a document reader with Text-to-Speech capabilities, offering a TTS read along experience with narration for both PDF and EPUB documents. It can use any OpenAI compatible TTS endpoint, including [Kokoro-FastAPI](https://github.com/remsky/Kokoro-FastAPI).
|
||||
OpenReader WebUI is a document reader with Text-to-Speech capabilities, offering a TTS read along experience with narration for both PDF and EPUB documents. It can use any OpenAI compatible TTS endpoint, including [Kokoro-FastAPI](https://github.com/remsky/Kokoro-FastAPI) and [Orpheus-FastAPI](https://github.com/Lex-au/Orpheus-FastAPI)
|
||||
|
||||
> Highly available demo currently available at [https://openreader.richardr.dev/](https://openreader.richardr.dev/)
|
||||
|
||||
- 🎯 **TTS API Integration**:
|
||||
- Compatible with OpenAI text to speech API and GPT-4o Mini TTS, Kokoro-FastAPI TTS, or any other compatible service
|
||||
- Support for multiple TTS models (tts-1, tts-1-hd, gpt-4o-mini-tts, kokoro)
|
||||
- Custom model support for experimental or self-hosted models
|
||||
- Model-specific instructions support (for gpt-4o-mini-tts)
|
||||
- Compatible with OpenAI text to speech API and GPT-4o Mini TTS, Kokoro-FastAPI TTS, Orpheus FastAPI or any other compatible service
|
||||
- Support for TTS models (tts-1, tts-1-hd, gpt-4o-mini-tts, kokoro, and custom)
|
||||
- 💾 **Local-First Architecture**: Uses IndexedDB browser storage for documents
|
||||
- 🛜 **Optional Server-side documents**: Manually upload documents to the next backend for all users to download
|
||||
- 📖 **Read Along Experience**: Follow along with highlighted text as the TTS narrates
|
||||
- 📄 **Document formats**: EPUB, PDF, DOCX (with libreoffice installed)
|
||||
- 🎧 **Audiobook Creation**: Create and export audiobooks from PDF and ePub files with m4b format
|
||||
- 🎧 **Audiobook Creation**: Create and export audiobooks from PDF and ePub files **(in m4b format with ffmpeg and aac TTS output)**
|
||||
- 📲 **Mobile Support**: Works on mobile devices, and can be added as a PWA web app
|
||||
- 🎨 **Customizable Experience**:
|
||||
- 🔑 Set TTS API base URL (and optional API key)
|
||||
- 🤖 Choose from multiple TTS models or use custom models
|
||||
- 🎯 Set model-specific instructions for GPT-4o Mini TTS
|
||||
- 🏎️ Adjustable playback speed
|
||||
- 📐 Customize PDF text extraction margins
|
||||
- 🗣️ Multiple voice options (checks `/v1/audio/voices` endpoint)
|
||||
- 🎨 Multiple app theme options
|
||||
|
||||
|
||||
> Orpheus-FastAPI will only work through a [fork of Orpheus-FastAPI](https://github.com/richardr1126/LlamaCpp-Orpheus-FastAPI)
|
||||
|
||||
### 🛠️ Work in progress
|
||||
- [x] **Audiobook creation and download** (m4b format)
|
||||
- [x] **Get PDFs on iOS 17 and below working 🤞**
|
||||
- [x] **Support for GPT-4o Mini TTS with instructions**
|
||||
- [ ] **End-to-end Testing**: More playwright tests (in progress)
|
||||
- [ ] **More document formats**: .txt, .md
|
||||
- [ ] **Support more TTS APIs**: ElevenLabs, etc.
|
||||
- [x] **Intial e2e testing**: More playwright tests (in progress)
|
||||
- [x] **Orpheus-FastAPI support**: (in progress, submitted PR to Orpheus-FastAPI)
|
||||
- [ ] **More document formats**: .txt, .md, native .docx support
|
||||
- [ ] **Support non-OpenAI TTS APIs**: ElevenLabs, etc.
|
||||
- [ ] **Accessibility Improvements**
|
||||
|
||||
## 🐳 Docker Quick Start
|
||||
|
||||
### Prerequisites
|
||||
- Recent version of Docker installed on your machine
|
||||
- A TTS API server (Kokoro-FastAPI, Orpheus-FastAPI, or OpenAI API)
|
||||
|
||||
```bash
|
||||
docker run --name openreader-webui \
|
||||
-p 3003:3003 \
|
||||
|
|
@ -69,11 +71,17 @@ Visit [http://localhost:3003](http://localhost:3003) to run the app and set your
|
|||
|
||||
### ⬆️ Update Docker Image
|
||||
```bash
|
||||
docker stop openreader-webui && docker rm openreader-webui
|
||||
docker stop openreader-webui && \
|
||||
docker rm openreader-webui && \
|
||||
docker pull ghcr.io/richardr1126/openreader-webui:latest
|
||||
```
|
||||
|
||||
### Adding to a Docker Compose (i.e. with open-webui or Kokoro-FastAPI)
|
||||
|
||||
> Note: This is an example of how to add OpenReader WebUI to a docker-compose file. You can add it to your existing docker-compose file or create a new one in this directory. Then run `docker-compose up --build` to start the services.
|
||||
|
||||
```bash
|
||||
|
||||
Create or add to a `docker-compose.yml`:
|
||||
```yaml
|
||||
volumes:
|
||||
|
|
@ -92,11 +100,6 @@ services:
|
|||
restart: unless-stopped
|
||||
```
|
||||
|
||||
## [**Demo**](https://openreader.richardr.dev/)
|
||||
|
||||
|
||||
https://github.com/user-attachments/assets/262b9a01-c608-4fee-893c-9461dd48c99b
|
||||
|
||||
## Dev Installation
|
||||
|
||||
### Prerequisites
|
||||
|
|
@ -147,7 +150,7 @@ For feature requests or ideas you have for the project, please use the [Discussi
|
|||
|
||||
## 🙋♂️ Support and issues
|
||||
|
||||
For general questions, you can reach out to me on [Bluesky](https://bsky.app/profile/richardr.dev). If you encounter issues, please open an issue on GitHub following the template (which is very simple).
|
||||
If you encounter issues, please open an issue on GitHub following the template (which is very light).
|
||||
|
||||
## 👥 Contributing
|
||||
|
||||
|
|
@ -173,7 +176,7 @@ Contributions are welcome! Fork the repository and submit a pull request with yo
|
|||
- [react-pdf](https://github.com/wojtekmaj/react-pdf)
|
||||
- [pdf.js](https://mozilla.github.io/pdf.js/)
|
||||
- **EPUB:**
|
||||
- [react-reader](https://github.com/happyr/react-reader)
|
||||
- [react-reader](https://github.com/gerhardsletten/react-reader)
|
||||
- [epubjs](https://github.com/futurepress/epub.js/)
|
||||
- **UI:**
|
||||
- [Tailwind CSS](https://tailwindcss.com)
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ import { Button } from '@headlessui/react';
|
|||
export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
||||
currentPage: number;
|
||||
numPages: number | undefined;
|
||||
skipToLocation: (location: string | number) => void;
|
||||
skipToLocation: (location: string | number, shouldPause?: boolean) => void;
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex items-center space-x-1">
|
||||
{/* Page back */}
|
||||
<Button
|
||||
onClick={() => skipToLocation(currentPage - 1)}
|
||||
onClick={() => skipToLocation(currentPage - 1, true)}
|
||||
disabled={currentPage <= 1}
|
||||
className="relative p-2 rounded-full text-foreground hover:bg-offbase data-[hover]:bg-offbase data-[active]:bg-offbase/80 transition-colors duration-200 focus:outline-none disabled:opacity-50"
|
||||
aria-label="Previous page"
|
||||
|
|
@ -30,7 +30,7 @@ export const Navigator = ({ currentPage, numPages, skipToLocation }: {
|
|||
|
||||
{/* Page forward */}
|
||||
<Button
|
||||
onClick={() => skipToLocation(currentPage + 1)}
|
||||
onClick={() => skipToLocation(currentPage + 1, true)}
|
||||
disabled={currentPage >= (numPages || 1)}
|
||||
className="relative p-2 rounded-full text-foreground hover:bg-offbase data-[hover]:bg-offbase data-[active]:bg-offbase/80 transition-colors duration-200 focus:outline-none disabled:opacity-50"
|
||||
aria-label="Next page"
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ export function PDFProvider({ children }: { children: ReactNode }) {
|
|||
// This prevents unnecessary resets of the sentence index
|
||||
if (text !== currDocText || text === '') {
|
||||
setCurrDocText(text);
|
||||
setTTSText(text, true);
|
||||
setTTSText(text);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading PDF text:', error);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import {
|
|||
useRef,
|
||||
useMemo,
|
||||
ReactNode,
|
||||
ReactElement
|
||||
} from 'react';
|
||||
import { Howl } from 'howler';
|
||||
import toast from 'react-hot-toast';
|
||||
|
|
@ -73,7 +74,7 @@ interface TTSContextType {
|
|||
setCurrDocPages: (num: number | undefined) => void;
|
||||
setSpeedAndRestart: (speed: number) => void;
|
||||
setVoiceAndRestart: (voice: string) => void;
|
||||
skipToLocation: (location: string | number) => void;
|
||||
skipToLocation: (location: string | number, shouldPause?: boolean) => void;
|
||||
registerLocationChangeHandler: (handler: (location: string | number) => void) => void; // EPUB-only: Handles chapter navigation
|
||||
setIsEPUB: (isEPUB: boolean) => void;
|
||||
}
|
||||
|
|
@ -89,7 +90,7 @@ const TTSContext = createContext<TTSContextType | undefined>(undefined);
|
|||
* @param {ReactNode} props.children - Child components to be wrapped by the provider
|
||||
* @returns {JSX.Element} TTSProvider component
|
||||
*/
|
||||
export function TTSProvider({ children }: { children: ReactNode }) {
|
||||
export function TTSProvider({ children }: { children: ReactNode }): ReactElement {
|
||||
// Configuration context consumption
|
||||
const {
|
||||
apiKey: openApiKey,
|
||||
|
|
@ -196,6 +197,15 @@ export function TTSProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
}, [activeHowl]);
|
||||
|
||||
/**
|
||||
* Pauses the current audio playback
|
||||
* Used for external control of playback state
|
||||
*/
|
||||
const pause = useCallback(() => {
|
||||
abortAudio();
|
||||
setIsPlaying(false);
|
||||
}, [abortAudio]);
|
||||
|
||||
/**
|
||||
* Navigates to a specific location in the document
|
||||
* Works for both PDF pages and EPUB locations
|
||||
|
|
@ -203,9 +213,10 @@ export function TTSProvider({ children }: { children: ReactNode }) {
|
|||
* @param {string | number} location - The target location to navigate to
|
||||
* @param {boolean} keepPlaying - Whether to maintain playback state
|
||||
*/
|
||||
const skipToLocation = useCallback((location: string | number) => {
|
||||
const skipToLocation = useCallback((location: string | number, shouldPause = false) => {
|
||||
// Reset state for new content in correct order
|
||||
abortAudio();
|
||||
if (shouldPause) setIsPlaying(false);
|
||||
setCurrentIndex(0);
|
||||
setSentences([]);
|
||||
setCurrDocPage(location);
|
||||
|
|
@ -342,15 +353,6 @@ export function TTSProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
}, [abortAudio]);
|
||||
|
||||
/**
|
||||
* Pauses the current audio playback
|
||||
* Used for external control of playback state
|
||||
*/
|
||||
const pause = useCallback(() => {
|
||||
abortAudio();
|
||||
setIsPlaying(false);
|
||||
}, [abortAudio]);
|
||||
|
||||
|
||||
/**
|
||||
* Moves forward one sentence in the text
|
||||
|
|
|
|||
Loading…
Reference in a new issue