package library import ( "crypto/sha1" "encoding/hex" "os" "path/filepath" "sort" "strings" ) type Service struct { booksDir string } func New(booksDir string) *Service { return &Service{booksDir: booksDir} } func (s *Service) BooksDir() string { return s.booksDir } func (s *Service) ListBooks() ([]Book, error) { entries, err := os.ReadDir(s.booksDir) if err != nil { if os.IsNotExist(err) { return []Book{}, nil } return nil, err } books := make([]Book, 0, len(entries)) for _, e := range entries { if e.IsDir() { continue } ext := strings.ToLower(filepath.Ext(e.Name())) if ext != ".epub" && ext != ".pdf" { continue } fullPath := filepath.Join(s.booksDir, e.Name()) id := stableID(e.Name()) title := strings.TrimSuffix(e.Name(), filepath.Ext(e.Name())) books = append(books, Book{ ID: id, Title: title, Filename: e.Name(), Path: fullPath, Format: strings.TrimPrefix(ext, "."), }) } sort.Slice(books, func(i, j int) bool { return strings.ToLower(books[i].Title) < strings.ToLower(books[j].Title) }) return books, nil } func (s *Service) FindBook(id string) (*Book, error) { books, err := s.ListBooks() if err != nil { return nil, err } for i := range books { if books[i].ID == id { return &books[i], nil } } return nil, nil } func stableID(input string) string { h := sha1.Sum([]byte(strings.ToLower(input))) return hex.EncodeToString(h[:8]) }