diff --git a/CHANGELOG.md b/CHANGELOG.md index 88c948d..d68b989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,20 @@ # v1.4.0 ## Added -- Jubeatools can now handle HAKUs, in the following formats : +- Jubeatools can now handle HAKUs in the following formats : - [memon:v1.0.0] -- [memon] 🎉 inital support for v1.0.0 ! + - [eve] + - [jbsq] +- [memon] + - 🎉 inital support for v1.0.0 ! + - `--merge` option allows for several memon files to be merged when + jubeatools is called on a folder ## Changed - Improved the merging procedure for song objects +- Re-enable calling the CLI on a folder, this was disabled for some reason ? +- The song class now uses a regular dict to map difficuty names to chart + objects, dissalowing files with duplicate difficulties (`memon:legacy` was the + only format that *technically* supported this anyway, I conscider it an edge + case not really worth handling) # v1.3.0 ## Added diff --git a/jubeatools/cli/cli.py b/jubeatools/cli/cli.py index 7300e51..7677924 100644 --- a/jubeatools/cli/cli.py +++ b/jubeatools/cli/cli.py @@ -5,21 +5,23 @@ from typing import Any, Dict, Optional import click -from jubeatools.formats import DUMPERS, LOADERS -from jubeatools.formats.enum import Format +from jubeatools.formats import DUMPERS, LOADERS, Format from jubeatools.formats.guess import guess_format from .helpers import dumper_option, loader_option @click.command() -@click.argument("src", type=click.Path(exists=True, dir_okay=False)) +@click.argument("src", type=click.Path(exists=True, dir_okay=True)) @click.argument("dst", type=click.Path()) @click.option( "--input-format", "input_format", type=click.Choice(list(f._value_ for f in LOADERS.keys())), - help="Input file format", + help=( + "Force jubeatools to read the input file/folder as the given format." + "If this option is not used jubeatools will try to guess the format" + ), ) @click.option( "-f", @@ -45,6 +47,11 @@ from .helpers import dumper_option, loader_option "the nearest 1/beat_snap beat" ), ) +@loader_option( + "--merge", + is_flag=True, + help="For memon, if called on a folder, merge all the .memon files found", +) def convert( src: str, dst: str, diff --git a/jubeatools/cli/tests/data/memon_merge/Sky Bus For Hire ADV.memon b/jubeatools/cli/tests/data/memon_merge/Sky Bus For Hire ADV.memon new file mode 100644 index 0000000..a0cc189 --- /dev/null +++ b/jubeatools/cli/tests/data/memon_merge/Sky Bus For Hire ADV.memon @@ -0,0 +1,25 @@ +{ + "data": { + "ADV": { + "level": 5, + "notes": [ + { + "l": 0, + "n": 1, + "p": 0, + "t": 1680 + } + ], + "resolution": 240 + } + }, + "metadata": { + "BPM": 180.28199768066406, + "album cover path": "2a03puritans.png", + "artist": "commandycan", + "music path": "Sky Bus For Hire.ogg", + "offset": -0.028, + "song title": "Sky Bus For Hire" + }, + "version": "0.1.0" +} diff --git a/jubeatools/cli/tests/data/memon_merge/Sky Bus For Hire BSC.memon b/jubeatools/cli/tests/data/memon_merge/Sky Bus For Hire BSC.memon new file mode 100644 index 0000000..80bc59b --- /dev/null +++ b/jubeatools/cli/tests/data/memon_merge/Sky Bus For Hire BSC.memon @@ -0,0 +1,25 @@ +{ + "data": { + "BSC": { + "level": 1, + "notes": [ + { + "l": 0, + "n": 2, + "p": 0, + "t": 1680 + } + ], + "resolution": 240 + } + }, + "metadata": { + "BPM": 180.28199768066406, + "album cover path": "2a03puritans.png", + "artist": "commandycan", + "music path": "Sky Bus For Hire.ogg", + "offset": -0.028, + "song title": "Sky Bus For Hire" + }, + "version": "0.1.0" +} diff --git a/jubeatools/cli/tests/data/memon_merge/Sky Bus For Hire EXT.memon b/jubeatools/cli/tests/data/memon_merge/Sky Bus For Hire EXT.memon new file mode 100644 index 0000000..8327f8a --- /dev/null +++ b/jubeatools/cli/tests/data/memon_merge/Sky Bus For Hire EXT.memon @@ -0,0 +1,25 @@ +{ + "data": { + "EXT": { + "level": 10, + "notes": [ + { + "l": 0, + "n": 15, + "p": 0, + "t": 1680 + } + ], + "resolution": 240 + } + }, + "metadata": { + "BPM": 180.28199768066406, + "album cover path": "2a03puritans.png", + "artist": "commandycan", + "music path": "Sky Bus For Hire.ogg", + "offset": -0.028, + "song title": "Sky Bus For Hire" + }, + "version": "0.1.0" +} diff --git a/jubeatools/cli/tests/data/memon_merge/__init__.py b/jubeatools/cli/tests/data/memon_merge/__init__.py new file mode 100644 index 0000000..de5849e --- /dev/null +++ b/jubeatools/cli/tests/data/memon_merge/__init__.py @@ -0,0 +1,2 @@ +"""This file is here so the test code can use importlib as a portable way to +open test data in this folder""" diff --git a/jubeatools/cli/tests/test_cli.py b/jubeatools/cli/tests/test_cli.py index 7ad93e1..c520ff8 100644 --- a/jubeatools/cli/tests/test_cli.py +++ b/jubeatools/cli/tests/test_cli.py @@ -1,15 +1,17 @@ from importlib import resources +from pathlib import Path from click.testing import CliRunner +from jubeatools import song as jbt +from jubeatools.formats import LOADERS, Format + from ..cli import convert from . import data def test_that_ommiting_beat_snap_works() -> None: - """ - As pointed out by https://github.com/Stepland/jubeatools/issues/17 - """ + """As pointed out by https://github.com/Stepland/jubeatools/issues/17""" runner = CliRunner() with runner.isolated_filesystem(), resources.path( data, "Life Without You.eve" @@ -20,3 +22,50 @@ def test_that_ommiting_beat_snap_works() -> None: if result.exception: raise result.exception assert result.exit_code == 0 + + +def test_that_is_flag_works_the_way_intended() -> None: + """It's unclear to me what the default value is for an option with + is_flag=True""" + with resources.path(data, "Life Without You.eve") as p: + called_with_the_flag = convert.make_context( + "convert", + [str(p.resolve(strict=True)), "out.txt", "-f", "memo2", "--circlefree"], + ) + assert called_with_the_flag.params["dumper_options"]["circle_free"] is True + + called_without_the_flag = convert.make_context( + "convert", [str(p.resolve(strict=True)), "out.txt", "-f", "memo2"] + ) + dumper_options = called_without_the_flag.params.get("dumper_options") + if dumper_options is not None: + circle_free = dumper_options.get("circle_free") + assert not circle_free + + +def test_that_the_merge_option_works_for_memon_files() -> None: + runner = CliRunner() + with runner.isolated_filesystem(), resources.path(data, "memon_merge") as p: + result = runner.invoke( + convert, + [ + "--input-format", + "memon:v0.1.0", + str(p.resolve(strict=True)), + "--merge", + "out.memon", + "-f", + "memon:v0.1.0", + ], + ) + if result.exception: + raise result.exception + assert result.exit_code == 0 + + memon_loader = LOADERS[Format.MEMON_0_1_0] + bsc = memon_loader(p / "Sky Bus For Hire BSC.memon") + adv = memon_loader(p / "Sky Bus For Hire ADV.memon") + ext = memon_loader(p / "Sky Bus For Hire EXT.memon") + merged_by_cli = LOADERS[Format.MEMON_0_1_0](Path("out.memon")) + merged_with_python = jbt.Song.from_monochart_instances(bsc, adv, ext) + assert merged_by_cli == merged_with_python diff --git a/jubeatools/formats/__init__.py b/jubeatools/formats/__init__.py index 56e6d1c..75bf893 100644 --- a/jubeatools/formats/__init__.py +++ b/jubeatools/formats/__init__.py @@ -1,5 +1,5 @@ """ Module containing all the load/dump code for all file formats """ -from .enum import Format +from .format_names import Format from .loaders_and_dumpers import DUMPERS, LOADERS diff --git a/jubeatools/formats/enum.py b/jubeatools/formats/enum.py deleted file mode 100644 index b4b198a..0000000 --- a/jubeatools/formats/enum.py +++ /dev/null @@ -1,16 +0,0 @@ -from enum import Enum - - -class Format(str, Enum): - EVE = "eve" - JBSQ = "jbsq" - MALODY = "malody" - MEMON_LEGACY = "memon:legacy" - MEMON_0_1_0 = "memon:v0.1.0" - MEMON_0_2_0 = "memon:v0.2.0" - MEMON_0_3_0 = "memon:v0.3.0" - MEMON_1_0_0 = "memon:v1.0.0" - MONO_COLUMN = "mono-column" - MEMO = "memo" - MEMO_1 = "memo1" - MEMO_2 = "memo2" diff --git a/jubeatools/formats/guess.py b/jubeatools/formats/guess.py index fc4821c..364e3b2 100644 --- a/jubeatools/formats/guess.py +++ b/jubeatools/formats/guess.py @@ -2,7 +2,7 @@ import json import re from pathlib import Path -from .enum import Format +from .format_names import Format def guess_format(path: Path) -> Format: diff --git a/jubeatools/formats/jubeat_analyser/memo/load.py b/jubeatools/formats/jubeat_analyser/memo/load.py index 498e5c7..2a4d5a7 100644 --- a/jubeatools/formats/jubeat_analyser/memo/load.py +++ b/jubeatools/formats/jubeat_analyser/memo/load.py @@ -349,4 +349,4 @@ def _load_memo_file(lines: List[str]) -> Song: def load_memo(path: Path, **kwargs: Any) -> Song: files = load_folder(path) charts = [_load_memo_file(lines) for _, lines in files.items()] - return Song.from_monochart_instances(charts) + return Song.from_monochart_instances(*charts) diff --git a/jubeatools/formats/jubeat_analyser/memo1/load.py b/jubeatools/formats/jubeat_analyser/memo1/load.py index 2667c26..43f6f57 100644 --- a/jubeatools/formats/jubeat_analyser/memo1/load.py +++ b/jubeatools/formats/jubeat_analyser/memo1/load.py @@ -340,4 +340,4 @@ def _load_memo1_file(lines: List[str]) -> Song: def load_memo1(path: Path, **kwargs: Any) -> Song: files = load_folder(path) charts = [_load_memo1_file(lines) for _, lines in files.items()] - return Song.from_monochart_instances(charts) + return Song.from_monochart_instances(*charts) diff --git a/jubeatools/formats/jubeat_analyser/memo2/load.py b/jubeatools/formats/jubeat_analyser/memo2/load.py index ec48ffe..6009586 100644 --- a/jubeatools/formats/jubeat_analyser/memo2/load.py +++ b/jubeatools/formats/jubeat_analyser/memo2/load.py @@ -459,4 +459,4 @@ def _load_memo2_file(lines: List[str]) -> Song: def load_memo2(path: Path, **kwargs: Any) -> Song: files = load_folder(path) charts = [_load_memo2_file(lines) for _, lines in files.items()] - return Song.from_monochart_instances(charts) + return Song.from_monochart_instances(*charts) diff --git a/jubeatools/formats/jubeat_analyser/mono_column/load.py b/jubeatools/formats/jubeat_analyser/mono_column/load.py index 98f63ae..18d52fd 100644 --- a/jubeatools/formats/jubeat_analyser/mono_column/load.py +++ b/jubeatools/formats/jubeat_analyser/mono_column/load.py @@ -246,7 +246,7 @@ class MonoColumnParser(JubeatAnalyserParser): def load_mono_column(path: Path, **kwargs: Any) -> Song: files = load_folder(path) charts = [_load_mono_column_file(lines) for _, lines in files.items()] - return Song.from_monochart_instances(charts) + return Song.from_monochart_instances(*charts) def _load_mono_column_file(lines: List[str]) -> Song: diff --git a/jubeatools/formats/jubeat_analyser/tests/memo/test_memo.py b/jubeatools/formats/jubeat_analyser/tests/memo/test_memo.py index 7275340..a14880f 100644 --- a/jubeatools/formats/jubeat_analyser/tests/memo/test_memo.py +++ b/jubeatools/formats/jubeat_analyser/tests/memo/test_memo.py @@ -7,7 +7,7 @@ from hypothesis import note as hypothesis_note from hypothesis import strategies as st from jubeatools import song -from jubeatools.formats.enum import Format +from jubeatools.formats.format_names import Format from jubeatools.formats.jubeat_analyser.memo.dump import _dump_memo_chart from jubeatools.formats.jubeat_analyser.memo.load import MemoParser from jubeatools.testutils import strategies as jbst diff --git a/jubeatools/formats/konami/dump_tools.py b/jubeatools/formats/konami/dump_tools.py index c44097e..37a7a9e 100644 --- a/jubeatools/formats/konami/dump_tools.py +++ b/jubeatools/formats/konami/dump_tools.py @@ -1,7 +1,7 @@ import math from fractions import Fraction from functools import singledispatch -from typing import List +from typing import List, Optional, Set from more_itertools import numeric_range @@ -11,10 +11,12 @@ from jubeatools.formats.timemap import TimeMap from .commons import AnyNote, Command, Event, bpm_to_value, ticks_at_beat -def make_events_from_chart(notes: List[AnyNote], timing: song.Timing) -> List[Event]: +def make_events_from_chart( + notes: List[AnyNote], timing: song.Timing, hakus: Optional[Set[song.BeatsTime]] +) -> List[Event]: time_map = TimeMap.from_timing(timing) note_events = make_note_events(notes, time_map) - timing_events = make_timing_events(notes, timing, time_map) + timing_events = make_timing_events(notes, timing, hakus, time_map) return sorted(note_events + timing_events) @@ -38,14 +40,21 @@ def make_long_note_event(note: song.LongNote, time_map: TimeMap) -> Event: def make_timing_events( - notes: List[AnyNote], timing: song.Timing, time_map: TimeMap + notes: List[AnyNote], + timing: song.Timing, + hakus: Optional[Set[song.BeatsTime]], + time_map: TimeMap, ) -> List[Event]: bpm_events = [make_bpm_event(e, time_map) for e in timing.events] end_beat = choose_end_beat(notes) end_event = make_end_event(end_beat, time_map) measure_events = make_measure_events(end_beat, time_map) - beat_events = make_beat_events(end_beat, time_map) - return bpm_events + measure_events + beat_events + [end_event] + if hakus is not None: + haku_events = dump_hakus(hakus, time_map) + else: + haku_events = make_regular_hakus(end_beat, time_map) + + return bpm_events + measure_events + haku_events + [end_event] def make_bpm_event(bpm_change: song.BPMEvent, time_map: TimeMap) -> Event: @@ -94,14 +103,18 @@ def make_measure_event(beat: song.BeatsTime, time_map: TimeMap) -> Event: return Event(time=ticks, command=Command.MEASURE, value=0) -def make_beat_events(end_beat: song.BeatsTime, time_map: TimeMap) -> List[Event]: +def dump_hakus(hakus: Set[song.BeatsTime], time_map: TimeMap) -> List[Event]: + return [make_haku_event(beat, time_map) for beat in sorted(hakus)] + + +def make_regular_hakus(end_beat: song.BeatsTime, time_map: TimeMap) -> List[Event]: start = song.BeatsTime(0) stop = end_beat + song.BeatsTime(1, 2) step = song.BeatsTime(1) beats = numeric_range(start, stop, step) - return [make_beat_event(beat, time_map) for beat in beats] + return [make_haku_event(beat, time_map) for beat in beats] -def make_beat_event(beat: song.BeatsTime, time_map: TimeMap) -> Event: +def make_haku_event(beat: song.BeatsTime, time_map: TimeMap) -> Event: ticks = ticks_at_beat(beat, time_map) return Event(time=ticks, command=Command.HAKU, value=0) diff --git a/jubeatools/formats/konami/eve/dump.py b/jubeatools/formats/konami/eve/dump.py index 7f7aca3..bf12c64 100644 --- a/jubeatools/formats/konami/eve/dump.py +++ b/jubeatools/formats/konami/eve/dump.py @@ -10,8 +10,8 @@ from ..dump_tools import make_events_from_chart def _dump_eve(song: song.Song, **kwargs: dict) -> List[ChartFile]: res = [] - for dif, chart, timing in song.iter_charts_with_applicable_timing(): - events = make_events_from_chart(chart.notes, timing) + for dif, chart, timing, hakus in song.iter_charts(): + events = make_events_from_chart(chart.notes, timing, hakus) chart_text = "\n".join(e.dump() for e in events) chart_bytes = chart_text.encode("ascii") res.append(ChartFile(chart_bytes, song, dif, chart)) diff --git a/jubeatools/formats/konami/eve/load.py b/jubeatools/formats/konami/eve/load.py index d42e738..fab9e37 100644 --- a/jubeatools/formats/konami/eve/load.py +++ b/jubeatools/formats/konami/eve/load.py @@ -11,7 +11,7 @@ from ..load_tools import make_chart_from_events def load_eve(path: Path, *, beat_snap: int = 240, **kwargs: Any) -> song.Song: files = load_folder(path) charts = [_load_eve(l, p, beat_snap=beat_snap) for p, l in files.items()] - return song.Song.from_monochart_instances(charts) + return song.Song.from_monochart_instances(*charts) def load_file(path: Path) -> List[str]: diff --git a/jubeatools/formats/konami/jbsq/dump.py b/jubeatools/formats/konami/jbsq/dump.py index c83a678..9210d1c 100644 --- a/jubeatools/formats/konami/jbsq/dump.py +++ b/jubeatools/formats/konami/jbsq/dump.py @@ -15,8 +15,8 @@ from . import construct def _dump_jbsq(song: song.Song, **kwargs: dict) -> List[ChartFile]: res = [] - for dif, chart, timing in song.iter_charts_with_applicable_timing(): - events = make_events_from_chart(chart.notes, timing) + for dif, chart, timing, hakus in song.iter_charts(): + events = make_events_from_chart(chart.notes, timing, hakus) jbsq_chart = make_jbsq_chart(events, chart.notes) chart_bytes = construct.jbsq.build(jbsq_chart) res.append(ChartFile(chart_bytes, song, dif, chart)) diff --git a/jubeatools/formats/konami/jbsq/load.py b/jubeatools/formats/konami/jbsq/load.py index e7ad02c..b607188 100644 --- a/jubeatools/formats/konami/jbsq/load.py +++ b/jubeatools/formats/konami/jbsq/load.py @@ -15,7 +15,7 @@ def load_jbsq(path: Path, *, beat_snap: int = 240, **kwargs: Any) -> song.Song: load_jbsq_file(bytes_, path, beat_snap=beat_snap) for path, bytes_ in files.items() ] - return song.Song.from_monochart_instances(charts) + return song.Song.from_monochart_instances(*charts) def load_file(path: Path) -> bytes: diff --git a/jubeatools/formats/konami/load_tools.py b/jubeatools/formats/konami/load_tools.py index 415acb8..9ea50cf 100644 --- a/jubeatools/formats/konami/load_tools.py +++ b/jubeatools/formats/konami/load_tools.py @@ -1,5 +1,7 @@ from decimal import Decimal -from typing import Iterable, List +from typing import Iterable, List, Optional, Set + +from more_itertools import numeric_range from jubeatools import song from jubeatools.formats.load_tools import round_beats @@ -36,17 +38,22 @@ def make_chart_from_events(events: Iterable[Event], beat_snap: int = 240) -> son ] all_notes = sorted(tap_notes + long_notes, key=lambda n: (n.time, n.position)) timing = time_map.convert_to_timing_info(beat_snap=beat_snap) - return song.Chart(level=Decimal(0), timing=timing, notes=all_notes) + end_tick = events_by_command[Command.END].pop().time + hakus = make_hakus( + [e.time for e in events_by_command[Command.HAKU]], + end_tick, + time_map, + beat_snap, + ) + return song.Chart(level=Decimal(0), timing=timing, notes=all_notes, hakus=hakus) def make_tap_note( ticks: int, value: int, time_map: TimeMap, beat_snap: int ) -> song.TapNote: - seconds = ticks_to_seconds(ticks) - raw_beats = time_map.beats_at(seconds) - beats = round_beats(raw_beats, beat_snap) + time = beats_at_tick(ticks, time_map, beat_snap) position = song.NotePosition.from_index(value) - return song.TapNote(time=beats, position=position) + return song.TapNote(time=time, position=position) def make_long_note( @@ -67,3 +74,52 @@ def make_long_note( return song.LongNote( time=beats, position=position, duration=beats_duration, tail_tip=tail_pos ) + + +def make_hakus( + hakus: List[int], end: int, time_map: TimeMap, beat_snap: int +) -> Optional[Set[song.BeatsTime]]: + """Try to detect if the haku pattern is regular, in which case return None, + otherwise return the parsed hakus""" + roughly_rounded_hakus = make_raw_hakus(hakus, time_map, beat_snap=4) + rough_end = beats_at_tick(end, time_map, beat_snap=4) + if follows_regular_haku_pattern(roughly_rounded_hakus, rough_end): + return None + else: + return make_raw_hakus(hakus, time_map, beat_snap) + + +def make_raw_hakus( + hakus: List[int], time_map: TimeMap, beat_snap: int +) -> Set[song.BeatsTime]: + return set(beats_at_tick(haku, time_map, beat_snap) for haku in hakus) + + +def follows_regular_haku_pattern( + hakus: Set[song.BeatsTime], end_command: song.BeatsTime +) -> bool: + """Regular hakus extend at least till the END command in a 4/4 rhythm""" + if len(hakus) == 0: + return False + + start = min(hakus) + if (start % 1) != 0: + return False + + haku_end = max(hakus) + if (haku_end % 1) != 0: + return False + + if haku_end < end_command: + return False + + stop = haku_end + song.BeatsTime(1, 2) + step = song.BeatsTime(1) + regular = numeric_range(start, stop, step) + return sorted(hakus) == list(regular) + + +def beats_at_tick(tick: int, time_map: TimeMap, beat_snap: int) -> song.BeatsTime: + seconds = ticks_to_seconds(tick) + raw_beats = time_map.beats_at(seconds) + return round_beats(raw_beats, beat_snap) diff --git a/jubeatools/formats/konami/testutils.py b/jubeatools/formats/konami/testutils.py index 5f8d71f..e4fa4d9 100644 --- a/jubeatools/formats/konami/testutils.py +++ b/jubeatools/formats/konami/testutils.py @@ -12,7 +12,7 @@ simple_beat_strat = jbst.beat_time( @st.composite def eve_compatible_song(draw: st.DrawFn) -> song.Song: - """eve only keeps notes, timing info and difficulty, + """eve only keeps notes, hakus, timing info and difficulty, the precision you can get out of it is also severly limited""" diff = draw(st.sampled_from(list(song.Difficulty))) chart = draw( @@ -42,6 +42,7 @@ def eve_compatible_song(draw: st.DrawFn) -> song.Song: beat_time_strat=simple_beat_strat, ), level_strat=st.just(Decimal(0)), + hakus_strat=st.one_of(st.none(), st.sets(simple_beat_strat)), ) ) return song.Song( diff --git a/jubeatools/formats/loaders_and_dumpers.py b/jubeatools/formats/loaders_and_dumpers.py index 7aa613c..7a959c0 100644 --- a/jubeatools/formats/loaders_and_dumpers.py +++ b/jubeatools/formats/loaders_and_dumpers.py @@ -1,7 +1,7 @@ from typing import Dict from . import jubeat_analyser, konami, malody, memon -from .enum import Format +from .format_names import Format from .typing import Dumper, Loader LOADERS: Dict[Format, Loader] = { diff --git a/jubeatools/formats/malody/load.py b/jubeatools/formats/malody/load.py index dadc4ff..05a5dc9 100644 --- a/jubeatools/formats/malody/load.py +++ b/jubeatools/formats/malody/load.py @@ -18,7 +18,7 @@ from . import schema as malody def load_malody(path: Path, **kwargs: Any) -> song.Song: files = load_folder(path) charts = [load_malody_file(d) for d in files.values()] - return song.Song.from_monochart_instances(charts) + return song.Song.from_monochart_instances(*charts) def load_file(path: Path) -> Any: diff --git a/jubeatools/formats/memon/tools.py b/jubeatools/formats/memon/tools.py index 6ea2aaf..0391378 100644 --- a/jubeatools/formats/memon/tools.py +++ b/jubeatools/formats/memon/tools.py @@ -31,7 +31,7 @@ def make_memon_folder_loader(memon_loader: Callable[[Any], jbt.Song]) -> Loader: ) charts = [memon_loader(d) for d in files.values()] - return jbt.Song.from_monochart_instances(charts) + return jbt.Song.from_monochart_instances(*charts) return load diff --git a/jubeatools/formats/memon/v0/load.py b/jubeatools/formats/memon/v0/load.py index 180de09..8aaf2ef 100644 --- a/jubeatools/formats/memon/v0/load.py +++ b/jubeatools/formats/memon/v0/load.py @@ -1,7 +1,5 @@ from pathlib import Path -from typing import Any, Union - -from multidict import MultiDict +from typing import Any, Dict, Union from jubeatools import song as jbt from jubeatools.utils import none_or @@ -38,18 +36,17 @@ def _load_memon_legacy(raw_memon: Any) -> jbt.Song: events=[jbt.BPMEvent(time=jbt.BeatsTime(0), BPM=file["metadata"]["BPM"])], beat_zero_offset=jbt.SecondsTime(-file["metadata"]["offset"]), ) - charts: MultiDict[jbt.Chart] = MultiDict() + charts: Dict[str, jbt.Chart] = {} for memon_chart in file["data"]: - charts.add( - memon_chart["dif_name"], - jbt.Chart( - level=memon_chart["level"], - notes=[ - _load_memon_note_v0(note, memon_chart["resolution"]) - for note in memon_chart["notes"] - ], - ), + difficulty = memon_chart["dif_name"] + chart = jbt.Chart( + level=memon_chart["level"], + notes=[ + _load_memon_note_v0(note, memon_chart["resolution"]) + for note in memon_chart["notes"] + ], ) + charts[difficulty] = chart return jbt.Song(metadata=metadata, charts=charts, common_timing=common_timing) @@ -70,18 +67,16 @@ def _load_memon_0_1_0(raw_memon: Any) -> jbt.Song: events=[jbt.BPMEvent(time=jbt.BeatsTime(0), BPM=file["metadata"]["BPM"])], beat_zero_offset=jbt.SecondsTime(-file["metadata"]["offset"]), ) - charts: MultiDict[jbt.Chart] = MultiDict() + charts: Dict[str, jbt.Chart] = {} for difficulty, memon_chart in file["data"].items(): - charts.add( - difficulty, - jbt.Chart( - level=memon_chart["level"], - notes=[ - _load_memon_note_v0(note, memon_chart["resolution"]) - for note in memon_chart["notes"] - ], - ), + chart = jbt.Chart( + level=memon_chart["level"], + notes=[ + _load_memon_note_v0(note, memon_chart["resolution"]) + for note in memon_chart["notes"] + ], ) + charts[difficulty] = chart return jbt.Song(metadata=metadata, charts=charts, common_timing=common_timing) @@ -109,18 +104,16 @@ def _load_memon_0_2_0(raw_memon: Any) -> jbt.Song: events=[jbt.BPMEvent(time=jbt.BeatsTime(0), BPM=file["metadata"]["BPM"])], beat_zero_offset=jbt.SecondsTime(-file["metadata"]["offset"]), ) - charts: MultiDict[jbt.Chart] = MultiDict() + charts: Dict[str, jbt.Chart] = {} for difficulty, memon_chart in file["data"].items(): - charts.add( - difficulty, - jbt.Chart( - level=memon_chart["level"], - notes=[ - _load_memon_note_v0(note, memon_chart["resolution"]) - for note in memon_chart["notes"] - ], - ), + chart = jbt.Chart( + level=memon_chart["level"], + notes=[ + _load_memon_note_v0(note, memon_chart["resolution"]) + for note in memon_chart["notes"] + ], ) + charts[difficulty] = chart return jbt.Song(metadata=metadata, charts=charts, common_timing=common_timing) @@ -149,18 +142,16 @@ def _load_memon_0_3_0(raw_memon: Any) -> jbt.Song: events=[jbt.BPMEvent(time=jbt.BeatsTime(0), BPM=file["metadata"]["BPM"])], beat_zero_offset=jbt.SecondsTime(-file["metadata"]["offset"]), ) - charts: MultiDict[jbt.Chart] = MultiDict() + charts: Dict[str, jbt.Chart] = {} for difficulty, memon_chart in file["data"].items(): - charts.add( - difficulty, - jbt.Chart( - level=memon_chart["level"], - notes=[ - _load_memon_note_v0(note, memon_chart["resolution"]) - for note in memon_chart["notes"] - ], - ), + chart = jbt.Chart( + level=memon_chart["level"], + notes=[ + _load_memon_note_v0(note, memon_chart["resolution"]) + for note in memon_chart["notes"] + ], ) + charts[difficulty] = chart return jbt.Song(metadata=metadata, charts=charts, common_timing=common_timing) diff --git a/jubeatools/formats/memon/v0/test_v0.py b/jubeatools/formats/memon/v0/test_v0.py index 54fe1d7..f2ebd56 100644 --- a/jubeatools/formats/memon/v0/test_v0.py +++ b/jubeatools/formats/memon/v0/test_v0.py @@ -4,7 +4,7 @@ import hypothesis.strategies as st from hypothesis import given from jubeatools import song -from jubeatools.formats.enum import Format +from jubeatools.formats.format_names import Format from jubeatools.testutils import strategies as jbst from jubeatools.testutils.test_patterns import dump_and_load_then_compare diff --git a/jubeatools/formats/memon/v1/dump.py b/jubeatools/formats/memon/v1/dump.py index 1778586..4f87c8e 100644 --- a/jubeatools/formats/memon/v1/dump.py +++ b/jubeatools/formats/memon/v1/dump.py @@ -12,9 +12,7 @@ from ..tools import make_memon_dumper from . import schema as memon -def _dump_memon_1_0_0( - song: jbt.Song, use_fractions: bool = False, **kwargs: Any -) -> SongFile: +def _dump_memon_1_0_0(song: jbt.Song, **kwargs: Any) -> SongFile: metadata = dump_metadata(song.metadata) common_timing = dump_file_timing(song) charts = { diff --git a/jubeatools/formats/memon/v1/tests/test_v1.py b/jubeatools/formats/memon/v1/tests/test_v1.py index 1dfe907..65ac55f 100644 --- a/jubeatools/formats/memon/v1/tests/test_v1.py +++ b/jubeatools/formats/memon/v1/tests/test_v1.py @@ -5,7 +5,7 @@ import hypothesis.strategies as st from hypothesis import given from jubeatools import song -from jubeatools.formats.enum import Format +from jubeatools.formats.format_names import Format from jubeatools.testutils import strategies as jbst from jubeatools.testutils.test_patterns import dump_and_load_then_compare diff --git a/jubeatools/song.py b/jubeatools/song.py index fbc905b..ce737ea 100644 --- a/jubeatools/song.py +++ b/jubeatools/song.py @@ -17,6 +17,7 @@ from pathlib import Path from typing import ( Any, Callable, + Dict, Iterable, Iterator, List, @@ -28,8 +29,6 @@ from typing import ( Union, ) -from multidict import MultiDict - from jubeatools.utils import none_or BeatsTime = Fraction @@ -223,14 +222,13 @@ class Metadata: preview_file: Optional[Path] = None @classmethod - def permissive_merge(cls, metadatas: Iterable["Metadata"]) -> "Metadata": + def permissive_merge(cls, *metadatas: "Metadata") -> "Metadata": """Make the "sum" of all the given metadata instances, if possible. If several instances have different defined values for the same field, merging will fail. Fields with Noneor empty values (empty string or empty path) are conscidered undefined and their values can be replaced by an actual value if supplied by at least one object from the given iterable.""" - metadatas = list(metadatas) return cls( **{f.name: _get_common_value(f, metadatas) for f in fields(cls)}, ) @@ -280,18 +278,18 @@ class Song: A Song is a set of charts with associated metadata""" metadata: Metadata - charts: Mapping[str, Chart] = field(default_factory=MultiDict) + charts: Mapping[str, Chart] = field(default_factory=dict) common_timing: Optional[Timing] = None common_hakus: Optional[Set[BeatsTime]] = None @classmethod - def from_monochart_instances(cls, songs: Iterable["Song"]) -> "Song": - metadata = Metadata.permissive_merge(song.metadata for song in songs) - charts: MultiDict[Chart] = MultiDict() + def from_monochart_instances(cls, *songs: "Song") -> "Song": + metadata = Metadata.permissive_merge(*(song.metadata for song in songs)) + charts: Dict[str, Chart] = {} for song in songs: song.remove_common_timing() song.remove_common_hakus() - charts.extend(song.charts) + charts.update(song.charts) merged = cls( metadata=metadata, @@ -351,3 +349,15 @@ class Song: f"Neither song nor {dif} chart have any timing information" ) yield dif, chart, timing + + def iter_charts( + self, + ) -> Iterator[Tuple[str, Chart, Timing, Optional[Set[BeatsTime]]]]: + for dif, chart in self.charts.items(): + timing = chart.timing or self.common_timing + if timing is None: + raise ValueError( + f"Neither song nor {dif} chart have any timing information" + ) + hakus = chart.hakus if chart.hakus is not None else self.common_hakus + yield dif, chart, timing, hakus diff --git a/jubeatools/testutils/strategies.py b/jubeatools/testutils/strategies.py index eab8f8b..45096b2 100644 --- a/jubeatools/testutils/strategies.py +++ b/jubeatools/testutils/strategies.py @@ -10,7 +10,6 @@ from pathlib import Path from typing import Dict, Iterable, Optional, Set, Union import hypothesis.strategies as st -from multidict import MultiDict from jubeatools.song import ( BeatsTime, @@ -286,14 +285,9 @@ def song( chart_strat: st.SearchStrategy[Chart] = chart(), metadata_strat: st.SearchStrategy[Metadata] = metadata(), ) -> Song: - diffs = draw(diffs_strat) - charts: MultiDict[Chart] = MultiDict() - for diff_name in diffs: - charts.add(diff_name, draw(chart_strat)) - return Song( metadata=draw(metadata_strat), - charts=charts, + charts={difficulty: draw(chart_strat) for difficulty in draw(diffs_strat)}, common_timing=draw(common_timing_strat), common_hakus=draw(common_hakus_strat), ) diff --git a/jubeatools/testutils/test_patterns.py b/jubeatools/testutils/test_patterns.py index 2a43686..e2171ef 100644 --- a/jubeatools/testutils/test_patterns.py +++ b/jubeatools/testutils/test_patterns.py @@ -6,8 +6,7 @@ from typing import Callable, ContextManager, Iterator, Optional from hypothesis import note from jubeatools import song -from jubeatools.formats import DUMPERS, LOADERS -from jubeatools.formats.enum import Format +from jubeatools.formats import DUMPERS, LOADERS, Format from jubeatools.formats.guess import guess_format diff --git a/poetry.lock b/poetry.lock index 23b30bd..98a1ff3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -238,14 +238,6 @@ category = "main" optional = false python-versions = ">=3.5" -[[package]] -name = "multidict" -version = "5.2.0" -description = "multidict implementation" -category = "main" -optional = false -python-versions = ">=3.6" - [[package]] name = "mypy" version = "0.910" @@ -523,7 +515,7 @@ typing-extensions = ">=3.7.4" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "41155bca4070edc9eb6e6369890af55397251caeab4e5c2db62ef7785410853e" +content-hash = "971f32ab1478240f072615b119a1df87aebccfb26e6ee4cf013d938ff15db690" [metadata.files] atomicwrites = [ @@ -596,80 +588,6 @@ more-itertools = [ {file = "more-itertools-8.12.0.tar.gz", hash = "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"}, {file = "more_itertools-8.12.0-py3-none-any.whl", hash = "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b"}, ] -multidict = [ - {file = "multidict-5.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3822c5894c72e3b35aae9909bef66ec83e44522faf767c0ad39e0e2de11d3b55"}, - {file = "multidict-5.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:28e6d883acd8674887d7edc896b91751dc2d8e87fbdca8359591a13872799e4e"}, - {file = "multidict-5.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b61f85101ef08cbbc37846ac0e43f027f7844f3fade9b7f6dd087178caedeee7"}, - {file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9b668c065968c5979fe6b6fa6760bb6ab9aeb94b75b73c0a9c1acf6393ac3bf"}, - {file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:517d75522b7b18a3385726b54a081afd425d4f41144a5399e5abd97ccafdf36b"}, - {file = "multidict-5.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1b4ac3ba7a97b35a5ccf34f41b5a8642a01d1e55454b699e5e8e7a99b5a3acf5"}, - {file = "multidict-5.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:df23c83398715b26ab09574217ca21e14694917a0c857e356fd39e1c64f8283f"}, - {file = "multidict-5.2.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e58a9b5cc96e014ddf93c2227cbdeca94b56a7eb77300205d6e4001805391747"}, - {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f76440e480c3b2ca7f843ff8a48dc82446b86ed4930552d736c0bac507498a52"}, - {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cfde464ca4af42a629648c0b0d79b8f295cf5b695412451716531d6916461628"}, - {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0fed465af2e0eb6357ba95795d003ac0bdb546305cc2366b1fc8f0ad67cc3fda"}, - {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:b70913cbf2e14275013be98a06ef4b412329fe7b4f83d64eb70dce8269ed1e1a"}, - {file = "multidict-5.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a5635bcf1b75f0f6ef3c8a1ad07b500104a971e38d3683167b9454cb6465ac86"}, - {file = "multidict-5.2.0-cp310-cp310-win32.whl", hash = "sha256:77f0fb7200cc7dedda7a60912f2059086e29ff67cefbc58d2506638c1a9132d7"}, - {file = "multidict-5.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:9416cf11bcd73c861267e88aea71e9fcc35302b3943e45e1dbb4317f91a4b34f"}, - {file = "multidict-5.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fd77c8f3cba815aa69cb97ee2b2ef385c7c12ada9c734b0f3b32e26bb88bbf1d"}, - {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ec9aea6223adf46999f22e2c0ab6cf33f5914be604a404f658386a8f1fba37"}, - {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5283c0a00f48e8cafcecadebfa0ed1dac8b39e295c7248c44c665c16dc1138b"}, - {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5f79c19c6420962eb17c7e48878a03053b7ccd7b69f389d5831c0a4a7f1ac0a1"}, - {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e4a67f1080123de76e4e97a18d10350df6a7182e243312426d508712e99988d4"}, - {file = "multidict-5.2.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:94b117e27efd8e08b4046c57461d5a114d26b40824995a2eb58372b94f9fca02"}, - {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2e77282fd1d677c313ffcaddfec236bf23f273c4fba7cdf198108f5940ae10f5"}, - {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:116347c63ba049c1ea56e157fa8aa6edaf5e92925c9b64f3da7769bdfa012858"}, - {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:dc3a866cf6c13d59a01878cd806f219340f3e82eed514485e094321f24900677"}, - {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ac42181292099d91217a82e3fa3ce0e0ddf3a74fd891b7c2b347a7f5aa0edded"}, - {file = "multidict-5.2.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:f0bb0973f42ffcb5e3537548e0767079420aefd94ba990b61cf7bb8d47f4916d"}, - {file = "multidict-5.2.0-cp36-cp36m-win32.whl", hash = "sha256:ea21d4d5104b4f840b91d9dc8cbc832aba9612121eaba503e54eaab1ad140eb9"}, - {file = "multidict-5.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:e6453f3cbeb78440747096f239d282cc57a2997a16b5197c9bc839099e1633d0"}, - {file = "multidict-5.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3def943bfd5f1c47d51fd324df1e806d8da1f8e105cc7f1c76a1daf0f7e17b0"}, - {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35591729668a303a02b06e8dba0eb8140c4a1bfd4c4b3209a436a02a5ac1de11"}, - {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8cacda0b679ebc25624d5de66c705bc53dcc7c6f02a7fb0f3ca5e227d80422"}, - {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:baf1856fab8212bf35230c019cde7c641887e3fc08cadd39d32a421a30151ea3"}, - {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a43616aec0f0d53c411582c451f5d3e1123a68cc7b3475d6f7d97a626f8ff90d"}, - {file = "multidict-5.2.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25cbd39a9029b409167aa0a20d8a17f502d43f2efebfe9e3ac019fe6796c59ac"}, - {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a2cbcfbea6dc776782a444db819c8b78afe4db597211298dd8b2222f73e9cd0"}, - {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d2d7d1fff8e09d99354c04c3fd5b560fb04639fd45926b34e27cfdec678a704"}, - {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a37e9a68349f6abe24130846e2f1d2e38f7ddab30b81b754e5a1fde32f782b23"}, - {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:637c1896497ff19e1ee27c1c2c2ddaa9f2d134bbb5e0c52254361ea20486418d"}, - {file = "multidict-5.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9815765f9dcda04921ba467957be543423e5ec6a1136135d84f2ae092c50d87b"}, - {file = "multidict-5.2.0-cp37-cp37m-win32.whl", hash = "sha256:8b911d74acdc1fe2941e59b4f1a278a330e9c34c6c8ca1ee21264c51ec9b67ef"}, - {file = "multidict-5.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:380b868f55f63d048a25931a1632818f90e4be71d2081c2338fcf656d299949a"}, - {file = "multidict-5.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e7d81ce5744757d2f05fc41896e3b2ae0458464b14b5a2c1e87a6a9d69aefaa8"}, - {file = "multidict-5.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d1d55cdf706ddc62822d394d1df53573d32a7a07d4f099470d3cb9323b721b6"}, - {file = "multidict-5.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4771d0d0ac9d9fe9e24e33bed482a13dfc1256d008d101485fe460359476065"}, - {file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da7d57ea65744d249427793c042094c4016789eb2562576fb831870f9c878d9e"}, - {file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdd68778f96216596218b4e8882944d24a634d984ee1a5a049b300377878fa7c"}, - {file = "multidict-5.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ecc99bce8ee42dcad15848c7885197d26841cb24fa2ee6e89d23b8993c871c64"}, - {file = "multidict-5.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:067150fad08e6f2dd91a650c7a49ba65085303fcc3decbd64a57dc13a2733031"}, - {file = "multidict-5.2.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:78c106b2b506b4d895ddc801ff509f941119394b89c9115580014127414e6c2d"}, - {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e6c4fa1ec16e01e292315ba76eb1d012c025b99d22896bd14a66628b245e3e01"}, - {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b227345e4186809d31f22087d0265655114af7cda442ecaf72246275865bebe4"}, - {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:06560fbdcf22c9387100979e65b26fba0816c162b888cb65b845d3def7a54c9b"}, - {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7878b61c867fb2df7a95e44b316f88d5a3742390c99dfba6c557a21b30180cac"}, - {file = "multidict-5.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:246145bff76cc4b19310f0ad28bd0769b940c2a49fc601b86bfd150cbd72bb22"}, - {file = "multidict-5.2.0-cp38-cp38-win32.whl", hash = "sha256:c30ac9f562106cd9e8071c23949a067b10211917fdcb75b4718cf5775356a940"}, - {file = "multidict-5.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:f19001e790013ed580abfde2a4465388950728861b52f0da73e8e8a9418533c0"}, - {file = "multidict-5.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c1ff762e2ee126e6f1258650ac641e2b8e1f3d927a925aafcfde943b77a36d24"}, - {file = "multidict-5.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bd6c9c50bf2ad3f0448edaa1a3b55b2e6866ef8feca5d8dbec10ec7c94371d21"}, - {file = "multidict-5.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc66d4016f6e50ed36fb39cd287a3878ffcebfa90008535c62e0e90a7ab713ae"}, - {file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9acb76d5f3dd9421874923da2ed1e76041cb51b9337fd7f507edde1d86535d6"}, - {file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dfc924a7e946dd3c6360e50e8f750d51e3ef5395c95dc054bc9eab0f70df4f9c"}, - {file = "multidict-5.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32fdba7333eb2351fee2596b756d730d62b5827d5e1ab2f84e6cbb287cc67fe0"}, - {file = "multidict-5.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b9aad49466b8d828b96b9e3630006234879c8d3e2b0a9d99219b3121bc5cdb17"}, - {file = "multidict-5.2.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:93de39267c4c676c9ebb2057e98a8138bade0d806aad4d864322eee0803140a0"}, - {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9bef5cff994ca3026fcc90680e326d1a19df9841c5e3d224076407cc21471a1"}, - {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:5f841c4f14331fd1e36cbf3336ed7be2cb2a8f110ce40ea253e5573387db7621"}, - {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:38ba256ee9b310da6a1a0f013ef4e422fca30a685bcbec86a969bd520504e341"}, - {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3bc3b1621b979621cee9f7b09f024ec76ec03cc365e638126a056317470bde1b"}, - {file = "multidict-5.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6ee908c070020d682e9b42c8f621e8bb10c767d04416e2ebe44e37d0f44d9ad5"}, - {file = "multidict-5.2.0-cp39-cp39-win32.whl", hash = "sha256:1c7976cd1c157fa7ba5456ae5d31ccdf1479680dc9b8d8aa28afabc370df42b8"}, - {file = "multidict-5.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:c9631c642e08b9fff1c6255487e62971d8b8e821808ddd013d8ac058087591ac"}, - {file = "multidict-5.2.0.tar.gz", hash = "sha256:0dd1c93edb444b33ba2274b66f63def8a327d607c6c790772f448a53b6ea59ce"}, -] mypy = [ {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, diff --git a/pyproject.toml b/pyproject.toml index 7eb4d7b..09ccd52 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,6 @@ repository = "https://github.com/Stepland/jubeatools" [tool.poetry.dependencies] python = "^3.8" -multidict = "^5.1.0" click = "^8.0.3" path = "^15.1.2" simplejson = "^3.17.0"