@hazeljs/pdf-to-audio 0.2.0-alpha.1

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 (55) hide show
  1. package/LICENSE +192 -0
  2. package/README.md +96 -0
  3. package/dist/index.d.ts +12 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +18 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.test.d.ts +2 -0
  8. package/dist/index.test.d.ts.map +1 -0
  9. package/dist/index.test.js +49 -0
  10. package/dist/index.test.js.map +1 -0
  11. package/dist/pdf-extractor.d.ts +5 -0
  12. package/dist/pdf-extractor.d.ts.map +1 -0
  13. package/dist/pdf-extractor.js +15 -0
  14. package/dist/pdf-extractor.js.map +1 -0
  15. package/dist/pdf-extractor.test.d.ts +2 -0
  16. package/dist/pdf-extractor.test.d.ts.map +1 -0
  17. package/dist/pdf-extractor.test.js +40 -0
  18. package/dist/pdf-extractor.test.js.map +1 -0
  19. package/dist/pdf-to-audio-queue.worker.d.ts +16 -0
  20. package/dist/pdf-to-audio-queue.worker.d.ts.map +1 -0
  21. package/dist/pdf-to-audio-queue.worker.js +103 -0
  22. package/dist/pdf-to-audio-queue.worker.js.map +1 -0
  23. package/dist/pdf-to-audio-queue.worker.test.d.ts +2 -0
  24. package/dist/pdf-to-audio-queue.worker.test.d.ts.map +1 -0
  25. package/dist/pdf-to-audio-queue.worker.test.js +24 -0
  26. package/dist/pdf-to-audio-queue.worker.test.js.map +1 -0
  27. package/dist/pdf-to-audio.controller.d.ts +10 -0
  28. package/dist/pdf-to-audio.controller.d.ts.map +1 -0
  29. package/dist/pdf-to-audio.controller.js +210 -0
  30. package/dist/pdf-to-audio.controller.js.map +1 -0
  31. package/dist/pdf-to-audio.controller.test.d.ts +2 -0
  32. package/dist/pdf-to-audio.controller.test.d.ts.map +1 -0
  33. package/dist/pdf-to-audio.controller.test.js +252 -0
  34. package/dist/pdf-to-audio.controller.test.js.map +1 -0
  35. package/dist/pdf-to-audio.module.d.ts +30 -0
  36. package/dist/pdf-to-audio.module.d.ts.map +1 -0
  37. package/dist/pdf-to-audio.module.js +66 -0
  38. package/dist/pdf-to-audio.module.js.map +1 -0
  39. package/dist/pdf-to-audio.module.test.d.ts +2 -0
  40. package/dist/pdf-to-audio.module.test.d.ts.map +1 -0
  41. package/dist/pdf-to-audio.module.test.js +50 -0
  42. package/dist/pdf-to-audio.module.test.js.map +1 -0
  43. package/dist/pdf-to-audio.service.d.ts +10 -0
  44. package/dist/pdf-to-audio.service.d.ts.map +1 -0
  45. package/dist/pdf-to-audio.service.js +132 -0
  46. package/dist/pdf-to-audio.service.js.map +1 -0
  47. package/dist/pdf-to-audio.service.test.d.ts +2 -0
  48. package/dist/pdf-to-audio.service.test.d.ts.map +1 -0
  49. package/dist/pdf-to-audio.service.test.js +146 -0
  50. package/dist/pdf-to-audio.service.test.js.map +1 -0
  51. package/dist/types.d.ts +16 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +3 -0
  54. package/dist/types.js.map +1 -0
  55. package/package.json +60 -0
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@hazeljs/core");
4
+ const pdf_to_audio_module_1 = require("./pdf-to-audio.module");
5
+ const pdf_to_audio_service_1 = require("./pdf-to-audio.service");
6
+ const pdf_to_audio_controller_1 = require("./pdf-to-audio.controller");
7
+ const pdf_to_audio_queue_worker_1 = require("./pdf-to-audio-queue.worker");
8
+ describe('PdfToAudioModule', () => {
9
+ it('should have correct module metadata', () => {
10
+ const metadata = (0, core_1.getModuleMetadata)(pdf_to_audio_module_1.PdfToAudioModule);
11
+ expect(metadata).toBeDefined();
12
+ expect(metadata?.providers).toBeDefined();
13
+ expect(metadata?.providers?.length).toBe(4);
14
+ expect(metadata?.providers).toContain(pdf_to_audio_service_1.PdfToAudioService);
15
+ expect(metadata?.providers).toContain(pdf_to_audio_queue_worker_1.PdfToAudioQueueWorker);
16
+ expect(metadata?.providers?.some((p) => typeof p === 'object' &&
17
+ p !== null &&
18
+ 'provide' in p &&
19
+ p.provide === pdf_to_audio_module_1.PDF_TO_AUDIO_OUTPUT_DIR)).toBe(true);
20
+ expect(metadata?.controllers).toBeDefined();
21
+ expect(metadata?.controllers?.length).toBe(1);
22
+ expect(metadata?.controllers).toContain(pdf_to_audio_controller_1.PdfToAudioController);
23
+ });
24
+ it('should include custom outputDir in forRoot config', () => {
25
+ const config = pdf_to_audio_module_1.PdfToAudioModule.forRoot({
26
+ connection: { host: 'localhost', port: 6379 },
27
+ outputDir: './custom/audio-output',
28
+ });
29
+ const outputDirProvider = config.providers?.find((p) => typeof p === 'object' &&
30
+ p !== null &&
31
+ 'provide' in p &&
32
+ p.provide === pdf_to_audio_module_1.PDF_TO_AUDIO_OUTPUT_DIR);
33
+ expect(outputDirProvider).toBeDefined();
34
+ expect(outputDirProvider?.useValue).toContain('custom');
35
+ expect(outputDirProvider?.useValue).toContain('audio-output');
36
+ });
37
+ it('should use default outputDir when not specified in forRoot', () => {
38
+ const config = pdf_to_audio_module_1.PdfToAudioModule.forRoot({
39
+ connection: { host: 'localhost', port: 6379 },
40
+ });
41
+ const outputDirProvider = config.providers?.find((p) => typeof p === 'object' &&
42
+ p !== null &&
43
+ 'provide' in p &&
44
+ p.provide === pdf_to_audio_module_1.PDF_TO_AUDIO_OUTPUT_DIR);
45
+ expect(outputDirProvider).toBeDefined();
46
+ expect(outputDirProvider?.useValue).toContain('data');
47
+ expect(outputDirProvider?.useValue).toContain('pdf-to-audio');
48
+ });
49
+ });
50
+ //# sourceMappingURL=pdf-to-audio.module.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-to-audio.module.test.js","sourceRoot":"","sources":["../src/pdf-to-audio.module.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { OpenAIProvider } from '@hazeljs/ai';
2
+ import type { PdfToAudioOptions } from './types';
3
+ export declare class PdfToAudioService {
4
+ private readonly aiProvider;
5
+ private splitter;
6
+ constructor(aiProvider: OpenAIProvider);
7
+ convert(pdfBuffer: Buffer, options?: PdfToAudioOptions, onProgress?: (completed: number, total: number, phase?: string) => void): Promise<Buffer>;
8
+ private generateSummary;
9
+ }
10
+ //# sourceMappingURL=pdf-to-audio.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-to-audio.service.d.ts","sourceRoot":"","sources":["../src/pdf-to-audio.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAuBjD,qBACa,iBAAiB;IAOhB,OAAO,CAAC,QAAQ,CAAC,UAAU;IANvC,OAAO,CAAC,QAAQ,CAIb;gBAE0B,UAAU,EAAE,cAAc;IAEjD,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,iBAAiB,EAC3B,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,GACtE,OAAO,CAAC,MAAM,CAAC;YAyEJ,eAAe;CA2B9B"}
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PdfToAudioService = void 0;
13
+ const core_1 = require("@hazeljs/core");
14
+ const ai_1 = require("@hazeljs/ai");
15
+ const rag_1 = require("@hazeljs/rag");
16
+ const pdf_extractor_1 = require("./pdf-extractor");
17
+ /** Chunk size matching OpenAI TTS character limit */
18
+ const TTS_CHUNK_SIZE = 4096;
19
+ /** Max chars to send for summary (to stay within token limits) */
20
+ const SUMMARY_CONTEXT_LIMIT = 12000;
21
+ /** Ensure text chunks never exceed TTS limit (splitter can produce larger chunks) */
22
+ function enforceChunkSize(chunks) {
23
+ const result = [];
24
+ for (const chunk of chunks) {
25
+ if (chunk.length <= TTS_CHUNK_SIZE) {
26
+ result.push(chunk);
27
+ }
28
+ else {
29
+ for (let i = 0; i < chunk.length; i += TTS_CHUNK_SIZE) {
30
+ result.push(chunk.slice(i, i + TTS_CHUNK_SIZE));
31
+ }
32
+ }
33
+ }
34
+ return result;
35
+ }
36
+ let PdfToAudioService = class PdfToAudioService {
37
+ constructor(aiProvider) {
38
+ this.aiProvider = aiProvider;
39
+ this.splitter = new rag_1.RecursiveTextSplitter({
40
+ chunkSize: TTS_CHUNK_SIZE,
41
+ chunkOverlap: 0,
42
+ separators: ['\n\n', '\n', '. ', ' ', ''],
43
+ });
44
+ }
45
+ async convert(pdfBuffer, options, onProgress) {
46
+ core_1.logger.info('PdfToAudioService.convert: extracting text');
47
+ onProgress?.(0, 0, 'extracting');
48
+ const text = await (0, pdf_extractor_1.extractText)(pdfBuffer);
49
+ if (!text || !text.trim()) {
50
+ throw new Error('PDF contains no extractable text');
51
+ }
52
+ const speech = this.aiProvider.speech?.bind(this.aiProvider);
53
+ if (!speech) {
54
+ throw new Error('AI provider does not support TTS (speech)');
55
+ }
56
+ const voice = options?.voice || 'alloy';
57
+ const model = options?.model || 'tts-1';
58
+ const format = options?.format || 'mp3';
59
+ const includeSummary = options?.includeSummary !== false;
60
+ const summaryOnly = options?.summaryOnly === true;
61
+ const audioBuffers = [];
62
+ let completedChunks = 0;
63
+ if (includeSummary || summaryOnly) {
64
+ core_1.logger.info('PdfToAudioService.convert: generating summary');
65
+ onProgress?.(0, 0, 'summarizing');
66
+ }
67
+ const summaryText = includeSummary || summaryOnly ? await this.generateSummary(text, summaryOnly) : '';
68
+ const summaryChunks = summaryText
69
+ ? enforceChunkSize(this.splitter
70
+ .split(summaryText)
71
+ .map((c) => c.trim())
72
+ .filter(Boolean))
73
+ : [];
74
+ const docChunks = summaryOnly
75
+ ? []
76
+ : enforceChunkSize(this.splitter
77
+ .split(text)
78
+ .map((c) => c.trim())
79
+ .filter(Boolean));
80
+ const totalChunks = summaryChunks.length + docChunks.length;
81
+ onProgress?.(0, totalChunks, 'converting');
82
+ core_1.logger.info(`PdfToAudioService.convert: ${totalChunks} chunks to process (summaryOnly=${summaryOnly})`);
83
+ if (summaryChunks.length > 0) {
84
+ for (const chunk of summaryChunks) {
85
+ const audioBuffer = await speech(chunk, { voice, model, format });
86
+ audioBuffers.push(audioBuffer);
87
+ completedChunks++;
88
+ onProgress?.(completedChunks, totalChunks);
89
+ }
90
+ }
91
+ for (const chunk of docChunks) {
92
+ const audioBuffer = await speech(chunk, { voice, model, format });
93
+ audioBuffers.push(audioBuffer);
94
+ completedChunks++;
95
+ onProgress?.(completedChunks, totalChunks);
96
+ }
97
+ if (audioBuffers.length === 0) {
98
+ throw new Error('No audio chunks were generated');
99
+ }
100
+ return Buffer.concat(audioBuffers);
101
+ }
102
+ async generateSummary(text, summaryOnly = false) {
103
+ const context = text.length > SUMMARY_CONTEXT_LIMIT ? text.slice(0, SUMMARY_CONTEXT_LIMIT) + '...' : text;
104
+ const response = await this.aiProvider.complete({
105
+ messages: [
106
+ {
107
+ role: 'system',
108
+ content: 'You write brief document summaries for audio narration. Output 2-4 concise sentences that introduce the document. Preserve the source language—do not translate. Write in a natural, spoken style. No markdown or formatting.',
109
+ },
110
+ {
111
+ role: 'user',
112
+ content: `Summarize this document for an audio introduction. Keep the same language as the document:\n\n${context}`,
113
+ },
114
+ ],
115
+ model: 'gpt-4o-mini',
116
+ maxTokens: 150,
117
+ temperature: 0.5,
118
+ });
119
+ const summary = response.content?.trim();
120
+ if (!summary)
121
+ return '';
122
+ return summaryOnly
123
+ ? `Document summary. ${summary}`
124
+ : `Document summary. ${summary} Now, the full document.`;
125
+ }
126
+ };
127
+ exports.PdfToAudioService = PdfToAudioService;
128
+ exports.PdfToAudioService = PdfToAudioService = __decorate([
129
+ (0, core_1.Service)(),
130
+ __metadata("design:paramtypes", [ai_1.OpenAIProvider])
131
+ ], PdfToAudioService);
132
+ //# sourceMappingURL=pdf-to-audio.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-to-audio.service.js","sourceRoot":"","sources":["../src/pdf-to-audio.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,wCAAgD;AAChD,oCAA6C;AAC7C,sCAAqD;AACrD,mDAA8C;AAG9C,qDAAqD;AACrD,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,kEAAkE;AAClE,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAEpC,qFAAqF;AACrF,SAAS,gBAAgB,CAAC,MAAgB;IACxC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,MAAM,IAAI,cAAc,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,cAAc,EAAE,CAAC;gBACtD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAGM,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAO5B,YAA6B,UAA0B;QAA1B,eAAU,GAAV,UAAU,CAAgB;QAN/C,aAAQ,GAAG,IAAI,2BAAqB,CAAC;YAC3C,SAAS,EAAE,cAAc;YACzB,YAAY,EAAE,CAAC;YACf,UAAU,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;SAC1C,CAAC,CAAC;IAEuD,CAAC;IAE3D,KAAK,CAAC,OAAO,CACX,SAAiB,EACjB,OAA2B,EAC3B,UAAuE;QAEvE,aAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QAC1D,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,IAAA,2BAAW,EAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACtD,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7D,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,OAAO,CAAC;QACxC,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,OAAO,CAAC;QACxC,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC;QACxC,MAAM,cAAc,GAAG,OAAO,EAAE,cAAc,KAAK,KAAK,CAAC;QACzD,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;QAElD,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,IAAI,eAAe,GAAG,CAAC,CAAC;QAExB,IAAI,cAAc,IAAI,WAAW,EAAE,CAAC;YAClC,aAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;YAC7D,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,WAAW,GACf,cAAc,IAAI,WAAW,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACrF,MAAM,aAAa,GAAG,WAAW;YAC/B,CAAC,CAAC,gBAAgB,CACd,IAAI,CAAC,QAAQ;iBACV,KAAK,CAAC,WAAW,CAAC;iBAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;iBACpB,MAAM,CAAC,OAAO,CAAC,CACnB;YACH,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,SAAS,GAAG,WAAW;YAC3B,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,gBAAgB,CACd,IAAI,CAAC,QAAQ;iBACV,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;iBACpB,MAAM,CAAC,OAAO,CAAC,CACnB,CAAC;QACN,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;QAE5D,UAAU,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;QAC3C,aAAM,CAAC,IAAI,CACT,8BAA8B,WAAW,mCAAmC,WAAW,GAAG,CAC3F,CAAC;QAEF,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;gBAClC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;gBAClE,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBAC/B,eAAe,EAAE,CAAC;gBAClB,UAAU,EAAE,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;YAC9B,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YAClE,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC/B,eAAe,EAAE,CAAC;YAClB,UAAU,EAAE,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;QAC7C,CAAC;QAED,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IACrC,CAAC;IAEO,KAAK,CAAC,eAAe,CAAC,IAAY,EAAE,WAAW,GAAG,KAAK;QAC7D,MAAM,OAAO,GACX,IAAI,CAAC,MAAM,GAAG,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QAE5F,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAC9C,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,QAAQ;oBACd,OAAO,EACL,+NAA+N;iBAClO;gBACD;oBACE,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE,iGAAiG,OAAO,EAAE;iBACpH;aACF;YACD,KAAK,EAAE,aAAa;YACpB,SAAS,EAAE,GAAG;YACd,WAAW,EAAE,GAAG;SACjB,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QACxB,OAAO,WAAW;YAChB,CAAC,CAAC,qBAAqB,OAAO,EAAE;YAChC,CAAC,CAAC,qBAAqB,OAAO,0BAA0B,CAAC;IAC7D,CAAC;CACF,CAAA;AAjHY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,cAAO,GAAE;qCAQiC,mBAAc;GAP5C,iBAAiB,CAiH7B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pdf-to-audio.service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-to-audio.service.test.d.ts","sourceRoot":"","sources":["../src/pdf-to-audio.service.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const pdf_to_audio_service_1 = require("./pdf-to-audio.service");
4
+ const mockExtractText = jest.fn();
5
+ jest.mock('./pdf-extractor', () => ({
6
+ extractText: (buffer) => mockExtractText(buffer),
7
+ }));
8
+ describe('PdfToAudioService', () => {
9
+ let mockSpeech;
10
+ let mockComplete;
11
+ let service;
12
+ beforeEach(() => {
13
+ jest.clearAllMocks();
14
+ mockSpeech = jest.fn().mockResolvedValue(Buffer.from('audio-chunk'));
15
+ mockComplete = jest.fn().mockResolvedValue({
16
+ content: 'This document covers important topics.',
17
+ });
18
+ const mockAiProvider = {
19
+ speech: mockSpeech,
20
+ complete: mockComplete,
21
+ };
22
+ service = new pdf_to_audio_service_1.PdfToAudioService(mockAiProvider);
23
+ });
24
+ describe('convert', () => {
25
+ it('should convert PDF to audio with document chunks', async () => {
26
+ mockExtractText.mockResolvedValue('Short document text.');
27
+ const result = await service.convert(Buffer.from('pdf'));
28
+ expect(mockExtractText).toHaveBeenCalledWith(Buffer.from('pdf'));
29
+ expect(mockSpeech).toHaveBeenCalled();
30
+ expect(Buffer.isBuffer(result)).toBe(true);
31
+ expect(result.length).toBeGreaterThan(0);
32
+ });
33
+ it('should include summary when includeSummary is true (default)', async () => {
34
+ mockExtractText.mockResolvedValue('Document content here.');
35
+ await service.convert(Buffer.from('pdf'));
36
+ expect(mockComplete).toHaveBeenCalledWith(expect.objectContaining({
37
+ messages: expect.arrayContaining([
38
+ expect.objectContaining({ role: 'system' }),
39
+ expect.objectContaining({
40
+ role: 'user',
41
+ content: expect.stringContaining('Document content'),
42
+ }),
43
+ ]),
44
+ model: 'gpt-4o-mini',
45
+ maxTokens: 150,
46
+ temperature: 0.5,
47
+ }));
48
+ expect(mockSpeech).toHaveBeenCalledWith(expect.stringContaining('Document summary'), expect.any(Object));
49
+ });
50
+ it('should skip summary when includeSummary is false', async () => {
51
+ mockExtractText.mockResolvedValue('Document content.');
52
+ await service.convert(Buffer.from('pdf'), { includeSummary: false });
53
+ expect(mockComplete).not.toHaveBeenCalled();
54
+ expect(mockSpeech).toHaveBeenCalledWith('Document content.', expect.any(Object));
55
+ });
56
+ it('should output only summary when summaryOnly is true', async () => {
57
+ mockExtractText.mockResolvedValue('Full document content here.');
58
+ mockComplete.mockResolvedValue({ content: 'This doc covers X, Y, Z.' });
59
+ await service.convert(Buffer.from('pdf'), { summaryOnly: true });
60
+ expect(mockComplete).toHaveBeenCalled();
61
+ expect(mockSpeech).toHaveBeenCalledTimes(1);
62
+ expect(mockSpeech).toHaveBeenCalledWith(expect.stringMatching(/^Document summary\. This doc covers X, Y, Z\.$/), expect.any(Object));
63
+ expect(mockSpeech).not.toHaveBeenCalledWith('Full document content here.', expect.any(Object));
64
+ });
65
+ it('should use custom voice and model options', async () => {
66
+ mockExtractText.mockResolvedValue('Text');
67
+ await service.convert(Buffer.from('pdf'), {
68
+ voice: 'nova',
69
+ model: 'tts-1-hd',
70
+ format: 'opus',
71
+ includeSummary: false,
72
+ });
73
+ expect(mockSpeech).toHaveBeenCalledWith('Text', expect.objectContaining({
74
+ voice: 'nova',
75
+ model: 'tts-1-hd',
76
+ format: 'opus',
77
+ }));
78
+ });
79
+ it('should throw when PDF has no extractable text', async () => {
80
+ mockExtractText.mockResolvedValue('');
81
+ await expect(service.convert(Buffer.from('pdf'))).rejects.toThrow('PDF contains no extractable text');
82
+ });
83
+ it('should throw when PDF has only whitespace', async () => {
84
+ mockExtractText.mockResolvedValue(' \n\t ');
85
+ await expect(service.convert(Buffer.from('pdf'))).rejects.toThrow('PDF contains no extractable text');
86
+ });
87
+ it('should throw when AI provider has no speech method', async () => {
88
+ const serviceNoSpeech = new pdf_to_audio_service_1.PdfToAudioService({ complete: mockComplete });
89
+ mockExtractText.mockResolvedValue('Text');
90
+ await expect(serviceNoSpeech.convert(Buffer.from('pdf'))).rejects.toThrow('AI provider does not support TTS (speech)');
91
+ });
92
+ it('should truncate long text for summary context', async () => {
93
+ const longText = 'a'.repeat(15000);
94
+ mockExtractText.mockResolvedValue(longText);
95
+ await service.convert(Buffer.from('pdf'));
96
+ const userMessage = mockComplete.mock.calls[0][0].messages.find((m) => m.role === 'user');
97
+ expect(userMessage.content).toContain('Summarize this document');
98
+ expect(userMessage.content).toContain('...');
99
+ expect(userMessage.content.length).toBeLessThanOrEqual(12000 + 100);
100
+ });
101
+ it('should handle empty summary from AI', async () => {
102
+ mockComplete.mockResolvedValueOnce({ content: '' });
103
+ mockExtractText.mockResolvedValue('Document text.');
104
+ const result = await service.convert(Buffer.from('pdf'));
105
+ expect(mockSpeech).toHaveBeenCalledWith('Document text.', expect.any(Object));
106
+ expect(Buffer.isBuffer(result)).toBe(true);
107
+ });
108
+ it('should split long document into multiple TTS chunks', async () => {
109
+ const longText = 'Word. '.repeat(1000);
110
+ mockExtractText.mockResolvedValue(longText);
111
+ await service.convert(Buffer.from('pdf'), { includeSummary: false });
112
+ expect(mockSpeech.mock.calls.length).toBeGreaterThan(1);
113
+ });
114
+ it('should propagate extractText errors', async () => {
115
+ mockExtractText.mockRejectedValue(new Error('PDF parse failed'));
116
+ await expect(service.convert(Buffer.from('pdf'))).rejects.toThrow('PDF parse failed');
117
+ });
118
+ it('should propagate speech errors', async () => {
119
+ mockExtractText.mockResolvedValue('Text');
120
+ mockSpeech.mockRejectedValue(new Error('TTS API error'));
121
+ await expect(service.convert(Buffer.from('pdf'), { includeSummary: false })).rejects.toThrow('TTS API error');
122
+ });
123
+ it('should call onProgress callback with completed and total', async () => {
124
+ mockExtractText.mockResolvedValue('Chunk one. Chunk two.');
125
+ const onProgress = jest.fn();
126
+ await service.convert(Buffer.from('pdf'), { includeSummary: false }, onProgress);
127
+ expect(onProgress).toHaveBeenCalled();
128
+ const lastCall = onProgress.mock.calls[onProgress.mock.calls.length - 1];
129
+ expect(lastCall[0]).toBe(lastCall[1]); // completed === total at end
130
+ expect(lastCall[1]).toBeGreaterThan(0);
131
+ });
132
+ it('should throw when no audio chunks generated (summaryOnly + empty summary)', async () => {
133
+ mockExtractText.mockResolvedValue('.');
134
+ mockComplete.mockResolvedValue({ content: '' });
135
+ await expect(service.convert(Buffer.from('pdf'), { summaryOnly: true })).rejects.toThrow('No audio chunks were generated');
136
+ });
137
+ it('should pass summaryOnly to generateSummary for different suffix', async () => {
138
+ mockExtractText.mockResolvedValue('Doc.');
139
+ mockComplete.mockResolvedValue({ content: 'Brief overview.' });
140
+ await service.convert(Buffer.from('pdf'), { summaryOnly: true });
141
+ expect(mockSpeech).toHaveBeenCalledWith('Document summary. Brief overview.', expect.any(Object));
142
+ expect(mockSpeech).not.toHaveBeenCalledWith(expect.stringContaining('Now, the full document'), expect.any(Object));
143
+ });
144
+ });
145
+ });
146
+ //# sourceMappingURL=pdf-to-audio.service.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-to-audio.service.test.js","sourceRoot":"","sources":["../src/pdf-to-audio.service.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * PDF-to-Audio conversion options
3
+ */
4
+ export interface PdfToAudioOptions {
5
+ /** Voice for TTS (e.g. alloy, echo, fable, onyx, nova, shimmer) */
6
+ voice?: string;
7
+ /** TTS model (e.g. tts-1, tts-1-hd) */
8
+ model?: string;
9
+ /** Output format: mp3 or opus */
10
+ format?: 'mp3' | 'opus';
11
+ /** Include an AI-generated document summary at the start of the audio (default: true) */
12
+ includeSummary?: boolean;
13
+ /** When true, output only the summary—do not read the full document (default: false) */
14
+ summaryOnly?: boolean;
15
+ }
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,yFAAyF;IACzF,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wFAAwF;IACxF,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@hazeljs/pdf-to-audio",
3
+ "version": "0.2.0-alpha.1",
4
+ "description": "Convert PDF documents to audio using TTS - PDF text extraction, chunking, and OpenAI TTS",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "clean": "rm -rf dist",
13
+ "lint": "eslint src --ext .ts",
14
+ "lint:fix": "eslint src --ext .ts --fix",
15
+ "test": "jest --coverage --passWithNoTests",
16
+ "test:watch": "jest --watch"
17
+ },
18
+ "keywords": [
19
+ "hazeljs",
20
+ "pdf",
21
+ "audio",
22
+ "tts",
23
+ "text-to-speech",
24
+ "pdf-to-audio"
25
+ ],
26
+ "author": "Muhammad Arslan <muhammad.arslan@hazeljs.com>",
27
+ "license": "Apache-2.0",
28
+ "bugs": {
29
+ "url": "https://github.com/hazeljs/hazel-js/issues"
30
+ },
31
+ "homepage": "https://hazeljs.com",
32
+ "dependencies": {
33
+ "bullmq": "^5.34.0",
34
+ "pdf-parse": "^1.1.1",
35
+ "reflect-metadata": "^0.2.1"
36
+ },
37
+ "peerDependencies": {
38
+ "@hazeljs/ai": ">=0.2.0-beta.0",
39
+ "@hazeljs/core": ">=0.2.0-beta.0",
40
+ "@hazeljs/queue": ">=0.2.0-beta.0",
41
+ "@hazeljs/rag": ">=0.2.0-beta.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/jest": "^29.5.11",
45
+ "@types/node": "^20.17.50",
46
+ "@types/pdf-parse": "^1.1.4",
47
+ "jest": "^29.7.0",
48
+ "ts-jest": "^29.1.2",
49
+ "typescript": "^5.3.3"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "https://github.com/hazel-js/hazeljs.git",
57
+ "directory": "packages/pdf-to-audio"
58
+ },
59
+ "gitHead": "cbc5ee2c12ced28fd0576faf13c5f078c1e8421e"
60
+ }