mirror of
https://github.com/CDrummond/bliss-analyser.git
synced 2026-10-22 22:32:08 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf3bdf26b9 | ||
|
|
cd2900b273 | ||
|
|
e32cdf4806 | ||
|
|
9c9fdad504 | ||
|
|
fe65d9fcf6 | ||
|
|
b13efd783b | ||
|
|
572f91eeec | ||
|
|
374be6a5c2 | ||
|
|
cf902e6d0c | ||
|
|
a3748f495c | ||
|
|
5c54879126 | ||
|
|
b57828cc22 | ||
|
|
f438239983 | ||
|
|
c1ba064f48 | ||
|
|
0c4335d02d | ||
|
|
fe10956341 | ||
|
|
e860048ff0 | ||
|
|
1428751b9e | ||
|
|
e546793864 | ||
|
|
e0ffdedced | ||
|
|
81f29db352 | ||
|
|
35ec7695e5 | ||
|
|
6f1abef1ad | ||
|
|
5168723d55 | ||
|
|
ee3b27d959 | ||
|
|
0e5f4fc1d1 | ||
|
|
beb7155ae6 | ||
|
|
84766d3e4f | ||
|
|
5ec6a30104 | ||
|
|
fa61d5a6c3 | ||
|
|
d06263efb0 | ||
|
|
d7da5dcd95 | ||
|
|
849fbe92fa |
No files matched your search
+35
-36
@@ -12,20 +12,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Packages
|
||||
run: sudo apt-get update && sudo apt-get install build-essential yasm -y
|
||||
|
||||
- name: Build
|
||||
- name: Build x86
|
||||
run: |
|
||||
cargo build --release --features=symphonia
|
||||
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
|
||||
docker build -t bliss-analyser-x86-symphonia - < docker/Dockerfile_x86_symphonia
|
||||
docker run --rm -v $PWD/target:/build -v $PWD:/src bliss-analyser-x86-symphonia
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -33,6 +25,23 @@ jobs:
|
||||
name: bliss-analyser-linux-x86-symphonia
|
||||
path: releases/
|
||||
|
||||
# Linux_x86_ffmpeg:
|
||||
# runs-on: ubuntu-latest
|
||||
#
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v1
|
||||
#
|
||||
# - name: Build x86
|
||||
# run: |
|
||||
# docker build -t bliss-analyser-x86-ffmpeg - < docker/Dockerfile_x86_ffmpeg
|
||||
# docker run --rm -v $PWD/target:/build -v $PWD:/src bliss-analyser-x86-ffmpeg
|
||||
#
|
||||
# - name: Upload artifacts
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: bliss-analyser-linux-x86-ffmpeg
|
||||
# path: releases/
|
||||
|
||||
Linux_x86_ffmpeg:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -60,18 +69,17 @@ jobs:
|
||||
name: bliss-analyser-linux-x86-ffmpeg
|
||||
path: releases/
|
||||
|
||||
|
||||
Linux_arm_symphonia:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Build
|
||||
- name: Build ARM
|
||||
run: |
|
||||
docker build --pull --no-cache -t bliss-analyser-cross - < docker/Dockerfile_Bullseye_symphonia
|
||||
docker run --rm -v $PWD/target:/build -v $PWD:/src bliss-analyser-cross
|
||||
docker build -t bliss-analyser-arm-symphonia - < docker/Dockerfile_arm_symphonia
|
||||
docker run --rm -v $PWD/target:/build -v $PWD:/src bliss-analyser-arm-symphonia
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -79,18 +87,17 @@ jobs:
|
||||
name: bliss-analyser-linux-arm-symphonia
|
||||
path: releases/
|
||||
|
||||
|
||||
Linux_arm_ffmpeg:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Build ARM static-ffmpeg on Debian
|
||||
- name: Build ARM
|
||||
run: |
|
||||
docker build -t bliss-analyser-cross - < docker/Dockerfile_Bullseye_static
|
||||
docker run --rm -v $PWD/target:/build -v $PWD:/src bliss-analyser-cross
|
||||
docker build -t bliss-analyser-arm-ffmpeg - < docker/Dockerfile_arm_ffmpeg
|
||||
docker run --rm -v $PWD/target:/build -v $PWD:/src bliss-analyser-arm-ffmpeg
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -98,9 +105,8 @@ jobs:
|
||||
name: bliss-analyser-linux-arm-ffmpeg
|
||||
path: releases/
|
||||
|
||||
|
||||
macOS_symphonia:
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15
|
||||
|
||||
steps:
|
||||
- name: Install Rust
|
||||
@@ -117,17 +123,14 @@ jobs:
|
||||
|
||||
- name: Install Rust support for ARM64 & prepare environment
|
||||
run: |
|
||||
rustup target add x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin
|
||||
mkdir releases
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cargo build --release --features update-aubio-bindings,symphonia
|
||||
strip target/release/bliss-analyser
|
||||
cp target/release/bliss-analyser releases/bliss-analyser-x86_64
|
||||
cargo build --target=x86_64-apple-darwin --release --features update-aubio-bindings,symphonia
|
||||
cargo build --target=aarch64-apple-darwin --release --features update-aubio-bindings,symphonia
|
||||
strip target/aarch64-apple-darwin/release/bliss-analyser
|
||||
cp target/aarch64-apple-darwin/release/bliss-analyser releases/bliss-analyser-arm64
|
||||
cp UserGuide.md releases/README.md
|
||||
cp LICENSE releases/
|
||||
cp configs/macos.ini releases/config.ini
|
||||
@@ -135,14 +138,10 @@ jobs:
|
||||
- name: Build fat binary
|
||||
run: |
|
||||
lipo -create \
|
||||
-arch x86_64 releases/bliss-analyser-x86_64 \
|
||||
-arch arm64 releases/bliss-analyser-arm64 \
|
||||
-arch x86_64 target/x86_64-apple-darwin/release/bliss-analyser \
|
||||
-arch arm64 target/aarch64-apple-darwin/release/bliss-analyser \
|
||||
-output releases/bliss-analyser
|
||||
|
||||
- name: Remove unused binaries
|
||||
run:
|
||||
rm releases/bliss-analyser-x86_64 releases/bliss-analyser-arm64
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
Generated
+6
-3
@@ -201,7 +201,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bliss-analyser"
|
||||
version = "0.5.0"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argparse",
|
||||
@@ -221,6 +221,7 @@ dependencies = [
|
||||
"rcue",
|
||||
"regex",
|
||||
"rusqlite",
|
||||
"serde_json",
|
||||
"substring",
|
||||
"ureq",
|
||||
"which 7.0.2",
|
||||
@@ -1479,13 +1480,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.109"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bliss-analyser"
|
||||
version = "0.5.0"
|
||||
version = "0.5.3"
|
||||
authors = ["Craig Drummond <craig.p.drummond@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
@@ -33,6 +33,7 @@ which = { version = "7.0.2", optional = true }
|
||||
rcue = { version = "0.1.3", optional = true }
|
||||
hhmmss = { version = "0.1.0", optional = true }
|
||||
ctrlc = "3.4"
|
||||
serde_json = "1.0.145"
|
||||
|
||||
[features]
|
||||
libav = ["bliss-audio/ffmpeg"]
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
0.5.3
|
||||
-----
|
||||
1. Fix stale case-only paths on case-insensitive filesystems - thanks to
|
||||
chrober for implementation.
|
||||
2. Fix invalid task warning with 'clearfail'.
|
||||
|
||||
0.5.2
|
||||
-----
|
||||
1. Fix Linux builds.
|
||||
|
||||
0.5.1
|
||||
-----
|
||||
1. Use serde_json to build JSONRPC notification strings.
|
||||
|
||||
0.5.0
|
||||
-----
|
||||
1. Update to bliss-rs 0.11.1
|
||||
|
||||
@@ -31,7 +31,7 @@ files, but has more limited codec support, and can fail on more files.
|
||||
To install dependencies on a Debian system:
|
||||
|
||||
```
|
||||
apt install -y clang libavcodec-dev libavformat-dev libavutil-dev libavfilter-dev libavdevice-dev pkg-config
|
||||
apt install -y clang libavcodec-dev libavformat-dev libavutil-dev libavfilter-dev libavdevice-dev pkg-config yasm
|
||||
```
|
||||
|
||||
To install dependencies on a Fedora system:
|
||||
@@ -57,7 +57,7 @@ their dependencies).
|
||||
To install dependencies on a Debian system:
|
||||
|
||||
```
|
||||
apt install -y clang libavcodec-dev libavformat-dev libavutil-dev libavfilter-dev libavdevice-dev pkg-config
|
||||
apt install -y clang libavcodec-dev libavformat-dev libavutil-dev libavfilter-dev libavdevice-dev pkg-config yasm
|
||||
```
|
||||
|
||||
To install dependencies on a Fedora system:
|
||||
|
||||
+10
-6
@@ -88,12 +88,15 @@ Command-line parameters
|
||||
* `-d` / `--db` Name and location of the database file.
|
||||
* `-l` / `--logging` Logging level; `trace`, `debug`, `info`, `warn`, `error`.
|
||||
Default is `info`.
|
||||
* `-k` / `--keep-old` When analysing tracks, `bliss-analyser` will remove any
|
||||
tracks specified in its database that are no-longer on the file-system. This
|
||||
parameter is used to prevent this.
|
||||
* `-k` / `--keep-old` When analysing tracks, `bliss-analyser` will remove database
|
||||
rows for tracks that are no longer on the filesystem. It also reconciles stored
|
||||
case-only path differences with the filesystem's exact spelling, preserving the
|
||||
existing analysis, and removes stale case-only duplicates. This option prevents
|
||||
all of those cleanup actions.
|
||||
* `-r` / `--dry-run` If this is supplied when analysing tracks, then no actual
|
||||
analysis will be performed, instead the logging will inform you how many new
|
||||
tracks are to be analysed and how many old tracks are left in the database.
|
||||
analysis or database cleanup will be performed, instead the logging will inform
|
||||
you how many new tracks are to be analysed, how many old tracks are left in the
|
||||
database, and which stored paths would have their filename casing corrected.
|
||||
* `-i` / `--ignore` Name and location of the file containing items to ignore.
|
||||
* `-L` / `--lms` Hostname, or IP address, of your LMS server.
|
||||
* `-J` / `--json` JSONRPC port number of your LMS server.
|
||||
@@ -102,7 +105,8 @@ tracks are to be analysed and how many old tracks are left in the database.
|
||||
files a `BLISS_ANALYSIS` tag (it present) in files.
|
||||
* `-W` / `--writetags` When using the `analyse` task, write analysis results to files
|
||||
within a `BLISS_ANALYSIS` tag.
|
||||
* `-p' / '--preserve` Attempt to preserve file modification time when writing tags.
|
||||
* `-p` / `--preserve` Attempt to preserve file modification time when writing tags.
|
||||
* `-t` / `--threads` Maximum number of threads to use for analysis.
|
||||
|
||||
Equivalent items specified in the INI config file (detailed above) will override
|
||||
any specified on the command-line.
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# Cross compilation environment for bliss-analyser
|
||||
|
||||
FROM debian:bookworm
|
||||
|
||||
RUN dpkg --add-architecture arm64 && \
|
||||
dpkg --add-architecture armhf
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get install -y curl git 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 pkg-config:arm64 \
|
||||
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 pkg-config:armhf \
|
||||
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-arm-libav.sh"]
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# Cross compilation environment for bliss-anlyser
|
||||
|
||||
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-arm-staticlibav.sh"]
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# Cross compilation environment for bliss-anlyser
|
||||
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get install -y curl git pkg-config
|
||||
RUN apt-get install -y build-essential yasm clang
|
||||
|
||||
RUN apt-get install -y libavutil-dev libavcodec-dev libavformat-dev \
|
||||
libavfilter-dev libavdevice-dev libswresample-dev libfftw3-dev \
|
||||
ffmpeg
|
||||
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin/:${PATH}"
|
||||
|
||||
RUN mkdir /build
|
||||
ENV CARGO_TARGET_DIR /build
|
||||
ENV CARGO_HOME /build/cache
|
||||
|
||||
RUN mkdir /src
|
||||
|
||||
WORKDIR /src
|
||||
CMD ["/src/docker/docker-build-x86-libav.sh"]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Cross compilation environment for bliss-anlyser
|
||||
|
||||
FROM debian:bullseye
|
||||
|
||||
RUN dpkg --add-architecture arm64 && \
|
||||
@@ -32,5 +30,5 @@ ENV CARGO_HOME /build/cache
|
||||
RUN mkdir /src
|
||||
|
||||
WORKDIR /src
|
||||
CMD ["/src/docker/docker-build-arm-libav.sh"]
|
||||
CMD ["/src/docker/docker-build-arm-ffmpeg.sh"]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# Cross compilation environment for bliss-anlyser
|
||||
|
||||
FROM debian:bullseye
|
||||
|
||||
RUN dpkg --add-architecture arm64 && \
|
||||
@@ -0,0 +1,30 @@
|
||||
FROM debian:bullseye
|
||||
|
||||
RUN dpkg --add-architecture amd64
|
||||
RUN apt-get update
|
||||
|
||||
RUN apt-get install -y curl git pkg-config yasm
|
||||
RUN apt-get install -y build-essential clang
|
||||
|
||||
# Download and extract musl-cross toolchain for x86_64
|
||||
RUN curl -LO https://musl.cc/x86_64-linux-musl-cross.tgz \
|
||||
&& tar -xzf x86_64-linux-musl-cross.tgz \
|
||||
&& mv x86_64-linux-musl-cross /opt/musl-toolchain \
|
||||
&& rm x86_64-linux-musl-cross.tgz
|
||||
|
||||
ENV PATH="/opt/musl-toolchain/bin:/root/.cargo/bin:${PATH}"
|
||||
|
||||
# Optionally, still install musl-tools (for musl-gcc wrapper, not strictly needed now)
|
||||
RUN apt-get install -y musl-tools musl-dev musl
|
||||
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
RUN rustup target add x86_64-unknown-linux-musl
|
||||
|
||||
RUN mkdir /build
|
||||
ENV CARGO_TARGET_DIR /build
|
||||
ENV CARGO_HOME /build/cache
|
||||
|
||||
RUN mkdir /src
|
||||
|
||||
WORKDIR /src
|
||||
CMD ["/src/docker/docker-build-x86-ffmpeg.sh"]
|
||||
@@ -0,0 +1,22 @@
|
||||
FROM debian:bullseye
|
||||
|
||||
RUN dpkg --add-architecture amd64
|
||||
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 musl-tools musl-dev musl
|
||||
|
||||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
ENV PATH="/root/.cargo/bin/:${PATH}"
|
||||
RUN rustup target add x86_64-unknown-linux-musl
|
||||
|
||||
RUN mkdir /build
|
||||
ENV CARGO_TARGET_DIR /build
|
||||
ENV CARGO_HOME /build/cache
|
||||
|
||||
RUN mkdir /src
|
||||
|
||||
WORKDIR /src
|
||||
CMD ["/src/docker/docker-build-x86-symphonia.sh"]
|
||||
|
||||
@@ -9,15 +9,15 @@ mkdir -p $DESTDIR/bin
|
||||
rm -rf $DESTDIR/bin/*
|
||||
|
||||
function build {
|
||||
echo Building for $1 to $3...
|
||||
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 --features=libav,staticlibav --target $1
|
||||
fi
|
||||
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 --features=libav,staticlibav --target $1
|
||||
fi
|
||||
|
||||
$2 /build/$1/release/bliss-analyser && cp /build/$1/release/bliss-analyser $DESTDIR/$3
|
||||
$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
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/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 --features=libav --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
|
||||
@@ -11,13 +11,11 @@ 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 --features=symphonia --target $1
|
||||
fi
|
||||
if [[ ! -f /build/$1/release/bliss-analyser ]]; then
|
||||
cargo build --release --features=symphonia --target $1
|
||||
fi
|
||||
|
||||
$2 /build/$1/release/bliss-analyser && cp /build/$1/release/bliss-analyser $DESTDIR/$3
|
||||
$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
|
||||
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/ash
|
||||
set -eux
|
||||
|
||||
uname -a
|
||||
DESTDIR=/src/releases
|
||||
mkdir -p $DESTDIR
|
||||
|
||||
function build {
|
||||
echo Building for $1 to $3...
|
||||
|
||||
if [[ ! -f /build/$1/release/bliss-analyser ]]; then
|
||||
cargo build --release --features=update-aubio-bindings,libav,staticlibav --target $1
|
||||
fi
|
||||
|
||||
$2 /build/$1/release/bliss-analyser && cp /build/$1/release/bliss-analyser $DESTDIR/$3
|
||||
}
|
||||
|
||||
build x86_64-unknown-linux-musl strip bliss-analyser
|
||||
|
||||
cp UserGuide.md $DESTDIR/README.md
|
||||
cp LICENSE $DESTDIR/
|
||||
cp configs/linux.ini $DESTDIR/config.ini
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
## #!/usr/bin/env bash
|
||||
set -eux
|
||||
|
||||
uname -a
|
||||
DESTDIR=/src/releases
|
||||
|
||||
mkdir -p $DESTDIR/bin
|
||||
rm -rf $DESTDIR/bin/*
|
||||
|
||||
export RUST_BACKTRACE=full
|
||||
cargo build --release --features=libav
|
||||
|
||||
strip /build/release/bliss-analyser && cp /build/release/bliss-analyser $DESTDIR/bliss-analyser
|
||||
|
||||
cp UserGuide.md $DESTDIR/README.md
|
||||
cp LICENSE $DESTDIR/
|
||||
cp configs/linux.ini $DESTDIR/config.ini
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
## #!/usr/bin/env bash
|
||||
set -eux
|
||||
|
||||
uname -a
|
||||
DESTDIR=/src/releases
|
||||
mkdir -p $DESTDIR
|
||||
|
||||
function build {
|
||||
echo Building for $1 to $3...
|
||||
|
||||
if [[ ! -f /build/$1/release/bliss-analyser ]]; then
|
||||
cargo build --release --features update-aubio-bindings,symphonia --target $1
|
||||
fi
|
||||
|
||||
$2 /build/$1/release/bliss-analyser && cp /build/$1/release/bliss-analyser $DESTDIR/$3
|
||||
}
|
||||
|
||||
build x86_64-unknown-linux-musl strip bliss-analyser
|
||||
|
||||
cp UserGuide.md $DESTDIR/README.md
|
||||
cp LICENSE $DESTDIR/
|
||||
cp configs/linux.ini $DESTDIR/config.ini
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# LMS-BlissMixer
|
||||
#
|
||||
# Copyright (c) 2022-2025 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
# Copyright (c) 2022-2026 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
# MIT license.
|
||||
#
|
||||
|
||||
|
||||
+6
-5
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Analyse music with Bliss
|
||||
*
|
||||
* Copyright (c) 2022-2025 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* Copyright (c) 2022-2026 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* GPLv3 license.
|
||||
*
|
||||
**/
|
||||
@@ -28,6 +28,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use std::thread;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use serde_json::json;
|
||||
|
||||
const DONT_ANALYSE: &str = ".notmusic";
|
||||
const MAX_ERRORS_TO_SHOW: usize = 100;
|
||||
@@ -57,9 +58,9 @@ fn handle_ctrl_c() {
|
||||
}
|
||||
|
||||
fn send_notif_msg(notifs: &mut NotifInfo, text: &str) {
|
||||
let json = &format!("{{\"id\":1, \"method\":\"slim.request\",\"params\":[\"\",[\"blissmixer\",\"analyser\",\"act:update\",\"msg:{}\"]]}}", text);
|
||||
let js = json!({"id":"1", "method":"slim.request", "params":["", ["blissmixer", "analyser", "act:update", format!("msg:{}", text)]]});
|
||||
log::info!("Sending notif to LMS: {}", text);
|
||||
let _ = ureq::post(¬ifs.address).send_string(&json);
|
||||
let _ = ureq::post(¬ifs.address).send_string(&js.to_string());
|
||||
}
|
||||
|
||||
fn send_notif(notifs: &mut NotifInfo, text: &str) {
|
||||
@@ -392,12 +393,12 @@ pub fn analyse_files(db_path: &str, mpaths: &Vec<PathBuf>, dry_run: bool, keep_o
|
||||
|
||||
db.init();
|
||||
|
||||
let mut changes_made = false;
|
||||
if !keep_old {
|
||||
send_notif(&mut notifs, "Removing old files from DB");
|
||||
db.remove_old(mpaths, dry_run);
|
||||
changes_made = db.remove_old(mpaths, dry_run);
|
||||
}
|
||||
|
||||
let mut changes_made = false;
|
||||
for path in mpaths {
|
||||
let mpath = path.clone();
|
||||
let cur = path.clone();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Analyse music with Bliss
|
||||
*
|
||||
* Copyright (c) 2022-2025 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* Copyright (c) 2022-2026 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* GPLv3 license.
|
||||
*
|
||||
**/
|
||||
@@ -10,10 +10,12 @@ use crate::tags;
|
||||
use bliss_audio::{Analysis, AnalysisIndex, FeaturesVersion};
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use rusqlite::{params, Connection};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::process;
|
||||
use std::thread;
|
||||
use std::thread::JoinHandle;
|
||||
@@ -76,6 +78,159 @@ pub struct Db {
|
||||
pub conn: Connection,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum PathResolution {
|
||||
Exact,
|
||||
Corrected(String),
|
||||
Missing,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
struct ExactPathResolver {
|
||||
roots: Vec<PathBuf>,
|
||||
directory_entries: HashMap<PathBuf, Option<DirectoryEntries>>,
|
||||
}
|
||||
|
||||
struct DirectoryEntries {
|
||||
exact: HashSet<OsString>,
|
||||
case_insensitive: HashMap<String, Option<OsString>>,
|
||||
}
|
||||
|
||||
impl ExactPathResolver {
|
||||
fn new(roots: &Vec<PathBuf>) -> Self {
|
||||
Self {
|
||||
roots: roots.to_vec(),
|
||||
directory_entries: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn entries(&mut self, directory: &Path) -> Option<&DirectoryEntries> {
|
||||
if !self.directory_entries.contains_key(directory) {
|
||||
let entries = match fs::read_dir(directory) {
|
||||
Ok(entries) => {
|
||||
let names = entries.map(|entry| entry.map(|entry| entry.file_name())).collect::<std::io::Result<Vec<_>>>();
|
||||
match names {
|
||||
Ok(names) => {
|
||||
let mut exact = HashSet::with_capacity(names.len());
|
||||
let mut case_insensitive = HashMap::with_capacity(names.len());
|
||||
|
||||
for name in names {
|
||||
exact.insert(name.clone());
|
||||
if let Some(entry_text) = name.to_str() {
|
||||
let folded = entry_text.to_lowercase();
|
||||
match case_insensitive.get_mut(&folded) {
|
||||
Some(existing) => *existing = None,
|
||||
None => {
|
||||
case_insensitive.insert(folded, Some(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(DirectoryEntries {
|
||||
exact,
|
||||
case_insensitive,
|
||||
})
|
||||
}
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
Err(_) => None,
|
||||
};
|
||||
self.directory_entries
|
||||
.insert(directory.to_path_buf(), entries);
|
||||
}
|
||||
self.directory_entries
|
||||
.get(directory)
|
||||
.and_then(|entries| entries.as_ref())
|
||||
}
|
||||
|
||||
fn resolve_under_root(&mut self, root: &Path, relative: &Path) -> PathResolution {
|
||||
let mut current = root.to_path_buf();
|
||||
let mut resolved = PathBuf::new();
|
||||
let mut corrected = false;
|
||||
for component in relative.components() {
|
||||
let requested = match component {
|
||||
Component::CurDir => continue,
|
||||
Component::Normal(name) => name,
|
||||
_ => return PathResolution::Unknown,
|
||||
};
|
||||
let entries = match self.entries(¤t) {
|
||||
Some(entries) => entries,
|
||||
None => return PathResolution::Unknown,
|
||||
};
|
||||
let actual = if entries.exact.contains(requested) {
|
||||
requested.to_os_string()
|
||||
} else {
|
||||
let folded = match requested.to_str() {
|
||||
Some(requested) => requested.to_lowercase(),
|
||||
None => return PathResolution::Missing,
|
||||
};
|
||||
let actual = match entries.case_insensitive.get(&folded) {
|
||||
Some(Some(actual)) => actual.clone(),
|
||||
Some(None) => return PathResolution::Unknown,
|
||||
None => return PathResolution::Missing,
|
||||
};
|
||||
corrected = true;
|
||||
actual
|
||||
};
|
||||
current.push(&actual);
|
||||
resolved.push(actual);
|
||||
}
|
||||
if resolved.as_os_str().is_empty() {
|
||||
PathResolution::Unknown
|
||||
} else if corrected {
|
||||
PathResolution::Corrected(normalize_db_path(&resolved))
|
||||
} else {
|
||||
PathResolution::Exact
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve(&mut self, db_path: &str) -> PathResolution {
|
||||
let (file_path, cue_suffix) = match db_path.find(CUE_MARKER) {
|
||||
Some(index) => (&db_path[..index], &db_path[index..]),
|
||||
None => (db_path, ""),
|
||||
};
|
||||
let relative = PathBuf::from(file_path);
|
||||
let mut correction: Option<String> = None;
|
||||
let mut unknown = false;
|
||||
for root in self.roots.clone() {
|
||||
match self.resolve_under_root(&root, &relative) {
|
||||
PathResolution::Exact => return PathResolution::Exact,
|
||||
PathResolution::Corrected(path) => {
|
||||
let candidate = format!("{}{}", path, cue_suffix);
|
||||
match correction.as_ref() {
|
||||
Some(existing) if existing != &candidate => {
|
||||
unknown = true;
|
||||
}
|
||||
_ => {
|
||||
correction = Some(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
PathResolution::Unknown => unknown = true,
|
||||
PathResolution::Missing => {}
|
||||
}
|
||||
}
|
||||
if unknown {
|
||||
PathResolution::Unknown
|
||||
} else if let Some(path) = correction {
|
||||
PathResolution::Corrected(path)
|
||||
} else {
|
||||
PathResolution::Missing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_db_path(path: &Path) -> String {
|
||||
let path = path.to_string_lossy().to_string();
|
||||
if cfg!(windows) {
|
||||
path.replace("\\", "/")
|
||||
} else {
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
impl Db {
|
||||
pub fn new(path: &String) -> Self {
|
||||
match Connection::open(path) {
|
||||
@@ -230,9 +385,11 @@ impl Db {
|
||||
let _ = self.conn.execute("DELETE FROM Failures WHERE File = ?;", params![path]);
|
||||
}
|
||||
|
||||
pub fn remove_old(&self, mpaths: &Vec<PathBuf>, dry_run: bool) {
|
||||
self.remove_old_from_table(mpaths, dry_run, "TracksV2");
|
||||
self.remove_old_from_table(mpaths, dry_run, "Failures");
|
||||
pub fn remove_old(&self, mpaths: &Vec<PathBuf>, dry_run: bool) -> bool {
|
||||
let mut resolver = ExactPathResolver::new(mpaths);
|
||||
let tracks_changed = self.remove_old_from_table(dry_run, "TracksV2", &mut resolver);
|
||||
let failures_changed = self.remove_old_from_table(dry_run, "Failures", &mut resolver);
|
||||
tracks_changed || failures_changed
|
||||
}
|
||||
|
||||
fn clear_failures(&self) {
|
||||
@@ -254,64 +411,85 @@ impl Db {
|
||||
ts
|
||||
}
|
||||
|
||||
fn remove_old_from_table(&self, mpaths: &Vec<PathBuf>, dry_run: bool, table: &str) {
|
||||
log::info!("Looking for non-existent tracks");
|
||||
let mut stmt = self.conn.prepare(format!("SELECT File FROM {};", table).as_str()).unwrap();
|
||||
let track_iter = stmt.query_map([], |row| Ok((row.get(0)?,))).unwrap();
|
||||
fn remove_old_from_table(&self, dry_run: bool, table: &str, resolver: &mut ExactPathResolver) -> bool {
|
||||
log::info!("Looking for missing or case-mismatched paths in {}", table);
|
||||
let mut stmt = self.conn.prepare(format!("SELECT File FROM {} ORDER BY File;", table).as_str()).unwrap();
|
||||
let file_iter = stmt.query_map([], |row| Ok((row.get(0)?,))).unwrap();
|
||||
let mut files: Vec<String> = Vec::new();
|
||||
for file in file_iter {
|
||||
files.push(file.unwrap().0);
|
||||
}
|
||||
|
||||
let known_paths: HashSet<String> = files.iter().cloned().collect();
|
||||
let mut claimed_paths: HashSet<String> = HashSet::new();
|
||||
let mut to_remove: Vec<String> = Vec::new();
|
||||
for tr in track_iter {
|
||||
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);
|
||||
let mut to_correct: Vec<(String, String)> = Vec::new();
|
||||
let mut unverified_count = 0;
|
||||
|
||||
for original in files {
|
||||
match resolver.resolve(&original) {
|
||||
PathResolution::Exact => {}
|
||||
PathResolution::Corrected(corrected) => {
|
||||
if known_paths.contains(&corrected)
|
||||
|| !claimed_paths.insert(corrected.clone())
|
||||
{
|
||||
to_remove.push(original);
|
||||
} else {
|
||||
to_correct.push((original, corrected));
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if cfg!(windows) {
|
||||
db_path = db_path.replace("/", "\\");
|
||||
}
|
||||
let mut exists = false;
|
||||
|
||||
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;
|
||||
PathResolution::Missing => to_remove.push(original),
|
||||
PathResolution::Unknown => {
|
||||
unverified_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
to_remove.push(orig_path);
|
||||
}
|
||||
}
|
||||
|
||||
let num_to_remove = to_remove.len();
|
||||
log::info!("Num non-existent tracks: {}", num_to_remove);
|
||||
if num_to_remove > 0 {
|
||||
if dry_run {
|
||||
let num_to_correct = to_correct.len();
|
||||
log::info!("Num missing/duplicate paths: {}", num_to_remove);
|
||||
log::info!("Num case-only path corrections: {}", num_to_correct);
|
||||
if unverified_count > 0 {
|
||||
log::warn!(
|
||||
"Could not verify exact filesystem spelling for {} path(s) in {}; keeping those database rows",
|
||||
unverified_count,
|
||||
table
|
||||
);
|
||||
}
|
||||
if dry_run {
|
||||
if num_to_remove > 0 {
|
||||
log::info!("The following need to be removed from database:");
|
||||
for t in to_remove {
|
||||
log::info!(" {}", t);
|
||||
for path in &to_remove {
|
||||
log::info!(" {}", path);
|
||||
}
|
||||
} else {
|
||||
let count_before = self.get_track_count();
|
||||
for t in to_remove {
|
||||
//log::debug!("Remove '{}'", t);
|
||||
let cmd = self.conn.execute(format!("DELETE FROM {} WHERE File = ?;", table).as_str(), params![t]);
|
||||
}
|
||||
if num_to_correct > 0 {
|
||||
log::info!("The following stored path spellings need correction:");
|
||||
for (from, to) in &to_correct {
|
||||
log::info!(" {} -> {}", from, to);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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 mut changed = false;
|
||||
for (from, to) in to_correct {
|
||||
let cmd = self.conn.execute(format!("UPDATE {} SET File = ? WHERE File = ?;", table).as_str(), params![to, from]);
|
||||
match cmd {
|
||||
Ok(count) => changed |= count > 0,
|
||||
Err(error) => {
|
||||
log::error!("Failed to correct '{}' to '{}' - {}", from, to, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
for path in to_remove {
|
||||
let cmd = self.conn.execute(format!("DELETE FROM {} WHERE File = ?;", table).as_str(), params![path]);
|
||||
match cmd {
|
||||
Ok(count) => changed |= count > 0,
|
||||
Err(error) => log::error!("Failed to remove '{}' - {}", path, error),
|
||||
}
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
pub fn get_track_count(&self) -> usize {
|
||||
@@ -561,3 +739,165 @@ pub fn update_ignore(db_path: &str, ignore_path: &PathBuf) {
|
||||
|
||||
db.close();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
struct TestMusicRoot {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
impl TestMusicRoot {
|
||||
fn new() -> Self {
|
||||
let unique = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let path = std::env::temp_dir().join(format!(
|
||||
"bliss-analyser-path-test-{}-{}",
|
||||
process::id(),
|
||||
unique
|
||||
));
|
||||
fs::create_dir_all(&path).unwrap();
|
||||
Self { path }
|
||||
}
|
||||
|
||||
fn create_file(&self, relative: &str) {
|
||||
let path = self.path.join(relative);
|
||||
fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
File::create(path).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestMusicRoot {
|
||||
fn drop(&mut self) {
|
||||
let _ = fs::remove_dir_all(&self.path);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_db() -> Db {
|
||||
let db = Db {
|
||||
conn: Connection::open_in_memory().unwrap(),
|
||||
};
|
||||
db.init();
|
||||
db
|
||||
}
|
||||
|
||||
fn insert_track(db: &Db, path: &str) -> i64 {
|
||||
db.conn
|
||||
.execute("INSERT INTO TracksV2 (File) VALUES (?);", params![path])
|
||||
.unwrap();
|
||||
db.conn.last_insert_rowid()
|
||||
}
|
||||
|
||||
fn track_paths(db: &Db) -> Vec<(i64, String)> {
|
||||
let mut statement = db
|
||||
.conn
|
||||
.prepare("SELECT rowid, File FROM TracksV2 ORDER BY File;")
|
||||
.unwrap();
|
||||
statement
|
||||
.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))
|
||||
.unwrap()
|
||||
.map(|row| row.unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn corrects_case_only_path_in_place() {
|
||||
let music = TestMusicRoot::new();
|
||||
music.create_file("Artist/Album/Track.mp3");
|
||||
let db = test_db();
|
||||
let original_rowid = insert_track(&db, "artist/album/track.mp3");
|
||||
|
||||
assert!(db.remove_old(&vec![music.path.clone()], false));
|
||||
assert_eq!(
|
||||
vec![(original_rowid, "Artist/Album/Track.mp3".to_string())],
|
||||
track_paths(&db)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removes_stale_case_variant_when_exact_row_exists() {
|
||||
let music = TestMusicRoot::new();
|
||||
music.create_file("Artist/Album/Track.mp3");
|
||||
let db = test_db();
|
||||
insert_track(&db, "artist/album/track.mp3");
|
||||
let exact_rowid = insert_track(&db, "Artist/Album/Track.mp3");
|
||||
|
||||
assert!(db.remove_old(&vec![music.path.clone()], false));
|
||||
assert_eq!(
|
||||
vec![(exact_rowid, "Artist/Album/Track.mp3".to_string())],
|
||||
track_paths(&db)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dry_run_reports_without_mutating() {
|
||||
let music = TestMusicRoot::new();
|
||||
music.create_file("Artist/Album/Track.mp3");
|
||||
let db = test_db();
|
||||
let original_rowid = insert_track(&db, "artist/album/track.mp3");
|
||||
|
||||
assert!(!db.remove_old(&vec![music.path.clone()], true));
|
||||
assert_eq!(
|
||||
vec![(original_rowid, "artist/album/track.mp3".to_string())],
|
||||
track_paths(&db)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn corrects_case_without_losing_cue_track_suffix() {
|
||||
let music = TestMusicRoot::new();
|
||||
music.create_file("Artist/Album/Image.flac");
|
||||
let db = test_db();
|
||||
let original_rowid = insert_track(&db, "artist/album/image.flac.CUE_TRACK.02");
|
||||
|
||||
assert!(db.remove_old(&vec![music.path.clone()], false));
|
||||
assert_eq!(
|
||||
vec![(
|
||||
original_rowid,
|
||||
"Artist/Album/Image.flac.CUE_TRACK.02".to_string()
|
||||
)],
|
||||
track_paths(&db)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removes_path_that_is_really_missing() {
|
||||
let music = TestMusicRoot::new();
|
||||
let db = test_db();
|
||||
insert_track(&db, "Artist/Album/Missing.mp3");
|
||||
|
||||
assert!(db.remove_old(&vec![music.path.clone()], false));
|
||||
assert!(track_paths(&db).is_empty());
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn keeps_distinct_files_that_differ_only_by_case() {
|
||||
let music = TestMusicRoot::new();
|
||||
music.create_file("Artist/Track.mp3");
|
||||
music.create_file("artist/track.mp3");
|
||||
let entries: HashSet<OsString> = fs::read_dir(&music.path)
|
||||
.unwrap()
|
||||
.map(|entry| entry.unwrap().file_name())
|
||||
.collect();
|
||||
if entries.len() != 2 {
|
||||
return;
|
||||
}
|
||||
let db = test_db();
|
||||
let first_rowid = insert_track(&db, "Artist/Track.mp3");
|
||||
let second_rowid = insert_track(&db, "artist/track.mp3");
|
||||
|
||||
assert!(!db.remove_old(&vec![music.path.clone()], false));
|
||||
assert_eq!(
|
||||
vec![
|
||||
(first_rowid, "Artist/Track.mp3".to_string()),
|
||||
(second_rowid, "artist/track.mp3".to_string())
|
||||
],
|
||||
track_paths(&db)
|
||||
);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Analyse music with Bliss
|
||||
*
|
||||
* Copyright (c) 2022-2025 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* Copyright (c) 2022-2026 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* GPLv3 license.
|
||||
*
|
||||
**/
|
||||
@@ -111,12 +111,12 @@ fn main() {
|
||||
}
|
||||
|
||||
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("export") && !task.eq_ignore_ascii_case("stopmixer") && !task.eq_ignore_ascii_case("analyse-lms") {
|
||||
&& !task.eq_ignore_ascii_case("upload") && !task.eq_ignore_ascii_case("export") && !task.eq_ignore_ascii_case("clearfail")
|
||||
&& !task.eq_ignore_ascii_case("stopmixer") && !task.eq_ignore_ascii_case("analyse-lms") {
|
||||
log::error!("Invalid task ({}) supplied", task);
|
||||
process::exit(-1);
|
||||
}
|
||||
|
||||
|
||||
if !config_file.is_empty() {
|
||||
let path = PathBuf::from(&config_file);
|
||||
if path.exists() && path.is_file() {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Analyse music with Bliss
|
||||
*
|
||||
* Copyright (c) 2022-2025 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* Copyright (c) 2022-2026 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* GPLv3 license.
|
||||
*
|
||||
**/
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Analyse music with Bliss
|
||||
*
|
||||
* Copyright (c) 2022-2025 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* Copyright (c) 2022-2026 Craig Drummond <craig.p.drummond@gmail.com>
|
||||
* GPLv3 license.
|
||||
*
|
||||
**/
|
||||
|
||||
Reference in new issue
Block a user