Load(); $video_path = APP_ROOT . "/usercontent/videos/$id/video.mp4"; } else { // not signed in $rand = rand(1, 47); $video_path = APP_ROOT . "/usercontent/fake_videos/$rand/video.mp4"; } CORSHelper(); Logger::Debug($video_path); if (file_exists($video_path)) { $mime_types = new MimeTypes(); $video_contents = file_get_contents($video_path); $video_size = filesize($video_path); $mime = $mime_types->getMimeType(pathinfo($video_path, PATHINFO_EXTENSION)); $response = response(); $response->header("Content-Type: $mime"); $response->header("Content-Length: $video_size"); $response->header("Cache-Control: no-cache"); return $video_contents; } else { throw new VideoNotFoundException($id, 404); } } public static function getInfo(string $id): string { $signed_in = signed_in(request()); if(!$signed_in) { $data = []; $titles = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeTitles")); $descriptions = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeDescriptions")); $usernames = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeUsernames")); for($i = 0; $i < rand(1, 13); $i++) { $data[] = [ "id" => rand(0, 203222), "title" => $titles[rand(0, count($titles) - 1)], "description" => $descriptions[rand(0, count($descriptions) - 1)], "likes" => rand(0, 48573), "dislikes" => rand(0, 202), "comments" => rand(0, 2029), "shares" => rand(15, 2321), "author" => [ "id" => rand(2, 2020), "username" => $usernames[rand(0, count($usernames) - 1)], "verified" => rand(1, 10) > 8 ], ]; } CORSHelper(); return api_json($data); } $video_information = new Video($id); $video_information->Load(); $author_information = new Account($video_information->author_id); $author_information->Load(); CORSHelper(); return api_json([ "id" => $video_information->id, "title" => $video_information->title, "description" => $video_information->description, "likes" => $video_information->likes, "dislikes" => $video_information->dislikes, "comments" => $video_information->comments, "shares" => $video_information->shares, "author" => [ "id" => $author_information->id, "verified" => $author_information->verified, "username" => $author_information->username ], ]); } public static function search(): string { $signed_in = signed_in(request()); if(!$signed_in) { $data = []; $titles = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeTitles")); $descriptions = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeDescriptions")); $usernames = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeUsernames")); for($i = 0; $i < rand(1, 13); $i++) { $data[] = [ "id" => rand(0, 203222), "title" => $titles[rand(0, count($titles) - 1)], "description" => $descriptions[rand(0, count($descriptions) - 1)], "likes" => rand(0, 48573), "dislikes" => rand(0, 202), "comments" => rand(0, 2029), "shares" => rand(15, 2321), "author" => [ "id" => rand(2, 2020), "username" => $usernames[rand(0, count($usernames) - 1)], "verified" => rand(1, 10) > 8 ], ]; } CORSHelper(); return api_json($data); } $query = input("query"); $video = new Video(title: $query); $videos = $video->LoadMany(); $data = []; foreach ($videos as $vid) { $account = new Account(id: $vid["author_id"]); $account->Load(); $data[] = [ "id" => $vid["id"], "title" => $vid["title"], "description" => $vid["description"], "likes" => $vid["likes"], "dislikes" => $vid["dislikes"], "comments" => Hajeebtok::$Database->Single("SELECT COUNT(*) FROM comments WHERE video_id = :id", ["id" => $vid["id"]]), "shares" => Hajeebtok::$Database->Single("SELECT COUNT(*) FROM messages INNER JOIN videos ON messages.video_id = videos.id WHERE videos.id = :id", ["id" => $vid["id"]]), "author" => [ "id" => $account->id, "verified" => $account->verified, "username" => $account->username ], ]; } CORSHelper(); return api_json($data); } public static function getThumbnail(string $id): string { $signed_in = signed_in(request()); if ($signed_in) { // Signed in //$video_information = new Video($id); //$video_information->Load(); $seconds = 2; $video_path = APP_ROOT . "/usercontent/videos/$id/video.mp4"; $frame_path = APP_ROOT . "/usercontent/videos/$id/thumbnail.png"; } else { // not signed in $rand = rand(1, 47); $seconds = 6; $video_path = APP_ROOT . "/usercontent/fake_videos/$rand/video.mp4"; $frame_path = APP_ROOT . "/usercontent/fake_videos/$rand/thumbnail.png"; } $mime_types = new MimeTypes(); if (!file_exists($video_path)) throw new VideoNotFoundException($id, 404); if (!file_exists($frame_path)) { Logger::Debug("Generating thumbnail for video $id"); $ffmpeg = FFMpeg\FFMpeg::create(); $video = $ffmpeg->open($video_path); $frame = $video->frame(FFMpeg\Coordinate\TimeCode::fromSeconds($seconds)); $frame->save($frame_path); } CORSHelper(); $mime = $mime_types->getMimeType(pathinfo($frame_path, PATHINFO_EXTENSION)); $response = response(); $response->header("Content-Type: $mime"); $response->header("Cache-Control: max-age=no-cache"); return file_get_contents($frame_path); } public static function getFeed(): string { $offset = intval(input("offset") ?? "0"); $limit = intval(Hajeebtok::$Config->GetByDotKey("Service.VideoFeedLimit")); $signed_in = signed_in(request()); if (!$signed_in) { $titles = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeTitles")); $descriptions = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeDescriptions")); $usernames = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeUsernames")); $feed = []; for($i = 0; $i < $limit; $i++) { $feed[] = [ "id" => rand(1, 10000), "title" => $titles[rand(0, count($titles) - 1)], "description" => $descriptions[rand(0, count($descriptions) - 1)], "likes" => rand(20000, 37020), "dislikes" => rand(2, 12343), "comments" => rand(2, 1029), "shares" => rand(2, 200000), "author" => [ "id" => rand(2, 59), "verified" => rand(1, 10) > 8, "username" => $usernames[rand(0, count($usernames) - 1)], ], ]; } CORSHelper(); return api_json($feed); } $id = get_token_id(request()); $feed = []; $videos = Hajeebtok::$Database->Query("SELECT * FROM videos WHERE id NOT IN (SELECT video_id FROM views WHERE account_id = :account_id) ORDER BY id DESC LIMIT :limit OFFSET :offset", ["account_id" => $id, "offset" => $offset, "limit" => $limit]); $vid_count = count($videos); if ($vid_count < $limit) { $fallback_videos = Hajeebtok::$Database->Query("SELECT * FROM videos ORDER BY id DESC LIMIT :remainder OFFSET :offset", ["offset" => $offset + $vid_count, "remainder" => $limit - $vid_count]); $videos = array_merge($videos, $fallback_videos); } foreach ($videos as $video) { $account = new Account($video["author_id"]); $account->Load(); $feed[] = [ "id" => $video["id"], "title" => $video["title"], "description" => $video["description"], "likes" => $video["likes"], "dislikes" => $video["dislikes"], "comments" => Hajeebtok::$Database->Single("SELECT COUNT(*) FROM comments WHERE video_id = :id", ["id" => $video["id"]]), "shares" => Hajeebtok::$Database->Single("SELECT COUNT(*) FROM messages INNER JOIN videos ON messages.video_id = videos.id WHERE videos.id = :id", ["id" => $video["id"]]), "author" => [ "id" => $video["author_id"], "verified" => $account->verified, "username" => $account->username, ], ]; } CORSHelper(); return api_json($feed); } public static function uploadVideo(): string { if (!signed_in(request())) throw new UnauthenticatedException(0, 401); $video_file = input()->file("video"); $title = input("title"); $description = input("description"); $author_id = get_token_id(request()); if (empty($video_file) || empty($title) || empty($author_id)) throw new InvalidRequestException(0, 400); if (!file_exists($video_file->getTmpName())) throw new InvalidRequestException(400); // save database object $video = new Video(title: $title, description: $description, author_id: $author_id); $video->Save(); // Save file $video_folder = APP_ROOT . "/usercontent/videos/" . $video->id; // check video file type $mimey = new MimeTypes(); $mime_type = $mimey->getMimeType($video_file->getExtension()); if ($mime_type != "video/mp4" && $mime_type != "video/ogg" && $mime_type != "video/webm" && $mime_type != "video/x-matroska") { $video->Delete(); throw new InvalidVideoException($video->id, 415); // media type unsupported } // move video mkdir($video_folder); Logger::Debug($video_file->getFilename() . " -> " . $video_folder . "/video.mp4"); $video_file->move($video_folder . "/video.mp4"); CORSHelper(); return api_json([ "id" => $video->id, "title" => $video->title, "description" => $video->description, "likes" => $video->likes, "dislikes" => $video->dislikes, "comments" => 0, "shares" => 0, "author" => [ "id" => $author_id, ] ]); } public static function getExplore() { $video_num = intval(input("video") ?? "0"); $offset = intval(input("offset") ?? "0"); $limit = intval(Hajeebtok::$Config->GetByDotKey("Service.VideoFeedLimit")); $signed_in = signed_in(request()); if (!$signed_in) { $feed = []; for($i = 0; $i < $limit; $i++) { $titles = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeTitles")); $descriptions = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeDescriptions")); $usernames = explode(",", Hajeebtok::$Config->GetByDotKey("Service.FakeUsernames")); $feed[] = [ "id" => rand(1, 100000), "title" => $titles[rand(0, count($titles) - 1)], "description" => $descriptions[rand(0, count($descriptions) - 1)], "likes" => rand(20000, 37020), "dislikes" => rand(2, 12343), "comments" => rand(2, 1029), "shares" => rand(2, 200000), "author" => [ "id" => rand(2, 59), "verified" => rand(1, 10) > 8, "username" => $usernames[rand(0, count($usernames) - 1)], ], ]; } CORSHelper(); return api_json($feed); } $id = get_token_id(request()); $videos = Hajeebtok::$Database->Query("SELECT * FROM videos ORDER BY RAND() LIMIT :limit OFFSET :offset", ["offset" => $offset, "limit" => $limit]); $feed = []; $valid_video = false; foreach ($videos as $video) { if ($video_num === 0) $valid_video = true; if ($video["id"] == $video_num) $valid_video = true; Logger::Debug($video["id"] . " -> " . $video["title"]); if (!$valid_video) continue; $account = new Account($video["author_id"]); $account->Load(); $feed[] = [ "id" => $video["id"], "title" => $video["title"], "description" => $video["description"], "likes" => $video["likes"], "dislikes" => $video["dislikes"], "comments" => Hajeebtok::$Database->Single("SELECT COUNT(*) FROM comments WHERE video_id = :id", ["id" => $video["id"]]), "shares" => Hajeebtok::$Database->Single("SELECT COUNT(*) FROM messages INNER JOIN videos ON messages.video_id = videos.id WHERE videos.id = :id", ["id" => $video["id"]]), "author" => [ "id" => $video["author_id"], "verified" => $account->verified, "username" => $account->username, ], ]; } CORSHelper(); return api_json($feed); } public static function RegisterRoutes(): void { SimpleRouter::group([ "prefix" => "/video", ], function () { SimpleRouter::get("/{id}/get", [VideoController::class, "getVideo"]); SimpleRouter::get("/{id}/info", [VideoController::class, "getInfo"]); SimpleRouter::get("/{id}/thumbnail", [VideoController::class, "getThumbnail"]); SimpleRouter::get("/feed", [VideoController::class, "getFeed"]); SimpleRouter::get("/explore", [VideoController::class, "getExplore"]); SimpleRouter::post("/upload", [VideoController::class, "uploadVideo"]); SimpleRouter::post("/search", [VideoController::class, "search"]); SimpleRouter::options("/{id}/get", "CORSHelper"); SimpleRouter::options("/{id}/info", "CORSHelper"); SimpleRouter::options("/{id}/thumbnail", "CORSHelper"); SimpleRouter::options("/upload", "CORSHelper"); SimpleRouter::options("/search", "CORSHelper"); SimpleRouter::options("/feed", "CORSHelper"); SimpleRouter::options("/explore", "CORSHelper"); }); } }