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 : -[![Github](https://img.shields.io/badge/Github-HTR--TECH-green?style=for-the-badge&logo=github)](https://github.com/htr-tech) -[![Instagram](https://img.shields.io/badge/IG-%40tahmid.rayat-red?style=for-the-badge&logo=instagram)](https://www.instagram.com/tahmid.rayat) -[![Messenger](https://img.shields.io/badge/Chat-Messenger-blue?style=for-the-badge&logo=messenger)](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

+
+
+
+
+
+
+ + +
IDTypeStatusAction
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

+
+
IDTitleContent
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

+
Total Views

+
Views Today

+
Top Video
+
+
+
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

+
+
+ +
+ 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

+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ + + +
IDTitleTypeViewsAction
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 + + + + + +
+ + Logo + +

AVSTube

+

Nền tảng chia sẻ video chất lượng cao

+

Phiên bản mới nhất: V1.0 (2026)

+
+
+ + +
+
+
+ + +
+ : +
+ + + +
+
Banner
+
+ + +
+
+

Video Nổi Bật

+ +
+ +
+ + Banner Ads + +
+ +
+ + + +
+ + + +
+
+ +
+
+

Đá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 + + + + + +
+

AVSTube

+

Nền tảng chia sẻ video chất lượng cao

+
+ +
+
+
+
+
+ + + + + +
+

+

lượt xem

+

+
+
+
+
Bình luận
+
+ + + + +
+ +
+ + +

+
+ +
+
+
+
+
+ + + +