Jubeat Analyser formats debug
This commit is contained in:
parent
29c380588a
commit
6bae083a57
@ -1,9 +1,8 @@
|
||||
"""Command Line Interface"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import click
|
||||
from path import Path
|
||||
|
||||
from jubeatools.formats import DUMPERS, LOADERS
|
||||
from jubeatools.formats.enum import JUBEAT_ANALYSER_FORMATS
|
||||
@ -27,8 +26,8 @@ from jubeatools.formats.guess import guess_format
|
||||
)
|
||||
def convert(src, dst, output_format, **kwargs):
|
||||
"""Convert SRC to DST using the format specified by -f"""
|
||||
|
||||
input_format = guess_format(Path(src))
|
||||
click.echo(f"Detected input file format : {input_format}")
|
||||
|
||||
try:
|
||||
loader = LOADERS[input_format]
|
||||
@ -40,7 +39,7 @@ def convert(src, dst, output_format, **kwargs):
|
||||
except KeyError:
|
||||
raise ValueError(f"Unsupported output format : {input_format}")
|
||||
|
||||
song = loader(src)
|
||||
song = loader(Path(src))
|
||||
|
||||
extra_args = {}
|
||||
if output_format in JUBEAT_ANALYSER_FORMATS:
|
||||
@ -49,7 +48,6 @@ def convert(src, dst, output_format, **kwargs):
|
||||
files = dumper(song, Path(dst), **extra_args)
|
||||
|
||||
for path, contents in files.items():
|
||||
path.makedirs_p()
|
||||
with path.open("wb") as f:
|
||||
f.write(contents)
|
||||
|
||||
|
@ -15,7 +15,7 @@ def guess_format(path: Path) -> Format:
|
||||
try:
|
||||
with path.open() as f:
|
||||
obj = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
except (json.JSONDecodeError, UnicodeDecodeError):
|
||||
pass
|
||||
else:
|
||||
return guess_memon_version(obj)
|
||||
|
@ -46,7 +46,7 @@ from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS
|
||||
|
||||
AnyNote = Union[TapNote, LongNote, LongNoteEnd]
|
||||
|
||||
EMPTY_BEAT_SYMBOL = "-" # U+0FF0D : FULLWIDTH HYPHEN-MINUS
|
||||
EMPTY_BEAT_SYMBOL = '−' # U+02212 : MINUS SIGN
|
||||
EMPTY_POSITION_SYMBOL = "□" # U+025A1 : WHITE SQUARE
|
||||
|
||||
|
||||
@ -252,7 +252,7 @@ def _dump_memo_chart(
|
||||
file.write(f"// Converted using jubeatools {__version__}\n")
|
||||
file.write(f"// https://github.com/Stepland/jubeatools\n\n")
|
||||
for _, section in sections.items():
|
||||
file.write(section.render(circle_free) + "\n")
|
||||
file.write(section.render(circle_free) + "\n\n")
|
||||
|
||||
return file
|
||||
|
||||
|
@ -47,7 +47,7 @@ from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS
|
||||
|
||||
AnyNote = Union[TapNote, LongNote, LongNoteEnd]
|
||||
|
||||
EMPTY_BEAT_SYMBOL = "-" # U+0FF0D : FULLWIDTH HYPHEN-MINUS
|
||||
EMPTY_BEAT_SYMBOL = "−" # U+02212 : MINUS SIGN
|
||||
EMPTY_POSITION_SYMBOL = "□" # U+025A1 : WHITE SQUARE
|
||||
|
||||
|
||||
|
@ -49,7 +49,7 @@ from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS
|
||||
|
||||
AnyNote = Union[TapNote, LongNote, LongNoteEnd]
|
||||
|
||||
EMPTY_BEAT_SYMBOL = "-" # U+0FF0D : FULLWIDTH HYPHEN-MINUS
|
||||
EMPTY_BEAT_SYMBOL = '−' # U+02212 : MINUS SIGN
|
||||
EMPTY_POSITION_SYMBOL = "□" # U+025A1 : WHITE SQUARE
|
||||
|
||||
|
||||
|
@ -247,28 +247,39 @@ def _long_note_tail_value_v0(note: LongNote) -> int:
|
||||
f"memon cannot represent a long note with its tail starting ({dx}, {dy}) away from the note"
|
||||
) from None
|
||||
|
||||
def _get_timing(song: Song) -> Timing:
|
||||
if song.global_timing is not None:
|
||||
return song.global_timing
|
||||
else:
|
||||
return next(
|
||||
chart.timing
|
||||
for chart in song.charts.values()
|
||||
if chart is not None
|
||||
)
|
||||
|
||||
def _raise_if_unfit_for_v0(song: Song, version: str) -> None:
|
||||
|
||||
"""Raises an exception if the Song object is ill-formed or contains information
|
||||
that cannot be represented in a memon v0.x.x file (includes legacy)"""
|
||||
that cannot be represented in a memon v0.x.y file (includes legacy)"""
|
||||
|
||||
if any(chart.timing is not None for chart in song.charts.values()):
|
||||
if len(set(chart.timing for chart in song.charts.values() if chart is not None)) > 1:
|
||||
raise ValueError(
|
||||
f"memon:{version} cannot represent a song with per-chart timing"
|
||||
)
|
||||
|
||||
if song.global_timing is None and all(chart.timing is None for chart in song.charts.values()):
|
||||
raise ValueError("The song has no timing information")
|
||||
|
||||
if song.global_timing is None:
|
||||
raise ValueError("The song has no global timing information")
|
||||
timing = _get_timing(song)
|
||||
|
||||
number_of_timing_events = len(song.global_timing.events)
|
||||
number_of_timing_events = len(timing.events)
|
||||
if number_of_timing_events != 1:
|
||||
if number_of_timing_events == 0:
|
||||
raise ValueError("The song has no BPM")
|
||||
else:
|
||||
raise ValueError(f"memon:{version} does not handle BPM changes")
|
||||
|
||||
event = song.global_timing.events[0]
|
||||
event = timing.events[0]
|
||||
if event.BPM <= 0:
|
||||
raise ValueError(f"memon:{version} only accepts strictly positive BPMs")
|
||||
|
||||
@ -320,9 +331,11 @@ def _dump_memon_note_v0(
|
||||
return memon_note
|
||||
|
||||
|
||||
|
||||
def dump_memon_legacy(song: Song, path: Path) -> Dict[Path, bytes]:
|
||||
|
||||
_raise_if_unfit_for_v0(song, "legacy")
|
||||
timing = _get_timing(song)
|
||||
|
||||
memon = {
|
||||
"metadata": {
|
||||
@ -330,8 +343,8 @@ def dump_memon_legacy(song: Song, path: Path) -> Dict[Path, bytes]:
|
||||
"artist": song.metadata.artist,
|
||||
"music path": str(song.metadata.audio),
|
||||
"jacket path": str(song.metadata.cover),
|
||||
"BPM": song.global_timing.events[0].BPM,
|
||||
"offset": -song.global_timing.beat_zero_offset,
|
||||
"BPM": timing.events[0].BPM,
|
||||
"offset": -timing.beat_zero_offset,
|
||||
},
|
||||
"data": [],
|
||||
}
|
||||
@ -361,7 +374,8 @@ def dump_memon_legacy(song: Song, path: Path) -> Dict[Path, bytes]:
|
||||
|
||||
def dump_memon_0_1_0(song: Song, path: Path) -> Dict[Path, bytes]:
|
||||
|
||||
_raise_if_unfit_for_v0(song, "legacy")
|
||||
_raise_if_unfit_for_v0(song, "v0.1.0")
|
||||
timing= _get_timing(song)
|
||||
|
||||
memon = {
|
||||
"version": "0.1.0",
|
||||
@ -370,8 +384,8 @@ def dump_memon_0_1_0(song: Song, path: Path) -> Dict[Path, bytes]:
|
||||
"artist": song.metadata.artist,
|
||||
"music path": str(song.metadata.audio),
|
||||
"album cover path": str(song.metadata.cover),
|
||||
"BPM": song.global_timing.events[0].BPM,
|
||||
"offset": -song.global_timing.beat_zero_offset,
|
||||
"BPM": timing.events[0].BPM,
|
||||
"offset": -timing.beat_zero_offset,
|
||||
},
|
||||
"data": dict(),
|
||||
}
|
||||
@ -396,7 +410,8 @@ def dump_memon_0_1_0(song: Song, path: Path) -> Dict[Path, bytes]:
|
||||
|
||||
def dump_memon_0_2_0(song: Song, path: Path) -> Dict[Path, bytes]:
|
||||
|
||||
_raise_if_unfit_for_v0(song, "legacy")
|
||||
_raise_if_unfit_for_v0(song, "v0.2.0")
|
||||
timing = _get_timing(song)
|
||||
|
||||
memon = {
|
||||
"version": "0.2.0",
|
||||
@ -405,8 +420,8 @@ def dump_memon_0_2_0(song: Song, path: Path) -> Dict[Path, bytes]:
|
||||
"artist": song.metadata.artist,
|
||||
"music path": str(song.metadata.audio),
|
||||
"album cover path": str(song.metadata.cover),
|
||||
"BPM": song.global_timing.events[0].BPM,
|
||||
"offset": -song.global_timing.beat_zero_offset,
|
||||
"BPM": timing.events[0].BPM,
|
||||
"offset": -timing.beat_zero_offset,
|
||||
},
|
||||
"data": {},
|
||||
}
|
||||
@ -433,4 +448,4 @@ def dump_memon_0_2_0(song: Song, path: Path) -> Dict[Path, bytes]:
|
||||
else:
|
||||
filepath = path
|
||||
|
||||
return {filepath: _dump_to_json(memon)}
|
||||
return {filepath: _dump_to_json(memon)}
|
@ -123,7 +123,7 @@ class LongNote:
|
||||
yield position
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class BPMEvent:
|
||||
time: BeatsTime
|
||||
BPM: Decimal
|
||||
@ -134,6 +134,12 @@ class Timing:
|
||||
events: List[BPMEvent]
|
||||
beat_zero_offset: SecondsTime
|
||||
|
||||
def __hash__(self):
|
||||
return hash((
|
||||
tuple(self.events),
|
||||
self.beat_zero_offset,
|
||||
))
|
||||
|
||||
|
||||
@dataclass
|
||||
class Chart:
|
||||
|
Loading…
Reference in New Issue
Block a user