BobbyWibowo / lolisafe

Blazing fast file uploader and awesome bunker written in node! 🚀
MIT License
317 stars 56 forks source link

[FEATURE REQUEST] Embedded Video/Audio playback #174

Closed camjac251 closed 3 years ago

camjac251 commented 4 years ago

Would it possible if this app had a visual preview page for audio and video files? HTML supports embedding WAV files. Although directly opening wav files in Firefox forces them to download, no direct playing

camjac251 commented 4 years ago

https://videojs.com seems like a great player framework that has many customizable options There's also a plugin for adding a waveform for audio (which could resemble soundcloud in a way) https://collab-project.github.io/videojs-wavesurfer or https://github.com/michaeldzjap/waveplayer.js

camjac251 commented 4 years ago

I had another feature request but it would be dependent on this one so I'll add it here instead of a new issue.

To add to the video/audio player, could it be possible to support live transcoding of unsupported formats in HTML5? I found this package that looked really interesting https://www.npmjs.com/package/beamcoder

BobbyWibowo commented 4 years ago

For the videojs one, I guess you could potentially just create a plain ol' HTML file, that has a tiny script that would parse file name from the URL param (e.g. https://example.com/embed?name=abcd.mp4). It'd then simply embed the file in itself using videojs? A plain ol' HTML page will do, since there should be no need to query DB, even if merely to check file existence or whatever. That'd sound like a waste of processing power if anything. Just have the script detect 404 instead.

I guess it'd be pretty straightforward since everything would already be handled by videojs. I'm just not sure where exactly to implement it. For instance, files that are uploaded to my safe aren't even being served by the lolisafe instance. I went with the route of serving everything in the uploads folder straight with Nginx (hence the safe at safe.fiery.me, but the files at i.fiery.me).

Though perhaps just adding that simple embed page is already good enough?

With that, perhaps I can also replace the "Load Original" button in Dashboard's Display Preview for video file, to instead be a "View in embedded player" link, that will open the embed page in new tab, auto-filled with the file name.


could it be possible to support live transcoding of unsupported formats in HTML5?

I have no idea what's that supposed to mean.

camjac251 commented 4 years ago

Having a simple html page do all the work would be a nice lightweight option, not many other scripts can do that because they store short names in a DB and the file structure on disk is different than the permalinks. I didn't realize that files were served directly over nginx, that could possibly allow me to implement a CDN easily :)

What if an nginx location block was used with a regex pattern to match safe.fiery.me/v/filename.ext for viewing?

camjac251 commented 4 years ago

could it be possible to support live transcoding of unsupported formats in HTML5?

I have no idea what's that supposed to mean.

There's a few node libraries that would allow for it using ffmpeg. Essentially what it would do in theory is convert the video format on demand when a file is requested, to be playable in the browser. This could also uses HLS or DASH for streaming as well.

Dropbox for example I believe has this, either its offline or live. When you want to view a video file using the dropbox interface, whether its an uncompressed AVI file or its an HEVC 10-bit HDR stream, they should be able to play using their player that serves you an encoded version thats compatible for browser playback. Here's an example https://www.dropbox.com/s/uu8d6o2gzdn36bb/oscilloscope04_preview.mov?dl=0 If you download the file and try playing in Chrome or Firefox, it fails, but with Dropbox they transcode the file for their player to play it nicely (and include quality options too)

BobbyWibowo commented 4 years ago

that could possibly allow me to implement a CDN easily :)

That's the reasoning behind this fork's Cloudflare support. Mainly the ability to have Cloudflare's cache of specific files be automatically deleted, once that file has been deleted through the Dashboard (along with cache of their thumbnails whenever applicable).

I used to have i.fiery.me be fully-cached in Cloudflare regardless of file types (they allow this Cache Everything thing with Page Rules). They banned me a couple months ago because of it though.

What if an nginx location block was used with a regex pattern to match safe.fiery.me/v/filename.ext for viewing?

That's an option as well. A by-preference basis though. A rewrite directive should work to proxy-pass that in the correct format that is expected by the /embed or /v page (e.g. /v/(.*) => /v?name=$1). Basically only a rewrite in Nginx communication with the local lolisafe service. Clients will not need to be forwarded, so they will still see the URL being the shortened format from their end.

