Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
a3b0a88c41 |
|
@ -1,13 +0,0 @@
|
|||
# Editor configuration, see http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
|
@ -1,56 +0,0 @@
|
|||
{
|
||||
"root": true,
|
||||
"ignorePatterns": [
|
||||
"**/*"
|
||||
],
|
||||
"plugins": [
|
||||
"@nx"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx",
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"rules": {
|
||||
"@nx/enforce-module-boundaries": [
|
||||
"error",
|
||||
{
|
||||
"enforceBuildableLibDependency": true,
|
||||
"allow": [],
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "*",
|
||||
"onlyDependOnLibsWithTags": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@nx/typescript"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@nx/javascript"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
6
.gitignore
vendored
|
@ -50,4 +50,8 @@ Thumbs.db
|
|||
*.png
|
||||
*.jpeg
|
||||
*.jpg
|
||||
*.exr
|
||||
*.exr
|
||||
|
||||
# Added by cargo
|
||||
|
||||
/target
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
# Add files here to ignore them from prettier formatting
|
||||
/dist
|
||||
/coverage
|
13
.prettierrc
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"parser": "typescript",
|
||||
"trailingComma": "none",
|
||||
"useTabs": true,
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"endOfLine": "lf",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"printWidth": 150
|
||||
}
|
402
Cargo.lock
generated
Normal file
|
@ -0,0 +1,402 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "discard"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
||||
|
||||
[[package]]
|
||||
name = "euterpe"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euterpe_db"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euterpe_dj"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euterpe_player"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures-signals",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euterpe_preprocessor"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euterpe_visualizer"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-signals"
|
||||
version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b175f2f6600dd81d92d20cf10872b03ea9df6b2513ca7f672341260dacb1ab2"
|
||||
dependencies = [
|
||||
"discard",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"gensym",
|
||||
"log",
|
||||
"pin-project",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-macro",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gensym"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "913dce4c5f06c2ea40fc178c06f777ac89fc6b1383e90c254fafb1abe4ba3c82"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.197"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.197"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
19
Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"libs/dj",
|
||||
"libs/euterpe",
|
||||
"libs/db",
|
||||
"libs/player",
|
||||
"libs/preprocessor",
|
||||
"libs/visualizer",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.dependencies]
|
||||
thiserror = "1.0.58"
|
||||
wasm-bindgen = "0.2.92"
|
||||
futures-signals = "0.3.33"
|
||||
|
||||
[workspace.dependencies.web-sys]
|
||||
version = "0.3.69"
|
||||
features = ["AudioContext"]
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"babelrcRoots": [
|
||||
"*"
|
||||
]
|
||||
}
|
19
libs/db/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "euterpe_db"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Djkáťo <djkatovfx@gmail.com>"]
|
||||
description = "Fully featured web player"
|
||||
homepage = "https://github.com/euterpe/euterpe-source"
|
||||
repository = "https://github.com/euterpe/euterpe-source"
|
||||
documentation = "https://github.com/euterpe/euterpe-source"
|
||||
# keywords = ["player", "web"]
|
||||
# categories = ["web-programming::http-server"]
|
||||
|
||||
[dependencies]
|
||||
thiserror.workspace = true
|
||||
wasm-bindgen.workspace = true
|
||||
|
||||
[dependencies.web-sys]
|
||||
workspace = true
|
||||
features = []
|
19
libs/dj/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "euterpe_dj"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Djkáťo <djkatovfx@gmail.com>"]
|
||||
description = "Fully featured web player"
|
||||
homepage = "https://github.com/euterpe/euterpe-source"
|
||||
repository = "https://github.com/euterpe/euterpe-source"
|
||||
documentation = "https://github.com/euterpe/euterpe-source"
|
||||
# keywords = ["player", "web"]
|
||||
# categories = ["web-programming::http-server"]
|
||||
|
||||
[dependencies]
|
||||
thiserror.workspace = true
|
||||
wasm-bindgen.workspace = true
|
||||
|
||||
[dependencies.web-sys]
|
||||
workspace = true
|
||||
features = []
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
19
libs/euterpe/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "euterpe"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Djkáťo <djkatovfx@gmail.com>"]
|
||||
description = "Fully featured web player"
|
||||
homepage = "https://github.com/euterpe/euterpe-source"
|
||||
repository = "https://github.com/euterpe/euterpe-source"
|
||||
documentation = "https://github.com/euterpe/euterpe-source"
|
||||
# keywords = ["player", "web"]
|
||||
# categories = ["web-programming::http-server"]
|
||||
|
||||
[dependencies]
|
||||
thiserror.workspace = true
|
||||
wasm-bindgen.workspace = true
|
||||
|
||||
[dependencies.web-sys]
|
||||
workspace = true
|
||||
features = []
|
16
libs/euterpe/README.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Euterpe
|
||||
|
||||
Fully featured AudioContext music player for the web, written in Rust.
|
||||
|
||||
## Euterpe in production:
|
||||
|
||||
- Hypertrance ( [site](https://hypertrance.eu/), [repository](https://github.com/nuphory/hypertrance.eu) )
|
||||
|
||||
Features:
|
||||
|
||||
- "Local" library/database for songs, collections, artists, waveforms, artist links and much more!
|
||||
- Queue and history
|
||||
- Easy way to create Vector based audio visuals
|
||||
- Library automatization based on folder/file structure, preprocessing and encoding media files for all platforms
|
||||
- Safe. Provides wrappers for all functions that are either unsafe or don't give a success return. (very Rust inspired, yes.)
|
||||
- Frontend library agnostic
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
2
libs/player-web-test/Cargo.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[dependencies]
|
||||
euterpe_player = { path = "player", version = "*" }
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
0
libs/player-web-test/src/assets/.gitkeep
Normal file
0
libs/player-web-test/src/main.rs
Normal file
36
libs/player/Cargo.toml
Normal file
|
@ -0,0 +1,36 @@
|
|||
[package]
|
||||
name = "euterpe_player"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Djkáťo <djkatovfx@gmail.com>"]
|
||||
description = "Fully featured web player"
|
||||
homepage = "https://github.com/euterpe/euterpe-source"
|
||||
repository = "https://github.com/euterpe/euterpe-source"
|
||||
documentation = "https://github.com/euterpe/euterpe-source"
|
||||
# keywords = ["player", "web"]
|
||||
# categories = ["web-programming::http-server"]
|
||||
|
||||
[dependencies]
|
||||
thiserror.workspace = true
|
||||
wasm-bindgen.workspace = true
|
||||
futures-signals = "0.3.33"
|
||||
wasm-bindgen-futures = "0.4.42"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies.web-sys]
|
||||
features = [
|
||||
"AudioContext",
|
||||
"AbortController",
|
||||
"Event",
|
||||
"MediaElementAudioSourceNode",
|
||||
"AnalyserNode",
|
||||
"AudioDestinationNode",
|
||||
"AudioContextState",
|
||||
"HtmlAudioElement",
|
||||
"BaseAudioContext",
|
||||
"GainNode",
|
||||
"AudioParam",
|
||||
]
|
||||
workspace = true
|
277
libs/player/src/lib.rs
Normal file
|
@ -0,0 +1,277 @@
|
|||
use futures_signals::signal::Mutable;
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::{
|
||||
AudioContext, AudioContextState, AudioNode, GainNode, HtmlAudioElement,
|
||||
MediaElementAudioSourceNode,
|
||||
};
|
||||
|
||||
pub struct MusicPlayerBuilder {
|
||||
audio_context: AudioContext,
|
||||
audio_element: HtmlAudioElement,
|
||||
gain: GainNode,
|
||||
track: MediaElementAudioSourceNode,
|
||||
volume: f32,
|
||||
prev_node: Option<AudioNode>,
|
||||
is_gain_connected: bool,
|
||||
}
|
||||
|
||||
impl MusicPlayerBuilder {
|
||||
pub fn new(audio_element: HtmlAudioElement) -> Result<MusicPlayerBuilder, JsValue> {
|
||||
if audio_element.is_undefined() {
|
||||
return Err("Audio Element is undefined".into());
|
||||
}
|
||||
let audio_context = AudioContext::new()?;
|
||||
let track = audio_context.create_media_element_source(&audio_element)?;
|
||||
let gain = audio_context.create_gain()?;
|
||||
Ok(MusicPlayerBuilder {
|
||||
audio_context,
|
||||
audio_element,
|
||||
gain,
|
||||
track,
|
||||
volume: 1.,
|
||||
prev_node: None,
|
||||
is_gain_connected: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_analyzer(mut self) -> Result<MusicPlayerBuilder, JsValue> {
|
||||
let analyzer = self.audio_context.create_analyser()?;
|
||||
if let Some(node) = &self.prev_node {
|
||||
node.connect_with_audio_node(&analyzer)?;
|
||||
} else {
|
||||
self.track.connect_with_audio_node(&analyzer)?;
|
||||
}
|
||||
self.prev_node = Some(analyzer.into());
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn add_custom_node(mut self, node: AudioNode) -> Result<MusicPlayerBuilder, JsValue> {
|
||||
match &self.prev_node {
|
||||
Some(n) => n.connect_with_audio_node(&node)?,
|
||||
None => self.track.connect_with_audio_node(&node)?,
|
||||
};
|
||||
self.prev_node = Some(node);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<MusicPlayer, JsValue> {
|
||||
if !self.is_gain_connected {
|
||||
match &self.prev_node {
|
||||
None => {
|
||||
self.track.connect_with_audio_node(self.gain.as_ref())?;
|
||||
}
|
||||
Some(node) => {
|
||||
node.connect_with_audio_node(self.gain.as_ref())?;
|
||||
}
|
||||
};
|
||||
}
|
||||
if let Some(node) = &self.prev_node {
|
||||
node.connect_with_audio_node(self.audio_context.destination().as_ref())?;
|
||||
}
|
||||
Ok(MusicPlayer {
|
||||
audio_context: self.audio_context,
|
||||
gain: self.gain,
|
||||
volume: Mutable::new(self.volume),
|
||||
audio_element: self.audio_element,
|
||||
volume_cache: 0.,
|
||||
current_song_duration: Mutable::new(0.),
|
||||
is_playing: Mutable::new(false),
|
||||
time: Mutable::new(0.),
|
||||
current_song_path: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MusicPlayer {
|
||||
pub current_song_duration: Mutable<f64>,
|
||||
pub is_playing: Mutable<bool>,
|
||||
pub time: Mutable<f32>,
|
||||
pub audio_context: AudioContext,
|
||||
pub audio_element: HtmlAudioElement,
|
||||
pub gain: GainNode,
|
||||
pub volume: Mutable<f32>,
|
||||
current_song_path: Option<String>,
|
||||
volume_cache: f32,
|
||||
}
|
||||
|
||||
impl MusicPlayer {
|
||||
pub fn mute_toggle(&mut self) {
|
||||
if self.gain.gain().value() == 0. {
|
||||
self.unmute();
|
||||
} else {
|
||||
self.mute()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mute(&mut self) {
|
||||
self.volume_cache = self.gain.gain().value();
|
||||
/* Gentler mute, doesn't pop
|
||||
gain.gain.linearRampToValueAtTime(
|
||||
0,
|
||||
audio_context.currentTime + 0.1
|
||||
);*/
|
||||
self.volume.set(0.);
|
||||
self.gain.gain().set_value(0.);
|
||||
}
|
||||
|
||||
pub fn unmute(&mut self) {
|
||||
self.volume.set(self.volume_cache);
|
||||
self.gain.gain().set_value(self.volume_cache);
|
||||
}
|
||||
|
||||
pub fn change_volume(&mut self, volume: f32) {
|
||||
self.volume.set(volume);
|
||||
self.gain.gain().set_value(volume);
|
||||
}
|
||||
|
||||
pub async fn seek(&mut self, new_time: f64) -> Result<(), ()> {
|
||||
if self.audio_context.state() != AudioContextState::Running {
|
||||
self.is_playing.set(false);
|
||||
}
|
||||
self.audio_element.set_current_time(new_time);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn play_toggle(&mut self) -> Result<(), ()> {
|
||||
if self.audio_context.state() != AudioContextState::Running {
|
||||
JsFuture::from(self.audio_context.resume().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
if self.audio_element.paused() {
|
||||
// try {
|
||||
JsFuture::from(self.audio_element.play().unwrap())
|
||||
.await
|
||||
.unwrap();
|
||||
self.is_playing.set(true);
|
||||
// } catch (e) {
|
||||
self.is_playing.set(false);
|
||||
// throw e
|
||||
// }
|
||||
} else {
|
||||
self.audio_element.pause().unwrap();
|
||||
self.is_playing.set(false);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn play(&mut self) -> Result<&mut Self, JsValue> {
|
||||
if self.is_playing.get() {
|
||||
return Ok(self);
|
||||
}
|
||||
if self.audio_context.state() != AudioContextState::Running {
|
||||
JsFuture::from(self.audio_context.resume()?).await?;
|
||||
}
|
||||
if self.audio_element.paused() {
|
||||
// try {
|
||||
JsFuture::from(self.audio_element.play()?).await?;
|
||||
self.is_playing.set(true);
|
||||
// } catch (e) {
|
||||
self.is_playing.set(false);
|
||||
// throw e
|
||||
// }
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn pause(&mut self) -> Result<&mut Self, JsValue> {
|
||||
self.audio_element.pause()?;
|
||||
self.is_playing.set(false);
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub async fn new_song(&mut self, path: String) -> Result<&mut Self, JsValue> {
|
||||
if self.audio_context.state() != AudioContextState::Running {
|
||||
JsFuture::from(self.audio_context.resume()?).await?;
|
||||
}
|
||||
self.audio_element.set_src(&path);
|
||||
Ok(self)
|
||||
/*
|
||||
let good_abort_controller = AbortController::new()?;
|
||||
let bad_abort_controller = AbortController::new()?;
|
||||
|
||||
let can_play_through: Box<dyn FnMut(_)> = Box::new(move |_: web_sys::Event| {
|
||||
good_abort_controller.abort();
|
||||
});
|
||||
let can_play_through_cb = Closure::wrap(can_play_through);
|
||||
|
||||
let bad_event: Box<dyn FnMut(_)> = Box::new(move |_: web_sys::Event| {
|
||||
bad_abort_controller.abort();
|
||||
});
|
||||
let bad_event_cb = Closure::wrap(bad_event);
|
||||
|
||||
self.audio_element.add_event_listener_with_callback(
|
||||
"canplaythrough",
|
||||
can_play_through_cb.as_ref().unchecked_ref(),
|
||||
)?;
|
||||
|
||||
self.audio_element
|
||||
.add_event_listener_with_callback("error", abort_cb.as_ref().unchecked_ref())?;
|
||||
|
||||
self.audio_element.remove_event_listener_with_callback(
|
||||
"canplaythrough",
|
||||
abort_cb.as_ref().unchecked_ref(),
|
||||
)?;
|
||||
self.audio_element
|
||||
.remove_event_listener_with_callback("error", abort_cb.as_ref().unchecked_ref())?;
|
||||
self.audio_element
|
||||
.remove_event_listener_with_callback("stalled", abort_cb.as_ref().unchecked_ref())?;
|
||||
*/
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Will parse the duration of the song to make it easy to display in UI
|
||||
// * If somethings undefined it returns "0:00"
|
||||
// */
|
||||
// pub fn get_formatted_duration() {
|
||||
// let dur = self.audio_element.duration;
|
||||
// self.current_song_duration = self.audio_element.duration;
|
||||
//
|
||||
// if (dur == 0 || !dur) return "0:00";
|
||||
//
|
||||
// // ~ is Bitwise NOT, equivalent to Math.floor()
|
||||
// let hrs = ~~(dur / 3600)
|
||||
// let mins = ~~((dur % 3600) / 60)
|
||||
// let secs = ~~dur % 60
|
||||
//
|
||||
// let ret = ""
|
||||
// if (hrs > 0) {
|
||||
// ret += "" + hrs + ":" + (mins < 10 ? "0" : "")
|
||||
// }
|
||||
//
|
||||
// ret += "" + mins + ":" + (secs < 10 ? "0" : "")
|
||||
// ret += "" + secs
|
||||
// return ret
|
||||
// }
|
||||
// /**
|
||||
// * Will parse the current time of the song to make it easy to display in UI
|
||||
// * If somethings undefined it returns "0:00"
|
||||
// */
|
||||
fn get_formatted_current_time(time: f64) -> String {
|
||||
if time == 0. {
|
||||
return "0:00".to_owned();
|
||||
}
|
||||
let hrs = f64::floor(time / 3600.);
|
||||
let mins = f64::floor((time % 3600.) / 60.);
|
||||
let secs = time / 60.;
|
||||
let mut res = "".to_owned();
|
||||
if hrs > 0. {
|
||||
res = hrs.to_string()
|
||||
+ ":"
|
||||
+ match mins < 10. {
|
||||
true => "0",
|
||||
false => "",
|
||||
};
|
||||
}
|
||||
res = res
|
||||
+ &mins.to_string()
|
||||
+ ":"
|
||||
+ match secs < 10. {
|
||||
true => "0",
|
||||
false => "",
|
||||
};
|
||||
res = res + &secs.to_string();
|
||||
res
|
||||
}
|
||||
}
|
1
.eslintignore → libs/preprocessor/.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
.git
|
||||
node_modules
|
19
libs/preprocessor/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "euterpe_preprocessor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Djkáťo <djkatovfx@gmail.com>"]
|
||||
description = "Fully featured web player"
|
||||
homepage = "https://github.com/euterpe/euterpe-source"
|
||||
repository = "https://github.com/euterpe/euterpe-source"
|
||||
documentation = "https://github.com/euterpe/euterpe-source"
|
||||
# keywords = ["player", "web"]
|
||||
# categories = ["web-programming::http-server"]
|
||||
|
||||
[dependencies]
|
||||
thiserror.workspace = true
|
||||
wasm-bindgen.workspace = true
|
||||
|
||||
[dependencies.web-sys]
|
||||
workspace = true
|
||||
features = []
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
3
libs/preprocessor/src/main.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
todo!();
|
||||
}
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
0
libs/visualizer-web-test/src/assets/.gitkeep
Normal file
19
libs/visualizer/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "euterpe_visualizer"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Djkáťo <djkatovfx@gmail.com>"]
|
||||
description = "Fully featured web player"
|
||||
homepage = "https://github.com/euterpe/euterpe-source"
|
||||
repository = "https://github.com/euterpe/euterpe-source"
|
||||
documentation = "https://github.com/euterpe/euterpe-source"
|
||||
# keywords = ["player", "web"]
|
||||
# categories = ["web-programming::http-server"]
|
||||
|
||||
[dependencies]
|
||||
thiserror.workspace = true
|
||||
wasm-bindgen.workspace = true
|
||||
|
||||
[dependencies.web-sys]
|
||||
workspace = true
|
||||
features = []
|
0
libs/visualizer/src/lib.rs
Normal file
76
nx.json
|
@ -1,76 +0,0 @@
|
|||
{
|
||||
"$schema": "./node_modules/nx/schemas/nx-schema.json",
|
||||
"affected": {
|
||||
"defaultBase": "master"
|
||||
},
|
||||
"tasksRunnerOptions": {
|
||||
"default": {
|
||||
"runner": "nx/tasks-runners/default",
|
||||
"options": {
|
||||
"cacheableOperations": [
|
||||
"build",
|
||||
"lint",
|
||||
"test",
|
||||
"e2e"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"targetDefaults": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"inputs": [
|
||||
"production",
|
||||
"^production"
|
||||
]
|
||||
},
|
||||
"lint": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"{workspaceRoot}/.eslintrc.json",
|
||||
"{workspaceRoot}/.eslintignore"
|
||||
]
|
||||
},
|
||||
"e2e": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production"
|
||||
]
|
||||
},
|
||||
"test": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production"
|
||||
]
|
||||
}
|
||||
},
|
||||
"namedInputs": {
|
||||
"default": [
|
||||
"{projectRoot}/**/*",
|
||||
"sharedGlobals"
|
||||
],
|
||||
"production": [
|
||||
"default",
|
||||
"!{projectRoot}/.eslintrc.json",
|
||||
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
|
||||
"!{projectRoot}/tsconfig.spec.json"
|
||||
],
|
||||
"sharedGlobals": [
|
||||
"{workspaceRoot}/babel.config.json"
|
||||
]
|
||||
},
|
||||
"workspaceLayout": {
|
||||
"appsDir": "packages",
|
||||
"libsDir": "packages"
|
||||
},
|
||||
"generators": {
|
||||
"@nx/web:application": {
|
||||
"style": "css",
|
||||
"linter": "eslint",
|
||||
"unitTestRunner": "vitest",
|
||||
"e2eTestRunner": "cypress"
|
||||
}
|
||||
}
|
||||
}
|
10994
package-lock.json
generated
45
package.json
|
@ -1,45 +0,0 @@
|
|||
{
|
||||
"name": "@euterpe.js/source",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"format": "prettier --write '**/*.{js,ts,css,html,json,mjs}'",
|
||||
"publish-player": "nx build player && cd dist/packages/player && npm publish --access=public",
|
||||
"publish-visualizer": "nx build visualizer && cd dist/packages/visualizer && npm publish --access=public",
|
||||
"publish-library": "nx build music-library && cd dist/packages/music-library && npm publish --access=public",
|
||||
"publish-euterpe": "nx build euterpe && cd dist/packages/euterpe && npm publish --access=public",
|
||||
"publish-all": "npm run publish-player && npm run publish-library && npm run publish-visualizer && npm run publish-euterpe"
|
||||
},
|
||||
"private": false,
|
||||
"devDependencies": {
|
||||
"@nx/cypress": "16.2.1",
|
||||
"@nx/eslint-plugin": "16.2.1",
|
||||
"@nx/js": "16.2.1",
|
||||
"@nx/linter": "16.2.1",
|
||||
"@nx/vite": "^16.2.1",
|
||||
"@nx/web": "^16.2.1",
|
||||
"@nx/workspace": "16.2.1",
|
||||
"@swc/core": "~1.3.51",
|
||||
"@types/node": "^20.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||
"@typescript-eslint/parser": "^5.58.0",
|
||||
"@vitest/coverage-c8": "^0.31.0",
|
||||
"@vitest/ui": "^0.31.0",
|
||||
"cypress": "^12.11.0",
|
||||
"eslint": "~8.15.0",
|
||||
"eslint-config-prettier": "8.1.0",
|
||||
"eslint-plugin-cypress": "^2.10.3",
|
||||
"jsdom": "~20.0.3",
|
||||
"nx": "16.2.1",
|
||||
"prettier": "^2.6.2",
|
||||
"swc-loader": "0.1.15",
|
||||
"typescript": "~5.0.2",
|
||||
"vite": "^4.3.4",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-tsconfig-paths": "^4.0.2",
|
||||
"vitest": "^0.31.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"name": "@euterpe.js/dj",
|
||||
"version": "0.0.1",
|
||||
"type": "module"
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
{
|
||||
"name": "dj",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/dj/src",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:tsc",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"options": {
|
||||
"outputPath": "dist/packages/dj",
|
||||
"main": "packages/dj/src/index.ts",
|
||||
"tsConfig": "packages/dj/tsconfig.lib.json",
|
||||
"assets": [
|
||||
"packages/dj/*.md"
|
||||
]
|
||||
}
|
||||
},
|
||||
"publish": {
|
||||
"command": "node tools/scripts/publish.mjs dj {args.ver} {args.tag}",
|
||||
"dependsOn": [
|
||||
"build"
|
||||
]
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/linter:eslint",
|
||||
"outputs": [
|
||||
"{options.outputFile}"
|
||||
],
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"packages/dj/**/*.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
# Euterpe
|
||||
|
||||
Fully featured AudioContext music player for the web.
|
||||
|
||||
## Euterpe in production:
|
||||
- Hypertrance ( [site](https://hypertrance.eu/), [repository](https://github.com/nuphory/hypertrance.eu) )
|
||||
|
||||
Features:
|
||||
- "Local" library/database for songs, collections, artists, waveforms, artist links and much more!
|
||||
- Queue and history
|
||||
- Easy way to create Vector based audio visuals
|
||||
- Library automatization based on folder/file structure, preprocessing and encoding media files for all platforms
|
||||
- Safe. Provides wrappers for all functions that are either unsafe or don't give a success return. (very Rust inspired, yes.)
|
||||
- Frontend library agnostic
|
||||
|
||||
## How to use:
|
||||
|
||||
#### Simple demo [here](https://github.com/euterpe-js/euterpe-source/tree/master/packages/euterpe-web-test)
|
||||
|
||||
Since this package is just a compilation of our smaller modules, you can read individual modules' tutorials on their respective npm page:
|
||||
- [Euterpe Player](https://www.npmjs.com/package/@euterpe.js/player)
|
||||
- [Euterpe Visualizer](https://www.npmjs.com/package/@euterpe.js/visualizer)
|
||||
- [Euterpe Music Library](https://www.npmjs.com/package/@euterpe.js/music-library)
|
||||
|
||||
You can further check out how to automate database creation from folder structure, auto encode media for all platforms and create waveform svgs for songs here:
|
||||
- [Euterpe Preprocessor](https://www.npmjs.com/package/@euterpe.js/preprocessor)
|
||||
|
||||
This module builds on those, and further adds functions for playing backwards, forwards and managing the queue.
|
||||
|
||||
First we create a database with our songs
|
||||
|
||||
`db.ts`
|
||||
```ts
|
||||
import { DB, Song, Artist, Ref, RefTo, Platforms } from "@euterpe.js/music-library"
|
||||
export const db = new DB
|
||||
|
||||
db.add([
|
||||
//The IDs are added incrementally & are 0 based., so first artists ID added is 0, next 1 etc...
|
||||
//You can specify the ID manually if you want
|
||||
new Artist({
|
||||
name: "Machinedrum",
|
||||
}),
|
||||
new Artist({
|
||||
name: "Tanerélle",
|
||||
}),
|
||||
new Artist({
|
||||
name: "Mono/Poly",
|
||||
}),
|
||||
new Artist({
|
||||
name: "IMANU",
|
||||
links: [
|
||||
[Platforms.Spotify, new URL("https://open.spotify.com/artist/5Y7rFm0tiJTVDzGLMzz0W1?si=DRaZyugTTIqlBHDkMGKVqA&nd=1")]
|
||||
]
|
||||
}),
|
||||
])
|
||||
db.add([
|
||||
new Song({
|
||||
//Refrences are constructed as such. This allows to get to the artist from either collection or song
|
||||
artists: [new Ref(RefTo.Artists, 2), new Ref(RefTo.Artists, 3), new Ref(RefTo.Artists, 4)],
|
||||
duration: 252,
|
||||
name: "Star",
|
||||
remix_artists: [new Ref(RefTo.Artists, 5)],
|
||||
url: new URL("http://" + window.location.host + "/Machinedrum, Tanerelle & Mono Poly - Star (IMANU Remix) final.mp3")
|
||||
}),
|
||||
])
|
||||
|
||||
```
|
||||
|
||||
Then we build our Euterpe player and assign the db to it. Then it's just a matter of creating event listeners to the dom and binding them to Euterpes functions.
|
||||
|
||||
`main.ts`
|
||||
```ts
|
||||
import { db } from "./db";
|
||||
import { EuterpeBuilder } from "@euterpe.js/euterpe"
|
||||
|
||||
let is_seeking = false
|
||||
const euterpe = new EuterpeBuilder(document.querySelector("#audio")!, db)
|
||||
.build()
|
||||
|
||||
document.querySelector("#seek")?.addEventListener("mouseup", (e) => {
|
||||
try {
|
||||
euterpe.try_seek(e.target?.valueAsNumber)
|
||||
} catch {
|
||||
alert("Failed seeking! " + e)
|
||||
}
|
||||
is_seeking = false
|
||||
})
|
||||
|
||||
euterpe.on_song_change((_, song_name) => {
|
||||
document.querySelector("#text-playing")!.innerHTML = song_name
|
||||
})
|
||||
|
||||
document.querySelector("#previous")?.addEventListener("click", () => {
|
||||
euterpe.try_previous_song_looping().catch((e) => alert(e + "Failed to change song"))
|
||||
})
|
||||
|
||||
document.querySelector("#next")?.addEventListener("click", () => {
|
||||
euterpe.try_next_song_looping().catch((e) => alert(e + "Failed to change song"))
|
||||
})
|
||||
|
||||
document.querySelector("#mute")?.addEventListener("click", () => {
|
||||
euterpe.mute()
|
||||
})
|
||||
|
||||
document.querySelector("#unmute")?.addEventListener("click", () => {
|
||||
euterpe.unmute()
|
||||
})
|
||||
|
||||
document.querySelector("#toggle-play")?.addEventListener("click", () => {
|
||||
euterpe.try_play_toggle().catch((e) => alert("failed to toggle pause/play!" + e))
|
||||
})
|
||||
|
||||
document.querySelector("#volume")?.addEventListener("input", (e) => {
|
||||
euterpe.change_volume(e.target?.valueAsNumber)
|
||||
})
|
||||
|
||||
//disables time updates so the time slider doesn't slip away from user
|
||||
document.querySelector("#seek")?.addEventListener("mousedown", () => {
|
||||
is_seeking = true
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
Then we can set up listeners to Euterpes events to keep the UI up todate as well
|
||||
|
||||
`main.ts`
|
||||
```ts
|
||||
//...
|
||||
// Subscriptions to song and AudioContext changes, eg. time, name..
|
||||
euterpe.on_duration_formatted((time) => {
|
||||
document.querySelector("#duration")!.innerHTML = time
|
||||
document.querySelector("#seek")!.max = "" + euterpe.current_song_duration
|
||||
})
|
||||
|
||||
euterpe.on_time_tick_formatted((time) => {
|
||||
document.querySelector("#current")!.innerHTML = time
|
||||
})
|
||||
|
||||
euterpe.on_time_tick((time) => {
|
||||
if (is_seeking) return
|
||||
document.querySelector("#seek")!.value = "" + time
|
||||
dev_queue_update()
|
||||
dev_history_update()
|
||||
})
|
||||
|
||||
euterpe.on_song_change((_, song_name) => {
|
||||
document.querySelector("#text-playing")!.innerHTML = song_name
|
||||
})
|
||||
|
||||
//preload after setting all listeners to make sure you capture the song update!
|
||||
euterpe.try_preload_song(0).catch((e) => console.log(e + " Failed to preload"))
|
||||
|
||||
//..
|
||||
function dev_queue_update() {
|
||||
const p = document.querySelector("#queue-info") as HTMLParagraphElement
|
||||
const dev_arr = []
|
||||
for (const song of euterpe.queue) {
|
||||
dev_arr.push(`Name: ${song.name}, ID: ${song.id} |`)
|
||||
}
|
||||
p.innerHTML = dev_arr.toString()
|
||||
}
|
||||
|
||||
function dev_history_update() {
|
||||
const p = document.querySelector("#history-info") as HTMLParagraphElement
|
||||
const dev_arr = []
|
||||
for (const song of euterpe.played_history) {
|
||||
dev_arr.push(`Name: ${song.name}, ID: ${song.id} |`)
|
||||
}
|
||||
p.innerHTML = dev_arr.toString()
|
||||
}
|
||||
|
||||
```
|
||||
and it's done!
|
||||
For vizualizer demo, or how to use the core parts of the Euterpe libraries separately, check out the individual repos readmes.
|
||||
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
{
|
||||
"name": "@euterpe.js/euterpe",
|
||||
"version": "2.1.0",
|
||||
"type": "module",
|
||||
"description": "Fully featured solution for playing music on the web. Support for local library, audio visuals and more!",
|
||||
"main": "./src/index.js",
|
||||
"author": {
|
||||
"name": "Djkáťo",
|
||||
"email": "djkatovfx@gmail.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/euterpe-js/euterpe-source.git"
|
||||
},
|
||||
"homepage": "https://github.com/euterpe-js/euterpe-source/tree/master/packages/euterpe#readme",
|
||||
"keywords": [
|
||||
"audio",
|
||||
"library",
|
||||
"music-database",
|
||||
"audio-player",
|
||||
"webaudio",
|
||||
"database",
|
||||
"db"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.d.ts",
|
||||
"import": "./src/index.js",
|
||||
"require": "./src/lib/euterpe.js"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
{
|
||||
"name": "euterpe",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/euterpe/src",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:tsc",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"options": {
|
||||
"outputPath": "dist/packages/euterpe",
|
||||
"main": "packages/euterpe/src/index.ts",
|
||||
"tsConfig": "packages/euterpe/tsconfig.lib.json",
|
||||
"assets": [
|
||||
"packages/euterpe/*.md"
|
||||
]
|
||||
}
|
||||
},
|
||||
"publish": {
|
||||
"command": "node tools/scripts/publish.mjs euterpe {args.ver} {args.tag}",
|
||||
"dependsOn": [
|
||||
"build"
|
||||
]
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/linter:eslint",
|
||||
"outputs": [
|
||||
"{options.outputFile}"
|
||||
],
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"packages/euterpe/**/*.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export function euterpe(): string {
|
||||
return "euterpe"
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "ESNext",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
|
||||
}
|