# Vendor patches vs. abctoolkit 0.0.6

Two bugs are fixed relative to the upstream PyPI package.

---

## Bug 1 — `[V:N]` duplication in interleaved output (`abctoolkit/utils.py`)

### Root cause

`xml2abc` (and some other converters) emit unrotated ABC where every body line
is prefixed with an inline voice marker:

```
V:1
[V:1] C2 D2 E2 F2 | G4 z4 |]
V:2
[V:2] E2 F2 G2 A2 | B4 z4 |]
```

`rotate_abc` calls `extract_metadata_and_parts`, which collects the raw body
text for each voice, then re-prefixes every bar with `[V:N]`. Because the
original markers were never stripped, the output doubled them up.

### Before (abctoolkit 0.0.6)

Input:
```
X:1
M:4/4
L:1/8
K:C
V:1
[V:1] C2 D2 E2 F2 | G4 z4 |]
V:2
[V:2] E2 F2 G2 A2 | B4 z4 |]
```

Output after `rotate_abc`:
```
[V:1][V:1] C2 D2 E2 F2 | G4 z4 |][V:2][V:2] E2 F2 G2 A2 | B4 z4 |]
```

The doubled markers make the interleaved ABC unparseable by downstream tools.

### After (this vendor copy)

Same input produces:
```
[V:1] C2 D2 E2 F2 | G4 z4 |][V:2] E2 F2 G2 A2 | B4 z4 |]
```

### Fix

In `extract_metadata_and_parts` (`abctoolkit/utils.py`), after the body text
for each voice is collected, strip all occurrences of its own `[V:symbol]`
marker before storing it:

```python
text = re.sub(r'\[' + re.escape(symbol) + r'\]\s*', '', text)
```

Only the matching voice's marker is stripped, so cross-voice markers in
genuinely malformed multi-voice body text are left untouched.

---

## Bug 2 — tunebody detection misses `V:1` with trailing attributes (`abctoolkit/utils.py`)

### Root cause

`extract_metadata_and_tunebody` searched for the last line whose stripped
content was exactly `'V:1'`. Many real-world files include attributes on the
same line:

```
V:1 clef=treble nm="Piano"
```

The exact-match check skipped this line, so the function raised
`'tunebody index not found'` and the file was silently dropped.

### Fix

Replace the exact string comparison with a regex that matches `V:1` followed
by optional whitespace or end-of-string:

```python
if re.match(r'^V:1(\s|$)', line.strip()):
```

The same pattern is applied when collecting part symbols in the loop, so
`V:1 clef=treble` is normalised to `V:1` as the dict key.
