subgapfix 0.0.1__py3-none-any.whl → 0.0.3__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.
- subgapfix/subgapfix.py +163 -51
- subgapfix-0.0.3.dist-info/METADATA +121 -0
- subgapfix-0.0.3.dist-info/RECORD +7 -0
- {subgapfix-0.0.1.dist-info → subgapfix-0.0.3.dist-info}/WHEEL +1 -1
- subgapfix-0.0.3.dist-info/entry_points.txt +2 -0
- subgapfix-0.0.1.dist-info/METADATA +0 -97
- subgapfix-0.0.1.dist-info/RECORD +0 -7
- subgapfix-0.0.1.dist-info/entry_points.txt +0 -2
- {subgapfix-0.0.1.dist-info → subgapfix-0.0.3.dist-info}/top_level.txt +0 -0
subgapfix/subgapfix.py
CHANGED
|
@@ -1,51 +1,163 @@
|
|
|
1
|
-
import
|
|
2
|
-
import srt
|
|
3
|
-
from datetime import timedelta
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import srt
|
|
3
|
+
from datetime import timedelta
|
|
4
|
+
import typer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
app = typer.Typer(
|
|
8
|
+
help="Extend subtitle display durations in gaps (better readability without overlaps).",
|
|
9
|
+
add_completion=True,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def validate_input_file(input_file: Path) -> None:
|
|
14
|
+
"""Check if the input is a valid .srt file."""
|
|
15
|
+
if input_file.suffix.lower() != ".srt":
|
|
16
|
+
typer.secho(
|
|
17
|
+
f"Error: Input file must have .srt extension (got: {input_file.name})",
|
|
18
|
+
fg=typer.colors.RED,
|
|
19
|
+
err=True,
|
|
20
|
+
)
|
|
21
|
+
raise typer.Exit(1)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def prepare_output_path(input_file: Path, output: Path | None) -> Path:
|
|
25
|
+
"""Determine output path — use default if not provided."""
|
|
26
|
+
if output is None:
|
|
27
|
+
return input_file.with_stem(input_file.stem + "_fixed")
|
|
28
|
+
return output
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate_parameters(extend_start: float, min_gap: float, extend_final_sub: float) -> None:
|
|
32
|
+
"""Ensure logical parameter constraints."""
|
|
33
|
+
if min_gap <= extend_start:
|
|
34
|
+
typer.secho(
|
|
35
|
+
f"Error: --min-gap ({min_gap}) must be > --extend-start ({extend_start})",
|
|
36
|
+
fg=typer.colors.RED,
|
|
37
|
+
err=True,
|
|
38
|
+
)
|
|
39
|
+
raise typer.Exit(1)
|
|
40
|
+
|
|
41
|
+
if extend_final_sub < 0:
|
|
42
|
+
typer.secho(
|
|
43
|
+
f"Error: --extend-final-sub must be greater than or equal to 0",
|
|
44
|
+
fg=typer.colors.RED,
|
|
45
|
+
err=True,
|
|
46
|
+
)
|
|
47
|
+
raise typer.Exit(1)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def load_subtitles(input_file: Path) -> list[srt.Subtitle]:
|
|
51
|
+
"""Read and parse the SRT file."""
|
|
52
|
+
try:
|
|
53
|
+
content = input_file.read_text(encoding="utf-8")
|
|
54
|
+
return list(srt.parse(content))
|
|
55
|
+
except Exception as e:
|
|
56
|
+
typer.secho(f"Cannot parse SRT: {e}", fg=typer.colors.RED, err=True)
|
|
57
|
+
raise typer.Exit(1)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def extend_gaps(
|
|
61
|
+
subs: list[srt.Subtitle],
|
|
62
|
+
extend_start: float,
|
|
63
|
+
extend_end_max: float,
|
|
64
|
+
min_gap: float,
|
|
65
|
+
extend_final_sub: float
|
|
66
|
+
) -> int:
|
|
67
|
+
"""Apply gap extension logic. Returns number of changed pairs."""
|
|
68
|
+
changes = 0
|
|
69
|
+
|
|
70
|
+
for i in range(len(subs) - 1):
|
|
71
|
+
a, b = subs[i], subs[i + 1]
|
|
72
|
+
gap = (b.start - a.end).total_seconds()
|
|
73
|
+
|
|
74
|
+
old_a_end = a.end
|
|
75
|
+
old_b_start = b.start
|
|
76
|
+
|
|
77
|
+
if gap < min_gap:
|
|
78
|
+
delta = gap / 2
|
|
79
|
+
a.end += timedelta(seconds=delta)
|
|
80
|
+
b.start -= timedelta(seconds=delta)
|
|
81
|
+
elif gap >= min_gap:
|
|
82
|
+
extendable = gap - extend_start
|
|
83
|
+
if extendable > 0:
|
|
84
|
+
extension = min(extendable, extend_end_max)
|
|
85
|
+
a.end += timedelta(seconds=extension)
|
|
86
|
+
b.start -= timedelta(seconds=extend_start)
|
|
87
|
+
|
|
88
|
+
# Prevent overlap / negative duration
|
|
89
|
+
if b.start <= a.end:
|
|
90
|
+
b.start = a.end + timedelta(milliseconds=10)
|
|
91
|
+
|
|
92
|
+
if a.end != old_a_end or b.start != old_b_start:
|
|
93
|
+
changes += 1
|
|
94
|
+
|
|
95
|
+
# Finally give the very last subtitle +1 second
|
|
96
|
+
if subs and extend_final_sub > 0:
|
|
97
|
+
last = subs[-1]
|
|
98
|
+
last.end += timedelta(seconds=extend_final_sub)
|
|
99
|
+
changes += 1
|
|
100
|
+
|
|
101
|
+
return changes
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@app.command()
|
|
105
|
+
def main(
|
|
106
|
+
|
|
107
|
+
input_file: Path = typer.Argument(..., exists=True, dir_okay=False),
|
|
108
|
+
|
|
109
|
+
output: Path = typer.Option(
|
|
110
|
+
None,
|
|
111
|
+
"--output",
|
|
112
|
+
"-o",
|
|
113
|
+
help="Output file. Default: <input>_fixed.srt in same folder",
|
|
114
|
+
),
|
|
115
|
+
|
|
116
|
+
extend_start: float = typer.Option(
|
|
117
|
+
0.5, "--extend-sub-start", "-ess", help="Seconds to pull next sub backward"
|
|
118
|
+
),
|
|
119
|
+
|
|
120
|
+
extend_end_max: float = typer.Option(
|
|
121
|
+
2.0, "--extend-sub-end", "-ese", help="Max seconds to extend current sub forward"
|
|
122
|
+
),
|
|
123
|
+
|
|
124
|
+
min_gap: float = typer.Option(
|
|
125
|
+
1.0, "--min-gap", "-mg", help="Only extend when gap ≥ this value"
|
|
126
|
+
),
|
|
127
|
+
|
|
128
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show amount changes without writing"),
|
|
129
|
+
|
|
130
|
+
extend_final_sub: float = typer.Option(
|
|
131
|
+
1.0,
|
|
132
|
+
"--extend-final-sub",
|
|
133
|
+
"-efs",
|
|
134
|
+
help="Seconds to add to the very last subtitle"
|
|
135
|
+
)
|
|
136
|
+
):
|
|
137
|
+
|
|
138
|
+
validate_input_file(input_file)
|
|
139
|
+
|
|
140
|
+
output_path = prepare_output_path(input_file, output)
|
|
141
|
+
|
|
142
|
+
if not dry_run:
|
|
143
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
144
|
+
|
|
145
|
+
validate_parameters(extend_start, min_gap, extend_final_sub)
|
|
146
|
+
|
|
147
|
+
subs = load_subtitles(input_file)
|
|
148
|
+
|
|
149
|
+
changes = extend_gaps(subs, extend_start, extend_end_max, min_gap, extend_final_sub)
|
|
150
|
+
|
|
151
|
+
if dry_run:
|
|
152
|
+
typer.echo(f"The dry run ran succesfully and detected {changes} changes to subtitle pairs.")
|
|
153
|
+
return
|
|
154
|
+
|
|
155
|
+
output_path.write_text(srt.compose(subs), encoding="utf-8")
|
|
156
|
+
typer.secho(
|
|
157
|
+
f"Done. Wrote {len(subs)} subtitles → {output_path}",
|
|
158
|
+
fg=typer.colors.GREEN,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
if __name__ == "__main__":
|
|
163
|
+
app()
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: subgapfix
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: A small CLI tool to extend subtitle durations in SRT files to make them more readable on a screen.
|
|
5
|
+
Author-email: Reinder Sinnema <reinder@w3bunker.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: srt>=3.5.3
|
|
10
|
+
Requires-Dist: typer>=0.21.1
|
|
11
|
+
|
|
12
|
+
# SubGapFix
|
|
13
|
+
|
|
14
|
+
[](https://pypi.org/project/subgapfix/)
|
|
15
|
+
[](https://pypi.org/project/subgapfix/)
|
|
16
|
+
[](https://opensource.org/licenses/MIT)
|
|
17
|
+
|
|
18
|
+
**SubGapFix** is a lightweight CLI tool that makes subtitles easier and more comfortable to read by **intelligently extending their display duration** — especially useful for auto-generated subtitles.
|
|
19
|
+
|
|
20
|
+
It works great with transcriptions from **[WhisperX](https://github.com/m-bain/whisperX)**.
|
|
21
|
+
|
|
22
|
+
## ✨ Features
|
|
23
|
+
|
|
24
|
+
- Extends subtitle display time when there is a **gap** before the next line
|
|
25
|
+
- Safely prevents overlaps and negative durations
|
|
26
|
+
- Dry-run mode to preview changes without writing files
|
|
27
|
+
- Creates output folders automatically if needed
|
|
28
|
+
- Only processes valid `.srt` files
|
|
29
|
+
|
|
30
|
+
## 📦 Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install subgapfix
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Requires Python 3.8+.
|
|
37
|
+
|
|
38
|
+
Dependencies:
|
|
39
|
+
- `typer` — beautiful CLI interface
|
|
40
|
+
- `srt` — reliable SRT parsing
|
|
41
|
+
|
|
42
|
+
## 🚀 Quick Start
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Basic usage — creates episode_fixed.srt in the same folder
|
|
46
|
+
subgapfix episode.srt
|
|
47
|
+
|
|
48
|
+
# Custom output file (creates folders if needed)
|
|
49
|
+
subgapfix podcast/episode.srt -o podcast/enhanced/episode_subtitles.srt
|
|
50
|
+
|
|
51
|
+
# Preview changes without modifying anything
|
|
52
|
+
subgapfix input.srt --dry-run
|
|
53
|
+
|
|
54
|
+
# Customize timings
|
|
55
|
+
subgapfix input.srt --extend-sub-start 0.8 --extend-sub-end 3.0 --min-gap 1.5 --extend-final-sub 4.5
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## ⚙️ All Options
|
|
59
|
+
|
|
60
|
+
| Flag / Short | Default | Description |
|
|
61
|
+
|--------------|---------|-------------|
|
|
62
|
+
| `input_file` (positional) | — | Path to the input `.srt` file (must exist) |
|
|
63
|
+
| `-o, --output` | `<input>_fixed.srt` | Output file path (folders auto-created) |
|
|
64
|
+
| `--extend-sub-start`, `-ess` | `0.5` | Seconds to pull the **next** subtitle backward (tries to borrow from gap) |
|
|
65
|
+
| `--extend-sub-end`, `-ese` | `2.0` | Maximum seconds to extend current subtitle forward into the gap |
|
|
66
|
+
| `--min-gap`, `-mg` | `1.0` | Only apply extension if gap is at least this long (seconds) |
|
|
67
|
+
| `--dry-run` | `false` | Show how many pairs would change — no file written |
|
|
68
|
+
| `--extend-final-sub`, `-efs` | `1.0` | Number of seconds to add to the last subtitle |
|
|
69
|
+
| `--help` | — | Show full help and exit |
|
|
70
|
+
|
|
71
|
+
## How It Works
|
|
72
|
+
|
|
73
|
+
1. Reads and validates the `.srt` file
|
|
74
|
+
2. For each pair of consecutive subtitles:
|
|
75
|
+
- If gap < `--min-gap` → splits the gap evenly (closes small gaps)
|
|
76
|
+
- If gap ≥ `--min-gap` → extends current subtitle up to `--extend-end-max`, pulls next one back by `--extend-start`
|
|
77
|
+
3. Prevents any overlap by enforcing a tiny 10 ms safety gap
|
|
78
|
+
4. Add `--extend-final-sub` to the last subtitle for a precise ending
|
|
79
|
+
5. Writes the result (or just reports changes in dry-run)
|
|
80
|
+
|
|
81
|
+
**Before** (typical WhisperX-style tight timings):
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
1
|
|
85
|
+
00:00:01,837 --> 00:00:02,502
|
|
86
|
+
SubGapFix makes our lives
|
|
87
|
+
|
|
88
|
+
2
|
|
89
|
+
00:00:03,147 --> 00:00:03,571
|
|
90
|
+
much easier.
|
|
91
|
+
|
|
92
|
+
3
|
|
93
|
+
00:00:04,176 --> 00:00:04,518
|
|
94
|
+
Yes, I agree.
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**After** (with defaults):
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
1
|
|
101
|
+
00:00:01,837 --> 00:00:02,824
|
|
102
|
+
SubGapFix makes our lives
|
|
103
|
+
|
|
104
|
+
2
|
|
105
|
+
00:00:02,834 --> 00:00:03,873
|
|
106
|
+
much easier.
|
|
107
|
+
|
|
108
|
+
3
|
|
109
|
+
00:00:03,883 --> 00:00:04,679
|
|
110
|
+
Yes, I agree.
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## 💡 Why SubGapFix?
|
|
114
|
+
|
|
115
|
+
Modern speech-to-text tools often prioritize transcription accuracy over comfortable reading speed.
|
|
116
|
+
Subtitles flash by too quickly → viewers miss text or feel rushed.
|
|
117
|
+
|
|
118
|
+
SubGapFix **does not shift timings** or re-sync — it only **lengthens display time** using existing gaps.
|
|
119
|
+
This keeps perfect sync with the video while making subtitles far more readable.
|
|
120
|
+
|
|
121
|
+
Perfect for lectures, interviews, conversations, podcasts.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
subgapfix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
subgapfix/subgapfix.py,sha256=6Teg69GW88Y2MeegJA-YIm5pSiylGI1dFsULzgTE_EU,4709
|
|
3
|
+
subgapfix-0.0.3.dist-info/METADATA,sha256=zK7agcIXqb6GYWUHPoIBJgOG6L3aveXCq4CaRxfYYGU,3999
|
|
4
|
+
subgapfix-0.0.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
5
|
+
subgapfix-0.0.3.dist-info/entry_points.txt,sha256=JhP4WR6kiSkl8dE77M5WpByoX8deTaHUiTC-xdwl8t8,54
|
|
6
|
+
subgapfix-0.0.3.dist-info/top_level.txt,sha256=1Kmf1LI51trmbRihayDZRnKSCfEQDR7jSyBELfkwTa8,10
|
|
7
|
+
subgapfix-0.0.3.dist-info/RECORD,,
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: subgapfix
|
|
3
|
-
Version: 0.0.1
|
|
4
|
-
Summary: A small CLI tool to extend subtitle durations in SRT files by redefining gaps between subtitles.
|
|
5
|
-
Author-email: Reinder Sinnema <reinder@w3bunker.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Requires-Python: >=3.8
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: srt>=3.5.3
|
|
10
|
-
|
|
11
|
-
# SubGapFix
|
|
12
|
-
|
|
13
|
-
A small Python CLI tool to **extend subtitle durations** in `.srt` files so they stay on screen longer and feel more natural to read.
|
|
14
|
-
Designed especially for subtitles generated by [WhisperX](https://github.com/m-bain/whisperX), which often have very tight timings.
|
|
15
|
-
|
|
16
|
-
## ✨ Features
|
|
17
|
-
|
|
18
|
-
- Automatically extends subtitles when there’s a gap before the next subtitle.
|
|
19
|
-
- Prevents overlaps by adjusting start/end times safely.
|
|
20
|
-
- Configurable via CLI arguments.
|
|
21
|
-
|
|
22
|
-
## 📦 Installation
|
|
23
|
-
|
|
24
|
-
Clone the repository and install dependencies:
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
git clone https://github.com/yourusername/subgapfix.git
|
|
28
|
-
cd subgapfix
|
|
29
|
-
pip install -r requirements.txt
|
|
30
|
-
````
|
|
31
|
-
|
|
32
|
-
Dependencies:
|
|
33
|
-
|
|
34
|
-
* [`srt`](https://pypi.org/project/srt/)
|
|
35
|
-
|
|
36
|
-
## 🔧 Usage
|
|
37
|
-
|
|
38
|
-
Basic usage:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
python subgapfix.py input.srt -o easyreading.srt
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### Options
|
|
45
|
-
|
|
46
|
-
| Option | Default | Description |
|
|
47
|
-
|----------------------------|-----------------|---------------------------------------------------------------------------|
|
|
48
|
-
| `-o, --output` | `subgapfix.srt` | Output SRT file |
|
|
49
|
-
| `--extend-sub-start, -ess` | `0.5` | Seconds to add to start of subtitle. Default is `0.5` |
|
|
50
|
-
| `--extend-sub-end, -ese` | `2.0` | Maximum seconds to add to end of subtitle. Default is `2.0` |
|
|
51
|
-
| `--min-gap, -mg` | `1.0` | Minimum gap (in seconds) required to apply extension. Default is `1.0` |
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
### How It Works
|
|
55
|
-
|
|
56
|
-
The script reads your `.srt` file, loops through each pair of consecutive subtitles, and adjusts their timings to make them easier to read.
|
|
57
|
-
|
|
58
|
-
### Example
|
|
59
|
-
|
|
60
|
-
Input:
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
1
|
|
64
|
-
00:00:01,000 --> 00:00:03,000
|
|
65
|
-
Hello there.
|
|
66
|
-
|
|
67
|
-
2
|
|
68
|
-
00:00:06,000 --> 00:00:08,000
|
|
69
|
-
How are you?
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Run:
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
python subgapfix.py input.srt -o fixed.srt
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Output:
|
|
79
|
-
|
|
80
|
-
```
|
|
81
|
-
1
|
|
82
|
-
00:00:01,000 --> 00:00:05,000
|
|
83
|
-
Hello there.
|
|
84
|
-
|
|
85
|
-
2
|
|
86
|
-
00:00:05,500 --> 00:00:08,000
|
|
87
|
-
How are you?
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## 💡 Why?
|
|
91
|
-
|
|
92
|
-
Tools like WhisperX produce accurate subtitles, but their timings are often too **tight** for comfortable reading.
|
|
93
|
-
`SubGapFix` helps subtitles stay visible longer while keeping synchronization intact.
|
|
94
|
-
|
|
95
|
-
## 🛠 Roadmap
|
|
96
|
-
|
|
97
|
-
* Package for PyPI (`pip install subgapfix`)
|
subgapfix-0.0.1.dist-info/RECORD
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
subgapfix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
subgapfix/subgapfix.py,sha256=tDDn4cqL-jw9KfPK4tkVaxBLVh_ZZ53xWCwi6PWTbDo,2254
|
|
3
|
-
subgapfix-0.0.1.dist-info/METADATA,sha256=Tqyy5R3_mDF0CXqRAXsfkpp_x_t5uxk6BT1EqRruLRc,2620
|
|
4
|
-
subgapfix-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
5
|
-
subgapfix-0.0.1.dist-info/entry_points.txt,sha256=2ln9RkMCsMtg0kFARJgoY7JKxBR4cEzxj4s-bKGCVUw,55
|
|
6
|
-
subgapfix-0.0.1.dist-info/top_level.txt,sha256=1Kmf1LI51trmbRihayDZRnKSCfEQDR7jSyBELfkwTa8,10
|
|
7
|
-
subgapfix-0.0.1.dist-info/RECORD,,
|
|
File without changes
|