Initial commit

This commit is contained in:
2025-10-02 13:07:50 +02:00
commit 8a0e1faf75
10 changed files with 323 additions and 0 deletions

10
.editorconfig Normal file
View 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
View File

@ -0,0 +1,5 @@
# Media files
/anime
# Remuxed files
/video

36
api/info.php Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}
?>