# LiveSplit LSS File Parser
A comprehensive Python module for parsing and manipulating LiveSplit `.lss` files. This module converts XML-based LSS files into easy-to-use Python objects, allowing for programmatic analysis and manipulation of speedrun data.
## Features
- **Complete LSS File Support**: Parse all elements of LSS files including metadata, attempts, segments, and timing data
- **Pydantic-Based Models**: Robust data validation and serialization using Pydantic BaseModel
- **Bidirectional Conversion**: Load LSS files into Python objects and save them back to LSS format
- **Type Safety**: Full type annotations with automatic validation for better IDE support and code reliability
- **JSON Serialization**: Built-in support for JSON export/import via Pydantic
- **Data Validation**: Automatic type conversion and validation (e.g., integers to strings where needed)
- **Comprehensive API**: Simple interface with just two main functions: `load_lss_file()` and `save_lss_file()`
## Installation
This module requires Pydantic. Install dependencies:
```bash
pip install pydantic
```
Then copy the `lss_parser.py` file to your project directory or install it as a module.
```python
import lss_parser
```
## Quick Start
### Loading an LSS File
```python
import lss_parser
# Load a LiveSplit file
run = lss_parser.load_lss_file("my_speedrun.lss")
# Access basic information
print(f"Game: {run.game_name}")
print(f"Category: {run.category_name}")
print(f"Total Attempts: {run.attempt_count}")
print(f"Number of Segments: {len(run.segments)}")
```
### Saving an LSS File
```python
# Modify the run data
run.game_name = "Modified Game Name"
run.category_name = "Any% Modified"
# Save the modified run
lss_parser.save_lss_file(run, "modified_speedrun.lss")
```
## Data Structure
The module provides several Pydantic models to represent LSS file structure:
### `Run` - Main Container
- `game_name`: Name of the game
- `category_name`: Speedrun category
- `version`: LSS file version
- `attempt_count`: Total number of attempts
- `segments`: List of segments (splits)
- `attempt_history`: List of all attempts
- `metadata`: Platform, region, and other metadata
- `auto_splitter_settings`: Auto-splitter configuration
### `Segment` - Individual Splits
- `name`: Segment name
- `icon`: Icon path/data
- `split_times`: List of split times with different timing methods
- `best_segment_time`: Best time for this segment
- `segment_history`: Historical times for this segment
### `Attempt` - Run Attempts
- `id`: Unique attempt identifier
- `started`: Start time/date
- `ended`: End time/date
- `time`: Timing data (real time, game time, pause time)
### `Time` - Timing Information
- `real_time`: Real world time
- `game_time`: In-game time
- `pause_time`: Paused time
## Pydantic Features
The module leverages Pydantic's powerful features:
### Data Validation
```python
import lss_parser
# Automatic type conversion
time = lss_parser.Time(real_time=123) # Converts to "123"
print(time.real_time) # "123"
# Validation on assignment
run = lss_parser.Run(game_name="Test Game")
run.attempt_count = "42" # Automatically converted to int
```
### JSON Serialization
```python
# Export to JSON
run = lss_parser.load_lss_file("speedrun.lss")
run_dict = run.model_dump() # or run.dict() for older Pydantic versions
# Import from JSON
run_data = {...} # JSON data
run = lss_parser.Run(**run_data)
```
### Type Safety
```python
# IDE support with type hints
def analyze_run(run: lss_parser.Run) -> None:
for segment in run.segments: # Full IDE autocomplete
print(f"Segment: {segment.name}")
print(f"Best time: {segment.best_segment_time.real_time}")
```
## Usage Examples
### Analyzing Speedrun Data
```python
import lss_parser
# Load the file
run = lss_parser.load_lss_file("my_speedrun.lss")
# Find completed attempts
completed_attempts = [a for a in run.attempt_history if a.time.real_time]
print(f"Completed {len(completed_attempts)} out of {len(run.attempt_history)} attempts")
# Analyze segment times
for segment in run.segments:
print(f"{segment.name}: Best {segment.best_segment_time.real_time or 'N/A'}")
# Get personal best
pb_attempts = [a for a in completed_attempts if a.time.real_time]
if pb_attempts:
best_time = min(pb_attempts, key=lambda x: x.time.real_time)
print(f"Personal Best: {best_time.time.real_time}")
```
### Creating a New Run
```python
import lss_parser
# Create a new run
new_run = lss_parser.Run(
game_name="My Game",
category_name="Any%"
)
# Add segments
segments = [
lss_parser.Segment(
name="Level 1",
best_segment_time=lss_parser.Time(real_time="00:01:30.0000000")
),
lss_parser.Segment(
name="Level 2",
best_segment_time=lss_parser.Time(real_time="00:02:15.0000000")
),
lss_parser.Segment(
name="Boss",
best_segment_time=lss_parser.Time(real_time="00:03:45.0000000")
)
]
new_run.segments = segments
# Add metadata
new_run.metadata.platform = "PC"
new_run.metadata.region = "USA"
# Save the new run
lss_parser.save_lss_file(new_run, "new_speedrun.lss")
```
### Modifying Existing Data
```python
import lss_parser
# Load existing file
run = lss_parser.load_lss_file("existing_run.lss")
# Modify segment names
for segment in run.segments:
segment.name = f"Modified {segment.name}"
# Add a new segment
new_segment = lss_parser.Segment(
name="New Final Boss",
best_segment_time=lss_parser.Time(real_time="00:05:00.0000000")
)
run.segments.append(new_segment)
# Update attempt count
run.attempt_count = len(run.attempt_history)
# Save changes
lss_parser.save_lss_file(run, "modified_run.lss")
```
## API Reference
### Main Functions
#### `load_lss_file(file_path: Union[str, Path]) -> Run`
Loads and parses an LSS file into a Run object.
**Parameters:**
- `file_path`: Path to the .lss file
**Returns:**
- `Run` object containing all parsed data
**Raises:**
- `FileNotFoundError`: If the file doesn't exist
- `ValueError`: If the file is not a valid LSS file
- `xml.etree.ElementTree.ParseError`: If the XML is malformed
#### `save_lss_file(run: Run, file_path: Union[str, Path]) -> None`
Saves a Run object to an LSS file.
**Parameters:**
- `run`: Run object to save
- `file_path`: Path where to save the .lss file
**Raises:**
- `ValueError`: If the file path is invalid
### Data Models
All data models are implemented using Pydantic's `BaseModel` and include:
- Type annotations with automatic validation
- Default values where appropriate
- Automatic data conversion and validation
- JSON serialization/deserialization support
- IDE support with full type hints
## Error Handling
The module includes comprehensive error handling:
```python
import lss_parser
try:
run = lss_parser.load_lss_file("nonexistent.lss")
except FileNotFoundError:
print("File not found!")
except ValueError as e:
print(f"Invalid file: {e}")
except Exception as e:
print(f"Parse error: {e}")
```
## Testing
The module includes comprehensive tests. Run them with:
```bash
python test_parser.py
```
## Demo
See `demo.py` for a comprehensive demonstration of all features:
```bash
python demo.py
```
## File Format Support
This parser supports LiveSplit LSS files version 1.7.0 and should be compatible with other versions. The parser handles:
- Game metadata (name, category, platform, region)
- Attempt history with timestamps and timing data
- Segment information with split times and history
- Auto-splitter settings
- Custom variables and platform-specific data
## License
This module is provided as-is for educational and personal use. It is not affiliated with LiveSplit or the LiveSplit development team.
## Contributing
This is a complete, self-contained module designed for parsing LSS files. Feel free to extend it for your specific needs.
Raw data
{
"_id": null,
"home_page": null,
"name": "lss-parser",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "livesplit, lss, parser, speedrunning, xml",
"author": "mushroomsuprise",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/95/c5/6204b158364ecb31d324d769cad25983ff4ac451f0d4bddcb55913c560e3/lss_parser-0.1.0.tar.gz",
"platform": null,
"description": "# LiveSplit LSS File Parser\n\nA comprehensive Python module for parsing and manipulating LiveSplit `.lss` files. This module converts XML-based LSS files into easy-to-use Python objects, allowing for programmatic analysis and manipulation of speedrun data.\n\n## Features\n\n- **Complete LSS File Support**: Parse all elements of LSS files including metadata, attempts, segments, and timing data\n- **Pydantic-Based Models**: Robust data validation and serialization using Pydantic BaseModel\n- **Bidirectional Conversion**: Load LSS files into Python objects and save them back to LSS format\n- **Type Safety**: Full type annotations with automatic validation for better IDE support and code reliability\n- **JSON Serialization**: Built-in support for JSON export/import via Pydantic\n- **Data Validation**: Automatic type conversion and validation (e.g., integers to strings where needed)\n- **Comprehensive API**: Simple interface with just two main functions: `load_lss_file()` and `save_lss_file()`\n\n## Installation\n\nThis module requires Pydantic. Install dependencies:\n\n```bash\npip install pydantic\n```\n\nThen copy the `lss_parser.py` file to your project directory or install it as a module.\n\n```python\nimport lss_parser\n```\n\n## Quick Start\n\n### Loading an LSS File\n\n```python\nimport lss_parser\n\n# Load a LiveSplit file\nrun = lss_parser.load_lss_file(\"my_speedrun.lss\")\n\n# Access basic information\nprint(f\"Game: {run.game_name}\")\nprint(f\"Category: {run.category_name}\")\nprint(f\"Total Attempts: {run.attempt_count}\")\nprint(f\"Number of Segments: {len(run.segments)}\")\n```\n\n### Saving an LSS File\n\n```python\n# Modify the run data\nrun.game_name = \"Modified Game Name\"\nrun.category_name = \"Any% Modified\"\n\n# Save the modified run\nlss_parser.save_lss_file(run, \"modified_speedrun.lss\")\n```\n\n## Data Structure\n\nThe module provides several Pydantic models to represent LSS file structure:\n\n### `Run` - Main Container\n- `game_name`: Name of the game\n- `category_name`: Speedrun category\n- `version`: LSS file version\n- `attempt_count`: Total number of attempts\n- `segments`: List of segments (splits)\n- `attempt_history`: List of all attempts\n- `metadata`: Platform, region, and other metadata\n- `auto_splitter_settings`: Auto-splitter configuration\n\n### `Segment` - Individual Splits\n- `name`: Segment name\n- `icon`: Icon path/data\n- `split_times`: List of split times with different timing methods\n- `best_segment_time`: Best time for this segment\n- `segment_history`: Historical times for this segment\n\n### `Attempt` - Run Attempts\n- `id`: Unique attempt identifier\n- `started`: Start time/date\n- `ended`: End time/date\n- `time`: Timing data (real time, game time, pause time)\n\n### `Time` - Timing Information\n- `real_time`: Real world time\n- `game_time`: In-game time\n- `pause_time`: Paused time\n\n## Pydantic Features\n\nThe module leverages Pydantic's powerful features:\n\n### Data Validation\n```python\nimport lss_parser\n\n# Automatic type conversion\ntime = lss_parser.Time(real_time=123) # Converts to \"123\"\nprint(time.real_time) # \"123\"\n\n# Validation on assignment\nrun = lss_parser.Run(game_name=\"Test Game\")\nrun.attempt_count = \"42\" # Automatically converted to int\n```\n\n### JSON Serialization\n```python\n# Export to JSON\nrun = lss_parser.load_lss_file(\"speedrun.lss\")\nrun_dict = run.model_dump() # or run.dict() for older Pydantic versions\n\n# Import from JSON\nrun_data = {...} # JSON data\nrun = lss_parser.Run(**run_data)\n```\n\n### Type Safety\n```python\n# IDE support with type hints\ndef analyze_run(run: lss_parser.Run) -> None:\n for segment in run.segments: # Full IDE autocomplete\n print(f\"Segment: {segment.name}\")\n print(f\"Best time: {segment.best_segment_time.real_time}\")\n```\n\n## Usage Examples\n\n### Analyzing Speedrun Data\n\n```python\nimport lss_parser\n\n# Load the file\nrun = lss_parser.load_lss_file(\"my_speedrun.lss\")\n\n# Find completed attempts\ncompleted_attempts = [a for a in run.attempt_history if a.time.real_time]\nprint(f\"Completed {len(completed_attempts)} out of {len(run.attempt_history)} attempts\")\n\n# Analyze segment times\nfor segment in run.segments:\n print(f\"{segment.name}: Best {segment.best_segment_time.real_time or 'N/A'}\")\n \n# Get personal best\npb_attempts = [a for a in completed_attempts if a.time.real_time]\nif pb_attempts:\n best_time = min(pb_attempts, key=lambda x: x.time.real_time)\n print(f\"Personal Best: {best_time.time.real_time}\")\n```\n\n### Creating a New Run\n\n```python\nimport lss_parser\n\n# Create a new run\nnew_run = lss_parser.Run(\n game_name=\"My Game\",\n category_name=\"Any%\"\n)\n\n# Add segments\nsegments = [\n lss_parser.Segment(\n name=\"Level 1\",\n best_segment_time=lss_parser.Time(real_time=\"00:01:30.0000000\")\n ),\n lss_parser.Segment(\n name=\"Level 2\", \n best_segment_time=lss_parser.Time(real_time=\"00:02:15.0000000\")\n ),\n lss_parser.Segment(\n name=\"Boss\",\n best_segment_time=lss_parser.Time(real_time=\"00:03:45.0000000\")\n )\n]\n\nnew_run.segments = segments\n\n# Add metadata\nnew_run.metadata.platform = \"PC\"\nnew_run.metadata.region = \"USA\"\n\n# Save the new run\nlss_parser.save_lss_file(new_run, \"new_speedrun.lss\")\n```\n\n### Modifying Existing Data\n\n```python\nimport lss_parser\n\n# Load existing file\nrun = lss_parser.load_lss_file(\"existing_run.lss\")\n\n# Modify segment names\nfor segment in run.segments:\n segment.name = f\"Modified {segment.name}\"\n\n# Add a new segment\nnew_segment = lss_parser.Segment(\n name=\"New Final Boss\",\n best_segment_time=lss_parser.Time(real_time=\"00:05:00.0000000\")\n)\nrun.segments.append(new_segment)\n\n# Update attempt count\nrun.attempt_count = len(run.attempt_history)\n\n# Save changes\nlss_parser.save_lss_file(run, \"modified_run.lss\")\n```\n\n## API Reference\n\n### Main Functions\n\n#### `load_lss_file(file_path: Union[str, Path]) -> Run`\nLoads and parses an LSS file into a Run object.\n\n**Parameters:**\n- `file_path`: Path to the .lss file\n\n**Returns:**\n- `Run` object containing all parsed data\n\n**Raises:**\n- `FileNotFoundError`: If the file doesn't exist\n- `ValueError`: If the file is not a valid LSS file\n- `xml.etree.ElementTree.ParseError`: If the XML is malformed\n\n#### `save_lss_file(run: Run, file_path: Union[str, Path]) -> None`\nSaves a Run object to an LSS file.\n\n**Parameters:**\n- `run`: Run object to save\n- `file_path`: Path where to save the .lss file\n\n**Raises:**\n- `ValueError`: If the file path is invalid\n\n### Data Models\n\nAll data models are implemented using Pydantic's `BaseModel` and include:\n- Type annotations with automatic validation\n- Default values where appropriate\n- Automatic data conversion and validation\n- JSON serialization/deserialization support\n- IDE support with full type hints\n\n## Error Handling\n\nThe module includes comprehensive error handling:\n\n```python\nimport lss_parser\n\ntry:\n run = lss_parser.load_lss_file(\"nonexistent.lss\")\nexcept FileNotFoundError:\n print(\"File not found!\")\nexcept ValueError as e:\n print(f\"Invalid file: {e}\")\nexcept Exception as e:\n print(f\"Parse error: {e}\")\n```\n\n## Testing\n\nThe module includes comprehensive tests. Run them with:\n\n```bash\npython test_parser.py\n```\n\n## Demo\n\nSee `demo.py` for a comprehensive demonstration of all features:\n\n```bash\npython demo.py\n```\n\n## File Format Support\n\nThis parser supports LiveSplit LSS files version 1.7.0 and should be compatible with other versions. The parser handles:\n\n- Game metadata (name, category, platform, region)\n- Attempt history with timestamps and timing data\n- Segment information with split times and history\n- Auto-splitter settings\n- Custom variables and platform-specific data\n\n## License\n\nThis module is provided as-is for educational and personal use. It is not affiliated with LiveSplit or the LiveSplit development team.\n\n## Contributing\n\nThis is a complete, self-contained module designed for parsing LSS files. Feel free to extend it for your specific needs.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A Python module for parsing and manipulating LiveSplit .lss files",
"version": "0.1.0",
"project_urls": {
"Bug Reports": "https://github.com/mushroomsuprise/lss-parser/issues",
"Homepage": "https://github.com/mushroomsuprise/lss-parser",
"Source": "https://github.com/mushroomsuprise/lss-parser"
},
"split_keywords": [
"livesplit",
" lss",
" parser",
" speedrunning",
" xml"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "63bd01694fb41b065103663b2f5d78fdc4304db82e65e6ffa7bcce9835d98b50",
"md5": "bb3050f455dee8f7e0d989dc3b5ebece",
"sha256": "0b9581410111356ef63347bfb9dc6c157eff5a22a4274224be86e2aef87d6e9c"
},
"downloads": -1,
"filename": "lss_parser-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "bb3050f455dee8f7e0d989dc3b5ebece",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 10084,
"upload_time": "2025-07-12T22:10:56",
"upload_time_iso_8601": "2025-07-12T22:10:56.291155Z",
"url": "https://files.pythonhosted.org/packages/63/bd/01694fb41b065103663b2f5d78fdc4304db82e65e6ffa7bcce9835d98b50/lss_parser-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "95c56204b158364ecb31d324d769cad25983ff4ac451f0d4bddcb55913c560e3",
"md5": "c6c080ec127d23f2e0b7ed51f5c546f2",
"sha256": "6e8f46f1aa320a753db62f3d83cef399acde8de16ccda5f73b562ecf92f7b250"
},
"downloads": -1,
"filename": "lss_parser-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "c6c080ec127d23f2e0b7ed51f5c546f2",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 57655,
"upload_time": "2025-07-12T22:10:57",
"upload_time_iso_8601": "2025-07-12T22:10:57.522863Z",
"url": "https://files.pythonhosted.org/packages/95/c5/6204b158364ecb31d324d769cad25983ff4ac451f0d4bddcb55913c560e3/lss_parser-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-12 22:10:57",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "mushroomsuprise",
"github_project": "lss-parser",
"github_not_found": true,
"lcname": "lss-parser"
}