audio-transcode-watcher 0.4.2__py3-none-any.whl
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.
- audio_transcode_watcher/__init__.py +3 -0
- audio_transcode_watcher/config.py +190 -0
- audio_transcode_watcher/encoder.py +208 -0
- audio_transcode_watcher/lyrics.py +193 -0
- audio_transcode_watcher/main.py +97 -0
- audio_transcode_watcher/sync.py +471 -0
- audio_transcode_watcher/utils.py +150 -0
- audio_transcode_watcher/watcher.py +109 -0
- audio_transcode_watcher-0.4.2.dist-info/METADATA +305 -0
- audio_transcode_watcher-0.4.2.dist-info/RECORD +13 -0
- audio_transcode_watcher-0.4.2.dist-info/WHEEL +4 -0
- audio_transcode_watcher-0.4.2.dist-info/entry_points.txt +2 -0
- audio_transcode_watcher-0.4.2.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""File system watcher for audio-transcode-watcher."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from watchdog.events import FileSystemEventHandler
|
|
8
|
+
from watchdog.observers import Observer
|
|
9
|
+
|
|
10
|
+
from .config import Config
|
|
11
|
+
from .sync import (
|
|
12
|
+
delete_outputs,
|
|
13
|
+
delete_sidecars,
|
|
14
|
+
process_source_file,
|
|
15
|
+
safety_guard_active,
|
|
16
|
+
sync_sidecars,
|
|
17
|
+
)
|
|
18
|
+
from .utils import has_audio_extension, has_sidecar_extension, is_audio_file
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AudioSyncHandler(FileSystemEventHandler):
|
|
22
|
+
"""
|
|
23
|
+
Watchdog event handler for audio file changes.
|
|
24
|
+
|
|
25
|
+
Handles file creation, modification, deletion, and rename events.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, config: Config) -> None:
|
|
29
|
+
super().__init__()
|
|
30
|
+
self.config = config
|
|
31
|
+
|
|
32
|
+
def _process_later(self, path: str, force: bool = False) -> None:
|
|
33
|
+
"""Process a file after a brief delay to coalesce rapid events."""
|
|
34
|
+
if not is_audio_file(path):
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
# Small delay to coalesce burst events
|
|
38
|
+
time.sleep(0.2)
|
|
39
|
+
|
|
40
|
+
if safety_guard_active(self.config):
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
process_source_file(path, self.config, force=force, check_stable=True)
|
|
44
|
+
|
|
45
|
+
def on_created(self, event) -> None:
|
|
46
|
+
"""Handle file creation (audio or sidecar)."""
|
|
47
|
+
if event.is_directory:
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
if has_sidecar_extension(event.src_path):
|
|
51
|
+
sync_sidecars(event.src_path, self.config)
|
|
52
|
+
else:
|
|
53
|
+
self._process_later(event.src_path, force=False)
|
|
54
|
+
|
|
55
|
+
def on_modified(self, event) -> None:
|
|
56
|
+
"""Handle file modification (audio or sidecar)."""
|
|
57
|
+
if event.is_directory:
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
if safety_guard_active(self.config):
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
if has_sidecar_extension(event.src_path):
|
|
64
|
+
sync_sidecars(event.src_path, self.config)
|
|
65
|
+
else:
|
|
66
|
+
# Delete old outputs first, then re-encode
|
|
67
|
+
delete_outputs(event.src_path, self.config)
|
|
68
|
+
self._process_later(event.src_path, force=True)
|
|
69
|
+
|
|
70
|
+
def on_moved(self, event) -> None:
|
|
71
|
+
"""Handle file rename/move (audio or sidecar)."""
|
|
72
|
+
if event.is_directory:
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
if has_sidecar_extension(event.src_path) or has_sidecar_extension(event.dest_path):
|
|
76
|
+
# Delete old sidecar copies, sync new ones
|
|
77
|
+
if has_sidecar_extension(event.src_path):
|
|
78
|
+
delete_sidecars(event.src_path, self.config)
|
|
79
|
+
if has_sidecar_extension(event.dest_path):
|
|
80
|
+
sync_sidecars(event.dest_path, self.config)
|
|
81
|
+
else:
|
|
82
|
+
# Audio file move
|
|
83
|
+
if has_audio_extension(event.src_path):
|
|
84
|
+
delete_outputs(event.src_path, self.config)
|
|
85
|
+
if is_audio_file(event.dest_path):
|
|
86
|
+
self._process_later(event.dest_path, force=True)
|
|
87
|
+
|
|
88
|
+
def on_deleted(self, event) -> None:
|
|
89
|
+
"""Handle file deletion (audio or sidecar)."""
|
|
90
|
+
if event.is_directory:
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
if has_sidecar_extension(event.src_path):
|
|
94
|
+
delete_sidecars(event.src_path, self.config)
|
|
95
|
+
elif has_audio_extension(event.src_path):
|
|
96
|
+
delete_outputs(event.src_path, self.config)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def start_watcher(config: Config) -> Observer:
|
|
100
|
+
"""
|
|
101
|
+
Start the file system watcher.
|
|
102
|
+
|
|
103
|
+
Returns the observer instance (call observer.stop() to stop).
|
|
104
|
+
"""
|
|
105
|
+
observer = Observer()
|
|
106
|
+
handler = AudioSyncHandler(config)
|
|
107
|
+
observer.schedule(handler, config.source_path, recursive=False)
|
|
108
|
+
observer.start()
|
|
109
|
+
return observer
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: audio-transcode-watcher
|
|
3
|
+
Version: 0.4.2
|
|
4
|
+
Summary: Watch a source folder and automatically transcode audio files to multiple formats
|
|
5
|
+
Project-URL: Homepage, https://github.com/GeiserX/audio-transcode-watcher
|
|
6
|
+
Project-URL: Repository, https://github.com/GeiserX/audio-transcode-watcher
|
|
7
|
+
Project-URL: Issues, https://github.com/GeiserX/audio-transcode-watcher/issues
|
|
8
|
+
Author: GeiserX
|
|
9
|
+
License-Expression: GPL-3.0-or-later
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: aac,alac,audio,ffmpeg,flac,mp3,transcode,watchdog
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Conversion
|
|
20
|
+
Requires-Python: >=3.14
|
|
21
|
+
Requires-Dist: mutagen>=1.47.0
|
|
22
|
+
Requires-Dist: openai-whisper>=20240930
|
|
23
|
+
Requires-Dist: pyyaml>=6.0
|
|
24
|
+
Requires-Dist: syncedlyrics>=1.0.0
|
|
25
|
+
Requires-Dist: watchdog>=4.0.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-mock>=3.12.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<img src="https://raw.githubusercontent.com/GeiserX/audio-transcode-watcher/main/docs/images/banner.svg" alt="audio-transcode-watcher banner" width="900" />
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
<p align="center">
|
|
37
|
+
<strong>A containerized service that watches a source folder and automatically transcodes audio files to multiple formats simultaneously.</strong>
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
<p align="center">
|
|
41
|
+
<a href="https://pypi.org/project/audio-transcode-watcher/"><img src="https://img.shields.io/pypi/v/audio-transcode-watcher?style=flat-square" alt="PyPI" /></a>
|
|
42
|
+
<a href="https://github.com/GeiserX/audio-transcode-watcher/actions/workflows/tests.yml"><img src="https://github.com/GeiserX/audio-transcode-watcher/actions/workflows/tests.yml/badge.svg" alt="Tests" /></a>
|
|
43
|
+
<a href="https://hub.docker.com/r/drumsergio/audio-transcoder"><img src="https://img.shields.io/docker/pulls/drumsergio/audio-transcoder" alt="Docker Pulls" /></a>
|
|
44
|
+
<a href="https://hub.docker.com/r/drumsergio/audio-transcoder"><img src="https://img.shields.io/docker/image-size/drumsergio/audio-transcoder/latest" alt="Docker Image Size" /></a>
|
|
45
|
+
<a href="https://www.gnu.org/licenses/gpl-3.0"><img src="https://img.shields.io/badge/License-GPLv3-blue.svg" alt="License: GPL v3" /></a>
|
|
46
|
+
<a href="https://github.com/GeiserX/audio-transcode-watcher/releases"><img src="https://img.shields.io/github/v/release/GeiserX/audio-transcode-watcher" alt="GitHub Release" /></a>
|
|
47
|
+
</p>
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
Perfect for maintaining a music library in multiple formats -- lossless for archival, lossy for portable devices -- without lifting a finger. Drop a FLAC into your source folder and get ALAC, MP3, AAC, and Opus copies instantly.
|
|
52
|
+
|
|
53
|
+
## Table of Contents
|
|
54
|
+
|
|
55
|
+
- [Features](#features)
|
|
56
|
+
- [Quick Start](#quick-start)
|
|
57
|
+
- [Configuration](#configuration)
|
|
58
|
+
- [How It Works](#how-it-works)
|
|
59
|
+
- [Performance](#performance)
|
|
60
|
+
- [Verification Tool](#verification-tool)
|
|
61
|
+
- [Development](#development)
|
|
62
|
+
- [License](#license)
|
|
63
|
+
- [Contributing](#contributing)
|
|
64
|
+
|
|
65
|
+
## Features
|
|
66
|
+
|
|
67
|
+
- **Real-time file watching** -- Detects new, modified, renamed, or deleted files via watchdog
|
|
68
|
+
- **Multiple simultaneous outputs** -- Transcode to any number of formats in a single pass
|
|
69
|
+
- **Six codecs** -- ALAC, AAC, MP3, Opus, FLAC, WAV with configurable bitrates
|
|
70
|
+
- **Synced lyrics** -- Auto-fetches `.lrc` lyrics with Whisper speech-to-text fallback
|
|
71
|
+
- **Artwork preservation** -- Optionally embeds cover art in output files
|
|
72
|
+
- **Atomic writes** -- No partial or corrupted files on failure
|
|
73
|
+
- **Orphan cleanup** -- Automatically removes outputs that no longer have a source
|
|
74
|
+
- **Safety guards** -- Prevents accidental mass deletion if folders appear empty
|
|
75
|
+
- **Unicode safe** -- Properly handles special characters in filenames
|
|
76
|
+
- **Docker-first** -- Ships as a lightweight container built on Python 3.14-slim + FFmpeg
|
|
77
|
+
|
|
78
|
+
## Quick Start
|
|
79
|
+
|
|
80
|
+
### Docker Compose (Recommended)
|
|
81
|
+
|
|
82
|
+
**1.** Create a `config.yaml` file:
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
source:
|
|
86
|
+
path: /music/flac
|
|
87
|
+
|
|
88
|
+
outputs:
|
|
89
|
+
- name: alac
|
|
90
|
+
codec: alac
|
|
91
|
+
path: /music/alac
|
|
92
|
+
|
|
93
|
+
- name: mp3-256
|
|
94
|
+
codec: mp3
|
|
95
|
+
bitrate: 256k
|
|
96
|
+
path: /music/mp3
|
|
97
|
+
|
|
98
|
+
- name: aac-256
|
|
99
|
+
codec: aac
|
|
100
|
+
bitrate: 256k
|
|
101
|
+
path: /music/aac
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**2.** Create a `docker-compose.yml`:
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
services:
|
|
108
|
+
audio-transcoder:
|
|
109
|
+
image: drumsergio/audio-transcoder:0.4.1
|
|
110
|
+
container_name: audio_transcoder
|
|
111
|
+
environment:
|
|
112
|
+
- TZ=Europe/Madrid
|
|
113
|
+
- CONFIG_FILE=/app/config.yaml
|
|
114
|
+
volumes:
|
|
115
|
+
- ./config.yaml:/app/config.yaml:ro
|
|
116
|
+
- /path/to/flac:/music/flac:ro
|
|
117
|
+
- /path/to/alac:/music/alac
|
|
118
|
+
- /path/to/mp3:/music/mp3
|
|
119
|
+
- /path/to/aac:/music/aac
|
|
120
|
+
restart: unless-stopped
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**3.** Start the service:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
docker compose up -d
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Docker CLI
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
docker run -d \
|
|
133
|
+
--name audio_transcoder \
|
|
134
|
+
-e TZ=Europe/Madrid \
|
|
135
|
+
-e CONFIG_FILE=/app/config.yaml \
|
|
136
|
+
-v ./config.yaml:/app/config.yaml:ro \
|
|
137
|
+
-v /path/to/flac:/music/flac:ro \
|
|
138
|
+
-v /path/to/mp3:/music/mp3 \
|
|
139
|
+
--restart unless-stopped \
|
|
140
|
+
drumsergio/audio-transcoder:0.4.1
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Configuration
|
|
144
|
+
|
|
145
|
+
Configuration is provided via a YAML file. Set the `CONFIG_FILE` environment variable to its path inside the container.
|
|
146
|
+
|
|
147
|
+
### Full Configuration Example
|
|
148
|
+
|
|
149
|
+
```yaml
|
|
150
|
+
# Source folder containing original audio files
|
|
151
|
+
source:
|
|
152
|
+
path: /music/flac
|
|
153
|
+
|
|
154
|
+
# Output destinations -- define as many as you need
|
|
155
|
+
outputs:
|
|
156
|
+
# Lossless ALAC for Apple devices
|
|
157
|
+
- name: alac
|
|
158
|
+
codec: alac
|
|
159
|
+
path: /music/alac
|
|
160
|
+
include_artwork: true
|
|
161
|
+
|
|
162
|
+
# High-quality MP3 for broad compatibility
|
|
163
|
+
- name: mp3-320
|
|
164
|
+
codec: mp3
|
|
165
|
+
bitrate: 320k
|
|
166
|
+
path: /music/mp3-320
|
|
167
|
+
include_artwork: true
|
|
168
|
+
|
|
169
|
+
# Balanced MP3 for portable devices
|
|
170
|
+
- name: mp3-192
|
|
171
|
+
codec: mp3
|
|
172
|
+
bitrate: 192k
|
|
173
|
+
path: /music/mp3-192
|
|
174
|
+
include_artwork: true
|
|
175
|
+
|
|
176
|
+
# AAC for modern devices
|
|
177
|
+
- name: aac-256
|
|
178
|
+
codec: aac
|
|
179
|
+
bitrate: 256k
|
|
180
|
+
path: /music/aac
|
|
181
|
+
include_artwork: true
|
|
182
|
+
|
|
183
|
+
# Opus for streaming (best quality-to-size ratio)
|
|
184
|
+
- name: opus-128
|
|
185
|
+
codec: opus
|
|
186
|
+
bitrate: 128k
|
|
187
|
+
path: /music/opus
|
|
188
|
+
|
|
189
|
+
# Optional settings
|
|
190
|
+
settings:
|
|
191
|
+
# Delete all outputs and re-encode on startup
|
|
192
|
+
force_reencode: false
|
|
193
|
+
|
|
194
|
+
# Maximum time to wait for a file to become stable (seconds)
|
|
195
|
+
stability_timeout: 60
|
|
196
|
+
|
|
197
|
+
# Minimum time a file must be unchanged before processing (seconds)
|
|
198
|
+
min_stable_seconds: 1.0
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Supported Codecs
|
|
202
|
+
|
|
203
|
+
| Codec | Extension | Bitrate | Artwork | Description |
|
|
204
|
+
|--------|-----------|-----------|---------|----------------------------|
|
|
205
|
+
| `alac` | `.m4a` | N/A | Yes | Lossless, Apple compatible |
|
|
206
|
+
| `aac` | `.m4a` | 64k--320k | Yes | Lossy, excellent quality |
|
|
207
|
+
| `mp3` | `.mp3` | 64k--320k | Yes | Lossy, universal support |
|
|
208
|
+
| `opus` | `.opus` | 32k--256k | No | Lossy, best quality/size |
|
|
209
|
+
| `flac` | `.flac` | N/A | Yes | Lossless, open format |
|
|
210
|
+
| `wav` | `.wav` | N/A | No | Lossless, uncompressed |
|
|
211
|
+
|
|
212
|
+
### JSON Configuration
|
|
213
|
+
|
|
214
|
+
You can alternatively provide configuration as a JSON string via the `CONFIG_JSON` environment variable:
|
|
215
|
+
|
|
216
|
+
```yaml
|
|
217
|
+
environment:
|
|
218
|
+
- CONFIG_JSON={"source":{"path":"/music/flac"},"outputs":[{"name":"mp3","codec":"mp3","bitrate":"256k","path":"/music/mp3"}]}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## How It Works
|
|
222
|
+
|
|
223
|
+
1. **Initial sync** -- On startup, scans the source folder and encodes any missing files to all configured outputs.
|
|
224
|
+
2. **Watch mode** -- Continuously monitors the source folder for changes:
|
|
225
|
+
- **New files** are encoded to all configured outputs
|
|
226
|
+
- **Modified files** are re-encoded to all outputs
|
|
227
|
+
- **Renamed files** trigger deletion of old outputs and creation of new ones
|
|
228
|
+
- **Deleted files** have their corresponding outputs removed
|
|
229
|
+
3. **Orphan cleanup** -- Removes output files that no longer have a matching source.
|
|
230
|
+
4. **Lyrics sync** -- Fetches synced `.lrc` lyrics from online databases; falls back to Whisper transcription when no lyrics are found.
|
|
231
|
+
|
|
232
|
+
### Safety Guards
|
|
233
|
+
|
|
234
|
+
The service includes multiple guards to prevent data loss:
|
|
235
|
+
|
|
236
|
+
- If the source folder appears empty, no deletions are performed
|
|
237
|
+
- If any output folder appears empty, no deletions are performed
|
|
238
|
+
- All writes are atomic -- encoding happens to a temporary file that is moved into place only on success
|
|
239
|
+
|
|
240
|
+
## Performance
|
|
241
|
+
|
|
242
|
+
- **Parallel processing** -- Multiple output formats are encoded concurrently
|
|
243
|
+
- **Incremental sync** -- Only missing or changed files are processed; unchanged files are skipped
|
|
244
|
+
- **Stability detection** -- Files are not processed until they have been stable on disk for a configurable period, avoiding partial reads during large copies or network transfers
|
|
245
|
+
- **Low idle footprint** -- Uses inotify/FSEvents-based watching with minimal CPU usage when idle
|
|
246
|
+
|
|
247
|
+
## Verification Tool
|
|
248
|
+
|
|
249
|
+
A built-in verification tool checks that all outputs are in sync with the source:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
# Basic sync check
|
|
253
|
+
docker exec audio_transcoder python /app/tools/verify_sync.py --config /app/config.yaml
|
|
254
|
+
|
|
255
|
+
# Thorough check including duration comparison
|
|
256
|
+
docker exec audio_transcoder python /app/tools/verify_sync.py --config /app/config.yaml --check-duration -v
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Development
|
|
260
|
+
|
|
261
|
+
### Requirements
|
|
262
|
+
|
|
263
|
+
- Docker (recommended), or Python 3.14+ with FFmpeg installed
|
|
264
|
+
- [Hatch](https://hatch.pypa.io/) build system
|
|
265
|
+
|
|
266
|
+
### Running Tests
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
python -m venv .venv
|
|
270
|
+
source .venv/bin/activate
|
|
271
|
+
pip install -e ".[dev]"
|
|
272
|
+
|
|
273
|
+
# Run tests with coverage
|
|
274
|
+
pytest
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Building the Docker Image
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
docker build -t audio-transcoder:dev .
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Related Music Tools
|
|
284
|
+
|
|
285
|
+
| Project | Description |
|
|
286
|
+
|---------|-------------|
|
|
287
|
+
| [slskd-transform](https://github.com/GeiserX/slskd-transform) | Bulk upgrade your music library from lossy to lossless via Soulseek |
|
|
288
|
+
| [telegram-slskd-local-bot](https://github.com/GeiserX/telegram-slskd-local-bot) | Automated music discovery and download via Telegram |
|
|
289
|
+
| [jellyfin-encoder](https://github.com/GeiserX/jellyfin-encoder) | Automatic 720p HEVC/AV1 transcoding for Jellyfin |
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
## License
|
|
293
|
+
|
|
294
|
+
This project is licensed under the **GNU General Public License v3.0** -- see the [LICENSE](LICENSE) file for details.
|
|
295
|
+
|
|
296
|
+
## Contributing
|
|
297
|
+
|
|
298
|
+
Contributions are welcome. Please open an issue to discuss significant changes before submitting a pull request.
|
|
299
|
+
|
|
300
|
+
1. Fork the repository
|
|
301
|
+
2. Create a feature branch (`git checkout -b feat/amazing-feature`)
|
|
302
|
+
3. Run tests (`pytest`)
|
|
303
|
+
4. Commit your changes (`git commit -m 'feat: add amazing feature'`)
|
|
304
|
+
5. Push to the branch (`git push origin feat/amazing-feature`)
|
|
305
|
+
6. Open a Pull Request
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
audio_transcode_watcher/__init__.py,sha256=0CMXt0pg_rLju7to1KdACZCgI4iF2bjfs9J0NuBlmBQ,89
|
|
2
|
+
audio_transcode_watcher/config.py,sha256=0_Dcg3GM0ad9YVjv1DwgO6AeGd-Y8lI7ZSfhvUdVwt8,6319
|
|
3
|
+
audio_transcode_watcher/encoder.py,sha256=rezEqMFn0DrkIn1YuVMUxF6CqTV56R5A1SqyBHRw9II,5839
|
|
4
|
+
audio_transcode_watcher/lyrics.py,sha256=mJS3YCXsKEVXw-MHDHbuRRcvnJcjBYMhSRGOoYlVdGk,6173
|
|
5
|
+
audio_transcode_watcher/main.py,sha256=Uui0u_CwGcjDLoMXerKQefMndx0XS_uKioQDfqCN41g,2515
|
|
6
|
+
audio_transcode_watcher/sync.py,sha256=5jnXXaHDA60odnLv5TTGiUBiOykVv3ckFOP4RRTm4V8,17302
|
|
7
|
+
audio_transcode_watcher/utils.py,sha256=DoRJZx15wBYpYYwswtxcpFArAo-w0Tkr1AB6S2XF3cM,4067
|
|
8
|
+
audio_transcode_watcher/watcher.py,sha256=C4ipvFhOVqJnbs9VjXtyAb5SUJkP9YlJW8NG1s5QyA4,3564
|
|
9
|
+
audio_transcode_watcher-0.4.2.dist-info/METADATA,sha256=lDTx5i1QXkDIPblpmB56LZ6CUjjE-Dl_A5E-YhaUwEw,10683
|
|
10
|
+
audio_transcode_watcher-0.4.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
11
|
+
audio_transcode_watcher-0.4.2.dist-info/entry_points.txt,sha256=A9zP7O7DJdX-ThutrcqgbII8UX_GHr2_UoD87GBswe8,78
|
|
12
|
+
audio_transcode_watcher-0.4.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
13
|
+
audio_transcode_watcher-0.4.2.dist-info/RECORD,,
|