- PdfReader renders PDFs to canvas with page navigation and page-number progress, reusing the same integer progress model as comics. - Reader routes by file container, not media kind, so a PDF in a comics library and a PDF ebook both open in the PDF reader. - pdf.js is dynamically imported: the main bundle stays 443 kB and the 474 kB pdf chunk plus 1.26 MB worker load only when a PDF is actually opened. - comics cover extractor now only handles zip archives. A PDF in a comics library is a valid item but not a zip, so it returns no cover instead of logging an error on every request. - fixed the page scale to leave slack for the vertical scrollbar, which was otherwise forcing a horizontal scrollbar on tall pages. Verified live: a 3-page PDF renders, navigates, and reports 33% progress.
62 lines
1.4 KiB
Go
62 lines
1.4 KiB
Go
package comics
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/mithral/vellum/internal/media"
|
|
)
|
|
|
|
func init() {
|
|
media.RegisterCoverExtractor(Kind, ExtractCover)
|
|
}
|
|
|
|
// ExtractCover writes the first page of the archive into the cache and returns
|
|
// its path. A comic's first page is its cover by convention.
|
|
//
|
|
// Only zip-based archives are handled. A PDF in a comics library is a valid
|
|
// item but not a zip, so it returns no cover rather than logging an error on
|
|
// every request — the grid falls back to a placeholder.
|
|
func ExtractCover(ctx context.Context, db *sql.DB, item media.Item, absPath, cacheDir string) (string, error) {
|
|
switch strings.ToLower(filepath.Ext(item.Path)) {
|
|
case ".cbz", ".zip":
|
|
default:
|
|
return "", nil
|
|
}
|
|
|
|
pages, err := ListPages(absPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
dir, err := media.CoverCacheDir(cacheDir, Kind)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
ext := strings.ToLower(path.Ext(pages[0].Name))
|
|
if ext == "" {
|
|
ext = ".jpg"
|
|
}
|
|
outPath := filepath.Join(dir, fmt.Sprintf("%d%s", item.ID, ext))
|
|
|
|
data, err := ReadPage(absPath, pages[0].Name)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
tmp := outPath + ".tmp"
|
|
if err := os.WriteFile(tmp, data, 0o640); err != nil {
|
|
return "", fmt.Errorf("comics: write cover: %w", err)
|
|
}
|
|
if err := os.Rename(tmp, outPath); err != nil {
|
|
_ = os.Remove(tmp)
|
|
return "", err
|
|
}
|
|
return outPath, nil
|
|
}
|