taters 0.1.0__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.
Files changed (50) hide show
  1. taters-0.1.0/LICENSE +21 -0
  2. taters-0.1.0/PKG-INFO +161 -0
  3. taters-0.1.0/README.MD +131 -0
  4. taters-0.1.0/pyproject.toml +72 -0
  5. taters-0.1.0/setup.cfg +4 -0
  6. taters-0.1.0/src/taters/Taters.py +115 -0
  7. taters-0.1.0/src/taters/__init__.py +0 -0
  8. taters-0.1.0/src/taters/audio/__init__.py +0 -0
  9. taters-0.1.0/src/taters/audio/convert_to_wav.py +112 -0
  10. taters-0.1.0/src/taters/audio/diarize_with_thirdparty.py +5 -0
  11. taters-0.1.0/src/taters/audio/diarizer/__init__.py +0 -0
  12. taters-0.1.0/src/taters/audio/diarizer/whisper-diarization/__init__.py +0 -0
  13. taters-0.1.0/src/taters/audio/diarizer/whisper-diarization/diarization/__init__.py +3 -0
  14. taters-0.1.0/src/taters/audio/diarizer/whisper-diarization/diarization/msdd/msdd.py +100 -0
  15. taters-0.1.0/src/taters/audio/diarizer/whisper-diarization/diarize.py +247 -0
  16. taters-0.1.0/src/taters/audio/diarizer/whisper-diarization/diarize_custom.py +374 -0
  17. taters-0.1.0/src/taters/audio/diarizer/whisper-diarization/diarize_parallel.py +269 -0
  18. taters-0.1.0/src/taters/audio/diarizer/whisper-diarization/helpers.py +552 -0
  19. taters-0.1.0/src/taters/audio/diarizer/whisper_diar_wrapper.py +293 -0
  20. taters-0.1.0/src/taters/audio/extract_wav_from_video.py +177 -0
  21. taters-0.1.0/src/taters/audio/extract_whisper_embeddings.py +243 -0
  22. taters-0.1.0/src/taters/audio/extract_whisper_embeddings_subproc.py +574 -0
  23. taters-0.1.0/src/taters/audio/split_wav_by_speaker.py +182 -0
  24. taters-0.1.0/src/taters/helpers/__init__.py +0 -0
  25. taters-0.1.0/src/taters/helpers/feature_gather.py +349 -0
  26. taters-0.1.0/src/taters/helpers/find_files.py +222 -0
  27. taters-0.1.0/src/taters/helpers/text_gather.py +520 -0
  28. taters-0.1.0/src/taters/pipelines/__init__.py +0 -0
  29. taters-0.1.0/src/taters/pipelines/run_pipeline.py +384 -0
  30. taters-0.1.0/src/taters/text/analyze_with_archetypes.py +238 -0
  31. taters-0.1.0/src/taters/text/analyze_with_dictionaries.py +246 -0
  32. taters-0.1.0/src/taters/text/dictionary_analyzers/__init__.py +0 -0
  33. taters-0.1.0/src/taters/text/dictionary_analyzers/multi_archetype_analyzer.py +125 -0
  34. taters-0.1.0/src/taters/text/dictionary_analyzers/multi_dict_analyzer.py +254 -0
  35. taters-0.1.0/src/taters/text/extract_sentence_embeddings.py +329 -0
  36. taters-0.1.0/src/taters/video/__init__.py +0 -0
  37. taters-0.1.0/src/taters/video/extract_features.py +0 -0
  38. taters-0.1.0/src/taters/video/features_basic.py +0 -0
  39. taters-0.1.0/src/taters/video/features_motion.py +0 -0
  40. taters-0.1.0/src/taters/video/features_ocr.py +0 -0
  41. taters-0.1.0/src/taters/video/features_shots.py +0 -0
  42. taters-0.1.0/src/taters/video/read_video.py +0 -0
  43. taters-0.1.0/src/taters/video/windowing.py +0 -0
  44. taters-0.1.0/src/taters.egg-info/PKG-INFO +161 -0
  45. taters-0.1.0/src/taters.egg-info/SOURCES.txt +48 -0
  46. taters-0.1.0/src/taters.egg-info/dependency_links.txt +1 -0
  47. taters-0.1.0/src/taters.egg-info/entry_points.txt +2 -0
  48. taters-0.1.0/src/taters.egg-info/not-zip-safe +1 -0
  49. taters-0.1.0/src/taters.egg-info/requires.txt +14 -0
  50. taters-0.1.0/src/taters.egg-info/top_level.txt +1 -0
