miniradio_server 0.0.3 → 0.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +215 -101
- data/exe/miniradio_server +5 -0
- data/lib/miniradio_server/app.rb +129 -78
- data/lib/miniradio_server/cli.rb +55 -0
- data/lib/miniradio_server/templ/index.html.slim +53 -42
- data/lib/miniradio_server/version.rb +1 -1
- data/lib/miniradio_server.rb +6 -72
- data/lib/public/player.js +312 -0
- data/lib/public/style/main.css +55 -21
- metadata +76 -11
- data/Rakefile +0 -8
- data/hls_cache/.gitkeep +0 -0
- data/mp3_files/.gitkeep +0 -0
- data/sig/miniradio_server.rbs +0 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 656e3006935b38c62d1089e33a604d36887618e1d256cfd366973c55cbbb28b2
|
|
4
|
+
data.tar.gz: d473daa0a115e3f03719f0a642a5d38e4f4ea91986872ae69b215a3ca3efaaa7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '05839d4583440e44b8a07340d4c31f441f7441502639f77a63e3bb23c39af1119f8a859f76ea4811c72dfa9e18ea26337cd7c22839583b614c231ccaac80d5fb'
|
|
7
|
+
data.tar.gz: 9fcdbf122d76f3fd539c2d17422ec1833156af7315e0e19bd43361d64090f47764f149698ea4dc2e74bbcb86f0fd21647c8795a5549b1afe92233b510917426e
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Package the `miniradio_server` command, Slim templates, and player assets for
|
|
6
|
+
installation with RubyGems. Support Ruby 3.4 and later.
|
|
7
|
+
- Configure source/cache directories, port, and FFmpeg through CLI options;
|
|
8
|
+
requiring the library does not start the server.
|
|
9
|
+
- Stream MP3 files through FFmpeg with persistent HLS caching, safe paths, and
|
|
10
|
+
support for spaces and Japanese filenames.
|
|
11
|
+
- Provide a shared hls.js player with track metadata, embedded artwork, seeking,
|
|
12
|
+
volume, previous/next, repeat, and shuffle controls.
|
|
13
|
+
- Verify Ruby style, coverage, player behavior, dependency security, and the
|
|
14
|
+
installed gem in CI.
|
data/README.md
CHANGED
|
@@ -1,124 +1,238 @@
|
|
|
1
|
-
# Miniradio Server
|
|
2
|
-
This is a basic HTTP Live Streaming (HLS) server written in Ruby using the Rack interface. It serves MP3 audio files by converting them on-the-fly into HLS format (M3U8 playlist and MP3 segment files) using `ffmpeg`. Converted files are cached for subsequent requests.
|
|
3
|
-
This server is designed for simplicity and primarily targets Video on Demand (VOD) scenarios where you want to stream existing MP3 files via HLS without pre-converting them.
|
|
4
|
-
|
|
5
|
-
## Prerequisites
|
|
6
|
-
Before running the server, ensure you have the following installed:
|
|
7
|
-
1. **Ruby:** Version 3.1 or later (tested on 3.4).
|
|
8
|
-
2. **Dependency Gems:** Install using `bundle`.
|
|
9
|
-
3. **FFmpeg:** A recent version of `ffmpeg` must be installed and accessible in your system's PATH. You can download it from [ffmpeg.org](https://ffmpeg.org/) or install it via your system's package manager (e.g., `apt install ffmpeg`, `brew install ffmpeg`).
|
|
10
|
-
## Setup
|
|
11
|
-
1. **Install Gems:**
|
|
12
|
-
```bash
|
|
13
|
-
gem install miniradio_server
|
|
14
|
-
```
|
|
15
|
-
2. **Create MP3 Directory:** Create a directory named `mp3_files` in the same location as the script.
|
|
16
|
-
```bash
|
|
17
|
-
mkdir mp3_files
|
|
18
|
-
```
|
|
19
|
-
*(Alternatively, change the `MP3_SRC_DIR` constant in the script).*
|
|
20
|
-
3. **Add MP3 Files:** Place the MP3 files you want to stream into the `mp3_files` directory.
|
|
21
|
-
* **Important:** Use simple, URL-safe filenames for your MP3s (e.g., letters, numbers, underscores, hyphens). Spaces or special characters might cause issues. Example: `my_cool_song.mp3`, `podcast_episode_1.mp3`.
|
|
22
|
-
4. **Cache Directory:** The script will automatically create a `hls_cache` directory (or the directory specified by `HLS_CACHE_DIR`) when the first conversion occurs. Ensure the script has write permissions in its parent directory.
|
|
23
|
-
|
|
24
|
-
## Running the Server
|
|
25
|
-
Navigate to the directory containing the script in your terminal and run:
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
bin/miniradio_server
|
|
29
|
-
Info: ffmpeg found.
|
|
30
|
-
Starting HLS conversion and streaming server on port 9292...
|
|
31
|
-
MP3 Source Directory: /path/to/your/project/mp3_files
|
|
32
|
-
HLS Cache Directory: /path/to/your/project/hls_cache
|
|
33
|
-
Default External Encoding: UTF-8
|
|
34
|
-
Using Handler: Rackup::Handler::WEBrick
|
|
35
|
-
Example Streaming URL: http://localhost:9292/stream/{mp3_filename_without_extension}/playlist.m3u8
|
|
36
|
-
e.g., If mp3_files/ contains my_music.mp3 -> http://localhost:9292/stream/my_music/playlist.m3u8
|
|
37
|
-
Press Ctrl+C to stop.
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
The server will run in the foreground. Press Ctrl+C to stop it.
|
|
41
|
-
|
|
42
|
-
## Usage: Accessing the Index Page
|
|
43
|
-
|
|
44
|
-
You can access the index page listing available MP3 files by navigating to `http://localhost:{SERVER_PORT}/` in your web browser.
|
|
45
|
-
|
|
46
|
-
## Usage: Accessing Streams
|
|
1
|
+
# Miniradio Server
|
|
47
2
|
|
|
48
|
-
|
|
3
|
+
A small Ruby/Rack server that streams existing MP3 files via HTTP Live Streaming
|
|
4
|
+
(HLS). FFmpeg converts each track on its first request; later requests reuse a
|
|
5
|
+
persistent disk cache. This is a VOD server, not a live radio broadcaster.
|
|
49
6
|
|
|
50
|
-
The
|
|
7
|
+
The web player uses one audio element and loads streams only when selected.
|
|
8
|
+
It supports continuous playback, play/pause, previous/next, repeat track, and
|
|
9
|
+
shuffle. All compatible browsers, including Safari, use hls.js for HLS playback.
|
|
51
10
|
|
|
52
|
-
|
|
11
|
+
## Requirements
|
|
53
12
|
|
|
54
|
-
**
|
|
13
|
+
- Ruby 3.4 or later. The repository uses Ruby **4.0.7** via `.ruby-version`;
|
|
14
|
+
CI also checks the supported minimum Ruby 3.4 series.
|
|
15
|
+
- FFmpeg on `PATH` (`brew install ffmpeg` or `apt install ffmpeg`).
|
|
16
|
+
- Internet access for the web player's Pico CSS and hls.js CDN assets.
|
|
17
|
+
- Node.js 18 or later for player development tests; not needed to run the server.
|
|
55
18
|
|
|
56
|
-
|
|
19
|
+
## Install and run
|
|
57
20
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
You can modify the following constants at the top of the script (miniradio\_server.rb):
|
|
21
|
+
```sh
|
|
22
|
+
gem install miniradio_server -v 0.1.0
|
|
23
|
+
mkdir -p mp3_files
|
|
24
|
+
# Copy your .mp3 files into mp3_files, then:
|
|
25
|
+
miniradio_server
|
|
26
|
+
```
|
|
65
27
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
- HLS\_SEGMENT\_DURATION: The target duration (in seconds) for each HLS segment.
|
|
28
|
+
Open <http://localhost:9292/>. Both `mp3_files` and `hls_cache` are created at
|
|
29
|
+
startup in the current working directory. Spaces, Japanese text, and other
|
|
30
|
+
non-ASCII filenames are supported; generated stream links are URL-encoded.
|
|
31
|
+
Only `.mp3` files directly inside the source directory are listed.
|
|
71
32
|
|
|
72
|
-
|
|
33
|
+
These instructions target version **0.1.0**. Until it is published to RubyGems,
|
|
34
|
+
build and install it from the source checkout using the release preparation
|
|
35
|
+
commands below.
|
|
73
36
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- The lock is released.
|
|
81
|
-
4. If the cache does exist (or after successful conversion), the server serves the requested playlist.m3u8 file.
|
|
82
|
-
5. The client parses the M3U8 playlist and requests the individual MP3 segment files listed within it (e.g., /stream/my\_song/segment000.mp3, /stream/my\_song/segment001.mp3, etc.).
|
|
83
|
-
6. The server serves these segment files directly from the cache directory.
|
|
37
|
+
```sh
|
|
38
|
+
miniradio_server --mp3-dir /path/to/music --cache-dir /path/to/cache --port 9393
|
|
39
|
+
miniradio_server --ffmpeg /path/to/ffmpeg
|
|
40
|
+
miniradio_server --help
|
|
41
|
+
miniradio_server --version
|
|
42
|
+
```
|
|
84
43
|
|
|
85
|
-
|
|
44
|
+
The server stays in the foreground; press Ctrl+C to stop it. Defaults are port
|
|
45
|
+
9292, `ffmpeg`, and a target HLS segment duration of 10 seconds. Custom Ruby
|
|
46
|
+
applications can instantiate `MiniradioServer::App` with their own directories,
|
|
47
|
+
FFmpeg command, segment duration, and logger. Requiring the library does not
|
|
48
|
+
start a server.
|
|
49
|
+
|
|
50
|
+
## Playback
|
|
51
|
+
|
|
52
|
+
The player loads hls.js 1.x from the CDN and requires Media Source Extensions
|
|
53
|
+
(MSE) or Managed Media Source (MMS) with support for the stream's audio codec.
|
|
54
|
+
According to [hls.js compatibility documentation](https://github.com/video-dev/hls.js#compatibility),
|
|
55
|
+
Safari targets include macOS Safari 10+ (macOS 10.11+), iPadOS Safari 13+,
|
|
56
|
+
and iOS Safari 17.1+ (MMS requires hls.js 1.5.0+). These are library targets;
|
|
57
|
+
actual MP3 playback must also be checked on the target device. Older iPhones
|
|
58
|
+
without MMS cannot use this player, even if they support native HLS.
|
|
59
|
+
The player checks `Hls.isSupported()` and displays an error if hls.js is
|
|
60
|
+
unsupported or fails to load; there is no native HLS fallback.
|
|
61
|
+
Remote playback (including AirPlay) is disabled to allow
|
|
62
|
+
[Safari MMS playback without a native alternative](https://webkit.org/blog/14735/webkit-features-in-safari-17-1/).
|
|
63
|
+
|
|
64
|
+
- The shared player above the track list shows the selected track's title,
|
|
65
|
+
artist, album, embedded artwork, playback state, and elapsed/total time.
|
|
66
|
+
Missing tags use the filename or an information-unavailable label; missing
|
|
67
|
+
artwork uses a placeholder. Selecting a row updates this shared player.
|
|
68
|
+
- The seek bar changes playback position once the duration and seekable range
|
|
69
|
+
are available. Volume and mute stay unchanged when selecting another track;
|
|
70
|
+
devices that cannot change volume from the page show a device-control hint.
|
|
71
|
+
- **Play all** starts at the first track and can restart a finished playlist.
|
|
72
|
+
- Each row has a keyboard-accessible play button.
|
|
73
|
+
- **Previous** wraps from the first track to the last. **Next** stops at the end
|
|
74
|
+
of the list when shuffle is off.
|
|
75
|
+
- **Repeat track** repeats the current track when it ends; it does not loop the
|
|
76
|
+
entire playlist. Manual previous/next still select another track.
|
|
77
|
+
- **Shuffle** chooses a different random track when there is more than one
|
|
78
|
+
track, and continues until paused. Repeat track takes precedence on track end.
|
|
79
|
+
- Playback controls are grouped in the shared player; rows only select tracks.
|
|
80
|
+
When the playlist finishes, the last selected track stays visible. **Play**
|
|
81
|
+
restarts that track, while **Play all** restarts from the first track.
|
|
82
|
+
|
|
83
|
+
The first play of a track may take a few seconds while FFmpeg creates its cache.
|
|
84
|
+
An overlapping request for the same conversion receives HTTP 503 with
|
|
85
|
+
`retry-after: 5`. If playback fails, the page displays a message; select the track
|
|
86
|
+
again or press **Retry** to reload it, or press **Play** if the browser blocked
|
|
87
|
+
playback. Track information stays visible when playback is paused or fails.
|
|
88
|
+
|
|
89
|
+
### Artwork
|
|
90
|
+
|
|
91
|
+
Only the selected track's artwork is requested, via
|
|
92
|
+
`/artwork/{URL-encoded-filename-without-extension}`. The server returns the
|
|
93
|
+
first eligible embedded JPEG or PNG (up to 5 MiB), with a MIME type determined
|
|
94
|
+
from the binary signature. It does not fetch external covers, search neighboring
|
|
95
|
+
image files, convert images, or create an artwork disk cache. Missing,
|
|
96
|
+
unsupported, oversized, or unreadable artwork returns 404 and shows a placeholder
|
|
97
|
+
without interrupting playback. The size limit bounds the served image, not the
|
|
98
|
+
MP3 parser's memory usage. Unreadable track metadata falls back to the filename
|
|
99
|
+
so one bad tag does not prevent the library from displaying.
|
|
100
|
+
|
|
101
|
+
## Direct streams and cache
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
http://localhost:9292/stream/{URL-encoded-filename-without-extension}/playlist.m3u8
|
|
105
|
+
```
|
|
86
106
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
- **Security:** Basic checks against directory traversal are included, but it's not hardened for production use against malicious requests. No authentication/authorization is implemented.
|
|
90
|
-
- **Performance:** Relies on ffmpeg execution per file (first request only). Uses Ruby's WEBrick via rackup, which is single-threaded by default and not ideal for high-concurrency production loads.
|
|
91
|
-
- **Error Handling:** Basic error handling is implemented, but complex ffmpeg issues or edge cases might not be handled gracefully.
|
|
92
|
-
- **Resource Usage:** Conversion can be CPU-intensive (though -c:a copy helps significantly) and disk I/O intensive during the first request for a file.
|
|
107
|
+
For `my song.mp3`, use `/stream/my%20song/playlist.m3u8`. HLS-compatible players
|
|
108
|
+
can request the playlist and the segment URLs it contains directly.
|
|
93
109
|
|
|
110
|
+
FFmpeg copies the audio codec without re-encoding. Each track gets a cache
|
|
111
|
+
subdirectory containing `playlist.m3u8` and `segmentNNN.mp3` files. Despite their
|
|
112
|
+
extension, the segments use FFmpeg's default HLS MPEG-TS container. The cache
|
|
113
|
+
persists across restarts. Failed conversions are cleaned up and can be retried.
|
|
114
|
+
When a source MP3 changes, manually remove its cache subdirectory while the
|
|
115
|
+
server is stopped; automatic invalidation is not implemented.
|
|
94
116
|
|
|
95
117
|
## Development
|
|
96
118
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
```bash
|
|
119
|
+
```sh
|
|
100
120
|
git clone https://github.com/koichiro/miniradio_server.git
|
|
101
121
|
cd miniradio_server
|
|
102
|
-
|
|
122
|
+
bin/setup
|
|
103
123
|
bin/miniradio_server
|
|
124
|
+
bundle exec exe/miniradio_server --help
|
|
104
125
|
```
|
|
105
126
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
## Contributing
|
|
118
|
-
|
|
119
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/koichiro/miniradio_server.
|
|
127
|
+
```sh
|
|
128
|
+
bundle exec rake lint # Standard Ruby style checks
|
|
129
|
+
bundle exec standardrb --fix # Apply Standard's automatic formatting
|
|
130
|
+
bundle exec rake test # Ruby tests and the 90% coverage gate
|
|
131
|
+
bundle exec rake test_player # Player logic tests using Node.js
|
|
132
|
+
bundle exec rake check # Lint and both test suites (also the default rake task)
|
|
133
|
+
bundle exec rake audit # Update the advisory database and audit locked gems
|
|
134
|
+
bundle exec rake build # Build pkg/miniradio_server-0.1.0.gem
|
|
135
|
+
bundle exec rake smoke_gem # Build/install in a temporary directory and check CLI/assets
|
|
136
|
+
```
|
|
120
137
|
|
|
121
|
-
|
|
138
|
+
The Ruby suite runs real HLS conversion tests when FFmpeg is installed, and
|
|
139
|
+
skips those tests otherwise. CI installs FFmpeg so conversion tests always run.
|
|
140
|
+
Standard checks Ruby source, tests, executables, and project configuration using
|
|
141
|
+
Ruby 3.4 syntax as the supported minimum; no style violations are grandfathered.
|
|
142
|
+
SimpleCov measures Ruby **line coverage** and fails the test command if the
|
|
143
|
+
overall coverage or any measured source file falls below **90%**. All runtime
|
|
144
|
+
Ruby files under `lib/` are tracked, including files not loaded by tests; only
|
|
145
|
+
the declarative `version.rb` metadata loaded by Bundler before instrumentation
|
|
146
|
+
is excluded. Reports contain the current run only, without merging earlier runs.
|
|
147
|
+
Open `coverage/index.html` for the HTML report or read `coverage/coverage.json`
|
|
148
|
+
for machine-readable results. GitHub Actions runs the same lint/coverage checks
|
|
149
|
+
on Ruby 3.4 and 4.0.7 and uploads each coverage report as an artifact, including
|
|
150
|
+
when tests or the coverage threshold fail.
|
|
151
|
+
Player tests cover control behavior, hls.js readiness, native-capable browsers,
|
|
152
|
+
unavailable hls.js, and URL-encoded Japanese filenames using simulated DOM/media
|
|
153
|
+
APIs. Before releasing, also check actual playback in Safari and Chrome: initial play, rapid track changes, next-track autoplay, pause/resume,
|
|
154
|
+
repeat, shuffle, seeking, and empty libraries. Include Japanese filenames and
|
|
155
|
+
check macOS Safari and iOS/iPadOS Safari on actual devices; simulated player
|
|
156
|
+
tests do not verify decoding or browser autoplay policies.
|
|
157
|
+
|
|
158
|
+
## Release 0.1.0
|
|
159
|
+
|
|
160
|
+
Prepare and verify the release artifact:
|
|
161
|
+
|
|
162
|
+
```sh
|
|
163
|
+
bundle install
|
|
164
|
+
bundle exec rake check
|
|
165
|
+
bundle exec rake audit
|
|
166
|
+
bundle exec rake smoke_gem
|
|
167
|
+
# Install the built artifact for local use:
|
|
168
|
+
gem install ./pkg/miniradio_server-0.1.0.gem --no-document
|
|
169
|
+
miniradio_server --version # 0.1.0
|
|
170
|
+
```
|
|
122
171
|
|
|
123
|
-
|
|
172
|
+
The smoke check installs only the built gem into a temporary gem home, using
|
|
173
|
+
runtime dependencies already installed by Bundler. It runs outside the checkout
|
|
174
|
+
and checks the command, version, help, rendered page, JavaScript, and CSS.
|
|
175
|
+
It does not start a listening HTTP server. The package includes runtime files,
|
|
176
|
+
README, changelog, and license; tests, sample MP3s, and local caches are excluded.
|
|
177
|
+
FFmpeg is an external requirement and is not bundled in the gem.
|
|
178
|
+
|
|
179
|
+
After merging the release PR, complete the browser checks described above and
|
|
180
|
+
run the following from an up-to-date, clean `main` checkout with GitHub push
|
|
181
|
+
access and RubyGems publishing credentials for `miniradio_server`:
|
|
182
|
+
|
|
183
|
+
```sh
|
|
184
|
+
git switch main
|
|
185
|
+
git pull --ff-only
|
|
186
|
+
bundle install
|
|
187
|
+
bundle exec rake release
|
|
188
|
+
```
|
|
124
189
|
|
|
190
|
+
Bundler's release task builds the gem, creates the `v0.1.0` Git tag, pushes it
|
|
191
|
+
to the Git remote, and publishes to RubyGems. The gemspec restricts publishing
|
|
192
|
+
to `https://rubygems.org`. Building, running the smoke check, and opening the PR
|
|
193
|
+
do not publish a gem. See [CHANGELOG.md](CHANGELOG.md) for the release contents.
|
|
194
|
+
|
|
195
|
+
## Dependency security
|
|
196
|
+
|
|
197
|
+
Dependabot checks Bundler dependencies and GitHub Actions every Monday at 09:00
|
|
198
|
+
Asia/Tokyo and opens update PRs. The existing quality checks and the dependency
|
|
199
|
+
security workflow run on those PRs too. Updates are reviewed and merged manually.
|
|
200
|
+
|
|
201
|
+
The `Dependency security` workflow runs `bundle exec rake audit` on every PR,
|
|
202
|
+
push to `main`, manual dispatch, and daily at approximately 06:17 Asia/Tokyo.
|
|
203
|
+
Each run refreshes [Ruby Advisory Database](https://github.com/rubysec/ruby-advisory-db)
|
|
204
|
+
and checks the entire `Gemfile.lock`, including runtime, development, and
|
|
205
|
+
transitive gems. Known vulnerabilities, insecure gem sources, or a failed
|
|
206
|
+
database refresh cause the job to fail; advisories are not ignored. Local audits
|
|
207
|
+
also require network access to refresh the database. Results appear in the
|
|
208
|
+
Actions job logs. Scheduled audits catch new advisories even without code changes.
|
|
209
|
+
|
|
210
|
+
Dependabot **alerts** and **security updates** are separate GitHub repository
|
|
211
|
+
settings; `dependabot.yml` enables version update PRs but cannot enable those
|
|
212
|
+
settings. Under **Settings → Advanced Security** (or **Code security and
|
|
213
|
+
analysis**), enable the dependency graph, Dependabot alerts, and Dependabot
|
|
214
|
+
security updates to receive advisory alerts and automatic security fix PRs.
|
|
215
|
+
See [GitHub's Dependabot documentation](https://docs.github.com/en/code-security/dependabot).
|
|
216
|
+
The Actions audit works independently of those settings. It audits locked Ruby
|
|
217
|
+
gems; browser CDN assets and FFmpeg are not part of `Gemfile.lock`.
|
|
218
|
+
|
|
219
|
+
## Limitations and next work
|
|
220
|
+
|
|
221
|
+
- VOD only; no live input, authentication, or authorization.
|
|
222
|
+
- The server is intended for trusted local use. WEBrick binds to its default
|
|
223
|
+
interface; restrict access with your network configuration when needed.
|
|
224
|
+
- Conversion is synchronous per request. Locks are per process, not shared
|
|
225
|
+
between multiple server processes.
|
|
226
|
+
- Directory traversal and symlink escapes are rejected, and track metadata is
|
|
227
|
+
rendered as text. Source/cache directories should remain under your control.
|
|
228
|
+
- No automatic cache invalidation, size limit, or eviction policy.
|
|
229
|
+
- Corrupt or unsupported audio may still fail playback even when its filename
|
|
230
|
+
is listed using the metadata fallback.
|
|
231
|
+
- Remaining work includes browser playback checks, cache lifecycle management,
|
|
232
|
+
and improved recovery from conversion/player errors.
|
|
233
|
+
|
|
234
|
+
## Contributing and license
|
|
235
|
+
|
|
236
|
+
Bug reports and pull requests are welcome on
|
|
237
|
+
[GitHub](https://github.com/koichiro/miniradio_server).
|
|
238
|
+
Licensed under MIT; see [LICENSE.txt](LICENSE.txt).
|