Name: music-sync Version: 0.1.1 Release: 1%{?dist} Summary: Sync a local music library to an iPhone without iTunes # Personal/local tool; MIT is just a permissive default -- change freely. License: MIT URL: https://github.com/example/music-sync Source0: %{name}-%{version}.tar.gz BuildArch: noarch BuildRequires: desktop-file-utils BuildRequires: librsvg2-tools BuildRequires: python3 BuildRequires: systemd-rpm-macros # For %check's unit test suite (tests/) -- mirrors the runtime Requires # below (%check runs in this build chroot, which only gets BuildRequires # installed, not Requires). BuildRequires: python3-pyside6 Requires: python3 Requires: python3-pyside6 Requires: python3-dbus Requires: glib2 Requires: xdg-utils Requires: hicolor-icon-theme # idevice_id/ideviceinfo/ideviceinstaller -- physical-presence detection, # device naming, and checking whether the destination app is installed. # There is no gvfs/UDisks2 layer in this app at all (unlike this project's # sibling, photo-import, which also handles SD cards and MTP cameras): # every sync target here is specifically an iPhone's file-sharing share, so # ifuse below is the only mount mechanism this app needs, full stop. Requires: libimobiledevice-utils # ifuse -- FUSE-mounts one app's house_arrest Documents share # (`ifuse --documents `), which is the actual sync destination. # Pulls in fuse/fuse3 automatically; no separate Requires needed for that. Requires: ifuse %description A personal, local-only Wayland desktop app that watches for an iPhone connecting and syncs a local music library folder to it -- specifically into a destination app on the phone that opts in to file sharing (VLC by default), since Apple never opened up the stock Music app's own library to third-party sync tools on Linux, and the old iTunesDB-on-disk trick libgpod/gtkpod used for classic iPods stopped working on iPhones once iOS moved to a SQLite-based library format years ago. Your library's existing Artist/Album folder structure is read directly (no tag scanning, no transcoding -- whatever format the file already is, VLC plays it) and mirrored onto the phone; a picker lets you sync the whole thing or just specific artists/albums. Repeat syncs are fast: a persisted device manifest means only new or changed tracks are ever re-copied. An optional "remove unselected" mode also deletes on-device tracks this app previously placed that have since fallen outside the current selection -- always a manual, confirmed action, never triggered by auto-sync. Native desktop notifications, XDG-portal-aware light/dark theming, no telemetry, no network access at all. %prep %autosetup -n %{name}-%{version} %build # Pure Python + QML: nothing to compile. Rasterize the app icon from its # SVG source at the sizes the hicolor theme expects. mkdir -p render for size in 16 32 48 64 128 256 512; do rsvg-convert -w "${size}" -h "${size}" icons/%{name}.svg -o "render/%{name}-${size}.png" done %install install -Dm755 packaging/%{name} %{buildroot}%{_bindir}/%{name} install -d %{buildroot}%{_datadir}/%{name} cp -a main.py backend qml icons %{buildroot}%{_datadir}/%{name}/ desktop-file-install --dir=%{buildroot}%{_datadir}/applications \ --set-key=Exec --set-value=%{name} \ --set-key=Icon --set-value=%{name} \ %{name}.desktop install -Dm644 icons/%{name}.svg %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/%{name}.svg for size in 16 32 48 64 128 256 512; do install -Dm644 "render/%{name}-${size}.png" \ "%{buildroot}%{_datadir}/icons/hicolor/${size}x${size}/apps/%{name}.png" done # A *user* unit (needs the Wayland session/D-Bus user bus), not a system # one -- installed but left disabled by default (no preset ships), so # `systemctl --user enable --now music-sync.service` is an opt-in step, # not a surprise background app after a routine package upgrade. install -Dm644 packaging/%{name}.service %{buildroot}%{_userunitdir}/%{name}.service %check desktop-file-validate %{buildroot}%{_datadir}/applications/%{name}.desktop find %{buildroot}%{_datadir}/%{name} -name '*.py' -print0 | xargs -0 python3 -c ' import ast, sys for path in sys.argv[1:]: with open(path, encoding="utf-8") as fh: ast.parse(fh.read(), path) ' # tests/ self-isolates (every test points XDG_* at a fresh tempdir -- see # tests/testutil.py), so this is safe to run as-is; a failure here fails # the whole build, same convention as this project's sibling (photo-import). python3 -m unittest discover -s tests -v %post %systemd_user_post %{name}.service # Immediately restart for every currently logged-in user who has it # enabled/running, so installing an update actually takes effect right # away instead of waiting for the user to remember to do it by hand. # try-restart is a safe no-op for anyone who never enabled it. daemon-reload # first, scoped to *this* user's own systemd --user manager instance -- # see this project's sibling (photo-import)'s spec for why that ordering # matters (a unit-file change across an upgrade can otherwise leave the # restarted process orphaned from systemd's own tracking). loginctl list-users --no-legend 2>/dev/null | while read -r uid user _; do [ -S "/run/user/${uid}/bus" ] || continue XDG_RUNTIME_DIR="/run/user/${uid}" runuser -u "$user" -- \ systemctl --user daemon-reload >/dev/null 2>&1 || : XDG_RUNTIME_DIR="/run/user/${uid}" runuser -u "$user" -- \ systemctl --user try-restart %{name}.service >/dev/null 2>&1 || : done %preun %systemd_user_preun %{name}.service %postun %systemd_user_postun_with_restart %{name}.service %files %{_bindir}/%{name} %{_datadir}/%{name}/ %{_datadir}/applications/%{name}.desktop %{_datadir}/icons/hicolor/*/apps/%{name}.png %{_datadir}/icons/hicolor/scalable/apps/%{name}.svg %{_userunitdir}/%{name}.service %changelog * Mon Oct 26 2026 lacamar - 0.1.1-1 - Lint fixes * Sat Aug 15 2026 Music Sync - 0.1.0-1 - Initial release: iPhone detection via idevice_id/ideviceinstaller, an ifuse-mounted house_arrest Documents share (VLC by default, any file-sharing app's bundle ID supported) as the sync destination, an Artist/Album folder picker with tri-state selection, incremental sync backed by a persisted device manifest (no device re-scan needed on repeat syncs), an opt-in "remove unselected" mirror mode gated behind a confirmation dialog, auto-sync-on-connect (add-only, never deletes), desktop notifications, sync history, and a systemd user service.