@rel-packages/osu-beatmap-parser 0.1.8 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/build/osu-beatmap-parser.js +5826 -0
- package/build/osu-beatmap-parser.wasm +0 -0
- package/dist/index.d.ts +2 -9
- package/dist/index.js +11 -40
- package/dist/lib/bindings.d.ts +1 -0
- package/dist/lib/bindings.js +92 -0
- package/dist/types.d.ts +3 -6
- package/package.json +28 -10
- package/prebuilds/linux-x64/linux-x64/osu-beatmap-parser.node +0 -0
- package/prebuilds/win32-x64/win32-x64/osu-beatmap-parser.node +0 -0
- package/.prebuildrc +0 -6
- package/CMakeLists.txt +0 -68
- package/prebuilds/linux-x64/osu-beatmap-parser.node +0 -0
- package/prebuilds/win32-x64/osu-beatmap-parser.node +0 -0
- package/src/native/addon.cpp +0 -265
- package/src/native/addon.hpp +0 -26
- package/src/native/definitions.hpp +0 -56
- package/src/native/osu/audio.cpp +0 -30
- package/src/native/osu/audio.hpp +0 -34
- package/src/native/osu/parser.cpp +0 -294
- package/src/native/osu/parser.hpp +0 -14
- package/src/native/pool.hpp +0 -80
- package/src/types.ts +0 -48
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
import { OsuKey, OsuInput } from "./types";
|
|
2
|
-
export declare
|
|
3
|
-
export declare
|
|
4
|
-
id?: string;
|
|
5
|
-
};
|
|
6
|
-
export declare function process_beatmaps(inputs: (string | OsuInput)[], keys: OsuKey[]): Promise<(Record<OsuKey, string> & {
|
|
7
|
-
id?: string;
|
|
8
|
-
})[]>;
|
|
9
|
-
export declare function get_duration(location: string): number;
|
|
10
|
-
export declare function get_audio_duration(location: string): number;
|
|
2
|
+
export declare const get_property: (data: Uint8Array, key: OsuKey) => any;
|
|
3
|
+
export declare const get_properties: (input: Uint8Array | OsuInput, keys: OsuKey[]) => any;
|
|
11
4
|
export { OsuKey, OsuInput };
|
package/dist/index.js
CHANGED
|
@@ -1,46 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.get_properties = exports.get_property = void 0;
|
|
4
|
+
const bindings_1 = require("./lib/bindings");
|
|
5
|
+
const get_property = (data, key) => {
|
|
6
|
+
return bindings_1.native.get_property(data, key);
|
|
7
|
+
};
|
|
6
8
|
exports.get_property = get_property;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const node_gyp_build_1 = __importDefault(require("node-gyp-build"));
|
|
12
|
-
const path_1 = __importDefault(require("path"));
|
|
13
|
-
const native = (0, node_gyp_build_1.default)(path_1.default.join(__dirname, ".."));
|
|
14
|
-
function get_property(location, key) {
|
|
15
|
-
return native.get_property(location, key);
|
|
16
|
-
}
|
|
17
|
-
;
|
|
18
|
-
function get_properties(input, keys) {
|
|
19
|
-
const location = typeof input === "string" ? input : input.path;
|
|
20
|
-
const result = native.get_properties(location, keys);
|
|
21
|
-
if (typeof input !== "string" && input.id) {
|
|
9
|
+
const get_properties = (input, keys) => {
|
|
10
|
+
const data = input instanceof Uint8Array ? input : input.data;
|
|
11
|
+
const result = bindings_1.native.get_properties(data, keys);
|
|
12
|
+
if (!(input instanceof Uint8Array) && input.id) {
|
|
22
13
|
return { ...result, id: input.id };
|
|
23
14
|
}
|
|
24
15
|
return result;
|
|
25
|
-
}
|
|
26
|
-
;
|
|
27
|
-
async function process_beatmaps(inputs, keys) {
|
|
28
|
-
const locations = inputs.map(i => typeof i === "string" ? i : i.path);
|
|
29
|
-
const results = await native.process_beatmaps(locations, keys);
|
|
30
|
-
return results.map((res, i) => {
|
|
31
|
-
const input = inputs[i];
|
|
32
|
-
if (typeof input !== "string" && input.id) {
|
|
33
|
-
return { ...res, id: input.id };
|
|
34
|
-
}
|
|
35
|
-
return res;
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
;
|
|
39
|
-
function get_duration(location) {
|
|
40
|
-
return native.get_duration(location);
|
|
41
|
-
}
|
|
42
|
-
;
|
|
43
|
-
function get_audio_duration(location) {
|
|
44
|
-
return native.get_audio_duration(location);
|
|
45
|
-
}
|
|
46
|
-
;
|
|
16
|
+
};
|
|
17
|
+
exports.get_properties = get_properties;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare let native: any;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.native = void 0;
|
|
4
|
+
const IS_BROWSER = globalThis.window != undefined;
|
|
5
|
+
const load_native_module = () => {
|
|
6
|
+
if (IS_BROWSER) {
|
|
7
|
+
const wasm_proxy = {
|
|
8
|
+
instance: null,
|
|
9
|
+
get_property: (data, key) => {
|
|
10
|
+
if (wasm_proxy.instance == null) {
|
|
11
|
+
throw Error("WASM not loaded yet");
|
|
12
|
+
}
|
|
13
|
+
const wasm_inst = wasm_proxy.instance;
|
|
14
|
+
const buffer_ptr = wasm_inst._malloc(data.length);
|
|
15
|
+
wasm_inst.HEAPU8.set(data, buffer_ptr);
|
|
16
|
+
const result_val = wasm_inst.get_property(buffer_ptr, data.length, key);
|
|
17
|
+
wasm_inst._free(buffer_ptr);
|
|
18
|
+
return result_val;
|
|
19
|
+
},
|
|
20
|
+
get_properties: (data, keys) => {
|
|
21
|
+
if (wasm_proxy.instance == null) {
|
|
22
|
+
throw Error("WASM not loaded yet");
|
|
23
|
+
}
|
|
24
|
+
const wasm_inst = wasm_proxy.instance;
|
|
25
|
+
const buffer_ptr = wasm_inst._malloc(data.length);
|
|
26
|
+
wasm_inst.HEAPU8.set(data, buffer_ptr);
|
|
27
|
+
const results_obj = wasm_inst.get_properties(buffer_ptr, data.length, keys);
|
|
28
|
+
wasm_inst._free(buffer_ptr);
|
|
29
|
+
return results_obj;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
const create_func = globalThis.create_osu_parser;
|
|
33
|
+
if (create_func != undefined) {
|
|
34
|
+
create_func().then((instance) => {
|
|
35
|
+
wasm_proxy.instance = instance;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return wasm_proxy;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const fs_mod = require("fs");
|
|
42
|
+
const path_mod = require("path");
|
|
43
|
+
const load_prebuilt = () => {
|
|
44
|
+
const OS_PLATFORM = process.platform;
|
|
45
|
+
const CPU_ARCH = process.arch;
|
|
46
|
+
const SCAN_PATHS = [
|
|
47
|
+
path_mod.join(__dirname, "..", "..", "prebuilds", `${OS_PLATFORM}-${CPU_ARCH}`, "osu-beatmap-parser.node"),
|
|
48
|
+
path_mod.join(__dirname, "prebuilds", `${OS_PLATFORM}-${CPU_ARCH}`, "osu-beatmap-parser.node"),
|
|
49
|
+
];
|
|
50
|
+
for (const path_str of SCAN_PATHS) {
|
|
51
|
+
if (fs_mod.existsSync(path_str)) {
|
|
52
|
+
return require(path_str);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
};
|
|
57
|
+
const load_local = () => {
|
|
58
|
+
const SCAN_PATHS = [
|
|
59
|
+
path_mod.join(__dirname, "..", "..", "build", "osu-beatmap-parser.node"),
|
|
60
|
+
path_mod.join(__dirname, "..", "..", "build", "Release", "osu-beatmap-parser.node"),
|
|
61
|
+
path_mod.join(__dirname, "..", "build", "osu-beatmap-parser.node"),
|
|
62
|
+
path_mod.join(__dirname, "build", "osu-beatmap-parser.node"),
|
|
63
|
+
];
|
|
64
|
+
for (const path_str of SCAN_PATHS) {
|
|
65
|
+
if (fs_mod.existsSync(path_str)) {
|
|
66
|
+
return require(path_str);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
};
|
|
71
|
+
const bin_module = load_prebuilt() || load_local();
|
|
72
|
+
if (bin_module) {
|
|
73
|
+
return bin_module;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (e) { }
|
|
77
|
+
return false;
|
|
78
|
+
};
|
|
79
|
+
exports.native = load_native_module();
|
|
80
|
+
try {
|
|
81
|
+
if (exports.native == false && IS_BROWSER == false) {
|
|
82
|
+
throw Error("failed to find native module...");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (IS_BROWSER) {
|
|
87
|
+
exports.native = {};
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
throw new Error("failed to load native module...\n" + err);
|
|
91
|
+
}
|
|
92
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
export type OsuKey = "AudioFilename" | "AudioLeadIn" | "PreviewTime" | "Countdown" | "SampleSet" | "StackLeniency" | "Mode" | "LetterboxInBreaks" | "WidescreenStoryboard" | "Bookmarks" | "DistanceSpacing" | "BeatDivisor" | "GridSize" | "TimelineZoom" | "Title" | "TitleUnicode" | "Artist" | "ArtistUnicode" | "Creator" | "Version" | "Source" | "Tags" | "BeatmapID" | "BeatmapSetID" | "HPDrainRate" | "CircleSize" | "OverallDifficulty" | "ApproachRate" | "SliderMultiplier" | "SliderTickRate" | "Background" | "Video" | "Storyboard" | "Duration";
|
|
2
2
|
export interface OsuInput {
|
|
3
|
-
|
|
3
|
+
data: Uint8Array;
|
|
4
4
|
id?: string;
|
|
5
5
|
}
|
|
6
6
|
export interface INativeExporter {
|
|
7
|
-
get_property(
|
|
8
|
-
get_properties(
|
|
9
|
-
process_beatmaps(locations: string[], keys: string[]): Promise<Record<string, string>[]>;
|
|
10
|
-
get_duration(location: string): number;
|
|
11
|
-
get_audio_duration(location: string): number;
|
|
7
|
+
get_property(data: Uint8Array, key: string): string;
|
|
8
|
+
get_properties(data: Uint8Array, keys: string[]): Record<string, string>;
|
|
12
9
|
}
|
package/package.json
CHANGED
|
@@ -1,24 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rel-packages/osu-beatmap-parser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": ".osu parser for nodejs",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"homepage": "https://github.com/mezleca/osu-beatmap-parser",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"update:patch": "npm version patch && git push --follow-tags",
|
|
10
|
+
"update:minor": "npm version minor && git push --follow-tags",
|
|
11
|
+
"update:major": "npm version major && git push --follow-tags",
|
|
12
|
+
"prepublishOnly": "npm run compile:tsc && mkdir -p lib",
|
|
13
|
+
"compile:native": "tsx scripts/build.ts native",
|
|
14
|
+
"compile:wasm": "tsx scripts/build.ts wasm",
|
|
11
15
|
"compile:tsc": "tsc",
|
|
12
|
-
"
|
|
13
|
-
"
|
|
16
|
+
"build": "tsx scripts/build.ts all && npm run compile:tsc",
|
|
17
|
+
"example:wasm": "bun run compile:wasm && bun run examples/wasm/server.ts",
|
|
18
|
+
"example:node": "bun run compile:native && tsx examples/node/index.ts"
|
|
14
19
|
},
|
|
15
|
-
"keywords": [
|
|
20
|
+
"keywords": [
|
|
21
|
+
"osu",
|
|
22
|
+
"beatmap",
|
|
23
|
+
"parser",
|
|
24
|
+
"native"
|
|
25
|
+
],
|
|
16
26
|
"author": "",
|
|
17
27
|
"license": "ISC",
|
|
18
28
|
"repository": {
|
|
19
29
|
"url": "https://github.com/mezleca/osu-beatmap-parser.git"
|
|
20
30
|
},
|
|
21
31
|
"type": "commonjs",
|
|
32
|
+
"files": [
|
|
33
|
+
"dist/",
|
|
34
|
+
"prebuilds/",
|
|
35
|
+
"build/*.node",
|
|
36
|
+
"build/*.wasm",
|
|
37
|
+
"build/*.js",
|
|
38
|
+
"lib/bindings.ts",
|
|
39
|
+
"README.md"
|
|
40
|
+
],
|
|
22
41
|
"devDependencies": {
|
|
23
42
|
"@types/bindings": "^1.5.5",
|
|
24
43
|
"@types/node": "^24.10.1",
|
|
@@ -27,7 +46,6 @@
|
|
|
27
46
|
"tsx": "^4.21.0",
|
|
28
47
|
"typescript": "^5.9.3"
|
|
29
48
|
},
|
|
30
|
-
"dependencies": {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
49
|
+
"dependencies": {},
|
|
50
|
+
"optionalDependencies": {}
|
|
51
|
+
}
|
|
Binary file
|
|
Binary file
|
package/.prebuildrc
DELETED
package/CMakeLists.txt
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
cmake_minimum_required(VERSION 3.16)
|
|
2
|
-
|
|
3
|
-
if(WIN32)
|
|
4
|
-
if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
|
|
5
|
-
if(EXISTS "${CMAKE_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake")
|
|
6
|
-
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake")
|
|
7
|
-
elseif(EXISTS "C:/vcpkg/scripts/buildsystems/vcpkg.cmake")
|
|
8
|
-
set(CMAKE_TOOLCHAIN_FILE "C:/vcpkg/scripts/buildsystems/vcpkg.cmake")
|
|
9
|
-
endif()
|
|
10
|
-
endif()
|
|
11
|
-
set(VCPKG_TARGET_TRIPLET "x64-windows-static")
|
|
12
|
-
endif()
|
|
13
|
-
|
|
14
|
-
project(osu-beatmap-parser VERSION 0.0.1)
|
|
15
|
-
|
|
16
|
-
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
17
|
-
set(CMAKE_CXX_STANDARD 17)
|
|
18
|
-
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
19
|
-
set(BUILD_TESTING OFF CACHE BOOL "Build tests" FORCE)
|
|
20
|
-
set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries")
|
|
21
|
-
|
|
22
|
-
file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "src/native/*.cpp")
|
|
23
|
-
file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS "src/native/*.hpp")
|
|
24
|
-
|
|
25
|
-
if(WIN32)
|
|
26
|
-
find_package(SndFile CONFIG REQUIRED)
|
|
27
|
-
set(SNDFILE_LIBRARIES SndFile::sndfile)
|
|
28
|
-
else()
|
|
29
|
-
find_package(PkgConfig REQUIRED)
|
|
30
|
-
pkg_check_modules(SNDFILE REQUIRED sndfile)
|
|
31
|
-
endif()
|
|
32
|
-
|
|
33
|
-
execute_process(
|
|
34
|
-
COMMAND node -p "require('node-addon-api').include"
|
|
35
|
-
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
36
|
-
OUTPUT_VARIABLE NODE_ADDON_API_DIR
|
|
37
|
-
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
|
|
41
|
-
|
|
42
|
-
add_library(${PROJECT_NAME} SHARED ${SOURCES} ${HEADERS} ${CMAKE_JS_SRC})
|
|
43
|
-
|
|
44
|
-
set_target_properties(${PROJECT_NAME} PROPERTIES
|
|
45
|
-
PREFIX ""
|
|
46
|
-
SUFFIX ".node"
|
|
47
|
-
CXX_STANDARD 17
|
|
48
|
-
CXX_STANDARD_REQUIRED ON
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
if (MSVC)
|
|
52
|
-
set(MSVC_RUNTIME_LIBRARY OFF)
|
|
53
|
-
add_compile_definitions(_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
|
|
54
|
-
execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS})
|
|
55
|
-
target_compile_options(${PROJECT_NAME} PRIVATE /Zc:__cplusplus)
|
|
56
|
-
endif()
|
|
57
|
-
|
|
58
|
-
target_include_directories(${PROJECT_NAME} PRIVATE
|
|
59
|
-
${NODE_ADDON_API_DIR}
|
|
60
|
-
${CMAKE_JS_INC}
|
|
61
|
-
${SNDFILE_INCLUDE_DIRS}
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
target_link_libraries(
|
|
65
|
-
${PROJECT_NAME}
|
|
66
|
-
${CMAKE_JS_LIB}
|
|
67
|
-
${SNDFILE_LIBRARIES}
|
|
68
|
-
)
|
|
Binary file
|
|
Binary file
|
package/src/native/addon.cpp
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
#include <filesystem>
|
|
2
|
-
#include <napi.h>
|
|
3
|
-
#include <iostream>
|
|
4
|
-
#include <sndfile.h>
|
|
5
|
-
#include <string>
|
|
6
|
-
#include <thread>
|
|
7
|
-
#include <fstream>
|
|
8
|
-
#include <vector>
|
|
9
|
-
#include <atomic>
|
|
10
|
-
#include "osu/parser.hpp"
|
|
11
|
-
#include "osu/audio.hpp"
|
|
12
|
-
#include "addon.hpp"
|
|
13
|
-
#include "pool.hpp"
|
|
14
|
-
|
|
15
|
-
#define NOOP_FUNC(env) Napi::Function::New(env, [](const Napi::CallbackInfo& info){})
|
|
16
|
-
|
|
17
|
-
AudioAnalyzer audio_analizer;
|
|
18
|
-
|
|
19
|
-
std::filesystem::path dirname(const std::string &path) {
|
|
20
|
-
std::filesystem::path p(path);
|
|
21
|
-
return p.parent_path();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
std::string read_file(const std::string& path) {
|
|
25
|
-
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
|
26
|
-
|
|
27
|
-
if (!file.is_open()) {
|
|
28
|
-
return "";
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
std::streamsize size = file.tellg();
|
|
32
|
-
file.seekg(0, std::ios::beg);
|
|
33
|
-
|
|
34
|
-
std::string buffer(size, ' ');
|
|
35
|
-
|
|
36
|
-
if (file.read(buffer.data(), size)) {
|
|
37
|
-
return buffer;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return "";
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
Napi::Value ParserAddon::get_property(const Napi::CallbackInfo& info) {
|
|
44
|
-
if (info.Length() < 2) {
|
|
45
|
-
return Napi::String::New(info.Env(), "");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!info[0].IsString() || !info[1].IsString()) {
|
|
49
|
-
return Napi::String::New(info.Env(), "");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
std::string location = info[0].As<Napi::String>().Utf8Value();
|
|
53
|
-
std::string key = info[1].As<Napi::String>().Utf8Value();
|
|
54
|
-
std::string content = read_file(location);
|
|
55
|
-
|
|
56
|
-
if (content.empty()) {
|
|
57
|
-
return Napi::String::New(info.Env(), "");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return Napi::String::From(info.Env(), osu_parser::get_property(content, key));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
Napi::Value ParserAddon::get_properties(const Napi::CallbackInfo& info) {
|
|
64
|
-
if (info.Length() < 2) {
|
|
65
|
-
return Napi::Object::New(info.Env());
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
std::string location = info[0].As<Napi::String>().Utf8Value();
|
|
69
|
-
Napi::Array keys_array = info[1].As<Napi::Array>();
|
|
70
|
-
|
|
71
|
-
std::vector<std::string> keys;
|
|
72
|
-
|
|
73
|
-
for (uint32_t i = 0; i < keys_array.Length(); i++) {
|
|
74
|
-
Napi::Value val = keys_array[i];
|
|
75
|
-
if (val.IsString()) {
|
|
76
|
-
keys.push_back(val.As<Napi::String>().Utf8Value());
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
std::string content = read_file(location);
|
|
81
|
-
|
|
82
|
-
if (content.empty()) {
|
|
83
|
-
return Napi::Object::New(info.Env());
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
auto results = osu_parser::get_properties(content, keys);
|
|
87
|
-
|
|
88
|
-
Napi::Object obj = Napi::Object::New(info.Env());
|
|
89
|
-
|
|
90
|
-
for (const auto& [k, v] : results) {
|
|
91
|
-
obj.Set(k, v);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return obj;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
struct BatchContext {
|
|
98
|
-
std::vector<std::string> paths;
|
|
99
|
-
std::vector<std::string> keys;
|
|
100
|
-
std::vector<std::unordered_map<std::string, std::string>> results;
|
|
101
|
-
std::atomic<size_t> completed_count{0};
|
|
102
|
-
Napi::ThreadSafeFunction tsfn;
|
|
103
|
-
Napi::Promise::Deferred deferred;
|
|
104
|
-
bool needs_duration = false;
|
|
105
|
-
|
|
106
|
-
BatchContext(Napi::Env env) : deferred(Napi::Promise::Deferred::New(env)) {}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
void process_chunk(BatchContext* context, size_t start, size_t end) {
|
|
110
|
-
for (size_t i = start; i < end; i++) {
|
|
111
|
-
std::string content = read_file(context->paths[i]);
|
|
112
|
-
if (!content.empty()) {
|
|
113
|
-
context->results[i] = osu_parser::get_properties(content, context->keys);
|
|
114
|
-
|
|
115
|
-
if (context->needs_duration) {
|
|
116
|
-
std::string audio_file_name = osu_parser::get_property(content, "AudioFilename");
|
|
117
|
-
if (!audio_file_name.empty()) {
|
|
118
|
-
std::filesystem::path audio_path = dirname(context->paths[i]) / audio_file_name;
|
|
119
|
-
double duration = audio_analizer.get_audio_duration(audio_path.string());
|
|
120
|
-
context->results[i]["Duration"] = std::to_string(duration);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
size_t completed = context->completed_count.fetch_add(end - start) + (end - start);
|
|
127
|
-
|
|
128
|
-
if (completed == context->paths.size()) {
|
|
129
|
-
auto callback = [context](Napi::Env env, Napi::Function jsCallback) {
|
|
130
|
-
Napi::Array result_array = Napi::Array::New(env, context->results.size());
|
|
131
|
-
|
|
132
|
-
for (size_t i = 0; i < context->results.size(); i++) {
|
|
133
|
-
Napi::Object obj = Napi::Object::New(env);
|
|
134
|
-
for (const auto& [k, v] : context->results[i]) {
|
|
135
|
-
obj.Set(k, v);
|
|
136
|
-
}
|
|
137
|
-
result_array[i] = obj;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
context->deferred.Resolve(result_array);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
context->tsfn.BlockingCall(callback);
|
|
144
|
-
context->tsfn.Release();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
Napi::Value ParserAddon::process_beatmaps(const Napi::CallbackInfo& info) {
|
|
149
|
-
Napi::Env env = info.Env();
|
|
150
|
-
|
|
151
|
-
if (info.Length() < 2) {
|
|
152
|
-
auto deferred = Napi::Promise::Deferred::New(env);
|
|
153
|
-
deferred.Reject(Napi::String::New(env, "Invalid arguments"));
|
|
154
|
-
return deferred.Promise();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
Napi::Array paths_array = info[0].As<Napi::Array>();
|
|
158
|
-
Napi::Array keys_array = info[1].As<Napi::Array>();
|
|
159
|
-
|
|
160
|
-
auto context = new BatchContext(env);
|
|
161
|
-
|
|
162
|
-
for (uint32_t i = 0; i < paths_array.Length(); i++) {
|
|
163
|
-
context->paths.push_back(Napi::Value(paths_array[i]).As<Napi::String>().Utf8Value());
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
for (uint32_t i = 0; i < keys_array.Length(); i++) {
|
|
167
|
-
std::string key = Napi::Value(keys_array[i]).As<Napi::String>().Utf8Value();
|
|
168
|
-
context->keys.push_back(key);
|
|
169
|
-
if (key == "Duration") {
|
|
170
|
-
context->needs_duration = true;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
context->results.resize(context->paths.size());
|
|
175
|
-
|
|
176
|
-
context->tsfn = Napi::ThreadSafeFunction::New(
|
|
177
|
-
env,
|
|
178
|
-
NOOP_FUNC(env),
|
|
179
|
-
"ProcessBeatmaps",
|
|
180
|
-
0,
|
|
181
|
-
1,
|
|
182
|
-
[context](Napi::Env) {
|
|
183
|
-
delete context;
|
|
184
|
-
}
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
size_t total_files = context->paths.size();
|
|
188
|
-
size_t thread_count = std::thread::hardware_concurrency();
|
|
189
|
-
|
|
190
|
-
if (thread_count == 0) {
|
|
191
|
-
thread_count = 1;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
size_t chunk_size = (total_files + thread_count - 1) / thread_count;
|
|
195
|
-
|
|
196
|
-
for (size_t t = 0; t < thread_count; t++) {
|
|
197
|
-
size_t start = t * chunk_size;
|
|
198
|
-
size_t end = std::min(start + chunk_size, total_files);
|
|
199
|
-
|
|
200
|
-
if (start >= end) {
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
pool.enqueue([context, start, end]() {
|
|
205
|
-
process_chunk(context, start, end);
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return context->deferred.Promise();
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
Napi::Value ParserAddon::get_duration(const Napi::CallbackInfo& info) {
|
|
213
|
-
if (info.Length() < 1) {
|
|
214
|
-
return Napi::Number::New(info.Env(), 0.0);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (!info[0].IsString()) {
|
|
218
|
-
return Napi::String::New(info.Env(), "");
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
std::string location = info[0].As<Napi::String>().Utf8Value();
|
|
222
|
-
std::string content = read_file(location);
|
|
223
|
-
|
|
224
|
-
if (content.empty()) {
|
|
225
|
-
return Napi::Number::New(info.Env(), 0.0);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
std::string audio_file_name = osu_parser::get_property(content, "AudioFilename");
|
|
229
|
-
std::filesystem::path audio_path = dirname(location) / audio_file_name;
|
|
230
|
-
|
|
231
|
-
return Napi::Number::From(info.Env(), audio_analizer.get_audio_duration(audio_path.string()));
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
Napi::Value ParserAddon::get_audio_duration(const Napi::CallbackInfo& info) {
|
|
235
|
-
if (info.Length() < 1) {
|
|
236
|
-
return Napi::Number::New(info.Env(), 0.0);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (!info[0].IsString()) {
|
|
240
|
-
return Napi::String::New(info.Env(), "");
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
std::string location = info[0].As<Napi::String>().Utf8Value();
|
|
244
|
-
std::filesystem::path audio_path(location);
|
|
245
|
-
|
|
246
|
-
return Napi::Number::From(info.Env(), audio_analizer.get_audio_duration(audio_path.string()));
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
Napi::Value ParserAddon::test_promise(const Napi::CallbackInfo& info) {
|
|
250
|
-
auto deffered = Napi::Promise::Deferred::New(info.Env());
|
|
251
|
-
auto tsfn = Napi::ThreadSafeFunction::New(info.Env(), NOOP_FUNC(info.Env()), "tsfn", 0, 1);
|
|
252
|
-
|
|
253
|
-
std::thread([tsfn, deffered]() {
|
|
254
|
-
tsfn.NonBlockingCall([deffered](Napi::Env env, Napi::Function) {
|
|
255
|
-
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
|
256
|
-
deffered.Resolve(Napi::Boolean::New(env, true));
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
tsfn.Release();
|
|
260
|
-
}).detach();
|
|
261
|
-
|
|
262
|
-
return deffered.Promise();
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
NODE_API_ADDON(ParserAddon)
|
package/src/native/addon.hpp
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
|
|
3
|
-
#include <napi.h>
|
|
4
|
-
#include "pool.hpp"
|
|
5
|
-
|
|
6
|
-
class ParserAddon : public Napi::Addon<ParserAddon> {
|
|
7
|
-
public:
|
|
8
|
-
ParserAddon(Napi::Env env, Napi::Object exports) {
|
|
9
|
-
pool.initialize(std::thread::hardware_concurrency());
|
|
10
|
-
DefineAddon(exports, {
|
|
11
|
-
InstanceMethod("get_property", &ParserAddon::get_property),
|
|
12
|
-
InstanceMethod("get_properties", &ParserAddon::get_properties),
|
|
13
|
-
InstanceMethod("process_beatmaps", &ParserAddon::process_beatmaps),
|
|
14
|
-
InstanceMethod("get_duration", &ParserAddon::get_duration),
|
|
15
|
-
InstanceMethod("get_audio_duration", &ParserAddon::get_audio_duration),
|
|
16
|
-
InstanceMethod("test_promise", &ParserAddon::test_promise)
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
Napi::Value get_property(const Napi::CallbackInfo& info);
|
|
21
|
-
Napi::Value get_properties(const Napi::CallbackInfo& info);
|
|
22
|
-
Napi::Value process_beatmaps(const Napi::CallbackInfo& info);
|
|
23
|
-
Napi::Value get_duration(const Napi::CallbackInfo& info);
|
|
24
|
-
Napi::Value get_audio_duration(const Napi::CallbackInfo& info);
|
|
25
|
-
Napi::Value test_promise(const Napi::CallbackInfo& info);
|
|
26
|
-
};
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
#include <string>
|
|
3
|
-
#include <unordered_set>
|
|
4
|
-
#include <unordered_map>
|
|
5
|
-
|
|
6
|
-
enum OSU_SECTIONS {
|
|
7
|
-
General = 0,
|
|
8
|
-
Editor,
|
|
9
|
-
Metadata,
|
|
10
|
-
Difficulty,
|
|
11
|
-
Events,
|
|
12
|
-
TimingPoints,
|
|
13
|
-
Colours,
|
|
14
|
-
HitObjects
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const std::unordered_map<std::string_view, std::string_view> KEY_TO_SECTION = {
|
|
18
|
-
{"AudioFilename", "[General]"},
|
|
19
|
-
{"AudioLeadIn", "[General]"},
|
|
20
|
-
{"PreviewTime", "[General]"},
|
|
21
|
-
{"Countdown", "[General]"},
|
|
22
|
-
{"SampleSet", "[General]"},
|
|
23
|
-
{"StackLeniency", "[General]"},
|
|
24
|
-
{"Mode", "[General]"},
|
|
25
|
-
{"LetterboxInBreaks", "[General]"},
|
|
26
|
-
{"WidescreenStoryboard", "[General]"},
|
|
27
|
-
{"Bookmarks", "[Editor]"},
|
|
28
|
-
{"DistanceSpacing", "[Editor]"},
|
|
29
|
-
{"BeatDivisor", "[Editor]"},
|
|
30
|
-
{"GridSize", "[Editor]"},
|
|
31
|
-
{"TimelineZoom", "[Editor]"},
|
|
32
|
-
{"Title", "[Metadata]"},
|
|
33
|
-
{"TitleUnicode", "[Metadata]"},
|
|
34
|
-
{"Artist", "[Metadata]"},
|
|
35
|
-
{"ArtistUnicode", "[Metadata]"},
|
|
36
|
-
{"Creator", "[Metadata]"},
|
|
37
|
-
{"Version", "[Metadata]"},
|
|
38
|
-
{"Source", "[Metadata]"},
|
|
39
|
-
{"Tags", "[Metadata]"},
|
|
40
|
-
{"BeatmapID", "[Metadata]"},
|
|
41
|
-
{"BeatmapSetID", "[Metadata]"},
|
|
42
|
-
{"HPDrainRate", "[Difficulty]"},
|
|
43
|
-
{"CircleSize", "[Difficulty]"},
|
|
44
|
-
{"OverallDifficulty", "[Difficulty]"},
|
|
45
|
-
{"ApproachRate", "[Difficulty]"},
|
|
46
|
-
{"SliderMultiplier", "[Difficulty]"},
|
|
47
|
-
{"SliderTickRate", "[Difficulty]"},
|
|
48
|
-
// special ones (doenst have keys)
|
|
49
|
-
{"Background", "[Events]"},
|
|
50
|
-
{"Video", "[Events]"},
|
|
51
|
-
{"Storyboard", "[Events]"},
|
|
52
|
-
// extra special ones (why not)
|
|
53
|
-
{"Duration", "[General]"}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const std::unordered_set<std::string_view> SPECIAL_KEYS {"Background", "Video", "Storyboard"};
|