trackfetch 2.2.5__tar.gz → 2.2.6__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.5/trackfetch.egg-info → trackfetch-2.2.6}/PKG-INFO +8 -6
- {trackfetch-2.2.5 → trackfetch-2.2.6}/README.md +7 -5
- {trackfetch-2.2.5 → trackfetch-2.2.6}/pyproject.toml +1 -1
- {trackfetch-2.2.5 → trackfetch-2.2.6}/tests/test_inline.py +42 -1
- {trackfetch-2.2.5 → trackfetch-2.2.6/trackfetch.egg-info}/PKG-INFO +8 -6
- {trackfetch-2.2.5 → trackfetch-2.2.6}/trackfetch.py +58 -10
- {trackfetch-2.2.5 → trackfetch-2.2.6}/LICENSE +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/setup.cfg +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/tests/test_config.py +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/tests/test_pipeline.py +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/tests/test_spotify.py +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/tests/test_tags_and_files.py +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/tests/test_text.py +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/tests/test_youtube.py +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/trackfetch.egg-info/SOURCES.txt +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/trackfetch.egg-info/dependency_links.txt +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/trackfetch.egg-info/entry_points.txt +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/trackfetch.egg-info/requires.txt +0 -0
- {trackfetch-2.2.5 → trackfetch-2.2.6}/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.6
|
|
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
|
|
@@ -266,8 +266,9 @@ spotify_client_id = your-client-id
|
|
|
266
266
|
spotify_client_secret = your-client-secret
|
|
267
267
|
```
|
|
268
268
|
|
|
269
|
-
Every option above can be set there, under the `[trackfetch]` section
|
|
270
|
-
|
|
269
|
+
Every option above can be set there, under the `[trackfetch]` section, except
|
|
270
|
+
`--track`, which only makes sense on the command line. The precedence is
|
|
271
|
+
always the same:
|
|
271
272
|
|
|
272
273
|
1. the command-line flag,
|
|
273
274
|
2. the config file,
|
|
@@ -275,8 +276,8 @@ precedence is always the same:
|
|
|
275
276
|
|
|
276
277
|
trackfetch looks for the config file in this order: the path given with
|
|
277
278
|
`--config`, the path in `$TRACKFETCH_CONFIG`, `trackfetch.ini` in the current
|
|
278
|
-
directory, `$XDG_CONFIG_HOME/trackfetch/trackfetch.ini
|
|
279
|
-
`~/.config/trackfetch/trackfetch.ini`
|
|
279
|
+
directory, `$XDG_CONFIG_HOME/trackfetch/trackfetch.ini`,
|
|
280
|
+
`~/.config/trackfetch/trackfetch.ini` and `~/.trackfetch.ini`.
|
|
280
281
|
|
|
281
282
|
### Spotify credentials
|
|
282
283
|
|
|
@@ -322,7 +323,8 @@ Daft Punk - One More Time
|
|
|
322
323
|
Sufjan Stevens - Mystery of Love - Remastered ← only the first " - " separates artist and title
|
|
323
324
|
```
|
|
324
325
|
|
|
325
|
-
The separator is space, hyphen, space (` - `). Lines without it are skipped
|
|
326
|
+
The separator is space, hyphen, space (` - `). Lines without it are skipped,
|
|
327
|
+
and trackfetch prints how many lines it skipped and which ones they were. See [`examples/songs.txt`](https://github.com/ByteMe6/trackfetch/blob/master/examples/songs.txt).
|
|
326
328
|
|
|
327
329
|
### Output folder
|
|
328
330
|
|
|
@@ -235,8 +235,9 @@ spotify_client_id = your-client-id
|
|
|
235
235
|
spotify_client_secret = your-client-secret
|
|
236
236
|
```
|
|
237
237
|
|
|
238
|
-
Every option above can be set there, under the `[trackfetch]` section
|
|
239
|
-
|
|
238
|
+
Every option above can be set there, under the `[trackfetch]` section, except
|
|
239
|
+
`--track`, which only makes sense on the command line. The precedence is
|
|
240
|
+
always the same:
|
|
240
241
|
|
|
241
242
|
1. the command-line flag,
|
|
242
243
|
2. the config file,
|
|
@@ -244,8 +245,8 @@ precedence is always the same:
|
|
|
244
245
|
|
|
245
246
|
trackfetch looks for the config file in this order: the path given with
|
|
246
247
|
`--config`, the path in `$TRACKFETCH_CONFIG`, `trackfetch.ini` in the current
|
|
247
|
-
directory, `$XDG_CONFIG_HOME/trackfetch/trackfetch.ini
|
|
248
|
-
`~/.config/trackfetch/trackfetch.ini`
|
|
248
|
+
directory, `$XDG_CONFIG_HOME/trackfetch/trackfetch.ini`,
|
|
249
|
+
`~/.config/trackfetch/trackfetch.ini` and `~/.trackfetch.ini`.
|
|
249
250
|
|
|
250
251
|
### Spotify credentials
|
|
251
252
|
|
|
@@ -291,7 +292,8 @@ Daft Punk - One More Time
|
|
|
291
292
|
Sufjan Stevens - Mystery of Love - Remastered ← only the first " - " separates artist and title
|
|
292
293
|
```
|
|
293
294
|
|
|
294
|
-
The separator is space, hyphen, space (` - `). Lines without it are skipped
|
|
295
|
+
The separator is space, hyphen, space (` - `). Lines without it are skipped,
|
|
296
|
+
and trackfetch prints how many lines it skipped and which ones they were. See [`examples/songs.txt`](https://github.com/ByteMe6/trackfetch/blob/master/examples/songs.txt).
|
|
295
297
|
|
|
296
298
|
### Output folder
|
|
297
299
|
|
|
@@ -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.6"
|
|
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"
|
|
@@ -127,6 +127,44 @@ class TestInlineTracks:
|
|
|
127
127
|
assert exc.value.code == 1
|
|
128
128
|
assert "Could not parse track" in capsys.readouterr().out
|
|
129
129
|
|
|
130
|
+
def test_inline_track_without_artist_exits(self, pipeline, monkeypatch, tmp_path, capsys):
|
|
131
|
+
with pytest.raises(SystemExit) as exc:
|
|
132
|
+
run_cli(monkeypatch, " - One More Time", "-o", tmp_path / "o")
|
|
133
|
+
|
|
134
|
+
assert exc.value.code == 1
|
|
135
|
+
assert "Could not parse track" in capsys.readouterr().out
|
|
136
|
+
|
|
137
|
+
def test_inline_track_without_title_exits(self, pipeline, monkeypatch, tmp_path, capsys):
|
|
138
|
+
with pytest.raises(SystemExit) as exc:
|
|
139
|
+
run_cli(monkeypatch, "Daft Punk - ", "-o", tmp_path / "o")
|
|
140
|
+
|
|
141
|
+
assert exc.value.code == 1
|
|
142
|
+
assert "Could not parse track" in capsys.readouterr().out
|
|
143
|
+
|
|
144
|
+
def test_wrong_separator_hints_at_the_track_format(
|
|
145
|
+
self, pipeline, monkeypatch, tmp_path, capsys
|
|
146
|
+
):
|
|
147
|
+
with pytest.raises(SystemExit) as exc:
|
|
148
|
+
run_cli(monkeypatch, "Daft Punk -One More Time", "-o", tmp_path / "o")
|
|
149
|
+
|
|
150
|
+
assert exc.value.code == 1
|
|
151
|
+
assert "Artist - Title" in capsys.readouterr().out
|
|
152
|
+
|
|
153
|
+
def test_unparsable_lines_in_a_file_are_reported(
|
|
154
|
+
self, pipeline, monkeypatch, tmp_path, capsys
|
|
155
|
+
):
|
|
156
|
+
songs = tmp_path / "songs.txt"
|
|
157
|
+
songs.write_text(
|
|
158
|
+
"Daft Punk -One More Time\nRadiohead - Paranoid Android\n",
|
|
159
|
+
encoding="utf-8",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
run_cli(monkeypatch, songs, "-o", tmp_path / "o")
|
|
163
|
+
log = capsys.readouterr().out
|
|
164
|
+
|
|
165
|
+
assert "WARNING: skipped 1 line(s)" in log
|
|
166
|
+
assert "Daft Punk -One More Time" in log
|
|
167
|
+
|
|
130
168
|
def test_inline_track_uses_the_output_rules(self, pipeline, monkeypatch, tmp_path):
|
|
131
169
|
out = tmp_path / "o"
|
|
132
170
|
config = tmp_path / "cfg.ini"
|
|
@@ -164,4 +202,7 @@ class TestCollectSources:
|
|
|
164
202
|
songs = tmp_path / "songs.txt"
|
|
165
203
|
songs.write_text("# comment\n\nA - B\nbroken line\nC - D\n", encoding="utf-8")
|
|
166
204
|
|
|
167
|
-
|
|
205
|
+
parsed, skipped = m.read_song_file(songs)
|
|
206
|
+
|
|
207
|
+
assert parsed == [("A", "B"), ("C", "D")]
|
|
208
|
+
assert skipped == ["broken line"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trackfetch
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.6
|
|
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
|
|
@@ -266,8 +266,9 @@ spotify_client_id = your-client-id
|
|
|
266
266
|
spotify_client_secret = your-client-secret
|
|
267
267
|
```
|
|
268
268
|
|
|
269
|
-
Every option above can be set there, under the `[trackfetch]` section
|
|
270
|
-
|
|
269
|
+
Every option above can be set there, under the `[trackfetch]` section, except
|
|
270
|
+
`--track`, which only makes sense on the command line. The precedence is
|
|
271
|
+
always the same:
|
|
271
272
|
|
|
272
273
|
1. the command-line flag,
|
|
273
274
|
2. the config file,
|
|
@@ -275,8 +276,8 @@ precedence is always the same:
|
|
|
275
276
|
|
|
276
277
|
trackfetch looks for the config file in this order: the path given with
|
|
277
278
|
`--config`, the path in `$TRACKFETCH_CONFIG`, `trackfetch.ini` in the current
|
|
278
|
-
directory, `$XDG_CONFIG_HOME/trackfetch/trackfetch.ini
|
|
279
|
-
`~/.config/trackfetch/trackfetch.ini`
|
|
279
|
+
directory, `$XDG_CONFIG_HOME/trackfetch/trackfetch.ini`,
|
|
280
|
+
`~/.config/trackfetch/trackfetch.ini` and `~/.trackfetch.ini`.
|
|
280
281
|
|
|
281
282
|
### Spotify credentials
|
|
282
283
|
|
|
@@ -322,7 +323,8 @@ Daft Punk - One More Time
|
|
|
322
323
|
Sufjan Stevens - Mystery of Love - Remastered ← only the first " - " separates artist and title
|
|
323
324
|
```
|
|
324
325
|
|
|
325
|
-
The separator is space, hyphen, space (` - `). Lines without it are skipped
|
|
326
|
+
The separator is space, hyphen, space (` - `). Lines without it are skipped,
|
|
327
|
+
and trackfetch prints how many lines it skipped and which ones they were. See [`examples/songs.txt`](https://github.com/ByteMe6/trackfetch/blob/master/examples/songs.txt).
|
|
326
328
|
|
|
327
329
|
### Output folder
|
|
328
330
|
|
|
@@ -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.6"
|
|
230
230
|
},
|
|
231
231
|
)
|
|
232
232
|
|
|
@@ -875,8 +875,13 @@ def apply_config(args, parser, settings):
|
|
|
875
875
|
|
|
876
876
|
|
|
877
877
|
def read_song_file(path: Path):
|
|
878
|
-
"""Read Artist - Title pairs from a text file.
|
|
878
|
+
"""Read Artist - Title pairs from a text file.
|
|
879
|
+
|
|
880
|
+
Returns a (songs, skipped) tuple, where skipped holds the lines that
|
|
881
|
+
could not be parsed.
|
|
882
|
+
"""
|
|
879
883
|
songs = []
|
|
884
|
+
skipped = []
|
|
880
885
|
|
|
881
886
|
for line in path.read_text(
|
|
882
887
|
encoding="utf-8",
|
|
@@ -893,7 +898,10 @@ def read_song_file(path: Path):
|
|
|
893
898
|
if song:
|
|
894
899
|
songs.append(song)
|
|
895
900
|
|
|
896
|
-
|
|
901
|
+
else:
|
|
902
|
+
skipped.append(line)
|
|
903
|
+
|
|
904
|
+
return songs, skipped
|
|
897
905
|
|
|
898
906
|
|
|
899
907
|
def collect_sources(args, settings):
|
|
@@ -921,10 +929,19 @@ def collect_sources(args, settings):
|
|
|
921
929
|
sources.append(path)
|
|
922
930
|
|
|
923
931
|
elif " - " in item:
|
|
932
|
+
if not parse_song(item):
|
|
933
|
+
raise ValueError(
|
|
934
|
+
f"Could not parse track: {item} "
|
|
935
|
+
f"(expected 'Artist - Title')"
|
|
936
|
+
)
|
|
937
|
+
|
|
924
938
|
sources.append(item)
|
|
925
939
|
|
|
926
940
|
else:
|
|
927
|
-
raise ValueError(
|
|
941
|
+
raise ValueError(
|
|
942
|
+
f"Input file not found: {item} "
|
|
943
|
+
f"(if this is a track, use the 'Artist - Title' format)"
|
|
944
|
+
)
|
|
928
945
|
|
|
929
946
|
for group in args.track or []:
|
|
930
947
|
for spec in group:
|
|
@@ -944,7 +961,16 @@ def main():
|
|
|
944
961
|
description=(
|
|
945
962
|
"Download music using Spotify metadata "
|
|
946
963
|
"and YouTube audio"
|
|
947
|
-
)
|
|
964
|
+
),
|
|
965
|
+
epilog=(
|
|
966
|
+
"A config file with the defaults can be used as well. It is "
|
|
967
|
+
"looked up in --config, $TRACKFETCH_CONFIG, ./trackfetch.ini, "
|
|
968
|
+
"$XDG_CONFIG_HOME/trackfetch/trackfetch.ini, "
|
|
969
|
+
"~/.config/trackfetch/trackfetch.ini and ~/.trackfetch.ini. "
|
|
970
|
+
"A command-line flag wins over the config file, and the config "
|
|
971
|
+
"file wins over the built-in default."
|
|
972
|
+
),
|
|
973
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
948
974
|
)
|
|
949
975
|
|
|
950
976
|
parser.add_argument(
|
|
@@ -984,7 +1010,10 @@ def main():
|
|
|
984
1010
|
"--config",
|
|
985
1011
|
type=Path,
|
|
986
1012
|
default=None,
|
|
987
|
-
help=
|
|
1013
|
+
help=(
|
|
1014
|
+
"Path to a config file "
|
|
1015
|
+
"(default: auto-detected, see below)"
|
|
1016
|
+
),
|
|
988
1017
|
)
|
|
989
1018
|
|
|
990
1019
|
parser.add_argument(
|
|
@@ -992,21 +1021,21 @@ def main():
|
|
|
992
1021
|
"--format",
|
|
993
1022
|
choices=list(AUDIO_FORMATS),
|
|
994
1023
|
default=None,
|
|
995
|
-
help="Audio format",
|
|
1024
|
+
help="Audio format (default: m4a)",
|
|
996
1025
|
)
|
|
997
1026
|
|
|
998
1027
|
parser.add_argument(
|
|
999
1028
|
"--title-only",
|
|
1000
1029
|
action="store_true",
|
|
1001
1030
|
default=None,
|
|
1002
|
-
help="Use only title in filename",
|
|
1031
|
+
help="Use only title in filename (default: off)",
|
|
1003
1032
|
)
|
|
1004
1033
|
|
|
1005
1034
|
parser.add_argument(
|
|
1006
1035
|
"--delay",
|
|
1007
1036
|
type=float,
|
|
1008
1037
|
default=None,
|
|
1009
|
-
help="Delay between songs in seconds",
|
|
1038
|
+
help="Delay between songs in seconds (default: 1.0)",
|
|
1010
1039
|
)
|
|
1011
1040
|
|
|
1012
1041
|
parser.add_argument(
|
|
@@ -1077,10 +1106,17 @@ def main():
|
|
|
1077
1106
|
failed_file = args.output / "failed.txt"
|
|
1078
1107
|
|
|
1079
1108
|
songs = []
|
|
1109
|
+
skipped = []
|
|
1080
1110
|
|
|
1081
1111
|
for source in sources:
|
|
1082
1112
|
if isinstance(source, Path):
|
|
1083
|
-
|
|
1113
|
+
file_songs, file_skipped = read_song_file(source)
|
|
1114
|
+
|
|
1115
|
+
songs.extend(file_songs)
|
|
1116
|
+
|
|
1117
|
+
skipped.extend(
|
|
1118
|
+
(source, line) for line in file_skipped
|
|
1119
|
+
)
|
|
1084
1120
|
|
|
1085
1121
|
else:
|
|
1086
1122
|
song = parse_song(source)
|
|
@@ -1093,6 +1129,18 @@ def main():
|
|
|
1093
1129
|
print(f"Found {len(songs)} songs")
|
|
1094
1130
|
print(f"Already completed: {len(done)}")
|
|
1095
1131
|
|
|
1132
|
+
if skipped:
|
|
1133
|
+
print(
|
|
1134
|
+
f"WARNING: skipped {len(skipped)} line(s) "
|
|
1135
|
+
f"that are not 'Artist - Title':"
|
|
1136
|
+
)
|
|
1137
|
+
|
|
1138
|
+
for path, line in skipped[:5]:
|
|
1139
|
+
print(f" {path}: {line}")
|
|
1140
|
+
|
|
1141
|
+
if len(skipped) > 5:
|
|
1142
|
+
print(f" ... and {len(skipped) - 5} more")
|
|
1143
|
+
|
|
1096
1144
|
if config_path:
|
|
1097
1145
|
print(f"Config: {config_path}")
|
|
1098
1146
|
|
|
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
|