dlna-here 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. dlna_here-0.1.0/.github/dependabot.yml +10 -0
  2. dlna_here-0.1.0/.github/workflows/ci.yml +88 -0
  3. dlna_here-0.1.0/.github/workflows/release.yml +103 -0
  4. dlna_here-0.1.0/.gitignore +8 -0
  5. dlna_here-0.1.0/LICENSE +21 -0
  6. dlna_here-0.1.0/PKG-INFO +217 -0
  7. dlna_here-0.1.0/README.md +192 -0
  8. dlna_here-0.1.0/ROADMAP.md +315 -0
  9. dlna_here-0.1.0/pyproject.toml +41 -0
  10. dlna_here-0.1.0/src/dlna_here/__init__.py +3 -0
  11. dlna_here-0.1.0/src/dlna_here/__main__.py +4 -0
  12. dlna_here-0.1.0/src/dlna_here/catalog.py +1258 -0
  13. dlna_here-0.1.0/src/dlna_here/cli.py +170 -0
  14. dlna_here-0.1.0/src/dlna_here/dlna.py +15 -0
  15. dlna_here-0.1.0/src/dlna_here/eventing.py +231 -0
  16. dlna_here-0.1.0/src/dlna_here/interfaces.py +198 -0
  17. dlna_here-0.1.0/src/dlna_here/keyboard.py +132 -0
  18. dlna_here-0.1.0/src/dlna_here/monitor.py +68 -0
  19. dlna_here-0.1.0/src/dlna_here/server.py +778 -0
  20. dlna_here-0.1.0/src/dlna_here/ssdp.py +613 -0
  21. dlna_here-0.1.0/src/dlna_here/streaming.py +493 -0
  22. dlna_here-0.1.0/src/dlna_here/upnp_xml.py +553 -0
  23. dlna_here-0.1.0/tests/smoke_test.py +50 -0
  24. dlna_here-0.1.0/tests/test_catalog.py +653 -0
  25. dlna_here-0.1.0/tests/test_cli.py +142 -0
  26. dlna_here-0.1.0/tests/test_eventing.py +121 -0
  27. dlna_here-0.1.0/tests/test_interfaces.py +76 -0
  28. dlna_here-0.1.0/tests/test_keyboard.py +126 -0
  29. dlna_here-0.1.0/tests/test_monitor.py +56 -0
  30. dlna_here-0.1.0/tests/test_server.py +716 -0
  31. dlna_here-0.1.0/tests/test_ssdp.py +289 -0
  32. dlna_here-0.1.0/tests/test_streaming.py +331 -0
  33. dlna_here-0.1.0/tests/test_upnp_xml.py +344 -0
  34. dlna_here-0.1.0/uv.lock +23 -0
