diff --git a/internal/index/bleve_read.go b/internal/index/bleve_read.go index 73c4122c..80e78cc9 100644 --- a/internal/index/bleve_read.go +++ b/internal/index/bleve_read.go @@ -235,6 +235,10 @@ func (b *BleveIndexer) SameSubjects(slugID string, quantity int) ([]Document, er return []Document{}, err } + if len(doc.Subjects) == 0 { + return []Document{}, err + } + bq := bleve.NewBooleanQuery() subjectsCompoundQuery := bleve.NewDisjunctionQuery() @@ -294,6 +298,10 @@ func (b *BleveIndexer) SameAuthors(slugID string, quantity int) ([]Document, err return []Document{}, err } + if len(doc.Authors) == 0 { + return []Document{}, err + } + authorsCompoundQuery := bleve.NewDisjunctionQuery() for _, slug := range doc.AuthorsSlugs { qu := bleve.NewTermQuery(slug) diff --git a/internal/index/bleve_test.go b/internal/index/bleve_test.go index 8b87f26b..7d4be968 100644 --- a/internal/index/bleve_test.go +++ b/internal/index/bleve_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/blevesearch/bleve/v2" + "github.com/pirmd/epub" "github.com/spf13/afero" "github.com/svera/coreander/v4/internal/index" "github.com/svera/coreander/v4/internal/metadata" @@ -21,22 +22,24 @@ func TestIndexAndSearch(t *testing.T) { } mockMetadataReaders := map[string]metadata.Reader{ - ".epub": metadata.ReaderMock{ - MetadataFake: func(file string) (metadata.Metadata, error) { + ".epub": metadata.EpubReader{ + GetMetadataFromFile: func(file string) (*epub.Information, error) { return tcase.mockedMeta, nil }, + GetPackageFromFile: epub.GetPackageFromFile, }, } appFS := afero.NewMemMapFs() - idx := index.NewBleve(indexMem, appFS, "lib", mockMetadataReaders) - // create test files and directories appFS.MkdirAll("lib", 0755) - afero.WriteFile(appFS, tcase.filename, []byte(""), 0644) + if err = afero.WriteFile(appFS, tcase.filename, []byte(""), 0644); err != nil { + t.Errorf("Couldn't write file %s", tcase.filename) + } - err = idx.AddLibrary(1, true) - if err != nil { + idx := index.NewBleve(indexMem, appFS, "lib", mockMetadataReaders) + + if err = idx.AddLibrary(1, true); err != nil { t.Errorf("Error indexing: %s", err.Error()) } res, err := idx.Search(tcase.search, 1, 10) @@ -44,7 +47,7 @@ func TestIndexAndSearch(t *testing.T) { t.Errorf("Error searching: %s", err.Error()) } if !reflect.DeepEqual(res, tcase.expectedResult) { - t.Errorf("Wrong result returned, expected %#v, got %#v", tcase.expectedResult, res) + t.Errorf("Wrong result returned, expected\n %#v,\n got\n %#v\n", tcase.expectedResult, res) } }) } @@ -53,7 +56,7 @@ func TestIndexAndSearch(t *testing.T) { type testCase struct { name string filename string - mockedMeta metadata.Metadata + mockedMeta *epub.Information search string expectedResult result.Paginated[[]index.Document] } @@ -63,12 +66,17 @@ func testCases() []testCase { { "Look for a term without accent must return accented results", "lib/book1.epub", - metadata.Metadata{ - Title: "Test A", - Authors: []string{"Pérez"}, - Description: "Just test metadata", - Language: "es", - Subjects: []string{"History", "Middle age"}, + &epub.Information{ + Title: []string{"Test A"}, + Creator: []epub.Author{ + { + FullName: "Pérez", + Role: "aut", + }, + }, + Description: []string{"Just test metadata"}, + Language: []string{"es"}, + Subject: []string{"History", "Middle age"}, }, "perez", result.NewPaginated[[]index.Document]( @@ -82,8 +90,9 @@ func testCases() []testCase { Metadata: metadata.Metadata{ Title: "Test A", Authors: []string{"Pérez"}, - Description: "Just test metadata", + Description: "
Just test metadata
", Subjects: []string{"History", "Middle age"}, + Format: "EPUB", }, AuthorsSlugs: []string{"perez"}, SeriesSlug: "", @@ -95,12 +104,17 @@ func testCases() []testCase { { "Look for a term without circumflex accent must return circumflexed results", "lib/book2.epub", - metadata.Metadata{ - Title: "Test B", - Authors: []string{"Benoît"}, - Description: "Just test metadata", - Language: "fr", - Subjects: []string{""}, + &epub.Information{ + Title: []string{"Test B"}, + Creator: []epub.Author{ + { + FullName: "Benoît", + Role: "aut", + }, + }, + Description: []string{"Just test metadata"}, + Language: []string{"fr"}, + Subject: []string{}, }, "benoit", result.NewPaginated[[]index.Document]( @@ -114,12 +128,13 @@ func testCases() []testCase { Metadata: metadata.Metadata{ Title: "Test B", Authors: []string{"Benoît"}, - Description: "Just test metadata", - Subjects: []string{""}, + Description: "Just test metadata
", + Subjects: []string{}, + Format: "EPUB", }, AuthorsSlugs: []string{"benoit"}, SeriesSlug: "", - SubjectsSlugs: []string{""}, + SubjectsSlugs: []string{}, }, }, ), @@ -127,12 +142,17 @@ func testCases() []testCase { { "Look for several, not exact terms must return a result with all those terms, even if there is something in between", "lib/book3.epub", - metadata.Metadata{ - Title: "Test C", - Authors: []string{"Clifford D. Simak"}, - Description: "Just test metadata", - Language: "en", - Subjects: []string{""}, + &epub.Information{ + Title: []string{"Test C"}, + Creator: []epub.Author{ + { + FullName: "Clifford D. Simak", + Role: "aut", + }, + }, + Description: []string{"Just test metadata"}, + Language: []string{"en"}, + Subject: []string{}, }, "clifford simak", result.NewPaginated[[]index.Document]( @@ -146,12 +166,13 @@ func testCases() []testCase { Metadata: metadata.Metadata{ Title: "Test C", Authors: []string{"Clifford D. Simak"}, - Description: "Just test metadata", - Subjects: []string{""}, + Description: "Just test metadata
", + Subjects: []string{}, + Format: "EPUB", }, AuthorsSlugs: []string{"clifford-d-simak"}, SeriesSlug: "", - SubjectsSlugs: []string{""}, + SubjectsSlugs: []string{}, }, }, ), @@ -159,12 +180,17 @@ func testCases() []testCase { { "Look for several, not exact terms must return a result with all those terms, even if there is something in between", "lib/book4.epub", - metadata.Metadata{ - Title: "Test D", - Authors: []string{"James Ellroy"}, - Description: "Just test metadata", - Language: "en", - Subjects: []string{""}, + &epub.Information{ + Title: []string{"Test D"}, + Creator: []epub.Author{ + { + FullName: "James Ellroy", + Role: "aut", + }, + }, + Description: []string{"Just test metadata"}, + Language: []string{"en"}, + Subject: []string{}, }, "james ellroy", result.NewPaginated[[]index.Document]( @@ -177,12 +203,13 @@ func testCases() []testCase { Slug: "james-ellroy-test-d", Metadata: metadata.Metadata{Title: "Test D", Authors: []string{"James Ellroy"}, - Description: "Just test metadata", - Subjects: []string{""}, + Description: "Just test metadata
", + Subjects: []string{}, + Format: "EPUB", }, AuthorsSlugs: []string{"james-ellroy"}, SeriesSlug: "", - SubjectsSlugs: []string{""}, + SubjectsSlugs: []string{}, }, }, ), @@ -190,12 +217,17 @@ func testCases() []testCase { { "Look for several, not exact terms with multiple leading, trailing and in-between spaces must return a result with all those terms, even if there is something in between", "lib/book5.epub", - metadata.Metadata{ - Title: "Test E", - Authors: []string{"James Ellroy"}, - Description: "Just test metadata", - Language: "en", - Subjects: []string{""}, + &epub.Information{ + Title: []string{"Test E"}, + Creator: []epub.Author{ + { + FullName: "James Ellroy", + Role: "aut", + }, + }, + Description: []string{"Just test metadata"}, + Language: []string{"en"}, + Subject: []string{}, }, " james ellroy ", result.NewPaginated[[]index.Document]( @@ -208,12 +240,13 @@ func testCases() []testCase { Slug: "james-ellroy-test-e", Metadata: metadata.Metadata{Title: "Test E", Authors: []string{"James Ellroy"}, - Description: "Just test metadata", - Subjects: []string{""}, + Description: "Just test metadata
", + Subjects: []string{}, + Format: "EPUB", }, AuthorsSlugs: []string{"james-ellroy"}, SeriesSlug: "", - SubjectsSlugs: []string{""}, + SubjectsSlugs: []string{}, }, }, ), @@ -221,12 +254,17 @@ func testCases() []testCase { { "Test genre spanish stemmer", "lib/book6.epub", - metadata.Metadata{ - Title: "La Guerrera", - Authors: []string{"Anónimo"}, - Description: "Just test metadata", - Language: "es", - Subjects: []string{"History", "Middle age"}, + &epub.Information{ + Title: []string{"La Guerrera"}, + Creator: []epub.Author{ + { + FullName: "Anónimo", + Role: "aut", + }, + }, + Description: []string{"Just test metadata"}, + Language: []string{"es"}, + Subject: []string{"History", "Middle age"}, }, "guerrero", result.NewPaginated[[]index.Document]( @@ -240,8 +278,9 @@ func testCases() []testCase { Metadata: metadata.Metadata{ Title: "La Guerrera", Authors: []string{"Anónimo"}, - Description: "Just test metadata", + Description: "Just test metadata
", Subjects: []string{"History", "Middle age"}, + Format: "EPUB", }, AuthorsSlugs: []string{"anonimo"}, SeriesSlug: "", @@ -253,12 +292,17 @@ func testCases() []testCase { { "Test plural italian stemmer", "lib/book7.epub", - metadata.Metadata{ - Title: "Fratelli", - Authors: []string{"Anónimo"}, - Description: "Just test metadata", - Language: "it", - Subjects: []string{"History", "Middle age"}, + &epub.Information{ + Title: []string{"Fratelli"}, + Creator: []epub.Author{ + { + FullName: "Anónimo", + Role: "aut", + }, + }, + Description: []string{"Just test metadata"}, + Language: []string{"it"}, + Subject: []string{"History", "Middle age"}, }, "fratello", result.NewPaginated[[]index.Document]( @@ -272,8 +316,9 @@ func testCases() []testCase { Metadata: metadata.Metadata{ Title: "Fratelli", Authors: []string{"Anónimo"}, - Description: "Just test metadata", + Description: "Just test metadata
", Subjects: []string{"History", "Middle age"}, + Format: "EPUB", }, AuthorsSlugs: []string{"anonimo"}, SeriesSlug: "", @@ -285,12 +330,17 @@ func testCases() []testCase { { "Test genre spanish stemmer", "lib/book8.epub", - metadata.Metadata{ - Title: "El Infinito en un Junco", - Authors: []string{"Irene Vallejo"}, - Description: "Just test metadata", - Language: "es", - Subjects: []string{"History", "Middle age"}, + &epub.Information{ + Title: []string{"El Infinito en un Junco"}, + Creator: []epub.Author{ + { + FullName: "Irene Vallejo", + Role: "aut", + }, + }, + Description: []string{"Just test metadata"}, + Language: []string{"es"}, + Subject: []string{"History", "Middle age"}, }, "infinito junco", result.NewPaginated[[]index.Document]( @@ -304,8 +354,9 @@ func testCases() []testCase { Metadata: metadata.Metadata{ Title: "El Infinito en un Junco", Authors: []string{"Irene Vallejo"}, - Description: "Just test metadata", + Description: "Just test metadata
", Subjects: []string{"History", "Middle age"}, + Format: "EPUB", }, AuthorsSlugs: []string{"irene-vallejo"}, SeriesSlug: "", @@ -317,12 +368,17 @@ func testCases() []testCase { { "Test spanish stemmer returning accented word while using unaccented word in search", "lib/book9.epub", - metadata.Metadata{ - Title: "Últimos días en Colditz", - Authors: []string{"Patrick R. Reid"}, - Description: "Just test metadata", - Language: "es", - Subjects: []string{"History", "WWII"}, + &epub.Information{ + Title: []string{"Últimos días en Colditz"}, + Creator: []epub.Author{ + { + FullName: "Patrick R. Reid", + Role: "aut", + }, + }, + Description: []string{"Just test metadata"}, + Language: []string{"es"}, + Subject: []string{"History", "WWII"}, }, "ultimos", result.NewPaginated[[]index.Document]( @@ -336,8 +392,9 @@ func testCases() []testCase { Metadata: metadata.Metadata{ Title: "Últimos días en Colditz", Authors: []string{"Patrick R. Reid"}, - Description: "Just test metadata", + Description: "Just test metadata
", Subjects: []string{"History", "WWII"}, + Format: "EPUB", }, AuthorsSlugs: []string{"patrick-r-reid"}, SeriesSlug: "", @@ -346,5 +403,43 @@ func testCases() []testCase { }, ), }, + { + "Weird case with ',' as subject or '&' as author", + "lib/book10.epub", + &epub.Information{ + Title: []string{"Sin nombre"}, + Creator: []epub.Author{ + { + FullName: "&", + Role: "aut", + }, + }, + Description: []string{"Just test metadata"}, + Language: []string{"es"}, + Subject: []string{","}, + }, + "sin nombre", + result.NewPaginated[[]index.Document]( + model.ResultsPerPage, + 1, + 1, + []index.Document{ + { + ID: "book10.epub", + Slug: "sin-nombre", + Metadata: metadata.Metadata{ + Title: "Sin nombre", + Authors: []string{""}, + Description: "Just test metadata
", + Subjects: []string{}, + Format: "EPUB", + }, + AuthorsSlugs: []string{""}, + SeriesSlug: "", + SubjectsSlugs: []string{}, + }, + }, + ), + }, } } diff --git a/internal/index/bleve_write.go b/internal/index/bleve_write.go index afa616e1..94dfa9d4 100644 --- a/internal/index/bleve_write.go +++ b/internal/index/bleve_write.go @@ -29,8 +29,7 @@ func (b *BleveIndexer) AddFile(file string) (string, error) { document := b.createDocument(meta, file, nil) - err = b.idx.Index(document.ID, document) - if err != nil { + if err = b.idx.Index(document.ID, document); err != nil { return "", fmt.Errorf("error indexing file %s: %s", file, err) } diff --git a/internal/metadata/epub.go b/internal/metadata/epub.go index 4230e776..58ea544d 100644 --- a/internal/metadata/epub.go +++ b/internal/metadata/epub.go @@ -17,15 +17,25 @@ import ( "github.com/pirmd/epub" ) -type EpubReader struct{} +type EpubReader struct { + GetMetadataFromFile func(path string) (*epub.Information, error) + GetPackageFromFile func(path string) (*epub.PackageDocument, error) +} -func (e EpubReader) Metadata(file string) (Metadata, error) { +func NewEpubReader() EpubReader { + return EpubReader{ + GetMetadataFromFile: epub.GetMetadataFromFile, + GetPackageFromFile: epub.GetPackageFromFile, + } +} + +func (e EpubReader) Metadata(filename string) (Metadata, error) { bk := Metadata{} - meta, err := epub.GetMetadataFromFile(file) + meta, err := e.GetMetadataFromFile(filename) if err != nil { return bk, err } - title := strings.TrimSuffix(filepath.Base(file), filepath.Ext(file)) + title := strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename)) if len(meta.Title) > 0 && len(meta.Title[0]) > 0 { title = meta.Title[0] } @@ -35,10 +45,11 @@ func (e EpubReader) Metadata(file string) (Metadata, error) { // Some epub files mistakenly put all authors in a single field instead of using a field for each one. // We want to identify those cases looking for specific separators and then indexing each author properly. names := strings.Split(creator.FullName, "&") - for i := range names { - names[i] = strings.TrimSpace(names[i]) + for _, name := range names { + if name = strings.TrimSpace(name); name != "" { + authors = append(authors, name) + } } - authors = append(authors, names...) } } @@ -55,10 +66,11 @@ func (e EpubReader) Metadata(file string) (Metadata, error) { // Some epub files mistakenly put all subjects in a single field instead of using a field for each one. // We want to identify those cases looking for specific separators and then indexing each subject properly. names := strings.Split(subject, ",") - for i := range names { - names[i] = strings.TrimSpace(names[i]) + for _, name := range names { + if name = strings.TrimSpace(name); name != "" { + subjects = append(subjects, name) + } } - subjects = append(subjects, names...) } description := "" @@ -108,9 +120,9 @@ func (e EpubReader) Metadata(file string) (Metadata, error) { Format: "EPUB", Subjects: subjects, } - w, err := words(file) + w, err := words(filename) if err != nil { - log.Println(err) + log.Printf("Cannot count words in %s: $%s\n", filename, err) } bk.Words = float64(w) return bk, nil @@ -122,7 +134,7 @@ func (e EpubReader) Cover(documentFullPath string, coverMaxWidth int) ([]byte, e coverFileName := "" - opf, err := epub.GetPackageFromFile(documentFullPath) + opf, err := e.GetPackageFromFile(documentFullPath) if err != nil { return nil, err } diff --git a/internal/metadata/reader_mock.go b/internal/metadata/reader_mock.go deleted file mode 100644 index 4c59f7df..00000000 --- a/internal/metadata/reader_mock.go +++ /dev/null @@ -1,25 +0,0 @@ -package metadata - -type ReaderMock struct { - MetadataFake func(file string) (Metadata, error) - CoverFake func(documentFullPath string) ([]byte, error) -} - -func NewReaderMock() ReaderMock { - return ReaderMock{ - MetadataFake: func(file string) (Metadata, error) { - return Metadata{}, nil - }, - CoverFake: func(documentFullPath string) ([]byte, error) { - return []byte{}, nil - }, - } -} - -func (e ReaderMock) Metadata(file string) (Metadata, error) { - return e.MetadataFake(file) -} - -func (e ReaderMock) Cover(documentFullPath string, coverMaxWidth int) ([]byte, error) { - return e.CoverFake(documentFullPath) -} diff --git a/internal/webserver/embedded/css/display.css b/internal/webserver/embedded/css/display.css index 820b9bd9..9c52a927 100644 --- a/internal/webserver/embedded/css/display.css +++ b/internal/webserver/embedded/css/display.css @@ -101,14 +101,19 @@ a.collapse-control.collapsed:after { } } -.cover-corner { - clip-path: polygon(100% 0, 100% calc(100% - 3em), calc(100% - 3em) 100%, 0 100%, 0 0); -} - .cover-background { - background-color: #eee; + background-image: url("../images/generic.jpg"); + background-size: cover; } .cursor-pointer { cursor: pointer; } + +.w-10 { + width: 10%; +} + +.actions .dropdown-menu { + white-space: nowrap; +} diff --git a/internal/webserver/embedded/images/eye-solid.svg b/internal/webserver/embedded/images/eye-solid.svg index f4559e6c..787eaa8e 100644 --- a/internal/webserver/embedded/images/eye-solid.svg +++ b/internal/webserver/embedded/images/eye-solid.svg @@ -1,4 +1,4 @@ -