I basically already do something similar for local lolisafe installations (having Nginx rewrite localhost/lolisafe(/.*) requests into $1 when proxy-passing).

BobbyWibowo commented 4 years ago

Essentially what it would do in theory is convert the video format on demand when a file is requested, to be playable in the browser.

I see, cool stuff. I don't think I'll be interested in picking it up though. Mainly cause I don't see myself using it personally.

I host fiery.me on a pretty cheap and old server rack. Any processing-intensive tasks are its bane. My server is already struggling with ~2k active users, assuming all 2k attempt to stream a bunch of different video files at around the same time. There is none of that fancy transcoding in there, but it already struggles, lawl.

camjac251 commented 4 years ago

Oh wow they banned you? 23TB is quite a lot of bandwidth. My plan is just to use CloudFlare for DNS and hopefully BunnyCDN mirroring a Wasabi endpoint. I've been using rclone for mounting a cloud drive as a local folder for another upload script so I might do the same if S3 can't be supported.

If the embed page is served through node, it could make HLS streams easier. I did a bit of searching for node modules. I found these that might help

VideoJS supports streams https://www.npmjs.com/package/@videojs/http-streaming and has a quality selector plugin https://www.npmjs.com/package/videojs-max-quality-selector

Streams could be handled on-demand or possibly always available (would use less cpu but more disk).

https://github.com/dcollien/mp4filechecker https://www.npmjs.com/package/stream-transcoder-2 https://www.npmjs.com/package/hls-server#producing-streams

BobbyWibowo commented 4 years ago

Oh wow they banned you? 23TB is quite a lot of bandwidth.

Still ramping up ~1TiB per day till date. Also it was just the domain being blacklisted I think. My account had 2 domains in it, but the other one was left alone, lawl. I could probably make an appeal and promise not to use the Cache Everything rule abusively, but I just couldn't be bothered. My server didn't have bandwidth limit to begin with. But yeah, it's struggling during peak traffic.

samstarnes commented 4 years ago

Not sure if this would help but it's somewhat relevant. I also wanted a feature like this for my site for pictures and videos however some of the elements I've uploaded to LS have been used in several pages and it breaks a bunch. Probably due to my poorly coded rewrites in nginx and the URL shortener conflicting.

This example however could easily be adjusted for mp4/webm and the like using videojs or converting to HLS. I was particularly interested in this nginx-vod-module for doing it the HLS way. I'd prefer to use HLS since it seems WebKit browsers want to load the entire video instead of the MOOV atom first.

But this also could be adjusted with the -movflags +faststart flags using ffmpeg and you could avoid using HLS altogether. It'd take a little extra processing to change videos on upload but could potentially be a better option. I'm not sure how much processing it takes for HLS vs. a one time edit of uploaded files. But as for a test I wrote this. It isn't great but it kind of did what I wanted? I could be totally going the wrong way about all of this. I set this problem to the side for a later date. Using lite-url for the URL parsing. Example.

<style>
html{background:#0e0e0e;}
.child{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);}
</style>
<script src="js/lite-url-min.js"></script>
<script>
var url = 'https://0x0.la/u/XuoL0lR.png';
var parsed = new liteURL(url);
file = JSON.stringify(parsed.pathname);
result = file.substring(1, file.length-1);
document.write('<div class="child"><img src="https://0x0.la' + result + '"></div>');
</script>
BobbyWibowo commented 4 years ago

Using lite-url for the URL parsing.

Since I was thinking of using name?= query or something, I was thinking a short solution like this would've been more preferable instead of an entire library (although the one you linked is pretty light already). URLSearchParams would've been great if not for the fact that it doesn't work on IE, among other things.

But yeah, that seems about right. I was also thinking of using either pages or views directory (the former works with plain old HTML, the latter uses Nunjucks), as they'll automatically be served without the .html extension.

Something is clearly wrong with your setup if you really need that for images however, lawl.

samstarnes commented 4 years ago

Haha there's nothing wrong with the displaying of images. I'll be honest, it was more of a way I could poorly implement ads. Just a single ad that would generate revenue from memes or videos so I could donate to Pitu for the creation and yourself for consistent updates. Not only that but I'd prefer dark mode since default viewing for some browsers have white backgrounds when viewing images and I'd rather not blind users.