@@ -0,0 +1,10 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule:
6
+ interval: monthly
7
+ groups:
8
+ github-actions:
9
+ patterns:
10
+ - "*"
@@ -0,0 +1,88 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: ci-${{ github.workflow }}-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ test:
18
+ name: ${{ matrix.os }} / Python ${{ matrix.python-version }}
19
+ runs-on: ${{ matrix.os }}
20
+ timeout-minutes: 15
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ os:
25
+ - ubuntu-latest
26
+ - windows-latest
27
+ - macos-latest
28
+ python-version:
29
+ - "3.11"
30
+ - "3.13"
31
+
32
+ steps:
33
+ - name: Check out source
34
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35
+ with:
36
+ persist-credentials: false
37
+
38
+ - name: Install uv and Python
39
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
40
+ with:
41
+ version: "0.11.32"
42
+ python-version: ${{ matrix.python-version }}
43
+ enable-cache: false
44
+
45
+ - name: Install the project
46
+ run: uv sync --locked
47
+
48
+ - name: Run unit tests
49
+ run: uv run --frozen python -m unittest discover -s tests -v
50
+
51
+ quality:
52
+ name: Lint, format, and build
53
+ runs-on: ubuntu-latest
54
+ timeout-minutes: 15
55
+
56
+ steps:
57
+ - name: Check out source
58
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
59
+ with:
60
+ persist-credentials: false
61
+
62
+ - name: Install uv and Python
63
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
64
+ with:
65
+ version: "0.11.32"
66
+ python-version: "3.13"
67
+ enable-cache: false
68
+
69
+ - name: Lint
70
+ run: uvx --from ruff==0.16.0 ruff check .
71
+
72
+ - name: Check formatting
73
+ run: uvx --from ruff==0.16.0 ruff format --check .
74
+
75
+ - name: Build distributions
76
+ run: uv build --no-sources
77
+
78
+ - name: Smoke-test wheel
79
+ shell: bash
80
+ run: |
81
+ wheel="$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
82
+ uv run --isolated --no-project --with "$wheel" python tests/smoke_test.py
83
+
84
+ - name: Smoke-test source distribution
85
+ shell: bash
86
+ run: |
87
+ sdist="$(find dist -maxdepth 1 -name '*.tar.gz' -print -quit)"
88
+ uv run --isolated --no-project --with "$sdist" python tests/smoke_test.py
@@ -0,0 +1,103 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ name: Build and verify distributions
14
+ runs-on: ubuntu-latest
15
+ timeout-minutes: 15
16
+
17
+ steps:
18
+ - name: Check out source
19
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
20
+ with:
21
+ fetch-depth: 0
22
+ persist-credentials: false
23
+
24
+ - name: Install uv and Python
25
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
26
+ with:
27
+ version: "0.11.32"
28
+ python-version: "3.13"
29
+ enable-cache: false
30
+
31
+ - name: Verify release tag
32
+ shell: bash
33
+ run: |
34
+ expected_tag="v$(uv version --short)"
35
+ if [[ "$GITHUB_REF_NAME" != "$expected_tag" ]]; then
36
+ echo "Release tag $GITHUB_REF_NAME does not match $expected_tag." >&2
37
+ exit 1
38
+ fi
39
+ git fetch --no-tags origin main:refs/remotes/origin/main
40
+ if ! git merge-base --is-ancestor "$GITHUB_SHA" refs/remotes/origin/main; then
41
+ echo "Tagged commit is not on the main branch." >&2
42
+ exit 1
43
+ fi
44
+
45
+ - name: Install the project
46
+ run: uv sync --locked
47
+
48
+ - name: Run unit tests
49
+ run: uv run --frozen python -m unittest discover -s tests -v
50
+
51
+ - name: Lint
52
+ run: uvx --from ruff==0.16.0 ruff check .
53
+
54
+ - name: Check formatting
55
+ run: uvx --from ruff==0.16.0 ruff format --check .
56
+
57
+ - name: Build distributions
58
+ run: uv build --no-sources
59
+
60
+ - name: Smoke-test wheel
61
+ shell: bash
62
+ run: |
63
+ wheel="$(find dist -maxdepth 1 -name '*.whl' -print -quit)"
64
+ uv run --isolated --no-project --with "$wheel" python tests/smoke_test.py
65
+
66
+ - name: Smoke-test source distribution
67
+ shell: bash
68
+ run: |
69
+ sdist="$(find dist -maxdepth 1 -name '*.tar.gz' -print -quit)"
70
+ uv run --isolated --no-project --with "$sdist" python tests/smoke_test.py
71
+
72
+ - name: Upload distributions
73
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
74
+ with:
75
+ name: python-package-distributions
76
+ path: |
77
+ dist/*.whl
78
+ dist/*.tar.gz
79
+ if-no-files-found: error
80
+ retention-days: 7
81
+
82
+ publish:
83
+ name: Publish distributions
84
+ needs: build
85
+ runs-on: ubuntu-latest
86
+ environment:
87
+ name: pypi
88
+ url: https://pypi.org/p/dlna-here
89
+ permissions:
90
+ id-token: write
91
+
92
+ steps:
93
+ - name: Download distributions
94
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
95
+ with:
96
+ name: python-package-distributions
97
+ path: dist/
98
+
99
+ - name: Publish to PyPI
100
+ uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
101
+ with:
102
+ packages-dir: dist/
103
+ attestations: true
@@ -0,0 +1,8 @@
1
+ /.venv/
2
+ /build/
3
+ /dist/
4
+ /.pytest_cache/
5
+ /.ruff_cache/
6
+ **/__pycache__/
7
+ *.py[cod]
8
+ *.egg-info/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 John Paul Ellis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,217 @@
1
+ Metadata-Version: 2.4
2
+ Name: dlna-here
3
+ Version: 0.1.0
4
+ Summary: Serve a local media file or directory as a no-transcoding UPnP AV/DLNA server
5
+ Project-URL: Homepage, https://github.com/pseudosavant/dlna-here
6
+ Project-URL: Repository, https://github.com/pseudosavant/dlna-here
7
+ Project-URL: Issues, https://github.com/pseudosavant/dlna-here/issues
8
+ Author: John Paul Ellis
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: cli,dlna,media-server,streaming,upnp
12
+ Classifier: Environment :: Console
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Multimedia
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: ifaddr<1,>=0.2.0
24
+ Description-Content-Type: text/markdown
25
+
26
+ # dlna-here
27
+
28
+ `dlna-here` is a small command-line UPnP/DLNA media server for sharing a local
29
+ folder—or one media file—on your local network. It serves videos, audio, and
30
+ images exactly as they are, with no transcoding and no persistent media
31
+ database.
32
+
33
+ ## Quick start
34
+
35
+ Once [`uv`](https://docs.astral.sh/uv/) is installed, open a terminal in any
36
+ media folder and run:
37
+
38
+ ```console
39
+ uvx dlna-here
40
+ ```
41
+
42
+ That is it. There is no separate `dlna-here` installation or configuration
43
+ step. Open the UPnP/DLNA browser on a player connected to the same network and
44
+ look for `<folder> on <computer>`.
45
+
46
+ Press `Q` or `q` in the terminal to stop the server. `Ctrl+C` works too. On a
47
+ terminal that does not support immediate single-key input, enter `q` and press
48
+ Enter.
49
+
50
+ You can also serve a different folder:
51
+
52
+ ```console
53
+ uvx dlna-here "D:\Videos"
54
+ ```
55
+
56
+ Or expose one file without making its containing folder or sibling files
57
+ browsable:
58
+
59
+ ```console
60
+ uvx dlna-here "D:\Videos\Movie.mp4"
61
+ ```
62
+
63
+ ## What it does
64
+
65
+ - Preserves the filesystem's folder hierarchy.
66
+ - Reflects additions, removals, and renames without restarting the server.
67
+ - Serves original files with HTTP byte-range support for playback and seeking.
68
+ - Uses filenames and filesystem metadata for browsing; it does not read media
69
+ contents before a player requests a file.
70
+ - Excludes hidden files and directories.
71
+ - Shows media names without the final filename extension by default.
72
+ - Follows symbolic links and Windows junctions by default, with cycle
73
+ protection.
74
+ - Supports Windows, Linux, and macOS. Windows is the primary platform.
75
+
76
+ ## Command-line arguments
77
+
78
+ ```text
79
+ dlna-here [path] [options]
80
+ ```
81
+
82
+ | Argument | Description |
83
+ | --- | --- |
84
+ | `[path]` | File or folder to serve. The current directory is used when omitted. A file path exposes only that supported media file. |
85
+ | `-h`, `--help` | Show the complete command help and exit. |
86
+ | `--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. |
87
+ | `--port PORT` | Set the HTTP media port. The default, `0`, asks the operating system to choose an available port. |
88
+ | `--name NAME` | Set the friendly server name displayed by media players. The default is `<file-or-folder> on <computer>`. |
89
+ | `--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. |
90
+ | `--no-follow-links` | Omit symbolic links and Windows junctions. By default they are followed, including links whose targets are outside the served folder. |
91
+ | `--list-interfaces` | List the IPv4 interfaces available for `--interface` and exit. |
92
+ | `--verbose` | Print protocol, filesystem, request, and transfer diagnostics. |
93
+ | `--version` | Print the `dlna-here` version and exit. |
94
+ | `--about` | Print project and license information and exit. |
95
+
96
+ For example:
97
+
98
+ ```console
99
+ uvx dlna-here "D:\Videos" --name "Living room videos"
100
+ uvx dlna-here --list-interfaces
101
+ uvx dlna-here --interface "Wi-Fi"
102
+ uvx dlna-here --interface 192.168.1.25 --port 8200
103
+ uvx dlna-here --show-extensions --no-follow-links
104
+ uvx dlna-here --verbose
105
+ ```
106
+
107
+ The HTTP server binds to the selected IPv4 address. UPnP discovery continues
108
+ to use its standard IPv4 multicast address and UDP port 1900.
109
+
110
+ ## Supported media files
111
+
112
+ File extensions are matched case-insensitively.
113
+
114
+ - Video: `.3g2`, `.3gp`, `.asf`, `.avi`, `.divx`, `.flv`, `.m2t`, `.m2ts`,
115
+ `.m4v`, `.mkv`, `.mov`, `.mp4`, `.mpe`, `.mpeg`, `.mpg`, `.mts`, `.ogm`,
116
+ `.ogv`, `.ts`, `.vob`, `.webm`, `.wmv`
117
+ - Audio: `.aac`, `.aif`, `.aiff`, `.alac`, `.flac`, `.m4a`, `.mp2`, `.mp3`,
118
+ `.oga`, `.ogg`, `.opus`, `.wav`, `.wave`, `.wma`
119
+ - Images: `.avif`, `.bmp`, `.gif`, `.heic`, `.heif`, `.jpe`, `.jpeg`, `.jpg`,
120
+ `.png`, `.thm`, `.tif`, `.tiff`, `.webp`
121
+
122
+ Support here means that `dlna-here` recognizes the extension, advertises an
123
+ appropriate media type, and serves the original bytes. It does not inspect or
124
+ convert the container, video codec, audio codec, or image encoding. The
125
+ receiving player must support the actual file.
126
+
127
+ ## Sidecar thumbnails and artwork
128
+
129
+ `dlna-here` can advertise existing `.jpg`, `.jpeg`, `.png`, or `.thm` images
130
+ as sidecar artwork for video and audio without opening the media file.
131
+
132
+ The simplest arrangement uses matching filenames:
133
+
134
+ ```text
135
+ Movies/
136
+ ├── Movie.mp4
137
+ └── Movie.jpg
138
+ ```
139
+
140
+ Artwork is selected in this order:
141
+
142
+ 1. Same-stem artwork such as `Movie.jpg`
143
+ 2. Item-specific artwork such as `Movie-poster.png` or `Movie.poster.jpg`
144
+ 3. Folder artwork named `cover`, `folder`, `album`, or `poster`
145
+
146
+ The original artwork file is served without resizing or converting it.
147
+ Artwork selected for a video is hidden from the ordinary image listing.
148
+ Single-file mode can use sidecar artwork from the selected file's directory
149
+ without exposing any other files there.
150
+
151
+ To create missing same-stem JPEG thumbnails recursively for a folder of
152
+ videos, use the helper script from
153
+ [`player.html`](https://github.com/pseudosavant/player.html). It requires
154
+ `ffmpeg` on `PATH`, skips videos that already have a thumbnail, and does not
155
+ overwrite existing artwork:
156
+
157
+ ```console
158
+ uv run https://raw.githubusercontent.com/pseudosavant/player.html/main/prerender-video-thumbnails.py "D:\Videos"
159
+ ```
160
+
161
+ ## What it deliberately does not do
162
+
163
+ - It does not transcode video or audio.
164
+ - It does not remux media or mux subtitles into a stream.
165
+ - It does not expose or manage external subtitle files.
166
+ - It does not build or maintain a persistent media database.
167
+ - It does not have a web interface, user accounts, or authentication.
168
+ - It does not have persistent configuration; each run is controlled by
169
+ command-line arguments.
170
+
171
+ The catalog is built from the live filesystem as players browse it. Clients
172
+ receive the original file and decide whether they can play it.
173
+
174
+ ## Network security
175
+
176
+ `dlna-here` is an unauthenticated server intended for a trusted local network.
177
+ Anyone who can reach it can browse and request the files it exposes. Do not
178
+ publish its HTTP port to the Internet.
179
+
180
+ ## Tested players
181
+
182
+ `dlna-here` has been tested with:
183
+
184
+ - VLC
185
+ - Windows Media Player Legacy (`wmplayer.exe`)
186
+ - Roku Media Player
187
+ - Xbox Media Player
188
+
189
+ Individual format and codec support still depends on the player.
190
+
191
+ ## Development
192
+
193
+ Run the current source checkout:
194
+
195
+ ```console
196
+ uvx --refresh --from . dlna-here
197
+ ```
198
+
199
+ Run the test and build checks:
200
+
201
+ ```console
202
+ uv run python -m unittest discover -s tests -v
203
+ uvx ruff check .
204
+ uvx ruff format --check .
205
+ uv build
206
+ ```
207
+
208
+ See
209
+ [ROADMAP.md](https://github.com/pseudosavant/dlna-here/blob/main/ROADMAP.md)
210
+ for project status and permanent non-goals.
211
+
212
+ ## License
213
+
214
+ `dlna-here` is licensed under the
215
+ [MIT License](https://github.com/pseudosavant/dlna-here/blob/main/LICENSE).
216
+
217
+ Copyright (c) 2026 John Paul Ellis
@@ -0,0 +1,192 @@
1
+ # dlna-here
2
+
3
+ `dlna-here` is a small command-line UPnP/DLNA media server for sharing a local
4
+ folder—or one media file—on your local network. It serves videos, audio, and
5
+ images exactly as they are, with no transcoding and no persistent media
6
+ database.
7
+
8
+ ## Quick start
9
+
10
+ Once [`uv`](https://docs.astral.sh/uv/) is installed, open a terminal in any
11
+ media folder and run:
12
+
13
+ ```console
14
+ uvx dlna-here
15
+ ```
16
+
17
+ That is it. There is no separate `dlna-here` installation or configuration
18
+ step. Open the UPnP/DLNA browser on a player connected to the same network and
19
+ look for `<folder> on <computer>`.
20
+
21
+ Press `Q` or `q` in the terminal to stop the server. `Ctrl+C` works too. On a
22
+ terminal that does not support immediate single-key input, enter `q` and press
23
+ Enter.
24
+
25
+ You can also serve a different folder:
26
+
27
+ ```console
28
+ uvx dlna-here "D:\Videos"
29
+ ```
30
+
31
+ Or expose one file without making its containing folder or sibling files
32
+ browsable:
33
+
34
+ ```console
35
+ uvx dlna-here "D:\Videos\Movie.mp4"
36
+ ```
37
+
38
+ ## What it does
39
+
40
+ - Preserves the filesystem's folder hierarchy.
41
+ - Reflects additions, removals, and renames without restarting the server.
42
+ - Serves original files with HTTP byte-range support for playback and seeking.
43
+ - Uses filenames and filesystem metadata for browsing; it does not read media
44
+ contents before a player requests a file.
45
+ - Excludes hidden files and directories.
46
+ - Shows media names without the final filename extension by default.
47
+ - Follows symbolic links and Windows junctions by default, with cycle
48
+ protection.
49
+ - Supports Windows, Linux, and macOS. Windows is the primary platform.
50
+
51
+ ## Command-line arguments
52
+
53
+ ```text
54
+ dlna-here [path] [options]
55
+ ```
56
+
57
+ | Argument | Description |
58
+ | --- | --- |
59
+ | `[path]` | File or folder to serve. The current directory is used when omitted. A file path exposes only that supported media file. |
60
+ | `-h`, `--help` | Show the complete command help and exit. |
61
+ | `--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. |
62
+ | `--port PORT` | Set the HTTP media port. The default, `0`, asks the operating system to choose an available port. |
63
+ | `--name NAME` | Set the friendly server name displayed by media players. The default is `<file-or-folder> on <computer>`. |
64
+ | `--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. |
65
+ | `--no-follow-links` | Omit symbolic links and Windows junctions. By default they are followed, including links whose targets are outside the served folder. |
66
+ | `--list-interfaces` | List the IPv4 interfaces available for `--interface` and exit. |
67
+ | `--verbose` | Print protocol, filesystem, request, and transfer diagnostics. |
68
+ | `--version` | Print the `dlna-here` version and exit. |
69
+ | `--about` | Print project and license information and exit. |
70
+
71
+ For example:
72
+
73
+ ```console
74
+ uvx dlna-here "D:\Videos" --name "Living room videos"
75
+ uvx dlna-here --list-interfaces
76
+ uvx dlna-here --interface "Wi-Fi"
77
+ uvx dlna-here --interface 192.168.1.25 --port 8200
78
+ uvx dlna-here --show-extensions --no-follow-links
79
+ uvx dlna-here --verbose
80
+ ```
81
+
82
+ The HTTP server binds to the selected IPv4 address. UPnP discovery continues
83
+ to use its standard IPv4 multicast address and UDP port 1900.
84
+
85
+ ## Supported media files
86
+
87
+ File extensions are matched case-insensitively.
88
+
89
+ - Video: `.3g2`, `.3gp`, `.asf`, `.avi`, `.divx`, `.flv`, `.m2t`, `.m2ts`,
90
+ `.m4v`, `.mkv`, `.mov`, `.mp4`, `.mpe`, `.mpeg`, `.mpg`, `.mts`, `.ogm`,
91
+ `.ogv`, `.ts`, `.vob`, `.webm`, `.wmv`
92
+ - Audio: `.aac`, `.aif`, `.aiff`, `.alac`, `.flac`, `.m4a`, `.mp2`, `.mp3`,
93
+ `.oga`, `.ogg`, `.opus`, `.wav`, `.wave`, `.wma`
94
+ - Images: `.avif`, `.bmp`, `.gif`, `.heic`, `.heif`, `.jpe`, `.jpeg`, `.jpg`,
95
+ `.png`, `.thm`, `.tif`, `.tiff`, `.webp`
96
+
97
+ Support here means that `dlna-here` recognizes the extension, advertises an
98
+ appropriate media type, and serves the original bytes. It does not inspect or
99
+ convert the container, video codec, audio codec, or image encoding. The
100
+ receiving player must support the actual file.
101
+
102
+ ## Sidecar thumbnails and artwork
103
+
104
+ `dlna-here` can advertise existing `.jpg`, `.jpeg`, `.png`, or `.thm` images
105
+ as sidecar artwork for video and audio without opening the media file.
106
+
107
+ The simplest arrangement uses matching filenames:
108
+
109
+ ```text
110
+ Movies/
111
+ ├── Movie.mp4
112
+ └── Movie.jpg
113
+ ```
114
+
115
+ Artwork is selected in this order:
116
+
117
+ 1. Same-stem artwork such as `Movie.jpg`
118
+ 2. Item-specific artwork such as `Movie-poster.png` or `Movie.poster.jpg`
119
+ 3. Folder artwork named `cover`, `folder`, `album`, or `poster`
120
+
121
+ The original artwork file is served without resizing or converting it.
122
+ Artwork selected for a video is hidden from the ordinary image listing.
123
+ Single-file mode can use sidecar artwork from the selected file's directory
124
+ without exposing any other files there.
125
+
126
+ To create missing same-stem JPEG thumbnails recursively for a folder of
127
+ videos, use the helper script from
128
+ [`player.html`](https://github.com/pseudosavant/player.html). It requires
129
+ `ffmpeg` on `PATH`, skips videos that already have a thumbnail, and does not
130
+ overwrite existing artwork:
131
+
132
+ ```console
133
+ uv run https://raw.githubusercontent.com/pseudosavant/player.html/main/prerender-video-thumbnails.py "D:\Videos"
134
+ ```
135
+
136
+ ## What it deliberately does not do
137
+
138
+ - It does not transcode video or audio.
139
+ - It does not remux media or mux subtitles into a stream.
140
+ - It does not expose or manage external subtitle files.
141
+ - It does not build or maintain a persistent media database.
142
+ - It does not have a web interface, user accounts, or authentication.
143
+ - It does not have persistent configuration; each run is controlled by
144
+ command-line arguments.
145
+
146
+ The catalog is built from the live filesystem as players browse it. Clients
147
+ receive the original file and decide whether they can play it.
148
+
149
+ ## Network security
150
+
151
+ `dlna-here` is an unauthenticated server intended for a trusted local network.
152
+ Anyone who can reach it can browse and request the files it exposes. Do not
153
+ publish its HTTP port to the Internet.
154
+
155
+ ## Tested players
156
+
157
+ `dlna-here` has been tested with:
158
+
159
+ - VLC
160
+ - Windows Media Player Legacy (`wmplayer.exe`)
161
+ - Roku Media Player
162
+ - Xbox Media Player
163
+
164
+ Individual format and codec support still depends on the player.
165
+
166
+ ## Development
167
+
168
+ Run the current source checkout:
169
+
170
+ ```console
171
+ uvx --refresh --from . dlna-here
172
+ ```
173
+
174
+ Run the test and build checks:
175
+
176
+ ```console
177
+ uv run python -m unittest discover -s tests -v
178
+ uvx ruff check .
179
+ uvx ruff format --check .
180
+ uv build
181
+ ```
182
+
183
+ See
184
+ [ROADMAP.md](https://github.com/pseudosavant/dlna-here/blob/main/ROADMAP.md)
185
+ for project status and permanent non-goals.
186
+
187
+ ## License
188
+
189
+ `dlna-here` is licensed under the
190
+ [MIT License](https://github.com/pseudosavant/dlna-here/blob/main/LICENSE).
191
+
192
+ Copyright (c) 2026 John Paul Ellis