ebk 0.4.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. ebk/__init__.py +35 -0
  2. ebk/ai/__init__.py +23 -0
  3. ebk/ai/knowledge_graph.py +450 -0
  4. ebk/ai/llm_providers/__init__.py +26 -0
  5. ebk/ai/llm_providers/anthropic.py +209 -0
  6. ebk/ai/llm_providers/base.py +295 -0
  7. ebk/ai/llm_providers/gemini.py +285 -0
  8. ebk/ai/llm_providers/ollama.py +294 -0
  9. ebk/ai/metadata_enrichment.py +394 -0
  10. ebk/ai/question_generator.py +328 -0
  11. ebk/ai/reading_companion.py +224 -0
  12. ebk/ai/semantic_search.py +433 -0
  13. ebk/ai/text_extractor.py +393 -0
  14. ebk/calibre_import.py +66 -0
  15. ebk/cli.py +6433 -0
  16. ebk/config.py +230 -0
  17. ebk/db/__init__.py +37 -0
  18. ebk/db/migrations.py +507 -0
  19. ebk/db/models.py +725 -0
  20. ebk/db/session.py +144 -0
  21. ebk/decorators.py +1 -0
  22. ebk/exports/__init__.py +0 -0
  23. ebk/exports/base_exporter.py +218 -0
  24. ebk/exports/echo_export.py +279 -0
  25. ebk/exports/html_library.py +1743 -0
  26. ebk/exports/html_utils.py +87 -0
  27. ebk/exports/hugo.py +59 -0
  28. ebk/exports/jinja_export.py +286 -0
  29. ebk/exports/multi_facet_export.py +159 -0
  30. ebk/exports/opds_export.py +232 -0
  31. ebk/exports/symlink_dag.py +479 -0
  32. ebk/exports/zip.py +25 -0
  33. ebk/extract_metadata.py +341 -0
  34. ebk/ident.py +89 -0
  35. ebk/library_db.py +1440 -0
  36. ebk/opds.py +748 -0
  37. ebk/plugins/__init__.py +42 -0
  38. ebk/plugins/base.py +502 -0
  39. ebk/plugins/hooks.py +442 -0
  40. ebk/plugins/registry.py +499 -0
  41. ebk/repl/__init__.py +9 -0
  42. ebk/repl/find.py +126 -0
  43. ebk/repl/grep.py +173 -0
  44. ebk/repl/shell.py +1677 -0
  45. ebk/repl/text_utils.py +320 -0
  46. ebk/search_parser.py +413 -0
  47. ebk/server.py +3608 -0
  48. ebk/services/__init__.py +28 -0
  49. ebk/services/annotation_extraction.py +351 -0
  50. ebk/services/annotation_service.py +380 -0
  51. ebk/services/export_service.py +577 -0
  52. ebk/services/import_service.py +447 -0
  53. ebk/services/personal_metadata_service.py +347 -0
  54. ebk/services/queue_service.py +253 -0
  55. ebk/services/tag_service.py +281 -0
  56. ebk/services/text_extraction.py +317 -0
  57. ebk/services/view_service.py +12 -0
  58. ebk/similarity/__init__.py +77 -0
  59. ebk/similarity/base.py +154 -0
  60. ebk/similarity/core.py +471 -0
  61. ebk/similarity/extractors.py +168 -0
  62. ebk/similarity/metrics.py +376 -0
  63. ebk/skills/SKILL.md +182 -0
  64. ebk/skills/__init__.py +1 -0
  65. ebk/vfs/__init__.py +101 -0
  66. ebk/vfs/base.py +298 -0
  67. ebk/vfs/library_vfs.py +122 -0
  68. ebk/vfs/nodes/__init__.py +54 -0
  69. ebk/vfs/nodes/authors.py +196 -0
  70. ebk/vfs/nodes/books.py +480 -0
  71. ebk/vfs/nodes/files.py +155 -0
  72. ebk/vfs/nodes/metadata.py +385 -0
  73. ebk/vfs/nodes/root.py +100 -0
  74. ebk/vfs/nodes/similar.py +165 -0
  75. ebk/vfs/nodes/subjects.py +184 -0
  76. ebk/vfs/nodes/tags.py +371 -0
  77. ebk/vfs/resolver.py +228 -0
  78. ebk/vfs_router.py +275 -0
  79. ebk/views/__init__.py +32 -0
  80. ebk/views/dsl.py +668 -0
  81. ebk/views/service.py +619 -0
  82. ebk-0.4.4.dist-info/METADATA +755 -0
  83. ebk-0.4.4.dist-info/RECORD +87 -0
  84. ebk-0.4.4.dist-info/WHEEL +5 -0
  85. ebk-0.4.4.dist-info/entry_points.txt +2 -0
  86. ebk-0.4.4.dist-info/licenses/LICENSE +21 -0
  87. ebk-0.4.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,54 @@
