sounddiff 0.2.0__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.github/workflows/ci.yml +6 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/CHANGELOG.md +12 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/CLAUDE.md +7 -3
- {sounddiff-0.2.0 → sounddiff-0.2.1}/PKG-INFO +1 -1
- {sounddiff-0.2.0 → sounddiff-0.2.1}/pyproject.toml +1 -1
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/__init__.py +1 -1
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/formats.py +16 -1
- {sounddiff-0.2.0 → sounddiff-0.2.1}/tests/test_formats.py +29 -0
- sounddiff-0.2.0/.github/workflows/labeler.yml +0 -17
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.coderabbit.yaml +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.github/CODEOWNERS +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.github/FUNDING.yml +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.github/SECURITY.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.github/labeler.yml +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.github/workflows/release.yml +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.gitignore +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/.pre-commit-config.yaml +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/CODE_OF_CONDUCT.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/CONTRIBUTING.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/LICENSE +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/README.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/docs/api.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/docs/architecture.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/docs/cli-2.0-design.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/docs/contributing.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/docs/index.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/docs/install.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/docs/usage.md +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/examples/basic_comparison.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/examples/batch_compare.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/examples/ci_regression_test.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/scripts/generate_test_audio.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/__main__.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/cli.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/core.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/detection.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/loudness.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/report.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/spectral.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/temporal.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/threshold.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/types.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/src/sounddiff/voice.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/templates/report.html.j2 +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/tests/__init__.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/tests/conftest.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/tests/test_cli.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/tests/test_detection.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/tests/test_loudness.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/tests/test_report.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/tests/test_spectral.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/tests/test_temporal.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/tests/test_threshold.py +0 -0
- {sounddiff-0.2.0 → sounddiff-0.2.1}/tests/test_voice.py +0 -0
|
@@ -11,6 +11,12 @@ jobs:
|
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
steps:
|
|
13
13
|
- uses: actions/checkout@v4
|
|
14
|
+
with:
|
|
15
|
+
fetch-depth: 0
|
|
16
|
+
- name: Scan for secrets
|
|
17
|
+
uses: trufflesecurity/trufflehog@main
|
|
18
|
+
with:
|
|
19
|
+
extra_args: --only-verified
|
|
14
20
|
- uses: actions/setup-python@v5
|
|
15
21
|
with:
|
|
16
22
|
python-version: "3.13"
|
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.1] - 2026-03-27
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Compressed files (MP3, AAC, etc.) processed via ffmpeg now display their original format name instead of "WAV" (by @jaimefgdev, #62)
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- CI consolidated to a single check with TruffleHog secrets scanning
|
|
17
|
+
|
|
18
|
+
[0.2.1]: https://github.com/systemblueteam/sounddiff/compare/v0.2.0...v0.2.1
|
|
19
|
+
|
|
8
20
|
## [0.2.0] - 2026-03-27
|
|
9
21
|
|
|
10
22
|
### Added
|
|
@@ -47,7 +47,12 @@ src/sounddiff/
|
|
|
47
47
|
## CI & Release
|
|
48
48
|
|
|
49
49
|
- CI: one `ci` job, lint + test on Python 3.13, Ubuntu only.
|
|
50
|
-
- Releases are tag-based.
|
|
50
|
+
- Releases are tag-based. Full flow:
|
|
51
|
+
1. Bump version in BOTH `pyproject.toml` AND `src/sounddiff/__init__.py`
|
|
52
|
+
2. Update CHANGELOG.md (Keep a Changelog format)
|
|
53
|
+
3. PR the version bump. Branch protection requires 1 review -- use `gh pr merge --admin` since self-approval is blocked.
|
|
54
|
+
4. `git tag v0.x.0 && git push --tags` triggers build, PyPI publish, and GitHub Release.
|
|
55
|
+
5. Edit the auto-generated release: `gh release edit v0.x.0 --repo systemblueteam/sounddiff --notes "..."` -- the CI-generated notes are basic; write proper ones with contributor credits.
|
|
51
56
|
- PyPI trusted publisher is configured. Never use API tokens for uploads.
|
|
52
57
|
- CodeRabbit reviews all PRs including `.github/` files.
|
|
53
58
|
|
|
@@ -66,11 +71,10 @@ src/sounddiff/
|
|
|
66
71
|
## Project
|
|
67
72
|
|
|
68
73
|
- [Issues](https://github.com/systemblueteam/sounddiff/issues) | [Wiki](https://github.com/systemblueteam/sounddiff/wiki)
|
|
69
|
-
- Milestones: v0.
|
|
74
|
+
- Milestones: v0.3.0 (integrations), v0.4.0 (TBD)
|
|
70
75
|
|
|
71
76
|
## Gotchas
|
|
72
77
|
|
|
73
78
|
- `pip` doesn't work system-wide on macOS. Use `pipx install sounddiff` for global CLI access, or work inside the venv.
|
|
74
79
|
- Spectral delta_db clamps energy to -100 dB floor before computing deltas. Without this, noise floor differences produce absurd values (+80 dB).
|
|
75
80
|
- Rich Console with `record=True` still writes to stdout. Must pass `file=io.StringIO()` to capture without printing.
|
|
76
|
-
- `--verbose` flag exists in the CLI but isn't wired to anything yet.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sounddiff
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Structured audio comparison for producers and developers. Think git diff, but for audio.
|
|
5
5
|
Project-URL: Homepage, https://github.com/systemblueteam/sounddiff
|
|
6
6
|
Project-URL: Documentation, https://github.com/systemblueteam/sounddiff/tree/main/docs
|
|
@@ -21,6 +21,15 @@ NATIVE_FORMATS = {".wav", ".flac", ".ogg", ".aiff", ".aif"}
|
|
|
21
21
|
# Formats that require ffmpeg
|
|
22
22
|
FFMPEG_FORMATS = {".mp3", ".aac", ".m4a", ".wma", ".opus"}
|
|
23
23
|
|
|
24
|
+
# Mapping for original format display names in metadata
|
|
25
|
+
FORMAT_DISPLAY_NAMES: dict[str, str] = {
|
|
26
|
+
".mp3": "MP3",
|
|
27
|
+
".aac": "AAC",
|
|
28
|
+
".m4a": "AAC",
|
|
29
|
+
".wma": "WMA",
|
|
30
|
+
".opus": "Opus",
|
|
31
|
+
}
|
|
32
|
+
|
|
24
33
|
|
|
25
34
|
def _remove_if_exists(path: str) -> None:
|
|
26
35
|
"""Remove a file if it exists, silently ignoring missing files."""
|
|
@@ -100,6 +109,12 @@ def load_audio(path: str | Path) -> tuple[np.ndarray, AudioMetadata]:
|
|
|
100
109
|
except RuntimeError as e:
|
|
101
110
|
raise RuntimeError(f"Cannot read audio file: {original_filepath} ({e})") from e
|
|
102
111
|
|
|
112
|
+
if original_filepath != read_filepath:
|
|
113
|
+
ext = original_filepath.suffix.lower()
|
|
114
|
+
display_format = FORMAT_DISPLAY_NAMES.get(ext, ext.lstrip(".").upper())
|
|
115
|
+
else:
|
|
116
|
+
display_format = info.format
|
|
117
|
+
|
|
103
118
|
try:
|
|
104
119
|
file_size: int | None = original_filepath.stat().st_size
|
|
105
120
|
except OSError:
|
|
@@ -111,7 +126,7 @@ def load_audio(path: str | Path) -> tuple[np.ndarray, AudioMetadata]:
|
|
|
111
126
|
sample_rate=sample_rate,
|
|
112
127
|
channels=data.shape[1],
|
|
113
128
|
bit_depth=_subtype_to_bits(info.subtype),
|
|
114
|
-
format_name=
|
|
129
|
+
format_name=display_format,
|
|
115
130
|
frames=len(data),
|
|
116
131
|
file_size=file_size,
|
|
117
132
|
)
|
|
@@ -56,6 +56,35 @@ class TestLoadAudio:
|
|
|
56
56
|
data, meta = load_audio(FIXTURES_DIR / "sine_a.wav")
|
|
57
57
|
assert meta.frames == len(data)
|
|
58
58
|
|
|
59
|
+
def test_transcoded_format_name_is_correct(self, tmp_path: Path) -> None:
|
|
60
|
+
"""Verifies that transcoded files show their original format name, not WAV."""
|
|
61
|
+
from unittest.mock import patch
|
|
62
|
+
|
|
63
|
+
# 1. Creamos un archivo MP3 falso para pasar la validación de path.exists()
|
|
64
|
+
fake_mp3 = tmp_path / "test.mp3"
|
|
65
|
+
fake_mp3.write_text("fake audio content")
|
|
66
|
+
|
|
67
|
+
# 2. Simulamos que ffmpeg existe y que la lectura del WAV temporal funciona
|
|
68
|
+
with (
|
|
69
|
+
patch("sounddiff.formats.shutil.which", return_value="ffmpeg"),
|
|
70
|
+
patch("sounddiff.formats.subprocess.run"),
|
|
71
|
+
patch("sounddiff.formats.sf.info") as mock_info,
|
|
72
|
+
patch("sounddiff.formats.sf.read") as mock_read,
|
|
73
|
+
):
|
|
74
|
+
# Configuramos el mock para simular lo que devolvería el WAV temporal
|
|
75
|
+
class MockInfo:
|
|
76
|
+
format = "WAV"
|
|
77
|
+
subtype = "PCM_16"
|
|
78
|
+
|
|
79
|
+
mock_info.return_value = MockInfo()
|
|
80
|
+
mock_read.return_value = (np.zeros((100, 2), dtype=np.float64), 44100)
|
|
81
|
+
|
|
82
|
+
# 3. Llamamos a tu función
|
|
83
|
+
_, meta = load_audio(fake_mp3)
|
|
84
|
+
|
|
85
|
+
# 4. LA COMPROBACIÓN FINAL: Debe decir MP3 y no WAV
|
|
86
|
+
assert meta.format_name == "MP3"
|
|
87
|
+
|
|
59
88
|
def test_file_size_is_populated(self) -> None:
|
|
60
89
|
_, meta = load_audio(FIXTURES_DIR / "sine_a.wav")
|
|
61
90
|
assert meta.file_size is not None
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
name: Labeler
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
types: [opened, synchronize]
|
|
6
|
-
|
|
7
|
-
permissions:
|
|
8
|
-
contents: read
|
|
9
|
-
pull-requests: write
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
label:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
steps:
|
|
15
|
-
- uses: actions/labeler@v5
|
|
16
|
-
with:
|
|
17
|
-
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|