The name?= parameter would work but I'd like the URL to remain "clean." Somehow to handle everything server side. It's probably asking for too much and I'll probably bite the bullet and end up doing what Zero Width Shortener does.

BobbyWibowo commented 4 years ago

The name?= parameter would work but I'd like the URL to remain "clean."

Then imo could've opted with location.pathname instead, cause built-in: https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname At least your previous code example didn't use the lib for anything else other than that.

I just didn't particularly favor the idea because there was no way to make that work by the current pages or views system (i.e. can't just pop in a new page file). I'd instead need to go with a new route like the one we have for album public pages (routes/album.js).

Granted, that wouldn't really increase the difficulty either.

I just liked the idea of using Nunjucks since the page will be cached on production environment (though it's probably just a few ms differences, so I'm not sure why I care that much). I also find delegating the URL parsing to client-side JS to be a key element, since videojs wouldn't work in browsers that disabled JS either (and ads in your case).

And finally, doing a rewrite from Nginx like the one I mentioned a few replies back, should work either way.

however some of the elements I've uploaded to LS have been used in several pages and it breaks a bunch. ... I'll be honest, it was more of a way I could poorly implement ads.

What's your current plan to implement that btw?

Other revenue-first image hosters that I've came across would pretty much disable hotlinking the images. Or they'll suggest embedding thumbnails, which can be hotlinked, that are then linked to the ads-filled pages. So basically no sharing in Discord for example. Hosters like us that allow hotlinking have the benefit of Discord being able to proxy the images for previews, etc. But then, allowing hotlinking sorta pretty much prevents revenue by a lot.

camjac251 commented 4 years ago

I think the thumbnail script might be adaptable for HLS streams. each video would have video parts and a master playlist (.m3u8) that in plaintext reads out a list of urls for each part on a new line. Using fluent-ffmpeg it can be possible, maybe optional? https://www.npmjs.com/package/hls-server#producing-streams has an example about doing it with fluent-ffmpeg

And additional info here https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/issues/819 and another example https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/blob/master/examples/livertmp2hls.js

I would open a new issue but HLS streams wouldn't make much sense without the initial embedded video/audio/image viewer.

samstarnes commented 4 years ago

What's your current plan to implement that btw?

My original thought was Gfycat's implementation but they convert everything to mp4 or webm for view and then something like Discord downloads the video. Then I thought converting it to base64 and somehow embedding but remembered despite it being base64 you'd attach the file and upload it anyway so that too wouldn't work. There's no way around it.

The more I look into this, the more I realize the impossibility of the task. Adding an additional random (and maybe invisible) token to the end of a parameter with nginx rewrites may be the best option. Hotlinking is obviously a problem and I'd either 1) disallow all hotlinking altogether [not doing that] 2) accept the fact not all files could earn revenue.

Using fluent-ffmpeg it can be possible, maybe optional?

I think this would be a great idea. Had no idea it existed.

BobbyWibowo commented 3 years ago

It's pretty barebone, but it works for now I think: MP3 https://safe.fiery.me/v/jJNVK.mp3 FLAC https://safe.fiery.me/v/rv5ZA.flac MP4 https://safe.fiery.me/v/Xk7Db.mp4 Also accessible with https://safe.fiery.me/player/id (e.g. https://safe.fiery.me/player/Xk7Db.mp4). And being barebone of course means there are no additional support for other formats like MKV and all those memes.

Audio files have waveform, tho it seems the waveform itself will only appear after the entire audio file have been loaded.

Everything except routing is done in client-side JS file. Routing is done server-side by lolisafe, so no need to do nginx rewrite memes.

Also updated the Display preview modal for media files in Dashboard, to show link to the embedded player instead of the good ol' "Load original" (that will instead only apply to images now). Media files as in including audio files too of course.

Also haven't tested WAV files. They should theoretically work as long as the server sets its Content-Type header with audio/wave or any of its equivalent, and if the browser is actually capable of playing them of course (doc: https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers#WAVE_WAV).

FLAC files in particular weren't being set as audio/flac by my server, as it wasn't even there in this file https://github.com/nginx/nginx/blob/master/conf/mime.types, but works fine after manually adding it to my mime.types file. That's something to remember I suppose.