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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f70ca64739dfbd93c1badac47fb27cfde9a116c7386284f80becd71c018f091d
4
- data.tar.gz: 9232efa32e08339a155806b2e986c93310840c2d4ad48f6f57adb01227463d2c
3
+ metadata.gz: 656e3006935b38c62d1089e33a604d36887618e1d256cfd366973c55cbbb28b2
4
+ data.tar.gz: d473daa0a115e3f03719f0a642a5d38e4f4ea91986872ae69b215a3ca3efaaa7
5
5
  SHA512:
6
- metadata.gz: 2faf237ac46476883a6a5299d5da982d45c9fbb547154871c60587237bde37a6d9b66ae45cd7db2127736d37c7d0e3a8509730e0fd5c51fa12b5fa20e9fc28ed
7
- data.tar.gz: 6ae6dabe71bb1d51ba29f60c1c2f2389b1029a9657c19a3e5558c342db6884f5cd1f324762988a62bb39e6e9fb5271ee7f1b83401d6ba4bbe3b3d855b6e93d8d
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 is Simple Ruby HLS Server for MP3s
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
- Once the server is running, you can access the HLS streams using an HLS-compatible player (like VLC, QuickTime Player on macOS/iOS, Safari, or web players using hls.js).
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 URL format is:
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
- http\://localhost:{SERVER\_PORT}/stream/{mp3\_filename\_without\_extension}/playlist.m3u8
11
+ ## Requirements
53
12
 
54
- **Example:**
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
- If you have an MP3 file named mp3\_files/awesome\_track.mp3 and the server is running on the default port 9292, the streaming URL would be:
19
+ ## Install and run
57
20
 
58
- http\://localhost:9292/stream/awesome\_track/playlist.m3u8
59
-
60
- **Note:** The first time you request a specific stream, the server will run ffmpeg to convert the MP3. This might take a few seconds depending on the file size. Subsequent requests for the same stream will be served instantly from the cache.
61
-
62
- ## Configuration
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
- - MP3\_SRC\_DIR: Path to the directory containing your original MP3 files.
67
- - HLS\_CACHE\_DIR: Path to the directory where HLS segments and playlists will be cached.
68
- - SERVER\_PORT: The network port the server listens on.
69
- - FFMPEG\_COMMAND: The command used to execute ffmpeg (change if it's not in your PATH).
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
- ## How it Works
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
- 1. A client requests an M3U8 playlist URL (e.g., /stream/my\_song/playlist.m3u8).
75
- 2. The server checks if the corresponding HLS files (hls\_cache/my\_song/playlist.m3u8 and segments) exist in the cache directory.
76
- 3. If the cache does not exist:
77
- - It verifies the original mp3\_files/my\_song.mp3 exists.
78
- - It acquires a lock specific to my\_song to prevent simultaneous conversions.
79
- - It runs ffmpeg to convert my\_song.mp3 into hls\_cache/my\_song/playlist.m3u8 and hls\_cache/my\_song/segmentXXX.mp3.
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
- ## Limitations
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
- - **VOD Only:** This server is designed for Video on Demand (pre-existing files) and does not support live streaming.
88
- - **Basic Caching:** Cache is persistent but simple. There's no automatic cache invalidation if the source MP3 changes. You would need to manually clear the corresponding subdirectory in hls\_cache.
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
- Install from git repository:
98
-
99
- ```bash
119
+ ```sh
100
120
  git clone https://github.com/koichiro/miniradio_server.git
101
121
  cd miniradio_server
102
- bundle
122
+ bin/setup
103
123
  bin/miniradio_server
124
+ bundle exec exe/miniradio_server --help
104
125
  ```
105
126
 
106
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
107
-
108
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org/gems/miniradio_server).
109
-
110
- ## ToDo
111
-
112
- * Continuous playback of multiple Music tracks
113
- * :white_check_mark: ~~Use hls.js to support playback in Chrome.~~
114
- * :white_check_mark: ~~Rendering of the Delivered Music list page~~
115
- * :white_check_mark: ~~Multilingual support for file names.~~
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
- ## License
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
- This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the LICENSE file for details (or assume MIT if no LICENSE file is present).
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).
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "miniradio_server/cli"
5
+ exit MiniradioServer::CLI.run