trackfetch 2.2.0__tar.gz → 2.2.5__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.
- {trackfetch-2.2.0/trackfetch.egg-info → trackfetch-2.2.5}/PKG-INFO +20 -3
- {trackfetch-2.2.0 → trackfetch-2.2.5}/README.md +19 -2
- {trackfetch-2.2.0 → trackfetch-2.2.5}/pyproject.toml +1 -1
- {trackfetch-2.2.0 → trackfetch-2.2.5}/tests/test_config.py +1 -1
- trackfetch-2.2.5/tests/test_inline.py +167 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5/trackfetch.egg-info}/PKG-INFO +20 -3
- {trackfetch-2.2.0 → trackfetch-2.2.5}/trackfetch.egg-info/SOURCES.txt +1 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5}/trackfetch.py +103 -25
- {trackfetch-2.2.0 → trackfetch-2.2.5}/LICENSE +0 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5}/setup.cfg +0 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5}/tests/test_pipeline.py +0 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5}/tests/test_spotify.py +0 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5}/tests/test_tags_and_files.py +0 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5}/tests/test_text.py +0 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5}/tests/test_youtube.py +0 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5}/trackfetch.egg-info/dependency_links.txt +0 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5}/trackfetch.egg-info/entry_points.txt +0 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5}/trackfetch.egg-info/requires.txt +0 -0
- {trackfetch-2.2.0 → trackfetch-2.2.5}/trackfetch.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trackfetch
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.5
|
|
4
4
|
Summary: Turn a text file of songs into tagged MP3s: Spotify metadata and cover art, best-match YouTube audio via yt-dlp.
|
|
5
5
|
Author: ByteMe6
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -213,12 +213,13 @@ trackfetch "My Playlist.txt" -o ~/Music/"My Playlist"
|
|
|
213
213
|
## Usage
|
|
214
214
|
|
|
215
215
|
```text
|
|
216
|
-
trackfetch [-h] [-o OUTPUT] [--config CONFIG] [-f {mp3,m4a,opus}] [--title-only] [--delay DELAY] [--spotify-client-id ID] [--spotify-client-secret SECRET] [
|
|
216
|
+
trackfetch [-h] [--track ARTIST - TITLE ...] [-o OUTPUT] [--config CONFIG] [-f {mp3,m4a,opus}] [--title-only] [--delay DELAY] [--spotify-client-id ID] [--spotify-client-secret SECRET] [INPUT ...]
|
|
217
217
|
```
|
|
218
218
|
|
|
219
219
|
| Option | Default | What it does |
|
|
220
220
|
| --- | --- | --- |
|
|
221
|
-
| `
|
|
221
|
+
| `INPUT` | required | Text file with one `Artist - Title` per line, or an inline `Artist - Title` track. Can also be set in the [config file](#configuration-file). |
|
|
222
|
+
| `--track` | — | Track to download inline, `Artist - Title`. Can be given several times. |
|
|
222
223
|
| `-o`, `--output` | current directory | Folder to save the files in. It's created if it doesn't exist. |
|
|
223
224
|
| `--config` | auto-detected | Path to a [config file](#configuration-file). |
|
|
224
225
|
| `-f`, `--format` | `m4a` | Audio format: `mp3`, `m4a` or `opus`. See [Audio formats](#audio-formats). |
|
|
@@ -232,8 +233,24 @@ trackfetch songs.txt -o ~/Music/RoadTrip # save to a specific folder
|
|
|
232
233
|
trackfetch songs.txt -o /media/usb --title-only # short names for a car stereo
|
|
233
234
|
trackfetch big-list.txt --delay 3 # go easy on rate limits
|
|
234
235
|
trackfetch songs.txt --format opus # original YouTube audio, no re-encode
|
|
236
|
+
trackfetch "Daft Punk - One More Time" # one track, no file needed
|
|
237
|
+
trackfetch "A - B" "C - D" --track "E - F" # a few tracks in one go
|
|
235
238
|
```
|
|
236
239
|
|
|
240
|
+
### Inline tracks
|
|
241
|
+
|
|
242
|
+
Skip the file and name the tracks right on the command line:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
trackfetch "Daft Punk - One More Time"
|
|
246
|
+
trackfetch "Daft Punk - One More Time" "Radiohead - Paranoid Android"
|
|
247
|
+
trackfetch --track "Daft Punk - One More Time" --track "Radiohead - Paranoid Android"
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
A file and inline tracks can be mixed: `trackfetch songs.txt --track "Blur - Song 2"`.
|
|
251
|
+
The separator is the same ` - ` as in the file, and the first ` - ` splits the
|
|
252
|
+
artist from the title.
|
|
253
|
+
|
|
237
254
|
### Configuration file
|
|
238
255
|
|
|
239
256
|
Put your defaults in an INI file and forget the flags:
|
|
@@ -182,12 +182,13 @@ trackfetch "My Playlist.txt" -o ~/Music/"My Playlist"
|
|
|
182
182
|
## Usage
|
|
183
183
|
|
|
184
184
|
```text
|
|
185
|
-
trackfetch [-h] [-o OUTPUT] [--config CONFIG] [-f {mp3,m4a,opus}] [--title-only] [--delay DELAY] [--spotify-client-id ID] [--spotify-client-secret SECRET] [
|
|
185
|
+
trackfetch [-h] [--track ARTIST - TITLE ...] [-o OUTPUT] [--config CONFIG] [-f {mp3,m4a,opus}] [--title-only] [--delay DELAY] [--spotify-client-id ID] [--spotify-client-secret SECRET] [INPUT ...]
|
|
186
186
|
```
|
|
187
187
|
|
|
188
188
|
| Option | Default | What it does |
|
|
189
189
|
| --- | --- | --- |
|
|
190
|
-
| `
|
|
190
|
+
| `INPUT` | required | Text file with one `Artist - Title` per line, or an inline `Artist - Title` track. Can also be set in the [config file](#configuration-file). |
|
|
191
|
+
| `--track` | — | Track to download inline, `Artist - Title`. Can be given several times. |
|
|
191
192
|
| `-o`, `--output` | current directory | Folder to save the files in. It's created if it doesn't exist. |
|
|
192
193
|
| `--config` | auto-detected | Path to a [config file](#configuration-file). |
|
|
193
194
|
| `-f`, `--format` | `m4a` | Audio format: `mp3`, `m4a` or `opus`. See [Audio formats](#audio-formats). |
|
|
@@ -201,8 +202,24 @@ trackfetch songs.txt -o ~/Music/RoadTrip # save to a specific folder
|
|
|
201
202
|
trackfetch songs.txt -o /media/usb --title-only # short names for a car stereo
|
|
202
203
|
trackfetch big-list.txt --delay 3 # go easy on rate limits
|
|
203
204
|
trackfetch songs.txt --format opus # original YouTube audio, no re-encode
|
|
205
|
+
trackfetch "Daft Punk - One More Time" # one track, no file needed
|
|
206
|
+
trackfetch "A - B" "C - D" --track "E - F" # a few tracks in one go
|
|
204
207
|
```
|
|
205
208
|
|
|
209
|
+
### Inline tracks
|
|
210
|
+
|
|
211
|
+
Skip the file and name the tracks right on the command line:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
trackfetch "Daft Punk - One More Time"
|
|
215
|
+
trackfetch "Daft Punk - One More Time" "Radiohead - Paranoid Android"
|
|
216
|
+
trackfetch --track "Daft Punk - One More Time" --track "Radiohead - Paranoid Android"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
A file and inline tracks can be mixed: `trackfetch songs.txt --track "Blur - Song 2"`.
|
|
220
|
+
The separator is the same ` - ` as in the file, and the first ` - ` splits the
|
|
221
|
+
artist from the title.
|
|
222
|
+
|
|
206
223
|
### Configuration file
|
|
207
224
|
|
|
208
225
|
Put your defaults in an INI file and forget the flags:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "trackfetch"
|
|
7
|
-
version = "2.2.
|
|
7
|
+
version = "2.2.5"
|
|
8
8
|
description = "Turn a text file of songs into tagged MP3s: Spotify metadata and cover art, best-match YouTube audio via yt-dlp."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "GPL-3.0-or-later"
|
|
@@ -261,7 +261,7 @@ class TestMainWithConfig:
|
|
|
261
261
|
run_cli(monkeypatch)
|
|
262
262
|
|
|
263
263
|
assert exc.value.code == 1
|
|
264
|
-
assert "No input
|
|
264
|
+
assert "No input" in capsys.readouterr().out
|
|
265
265
|
|
|
266
266
|
def test_input_can_come_from_the_config(self, pipeline, monkeypatch, tmp_path):
|
|
267
267
|
songs = tmp_path / "songs.txt"
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
from conftest import FIXTURES, make_track
|
|
6
|
+
|
|
7
|
+
import trackfetch as m
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def pipeline(monkeypatch):
|
|
12
|
+
state = {
|
|
13
|
+
"track": make_track("One More Time", ["Daft Punk"], album="Discovery", track_number=1),
|
|
14
|
+
"video": {"title": "Daft Punk - One More Time (Official Audio)", "id": "abc"},
|
|
15
|
+
"searched": [],
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def fake_find_spotify(sp, artist, title):
|
|
19
|
+
state["searched"].append((artist, title))
|
|
20
|
+
return state["track"]
|
|
21
|
+
|
|
22
|
+
def fake_download_audio(url, temp_dir, audio_format="mp3"):
|
|
23
|
+
path = temp_dir / f"audio.{audio_format}"
|
|
24
|
+
if audio_format == "mp3":
|
|
25
|
+
path.write_bytes(b"\x00" * 64)
|
|
26
|
+
else:
|
|
27
|
+
shutil.copy(FIXTURES / f"silence.{audio_format}", path)
|
|
28
|
+
return path
|
|
29
|
+
|
|
30
|
+
monkeypatch.setattr(m, "find_spotify_track", fake_find_spotify)
|
|
31
|
+
monkeypatch.setattr(m, "find_youtube_video", lambda a, t: state["video"])
|
|
32
|
+
monkeypatch.setattr(m, "download_audio", fake_download_audio)
|
|
33
|
+
monkeypatch.setattr(m, "download_cover", lambda url, dest: dest.write_bytes(b"x"))
|
|
34
|
+
monkeypatch.setattr(m, "get_spotify", lambda *a, **k: object())
|
|
35
|
+
monkeypatch.setattr(m.time, "sleep", lambda s: None)
|
|
36
|
+
return state
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def run_cli(monkeypatch, *args):
|
|
40
|
+
monkeypatch.setattr(sys, "argv", ["trackfetch", *map(str, args)])
|
|
41
|
+
m.main()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TestInlineTracks:
|
|
45
|
+
def test_single_inline_track(self, pipeline, monkeypatch, tmp_path):
|
|
46
|
+
out = tmp_path / "o"
|
|
47
|
+
|
|
48
|
+
run_cli(monkeypatch, "Daft Punk - One More Time", "-o", out)
|
|
49
|
+
|
|
50
|
+
assert pipeline["searched"] == [("Daft Punk", "One More Time")]
|
|
51
|
+
assert (out / "Daft Punk - One More Time.m4a").exists()
|
|
52
|
+
|
|
53
|
+
def test_several_inline_tracks(self, pipeline, monkeypatch, tmp_path):
|
|
54
|
+
out = tmp_path / "o"
|
|
55
|
+
|
|
56
|
+
run_cli(
|
|
57
|
+
monkeypatch,
|
|
58
|
+
"Daft Punk - One More Time",
|
|
59
|
+
"Radiohead - Paranoid Android",
|
|
60
|
+
"-o",
|
|
61
|
+
out,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
assert [artist for artist, _ in pipeline["searched"]] == [
|
|
65
|
+
"Daft Punk",
|
|
66
|
+
"Radiohead",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
def test_track_flag(self, pipeline, monkeypatch, tmp_path):
|
|
70
|
+
out = tmp_path / "o"
|
|
71
|
+
|
|
72
|
+
run_cli(
|
|
73
|
+
monkeypatch,
|
|
74
|
+
"--track",
|
|
75
|
+
"Daft Punk - One More Time",
|
|
76
|
+
"--track",
|
|
77
|
+
"Radiohead - Paranoid Android",
|
|
78
|
+
"-o",
|
|
79
|
+
out,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
assert len(pipeline["searched"]) == 2
|
|
83
|
+
|
|
84
|
+
def test_track_flag_with_several_values(self, pipeline, monkeypatch, tmp_path):
|
|
85
|
+
out = tmp_path / "o"
|
|
86
|
+
|
|
87
|
+
run_cli(
|
|
88
|
+
monkeypatch,
|
|
89
|
+
"--track",
|
|
90
|
+
"Daft Punk - One More Time",
|
|
91
|
+
"Radiohead - Paranoid Android",
|
|
92
|
+
"-o",
|
|
93
|
+
out,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
assert len(pipeline["searched"]) == 2
|
|
97
|
+
|
|
98
|
+
def test_file_and_inline_track_together(self, pipeline, monkeypatch, tmp_path):
|
|
99
|
+
songs = tmp_path / "songs.txt"
|
|
100
|
+
songs.write_text("Daft Punk - One More Time\n", encoding="utf-8")
|
|
101
|
+
out = tmp_path / "o"
|
|
102
|
+
|
|
103
|
+
run_cli(monkeypatch, songs, "--track", "Radiohead - Paranoid Android", "-o", out)
|
|
104
|
+
|
|
105
|
+
assert len(pipeline["searched"]) == 2
|
|
106
|
+
|
|
107
|
+
def test_inline_track_and_file_in_one_call(self, pipeline, monkeypatch, tmp_path):
|
|
108
|
+
songs = tmp_path / "songs.txt"
|
|
109
|
+
songs.write_text("Daft Punk - One More Time\n", encoding="utf-8")
|
|
110
|
+
out = tmp_path / "o"
|
|
111
|
+
|
|
112
|
+
run_cli(monkeypatch, "Radiohead - Paranoid Android", songs, "-o", out)
|
|
113
|
+
|
|
114
|
+
assert len(pipeline["searched"]) == 2
|
|
115
|
+
|
|
116
|
+
def test_missing_file_exits(self, pipeline, monkeypatch, tmp_path, capsys):
|
|
117
|
+
with pytest.raises(SystemExit) as exc:
|
|
118
|
+
run_cli(monkeypatch, "songs.txt", "-o", tmp_path / "o")
|
|
119
|
+
|
|
120
|
+
assert exc.value.code == 1
|
|
121
|
+
assert "Input file not found" in capsys.readouterr().out
|
|
122
|
+
|
|
123
|
+
def test_unparsable_track_flag_exits(self, pipeline, monkeypatch, tmp_path, capsys):
|
|
124
|
+
with pytest.raises(SystemExit) as exc:
|
|
125
|
+
run_cli(monkeypatch, "--track", "nonsense", "-o", tmp_path / "o")
|
|
126
|
+
|
|
127
|
+
assert exc.value.code == 1
|
|
128
|
+
assert "Could not parse track" in capsys.readouterr().out
|
|
129
|
+
|
|
130
|
+
def test_inline_track_uses_the_output_rules(self, pipeline, monkeypatch, tmp_path):
|
|
131
|
+
out = tmp_path / "o"
|
|
132
|
+
config = tmp_path / "cfg.ini"
|
|
133
|
+
config.write_text(f"[trackfetch]\noutput = {out}\n", encoding="utf-8")
|
|
134
|
+
|
|
135
|
+
run_cli(monkeypatch, "Daft Punk - One More Time", "--config", config)
|
|
136
|
+
|
|
137
|
+
assert (out / "Daft Punk - One More Time.m4a").exists()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class TestCollectSources:
|
|
141
|
+
def test_config_input_is_a_file(self, tmp_path):
|
|
142
|
+
songs = tmp_path / "songs.txt"
|
|
143
|
+
songs.write_text("A - B\n", encoding="utf-8")
|
|
144
|
+
|
|
145
|
+
class Args:
|
|
146
|
+
inputs = []
|
|
147
|
+
track = None
|
|
148
|
+
|
|
149
|
+
sources = m.collect_sources(Args(), {"input": str(songs)})
|
|
150
|
+
|
|
151
|
+
assert sources == [songs]
|
|
152
|
+
|
|
153
|
+
def test_missing_config_input_raises(self, tmp_path):
|
|
154
|
+
class Args:
|
|
155
|
+
inputs = []
|
|
156
|
+
track = None
|
|
157
|
+
|
|
158
|
+
with pytest.raises(ValueError) as exc:
|
|
159
|
+
m.collect_sources(Args(), {"input": str(tmp_path / "nope.txt")})
|
|
160
|
+
|
|
161
|
+
assert "Input file not found" in str(exc.value)
|
|
162
|
+
|
|
163
|
+
def test_read_song_file_skips_comments(self, tmp_path):
|
|
164
|
+
songs = tmp_path / "songs.txt"
|
|
165
|
+
songs.write_text("# comment\n\nA - B\nbroken line\nC - D\n", encoding="utf-8")
|
|
166
|
+
|
|
167
|
+
assert m.read_song_file(songs) == [("A", "B"), ("C", "D")]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trackfetch
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.5
|
|
4
4
|
Summary: Turn a text file of songs into tagged MP3s: Spotify metadata and cover art, best-match YouTube audio via yt-dlp.
|
|
5
5
|
Author: ByteMe6
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -213,12 +213,13 @@ trackfetch "My Playlist.txt" -o ~/Music/"My Playlist"
|
|
|
213
213
|
## Usage
|
|
214
214
|
|
|
215
215
|
```text
|
|
216
|
-
trackfetch [-h] [-o OUTPUT] [--config CONFIG] [-f {mp3,m4a,opus}] [--title-only] [--delay DELAY] [--spotify-client-id ID] [--spotify-client-secret SECRET] [
|
|
216
|
+
trackfetch [-h] [--track ARTIST - TITLE ...] [-o OUTPUT] [--config CONFIG] [-f {mp3,m4a,opus}] [--title-only] [--delay DELAY] [--spotify-client-id ID] [--spotify-client-secret SECRET] [INPUT ...]
|
|
217
217
|
```
|
|
218
218
|
|
|
219
219
|
| Option | Default | What it does |
|
|
220
220
|
| --- | --- | --- |
|
|
221
|
-
| `
|
|
221
|
+
| `INPUT` | required | Text file with one `Artist - Title` per line, or an inline `Artist - Title` track. Can also be set in the [config file](#configuration-file). |
|
|
222
|
+
| `--track` | — | Track to download inline, `Artist - Title`. Can be given several times. |
|
|
222
223
|
| `-o`, `--output` | current directory | Folder to save the files in. It's created if it doesn't exist. |
|
|
223
224
|
| `--config` | auto-detected | Path to a [config file](#configuration-file). |
|
|
224
225
|
| `-f`, `--format` | `m4a` | Audio format: `mp3`, `m4a` or `opus`. See [Audio formats](#audio-formats). |
|
|
@@ -232,8 +233,24 @@ trackfetch songs.txt -o ~/Music/RoadTrip # save to a specific folder
|
|
|
232
233
|
trackfetch songs.txt -o /media/usb --title-only # short names for a car stereo
|
|
233
234
|
trackfetch big-list.txt --delay 3 # go easy on rate limits
|
|
234
235
|
trackfetch songs.txt --format opus # original YouTube audio, no re-encode
|
|
236
|
+
trackfetch "Daft Punk - One More Time" # one track, no file needed
|
|
237
|
+
trackfetch "A - B" "C - D" --track "E - F" # a few tracks in one go
|
|
235
238
|
```
|
|
236
239
|
|
|
240
|
+
### Inline tracks
|
|
241
|
+
|
|
242
|
+
Skip the file and name the tracks right on the command line:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
trackfetch "Daft Punk - One More Time"
|
|
246
|
+
trackfetch "Daft Punk - One More Time" "Radiohead - Paranoid Android"
|
|
247
|
+
trackfetch --track "Daft Punk - One More Time" --track "Radiohead - Paranoid Android"
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
A file and inline tracks can be mixed: `trackfetch songs.txt --track "Blur - Song 2"`.
|
|
251
|
+
The separator is the same ` - ` as in the file, and the first ` - ` splits the
|
|
252
|
+
artist from the title.
|
|
253
|
+
|
|
237
254
|
### Configuration file
|
|
238
255
|
|
|
239
256
|
Put your defaults in an INI file and forget the flags:
|
|
@@ -226,7 +226,7 @@ def download_cover(url: str, destination: Path):
|
|
|
226
226
|
url,
|
|
227
227
|
timeout=30,
|
|
228
228
|
headers={
|
|
229
|
-
"User-Agent": "trackfetch/2.2"
|
|
229
|
+
"User-Agent": "trackfetch/2.2.5"
|
|
230
230
|
},
|
|
231
231
|
)
|
|
232
232
|
|
|
@@ -846,7 +846,7 @@ def apply_config(args, parser, settings):
|
|
|
846
846
|
for action in parser._actions:
|
|
847
847
|
dest = action.dest
|
|
848
848
|
|
|
849
|
-
if dest in ("help", "version", "config"):
|
|
849
|
+
if dest in ("help", "version", "config", "inputs", "track"):
|
|
850
850
|
continue
|
|
851
851
|
|
|
852
852
|
if getattr(args, dest, None) is not None:
|
|
@@ -874,6 +874,71 @@ def apply_config(args, parser, settings):
|
|
|
874
874
|
setattr(args, dest, value)
|
|
875
875
|
|
|
876
876
|
|
|
877
|
+
def read_song_file(path: Path):
|
|
878
|
+
"""Read Artist - Title pairs from a text file."""
|
|
879
|
+
songs = []
|
|
880
|
+
|
|
881
|
+
for line in path.read_text(
|
|
882
|
+
encoding="utf-8",
|
|
883
|
+
errors="replace",
|
|
884
|
+
).splitlines():
|
|
885
|
+
|
|
886
|
+
line = line.strip()
|
|
887
|
+
|
|
888
|
+
if not line or line.startswith("#"):
|
|
889
|
+
continue
|
|
890
|
+
|
|
891
|
+
song = parse_song(line)
|
|
892
|
+
|
|
893
|
+
if song:
|
|
894
|
+
songs.append(song)
|
|
895
|
+
|
|
896
|
+
return songs
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
def collect_sources(args, settings):
|
|
900
|
+
"""Build the ordered list of inputs: file paths and inline tracks.
|
|
901
|
+
|
|
902
|
+
Every item is either a Path to a text file or an inline
|
|
903
|
+
"Artist - Title" string.
|
|
904
|
+
"""
|
|
905
|
+
sources = []
|
|
906
|
+
|
|
907
|
+
config_input = config_str_value(settings, "input")
|
|
908
|
+
|
|
909
|
+
if config_input:
|
|
910
|
+
path = Path(config_input).expanduser()
|
|
911
|
+
|
|
912
|
+
if not path.is_file():
|
|
913
|
+
raise ValueError(f"Input file not found: {path}")
|
|
914
|
+
|
|
915
|
+
sources.append(path)
|
|
916
|
+
|
|
917
|
+
for item in args.inputs:
|
|
918
|
+
path = Path(item).expanduser()
|
|
919
|
+
|
|
920
|
+
if path.is_file():
|
|
921
|
+
sources.append(path)
|
|
922
|
+
|
|
923
|
+
elif " - " in item:
|
|
924
|
+
sources.append(item)
|
|
925
|
+
|
|
926
|
+
else:
|
|
927
|
+
raise ValueError(f"Input file not found: {item}")
|
|
928
|
+
|
|
929
|
+
for group in args.track or []:
|
|
930
|
+
for spec in group:
|
|
931
|
+
if not parse_song(spec):
|
|
932
|
+
raise ValueError(
|
|
933
|
+
f"Could not parse track: {spec} "
|
|
934
|
+
f"(expected 'Artist - Title')"
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
sources.append(spec)
|
|
938
|
+
|
|
939
|
+
return sources
|
|
940
|
+
|
|
941
|
+
|
|
877
942
|
def main():
|
|
878
943
|
parser = argparse.ArgumentParser(
|
|
879
944
|
description=(
|
|
@@ -883,10 +948,24 @@ def main():
|
|
|
883
948
|
)
|
|
884
949
|
|
|
885
950
|
parser.add_argument(
|
|
886
|
-
"
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
help=
|
|
951
|
+
"inputs",
|
|
952
|
+
nargs="*",
|
|
953
|
+
metavar="INPUT",
|
|
954
|
+
help=(
|
|
955
|
+
"Text file with Artist - Title, "
|
|
956
|
+
"or an inline 'Artist - Title' track"
|
|
957
|
+
),
|
|
958
|
+
)
|
|
959
|
+
|
|
960
|
+
parser.add_argument(
|
|
961
|
+
"--track",
|
|
962
|
+
action="append",
|
|
963
|
+
nargs="+",
|
|
964
|
+
metavar="ARTIST - TITLE",
|
|
965
|
+
help=(
|
|
966
|
+
"Track to download inline, "
|
|
967
|
+
"can be given several times"
|
|
968
|
+
),
|
|
890
969
|
)
|
|
891
970
|
|
|
892
971
|
parser.add_argument(
|
|
@@ -974,15 +1053,19 @@ def main():
|
|
|
974
1053
|
if args.delay is None:
|
|
975
1054
|
args.delay = DEFAULT_DELAY
|
|
976
1055
|
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
)
|
|
1056
|
+
try:
|
|
1057
|
+
sources = collect_sources(args, settings)
|
|
1058
|
+
|
|
1059
|
+
except ValueError as error:
|
|
1060
|
+
print(f"ERROR: {error}")
|
|
982
1061
|
sys.exit(1)
|
|
983
1062
|
|
|
984
|
-
if not
|
|
985
|
-
print(
|
|
1063
|
+
if not sources:
|
|
1064
|
+
print(
|
|
1065
|
+
"ERROR: No input. "
|
|
1066
|
+
"Pass a file or an inline 'Artist - Title' track, "
|
|
1067
|
+
"or set 'input' in the config file."
|
|
1068
|
+
)
|
|
986
1069
|
sys.exit(1)
|
|
987
1070
|
|
|
988
1071
|
args.output.mkdir(
|
|
@@ -995,20 +1078,15 @@ def main():
|
|
|
995
1078
|
|
|
996
1079
|
songs = []
|
|
997
1080
|
|
|
998
|
-
for
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
).splitlines():
|
|
1002
|
-
|
|
1003
|
-
line = line.strip()
|
|
1081
|
+
for source in sources:
|
|
1082
|
+
if isinstance(source, Path):
|
|
1083
|
+
songs.extend(read_song_file(source))
|
|
1004
1084
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
song = parse_song(line)
|
|
1085
|
+
else:
|
|
1086
|
+
song = parse_song(source)
|
|
1009
1087
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1088
|
+
if song:
|
|
1089
|
+
songs.append(song)
|
|
1012
1090
|
|
|
1013
1091
|
done = read_done(done_file)
|
|
1014
1092
|
|
|
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
|