Skip to content

feat(mediaserver): accept several path mapping pairs, longest prefix wins - #224

Open
azuretek wants to merge 1 commit into
Abrechen2:masterfrom
azuretek:feat/media-server-multiple-path-mappings
Open

azuretek wants to merge 1 commit into
Abrechen2:masterfrom
azuretek:feat/media-server-multiple-path-mappings

Conversation

@azuretek

Copy link
Copy Markdown

Fixes #223.

Summary

  • A media server path_mapping now takes several from:to pairs separated by ;, and the pair whose from_path is the longest prefix of the file is applied.
  • Why: since [Bug] Plex item refresh never matches, so every subtitle triggers a full refresh of every Plex library #219 a subtitle gets a partial scan only when the mapped folder sits inside a library root. A server that mounts each library under its own root (/media/tv as /data/tv, /media/movies as /data/films) could be mapped for one library only, so every subtitle for the other ended in a full scan of it. A second pair was not parsed either: it became part of to_path and broke the first pair too.
  • A single pair, an empty mapping and a path no pair matches behave exactly as before. : stays the separator within a pair, so no existing config changes meaning.
  • The change is in MediaServer.apply_path_mapping, so Plex, Jellyfin, Kodi and Shoko all get it. Their field help text, the en/de UI strings and the CHANGELOG say how to enter several pairs.

Test plan

  • New tests/test_media_server_path_mapping.py: 10 cases, failing on master and passing with the change.
  • Backend suite with the change against the same run on master: same failures, plus the 10 new passes.
  • ruff check and ruff format --check on the changed files.
  • Both locale files still parse.
  • Manually tested: this branch against a real Plex server with two pairs (Evidence, "Live").
  • Frontend tests not run: the only frontend change is the two locale strings, and no frontend test references them.
  • mypy: no errors in mediaserver/base.py. The repo-wide errors, including the ones in plex.py and kodi.py, are on lines this PR does not touch (CI runs it with continue-on-error).

Evidence

The new tests on master (47a2b65f), patch removed: 6 fail, and the 4 that pass are the controls that must not change (single pair, no mapping, no match).

tests/test_media_server_path_mapping.py ...FF.FFFF                       [100%]
test_media_server_path_mapping.py:40: AssertionError: assert '/data/tv;/me...s/Show/ep.mkv' == '/data/tv/Show/ep.mkv'
test_media_server_path_mapping.py:40: AssertionError: assert '/media/movie...020)/Film.mkv' == '/data/films/...020)/Film.mkv'
test_media_server_path_mapping.py:40: AssertionError: assert '/media/movies/F/F.mkv' == '/data/films/F/F.mkv'
test_media_server_path_mapping.py:49: AssertionError: assert '/data;/media...e/Show/ep.mkv' == '/anime/Show/ep.mkv'
test_media_server_path_mapping.py:49: AssertionError: assert '/anime;/medi...a/Show/ep.mkv' == '/anime/Show/ep.mkv'
test_media_server_path_mapping.py:67: AssertionError: expected call not found.
========================= 6 failed, 4 passed in 1.00s ==========================

The first line is the reported failure: with two pairs, a TV episode maps to /data/tv;/media/movies:/data/films/Show/ep.mkv, which no library contains.

With the change:

tests/test_media_server_path_mapping.py ..........                       [100%]
============================== 10 passed in 0.83s ==============================

Backend suite, pytest --tb=no -q -n 8, Python 3.12.15, in the 1.16.0 image plus requirements-dev.txt, run as a non-root user:

failed passed skipped
master 9 9125 1
this branch 9 9135 1

The same 9 tests fail on both sides and none are in code this PR touches: 6 in tests/performance (which CI ignores), test_media_dir_temp_writers.py::test_diff_apply_keeps_sidecar_readable and the two test_track_resync_provenance.py::test_every_sync_writer_keeps_a_download_genuine ffsubsync cases.

Lint:

$ ruff check mediaserver/base.py mediaserver/plex.py mediaserver/jellyfin.py mediaserver/kodi.py mediaserver/shoko.py tests/test_media_server_path_mapping.py
All checks passed!
$ ruff format --check (same files)
6 files already formatted

Live, against a real Plex Media Server 1.43.4 whose libraries are mounted separately (Sublarr sees <root>/TV and <root>/Movies, Plex sees /data/tv and /data/movies). This branch's backend/ ran in the 1.16.0 image, with path_mapping <root>/TV:/data/tv;<root>/Movies:/data/movies, calling PlexServer.refresh_item for a real movie file:

mapped /data/movies/Castaway on the Moon (2009) {tmdb-18438}/Castaway on the Moon (2009) {tmdb-18438} [Bluray-1080p][DTS 5.1][x264]-PHOBOS.mkv
result RefreshResult(success=True, message='Scanned /data/movies/Castaway on the Moon (2009) {tmdb-18438} in Movies', server_name='Plex', item_id=None, needs_library_refresh=False)

Plex's log for the same call, a partial scan of that one folder and no full refresh:

GET /library/sections/1/refresh?path=%2Fdata%2Fmovies%2FCastaway+on+the+Moon+%282009%29+%7Btmdb-18438%7D

On 1.16.0 with only the TV pair possible, the same movie ends in section.update() on the whole Movies library.

…wins

A media server's path_mapping held one from:to pair. A server that mounts
each library under its own root (/media/tv as /data/tv, /media/movies as
/data/films) could only be mapped for one of them, and since the partial
scan from Abrechen2#219 needs the mapped folder inside a library root, every
subtitle for the other library fell back to a full scan of it. A second
pair was not parsed either: it became part of to_path, so the first pair
broke too.

apply_path_mapping now splits on ";", the list separator
SUBLARR_PATH_MAPPING already uses, keeps ":" within a pair so every
existing config means what it did, and applies the pair whose from_path is
the longest prefix of the file, so a specific pair wins in any order. All
four media server backends use it, and their help text and the UI strings
say how to enter several.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Media server path mapping can hold only one pair, so separately mounted libraries cannot all be mapped

1 participant