one entry

This commit is contained in:
2024-12-24 16:11:29 -06:00
parent 6e1407b875
commit b4201be474
3 changed files with 151 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
---
title: Light Weight Video Streaming Server
date: 2024-12-24
---
> [!NOTE]
> This is another installation in my *magnum opus*, "Random-Things-I-Make-for-Absolutely-no-Reason".
# The Problem
I've picked up watching portions of shows and dropping them on a whim, but generally don't want to stream them thanks to the network load caused by my other projects[^1]. This requires downloading and hosting the files myself, perfectly feasible given my resources.
Hosting these files over the internet, however, would not be feasible due to the low outgoing upload speeds provided by my godforsaken ISP. The client, in cases would just be my phone or laptop. I will acknowledge, though, that options like Plex, Jellyfin, and others exist to manage almost the same requirements.
The sole reason I've chosen not to go with these is the resource overhead demanded. I spun up an instance of Jellyfin to prove this point. At idle, $\ge0.5$ GB of memory was used alongside a not insignificant CPU/GPU allocation.
Looking further into the processes involved, I found that servers like Jellyfin and Plex use the HSL video streaming paradigm, requiring server-side segmentation, encoding/decoding, and scaling. In my situation, bandwidth was an unlimited resource, so the overhead was unnecessary.
# The Solution
I really just had to write a script to present all the videos on a webserver in a drop-down list with a decent video player. Although there is nothing I hate more in the world than a Python network application, I'm too lazy to do anything else.
Flask python API/`html` proxy (`server.py`):
```python
from flask import Flask, jsonify, send_from_directory, render_template
import os
app = Flask(__name__)
# Get the current working directory (where the Python script is run from)
video_dir = os.getcwd() # Ensure this is where your .mp4 files and index.html are located
@app.route('/')
def serve_index():
"""Serve the index.html file."""
return send_from_directory(video_dir, 'index.html')
@app.route('/videos')
def list_videos():
"""Return a list of .mp4 files in the directory."""
files = [f for f in os.listdir(video_dir) if f.endswith('.mp4')]
return jsonify(files)
@app.route('/videos/<filename>')
def serve_file(filename):
"""Serve video files to the browser from the videos directory."""
return send_from_directory(video_dir, filename)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
```
And the `html` front-end (`index.html`):
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>4K Video Player</title>
<link href="https://unpkg.com/video.js/dist/video-js.min.css" rel="stylesheet">
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f4f4f9;
}
h1 {
text-align: center;
}
select {
width: 100%;
padding: 10px;
margin-bottom: 20px;
}
.video-container {
max-width: 100%;
margin: 0 auto;
}
</style>
</head>
<body>
<h1>4K Video Player</h1>
<select id="videoList" onchange="loadVideo(this.value)">
<option value="">Select a video</option>
</select>
<div class="video-container">
<video id="player" class="video-js vjs-default-skin" controls>
<source id="videoSource" src="" type="video/mp4">
Your browser does not support the video tag.
</video>
</div>
<script src="https://unpkg.com/video.js/dist/video.min.js"></script>
<script>
var player = videojs('player', {
controls: true,
autoplay: false,
preload: 'auto',
width: '100%',
height: 'auto',
poster: 'path/to/your/poster-image.jpg', // Optional: add a thumbnail image for video preview
fluid: true, // Ensures responsive video sizing
playbackRates: [0.5, 1, 1.5, 2], // Allow the user to control playback speed
controlBar: {
playToggle: true,
volumePanel: { inline: false }, // Displays volume control as a separate panel
currentTimeDisplay: true,
timeDivider: true,
durationDisplay: true,
remainingTimeDisplay: true,
fullscreenToggle: true
},
sources: [{
type: 'video/mp4',
src: ''
}]
});
// Populate video dropdown list
const videoList = document.getElementById('videoList');
fetch('/videos')
.then(response => response.json())
.then(files => {
files.forEach(file => {
const option = document.createElement('option');
option.value = file;
option.textContent = file;
videoList.appendChild(option);
});
});
// Load the selected video into the player
function loadVideo(src) {
if (!src) return; // If no video selected, do nothing
const videoSource = document.getElementById('videoSource');
videoSource.src = '/videos/' + src.replace('.mkv', '.mp4'); // Set video source path
player.src({ type: 'video/mp4', src: '/videos/' + src.replace('.mkv', '.mp4') });
player.play(); // Start playback automatically after video is loaded
}
</script>
</body>
</html>
```
Resulting in:
![](maker-portfolio-1.png)
[^1]: I will write these up here at some point.

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB