mirror of
https://github.com/CDrummond/bliss-analyser.git
synced 2026-10-22 22:32:08 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04baedbfc2 | ||
|
|
609b6faff9 | ||
|
|
fe65d35066 | ||
|
|
75aeee128f | ||
|
|
65e40acce4 | ||
|
|
a9f819f39b | ||
|
|
4704624b2f | ||
|
|
c3083d9a1a | ||
|
|
90d4dfec23 | ||
|
|
e89aee1f4e | ||
|
|
703e39508c | ||
|
|
eb16d8379d | ||
|
|
a220b4d0c5 | ||
|
|
6d61616dda | ||
|
|
d85424d27c | ||
|
|
144c8a7ffe | ||
|
|
ab0adc96ca | ||
|
|
7cf62c3ad0 | ||
|
|
d9841b3527 | ||
|
|
3b6872b2a0 | ||
|
|
45b20902d6 | ||
|
|
9feb80bbd1 | ||
|
|
b9b8fdabfc | ||
|
|
a818628706 | ||
|
|
370c533a49 | ||
|
|
9aae7825d0 | ||
|
|
ebddf8febe | ||
|
|
143fb2bf62 | ||
|
|
323978cbeb | ||
|
|
a749c03195 | ||
|
|
bfd2a44e2e | ||
|
|
943227c16b | ||
|
|
b7259cd0ae | ||
|
|
f023faa336 | ||
|
|
074be07d18 | ||
|
|
a12971df17 | ||
|
|
8ba10a0f8e | ||
|
|
cf0e6cf5fb | ||
|
|
de2c2dfaa3 | ||
|
|
1a45791e58 | ||
|
|
b5f35dcf36 | ||
|
|
cd12e805c3 | ||
|
|
fa87a7e135 | ||
|
|
df17032377 | ||
|
|
e47c2b477e | ||
|
|
133e3e2204 | ||
|
|
3dec74d328 | ||
|
|
cb323f3efe | ||
|
|
69303d62c6 | ||
|
|
f05e11da2e | ||
|
|
37947564fc | ||
|
|
8a5d9bc276 | ||
|
|
467e341cfb | ||
|
|
2788759829 | ||
|
|
0653e365b8 | ||
|
|
985902325e | ||
|
|
f3903383c9 | ||
|
|
f13601f1c2 | ||
|
|
dfb87881c9 | ||
|
|
b1f89050e2 | ||
|
|
94ef2e2c1f | ||
|
|
7fb2e66c58 | ||
|
|
9f8ea6e490 | ||
|
|
0aa10918ae | ||
|
|
caa8f124a7 | ||
|
|
dabbfdb206 | ||
|
|
182d980395 | ||
|
|
c51de47696 | ||
|
|
f6edb983d3 | ||
|
|
625248fbc6 | ||
|
|
37d9f627ad | ||
|
|
b61aee0a6a | ||
|
|
51ce6cadc7 | ||
|
|
b097f06b5f | ||
|
|
e0a6759af5 | ||
|
|
0153b85b65 | ||
|
|
311c5e4242 | ||
|
|
4f7850bc40 | ||
|
|
e88c4446d2 | ||
|
|
4addd27329 | ||
|
|
0db7d882c2 | ||
|
|
7a6317b28c | ||
|
|
7998278c9b | ||
|
|
08e43bf609 | ||
|
|
302b0f9483 | ||
|
|
a6284c3761 | ||
|
|
cf729aaef3 | ||
|
|
2ff95c27c5 | ||
|
|
4c40a0b41b | ||
|
|
649ce31907 | ||
|
|
6d53ac16a9 | ||
|
|
a859f7ac0f | ||
|
|
f3a86414dc | ||
|
|
e8614340f0 | ||
|
|
b361948396 | ||
|
|
25c01f4434 | ||
|
|
5fef00c24a | ||
|
|
ef772ad30f | ||
|
|
caa77bd847 |
No files matched your search
+74
-24
@@ -7,7 +7,7 @@ env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
Linux:
|
||||
Linux_ffmpeg4:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
@@ -15,8 +15,8 @@ jobs:
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Packages
|
||||
run: sudo apt-get update && sudo apt-get install build-essential yasm libavutil-dev libavcodec-dev libavformat-dev libavfilter-dev libavfilter-dev libavdevice-dev libswresample-dev libfftw3-dev ffmpeg
|
||||
|
||||
run: sudo apt-get update && sudo apt-get install build-essential yasm libavutil-dev libavcodec-dev libavformat-dev libavfilter-dev libavdevice-dev libswresample-dev libfftw3-dev ffmpeg -y
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build --release
|
||||
@@ -30,41 +30,91 @@ jobs:
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: bliss-analyser-linux
|
||||
name: bliss-analyser-linux-x86-ffmpeg4
|
||||
path: releases/
|
||||
|
||||
macOS:
|
||||
runs-on: macos-11.0
|
||||
# Linux ffmpeg5 builds used a ppa which is now paywalled. Therefore until there is a github runner-image
|
||||
# Of 24.04 (which should have ffmpeg5?) then these builds are disabled. :(
|
||||
#
|
||||
# Linux_ffmpeg5:
|
||||
# runs-on: ubuntu-24.04
|
||||
#
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v1
|
||||
#
|
||||
# - name: Packages
|
||||
# run: sudo apt-get update && sudo apt-get install build-essential yasm libavutil-dev libavcodec-dev libavformat-dev libavfilter-dev libavdevice-dev libswresample-dev libfftw3-dev ffmpeg -y
|
||||
#
|
||||
# - name: Build
|
||||
# run: |
|
||||
# cargo build --release
|
||||
# strip target/release/bliss-analyser
|
||||
# mkdir releases
|
||||
# cp target/release/bliss-analyser releases/bliss-analyser
|
||||
# cp UserGuide.md releases/README.md
|
||||
# cp LICENSE releases/
|
||||
# cp configs/linux.ini releases/config.ini
|
||||
#
|
||||
# - name: Upload artifacts
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: bliss-analyser-linux-x86-ffmpeg5
|
||||
# path: releases/
|
||||
|
||||
|
||||
Linux_ARM:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install deps
|
||||
- name: Build ARM
|
||||
run: |
|
||||
brew install ffmpeg@5
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir releases
|
||||
cargo build --release
|
||||
strip target/release/bliss-analyser
|
||||
cp target/release/bliss-analyser releases/bliss-analyser
|
||||
cp UserGuide.md releases/README.md
|
||||
cp LICENSE releases/
|
||||
cp configs/macos.ini releases/config.ini
|
||||
docker build -t bliss-analyser-cross - < docker/Dockerfile
|
||||
docker run --rm -v $PWD/target:/build -v $PWD:/src bliss-analyser-cross
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: bliss-analyser-mac
|
||||
name: bliss-analyser-linux-arm
|
||||
path: releases/
|
||||
|
||||
# macOs builds are failing: error: could not find system library 'libavutil' required by the 'ffmpeg-sys-next' crate
|
||||
# macOS:
|
||||
# runs-on: macos-11.0
|
||||
#
|
||||
# steps:
|
||||
# - name: Install Rust
|
||||
# uses: actions-rs/toolchain@v1
|
||||
# with:
|
||||
# toolchain: stable
|
||||
#
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v2
|
||||
#
|
||||
# - name: Install deps
|
||||
# run: |
|
||||
# brew install ffmpeg@5
|
||||
#
|
||||
# - name: Build
|
||||
# run: |
|
||||
# mkdir releases
|
||||
# cargo build --release
|
||||
# strip target/release/bliss-analyser
|
||||
# cp target/release/bliss-analyser releases/bliss-analyser
|
||||
# cp UserGuide.md releases/README.md
|
||||
# cp LICENSE releases/
|
||||
# cp configs/macos.ini releases/config.ini
|
||||
#
|
||||
# - name: Upload artifacts
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: bliss-analyser-mac
|
||||
# path: releases/
|
||||
|
||||
|
||||
Windows:
|
||||
runs-on: windows-2019
|
||||
|
||||
|
||||
Generated
+152
-290
@@ -58,7 +58,7 @@ version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
@@ -76,10 +76,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.59.2"
|
||||
name = "base64"
|
||||
version = "0.21.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
|
||||
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.64.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
@@ -92,6 +98,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 1.0.99",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -113,7 +120,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bliss-analyser"
|
||||
version = "0.0.2"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argparse",
|
||||
@@ -122,9 +129,11 @@ dependencies = [
|
||||
"configparser",
|
||||
"dirs",
|
||||
"env_logger",
|
||||
"if_chain",
|
||||
"indicatif",
|
||||
"lofty",
|
||||
"log",
|
||||
"num_cpus",
|
||||
"regex",
|
||||
"rusqlite",
|
||||
"substring",
|
||||
@@ -133,22 +142,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bliss-audio"
|
||||
version = "0.4.6"
|
||||
source = "git+https://github.com/Polochon-street/bliss-rs?rev=5f366b0#5f366b0d809886265ac54398a61523431b44f8a0"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6be62e7277f5bac99a77aec72d8861baea0431085868ad836fb06c434292386"
|
||||
dependencies = [
|
||||
"bliss-audio-aubio-rs",
|
||||
"crossbeam",
|
||||
"env_logger",
|
||||
"ffmpeg-next",
|
||||
"lazy_static",
|
||||
"ffmpeg-sys-next",
|
||||
"log",
|
||||
"ndarray",
|
||||
"ndarray-npy",
|
||||
"ndarray-stats",
|
||||
"noisy_float",
|
||||
"num_cpus",
|
||||
"rayon",
|
||||
"ripemd160",
|
||||
"rcue",
|
||||
"ripemd",
|
||||
"rustfft",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
@@ -157,50 +165,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bliss-audio-aubio-rs"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe01698d293ee91e334339d6436f17eac30d94bebaa668c5799c8206384dfeb1"
|
||||
checksum = "fb6b0b33b16bdb7489951c56294f4f9c321d91047ae29aebb91dfcd84a09086f"
|
||||
dependencies = [
|
||||
"bliss-audio-aubio-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bliss-audio-aubio-sys"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ef9fab7b922bdd057bb06fa2a2fa79d2a93bec3dd576320511cb3dfe21e78a9"
|
||||
checksum = "bd4d47e7b82164c30a806717cf5562f87e5b136b79b3d942c9ad789134116f2f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.7.3"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"byte-tools",
|
||||
"byteorder",
|
||||
"generic-array 0.12.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
|
||||
dependencies = [
|
||||
"byte-tools",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -209,12 +196,6 @@ version = "3.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
@@ -311,9 +292,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845"
|
||||
checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-channel",
|
||||
@@ -378,21 +359,23 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array 0.12.4",
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"generic-array 0.14.5",
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -431,12 +414,6 @@ dependencies = [
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
@@ -451,9 +428,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "ffmpeg-next"
|
||||
version = "5.0.2"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70a1fc87054b079ade0b081b023c5fef309cd4bbcb569c09eed2aa39b25a0a11"
|
||||
checksum = "8af03c47ad26832ab3aabc4cdbf210af3d3b878783edd5a7ba044ba33aab7a60"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"ffmpeg-sys-next",
|
||||
@@ -462,9 +439,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ffmpeg-sys-next"
|
||||
version = "5.0.1"
|
||||
version = "6.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba12dea33516e30c160ce557c7e43dd857276368eb1cd0eef4fce6529f2dee5"
|
||||
checksum = "cf650f461ccf130f4eef4927affed703cc387b183bfc4a7dfee86a076c131127"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
@@ -476,13 +453,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.22"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
|
||||
checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crc32fast",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
@@ -496,15 +471,6 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.5"
|
||||
@@ -563,12 +529,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@@ -579,6 +542,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
@@ -596,6 +565,12 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "if_chain"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.0"
|
||||
@@ -686,33 +661,36 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lofty"
|
||||
version = "0.5.3"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc751ba10f732cf5c522b9bc887027c9eee277512a38a3a0c4f1806bdcd730d"
|
||||
checksum = "8143c1ac799df98778738e48e403990dcae27c9843f89ae0bd79967ddd512448"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.2",
|
||||
"byteorder",
|
||||
"cfg-if",
|
||||
"flate2",
|
||||
"lofty_attr",
|
||||
"log",
|
||||
"ogg_pager",
|
||||
"once_cell",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
name = "lofty_attr"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
checksum = "764b60e1ddd07e5665a6a17636a95cd7d8f3b86c73503a69c32979d05f72f3cf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
name = "log"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
@@ -752,47 +730,32 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.4"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndarray"
|
||||
version = "0.15.4"
|
||||
version = "0.15.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dec23e6762830658d2b3d385a75aa212af2f67a4586d4442907144f3bb6a1ca8"
|
||||
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
|
||||
dependencies = [
|
||||
"matrixmultiply",
|
||||
"num-complex 0.4.0",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"rawpointer",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndarray-npy"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f85776816e34becd8bd9540818d7dc77bf28307f3b3dcc51cc82403c6931680c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"ndarray",
|
||||
"num-complex 0.4.0",
|
||||
"num-traits",
|
||||
"py_literal",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndarray-stats"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22877ad014bafa2f7dcfa5d556b0c7a52b0546cc98061a1ebef6d1834957b069"
|
||||
checksum = "af5a8477ac96877b5bd1fd67e0c28736c12943aba24eda92b127e036b0c8f400"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itertools",
|
||||
@@ -823,26 +786,6 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.0"
|
||||
@@ -873,11 +816,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.1"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.3.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -889,36 +832,24 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "ogg_pager"
|
||||
version = "0.3.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a846c4c0578b95d4568e6f48210dd6045e7a9572af2b020062d06b6dea049f4"
|
||||
checksum = "0d218a406e5de88e1c492d0162d569916f7436efe851ba5cc40a4bf4fa97cb40"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.9.0"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.6"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
|
||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
@@ -932,49 +863,6 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
dependencies = [
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
|
||||
dependencies = [
|
||||
"maplit",
|
||||
"pest",
|
||||
"sha-1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.24"
|
||||
@@ -989,40 +877,27 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "primal-check"
|
||||
version = "0.3.1"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01419cee72c1a1ca944554e23d83e483e1bccf378753344e881de28b5487511d"
|
||||
checksum = "9df7f93fd637f083201473dab4fee2db4c429d32e55e3299980ab3957ab916a0"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "py_literal"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "102df7a3d46db9d3891f178dcc826dc270a6746277a9ae6436f8d29fd490a8e1"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-complex 0.4.0",
|
||||
"num-traits",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.15"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1088,6 +963,12 @@ dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rcue"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fca1481d62f18158646de2ec552dd63f8bdc5be6448389b192ba95c939df997e"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
@@ -1138,14 +1019,12 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ripemd160"
|
||||
version = "0.9.1"
|
||||
name = "ripemd"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251"
|
||||
checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug 0.3.0",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1169,7 +1048,7 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.13.0",
|
||||
"blake2b_simd",
|
||||
"constant_time_eq",
|
||||
"crossbeam-utils",
|
||||
@@ -1183,16 +1062,17 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustfft"
|
||||
version = "5.1.1"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1869bb2a6ff77380d52ff4bc631f165637035a55855c76aa462c85474dadc42f"
|
||||
checksum = "e17d4f6cbdb180c9f4b2a26bbf01c4e647f1e1dea22fe8eb9db54198b32f9434"
|
||||
dependencies = [
|
||||
"num-complex 0.3.1",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"primal-check",
|
||||
"strength_reduce",
|
||||
"transpose",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1207,6 +1087,12 @@ dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@@ -1223,18 +1109,6 @@ dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
|
||||
dependencies = [
|
||||
"block-buffer 0.7.3",
|
||||
"digest 0.8.1",
|
||||
"fake-simd",
|
||||
"opaque-debug 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.1.0"
|
||||
@@ -1255,26 +1129,27 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "strength_reduce"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3ff2f71c82567c565ba4b3009a9350a96a7269eaa4001ebedae926230bc2254"
|
||||
checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.21.0"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2"
|
||||
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.21.1"
|
||||
version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec"
|
||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"rustversion",
|
||||
"syn 1.0.99",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1288,13 +1163,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.86"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1318,22 +1204,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.30"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1378,18 +1264,18 @@ version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.19"
|
||||
@@ -1399,18 +1285,6 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
@@ -1423,7 +1297,7 @@ version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9399fa2f927a3d327187cbd201480cee55bee6ac5d3c77dd27f0c6814cff16d5"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.13.0",
|
||||
"chunked_transfer",
|
||||
"flate2",
|
||||
"log",
|
||||
@@ -1491,7 +1365,7 @@ dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.99",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -1513,7 +1387,7 @@ checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.99",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -1583,15 +1457,3 @@ name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
"thiserror",
|
||||
]
|
||||
+6
-4
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "bliss-analyser"
|
||||
version = "0.0.2"
|
||||
version = "0.2.2"
|
||||
authors = ["Craig Drummond <craig.p.drummond@gmail.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
description = "Analyse audio files with bliss-rs"
|
||||
repository = "https://github.com/CDrummond/bliss-analyser"
|
||||
@@ -10,17 +10,19 @@ keywords = ["audio", "song", "similarity"]
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
bliss-audio = { git = "https://github.com/Polochon-street/bliss-rs", rev = "5f366b0" }
|
||||
bliss-audio = { version = "0.6.9", features = [] }
|
||||
argparse = "0.2.2"
|
||||
anyhow = "1.0.40"
|
||||
rusqlite = { version = "0.25.0", features = ["bundled"] }
|
||||
log = "0.4.14"
|
||||
env_logger = "0.8.4"
|
||||
indicatif = "0.16.2"
|
||||
lofty = "0.5.3"
|
||||
lofty = "0.15.0"
|
||||
dirs = "1"
|
||||
chrono = "0.4.19"
|
||||
regex = "1"
|
||||
substring = "1.4.5"
|
||||
ureq = "2.4.0"
|
||||
configparser = "3.0.0"
|
||||
if_chain = "1.0.2"
|
||||
num_cpus = "1.13.0"
|
||||
@@ -1,3 +1,36 @@
|
||||
0.2.3
|
||||
-----
|
||||
1. Add option to limit number of concurrent threads.
|
||||
2. Update version of tag reader library.
|
||||
3. Update version of bliss library.
|
||||
|
||||
0.2.2
|
||||
-----
|
||||
1. Update version of tag reader library.
|
||||
2. Update version of bliss library.
|
||||
|
||||
0.2.1
|
||||
-----
|
||||
1. Update version of tag reader library.
|
||||
2. Fix checking if CUE already analysed.
|
||||
|
||||
0.2.0
|
||||
-----
|
||||
1. Tidy up code, thanks to Serial-ATA
|
||||
2. Update version of tag reader library, should now support ID3v2 in FLAC.
|
||||
3. Show error message if can't open, or create, database file.
|
||||
4. Update version of bliss-rs, this now handles CUE processing internally.
|
||||
|
||||
0.1.0
|
||||
-----
|
||||
1. Add support for analysing CUE files.
|
||||
2. Output list of (up to 100) tracks that failed to analyse.
|
||||
3. When performing a dry-run analysis (--dry-run) print paths of all tracks to
|
||||
be analysed and to be removed.
|
||||
4. Use git version of tag reader library.
|
||||
5. Support up to 5 music folders (music, music_1, music_2, music_3, and
|
||||
music_4).
|
||||
|
||||
0.0.2
|
||||
-----
|
||||
1. Package vcruntime140.dll with Windows ZIP.
|
||||
|
||||
@@ -16,6 +16,11 @@ To install dependencies on a Debian system:
|
||||
apt install -y clang libavcodec-dev libavformat-dev libavutil-dev libavfilter-dev libavdevice-dev pkg-config
|
||||
```
|
||||
|
||||
To install dependencies on a Fedora system:
|
||||
```
|
||||
dnf install ffmpeg-devel clang pkg-config
|
||||
```
|
||||
|
||||
Build with `cargo build --release`
|
||||
|
||||
|
||||
|
||||
+18
-2
@@ -6,6 +6,8 @@ upload its database of music analysis to LMS. The `Bliss Mixer` LMS plugin can
|
||||
then use this information to provide music mixes for LMS's `Don't Stop the Music`
|
||||
feature.
|
||||
|
||||
**NOTE:** You must run this application from a terminal window (e.g. cmd.com or
|
||||
PowerShell for Windows), as there is no graphical user interface.
|
||||
|
||||
|
||||
Quick guide
|
||||
@@ -102,11 +104,13 @@ ignore=ignore.txt
|
||||
|
||||
The following items are supported:
|
||||
* `music` specifies the location of your music collection - e.g. `c:\Users\user\Music`
|
||||
for windows. This default to `Music` within the user's home folder.
|
||||
for windows. This default to `Music` within the user's home folder. Up to 4 other
|
||||
music folders may be specified via `music_1`, `music_2`, `music_3`, and `music_4`
|
||||
* `db` specifies the name and location of the database file used to store the
|
||||
analysis results. This will default to `bliss.db` in the current folder.
|
||||
* `lms` specifies the hostname, or IP address, of your LMS server. This is used
|
||||
when uploading the database file to LMS. This defaults to `127.0.0.1`
|
||||
when uploading the database file to LMS. This defaults to `127.0.0.1` If your LMS is
|
||||
password protected then use `user:pass@server` - e.g. `lms=pi:abc123@127.0.0.1`
|
||||
* `ignore` specifies the name and location of a file containing items to ignore
|
||||
in mixes. See the `Ignore` section later on for more details.
|
||||
|
||||
@@ -174,6 +178,14 @@ As a rough guide, a 2015-era i7 8-core laptop with SSD analyses around 14000
|
||||
tracks/hour.
|
||||
|
||||
|
||||
CUE files
|
||||
---------
|
||||
|
||||
If the anlyser encounters an audio file with a matching CUE file (e.g.
|
||||
`album.flac` and `album.cue` in same folder) then it will attempt to analyse the
|
||||
individual tracks contained within.
|
||||
|
||||
|
||||
Exclude folders
|
||||
---------------
|
||||
|
||||
@@ -244,6 +256,10 @@ entries):
|
||||
.\bliss-analyser.exe tags
|
||||
```
|
||||
|
||||
*NOTE* Tag re-reading is not implemented for CUE tracks. Therefore if you update
|
||||
your CUE files you will need to remove these from the database and re-analyse,
|
||||
or manually edit the meta-data in `bliss.db` using an SQLite GUI.
|
||||
|
||||
|
||||
|
||||
Ignoring tracks in mixes
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Cross compilation environment for bliss-mixer
|
||||
|
||||
FROM debian:bullseye
|
||||
|
||||
RUN dpkg --add-architecture arm64 && \
|
||||
dpkg --add-architecture armhf
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get install -y curl git pkg-config yasm
|
||||
RUN apt-get install -y build-essential clang
|
||||
RUN apt-get install -y crossbuild-essential-armhf crossbuild-essential-arm64
|
||||
|
||||
RUN apt-get install -y libavutil-dev:arm64 libavcodec-dev:arm64 libavformat-dev:arm64 \
|
||||
libavfilter-dev:arm64 libavdevice-dev:arm64 libswresample-dev:arm64 libfftw3-dev:arm64
|
||||
|
||||
RUN apt-get install -y libavutil-dev:armhf libavcodec-dev:armhf libavformat-dev:armhf \
|
||||
libavfilter-dev:armhf libavdevice-dev:armhf libswresample-dev:armhf libfftw3-dev:armhf
|
||||
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin/:${PATH}"
|
||||
RUN rustup target add aarch64-unknown-linux-gnu && \
|
||||
rustup target add arm-unknown-linux-gnueabihf
|
||||
|
||||
RUN mkdir /.cargo && \
|
||||
echo '[target.aarch64-unknown-linux-gnu]\nlinker = "aarch64-linux-gnu-gcc"' > /.cargo/config && \
|
||||
echo '[target.arm-unknown-linux-gnueabihf]\nlinker = "arm-linux-gnueabihf-gcc"' >> /.cargo/config
|
||||
|
||||
RUN mkdir /build
|
||||
ENV CARGO_TARGET_DIR /build
|
||||
ENV CARGO_HOME /build/cache
|
||||
|
||||
RUN mkdir /src
|
||||
|
||||
WORKDIR /src
|
||||
CMD ["/src/docker/docker-build.sh"]
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
## #!/usr/bin/env bash
|
||||
set -eux
|
||||
|
||||
uname -a
|
||||
DESTDIR=/src/releases
|
||||
|
||||
mkdir -p $DESTDIR/bin
|
||||
rm -rf $DESTDIR/bin/*
|
||||
|
||||
function build {
|
||||
echo Building for $1 to $3...
|
||||
|
||||
if [[ ! -f /build/$1/release/bliss-analyser ]]; then
|
||||
export RUST_BACKTRACE=full
|
||||
export PKG_CONFIG=${1//unknown-/}-pkg-config
|
||||
BINDGEN_EXTRA_CLANG_ARGS="--sysroot /usr/${1//unknown-/}" cargo build --release --target $1
|
||||
fi
|
||||
|
||||
$2 /build/$1/release/bliss-analyser && cp /build/$1/release/bliss-analyser $DESTDIR/$3
|
||||
}
|
||||
|
||||
build arm-unknown-linux-gnueabihf arm-linux-gnueabihf-strip bin/bliss-analyser-armhf
|
||||
build aarch64-unknown-linux-gnu aarch64-linux-gnu-strip bin/bliss-analyser-aarch64
|
||||
|
||||
cp UserGuide.md $DESTDIR/README.md
|
||||
cp LICENSE $DESTDIR/
|
||||
cp configs/linux.ini $DESTDIR/config.ini
|
||||
cp scripts/bliss-analyser-arm $DESTDIR/bliss-analyser
|
||||
+22
-4
@@ -3,15 +3,17 @@
|
||||
#
|
||||
# LMS-BlissMixer
|
||||
#
|
||||
# Copyright (c) 2022 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
# Copyright (c) 2022-2023 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
# MIT license.
|
||||
#
|
||||
|
||||
import datetime, os, requests, sys, time
|
||||
import datetime, os, requests, shutil, subprocess, sys, tempfile, time
|
||||
|
||||
GITHUB_TOKEN_FILE = "%s/.config/github-token" % os.path.expanduser('~')
|
||||
GITHUB_REPO = "CDrummond/bliss-analyser"
|
||||
GITHUB_ARTIFACTS = ["bliss-analyser-linux", "bliss-analyser-mac", "bliss-analyser-windows"]
|
||||
#UNIX_ARTIFICATS = ["bliss-analyser-linux-x86-ffmpeg4", "bliss-analyser-linux-x86-ffmpeg5", "bliss-analyser-linux-arm", "bliss-analyser-mac"]
|
||||
UNIX_ARTIFACTS = ["bliss-analyser-linux-x86-ffmpeg4", "bliss-analyser-linux-arm"]
|
||||
GITHUB_ARTIFACTS = UNIX_ARTIFACTS + ["bliss-analyser-windows"]
|
||||
|
||||
def info(s):
|
||||
print("INFO: %s" %s)
|
||||
@@ -67,6 +69,22 @@ def download_artifacts(version):
|
||||
break
|
||||
|
||||
|
||||
def make_executable(version):
|
||||
cwd = os.getcwd()
|
||||
for a in UNIX_ARTIFACTS:
|
||||
archive = "%s-%s.zip" % (a, version)
|
||||
info("Making analyser executable in %s" % archive)
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
subprocess.call(["unzip", archive, "-d", td], shell=False)
|
||||
os.remove(archive)
|
||||
os.chdir(td)
|
||||
subprocess.call(["chmod", "a+x", "%s/bliss-analyser" % td], shell=False)
|
||||
if a == "bliss-analyser-linux-arm":
|
||||
subprocess.call(["chmod", "a+x", "%s/bin/bliss-analyser-armhf" % td, "%s/bin/bliss-analyser-aarch64" % td], shell=False)
|
||||
shutil.make_archive("%s/%s-%s" % (cwd, a, version), "zip")
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
def checkVersion(version):
|
||||
try:
|
||||
parts=version.split('.')
|
||||
@@ -85,4 +103,4 @@ if version!="test":
|
||||
checkVersion(version)
|
||||
|
||||
download_artifacts(version)
|
||||
|
||||
make_executable(version)
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
ARCH=`arch`
|
||||
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
|
||||
|
||||
if [ "$ARCH" = "aarch64" ] ; then
|
||||
$SCRIPT_DIR/bin/bliss-analyser-aarch64 $*
|
||||
else
|
||||
$SCRIPT_DIR/bin/bliss-analyser-armhf $*
|
||||
fi
|
||||
+213
-110
@@ -1,166 +1,268 @@
|
||||
/**
|
||||
* Analyse music with Bliss
|
||||
*
|
||||
* Copyright (c) 2022 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* Copyright (c) 2022-2023 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* GPLv3 license.
|
||||
*
|
||||
**/
|
||||
|
||||
use anyhow::{Result};
|
||||
use bliss_audio::{library::analyze_paths_streaming};
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::PathBuf;
|
||||
use crate::db;
|
||||
use crate::tags;
|
||||
use anyhow::Result;
|
||||
use bliss_audio::{analyze_paths_with_cores};
|
||||
use if_chain::if_chain;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryInto;
|
||||
use std::fs::{DirEntry, File};
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::{Path, PathBuf};
|
||||
use num_cpus;
|
||||
|
||||
const DONT_ANALYSE:&str = ".notmusic";
|
||||
const MAX_TAG_ERRORS_TO_SHOW:usize = 25;
|
||||
const DONT_ANALYSE: &str = ".notmusic";
|
||||
const MAX_ERRORS_TO_SHOW: usize = 100;
|
||||
const MAX_TAG_ERRORS_TO_SHOW: usize = 50;
|
||||
const VALID_EXTENSIONS: [&str; 5] = ["m4a", "mp3", "ogg", "flac", "opus"];
|
||||
|
||||
fn get_file_list(db:&mut db::Db, mpath:&PathBuf, path:&PathBuf, track_paths:&mut Vec<String>) {
|
||||
if path.is_dir() {
|
||||
match path.read_dir() {
|
||||
Ok(items) => {
|
||||
for item in items {
|
||||
match item {
|
||||
Ok(entry) => {
|
||||
let pb = entry.path().to_path_buf();
|
||||
if entry.path().is_dir() {
|
||||
let mut check = pb.clone();
|
||||
check.push(PathBuf::from(DONT_ANALYSE));
|
||||
if check.exists() {
|
||||
log::info!("Skipping '{}', found '{}'", pb.to_string_lossy(), DONT_ANALYSE);
|
||||
} else {
|
||||
get_file_list(db, mpath, &entry.path(), track_paths);
|
||||
}
|
||||
} else if entry.path().is_file() {
|
||||
let e = pb.extension();
|
||||
if e.is_some() {
|
||||
let ext = e.unwrap().to_string_lossy();
|
||||
if ext=="m4a" || ext=="mp3" || ext=="ogg" || ext=="flac" || ext=="opus" {
|
||||
match pb.strip_prefix(mpath) {
|
||||
Ok(stripped) => {
|
||||
let mut cue = pb.clone();
|
||||
cue.set_extension("cue");
|
||||
if cue.exists() {
|
||||
log::warn!("Found CUE album '{}' - not currently handled!", pb.to_string_lossy());
|
||||
} else {
|
||||
let spb = stripped.to_path_buf();
|
||||
let sname = String::from(spb.to_string_lossy());
|
||||
match db.get_rowid(&sname) {
|
||||
Ok(id) => {
|
||||
if id<=0 {
|
||||
track_paths.push(String::from(pb.to_string_lossy()));
|
||||
}
|
||||
},
|
||||
Err(_) => { }
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => { }
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => { }
|
||||
fn get_file_list(db: &mut db::Db, mpath: &Path, path: &Path, track_paths: &mut Vec<String>) {
|
||||
if !path.is_dir() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Ok(items) = path.read_dir() {
|
||||
for item in items {
|
||||
if let Ok(entry) = item {
|
||||
check_dir_entry(db, mpath, entry, track_paths);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyse_new_files(db:&db::Db, mpath: &PathBuf, track_paths:Vec<String>) -> Result<()> {
|
||||
fn check_dir_entry(db: &mut db::Db, mpath: &Path, entry: DirEntry, track_paths: &mut Vec<String>) {
|
||||
let pb = entry.path();
|
||||
if pb.is_dir() {
|
||||
let check = pb.join(DONT_ANALYSE);
|
||||
if check.exists() {
|
||||
log::info!("Skipping '{}', found '{}'", pb.to_string_lossy(), DONT_ANALYSE);
|
||||
} else {
|
||||
get_file_list(db, mpath, &pb, track_paths);
|
||||
}
|
||||
} else if pb.is_file() {
|
||||
if_chain! {
|
||||
if let Some(ext) = pb.extension();
|
||||
let ext = ext.to_string_lossy();
|
||||
if VALID_EXTENSIONS.contains(&&*ext);
|
||||
if let Ok(stripped) = pb.strip_prefix(mpath);
|
||||
then {
|
||||
let sname = String::from(stripped.to_string_lossy());
|
||||
let mut cue_file = pb.clone();
|
||||
cue_file.set_extension("cue");
|
||||
if cue_file.exists() {
|
||||
// For cue files, check if first track is in DB
|
||||
let mut cue_track_path = pb.clone();
|
||||
let ext = pb.extension().unwrap().to_string_lossy();
|
||||
cue_track_path.set_extension(format!("{}{}1", ext, db::CUE_MARKER));
|
||||
if let Ok(cue_track_stripped) = cue_track_path.strip_prefix(mpath) {
|
||||
let cue_track_sname = String::from(cue_track_stripped.to_string_lossy());
|
||||
if let Ok(id) = db.get_rowid(&cue_track_sname) {
|
||||
if id<=0 {
|
||||
track_paths.push(String::from(cue_file.to_string_lossy()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Ok(id) = db.get_rowid(&sname) {
|
||||
if id<=0 {
|
||||
track_paths.push(String::from(pb.to_string_lossy()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>, max_threads: usize) -> Result<()> {
|
||||
let total = track_paths.len();
|
||||
let pb = ProgressBar::new(total.try_into().unwrap());
|
||||
let style = ProgressStyle::default_bar()
|
||||
.template("[{elapsed_precise}] [{bar:25}] {percent:>3}% {pos:>6}/{len:6} {wide_msg}")
|
||||
.progress_chars("=> ");
|
||||
pb.set_style(style);
|
||||
let progress = ProgressBar::new(total.try_into().unwrap()).with_style(
|
||||
ProgressStyle::default_bar()
|
||||
.template("[{elapsed_precise}] [{bar:25}] {percent:>3}% {pos:>6}/{len:6} {wide_msg}")
|
||||
.progress_chars("=> "),
|
||||
);
|
||||
let cpu_threads: NonZeroUsize = match max_threads {
|
||||
0 => NonZeroUsize::new(num_cpus::get()).unwrap(),
|
||||
_ => NonZeroUsize::new(max_threads).unwrap(),
|
||||
};
|
||||
|
||||
let results = analyze_paths_streaming(track_paths)?;
|
||||
let mut analysed = 0;
|
||||
let mut failed = 0;
|
||||
let mut tag_error:Vec<String> = Vec::new();
|
||||
let mut failed: Vec<String> = Vec::new();
|
||||
let mut tag_error: Vec<String> = Vec::new();
|
||||
let mut reported_cue:HashSet<String> = HashSet::new();
|
||||
|
||||
log::info!("Analysing new tracks");
|
||||
for (path, result) in results {
|
||||
let pbuff = PathBuf::from(&path);
|
||||
let stripped = pbuff.strip_prefix(mpath).unwrap();
|
||||
log::info!("Analysing new files");
|
||||
for (path, result) in analyze_paths_with_cores(track_paths, cpu_threads) {
|
||||
let stripped = path.strip_prefix(mpath).unwrap();
|
||||
let spbuff = stripped.to_path_buf();
|
||||
let sname = String::from(spbuff.to_string_lossy());
|
||||
pb.set_message(format!("{}", sname));
|
||||
progress.set_message(format!("{}", sname));
|
||||
let mut inc_progress = true; // Only want to increment progress once for cue tracks
|
||||
match result {
|
||||
Ok(track) => {
|
||||
let cpath = String::from(path);
|
||||
let meta = tags::read(&cpath);
|
||||
if meta.title.is_empty() && meta.artist.is_empty() && meta.album.is_empty() && meta.genre.is_empty() {
|
||||
tag_error.push(sname.clone());
|
||||
}
|
||||
let cpath = String::from(path.to_string_lossy());
|
||||
match track.cue_info {
|
||||
Some(cue) => {
|
||||
match track.track_number {
|
||||
Some(track_num) => {
|
||||
let t_num = track_num.parse::<i32>().unwrap();
|
||||
if reported_cue.contains(&cpath) {
|
||||
inc_progress = false;
|
||||
} else {
|
||||
analysed += 1;
|
||||
reported_cue.insert(cpath);
|
||||
}
|
||||
let meta = db::Metadata {
|
||||
title: track.title.unwrap_or_default().to_string(),
|
||||
artist: track.artist.unwrap_or_default().to_string(),
|
||||
album: track.album.unwrap_or_default().to_string(),
|
||||
album_artist: track.album_artist.unwrap_or_default().to_string(),
|
||||
genre: track.genre.unwrap_or_default().to_string(),
|
||||
duration: track.duration.as_secs() as u32
|
||||
};
|
||||
|
||||
db.add_track(&sname, &meta, &track.analysis);
|
||||
analysed += 1;
|
||||
},
|
||||
Err(_) => {
|
||||
failed += 1;
|
||||
// Remove prefix from audio_file_path
|
||||
let pbuff = PathBuf::from(&cue.audio_file_path);
|
||||
let stripped = pbuff.strip_prefix(mpath).unwrap();
|
||||
let spbuff = stripped.to_path_buf();
|
||||
let sname = String::from(spbuff.to_string_lossy());
|
||||
|
||||
let db_path = format!("{}{}{}", sname, db::CUE_MARKER, t_num);
|
||||
db.add_track(&db_path, &meta, &track.analysis);
|
||||
}
|
||||
None => { failed.push(format!("{} - No track number?", sname)); }
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Use lofty to read tags here, and not bliss's, so that if update
|
||||
// tags is ever used they are from the same source.
|
||||
let mut meta = tags::read(&cpath);
|
||||
if meta.is_empty() {
|
||||
// Lofty failed? Try from bliss...
|
||||
meta.title = track.title.unwrap_or_default().to_string();
|
||||
meta.artist = track.artist.unwrap_or_default().to_string();
|
||||
meta.album = track.album.unwrap_or_default().to_string();
|
||||
meta.album_artist = track.album_artist.unwrap_or_default().to_string();
|
||||
meta.genre = track.genre.unwrap_or_default().to_string();
|
||||
meta.duration = track.duration.as_secs() as u32;
|
||||
}
|
||||
if meta.is_empty() {
|
||||
tag_error.push(sname.clone());
|
||||
}
|
||||
db.add_track(&sname, &meta, &track.analysis);
|
||||
analysed += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => { failed.push(format!("{} - {}", sname, e)); }
|
||||
};
|
||||
pb.inc(1);
|
||||
|
||||
if inc_progress {
|
||||
progress.inc(1);
|
||||
}
|
||||
}
|
||||
|
||||
progress.finish_with_message("Finished!");
|
||||
log::info!("{} Analysed. {} Failure(s).", analysed, failed.len());
|
||||
if !failed.is_empty() {
|
||||
let total = failed.len();
|
||||
failed.truncate(MAX_ERRORS_TO_SHOW);
|
||||
|
||||
log::error!("Failed to analyse the following file(s):");
|
||||
for err in failed {
|
||||
log::error!(" {}", err);
|
||||
}
|
||||
if total > MAX_ERRORS_TO_SHOW {
|
||||
log::error!(" + {} other(s)", total - MAX_ERRORS_TO_SHOW);
|
||||
}
|
||||
}
|
||||
pb.finish_with_message(format!("{} Analysed. {} Failure(s).", analysed, failed));
|
||||
if !tag_error.is_empty() {
|
||||
let total = tag_error.len();
|
||||
tag_error.truncate(MAX_TAG_ERRORS_TO_SHOW);
|
||||
|
||||
log::error!("Failed to read tags of the folling track(s):");
|
||||
log::error!("Failed to read tags of the following file(s):");
|
||||
for err in tag_error {
|
||||
log::error!(" {}", err);
|
||||
}
|
||||
if total>MAX_TAG_ERRORS_TO_SHOW {
|
||||
if total > MAX_TAG_ERRORS_TO_SHOW {
|
||||
log::error!(" + {} other(s)", total - MAX_TAG_ERRORS_TO_SHOW);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn analyse_files(db_path: &str, mpath: &PathBuf, dry_run:bool, keep_old:bool, max_num_tracks:usize) {
|
||||
let mut track_paths:Vec<String> = Vec::new();
|
||||
pub fn analyse_files(db_path: &str, mpaths: &Vec<PathBuf>, dry_run: bool, keep_old: bool, max_num_tracks: usize, max_threads: usize) {
|
||||
let mut db = db::Db::new(&String::from(db_path));
|
||||
let cur = PathBuf::from(mpath);
|
||||
let mut track_count_left = max_num_tracks;
|
||||
|
||||
db.init();
|
||||
log::info!("Looking for new tracks");
|
||||
get_file_list(&mut db, mpath, &cur, &mut track_paths);
|
||||
log::info!("Num new tracks: {}", track_paths.len());
|
||||
if !dry_run && max_num_tracks>0 && track_paths.len()>max_num_tracks {
|
||||
log::info!("Only analysing {} tracks", max_num_tracks);
|
||||
track_paths.truncate(max_num_tracks);
|
||||
}
|
||||
|
||||
if !keep_old {
|
||||
db.remove_old(mpath, dry_run);
|
||||
db.remove_old(mpaths, dry_run);
|
||||
}
|
||||
if !dry_run {
|
||||
if track_paths.len()>0 {
|
||||
match analyse_new_files(&db, mpath, track_paths) {
|
||||
Ok(_) => { },
|
||||
Err(_) => { }
|
||||
|
||||
for path in mpaths {
|
||||
let mpath = path.clone();
|
||||
let cur = path.clone();
|
||||
let mut track_paths: Vec<String> = Vec::new();
|
||||
|
||||
if mpaths.len() > 1 {
|
||||
log::info!("Looking for new files in {}", mpath.to_string_lossy());
|
||||
} else {
|
||||
log::info!("Looking for new files");
|
||||
}
|
||||
get_file_list(&mut db, &mpath, &cur, &mut track_paths);
|
||||
track_paths.sort();
|
||||
log::info!("Num new files: {}", track_paths.len());
|
||||
|
||||
if dry_run {
|
||||
if !track_paths.is_empty() {
|
||||
log::info!("The following need to be analysed:");
|
||||
for track in track_paths {
|
||||
log::info!(" {}", track);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::info!("No new tracks to analyse");
|
||||
if max_num_tracks > 0 {
|
||||
if track_paths.len() > track_count_left {
|
||||
log::info!("Only analysing {} files", track_count_left);
|
||||
track_paths.truncate(track_count_left);
|
||||
}
|
||||
track_count_left -= track_paths.len();
|
||||
}
|
||||
|
||||
if !track_paths.is_empty() {
|
||||
match analyse_new_files(&db, &mpath, track_paths, max_threads) {
|
||||
Ok(_) => { }
|
||||
Err(e) => { log::error!("Analysis returned error: {}", e); }
|
||||
}
|
||||
} else {
|
||||
log::info!("No new files to analyse");
|
||||
}
|
||||
|
||||
if max_num_tracks > 0 && track_count_left <= 0 {
|
||||
log::info!("File limit reached");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
pub fn read_tags(db_path: &str, mpath: &PathBuf) {
|
||||
pub fn read_tags(db_path: &str, mpaths: &Vec<PathBuf>) {
|
||||
let db = db::Db::new(&String::from(db_path));
|
||||
db.init();
|
||||
db.update_tags(&mpath);
|
||||
db.update_tags(&mpaths);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@@ -171,11 +273,12 @@ pub fn update_ignore(db_path: &str, ignore_path: &PathBuf) {
|
||||
db.init();
|
||||
|
||||
db.clear_ignore();
|
||||
for (_index, line) in reader.lines().enumerate() {
|
||||
let line = line.unwrap();
|
||||
let mut lines = reader.lines();
|
||||
while let Some(Ok(line)) = lines.next() {
|
||||
if !line.is_empty() && !line.starts_with("#") {
|
||||
db.set_ignore(&line);
|
||||
}
|
||||
}
|
||||
|
||||
db.close();
|
||||
}
|
||||
@@ -1,52 +1,73 @@
|
||||
/**
|
||||
* Analyse music with Bliss
|
||||
*
|
||||
* Copyright (c) 2022 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* Copyright (c) 2022-2023 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* GPLv3 license.
|
||||
*
|
||||
**/
|
||||
|
||||
use crate::tags;
|
||||
use bliss_audio::{Analysis, AnalysisIndex};
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use rusqlite::{Connection, params};
|
||||
use rusqlite::{params, Connection};
|
||||
use std::convert::TryInto;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
use crate::tags;
|
||||
|
||||
pub const CUE_MARKER: &str = ".CUE_TRACK.";
|
||||
|
||||
pub struct FileMetadata {
|
||||
pub rowid:usize,
|
||||
pub file:String,
|
||||
pub title:Option<String>,
|
||||
pub artist:Option<String>,
|
||||
pub album_artist:Option<String>,
|
||||
pub album:Option<String>,
|
||||
pub genre:Option<String>,
|
||||
pub duration:u32
|
||||
pub rowid: usize,
|
||||
pub file: String,
|
||||
pub title: Option<String>,
|
||||
pub artist: Option<String>,
|
||||
pub album_artist: Option<String>,
|
||||
pub album: Option<String>,
|
||||
pub genre: Option<String>,
|
||||
pub duration: u32,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq)]
|
||||
pub struct Metadata {
|
||||
pub title:String,
|
||||
pub artist:String,
|
||||
pub album_artist:String,
|
||||
pub album:String,
|
||||
pub genre:String,
|
||||
pub duration:u32
|
||||
pub title: String,
|
||||
pub artist: String,
|
||||
pub album_artist: String,
|
||||
pub album: String,
|
||||
pub genre: String,
|
||||
pub duration: u32,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.title.is_empty()
|
||||
&& self.artist.is_empty()
|
||||
&& self.album_artist.is_empty()
|
||||
&& self.album.is_empty()
|
||||
&& self.genre.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Db {
|
||||
pub conn: Connection
|
||||
pub conn: Connection,
|
||||
}
|
||||
|
||||
impl Db {
|
||||
pub fn new(path: &String) -> Self {
|
||||
Self {
|
||||
conn: Connection::open(path).unwrap(),
|
||||
match Connection::open(path) {
|
||||
Ok(conn) => {
|
||||
Self {
|
||||
conn: conn,
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed top open/create database. {}", e);
|
||||
process::exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&self) {
|
||||
match self.conn.execute(
|
||||
let cmd = self.conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS Tracks (
|
||||
File text primary key,
|
||||
Title text,
|
||||
@@ -76,39 +97,35 @@ impl Db {
|
||||
Chroma8 real,
|
||||
Chroma9 real,
|
||||
Chroma10 real
|
||||
);",[]) {
|
||||
Ok(_) => { },
|
||||
Err(_) => {
|
||||
log::error!("Failed to create DB table");
|
||||
process::exit(-1);
|
||||
}
|
||||
);",
|
||||
[],
|
||||
);
|
||||
|
||||
if cmd.is_err() {
|
||||
log::error!("Failed to create DB table");
|
||||
process::exit(-1);
|
||||
}
|
||||
match self.conn.execute("CREATE UNIQUE INDEX IF NOT EXISTS Tracks_idx ON Tracks(File)", []) {
|
||||
Ok(_) => { },
|
||||
Err(_) => {
|
||||
log::error!("Failed to create DB index");
|
||||
process::exit(-1);
|
||||
}
|
||||
|
||||
let cmd = self.conn.execute("CREATE UNIQUE INDEX IF NOT EXISTS Tracks_idx ON Tracks(File)", []);
|
||||
|
||||
if cmd.is_err() {
|
||||
log::error!("Failed to create DB index");
|
||||
process::exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(self) {
|
||||
match self.conn.close() {
|
||||
Ok(_) => { },
|
||||
Err(_) => { }
|
||||
}
|
||||
let _ = self.conn.close();
|
||||
}
|
||||
|
||||
pub fn get_rowid(&self, path: &String) -> Result<usize, rusqlite::Error> {
|
||||
let mut db_path = path.clone();
|
||||
pub fn get_rowid(&self, path: &str) -> Result<usize, rusqlite::Error> {
|
||||
let mut db_path = path.to_string();
|
||||
if cfg!(windows) {
|
||||
db_path = db_path.replace("\\", "/");
|
||||
}
|
||||
let mut stmt = self.conn.prepare("SELECT rowid FROM Tracks WHERE File=:path;")?;
|
||||
let track_iter = stmt.query_map(&[(":path", &db_path)], |row| {
|
||||
Ok(row.get(0)?)
|
||||
}).unwrap();
|
||||
let mut rowid:usize = 0;
|
||||
let track_iter = stmt.query_map(&[(":path", &db_path)], |row| Ok(row.get(0)?)).unwrap();
|
||||
let mut rowid: usize = 0;
|
||||
for tr in track_iter {
|
||||
rowid = tr.unwrap();
|
||||
break;
|
||||
@@ -116,82 +133,103 @@ impl Db {
|
||||
Ok(rowid)
|
||||
}
|
||||
|
||||
pub fn add_track(&self, path: &String, meta: &Metadata, analysis:&Analysis) {
|
||||
pub fn add_track(&self, path: &String, meta: &Metadata, analysis: &Analysis) {
|
||||
let mut db_path = path.clone();
|
||||
if cfg!(windows) {
|
||||
db_path = db_path.replace("\\", "/");
|
||||
}
|
||||
match self.get_rowid(&path) {
|
||||
Ok(id) => {
|
||||
if id<=0 {
|
||||
if id <= 0 {
|
||||
match self.conn.execute("INSERT INTO Tracks (File, Title, Artist, AlbumArtist, Album, Genre, Duration, Ignore, Tempo, Zcr, MeanSpectralCentroid, StdDevSpectralCentroid, MeanSpectralRolloff, StdDevSpectralRolloff, MeanSpectralFlatness, StdDevSpectralFlatness, MeanLoudness, StdDevLoudness, Chroma1, Chroma2, Chroma3, Chroma4, Chroma5, Chroma6, Chroma7, Chroma8, Chroma9, Chroma10) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
|
||||
params![db_path, meta.title, meta.artist, meta.album_artist, meta.album, meta.genre, meta.duration, 0,
|
||||
analysis[AnalysisIndex::Tempo], analysis[AnalysisIndex::Zcr], analysis[AnalysisIndex::MeanSpectralCentroid], analysis[AnalysisIndex::StdDeviationSpectralCentroid], analysis[AnalysisIndex::MeanSpectralRolloff],
|
||||
analysis[AnalysisIndex::StdDeviationSpectralRolloff], analysis[AnalysisIndex::MeanSpectralFlatness], analysis[AnalysisIndex::StdDeviationSpectralFlatness], analysis[AnalysisIndex::MeanLoudness], analysis[AnalysisIndex::StdDeviationLoudness],
|
||||
analysis[AnalysisIndex::Chroma1], analysis[AnalysisIndex::Chroma2], analysis[AnalysisIndex::Chroma3], analysis[AnalysisIndex::Chroma4], analysis[AnalysisIndex::Chroma5],
|
||||
analysis[AnalysisIndex::Chroma6], analysis[AnalysisIndex::Chroma7], analysis[AnalysisIndex::Chroma8], analysis[AnalysisIndex::Chroma9], analysis[AnalysisIndex::Chroma10]]) {
|
||||
Ok(_) => { },
|
||||
Ok(_) => { }
|
||||
Err(e) => { log::error!("Failed to insert '{}' into database. {}", path, e); }
|
||||
}
|
||||
} else {
|
||||
match self.conn.execute("UPDATE Tracks SET Title=?, Artist=?, AlbumArtist=?, Album=?, Genre=?, Duration=?, Tempo=?, Zcr=?, MeanSpectralCentroid=?, StdDevSpectralCentroid=?, MeanSpectralRolloff=?, StdDevSpectralRolloff=?, MeanSpectralFlatness=?, StdDevSpectralFlatness=?, MeanLoudness=?, StdDevLoudness=?, Chroma1=?, Chroma2=?, Chroma3=?, Chroma4=?, Chroma5=?, Chroma6=?, Chroma7=?, Chroma8=?, Chroma9=?, Chroma10=? WHERE rowid=?);",
|
||||
match self.conn.execute("UPDATE Tracks SET Title=?, Artist=?, AlbumArtist=?, Album=?, Genre=?, Duration=?, Tempo=?, Zcr=?, MeanSpectralCentroid=?, StdDevSpectralCentroid=?, MeanSpectralRolloff=?, StdDevSpectralRolloff=?, MeanSpectralFlatness=?, StdDevSpectralFlatness=?, MeanLoudness=?, StdDevLoudness=?, Chroma1=?, Chroma2=?, Chroma3=?, Chroma4=?, Chroma5=?, Chroma6=?, Chroma7=?, Chroma8=?, Chroma9=?, Chroma10=? WHERE rowid=?;",
|
||||
params![meta.title, meta.artist, meta.album_artist, meta.album, meta.genre, meta.duration,
|
||||
analysis[AnalysisIndex::Tempo], analysis[AnalysisIndex::Zcr], analysis[AnalysisIndex::MeanSpectralCentroid], analysis[AnalysisIndex::StdDeviationSpectralCentroid], analysis[AnalysisIndex::MeanSpectralRolloff],
|
||||
analysis[AnalysisIndex::StdDeviationSpectralRolloff], analysis[AnalysisIndex::MeanSpectralFlatness], analysis[AnalysisIndex::StdDeviationSpectralFlatness], analysis[AnalysisIndex::MeanLoudness], analysis[AnalysisIndex::StdDeviationLoudness],
|
||||
analysis[AnalysisIndex::Chroma1], analysis[AnalysisIndex::Chroma2], analysis[AnalysisIndex::Chroma3], analysis[AnalysisIndex::Chroma4], analysis[AnalysisIndex::Chroma5],
|
||||
analysis[AnalysisIndex::Chroma6], analysis[AnalysisIndex::Chroma7], analysis[AnalysisIndex::Chroma8], analysis[AnalysisIndex::Chroma9], analysis[AnalysisIndex::Chroma10], id]) {
|
||||
Ok(_) => { },
|
||||
Ok(_) => { }
|
||||
Err(e) => { log::error!("Failed to update '{}' in database. {}", path, e); }
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(_) => { }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_old(&self, mpath:&Path, dry_run:bool) {
|
||||
log::info!("Looking for non-existant tracks");
|
||||
pub fn remove_old(&self, mpaths: &Vec<PathBuf>, dry_run: bool) {
|
||||
log::info!("Looking for non-existent tracks");
|
||||
let mut stmt = self.conn.prepare("SELECT File FROM Tracks;").unwrap();
|
||||
let track_iter = stmt.query_map([], |row| {
|
||||
Ok((row.get(0)?,))
|
||||
}).unwrap();
|
||||
let mut to_remove:Vec<String> = Vec::new();
|
||||
let track_iter = stmt.query_map([], |row| Ok((row.get(0)?,))).unwrap();
|
||||
let mut to_remove: Vec<String> = Vec::new();
|
||||
for tr in track_iter {
|
||||
let mut db_path:String = tr.unwrap().0;
|
||||
let mut db_path: String = tr.unwrap().0;
|
||||
let orig_path = db_path.clone();
|
||||
match orig_path.find(CUE_MARKER) {
|
||||
Some(s) => {
|
||||
db_path.truncate(s);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if cfg!(windows) {
|
||||
db_path = db_path.replace("/", "\\");
|
||||
}
|
||||
let path = mpath.join(PathBuf::from(db_path.clone()));
|
||||
let mut exists = false;
|
||||
|
||||
if !path.exists() {
|
||||
for mpath in mpaths {
|
||||
let path = mpath.join(PathBuf::from(db_path.clone()));
|
||||
//log::debug!("Check if '{}' exists.", path.to_string_lossy());
|
||||
|
||||
if path.exists() {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
to_remove.push(orig_path);
|
||||
}
|
||||
}
|
||||
|
||||
let num_to_remove = to_remove.len();
|
||||
log::info!("Num non-existant tracks: {}", num_to_remove);
|
||||
if !dry_run && num_to_remove>0 {
|
||||
let count_before = self.get_track_count();
|
||||
for t in to_remove {
|
||||
log::debug!("Remove '{}'", t);
|
||||
match self.conn.execute("DELETE FROM Tracks WHERE File = ?;", params![t]) {
|
||||
Ok(_) => { },
|
||||
Err(e) => { log::error!("Failed to remove '{}' - {}", t, e) }
|
||||
log::info!("Num non-existent tracks: {}", num_to_remove);
|
||||
if num_to_remove > 0 {
|
||||
if dry_run {
|
||||
log::info!("The following need to be removed from database:");
|
||||
for t in to_remove {
|
||||
log::info!(" {}", t);
|
||||
}
|
||||
} else {
|
||||
let count_before = self.get_track_count();
|
||||
for t in to_remove {
|
||||
//log::debug!("Remove '{}'", t);
|
||||
let cmd = self.conn.execute("DELETE FROM Tracks WHERE File = ?;", params![t]);
|
||||
|
||||
if let Err(e) = cmd {
|
||||
log::error!("Failed to remove '{}' - {}", t, e)
|
||||
}
|
||||
}
|
||||
let count_now = self.get_track_count();
|
||||
if (count_now + num_to_remove) != count_before {
|
||||
log::error!("Failed to remove all tracks. Count before: {}, wanted to remove: {}, count now: {}", count_before, num_to_remove, count_now);
|
||||
}
|
||||
}
|
||||
let count_now = self.get_track_count();
|
||||
if (count_now + num_to_remove) != count_before {
|
||||
log::error!("Failed to remove all tracks. Count before: {}, wanted to remove: {}, count now: {}", count_before, num_to_remove, count_now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_track_count(&self) -> usize {
|
||||
let mut stmt = self.conn.prepare("SELECT COUNT(*) FROM Tracks;").unwrap();
|
||||
let track_iter = stmt.query_map([], |row| {
|
||||
Ok(row.get(0)?)
|
||||
}).unwrap();
|
||||
let mut count:usize = 0;
|
||||
let track_iter = stmt.query_map([], |row| Ok(row.get(0)?)).unwrap();
|
||||
let mut count: usize = 0;
|
||||
for tr in track_iter {
|
||||
count = tr.unwrap();
|
||||
break;
|
||||
@@ -199,77 +237,94 @@ impl Db {
|
||||
count
|
||||
}
|
||||
|
||||
pub fn update_tags(&self, mpath:&PathBuf) {
|
||||
pub fn update_tags(&self, mpaths: &Vec<PathBuf>) {
|
||||
let total = self.get_track_count();
|
||||
if total>0 {
|
||||
let pb = ProgressBar::new(total.try_into().unwrap());
|
||||
let style = ProgressStyle::default_bar()
|
||||
.template("[{elapsed_precise}] [{bar:25}] {percent:>3}% {pos:>6}/{len:6} {wide_msg}")
|
||||
.progress_chars("=> ");
|
||||
pb.set_style(style);
|
||||
if total > 0 {
|
||||
let progress = ProgressBar::new(total.try_into().unwrap()).with_style(
|
||||
ProgressStyle::default_bar()
|
||||
.template(
|
||||
"[{elapsed_precise}] [{bar:25}] {percent:>3}% {pos:>6}/{len:6} {wide_msg}",
|
||||
)
|
||||
.progress_chars("=> "),
|
||||
);
|
||||
|
||||
let mut stmt = self.conn.prepare("SELECT rowid, File, Title, Artist, AlbumArtist, Album, Genre, Duration FROM Tracks ORDER BY File ASC;").unwrap();
|
||||
let track_iter = stmt.query_map([], |row| {
|
||||
Ok(FileMetadata {
|
||||
rowid: row.get(0)?,
|
||||
file: row.get(1)?,
|
||||
title: row.get(2)?,
|
||||
artist: row.get(3)?,
|
||||
album_artist: row.get(4)?,
|
||||
album: row.get(5)?,
|
||||
genre: row.get(6)?,
|
||||
duration: row.get(7)?,
|
||||
let track_iter = stmt
|
||||
.query_map([], |row| {
|
||||
Ok(FileMetadata {
|
||||
rowid: row.get(0)?,
|
||||
file: row.get(1)?,
|
||||
title: row.get(2)?,
|
||||
artist: row.get(3)?,
|
||||
album_artist: row.get(4)?,
|
||||
album: row.get(5)?,
|
||||
genre: row.get(6)?,
|
||||
duration: row.get(7)?,
|
||||
})
|
||||
})
|
||||
}).unwrap();
|
||||
.unwrap();
|
||||
|
||||
let mut updated = 0;
|
||||
for tr in track_iter {
|
||||
let dbtags = tr.unwrap();
|
||||
let dtags = Metadata{
|
||||
title:dbtags.title.unwrap_or(String::new()),
|
||||
artist:dbtags.artist.unwrap_or(String::new()),
|
||||
album_artist:dbtags.album_artist.unwrap_or(String::new()),
|
||||
album:dbtags.album.unwrap_or(String::new()),
|
||||
genre:dbtags.genre.unwrap_or(String::new()),
|
||||
duration:dbtags.duration
|
||||
};
|
||||
pb.set_message(format!("{}", dbtags.file));
|
||||
let path = String::from(mpath.join(&dbtags.file).to_string_lossy());
|
||||
let ftags = tags::read(&path);
|
||||
if ftags.title.is_empty() && ftags.artist.is_empty() && ftags.album_artist.is_empty() && ftags.album.is_empty() && ftags.genre.is_empty() {
|
||||
log::error!("Failed to read tags of '{}'", dbtags.file);
|
||||
} else if ftags.duration!=dtags.duration || ftags.title!=dtags.title || ftags.artist!=dtags.artist || ftags.album_artist!=dtags.album_artist || ftags.album!=dtags.album || ftags.genre!=dtags.genre {
|
||||
match self.conn.execute("UPDATE Tracks SET Title=?, Artist=?, AlbumArtist=?, Album=?, Genre=?, Duration=? WHERE rowid=?;",
|
||||
params![ftags.title, ftags.artist, ftags.album_artist, ftags.album, ftags.genre, ftags.duration, dbtags.rowid]) {
|
||||
Ok(_) => { updated += 1; },
|
||||
Err(e) => { log::error!("Failed to update tags of '{}'. {}", dbtags.file, e); }
|
||||
if !dbtags.file.contains(CUE_MARKER) {
|
||||
let dtags = Metadata {
|
||||
title: dbtags.title.unwrap_or_default(),
|
||||
artist: dbtags.artist.unwrap_or_default(),
|
||||
album_artist: dbtags.album_artist.unwrap_or_default(),
|
||||
album: dbtags.album.unwrap_or_default(),
|
||||
genre: dbtags.genre.unwrap_or_default(),
|
||||
duration: dbtags.duration,
|
||||
};
|
||||
progress.set_message(format!("{}", dbtags.file));
|
||||
|
||||
for mpath in mpaths {
|
||||
let track_path = mpath.join(&dbtags.file);
|
||||
if track_path.exists() {
|
||||
let path = String::from(track_path.to_string_lossy());
|
||||
let ftags = tags::read(&path);
|
||||
if ftags.is_empty() {
|
||||
log::error!("Failed to read tags of '{}'", dbtags.file);
|
||||
} else if ftags != dtags {
|
||||
match self.conn.execute("UPDATE Tracks SET Title=?, Artist=?, AlbumArtist=?, Album=?, Genre=?, Duration=? WHERE rowid=?;",
|
||||
params![ftags.title, ftags.artist, ftags.album_artist, ftags.album, ftags.genre, ftags.duration, dbtags.rowid]) {
|
||||
Ok(_) => { updated += 1; }
|
||||
Err(e) => { log::error!("Failed to update tags of '{}'. {}", dbtags.file, e); }
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pb.inc(1);
|
||||
progress.inc(1);
|
||||
}
|
||||
pb.finish_with_message(format!("{} Updated.", updated))
|
||||
progress.finish_with_message(format!("{} Updated.", updated))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_ignore(&self) {
|
||||
match self.conn.execute("UPDATE Tracks SET Ignore=0;", []) {
|
||||
Ok(_) => { },
|
||||
Err(e) => { log::error!("Failed clear Ignore column. {}", e); }
|
||||
let cmd = self.conn.execute("UPDATE Tracks SET Ignore=0;", []);
|
||||
|
||||
if let Err(e) = cmd {
|
||||
log::error!("Failed clear Ignore column. {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_ignore(&self, line:&str) {
|
||||
pub fn set_ignore(&self, line: &str) {
|
||||
log::info!("Ignore: {}", line);
|
||||
if line.starts_with("SQL:") {
|
||||
let sql = &line[4..];
|
||||
match self.conn.execute(&format!("UPDATE Tracks Set Ignore=1 WHERE {}", sql), []) {
|
||||
Ok(_) => { },
|
||||
Err(e) => { log::error!("Failed set Ignore column for '{}'. {}", line, e); }
|
||||
let cmd = self.conn.execute(&format!("UPDATE Tracks Set Ignore=1 WHERE {}", sql), []);
|
||||
|
||||
if let Err(e) = cmd {
|
||||
log::error!("Failed set Ignore column for '{}'. {}", line, e);
|
||||
}
|
||||
} else {
|
||||
match self.conn.execute(&format!("UPDATE Tracks SET Ignore=1 WHERE File LIKE \"{}%\"", line), []) {
|
||||
Ok(_) => { },
|
||||
Err(e) => { log::error!("Failed set Ignore column for '{}'. {}", line, e); }
|
||||
let cmd = self.conn.execute(&format!("UPDATE Tracks SET Ignore=1 WHERE File LIKE \"{}%\"", line), []);
|
||||
|
||||
if let Err(e) = cmd {
|
||||
log::error!("Failed set Ignore column for '{}'. {}", line, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
-32
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* Analyse music with Bliss
|
||||
*
|
||||
* Copyright (c) 2022 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* Copyright (c) 2022-2023 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* GPLv3 license.
|
||||
*
|
||||
**/
|
||||
|
||||
use argparse::{ArgumentParser, Store, StoreTrue};
|
||||
use chrono::Local;
|
||||
use configparser::ini::Ini;
|
||||
@@ -18,9 +19,8 @@ mod db;
|
||||
mod tags;
|
||||
mod upload;
|
||||
|
||||
|
||||
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
const TOP_LEVEL_INI_TAG:&str = "Bliss";
|
||||
const TOP_LEVEL_INI_TAG: &str = "Bliss";
|
||||
|
||||
fn main() {
|
||||
let mut config_file = "config.ini".to_string();
|
||||
@@ -28,15 +28,19 @@ fn main() {
|
||||
let mut logging = "info".to_string();
|
||||
let mut music_path = ".".to_string();
|
||||
let mut ignore_file = "ignore.txt".to_string();
|
||||
let mut keep_old:bool = false;
|
||||
let mut dry_run:bool = false;
|
||||
let mut keep_old: bool = false;
|
||||
let mut dry_run: bool = false;
|
||||
let mut task = "".to_string();
|
||||
let mut lms_host = "127.0.0.1".to_string();
|
||||
let mut max_num_tracks:usize = 0;
|
||||
let mut max_num_files: usize = 0;
|
||||
let mut music_paths: Vec<PathBuf> = Vec::new();
|
||||
let mut max_threads: usize = 0;
|
||||
|
||||
match dirs::home_dir() {
|
||||
Some(path) => { music_path = String::from(path.join("Music").to_string_lossy()); }
|
||||
None => { }
|
||||
Some(path) => {
|
||||
music_path = String::from(path.join("Music").to_string_lossy());
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
{
|
||||
@@ -44,7 +48,7 @@ fn main() {
|
||||
let music_path_help = format!("Music folder (default: {})", &music_path);
|
||||
let db_path_help = format!("Database location (default: {})", &db_path);
|
||||
let logging_help = format!("Log level; trace, debug, info, warn, error. (default: {})", logging);
|
||||
let ignore_file_help = format!("File containg items to mark as ignored. (default: {})", ignore_file);
|
||||
let ignore_file_help = format!("File contains items to mark as ignored. (default: {})", ignore_file);
|
||||
let lms_host_help = format!("LMS hostname or IP address (default: {})", &lms_host);
|
||||
let description = format!("Bliss Analyser v{}", VERSION);
|
||||
|
||||
@@ -56,29 +60,34 @@ fn main() {
|
||||
arg_parse.refer(&mut music_path).add_option(&["-m", "--music"], Store, &music_path_help);
|
||||
arg_parse.refer(&mut db_path).add_option(&["-d", "--db"], Store, &db_path_help);
|
||||
arg_parse.refer(&mut logging).add_option(&["-l", "--logging"], Store, &logging_help);
|
||||
arg_parse.refer(&mut keep_old).add_option(&["-k", "--keep-old"], StoreTrue, "Don't remove tracks from DB if they don't exist (used with analyse task)");
|
||||
arg_parse.refer(&mut keep_old).add_option(&["-k", "--keep-old"], StoreTrue, "Don't remove files from DB if they don't exist (used with analyse task)");
|
||||
arg_parse.refer(&mut dry_run).add_option(&["-r", "--dry-run"], StoreTrue, "Dry run, only show what needs to be done (used with analyse task)");
|
||||
arg_parse.refer(&mut ignore_file).add_option(&["-i", "--ignore"], Store, &ignore_file_help);
|
||||
arg_parse.refer(&mut lms_host).add_option(&["-L", "--lms"], Store, &lms_host_help);
|
||||
arg_parse.refer(&mut max_num_tracks).add_option(&["-n", "--numtracks"], Store, "Maximum number of tracks to analyse");
|
||||
arg_parse.refer(&mut max_num_files).add_option(&["-n", "--numfiles"], Store, "Maximum number of files to analyse");
|
||||
arg_parse.refer(&mut max_threads).add_option(&["-t", "--threads"], Store, "Maximum number of threads to use for analysis");
|
||||
arg_parse.refer(&mut task).add_argument("task", Store, "Task to perform; analyse, tags, ignore, upload, stopmixer.");
|
||||
arg_parse.parse_args_or_exit();
|
||||
}
|
||||
|
||||
if !(logging.eq_ignore_ascii_case("trace") || logging.eq_ignore_ascii_case("debug") || logging.eq_ignore_ascii_case("info") || logging.eq_ignore_ascii_case("warn") || logging.eq_ignore_ascii_case("error")) {
|
||||
if !(logging.eq_ignore_ascii_case("trace") || logging.eq_ignore_ascii_case("debug") || logging.eq_ignore_ascii_case("info")
|
||||
|| logging.eq_ignore_ascii_case("warn") || logging.eq_ignore_ascii_case("error")) {
|
||||
logging = String::from("info");
|
||||
}
|
||||
let mut builder = env_logger::Builder::from_env(env_logger::Env::default().filter_or("XXXXXXXX", logging));
|
||||
builder.filter(Some("bliss_audio"), LevelFilter::Error);
|
||||
builder.format(|buf, record| writeln!(buf, "[{} {:.1}] {}", Local::now().format("%Y-%m-%d %H:%M:%S"), record.level(), record.args()));
|
||||
builder.format(|buf, record| {
|
||||
writeln!(buf, "[{} {:.1}] {}", Local::now().format("%Y-%m-%d %H:%M:%S"), record.level(), record.args())
|
||||
});
|
||||
builder.init();
|
||||
|
||||
if task.is_empty() {
|
||||
if task.is_empty() {
|
||||
log::error!("No task specified, please choose from; analyse, tags, ignore, upload");
|
||||
process::exit(-1);
|
||||
}
|
||||
|
||||
if !task.eq_ignore_ascii_case("analyse") && !task.eq_ignore_ascii_case("tags") && !task.eq_ignore_ascii_case("ignore") && !task.eq_ignore_ascii_case("upload") && !task.eq_ignore_ascii_case("stopmixer") {
|
||||
if !task.eq_ignore_ascii_case("analyse") && !task.eq_ignore_ascii_case("tags") && !task.eq_ignore_ascii_case("ignore")
|
||||
&& !task.eq_ignore_ascii_case("upload") && !task.eq_ignore_ascii_case("stopmixer") {
|
||||
log::error!("Invalid task ({}) supplied", task);
|
||||
process::exit(-1);
|
||||
}
|
||||
@@ -89,23 +98,26 @@ fn main() {
|
||||
let mut config = Ini::new();
|
||||
match config.load(&config_file) {
|
||||
Ok(_) => {
|
||||
match config.get(TOP_LEVEL_INI_TAG, "music") {
|
||||
Some(val) => { music_path = val; },
|
||||
None => { }
|
||||
let path_keys: [&str; 5] = ["music", "music_1", "music_2", "music_3", "music_4"];
|
||||
for key in &path_keys {
|
||||
match config.get(TOP_LEVEL_INI_TAG, key) {
|
||||
Some(val) => { music_paths.push(PathBuf::from(&val)); }
|
||||
None => { }
|
||||
}
|
||||
}
|
||||
match config.get(TOP_LEVEL_INI_TAG, "db") {
|
||||
Some(val) => { db_path = val; },
|
||||
Some(val) => { db_path = val; }
|
||||
None => { }
|
||||
}
|
||||
match config.get(TOP_LEVEL_INI_TAG, "lms") {
|
||||
Some(val) => { lms_host = val; },
|
||||
Some(val) => { lms_host = val; }
|
||||
None => { }
|
||||
}
|
||||
match config.get(TOP_LEVEL_INI_TAG, "ignore") {
|
||||
Some(val) => { ignore_file = val; },
|
||||
Some(val) => { ignore_file = val; }
|
||||
None => { }
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to load config file. {}", e);
|
||||
process::exit(-1);
|
||||
@@ -114,6 +126,10 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
if music_paths.is_empty() {
|
||||
music_paths.push(PathBuf::from(&music_path));
|
||||
}
|
||||
|
||||
if task.eq_ignore_ascii_case("stopmixer") {
|
||||
upload::stop_mixer(&lms_host);
|
||||
} else {
|
||||
@@ -136,18 +152,19 @@ fn main() {
|
||||
process::exit(-1);
|
||||
}
|
||||
} else {
|
||||
let mpath = PathBuf::from(&music_path);
|
||||
if !mpath.exists() {
|
||||
log::error!("Music path ({}) does not exist", music_path);
|
||||
process::exit(-1);
|
||||
}
|
||||
if !mpath.is_dir() {
|
||||
log::error!("Music path ({}) is not a directory", music_path);
|
||||
process::exit(-1);
|
||||
for mpath in &music_paths {
|
||||
if !mpath.exists() {
|
||||
log::error!("Music path ({}) does not exist", mpath.to_string_lossy());
|
||||
process::exit(-1);
|
||||
}
|
||||
if !mpath.is_dir() {
|
||||
log::error!("Music path ({}) is not a directory", mpath.to_string_lossy());
|
||||
process::exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
if task.eq_ignore_ascii_case("tags") {
|
||||
analyse::read_tags(&db_path, &mpath);
|
||||
analyse::read_tags(&db_path, &music_paths);
|
||||
} else if task.eq_ignore_ascii_case("ignore") {
|
||||
let ignore_path = PathBuf::from(&ignore_file);
|
||||
if !ignore_path.exists() {
|
||||
@@ -160,7 +177,7 @@ fn main() {
|
||||
}
|
||||
analyse::update_ignore(&db_path, &ignore_path);
|
||||
} else {
|
||||
analyse::analyse_files(&db_path, &mpath, dry_run, keep_old, max_num_tracks);
|
||||
analyse::analyse_files(&db_path, &music_paths, dry_run, keep_old, max_num_files, max_threads);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+54
-64
@@ -1,87 +1,77 @@
|
||||
/**
|
||||
* Analyse music with Bliss
|
||||
*
|
||||
* Copyright (c) 2022 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* Copyright (c) 2022-2023 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* GPLv3 license.
|
||||
*
|
||||
**/
|
||||
|
||||
use lofty::{Accessor, ItemKey, Probe};
|
||||
use crate::db;
|
||||
use lofty::{Accessor, AudioFile, ItemKey, TaggedFileExt};
|
||||
use regex::Regex;
|
||||
use std::path::Path;
|
||||
use substring::Substring;
|
||||
use crate::db;
|
||||
|
||||
const MAX_GENRE_VAL:usize = 192;
|
||||
const MAX_GENRE_VAL: usize = 192;
|
||||
|
||||
pub fn read(track:&String) -> db::Metadata {
|
||||
let mut meta = db::Metadata{
|
||||
title:String::new(),
|
||||
artist:String::new(),
|
||||
album:String::new(),
|
||||
album_artist:String::new(),
|
||||
genre:String::new(),
|
||||
duration:180
|
||||
pub fn read(track: &String) -> db::Metadata {
|
||||
let mut meta = db::Metadata {
|
||||
duration: 180,
|
||||
..db::Metadata::default()
|
||||
};
|
||||
let path = Path::new(track);
|
||||
match Probe::open(path) {
|
||||
Ok(probe) => {
|
||||
match probe.read(true) {
|
||||
Ok(file) => {
|
||||
let tag = match file.primary_tag() {
|
||||
Some(primary_tag) => primary_tag,
|
||||
None => file.first_tag().expect("Error: No tags found!"),
|
||||
};
|
||||
|
||||
meta.title=tag.title().unwrap_or("").to_string();
|
||||
meta.artist=tag.artist().unwrap_or("").to_string();
|
||||
meta.album=tag.album().unwrap_or("").to_string();
|
||||
meta.album_artist=tag.get_string(&ItemKey::AlbumArtist).unwrap_or("").to_string();
|
||||
meta.genre=tag.genre().unwrap_or("").to_string();
|
||||
// Check whether MP3 as numeric genre, and if so covert to text
|
||||
if file.file_type().eq(&lofty::FileType::MP3) {
|
||||
match tag.genre() {
|
||||
Some(genre) => {
|
||||
let test = &genre.parse::<u8>();
|
||||
match test {
|
||||
Ok(val) => {
|
||||
let idx:usize = *val as usize;
|
||||
if idx<MAX_GENRE_VAL {
|
||||
meta.genre=lofty::id3::v1::GENRES[idx].to_string();
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
// Check for "(number)text"
|
||||
let re = Regex::new(r"^\([0-9]+\)").unwrap();
|
||||
if re.is_match(&genre) {
|
||||
match genre.find(")") {
|
||||
Some(end) => {
|
||||
let test = &genre.to_string().substring(1, end).parse::<u8>();
|
||||
match test {
|
||||
Ok(val) => {
|
||||
let idx:usize = *val as usize;
|
||||
if idx<MAX_GENRE_VAL {
|
||||
meta.genre=lofty::id3::v1::GENRES[idx].to_string();
|
||||
}
|
||||
},
|
||||
Err(_) => { }
|
||||
}
|
||||
},
|
||||
None => { }
|
||||
if let Ok(file) = lofty::read_from_path(Path::new(track)) {
|
||||
let tag = match file.primary_tag() {
|
||||
Some(primary_tag) => primary_tag,
|
||||
None => file.first_tag().expect("Error: No tags found!"),
|
||||
};
|
||||
|
||||
meta.title = tag.title().unwrap_or_default().to_string();
|
||||
meta.artist = tag.artist().unwrap_or_default().to_string();
|
||||
meta.album = tag.album().unwrap_or_default().to_string();
|
||||
meta.album_artist = tag.get_string(&ItemKey::AlbumArtist).unwrap_or_default().to_string();
|
||||
meta.genre = tag.genre().unwrap_or_default().to_string();
|
||||
|
||||
// Check whether MP3 has numeric genre, and if so covert to text
|
||||
if file.file_type().eq(&lofty::FileType::Mpeg) {
|
||||
match tag.genre() {
|
||||
Some(genre) => {
|
||||
let test = genre.parse::<u8>();
|
||||
match test {
|
||||
Ok(val) => {
|
||||
let idx: usize = val as usize;
|
||||
if idx < MAX_GENRE_VAL {
|
||||
meta.genre = lofty::id3::v1::GENRES[idx].to_string();
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// Check for "(number)text"
|
||||
let re = Regex::new(r"^\([0-9]+\)").unwrap();
|
||||
if re.is_match(&genre) {
|
||||
match genre.find(")") {
|
||||
Some(end) => {
|
||||
let test = genre.to_string().substring(1, end).parse::<u8>();
|
||||
|
||||
if let Ok(val) = test {
|
||||
let idx: usize = val as usize;
|
||||
if idx < MAX_GENRE_VAL {
|
||||
meta.genre =
|
||||
lofty::id3::v1::GENRES[idx].to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
None => { }
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
meta.duration=file.properties().duration().as_secs() as u32;
|
||||
},
|
||||
Err(_) => { }
|
||||
}
|
||||
None => { }
|
||||
}
|
||||
},
|
||||
Err(_) => { }
|
||||
}
|
||||
|
||||
meta.duration = file.properties().duration().as_secs() as u32;
|
||||
}
|
||||
|
||||
meta
|
||||
}
|
||||
}
|
||||
+41
-58
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Analyse music with Bliss
|
||||
*
|
||||
* Copyright (c) 2022 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* Copyright (c) 2022-2023 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* GPLv3 license.
|
||||
*
|
||||
**/
|
||||
@@ -12,93 +12,76 @@ use std::process;
|
||||
use substring::Substring;
|
||||
use ureq;
|
||||
|
||||
|
||||
fn fail(msg:&str) {
|
||||
fn fail(msg: &str) {
|
||||
log::error!("{}", msg);
|
||||
process::exit(-1);
|
||||
}
|
||||
|
||||
pub fn stop_mixer(lms:&String) {
|
||||
pub fn stop_mixer(lms: &String) {
|
||||
let stop_req = "{\"id\":1, \"method\":\"slim.request\",\"params\":[\"\",[\"blissmixer\",\"stop\"]]}";
|
||||
|
||||
log::info!("Asking plugin to stop mixer");
|
||||
match ureq::post(&format!("http://{}:9000/jsonrpc.js", lms)).send_string(&stop_req) {
|
||||
Ok(_) => { },
|
||||
Err(e) => { log::error!("Failed to ask plugin to stop mixer. {}", e); }
|
||||
let req = ureq::post(&format!("http://{}:9000/jsonrpc.js", lms)).send_string(&stop_req);
|
||||
if let Err(e) = req {
|
||||
log::error!("Failed to ask plugin to stop mixer. {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upload_db(db_path:&String, lms:&String) {
|
||||
pub fn upload_db(db_path: &String, lms: &String) {
|
||||
// First tell LMS to restart the mixer in upload mode
|
||||
let start_req = "{\"id\":1, \"method\":\"slim.request\",\"params\":[\"\",[\"blissmixer\",\"start-upload\"]]}";
|
||||
let mut port:u16 = 0;
|
||||
let mut port: u16 = 0;
|
||||
|
||||
log::info!("Requesting LMS plugin to allow uploads");
|
||||
|
||||
match ureq::post(&format!("http://{}:9000/jsonrpc.js", lms)).send_string(&start_req) {
|
||||
Ok(resp) => {
|
||||
match resp.into_string() {
|
||||
Ok(text) => {
|
||||
match text.find("\"port\":") {
|
||||
Some(s) => {
|
||||
let txt = text.to_string().substring(s+7, text.len()).to_string();
|
||||
match txt.find("}") {
|
||||
Some(e) => {
|
||||
let p = txt.substring(0, e);
|
||||
let test = p.parse::<u16>();
|
||||
match test {
|
||||
Ok(val) => {
|
||||
port = val;
|
||||
},
|
||||
Err(_) => { fail("Could not parse resp (cast)"); }
|
||||
}
|
||||
},
|
||||
None => { fail("Could not parse resp (closing)"); }
|
||||
Ok(resp) => match resp.into_string() {
|
||||
Ok(text) => match text.find("\"port\":") {
|
||||
Some(s) => {
|
||||
let txt = text.to_string().substring(s + 7, text.len()).to_string();
|
||||
match txt.find("}") {
|
||||
Some(e) => {
|
||||
let p = txt.substring(0, e);
|
||||
let test = p.parse::<u16>();
|
||||
match test {
|
||||
Ok(val) => { port = val; }
|
||||
Err(_) => { fail("Could not parse resp (cast)"); }
|
||||
}
|
||||
},
|
||||
None => { fail("Could not parse resp (no port)"); }
|
||||
}
|
||||
None => { fail("Could not parse resp (closing)"); }
|
||||
}
|
||||
},
|
||||
Err(_) => { fail("No text?")}
|
||||
}
|
||||
None => { fail("Could not parse resp (no port)"); }
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
fail(&format!("Failed to ask LMS plugin to allow upload. {}", e));
|
||||
Err(_) => fail("No text?"),
|
||||
}
|
||||
Err(e) => { fail(&format!("Failed to ask LMS plugin to allow upload. {}", e)); }
|
||||
}
|
||||
|
||||
if port<=0 {
|
||||
if port == 0 {
|
||||
fail("Invalid port");
|
||||
}
|
||||
|
||||
// Now we have port number, do the actual upload...
|
||||
log::info!("Uploading {}", db_path);
|
||||
match File::open(db_path) {
|
||||
Ok(file) => {
|
||||
match file.metadata() {
|
||||
Ok(meta) => {
|
||||
let buffered_reader = BufReader::new(file);
|
||||
log::info!("Length: {}", meta.len());
|
||||
match ureq::put(&format!("http://{}:{}/upload", lms, port))
|
||||
.set("Content-Length", &meta.len().to_string())
|
||||
.set("Content-Type", "application/octet-stream")
|
||||
.send(buffered_reader) {
|
||||
Ok(_) => {
|
||||
log::info!("Database uploaded");
|
||||
stop_mixer(lms);
|
||||
},
|
||||
Err(e) => {
|
||||
fail(&format!("Failed to upload database. {}", e));
|
||||
}
|
||||
Ok(file) => match file.metadata() {
|
||||
Ok(meta) => {
|
||||
let buffered_reader = BufReader::new(file);
|
||||
log::info!("Length: {}", meta.len());
|
||||
match ureq::put(&format!("http://{}:{}/upload", lms, port))
|
||||
.set("Content-Length", &meta.len().to_string())
|
||||
.set("Content-Type", "application/octet-stream")
|
||||
.send(buffered_reader) {
|
||||
Ok(_) => {
|
||||
log::info!("Database uploaded");
|
||||
stop_mixer(lms);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
fail(&format!("Failed to open database. {}", e));
|
||||
Err(e) => { fail(&format!("Failed to upload database. {}", e)); }
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
fail(&format!("Failed to open database. {}", e));
|
||||
Err(e) => { fail(&format!("Failed to open database. {}", e)); }
|
||||
}
|
||||
Err(e) => { fail(&format!("Failed to open database. {}", e)); }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user