Initial commit
This commit is contained in:
10
.editorconfig
Normal file
10
.editorconfig
Normal file
@ -0,0 +1,10 @@
|
||||
# Top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Use same style for all files
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# Media files
|
||||
/anime
|
||||
|
||||
# Remuxed files
|
||||
/video
|
||||
36
api/info.php
Normal file
36
api/info.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
chdir("..");
|
||||
require_once "src/util.php";
|
||||
|
||||
$query = $_SERVER["QUERY_STRING"];
|
||||
parse_str($query, $param);
|
||||
|
||||
$path = urlsafe_b64decode($param["file"]);
|
||||
$path = "./" . $path;
|
||||
$path = str_replace("..", "", $path);
|
||||
|
||||
$descSpec = [
|
||||
0 => array("pipe", "r"),
|
||||
1 => array("pipe", "w"),
|
||||
2 => array("pipe", "w"),
|
||||
];
|
||||
$proc = proc_open(["ffprobe", "-show_streams", "-of", "json", $path], $descSpec, $pipes);
|
||||
|
||||
fclose($pipes[0]);
|
||||
|
||||
$stdout = stream_get_contents($pipes[1]);
|
||||
$stderr = stream_get_contents($pipes[2]);
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
|
||||
$code = proc_close($proc);
|
||||
|
||||
if ($code)
|
||||
{
|
||||
header("Content-Type: text/plain");
|
||||
echo $stderr;
|
||||
} else {
|
||||
header("Content-Type: application/json");
|
||||
echo $stdout;
|
||||
}
|
||||
?>
|
||||
35
api/subtitles.php
Normal file
35
api/subtitles.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
chdir("..");
|
||||
require_once "src/util.php";
|
||||
require_once "src/ffmpeg.php";
|
||||
|
||||
$query = $_SERVER["QUERY_STRING"];
|
||||
parse_str($query, $param);
|
||||
|
||||
$path = urlsafe_b64decode($param["file"]);
|
||||
$path = "./" . $path;
|
||||
$path = str_replace("..", "", $path);
|
||||
|
||||
$index = $param["s"] ?? 0;
|
||||
|
||||
if (!is_dir("video/"))
|
||||
mkdir("video/");
|
||||
|
||||
$err = ffmpeg($path, "video/$query.vtt", ["-map", "0:s:$index"]);
|
||||
|
||||
if ($err)
|
||||
{
|
||||
header("Content-Type: text/plain");
|
||||
echo $err;
|
||||
} else {
|
||||
header("Location: ../video/$query.vtt", true, 302);
|
||||
}
|
||||
|
||||
// Remove old videos and subtitles
|
||||
foreach (scandir("video/") as $file)
|
||||
{
|
||||
$atime = fileatime("video/$file");
|
||||
if (time() - $atime > 3600)
|
||||
unlink("video/$file");
|
||||
}
|
||||
?>
|
||||
36
api/video.php
Normal file
36
api/video.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
chdir("..");
|
||||
require_once "src/util.php";
|
||||
require_once "src/ffmpeg.php";
|
||||
|
||||
$query = $_SERVER["QUERY_STRING"];
|
||||
parse_str($query, $param);
|
||||
|
||||
$path = urlsafe_b64decode($param["file"]);
|
||||
$path = "./" . $path;
|
||||
$path = str_replace("..", "", $path);
|
||||
|
||||
$video = $param["v"] ?? 0;
|
||||
$audio = $param["a"] ?? 0;
|
||||
|
||||
if (!is_dir("video/"))
|
||||
mkdir("video/");
|
||||
|
||||
$err = ffmpeg($path, "video/$query.mp4", ["-map", "0:v:$video", "-map", "0:a:$audio", "-c", "copy"]);
|
||||
|
||||
if ($err)
|
||||
{
|
||||
header("Content-Type: text/plain");
|
||||
echo $err;
|
||||
} else {
|
||||
header("Location: ../video/$query.mp4", true, 302);
|
||||
}
|
||||
|
||||
// Remove old videos and subtitles
|
||||
foreach (scandir("video/") as $file)
|
||||
{
|
||||
$atime = fileatime("video/$file");
|
||||
if (time() - $atime > 3600)
|
||||
unlink("video/$file");
|
||||
}
|
||||
?>
|
||||
34
index.php
Normal file
34
index.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
require_once "src/util.php";
|
||||
|
||||
$files = [];
|
||||
$dirIter = new RecursiveDirectoryIterator("anime");
|
||||
$iter = new RecursiveIteratorIterator($dirIter);
|
||||
|
||||
foreach ($iter as $entry)
|
||||
{
|
||||
if ($entry->isFile())
|
||||
{
|
||||
$files[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Anime</title>
|
||||
</head>
|
||||
<body>
|
||||
<ul>
|
||||
<?php
|
||||
foreach ($files as $file)
|
||||
{
|
||||
$path = $file->getPathName();
|
||||
$id = urlsafe_b64encode($path);
|
||||
echo "<li><a href=\"player.html#$id\">$path</a></li>";
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
70
js/player.js
Normal file
70
js/player.js
Normal file
@ -0,0 +1,70 @@
|
||||
$(async function()
|
||||
{
|
||||
const hash = location.hash.slice(1);
|
||||
|
||||
$("#status").text("Fetching media info...");
|
||||
let res = await fetch(`api/info.php?file=${hash}`);
|
||||
|
||||
$("#status").text("Parsing response data...");
|
||||
let media = await res.json();
|
||||
|
||||
$("#status").text("Constructing media player...");
|
||||
const audio = [];
|
||||
const text = [];
|
||||
|
||||
for (const stream of media.streams) switch (stream.codec_type)
|
||||
{
|
||||
case "audio":
|
||||
audio.push({
|
||||
label: stream.tags.title,
|
||||
language: stream.tags.language,
|
||||
});
|
||||
break;
|
||||
case "subtitle":
|
||||
text.push({
|
||||
label: stream.tags.title,
|
||||
language: stream.tags.language,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$("#vid").append($("<source>", {type: "video/mp4", src: `api/video.php?file=${hash}`}));
|
||||
for (let i = 0; i < text.length; i++)
|
||||
$("#vid").append($("<track>", {
|
||||
kind: "captions",
|
||||
label: text[i].label,
|
||||
srclang: text[i].language,
|
||||
src: `api/subtitles.php?file=${hash}&s=${i}`,
|
||||
}));
|
||||
|
||||
$("#status").text("Building video.js player...");
|
||||
const player = videojs("vid");
|
||||
let audioTrackList = player.audioTracks();
|
||||
|
||||
for (let i = 0; i < audio.length; i++)
|
||||
audioTrackList.addTrack(new videojs.AudioTrack({
|
||||
kind: "translation",
|
||||
label: audio[i].label,
|
||||
language: audio[i].language,
|
||||
}));
|
||||
|
||||
audioTrackList.addEventListener('change', function() {
|
||||
for (var i = 0; i < audioTrackList.length; i++) {
|
||||
var track = audioTrackList[i];
|
||||
|
||||
if (track.enabled) {
|
||||
let time = player.currentTime()
|
||||
player.src({type: "video/mp4", src: `api/video.php?file=${hash}&a=${i}`})
|
||||
player.ready(function(){
|
||||
player.currentTime(time);
|
||||
player.play();
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#status").remove();
|
||||
});
|
||||
14
player.html
Normal file
14
player.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="https://lesbian.ddns.net/Tomas/-/files/lib/web/video-js.css" rel="stylesheet" />
|
||||
<script src="https://lesbian.ddns.net/Tomas/-/files/lib/web/video.min.js"></script>
|
||||
<script src="https://lesbian.ddns.net/Tomas/-/files/lib/web/jquery.min.js"></script>
|
||||
<script src="js/player.js"></script>
|
||||
<title>Anime - Player</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2 id="status">"Video Player"</h2>
|
||||
<video id="vid" class="video-js" controls width="480" height="360"/>
|
||||
</body>
|
||||
</html>
|
||||
44
src/ffmpeg.php
Normal file
44
src/ffmpeg.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
require_once "src/util.php";
|
||||
|
||||
|
||||
function ffmpeg($input, $output, $params)
|
||||
{
|
||||
if (!is_file($input))
|
||||
return "Could not open $input";
|
||||
|
||||
if (is_file($output))
|
||||
{
|
||||
return false;
|
||||
} else {
|
||||
$descSpec = [
|
||||
0 => array("pipe", "r"),
|
||||
1 => array("pipe", "w"),
|
||||
2 => array("pipe", "w"),
|
||||
];
|
||||
$proc = proc_open(array_merge(["ffmpeg", "-i", $input], $params, [$output]), $descSpec, $pipes);
|
||||
|
||||
if (is_resource($proc))
|
||||
{
|
||||
fclose($pipes[0]);
|
||||
|
||||
$stderr = stream_get_contents($pipes[2]);
|
||||
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
$failed = proc_close($proc);
|
||||
|
||||
if ($failed)
|
||||
{
|
||||
if (is_file($output))
|
||||
unlink($output);
|
||||
return $stderr;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return "Failed to initialize ffmpeg";
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
39
src/util.php
Normal file
39
src/util.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
function urlsafe_b64encode($string)
|
||||
{
|
||||
$data = base64_encode($string);
|
||||
$data = str_replace(array('+','/','='),array('-','_',''),$data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
function urlsafe_b64decode($string)
|
||||
{
|
||||
$data = str_replace(array('-','_'),array('+','/'),$string);
|
||||
$mod4 = strlen($data) % 4;
|
||||
if ($mod4) {
|
||||
$data .= substr('====', $mod4);
|
||||
}
|
||||
return base64_decode($data);
|
||||
}
|
||||
|
||||
function sendfile($path)
|
||||
{
|
||||
$file = fopen($path, "rb");
|
||||
|
||||
header("Content-Length: " . filesize($path));
|
||||
|
||||
ob_end_flush();
|
||||
flush();
|
||||
|
||||
while (!feof($file))
|
||||
{
|
||||
$chunk = fread($file, 1024);
|
||||
echo $chunk;
|
||||
flush();
|
||||
}
|
||||
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user