mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
feat(device-sync): fixed cross-OS naming scheme + playlist folders
Replace the user-configurable filename template with a fixed scheme so
syncing the same library across Linux/Windows/macOS no longer re-downloads
files just because the template string differs by OS.
Track layout on the device:
{AlbumArtist}/{Album}/{TrackNum:02d} - {Title}.{ext}
Playlists/{PlaylistName}/{Index:02d} - {Artist} - {Title}.{ext}
Playlists/{PlaylistName}/{PlaylistName}.m3u8
Playlist tracks go into a self-contained folder with a sibling-referencing
Extended M3U, instead of being scattered across the album tree with an
external .m3u8 — the stick stays tidy even for 50-artist playlists.
Rust (lib.rs):
- TrackSyncInfo: drop disc_number/year (not used in fixed schema), add
album_artist, duration, playlist_name, playlist_index
- sanitize_path_component strengthens empty-result handling via new
sanitize_or() which falls back to "Unknown Artist/Album/Title"
- build_track_path() dispatches on playlist context — playlist tracks
get the "Playlists/{Name}/…" shape, album tracks the traditional tree
- albumArtist resolution: the server's albumArtist tag, falling back to
the track artist (empty-string treated as missing)
- calculate_sync_payload: dedup key is now (source_id, track_id) instead
of track_id alone, so a track appearing in both an album and a playlist
ends up on the device in both locations; playlist context is embedded
into the response JSON as _playlistName / _playlistIndex so the follow-
up sync_batch_to_device call can route each track correctly
- write_playlist_m3u8: rewritten — writes into the playlist folder with
sibling filenames instead of a top-level .m3u8 with ../relative paths
- New rename_device_files command: atomic renames (per-entry result,
skip-if-exists, skip-if-source-missing) plus empty-dir cleanup. Used
by the migration flow
- write_device_manifest: v2 format, drops filenameTemplate field
- Drop the `template` parameter from sync_track_to_device,
compute_sync_paths, calculate_sync_payload, sync_batch_to_device
Frontend:
- DeviceSyncStore: drop filenameTemplate from state and persist
- DeviceSync.tsx: strip template editor UI (presets, token buttons, live
preview); replace with a compact read-only schema info card
- trackToSyncInfo(track, url, playlistCtx?): optional playlist context
parameter. Tracks coming back from calculate_sync_payload with embedded
_playlistName/_playlistIndex fall through via the same parameter
- compute_sync_paths / write_playlist_m3u8 / delete paths now pass the
playlist context per-source so status checks + deletions hit the right
files (album tree vs playlist folder)
- Album-Artist fallback: frontend falls back to track artist when the
server has no albumArtist tag (empty strings treated as missing) —
"Unknown Artist" is only used as a last-resort placeholder
- New migration flow: "Reorganize existing files…" button opens a modal
that reads the legacy filenameTemplate from the v1 manifest, computes
per-track (old, new) rename pairs, detects collisions (two old files
mapping onto one new path), and executes via rename_device_files.
Playlist-only tracks are left for the next sync to re-download into
the new playlist folder
- Small JS-side applyLegacyTemplate() helper: only used by the migration
preview so we can diff old paths against the current files on disk
- CSS: device-sync-schema-section / -code / -hint, migrate-modal +
summary + warning + result layout
i18n (en + de):
- Remove dead keys: filenameTemplate, templatePreview, templateHint,
templatePresetStandard/MultiDisc/AltFolder, tokenSlashHint
- Add schemaLabel, schemaHint, migrateButton/Title/Tooltip/Loading/
NothingToDo/NoTemplate/FilesToRename/Unchanged/Collisions/
PreviewNote/Executing/Success/Failed/ShowErrors/Start
Other locales (fr/nl/zh/nb/ru/es) fall through to the English defaultValue
until translated in a follow-up pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+17
-7
@@ -1144,9 +1144,24 @@ export const deTranslation = {
|
||||
free: 'frei',
|
||||
notMountedVolume: 'Ziel befindet sich nicht auf einem eingehängten Laufwerk. Das Gerät wurde möglicherweise getrennt.',
|
||||
chooseFolder: 'Auswählen…',
|
||||
filenameTemplate: 'Dateinamen-Vorlage',
|
||||
targetDevice: 'Zielgerät',
|
||||
templateHint: 'Variablen: {artist}, {album}, {title}, {track_number}, {disc_number}, {year}',
|
||||
schemaLabel: 'Benennungsschema',
|
||||
schemaHint: 'Festes Schema für zuverlässige Synchronisation über verschiedene Betriebssysteme hinweg. Playlists werden als .m3u8-Dateien geschrieben und referenzieren die Album-Tracks — keine Duplikate auf dem Gerät.',
|
||||
migrateButton: 'Bestehende Dateien re-organisieren…',
|
||||
migrateTooltip: 'Vorhandene Dateien auf dem Gerät nach dem neuen Schema umbenennen (basierend auf der alten Namensvorlage).',
|
||||
migrateTitle: 'Bestehende Dateien re-organisieren',
|
||||
migrateLoading: 'Analysiere vorhandene Dateien…',
|
||||
migrateNothingToDo: 'Alle vorhandenen Dateien entsprechen bereits dem neuen Schema — nichts zu tun.',
|
||||
migrateNoTemplate: 'Keine alte Namensvorlage auf dem Gerät gefunden. Migration ist nur möglich, wenn der Stick mit einer Psysonic-Version synchronisiert wurde, die konfigurierbare Vorlagen unterstützte.',
|
||||
migrateFilesToRename: 'Dateien werden umbenannt',
|
||||
migrateUnchanged: '{{n}} Dateien liegen bereits am korrekten Pfad',
|
||||
migrateCollisions: '{{n}} Dateien können nicht automatisch umbenannt werden (mehrere Tracks zeigen auf dasselbe Ziel). Sie bleiben unverändert — beim nächsten Sync werden sie neu heruntergeladen.',
|
||||
migratePreviewNote: 'Alte Vorlage: {{tpl}}',
|
||||
migrateExecuting: 'Dateien werden umbenannt…',
|
||||
migrateSuccess: '{{n}} Dateien erfolgreich umbenannt',
|
||||
migrateFailed: '{{n}} Umbenennungen fehlgeschlagen',
|
||||
migrateShowErrors: 'Fehler anzeigen',
|
||||
migrateStart: 'Umbenennen starten',
|
||||
onDevice: 'Auf dem Gerät',
|
||||
addSources: 'Hinzufügen…',
|
||||
colName: 'Name',
|
||||
@@ -1167,11 +1182,6 @@ export const deTranslation = {
|
||||
actionTransfer: 'Auf Gerät übertragen',
|
||||
actionDelete: 'Vom Gerät löschen',
|
||||
actionApplyAll: 'Änderungen synchronisieren',
|
||||
templatePreview: 'Vorschau',
|
||||
templatePresetStandard: 'Standard',
|
||||
templatePresetMultiDisc: 'Multi-Disc',
|
||||
templatePresetAltFolder: 'Alt. Ordner',
|
||||
tokenSlashHint: '/ = Ordner-Trennzeichen',
|
||||
cancel: 'Abbrechen',
|
||||
noTargetDir: 'Bitte zuerst einen Zielordner auswählen.',
|
||||
noSources: 'Bitte mindestens eine Quelle auswählen.',
|
||||
|
||||
Reference in New Issue
Block a user