taters-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ryan L. Boyd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
taters-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.4
2
+ Name: taters
3
+ Version: 0.1.0
4
+ Summary: Analyze, process, and extract from many types of input data. Highly modular/customizable.
5
+ Author-email: "Ryan L. Boyd" <ryan@ryanboyd.io>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ryanboyd/taters
8
+ Project-URL: Issues, https://github.com/ryanboyd/taters/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
13
+ Classifier: Intended Audience :: Science/Research
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: faster-whisper>=1.1.0
18
+ Requires-Dist: transformers>=4.38.0
19
+ Requires-Dist: librosa>=0.10.1
20
+ Requires-Dist: pydub>=0.25.1
21
+ Requires-Dist: contentcoder
22
+ Requires-Dist: archetyper
23
+ Requires-Dist: nltk
24
+ Requires-Dist: sentence-transformers
25
+ Provides-Extra: diarization
26
+ Requires-Dist: nemo-toolkit[asr]>=2.dev; extra == "diarization"
27
+ Provides-Extra: cuda
28
+ Requires-Dist: nvidia-cudnn-cu12; extra == "cuda"
29
+ Dynamic: license-file
30
+
31
+ # TATERS — Takes All Things, Extracts Relevant Stuff
32
+
33
+ Taters is a broad-scope toolkit for researchers that can be used to extract features from multiple types of data (video, audio, text) into clean, analysis-ready artifacts and features. Think of it as a small, dependable kitchen crew for your data: you bring potatoes (files), it handles the peeling, chopping, and plating.
34
+
35
+ **Status:** active WIP. It works today, but expect some rough edges and breaking changes as the project grows.
36
+
37
+ ---
38
+
39
+ ## What Taters is (and isn't)
40
+
41
+ Taters is **a library and a CLI** for end-to-end A/V + text processing with predictable outputs. It's **not** a monolithic "black box" pipeline — each step is a clear, reusable function you can run on its own or string together with YAML presets.
42
+
43
+ ---
44
+
45
+ ## What you can do with it (high level)
46
+
47
+ Note: everything below is currently implemented, but is highly subject to change as the project evolves.
48
+
49
+ * **Pull audio from video**: extract one or more WAV streams from containers.
50
+ * **Diarize + transcribe**: wrap a proven third-party stack to produce per-recording CSV/SRT/TXT.
51
+ * **Per-speaker WAVs**: build one WAV per speaker from a transcript CSV.
52
+ * **Embeddings**
53
+
54
+ * **Whisper encoder embeddings** (segment-level from a transcript or general audio without one).
55
+ * **Sentence embeddings** (mean per row) for any text dataset.
56
+ * **Text gatherer**: stream CSVs or folders of `.txt` into a single “analysis-ready” CSV, with optional grouping.
57
+ * **Feature extraction**
58
+
59
+ * **Dictionary coding** across any number of ContentCoder dictionaries → one wide CSV with stable column order.
60
+ * **Archetype scoring** with sentence-transformers → tidy, fixed columns.
61
+ * **Predictable outputs**: if you don't specify a path, Taters writes to `./features/<kind>/<filename>.csv`, where `<filename>` reflects how the text was gathered (e.g., grouped vs. concatenated).
62
+
63
+ ---
64
+
65
+ ## How you'll use it
66
+
67
+ ### Python (quick sketch)
68
+
69
+ ```python
70
+ from taters import Taters
71
+ t = Taters()
72
+
73
+ # 1) Audio from video
74
+ wavs = t.audio.extract_wavs_from_video(input_path="input.mp4")
75
+
76
+ # 2) Diarize (CSV/SRT/TXT)
77
+ diar = t.audio.diarize_with_thirdparty(audio_path=wavs[0], device="cuda")
78
+
79
+ # 3) Features (defaults write under ./features/<kind>/)
80
+ t.audio.extract_whisper_embeddings(source_wav=wavs[0], transcript_csv=diar["csv"])
81
+ t.text.analyze_with_dictionaries(csv_path=diar["csv"], dict_paths=["dicts/LIWC-22.dicx"])
82
+ t.text.analyze_with_archetypes(csv_path=diar["csv"], archetype_csvs=["archetypes/Resilience.csv"])
83
+ t.text.extract_sentence_embeddings(csv_path=diar["csv"], text_cols=["text"], id_cols=["speaker"], group_by=["speaker"])
84
+ ```
85
+
86
+ ### CLI (quick sketch)
87
+
88
+ ```bash
89
+ # Diarize
90
+ python -m taters.audio.diarize_with_thirdparty \
91
+ --audio_path audio/session.wav --device cuda
92
+
93
+ # Whisper embeddings (general audio; non-silent spans + mean pool)
94
+ python -m taters.audio.extract_whisper_embeddings \
95
+ --source_wav audio/session.wav --strategy nonsilent --aggregate mean
96
+
97
+ # Gather text from CSV (auto names the output if --out omitted)
98
+ python -m taters.helpers.text_gather \
99
+ --csv transcripts/session.csv --text-col text --group-by speaker --delimiter ,
100
+ ```
101
+
102
+ ### Pipelines (do it all at once)
103
+
104
+ Presets live in YAML (e.g., `taters/pipelines/presets/`). Point at a folder, choose a preset, and Taters will run the steps in order—using each step's output as the next step's input. You can override variables (like models, device, overwrite behavior) on the command line.
105
+
106
+ ---
107
+
108
+ ## Install (tidy version)
109
+
110
+ Use a fresh virtual environment. Seriously, a fresh virtual environment is strongly recommended.
111
+
112
+ ```bash
113
+ python -m venv venv-taters
114
+ source venv-taters/bin/activate
115
+ ```
116
+
117
+ ### Quick path (when available)
118
+
119
+ ```bash
120
+ pip install "taters[diarization,cuda]"
121
+ ```
122
+
123
+ Then install the three git extras used by the diarization wrapper:
124
+
125
+ ```bash
126
+ pip install git+https://github.com/MahmoudAshraf97/demucs.git
127
+ pip install git+https://github.com/oliverguhr/deepmultilingualpunctuation.git
128
+ pip install git+https://github.com/MahmoudAshraf97/ctc-forced-aligner.git
129
+ ```
130
+
131
+ Install PyTorch built for **CUDA 12.4** (the stack ChopShop targets):
132
+
133
+ ```bash
134
+ pip install --force-reinstall --no-cache-dir \
135
+ torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 \
136
+ --index-url https://download.pytorch.org/whl/cu124
137
+ ```
138
+
139
+ And ensure **FFmpeg** is on your `PATH` (Ubuntu: `sudo apt-get install ffmpeg`, macOS: `brew install ffmpeg`).
140
+
141
+ > Tip: If you hit CUDA/cuDNN loader errors, it usually means your runtime and wheel builds don't match. Keep CUDA **12.4**, `cu124` wheels, and cuDNN 9 aligned.
142
+
143
+ ---
144
+
145
+ ## Roadmap (short)
146
+
147
+ * More feature families
148
+ * More obviously composable pipelines (per-item + global phases, manifests, post-run aggregation).
149
+ * Rich gatherers/aggregators to unify outputs across large runs.
150
+ * Clear docs, examples, and ready-to-run presets.
151
+
152
+ If you try Taters on a real project, feedback on your flow and pain points is incredibly helpful.
153
+
154
+ ---
155
+
156
+ ## License & credits
157
+
158
+ MIT license. Built on top of excellent open-source projects (Faster-Whisper, sentence-transformers, ContentCoder, and an incredible [community diarization stack](https://github.com/MahmoudAshraf97/whisper-diarization).
159
+
160
+ *(Taters grew out of the earlier "ChopShop" prototype; many ideas and defaults carry over.)*
161
+
taters-0.1.0/README.MD ADDED
@@ -0,0 +1,131 @@
1
+ # TATERS — Takes All Things, Extracts Relevant Stuff
2
+
3
+ Taters is a broad-scope toolkit for researchers that can be used to extract features from multiple types of data (video, audio, text) into clean, analysis-ready artifacts and features. Think of it as a small, dependable kitchen crew for your data: you bring potatoes (files), it handles the peeling, chopping, and plating.
4
+
5
+ **Status:** active WIP. It works today, but expect some rough edges and breaking changes as the project grows.
6
+
7
+ ---
8
+
9
+ ## What Taters is (and isn't)
10
+
11
+ Taters is **a library and a CLI** for end-to-end A/V + text processing with predictable outputs. It's **not** a monolithic "black box" pipeline — each step is a clear, reusable function you can run on its own or string together with YAML presets.
12
+
13
+ ---
14
+
15
+ ## What you can do with it (high level)
16
+
17
+ Note: everything below is currently implemented, but is highly subject to change as the project evolves.
18
+
19
+ * **Pull audio from video**: extract one or more WAV streams from containers.
20
+ * **Diarize + transcribe**: wrap a proven third-party stack to produce per-recording CSV/SRT/TXT.
21
+ * **Per-speaker WAVs**: build one WAV per speaker from a transcript CSV.
22
+ * **Embeddings**
23
+
24
+ * **Whisper encoder embeddings** (segment-level from a transcript or general audio without one).
25
+ * **Sentence embeddings** (mean per row) for any text dataset.
26
+ * **Text gatherer**: stream CSVs or folders of `.txt` into a single “analysis-ready” CSV, with optional grouping.
27
+ * **Feature extraction**
28
+
29
+ * **Dictionary coding** across any number of ContentCoder dictionaries → one wide CSV with stable column order.
30
+ * **Archetype scoring** with sentence-transformers → tidy, fixed columns.
31
+ * **Predictable outputs**: if you don't specify a path, Taters writes to `./features/<kind>/<filename>.csv`, where `<filename>` reflects how the text was gathered (e.g., grouped vs. concatenated).
32
+
33
+ ---
34
+
35
+ ## How you'll use it
36
+
37
+ ### Python (quick sketch)
38
+
39
+ ```python
40
+ from taters import Taters
41
+ t = Taters()
42
+
43
+ # 1) Audio from video
44
+ wavs = t.audio.extract_wavs_from_video(input_path="input.mp4")
45
+
46
+ # 2) Diarize (CSV/SRT/TXT)
47
+ diar = t.audio.diarize_with_thirdparty(audio_path=wavs[0], device="cuda")
48
+
49
+ # 3) Features (defaults write under ./features/<kind>/)
50
+ t.audio.extract_whisper_embeddings(source_wav=wavs[0], transcript_csv=diar["csv"])
51
+ t.text.analyze_with_dictionaries(csv_path=diar["csv"], dict_paths=["dicts/LIWC-22.dicx"])
52
+ t.text.analyze_with_archetypes(csv_path=diar["csv"], archetype_csvs=["archetypes/Resilience.csv"])
53
+ t.text.extract_sentence_embeddings(csv_path=diar["csv"], text_cols=["text"], id_cols=["speaker"], group_by=["speaker"])
54
+ ```
55
+
56
+ ### CLI (quick sketch)
57
+
58
+ ```bash
59
+ # Diarize
60
+ python -m taters.audio.diarize_with_thirdparty \
61
+ --audio_path audio/session.wav --device cuda
62
+
63
+ # Whisper embeddings (general audio; non-silent spans + mean pool)
64
+ python -m taters.audio.extract_whisper_embeddings \
65
+ --source_wav audio/session.wav --strategy nonsilent --aggregate mean
66
+
67
+ # Gather text from CSV (auto names the output if --out omitted)
68
+ python -m taters.helpers.text_gather \
69
+ --csv transcripts/session.csv --text-col text --group-by speaker --delimiter ,
70
+ ```
71
+
72
+ ### Pipelines (do it all at once)
73
+
74
+ Presets live in YAML (e.g., `taters/pipelines/presets/`). Point at a folder, choose a preset, and Taters will run the steps in order—using each step's output as the next step's input. You can override variables (like models, device, overwrite behavior) on the command line.
75
+
76
+ ---
77
+
78
+ ## Install (tidy version)
79
+
80
+ Use a fresh virtual environment. Seriously, a fresh virtual environment is strongly recommended.
81
+
82
+ ```bash
83
+ python -m venv venv-taters
84
+ source venv-taters/bin/activate
85
+ ```
86
+
87
+ ### Quick path (when available)
88
+
89
+ ```bash
90
+ pip install "taters[diarization,cuda]"
91
+ ```
92
+
93
+ Then install the three git extras used by the diarization wrapper:
94
+
95
+ ```bash
96
+ pip install git+https://github.com/MahmoudAshraf97/demucs.git
97
+ pip install git+https://github.com/oliverguhr/deepmultilingualpunctuation.git
98
+ pip install git+https://github.com/MahmoudAshraf97/ctc-forced-aligner.git
99
+ ```
100
+
101
+ Install PyTorch built for **CUDA 12.4** (the stack ChopShop targets):
102
+
103
+ ```bash
104
+ pip install --force-reinstall --no-cache-dir \
105
+ torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 \
106
+ --index-url https://download.pytorch.org/whl/cu124
107
+ ```
108
+
109
+ And ensure **FFmpeg** is on your `PATH` (Ubuntu: `sudo apt-get install ffmpeg`, macOS: `brew install ffmpeg`).
110
+
111
+ > Tip: If you hit CUDA/cuDNN loader errors, it usually means your runtime and wheel builds don't match. Keep CUDA **12.4**, `cu124` wheels, and cuDNN 9 aligned.
112
+
113
+ ---
114
+
115
+ ## Roadmap (short)
116
+
117
+ * More feature families
118
+ * More obviously composable pipelines (per-item + global phases, manifests, post-run aggregation).
119
+ * Rich gatherers/aggregators to unify outputs across large runs.
120
+ * Clear docs, examples, and ready-to-run presets.
121
+
122
+ If you try Taters on a real project, feedback on your flow and pain points is incredibly helpful.
123
+
124
+ ---
125
+
126
+ ## License & credits
127
+
128
+ MIT license. Built on top of excellent open-source projects (Faster-Whisper, sentence-transformers, ContentCoder, and an incredible [community diarization stack](https://github.com/MahmoudAshraf97/whisper-diarization).
129
+
130
+ *(Taters grew out of the earlier "ChopShop" prototype; many ideas and defaults carry over.)*
131
+
@@ -0,0 +1,72 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "taters"
7
+ version = "0.1.0"
8
+ authors = [{ name = "Ryan L. Boyd", email = "ryan@ryanboyd.io" }]
9
+ description = "Analyze, process, and extract from many types of input data. Highly modular/customizable."
10
+
11
+ # Make sure the filename matches your repo exactly (case-sensitive on Linux)
12
+ readme = { file = "README.MD", content-type = "text/markdown" }
13
+ requires-python = ">=3.10"
14
+ license = { text = "MIT" }
15
+
16
+ dependencies = [
17
+ # Core, minimal runtime for splitting, per-speaker builds, and embeddings
18
+ "faster-whisper>=1.1.0",
19
+ "transformers>=4.38.0", # for WhisperFeatureExtractor
20
+ "librosa>=0.10.1",
21
+ "pydub>=0.25.1",
22
+ "contentcoder",
23
+ "archetyper",
24
+ "nltk",
25
+ "sentence-transformers",
26
+ # NOTE TO SELF:
27
+ # Do NOT list torch/torchaudio here;
28
+ # users must install the right CUDA wheel themselves
29
+ ]
30
+
31
+ classifiers = [
32
+ "Programming Language :: Python :: 3",
33
+ "License :: OSI Approved :: MIT License",
34
+ "Operating System :: OS Independent",
35
+ "Topic :: Multimedia :: Sound/Audio :: Analysis",
36
+ "Intended Audience :: Science/Research",
37
+ ]
38
+
39
+ [project.urls]
40
+ Homepage = "https://github.com/ryanboyd/taters"
41
+ Issues = "https://github.com/ryanboyd/taters/issues"
42
+
43
+ [project.optional-dependencies]
44
+ # Full diarization pipeline (large downloads)
45
+ diarization = [
46
+ "nemo-toolkit[asr]>=2.dev",
47
+ ]
48
+ # User-space cuDNN libs for CUDA 12 (handy on many systems)
49
+ cuda = [
50
+ "nvidia-cudnn-cu12",
51
+ ]
52
+
53
+ [tool.setuptools]
54
+ # Map top-level packages to the 'src' directory
55
+ package-dir = {"" = "src"}
56
+ include-package-data = true
57
+ zip-safe = false
58
+
59
+
60
+ # find packages
61
+ packages = { find = { where = ["src"], include = ["taters*"] } }
62
+
63
+
64
+ [tool.setuptools.package-data]
65
+ # Because I vendored the diarizer repo under taters/audio/diarizer/whisper-diarization/
66
+ "taters.diarizer" = ["whisper-diarization/**"]
67
+ "taters.pipeline.presets" = ["*.yaml"]
68
+
69
+ [project.scripts]
70
+ # Entry points must target a callable, not __main__
71
+ # Ensure extract_whisper_embeddings.py defines a `main()` (or `cli()`) function.
72
+ taters-embeddings = "taters.extract_whisper_embeddings:main"
taters-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,115 @@
1
+ # taters/Taters.py
2
+ from __future__ import annotations
3
+ from pathlib import Path
4
+ from typing import Any
5
+ import inspect
6
+
7
+ def _abs(p: str | Path) -> str:
8
+ return str(Path(p).resolve())
9
+
10
+ def _forward(func, kwargs: dict[str, Any]):
11
+ sig = inspect.signature(func)
12
+ try:
13
+ # validate names & requireds; don't execute defaults here
14
+ sig.bind_partial(**kwargs)
15
+ except TypeError as e:
16
+ allowed = ", ".join([str(p) for p in sig.parameters.values()])
17
+ raise TypeError(f"{func.__module__}.{func.__name__}: {e}\nAllowed params: {allowed}")
18
+ return func(**kwargs)
19
+
20
+
21
+ class Taters:
22
+ def __init__(self):
23
+ self.audio = _AudioAPI(self)
24
+ self.text = _TextAPI(self)
25
+ self.helpers = _HelpersAPI(self)
26
+
27
+ # back-compat pass-throughs
28
+
29
+ # audio
30
+ def convert_to_wav(self, **kwargs): return self.audio.convert_to_wav(**kwargs)
31
+ def extract_wavs_from_video(self, **kwargs): return self.audio.extract_wavs_from_video(**kwargs)
32
+ def split_wav_by_speaker(self, **kwargs): return self.audio.split_wav_by_speaker(**kwargs)
33
+ def extract_whisper_embeddings(self, **kwargs): return self.audio.extract_whisper_embeddings(**kwargs)
34
+ def diarize_with_thirdparty(self, **kwargs): return self.audio.diarize_with_thirdparty(**kwargs)
35
+
36
+ #text
37
+ def analyze_with_dictionaries(self, **kwargs): return self.text.analyze_with_dictionaries(**kwargs)
38
+ def analyze_with_archetypes(self, **kwargs): return self.text.analyze_with_archetypes(**kwargs)
39
+ def extract_sentence_embeddings(self, **kwargs): return self.text.extract_sentence_embeddings(**kwargs)
40
+
41
+ # helpers
42
+ def txt_folder_to_analysis_ready_csv(self, **kwargs): return self.helpers.txt_folder_to_analysis_ready_csv(**kwargs)
43
+ def csv_to_analysis_ready_csv(self, **kwargs): return self.helpers.csv_to_analysis_ready_csv(**kwargs)
44
+ def find_files(self, **kwargs): return self.helpers.find_files(**kwargs)
45
+ def feature_gather(self, **kwargs): return self.helpers.feature_gather(**kwargs)
46
+
47
+
48
+ def txt_folder_to_analysis_ready_csv(self, **kwargs):
49
+ from .helpers.text_gather import txt_folder_to_analysis_ready_csv
50
+ return _forward(txt_folder_to_analysis_ready_csv, kwargs)
51
+
52
+ def csv_to_analysis_ready_csv(self, **kwargs):
53
+ from .helpers.text_gather import csv_to_analysis_ready_csv
54
+ return _forward(csv_to_analysis_ready_csv, kwargs)
55
+
56
+
57
+ class _AudioAPI:
58
+ def __init__(self, parent: Taters): self._cs = parent
59
+
60
+ def convert_to_wav(self, **kwargs):
61
+ from .audio.convert_to_wav import convert_audio_to_wav
62
+ return _forward(convert_audio_to_wav, kwargs)
63
+
64
+ def extract_wavs_from_video(self, **kwargs):
65
+ from .audio.extract_wav_from_video import split_audio_streams_to_wav
66
+ return _forward(split_audio_streams_to_wav, kwargs)
67
+
68
+ def split_wav_by_speaker(self, **kwargs):
69
+ from .audio.split_wav_by_speaker import make_speaker_wavs_from_csv
70
+ return _forward(make_speaker_wavs_from_csv, kwargs)
71
+
72
+ def extract_whisper_embeddings(self, **kwargs):
73
+ from .audio.extract_whisper_embeddings import extract_whisper_embeddings
74
+ return _forward(extract_whisper_embeddings, kwargs)
75
+
76
+ def diarize_with_thirdparty(self, **kwargs):
77
+ from .audio.diarizer.whisper_diar_wrapper import run_whisper_diarization_repo
78
+ return _forward(run_whisper_diarization_repo, kwargs)
79
+
80
+
81
+ class _TextAPI:
82
+ def __init__(self, parent: Taters): self._cs = parent
83
+
84
+ def analyze_with_dictionaries(self, **kwargs):
85
+ from .text.analyze_with_dictionaries import analyze_with_dictionaries
86
+ return _forward(analyze_with_dictionaries, kwargs)
87
+
88
+ def analyze_with_archetypes(self, **kwargs):
89
+ from .text.analyze_with_archetypes import analyze_with_archetypes
90
+ return _forward(analyze_with_archetypes, kwargs)
91
+
92
+ def extract_sentence_embeddings(self, **kwargs):
93
+ from .text.extract_sentence_embeddings import analyze_with_sentence_embeddings
94
+ return _forward(analyze_with_sentence_embeddings, kwargs)
95
+
96
+
97
+ class _HelpersAPI:
98
+ def __init__(self, parent: Taters): self._cs = parent
99
+
100
+ def txt_folder_to_analysis_ready_csv(self, **kwargs):
101
+ from .helpers.text_gather import txt_folder_to_analysis_ready_csv
102
+ return _forward(txt_folder_to_analysis_ready_csv, kwargs)
103
+
104
+ def csv_to_analysis_ready_csv(self, **kwargs):
105
+ from .helpers.text_gather import csv_to_analysis_ready_csv
106
+ return _forward(csv_to_analysis_ready_csv, kwargs)
107
+
108
+ def find_files(self, **kwargs):
109
+ from .helpers.find_files import find_files
110
+ return _forward(find_files, kwargs)
111
+
112
+ def feature_gather(self, **kwargs):
113
+ from .helpers import feature_gather
114
+ return _forward(feature_gather, kwargs)
115
+
File without changes
File without changes
@@ -0,0 +1,112 @@
1
+ # taters/audio/convert_to_wav.py
2
+ from __future__ import annotations
3
+ import shutil
4
+ import subprocess
5
+ from pathlib import Path
6
+ from typing import Optional, Union
7
+
8
+ class FFmpegNotFoundError(RuntimeError):
9
+ pass
10
+
11
+ def _check_ffmpeg():
12
+ if shutil.which("ffmpeg") is None or shutil.which("ffprobe") is None:
13
+ raise FFmpegNotFoundError("ffmpeg and/or ffprobe not found on PATH.")
14
+
15
+ def convert_audio_to_wav(
16
+ input_path: Union[str, Path],
17
+ *,
18
+ output_path: Optional[Union[str, Path]] = None,
19
+ output_dir: Optional[Union[str, Path]] = None,
20
+ sample_rate: int = 16000, # common for ASR
21
+ bit_depth: int = 16, # 16/24/32 signed PCM
22
+ channels: int = 1, # 1=mono, 2=stereo
23
+ overwrite_existing: bool = False, # if the file already exists, let's not overwrite by default
24
+ ) -> Path:
25
+ """
26
+ Convert any audio (or A/V container) to a PCM WAV file using ffmpeg.
27
+
28
+ If output_path is None and output_dir is None, writes <input_stem>.wav next to input.
29
+ If output_dir is given (and output_path is None), writes <output_dir>/<input_stem>.wav.
30
+ If output_path is given, it takes precedence.
31
+
32
+ Returns the Path to the created WAV.
33
+ """
34
+ _check_ffmpeg()
35
+
36
+ in_path = Path(input_path).resolve()
37
+ if not in_path.exists():
38
+ raise FileNotFoundError(f"Input file not found: {in_path}")
39
+
40
+ if output_path and output_dir:
41
+ raise ValueError("Provide at most one of output_path or output_dir.")
42
+
43
+ if output_path:
44
+ out_path = Path(output_path).resolve()
45
+ else:
46
+ base = in_path.stem + ".wav"
47
+ out_dir = Path(output_dir).resolve() if output_dir else Path.cwd() / "audio"
48
+ out_dir.mkdir(parents=True, exist_ok=True)
49
+ out_path = out_dir / base
50
+
51
+ if not overwrite_existing and Path(out_path).is_file():
52
+ print("WAV file already exists; returning existing file.")
53
+ return out_path
54
+
55
+ pcm_map = {16: "pcm_s16le", 24: "pcm_s24le", 32: "pcm_s32le"}
56
+ if bit_depth not in pcm_map:
57
+ raise ValueError("bit_depth must be one of {16, 24, 32}.")
58
+ if channels not in (1, 2):
59
+ raise ValueError("channels must be 1 (mono) or 2 (stereo).")
60
+
61
+ cmd = [
62
+ "ffmpeg",
63
+ "-nostdin",
64
+ "-hide_banner", "-loglevel", "error",
65
+ "-y" if overwrite_existing else "-n",
66
+ "-i", str(in_path),
67
+ "-vn", # ignore video
68
+ "-acodec", pcm_map[bit_depth],
69
+ "-ar", str(sample_rate),
70
+ "-ac", str(channels),
71
+ str(out_path),
72
+ ]
73
+
74
+ result = subprocess.run(cmd, capture_output=True, text=True, stdin=subprocess.DEVNULL)
75
+ if result.returncode != 0:
76
+ if not overwrite_existing and out_path.exists():
77
+ raise FileExistsError(f"Target exists (use overwrite=True): {out_path}")
78
+ raise RuntimeError(f"ffmpeg failed: {result.stderr.strip()}")
79
+
80
+ return out_path
81
+
82
+
83
+ # --- CLI --------------------------------------------------------------------
84
+ def _build_arg_parser():
85
+ import argparse
86
+ p = argparse.ArgumentParser(description="Convert any audio (or A/V) file to PCM WAV via ffmpeg.")
87
+ p.add_argument("input", help="Input file (audio or video container)")
88
+ p.add_argument("--out", dest="output_path", default=None,
89
+ help="Exact output .wav path (overrides --out-dir)")
90
+ p.add_argument("--out-dir", dest="output_dir", default=None,
91
+ help="Directory for output (filename will be <input_stem>.wav)")
92
+ p.add_argument("--sr", dest="sample_rate", type=int, default=16000, help="Sample rate (Hz)")
93
+ p.add_argument("--bit-depth", type=int, choices=[16, 24, 32], default=16, help="PCM bit depth")
94
+ p.add_argument("--channels", type=int, choices=[1, 2], default=1, help="1=mono, 2=stereo")
95
+ p.add_argument("--overwrite_existing", type=bool, default=False, help="Overwrite existing output")
96
+ return p
97
+
98
+ def main():
99
+ args = _build_arg_parser().parse_args()
100
+ out = convert_audio_to_wav(
101
+ args.input,
102
+ output_path=args.output_path,
103
+ output_dir=args.output_dir,
104
+ sample_rate=args.sample_rate,
105
+ bit_depth=args.bit_depth,
106
+ channels=args.channels,
107
+ overwrite_existing=args.overwrite_existing,
108
+ )
109
+ print(str(out))
110
+
111
+ if __name__ == "__main__":
112
+ main()
@@ -0,0 +1,5 @@
1
+ # thin alias (args still pass through)
2
+ from .diarizer.whisper_diar_wrapper import main as main
3
+
4
+ if __name__ == "__main__":
5
+ main()
File without changes
@@ -0,0 +1,3 @@
1
+ from .msdd.msdd import MSDDDiarizer
2
+
3
+ __all__ = ["MSDDDiarizer"]