archive-ccc8458/video/api/upload.php 3.4 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
<?php
declare(strict_types=1);
header('Content-Type: application/json');

require_once __DIR__ . '/../lib/auth.php';
require_once __DIR__ . '/../lib/hash.php';

require_https();

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['error' => 'method not allowed']);
    exit;
}

if (!rate_limit_check()) {
    http_response_code(429);
    echo json_encode(['error' => 'too many failed attempts, try again later']);
    exit;
}

$pw = password_from_request();
if ($pw === null || $pw === '' || !verify_password($pw)) {
    rate_limit_fail();
    http_response_code(401);
    echo json_encode(['error' => 'invalid password']);
    exit;
}
rate_limit_reset();

if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
    http_response_code(400);
    echo json_encode([
        'error' => 'upload failed',
        'code'  => $_FILES['file']['error'] ?? 'no file',
    ]);
    exit;
}

$f = $_FILES['file'];
if ($f['size'] > MAX_UPLOAD_BYTES) {
    http_response_code(413);
    echo json_encode(['error' => 'file too large']);
    exit;
}

$mime = (new finfo(FILEINFO_MIME_TYPE))->file($f['tmp_name']) ?: '';
if (!isset(ALLOWED_TYPES[$mime])) {
    http_response_code(415);
    echo json_encode(['error' => 'unsupported video type', 'detected' => $mime]);
    exit;
}
$ext = ALLOWED_TYPES[$mime];

if (!is_dir(VIDEO_DIR)) @mkdir(VIDEO_DIR, 0755, true);

do {
    $hash = generate_video_hash();
    $dest = VIDEO_DIR . '/' . $hash . '.' . $ext;
} while (file_exists($dest));

if (!move_uploaded_file($f['tmp_name'], $dest)) {
    http_response_code(500);
    echo json_encode(['error' => 'could not save file']);
    exit;
}
@chmod($dest, 0644);

// Optional thumbnail. If present-but-invalid, fail the whole upload (and
// delete the video we just saved) so the client knows.
$thumbUrl = null;
if (isset($_FILES['thumbnail']) && $_FILES['thumbnail']['error'] !== UPLOAD_ERR_NO_FILE) {
    $t = $_FILES['thumbnail'];
    if ($t['error'] !== UPLOAD_ERR_OK) {
        @unlink($dest);
        http_response_code(400);
        echo json_encode(['error' => 'thumbnail upload failed', 'code' => $t['error']]);
        exit;
    }
    if ($t['size'] > MAX_THUMB_BYTES) {
        @unlink($dest);
        http_response_code(413);
        echo json_encode(['error' => 'thumbnail too large']);
        exit;
    }
    $tMime = (new finfo(FILEINFO_MIME_TYPE))->file($t['tmp_name']) ?: '';
    if (!isset(ALLOWED_THUMB_TYPES[$tMime])) {
        @unlink($dest);
        http_response_code(415);
        echo json_encode(['error' => 'unsupported thumbnail type', 'detected' => $tMime]);
        exit;
    }
    $tExt = ALLOWED_THUMB_TYPES[$tMime];
    $tDest = VIDEO_DIR . '/' . $hash . '.' . $tExt;
    if (!move_uploaded_file($t['tmp_name'], $tDest)) {
        @unlink($dest);
        http_response_code(500);
        echo json_encode(['error' => 'could not save thumbnail']);
        exit;
    }
    @chmod($tDest, 0644);
    $thumbUrl = SITE_BASE . VIDEO_URL . '/' . $hash . '.' . $tExt;
}

$unlisted = filter_var($_POST['unlisted'] ?? false, FILTER_VALIDATE_BOOLEAN);
set_unlisted($hash, $unlisted);

echo json_encode([
    'hash'      => $hash,
    'url'       => SITE_BASE . '/video/' . $hash,
    'embed'     => SITE_BASE . '/video/' . $hash . '/embed',
    'file'      => SITE_BASE . VIDEO_URL . '/' . $hash . '.' . $ext,
    'thumbnail' => $thumbUrl,
    'unlisted'  => $unlisted,
]);