1
+ """VFS node implementations."""
2
+
3
+ from ebk.vfs.nodes.root import RootNode
4
+ from ebk.vfs.nodes.books import BooksDirectoryNode, BookNode
5
+ from ebk.vfs.nodes.metadata import (
6
+ TitleFileNode,
7
+ AuthorsFileNode,
8
+ SubjectsFileNode,
9
+ DescriptionFileNode,
10
+ TextFileNode,
11
+ YearFileNode,
12
+ LanguageFileNode,
13
+ PublisherFileNode,
14
+ MetadataFileNode,
15
+ )
16
+ from ebk.vfs.nodes.files import FilesDirectoryNode, PhysicalFileNode
17
+ from ebk.vfs.nodes.similar import SimilarDirectoryNode, SimilarBookSymlink
18
+ from ebk.vfs.nodes.authors import AuthorsDirectoryNode, AuthorNode
19
+ from ebk.vfs.nodes.subjects import SubjectsDirectoryNode, SubjectNode
20
+ from ebk.vfs.nodes.tags import (
21
+ TagsDirectoryNode,
22
+ TagNode,
23
+ TagDescriptionFile,
24
+ TagColorFile,
25
+ TagStatsFile,
26
+ )
27
+
28
+ __all__ = [
29
+ "RootNode",
30
+ "BooksDirectoryNode",
31
+ "BookNode",
32
+ "TitleFileNode",
33
+ "AuthorsFileNode",
34
+ "SubjectsFileNode",
35
+ "DescriptionFileNode",
36
+ "TextFileNode",
37
+ "YearFileNode",
38
+ "LanguageFileNode",
39
+ "PublisherFileNode",
40
+ "MetadataFileNode",
41
+ "FilesDirectoryNode",
42
+ "PhysicalFileNode",
43
+ "SimilarDirectoryNode",
44
+ "SimilarBookSymlink",
45
+ "AuthorsDirectoryNode",
46
+ "AuthorNode",
47
+ "SubjectsDirectoryNode",
48
+ "SubjectNode",
49
+ "TagsDirectoryNode",
50
+ "TagNode",
51
+ "TagDescriptionFile",
52
+ "TagColorFile",
53
+ "TagStatsFile",
54
+ ]
@@ -0,0 +1,196 @@
1
+ """Author-related VFS nodes."""
2
+
3
+ from typing import List, Optional, Dict, Any
4
+
5
+ from ebk.vfs.base import VirtualNode, DirectoryNode, SymlinkNode, Node
6
+ from ebk.library_db import Library
7
+ from ebk.db.models import Author
8
+
9
+
10
+ class AuthorsDirectoryNode(VirtualNode):
11
+ """/authors/ - Virtual directory listing all authors.
12
+
13
+ Each child is an AuthorNode representing books by that author.
14
+ """
15
+
16
+ def __init__(self, library: Library, parent: Optional[DirectoryNode] = None):
17
+ """Initialize authors directory.
18
+
19
+ Args:
20
+ library: Library instance
21
+ parent: Parent node (usually root)
22
+ """
23
+ super().__init__(name="authors", parent=parent)
24
+ self.library = library
25
+
26
+ def list_children(self) -> List[Node]:
27
+ """List all authors.
28
+
29
+ Returns:
30
+ List of AuthorNode instances
31
+ """
32
+ # Query all authors from database
33
+ # For now, we'll get unique authors from books
34
+ authors_query = self.library.session.query(Author).all()
35
+
36
+ author_nodes = []
37
+ for author in authors_query:
38
+ # Create a slug from author name
39
+ slug = self._make_slug(author.name)
40
+ node = AuthorNode(author, slug, self.library, parent=self)
41
+ author_nodes.append(node)
42
+
43
+ return author_nodes
44
+
45
+ def get_child(self, name: str) -> Optional[Node]:
46
+ """Get an author by slug.
47
+
48
+ Args:
49
+ name: Author slug (e.g., "knuth-donald")
50
+
51
+ Returns:
52
+ AuthorNode or None
53
+ """
54
+ # Try to find author by matching slug
55
+ authors = self.library.session.query(Author).all()
56
+
57
+ for author in authors:
58
+ slug = self._make_slug(author.name)
59
+ if slug == name:
60
+ return AuthorNode(author, slug, self.library, parent=self)
61
+
62
+ return None
63
+
64
+ def _make_slug(self, name: str) -> str:
65
+ """Convert author name to filesystem-safe slug.
66
+
67
+ Args:
68
+ name: Author name
69
+
70
+ Returns:
71
+ Slugified name (e.g., "Donald Knuth" -> "knuth-donald")
72
+ """
73
+ # Simple slugification: lowercase, replace spaces with hyphens
74
+ # Reverse name order (Last, First -> first-last)
75
+ parts = name.lower().split()
76
+ if len(parts) >= 2:
77
+ # Assume "First Last" or "Last, First"
78
+ if "," in name:
79
+ # "Last, First" format
80
+ slug = "-".join(reversed([p.strip(",") for p in parts]))
81
+ else:
82
+ # "First Last" format - reverse to "last-first"
83
+ slug = "-".join(reversed(parts))
84
+ else:
85
+ slug = "-".join(parts)
86
+
87
+ # Remove special characters
88
+ slug = "".join(c for c in slug if c.isalnum() or c == "-")
89
+ return slug
90
+
91
+ def get_info(self) -> Dict[str, Any]:
92
+ """Get authors directory info.
93
+
94
+ Returns:
95
+ Dict with directory information
96
+ """
97
+ total = self.library.session.query(Author).count()
98
+ return {
99
+ "type": "virtual",
100
+ "name": "authors",
101
+ "total_authors": total,
102
+ "path": self.get_path(),
103
+ }
104
+
105
+
106
+ class AuthorNode(VirtualNode):
107
+ """/authors/knuth-donald/ - Books by a specific author.
108
+
109
+ Contains symlinks to books by this author.
110
+ """
111
+
112
+ def __init__(
113
+ self,
114
+ author: Author,
115
+ slug: str,
116
+ library: Library,
117
+ parent: Optional[DirectoryNode] = None,
118
+ ):
119
+ """Initialize author node.
120
+
121
+ Args:
122
+ author: Author database model
123
+ slug: Author slug for URL
124
+ library: Library instance
125
+ parent: Parent node (usually AuthorsDirectoryNode)
126
+ """
127
+ super().__init__(name=slug, parent=parent)
128
+ self.author = author
129
+ self.library = library
130
+
131
+ def list_children(self) -> List[Node]:
132
+ """List books by this author as symlinks.
133
+
134
+ Returns:
135
+ List of SymlinkNode instances
136
+ """
137
+ symlinks = []
138
+ for book in self.author.books:
139
+ target_path = f"/books/{book.id}"
140
+ name = str(book.id)
141
+
142
+ # Include book metadata for display
143
+ metadata = {
144
+ "title": book.title or "Untitled",
145
+ }
146
+ if book.authors:
147
+ metadata["author"] = ", ".join([a.name for a in book.authors])
148
+
149
+ symlink = SymlinkNode(name, target_path, parent=self, metadata=metadata)
150
+ symlinks.append(symlink)
151
+
152
+ return symlinks
153
+
154
+ def get_child(self, name: str) -> Optional[Node]:
155
+ """Get a book symlink by ID.
156
+
157
+ Args:
158
+ name: Book ID as string
159
+
160
+ Returns:
161
+ SymlinkNode or None
162
+ """
163
+ try:
164
+ book_id = int(name)
165
+ except ValueError:
166
+ return None
167
+
168
+ # Check if this book is by this author
169
+ for book in self.author.books:
170
+ if book.id == book_id:
171
+ target_path = f"/books/{book.id}"
172
+
173
+ # Include book metadata for display
174
+ metadata = {
175
+ "title": book.title or "Untitled",
176
+ }
177
+ if book.authors:
178
+ metadata["author"] = ", ".join([a.name for a in book.authors])
179
+
180
+ return SymlinkNode(name, target_path, parent=self, metadata=metadata)
181
+
182
+ return None
183
+
184
+ def get_info(self) -> Dict[str, Any]:
185
+ """Get author node info.
186
+
187
+ Returns:
188
+ Dict with author information
189
+ """
190
+ return {
191
+ "type": "virtual",
192
+ "name": self.name,
193
+ "author": self.author.name,
194
+ "book_count": len(self.author.books),
195
+ "path": self.get_path(),
196
+ }