-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from circleguard/osrparse
port circleparse changes
- Loading branch information
Showing
11 changed files
with
273 additions
and
180 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
.vscode/ | ||
*.DS_Store | ||
|
||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,64 +1,68 @@ | ||
[![Build Status](https://travis-ci.org/kszlim/osu-replay-parser.svg?branch=master)](https://travis-ci.org/kszlim/osu-replay-parser) | ||
# osrparse, a parser for osu replays in Python | ||
[![PyPi version](https://badge.fury.io/py/osrparse.svg)](https://pypi.org/project/osrparse/) | ||
[![Build Status](https://travis-ci.org/kszlim/osu-replay-parse.svg?branch=master)](https://travis-ci.org/kszlim/osu-replay-parser) | ||
|
||
This is a parser for osu! rhythm game replays as described by https://osu.ppy.sh/wiki/Osr_(file_format) | ||
# osrparse, a python parser for osu! replays | ||
|
||
This is a parser for osu! replay files (.osr) as described by <https://osu.ppy.sh/wiki/en/osu%21_File_Formats/Osr_%28file_format%29>. | ||
|
||
## Installation | ||
To install osrparse, simply: | ||
``` | ||
$ pip install osrparse | ||
|
||
To install, simply: | ||
|
||
```sh | ||
pip install osrparse | ||
``` | ||
|
||
## Documentation | ||
|
||
To parse a replay from a filepath: | ||
|
||
```python | ||
from osrparse import parse_replay_file | ||
|
||
#returns instance of Replay | ||
parse_replay_file("path_to_osr.osr") | ||
# returns a Replay object | ||
parse_replay_file("path/to/osr.osr") | ||
``` | ||
|
||
To parse a replay from a bytestring: | ||
To parse a replay from an lzma string (such as the one returned from the `/get_replay` osu! api endpoint): | ||
|
||
```python | ||
from osrparse import parse_replay | ||
|
||
#returns instance of Replay given the replay data encoded as a bytestring | ||
parse_replay(byteString) | ||
# returns a Replay object that only has a `play_data` attribute | ||
parse_replay(lzma_string, pure_lzma=True) | ||
``` | ||
|
||
To check for a gamemode: | ||
```python | ||
from osrparse.enums import GameMode | ||
if replay.game_mode is GameMode.Standard: | ||
print("This is GameMode Standard indeed!") | ||
``` | ||
Note that if you use the `/get_replay` endpoint to retrieve a replay, you must decode the response before passing it to osrparse, as the response is encoded in base 64 by default. | ||
|
||
Replay objects provide the following fields: | ||
|
||
Replay instances provide these fields | ||
```python | ||
self.game_mode #GameMode enum | ||
self.game_version #Integer | ||
self.beatmap_hash #String | ||
self.player_name #String | ||
self.replay_hash #String | ||
self.number_300s #Integer | ||
self.number_100s #Integer | ||
self.number_50s #Integer | ||
self.gekis #Integer | ||
self.katus #Integer | ||
self.misses #Integer | ||
self.score #Integer | ||
self.max_combo #Integer | ||
self.is_perfect_combo #Boolean | ||
self.mod_combination #frozenset of Mods | ||
self.life_bar_graph #String, unparsed as of now | ||
self.timestamp #Python Datetime object | ||
self.play_data #List of ReplayEvent instances | ||
self.game_mode # GameMode enum | ||
self.game_version # int | ||
self.beatmap_hash # str | ||
self.player_name # str | ||
self.replay_hash # str | ||
self.number_300s # int | ||
self.number_100s # int | ||
self.number_50s # int | ||
self.gekis # int | ||
self.katus # int | ||
self.misses # int | ||
self.score # int | ||
self.max_combo # int | ||
self.is_perfect_combo # bool | ||
self.mod_combination # Mod enum | ||
self.life_bar_graph # str, currently unparsed | ||
self.timestamp # datetime.datetime object | ||
self.play_data # list[ReplayEvent] | ||
``` | ||
|
||
ReplayEvent instances provide these fields | ||
ReplayEvent objects provide the following fields: | ||
|
||
```python | ||
self.time_since_previous_action #Integer representing time in milliseconds | ||
self.x #x axis location | ||
self.y #y axis location | ||
self.keys_pressed #bitwise sum of keys pressed, documented in OSR format page. | ||
self.time_since_previous_action # int (in milliseconds) | ||
self.x # x axis location | ||
self.y # y axis location | ||
self.keys_pressed # bitwise sum of keys pressed, documented in OSR format page | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,9 @@ | ||
from .replay import parse_replay_file, parse_replay | ||
from osrparse.enums import GameMode, Mod | ||
from osrparse.parse import parse_replay_file, parse_replay | ||
from osrparse.replay import Replay, ReplayEvent | ||
|
||
__version__ = "4.0.0" | ||
|
||
|
||
__all__ = ["GameMode", "Mod", "parse_replay_file", "parse_replay", "Replay", | ||
"ReplayEvent"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,41 @@ | ||
from enum import Enum | ||
|
||
from enum import Enum, IntFlag | ||
|
||
class GameMode(Enum): | ||
Standard = 0 | ||
Taiko = 1 | ||
CatchTheBeat = 2 | ||
Osumania = 3 | ||
|
||
STD = 0 | ||
TAIKO = 1 | ||
CTB = 2 | ||
MANIA = 3 | ||
|
||
class Mod(Enum): | ||
NoMod = 0 | ||
NoFail = 1 | ||
Easy = 2 | ||
NoVideo = 4 | ||
Hidden = 8 | ||
HardRock = 16 | ||
SuddenDeath = 32 | ||
DoubleTime = 64 | ||
Relax = 128 | ||
HalfTime = 256 | ||
Nightcore = 512 | ||
Flashlight = 1024 | ||
Autoplay = 2048 | ||
SpunOut = 4096 | ||
Autopilot = 8192 | ||
Perfect = 16384 | ||
Key4 = 32768 | ||
Key5 = 65536 | ||
Key6 = 131072 | ||
Key7 = 262144 | ||
Key8 = 524288 | ||
keyMod = 1015808 | ||
FadeIn = 1048576 | ||
Random = 2097152 | ||
LastMod = 4194304 | ||
TargetPractice = 8388608 | ||
Key9 = 16777216 | ||
Coop = 33554432 | ||
Key1 = 67108864 | ||
Key3 = 134217728 | ||
Key2 = 268435456 | ||
class Mod(IntFlag): | ||
NoMod = 0, | ||
NoFail = 1 << 0, | ||
Easy = 1 << 1, | ||
TouchDevice = 1 << 2, | ||
Hidden = 1 << 3, | ||
HardRock = 1 << 4, | ||
SuddenDeath = 1 << 5, | ||
DoubleTime = 1 << 6, | ||
Relax = 1 << 7, | ||
HalfTime = 1 << 8, | ||
Nightcore = 1 << 9, | ||
Flashlight = 1 << 10, | ||
Autoplay = 1 << 11, | ||
SpunOut = 1 << 12, | ||
Autopilot = 1 << 13, | ||
Perfect = 1 << 14, | ||
Key4 = 1 << 15, | ||
Key5 = 1 << 16, | ||
Key6 = 1 << 17, | ||
Key7 = 1 << 18, | ||
Key8 = 1 << 19, | ||
FadeIn = 1 << 20, | ||
Random = 1 << 21, | ||
Cinema = 1 << 22, | ||
Target = 1 << 23, | ||
Key9 = 1 << 24, | ||
KeyCoop = 1 << 25, | ||
Key1 = 1 << 26, | ||
Key3 = 1 << 27, | ||
Key2 = 1 << 28, | ||
ScoreV2 = 1 << 29, | ||
Mirror = 1 << 30 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import os | ||
from typing import Union | ||
|
||
from osrparse.replay import Replay | ||
|
||
def parse_replay(replay_data: str, pure_lzma: bool = False, decompressed_lzma: bool = False) -> Replay: | ||
""" | ||
Parses a Replay from the given replay data. | ||
Args: | ||
String replay_data: The replay data from either parsing an osr file or from the api get_replay endpoint. | ||
Boolean pure_lzma: Whether replay_data conatins the entirety of an osr file, or only the lzma compressed | ||
data containing the cursor movements and keyboard presses of the player. | ||
If replay data was loaded from an osr, this value should be False, as an osr contains | ||
more information than just the lzma, such as username and game version (see | ||
https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osr_(file_format)). If replay data | ||
was retrieved from the api, this value should be True, as the api only | ||
returns the lzma data (see https://github.com/ppy/osu-api/wiki#apiget_replay) | ||
Boolean decompressed_lzma: Whether replay_data is compressed lzma, or decompressed | ||
(and decoded to ascii) lzma. For example, the following calls are equivalent: | ||
``` | ||
>>> osrparse.parse_replay(lzma_string, pure_lzma=True) | ||
``` | ||
and | ||
``` | ||
>>> lzma_string = lzma.decompress(lzma_string).decode("ascii") | ||
>>> osrparse.parse_replay(lzma_string, pure_lzma=True, decompressed_lzma=True) | ||
``` | ||
This parameter only has an affect if ``pure_lzma`` is ``True``. | ||
Returns: | ||
A Replay object with the fields specific in the Replay's init method. If pure_lzma is False, all fields will | ||
be filled (nonnull). If pure_lzma is True, only the play_data will be filled. | ||
""" | ||
|
||
return Replay(replay_data, pure_lzma, decompressed_lzma) | ||
|
||
def parse_replay_file(replay_path: Union[os.PathLike, str], pure_lzma: bool = False) -> Replay: | ||
""" | ||
Parses a Replay from the file at the given path. | ||
Args: | ||
[String or Path]: A pathlike object representing the absolute path to the file to parse data from. | ||
Boolean pure_lzma: False if the file contains data equivalent to an osr file (or is itself an osr file), | ||
and True if the file contains only lzma data. See parse_replay documentation for | ||
more information on the difference between these two and how each affect the | ||
fields in the final Replay object. | ||
""" | ||
|
||
with open(replay_path, 'rb') as f: | ||
data = f.read() | ||
return parse_replay(data, pure_lzma) |
Oops, something went wrong.