srtmanager 0.1.0__tar.gz

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.
@@ -0,0 +1,613 @@
1
+ Metadata-Version: 2.3
2
+ Name: srtmanager
3
+ Version: 0.1.0
4
+ Summary: Advanced, expressive, production-safe subtitle manager for `.srt` files.
5
+ Description-Content-Type: text/markdown
6
+
7
+ # 🎬 SRTManager
8
+
9
+ **Advanced, expressive, production-safe subtitle manager for `.srt` files.**
10
+
11
+ `SRTManager` is a high-level abstraction built on top of the `srt` Python library that enforces strict subtitle invariants while providing powerful transformation utilities like shifting, slicing, scaling, merging, gap compression, content mapping, and more.
12
+
13
+ ---
14
+
15
+ ## ✨ Features
16
+
17
+ * ✅ Automatic sorting & reindexing
18
+ * ✅ Overlap detection
19
+ * ✅ Timestamp validation (no negative times)
20
+ * ✅ Immutable-style transformations
21
+ * ✅ Shift subtitles forward/backward
22
+ * ✅ Merge subtitle files safely
23
+ * ✅ Clip/slice by time range
24
+ * ✅ Search by text
25
+ * ✅ Split & join subtitle segments
26
+ * ✅ Gap compression
27
+ * ✅ Duration scaling
28
+ * ✅ Functional content transformations
29
+
30
+ ---
31
+
32
+ ## 📦 Installation
33
+
34
+ ```bash
35
+ pip install srtmanager
36
+ ```
37
+
38
+ Then place `SRTManager` in your project.
39
+
40
+ ---
41
+
42
+ ## 🧠 Core Invariants
43
+
44
+ Every `SRTManager` instance guarantees:
45
+
46
+ 1. Subtitles are sorted by start time
47
+ 2. Subtitles are sequentially indexed from `1`
48
+ 3. No overlapping subtitles
49
+ 4. All timestamps are ≥ 0
50
+
51
+ If any invariant is violated:
52
+
53
+ ```python
54
+ SRTValidationError
55
+ ```
56
+
57
+ is raised.
58
+
59
+ ---
60
+
61
+ ## 🚀 Quick Start
62
+
63
+ ```python
64
+ from srtmanager import SRTManager
65
+
66
+ # Load from file
67
+ subs = SRTManager.from_file("input.srt")
68
+
69
+ # Shift forward by 2 seconds
70
+ shifted = subs >> 2
71
+
72
+ # Search for text
73
+ hello_lines = subs["hello"]
74
+
75
+ # Slice between 10s and 30s
76
+ clip = subs[10:30]
77
+
78
+ # Save result
79
+ shifted.save("output.srt")
80
+ ```
81
+
82
+ ---
83
+
84
+ # 📚 API Documentation
85
+
86
+ ---
87
+
88
+ ## 🔹 Constructors
89
+
90
+ ### `SRTManager.from_file(path: str)`
91
+
92
+ Load subtitles from `.srt` file.
93
+
94
+ ```python
95
+ subs = SRTManager.from_file("movie.srt")
96
+ ```
97
+
98
+ ---
99
+
100
+ ### `SRTManager.from_string(raw: str)`
101
+
102
+ Parse subtitles from raw string.
103
+
104
+ ---
105
+
106
+ ## 🔹 Basic Properties
107
+
108
+ ### `len(manager)`
109
+
110
+ Returns number of subtitles.
111
+
112
+ ### `manager.start`
113
+
114
+ Start timestamp of first subtitle.
115
+
116
+ ### `manager.end`
117
+
118
+ End timestamp of last subtitle.
119
+
120
+ ### `manager.duration`
121
+
122
+ Total duration (`end - start`).
123
+
124
+ You can also **scale duration**:
125
+
126
+ ```python
127
+ subs.duration = 120 # scale entire timeline to 120 seconds
128
+ ```
129
+
130
+ ---
131
+
132
+ ## 🔹 Time Shifting
133
+
134
+ ### `shift(seconds: float) -> SRTManager`
135
+
136
+ ```python
137
+ new_subs = subs.shift(2.5)
138
+ ```
139
+
140
+ ### Operator Overloads
141
+
142
+ ```python
143
+ subs >> 2 # shift forward 2 seconds
144
+ subs << 1 # shift backward 1 second
145
+ ```
146
+
147
+ ---
148
+
149
+ ## 🔹 Merge
150
+
151
+ ```python
152
+ combined = subs1 + subs2
153
+ ```
154
+
155
+ * Automatically shifts second file to avoid overlap.
156
+ * Maintains invariants.
157
+
158
+ ---
159
+
160
+ ## 🔹 Slice (Time Clipping)
161
+
162
+ ```python
163
+ clip = subs.slice(10, 30)
164
+ # or
165
+ clip = subs[10:30]
166
+ ```
167
+
168
+ * Clips subtitles within range
169
+ * Adjusts start/end if partially overlapping
170
+
171
+ ---
172
+
173
+ ## 🔹 Find (Search)
174
+
175
+ ```python
176
+ results = subs.find("hello")
177
+ results = subs.find("HELLO", case_sensitive=True)
178
+
179
+ # Shortcut
180
+ results = subs["hello"]
181
+ ```
182
+
183
+ Returns new `SRTManager` containing matches.
184
+
185
+ ---
186
+
187
+ ## 🔹 Split
188
+
189
+ Split subtitles using delimiter:
190
+
191
+ ```python
192
+ parts = subs.split("<line>")
193
+ ```
194
+
195
+ Returns list of `SRTManager` instances.
196
+
197
+ ---
198
+
199
+ ## 🔹 Join as Single Subtitle
200
+
201
+ ```python
202
+ single = subs.join_as_single()
203
+ ```
204
+
205
+ Returns one merged `srt.Subtitle`.
206
+
207
+ ---
208
+
209
+ ## 🔹 Gap Compression
210
+
211
+ Removes silent gaps between subtitles while preserving durations:
212
+
213
+ ```python
214
+ tight = subs.compress_gaps()
215
+ ```
216
+
217
+ ---
218
+
219
+ ## 🔹 Content Transformation
220
+
221
+ Functional style mapping:
222
+
223
+ ```python
224
+ upper = subs.map_content(lambda text: text.upper())
225
+ ```
226
+
227
+ ---
228
+
229
+ ## 🔹 Plain Text Export
230
+
231
+ ```python
232
+ text = subs.to_plain_text()
233
+ ```
234
+
235
+ ---
236
+
237
+ ## 🔹 Save
238
+
239
+ ```python
240
+ subs.save("output.srt")
241
+ ```
242
+
243
+ ---
244
+
245
+ ## 🔹 Add Raw Subtitles (Validated)
246
+
247
+ ```python
248
+ subs.add_raw(new_subtitles)
249
+ ```
250
+
251
+ Re-validates:
252
+
253
+ * Sorting
254
+ * Indexing
255
+ * Overlaps
256
+
257
+ Raises `SRTValidationError` if invalid.
258
+
259
+ ---
260
+
261
+ # 🛡 Error Handling
262
+
263
+ ### `SRTValidationError`
264
+
265
+ Raised when:
266
+
267
+ * Negative timestamps
268
+ * End before start
269
+ * Overlapping subtitles
270
+
271
+ Example:
272
+
273
+ ```python
274
+ try:
275
+ subs = SRTManager(invalid_subtitles)
276
+ except SRTValidationError as e:
277
+ print("Invalid SRT:", e)
278
+ ```
279
+
280
+ ---
281
+
282
+ # 🧩 Design Philosophy
283
+
284
+ This library follows:
285
+
286
+ * **Immutability-first transformations**
287
+ * **Strong invariants**
288
+ * **Operator overloading for expressiveness**
289
+ * **Functional programming patterns**
290
+ * **Production-safe validation**
291
+
292
+ ---
293
+
294
+ # 🔮 Example Workflow
295
+
296
+ ```python
297
+ subs = (
298
+ SRTManager.from_file("raw.srt")
299
+ .shift(1.2)
300
+ .map_content(str.strip)
301
+ .compress_gaps()
302
+ )
303
+
304
+ subs.save("cleaned.srt")
305
+ ```
306
+
307
+ ---
308
+
309
+ # 📌 Ideal Use Cases
310
+
311
+ * Subtitle synchronization
312
+ * Post-processing AI-generated subtitles
313
+ * Timeline alignment
314
+ * Subtitle merging pipelines
315
+ * Research projects
316
+ * Video automation tools
317
+
318
+ ---
319
+
320
+ # 🧠 Author Notes
321
+
322
+ Built for developers who want:
323
+
324
+ * Clean abstraction
325
+ * No silent timeline corruption
326
+ * Predictable transformations
327
+ * Composable subtitle workflows
328
+
329
+
330
+
331
+
332
+ Below are **advanced, production-level usage patterns** for your `SRTManager` implementation
333
+
334
+ These go beyond basic examples and show how to build real subtitle pipelines.
335
+
336
+ ---
337
+
338
+ # 🔥 Advanced Usage Examples
339
+
340
+ ---
341
+
342
+ # 1️⃣ Auto-Sync Two Subtitle Files
343
+
344
+ ### 🎯 Problem
345
+
346
+ You have:
347
+
348
+ * `dialogue.srt`
349
+ * `translated.srt` (starts earlier)
350
+
351
+ You want to align them safely and merge.
352
+
353
+ ```python
354
+ from srtmanager import SRTManager
355
+
356
+ dialogue = SRTManager.from_file("dialogue.srt")
357
+ translated = SRTManager.from_file("translated.srt")
358
+
359
+ # Align translated to dialogue start
360
+ offset = (dialogue.start - translated.start).total_seconds()
361
+ translated_aligned = translated.shift(offset)
362
+
363
+ # Merge safely (no overlap corruption)
364
+ final = dialogue + translated_aligned
365
+
366
+ final.save("merged.srt")
367
+ ```
368
+
369
+ ✔ Automatically prevents overlap
370
+ ✔ Keeps indexes clean
371
+ ✔ Guarantees invariants
372
+
373
+ ---
374
+
375
+ # 2️⃣ Normalize AI-Generated Subtitles
376
+
377
+ ### 🎯 Problem
378
+
379
+ AI subtitles often contain:
380
+
381
+ * Extra spaces
382
+ * Broken casing
383
+ * Random gaps
384
+
385
+ ### 🧠 Clean Pipeline
386
+
387
+ ```python
388
+ def clean_text(text: str) -> str:
389
+ return " ".join(text.strip().split()).capitalize()
390
+
391
+ subs = (
392
+ SRTManager.from_file("ai_output.srt")
393
+ .map_content(clean_text)
394
+ .compress_gaps()
395
+ )
396
+
397
+ subs.save("clean_ai_output.srt")
398
+ ```
399
+
400
+ ✔ Removes weird spacing
401
+ ✔ Fixes formatting
402
+ ✔ Eliminates timing gaps
403
+
404
+ ---
405
+
406
+ # 3️⃣ Clip Scene + Retime to New Duration
407
+
408
+ ### 🎯 Problem
409
+
410
+ You want only the segment from 60s–120s
411
+ Then stretch it to exactly 30 seconds.
412
+
413
+ ```python
414
+ clip = SRTManager.from_file("movie.srt")[60:120]
415
+
416
+ # Scale duration to 30 seconds
417
+ clip.duration = 30
418
+
419
+ clip.save("scene_shortened.srt")
420
+ ```
421
+
422
+ ✔ Automatically scales proportionally
423
+ ✔ Keeps relative timing intact
424
+
425
+ ---
426
+
427
+ # 4️⃣ Subtitle Chunking (Episode Segmentation)
428
+
429
+ ### 🎯 Problem
430
+
431
+ You use `<line>` as delimiter to mark chapter breaks.
432
+
433
+ ```python
434
+ subs = SRTManager.from_file("lecture.srt")
435
+
436
+ parts = subs.split("<line>")
437
+
438
+ for i, part in enumerate(parts, 1):
439
+ part.save(f"chapter_{i}.srt")
440
+ ```
441
+
442
+ ✔ Clean segmentation
443
+ ✔ Maintains proper indexing
444
+
445
+ ---
446
+
447
+ # 5️⃣ Build a Search Engine on Subtitles
448
+
449
+ ### 🎯 Problem
450
+
451
+ Extract all dialogue containing a keyword.
452
+
453
+ ```python
454
+ subs = SRTManager.from_file("podcast.srt")
455
+
456
+ crypto_mentions = subs["blockchain"]
457
+
458
+ print(crypto_mentions.to_plain_text())
459
+ ```
460
+
461
+ ✔ Returns only matching subtitles
462
+ ✔ Keeps original timestamps
463
+
464
+ ---
465
+
466
+ # 6️⃣ Remove Silence but Keep Flow
467
+
468
+ ### 🎯 Problem
469
+
470
+ You want subtitles to play continuously without silent gaps.
471
+
472
+ ```python
473
+ tight = (
474
+ SRTManager.from_file("documentary.srt")
475
+ .compress_gaps()
476
+ )
477
+
478
+ tight.save("no_silence.srt")
479
+ ```
480
+
481
+ Original:
482
+
483
+ ```
484
+ 00:00:01 → 00:00:03
485
+ 00:00:10 → 00:00:12
486
+ ```
487
+
488
+ Compressed:
489
+
490
+ ```
491
+ 00:00:01 → 00:00:03
492
+ 00:00:03 → 00:00:05
493
+ ```
494
+
495
+ ✔ Keeps duration of each subtitle
496
+ ✔ Removes dead air
497
+
498
+ ---
499
+
500
+ # 7️⃣ Subtitle Stitching (Multiple Episodes → Movie Cut)
501
+
502
+ ### 🎯 Problem
503
+
504
+ Combine multiple SRT files into one continuous timeline.
505
+
506
+ ```python
507
+ ep1 = SRTManager.from_file("ep1.srt")
508
+ ep2 = SRTManager.from_file("ep2.srt")
509
+ ep3 = SRTManager.from_file("ep3.srt")
510
+
511
+ movie_cut = ep1 + ep2 + ep3
512
+
513
+ movie_cut.save("full_movie.srt")
514
+ ```
515
+
516
+ ✔ Automatically offsets later files
517
+ ✔ No overlap errors
518
+
519
+ ---
520
+
521
+ # 8️⃣ Build a Subtitle Analytics Tool
522
+
523
+ ### 🎯 Word Frequency Counter
524
+
525
+ ```python
526
+ from collections import Counter
527
+
528
+ subs = SRTManager.from_file("debate.srt")
529
+
530
+ words = subs.to_plain_text().lower().split()
531
+ freq = Counter(words)
532
+
533
+ print(freq.most_common(20))
534
+ ```
535
+
536
+ ✔ Works perfectly because `to_plain_text()` strips formatting
537
+
538
+ ---
539
+
540
+ # 9️⃣ Convert Subtitles to Voice Script
541
+
542
+ ### 🎯 Create Narration Script
543
+
544
+ ```python
545
+ subs = SRTManager.from_file("course.srt")
546
+
547
+ script = subs.join_as_single(sep=" ")
548
+
549
+ with open("script.txt", "w") as f:
550
+ f.write(script.content)
551
+ ```
552
+
553
+ ✔ Preserves full timeline span
554
+ ✔ Merges content cleanly
555
+
556
+ ---
557
+
558
+ # 🔟 Real Production Pipeline Example
559
+
560
+ ```python
561
+ def production_pipeline(path_in: str, path_out: str):
562
+ subs = (
563
+ SRTManager.from_file(path_in)
564
+ .shift(0.5)
565
+ .map_content(lambda t: t.replace("uh", "").strip())
566
+ .compress_gaps()
567
+ )
568
+
569
+ subs.save(path_out)
570
+
571
+
572
+ production_pipeline("raw.srt", "final.srt")
573
+ ```
574
+
575
+ ✔ Sync correction
576
+ ✔ Filler word removal
577
+ ✔ Timeline tightening
578
+ ✔ Safe save
579
+
580
+ ---
581
+
582
+ # ⚙️ Advanced Pattern: Functional Chaining
583
+
584
+ Because transformations return new `SRTManager` instances:
585
+
586
+ ```python
587
+ final = (
588
+ SRTManager.from_file("input.srt")
589
+ >> 1.2
590
+ .map_content(str.upper)
591
+ .slice(30, 90)
592
+ .compress_gaps()
593
+ )
594
+ ```
595
+
596
+ This enables:
597
+
598
+ * Declarative pipelines
599
+ * Immutable-style transformations
600
+ * Predictable behavior
601
+
602
+ ---
603
+
604
+ # 🏗 Design Strength Shown in Advanced Use
605
+
606
+ Your architecture supports:
607
+
608
+ * Deterministic transformations
609
+ * Strong validation
610
+ * Clean composition
611
+ * Safe timeline arithmetic
612
+ * Operator expressiveness
613
+