mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-19 22:17:29 +02:00
168 lines
10 KiB
Markdown
168 lines
10 KiB
Markdown
# Network storage
|
|
|
|
Open Settings → Network storage in Material or Mornye. Add a named connection,
|
|
choose the protocol, enter its address and optional credentials, then choose
|
|
**Connect and save**. The app checks access before saving. Edit the connection
|
|
to change credentials or remove it. Removing a connection never deletes files
|
|
from the server.
|
|
|
|
- SMB: `smb://nas/Music/`, optional username, password, and domain. Defaults to
|
|
port 445; explicit ports are supported. The native libsmb2 client negotiates
|
|
SMB 2.0.2, 2.1, 3.0, 3.0.2 or 3.1.1 and supports required signing and encryption.
|
|
SMB1 is never offered. Leave the domain empty unless your server requires it.
|
|
OpenWRT can run Samba4 or ksmbd; use the share name and the same account as
|
|
the other client. Guest access is only attempted when credentials are blank,
|
|
never as an automatic fallback after a named account fails.
|
|
- WebDAV: `https://nas.example/dav/music/`, optional HTTP Basic authentication.
|
|
Routers such as GL.iNet use self-signed HTTPS certificates. If validation
|
|
fails, the connection form shows the server origin and SHA-256 fingerprint.
|
|
**Trust certificate and retry** saves that exact certificate exception with
|
|
the connection only after a successful access check. Changing the server
|
|
address or certificate requires a new approval. This applies to browsing,
|
|
artwork, metadata and audio requests through the same transport; certificate
|
|
checking stays enabled for other servers. Paste the `https://` link supplied
|
|
by the router, including its WebDAV port (e.g. 6008), rather than `dav://`.
|
|
GL.iNet documents its self-signed certificate behavior at
|
|
https://docs.gl-inet.com/router/en/4/interface_guide/network_storage/.
|
|
- HTTP/HTTPS: direct audio URL or an HTML directory index. This does not scrape
|
|
arbitrary websites, access cloud account APIs, or discover media hidden behind
|
|
JavaScript/login forms. Use the final directory URL; canonical trailing-slash
|
|
redirects are supported. Cross-origin redirects are rejected.
|
|
|
|
Connections are stored in the platform secure store. Playback sessions contain
|
|
opaque connection IDs and relative paths instead of server credentials. A
|
|
random-token loopback proxy handles GET, HEAD and byte ranges. Files stream on
|
|
demand without making a full local copy; HTTP seeking depends on the server's
|
|
range support. Requests have a 20-second connection/read-inactivity timeout.
|
|
|
|
Browsing alone does not add anything to the Library. Inside an SMB or WebDAV
|
|
folder, choose **Add folder to Library** to index that folder and its subfolders.
|
|
The source can then be rescanned, disabled or removed in Local Library settings.
|
|
Scans read one file's tags at a time and persist embedded artwork on the device;
|
|
audio stays on the server. Failed folder/tag reads preserve previously indexed
|
|
rows, and a temporarily unreachable NAS does not hide the cached collection.
|
|
Network Library tracks use the built-in player and remote file editing/deletion
|
|
is unavailable. Adding a folder does not make it a download destination.
|
|
|
|
## Download destination
|
|
|
|
Choose **Settings → Files → Network download destination**, browse to an SMB or
|
|
WebDAV folder, and select **Download to this folder**. The same button is present
|
|
when browsing Network storage normally. A temporary write/delete probe must
|
|
succeed before the selection is saved. **Use local download folder** restores
|
|
the previously configured local/SAF destination. HTTP directory indexes are
|
|
read-only.
|
|
|
|
Each queued download records its selected network destination. Audio is first
|
|
downloaded to a private per-item staging directory. Metadata, embedded cover,
|
|
lyrics, track ReplayGain, extension post-processing and automatic conversion
|
|
finish locally. Folder organization and the final filename are retained when
|
|
uploading; an existing name receives a numbered suffix instead of being
|
|
overwritten. LRC sidecars, when enabled, are uploaded alongside the audio.
|
|
|
|
Transfers stream bounded chunks to a hidden `.part` file, verify its size and
|
|
publish with a non-overwriting SMB rename or WebDAV MOVE (`Overwrite: F`, as
|
|
specified in https://www.rfc-editor.org/rfc/rfc4918.html#section-9.9.3).
|
|
libsmb2 also sets `replace_if_exist=0` for rename. Completion/history is published
|
|
only after the transfer succeeds, so the normal Library download history
|
|
refresh exposes the playable `network://` entry immediately. Credentials stay
|
|
in the secure connection store, never in the transfer journal or history.
|
|
|
|
A failed transfer retains finalized local audio and an atomic on-disk journal.
|
|
Retry, including after an app restart, uploads that audio again without fetching
|
|
or processing the song again. A lost publication response is reconciled by
|
|
streaming SHA-256 comparison before adopting an existing remote file. Local
|
|
staging is removed after history completion or when a failed item is dismissed.
|
|
An unreachable NAS may leave a hidden partial file until the same transfer is
|
|
retried; existing completed music is never deleted.
|
|
|
|
Network destinations currently use the shared Flutter queue even when Native
|
|
Download Worker is enabled, because the autonomous worker cannot yet perform
|
|
the network publication handoff. Keep the app open until upload finishes; this
|
|
does not promise transfers after force-stop or indefinite iOS background time.
|
|
Track ReplayGain is supported; later album-wide ReplayGain rewriting across
|
|
already-published tracks is not. Failed uploads occupy staging space until
|
|
retried or removed.
|
|
|
|
Selecting a song queues audio files in its current folder. Titles initially use
|
|
filenames; standard `cover.jpg`, `folder.jpg`, `front.jpg`, `cover.png` and
|
|
`folder.png` images in that folder provide initial artwork. When a song plays
|
|
or its Lyrics/Details view opens, embedded metadata and lyrics are read using
|
|
the same native tag parsers as local files. Embedded artwork takes precedence
|
|
over the folder image. LRC timestamps embedded in the lyrics use the regular
|
|
synchronized lyrics renderer; plain lyrics remain plain text.
|
|
|
|
Metadata uses 256 KiB range reads through the same proxy for SMB, WebDAV and
|
|
HTTP. Tags at the end of M4A files are reached by seeking past audio data. Each
|
|
read has a 20-second budget and a 16 MiB transfer cap, with bounded memory and
|
|
cover caches. Playback and Lyrics share in-flight reads and cache results for
|
|
five minutes. Large HTTP files require server byte-range support; a tag read
|
|
failure leaves playback available with filename/folder artwork fallbacks.
|
|
|
|
Remote metadata editing, separate sidecar lyric files, offline downloads,
|
|
AutoMix and direct USB/DAP bit-perfect output are not part of this browser.
|
|
Normal playback uses the platform audio decoder, so codec support follows the
|
|
device.
|
|
|
|
## Validation
|
|
|
|
`flutter test test/network_upload_test.dart test/network_download_destination_test.dart`
|
|
checks authenticated WebDAV writes, MKCOL, MOVE, collision handling, sidecars,
|
|
interrupted transfers, lost responses, restart journals, and destination UI in
|
|
both themes. The SMB upload test uses the pinned native library and a disposable
|
|
Samba fixture with encryption mandatory:
|
|
`python3 scripts/test_network_smb3_server.py --port 1446 --writable` and
|
|
`TEST_SMB_UPLOAD=1 TEST_SMB_PORT=1446 TEST_SMB_LIBRARY=<host-library> flutter test test/network_smb_upload_test.dart`.
|
|
It validates upload, non-overwriting publication and retry reconciliation in
|
|
existing folders. This host's unprivileged macOS Samba creates new directories
|
|
with mode 000 even with its own smbclient; nested folder creation is covered by
|
|
the WebDAV fixture, not claimed as a successful SMB host-device check.
|
|
|
|
`flutter test test/network_storage_service_test.dart test/network_storage_screen_test.dart`
|
|
checks URL/path handling, HTML/WebDAV parsing, authenticated HTTP transport,
|
|
range/HEAD requests, connection persistence/removal and browser navigation.
|
|
|
|
For the optional signed, authenticated SMB2 test, install `impacket` in a
|
|
temporary Python virtual environment and run `scripts/test_network_smb_server.py`.
|
|
Then run `TEST_SMB=1 flutter test test/network_storage_service_test.dart`.
|
|
Set `TEST_SMB_PORT` for the fixture's port (default 1445). For host Flutter tests,
|
|
set `TEST_SMB_LIBRARY` to the matching libsmb2 native binary from the pinned
|
|
dart_smb2 release. Stop the fixture after testing. `network_smb_transport_test.dart`
|
|
captures the actual negotiation packet and checks that SMB3 is offered without
|
|
an SMB1 negotiate. Run the same service test against Samba restricted to each
|
|
SMB2/3 dialect, with signing mandatory and encryption required for SMB3 tests.
|
|
|
|
The pinned dart_smb2 0.1.3 plugin verifies native download checksums at build time
|
|
(libsmb2-r8). Its LGPL library is dynamically linked. License text is included
|
|
in the app's licenses; corresponding build scripts, patches and source are at
|
|
https://github.com/ales-drnz/libsmb2-scripts and
|
|
https://github.com/sahlberg/libsmb2.
|
|
|
|
Samba4 fixture (isolated account database, loopback only, no system service):
|
|
`python3 scripts/test_network_smb3_server.py --dialect SMB3_11 --encryption required`.
|
|
Use `--dialect SMB3_00`, `SMB3_02`, `SMB2_10` or `SMB2_02` to constrain negotiation;
|
|
SMB2 requires `--encryption off`. Guest validation uses `--guest --encryption off`
|
|
on a second port, passed to the test as `TEST_SMB_GUEST_PORT`.
|
|
|
|
Android emulator transport validation:
|
|
`flutter test integration_test/network_smb_test.dart -d emulator-5554 --dart-define=SMB_TEST_URL=smb://10.0.2.2:1445/MUSIC/`.
|
|
For the iOS simulator, use `127.0.0.1` instead of `10.0.2.2`.
|
|
|
|
The transport was validated against an isolated Samba 4.25 server with mandatory
|
|
signing for SMB 2.0.2/2.1 and mandatory encryption for SMB 3.0/3.0.2/3.1.1.
|
|
Checks cover empty/WORKGROUP domains, explicit guest access, failed credentials,
|
|
share enumeration, file listing, HEAD and byte ranges. These fixtures validate
|
|
protocol behavior, not the configuration of a particular OpenWRT router.
|
|
|
|
`flutter test integration_test/network_playback_test.dart -d <device>` exercises
|
|
the real native tag reader with embedded FLAC tags/lyrics and verifies bounded
|
|
range transfers and cache reuse. It also exercises the platform decoder with
|
|
silent WAV data through the loopback proxy, including preparation, playback
|
|
and seeking.
|
|
|
|
`cargo test -p spotiflac-mobile network_tags --lib` from `rust_backend` checks
|
|
FLAC metadata/cover extraction, seeking past M4A audio to tail tags, and rejection
|
|
of external URLs and large non-range responses. Flutter metadata service tests
|
|
cover shared reads, recoverable failures, and embedded cover caching; the Now
|
|
Playing tests also check that network lyrics use the shared metadata result.
|