clem2itunes

Python versions PyPI - Version GitHub tag (with filter) License GitHub commits since latest release (by SemVer including pre-releases) CodeQL QA Tests Coverage Status Dependabot Documentation Status mypy uv pytest Ruff Downloads Stargazers pre-commit Prettier Follow @Tatsh Mastodon Follow

Crazy way to synchronise a remote Strawberry rated library to Music.app using Python, JXA and SSH.

Requires the following programs on the source side:

Commands

clem2itunes

Tools for Strawberry libraries.

Usage

clem2itunes [OPTIONS] COMMAND [ARGS]...

Options

-d, --debug

Enable debug level logging.

create-library

Create a curated music library from a Strawberry or Clementine database.

Usage

clem2itunes create-library [OPTIONS] [DIRECTORY]

Options

--database <database>

Path to the database file.

--include-no-cover

Include files without embedded cover art.

--no-si

Do not use SI units.

--split-dir <split_dir>

Directory to store MP3 files generated by splitting them by their CUE file.

-m, --max-size <max_size>

Maximum size in GB.

--flac

Allow FLAC files.

-t, --threshold <threshold>

Rating threshold out of 1.

Arguments

DIRECTORY

Optional argument

sync

Sync remote library to local machine.

Usage

clem2itunes sync [OPTIONS] HOST

Options

--include-no-cover

Include files without embedded cover art.

--no-si

Do not use SI units.

--remote-create-lib <remote_create_lib>

Remote create-lib script (full path or a file in PATH).

--splitcue-cache-dir <splitcue_cache_dir>

Remote directory to save split CUE data (%(username)s will be replaced with the current username or the argument to –user).

-S, --size-limit <size_limit>

Size limit in GB.

-l, --local-dir <local_dir>

Local directory to sync to.

-r, --remote-dir <remote_dir>

Remote directory to sync (%(username)s will be replaced with the current username or the argument to –user).

-u, --user <user>

Remote username.

-t, --threshold <threshold>

Minimum rating out of 1.

Arguments

HOST

Required argument

create-library is useful for creating a maximally sized library of music for copying to any device based on song ratings. It tries to avoid duplicates, and splits MP3s (losslessly) using CUE files.

sync is only for use on macOS to copy songs over, add them to iTunes/Music, and set ratings.

Sync a library to an Android device

Assumes the library is at ~/import. You have to create the /sdcard/Music/import directory on the device first.

for i in ~/import/*; do adb push --sync -Z "$(readlink -f "$i")" /sdcard/Music/import; done

If your machine lacks readlink, use perl -MCwd -le 'print Cwd::abs_path shift' ... instead.

Library

Utility functions.

async clem2itunes.utils.atomic_parsley(*args: str) Process

Run AtomicParsley command.

async clem2itunes.utils.can_read_file(file: Path) bool

Try to read a file.

Parameters:
file : Path

File to check.

Returns:

True if the file can be opened for reading.

Return type:

bool

async clem2itunes.utils.create_library(outdir_p: Path, split_dir: Path, database: Path | None = None, threshold: float = 0.6, max_size: int = 32, *, flac: bool = False, include_no_cover: bool = False, use_si: bool = True) None

Create a curated music library from a Strawberry or Clementine database.

Parameters:
outdir_p : Path

Directory to save the curated library.

split_dir : Path

Directory to save split CUE data.

database : Path | None

Path to the database file. If not passed, the Strawberry database file will be used.

threshold : float

Rating threshold out of 1.0.

max_size : int

Maximum size of the library in GiB or GB. If use_si is True, the size is in GB, otherwise it is in GiB.

flac : bool

If True, allow FLAC files. Otherwise, only MP3 and M4A files are allowed.

include_no_cover : bool

If True, include files without embedded cover art.

use_si : bool

If True, use SI units (GB) for the maximum size. Otherwise, use binary units (GiB).

async clem2itunes.utils.get_db_file_path() Path

Get the path to the Strawberry database file.

Returns:

Resolved path to the database file.

Return type:

Path

async clem2itunes.utils.get_songs_from_db(database: Path | None = None, threshold: float = 0.6, *, flac: bool = False) AsyncIterator[tuple[float, str, str, Path, int]]

Get song information from the Strawberry database.

Yields:

tuple[float, str, str, Path, int] – A tuple containing the rating, artist, title, filename, and track number.

async clem2itunes.utils.has_cover(file: Path) bool

Check if a file has an embedded cover.

Parameters:
file : Path

Audio file to inspect.

Returns:

True if an embedded cover is present.

Return type:

bool

Raises:
async clem2itunes.utils.id3ted(*args: str) Process

Run id3ted command.

async clem2itunes.utils.is_mp3_stream_valid(file: Path) bool

Check if the MP3 stream is valid.

Parameters:
file : Path

MP3 file to validate.

Returns:

True if the stream is valid or only has a known benign issue.

Return type:

bool

async clem2itunes.utils.mp3check(*args: str) Process

Run mp3check command.

async clem2itunes.utils.mp3splt(*args: str) Process

Run mp3splt command.

async clem2itunes.utils.osascript(*args: str) Process

Run osascript command.

async clem2itunes.utils.rsync(*args: str) Process

Run rsync command.

clem2itunes.utils.runner(name: str, *, stderr: int | None = None, stdout: int | None = -1, check: bool = True, text: bool = True) collections.abc.Callable[..., Coroutine[Any, Any, asp.Process]]

Create a function to run a command with the given arguments.

Parameters:
name : str

Executable name.

stderr : int | None

Subprocess stderr configuration.

stdout : int | None

Subprocess stdout configuration.

check : bool

Whether to raise on non-zero exit.

text : bool

Whether to decode output as text.

Returns:

  • Callable[…, Coroutine[Any, Any, asp.Process]] – Async wrapper that runs the command with the given arguments.

  • Arguments are based on subprocess.run().

async clem2itunes.utils.split_cue(temp_dir: Path, cue_file: Path, mp3_file: Path, track: int) Path

Split MP3 file using a CUE file.

Parameters:
temp_dir : Path

Directory for split output.

cue_file : Path

Path to the CUE file.

mp3_file : Path

Source MP3 file.

track : int

Track index to extract.

Returns:

Path to the split track MP3 file.

Return type:

Path

Raises:

RuntimeError – If mp3splt stdout was not piped.

async clem2itunes.utils.ssh(*args: str) Process

Run ssh command.

async clem2itunes.utils.try_split_cue(file: Path, split_dir: Path, track: int, artist: str, title: str) Path | None

Try to split a MP3 file using its associated CUE file.

Parameters:
file : Path

MP3 file path.

split_dir : Path

Base directory for split output.

track : int

Track index.

artist : str

Artist name (for logging).

title : str

Track title (for logging).

Returns:

Path to the split file, None if the CUE split points are invalid, or the original file if no CUE exists.

Return type:

Path | None

Raises:

sp.CalledProcessError – If the CUE file cannot be processed.

Indices and tables