shamela 1.0.6 → 1.2.0

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.
package/README.md CHANGED
@@ -1,18 +1,18 @@
1
- # Shamela
1
+ # shamela
2
2
 
3
3
  [![wakatime](https://wakatime.com/badge/user/a0b906ce-b8e7-4463-8bce-383238df6d4b/project/faef70ab-efdb-448b-ab83-0fc66c95888e.svg)](https://wakatime.com/badge/user/a0b906ce-b8e7-4463-8bce-383238df6d4b/project/faef70ab-efdb-448b-ab83-0fc66c95888e)
4
4
  [![E2E](https://github.com/ragaeeb/shamela/actions/workflows/e2e.yml/badge.svg)](https://github.com/ragaeeb/shamela/actions/workflows/e2e.yml)
5
5
  [![Node.js CI](https://github.com/ragaeeb/shamela/actions/workflows/build.yml/badge.svg)](https://github.com/ragaeeb/shamela/actions/workflows/build.yml) ![GitHub License](https://img.shields.io/github/license/ragaeeb/shamela)
6
6
  ![GitHub Release](https://img.shields.io/github/v/release/ragaeeb/shamela)
7
7
  [![codecov](https://codecov.io/gh/ragaeeb/shamela/graph/badge.svg?token=PK55V1R324)](https://codecov.io/gh/ragaeeb/shamela)
8
- [![Size](https://deno.bundlejs.com/badge?q=shamela@1.0.5)](https://bundlejs.com/?q=shamela%401.0.5)
8
+ [![Size](https://deno.bundlejs.com/badge?q=shamela@latest)](https://bundlejs.com/?q=shamela%40latest)
9
9
  ![typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label&color=blue)
10
10
  ![npm](https://img.shields.io/npm/v/shamela)
11
11
  ![npm](https://img.shields.io/npm/dm/shamela)
12
12
  ![GitHub issues](https://img.shields.io/github/issues/ragaeeb/shamela)
13
13
  ![GitHub stars](https://img.shields.io/github/stars/ragaeeb/shamela?style=social)
14
14
 
15
- A `NodeJS` library for accessing and downloading Maktabah Shamela v4 APIs. This library provides easy-to-use functions to interact with the Shamela API, download master and book databases, and retrieve book data programmatically.
15
+ A `Node.js` library for accessing and downloading Maktabah Shamela v4 APIs. This library provides easy-to-use functions to interact with the Shamela API, download master and book databases, and retrieve book data programmatically.
16
16
 
17
17
  ## Table of Contents
18
18
 
@@ -26,15 +26,24 @@ A `NodeJS` library for accessing and downloading Maktabah Shamela v4 APIs. This
26
26
  - [getBookMetadata](#getbookmetadata)
27
27
  - [downloadBook](#downloadbook)
28
28
  - [getBook](#getbook)
29
+ - [getCoverUrl](#getcoverurl)
29
30
  - [Examples](#examples)
30
31
  - [Downloading the Master Database](#downloading-the-master-database)
31
32
  - [Downloading a Book](#downloading-a-book)
32
33
  - [Retrieving Book Data](#retrieving-book-data)
34
+ - [Getting Book Cover URLs](#getting-book-cover-urls)
35
+ - [Data Structures](#data-structures)
33
36
  - [Testing](#testing)
34
37
  - [License](#license)
35
38
 
36
39
  ## Installation
37
40
 
41
+ ```bash
42
+ bun add shamela
43
+ ```
44
+
45
+ or
46
+
38
47
  ```bash
39
48
  npm install shamela
40
49
  ```
@@ -55,9 +64,10 @@ pnpm install shamela
55
64
 
56
65
  Before using the library, you need to set up some environment variables for API keys and endpoints:
57
66
 
58
- `SHAMELA_API_KEY`: Your API key for accessing the Shamela API.
59
- `SHAMELA_API_MASTER_PATCH_ENDPOINT`: The endpoint URL for the master database patches.
60
- `SHAMELA_API_BOOKS_ENDPOINT`: The base endpoint URL for book-related API calls.
67
+ - `SHAMELA_API_KEY`: Your API key for accessing the Shamela API.
68
+ - `SHAMELA_API_MASTER_PATCH_ENDPOINT`: The endpoint URL for the master database patches.
69
+ - `SHAMELA_API_BOOKS_ENDPOINT`: The base endpoint URL for book-related API calls.
70
+
61
71
  You can set these variables in a `.env` file at the root of your project:
62
72
 
63
73
  ```dotenv
@@ -73,7 +83,14 @@ SHAMELA_API_BOOKS_ENDPOINT=https://shamela.ws/api/books
73
83
  First, import the library functions into your project:
74
84
 
75
85
  ```javascript
76
- import { getMasterMetadata, downloadMasterDatabase, getBookMetadata, downloadBook, getBook } from 'shamela';
86
+ import {
87
+ getMasterMetadata,
88
+ downloadMasterDatabase,
89
+ getBookMetadata,
90
+ downloadBook,
91
+ getBook,
92
+ getCoverUrl,
93
+ } from 'shamela';
77
94
  ```
78
95
 
79
96
  ### API Functions
@@ -84,34 +101,54 @@ Fetches metadata for the master database.
84
101
 
85
102
  ```typescript
86
103
  getMasterMetadata(version?: number): Promise<GetMasterMetadataResponsePayload>
87
-
88
104
  ```
89
105
 
90
- - version (optional): The version number of the master database you want to fetch.
106
+ - `version` (optional): The version number of the master database you want to check for updates (defaults to 0)
107
+
108
+ **Returns:** Promise that resolves to master database metadata including download URL and version
91
109
 
92
- Example:
110
+ **Example:**
93
111
 
94
112
  ```javascript
95
113
  const masterMetadata = await getMasterMetadata();
114
+ console.log(masterMetadata.url); // Download URL for master database patch
115
+ console.log(masterMetadata.version); // Latest version number
116
+
117
+ // Check for updates from a specific version
118
+ const updates = await getMasterMetadata(5);
96
119
  ```
97
120
 
98
121
  #### downloadMasterDatabase
99
122
 
100
- Downloads the master database and saves it to a specified path.
123
+ Downloads the master database and saves it to a specified path. The master database contains comprehensive information about all books, authors, and categories available in the Shamela library.
101
124
 
102
125
  ```typescript
103
126
  downloadMasterDatabase(options: DownloadMasterOptions): Promise<string>
104
-
105
127
  ```
106
128
 
107
- - options: An object containing:
108
- - masterMetadata (optional): The metadata obtained from getMasterMetadata.
109
- - outputFile: An object specifying the output path.
129
+ - `options`: Configuration object containing:
130
+ - `masterMetadata` (optional): Pre-fetched metadata from `getMasterMetadata`
131
+ - `outputFile`: Object with `path` property specifying the output file path
110
132
 
111
- Example:
133
+ **Returns:** Promise that resolves to the path of the created output file
134
+
135
+ **Example:**
112
136
 
113
137
  ```javascript
138
+ // Download as SQLite database
139
+ await downloadMasterDatabase({
140
+ outputFile: { path: './master.db' },
141
+ });
142
+
143
+ // Download as JSON
144
+ await downloadMasterDatabase({
145
+ outputFile: { path: './master.json' },
146
+ });
147
+
148
+ // Use pre-fetched metadata for efficiency
149
+ const masterMetadata = await getMasterMetadata();
114
150
  await downloadMasterDatabase({
151
+ masterMetadata,
115
152
  outputFile: { path: './master.db' },
116
153
  });
117
154
  ```
@@ -124,33 +161,101 @@ Fetches metadata for a specific book.
124
161
  getBookMetadata(id: number, options?: GetBookMetadataOptions): Promise<GetBookMetadataResponsePayload>
125
162
  ```
126
163
 
127
- - id: The ID of the book.
128
- - options (optional): An object containing:
129
- - majorVersion: The major version of the book.
130
- - minorVersion: The minor version of the book.
164
+ - `id`: The unique identifier of the book
165
+ - `options` (optional): Configuration object containing:
166
+ - `majorVersion`: The major version to check against
167
+ - `minorVersion`: The minor version to check against
131
168
 
132
- Example:
169
+ **Returns:** Promise that resolves to book metadata including release URLs and versions
170
+
171
+ **Example:**
133
172
 
134
173
  ```javascript
135
- await downloadMasterDatabase({
136
- outputFile: { path: './master.db' },
174
+ const metadata = await getBookMetadata(26592);
175
+ console.log(metadata.majorReleaseUrl); // URL for downloading the book
176
+ console.log(metadata.majorRelease); // Major version number
177
+
178
+ // Check specific versions
179
+ const versionedMetadata = await getBookMetadata(26592, {
180
+ majorVersion: 1,
181
+ minorVersion: 2,
182
+ });
183
+ ```
184
+
185
+ #### downloadBook
186
+
187
+ Downloads and processes a book from the Shamela database. This function downloads the book's database files, applies patches if available, and exports the data to the specified format.
188
+
189
+ ```typescript
190
+ downloadBook(id: number, options: DownloadBookOptions): Promise<string>
191
+ ```
192
+
193
+ - `id`: The unique identifier of the book to download
194
+ - `options`: Configuration object containing:
195
+ - `bookMetadata` (optional): Pre-fetched metadata from `getBookMetadata`
196
+ - `outputFile`: Object with `path` property specifying the output file path
197
+
198
+ **Returns:** Promise that resolves to the path of the created output file
199
+
200
+ **Example:**
201
+
202
+ ```javascript
203
+ // Download as JSON
204
+ await downloadBook(26592, {
205
+ outputFile: { path: './book.json' },
206
+ });
207
+
208
+ // Download as SQLite database
209
+ await downloadBook(26592, {
210
+ outputFile: { path: './book.db' },
211
+ });
212
+
213
+ // Use pre-fetched metadata for efficiency
214
+ const bookMetadata = await getBookMetadata(26592);
215
+ await downloadBook(26592, {
216
+ bookMetadata,
217
+ outputFile: { path: './book.db' },
137
218
  });
138
219
  ```
139
220
 
140
221
  #### getBook
141
222
 
142
- Retrieves the data of a book as a JavaScript object.
223
+ Retrieves complete book data as a JavaScript object. This is a convenience function that handles temporary file creation and cleanup automatically.
143
224
 
144
225
  ```typescript
145
226
  getBook(id: number): Promise<BookData>
146
227
  ```
147
228
 
148
- - id: The ID of the book.
229
+ - `id`: The unique identifier of the book to retrieve
230
+
231
+ **Returns:** Promise that resolves to complete book data including pages and titles
149
232
 
150
- Example:
233
+ **Example:**
151
234
 
152
235
  ```javascript
153
236
  const bookData = await getBook(26592);
237
+ console.log(bookData.pages.length); // Number of pages in the book
238
+ console.log(bookData.titles?.length); // Number of title entries
239
+ console.log(bookData.pages[0].content); // Content of the first page
240
+ ```
241
+
242
+ #### getCoverUrl
243
+
244
+ Generates the URL for a book's cover image.
245
+
246
+ ```typescript
247
+ getCoverUrl(bookId: number): string
248
+ ```
249
+
250
+ - `bookId`: The unique identifier of the book
251
+
252
+ **Returns:** The complete URL to the book's cover image
253
+
254
+ **Example:**
255
+
256
+ ```javascript
257
+ const coverUrl = getCoverUrl(26592);
258
+ console.log(coverUrl); // "https://shamela.ws/covers/26592.jpg"
154
259
  ```
155
260
 
156
261
  ## Examples
@@ -161,21 +266,47 @@ const bookData = await getBook(26592);
161
266
  import { downloadMasterDatabase } from 'shamela';
162
267
 
163
268
  (async () => {
164
- await downloadMasterDatabase({
165
- outputFile: { path: './master.db' },
166
- });
269
+ try {
270
+ // Download as SQLite database
271
+ const dbPath = await downloadMasterDatabase({
272
+ outputFile: { path: './shamela_master.db' },
273
+ });
274
+ console.log(`Master database downloaded to: ${dbPath}`);
275
+
276
+ // Download as JSON for programmatic access
277
+ const jsonPath = await downloadMasterDatabase({
278
+ outputFile: { path: './shamela_master.json' },
279
+ });
280
+ console.log(`Master data exported to: ${jsonPath}`);
281
+ } catch (error) {
282
+ console.error('Error downloading master database:', error);
283
+ }
167
284
  })();
168
285
  ```
169
286
 
170
287
  ### Downloading a Book
171
288
 
172
289
  ```javascript
173
- import { downloadBook } from 'shamela';
290
+ import { downloadBook, getBookMetadata } from 'shamela';
174
291
 
175
292
  (async () => {
176
- await downloadBook(26592, {
177
- outputFile: { path: './book.db' },
178
- });
293
+ const bookId = 26592;
294
+
295
+ try {
296
+ // Download book as database file
297
+ await downloadBook(bookId, {
298
+ outputFile: { path: `./book_${bookId}.db` },
299
+ });
300
+
301
+ // Download with pre-fetched metadata
302
+ const metadata = await getBookMetadata(bookId);
303
+ await downloadBook(bookId, {
304
+ bookMetadata: metadata,
305
+ outputFile: { path: `./book_${bookId}.json` },
306
+ });
307
+ } catch (error) {
308
+ console.error('Error downloading book:', error);
309
+ }
179
310
  })();
180
311
  ```
181
312
 
@@ -185,23 +316,109 @@ import { downloadBook } from 'shamela';
185
316
  import { getBook } from 'shamela';
186
317
 
187
318
  (async () => {
188
- const bookData = await getBook(26592);
189
- console.log(bookData);
319
+ try {
320
+ const bookData = await getBook(26592);
321
+
322
+ console.log(`Book has ${bookData.pages.length} pages`);
323
+
324
+ if (bookData.titles) {
325
+ console.log(`Book has ${bookData.titles.length} titles/chapters`);
326
+
327
+ // Display table of contents
328
+ bookData.titles.forEach((title) => {
329
+ console.log(`${title.id}: ${title.content} (Page ${title.page})`);
330
+ });
331
+ }
332
+
333
+ // Access page content
334
+ const firstPage = bookData.pages[0];
335
+ console.log(`First page content: ${firstPage.content.substring(0, 100)}...`);
336
+ } catch (error) {
337
+ console.error('Error retrieving book:', error);
338
+ }
339
+ })();
340
+ ```
341
+
342
+ ### Getting Book Cover URLs
343
+
344
+ ```javascript
345
+ import { getCoverUrl, downloadMasterDatabase } from 'shamela';
346
+
347
+ (async () => {
348
+ try {
349
+ // Download master data to get book information
350
+ const masterData = await downloadMasterDatabase({
351
+ outputFile: { path: './master.json' },
352
+ });
353
+
354
+ // Read the master data
355
+ const data = await Bun.file('./master.json').json();
356
+
357
+ // Generate cover URLs for all books
358
+ data.books.forEach((book) => {
359
+ const coverUrl = getCoverUrl(book.id);
360
+ console.log(`${book.name}: ${coverUrl}`);
361
+ });
362
+ } catch (error) {
363
+ console.error('Error processing covers:', error);
364
+ }
190
365
  })();
191
366
  ```
192
367
 
368
+ ## Data Structures
369
+
370
+ The library provides comprehensive TypeScript types for all data structures:
371
+
372
+ ### BookData
373
+
374
+ - `pages`: Array of raw rows from the `page` table, including `content`, `id`, `part`, `page`, `number`, `services`, and `is_deleted`.
375
+ - `titles`: Array of raw rows from the `title` table with `content`, `id`, `page`, `parent`, and `is_deleted`.
376
+
377
+ ### MasterData
378
+
379
+ - `authors`: Raw entries from the `author` table with the original `biography`, `death_text`, `death_number`, `is_deleted`, and `name` fields.
380
+ - `books`: Raw entries from the `book` table containing the original metadata columns (`author`, `bibliography`, `category`, `date`, `hint`, `major_release`, `metadata`, `minor_release`, `pdf_links`, `printed`, `type`, and `is_deleted`).
381
+ - `categories`: Raw entries from the `category` table including `is_deleted`, `order`, and `name`.
382
+
383
+ ### Page
384
+
385
+ - `id`: Unique identifier.
386
+ - `content`: Text content of the page.
387
+ - `part`, `page`, `number`: Numeric references stored exactly as they appear in the source database.
388
+ - `services`: Optional metadata column from the source database.
389
+ - `is_deleted`: Flag indicating whether the page has been marked as deleted in Shamela updates.
390
+
391
+ ### Title
392
+
393
+ - `id`: Unique identifier.
394
+ - `content`: Title text.
395
+ - `page`: Page number where title appears (if available).
396
+ - `parent`: Optional parent title ID for hierarchical structure.
397
+ - `is_deleted`: Flag indicating whether the title has been marked as deleted.
398
+
399
+ ### Content helpers
400
+
401
+ - `parseContentRobust(content: string)`: Converts Shamela page HTML into a list of structured lines while preserving title markers and punctuation.
402
+ - `sanitizePageContent(content: string)`: Removes common footnote markers and normalises ligatures from Shamela pages.
403
+
193
404
  ## Testing
194
405
 
195
- The library includes tests to help you understand how the APIs are used. To run the tests, ensure you have the necessary environment variables set, then execute:
406
+ The library includes comprehensive tests. To run them, ensure you have the necessary environment variables set, then execute:
196
407
 
197
408
  ```bash
198
- npm run test
409
+ bun test
199
410
  ```
200
411
 
201
412
  For end-to-end tests:
202
413
 
203
414
  ```bash
204
- npm run e2e
415
+ bun run e2e
416
+ ```
417
+
418
+ For CI environment:
419
+
420
+ ```bash
421
+ bun run e2e:ci
205
422
  ```
206
423
 
207
424
  ## License