diff --git a/docs/configuration/audio-addict.md b/docs/configuration/audio-addict.md
index 9936bf193..0c83b83e1 100644
--- a/docs/configuration/audio-addict.md
+++ b/docs/configuration/audio-addict.md
@@ -26,3 +26,66 @@ audio_addict_pass = PASSWORD
## Location
The **Audio Addict Radio Network** folder is located within your media library. From there, you can browse and access all available stations directly.
+
+## Shows and episodes
+
+Besides the live stations, DI.fm network offers an **Events** folder. It lists the upcoming shows, and
+for a show that has already been broadcast it also offers the recording, so you can listen to it
+afterwards.
+
+Shows with a back catalogue get a folder of their own holding the older episodes. Some shows have
+hundreds of them, so the folder is split into smaller groups, each labelled with the episode numbers
+and dates it contains.
+
+```
+# How many episodes one group holds before it is split. Default 20, minimum 1.
+audio_addict_episodes_per_container = 20
+```
+
+Building this list means asking the service for the events and for each show's episodes, which takes
+a moment. UMS remembers the answer so that browsing and starting playback do not each pay for it
+again:
+
+```
+# How long the show and episode lists are reused, in minutes.
+# Default 60. Set to 0 to ask the service every time.
+audio_addict_tree_cache_ttl_minutes = 60
+```
+
+A longer time makes browsing snappier, a shorter one picks up newly published episodes sooner. The
+addresses UMS receives stay valid far longer than the default hour, so there is no need to keep this
+value small.
+
+## Live titles
+
+Audio Addict stations do not announce the current track inside the audio stream. UMS asks the
+service instead and delivers the result to your player, so you still see what is playing. This is
+described in [Internet radio and live titles](/configuration/internet-radio).
+
+```
+# Send live titles for Audio Addict stations. On by default.
+audio_addict_icy_metadata = TRUE
+```
+
+## Curated playlists
+
+Curated playlists play one track after another from your account. When the playlist reaches its end,
+UMS can start over instead of stopping:
+
+```
+# Start a curated playlist again when it ends. Off by default.
+audio_addict_playlist_loop = FALSE
+```
+
+
+## All settings at a glance
+
+| Setting | Default | Purpose |
+|---|---|---|
+| `audio_addict_user` | — | Your account name |
+| `audio_addict_pass` | — | Your password |
+| `audio_addict_europe` | `TRUE` | Prefer the European streaming server |
+| `audio_addict_icy_metadata` | `TRUE` | Send live titles to the player |
+| `audio_addict_episodes_per_container` | `20` | Episodes per group before splitting |
+| `audio_addict_tree_cache_ttl_minutes` | `60` | How long show and episode lists are reused |
+| `audio_addict_playlist_loop` | `FALSE` | Repeat a curated playlist when it ends |
diff --git a/docs/configuration/change-notifications.md b/docs/configuration/change-notifications.md
new file mode 100644
index 000000000..5eac763ff
--- /dev/null
+++ b/docs/configuration/change-notifications.md
@@ -0,0 +1,51 @@
+# Change Notifications
+
+When something in your library changes, connected apps should notice without you pressing refresh —
+and without asking UMS every few seconds whether anything happened. UPnP solves this with
+subscriptions: an app registers its interest once, and UMS tells it when there is news.
+
+This works in both directions, and the two are controlled separately.
+
+## What UMS announces about your library
+
+UMS publishes two pieces of information whenever the library changes:
+
+- a **counter** that goes up on any change, which tells a subscriber that something happened
+- a **list of the containers that changed**, which tells it what
+
+The second one is what keeps things quick. An app that learns only "something changed" has to
+reload; one that learns "this folder changed" reloads that folder and leaves the rest alone.
+
+Both are announced at most a few times per second. A scan that imports a thousand files therefore
+does not produce a thousand notifications — the changes are collected and reported as a handful of
+messages.
+
+Typical moments when this happens:
+
+- a file appears in, or disappears from, a shared folder
+- you [edit a playlist](/configuration/playlist-editing) from a control point
+- you rate something, because that changes what the ratings folders contain
+- artwork is added for a station or a folder
+
+Whether your app acts on the news is up to the app. Most refresh the view you are looking at; some
+only use the information when you open a folder again. If a change does not appear, it is worth
+opening the folder anew before assuming it was not delivered.
+
+## What UMS listens to on your players
+
+The other direction is UMS subscribing to your **players**, so it can show what they are doing —
+what is playing, whether they are paused, how loud they are — and offer controls for them.
+
+```
+# Follow the players on the network. On by default.
+upnp_subscribe_services = TRUE
+```
+
+Leave this on when UMS is the place you control playback from.
+
+Turn it off when playback is driven by a separate control point. In that arrangement UMS has no
+business following and claiming control of the players: the control point is doing that, and two
+parties steering the same device is a good way to get confusing results. Switching this off makes
+UMS keep its hands off and simply serve the media.
+
+See [Two-box and three-box setups](/configuration/control-point-setups) for what that arrangement looks like.
diff --git a/docs/configuration/control-point-setups.md b/docs/configuration/control-point-setups.md
new file mode 100644
index 000000000..13fff693d
--- /dev/null
+++ b/docs/configuration/control-point-setups.md
@@ -0,0 +1,51 @@
+# Two-Box and Three-Box Setups
+
+UPnP knows two ways of arranging the parts of a media system, and UMS behaves differently in each.
+
+In a **two-box** setup, the device that browses your library is also the device that plays: a TV, a
+streaming box, a phone app that shows the folders and then plays the track itself. The device asking
+for the list and the device asking for the audio are one and the same.
+
+In a **three-box** setup they are separate. A *control point* — an app on your phone or a web
+interface — shows the library and tells a *renderer* to play. The app never touches the audio; it
+only points the renderer at it. This is the arrangement typical of hi-fi streamers, where a
+dedicated device does the playing and you steer it from somewhere else.
+
+## Why UMS has to know the difference
+
+UMS decides how to deliver a track based on what the receiving device can handle: a player that
+cannot decode FLAC gets a converted stream, one that can gets the file untouched.
+
+In a two-box setup that decision is easy, because whoever asked for the list is whoever will play.
+In a three-box setup it is not: the request for the list comes from your phone, the request for the
+audio from the streamer. Deciding on the basis of the phone would be exactly wrong — it would send
+the streamer whatever suits a phone.
+
+## How UMS tells them apart
+
+```
+# Require accounts and sign-in. On by default.
+authentication_enabled = TRUE
+```
+
+With authentication **on**, UMS ties playback to the identity that browsed, so that what a user is
+allowed to see is also what that user gets to play. This is the right behaviour for a household with
+several accounts and per-user access, and it assumes a two-box arrangement.
+
+With authentication **off**, UMS stops assuming the two are the same. It determines the playing
+device at the moment the audio is requested, from that request itself — its address and what it says
+about itself — and makes the conversion decision for *that* device. The identity in the browse
+request is then understood as the control point it actually is.
+
+Two devices sharing one address, which happens when the control point and the renderer run on the
+same machine, are still told apart, because they are distinguished by what they report about
+themselves rather than by the address alone.
+
+## Choosing
+
+Neither setting is more correct than the other; for maximum compatibility turn authentication **off**. The price is that UMS then has no accounts: there is no per-user access control, and everything it shares is visible to everything on the network that can
+reach it. If that is a concern, restrict which devices may reach UMS at all — see
+[Security and privacy](/configuration/security-and-privacy).
+
+Leave it **on** if your players browse and play for themselves, or if you need the per-user
+separation. This is the default.
diff --git a/docs/configuration/internet-radio.md b/docs/configuration/internet-radio.md
new file mode 100644
index 000000000..1c14e2806
--- /dev/null
+++ b/docs/configuration/internet-radio.md
@@ -0,0 +1,87 @@
+# Internet Radio and Live Titles
+
+An internet radio station does not only send audio, it also announces the track it is playing right
+now. UMS picks that announcement up and passes it on, so your control point can show what is
+currently on air instead of just the station name.
+
+This works for radio streams you have added to a playlist and for the stations of the
+[Audio Addict Network](/configuration/audio-addict).
+
+## How the title reaches your player
+
+There are two ways a title can travel, and UMS uses whichever fits:
+
+- Players that ask for in-band metadata get the title woven into the audio stream, the way internet
+ radio has always done it.
+- Players that do not ask : Because UMS reads the announcement itself and pushes it to the control point separately, the live title
+ appears even with players that know nothing about radio metadata.
+
+There is one case without live titles: if UMS has to **transcode** the stream for your player, the
+title cannot be woven in, because the transcoder produces the audio and knows nothing about the
+station's announcements. Streams that play directly — which is the normal case for MP3 and AAC
+stations — are unaffected.
+
+## Interpreting the announcement
+
+Stations announce a single line of text, and there is no rule that says which part is the artist.
+The common convention is:
+
+```text
+Miles Davis - So What
+```
+
+but some stations put the station name at the end instead:
+
+```text
+So What - Jazz Radio on example.com
+```
+
+Taken as "artist first", the second line would list *So What* as the artist. UMS therefore looks at
+the part after the last dash and treats it as a station name, not an artist, when any of these
+applies:
+
+- it contains something that looks like a web address, such as `example.com`
+- it resembles the station name UMS already knows from your playlist
+- it resembles the host name of the stream address, ignoring generic parts like `stream`, `listen`
+ or `radio`
+
+If none of them applies, the first part is taken as the artist, which is the usual convention.
+
+## Correcting a station by hand
+
+The rules above get it right for most stations, but a station can always break them — a show name
+at the end, an unusual separator, or an artist whose name happens to contain a domain. For those,
+tell UMS explicitly with an `#EXTICYORDER:` line in the playlist, directly above the stream:
+
+```text
+#EXTM3U
+#EXTINF:-1,Jazz Radio
+#EXTICYORDER:title-first
+http://stream.example.com/jazz.mp3
+```
+
+| Value | Meaning |
+|---|---|
+| `auto` | Decide automatically, as described above. This is the default. |
+| `artist-first` | Always read the announcement as *Artist - Title*. |
+| `title-first` | Always read it as *Title - Station*. |
+
+An unknown value is treated as `auto`. The setting applies to that one station; see
+[Playlist directives](/configuration/playlist-directives) for how such lines work in general.
+
+If your control point offers a way to switch the order while listening, it can set the same value
+through UMS, and UMS writes it into the playlist file so the choice survives a restart.
+
+## Audio Addict stations
+
+Stations of the Audio Addict Network do not announce their titles in the stream. UMS asks the
+service for the current track instead and delivers it the same way, so the behaviour is identical
+from the outside.
+
+```
+# Send live titles for Audio Addict stations. On by default.
+audio_addict_icy_metadata = TRUE
+```
+
+Turn this off if a player of yours reacts badly to in-band metadata; the titles pushed to the
+control point are unaffected by it.
diff --git a/docs/configuration/performance.md b/docs/configuration/performance.md
new file mode 100644
index 000000000..be413ce6b
--- /dev/null
+++ b/docs/configuration/performance.md
@@ -0,0 +1,78 @@
+# Performance
+
+The defaults are chosen to be safe on modest hardware. On a machine with a large library and cores
+to spare, a few settings are worth revisiting — and one of them matters more than the rest.
+
+Change one thing at a time and watch the result. Every setting here trades one resource for another,
+so a value that helps one installation can hurt the next.
+
+## Reading the library in parallel
+
+Before UMS can present a file, it has to work out what it is: read the metadata, find the artwork,
+determine what the requesting device needs. This is the single biggest factor in how long a scan
+takes and how quickly a large folder opens, and it is done by a pool of worker threads.
+
+```
+# How many files UMS works on at once. Default 4, allowed 1 to 64.
+media_resolve_threads = 4
+```
+
+The work is dominated by waiting — for the disk, for the database, occasionally for the network — so
+more threads help well beyond the number of cores you have. On a server with a large library, a
+value in the region of the core count or above is reasonable. On a small machine, or one where UMS
+shares the disk with something else, leave it alone.
+
+This setting also sizes the pool of database connections, which is kept at twice the number of
+threads and never below ten. That is deliberate: worker threads need connections, and raising the
+threads without the connections to go with them would leave them queueing for one another. Because
+the two move together, there is nothing separate to adjust.
+
+## Database memory
+
+The database keeps recently used pages in memory. How much it may use is decided automatically, and
+that is normally the right answer:
+
+```
+# Database cache in KB. Default -1, which lets UMS decide.
+database_media_cache_size = -1
+```
+
+Two further switches trade memory for speed. Both are off by default and both are worth a
+measurement rather than a guess:
+
+```
+# Hold database indexes in memory.
+database_media_use_memory_indexes = FALSE
+
+# Let the cache give memory back under pressure instead of holding it.
+database_media_use_cache_soft = FALSE
+```
+
+Memory indexes make lookups faster at the cost of memory that is then unavailable for anything else.
+The soft cache is the opposite trade: it releases memory when the system needs it, which keeps UMS
+out of trouble on a machine that is short of memory, at the price of having to read those pages
+again afterwards.
+
+## Logging
+
+By default UMS writes each log line out as it happens. Buffering makes logging cheaper:
+
+```
+# Collect log lines before writing them out. Off by default.
+logging_buffered = FALSE
+```
+
+The catch shows up exactly when you need the log most. If UMS is killed or crashes, whatever is
+still in the buffer is lost, so the log can stop short of the event you are investigating — and an
+abruptly ending log then looks like a freeze that never happened. Leave buffering off while chasing
+a problem.
+
+## What to measure
+
+Two numbers tell you whether a change helped:
+
+- How long a full scan of your library takes. It is in the log, as a line reporting the media scan
+ completed and the number of seconds it needed.
+- How long it takes to open your largest folder in a control point, once after a restart and once
+ again straight afterwards. The first time includes the work of building the folder, the second
+ does not, and the difference is what these settings act on.
diff --git a/docs/configuration/playlist-directives.md b/docs/configuration/playlist-directives.md
new file mode 100644
index 000000000..a8272433f
--- /dev/null
+++ b/docs/configuration/playlist-directives.md
@@ -0,0 +1,92 @@
+# Playlist Directives
+
+A playlist entry is just an address. That is enough for a file on disk, where UMS can read the tags
+and find the cover next to it, but not for an internet radio station: there is no file to read, so
+artwork, rating and other details have nowhere to live.
+
+Directives fill that gap. They are comment lines that carry the missing information along with the
+entry, inside the playlist file itself.
+
+## How a directive is written
+
+Put the directive **directly above** the entry it belongs to, the same way `#EXTINF:` works:
+
+```text
+#EXTM3U
+#EXTINF:-1,Jazz Radio
+#EXTIMG:https://example.com/stations/jazz.png
+#EXTRATING:4
+http://stream.example.com/jazz.mp3
+
+#EXTINF:-1,Rock Radio
+#EXTIMG:https://example.com/stations/rock.png
+http://stream.example.com/rock.mp3
+```
+
+The rules are the same for all of them:
+
+- A directive applies to the **next entry only**. Several directives can precede the same entry, as
+ above, and each entry needs its own lines.
+- Only `.m3u` and `.m3u8` playlists. Other playlist formats have no place for them.
+- Capitalisation of the directive name does not matter: `#EXTIMG:` and `#extimg:` both work.
+- Any other comment line is left alone, so your own notes in the file are safe.
+
+## The directives
+
+| Directive | Applies to | Purpose |
+|---|---|---|
+| `#EXTIMG:` | the next entry | Artwork for an entry that has no file to take it from |
+| `#EXTRATING:` | the next entry | Star rating of that entry |
+| `#EXTICYORDER:` | the next entry | How to read the live title of a radio station |
+| `#RADIOBROWSERUUID:` | the next entry | Identifies a station in the RadioBrowser directory |
+| `#EXTPLAYLISTRATING:` | the whole playlist | Star rating of the playlist itself |
+
+### Artwork
+
+`#EXTIMG:` takes an absolute address of a picture, which UMS downloads once and then uses for that
+entry. See [How to add artwork](/guides/how-to-add-artwork) for the details and the accepted
+forms.
+
+### Ratings
+
+`#EXTRATING:` and `#EXTPLAYLISTRATING:` take a whole number from **0 to 5**. The first rates the
+entry that follows it, the second rates the playlist as a whole and can stand anywhere in the file.
+
+```text
+#EXTPLAYLISTRATING:5
+#EXTM3U
+#EXTINF:-1,Jazz Radio
+#EXTRATING:4
+http://stream.example.com/jazz.mp3
+```
+
+A rating in the file is the **starting value**, not a running record: UMS applies it when the entry
+is not rated yet and leaves an existing rating alone. So editing the file changes nothing for
+something you have already rated in your control point — rate it there instead, and UMS updates the
+line for you.
+
+For a file on disk this is a convenience, because the rating is stored in the database and can also
+be written into the file's tags. For a radio station it is the only durable home the rating has: it
+is what lets the rating survive a rebuilt database and travel with the playlist when you copy it.
+
+### Radio station identity
+
+`#RADIOBROWSERUUID:` holds the identifier a station has in the RadioBrowser directory. With it, UMS
+can look up additional details about the station instead of showing only what the stream itself
+reveals. Control points that offer a station picker write this line themselves when you pick a
+station, so there is rarely a reason to type it by hand.
+
+### Title order
+
+`#EXTICYORDER:` controls whether a station's announcement is read as *Artist - Title* or as
+*Title - Station*. See [Internet radio and live titles](/configuration/internet-radio).
+
+## Directives UMS writes itself
+
+You do not have to edit playlists by hand. When your control point sets artwork, a rating or the
+title order, UMS writes the corresponding line into the playlist file. Each directive is replaced
+individually, so setting a cover never disturbs a rating and the other way round, and removing a
+value removes just its line.
+
+Artwork is only written after the picture has actually been loaded, so a broken address leaves the
+file untouched instead of storing a dead link.
diff --git a/docs/configuration/playlist-editing.md b/docs/configuration/playlist-editing.md
new file mode 100644
index 000000000..ffa74c8a6
--- /dev/null
+++ b/docs/configuration/playlist-editing.md
@@ -0,0 +1,58 @@
+# Editing Playlists from a Control Point
+
+Playlists are ordinary `.m3u8` files, and you can always edit them in a text editor. UMS can also
+let your control point do it for you: create a playlist, add the track you are listening to, drop
+one you no longer want, rename or delete the whole thing — without touching a file by hand.
+
+This is switched **off** by default. Writing to a playlist means writing to your disk, so it has to
+be something you allow deliberately rather than something that is simply on.
+
+```
+# Allow control points to create, change and delete objects. Off by default.
+upnp_cds_write = TRUE
+```
+
+## What becomes possible
+
+With the setting on, a control point that supports it can
+
+- create a new playlist,
+- add an item to a playlist, including one it is playing right now,
+- remove an item from a playlist,
+- rename or delete a playlist.
+
+Whether you see buttons for all of this depends on your control point. UMS only makes the operations
+available; nothing happens unless something asks for it.
+
+What UMS writes are plain playlist files in your shared folders — not entries in a database that
+only UMS understands. You can open them in any editor afterwards, copy them to another player, or
+keep them in a backup. Additional details that a playlist entry cannot express on its own, such as
+artwork for a radio station, are stored as [directives](/configuration/playlist-directives) in the same file.
+
+Changes are announced to connected control points, so a playlist edited on one device shows its new
+contents on the others without a manual refresh.
+
+## Uploading files
+
+A second setting governs something different and considerably more far-reaching: whether a device
+may send **file content** to UMS and have it written to disk.
+
+```
+# Allow unidentified devices to write files to disk. Off by default.
+anonymous_devices_write = TRUE
+```
+
+Leave this off unless you have a concrete reason. It permits a device on your network that UMS
+cannot attribute to a known user to create, replace and delete media files in your shared folders.
+That is a large amount of trust to extend to anything that happens to be on the network, and it is
+why the setting exists separately from playlist editing instead of being included in it.
+
+## Both settings can be changed remotely
+
+A control point can read and set both switches itself, in the same way it reads any other UMS
+setting. That is convenient — you can turn playlist editing on from the app you are already holding
+— but it also means the value in your configuration file is not the last word: anything that can
+reach the UPnP service on your network can change it.
+
+If that matters to you, restrict which devices may reach UMS at all. See
+[Security and privacy](/configuration/security-and-privacy).
diff --git a/docs/configuration/radiobrowser.md b/docs/configuration/radiobrowser.md
new file mode 100644
index 000000000..142bfb252
--- /dev/null
+++ b/docs/configuration/radiobrowser.md
@@ -0,0 +1,59 @@
+# RadioBrowser
+
+Adding an internet radio station to a playlist normally means hunting down its stream address
+somewhere on the web and pasting it into a file. UMS can take that step off your hands: it is
+connected to **RadioBrowser**, a community-maintained directory of internet radio stations, and can
+search it and add a station for you.
+
+There is nothing to configure. The directory is used on demand, and only when you ask for it.
+
+## Finding a station
+
+If your control point offers a station search, it is asking UMS, and UMS is asking RadioBrowser.
+Stations can be narrowed down by
+
+- name
+- country
+- language
+- tag, such as a genre or a theme
+
+Which of these your control point lets you use, and how it presents them, is up to the control
+point. UMS also supplies the possible values for the country, language and tag filters, so such a
+search box can offer them as a list instead of making you type them.
+
+Long result lists are fetched in portions, so a broad search does not have to be transferred in one
+go.
+
+## Adding a station
+
+When you pick a station, UMS writes a complete entry into the playlist you chose — not just the
+address:
+
+- the **stream address**
+- the **station name**, or a title of your own if your control point lets you enter one
+- the **station logo**, so the entry has a picture instead of a default icon
+- the **station identifier** from the directory
+
+The last one is the interesting part. It stays in the playlist file as a
+[`#RADIOBROWSERUUID:` directive](/configuration/playlist-directives), which means the entry remains connected to
+the directory entry it came from. UMS uses that connection to fill in details the stream itself does
+not reveal, such as the genre and the bitrate.
+
+The result is a playlist file that still reads like a plain playlist and works in any other player:
+
+```text
+#EXTM3U
+#EXTINF:-1,Jazz Radio
+#EXTIMG:https://example.com/logos/jazzradio.png
+#RADIOBROWSERUUID:962cf2e0-0601-11e8-ae97-52543be04c81
+http://stream.example.com/jazz.mp3
+```
+
+You can write such an entry by hand as well, if you know the station's identifier. Leaving the
+directive out costs you only the extra details; the station itself plays either way.
+
+## Afterwards
+
+The station behaves like any other entry in the playlist from then on. You can rate it, replace its
+picture, and correct how its live titles are read; see
+[Internet radio and live titles](/configuration/internet-radio).
diff --git a/docs/configuration/ratings-and-likes.md b/docs/configuration/ratings-and-likes.md
new file mode 100644
index 000000000..d15176bd8
--- /dev/null
+++ b/docs/configuration/ratings-and-likes.md
@@ -0,0 +1,66 @@
+# Ratings and Likes
+
+UMS can remember how you rate your music, from zero to five stars, and control points that offer
+rating buttons can set and change those values while you listen.
+
+Likes are not a separate mechanism. A **like is a rating of five stars**, a **dislike is zero**. If
+your control point shows a heart instead of stars, it is writing the same value into the same place,
+which is why the two never contradict each other.
+
+## Where a rating is kept
+
+The rating lives in the UMS database, attached to the item rather than to the file. That has two
+consequences worth knowing:
+
+- It works for things that have nowhere to store a rating of their own — an internet radio station,
+ for instance, or an audio format whose tags have no rating field.
+- It is independent of the file, so re-tagging your collection with another tool does not overwrite
+ what you rated in UMS.
+
+If you would rather have the rating in the files as well, so that other programs can see it, UMS can
+write it into the tag when you rate something:
+
+```
+# Also write the rating into the file's tag. Off by default.
+audio_update_rating_tag = TRUE
+```
+
+This only applies to files that can hold a rating, and only from the moment you turn it on — it does
+not go back and write out ratings you set earlier.
+
+A rating that has to survive a rebuilt database, or travel with a playlist you copy elsewhere,
+belongs in the playlist file instead. See [Playlist directives](/configuration/playlist-directives).
+
+## The My Albums folder
+
+Albums you have liked are collected in a folder called **My Albums**. It is filled automatically:
+like an album and it appears, remove the like and it is gone again.
+
+By default the folder sits inside the media library with the other audio folders. If you reach for
+it often, you can have it shown at the top level instead:
+
+```
+# Show My Albums in the root folder. Off by default.
+audio_likes_visible_root = TRUE
+```
+
+## Searching by rating
+
+UMS announces the rating as one of the properties it can be searched on, so a control point can ask
+for it the same way it asks for a title or an artist. A request for everything rated five stars
+finds what the My Albums folder shows; any other value works as well.
+
+The search matches an **exact** value. There is no way to ask for "four stars or better" in one
+request — a control point that offers such a filter has to ask for each value separately and put the
+answers together itself.
+
+The ratings are read from the database, so the search also finds items that carry no rating in their
+file, such as radio stations.
+
+## Backing them up
+
+Because ratings and likes live in the database, they are worth keeping a copy of before anything
+drastic happens to it. UMS can hand them out and take them back in, which also makes it possible to
+move them to another installation. The
+[UMS UPnP service](/configuration/ums-extended-service) lists the actions a control point uses for
+that.
diff --git a/docs/configuration/search.md b/docs/configuration/search.md
new file mode 100644
index 000000000..2c0c360bb
--- /dev/null
+++ b/docs/configuration/search.md
@@ -0,0 +1,80 @@
+# Search
+
+When a control point offers a search box, it does not filter the list it already has. It sends the
+search to UMS as a UPnP criterion such as `dc:title contains "beatles"`, and UMS answers with the
+matching items. How that criterion is turned into a query is what the settings on this page control.
+
+## The two search engines
+
+UMS can answer a search in two ways:
+
+- **Full-text index (default).** UMS keeps a Lucene index next to the database and searches that.
+ It is fast even on large libraries and it is what makes partial words and typo tolerance possible.
+- **Plain database search.** A straight SQL query. Slower on large libraries and it only matches
+ what is literally there, but it has no index to go out of date.
+
+The full-text index covers the file name of every media file, and for audio also the song title,
+album, artist, album artist, composer, conductor and genre.
+
+UMS switches to the database search on its own when a control point sends a criterion the index
+cannot answer, so turning the index off is rarely necessary.
+
+```
+# Use the full-text index. FALSE falls back to a plain database search.
+search_lucene_use_engine = TRUE
+```
+
+## Searching for part of a word
+
+Most search boxes send a `contains` criterion while you are still typing. UMS turns each word of
+your input into two alternatives at once:
+
+- a **prefix** match, so `beat` already finds *Beatles* before you finish the word
+- a **fuzzy** match, so `beatlse` still finds *Beatles* despite the typo
+
+```
+# Tolerate typos in addition to matching prefixes.
+search_lucene_contains_fuzzy = TRUE
+```
+
+Both settings match prefixes. They differ in what else they match:
+
+| | `TRUE` (default) | `FALSE` |
+|---|---|---|
+| `beat` finds *Beatles* | yes | yes |
+| `beatlse` finds *Beatles* | yes | no |
+| unrelated results on short input | possible | no |
+
+The last row is the reason the setting exists. Typo tolerance allows up to two wrong, missing or
+extra letters, and on a short word that reaches surprisingly far: searching for `ranz` also returns
+everything called *Jazz*, because two substitutions get you from one to the other. If that noise
+bothers you more than the typo tolerance helps, set it to `FALSE`.
+
+Two details worth knowing:
+
+- A prefix is only used from **three characters** on. One or two letters would match a large part of
+ the library without telling you anything the typo tolerance does not already find.
+- Putting your input in quotation marks searches for the **words next to each other** instead, which
+ is useful when the individual words are common but the combination is not.
+
+## Searching for an exact value
+
+Some criteria ask for an exact value rather than a part of one — a control point browsing by genre
+sends `upnp:genre = "Jazz"`, for instance. By default that is taken literally.
+
+```
+# Tolerate typos in exact matches as well. Off by default.
+search_lucene_equal_fuzzy = FALSE
+```
+
+Turning this on makes exact comparisons tolerate typos too. That is rarely what you want, because
+these criteria usually come from the control point itself rather than from something you typed, and
+a value that is nearly right is then quietly treated as right.
+
+## Notes
+
+Search ignores capitalisation. `chapitre`, `Chapitre` and `CHAPITRE` return the same results.
+
+If a search does not return what you expect, the debug log shows the query UMS built from your
+input, on a line beginning with `lucene search string is`. Comparing it with what you typed usually
+explains the result on the spot.
diff --git a/docs/guides/how-to-add-artist-images.md b/docs/guides/how-to-add-artist-images.md
new file mode 100644
index 000000000..51d972884
--- /dev/null
+++ b/docs/guides/how-to-add-artist-images.md
@@ -0,0 +1,72 @@
+# How to add artist images
+
+When you browse your library by artist, UMS can show a picture for each artist instead of a generic
+folder icon. The picture does not come from the audio files — it comes from the folder that carries
+the artist's name.
+
+## How UMS finds the picture
+
+You tell UMS **one** directory: the one whose subfolders are named after your artists. For an artist,
+UMS then looks for a subfolder with exactly that name and uses that folder's artwork.
+
+```
+# The directory whose subfolders are named after the artists.
+audio_artist_dir = /music/Albums
+```
+
+So with a library like this:
+
+```text
+/music/Albums/
+|-- Frank Sinatra/
+| |-- folder.jpg <- the picture shown for "Frank Sinatra"
+| `-- Come Fly with Me/
+|-- Miles Davis/
+| |-- folder.jpg
+| `-- Kind of Blue/
+`-- Zaz/
+ `-- Paris/ <- no folder.jpg, so Zaz gets the default icon
+```
+
+*Frank Sinatra* and *Miles Davis* get their pictures, *Zaz* does not.
+
+The artwork inside the artist folder follows the ordinary rules, so the file is called `folder.jpg`,
+`folder.png`, `folder.webp` and so on. See [How to add artwork](/guides/how-to-add-artwork) for the full
+list of supported formats.
+
+## Two conditions
+
+An artist picture appears only when both are true:
+
+1. A subfolder of the artist directory is named **exactly** like the artist, ignoring capitalisation.
+ *Frank Sinatra* finds the folder `Frank Sinatra`, and also `frank sinatra`, but not
+ `Sinatra, Frank`. The name has to match the whole folder name, not a part of it.
+2. That folder has artwork of its own.
+
+If either is missing, UMS falls back to the default icon. Nothing is broken — there is simply no
+picture to show.
+
+## Finding out which artists have none
+
+The quickest check is on the file system: list the subfolders of your artist directory and look for
+those without an image file. On Linux or macOS:
+
+```bash
+cd /music/Albums
+for d in */; do
+ find "$d" -maxdepth 1 -iname 'folder.*' | grep -q . || echo "no image: $d"
+done
+```
+
+Everything it prints is an artist that will show the default icon.
+
+Two causes are common. Either the folder really has no picture yet, or the folder name and the
+artist name in the tags have drifted apart — a guest artist, a different spelling, or a name written
+as *Surname, First name* on disk but the other way round in the tags. The second case is worth
+checking before you go looking for a picture that is already there.
+
+## Setting the directory from your control point
+
+If your control point supports it, you can point UMS at the artist directory by browsing to it and
+choosing it there, instead of editing the configuration file. UMS stores the path itself, so the
+setting survives a restart either way.
diff --git a/docs/guides/how-to-add-artwork.md b/docs/guides/how-to-add-artwork.md
index 7a73f978e..63bc1f1d9 100644
--- a/docs/guides/how-to-add-artwork.md
+++ b/docs/guides/how-to-add-artwork.md
@@ -66,3 +66,44 @@ In this example:
- `Music/Jazz/Jazz.jpg` is used for the `Jazz.m3u8` playlist.
- `Music/Jazz/Smooth.m3u8` has no matching `Smooth.*` image, so the `Jazz` folder artwork is used.
+
+## Web streams inside a playlist
+
+The naming rules above work because there is a file to put the image next to. An internet radio
+station or any other web address listed in a playlist has no such file, so it needs a different way
+to carry its artwork: a `#EXTIMG:` line that points at the picture.
+
+Put the line directly **above** the entry it belongs to, the same way `#EXTINF:` works. It applies to
+the next entry only, so each stream gets its own line:
+
+```text
+#EXTM3U
+#EXTINF:-1,Jazz Radio
+#EXTIMG:https://example.com/stations/jazz-radio.png
+http://stream.example.com/jazz.mp3
+
+#EXTINF:-1,Rock Radio
+#EXTIMG:https://example.com/stations/rock-radio.jpg
+http://stream.example.com/rock.mp3
+```
+
+UMS downloads the image once, stores it, and uses it for that stream from then on. It does not fetch
+it again on every start.
+
+What the line accepts:
+
+- An **absolute** address. `https://example.com/cover.png` works, `cover.png` and `../art/cover.png`
+ do not — a relative path is ignored and the stream keeps the default image.
+- A `data:` URI, if you would rather keep the picture inside the playlist file than host it somewhere.
+- Only `.m3u` and `.m3u8` playlists. Other playlist formats have no place to put the line.
+
+Capitalisation of the directive itself does not matter, `#EXTIMG:` and `#extimg:` both work.
+
+### Letting UMS write the line for you
+
+If your control point can set the artwork of an entry, you do not have to edit the playlist by hand.
+UMS writes the `#EXTIMG:` line into the playlist file itself, and only after it has successfully
+loaded the picture — so a broken address leaves the file untouched instead of storing a dead link.
+
+Because the artwork then lives in the playlist file rather than only in the database, it survives a
+rebuilt database and travels with the file when you copy the playlist elsewhere.
diff --git a/docusaurus.config.ts b/docusaurus.config.ts
index 82a63bdda..123e28011 100644
--- a/docusaurus.config.ts
+++ b/docusaurus.config.ts
@@ -67,7 +67,7 @@ const config: Config = {
lastVersion: 'current',
versions: {
current: {
- label: '15.x',
+ label: '16.x',
},
},
},
diff --git a/versioned_docs/version-15.x/configuration/_category_.json b/versioned_docs/version-15.x/configuration/_category_.json
new file mode 100644
index 000000000..aa78b890c
--- /dev/null
+++ b/versioned_docs/version-15.x/configuration/_category_.json
@@ -0,0 +1,8 @@
+{
+ "label": "Configuration",
+ "position": 3,
+ "link": {
+ "type": "generated-index",
+ "description": "How to configure UMS"
+ }
+}
diff --git a/versioned_docs/version-15.x/configuration/audio-addict.md b/versioned_docs/version-15.x/configuration/audio-addict.md
new file mode 100644
index 000000000..9936bf193
--- /dev/null
+++ b/versioned_docs/version-15.x/configuration/audio-addict.md
@@ -0,0 +1,28 @@
+# Audio Addict Network Radio
+
+The **Audio Addict Network** is a collection of premium internet radio stations, including:
+
+Classical Radio, Radio Tunes, Rock Radio, Jazz Radio, Zen Radio, and DI.fm.
+
+A paid **premium account** is required to access these radio stations. You only need to register on one of the sites, as the same username and password will grant access to all of them.
+
+UMS automatically reads all available AudioAddict radio sites, their channels, and filters. The **Favorites** channel is also supported.
+
+## Configuration
+
+To enable AudioAddict support, add the following lines to your `UMS.conf` file:
+
+```
+# TRUE = prefer European streaming server
+audio_addict_europe = TRUE or FALSE
+
+# Your username
+audio_addict_user = USERNAME
+
+# Your password
+audio_addict_pass = PASSWORD
+```
+
+## Location
+
+The **Audio Addict Radio Network** folder is located within your media library. From there, you can browse and access all available stations directly.
diff --git a/versioned_docs/version-15.x/configuration/external-api.md b/versioned_docs/version-15.x/configuration/external-api.md
new file mode 100644
index 000000000..12a6aed56
--- /dev/null
+++ b/versioned_docs/version-15.x/configuration/external-api.md
@@ -0,0 +1,454 @@
+# External API
+
+The external API enables programs to access or trigger UMS functionalities with a HTTP call.
+
+## How to enable the external API
+
+Edit UMS.conf and configure an api_key like this
+
+`api_key = secret_password`
+
+The _`secret_password`_ must have a minimum of 12 chars.
+
+## API usage
+
+If the external API is enabled, the API is accessible with a POST call to /api/COMMAND
+
+### Folder Scanning
+
+#### rescan
+
+| Intention | Rescans the complete library |
+| ----------- | ----------- |
+| URI | `/api/folderscanner/rescan` |
+| POST BODY | NONE |
+| POST BODY example / description | This command needs no body content |
+| Available since | 10.4.2 |
+
+:::info
+This can be slow for large libraries
+:::
+
+Example:
+
+```shell
+curl -w "%{http_code}\n" -H "api-key: secret_password" http://localhost:5001/api/folderscanner/rescan
+```
+
+#### rescan file or folder
+
+| Intention | Rescans a partial subtree of the file system. |
+| ----------- | ----------- |
+| URI | `/api/folderscanner/rescanFileOrFolder` |
+| POST BODY | PATH_TO_SCAN |
+| POST BODY example / description | example: "/music/pop/Madonna". Path must be the root or a subfolder of a shared path. |
+| Available since | 10.4.2 |
+
+Example:
+
+```shell
+curl -d "PATH_TO_SCAN" -w "%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/folderscanner/rescanFileOrFolder
+```
+
+### Liking Music (albums and songs)
+
+#### like song
+
+Song will be marked as liked.
+
+| Intention | Like a song identified by musicBrainz trackId |
+| ----------- | ----------- |
+| URI | `/api/like/likesong` |
+| POST BODY | `musicBrainz_trackID` |
+| POST BODY example / description | b8695995-45e9-405d-b4aa-e50e8760fe25 |
+| Available since | 10.20 |
+
+Example:
+
+```shell
+curl -d "b8695995-45e9-405d-b4aa-e50e8760fe25" -w "%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/like/likesong
+```
+
+#### dislike song
+
+Song will not be disliked
+
+| Intention | Dislike a song identified by musicBrainz trackId |
+| ----------- | ----------- |
+| URI | `/api/like/dislikesong` |
+| POST BODY | `musicBrainz_trackID` |
+| POST BODY example / description | b8695995-45e9-405d-b4aa-e50e8760fe25 |
+| Available since | 10.20 |
+
+Example:
+
+```shell
+curl -d "b8695995-45e9-405d-b4aa-e50e8760fe25" -w "%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/like/dislikesong
+```
+
+#### is song liked
+
+Check if song is liked.
+
+| Intention | Check if song is liked identified by musicBrainz trackId |
+| ----------- | ----------- |
+| URI | `/api/like/issongliked` |
+| POST BODY | `musicBrainz_trackID` |
+| POST BODY example / description | b8695995-45e9-405d-b4aa-e50e8760fe25 |
+| RESPONSE BODY | `TRUE` or `FALSE` |
+| Available since | 10.20 |
+
+Example:
+
+```shell
+curl -d "b8695995-45e9-405d-b4aa-e50e8760fe25" -w "%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/like/issongliked
+```
+
+This call adds the liked attribute of the album identified by musicbrainz release-id `1e0eee38-a9f6-49bf-84d0-45d0647799af`.
+
+#### like album
+
+Set album like state to true.
+
+| Intention | Likes an album identified by musicBrainz releaseID |
+| ----------- | ----------- |
+| URI | `/api/like/likealbum` |
+| POST BODY | `musicBrainz_releaseID` |
+| POST BODY example / description | 1e0eee38-a9f6-49bf-84d0-45d0647799af |
+| Available since | 10.20 |
+
+Example:
+
+```shell
+curl -d "1e0eee38-a9f6-49bf-84d0-45d0647799af" -w "%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/like/likealbum
+```
+
+#### dislike album
+
+Remove album like state.
+
+| Intention | Dislike a song identified by musicBrainz releaseID |
+| ----------- | ----------- |
+| URI | `/api/like/dislikealbum` |
+| POST BODY | `musicBrainz_releaseID` |
+| POST BODY example / description | 1e0eee38-a9f6-49bf-84d0-45d0647799af |
+| Available since | 10.20 |
+
+Example:
+
+```shell
+curl -d "1e0eee38-a9f6-49bf-84d0-45d0647799af" -w "%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/like/dislikealbum
+```
+
+This call removed the liked attribute of the album identified by musicbrainz release-id `1e0eee38-a9f6-49bf-84d0-45d0647799af`.
+
+#### is album liked
+
+Check album like state.
+
+| Intention | Check if album is liked identified by musicBrainz releaseID |
+| ----------- | ----------- |
+| URI | `/api/like/isalbumliked` |
+| POST BODY | `musicBrainz_releaseID` |
+| POST BODY example / description | 1e0eee38-a9f6-49bf-84d0-45d0647799af |
+| RESPONSE BODY | "TRUE" or "FALSE" |
+| Available since | 10.20 |
+
+Example:
+
+```shell
+curl -d "1e0eee38-a9f6-49bf-84d0-45d0647799af" -w "%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/like/isalbumliked
+```
+
+This call checks if the album identified by musicbrainz release-id `1e0eee38-a9f6-49bf-84d0-45d0647799af` is liked.
+
+### Rating
+
+The rating API is responsible for rating songs. Rating information is saved in the internal database (cache enabled) and optionally in the file itself. If `audio_update_rating_tag = true` is set in UMS.conf the IDv3 rating field also being updated in the song file (if the songs file format is supported).
+
+While browsing the content directory server, MusicBrainzTrackID (if available) and audiotrackID are delivered as `desc` metadata within the DIDL element.
+
+#### set rating
+
+| Intention | Set rating in stars (0 - 5) on a song identified by musicBrainz trackId |
+| ----------- | ----------- |
+| URI | `/api/rating/setrating` |
+| POST BODY | `musicbrainzTrackId` /`stars` |
+| POST BODY example / description | b8695995-45e9-405d-b4aa-e50e8760fe25/3 |
+| Available since | 10.20 |
+
+Example:
+
+```shell
+curl -d "b8695995-45e9-405d-b4aa-e50e8760fe25/3" -w "%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/rating/setrating
+```
+
+This call sets the user rating of all songs identified by the musicbrainz track-id `b8695995-45e9-405d-b4aa-e50e8760fe25` to `3`.
+
+#### get rating
+
+Reads song rating from database
+
+| Intention | Get song rating in stars (0 - 5) by musicBrainz trackID. Response body contains the rating information. |
+| ----------- | ----------- |
+| URI | `/api/rating/getrating ` |
+| POST BODY | `musicbrainzTrackId` |
+| POST BODY example / description | b8695995-45e9-405d-b4aa-e50e8760fe25 |
+| RESPONSE BODY example | 3 |
+| Available since | 10.20 |
+
+Example:
+
+```shell
+curl -d "b8695995-45e9-405d-b4aa-e50e8760fe25" -w "%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/rating/getrating
+```
+
+This call reads the user rating of a song identified by the musicbrainz track-id `b8695995-45e9-405d-b4aa-e50e8760fe25`.
+
+#### set rating by audiotrack id
+
+| Intention | Set rating in stars (0 - 5) on a song identified by UMS internal audiotrackID |
+| ----------- | ----------- |
+| URI | `/api/rating/setRatingByAudiotrackId ` |
+| POST BODY | `trackID` /`stars` |
+| POST BODY example / description | 32 |
+| Available since | 11.0 |
+
+Example:
+
+```shell
+curl -d "32/3" -w "%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/rating/setrating
+```
+
+This call sets songs user rating identified by audiotrack id `32` to `3`.
+
+#### get rating by audiotrack id
+
+Reads song rating from database
+
+| Intention | Get song rating in stars (0 - 5) by UMS internal audiotrackID. Response body contains the rating information. |
+| ----------- | ----------- |
+| URI | `/api/rating/getRatingByAudiotrackId` |
+| POST BODY | trackId |
+| POST BODY example / description | 32 |
+| RESPONSE BODY example | 3 |
+| Available since | 11.0 |
+
+Example:
+
+```shell
+curl -d "32" -w "%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/rating/getRatingByAudiotrackId
+```
+
+This call reads the user rating of a song identified by UMS audiotrack-id `32`.
+
+### Backup / Restore
+
+User managed "liked album" entries can be backed up into a profile-directory subfolder named `database_backup`. The filename is `MUSIC_BRAINZ_RELEASE_LIKE`. In case UMS database gets deleted, just call restore.
+
+#### backup liked albums
+
+Backup table `MUSIC_BRAINZ_RELEASE_LIKE` to filesystem
+
+| Intention | backup liked songs to filesystem |
+| ----------- | ----------- |
+| URI | `/api/like/backupLikedAlbums` |
+| REQUEST TYPE | GET |
+| RESPONSE BODY | `OK` or error message |
+| Available since | 10.20 |
+
+Example:
+
+```shell
+curl -w "%{http_code}\n" -H "api-key: secret_password" -X GET http://localhost:5001/api/like/backupLikedAlbums
+```
+
+This call will create a backup file containing liked albums.
+
+#### restore liked albums
+
+Restores table `MUSIC_BRAINZ_RELEASE_LIKE` from filesystem
+
+| Intention | restore liked songs from backup file |
+| ----------- | ----------- |
+| URI | `/api/like/restoreLikedAlbums` |
+| REQUEST TYPE | GET |
+| RESPONSE BODY | `OK` or error message |
+| Available since | 10.20 |
+
+Example:
+
+```
+curl -w "%{http_code}\n" -H "api-key: secret_password" -X GET http://localhost:5001/api/like/restoreLikedAlbums
+```
+
+This call restores the backup file.
+
+### Playlist
+
+#### enable service
+
+Edit UMS.conf and configure a managed playlist folder by setting
+
+`managed_playlist_folder = PATH_TO_PLAYLIST_FOLDER`
+
+for enabling this service. By default this service is disabled. The playlist folder path should be located beneath a configured shared `folders` path for playlist changed made by this API to be visible by UMS.
+
+#### list all playlists
+
+Read available playlists. These playlist names have to be used for subsequent calls to add or remove songs.
+
+| Intention | Delivers all supported (`m3u`, `m3u8` and `pls`) and available playlists from configured folder. Besides playlist name, the playlists `playlistId` is |
+| ----------- | ----------- |
+| URI | `/api/playlist/getAllPlaylists` |
+| REQUEST TYPE | GET |
+| RESPONSE BODY | JSON array of playlist names |
+| RESPONSE BODY example | `["Pop","Jazz","Classic"]` |
+| Available since | 11.0 |
+
+Example:
+
+```shell
+curl -d "" -w "\n%{http_code}\n" -H "api-key: secret_password" -X GET http://localhost:5001/api/playlist/getAllPlaylists
+```
+
+This call will list all available playlists.
+
+#### list server accessible playlists
+
+These are all playlist known to UMS (database/cache enabled). These playlist names have to be used for subsequent calls to add or remove songs. The playlist ID can be used to navigate directly to the playlist by browsing the `objectId` `$DBID$PLAYLIST$` concat databaseId.
+
+| Intention | Delivers all supported (`m3u`, `m3u8` and `pls`) and available playlists from configured folder |
+| ----------- | ----------- |
+| URI | ```/api/playlist/``getserverplaylists` `` |
+| REQUEST TYPE | GET |
+| RESPONSE BODY | JSON array of playlist names |
+| RESPONSE BODY example | `[{"playlistName":"Jazz","playlistId":5},{"playlistName":"Charts","playlistId":343}]` |
+| Available since | dev branch |
+
+Example:
+
+```shell
+curl -d "" -w "\n%{http_code}\n" -H "api-key: secret_password" -X GET http://localhost:5001/api/playlist/getserverplaylists
+```
+
+This call will list list all available playlist accessible by UMS.
+
+#### adding songs to playlists
+
+The required `audiotrackid` is delivered during UPnP browse requests and can be extracted from the DIDL response attribute `descMetadata`
+
+```XML
+
+[...]
+ ID
+[...]
+
+```
+
+| Intention | Add song to playlist |
+| ----------- | ----------- |
+| URI | `/api/playlist/addSongToPlaylist` |
+| REQUEST TYPE | POST |
+| POST BODY | `audiotrackid/PLAYLIST` |
+| POST BODY example / description | 123/Pop |
+| RESPONSE BODY | NONE |
+| Available since | 11.0 |
+
+Example:
+
+```shell
+curl -d "123/Pop" -w "\n%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/playlist/addSongToPlaylist
+```
+
+This adds the song with the ID `123` to the playlist `Pop`.
+
+#### removing songs from playlists
+
+The required `audiotrackid` is delivered during UPnP browse requests and can be extracted from the DIDL response attribute `descMetadata`
+
+```XML
+
+[...]
+ ID
+[...]
+
+```
+
+``
+
+| Intention | Remove song from playlist |
+| ----------- | ----------- |
+| URI | `/api/playlist/removeSongFromPlaylist` |
+| REQUEST TYPE | POST |
+| POST BODY | `audiotrackid/PLAYLIST` |
+| POST BODY example / description | 123/Pop |
+| RESPONSE BODY | NONE |
+| Available since | 11.0 |
+
+Example:
+
+```shell
+curl -d "123/Pop" -w "\n%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/playlist/removeSongFromPlaylist
+```
+
+This removes the song with the ID `123` from the playlist `Pop`.
+
+#### create new playlists
+
+Playlist name should be provided without any path and without file extensions.
+
+| Intention | Creating new playlists in managed playlist folder |
+| ----------- | ----------- |
+| URI | `/api/playlist/createPlaylist` |
+| REQUEST TYPE | POST |
+| POST BODY | `PLAYLIST_NAME` |
+| POST BODY example / description | Contemporary |
+| RESPONSE BODY | NONE |
+| Available since | 11.0 |
+
+Example:
+
+```shell
+curl -d "Contemporary" -w "\n%{http_code}\n" -H "api-key: secret_password" -X POST http://localhost:5001/api/playlist/createPlaylist
+```
+
+This call creates a new playlist file named `Contemporary.m3u8` in the managed playlist folder.
+
+## Java code example
+
+This code snippet shows how to use the API with okhttp3 library.
+
+```Java
+import nextcp.dto.Config;
+import nextcp.dto.UmsServerApiKey;
+import okhttp3.Call;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+[...]
+
+ public String executeCall() throws IOException
+ {
+ String postBody = "1e0eee38-a9f6-49bf-84d0-45d0647799af";
+ String apiKey = "secret_password";
+ RequestBody body = RequestBody.create(postBody, MediaType.parse("application/text"));
+ String requestUrl = "http://127.0.0.1:5001/api/like/likealbum";
+ Request request = new Request.Builder().url(requestUrl).addHeader("api-key", apiKey).post(body).build();
+ Call call = okClient.newCall(request);
+ Response response = call.execute();
+ return response.body().string();
+ }
+```
+
+## HTTP return codes
+
+| 200 | OK |
+| 204 | success if no content is supposed to be returned |
+| 401 | invalid api key |
+| 404 | requested object cannot be found |
+| 417 | API request failed |
+| 503 | external api is not enabled. Set a `api_key` in UMS.conf with a length of 12 or more character |
diff --git a/versioned_docs/version-15.x/configuration/security-and-privacy.md b/versioned_docs/version-15.x/configuration/security-and-privacy.md
new file mode 100644
index 000000000..5ba5cbc9f
--- /dev/null
+++ b/versioned_docs/version-15.x/configuration/security-and-privacy.md
@@ -0,0 +1,91 @@
+# Security and Privacy
+
+## Introduction
+
+UMS serves media in two main ways - via DLNA/UPnP to be consumed via media player apps, and via HTTP(S) to be consumed via web browsers.
+
+Web browsers have easy security and privacy control by having user accounts with logins.
+
+Media player apps do not generally support the concept of a "user", so usually every device gets the same content. This might not be what you want. For example if you have two folders kids_safe and kids_unsafe you might want to restrict the renderers in the kids' room to only have access to the kids_safe folder. Another common situation is you are on the same network as people you do not want to have access to your media, like flatmates, so you want to block certain renderers completely.
+
+UMS provides a number of methods to control access in those situations.
+
+## Allow or block renderers or network devices by default
+You can choose the default strategy for renderers and network devices. You can allow or deny by default, with denylists and allowlists, for complete control.
+
+This is useful for shared living situations or wide/low-trust local networks. It is also useful for those of you using powerline adapters for your network since that can result in unwanted access from neighbors.
+
+
+
+
+
+## Block/allow renderers and network devices
+
+When you have chosen whether to allow or block unrecognized renderers by default, you can build your denylist or allowlist from the Home screen in the settings area.
+
+
+
+## Link person to renderer
+
+You can link user accounts to renderers/devices, allowing you to have independent content access and playback tracking.
+
+For example, if you have a TV in the living room and another in your bedroom, the living room TV doesn't need to be affected by what you watch in your bedroom.
+
+
+
+## Restrict shared content to certain groups
+
+You can now choose to share directories or online content with certain groups. For example, if you have a person (or a device that is assigned to a person) who is a child, you can assign them to the "Kids" group, and give that group access to the "Family" directory, but not the "Horror" or "Adult Only" content. Or give them access to the Kurzgesagt web feed, but not the history podcasts.
+
+
+
+## Hiding folders
+
+Control the visibility of the virtual folders. These settings can be found in UMS.conf file.
+To hide some folders while browsing, just set their value to true or tick them in the Navigation/Share Settings tab from the advanced GUI mode.
+
+```
+hide_recently_played_folder =true
+hide_new_media_folder =true
+hide_video_settings =true
+hide_transcode_folder =true
+hide_empty_folders =true
+hide_media_library_folder =true
+hide_live_subtitles_folder =true
+```
+
+To hide the Web folder, you will need to untick Enable external network in General Configuration tab from the advanced GUI mode or change the `external_network =' value to false in your UMS.conf file.
+This will have the side effect that the automatic updater won't work.
+The change(s) made from the GUI will be effective after a restart.
+
+## PIN code
+
+All the above methods restricts access from various renderers. But if you can get access to a render that is allowed to see a folder those methods will not help you (if the kids has access to the living room tv which have access to all media then they have access to that media). The PIN code solves this issue. It allows you to hide folders/media behind a PIN code which you must enter FROM the render. By default the input is a sequence of digits (0-9) just like an ATM code. I strongly suggests that you use digit based codes as it becomes hard to type in from the renderer. But if you are extra paranoid you can add letters. It works as follows:
+Add a file called UMS.code to the same directory as your UMS.conf and to that file add regexp,code where regexp is a regular expression just like in "UMS.deny" file and code is the code that will grant access to the folder/media. There is no length regulation on the code.
+For example:
+```
+.*private.*,1234
+```
+
+Will force you to enter a code if the folder/media contains the word "private" and the correct code is 1234. The code then stays valid for 4 hours (if you don't change that time).
+
+## Custom Device Configuration
+
+Any configuration property can also be set on a per-device basis by creating a custom device configuration to override the default UMS settings (for full details see Creating a Custom Device Configuration).
+
+For example, to customize the kids' TV:
+- Click the 'Customize this device' button in the top right of the renderer's GUI popup panel and specify a name for the configuration.
+- In the new conf file that opens up add any settings you wish to override for the TV, e.g. to change the server name and specify different folders:
+```
+#----------------------------------------------------------------------------
+# Custom Device profile
+# See DefaultRenderer.conf for descriptions of all possible renderer options
+# and UMS.conf for program options.
+
+# Options in this file override the default settings for the specific Sony Bravia EX device(s) listed below.
+# Specify devices by uuid (or address if no uuid), separated by commas if more than one.
+
+device = uuid:7744ff6c-541f-48a8-0878-05fdebf240db
+server_name = Kid Stuff
+folders = c:\kids\stuff, c:\kids\otherstuff
+```
diff --git a/versioned_docs/version-15.x/development/_category_.json b/versioned_docs/version-15.x/development/_category_.json
new file mode 100644
index 000000000..4455b7625
--- /dev/null
+++ b/versioned_docs/version-15.x/development/_category_.json
@@ -0,0 +1,8 @@
+{
+ "label": "Development",
+ "position": 3,
+ "link": {
+ "type": "generated-index",
+ "description": "How to build and develop UMS"
+ }
+}
diff --git a/versioned_docs/version-15.x/development/build-instructions.md b/versioned_docs/version-15.x/development/build-instructions.md
new file mode 100644
index 000000000..3d90b574c
--- /dev/null
+++ b/versioned_docs/version-15.x/development/build-instructions.md
@@ -0,0 +1,221 @@
+# Build instructions
+
+This document describes how to build Universal Media Server from the source files.
+
+*Important note:*
+Prebuilt Universal Media Server releases can be downloaded from: http://www.universalmediaserver.com/ so you DO NOT need to run these steps as a general user.
+
+The following software packages are required:
+
+* The Java JDK 17 (the JRE is not enough)
+* Git
+* Maven
+* [MediaInfo](https://mediaarea.net/en/MediaInfo/Download)
+
+# Short instructions
+
+If all required software packages are installed, the following commands will
+download the latest sources and build UMS:
+
+```bash
+git clone https://github.com/UniversalMediaServer/UniversalMediaServer.git
+cd universalmediaserver
+mvn package -P PACKAGENAME
+```
+
+Where `PACKAGENAME` is the name of the target operating system: `windows`, `macos`, `macos-arm`, `macos-pre1015` or `linux-*`, where `*` is the architecture; one of: `x86`, `x86_64`, `arm64`, `armel`, or `armhf`
+
+The result will be built in the "target" directory:
+
+* Windows: `UMS-setup.exe`
+* Linux: `UMS-linux-generic-x.xx.x.tar.gz`
+* macOS: `UMS-setup-macosx-x.xx.x.tar.gz`
+
+# Full instructions
+
+First all required software has to be installed:
+
+## 1. Download and install the Java JDK 17
+
+See https://bell-sw.com/pages/downloads/#/java-17-lts
+
+## 2. Download and install Git
+
+See https://git-scm.com/
+
+## 3. Download and extract Maven
+
+See http://maven.apache.org/
+
+## 4. Set environment variables
+
+### Windows
+
+Create new variables or append the value if the variable already exists:
+
+* Level: System, variable: `JAVA_HOME`, value: JDK install location
+* Level: User, variable `M2_HOME`, value: Maven extract location
+* Level: User, variable `M2`, value: `%M2_HOME%\bin`
+* Level: User, variable `PATH`, value `%M2%`
+
+### Linux
+
+Nothing to do.
+
+### macOS
+
+Nothing to do.
+
+## 5. Download the UMS source code
+
+```bash
+git clone https://github.com/UniversalMediaServer/UniversalMediaServer.git
+cd universalmediaserver
+```
+
+## 6. Update to the latest source (optional)
+
+```bash
+git pull
+```
+
+## 7. Compile the latest version of UMS
+
+```bash
+mvn package -P PACKAGENAME
+```
+
+Where `PACKAGENAME` is the name of the target operating system: `windows`, `macos`, `macos-arm`, `macos-pre1015` or `linux-*`, where `*` is the architecture; one of: `x86`, `x86_64`, `arm64`, `armel`, or `armhf`
+
+You can also specify an optional flag if you want to skip downloading binaries, which can be useful to speed up build time, particularly on Windows and Linux:
+
+```bash
+mvn package -P PACKAGENAME -Doffline=true
+```
+
+The resulting binaries will be built in the "target" directory:
+
+* Windows: `UMS-setup.exe`
+* Linux: `UMS-linux-generic-x.xx.x.tar.gz`
+* macOS: `ums-x.xx.x-SNAPSHOT-distribution/Universal Media Server.app`
+
+## Automatic builds
+
+These last two commands can easily be automated using a script e.g.:
+
+### Windows
+
+```bash
+rem build-UMS.bat
+start /D universalmediaserver /wait /b git pull
+start /D universalmediaserver /wait /b mvn package
+```
+
+### Linux, macOS &c.
+
+```bash
+#!/bin/sh
+# build-UMS.sh
+cd universalmediaserver
+git pull
+mvn package
+```
+
+# Packaging and cross-compilation
+
+This section explains how it is possible to compile and package for one system while on another.
+
+## Building the Windows binaries
+
+The Windows installers (`UMS-setup.exe`) and Windows executable (`UMS.exe`) can be built on non-Windows platforms.
+
+First of all, you'll need to have the `makensis` binary installed. On Debian/Ubuntu,
+this can be done with:
+
+```bash
+sudo apt-get install nsis
+```
+
+Then the `NSISDIR` environment needs to be set to the **absolute path** to the
+`nsis` directory. This can either be set per-command:
+
+```bash
+NSISDIR=$PWD/src/main/external-resources/third-party/nsis mvn ...
+```
+
+Either:
+- Temporarily in the current shell:
+ ```bash
+ export NSISDIR=$PWD/src/main/external-resources/third-party/nsis
+ mvn ...
+ ```
+- Or permanently:
+ ```bash
+ # these two commands only need to be run once
+ echo "export NSISDIR=$PWD/src/main/external-resources/third-party/nsis" >> ~/.bashrc
+ source ~/.bashrc
+
+ mvn...
+ ```
+
+For the sake of brevity, the following examples assume it has already been set.
+
+The Windows installer can now be built with one of the following commands:
+
+### On Linux and macOS
+
+```bash
+mvn package -P system-makensis,windows
+```
+
+## Building a Linux tarball
+
+### On Windows and macOS
+
+```bash
+mvn package -P linux-*
+```
+
+where `*` is one of: x86, x86_64, arm64, armel, or armhf
+
+## Building the macOS disk image
+
+### On Windows and Linux
+
+```bash
+mvn package -P macos
+hdiutil create -volname "Universal Media Server" -srcfolder target/ums-*-distribution UMS.dmg
+```
+
+## Building the macOS wizard installer
+
+1) Build UMS
+2) Install http://s.sudre.free.fr/Software/Packages/about.html
+3) Set a variable storing the directory path of the build distribution file, e.g.
+```bash
+export UMS_DIST_FOLDER="/Users/dev/ums/target/ums-7.3.1-SNAPSHOT-distribution/Universal Media Server.app"
+export UMS_LOGO_FILE="/Users/dev/ums/src/main/external-resources/third-party/nsis/Contrib/Graphics/Wizard/win.png"
+```
+4) Replace desired path inside the .pkgproj file
+```bash
+sed -i '' "s#UMS_DIST_FOLDER#$UMS_DIST_FOLDER#g" src/main/assembly/osx-installer.pkgproj
+sed -i '' "s#UMS_LOGO_FILE#$UMS_LOGO_FILE#g" src/main/assembly/osx-installer.pkgproj
+```
+5) Build .pkg installer. This will output to `/target/Universal Media Server.pkg`
+```bash
+/usr/local/bin/packagesbuild src/main/assembly/osx-installer.pkgproj
+```
+
+# Quick builds
+
+We have quick build scripts that are recommended during development for fast
+iteration. The scripts will compile the Java code, put it in the default install
+directory, and run the program, which will close any existing instance of UMS.
+
+It should work for 64-bit Windows and macOS. Can be extended for others easily if desired.
+
+```bash
+mvn verify -P quickrun-* -DskipTests
+```
+
+Where `*` is `macos` or `windows`
diff --git a/versioned_docs/version-15.x/guides/_category_.json b/versioned_docs/version-15.x/guides/_category_.json
new file mode 100644
index 000000000..2a4c8a295
--- /dev/null
+++ b/versioned_docs/version-15.x/guides/_category_.json
@@ -0,0 +1,8 @@
+{
+ "label": "Guides",
+ "position": 3,
+ "link": {
+ "type": "generated-index",
+ "description": "How to use UMS"
+ }
+}
diff --git a/versioned_docs/version-15.x/guides/how-to-add-artwork.md b/versioned_docs/version-15.x/guides/how-to-add-artwork.md
new file mode 100644
index 000000000..7a73f978e
--- /dev/null
+++ b/versioned_docs/version-15.x/guides/how-to-add-artwork.md
@@ -0,0 +1,68 @@
+# How to add artwork
+
+You can add your own artwork for folders and playlists so your frontend shows custom thumbnails instead of generic default images.
+
+This guide explains:
+- which image formats are supported
+- how to name artwork files
+- where to place those files
+- what happens when a playlist image is missing
+
+## Supported image formats
+
+The following file types are supported and checked in this order. The first matching image file is used:
+- `webp`
+- `png`
+- `jpeg`
+- `jpg`
+- `bmp`
+- `gif`
+
+You can use any one of these formats for both folder artwork and playlist artwork.
+
+## Folder
+
+To set artwork for a folder, place an image file named `folder.ext` inside that folder.
+
+Replace `.ext` with one of the supported extensions.
+
+Examples:
+- `folder.webp`
+- `folder.jpg`
+- `folder.png`
+
+## Playlists
+
+To set artwork for a playlist, use the same base filename as the playlist file.
+
+Example:
+- Playlist file: `Jazz.m3u8`
+- Artwork file: `Jazz.jpg` (or `Jazz.webp`, `Jazz.png`, and so on)
+
+If a matching playlist artwork file is not found, the folder artwork is used as a fallback.
+
+## Example folder structure
+
+Use this as a reference:
+
+```text
+Music/
+|-- folder.jpg
+|-- Jazz/
+| |-- folder.png
+| |-- Jazz.m3u8
+| |-- Jazz.jpg
+| |-- Smooth.m3u8
+| `-- smooth-track01.mp3
+`-- Rock/
+ |-- folder.webp
+ |-- RockHits.m3u8
+ `-- track01.mp3
+```
+
+In this example:
+- `Music/folder.jpg` is the artwork for the top-level `Music` folder.
+- `Music/Jazz/folder.png` is the artwork for the `Jazz` folder.
+- `Music/Jazz/Jazz.jpg` is used for the `Jazz.m3u8` playlist.
+- `Music/Jazz/Smooth.m3u8` has no matching `Smooth.*` image, so the `Jazz` folder artwork is used.
+
diff --git a/versioned_docs/version-15.x/guides/how-to-add-web-content.md b/versioned_docs/version-15.x/guides/how-to-add-web-content.md
new file mode 100644
index 000000000..97f7d4743
--- /dev/null
+++ b/versioned_docs/version-15.x/guides/how-to-add-web-content.md
@@ -0,0 +1,49 @@
+# How to add web content
+
+This article will explain how to add web content.
+
+## Video feeds
+
+You can subscribe to video feeds/channels by adding the RSS feed links, or in the case of YouTube, just input the link to the channel.
+
+### 1: Go to the Shared Content section
+
+In the UMS settings in your web browser, open the menu and select Shared Content
+
+
+### 2: Open the "Add new shared content" modal
+
+When you select the "Add new shared content" button, it will open a modal that allows you to add any type of media. The first step is to choose the "Video feed" type
+
+
+### 3: Add your feed
+
+Here you can add your feed
+
+#### Name
+
+The "Name" field is disabled for video feeds, because the feeds define their own names.
+
+#### Path
+
+The "Path" field defines the directory structure that will be displayed by UMS. For example, if you enter `Web/YouTube Channels`, your feed will be inside the `YouTube Channels` directory, nested within the `Web` directory. This lets you organize your content however you want, and is especially useful when you have different feed providers, and you are using UMS have them all in the same place.
+
+#### Source/URL
+
+This is the link to the video feed. It will usually end in `.xml`, but we handle YouTube differently to accept a channel URL directly, allowing you to enter e.g. `https://www.youtube.com/@kurzgesagt`
+
+#### Authorized groups
+
+The "Authorized groups" field lets you make this feed only available to certain groups defined in UMS that are associated with different users and/or devices. See [Security and Privacy](../configuration/security-and-privacy.md#link-person-to-renderer) for more details.
+
+When you are happy with the options you entered, select the "Add" button.
+
+### Feed order
+
+If the feed link was valid, you should now see the "Name" field populated, and now you can drag the feed up or down to control the order
+
+
+### Save changes
+
+You can repeat the previous steps to add/edit more content, and when you are happy with your changes, select the "Save" button at the bottom of the page. Now you can see your content on your devices:
+
\ No newline at end of file
diff --git a/versioned_docs/version-15.x/guides/how-to-improve-support-for-my-device.md b/versioned_docs/version-15.x/guides/how-to-improve-support-for-my-device.md
new file mode 100644
index 000000000..dd027d469
--- /dev/null
+++ b/versioned_docs/version-15.x/guides/how-to-improve-support-for-my-device.md
@@ -0,0 +1,43 @@
+# How to improve support for my device
+
+If your device is failing to do anything, like browsing folders or playing a file, it may be possible for you to fix it by changing settings in the renderer config file. Different devices/renderers/clients communicate with servers like UMS in different ways, so the config file tells UMS how to speak the same language as your device.
+
+Every configuration profile serves two purposes:
+- Allow UMS to recognize a specific renderer when it tries to connect
+- Define the possibilities of that renderer
+
+We have a default renderer config file that contains documentation on all of our renderer settings. See the latest version at https://github.com/UniversalMediaServer/UniversalMediaServer/blob/master/src/main/external-resources/renderers/DefaultRenderer.conf
+
+## Adding support for an unrecognized device
+
+When UMS does not recognize your device, it means none of the renderer configuration profiles match your device.
+The result is that UMS displays an `Unknown Renderer`, and since it does not know the possibilities of your renderer, it cannot provide optimized output for your device.
+
+The solution is to try creating your own renderer configuration file.
+1. Make a copy of the .conf file that is closest to your device. For example, if your Samsung TV is not recognized, one of the Samsung TV configs might be a good place to start from.
+
+1. Go to the `Logs` tab in UMS and look for the text `Media renderer was not recognized. Possible identifying HTTP headers:`. That information is what is needed to make UMS recognize your device.
+
+1. In your new .conf file, look for the line that defines `UserAgentSearch` and/or `UpnpDetailsSearch` and replace the values with that identifying information.
+
+1. Browse and play some media on your device. Take note of which media had a problem playing. Now you can move on to the next section to improve support for your device.
+
+## Improving support for a device
+
+1. If any of your media has a problem playing, the renderer config should be modified until it works. Refer to [DefaultRenderer.conf](https://raw.github.com/UniversalMediaServer/UniversalMediaServer/master/src/main/external-resources/renderers/DefaultRenderer.conf) for the full list of options. The most common ones to change are:
+ ```
+ Video
+ Audio
+ Image
+ TranscodeVideo
+ TranscodeAudio
+ SeekByTime
+ Supported
+ ```
+ Make sure you do not have `MediaInfo = false` in your new config, because that will stop the `Supported` lines from working.
+
+1. To make sure transcoding is working on your device, play a file from the `#--TRANSCODE--#` folder. Within that folder, play one of the `FFmpeg` entries. If it plays, then transcoding is working.
+
+1. The `Supported` lines need to be populated to tell UMS which files your device supports natively. It can be a good idea to find the manual for your device online and use that to help populate those lines.
+
+1. As well as that, you can have a look at other renderer configs inside the "renderers" folder in your installation directory, to see what they are doing. Sometimes you will need help, which we can give you on our forum, and please remember to tell us about the improvement when you make it, so that other users with your device can benefit from the fix. We will credit you in our release announcement and changelog.
diff --git a/versioned_docs/version-15.x/guides/how-to-play-media.md b/versioned_docs/version-15.x/guides/how-to-play-media.md
new file mode 100644
index 000000000..0a2160266
--- /dev/null
+++ b/versioned_docs/version-15.x/guides/how-to-play-media.md
@@ -0,0 +1,40 @@
+# How to play media
+
+This article will explain the 4 main ways to play video, audio and images via UMS.
+
+## 1: Play from a renderer interface
+
+When your renderer is connected to UMS via DLNA/UPnP, often it will display in a menu called "Media Servers" or something similar, depending on the renderer. From there, you can enter UMS and browse to the media you want to access.
+
+The implementation of this varies greatly per renderer, but the core functionality is the same; navigate to your chosen media and select it to play.
+
+## 2: Push from the UMS interface to a renderer
+
+The UMS interface itself has the ability to "push" media to renderers if they support the UPnP feature. It is a similar concept to casting.
+
+1. Open the UMS UI and identify the renderer you want to push media to. In this case it is the Panasonic VIERA VT60 Series TV.
+
+2. Click on the renderer image to open the control panel
+
+3. Choose the file you want to push by clicking the folder icon on the lower right, and it will be filled in on the lower left.
+If you do not see the player controls in this window, your renderer does not support this functionality. Be sure to check for firmware/software updates to your renderer as they may have added support for it
+
+4. Now you can either create a playlist by clicking the plus icon to the immediate right of the input box, or you can push the file to your renderer by clicking the play icon just above the middle of the input box, which will start it playing on your renderer.
+
+## 3: Play on the web interface
+
+The web interface can be accessed anywhere within your local network, including on computers and renderers. In many ways it offers our most advanced UI and features.
+
+1. Open the web interface. If you know the address you can go to that, otherwise an easy way is to click the Web interface button on our UI
+
+2. Clicking that will open the web interface in your default browser. From there you can navigate to your file and click to play it
+
+
+## 4: Push from the web interface to a renderer
+
+Similar to above in option 2, you can push your media to a renderer via our web interface.
+
+1. Clicking the little cast icon for a file when browsing a directory. It is on the lower right of the thumbnail.
+
+2. That will make a mini floating control panel appear, which you can use to control playback of the file, and manage dynamic playlists if you choose.
+
diff --git a/versioned_docs/version-15.x/guides/how-to-solve-connection-problems.md b/versioned_docs/version-15.x/guides/how-to-solve-connection-problems.md
new file mode 100644
index 000000000..4872f1774
--- /dev/null
+++ b/versioned_docs/version-15.x/guides/how-to-solve-connection-problems.md
@@ -0,0 +1,12 @@
+# How to solve connection problems
+
+This page provides solutions for the most common connection problems our users encounter.
+
+## UMS detects renderer, but renderer does not detect UMS
+
+This usually happens when your computer has more than one active network connection (for example, one wired and one wireless). You can solve it by setting which network interface you want:
+
+1. Open UMS
+2. Select the `General Settings` tab
+3. Expand the `Force networking on interface:` dropdown and select the interface you want. There might be a lot of options in the dropdown. The one you want will usually have an IP address in brackets after the name, for example:
+
\ No newline at end of file
diff --git a/versioned_docs/version-15.x/guides/how-to-use-the-fully-played-feature.md b/versioned_docs/version-15.x/guides/how-to-use-the-fully-played-feature.md
new file mode 100644
index 000000000..1a3beef2e
--- /dev/null
+++ b/versioned_docs/version-15.x/guides/how-to-use-the-fully-played-feature.md
@@ -0,0 +1,33 @@
+# How to use the "Fully played" feature
+
+The "Fully played" feature lets you keep track of files you have fully played; for a video it means you have watched at least 92%, and for audio you have listened to at least 92%.
+
+## Setup
+
+This feature is enabled by default, so if you have not changed anything, you can use it. If the feature is not working, please check that these steps have not been undone:
+
+1. On the **Navigation Settings** tab, choose your **Fully played action**. In particular, make sure you have not selected **Do nothing**. This is **Mark media** by default, which will overlay an icon on the thumbnail to indicate it has been watched.
+2. On the **Shared Content** tab, find the folder you want to track, and select the **Monitor played status of files** checkbox on that row.
+3. If you want played status to be tracked separately for different renderers, you can link renderers to different user accounts. Each account will store its own played statuses.
+
+## Marking files as fully played automatically
+
+After you have completed the setup steps, you just need to play 92% or more of a file.
+
+## Mark TV series, videos, or directories as fully played manually
+
+You can mark TV series and videos as fully played directly via the web player:
+
+
+
+To mark all files within a shared directory as fully played on the _old_ Settings interface, on the **Shared Content** tab, right-click on the directory you want to mark as fully played, and then click **Mark contents fully played**
+
+
+
+## Removing fully played status / unplaying
+
+To "unplay" a file, you can do one of the following options:
+
+- Rename the file in any way. You can put it back to its original name after that, because UMS will have cleared the entry from its cache when you changed the name.
+- Move the file to another directory. You can put it back to its original directory after that, because UMS will have cleared the entry from its cache when you moved the file.
+- To "unplay" a whole shared directory, on the **Shared Content** tab, right-click on the directory to mark as unplayed, and then click **Mark contents unplayed**.
diff --git a/versioned_docs/version-15.x/guides/img/how-to-add-web-content-1-shared-content.png b/versioned_docs/version-15.x/guides/img/how-to-add-web-content-1-shared-content.png
new file mode 100644
index 000000000..a0cf389cf
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-add-web-content-1-shared-content.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-add-web-content-2-add-modal.png b/versioned_docs/version-15.x/guides/img/how-to-add-web-content-2-add-modal.png
new file mode 100644
index 000000000..6e5b69f15
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-add-web-content-2-add-modal.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-add-web-content-3-see-name-and-sort.png b/versioned_docs/version-15.x/guides/img/how-to-add-web-content-3-see-name-and-sort.png
new file mode 100644
index 000000000..8913b602b
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-add-web-content-3-see-name-and-sort.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-add-web-content-4-feed-player.png b/versioned_docs/version-15.x/guides/img/how-to-add-web-content-4-feed-player.png
new file mode 100644
index 000000000..7e7f7a5c3
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-add-web-content-4-feed-player.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-play-media-1.png b/versioned_docs/version-15.x/guides/img/how-to-play-media-1.png
new file mode 100644
index 000000000..424e0869f
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-play-media-1.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-play-media-2.png b/versioned_docs/version-15.x/guides/img/how-to-play-media-2.png
new file mode 100644
index 000000000..80b026f69
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-play-media-2.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-play-media-3.png b/versioned_docs/version-15.x/guides/img/how-to-play-media-3.png
new file mode 100644
index 000000000..5d8b7cc28
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-play-media-3.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-play-media-4.png b/versioned_docs/version-15.x/guides/img/how-to-play-media-4.png
new file mode 100644
index 000000000..4a50ca2e7
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-play-media-4.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-play-media-5.png b/versioned_docs/version-15.x/guides/img/how-to-play-media-5.png
new file mode 100644
index 000000000..63bf2ad29
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-play-media-5.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-play-media-6.png b/versioned_docs/version-15.x/guides/img/how-to-play-media-6.png
new file mode 100644
index 000000000..a10087938
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-play-media-6.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-play-media-7.png b/versioned_docs/version-15.x/guides/img/how-to-play-media-7.png
new file mode 100644
index 000000000..dec52c8bb
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-play-media-7.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-solve-connection-problems.png b/versioned_docs/version-15.x/guides/img/how-to-solve-connection-problems.png
new file mode 100644
index 000000000..f6c368094
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-solve-connection-problems.png differ
diff --git a/versioned_docs/version-15.x/guides/img/how-to-use-the-fully-played-feature.png b/versioned_docs/version-15.x/guides/img/how-to-use-the-fully-played-feature.png
new file mode 100644
index 000000000..fe093f864
Binary files /dev/null and b/versioned_docs/version-15.x/guides/img/how-to-use-the-fully-played-feature.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v14-assign-account-to-renderer.png b/versioned_docs/version-15.x/img/whats-new-in-v14-assign-account-to-renderer.png
new file mode 100644
index 000000000..509f417bd
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v14-assign-account-to-renderer.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v14-block-renderer.png b/versioned_docs/version-15.x/img/whats-new-in-v14-block-renderer.png
new file mode 100644
index 000000000..febd01660
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v14-block-renderer.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v14-groups-kids.png b/versioned_docs/version-15.x/img/whats-new-in-v14-groups-kids.png
new file mode 100644
index 000000000..1a9d30e72
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v14-groups-kids.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v14-mark-tv-series-fully-played.png b/versioned_docs/version-15.x/img/whats-new-in-v14-mark-tv-series-fully-played.png
new file mode 100644
index 000000000..493d25c7a
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v14-mark-tv-series-fully-played.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v14-network-allowblock-preference.png b/versioned_docs/version-15.x/img/whats-new-in-v14-network-allowblock-preference.png
new file mode 100644
index 000000000..c20d987e2
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v14-network-allowblock-preference.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v14-renderer-allow-preference.png b/versioned_docs/version-15.x/img/whats-new-in-v14-renderer-allow-preference.png
new file mode 100644
index 000000000..3b3b61164
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v14-renderer-allow-preference.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v14-shared-content-group.png b/versioned_docs/version-15.x/img/whats-new-in-v14-shared-content-group.png
new file mode 100644
index 000000000..5c3fa42d1
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v14-shared-content-group.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v14-tmdb-edit-metadata.png b/versioned_docs/version-15.x/img/whats-new-in-v14-tmdb-edit-metadata.png
new file mode 100644
index 000000000..e4e6728d4
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v14-tmdb-edit-metadata.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v14-user-avatar.png b/versioned_docs/version-15.x/img/whats-new-in-v14-user-avatar.png
new file mode 100644
index 000000000..5b8c29ba3
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v14-user-avatar.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v15-mode-switcher.png b/versioned_docs/version-15.x/img/whats-new-in-v15-mode-switcher.png
new file mode 100644
index 000000000..a4e9ffa9b
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v15-mode-switcher.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v15-pin-code.png b/versioned_docs/version-15.x/img/whats-new-in-v15-pin-code.png
new file mode 100644
index 000000000..619c45fdd
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v15-pin-code.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v15-season-metadata.png b/versioned_docs/version-15.x/img/whats-new-in-v15-season-metadata.png
new file mode 100644
index 000000000..2a01c7cc7
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v15-season-metadata.png differ
diff --git a/versioned_docs/version-15.x/img/whats-new-in-v15-user-switcher.png b/versioned_docs/version-15.x/img/whats-new-in-v15-user-switcher.png
new file mode 100644
index 000000000..58f4c9824
Binary files /dev/null and b/versioned_docs/version-15.x/img/whats-new-in-v15-user-switcher.png differ
diff --git a/versioned_docs/version-15.x/installation/_category_.json b/versioned_docs/version-15.x/installation/_category_.json
new file mode 100644
index 000000000..9a4173b1b
--- /dev/null
+++ b/versioned_docs/version-15.x/installation/_category_.json
@@ -0,0 +1,8 @@
+{
+ "label": "Installation",
+ "position": 3,
+ "link": {
+ "type": "generated-index",
+ "description": "Most operating systems/environments do not need documentation for installation. This section is for those that do."
+ }
+}
diff --git a/versioned_docs/version-15.x/installation/docker.md b/versioned_docs/version-15.x/installation/docker.md
new file mode 100644
index 000000000..9872bf238
--- /dev/null
+++ b/versioned_docs/version-15.x/installation/docker.md
@@ -0,0 +1,112 @@
+# Docker
+
+Some of these steps may not apply to your installation. Understand what they do, and ignore, or customize as necessary.
+
+## Preparation
+
+For operating system support and service packages.
+
+### Debian Linux
+
+Install Docker (Engine): https://docs.docker.com/engine/install/debian/
+
+### Fedora Linux
+
+Install Docker (Engine): https://docs.docker.com/engine/install/fedora/
+
+#### Extra instructions
+
+```
+sudo usermod -a -G docker ;
+```
+
+Re-login or restart the machine.
+
+```
+sudo su -;
+mkdir /srv/UMS;
+chcon -t svirt_sandbox_file_t /srv/UMS;
+chgrp docker /srv/UMS;
+chmod -R g+w /srv/UMS;
+```
+
+Mount storage to host and link into that directory, probably read-only.
+`mount '/srv/UMS/Videos'`
+
+Test example:
+Simple symlinking to another path on the host system may not work, since there will be no access to it outside of the mounted volume path for the docker container. Try copying files inside this location instead.
+
+## Container Setup
+
+Mount the following volumes:
+- Media folder `/root/media`
+- Profile folder containing UMS.conf `/root/.config/UMS`
+
+Expose/forward these ports from the host: 1044, 5001, 9001.
+
+The following scripts accomplish that (using the fish shell):
+```
+sudo su -;
+set rootDir "$HOME/.config/UMS";
+mkdir -p "$rootDir/data";
+
+docker pull universalmediaserver/ums;
+
+docker create --name UMS \
+ -p 1044:1044 -p 5001:5001 -p 9001:9001 \
+ -v /srv/UMS:/root/media \
+ -v "$HOME/.config/UMS":/root/.config/UMS \
+ universalmediaserver/ums \
+;
+
+docker start UMS;
+```
+
+## Investigating Problems/Issues
+
+### General
+
+```
+docker ps -a;
+#docker attach [--no-stdin] UMS; # Still unintentionally stops container when done inspecting..
+docker container logs [-f] UMS;
+docker exec -it UMS /bin/sh;
+docker diff UMS;
+```
+
+For detailed logs in the terminal: `echo -e '\nlog_level=ALL' >> UMS.conf`
+
+```
+docker cp :/var/log/UMS/root/debug.log ./;
+```
+
+### Mount trouble
+
+Using Fedora CoreOS, I had access/permission denied problems trying to use bind mounts.
+
+It may be recommended to use the Docker-managed, named-volumes capability instead, but to avoid that complexity, I found that the additional `:Z` as a suffix to the bind mount's descriptor option value allowed container write access to host files.
+`:z` can also be used instead, but security advice may suggest keeping resources more isolated between application/service environments, rather than shared.
+
+Matching error messages can be seen using journalctl, so it is an SELinux problem.
+The solution for that would be to run `chcon -Rt svirt_sandbox_file_t` host_dir, but that also seems discouraged.
+
+Strangely this is not an issue on Fedora Workstation, but I guess installing it manually added a package to deal with this. Seems to be container-selinux.
+
+## References
+
+- https://docs.docker.com/storage/bind-mounts/#configure-the-selinux-label
+- https://drive.google.com/file/d/1ORNc113a8is1K1ZZtp1r3iz44uzJDeRp/view
+- https://fedora.pkgs.org/36/docker-ce-x86_64/docker-ce-20.10.16-3.fc36.x86_64.rpm.html#Install_HowTo
+- https://github.com/UniversalMediaServer/UniversalMediaServer/blob/master/docker/Dockerfile
+- https://github.com/UniversalMediaServer/UniversalMediaServer/issues/1841
+- https://github.com/UniversalMediaServer/UniversalMediaServer/issues/1841#issuecomment-672849793
+- https://github.com/UniversalMediaServer/UniversalMediaServer/pull/1599
+- https://github.com/UniversalMediaServer/UniversalMediaServer/tree/master/src/main/external-resources
+- https://hub.docker.com/r/universalmediaserver/ums
+- https://hub.docker.com/r/atamariya/ums/
+- https://pkgs.org/download/docker-ce
+- https://support.universalmediaserver.com/
+- https://www.universalmediaserver.com/download/#docker
+- https://www.universalmediaserver.com/forum/viewtopic.php?t=12922
+- https://www.universalmediaserver.com/forum/viewtopic.php?t=14580
+- https://www.universalmediaserver.com/forum/viewtopic.php?p=47952
diff --git a/versioned_docs/version-15.x/intro.md b/versioned_docs/version-15.x/intro.md
new file mode 100644
index 000000000..1cc81124a
--- /dev/null
+++ b/versioned_docs/version-15.x/intro.md
@@ -0,0 +1,14 @@
+---
+sidebar_position: 1
+slug: /
+---
+
+# Introduction
+
+This website contains the official support documentation for using Universal Media Server.
+
+If you are not a current user, and you are looking to download it or decide whether to use it, you will probably find [our main website](https://www.universalmediaserver.com) more useful.
+
+It is split into 3 main sections: Configuration, Guides, and Installation.
+
+If you came here looking for specific information and didn't find it, please let us know on GitHub so we can add it.
diff --git a/versioned_docs/version-15.x/whats-new-in-v15.md b/versioned_docs/version-15.x/whats-new-in-v15.md
new file mode 100644
index 000000000..06ae4e619
--- /dev/null
+++ b/versioned_docs/version-15.x/whats-new-in-v15.md
@@ -0,0 +1,57 @@
+---
+sidebar_position: 2
+---
+
+# What's New in v15
+
+## Overview
+
+The 2 main goals for V15 have been to simplify our interface, based on user feedback and reviews, and to make our user accounts easier to use.
+
+A quick overview of the changes in this version is:
+- A **user switcher** with support for **multiple logged-in users**
+- A **mode switcher** to change between the two major areas: **Player** and **Settings**
+- **Pin code** logins
+- Dynamic **TV season metadata**
+- A file **media info** button
+- Also rewrites of our notification and realtime code, improved memory management, and a lot of other changes to improve usability and reduce the number of actions needed to do what you want.
+
+## New features
+
+### User switcher
+
+You can now be logged in with multiple users in a browser at the same time, and switch between them.
+
+You can also enable a setting to display the available users on the login screen. This setting is opt-in for security reasons, because you might not want everyone to see all user accounts. There is also an in-between setting where user accounts are saved in the browser after that browser logs in, on a per-account basis.
+
+
+
+### Mode switcher
+
+The web interface has been split into two areas: Player and Settings.
+
+This should make the web interface more intuitive and easier navigate by reducing the number of clicks needed.
+
+
+
+### Pin code logins
+
+You can now enable pin code logins in the server settings.
+
+
+
+### Dynamic season metadata
+
+Seasons of TV shows sometimes have their own data like titles and cover images, so now we display that on the season pages.
+
+
+
+### More
+
+For a full list of all changes in v15, see [the full changelog](https://github.com/UniversalMediaServer/UniversalMediaServer/blob/main/CHANGELOG.md).
+
+## Migration
+
+There are no specific migration steps to be aware of.
+
+As with any major update, if you want to have the ability to go back to your current version before updating, you can make a backup of your profile directory, which contains your configuration and local database. You can find the location of that directory near the top of your program logs. Look for `Profile directory: [some page]/UMS`.
diff --git a/versioned_sidebars/version-15.x-sidebars.json b/versioned_sidebars/version-15.x-sidebars.json
new file mode 100644
index 000000000..caea0c03b
--- /dev/null
+++ b/versioned_sidebars/version-15.x-sidebars.json
@@ -0,0 +1,8 @@
+{
+ "tutorialSidebar": [
+ {
+ "type": "autogenerated",
+ "dirName": "."
+ }
+ ]
+}
diff --git a/versions.json b/versions.json
index f5040c626..fed3ffd4e 100644
--- a/versions.json
+++ b/versions.json
@@ -1,4 +1,5 @@
[
+ "15.x",
"14.x",
"13.x"
]