MusicDropper
A small KDE Plasma app to drop music onto a target folder (a mounted MP3 player, phone, SD card, car USB stick…). Dropped files are copied, never moved, and converted to a chosen format. The default format is high-quality MP3 (LAME VBR V0).
Behaviour
| Dropped file | What happens |
|---|---|
| Different codec (FLAC, WAV, Ogg, AAC… when the target is MP3) | Converted; tags and cover art kept (cover art isn't kept for Ogg/Opus targets) |
| Already in the target codec (e.g. any MP3 when the target is MP3) | Copied byte-for-byte, whatever its bitrate: re-encoding a 128k MP3 gains nothing, and re-encoding a 320k one loses quality. The one exception is a sample rate above the limit (below) |
Right codec, wrong container (e.g. raw .aac with the AAC/M4A target) |
Remuxed into the right container, without re-encoding |
| Already exists at the destination | Skipped by default (or overwrite / keep both) |
| Not audio, or a hidden file | Ignored |
Device bitrate limit (off by default): for devices that can't play files above some bitrate. When it is on, files already in the target format whose average bitrate is above the limit are re-encoded. All conversions are capped at the limit as well. For VBR profiles this means switching to the highest standard constant bitrate within the limit, because VBR frames can peak above the average (MP3 VBR peaks at 320 kbps).
Max sample rate (48 kHz by default, saved between sessions): files with a higher sample rate are resampled, including files already in the target format, which are otherwise copied unchanged. Some devices mishandle hi-res audio; Garmin watches, for example, are reported to play 96 kHz files at the wrong speed. Sources in the 44.1 kHz family (88.2, 176.4 kHz) go to 44.1 kHz, and others to 48 kHz, or the highest rate within a lower limit. Choose No limit to keep every file's rate where the format allows it. Opus always plays at 48 kHz, so this setting doesn't affect it, and MP3 can't exceed 48 kHz anyway.
Replace existing music (button; off by default and not saved): for the next drop only, first delete the music already in the target, then copy the dropped music. This is useful when a watch or player has little storage. It works like this:
- A confirmation dialog shows how many files will go and lists them. Cancel is the default button.
- Only audio files are deleted, including those in subfolders. Other files (playlists, device settings) are kept, and folders are removed only once they are empty.
- The app refuses if any dropped music lives inside the target, because deleting would destroy the source.
- The button switches itself off after every drop, whether you confirmed or cancelled.
- Command line:
--batch --replace-existingasks you to typeyes.--yesskips the question, and the command refuses when not run in a terminal.
Playlists: drop an .m3u, .m3u8 or .pls file and the tracks it lists are
copied (and converted) like dropped files. Then the playlist is written to the target:
- If you set your music library folder (in the window, or
--library DIR), a track inside it is copied to the same path relative to the target, so the target mirrors your library:Björk/Possibly Maybe.mp3,Artist/Album/CD1/01.mp3. Otherwise, and for tracks outside the library, a track is filed asArtist/Album/file, taken from its last two folders, plus a disc folder such asCD1. That rule can't tell a loose track in an artist folder from one in an album folder, soBjörk/Possibly Maybe.mp3would land in<library folder name>/Björk/. - Either way a song lands in the same place whichever playlist lists it, so playlists that share songs don't duplicate them.
- The playlist goes in the target folder as UTF-8
.m3u8, named after the dropped playlist, with paths relative to itself. It points at the files as written, with converted extensions, FAT-safe names and any "(1)" renames. - It is written after its tracks are done. Tracks that are missing or failed are left
out and listed under the playlist in the window. Stream URLs and Windows paths
(
C:\…) are left out too. - A track already on the target is kept in the playlist. If you also drop it directly, it is copied once, to wherever the direct drop puts it.
- The playlist file follows the "Existing files" option: by default an existing playlist of the same name is left alone, so choose Overwrite to update one.
- Playlists inside a dropped folder are ignored; drop the playlist itself.
Garmin lists .m3u and .m3u8 among the playlist formats its music watches support
natively, so playlists copied this way shouldn't need Garmin Express.
Free space: before copying anything, MusicDropper estimates how much space the drop will take and compares it with what's free on the target. Files copied unchanged count at their own size, and converted files at duration × output bitrate. 32 MB plus 2% is kept free as a margin.
- If it won't fit, the window asks Copy Anyway / Cancel, with Cancel the default
(screenshot: light, dark). In
batch mode you're asked in the terminal. When there's nobody to ask, the batch stops,
unless
--ignore-spaceis given. - Each file is checked again just before it is written. If the target fills up anyway, copying stops there and the remaining files are reported as cancelled.
- Free space comes from the filesystem for folders and mounted devices, and from KDE's
MTP service (
kmtpd) formtp:phones and watches.smb:andsftp:targets can't report it, so they aren't checked.
Other details:
- A dropped folder keeps its name and inner structure at the target.
- Output goes to a hidden temp file that is renamed into place only on success. A failed or cancelled conversion leaves no partial files behind. Sources are only ever read.
- "FAT-safe file names" (on by default) replaces
<>:"\|?*in output names, because most players use FAT/exFAT. - Several files are converted in parallel, one per CPU core.
Phones, watches and other KIO targets
The target can be a local/mounted folder or any KDE (KIO) URL. Pick it in the
folder dialog, or type it, e.g. mtp:/fenix 8 - 47mm/Internal Storage/Music,
smb://nas/music or sftp://host/music. KIO targets are written through
kioclient, which uses the same KIO workers as Dolphin.
MTP behaviour seen on a real device (Garmin fenix 8), and how it's handled:
-
Copy to a device from one program at a time. Two transfers to the same device at once can lose or corrupt files, while each reports success. MusicDropper looks after itself: one MusicDropper at a time uses a device. If another one (say a
--batchrun while the window is copying) is already using it, the window offers to Wait until it has finished, and--batchstops without copying anything (--waitmakes it wait instead). Other programs, such as Dolphin or Garmin Express, don't know about this: don't copy to the device with them while MusicDropper is copying to it, or the other way round. (Mistakes are unlikely to be silent, as each upload is size-checked, but a file written by the other program at the very name MusicDropper is copying could be lost.) -
Concurrent transfers silently lose files. Uploads are reported as successful, yet the files are missing or truncated. Conversions still run in parallel locally, but every device operation is serialised, and other MusicDropper processes are kept off the device (above). Each upload is checked against the size the device reports, and retried once if it is wrong.
-
Existing files can't be overwritten, not even by renaming onto them. So "Overwrite" uploads the new file under a hidden temporary name (
.musicdropper-…) and checks it, then removes the old file and renames the new one into place: a failed upload never costs the old file. If the rename then fails, the new file is uploaded again under its real name; if even that fails, the hidden copy is kept and its name reported, so the music is never lost from the device. New files are copied straight to their names. -
mkdirisn't recursive, so folders are created level by level. -
Names that differ only in case collide, so existence checks ignore case.
-
A missing target folder is never silently recreated. It usually means the device was unplugged or is locked.
-
Cancelled or failed uploads are removed from the device. Exceptions, in case another program has just written that name: if the device refuses a copy and a file then has that name, it is left alone and reported as possibly incomplete; and after a cancel, a file there at least as big as MusicDropper's is left alone.
-
The watch's Music folder only accepts audio files and
.m3u/.m3u8playlists; other files (.txt,.pls, images) are refused. MusicDropper only writes those two kinds. -
Unplugging during a batch fails the remaining files with "could not write to file". The fenix 8 discards the file that was being written, so plug back in and drop the same music again: what's already there is skipped and the rest is copied. Other devices may behave differently; if one keeps a partial file, delete it before dropping again.
-
If MusicDropper itself is killed or crashes mid-transfer, a hidden
.musicdropper-…file may be left in the target folder (for a local folder too). MusicDropper ignores such files, but other software, possibly the device itself, might show them; they can simply be deleted.
Sources: network shares and symlinks
- Mounted shares (fstab,
mount.cifs, NFS,/run/media/…) are ordinary folders. - Network locations browsed in Dolphin without mounting (
smb://,nfs://,sftp://…) can be dragged in or passed on the command line. They are mapped to local paths through kio-fuse (kio-fusepackage, part of a standard Plasma install).mtp:devices can only be used as targets. - Symbolic links are followed, including symlinked album folders inside a dropped folder. Each real folder is visited once, so link loops are harmless, and music reachable by several links is copied once.
- Broken links and unreadable files are reported as errors, not skipped silently.
- If the target is on a network filesystem that refuses permission or time changes, the files are still written; only that metadata is skipped.
KDE integration
- Application menu entry. "Open With" works for audio files too.
- Dolphin context menu: Copy & Convert with MusicDropper on audio files, playlists and folders.
- Drag files onto the window, onto its task manager entry, or onto a pinned launcher/desktop icon.
- Single instance: further drops and context-menu calls go to the open window.
- Progress is shown on the Plasma task manager entry. A desktop notification appears when a batch finishes while the window isn't focused.
- Uses the system Qt when available, so it gets the Breeze theme, icons and native KDE file dialogs.
install.shinstalls the app icon into~/.local/share/icons/hicolor. Without it the window and notifications use the copy bundled inmusicdropper/icons/.
Dependencies
MusicDropper is a Linux app, built for KDE Plasma.
Required
- Python 3.10 or later.
- PyQt6 and imageio-ffmpeg from PyPI. They are listed in
requirements.txt,pyproject.tomland the PEP 723 block inrun.py. imageio-ffmpeg provides a static ffmpeg with the LAME (MP3), Vorbis and Opus encoders, so no system ffmpeg is needed.
You don't have to install these yourself: install.sh does it, or uv run when running
from the checkout (see Installing).
Recommended: uv to manage the Python packages.
Without it everything still works through python3 -m venv and pip, but uv:
- is much faster at creating the environment and installing the packages;
- doesn't need the
python3-venvpackage (Debian and Ubuntu leave it out ofpython3); - can download a suitable Python if the system's is older than 3.10;
- runs the app straight from the checkout (
uv run run.py) and runs the tests against the locked versions inuv.lock.
Install it from your distribution if it packages uv, with
curl -LsSf https://astral.sh/uv/install.sh | sh, or with pipx install uv.
Optional system packages (Debian/Ubuntu names; a standard Plasma desktop has most of them)
python3-pyqt6: PyQt6 built against the system Qt. With it the app uses the Breeze theme and native KDE file dialogs; the PyPI PyQt6 brings its own Qt and doesn't. The installer uses it when it's present, with or without uv.ffmpeg: used instead of the bundled build when it's onPATH(with itsffprobefor reading files; without ffprobe the same details come fromffmpeg -i). To choose a build, setMUSICDROPPER_FFMPEG, andMUSICDROPPER_FFPROBE(empty means never use ffprobe).kde-cli-tools(kioclient) andkio-extras: needed for phones and watches (mtp:) and other KIO targets.kio-fuse: lets you drop music fromsmb:,sftp:andnfs:locations.python3-venv: only needed without uv, soinstall.shcan create a virtual environment with the standard library'svenv.
Installing
Get a release from the releases page and unpack it, or clone the current release with git:
git clone --branch v1.0.0 https://gitlab.com/aje.baugh/musicdropper.git
cd musicdropper
Then, from that folder:
./install.sh # per-user: ~/.local/bin, app menu, Dolphin action, icon, metainfo
./install.sh --venv # the same, but always use a virtual environment
./install.sh --uninstall
The installer sets up the Python packages using the first of these that works:
-
The system Python as it is, if it already has PyQt6 and an ffmpeg is available. Nothing is installed.
-
Otherwise a virtual environment in
~/.local/share/musicdropper/venv:- with uv (preferred) if it is installed;
- otherwise with
python3 -m venvand pip.
A system PyQt6 is still used if there is one, so only the missing packages are downloaded.
Running ./install.sh again (after installing uv, say) brings the environment up to
date. --uninstall removes it along with the launcher, menu entries, icon and metainfo;
your settings are kept. The metainfo (~/.local/share/metainfo) describes MusicDropper to
software centres such as Discover.
The installed musicdropper launcher (bin/musicdropper) uses the system Python if it
is complete, else the installer's environment, else uv run. Because the launcher is a
link into this checkout, a git pull updates the installed app; run ./install.sh
again if requirements.txt changed.
To run from the checkout without installing anything, use uv. It fetches the packages on first run:
uv run run.py
Running
musicdropper [FILES/FOLDERS...] # opens the window (or forwards to the open one) and copies them
musicdropper --batch -t /media/me/PLAYER/Music ~/Music/Album # headless
musicdropper --batch -t DIR -f opus-160 --max-kbps 128 FILES # other format, with limit
musicdropper --batch -t DIR -f flac --max-rate none FILES # keep hi-res sample rates
musicdropper --batch -t DIR ~/Playlists/Running.m3u # a playlist and its tracks
musicdropper --batch -t DIR --library ~/Music Running.m3u # tracks mirror ~/Music's layout
musicdropper --batch -t "mtp:/fenix 8 - 47mm/Internal Storage/Music" *.flac # to a watch
musicdropper --batch --wait -t "mtp:/…" *.flac # wait if another MusicDropper is using it
musicdropper --list-formats
uv run run.py takes the same arguments.
The About button, to the right of the target folder buttons, shows the version, licence and project links, plus the Python, Qt, PyQt and ffmpeg versions in use. Please include those versions when reporting a problem.
Settings (target, recent targets, format, options, window size) are stored in
~/.config/musicdropper/settings.json. Command-line options in --batch mode
override them for that run only.
Tests
With uv (preferred):
uv run --group dev pytest
uv run --group dev ruff check . # lint, as CI does
Without uv, use the installer's environment (or any virtual environment with the
requirements.txt packages) and add pytest:
~/.local/share/musicdropper/venv/bin/python -m pip install pytest
~/.local/share/musicdropper/venv/bin/python -m pytest
The tests generate small audio fixtures with ffmpeg. The GUI tests run on Qt's offscreen
platform. If the sample .flac/.mp3 files are in the project root (they are
git-ignored), an extra integration test runs on them. KIO handling is tested against a
fake kioclient (tests/fake_kioclient.py) that reproduces the MTP quirks above. To
round-trip through a real device (the test creates a scratch subfolder and removes it):
MUSICDROPPER_TEST_KIO_URL="mtp:/<device>/Internal Storage/Music" uv run --group dev pytest -k real_device
GitLab CI (.gitlab-ci.yml) runs on every push and merge request:
- lint: ruff, the lockfile, the
.desktopfile and shell syntax. - test: the whole suite on Python 3.10 and 3.14 with Debian's ffmpeg.
- test-pypi: the whole suite again with only PyPI packages: the bundled ffmpeg and no ffprobe.
- install-no-uv: runs
install.shwithout uv, converts a file with the installed launcher, then uninstalls.
The real-device tests are skipped there.
The screenshots are rendered by python3 tools/screenshots.py. It uses the system
PyQt6 with Breeze Light and Breeze Dark at 2× scale, needs no display, and should be
re-run after UI changes.
Changes are recorded in CHANGELOG.md. Source files carry SPDX licence lines, and a test checks that every tracked source file has them.
Layout
musicdropper/profiles.py output formats and their encoder settings
musicdropper/probe.py finding ffmpeg, reading file details (ffprobe or ffmpeg -i)
musicdropper/core.py expanding drops, decisions, naming, conflict handling, copy/transcode (no Qt)
musicdropper/playlists.py reading dropped playlists, writing them to the target
musicdropper/space.py free space on the target, size estimates
musicdropper/targets.py local folder vs KIO URL targets (kioclient, MTP workarounds)
musicdropper/settings.py persisted settings
musicdropper/gui.py PyQt6 window, drop zone and About dialog
musicdropper/kde.py D-Bus notifications, task-manager progress, app icon
musicdropper/icons/ app icon (freedesktop hicolor layout)
musicdropper/app.py CLI, single-instance handling
bin/musicdropper launcher (system Python, installer's venv, or uv)
install.sh per-user install/uninstall (see Installing)
run.py uv entry point with PEP 723 dependencies
requirements.txt dependencies for pip installs (kept in sync with pyproject.toml)
pyproject.toml, uv.lock package metadata (version from musicdropper/__init__.py) and locked dev environment
CHANGELOG.md release notes
.gitlab-ci.yml CI: lint, tests (two Pythons, PyPI-only ffmpeg), install without uv
data/ .desktop file, Dolphin service menu, AppStream metainfo
screenshots/ README and AppStream screenshots (tools/screenshots.py)
tools/ developer scripts
tests/ pytest suite, fake kioclient
branding/ logos and project avatar (see branding/README.md)
Metadata
Release files for musicdropper 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| musicdropper-1.0.0.tar.gz | 165.0 kB | Without provenance | Details for musicdropper-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| musicdropper-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for musicdropper-1.0.0-py3-none-any.whl |
Total release size: 281.0 kB
Release files / musicdropper-1.0.0.tar.gz
| Download URL | musicdropper-1.0.0.tar.gz |
|---|---|
| Size | 165.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4f31fc0e1a0db9f4521f2ef85204b9480675e82b0f681abed20d52a044917220
|
|
BLAKE2b-256 checksum How to use checksums |
aaec58473ef037cd8e1f667a3eee686d3f7272af464ec66b9f3d42008069f6f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / musicdropper-1.0.0-py3-none-any.whl
| Download URL | musicdropper-1.0.0-py3-none-any.whl |
|---|---|
| Size | 116.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2ccede1d63e0199f566f96c65bccdbea9e42d3e3b880ec7eaa347c998abbd23e
|
|
BLAKE2b-256 checksum How to use checksums |
d7acb7455a26d2d40de8ba9e2fa6f0cec5901d65e91ae401a492502b11e020b6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.21 {"installer":{"name":"uv","version":"0.12.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for musicdropper 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.