Skip to content

Commit 9cd9fff

Browse files
Initial commit
0 parents  commit 9cd9fff

13 files changed

Lines changed: 939 additions & 0 deletions

File tree

‎.github/workflows/publish-pypi.yml‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
jobs:
9+
publish:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- name: Set up Python
15+
uses: actions/setup-python@v5
16+
with:
17+
python-version: "3.11"
18+
19+
- name: Install build
20+
run: pip install build twine
21+
22+
- name: Build package
23+
run: python -m build
24+
25+
- name: Publish to PyPI
26+
env:
27+
TWINE_USERNAME: __token__
28+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
29+
run: twine upload dist/*

‎.gitignore‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Byte-compiled / optimized
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.so
6+
7+
# Virtual environments
8+
.venv/
9+
venv/
10+
ENV/
11+
env/
12+
13+
# Distribution / build
14+
build/
15+
dist/
16+
*.egg-info/
17+
*.egg
18+
19+
# PyInstaller
20+
*.manifest
21+
*.spec
22+
23+
# Installer
24+
*.msi
25+
*.pkg
26+
27+
# Testing / coverage
28+
.pytest_cache/
29+
.coverage
30+
htmlcov/
31+
.tox/
32+
.nox/
33+
34+
# IDE
35+
.idea/
36+
.vscode/
37+
*.swp
38+
*.swo
39+
40+
# OS
41+
.DS_Store
42+
Thumbs.db
43+
44+
# Local / env files
45+
.env
46+
.env.local
47+
*.local

‎LICENSE‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Annotation PRO
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

‎README.md‎

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# annotationpro-format
2+
3+
Python library for reading and writing **Annotation PRO** `.antx` files (XML annotation format used by the Annotation PRO application).
4+
5+
## About ANTX files
6+
7+
ANTX files are XML documents that store speech or audio annotations: layers, segments (with labels and time bounds), and project configuration. They are produced and consumed by [Annotation PRO](https://annotationpro.org/).
8+
9+
**Time in ANTX is stored in samples**, not in seconds. Each annotation file has a **samplerate** (e.g. 44100 Hz) in its configuration. Segment `start` and `duration` in the file are sample counts. To work in seconds you can use the samplerate from the annotation and the helper functions provided by this library (see below).
10+
11+
## Requirements
12+
13+
- Python 3.9+
14+
15+
## Installation
16+
17+
From PyPI:
18+
19+
```bash
20+
pip install annotationpro-format
21+
```
22+
23+
From source (development):
24+
25+
```bash
26+
pip install -e .
27+
```
28+
29+
## Usage
30+
31+
### Reading an ANTX file
32+
33+
```python
34+
from annotationpro_format import deserialize_annotation
35+
36+
with open("project.antx", "r", encoding="utf-8") as f:
37+
xml_content = f.read()
38+
39+
annotation = deserialize_annotation(xml_content)
40+
samplerate = int(annotation.configuration["Samplerate"])
41+
42+
for layer in annotation.layers:
43+
print(f"Layer: {layer.name}")
44+
for seg in layer.segments:
45+
print(f" {seg.start} – {seg.duration} (samples) – {seg.label}")
46+
```
47+
48+
### Writing an ANTX file (using samples)
49+
50+
Segment `start` and `duration` are stored in samples. You can set them directly when you work in sample counts:
51+
52+
```python
53+
from annotationpro_format import Annotation, Layer, Segment, serialize_annotation
54+
55+
annotation = Annotation(samplerate=44100)
56+
layer = Layer("Transcription")
57+
# start=0, duration=66150 means 0 to 1.5 seconds at 44100 Hz
58+
layer.segments.append(Segment("hello", start=0.0, duration=66150.0))
59+
annotation.layers.append(layer)
60+
61+
xml_str = serialize_annotation(annotation)
62+
with open("output.antx", "w", encoding="utf-8") as f:
63+
f.write(xml_str)
64+
```
65+
66+
### Working with time in seconds (helpers)
67+
68+
To avoid manual sample math, use the seconds helpers. They use the annotation’s samplerate to convert between seconds and samples.
69+
70+
**Writing segments with start/duration in seconds:**
71+
72+
```python
73+
from annotationpro_format import (
74+
Annotation,
75+
Layer,
76+
Segment,
77+
serialize_annotation,
78+
set_segment_start_seconds,
79+
set_segment_duration_seconds,
80+
)
81+
82+
annotation = Annotation(samplerate=44100)
83+
layer = Layer("Transcription")
84+
85+
# Create segment (start/duration in samples will be set by helpers)
86+
seg = Segment("hello", start=0.0, duration=0.0)
87+
set_segment_start_seconds(annotation, seg, 0.0) # 0 seconds
88+
set_segment_duration_seconds(annotation, seg, 1.5) # 1.5 seconds
89+
layer.segments.append(seg)
90+
91+
# Another segment: from 2.5 s to 4.0 s (duration 1.5 s)
92+
seg2 = Segment("world", start=0.0, duration=0.0)
93+
set_segment_start_seconds(annotation, seg2, 2.5)
94+
set_segment_duration_seconds(annotation, seg2, 1.5)
95+
layer.segments.append(seg2)
96+
97+
annotation.layers.append(layer)
98+
with open("output.antx", "w", encoding="utf-8") as f:
99+
f.write(serialize_annotation(annotation))
100+
```
101+
102+
**Reading segment times in seconds:**
103+
104+
```python
105+
from annotationpro_format import (
106+
deserialize_annotation,
107+
get_segment_start_seconds,
108+
get_segment_duration_seconds,
109+
)
110+
111+
with open("project.antx", "r", encoding="utf-8") as f:
112+
annotation = deserialize_annotation(f.read())
113+
114+
for layer in annotation.layers:
115+
for seg in layer.segments:
116+
start_sec = get_segment_start_seconds(annotation, seg)
117+
duration_sec = get_segment_duration_seconds(annotation, seg)
118+
print(f"{start_sec:.2f}s – {start_sec + duration_sec:.2f}s: {seg.label}")
119+
```
120+
121+
**Low-level conversion (seconds ↔ samples):**
122+
123+
```python
124+
from annotationpro_format import seconds_to_samples, samples_to_seconds
125+
126+
samplerate = 44100
127+
samples = seconds_to_samples(1.5, samplerate) # 66150.0
128+
seconds = samples_to_seconds(66150, samplerate) # 1.5
129+
```
130+
131+
## Model overview
132+
133+
- **Annotation** – Root object: `configuration` (dict, includes `Samplerate`), `layers` (list).
134+
- **Layer** – One annotation layer: `name`, `id`, `segments` (list-like `SegmentCollection`), plus display options (colors, height, etc.).
135+
- **Segment** – One segment: `label`, `start`, `duration` (in **samples**), and optional fields (`feature`, `language`, `group`, etc.).
136+
- **SegmentCollection** – List of segments for a layer; setting `id_layer` on the collection is handled when you append segments.
137+
- **AudioFile** – Model for audio file references (not used in the current serialization).
138+
139+
## Publishing to PyPI (for maintainers)
140+
141+
Publishing the library to PyPI is **fully automated** via GitHub Actions.
142+
143+
- A release is published when a **tag** matching `vX.Y.Z` is **pushed to GitHub**, e.g.:
144+
145+
```bash
146+
git tag v0.1.1
147+
git push origin v0.1.1
148+
```
149+
150+
- Before creating the tag, **bump the version** in `pyproject.toml`:
151+
152+
```toml
153+
[project]
154+
version = "0.1.1"
155+
```
156+
157+
- The **tag name** (e.g. `v0.1.1`) and the version in `pyproject.toml` (e.g. `0.1.1`) **must match**.
158+
159+
When the tag is pushed:
160+
161+
1. The workflow in `.github/workflows/publish-pypi.yml` runs.
162+
2. It checks out the code at that tag.
163+
3. It builds the package with `python -m build` (sdist + wheel).
164+
4. It uploads the artifacts in `dist/` to PyPI using `twine upload dist/*` and the `PYPI_API_TOKEN` secret configured in the repository.
165+
166+
If the workflow succeeds, the new version is immediately available on PyPI as `annotationpro-format`.
167+
168+
## License
169+
170+
MIT – see [LICENSE](LICENSE).

‎main.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# This is a sample Python script.
2+
3+
# Press Shift+F10 to execute it or replace it with your code.
4+
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
5+
6+
7+
def print_hi(name):
8+
# Use a breakpoint in the code line below to debug your script.
9+
print(f'Hi, {name}') # Press Ctrl+F8 to toggle the breakpoint.
10+
11+
12+
# Press the green button in the gutter to run the script.
13+
if __name__ == '__main__':
14+
print_hi('PyCharm')
15+
16+
# See PyCharm help at https://www.jetbrains.com/help/pycharm/

‎pyproject.toml‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[build-system]
2+
requires = ["setuptools>=61", "wheel"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "annotationpro-format"
7+
version = "0.1.0"
8+
description = "Read and write Annotation PRO .antx files (XML format)"
9+
readme = "README.md"
10+
requires-python = ">=3.9"
11+
license = "MIT"
12+
authors = [
13+
{ name = "Annotation PRO" }
14+
]
15+
keywords = ["annotation", "antx", "annotation-pro", "speech", "transcription"]
16+
classifiers = [
17+
"Development Status :: 3 - Alpha",
18+
"Intended Audience :: Developers",
19+
"Programming Language :: Python :: 3",
20+
"Programming Language :: Python :: 3.9",
21+
"Programming Language :: Python :: 3.10",
22+
"Programming Language :: Python :: 3.11",
23+
"Programming Language :: Python :: 3.12",
24+
]
25+
26+
[project.urls]
27+
Repository = "https://github.com/annotationpro/annotationpro-format-python"
28+
29+
[project.optional-dependencies]
30+
dev = ["pytest>=7"]
31+
32+
[tool.setuptools.packages.find]
33+
where = ["src"]
34+
include = ["annotationpro_format*"]
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
Annotation PRO antx format – read/write .antx files (Annotation PRO XML).
3+
"""
4+
5+
from .model import (
6+
VERSION,
7+
Annotation,
8+
AudioFile,
9+
Layer,
10+
Segment,
11+
SegmentCollection,
12+
)
13+
from .serializer import serialize_annotation
14+
from .deserializer import deserialize_annotation
15+
from .seconds import (
16+
seconds_to_samples,
17+
samples_to_seconds,
18+
get_segment_start_seconds,
19+
get_segment_duration_seconds,
20+
set_segment_start_seconds,
21+
set_segment_duration_seconds,
22+
)
23+
24+
__all__ = [
25+
"Annotation",
26+
"AudioFile",
27+
"Layer",
28+
"Segment",
29+
"SegmentCollection",
30+
"VERSION",
31+
"serialize_annotation",
32+
"deserialize_annotation",
33+
"seconds_to_samples",
34+
"samples_to_seconds",
35+
"get_segment_start_seconds",
36+
"get_segment_duration_seconds",
37+
"set_segment_start_seconds",
38+
"set_segment_duration_seconds",
39+
]

0 commit comments

Comments
 (0)