First draft of musicbrainz interface
Some reorganisation Remove unnecessary trait Basic example working Handle errors Handle dates Expand scope of MusicBrainz reference Type the musicbrainz refs Explicit constructors for str Handle MBIDs for albums Add search to the API Handle primary and secondary types Simplify AlbumDate Passing unit tests Tests pass Prevent compiler/clippy warnings Finish unit tests Clippy Remove old deserialize version
This commit is contained in:
parent
c53ba8f35f
commit
473825b396
593
Cargo.lock
generated
593
Cargo.lock
generated
@ -91,6 +91,12 @@ dependencies = [
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@ -103,6 +109,12 @@ version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
@ -167,6 +179,22 @@ dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.27.0"
|
||||
@ -204,6 +232,21 @@ version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
@ -220,6 +263,27 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
@ -235,12 +299,79 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
|
||||
|
||||
[[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-io"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
||||
|
||||
[[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-io",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
@ -275,6 +406,77 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
@ -285,12 +487,28 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
@ -306,6 +524,15 @@ version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[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 = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@ -355,6 +582,12 @@ version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
@ -413,6 +646,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"openssh",
|
||||
"ratatui",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"structopt",
|
||||
@ -423,6 +657,24 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "non-zero-byte-slice"
|
||||
version = "0.1.0"
|
||||
@ -491,6 +743,50 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
@ -532,6 +828,18 @@ version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.1.0"
|
||||
@ -629,6 +937,46 @@ dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eea5a9eb898d3783f17c6407670e3592fd174cb81a10e51d4c37f49450b9946"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
@ -648,6 +996,15 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
@ -660,12 +1017,44 @@ version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sendfd"
|
||||
version = "0.4.3"
|
||||
@ -707,6 +1096,18 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shell-escape"
|
||||
version = "0.1.5"
|
||||
@ -743,6 +1144,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.1"
|
||||
@ -868,6 +1278,33 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.0"
|
||||
@ -967,6 +1404,16 @@ dependencies = [
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-pipe"
|
||||
version = "0.2.12"
|
||||
@ -977,6 +1424,51 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "typed-builder"
|
||||
version = "0.18.1"
|
||||
@ -1047,6 +1539,12 @@ version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
@ -1059,12 +1557,97 @@ version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||
dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[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 2.0.48",
|
||||
"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 2.0.48",
|
||||
"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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@ -1219,6 +1802,16 @@ version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.32"
|
||||
|
@ -11,6 +11,7 @@ crossterm = { version = "0.27.0", optional = true}
|
||||
once_cell = { version = "1.19.0", optional = true}
|
||||
openssh = { version = "0.10.3", features = ["native-mux"], default-features = false, optional = true}
|
||||
ratatui = { version = "0.26.0", optional = true}
|
||||
reqwest = { version = "0.11.25", features = ["blocking", "json"], optional = true }
|
||||
serde = { version = "1.0.196", features = ["derive"], optional = true }
|
||||
serde_json = { version = "1.0.113", optional = true}
|
||||
structopt = { version = "0.3.26", optional = true}
|
||||
@ -32,6 +33,7 @@ bin = ["structopt"]
|
||||
database-json = ["serde", "serde_json"]
|
||||
library-beets = []
|
||||
library-beets-ssh = ["openssh", "tokio"]
|
||||
musicbrainz-api = ["reqwest", "serde", "serde_json"]
|
||||
tui = ["aho-corasick", "crossterm", "once_cell", "ratatui"]
|
||||
|
||||
[[bin]]
|
||||
@ -42,5 +44,9 @@ required-features = ["bin", "database-json", "library-beets", "library-beets-ssh
|
||||
name = "musichoard-edit"
|
||||
required-features = ["bin", "database-json"]
|
||||
|
||||
[[bin]]
|
||||
name = "musichoard-reqwest"
|
||||
required-features = ["musicbrainz-api"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
13
README.md
13
README.md
@ -1,5 +1,18 @@
|
||||
# Music Hoard
|
||||
|
||||
## Developing
|
||||
|
||||
### Pre-requisites
|
||||
|
||||
#### musicbrainz-api
|
||||
|
||||
This feature requires the `openssl` system library.
|
||||
|
||||
On Fedora:
|
||||
``` sh
|
||||
sudo dnf install openssl-devel
|
||||
```
|
||||
|
||||
## Usage notes
|
||||
|
||||
### Text selection
|
||||
|
@ -4,7 +4,7 @@ use structopt::{clap::AppSettings, StructOpt};
|
||||
|
||||
use musichoard::{
|
||||
collection::{album::AlbumId, artist::ArtistId},
|
||||
database::json::{backend::JsonDatabaseFileBackend, JsonDatabase},
|
||||
external::database::json::{backend::JsonDatabaseFileBackend, JsonDatabase},
|
||||
IMusicHoardDatabase, MusicHoard, MusicHoardBuilder, NoLibrary,
|
||||
};
|
||||
|
||||
|
@ -5,7 +5,7 @@ use std::{
|
||||
|
||||
use crate::core::collection::{
|
||||
merge::{Merge, MergeSorted, WithId},
|
||||
musicbrainz::MusicBrainzUrl,
|
||||
musicbrainz::MbAlbumRef,
|
||||
track::{Track, TrackFormat},
|
||||
};
|
||||
|
||||
@ -15,7 +15,9 @@ pub struct Album {
|
||||
pub id: AlbumId,
|
||||
pub date: AlbumDate,
|
||||
pub seq: AlbumSeq,
|
||||
pub musicbrainz: Option<MusicBrainzUrl>,
|
||||
pub musicbrainz: Option<MbAlbumRef>,
|
||||
pub primary_type: Option<AlbumPrimaryType>,
|
||||
pub secondary_types: Vec<AlbumSecondaryType>,
|
||||
pub tracks: Vec<Track>,
|
||||
}
|
||||
|
||||
@ -35,83 +37,34 @@ pub struct AlbumId {
|
||||
|
||||
// There are crates for handling dates, but we don't need much complexity beyond year-month-day.
|
||||
/// The album's release date.
|
||||
#[derive(Clone, Debug, Default, PartialEq, PartialOrd, Ord, Eq, Hash)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct AlbumDate {
|
||||
pub year: u32,
|
||||
pub month: AlbumMonth,
|
||||
pub day: u8,
|
||||
pub year: Option<u32>,
|
||||
pub month: Option<u8>,
|
||||
pub day: Option<u8>,
|
||||
}
|
||||
|
||||
impl AlbumDate {
|
||||
pub fn new<M: Into<AlbumMonth>>(year: u32, month: M, day: u8) -> Self {
|
||||
AlbumDate {
|
||||
year,
|
||||
month: month.into(),
|
||||
day,
|
||||
}
|
||||
pub fn new(year: Option<u32>, month: Option<u8>, day: Option<u8>) -> Self {
|
||||
AlbumDate { year, month, day }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for AlbumDate {
|
||||
fn from(value: u32) -> Self {
|
||||
AlbumDate::new(value, AlbumMonth::default(), 0)
|
||||
AlbumDate::new(Some(value), None, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Into<AlbumMonth>> From<(u32, M)> for AlbumDate {
|
||||
fn from(value: (u32, M)) -> Self {
|
||||
AlbumDate::new(value.0, value.1, 0)
|
||||
impl From<(u32, u8)> for AlbumDate {
|
||||
fn from(value: (u32, u8)) -> Self {
|
||||
AlbumDate::new(Some(value.0), Some(value.1), None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Into<AlbumMonth>> From<(u32, M, u8)> for AlbumDate {
|
||||
fn from(value: (u32, M, u8)) -> Self {
|
||||
AlbumDate::new(value.0, value.1, value.2)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Ord, Eq, Hash)]
|
||||
pub enum AlbumMonth {
|
||||
#[default]
|
||||
None = 0,
|
||||
January = 1,
|
||||
February = 2,
|
||||
March = 3,
|
||||
April = 4,
|
||||
May = 5,
|
||||
June = 6,
|
||||
July = 7,
|
||||
August = 8,
|
||||
September = 9,
|
||||
October = 10,
|
||||
November = 11,
|
||||
December = 12,
|
||||
}
|
||||
|
||||
impl From<u8> for AlbumMonth {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => AlbumMonth::January,
|
||||
2 => AlbumMonth::February,
|
||||
3 => AlbumMonth::March,
|
||||
4 => AlbumMonth::April,
|
||||
5 => AlbumMonth::May,
|
||||
6 => AlbumMonth::June,
|
||||
7 => AlbumMonth::July,
|
||||
8 => AlbumMonth::August,
|
||||
9 => AlbumMonth::September,
|
||||
10 => AlbumMonth::October,
|
||||
11 => AlbumMonth::November,
|
||||
12 => AlbumMonth::December,
|
||||
_ => AlbumMonth::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AlbumMonth {
|
||||
pub fn is_none(&self) -> bool {
|
||||
matches!(self, AlbumMonth::None)
|
||||
impl From<(u32, u8, u8)> for AlbumDate {
|
||||
fn from(value: (u32, u8, u8)) -> Self {
|
||||
AlbumDate::new(Some(value.0), Some(value.1), Some(value.2))
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,6 +72,50 @@ impl AlbumMonth {
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Ord, Eq, Hash)]
|
||||
pub struct AlbumSeq(pub u8);
|
||||
|
||||
/// Based on [MusicBrainz types](https://musicbrainz.org/doc/Release_Group/Type).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum AlbumPrimaryType {
|
||||
/// Album
|
||||
Album,
|
||||
/// Single
|
||||
Single,
|
||||
/// EP
|
||||
Ep,
|
||||
/// Broadcast
|
||||
Broadcast,
|
||||
/// Other
|
||||
Other,
|
||||
}
|
||||
|
||||
/// Based on [MusicBrainz types](https://musicbrainz.org/doc/Release_Group/Type).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum AlbumSecondaryType {
|
||||
/// Compilation
|
||||
Compilation,
|
||||
/// Soundtrack
|
||||
Soundtrack,
|
||||
/// Spokenword
|
||||
Spokenword,
|
||||
/// Interview
|
||||
Interview,
|
||||
/// Audiobook
|
||||
Audiobook,
|
||||
/// Audio drama
|
||||
AudioDrama,
|
||||
/// Live
|
||||
Live,
|
||||
/// Remix
|
||||
Remix,
|
||||
/// DJ-mix
|
||||
DjMix,
|
||||
/// Mixtape/Street
|
||||
MixtapeStreet,
|
||||
/// Demo
|
||||
Demo,
|
||||
/// Field recording
|
||||
FieldRecording,
|
||||
}
|
||||
|
||||
/// The album's ownership status.
|
||||
pub enum AlbumStatus {
|
||||
None,
|
||||
@ -135,12 +132,19 @@ impl AlbumStatus {
|
||||
}
|
||||
|
||||
impl Album {
|
||||
pub fn new<Id: Into<AlbumId>, Date: Into<AlbumDate>>(id: Id, date: Date) -> Self {
|
||||
pub fn new<Id: Into<AlbumId>, Date: Into<AlbumDate>>(
|
||||
id: Id,
|
||||
date: Date,
|
||||
primary_type: Option<AlbumPrimaryType>,
|
||||
secondary_types: Vec<AlbumSecondaryType>,
|
||||
) -> Self {
|
||||
Album {
|
||||
id: id.into(),
|
||||
date: date.into(),
|
||||
seq: AlbumSeq::default(),
|
||||
musicbrainz: None,
|
||||
primary_type,
|
||||
secondary_types,
|
||||
tracks: vec![],
|
||||
}
|
||||
}
|
||||
@ -160,6 +164,14 @@ impl Album {
|
||||
pub fn clear_seq(&mut self) {
|
||||
self.seq = AlbumSeq::default();
|
||||
}
|
||||
|
||||
pub fn set_musicbrainz_ref(&mut self, mbref: MbAlbumRef) {
|
||||
_ = self.musicbrainz.insert(mbref);
|
||||
}
|
||||
|
||||
pub fn clear_musicbrainz_ref(&mut self) {
|
||||
_ = self.musicbrainz.take();
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Album {
|
||||
@ -178,6 +190,11 @@ impl Merge for Album {
|
||||
fn merge_in_place(&mut self, other: Self) {
|
||||
assert_eq!(self.id, other.id);
|
||||
self.seq = std::cmp::max(self.seq, other.seq);
|
||||
|
||||
self.musicbrainz = self.musicbrainz.take().or(other.musicbrainz);
|
||||
self.primary_type = self.primary_type.take().or(other.primary_type);
|
||||
self.secondary_types.merge_in_place(other.secondary_types);
|
||||
|
||||
let tracks = mem::take(&mut self.tracks);
|
||||
self.tracks = MergeSorted::new(tracks.into_iter(), other.tracks.into_iter()).collect();
|
||||
}
|
||||
@ -213,54 +230,32 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn album_month() {
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(0), AlbumMonth::None);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(1), AlbumMonth::January);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(2), AlbumMonth::February);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(3), AlbumMonth::March);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(4), AlbumMonth::April);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(5), AlbumMonth::May);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(6), AlbumMonth::June);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(7), AlbumMonth::July);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(8), AlbumMonth::August);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(9), AlbumMonth::September);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(10), AlbumMonth::October);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(11), AlbumMonth::November);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(12), AlbumMonth::December);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(13), AlbumMonth::None);
|
||||
assert_eq!(<u8 as Into<AlbumMonth>>::into(255), AlbumMonth::None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn album_date_from() {
|
||||
let date: AlbumDate = 1986.into();
|
||||
assert_eq!(date, AlbumDate::new(1986, AlbumMonth::default(), 0));
|
||||
assert_eq!(date, AlbumDate::new(Some(1986), None, None));
|
||||
|
||||
let date: AlbumDate = (1986, 5).into();
|
||||
assert_eq!(date, AlbumDate::new(1986, AlbumMonth::May, 0));
|
||||
|
||||
let date: AlbumDate = (1986, AlbumMonth::June).into();
|
||||
assert_eq!(date, AlbumDate::new(1986, AlbumMonth::June, 0));
|
||||
assert_eq!(date, AlbumDate::new(Some(1986), Some(5), None));
|
||||
|
||||
let date: AlbumDate = (1986, 6, 8).into();
|
||||
assert_eq!(date, AlbumDate::new(1986, AlbumMonth::June, 8));
|
||||
assert_eq!(date, AlbumDate::new(Some(1986), Some(6), Some(8)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_date_seq_cmp() {
|
||||
let date = AlbumDate::new(2024, 3, 2);
|
||||
let date: AlbumDate = (2024, 3, 2).into();
|
||||
|
||||
let album_id_1 = AlbumId {
|
||||
title: String::from("album z"),
|
||||
};
|
||||
let mut album_1 = Album::new(album_id_1, date.clone());
|
||||
let mut album_1 = Album::new(album_id_1, date.clone(), None, vec![]);
|
||||
album_1.set_seq(AlbumSeq(1));
|
||||
|
||||
let album_id_2 = AlbumId {
|
||||
title: String::from("album a"),
|
||||
};
|
||||
let mut album_2 = Album::new(album_id_2, date.clone());
|
||||
let mut album_2 = Album::new(album_id_2, date.clone(), None, vec![]);
|
||||
album_2.set_seq(AlbumSeq(2));
|
||||
|
||||
assert_ne!(album_1, album_2);
|
||||
@ -269,7 +264,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn set_clear_seq() {
|
||||
let mut album = Album::new("An album", AlbumDate::default());
|
||||
let mut album = Album::new("An album", AlbumDate::default(), None, vec![]);
|
||||
|
||||
assert_eq!(album.seq, AlbumSeq(0));
|
||||
|
||||
@ -322,4 +317,34 @@ mod tests {
|
||||
let merged = left.clone().merge(right);
|
||||
assert_eq!(expected, merged);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_clear_musicbrainz_url() {
|
||||
const MUSICBRAINZ: &str =
|
||||
"https://musicbrainz.org/release-group/c12897a3-af7a-3466-8892-58af84765813";
|
||||
const MUSICBRAINZ_2: &str =
|
||||
"https://musicbrainz.org/release-group/0eaa9306-e6df-47be-94ce-04bfe3df782c";
|
||||
|
||||
let mut album = Album::new(AlbumId::new("an album"), AlbumDate::default(), None, vec![]);
|
||||
|
||||
let mut expected: Option<MbAlbumRef> = None;
|
||||
assert_eq!(album.musicbrainz, expected);
|
||||
|
||||
// Setting a URL on an album.
|
||||
album.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||
_ = expected.insert(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||
assert_eq!(album.musicbrainz, expected);
|
||||
|
||||
album.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||
assert_eq!(album.musicbrainz, expected);
|
||||
|
||||
album.set_musicbrainz_ref(MbAlbumRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
||||
_ = expected.insert(MbAlbumRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
||||
assert_eq!(album.musicbrainz, expected);
|
||||
|
||||
// Clearing URLs.
|
||||
album.clear_musicbrainz_ref();
|
||||
_ = expected.take();
|
||||
assert_eq!(album.musicbrainz, expected);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use std::{
|
||||
use crate::core::collection::{
|
||||
album::Album,
|
||||
merge::{Merge, MergeCollections, WithId},
|
||||
musicbrainz::MusicBrainzUrl,
|
||||
musicbrainz::MbArtistRef,
|
||||
};
|
||||
|
||||
/// An artist.
|
||||
@ -15,7 +15,7 @@ use crate::core::collection::{
|
||||
pub struct Artist {
|
||||
pub id: ArtistId,
|
||||
pub sort: Option<ArtistId>,
|
||||
pub musicbrainz: Option<MusicBrainzUrl>,
|
||||
pub musicbrainz: Option<MbArtistRef>,
|
||||
pub properties: HashMap<String, Vec<String>>,
|
||||
pub albums: Vec<Album>,
|
||||
}
|
||||
@ -58,11 +58,11 @@ impl Artist {
|
||||
_ = self.sort.take();
|
||||
}
|
||||
|
||||
pub fn set_musicbrainz_url(&mut self, url: MusicBrainzUrl) {
|
||||
_ = self.musicbrainz.insert(url);
|
||||
pub fn set_musicbrainz_ref(&mut self, mbref: MbArtistRef) {
|
||||
_ = self.musicbrainz.insert(mbref);
|
||||
}
|
||||
|
||||
pub fn clear_musicbrainz_url(&mut self) {
|
||||
pub fn clear_musicbrainz_ref(&mut self) {
|
||||
_ = self.musicbrainz.take();
|
||||
}
|
||||
|
||||
@ -216,23 +216,23 @@ mod tests {
|
||||
fn set_clear_musicbrainz_url() {
|
||||
let mut artist = Artist::new(ArtistId::new("an artist"));
|
||||
|
||||
let mut expected: Option<MusicBrainzUrl> = None;
|
||||
let mut expected: Option<MbArtistRef> = None;
|
||||
assert_eq!(artist.musicbrainz, expected);
|
||||
|
||||
// Setting a URL on an artist.
|
||||
artist.set_musicbrainz_url(MusicBrainzUrl::artist_from_str(MUSICBRAINZ).unwrap());
|
||||
_ = expected.insert(MusicBrainzUrl::artist_from_str(MUSICBRAINZ).unwrap());
|
||||
artist.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||
_ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||
assert_eq!(artist.musicbrainz, expected);
|
||||
|
||||
artist.set_musicbrainz_url(MusicBrainzUrl::artist_from_str(MUSICBRAINZ).unwrap());
|
||||
artist.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||
assert_eq!(artist.musicbrainz, expected);
|
||||
|
||||
artist.set_musicbrainz_url(MusicBrainzUrl::artist_from_str(MUSICBRAINZ_2).unwrap());
|
||||
_ = expected.insert(MusicBrainzUrl::artist_from_str(MUSICBRAINZ_2).unwrap());
|
||||
artist.set_musicbrainz_ref(MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
||||
_ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ_2).unwrap());
|
||||
assert_eq!(artist.musicbrainz, expected);
|
||||
|
||||
// Clearing URLs.
|
||||
artist.clear_musicbrainz_url();
|
||||
artist.clear_musicbrainz_ref();
|
||||
_ = expected.take();
|
||||
assert_eq!(artist.musicbrainz, expected);
|
||||
}
|
||||
|
@ -3,65 +3,110 @@ use std::fmt::{Debug, Display};
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::core::collection::Error;
|
||||
use crate::{core::collection::Error, interface::musicbrainz::Mbid};
|
||||
|
||||
/// MusicBrainz reference.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct MusicBrainzUrl(Url);
|
||||
const MB_DOMAIN: &str = "musicbrainz.org";
|
||||
|
||||
impl MusicBrainzUrl {
|
||||
pub fn mbid(&self) -> &str {
|
||||
// The URL is assumed to have been validated.
|
||||
self.0.path_segments().and_then(|mut ps| ps.nth(1)).unwrap()
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct MusicBrainzRef {
|
||||
mbid: Mbid,
|
||||
url: Url,
|
||||
}
|
||||
|
||||
pub fn artist_from_str<S: AsRef<str>>(url: S) -> Result<Self, Error> {
|
||||
Self::artist_from_url(url.as_ref().try_into()?)
|
||||
pub trait IMusicBrainzRef {
|
||||
fn mbid(&self) -> &Mbid;
|
||||
fn url(&self) -> &Url;
|
||||
fn entity() -> &'static str;
|
||||
}
|
||||
|
||||
pub fn album_from_str<S: AsRef<str>>(url: S) -> Result<Self, Error> {
|
||||
Self::album_from_url(url.as_ref().try_into()?)
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct MbArtistRef(MusicBrainzRef);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct MbAlbumRef(MusicBrainzRef);
|
||||
|
||||
macro_rules! impl_imusicbrainzref {
|
||||
($mbref:ident, $entity:literal) => {
|
||||
impl IMusicBrainzRef for $mbref {
|
||||
fn mbid(&self) -> &Mbid {
|
||||
&self.0.mbid
|
||||
}
|
||||
|
||||
pub fn artist_from_url(url: Url) -> Result<Self, Error> {
|
||||
Self::new(url, "artist")
|
||||
fn url(&self) -> &Url {
|
||||
&self.0.url
|
||||
}
|
||||
|
||||
pub fn album_from_url(url: Url) -> Result<Self, Error> {
|
||||
Self::new(url, "release-group")
|
||||
fn entity() -> &'static str {
|
||||
$entity
|
||||
}
|
||||
}
|
||||
|
||||
fn new(url: Url, mb_type: &str) -> Result<Self, Error> {
|
||||
impl TryFrom<Url> for $mbref {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(url: Url) -> Result<Self, Self::Error> {
|
||||
Ok($mbref(MusicBrainzRef::from_url(url, $mbref::entity())?))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Uuid> for $mbref {
|
||||
fn from(uuid: Uuid) -> Self {
|
||||
$mbref(MusicBrainzRef::from_uuid(uuid, $mbref::entity()))
|
||||
}
|
||||
}
|
||||
|
||||
impl $mbref {
|
||||
pub fn from_url_str<S: AsRef<str>>(url: S) -> Result<Self, Error> {
|
||||
let url: Url = url.as_ref().try_into()?;
|
||||
url.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl $mbref {
|
||||
pub fn from_uuid_str<S: AsRef<str>>(uuid: S) -> Result<Self, Error> {
|
||||
let uuid: Uuid = uuid.as_ref().try_into()?;
|
||||
Ok(uuid.into())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_imusicbrainzref!(MbArtistRef, "artist");
|
||||
impl_imusicbrainzref!(MbAlbumRef, "release-group");
|
||||
|
||||
impl MusicBrainzRef {
|
||||
fn from_url(url: Url, entity: &'static str) -> Result<Self, Error> {
|
||||
if !url
|
||||
.domain()
|
||||
.map(|u| u.ends_with("musicbrainz.org"))
|
||||
.map(|u| u.ends_with(MB_DOMAIN))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return Err(Self::invalid_url_error(url, mb_type));
|
||||
return Err(Self::invalid_url_error(url, entity));
|
||||
}
|
||||
|
||||
// path_segments only returns an empty iterator if the URL cannot-be-a-base. However, if the
|
||||
// URL cannot-be-a-base then it will fail the check above already as it won't have a domain.
|
||||
if url.path_segments().and_then(|mut ps| ps.nth(0)).unwrap() != mb_type {
|
||||
return Err(Self::invalid_url_error(url, mb_type));
|
||||
if url.path_segments().and_then(|mut ps| ps.nth(0)).unwrap() != entity {
|
||||
return Err(Self::invalid_url_error(url, entity));
|
||||
}
|
||||
|
||||
match url.path_segments().and_then(|mut ps| ps.nth(1)) {
|
||||
Some(segment) => Uuid::try_parse(segment)?,
|
||||
None => return Err(Self::invalid_url_error(url, mb_type)),
|
||||
let mbid = match url.path_segments().and_then(|mut ps| ps.nth(1)) {
|
||||
Some(segment) => Uuid::try_parse(segment)?.into(),
|
||||
None => return Err(Self::invalid_url_error(url, entity)),
|
||||
};
|
||||
|
||||
Ok(MusicBrainzUrl(url))
|
||||
Ok(MusicBrainzRef { mbid, url })
|
||||
}
|
||||
|
||||
fn invalid_url_error<U: Display>(url: U, mb_type: &str) -> Error {
|
||||
Error::UrlError(format!("invalid {mb_type} MusicBrainz URL: {url}"))
|
||||
}
|
||||
fn from_uuid(uuid: Uuid, entity: &'static str) -> Self {
|
||||
let uuid_str = uuid.to_string();
|
||||
let mbid = uuid.into();
|
||||
let url = Url::parse(&format!("https://{MB_DOMAIN}/{entity}/{uuid_str}")).unwrap();
|
||||
MusicBrainzRef { mbid, url }
|
||||
}
|
||||
|
||||
impl AsRef<str> for MusicBrainzUrl {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0.as_ref()
|
||||
fn invalid_url_error<U: Display>(url: U, entity: &'static str) -> Error {
|
||||
Error::UrlError(format!("invalid {entity} MusicBrainz URL: {url}"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,14 +119,18 @@ mod tests {
|
||||
let uuid = "d368baa8-21ca-4759-9731-0b2753071ad8";
|
||||
let url_str = format!("https://musicbrainz.org/artist/{uuid}");
|
||||
|
||||
let mb = MusicBrainzUrl::artist_from_str(&url_str).unwrap();
|
||||
assert_eq!(url_str, mb.as_ref());
|
||||
assert_eq!(uuid, mb.mbid());
|
||||
let mb = MbArtistRef::from_url_str(&url_str).unwrap();
|
||||
assert_eq!(url_str, mb.url().as_ref());
|
||||
assert_eq!(uuid, mb.mbid().uuid().to_string());
|
||||
|
||||
let mb = MbArtistRef::from_uuid_str(uuid).unwrap();
|
||||
assert_eq!(url_str, mb.url().as_ref());
|
||||
assert_eq!(uuid, mb.mbid().uuid().to_string());
|
||||
|
||||
let url: Url = url_str.as_str().try_into().unwrap();
|
||||
let mb = MusicBrainzUrl::artist_from_url(url).unwrap();
|
||||
assert_eq!(url_str, mb.as_ref());
|
||||
assert_eq!(uuid, mb.mbid());
|
||||
let mb: MbArtistRef = url.try_into().unwrap();
|
||||
assert_eq!(url_str, mb.url().as_ref());
|
||||
assert_eq!(uuid, mb.mbid().uuid().to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -89,21 +138,25 @@ mod tests {
|
||||
let uuid = "d368baa8-21ca-4759-9731-0b2753071ad8";
|
||||
let url_str = format!("https://musicbrainz.org/release-group/{uuid}");
|
||||
|
||||
let mb = MusicBrainzUrl::album_from_str(&url_str).unwrap();
|
||||
assert_eq!(url_str, mb.as_ref());
|
||||
assert_eq!(uuid, mb.mbid());
|
||||
let mb = MbAlbumRef::from_url_str(&url_str).unwrap();
|
||||
assert_eq!(url_str, mb.url().as_ref());
|
||||
assert_eq!(uuid, mb.mbid().uuid().to_string());
|
||||
|
||||
let mb = MbAlbumRef::from_uuid_str(uuid).unwrap();
|
||||
assert_eq!(url_str, mb.url().as_ref());
|
||||
assert_eq!(uuid, mb.mbid().uuid().to_string());
|
||||
|
||||
let url: Url = url_str.as_str().try_into().unwrap();
|
||||
let mb = MusicBrainzUrl::album_from_url(url).unwrap();
|
||||
assert_eq!(url_str, mb.as_ref());
|
||||
assert_eq!(uuid, mb.mbid());
|
||||
let mb: MbAlbumRef = url.try_into().unwrap();
|
||||
assert_eq!(url_str, mb.url().as_ref());
|
||||
assert_eq!(uuid, mb.mbid().uuid().to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_a_url() {
|
||||
let url = "not a url at all";
|
||||
let expected_error: Error = url::ParseError::RelativeUrlWithoutBase.into();
|
||||
let actual_error = MusicBrainzUrl::artist_from_str(url).unwrap_err();
|
||||
let actual_error = MbArtistRef::from_url_str(url).unwrap_err();
|
||||
assert_eq!(actual_error, expected_error);
|
||||
assert_eq!(actual_error.to_string(), expected_error.to_string());
|
||||
}
|
||||
@ -112,7 +165,7 @@ mod tests {
|
||||
fn invalid_url() {
|
||||
let url = "https://www.musicbutler.io/artist-page/483340948";
|
||||
let expected_error = Error::UrlError(format!("invalid artist MusicBrainz URL: {url}"));
|
||||
let actual_error = MusicBrainzUrl::artist_from_str(url).unwrap_err();
|
||||
let actual_error = MbArtistRef::from_url_str(url).unwrap_err();
|
||||
assert_eq!(actual_error, expected_error);
|
||||
assert_eq!(actual_error.to_string(), expected_error.to_string());
|
||||
}
|
||||
@ -121,7 +174,7 @@ mod tests {
|
||||
fn artist_invalid_type() {
|
||||
let url = "https://musicbrainz.org/release-group/i-am-not-a-uuid";
|
||||
let expected_error = Error::UrlError(format!("invalid artist MusicBrainz URL: {url}"));
|
||||
let actual_error = MusicBrainzUrl::artist_from_str(url).unwrap_err();
|
||||
let actual_error = MbArtistRef::from_url_str(url).unwrap_err();
|
||||
assert_eq!(actual_error, expected_error);
|
||||
assert_eq!(actual_error.to_string(), expected_error.to_string());
|
||||
}
|
||||
@ -131,7 +184,7 @@ mod tests {
|
||||
let url = "https://musicbrainz.org/artist/i-am-not-a-uuid";
|
||||
let expected_error =
|
||||
Error::UrlError(format!("invalid release-group MusicBrainz URL: {url}"));
|
||||
let actual_error = MusicBrainzUrl::album_from_str(url).unwrap_err();
|
||||
let actual_error = MbAlbumRef::from_url_str(url).unwrap_err();
|
||||
assert_eq!(actual_error, expected_error);
|
||||
assert_eq!(actual_error.to_string(), expected_error.to_string());
|
||||
}
|
||||
@ -140,7 +193,7 @@ mod tests {
|
||||
fn invalid_uuid() {
|
||||
let url = "https://musicbrainz.org/artist/i-am-not-a-uuid";
|
||||
let expected_error: Error = Uuid::try_parse("i-am-not-a-uuid").unwrap_err().into();
|
||||
let actual_error = MusicBrainzUrl::artist_from_str(url).unwrap_err();
|
||||
let actual_error = MbArtistRef::from_url_str(url).unwrap_err();
|
||||
assert_eq!(actual_error, expected_error);
|
||||
assert_eq!(actual_error.to_string(), expected_error.to_string());
|
||||
}
|
||||
@ -149,7 +202,7 @@ mod tests {
|
||||
fn missing_type() {
|
||||
let url = "https://musicbrainz.org";
|
||||
let expected_error = Error::UrlError(format!("invalid artist MusicBrainz URL: {url}/"));
|
||||
let actual_error = MusicBrainzUrl::artist_from_str(url).unwrap_err();
|
||||
let actual_error = MbArtistRef::from_url_str(url).unwrap_err();
|
||||
assert_eq!(actual_error, expected_error);
|
||||
assert_eq!(actual_error.to_string(), expected_error.to_string());
|
||||
}
|
||||
@ -158,7 +211,7 @@ mod tests {
|
||||
fn missing_uuid() {
|
||||
let url = "https://musicbrainz.org/artist";
|
||||
let expected_error = Error::UrlError(format!("invalid artist MusicBrainz URL: {url}"));
|
||||
let actual_error = MusicBrainzUrl::artist_from_str(url).unwrap_err();
|
||||
let actual_error = MbArtistRef::from_url_str(url).unwrap_err();
|
||||
assert_eq!(actual_error, expected_error);
|
||||
assert_eq!(actual_error.to_string(), expected_error.to_string());
|
||||
}
|
||||
|
@ -97,13 +97,13 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn no_database_load() {
|
||||
fn null_database_load() {
|
||||
let database = NullDatabase;
|
||||
assert!(database.load().unwrap().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_database_save() {
|
||||
fn null_database_save() {
|
||||
let mut database = NullDatabase;
|
||||
assert!(database.save(&vec![]).is_ok());
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use std::{collections::HashSet, fmt, num::ParseIntError, str::Utf8Error};
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
use crate::core::collection::{album::AlbumMonth, track::TrackFormat};
|
||||
use crate::core::collection::track::TrackFormat;
|
||||
|
||||
/// Trait for interacting with the music library.
|
||||
#[cfg_attr(test, automock)]
|
||||
@ -29,7 +29,7 @@ pub struct Item {
|
||||
pub album_artist: String,
|
||||
pub album_artist_sort: Option<String>,
|
||||
pub album_year: u32,
|
||||
pub album_month: AlbumMonth,
|
||||
pub album_month: u8,
|
||||
pub album_day: u8,
|
||||
pub album_title: String,
|
||||
pub track_number: u32,
|
||||
@ -45,7 +45,7 @@ pub enum Field {
|
||||
AlbumArtist(String),
|
||||
AlbumArtistSort(String),
|
||||
AlbumYear(u32),
|
||||
AlbumMonth(AlbumMonth),
|
||||
AlbumMonth(u8),
|
||||
AlbumDay(u8),
|
||||
AlbumTitle(String),
|
||||
TrackNumber(u32),
|
||||
@ -136,7 +136,7 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn no_library_list() {
|
||||
fn null_library_list() {
|
||||
let mut library = NullLibrary;
|
||||
assert!(library.list(&Query::default()).unwrap().is_empty());
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::core::{
|
||||
collection::{album::AlbumMonth, track::TrackFormat},
|
||||
interface::library::Item,
|
||||
};
|
||||
use crate::core::{collection::track::TrackFormat, interface::library::Item};
|
||||
|
||||
pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
vec![
|
||||
@ -11,7 +8,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘A’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1998,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title a.a"),
|
||||
track_number: 1,
|
||||
@ -24,7 +21,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘A’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1998,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title a.a"),
|
||||
track_number: 2,
|
||||
@ -40,7 +37,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘A’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1998,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title a.a"),
|
||||
track_number: 3,
|
||||
@ -53,7 +50,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘A’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1998,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title a.a"),
|
||||
track_number: 4,
|
||||
@ -66,7 +63,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘A’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2015,
|
||||
album_month: AlbumMonth::April,
|
||||
album_month: 4,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title a.b"),
|
||||
track_number: 1,
|
||||
@ -79,7 +76,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘A’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2015,
|
||||
album_month: AlbumMonth::April,
|
||||
album_month: 4,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title a.b"),
|
||||
track_number: 2,
|
||||
@ -92,7 +89,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘B’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2003,
|
||||
album_month: AlbumMonth::June,
|
||||
album_month: 6,
|
||||
album_day: 6,
|
||||
album_title: String::from("album_title b.a"),
|
||||
track_number: 1,
|
||||
@ -105,7 +102,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘B’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2003,
|
||||
album_month: AlbumMonth::June,
|
||||
album_month: 6,
|
||||
album_day: 6,
|
||||
album_title: String::from("album_title b.a"),
|
||||
track_number: 2,
|
||||
@ -121,7 +118,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘B’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title b.b"),
|
||||
track_number: 1,
|
||||
@ -134,7 +131,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘B’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title b.b"),
|
||||
track_number: 2,
|
||||
@ -150,7 +147,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘B’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2009,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title b.c"),
|
||||
track_number: 1,
|
||||
@ -163,7 +160,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘B’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2009,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title b.c"),
|
||||
track_number: 2,
|
||||
@ -179,7 +176,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘B’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2015,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title b.d"),
|
||||
track_number: 1,
|
||||
@ -192,7 +189,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘B’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2015,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title b.d"),
|
||||
track_number: 2,
|
||||
@ -208,7 +205,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("The Album_Artist ‘C’"),
|
||||
album_artist_sort: Some(String::from("Album_Artist ‘C’, The")),
|
||||
album_year: 1985,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title c.a"),
|
||||
track_number: 1,
|
||||
@ -221,7 +218,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("The Album_Artist ‘C’"),
|
||||
album_artist_sort: Some(String::from("Album_Artist ‘C’, The")),
|
||||
album_year: 1985,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title c.a"),
|
||||
track_number: 2,
|
||||
@ -237,7 +234,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("The Album_Artist ‘C’"),
|
||||
album_artist_sort: Some(String::from("Album_Artist ‘C’, The")),
|
||||
album_year: 2018,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title c.b"),
|
||||
track_number: 1,
|
||||
@ -250,7 +247,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("The Album_Artist ‘C’"),
|
||||
album_artist_sort: Some(String::from("Album_Artist ‘C’, The")),
|
||||
album_year: 2018,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title c.b"),
|
||||
track_number: 2,
|
||||
@ -266,7 +263,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘D’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1995,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title d.a"),
|
||||
track_number: 1,
|
||||
@ -279,7 +276,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘D’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1995,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title d.a"),
|
||||
track_number: 2,
|
||||
@ -295,7 +292,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘D’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2028,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title d.b"),
|
||||
track_number: 1,
|
||||
@ -308,7 +305,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Album_Artist ‘D’"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2028,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("album_title d.b"),
|
||||
track_number: 2,
|
||||
|
@ -1,2 +1,3 @@
|
||||
pub mod database;
|
||||
pub mod library;
|
||||
pub mod musicbrainz;
|
||||
|
180
src/core/interface/musicbrainz/mod.rs
Normal file
180
src/core/interface/musicbrainz/mod.rs
Normal file
@ -0,0 +1,180 @@
|
||||
//! Module for accessing MusicBrainz metadata.
|
||||
|
||||
use std::{fmt, num};
|
||||
|
||||
// TODO: #[cfg(test)]
|
||||
// TODO: use mockall::automock;
|
||||
|
||||
use uuid::{self, Uuid};
|
||||
|
||||
use crate::collection::album::Album;
|
||||
|
||||
/// Trait for interacting with the MusicBrainz API.
|
||||
// TODO: #[cfg_attr(test, automock)]
|
||||
pub trait IMusicBrainz {
|
||||
fn lookup_artist_release_groups(&mut self, mbid: &Mbid) -> Result<Vec<Album>, Error>;
|
||||
fn search_release_group(
|
||||
&mut self,
|
||||
arid: &Mbid,
|
||||
album: Album,
|
||||
) -> Result<Vec<Match<Album>>, Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Match<T> {
|
||||
pub score: u8,
|
||||
pub item: T,
|
||||
}
|
||||
|
||||
impl<T> Match<T> {
|
||||
pub fn new(score: u8, item: T) -> Self {
|
||||
Match { score, item }
|
||||
}
|
||||
}
|
||||
|
||||
/// Null implementation of [`IMusicBrainz`] for when the trait is required, but no communication
|
||||
/// with the MusicBrainz is desired.
|
||||
pub struct NullMusicBrainz;
|
||||
|
||||
impl IMusicBrainz for NullMusicBrainz {
|
||||
fn lookup_artist_release_groups(&mut self, _mbid: &Mbid) -> Result<Vec<Album>, Error> {
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
fn search_release_group(
|
||||
&mut self,
|
||||
_arid: &Mbid,
|
||||
_album: Album,
|
||||
) -> Result<Vec<Match<Album>>, Error> {
|
||||
Ok(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
/// The MusicBrainz ID.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Mbid(Uuid);
|
||||
|
||||
impl Mbid {
|
||||
pub fn uuid(&self) -> &Uuid {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Uuid> for Mbid {
|
||||
fn from(value: Uuid) -> Self {
|
||||
Mbid(value)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! try_from_impl_for_mbid {
|
||||
($from:ty) => {
|
||||
impl TryFrom<$from> for Mbid {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: $from) -> Result<Self, Self::Error> {
|
||||
Ok(Uuid::parse_str(value.as_ref())?.into())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try_from_impl_for_mbid!(&str);
|
||||
try_from_impl_for_mbid!(&String);
|
||||
try_from_impl_for_mbid!(String);
|
||||
|
||||
/// Error type for musicbrainz calls.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// Failed to parse input into an MBID.
|
||||
MbidParse(String),
|
||||
/// The API client failed.
|
||||
Client(String),
|
||||
/// The client reached the API rate limit.
|
||||
RateLimit,
|
||||
/// The API response could not be understood.
|
||||
Unknown(u16),
|
||||
/// Part of the response could not be parsed.
|
||||
Parse(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::MbidParse(s) => write!(f, "failed to parse input into an MBID: {s}"),
|
||||
Error::Client(s) => write!(f, "the API client failed: {s}"),
|
||||
Error::RateLimit => write!(f, "the API client reached the rate limit"),
|
||||
Error::Unknown(u) => write!(f, "the API response could not be understood: status {u}"),
|
||||
Error::Parse(s) => write!(f, "part of the response could not be parsed: {s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<uuid::Error> for Error {
|
||||
fn from(value: uuid::Error) -> Self {
|
||||
Error::MbidParse(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<num::ParseIntError> for Error {
|
||||
fn from(err: num::ParseIntError) -> Error {
|
||||
Error::Parse(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::core::collection::album::{AlbumDate, AlbumId};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn null_lookup_artist_release_groups() {
|
||||
let mut musicbrainz = NullMusicBrainz;
|
||||
let mbid: Mbid = "d368baa8-21ca-4759-9731-0b2753071ad8".try_into().unwrap();
|
||||
assert!(musicbrainz
|
||||
.lookup_artist_release_groups(&mbid)
|
||||
.unwrap()
|
||||
.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn null_search_release_group() {
|
||||
let mut musicbrainz = NullMusicBrainz;
|
||||
let mbid: Mbid = "d368baa8-21ca-4759-9731-0b2753071ad8".try_into().unwrap();
|
||||
let album = Album::new(AlbumId::new("an album"), AlbumDate::default(), None, vec![]);
|
||||
assert!(musicbrainz
|
||||
.search_release_group(&mbid, album)
|
||||
.unwrap()
|
||||
.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_type() {
|
||||
let album = Album::new(AlbumId::new("an album"), AlbumDate::default(), None, vec![]);
|
||||
let hit = Match::new(56, album);
|
||||
assert!(!format!("{hit:?}").is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors() {
|
||||
let mbid_err: Error = TryInto::<Mbid>::try_into("i-am-not-a-uuid").unwrap_err();
|
||||
assert!(!mbid_err.to_string().is_empty());
|
||||
assert!(!format!("{mbid_err:?}").is_empty());
|
||||
|
||||
let client_err: Error = Error::Client(String::from("a client error"));
|
||||
assert!(!client_err.to_string().is_empty());
|
||||
assert!(!format!("{client_err:?}").is_empty());
|
||||
|
||||
let rate_err: Error = Error::RateLimit;
|
||||
assert!(!rate_err.to_string().is_empty());
|
||||
assert!(!format!("{rate_err:?}").is_empty());
|
||||
|
||||
let unk_err: Error = Error::Unknown(404);
|
||||
assert!(!unk_err.to_string().is_empty());
|
||||
assert!(!format!("{unk_err:?}").is_empty());
|
||||
|
||||
let parse_err: Error = "not-a-number".parse::<u32>().unwrap_err().into();
|
||||
assert!(!parse_err.to_string().is_empty());
|
||||
assert!(!format!("{parse_err:?}").is_empty());
|
||||
}
|
||||
}
|
@ -1,11 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
core::{
|
||||
use crate::core::{
|
||||
interface::{database::IDatabase, library::ILibrary},
|
||||
musichoard::{database::IMusicHoardDatabase, MusicHoard, NoDatabase, NoLibrary},
|
||||
},
|
||||
Error,
|
||||
musichoard::{database::IMusicHoardDatabase, Error, MusicHoard, NoDatabase, NoLibrary},
|
||||
};
|
||||
|
||||
/// Builder for [`MusicHoard`]. Its purpose is to make it easier to set various combinations of
|
||||
|
@ -2,7 +2,7 @@ use crate::core::{
|
||||
collection::{
|
||||
album::{Album, AlbumId, AlbumSeq},
|
||||
artist::{Artist, ArtistId},
|
||||
musicbrainz::MusicBrainzUrl,
|
||||
musicbrainz::MbArtistRef,
|
||||
Collection,
|
||||
},
|
||||
interface::database::IDatabase,
|
||||
@ -123,15 +123,15 @@ impl<Database: IDatabase, Library> IMusicHoardDatabase for MusicHoard<Database,
|
||||
artist_id: Id,
|
||||
url: S,
|
||||
) -> Result<(), Error> {
|
||||
let mb = MusicBrainzUrl::artist_from_str(url)?;
|
||||
self.update_artist(artist_id.as_ref(), |artist| artist.set_musicbrainz_url(mb))
|
||||
let mb = MbArtistRef::from_url_str(url)?;
|
||||
self.update_artist(artist_id.as_ref(), |artist| artist.set_musicbrainz_ref(mb))
|
||||
}
|
||||
|
||||
fn clear_artist_musicbrainz<Id: AsRef<ArtistId>>(
|
||||
&mut self,
|
||||
artist_id: Id,
|
||||
) -> Result<(), Error> {
|
||||
self.update_artist(artist_id.as_ref(), |artist| artist.clear_musicbrainz_url())
|
||||
self.update_artist(artist_id.as_ref(), |artist| artist.clear_musicbrainz_ref())
|
||||
}
|
||||
|
||||
fn add_to_artist_property<Id: AsRef<ArtistId>, S: AsRef<str> + Into<String>>(
|
||||
@ -290,7 +290,7 @@ mod tests {
|
||||
use mockall::{predicate, Sequence};
|
||||
|
||||
use crate::core::{
|
||||
collection::{album::AlbumDate, artist::ArtistId, musicbrainz::MusicBrainzUrl},
|
||||
collection::{album::AlbumDate, artist::ArtistId},
|
||||
interface::database::{self, MockIDatabase},
|
||||
musichoard::{base::IMusicHoardBase, NoLibrary},
|
||||
testmod::FULL_COLLECTION,
|
||||
@ -433,7 +433,7 @@ mod tests {
|
||||
|
||||
assert!(music_hoard.add_artist(artist_id.clone()).is_ok());
|
||||
|
||||
let mut expected: Option<MusicBrainzUrl> = None;
|
||||
let mut expected: Option<MbArtistRef> = None;
|
||||
assert_eq!(music_hoard.collection[0].musicbrainz, expected);
|
||||
|
||||
// Setting a URL on an artist not in the collection is an error.
|
||||
@ -446,7 +446,7 @@ mod tests {
|
||||
assert!(music_hoard
|
||||
.set_artist_musicbrainz(&artist_id, MUSICBRAINZ)
|
||||
.is_ok());
|
||||
_ = expected.insert(MusicBrainzUrl::artist_from_str(MUSICBRAINZ).unwrap());
|
||||
_ = expected.insert(MbArtistRef::from_url_str(MUSICBRAINZ).unwrap());
|
||||
assert_eq!(music_hoard.collection[0].musicbrainz, expected);
|
||||
|
||||
// Clearing URLs on an artist that does not exist is an error.
|
||||
@ -567,9 +567,12 @@ mod tests {
|
||||
let album_id_2 = AlbumId::new("another album");
|
||||
|
||||
let mut database_result = vec![Artist::new(artist_id.clone())];
|
||||
database_result[0]
|
||||
.albums
|
||||
.push(Album::new(album_id.clone(), AlbumDate::default()));
|
||||
database_result[0].albums.push(Album::new(
|
||||
album_id.clone(),
|
||||
AlbumDate::default(),
|
||||
None,
|
||||
vec![],
|
||||
));
|
||||
|
||||
database
|
||||
.expect_load()
|
||||
|
@ -59,9 +59,9 @@ impl<Database, Library: ILibrary> MusicHoard<Database, Library> {
|
||||
};
|
||||
|
||||
let album_date = AlbumDate {
|
||||
year: item.album_year,
|
||||
month: item.album_month,
|
||||
day: item.album_day,
|
||||
year: Some(item.album_year).filter(|y| y > &0),
|
||||
month: Some(item.album_month).filter(|m| m > &0),
|
||||
day: Some(item.album_day).filter(|d| d > &0),
|
||||
};
|
||||
|
||||
let track = Track {
|
||||
@ -109,7 +109,7 @@ impl<Database, Library: ILibrary> MusicHoard<Database, Library> {
|
||||
{
|
||||
Some(album) => album.tracks.push(track),
|
||||
None => {
|
||||
let mut album = Album::new(album_id, album_date);
|
||||
let mut album = Album::new(album_id, album_date, None, vec![]);
|
||||
album.tracks.push(track);
|
||||
artist.albums.push(album);
|
||||
}
|
||||
|
@ -2,12 +2,12 @@ use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::core::collection::{
|
||||
album::{Album, AlbumDate, AlbumId, AlbumMonth, AlbumSeq},
|
||||
album::{Album, AlbumId, AlbumPrimaryType, AlbumSeq},
|
||||
artist::{Artist, ArtistId},
|
||||
musicbrainz::MusicBrainzUrl,
|
||||
musicbrainz::{MbAlbumRef, MbArtistRef},
|
||||
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
||||
};
|
||||
use crate::tests::*;
|
||||
use crate::testmod::*;
|
||||
|
||||
pub static LIBRARY_COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| library_collection!());
|
||||
pub static FULL_COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| full_collection!());
|
||||
pub static LIBRARY_COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| library::library_collection!());
|
||||
pub static FULL_COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| full::full_collection!());
|
||||
|
@ -3,7 +3,7 @@
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::database::json::IJsonDatabaseBackend;
|
||||
use crate::external::database::json::IJsonDatabaseBackend;
|
||||
|
||||
/// JSON database backend that uses a local file for persistent storage.
|
||||
pub struct JsonDatabaseFileBackend {
|
@ -109,6 +109,7 @@ mod tests {
|
||||
fn load() {
|
||||
let expected = expected();
|
||||
let result = Ok(DATABASE_JSON.to_owned());
|
||||
eprintln!("{DATABASE_JSON}");
|
||||
|
||||
let mut backend = MockIJsonDatabaseBackend::new();
|
||||
backend.expect_read().times(1).return_once(|| result);
|
@ -1,5 +1,5 @@
|
||||
pub static DATABASE_JSON: &str = "{\
|
||||
\"V20240308\":\
|
||||
\"V20240313\":\
|
||||
[\
|
||||
{\
|
||||
\"name\":\"Album_Artist ‘A’\",\
|
||||
@ -12,9 +12,13 @@ pub static DATABASE_JSON: &str = "{\
|
||||
\"albums\":[\
|
||||
{\
|
||||
\"title\":\"album_title a.a\",\"seq\":1,\
|
||||
\"musicbrainz\":\"https://musicbrainz.org/release-group/00000000-0000-0000-0000-000000000000\"\
|
||||
\"musicbrainz\":\"https://musicbrainz.org/release-group/00000000-0000-0000-0000-000000000000\",\
|
||||
\"primary_type\":\"Album\",\"secondary_types\":[]\
|
||||
},\
|
||||
{\"title\":\"album_title a.b\",\"seq\":1,\"musicbrainz\":null}\
|
||||
{\
|
||||
\"title\":\"album_title a.b\",\"seq\":1,\"musicbrainz\":null,\
|
||||
\"primary_type\":\"Album\",\"secondary_types\":[]\
|
||||
}\
|
||||
]\
|
||||
},\
|
||||
{\
|
||||
@ -30,16 +34,24 @@ pub static DATABASE_JSON: &str = "{\
|
||||
\"Qobuz\":[\"https://www.qobuz.com/nl-nl/interpreter/artist-b/download-streaming-albums\"]\
|
||||
},\
|
||||
\"albums\":[\
|
||||
{\"title\":\"album_title b.a\",\"seq\":1,\"musicbrainz\":null},\
|
||||
{\
|
||||
\"title\":\"album_title b.a\",\"seq\":1,\"musicbrainz\":null,\
|
||||
\"primary_type\":\"Album\",\"secondary_types\":[]\
|
||||
},\
|
||||
{\
|
||||
\"title\":\"album_title b.b\",\"seq\":3,\
|
||||
\"musicbrainz\":\"https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111111\"\
|
||||
\"musicbrainz\":\"https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111111\",\
|
||||
\"primary_type\":\"Album\",\"secondary_types\":[]\
|
||||
},\
|
||||
{\
|
||||
\"title\":\"album_title b.c\",\"seq\":2,\
|
||||
\"musicbrainz\":\"https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111112\"\
|
||||
\"musicbrainz\":\"https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111112\",\
|
||||
\"primary_type\":\"Album\",\"secondary_types\":[]\
|
||||
},\
|
||||
{\"title\":\"album_title b.d\",\"seq\":4,\"musicbrainz\":null}\
|
||||
{\
|
||||
\"title\":\"album_title b.d\",\"seq\":4,\"musicbrainz\":null,\
|
||||
\"primary_type\":\"Album\",\"secondary_types\":[]\
|
||||
}\
|
||||
]\
|
||||
},\
|
||||
{\
|
||||
@ -48,8 +60,14 @@ pub static DATABASE_JSON: &str = "{\
|
||||
\"musicbrainz\":\"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111\",\
|
||||
\"properties\":{},\
|
||||
\"albums\":[\
|
||||
{\"title\":\"album_title c.a\",\"seq\":0,\"musicbrainz\":null},\
|
||||
{\"title\":\"album_title c.b\",\"seq\":0,\"musicbrainz\":null}\
|
||||
{\
|
||||
\"title\":\"album_title c.a\",\"seq\":0,\"musicbrainz\":null,\
|
||||
\"primary_type\":\"Album\",\"secondary_types\":[]\
|
||||
},\
|
||||
{\
|
||||
\"title\":\"album_title c.b\",\"seq\":0,\"musicbrainz\":null,\
|
||||
\"primary_type\":\"Album\",\"secondary_types\":[]\
|
||||
}\
|
||||
]\
|
||||
},\
|
||||
{\
|
||||
@ -58,8 +76,14 @@ pub static DATABASE_JSON: &str = "{\
|
||||
\"musicbrainz\":null,\
|
||||
\"properties\":{},\
|
||||
\"albums\":[\
|
||||
{\"title\":\"album_title d.a\",\"seq\":0,\"musicbrainz\":null},\
|
||||
{\"title\":\"album_title d.b\",\"seq\":0,\"musicbrainz\":null}\
|
||||
{\
|
||||
\"title\":\"album_title d.a\",\"seq\":0,\"musicbrainz\":null,\
|
||||
\"primary_type\":\"Album\",\"secondary_types\":[]\
|
||||
},\
|
||||
{\
|
||||
\"title\":\"album_title d.b\",\"seq\":0,\"musicbrainz\":null,\
|
||||
\"primary_type\":\"Album\",\"secondary_types\":[]\
|
||||
}\
|
||||
]\
|
||||
}\
|
||||
]\
|
62
src/external/database/serde/common.rs
vendored
Normal file
62
src/external/database/serde/common.rs
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::core::collection::album::{AlbumPrimaryType, AlbumSecondaryType};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(remote = "AlbumPrimaryType")]
|
||||
pub enum SerdeAlbumPrimaryTypeDef {
|
||||
Album,
|
||||
Single,
|
||||
Ep,
|
||||
Broadcast,
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct SerdeAlbumPrimaryType(#[serde(with = "SerdeAlbumPrimaryTypeDef")] AlbumPrimaryType);
|
||||
|
||||
impl From<SerdeAlbumPrimaryType> for AlbumPrimaryType {
|
||||
fn from(value: SerdeAlbumPrimaryType) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AlbumPrimaryType> for SerdeAlbumPrimaryType {
|
||||
fn from(value: AlbumPrimaryType) -> Self {
|
||||
SerdeAlbumPrimaryType(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(remote = "AlbumSecondaryType")]
|
||||
pub enum SerdeAlbumSecondaryTypeDef {
|
||||
Compilation,
|
||||
Soundtrack,
|
||||
Spokenword,
|
||||
Interview,
|
||||
Audiobook,
|
||||
AudioDrama,
|
||||
Live,
|
||||
Remix,
|
||||
DjMix,
|
||||
MixtapeStreet,
|
||||
Demo,
|
||||
FieldRecording,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct SerdeAlbumSecondaryType(
|
||||
#[serde(with = "SerdeAlbumSecondaryTypeDef")] AlbumSecondaryType,
|
||||
);
|
||||
|
||||
impl From<SerdeAlbumSecondaryType> for AlbumSecondaryType {
|
||||
fn from(value: SerdeAlbumSecondaryType) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AlbumSecondaryType> for SerdeAlbumSecondaryType {
|
||||
fn from(value: AlbumSecondaryType) -> Self {
|
||||
SerdeAlbumSecondaryType(value)
|
||||
}
|
||||
}
|
@ -2,19 +2,22 @@ use std::collections::HashMap;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::core::{
|
||||
use crate::{
|
||||
core::{
|
||||
collection::{
|
||||
album::{Album, AlbumDate, AlbumId, AlbumSeq},
|
||||
artist::{Artist, ArtistId},
|
||||
musicbrainz::MusicBrainzUrl,
|
||||
musicbrainz::{MbAlbumRef, MbArtistRef},
|
||||
Collection,
|
||||
},
|
||||
interface::database::LoadError,
|
||||
},
|
||||
external::database::serde::common::{SerdeAlbumPrimaryType, SerdeAlbumSecondaryType},
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub enum DeserializeDatabase {
|
||||
V20240308(Vec<DeserializeArtist>),
|
||||
V20240313(Vec<DeserializeArtist>),
|
||||
}
|
||||
|
||||
impl TryFrom<DeserializeDatabase> for Collection {
|
||||
@ -22,10 +25,9 @@ impl TryFrom<DeserializeDatabase> for Collection {
|
||||
|
||||
fn try_from(database: DeserializeDatabase) -> Result<Self, Self::Error> {
|
||||
match database {
|
||||
DeserializeDatabase::V20240308(collection) => collection
|
||||
.into_iter()
|
||||
.map(|artist| artist.try_into())
|
||||
.collect(),
|
||||
DeserializeDatabase::V20240313(collection) => {
|
||||
collection.into_iter().map(TryInto::try_into).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -44,6 +46,8 @@ pub struct DeserializeAlbum {
|
||||
title: String,
|
||||
seq: u8,
|
||||
musicbrainz: Option<String>,
|
||||
primary_type: Option<SerdeAlbumPrimaryType>,
|
||||
secondary_types: Vec<SerdeAlbumSecondaryType>,
|
||||
}
|
||||
|
||||
impl TryFrom<DeserializeArtist> for Artist {
|
||||
@ -55,7 +59,7 @@ impl TryFrom<DeserializeArtist> for Artist {
|
||||
sort: artist.sort.map(ArtistId::new),
|
||||
musicbrainz: artist
|
||||
.musicbrainz
|
||||
.map(MusicBrainzUrl::artist_from_str)
|
||||
.map(MbArtistRef::from_url_str)
|
||||
.transpose()?,
|
||||
properties: artist.properties,
|
||||
albums: artist
|
||||
@ -77,8 +81,10 @@ impl TryFrom<DeserializeAlbum> for Album {
|
||||
seq: AlbumSeq(album.seq),
|
||||
musicbrainz: album
|
||||
.musicbrainz
|
||||
.map(MusicBrainzUrl::album_from_str)
|
||||
.map(MbAlbumRef::from_url_str)
|
||||
.transpose()?,
|
||||
primary_type: album.primary_type.map(Into::into),
|
||||
secondary_types: album.secondary_types.into_iter().map(Into::into).collect(),
|
||||
tracks: vec![],
|
||||
})
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
//! Helper module for backends that can use serde for (de)serialisation.
|
||||
|
||||
mod common;
|
||||
pub mod deserialize;
|
||||
pub mod serialize;
|
@ -2,16 +2,19 @@ use std::collections::BTreeMap;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::core::collection::{album::Album, artist::Artist, Collection};
|
||||
use crate::{
|
||||
core::collection::{album::Album, artist::Artist, musicbrainz::IMusicBrainzRef, Collection},
|
||||
external::database::serde::common::{SerdeAlbumPrimaryType, SerdeAlbumSecondaryType},
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum SerializeDatabase<'a> {
|
||||
V20240308(Vec<SerializeArtist<'a>>),
|
||||
V20240313(Vec<SerializeArtist<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Collection> for SerializeDatabase<'a> {
|
||||
fn from(collection: &'a Collection) -> Self {
|
||||
SerializeDatabase::V20240308(collection.iter().map(Into::into).collect())
|
||||
SerializeDatabase::V20240313(collection.iter().map(Into::into).collect())
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +32,8 @@ pub struct SerializeAlbum<'a> {
|
||||
title: &'a str,
|
||||
seq: u8,
|
||||
musicbrainz: Option<&'a str>,
|
||||
primary_type: Option<SerdeAlbumPrimaryType>,
|
||||
secondary_types: Vec<SerdeAlbumSecondaryType>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Artist> for SerializeArtist<'a> {
|
||||
@ -36,7 +41,7 @@ impl<'a> From<&'a Artist> for SerializeArtist<'a> {
|
||||
SerializeArtist {
|
||||
name: &artist.id.name,
|
||||
sort: artist.sort.as_ref().map(|id| id.name.as_ref()),
|
||||
musicbrainz: artist.musicbrainz.as_ref().map(AsRef::as_ref),
|
||||
musicbrainz: artist.musicbrainz.as_ref().map(|mb| mb.url().as_str()),
|
||||
properties: artist
|
||||
.properties
|
||||
.iter()
|
||||
@ -52,7 +57,14 @@ impl<'a> From<&'a Album> for SerializeAlbum<'a> {
|
||||
SerializeAlbum {
|
||||
title: &album.id.title,
|
||||
seq: album.seq.0,
|
||||
musicbrainz: album.musicbrainz.as_ref().map(AsRef::as_ref),
|
||||
musicbrainz: album.musicbrainz.as_ref().map(|mb| mb.url().as_str()),
|
||||
primary_type: album.primary_type.map(Into::into),
|
||||
secondary_types: album
|
||||
.secondary_types
|
||||
.iter()
|
||||
.copied()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
@ -8,8 +8,7 @@ use std::{
|
||||
str,
|
||||
};
|
||||
|
||||
use crate::core::interface::library::Error;
|
||||
use crate::library::beets::IBeetsLibraryExecutor;
|
||||
use crate::{core::interface::library::Error, external::library::beets::IBeetsLibraryExecutor};
|
||||
|
||||
const BEET_DEFAULT: &str = "beet";
|
||||
|
@ -76,7 +76,7 @@ impl ToBeetsArg for Field {
|
||||
Field::AlbumArtist(ref s) => format!("{negate}albumartist:{s}"),
|
||||
Field::AlbumArtistSort(ref s) => format!("{negate}albumartist_sort:{s}"),
|
||||
Field::AlbumYear(ref u) => format!("{negate}year:{u}"),
|
||||
Field::AlbumMonth(ref e) => format!("{negate}month:{}", *e as u8),
|
||||
Field::AlbumMonth(ref e) => format!("{negate}month:{}", { *e }),
|
||||
Field::AlbumDay(ref u) => format!("{negate}day:{u}"),
|
||||
Field::AlbumTitle(ref s) => format!("{negate}album:{s}"),
|
||||
Field::TrackNumber(ref u) => format!("{negate}track:{u}"),
|
||||
@ -111,11 +111,6 @@ pub struct BeetsLibrary<BLE> {
|
||||
executor: BLE,
|
||||
}
|
||||
|
||||
trait ILibraryPrivate {
|
||||
fn list_cmd_and_args(query: &Query) -> Vec<String>;
|
||||
fn list_to_items<S: AsRef<str>>(list_output: &[S]) -> Result<Vec<Item>, Error>;
|
||||
}
|
||||
|
||||
impl<BLE: IBeetsLibraryExecutor> BeetsLibrary<BLE> {
|
||||
/// Create a new beets library with the provided executor, e.g.
|
||||
/// [`executor::BeetsLibraryProcessExecutor`].
|
||||
@ -132,7 +127,7 @@ impl<BLE: IBeetsLibraryExecutor> ILibrary for BeetsLibrary<BLE> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<BLE: IBeetsLibraryExecutor> ILibraryPrivate for BeetsLibrary<BLE> {
|
||||
impl<BLE: IBeetsLibraryExecutor> BeetsLibrary<BLE> {
|
||||
fn list_cmd_and_args(query: &Query) -> Vec<String> {
|
||||
let mut cmd: Vec<String> = vec![String::from(CMD_LIST)];
|
||||
cmd.push(LIST_FORMAT_ARG.to_string());
|
||||
@ -159,7 +154,7 @@ impl<BLE: IBeetsLibraryExecutor> ILibraryPrivate for BeetsLibrary<BLE> {
|
||||
false => None,
|
||||
};
|
||||
let album_year = split[2].parse::<u32>()?;
|
||||
let album_month = split[3].parse::<u8>()?.into();
|
||||
let album_month = split[3].parse::<u8>()?;
|
||||
let album_day = split[4].parse::<u8>()?;
|
||||
let album_title = split[5].to_string();
|
||||
let track_number = split[6].parse::<u32>()?;
|
||||
@ -201,7 +196,7 @@ mod testmod;
|
||||
mod tests {
|
||||
use mockall::predicate;
|
||||
|
||||
use crate::{collection::album::AlbumMonth, core::interface::library::testmod::LIBRARY_ITEMS};
|
||||
use crate::core::interface::library::testmod::LIBRARY_ITEMS;
|
||||
|
||||
use super::*;
|
||||
use testmod::LIBRARY_BEETS;
|
||||
@ -235,7 +230,7 @@ mod tests {
|
||||
.exclude(Field::AlbumArtist(String::from("some.albumartist")))
|
||||
.exclude(Field::AlbumArtistSort(String::from("some.albumartist")))
|
||||
.include(Field::AlbumYear(3030))
|
||||
.include(Field::AlbumMonth(AlbumMonth::April))
|
||||
.include(Field::AlbumMonth(4))
|
||||
.include(Field::AlbumDay(6))
|
||||
.include(Field::TrackTitle(String::from("some.track")))
|
||||
.include(Field::TrackFormat(TrackFormat::Flac))
|
3
src/external/mod.rs
vendored
Normal file
3
src/external/mod.rs
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod database;
|
||||
pub mod library;
|
||||
pub mod musicbrainz;
|
46
src/external/musicbrainz/api/client.rs
vendored
Normal file
46
src/external/musicbrainz/api/client.rs
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
//! Module for interacting with the MusicBrainz API via an HTTP client.
|
||||
|
||||
use reqwest::{self, blocking::Client, header};
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use crate::external::musicbrainz::api::{ClientError, IMusicBrainzApiClient};
|
||||
|
||||
// GRCOV_EXCL_START
|
||||
pub struct MusicBrainzApiClient(Client);
|
||||
|
||||
impl MusicBrainzApiClient {
|
||||
pub fn new(user_agent: &'static str) -> Result<Self, ClientError> {
|
||||
let mut headers = header::HeaderMap::new();
|
||||
headers.insert(
|
||||
header::USER_AGENT,
|
||||
header::HeaderValue::from_static(user_agent),
|
||||
);
|
||||
headers.insert(
|
||||
header::ACCEPT,
|
||||
header::HeaderValue::from_static("application/json"),
|
||||
);
|
||||
|
||||
Ok(MusicBrainzApiClient(
|
||||
Client::builder().default_headers(headers).build()?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl IMusicBrainzApiClient for MusicBrainzApiClient {
|
||||
fn get<D: DeserializeOwned + 'static>(&mut self, url: &str) -> Result<D, ClientError> {
|
||||
let response = self.0.get(url).send()?;
|
||||
|
||||
if response.status().is_success() {
|
||||
Ok(response.json()?)
|
||||
} else {
|
||||
Err(ClientError::Status(response.status().as_u16()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<reqwest::Error> for ClientError {
|
||||
fn from(err: reqwest::Error) -> Self {
|
||||
ClientError::Client(err.to_string())
|
||||
}
|
||||
}
|
||||
// GRCOV_EXCL_STOP
|
404
src/external/musicbrainz/api/mod.rs
vendored
Normal file
404
src/external/musicbrainz/api/mod.rs
vendored
Normal file
@ -0,0 +1,404 @@
|
||||
//! Module for interacting with the [MusicBrainz API](https://musicbrainz.org/doc/MusicBrainz_API).
|
||||
|
||||
pub mod client;
|
||||
|
||||
use serde::{de::DeserializeOwned, Deserialize};
|
||||
use url::form_urlencoded;
|
||||
|
||||
#[cfg(test)]
|
||||
use mockall::automock;
|
||||
|
||||
use crate::core::{
|
||||
collection::{
|
||||
album::{Album, AlbumDate, AlbumPrimaryType, AlbumSecondaryType},
|
||||
musicbrainz::MbAlbumRef,
|
||||
},
|
||||
interface::musicbrainz::{Error, IMusicBrainz, Match, Mbid},
|
||||
};
|
||||
|
||||
const MB_BASE_URL: &str = "https://musicbrainz.org/ws/2";
|
||||
const MB_RATE_LIMIT_CODE: u16 = 503;
|
||||
|
||||
#[cfg_attr(test, automock)]
|
||||
pub trait IMusicBrainzApiClient {
|
||||
fn get<D: DeserializeOwned + 'static>(&mut self, url: &str) -> Result<D, ClientError>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ClientError {
|
||||
Client(String),
|
||||
Status(u16),
|
||||
}
|
||||
|
||||
impl From<ClientError> for Error {
|
||||
fn from(err: ClientError) -> Self {
|
||||
match err {
|
||||
ClientError::Client(s) => Error::Client(s),
|
||||
ClientError::Status(status) => match status {
|
||||
MB_RATE_LIMIT_CODE => Error::RateLimit,
|
||||
_ => Error::Unknown(status),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MusicBrainzApi<Mbc> {
|
||||
client: Mbc,
|
||||
}
|
||||
|
||||
impl<Mbc> MusicBrainzApi<Mbc> {
|
||||
pub fn new(client: Mbc) -> Self {
|
||||
MusicBrainzApi { client }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Mbc: IMusicBrainzApiClient> IMusicBrainz for MusicBrainzApi<Mbc> {
|
||||
fn lookup_artist_release_groups(&mut self, mbid: &Mbid) -> Result<Vec<Album>, Error> {
|
||||
let mbid = mbid.uuid().as_hyphenated().to_string();
|
||||
|
||||
let artist: ResponseLookupArtist = self
|
||||
.client
|
||||
.get(&format!("{MB_BASE_URL}/artist/{mbid}?inc=release-groups"))?;
|
||||
|
||||
artist
|
||||
.release_groups
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn search_release_group(
|
||||
&mut self,
|
||||
arid: &Mbid,
|
||||
album: Album,
|
||||
) -> Result<Vec<Match<Album>>, Error> {
|
||||
let title = &album.id.title;
|
||||
let arid = arid.uuid().as_hyphenated().to_string();
|
||||
let mut query = format!("\"{title}\" AND arid:{arid}");
|
||||
|
||||
if let Some(year) = album.date.year {
|
||||
query.push_str(&format!(" AND firstreleasedate:{year}"));
|
||||
}
|
||||
|
||||
let query: String = form_urlencoded::byte_serialize(query.as_bytes()).collect();
|
||||
|
||||
let results: ResponseSearchReleaseGroup = self
|
||||
.client
|
||||
.get(&format!("{MB_BASE_URL}/release-group?query={query}"))?;
|
||||
|
||||
results
|
||||
.release_groups
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||
struct ResponseLookupArtist {
|
||||
release_groups: Vec<LookupReleaseGroup>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||
struct LookupReleaseGroup {
|
||||
id: String,
|
||||
title: String,
|
||||
first_release_date: String,
|
||||
primary_type: SerdeAlbumPrimaryType,
|
||||
secondary_types: Vec<SerdeAlbumSecondaryType>,
|
||||
}
|
||||
|
||||
impl TryFrom<LookupReleaseGroup> for Album {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(entity: LookupReleaseGroup) -> Result<Self, Self::Error> {
|
||||
let mut album = Album::new(
|
||||
entity.title,
|
||||
AlbumDate::from_mb_date(&entity.first_release_date)?,
|
||||
Some(entity.primary_type.into()),
|
||||
entity.secondary_types.into_iter().map(Into::into).collect(),
|
||||
);
|
||||
let mbref = MbAlbumRef::from_uuid_str(entity.id)
|
||||
.map_err(|err| Error::MbidParse(err.to_string()))?;
|
||||
album.set_musicbrainz_ref(mbref);
|
||||
Ok(album)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||
struct ResponseSearchReleaseGroup {
|
||||
release_groups: Vec<SearchReleaseGroup>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||
struct SearchReleaseGroup {
|
||||
score: u8,
|
||||
id: String,
|
||||
title: String,
|
||||
first_release_date: String,
|
||||
primary_type: SerdeAlbumPrimaryType,
|
||||
}
|
||||
|
||||
impl TryFrom<SearchReleaseGroup> for Match<Album> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(entity: SearchReleaseGroup) -> Result<Self, Self::Error> {
|
||||
let mut album = Album::new(
|
||||
entity.title,
|
||||
AlbumDate::from_mb_date(&entity.first_release_date)?,
|
||||
Some(entity.primary_type.into()),
|
||||
vec![],
|
||||
);
|
||||
let mbref = MbAlbumRef::from_uuid_str(entity.id)
|
||||
.map_err(|err| Error::MbidParse(err.to_string()))?;
|
||||
album.set_musicbrainz_ref(mbref);
|
||||
Ok(Match::new(entity.score, album))
|
||||
}
|
||||
}
|
||||
|
||||
impl AlbumDate {
|
||||
fn from_mb_date(mb_date: &str) -> Result<AlbumDate, Error> {
|
||||
let mut elems = mb_date.split('-');
|
||||
|
||||
let elem = elems.next();
|
||||
let year = elem
|
||||
.and_then(|s| if s.is_empty() { None } else { Some(s.parse()) })
|
||||
.transpose()?;
|
||||
|
||||
let elem = elems.next();
|
||||
let month = elem.map(|s| s.parse()).transpose()?;
|
||||
|
||||
let elem = elems.next();
|
||||
let day = elem.map(|s| s.parse()).transpose()?;
|
||||
|
||||
Ok(AlbumDate::new(year, month, day))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(remote = "AlbumPrimaryType")]
|
||||
pub enum SerdeAlbumPrimaryTypeDef {
|
||||
Album,
|
||||
Single,
|
||||
#[serde(rename = "EP")]
|
||||
Ep,
|
||||
Broadcast,
|
||||
Other,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SerdeAlbumPrimaryType(#[serde(with = "SerdeAlbumPrimaryTypeDef")] AlbumPrimaryType);
|
||||
|
||||
impl From<SerdeAlbumPrimaryType> for AlbumPrimaryType {
|
||||
fn from(value: SerdeAlbumPrimaryType) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(remote = "AlbumSecondaryType")]
|
||||
pub enum SerdeAlbumSecondaryTypeDef {
|
||||
Compilation,
|
||||
Soundtrack,
|
||||
Spokenword,
|
||||
Interview,
|
||||
Audiobook,
|
||||
#[serde(rename = "Audio drama")]
|
||||
AudioDrama,
|
||||
Live,
|
||||
Remix,
|
||||
#[serde(rename = "DJ-mix")]
|
||||
DjMix,
|
||||
#[serde(rename = "Mixtape/Street")]
|
||||
MixtapeStreet,
|
||||
Demo,
|
||||
#[serde(rename = "Field recording")]
|
||||
FieldRecording,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SerdeAlbumSecondaryType(
|
||||
#[serde(with = "SerdeAlbumSecondaryTypeDef")] AlbumSecondaryType,
|
||||
);
|
||||
|
||||
impl From<SerdeAlbumSecondaryType> for AlbumSecondaryType {
|
||||
fn from(value: SerdeAlbumSecondaryType) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use mockall::predicate;
|
||||
|
||||
use crate::collection::album::AlbumId;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn lookup_artist_release_group() {
|
||||
let mut client = MockIMusicBrainzApiClient::new();
|
||||
let url = format!(
|
||||
"https://musicbrainz.org/ws/2/artist/{mbid}?inc=release-groups",
|
||||
mbid = "00000000-0000-0000-0000-000000000000",
|
||||
);
|
||||
|
||||
let release_group = LookupReleaseGroup {
|
||||
id: String::from("11111111-1111-1111-1111-111111111111"),
|
||||
title: String::from("an album"),
|
||||
first_release_date: String::from("1986-04"),
|
||||
primary_type: SerdeAlbumPrimaryType(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![SerdeAlbumSecondaryType(AlbumSecondaryType::Compilation)],
|
||||
};
|
||||
let response = ResponseLookupArtist {
|
||||
release_groups: vec![release_group],
|
||||
};
|
||||
|
||||
// For code coverage of derive(Debug).
|
||||
assert!(!format!("{response:?}").is_empty());
|
||||
|
||||
client
|
||||
.expect_get()
|
||||
.times(1)
|
||||
.with(predicate::eq(url))
|
||||
.return_once(|_| Ok(response));
|
||||
|
||||
let mut api = MusicBrainzApi::new(client);
|
||||
|
||||
let mbid: Mbid = "00000000-0000-0000-0000-000000000000".try_into().unwrap();
|
||||
let results = api.lookup_artist_release_groups(&mbid).unwrap();
|
||||
|
||||
let mut album = Album::new(
|
||||
AlbumId::new("an album"),
|
||||
(1986, 4),
|
||||
Some(AlbumPrimaryType::Album),
|
||||
vec![AlbumSecondaryType::Compilation],
|
||||
);
|
||||
album.set_musicbrainz_ref(
|
||||
MbAlbumRef::from_uuid_str("11111111-1111-1111-1111-111111111111").unwrap(),
|
||||
);
|
||||
let expected = vec![album];
|
||||
|
||||
assert_eq!(results, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn search_release_group() {
|
||||
let mut client = MockIMusicBrainzApiClient::new();
|
||||
let url = format!(
|
||||
"https://musicbrainz.org/ws/2\
|
||||
/release-group\
|
||||
?query=%22{title}%22+AND+arid%3A{arid}+AND+firstreleasedate%3A{year}",
|
||||
title = "an+album",
|
||||
arid = "00000000-0000-0000-0000-000000000000",
|
||||
year = "1986"
|
||||
);
|
||||
|
||||
let release_group = SearchReleaseGroup {
|
||||
score: 67,
|
||||
id: String::from("11111111-1111-1111-1111-111111111111"),
|
||||
title: String::from("an album"),
|
||||
first_release_date: String::from("1986-04"),
|
||||
primary_type: SerdeAlbumPrimaryType(AlbumPrimaryType::Album),
|
||||
};
|
||||
let response = ResponseSearchReleaseGroup {
|
||||
release_groups: vec![release_group],
|
||||
};
|
||||
|
||||
// For code coverage of derive(Debug).
|
||||
assert!(!format!("{response:?}").is_empty());
|
||||
|
||||
client
|
||||
.expect_get()
|
||||
.times(1)
|
||||
.with(predicate::eq(url))
|
||||
.return_once(|_| Ok(response));
|
||||
|
||||
let mut api = MusicBrainzApi::new(client);
|
||||
|
||||
let arid: Mbid = "00000000-0000-0000-0000-000000000000".try_into().unwrap();
|
||||
let album = Album::new(AlbumId::new("an album"), (1986, 4), None, vec![]);
|
||||
let matches = api.search_release_group(&arid, album).unwrap();
|
||||
|
||||
let mut album = Album::new(
|
||||
AlbumId::new("an album"),
|
||||
(1986, 4),
|
||||
Some(AlbumPrimaryType::Album),
|
||||
vec![],
|
||||
);
|
||||
album.set_musicbrainz_ref(
|
||||
MbAlbumRef::from_uuid_str("11111111-1111-1111-1111-111111111111").unwrap(),
|
||||
);
|
||||
let expected = vec![Match::new(67, album)];
|
||||
|
||||
assert_eq!(matches, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_errors() {
|
||||
let mut client = MockIMusicBrainzApiClient::new();
|
||||
|
||||
let error = ClientError::Client(String::from("get rekt"));
|
||||
assert!(!format!("{error:?}").is_empty());
|
||||
|
||||
client
|
||||
.expect_get::<ResponseLookupArtist>()
|
||||
.times(1)
|
||||
.return_once(|_| Err(ClientError::Client(String::from("get rekt scrub"))));
|
||||
|
||||
client
|
||||
.expect_get::<ResponseLookupArtist>()
|
||||
.times(1)
|
||||
.return_once(|_| Err(ClientError::Status(503)));
|
||||
|
||||
client
|
||||
.expect_get::<ResponseLookupArtist>()
|
||||
.times(1)
|
||||
.return_once(|_| Err(ClientError::Status(504)));
|
||||
|
||||
let mut api = MusicBrainzApi::new(client);
|
||||
|
||||
let mbid: Mbid = "00000000-0000-0000-0000-000000000000".try_into().unwrap();
|
||||
|
||||
let error = api.lookup_artist_release_groups(&mbid).unwrap_err();
|
||||
assert_eq!(error, Error::Client(String::from("get rekt scrub")));
|
||||
|
||||
let error = api.lookup_artist_release_groups(&mbid).unwrap_err();
|
||||
assert_eq!(error, Error::RateLimit);
|
||||
|
||||
let error = api.lookup_artist_release_groups(&mbid).unwrap_err();
|
||||
assert_eq!(error, Error::Unknown(504));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_mb_date() {
|
||||
assert_eq!(AlbumDate::from_mb_date("").unwrap(), AlbumDate::default());
|
||||
assert_eq!(AlbumDate::from_mb_date("1984").unwrap(), 1984.into());
|
||||
assert_eq!(
|
||||
AlbumDate::from_mb_date("1984-05").unwrap(),
|
||||
(1984, 5).into()
|
||||
);
|
||||
assert_eq!(
|
||||
AlbumDate::from_mb_date("1984-05-18").unwrap(),
|
||||
(1984, 5, 18).into()
|
||||
);
|
||||
assert!(AlbumDate::from_mb_date("1984-get-rekt").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde() {
|
||||
let primary_type = "\"EP\"";
|
||||
let primary_type: SerdeAlbumPrimaryType = serde_json::from_str(primary_type).unwrap();
|
||||
let primary_type: AlbumPrimaryType = primary_type.into();
|
||||
assert_eq!(primary_type, AlbumPrimaryType::Ep);
|
||||
|
||||
let secondary_type = "\"Field recording\"";
|
||||
let secondary_type: SerdeAlbumSecondaryType = serde_json::from_str(secondary_type).unwrap();
|
||||
let secondary_type: AlbumSecondaryType = secondary_type.into();
|
||||
assert_eq!(secondary_type, AlbumSecondaryType::FieldRecording);
|
||||
}
|
||||
}
|
2
src/external/musicbrainz/mod.rs
vendored
Normal file
2
src/external/musicbrainz/mod.rs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
#[cfg(feature = "musicbrainz-api")]
|
||||
pub mod api;
|
@ -1,8 +1,7 @@
|
||||
//! MusicHoard - a music collection manager.
|
||||
|
||||
mod core;
|
||||
pub mod database;
|
||||
pub mod library;
|
||||
pub mod external;
|
||||
|
||||
pub use core::collection;
|
||||
pub use core::interface;
|
||||
@ -14,4 +13,4 @@ pub use core::musichoard::{
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
mod tests;
|
||||
mod testmod;
|
||||
|
12
src/main.rs
12
src/main.rs
@ -10,15 +10,17 @@ use ratatui::{backend::CrosstermBackend, Terminal};
|
||||
use structopt::StructOpt;
|
||||
|
||||
use musichoard::{
|
||||
external::{
|
||||
database::json::{backend::JsonDatabaseFileBackend, JsonDatabase},
|
||||
interface::{
|
||||
database::{IDatabase, NullDatabase},
|
||||
library::{ILibrary, NullLibrary},
|
||||
},
|
||||
library::beets::{
|
||||
executor::{ssh::BeetsLibrarySshExecutor, BeetsLibraryProcessExecutor},
|
||||
BeetsLibrary,
|
||||
},
|
||||
},
|
||||
interface::{
|
||||
database::{IDatabase, NullDatabase},
|
||||
library::{ILibrary, NullLibrary},
|
||||
},
|
||||
MusicHoardBuilder, NoDatabase, NoLibrary,
|
||||
};
|
||||
|
||||
@ -135,4 +137,4 @@ fn main() {
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
mod tests;
|
||||
mod testmod;
|
||||
|
473
src/testmod/full.rs
Normal file
473
src/testmod/full.rs
Normal file
@ -0,0 +1,473 @@
|
||||
macro_rules! full_collection {
|
||||
() => {
|
||||
vec![
|
||||
Artist {
|
||||
id: ArtistId {
|
||||
name: "Album_Artist ‘A’".to_string(),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/000000000"),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![
|
||||
String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/artist-a/download-streaming-albums",
|
||||
)
|
||||
]),
|
||||
]),
|
||||
albums: vec![
|
||||
Album {
|
||||
id: AlbumId {
|
||||
title: "album_title a.a".to_string(),
|
||||
},
|
||||
date: 1998.into(),
|
||||
seq: AlbumSeq(1),
|
||||
musicbrainz: Some(MbAlbumRef::from_url_str(
|
||||
"https://musicbrainz.org/release-group/00000000-0000-0000-0000-000000000000"
|
||||
).unwrap()),
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track a.a.1".to_string(),
|
||||
},
|
||||
number: TrackNum(1),
|
||||
artist: vec!["artist a.a.1".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Flac,
|
||||
bitrate: 992,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track a.a.2".to_string(),
|
||||
},
|
||||
number: TrackNum(2),
|
||||
artist: vec![
|
||||
"artist a.a.2.1".to_string(),
|
||||
"artist a.a.2.2".to_string(),
|
||||
],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 320,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track a.a.3".to_string(),
|
||||
},
|
||||
number: TrackNum(3),
|
||||
artist: vec!["artist a.a.3".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Flac,
|
||||
bitrate: 1061,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track a.a.4".to_string(),
|
||||
},
|
||||
number: TrackNum(4),
|
||||
artist: vec!["artist a.a.4".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Flac,
|
||||
bitrate: 1042,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
Album {
|
||||
id: AlbumId {
|
||||
title: "album_title a.b".to_string(),
|
||||
},
|
||||
date: (2015, 4).into(),
|
||||
seq: AlbumSeq(1),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track a.b.1".to_string(),
|
||||
},
|
||||
number: TrackNum(1),
|
||||
artist: vec!["artist a.b.1".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Flac,
|
||||
bitrate: 1004,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track a.b.2".to_string(),
|
||||
},
|
||||
number: TrackNum(2),
|
||||
artist: vec!["artist a.b.2".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Flac,
|
||||
bitrate: 1077,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
Artist {
|
||||
id: ArtistId {
|
||||
name: "Album_Artist ‘B’".to_string(),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/111111111"),
|
||||
String::from("https://www.musicbutler.io/artist-page/111111112"),
|
||||
]),
|
||||
(String::from("Bandcamp"), vec![
|
||||
String::from("https://artist-b.bandcamp.com/")
|
||||
]),
|
||||
(String::from("Qobuz"), vec![
|
||||
String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/artist-b/download-streaming-albums",
|
||||
)
|
||||
]),
|
||||
]),
|
||||
albums: vec![
|
||||
Album {
|
||||
id: AlbumId {
|
||||
title: "album_title b.a".to_string(),
|
||||
},
|
||||
date: (2003, 6, 6).into(),
|
||||
seq: AlbumSeq(1),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track b.a.1".to_string(),
|
||||
},
|
||||
number: TrackNum(1),
|
||||
artist: vec!["artist b.a.1".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 190,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track b.a.2".to_string(),
|
||||
},
|
||||
number: TrackNum(2),
|
||||
artist: vec![
|
||||
"artist b.a.2.1".to_string(),
|
||||
"artist b.a.2.2".to_string(),
|
||||
],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 120,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
Album {
|
||||
id: AlbumId {
|
||||
title: "album_title b.b".to_string(),
|
||||
},
|
||||
date: 2008.into(),
|
||||
seq: AlbumSeq(3),
|
||||
musicbrainz: Some(MbAlbumRef::from_url_str(
|
||||
"https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111111"
|
||||
).unwrap()),
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track b.b.1".to_string(),
|
||||
},
|
||||
number: TrackNum(1),
|
||||
artist: vec!["artist b.b.1".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Flac,
|
||||
bitrate: 1077,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track b.b.2".to_string(),
|
||||
},
|
||||
number: TrackNum(2),
|
||||
artist: vec![
|
||||
"artist b.b.2.1".to_string(),
|
||||
"artist b.b.2.2".to_string(),
|
||||
],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 320,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
Album {
|
||||
id: AlbumId {
|
||||
title: "album_title b.c".to_string(),
|
||||
},
|
||||
date: 2009.into(),
|
||||
seq: AlbumSeq(2),
|
||||
musicbrainz: Some(MbAlbumRef::from_url_str(
|
||||
"https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111112"
|
||||
).unwrap()),
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track b.c.1".to_string(),
|
||||
},
|
||||
number: TrackNum(1),
|
||||
artist: vec!["artist b.c.1".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 190,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track b.c.2".to_string(),
|
||||
},
|
||||
number: TrackNum(2),
|
||||
artist: vec![
|
||||
"artist b.c.2.1".to_string(),
|
||||
"artist b.c.2.2".to_string(),
|
||||
],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 120,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
Album {
|
||||
id: AlbumId {
|
||||
title: "album_title b.d".to_string(),
|
||||
},
|
||||
date: 2015.into(),
|
||||
seq: AlbumSeq(4),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track b.d.1".to_string(),
|
||||
},
|
||||
number: TrackNum(1),
|
||||
artist: vec!["artist b.d.1".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 190,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track b.d.2".to_string(),
|
||||
},
|
||||
number: TrackNum(2),
|
||||
artist: vec![
|
||||
"artist b.d.2.1".to_string(),
|
||||
"artist b.d.2.2".to_string(),
|
||||
],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 120,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
Artist {
|
||||
id: ArtistId {
|
||||
name: "The Album_Artist ‘C’".to_string(),
|
||||
},
|
||||
sort: Some(ArtistId {
|
||||
name: "Album_Artist ‘C’, The".to_string(),
|
||||
}),
|
||||
musicbrainz: Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111"
|
||||
).unwrap()),
|
||||
properties: HashMap::new(),
|
||||
albums: vec![
|
||||
Album {
|
||||
id: AlbumId {
|
||||
title: "album_title c.a".to_string(),
|
||||
},
|
||||
date: 1985.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track c.a.1".to_string(),
|
||||
},
|
||||
number: TrackNum(1),
|
||||
artist: vec!["artist c.a.1".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 320,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track c.a.2".to_string(),
|
||||
},
|
||||
number: TrackNum(2),
|
||||
artist: vec![
|
||||
"artist c.a.2.1".to_string(),
|
||||
"artist c.a.2.2".to_string(),
|
||||
],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 120,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
Album {
|
||||
id: AlbumId {
|
||||
title: "album_title c.b".to_string(),
|
||||
},
|
||||
date: 2018.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track c.b.1".to_string(),
|
||||
},
|
||||
number: TrackNum(1),
|
||||
artist: vec!["artist c.b.1".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Flac,
|
||||
bitrate: 1041,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track c.b.2".to_string(),
|
||||
},
|
||||
number: TrackNum(2),
|
||||
artist: vec![
|
||||
"artist c.b.2.1".to_string(),
|
||||
"artist c.b.2.2".to_string(),
|
||||
],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Flac,
|
||||
bitrate: 756,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
Artist {
|
||||
id: ArtistId {
|
||||
name: "Album_Artist ‘D’".to_string(),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: None,
|
||||
properties: HashMap::new(),
|
||||
albums: vec![
|
||||
Album {
|
||||
id: AlbumId {
|
||||
title: "album_title d.a".to_string(),
|
||||
},
|
||||
date: 1995.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track d.a.1".to_string(),
|
||||
},
|
||||
number: TrackNum(1),
|
||||
artist: vec!["artist d.a.1".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 120,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track d.a.2".to_string(),
|
||||
},
|
||||
number: TrackNum(2),
|
||||
artist: vec![
|
||||
"artist d.a.2.1".to_string(),
|
||||
"artist d.a.2.2".to_string(),
|
||||
],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Mp3,
|
||||
bitrate: 120,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
Album {
|
||||
id: AlbumId {
|
||||
title: "album_title d.b".to_string(),
|
||||
},
|
||||
date: 2028.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track d.b.1".to_string(),
|
||||
},
|
||||
number: TrackNum(1),
|
||||
artist: vec!["artist d.b.1".to_string()],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Flac,
|
||||
bitrate: 841,
|
||||
},
|
||||
},
|
||||
Track {
|
||||
id: TrackId {
|
||||
title: "track d.b.2".to_string(),
|
||||
},
|
||||
number: TrackNum(2),
|
||||
artist: vec![
|
||||
"artist d.b.2.1".to_string(),
|
||||
"artist d.b.2.2".to_string(),
|
||||
],
|
||||
quality: TrackQuality {
|
||||
format: TrackFormat::Flac,
|
||||
bitrate: 756,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use full_collection;
|
@ -1,3 +1,4 @@
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! library_collection {
|
||||
() => {
|
||||
vec![
|
||||
@ -13,13 +14,11 @@ macro_rules! library_collection {
|
||||
id: AlbumId {
|
||||
title: "album_title a.a".to_string(),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 1998,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 1998.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: None,
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -74,13 +73,11 @@ macro_rules! library_collection {
|
||||
id: AlbumId {
|
||||
title: "album_title a.b".to_string(),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2015,
|
||||
month: AlbumMonth::April,
|
||||
day: 0,
|
||||
},
|
||||
date: (2015, 4).into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: None,
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -120,13 +117,11 @@ macro_rules! library_collection {
|
||||
id: AlbumId {
|
||||
title: "album_title b.a".to_string(),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2003,
|
||||
month: AlbumMonth::June,
|
||||
day: 6,
|
||||
},
|
||||
date: (2003, 6, 6).into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: None,
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -159,13 +154,11 @@ macro_rules! library_collection {
|
||||
id: AlbumId {
|
||||
title: "album_title b.b".to_string(),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2008,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 2008.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: None,
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -198,13 +191,11 @@ macro_rules! library_collection {
|
||||
id: AlbumId {
|
||||
title: "album_title b.c".to_string(),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2009,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 2009.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: None,
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -237,13 +228,11 @@ macro_rules! library_collection {
|
||||
id: AlbumId {
|
||||
title: "album_title b.d".to_string(),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2015,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 2015.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: None,
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -288,13 +277,11 @@ macro_rules! library_collection {
|
||||
id: AlbumId {
|
||||
title: "album_title c.a".to_string(),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 1985,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 1985.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: None,
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -327,13 +314,11 @@ macro_rules! library_collection {
|
||||
id: AlbumId {
|
||||
title: "album_title c.b".to_string(),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2018,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 2018.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: None,
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -376,13 +361,11 @@ macro_rules! library_collection {
|
||||
id: AlbumId {
|
||||
title: "album_title d.a".to_string(),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 1995,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 1995.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: None,
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -415,13 +398,11 @@ macro_rules! library_collection {
|
||||
id: AlbumId {
|
||||
title: "album_title d.b".to_string(),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2028,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 2028.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: None,
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -456,81 +437,5 @@ macro_rules! library_collection {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! full_collection {
|
||||
() => {{
|
||||
let mut collection = library_collection!();
|
||||
let mut iter = collection.iter_mut();
|
||||
|
||||
let artist_a = iter.next().unwrap();
|
||||
assert_eq!(artist_a.id.name, "Album_Artist ‘A’");
|
||||
|
||||
artist_a.musicbrainz = Some(MusicBrainzUrl::artist_from_str(
|
||||
"https://musicbrainz.org/artist/00000000-0000-0000-0000-000000000000",
|
||||
).unwrap());
|
||||
|
||||
artist_a.properties = HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/000000000"),
|
||||
]),
|
||||
(String::from("Qobuz"), vec![
|
||||
String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/artist-a/download-streaming-albums",
|
||||
)
|
||||
]),
|
||||
]);
|
||||
|
||||
artist_a.albums[0].seq = AlbumSeq(1);
|
||||
artist_a.albums[1].seq = AlbumSeq(1);
|
||||
|
||||
artist_a.albums[0].musicbrainz = Some(MusicBrainzUrl::album_from_str(
|
||||
"https://musicbrainz.org/release-group/00000000-0000-0000-0000-000000000000"
|
||||
).unwrap());
|
||||
|
||||
let artist_b = iter.next().unwrap();
|
||||
assert_eq!(artist_b.id.name, "Album_Artist ‘B’");
|
||||
|
||||
artist_b.musicbrainz = Some(MusicBrainzUrl::artist_from_str(
|
||||
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111",
|
||||
).unwrap());
|
||||
|
||||
artist_b.properties = HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
String::from("https://www.musicbutler.io/artist-page/111111111"),
|
||||
String::from("https://www.musicbutler.io/artist-page/111111112"),
|
||||
]),
|
||||
(String::from("Bandcamp"), vec![String::from("https://artist-b.bandcamp.com/")]),
|
||||
(String::from("Qobuz"), vec![
|
||||
String::from(
|
||||
"https://www.qobuz.com/nl-nl/interpreter/artist-b/download-streaming-albums",
|
||||
)
|
||||
]),
|
||||
]);
|
||||
|
||||
artist_b.albums[0].seq = AlbumSeq(1);
|
||||
artist_b.albums[1].seq = AlbumSeq(3);
|
||||
artist_b.albums[2].seq = AlbumSeq(2);
|
||||
artist_b.albums[3].seq = AlbumSeq(4);
|
||||
|
||||
artist_b.albums[1].musicbrainz = Some(MusicBrainzUrl::album_from_str(
|
||||
"https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111111"
|
||||
).unwrap());
|
||||
|
||||
artist_b.albums[2].musicbrainz = Some(MusicBrainzUrl::album_from_str(
|
||||
"https://musicbrainz.org/release-group/11111111-1111-1111-1111-111111111112"
|
||||
).unwrap());
|
||||
|
||||
let artist_c = iter.next().unwrap();
|
||||
assert_eq!(artist_c.id.name, "The Album_Artist ‘C’");
|
||||
|
||||
artist_c.musicbrainz = Some(MusicBrainzUrl::artist_from_str(
|
||||
"https://musicbrainz.org/artist/11111111-1111-1111-1111-111111111111",
|
||||
).unwrap());
|
||||
|
||||
// Nothing for artist_d
|
||||
|
||||
collection
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) use full_collection;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use library_collection;
|
2
src/testmod/mod.rs
Normal file
2
src/testmod/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod full;
|
||||
pub mod library;
|
@ -1,13 +1,13 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use musichoard::collection::{
|
||||
album::{Album, AlbumDate, AlbumId, AlbumMonth, AlbumSeq},
|
||||
album::{Album, AlbumId, AlbumPrimaryType, AlbumSeq},
|
||||
artist::{Artist, ArtistId},
|
||||
musicbrainz::MusicBrainzUrl,
|
||||
musicbrainz::{MbAlbumRef, MbArtistRef},
|
||||
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::tests::*;
|
||||
use crate::testmod::*;
|
||||
|
||||
pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| full_collection!());
|
||||
pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| full::full_collection!());
|
||||
|
@ -3,6 +3,7 @@ use std::collections::HashMap;
|
||||
use musichoard::collection::{
|
||||
album::{Album, AlbumDate, AlbumSeq, AlbumStatus},
|
||||
artist::Artist,
|
||||
musicbrainz::IMusicBrainzRef,
|
||||
track::{Track, TrackFormat, TrackQuality},
|
||||
Collection,
|
||||
};
|
||||
@ -202,7 +203,7 @@ struct ArtistOverlay<'a> {
|
||||
}
|
||||
|
||||
impl<'a> ArtistOverlay<'a> {
|
||||
fn opt_opt_to_str<S: AsRef<str>>(opt: Option<Option<&S>>) -> &str {
|
||||
fn opt_opt_to_str<S: AsRef<str> + ?Sized>(opt: Option<Option<&S>>) -> &str {
|
||||
opt.flatten().map(|item| item.as_ref()).unwrap_or("")
|
||||
}
|
||||
|
||||
@ -264,7 +265,7 @@ impl<'a> ArtistOverlay<'a> {
|
||||
MusicBrainz: {}\n{item_indent}\
|
||||
Properties: {}",
|
||||
artist.map(|a| a.id.name.as_str()).unwrap_or(""),
|
||||
Self::opt_opt_to_str(artist.map(|a| a.musicbrainz.as_ref())),
|
||||
Self::opt_opt_to_str(artist.map(|a| a.musicbrainz.as_ref().map(|mb| mb.url()))),
|
||||
Self::opt_hashmap_to_string(
|
||||
artist.map(|a| &a.properties),
|
||||
&double_item_indent,
|
||||
@ -329,12 +330,15 @@ impl<'a, 'b> AlbumState<'a, 'b> {
|
||||
}
|
||||
|
||||
fn display_album_date(date: &AlbumDate) -> String {
|
||||
if date.month.is_none() {
|
||||
format!("{}", date.year)
|
||||
} else if date.day == 0 {
|
||||
format!("{}‐{:02}", date.year, date.month as u8)
|
||||
} else {
|
||||
format!("{}‐{:02}‐{:02}", date.year, date.month as u8, date.day)
|
||||
match date.year {
|
||||
Some(year) => match date.month {
|
||||
Some(month) => match date.day {
|
||||
Some(day) => format!("{year}‐{month:02}‐{day:02}"),
|
||||
None => format!("{year}‐{month:02}"),
|
||||
},
|
||||
None => format!("{year}"),
|
||||
},
|
||||
None => String::from(""),
|
||||
}
|
||||
}
|
||||
|
||||
@ -804,17 +808,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn display_album_date() {
|
||||
assert_eq!(AlbumState::display_album_date(&AlbumDate::default()), "0");
|
||||
assert_eq!(AlbumState::display_album_date(&AlbumDate::default()), "");
|
||||
assert_eq!(AlbumState::display_album_date(&1990.into()), "1990");
|
||||
assert_eq!(AlbumState::display_album_date(&(1990, 5).into()), "1990‐05");
|
||||
assert_eq!(
|
||||
AlbumState::display_album_date(&AlbumDate::new(1990, 0, 0)),
|
||||
"1990"
|
||||
);
|
||||
assert_eq!(
|
||||
AlbumState::display_album_date(&AlbumDate::new(1990, 5, 0)),
|
||||
"1990‐05"
|
||||
);
|
||||
assert_eq!(
|
||||
AlbumState::display_album_date(&AlbumDate::new(1990, 5, 6)),
|
||||
AlbumState::display_album_date(&(1990, 5, 6).into()),
|
||||
"1990‐05‐06"
|
||||
);
|
||||
}
|
||||
@ -870,7 +868,7 @@ mod tests {
|
||||
let mut artists: Vec<Artist> = vec![Artist::new(ArtistId::new("An artist"))];
|
||||
artists[0]
|
||||
.albums
|
||||
.push(Album::new("An album", AlbumDate::default()));
|
||||
.push(Album::new("An album", AlbumDate::default(), None, vec![]));
|
||||
let mut selection = Selection::new(&artists);
|
||||
|
||||
draw_test_suite(&artists, &mut selection);
|
||||
|
@ -5,7 +5,7 @@ use tempfile::NamedTempFile;
|
||||
|
||||
use musichoard::{
|
||||
collection::{album::AlbumDate, artist::Artist, Collection},
|
||||
database::json::{backend::JsonDatabaseFileBackend, JsonDatabase},
|
||||
external::database::json::{backend::JsonDatabaseFileBackend, JsonDatabase},
|
||||
interface::database::IDatabase,
|
||||
};
|
||||
|
||||
|
@ -1 +1 @@
|
||||
{"V20240308":[{"name":"Аркона","sort":"Arkona","musicbrainz":"https://musicbrainz.org/artist/baad262d-55ef-427a-83c7-f7530964f212","properties":{"Bandcamp":["https://arkonamoscow.bandcamp.com/"],"MusicButler":["https://www.musicbutler.io/artist-page/283448581"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/arkona/download-streaming-albums"]},"albums":[{"title":"Slovo","seq":0,"musicbrainz":null}]},{"name":"Eluveitie","sort":null,"musicbrainz":"https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/269358403"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/eluveitie/download-streaming-albums"]},"albums":[{"title":"Vên [re‐recorded]","seq":0,"musicbrainz":null},{"title":"Slania","seq":0,"musicbrainz":null}]},{"name":"Frontside","sort":null,"musicbrainz":"https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/826588800"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/frontside/download-streaming-albums"]},"albums":[{"title":"…nasze jest królestwo, potęga i chwała na wieki…","seq":0,"musicbrainz":null}]},{"name":"Heaven’s Basement","sort":"Heaven’s Basement","musicbrainz":"https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/291158685"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/heaven-s-basement/download-streaming-albums"]},"albums":[{"title":"Paper Plague","seq":0,"musicbrainz":null},{"title":"Unbreakable","seq":0,"musicbrainz":null}]},{"name":"Metallica","sort":null,"musicbrainz":"https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/3996865"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/metallica/download-streaming-albums"]},"albums":[{"title":"Ride the Lightning","seq":0,"musicbrainz":null},{"title":"S&M","seq":0,"musicbrainz":null}]}]}
|
||||
{"V20240313":[{"name":"Аркона","sort":"Arkona","musicbrainz":"https://musicbrainz.org/artist/baad262d-55ef-427a-83c7-f7530964f212","properties":{"Bandcamp":["https://arkonamoscow.bandcamp.com/"],"MusicButler":["https://www.musicbutler.io/artist-page/283448581"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/arkona/download-streaming-albums"]},"albums":[{"title":"Slovo","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":[]}]},{"name":"Eluveitie","sort":null,"musicbrainz":"https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/269358403"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/eluveitie/download-streaming-albums"]},"albums":[{"title":"Vên [re‐recorded]","seq":0,"musicbrainz":null,"primary_type":"Ep","secondary_types":[]},{"title":"Slania","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":[]}]},{"name":"Frontside","sort":null,"musicbrainz":"https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/826588800"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/frontside/download-streaming-albums"]},"albums":[{"title":"…nasze jest królestwo, potęga i chwała na wieki…","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":[]}]},{"name":"Heaven’s Basement","sort":"Heaven’s Basement","musicbrainz":"https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/291158685"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/heaven-s-basement/download-streaming-albums"]},"albums":[{"title":"Paper Plague","seq":0,"musicbrainz":null,"primary_type":null,"secondary_types":[]},{"title":"Unbreakable","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":[]}]},{"name":"Metallica","sort":null,"musicbrainz":"https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab","properties":{"MusicButler":["https://www.musicbutler.io/artist-page/3996865"],"Qobuz":["https://www.qobuz.com/nl-nl/interpreter/metallica/download-streaming-albums"]},"albums":[{"title":"Ride the Lightning","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":[]},{"title":"S&M","seq":0,"musicbrainz":null,"primary_type":"Album","secondary_types":["Live"]}]}]}
|
@ -7,8 +7,10 @@ mod library;
|
||||
mod testlib;
|
||||
|
||||
use musichoard::{
|
||||
external::{
|
||||
database::json::{backend::JsonDatabaseFileBackend, JsonDatabase},
|
||||
library::beets::{executor::BeetsLibraryProcessExecutor, BeetsLibrary},
|
||||
},
|
||||
IMusicHoardBase, IMusicHoardDatabase, IMusicHoardLibrary, MusicHoard,
|
||||
};
|
||||
|
||||
|
@ -8,8 +8,8 @@ use std::{
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use musichoard::{
|
||||
external::library::beets::{executor::BeetsLibraryProcessExecutor, BeetsLibrary},
|
||||
interface::library::{Field, ILibrary, Item, Query},
|
||||
library::beets::{executor::BeetsLibraryProcessExecutor, BeetsLibrary},
|
||||
};
|
||||
|
||||
use crate::library::testmod::LIBRARY_ITEMS;
|
||||
|
@ -1,9 +1,6 @@
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use musichoard::{
|
||||
collection::{album::AlbumMonth, track::TrackFormat},
|
||||
interface::library::Item,
|
||||
};
|
||||
use musichoard::{collection::track::TrackFormat, interface::library::Item};
|
||||
|
||||
pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
vec![
|
||||
@ -11,7 +8,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 1,
|
||||
@ -24,7 +21,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 2,
|
||||
@ -37,7 +34,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 3,
|
||||
@ -50,7 +47,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 4,
|
||||
@ -63,7 +60,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 5,
|
||||
@ -76,7 +73,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 6,
|
||||
@ -89,7 +86,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 7,
|
||||
@ -102,7 +99,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 8,
|
||||
@ -115,7 +112,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 9,
|
||||
@ -128,7 +125,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 10,
|
||||
@ -141,7 +138,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 11,
|
||||
@ -154,7 +151,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 12,
|
||||
@ -167,7 +164,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 13,
|
||||
@ -180,7 +177,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Аркона"),
|
||||
album_artist_sort: Some(String::from("Arkona")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slovo"),
|
||||
track_number: 14,
|
||||
@ -193,7 +190,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2004,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Vên [re‐recorded]"),
|
||||
track_number: 1,
|
||||
@ -206,7 +203,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2004,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Vên [re‐recorded]"),
|
||||
track_number: 2,
|
||||
@ -219,7 +216,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2004,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Vên [re‐recorded]"),
|
||||
track_number: 3,
|
||||
@ -232,7 +229,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2004,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Vên [re‐recorded]"),
|
||||
track_number: 4,
|
||||
@ -245,7 +242,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2004,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Vên [re‐recorded]"),
|
||||
track_number: 5,
|
||||
@ -258,7 +255,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2004,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Vên [re‐recorded]"),
|
||||
track_number: 6,
|
||||
@ -271,7 +268,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 1,
|
||||
@ -284,7 +281,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 2,
|
||||
@ -297,7 +294,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 3,
|
||||
@ -310,7 +307,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 4,
|
||||
@ -323,7 +320,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 5,
|
||||
@ -336,7 +333,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 6,
|
||||
@ -349,7 +346,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 7,
|
||||
@ -362,7 +359,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 8,
|
||||
@ -375,7 +372,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 9,
|
||||
@ -388,7 +385,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 10,
|
||||
@ -401,7 +398,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 11,
|
||||
@ -414,7 +411,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Eluveitie"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2008,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Slania"),
|
||||
track_number: 12,
|
||||
@ -427,7 +424,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Frontside"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2001,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
track_number: 1,
|
||||
@ -440,7 +437,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Frontside"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2001,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
track_number: 2,
|
||||
@ -453,7 +450,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Frontside"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2001,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
track_number: 3,
|
||||
@ -466,7 +463,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Frontside"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2001,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
track_number: 4,
|
||||
@ -479,7 +476,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Frontside"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2001,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
track_number: 5,
|
||||
@ -492,7 +489,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Frontside"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2001,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
track_number: 6,
|
||||
@ -505,7 +502,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Frontside"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2001,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
track_number: 7,
|
||||
@ -518,7 +515,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Frontside"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2001,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
track_number: 8,
|
||||
@ -531,7 +528,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Frontside"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2001,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
track_number: 9,
|
||||
@ -544,7 +541,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Frontside"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2001,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
track_number: 10,
|
||||
@ -557,7 +554,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Frontside"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2001,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
track_number: 11,
|
||||
@ -570,7 +567,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Heaven’s Basement"),
|
||||
album_artist_sort: None,
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Paper Plague"),
|
||||
track_number: 0,
|
||||
@ -583,7 +580,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Heaven’s Basement"),
|
||||
album_artist_sort: Some(String::from("Heaven’s Basement")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Unbreakable"),
|
||||
track_number: 1,
|
||||
@ -596,7 +593,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Heaven’s Basement"),
|
||||
album_artist_sort: Some(String::from("Heaven’s Basement")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Unbreakable"),
|
||||
track_number: 2,
|
||||
@ -609,7 +606,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Heaven’s Basement"),
|
||||
album_artist_sort: Some(String::from("Heaven’s Basement")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Unbreakable"),
|
||||
track_number: 3,
|
||||
@ -622,7 +619,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Heaven’s Basement"),
|
||||
album_artist_sort: Some(String::from("Heaven’s Basement")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Unbreakable"),
|
||||
track_number: 4,
|
||||
@ -635,7 +632,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Heaven’s Basement"),
|
||||
album_artist_sort: Some(String::from("Heaven’s Basement")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Unbreakable"),
|
||||
track_number: 5,
|
||||
@ -648,7 +645,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Heaven’s Basement"),
|
||||
album_artist_sort: Some(String::from("Heaven’s Basement")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Unbreakable"),
|
||||
track_number: 6,
|
||||
@ -661,7 +658,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Heaven’s Basement"),
|
||||
album_artist_sort: Some(String::from("Heaven’s Basement")),
|
||||
album_year: 2011,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Unbreakable"),
|
||||
track_number: 7,
|
||||
@ -674,7 +671,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1984,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Ride the Lightning"),
|
||||
track_number: 1,
|
||||
@ -687,7 +684,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1984,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Ride the Lightning"),
|
||||
track_number: 2,
|
||||
@ -700,7 +697,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1984,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Ride the Lightning"),
|
||||
track_number: 3,
|
||||
@ -713,7 +710,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1984,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Ride the Lightning"),
|
||||
track_number: 4,
|
||||
@ -726,7 +723,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1984,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Ride the Lightning"),
|
||||
track_number: 5,
|
||||
@ -739,7 +736,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1984,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Ride the Lightning"),
|
||||
track_number: 6,
|
||||
@ -752,7 +749,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1984,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Ride the Lightning"),
|
||||
track_number: 7,
|
||||
@ -765,7 +762,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1984,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("Ride the Lightning"),
|
||||
track_number: 8,
|
||||
@ -778,7 +775,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 1,
|
||||
@ -791,7 +788,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 2,
|
||||
@ -804,7 +801,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 3,
|
||||
@ -817,7 +814,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 4,
|
||||
@ -830,7 +827,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 5,
|
||||
@ -843,7 +840,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 6,
|
||||
@ -856,7 +853,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 7,
|
||||
@ -869,7 +866,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 8,
|
||||
@ -882,7 +879,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 9,
|
||||
@ -895,7 +892,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 10,
|
||||
@ -908,7 +905,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 11,
|
||||
@ -921,7 +918,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 12,
|
||||
@ -934,7 +931,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 13,
|
||||
@ -947,7 +944,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 14,
|
||||
@ -960,7 +957,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 15,
|
||||
@ -973,7 +970,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 16,
|
||||
@ -986,7 +983,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 17,
|
||||
@ -999,7 +996,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 18,
|
||||
@ -1012,7 +1009,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 19,
|
||||
@ -1025,7 +1022,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 20,
|
||||
@ -1038,7 +1035,7 @@ pub static LIBRARY_ITEMS: Lazy<Vec<Item>> = Lazy::new(|| -> Vec<Item> {
|
||||
album_artist: String::from("Metallica"),
|
||||
album_artist_sort: None,
|
||||
album_year: 1999,
|
||||
album_month: AlbumMonth::None,
|
||||
album_month: 0,
|
||||
album_day: 0,
|
||||
album_title: String::from("S&M"),
|
||||
track_number: 21,
|
||||
|
@ -2,9 +2,9 @@ use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use musichoard::collection::{
|
||||
album::{Album, AlbumDate, AlbumId, AlbumMonth, AlbumSeq},
|
||||
album::{Album, AlbumId, AlbumPrimaryType, AlbumSecondaryType, AlbumSeq},
|
||||
artist::{Artist, ArtistId},
|
||||
musicbrainz::MusicBrainzUrl,
|
||||
musicbrainz::MbArtistRef,
|
||||
track::{Track, TrackFormat, TrackId, TrackNum, TrackQuality},
|
||||
Collection,
|
||||
};
|
||||
@ -18,7 +18,7 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
sort: Some(ArtistId{
|
||||
name: String::from("Arkona")
|
||||
}),
|
||||
musicbrainz: Some(MusicBrainzUrl::artist_from_str(
|
||||
musicbrainz: Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/baad262d-55ef-427a-83c7-f7530964f212"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
@ -36,13 +36,11 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
id: AlbumId {
|
||||
title: String::from("Slovo"),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2011,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 2011.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -206,8 +204,8 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
name: String::from("Eluveitie"),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: Some(MusicBrainzUrl::artist_from_str(
|
||||
"https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38",
|
||||
musicbrainz: Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/8000598a-5edb-401c-8e6d-36b167feaf38"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
@ -222,13 +220,11 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
id: AlbumId {
|
||||
title: String::from("Vên [re‐recorded]"),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2004,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 2004.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Ep),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -302,13 +298,11 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
id: AlbumId {
|
||||
title: String::from("Slania"),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2008,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 2008.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -451,8 +445,8 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
name: String::from("Frontside"),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: Some(MusicBrainzUrl::artist_from_str(
|
||||
"https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490",
|
||||
musicbrainz: Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/3a901353-fccd-4afd-ad01-9c03f451b490"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
@ -466,13 +460,11 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
id: AlbumId {
|
||||
title: String::from("…nasze jest królestwo, potęga i chwała na wieki…"),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2001,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 2001.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -605,8 +597,8 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
sort: Some(ArtistId {
|
||||
name: String::from("Heaven’s Basement"),
|
||||
}),
|
||||
musicbrainz: Some(MusicBrainzUrl::artist_from_str(
|
||||
"https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc",
|
||||
musicbrainz: Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/c2c4d56a-d599-4a18-bd2f-ae644e2198cc"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
@ -620,13 +612,11 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
id: AlbumId {
|
||||
title: String::from("Paper Plague"),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2011,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 2011.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: None,
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -644,13 +634,11 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
id: AlbumId {
|
||||
title: String::from("Unbreakable"),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 2011,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 2011.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -737,8 +725,8 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
name: String::from("Metallica"),
|
||||
},
|
||||
sort: None,
|
||||
musicbrainz: Some(MusicBrainzUrl::artist_from_str(
|
||||
"https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab",
|
||||
musicbrainz: Some(MbArtistRef::from_url_str(
|
||||
"https://musicbrainz.org/artist/65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab"
|
||||
).unwrap()),
|
||||
properties: HashMap::from([
|
||||
(String::from("MusicButler"), vec![
|
||||
@ -753,13 +741,11 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
id: AlbumId {
|
||||
title: String::from("Ride the Lightning"),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 1984,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 1984.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
@ -855,13 +841,11 @@ pub static COLLECTION: Lazy<Vec<Artist>> = Lazy::new(|| -> Collection {
|
||||
id: AlbumId {
|
||||
title: String::from("S&M"),
|
||||
},
|
||||
date: AlbumDate {
|
||||
year: 1999,
|
||||
month: AlbumMonth::None,
|
||||
day: 0,
|
||||
},
|
||||
date: 1999.into(),
|
||||
seq: AlbumSeq(0),
|
||||
musicbrainz: None,
|
||||
primary_type: Some(AlbumPrimaryType::Album),
|
||||
secondary_types: vec![AlbumSecondaryType::Live],
|
||||
tracks: vec![
|
||||
Track {
|
||||
id: TrackId {
|
||||
|
Loading…
Reference in New Issue
Block a user