forked from akai/readest
This commit is contained in:
@@ -296,6 +296,11 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP
|
||||
const importBooks = async (files: (string | File)[]) => {
|
||||
setLoading(true);
|
||||
const failedFiles = [];
|
||||
const errorMap: [string, string][] = [
|
||||
['No chapters detected.', _('No chapters detected.')],
|
||||
['Failed to parse EPUB.', _('Failed to parse the EPUB file.')],
|
||||
['Unsupported format.', _('This book format is not supported.')],
|
||||
];
|
||||
for (const file of files) {
|
||||
try {
|
||||
const book = await appService?.importBook(file, libraryBooks);
|
||||
@@ -308,10 +313,15 @@ const LibraryPageContent = ({ searchParams }: { searchParams: ReadonlyURLSearchP
|
||||
const filename = typeof file === 'string' ? file : file.name;
|
||||
const baseFilename = getBaseFilename(filename);
|
||||
failedFiles.push(baseFilename);
|
||||
const errorMessage =
|
||||
error instanceof Error
|
||||
? errorMap.find(([substring]) => error.message.includes(substring))?.[1] || ''
|
||||
: '';
|
||||
eventDispatcher.dispatch('toast', {
|
||||
message: _('Failed to import book(s): {{filenames}}', {
|
||||
filenames: listFormater(false).format(failedFiles),
|
||||
}),
|
||||
message:
|
||||
_('Failed to import book(s): {{filenames}}', {
|
||||
filenames: listFormater(false).format(failedFiles),
|
||||
}) + (errorMessage ? `\n${errorMessage}` : ''),
|
||||
type: 'error',
|
||||
});
|
||||
console.error('Failed to import book:', filename, error);
|
||||
|
||||
@@ -277,8 +277,8 @@ const TTSControl = () => {
|
||||
className={clsx(
|
||||
'fixed right-6 h-12 w-12',
|
||||
appService?.hasSafeAreaInset
|
||||
? 'bottom-[calc(env(safe-area-inset-bottom)+48px)]'
|
||||
: 'bottom-12',
|
||||
? 'bottom-[calc(env(safe-area-inset-bottom)+70px)]'
|
||||
: 'bottom-[70px] sm:bottom-14',
|
||||
)}
|
||||
>
|
||||
<TTSIcon isPlaying={isPlaying} onClick={togglePopup} />
|
||||
|
||||
@@ -21,7 +21,20 @@ const Popup = ({
|
||||
}) => (
|
||||
<div>
|
||||
<div
|
||||
className={`triangle text-base-300 absolute z-40 ${triangleClassName}`}
|
||||
id='popup-container'
|
||||
className={`bg-base-300 absolute rounded-lg font-sans shadow-xl ${className}`}
|
||||
style={{
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
left: `${position ? position.point.x : -999}px`,
|
||||
top: `${position ? position.point.y : -999}px`,
|
||||
...additionalStyle,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<div
|
||||
className={`triangle text-base-300 absolute ${triangleClassName}`}
|
||||
style={{
|
||||
left:
|
||||
trianglePosition?.dir === 'left'
|
||||
@@ -65,19 +78,6 @@ const Popup = ({
|
||||
: 'translateX(-50%)',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
id='popup-container'
|
||||
className={`bg-base-300 absolute z-30 rounded-lg font-sans shadow-xl ${className}`}
|
||||
style={{
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
left: `${position ? position.point.x : -999}px`,
|
||||
top: `${position ? position.point.y : -999}px`,
|
||||
...additionalStyle,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@@ -69,7 +69,12 @@ export const Toast = () => {
|
||||
messageClass.current,
|
||||
)}
|
||||
>
|
||||
{toastMessage}
|
||||
{toastMessage.split('\n').map((line, idx) => (
|
||||
<React.Fragment key={idx}>
|
||||
{line || <> </>}
|
||||
<br />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getBaseFilename } from './book';
|
||||
import { partialMD5 } from './md5';
|
||||
|
||||
interface Metadata {
|
||||
@@ -38,12 +39,12 @@ export class TxtToEpubConverter {
|
||||
public async convert(options: Txt2EpubOptions): Promise<ConversionResult> {
|
||||
const { file: txtFile, author: providedAuthor, language: providedLanguage } = options;
|
||||
|
||||
const fileContent = await this.readFileAsArrayBuffer(txtFile);
|
||||
const fileContent = await txtFile.arrayBuffer();
|
||||
const detectedEncoding = this.detectEncoding(fileContent) || 'utf-8';
|
||||
const decoder = new TextDecoder(detectedEncoding);
|
||||
const txtContent = decoder.decode(fileContent).trim();
|
||||
|
||||
const bookTitle = this.extractBookTitle(txtFile.name);
|
||||
const bookTitle = this.extractBookTitle(getBaseFilename(txtFile.name));
|
||||
const fileName = `${bookTitle}.epub`;
|
||||
|
||||
const fileHeader = txtContent.slice(0, 1024);
|
||||
@@ -358,15 +359,6 @@ export class TxtToEpubConverter {
|
||||
return 'en';
|
||||
}
|
||||
|
||||
private readFileAsArrayBuffer(file: File): Promise<ArrayBuffer> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => resolve(reader.result as ArrayBuffer);
|
||||
reader.onerror = reject;
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
}
|
||||
|
||||
private extractBookTitle(filename: string): string {
|
||||
const match = filename.match(/《([^》]+)》/);
|
||||
return match ? match[1]! : filename.split('.')[0]!;
|
||||
|
||||
Reference in New Issue
Block a user