Skip to content

To do list #16

Description

@donmerendolo

I'll probably do these this summer:

  • Separate the code into several, more manageable and readable files.
  • Rewrite main so it's more readable.
  • Rewrite _fetch_all_items so it doesn't take two kinds of data.
  • Add users option to config.cfg.
  • The program still creates legacy uris and external_ids in config by default. It shouldn't do that.
  • Running a bare -l changes the config file unnecessarily.
  • In config.cfg, format can take json and csv separated by a space. fields takes multiple arguments separated by commas. Choose one or allow both.
  • Write some tests.
  • Publish in PyPI? Use $XDG_CONFIG_HOME and place .cache somewhere else?
  • fields option in config should, by default, have the default fields, not be empty. Probably a comment above it with all options too.
  • Binaries' names are currently kinda ugly.

I'll probably release all of these in a v1.0.0 version, making it as much backwards compatible as I can.

Fable had some interesting suggestions and bugs to fix too:

Details Had a look through `exportify-cli.py` — a few things you might want to add:

Actual bugs I spotted:

  • The output option in config.cfg is dead code: -o has default="./playlists" in the click decorator, so output_param is always truthy and output_param if output_param else cfg.get(...) never falls back to config. Same pattern as your -l issue — config/CLI resolution deserves one unified pass.
  • Sorting is lexicographic on str(): --sort-key Popularity gives 9 > 10 > 100 order, and str(None) or "" is "None", so the or "" fallback never fires. Numeric keys need numeric comparison, and Nones should sort predictably.
  • os.get_terminal_size() raises OSError when stdout isn't a tty, so exportify-cli -l | grep foo or running from cron crashes. shutil.get_terminal_size() has a fallback built in.
  • In _fetch_all_items paginated mode, total = results.get("total") can be None for some endpoints, and while len(items) < total would then throw a TypeError. Worth a guard (results.get("next") is the more robust loop condition anyway).
  • The format resolution uses substring checks ("csv" in cfg.get(...)), so a typo'd or empty format silently exports nothing with exit code 0. Validate and error instead.
  • If you're building with PyInstaller --onefile, Path(__file__).parent points to the temp extraction dir (_MEIPASS), not next to the binary — so .cache and config.cfg may be written somewhere ephemeral. Check for sys.frozen and use Path(sys.executable).parent in that case (this ties into your XDG item).

Smaller stuff / polish:

  • CSV output writes list fields as Python reprs (['Artist A', 'Artist B']). Original exportify joins with commas — probably want ", ".join(...) for CSV at least.
  • README says playlist names support "partial matching" but the code only does startswith prefix matching. Align one with the other.
  • -c pointing at a nonexistent explicit path silently starts the config-creation wizard there; arguably that should be an error when the user passed the path themselves.
  • get_version() shells out to git at import time on every invocation — cheap but unnecessary; lazy-evaluate it.
  • The redundant if unfetched_ids: check inside the show-fetching loop in _fetch_all_items (it's already iterating over it).

Feature ideas:

  • Skip-unchanged exports: compare the playlist snapshot_id against the last export and skip if identical — makes -a on a cron job fast and diff-friendly (the original exportify does something similar).
  • Non-zero exit code / summary when some requested playlists failed or were skipped (ambiguous prefix currently just prints "Skipping" and exits 0).
  • Optional playlist metadata in output or a sidecar file: description, owner, snapshot_id, export timestamp.
  • A --quiet/--verbose flag now that you have logging set up (currently logger.info is invisible).
  • Once you split into modules and add tests: ruff + a CI lint/test workflow, since you already have .github/workflows.

The config/CLI resolution one is probably worth prioritizing since three of your existing items (-l writing config, legacy uris/external_ids, format vs fields syntax) plus the dead output fallback all stem from the same tangle — a single "config schema + precedence" refactor would knock them all out together.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions