From ebb6c70c0fc26c6fc855a68689771215dcdf6221 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hi=E1=BA=BFu=20Star?=
<66201416+huzely@users.noreply.github.com>
Date: Mon, 9 Mar 2026 16:53:28 +0700
Subject: [PATCH] Build complete AVSTube PHP/MySQL video sharing platform
---
README.md | 86 +++++++++++++------
admin/ads.php | 37 ++++++++
admin/announcements.php | 25 ++++++
admin/auth.php | 3 +
admin/dashboard.php | 39 +++++++++
admin/index.php | 4 +
admin/login.php | 37 ++++++++
admin/logout.php | 5 ++
admin/partials.php | 25 ++++++
admin/popup.php | 32 +++++++
admin/settings.php | 26 ++++++
admin/videos.php | 97 +++++++++++++++++++++
assets/css/style.css | 184 ++++++++++++++++++++++++++++++++++++++++
assets/js/main.js | 29 +++++++
comment.php | 21 +++++
config.php | 50 +++++++++++
index.php | 146 +++++++++++++++++++++++++++++++
search.php | 3 +
sql/avstube.sql | 70 +++++++++++++++
video.php | 85 +++++++++++++++++++
20 files changed, 978 insertions(+), 26 deletions(-)
create mode 100644 admin/ads.php
create mode 100644 admin/announcements.php
create mode 100644 admin/auth.php
create mode 100644 admin/dashboard.php
create mode 100644 admin/index.php
create mode 100644 admin/login.php
create mode 100644 admin/logout.php
create mode 100644 admin/partials.php
create mode 100644 admin/popup.php
create mode 100644 admin/settings.php
create mode 100644 admin/videos.php
create mode 100644 assets/css/style.css
create mode 100644 assets/js/main.js
create mode 100644 comment.php
create mode 100644 config.php
create mode 100644 index.php
create mode 100644 search.php
create mode 100644 sql/avstube.sql
create mode 100644 video.php
diff --git a/README.md b/README.md
index cdc16c5..7f79211 100644
--- a/README.md
+++ b/README.md
@@ -1,28 +1,62 @@
-# Decrypt
-### [+] Created By HTR-TECH (@***tahmid.rayat***)
-### [+] Disclaimer :
-***Decrypter is a tool to decrypt Encrypted Bash Scripts into a Readable Format.This Tool is created for Educational Purpose only.I am not responsible for any misuse of this tool.***
-
-
-
-### [+] Installation
-```apt update```
-
-```apt install git python2 -y```
-
-```git clone https://github.com/hax0rtahm1d/decrypt```
-
-```cd decrypt```
-
-```python2 dec.py```
-
-### Or, Use Single Command
-
-```
-apt update && apt install git python2 -y && git clone https://github.com/hax0rtahm1d/decrypt && cd decrypt && python2 dec.py
+# AVSTube
+
+Website chia sẻ video dùng **PHP 8 + MySQL + Bootstrap 5**.
+
+## 1) Cấu trúc thư mục
+
+```text
+/project (repo root)
+├── admin/
+│ ├── index.php
+│ ├── login.php
+│ ├── logout.php
+│ ├── dashboard.php
+│ ├── videos.php
+│ ├── ads.php
+│ ├── popup.php
+│ ├── announcements.php
+│ ├── settings.php
+│ ├── auth.php
+│ └── partials.php
+├── assets/
+│ ├── css/style.css
+│ └── js/main.js
+├── uploads/
+│ ├── thumbnails/
+│ └── videos/
+├── sql/avstube.sql
+├── config.php
+├── index.php
+├── video.php
+├── search.php
+├── comment.php
+└── README.md
```
-## [+] Find Me on :
-[](https://github.com/htr-tech)
-[](https://www.instagram.com/tahmid.rayat)
-[](https://m.me/tahmid.rayat.official)
+## 2) Cài đặt
+
+1. Tạo virtual host trỏ vào thư mục project.
+2. Tạo database và import file SQL:
+ ```bash
+ mysql -u root -p < sql/avstube.sql
+ ```
+3. Cập nhật DB trong `config.php` nếu khác mặc định.
+4. Cấp quyền ghi cho `uploads/`.
+5. Truy cập website tại `/index.php`.
+
+## 3) Tài khoản admin mặc định
+
+- Username: `admin`
+- Password: `admin2006`
+- URL: `/admin/login.php`
+
+## 4) Tính năng chính
+
+- Giao diện dark theo mẫu HTML yêu cầu (Bootstrap + Font Awesome).
+- Trang chủ grid video, hover scale, responsive.
+- Xem video hỗ trợ `embed` và `mp4`.
+- Bình luận theo video.
+- Tìm kiếm video theo tiêu đề.
+- Quản trị đầy đủ: dashboard/chart, video CRUD, ads, popup ads, announcements, settings.
+- Popup quảng cáo full màn hình, redirect khi click/X, ẩn vĩnh viễn bằng `localStorage`.
+- Prepared statements + validate input cơ bản.
diff --git a/admin/ads.php b/admin/ads.php
new file mode 100644
index 0000000..7659832
--- /dev/null
+++ b/admin/ads.php
@@ -0,0 +1,37 @@
+prepare('UPDATE ads SET status = IF(status=1,0,1) WHERE id=:id')->execute(['id' => $id]);
+ header('Location: ads.php');
+ exit;
+}
+if (isset($_GET['delete'])) {
+ $pdo->prepare('DELETE FROM ads WHERE id=:id')->execute(['id' => (int) $_GET['delete']]);
+ header('Location: ads.php');
+ exit;
+}
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ $type = in_array($_POST['type'] ?? '', ['banner', 'google'], true) ? $_POST['type'] : 'banner';
+ $image = trim($_POST['image'] ?? '');
+ $link = trim($_POST['link'] ?? '');
+ $pdo->prepare('INSERT INTO ads (type,image,link,status) VALUES (:type,:image,:link,1)')->execute(compact('type', 'image', 'link'));
+ header('Location: ads.php');
+ exit;
+}
+$ads = $pdo->query("SELECT * FROM ads WHERE type IN ('banner','google') ORDER BY id DESC")->fetchAll();
+adminHeader('Ads');
+?>
+
Ads
+
+| ID | Type | Status | Action |
+| = (int) $ad['id'] ?> | = e($ad['type']) ?> | = $ad['status'] ? 'ON' : 'OFF' ?> | Toggle Delete |
+
+
diff --git a/admin/announcements.php b/admin/announcements.php
new file mode 100644
index 0000000..17c58d4
--- /dev/null
+++ b/admin/announcements.php
@@ -0,0 +1,25 @@
+prepare('DELETE FROM announcements WHERE id=:id')->execute(['id' => (int) $_GET['delete']]);
+ header('Location: announcements.php');
+ exit;
+}
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ $title = trim($_POST['title'] ?? '');
+ $content = trim($_POST['content'] ?? '');
+ if ($title !== '' && $content !== '') {
+ $pdo->prepare('INSERT INTO announcements (title, content) VALUES (:title, :content)')->execute(compact('title', 'content'));
+ }
+ header('Location: announcements.php');
+ exit;
+}
+$rows = $pdo->query('SELECT * FROM announcements ORDER BY id DESC')->fetchAll();
+adminHeader('Announcements');
+?>
+Announcements
+
+| ID | Title | Content | |
|---|
| = (int)$r['id'] ?> | = e($r['title']) ?> | = e($r['content']) ?> | Delete |
+
diff --git a/admin/auth.php b/admin/auth.php
new file mode 100644
index 0000000..3b9d7b6
--- /dev/null
+++ b/admin/auth.php
@@ -0,0 +1,3 @@
+query('SELECT COUNT(*) FROM videos')->fetchColumn();
+$totalViews = (int) $pdo->query('SELECT COALESCE(SUM(views),0) FROM videos')->fetchColumn();
+$todayViews = (int) $pdo->query('SELECT COALESCE(SUM(daily_views),0) FROM video_stats WHERE view_date = CURDATE()')->fetchColumn();
+$topVideo = $pdo->query('SELECT title, views FROM videos ORDER BY views DESC LIMIT 1')->fetch();
+
+$chartRows = $pdo->query('SELECT view_date, COALESCE(SUM(daily_views),0) AS views FROM video_stats GROUP BY view_date ORDER BY view_date DESC LIMIT 7')->fetchAll();
+$labels = [];
+$values = [];
+foreach (array_reverse($chartRows) as $row) {
+ $labels[] = $row['view_date'];
+ $values[] = (int) $row['views'];
+}
+
+adminHeader('Dashboard');
+?>
+Dashboard
+
+
Total Video
= $totalVideos ?>
+
Total Views
= number_format($totalViews) ?>
+
Views Today
= number_format($todayViews) ?>
+
Top Video
= e($topVideo['title'] ?? 'N/A') ?>
+
+
+
Thống kê theo ngày
+
+
+
+
+
diff --git a/admin/index.php b/admin/index.php
new file mode 100644
index 0000000..5216136
--- /dev/null
+++ b/admin/index.php
@@ -0,0 +1,4 @@
+prepare('SELECT * FROM users WHERE username = :username LIMIT 1');
+ $stmt->execute(['username' => $username]);
+ $user = $stmt->fetch();
+
+ if ($user && password_verify($password, $user['password']) && $user['role'] === 'admin') {
+ $_SESSION['admin_id'] = $user['id'];
+ $_SESSION['admin_username'] = $user['username'];
+ header('Location: dashboard.php');
+ exit;
+ }
+ $error = 'Sai thông tin đăng nhập';
+}
+?>
+
+Admin Login
+
+
+
+AVSTube Admin
+
= e($error) ?>
+
+
diff --git a/admin/logout.php b/admin/logout.php
new file mode 100644
index 0000000..5000fd8
--- /dev/null
+++ b/admin/logout.php
@@ -0,0 +1,5 @@
+';
+ echo '' . e($title) . ' - AVSTube Admin';
+ echo '';
+ echo '';
+ echo '';
+ echo '';
+ echo '';
+}
+
+function adminFooter(): void
+{
+ echo '
';
+}
diff --git a/admin/popup.php b/admin/popup.php
new file mode 100644
index 0000000..48ec7c7
--- /dev/null
+++ b/admin/popup.php
@@ -0,0 +1,32 @@
+exec("DELETE FROM ads WHERE type='popup'");
+ $stmt = $pdo->prepare("INSERT INTO ads (type,image,link,status) VALUES ('popup',:image,:link,1)");
+ $stmt->execute(['image' => $image, 'link' => $links]);
+ header('Location: popup.php?saved=1');
+ exit;
+}
+
+$popup = $pdo->query("SELECT * FROM ads WHERE type='popup' ORDER BY id DESC LIMIT 1")->fetch();
+$links = array_values(array_filter(array_map('trim', explode(',', (string) ($popup['link'] ?? '')))));
+adminHeader('Popup Ads');
+?>
+Popup Ads
+Saved
+
+
diff --git a/admin/settings.php b/admin/settings.php
new file mode 100644
index 0000000..6b3262a
--- /dev/null
+++ b/admin/settings.php
@@ -0,0 +1,26 @@
+ $logo, 'site_banner' => $banner] as $key => $value) {
+ $stmt = $pdo->prepare('INSERT INTO settings (setting_key, setting_value) VALUES (:k, :v) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)');
+ $stmt->execute(['k' => $key, 'v' => $value]);
+ }
+ header('Location: settings.php?saved=1');
+ exit;
+}
+adminHeader('Settings');
+?>
+Settings
+Đã lưu cài đặt
+
+
diff --git a/admin/videos.php b/admin/videos.php
new file mode 100644
index 0000000..5e5816e
--- /dev/null
+++ b/admin/videos.php
@@ -0,0 +1,97 @@
+prepare('DELETE FROM videos WHERE id = :id');
+ $stmt->execute(['id' => $id]);
+ header('Location: videos.php');
+ exit;
+}
+
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ $id = (int) ($_POST['id'] ?? 0);
+ $title = trim($_POST['title'] ?? '');
+ $description = trim($_POST['description'] ?? '');
+ $type = $_POST['type'] === 'embed' ? 'embed' : 'mp4';
+ $videoUrl = trim($_POST['video_url'] ?? '');
+ $thumbnail = trim($_POST['thumbnail'] ?? '');
+ $duration = trim($_POST['duration'] ?? '00:00');
+
+ if (!empty($_FILES['mp4_file']['name']) && $_FILES['mp4_file']['error'] === UPLOAD_ERR_OK) {
+ $ext = strtolower(pathinfo($_FILES['mp4_file']['name'], PATHINFO_EXTENSION));
+ if ($ext === 'mp4') {
+ $name = uniqid('video_', true) . '.mp4';
+ $target = __DIR__ . '/../uploads/videos/' . $name;
+ move_uploaded_file($_FILES['mp4_file']['tmp_name'], $target);
+ $videoUrl = 'uploads/videos/' . $name;
+ }
+ }
+
+ if (!empty($_FILES['thumbnail_file']['name']) && $_FILES['thumbnail_file']['error'] === UPLOAD_ERR_OK) {
+ $ext = strtolower(pathinfo($_FILES['thumbnail_file']['name'], PATHINFO_EXTENSION));
+ if (in_array($ext, ['jpg', 'jpeg', 'png', 'webp'], true)) {
+ $name = uniqid('thumb_', true) . '.' . $ext;
+ $target = __DIR__ . '/../uploads/thumbnails/' . $name;
+ move_uploaded_file($_FILES['thumbnail_file']['tmp_name'], $target);
+ $thumbnail = 'uploads/thumbnails/' . $name;
+ }
+ }
+
+ if ($id > 0) {
+ $stmt = $pdo->prepare('UPDATE videos SET title=:title, description=:description, thumbnail=:thumbnail, video_url=:video_url, type=:type, duration=:duration WHERE id=:id');
+ $stmt->execute([
+ 'title' => $title,
+ 'description' => $description,
+ 'thumbnail' => $thumbnail,
+ 'video_url' => $videoUrl,
+ 'type' => $type,
+ 'duration' => $duration,
+ 'id' => $id,
+ ]);
+ } else {
+ $stmt = $pdo->prepare('INSERT INTO videos (title, description, thumbnail, video_url, type, duration) VALUES (:title, :description, :thumbnail, :video_url, :type, :duration)');
+ $stmt->execute([
+ 'title' => $title,
+ 'description' => $description,
+ 'thumbnail' => $thumbnail,
+ 'video_url' => $videoUrl,
+ 'type' => $type,
+ 'duration' => $duration,
+ ]);
+ }
+ header('Location: videos.php');
+ exit;
+}
+
+$edit = null;
+if (isset($_GET['edit'])) {
+ $stmt = $pdo->prepare('SELECT * FROM videos WHERE id = :id');
+ $stmt->execute(['id' => (int) $_GET['edit']]);
+ $edit = $stmt->fetch();
+}
+
+$videos = $pdo->query('SELECT * FROM videos ORDER BY id DESC')->fetchAll();
+adminHeader('Videos');
+?>
+Quản lý Video
+
+
+| ID | Title | Type | Views | Action |
+| = (int) $v['id'] ?> | = e($v['title']) ?> | = e($v['type']) ?> | = number_format((int) $v['views']) ?> |
+Edit Delete |
+
+
diff --git a/assets/css/style.css b/assets/css/style.css
new file mode 100644
index 0000000..22c2c54
--- /dev/null
+++ b/assets/css/style.css
@@ -0,0 +1,184 @@
+body {
+ background: #000;
+ color: #ddd;
+ font-family: Arial, Helvetica, sans-serif;
+ margin: 0;
+ padding: 0;
+}
+
+a {
+ color: #ddd;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #fff;
+}
+
+.header {
+ background: #111;
+ padding: 30px 20px;
+ text-align: center;
+ border-bottom: 2px solid #c00;
+}
+
+.header h1 {
+ color: #c00;
+ margin: 0;
+ font-size: 3.5rem;
+}
+
+.section {
+ padding: 60px 20px;
+}
+
+.video-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
+ gap: 20px;
+ margin-top: 30px;
+}
+
+.video-card {
+ background: #111;
+ border-radius: 10px;
+ overflow: hidden;
+ transition: all 0.3s;
+}
+
+.video-card:hover {
+ transform: scale(1.05);
+ box-shadow: 0 10px 30px rgba(200, 0, 0, 0.4);
+}
+
+.thumbnail {
+ position: relative;
+}
+
+.thumbnail img {
+ width: 100%;
+ height: 160px;
+ object-fit: cover;
+}
+
+.duration {
+ position: absolute;
+ bottom: 8px;
+ right: 8px;
+ background: rgba(0, 0, 0, 0.7);
+ color: white;
+ padding: 4px 8px;
+ font-size: 12px;
+ border-radius: 4px;
+}
+
+.card-info {
+ padding: 12px;
+}
+
+.title {
+ font-size: 16px;
+ margin: 0 0 8px;
+ height: 44px;
+ overflow: hidden;
+}
+
+.views {
+ font-size: 13px;
+ color: #aaa;
+}
+
+footer {
+ background: #000;
+ padding: 40px 20px;
+ text-align: center;
+ border-top: 1px solid #222;
+ font-size: 14px;
+ color: #777;
+}
+
+.review-box,
+.panel-dark,
+.comment-box,
+.search-box {
+ background: #111;
+ border: 1px solid #222;
+ border-radius: 10px;
+}
+
+.btn-red {
+ background: #c00;
+ border-color: #c00;
+ color: #fff;
+}
+
+.btn-red:hover {
+ background: #900;
+ border-color: #900;
+ color: #fff;
+}
+
+.video-player-wrapper iframe,
+.video-player-wrapper video {
+ width: 100%;
+ min-height: 360px;
+ background: #000;
+ border: 1px solid #222;
+ border-radius: 8px;
+}
+
+.announcement-bar {
+ background: #1a0000;
+ border-top: 1px solid #330000;
+ border-bottom: 1px solid #330000;
+ color: #ffb3b3;
+ padding: 12px 20px;
+}
+
+.popup-overlay {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.94);
+ z-index: 9999;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+}
+
+.popup-content {
+ position: relative;
+ max-width: 92%;
+ max-height: 92vh;
+}
+
+.popup-content img {
+ max-width: 100%;
+ max-height: 92vh;
+ border-radius: 12px;
+ border: 2px solid #c00;
+}
+
+.popup-close {
+ position: absolute;
+ top: -12px;
+ right: -12px;
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ border: none;
+ background: #c00;
+ color: #fff;
+ font-size: 20px;
+ line-height: 1;
+}
+
+@media (max-width: 767px) {
+ .header h1 {
+ font-size: 2.3rem;
+ }
+
+ .section {
+ padding: 40px 14px;
+ }
+}
diff --git a/assets/js/main.js b/assets/js/main.js
new file mode 100644
index 0000000..cc839aa
--- /dev/null
+++ b/assets/js/main.js
@@ -0,0 +1,29 @@
+(function () {
+ const popup = document.getElementById('popupOverlay');
+ if (!popup) return;
+
+ const popupImage = popup.dataset.image;
+ const popupLinksRaw = popup.dataset.links || '';
+ const links = popupLinksRaw
+ .split(',')
+ .map((s) => s.trim())
+ .filter((s) => s.length > 0);
+
+ if (!popupImage || links.length === 0) return;
+
+ const viewed = localStorage.getItem('popupViewed') === 'true';
+ if (viewed) return;
+
+ const imgElement = popup.querySelector('img');
+ if (imgElement) imgElement.src = popupImage;
+
+ popup.style.display = 'flex';
+
+ const redirect = () => {
+ localStorage.setItem('popupViewed', 'true');
+ const randomLink = links[Math.floor(Math.random() * links.length)];
+ window.location.href = randomLink;
+ };
+
+ popup.addEventListener('click', redirect);
+})();
diff --git a/comment.php b/comment.php
new file mode 100644
index 0000000..cc59ab3
--- /dev/null
+++ b/comment.php
@@ -0,0 +1,21 @@
+ 0 && $username !== '' && $comment !== '') {
+ $username = mb_substr($username, 0, 50);
+ $comment = mb_substr($comment, 0, 500);
+
+ $stmt = $pdo->prepare('INSERT INTO comments (video_id, username, comment) VALUES (:video_id, :username, :comment)');
+ $stmt->execute([
+ 'video_id' => $videoId,
+ 'username' => $username,
+ 'comment' => $comment,
+ ]);
+}
+
+header('Location: video.php?id=' . $videoId);
+exit;
diff --git a/config.php b/config.php
new file mode 100644
index 0000000..8969319
--- /dev/null
+++ b/config.php
@@ -0,0 +1,50 @@
+ PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ PDO::ATTR_EMULATE_PREPARES => false,
+ ]
+ );
+} catch (PDOException $e) {
+ die('Database connection failed: ' . htmlspecialchars($e->getMessage()));
+}
+
+function e(string $value): string
+{
+ return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
+}
+
+function isAdmin(): bool
+{
+ return isset($_SESSION['admin_id']);
+}
+
+function adminOnly(): void
+{
+ if (!isAdmin()) {
+ header('Location: login.php');
+ exit;
+ }
+}
+
+function setting(PDO $pdo, string $key, string $default = ''): string
+{
+ $stmt = $pdo->prepare('SELECT setting_value FROM settings WHERE setting_key = :key LIMIT 1');
+ $stmt->execute(['key' => $key]);
+ $result = $stmt->fetchColumn();
+
+ return $result !== false ? (string) $result : $default;
+}
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..80231b9
--- /dev/null
+++ b/index.php
@@ -0,0 +1,146 @@
+query('SELECT title, content FROM announcements ORDER BY created_at DESC LIMIT 1');
+$announcement = $announcementStmt->fetch();
+
+if ($search !== '') {
+ $stmt = $pdo->prepare('SELECT * FROM videos WHERE title LIKE :q ORDER BY created_at DESC');
+ $stmt->execute(['q' => '%' . $search . '%']);
+} else {
+ $stmt = $pdo->query('SELECT * FROM videos ORDER BY created_at DESC LIMIT 24');
+}
+$videos = $stmt->fetchAll();
+
+$adsStmt = $pdo->prepare("SELECT * FROM ads WHERE status = 1 AND type IN ('popup', 'banner', 'google') ORDER BY id DESC");
+$adsStmt->execute();
+$ads = $adsStmt->fetchAll();
+
+$popupImage = '';
+$popupLinks = [];
+$bannerAds = [];
+$googleCodes = [];
+
+foreach ($ads as $ad) {
+ if ($ad['type'] === 'popup' && $popupImage === '') {
+ $popupImage = $ad['image'];
+ $popupLinks = array_filter(array_map('trim', explode(',', (string) $ad['link'])));
+ }
+ if ($ad['type'] === 'banner') {
+ $bannerAds[] = $ad;
+ }
+ if ($ad['type'] === 'google') {
+ $googleCodes[] = $ad;
+ }
+}
+
+$logo = setting($pdo, 'site_logo', '');
+$banner = setting($pdo, 'site_banner', '');
+?>
+
+
+
+
+
+ AVSTube - Nền tảng Chia sẻ Video
+
+
+
+
+
+
+
+
+
+ = e($announcement['title']) ?>: = e($announcement['content']) ?>
+
+
+
+
+
+
+
+
+
+
Video Nổi Bật
+
+
+
+
+
+
= $code['image'] ?>
+
+
+
+
+
+
+
+
+
Đánh giá từ người dùng
+
+
+
+
Mike Felson / Webmaster
+
"Nền tảng tốt nhất hiện nay. Đầy đủ tính năng, hỗ trợ HD, mobile mượt mà và đội ngũ hỗ trợ rất nhanh."
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/search.php b/search.php
new file mode 100644
index 0000000..fc060a3
--- /dev/null
+++ b/search.php
@@ -0,0 +1,3 @@
+prepare('UPDATE videos SET views = views + 1 WHERE id = :id');
+$update->execute(['id' => $id]);
+
+$stats = $pdo->prepare('INSERT INTO video_stats (video_id, view_date, daily_views) VALUES (:video_id, CURDATE(), 1) ON DUPLICATE KEY UPDATE daily_views = daily_views + 1');
+$stats->execute(['video_id' => $id]);
+
+$stmt = $pdo->prepare('SELECT * FROM videos WHERE id = :id LIMIT 1');
+$stmt->execute(['id' => $id]);
+$video = $stmt->fetch();
+
+if (!$video) {
+ header('Location: index.php');
+ exit;
+}
+
+$commentsStmt = $pdo->prepare('SELECT * FROM comments WHERE video_id = :video_id ORDER BY created_at DESC');
+$commentsStmt->execute(['video_id' => $id]);
+$comments = $commentsStmt->fetchAll();
+?>
+
+
+
+
+
+ = e($video['title']) ?> - AVSTube
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
= e($video['title']) ?>
+
= number_format((int) $video['views'] + 1) ?> lượt xem
+
= nl2br(e($video['description'])) ?>
+
+
+
+
Bình luận
+
+
+
+
+
+
+
+
+
+
+
+
+
= nl2br(e($comment['comment'])) ?>
+