Metadata-Version: 2.4
Name: dlna-here
Version: 1.0.0
Summary: Serve a local media file or directory as a no-transcoding UPnP AV/DLNA server
Project-URL: Homepage, https://github.com/pseudosavant/dlna-here
Project-URL: Repository, https://github.com/pseudosavant/dlna-here
Project-URL: Issues, https://github.com/pseudosavant/dlna-here/issues
Author: John Paul Ellis
License-Expression: MIT
License-File: LICENSE
Keywords: cli,dlna,media-server,streaming,upnp
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Multimedia
Requires-Python: >=3.11
Requires-Dist: ifaddr<1,>=0.2.0
Description-Content-Type: text/markdown

# dlna-here

`dlna-here` is a small command-line UPnP/DLNA media server for sharing a local
folder—or one media file—on your local network. It serves videos, audio, and
images exactly as they are, with no transcoding and no persistent media
database.

## Quick start

Once [`uv`](https://docs.astral.sh/uv/) is installed, open a terminal in any
media folder and run:

```console
uvx dlna-here
```

That is it. There is no separate `dlna-here` installation or configuration
step. Open the UPnP/DLNA browser on a player connected to the same network and
look for `<folder> on <computer>`.

Press `Q` or `q` in the terminal to stop the server. `Ctrl+C` works too. On a
terminal that does not support immediate single-key input, enter `q` and press
Enter.

You can also serve a different folder:

```console
uvx dlna-here "D:\Videos"
```

On Linux or macOS, for example:

```console
uvx dlna-here ~/Videos
```

Or expose one file without making its containing folder or sibling files
browsable:

```console
uvx dlna-here "D:\Videos\Movie.mp4"
```

## What it does

- Preserves the filesystem's folder hierarchy.
- Reflects additions, removals, and renames without restarting the server.
- Serves original files with HTTP byte-range support for playback and seeking.
- Uses filenames and filesystem metadata for browsing; it does not read media
  contents before a player requests a file.
- Lets online-only files hydrate normally when they are selected for playback
  without reading them during browsing.
- Excludes hidden files and directories.
- Shows media names without the final filename extension by default.
- Follows symbolic links and Windows junctions by default, with cycle
  protection.
- Uses pure-Python runtime dependencies; it does not bundle codecs or native
  media libraries.
- Supports Windows, Linux, and macOS. Windows is the primary platform.

## Command-line arguments

```text
dlna-here [path] [options]
```

| Argument | Description |
| --- | --- |
| `[path]` | File or folder to serve. The current directory is used when omitted. A file path exposes only that supported media file. |
| `-h`, `--help` | Show the complete command help and exit. |
| `--interface auto\|NAME\|IPv4` | Select the IPv4 network interface. `auto` is the default and prefers the interface used by the system's default route. A displayed interface name or a concrete IPv4 address can also be used. |
| `--port PORT` | Set the HTTP media port. The default, `0`, asks the operating system to choose an available port. |
| `--name NAME` | Set the friendly server name displayed by media players. The default is `<file-or-folder> on <computer>`. |
| `--monitor-interval SECONDS` | Wait this many seconds after each completed filesystem change scan before starting the next. The default is `30`. Larger values reduce background work but delay `SystemUpdateID` changes and GENA notifications. Set it to `0` to disable background scans and filesystem-change notifications; live Browse behavior is unaffected. |
| `--show-extensions` | Show full filenames such as `Movie.mp4` instead of `Movie`. This also affects the automatic server name in single-file mode. Folder names are never changed. |
| `--no-follow-links` | Omit symbolic links and Windows junctions. By default they are followed, including links whose targets are outside the served folder. |
| `--list-interfaces` | List the IPv4 interfaces available for `--interface` and exit. |
| `--verbose` | Print protocol, filesystem, request, and transfer diagnostics. |
| `--version` | Print the `dlna-here` version and exit. |
| `--about` | Print project and license information and exit. |

For example:

```console
uvx dlna-here "D:\Videos" --name "Living room videos"
uvx dlna-here --list-interfaces
uvx dlna-here --interface "Wi-Fi"
uvx dlna-here --interface 192.168.1.25 --port 8200
uvx dlna-here --monitor-interval 60
uvx dlna-here --monitor-interval 0
uvx dlna-here --show-extensions --no-follow-links
uvx dlna-here --verbose
```

The HTTP server binds to the selected IPv4 address. UPnP discovery continues
to use its standard IPv4 multicast address and UDP port 1900.

`--monitor-interval` controls the background scan used to advance the UPnP
change ID and notify subscribed players. It does not change the live
filesystem model used when a player browses a folder. A larger value is useful
when lower background CPU and filesystem activity matter more than prompt
`SystemUpdateID` and GENA updates. A value of `0` disables change scans, so
fresh Browse requests remain current but players receive no automatic
filesystem-change notification from `dlna-here`.

## Supported media files

File extensions are matched case-insensitively.

- Video: `.3g2`, `.3gp`, `.asf`, `.avi`, `.divx`, `.flv`, `.m2t`, `.m2ts`,
  `.m4v`, `.mkv`, `.mov`, `.mp4`, `.mpe`, `.mpeg`, `.mpg`, `.mts`, `.ogm`,
  `.ogv`, `.ts`, `.vob`, `.webm`, `.wmv`
- Audio: `.aac`, `.aif`, `.aiff`, `.alac`, `.flac`, `.m4a`, `.mp2`, `.mp3`,
  `.oga`, `.ogg`, `.opus`, `.wav`, `.wave`, `.wma`
- Images: `.avif`, `.bmp`, `.gif`, `.heic`, `.heif`, `.jpe`, `.jpeg`, `.jpg`,
  `.png`, `.thm`, `.tif`, `.tiff`, `.webp`

Support here means that `dlna-here` recognizes the extension, advertises an
appropriate media type, and serves the original bytes. It does not inspect or
convert the container, video codec, audio codec, or image encoding. The
receiving player must support the actual file.

## Sidecar thumbnails and artwork

`dlna-here` can advertise existing `.jpg`, `.jpeg`, `.png`, or `.thm` images
as sidecar artwork for video and audio without opening the media file.

The simplest arrangement uses matching filenames:

```text
Movies/
├── Movie.mp4
└── Movie.jpg
```

Artwork is selected in this order:

1. Same-stem artwork such as `Movie.jpg`
2. Item-specific artwork such as `Movie-poster.png` or `Movie.poster.jpg`
3. Folder artwork named `cover`, `folder`, `album`, or `poster`

The original artwork file is served without resizing or converting it.
Artwork selected for a video is hidden from the ordinary image listing.
Single-file mode can use sidecar artwork from the selected file's directory
without exposing any other files there.

To create missing same-stem JPEG thumbnails recursively for a folder of
videos, use the helper script from
[`player.html`](https://github.com/pseudosavant/player.html). It requires
`ffmpeg` on `PATH`, skips videos that already have a thumbnail, and does not
overwrite existing artwork. This is a separate, optional tool; `dlna-here`
does not generate thumbnails or depend on FFmpeg:

```console
uv run https://raw.githubusercontent.com/pseudosavant/player.html/main/prerender-video-thumbnails.py "D:\Videos"
```

## What it deliberately does not do

- It does not transcode video or audio.
- It does not remux media or mux subtitles into a stream.
- It does not expose or manage external subtitle files.
- It does not inspect embedded media metadata.
- It does not generate thumbnails.
- It does not build or maintain a persistent media database.
- It does not have a web interface, user accounts, or authentication.
- It does not have persistent configuration; each run is controlled by
  command-line arguments.

The catalog is built from the live filesystem as players browse it. Clients
receive the original file and decide whether they can play it.

## Network security

`dlna-here` is an unauthenticated server intended for a trusted local network.
Anyone who can reach it can browse and request the files it exposes. Do not
publish its HTTP port to the Internet.

## Tested players

| Player | Validated server behavior |
| --- | --- |
| VLC | Discovery, hierarchical browsing, and video playback |
| Windows Media Player Legacy (`wmplayer.exe`) | Discovery, reconstructed filesystem folders, and video playback |
| Roku Media Player | Discovery, hierarchical browsing, and playback of tested audio, video, and images |
| Xbox Media Player | Discovery, browsing, and playback of tested audio, video, and images |

Simultaneous playback has been validated with Roku Media Player, VLC, and
Windows Media Player Legacy. Online-only OneDrive playback has also been tested
on Windows; the selected file hydrated on demand before streaming.

Individual format and codec support still depends on the player.

See
[`COMPATIBILITY.md`](https://github.com/pseudosavant/dlna-here/blob/main/COMPATIBILITY.md)
for the repeatable client test checklist and the information to include in a
compatibility report.

## Version 1 compatibility

Starting with 1.0.0, the supported public interface is the documented
`dlna-here` command line and its documented filesystem, HTTP, and UPnP
behavior. Existing options, defaults, and path semantics will remain
compatible throughout 1.x. Minor releases may add optional features,
recognized media extensions, efficiency improvements, or narrowly scoped
interoperability fixes. Incompatible changes to documented behavior are
reserved for 2.0 unless a security, path-exposure, or protocol-correctness fix
requires a correction.

The stable serving model includes directory and isolated single-file mode,
filesystem hierarchy, hidden-entry and link policies, live filename-only
browsing, sidecar artwork, original-file delivery, metadata-only `HEAD`,
single byte ranges, and the documented `--monitor-interval` behavior. Windows
remains the primary platform; Linux and macOS remain supported, with Python
3.11 as the minimum version throughout 1.x.

Media decoding remains the player's responsibility; the 1.x compatibility
promise does not mean every player supports every container or codec.
Python modules, exact help and diagnostic wording, benchmark values, and
byte-for-byte XML formatting are not public compatibility APIs.

## Development

Run the current source checkout:

```console
uvx --refresh --from . dlna-here
```

Run the test and build checks:

```console
uv run python -m unittest discover -s tests -v
uvx ruff check .
uvx ruff format --check .
uv build
```

The dependency-free development benchmark exercises catalog browsing,
filesystem monitoring, byte ranges, and simultaneous loopback streams:

```console
uv run python -m benchmarks.benchmark --smoke
uv run python -m benchmarks.benchmark
```

See
[BENCHMARKS.md](https://github.com/pseudosavant/dlna-here/blob/main/BENCHMARKS.md)
for its workload, output, and comparison guidance.

See
[ROADMAP.md](https://github.com/pseudosavant/dlna-here/blob/main/ROADMAP.md)
for project status and permanent non-goals.

## License

`dlna-here` is licensed under the
[MIT License](https://github.com/pseudosavant/dlna-here/blob/main/LICENSE).

Copyright (c) 2026 John Paul Ellis
