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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trackfetch
3
- Version: 2.2.5
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. The
270
- precedence is always the same:
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` (usually
279
- `~/.config/trackfetch/trackfetch.ini`) and `~/.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. See [`examples/songs.txt`](https://github.com/ByteMe6/trackfetch/blob/master/examples/songs.txt).
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. The
239
- precedence is always the same:
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` (usually
248
- `~/.config/trackfetch/trackfetch.ini`) and `~/.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. See [`examples/songs.txt`](https://github.com/ByteMe6/trackfetch/blob/master/examples/songs.txt).
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.5"
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
- assert m.read_song_file(songs) == [("A", "B"), ("C", "D")]
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.5
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. The
270
- precedence is always the same:
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` (usually
279
- `~/.config/trackfetch/trackfetch.ini`) and `~/.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. See [`examples/songs.txt`](https://github.com/ByteMe6/trackfetch/blob/master/examples/songs.txt).
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.5"
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
- return songs
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(f"Input file not found: {item}")
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="Path to a config file",
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
- songs.extend(read_song_file(source))
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