Recently, I had some important family events (holidays, birthday and New Year parties) with lots of videos recorded in 4k, many exceeding 180 seconds limit you can easily share via Viber app or WhatsApp and similar and it’s not always practical or desirable to share all of them on social networks like Facebook, YouTube, Vimeo or Google Photos / Google Drive for privacy reasons.
You sometimes wish to demonstrate your skills and set-up a simple video gallery on private server and provide direct downloads of original, uncompressed and not down-sampled videos with lower resolutions you get with other apps. If you upload and directly link to .mp4/mov/avi/mkv/mpg/etc files in messenger apps, the browsers like Chrome will start streaming (playing) them instead of downloading, which is not what sharing really means in this case.
Also, when you upload video clips on YouTube / Facebook and so on, they always lose original quality (in size and/or resolution), as files gets processed and re-compressed by various conversion and encoding algorithms.
How To Create Simple HTML VIDEO files Sharing / Download Page?
I had 2 simple requirements / goals:
- Simplicity. It had to be friendly to family members who aren’t tech savvy.
- It must prevent streaming (auto playing) of video files in browser, as the primary goal was to provide sharing / downloading originals, not viewing. While ordinary anchor (a href) links work with right-click or long-touch events in order to access download dialog, it is not a default operation in most modern browsers (streaming / playing is!).
1. PHP Solution (working / non-working)
My initial idea was to use a PHP language, simple server-side scripting with minimalist HTML layout. This was the original code:
1) index.php file:
In this file we essentially place basic HTML structure, and provide download links in anchor elements / tags.
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="UTF-8"/>
<meta content="True" name="HandheldFriendly"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=4.0, user-scalable=yes" />
<meta name="viewport" content="width=device-width"/>
<title>MP4 DOWNLOAD DEMO</title>
<style>
p{font-size:22px;font-weight:700;}
</style>
</head>
<body>
<p><a href="_download.php?id=1">1. DOWNLOAD #1 VIDEO</a></p>
<p><a href="_download.php?id=2">2. DOWNLOAD #2 VIDEO</a></p>
</body>
</html>
NOTE: Video files are named video1.mp4, video2.mp4 and so on, and they are in the same directory (path) as our index.php and download.php files in this example. You can, of course, change this by adding relative or absolute paths in the code.
2) download.php file:
Then, we just need to create another file used for processing all download requests on server side.
<?php
if (isset($_GET['id']) && is_int($_GET['id'])) {
$id = (int) $_GET['id'];
$filePath = 'video' . $id . '.mp4';
$filePath = 'video' . $id . '.mp4';
header("Content-Type: video/mp4");
header("Content-Length: ". filesize($filePath));
header('Content-Description: File Transfer');
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename="'.$fileName.'"');
readfile($filePath);
}
And, there was my first problem. Some files (usually smaller ones) were downloading and playing back just fine. However, the bigger ones were getting corrupted by the download process, impossible to playback on any device later (computer, phone, tablet).
Close inspection and byte-by-byte comparison revealed that headers of the downloaded files using PHP method were different, containing some browser information used for download, and some other bits and pieces not present in originals. For example, there was a +5 bytes difference in one file that was around 140 MB in size, so the downloaded file contained extra information, probably corrupting sensitive mp4 files video headers – not good!
Further investigation revealed that readfile() PHP function was causing mp4 video files corruption, and one of the main suspects / causes in my tests in conclusion was a limited download speed in some cases, combined with PHP timeout limit directive. Because, on my local test server (localhost), no such problems existed, as the download was lasting only couple of seconds at best (local file copy predominantly limited by internal hard-drive or ssd disk r/w speed and CPU, there is no network speed limitation involved).
While, there are several solutions to increase and fix it, either through directly editing php.ini file (on dedicated and VPS servers), or with .htaccess overrides on Cloud/Shared hosting plans to set or increase set_time_limit(0) parameter, I didn’t in particular like that idea at all, because of security reasons, server resource usage and what not.
In the end, I decided to entirely drop PHP idea from my project and went on with a much simpler pure HTML solution (read below).
2. HTML5 Solution (working)
For a simple task, such as linking to a local server files and downloading, there is no need to use PHP or any other server-side processing unless you wish to force users to click on special cloaked links and go to a mediated download process. But, that is a different matter entirely. That can be achieved as well, just with basic redirects, instead like above readfile() failed attempt.
index.html file:
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="UTF-8"/>
<meta content="True" name="HandheldFriendly"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=4.0, user-scalable=yes" />
<meta name="viewport" content="width=device-width"/>
<title>DOWNLOAD MP4 VIDEOS</title>
<style>
p{font-size:22px;font-weight:700;}
</style>
</head>
<body>
<p><a href="video1.mp4" download>1. DOWNLOAD #1 VIDEO</a></p>
<p><a href="video2.mp4" download>2. DOWNLOAD #2 VIDEO</a></p>
</body>
</html>
The entire trick above is to use HTML5 download attribute (and optionally target=”_blank”) in anchor elements to prevent streaming files directly in Chrome browsers on desktop and android or iOS (iPhone) devices.
You can later style it with CSS, add custom thumbnail / image holders for links and what not. I leave that task up to you.
CONCLUSION
There, for a moment I looked completely incompetent as a web developer in some of my family member’s eyes, but the problem was easily and relatively quickly fixed :)
Comments
Post A Comment