diff --git a/etc/sldl.tpl.conf b/etc/sldl.tpl.conf
index 2b76dd3..1721f93 100644
--- a/etc/sldl.tpl.conf
+++ b/etc/sldl.tpl.conf
@@ -1,7 +1,7 @@
-path = "/data"
-index-path = "/data/_metadata/index.sldl.csv"
-username = "${SLDL_USER}"
-password = "${SLDL_PASS}"
+path = "${SOCKSEEK_DATA}"
+index-path = "${SOCKSEEK_DATA}/_metadata/index.sldl.csv"
+username = "${SOCKSEEK_USER}"
+password = "${SOCKSEEK_PASS}"
name-format = "{artist(/)album(/)track( )title|(_missing_tags/)slsk-foldername(/)slsk-filename}"
diff --git a/justfile b/justfile
index 41a1b2b..cd1d5e0 100644
--- a/justfile
+++ b/justfile
@@ -32,6 +32,10 @@ link plist dir:
spotify ids="7f5tPIfj21UJI4zfDmH4rx 6sAl8LhU6ediFNJRuQQIQX":
cd maple-py && uv run src/spotify.py {{ids}} --csv
+# Download via sockseek (csv path, url, search string, ...) using sldl.tpl.conf settings
+sockseek input:
+ cd maple-py && uv run src/sockseek.py {{input}}
+
sync args:
s5cmd {{args}}
diff --git a/maple-py/src/sockseek.py b/maple-py/src/sockseek.py
new file mode 100644
index 0000000..72ac915
--- /dev/null
+++ b/maple-py/src/sockseek.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env -S uv run --script
+# /// script
+# requires-python = ">=3.14"
+# dependencies = []
+# ///
+"""Thin binding around the `sockseek` CLI.
+
+Creates the data directories and invokes sockseek with the CLI-argument
+equivalents of etc/sldl.tpl.conf, so the two stay in sync without rendering a
+config file. Reads SOCKSEEK_DATA / SOCKSEEK_USER / SOCKSEEK_PASS from the
+environment (loaded from .env by just).
+
+ just sockseek # csv path, url, search string, ...
+ just sockseek "tracks.csv -a" # extra flags pass through / override
+
+Any args after the input are forwarded to sockseek verbatim, placed after the
+defaults so they win on conflict.
+"""
+import os
+import subprocess
+import sys
+from pathlib import Path
+
+# Mirror of etc/sldl.tpl.conf. Keep these in sync with that file.
+NAME_FORMAT = "{artist(/)album(/)track( )title|(_missing_tags/)slsk-foldername(/)slsk-filename}"
+
+
+def env(key):
+ val = os.environ.get(key)
+ if not val:
+ sys.exit(f"missing env var: {key}")
+ return val
+
+
+def config_args(data, user, password):
+ """CLI equivalents of etc/sldl.tpl.conf (no-progress has no sockseek flag)."""
+ return [
+ "--path", data,
+ "--index-path", f"{data}/_metadata/index.sldl.csv",
+ "--user", user,
+ "--pass", password,
+ "--name-format", NAME_FORMAT,
+ "--pref-format", "flac,mp3",
+ "--pref-min-bitrate", "200",
+ "--pref-max-bitrate", "2500",
+ "--strict-conditions",
+ "--fast-search",
+ "--concurrent-jobs", "4",
+ ]
+
+
+def main():
+ if not sys.argv[1:]:
+ sys.exit("usage: sockseek.py [extra sockseek args...]")
+ inp, *extra = sys.argv[1:]
+
+ data = env("SOCKSEEK_DATA")
+ user = env("SOCKSEEK_USER")
+ password = env("SOCKSEEK_PASS")
+
+ Path(data, "_metadata").mkdir(parents=True, exist_ok=True)
+
+ args = config_args(data, user, password)
+ cmd = ["sockseek", inp, *args, *extra]
+
+ masked = ["***" if a == password else a for a in cmd]
+ print(" ".join(masked), file=sys.stderr)
+
+ raise SystemExit(subprocess.run(cmd).returncode)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/maple-py/src/spotify.py b/maple-py/src/spotify.py
index e678f32..5dddc54 100644
--- a/maple-py/src/spotify.py
+++ b/maple-py/src/spotify.py
@@ -157,10 +157,6 @@ def main():
write_csv(playlists, sys.stdout)
return
- if as_json:
- print(json.dumps(playlists, indent=2, ensure_ascii=False))
- return
-
for pl in playlists:
print(f"\n=== {pl['name']} ({len(pl['tracks'])} tracks) ===")
for n, t in enumerate(pl["tracks"], 1):