Commite Code
This commit is contained in:
5
Moodle/PHP/agets.sh
Normal file
5
Moodle/PHP/agets.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
aria2c -x 16 --content-disposition-default-utf8=true --check-certificate=false -i list.txt \
|
||||||
|
--continue=true \
|
||||||
|
--check-integrity=true \
|
||||||
|
--max-concurrent-downloads=5 \
|
||||||
|
--log=aria2.log --log-level=notice
|
62
Moodle/PHP/batch_run.sh
Normal file
62
Moodle/PHP/batch_run.sh
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Kiểm tra có đối số không
|
||||||
|
if [ -z "$1" ] || [ -z "$2" ]; then
|
||||||
|
echo "Usage: $(basename "$0") <arg1> <arg2>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
siteid=$1
|
||||||
|
courseid=$2
|
||||||
|
|
||||||
|
# Lưu thư mục hiện tại (nơi chạy script, không phải nơi đặt script)
|
||||||
|
BASEDIR="$(pwd)"
|
||||||
|
|
||||||
|
for dir in */ ; do
|
||||||
|
# Bỏ dấu '/' cuối
|
||||||
|
dirname="${dir%/}"
|
||||||
|
|
||||||
|
# Cắt phần số trước dấu '.' và trim khoảng trắng
|
||||||
|
index=$(echo "$dirname" | cut -d'.' -f1 | tr -d '[:space:]')
|
||||||
|
|
||||||
|
# Bỏ số 0 đầu nếu có
|
||||||
|
index_nozero=$(echo "$index" | sed 's/^0*//')
|
||||||
|
|
||||||
|
# Nếu chuỗi rỗng (trường hợp '0')
|
||||||
|
if [ -z "$index_nozero" ]; then
|
||||||
|
index_nozero=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Kiểm tra là số hợp lệ
|
||||||
|
if [[ "$index_nozero" =~ ^[0-9]+$ ]]; then
|
||||||
|
# Tính i-1
|
||||||
|
i_minus_1=$((index_nozero - 1))
|
||||||
|
|
||||||
|
echo "===> Đang xử lý thư mục: $dirname (index: $index_nozero, i-1: $i_minus_1)"
|
||||||
|
|
||||||
|
# Chuyển vào thư mục
|
||||||
|
cd "$dirname" || { echo "Không thể vào thư mục $dirname"; exit 1; }
|
||||||
|
|
||||||
|
# Gọi online.sh với courseid từ dòng lệnh và i-1
|
||||||
|
|
||||||
|
case "$siteid" in
|
||||||
|
1)
|
||||||
|
online.sh "$courseid" "$i_minus_1"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
elearning.sh "$courseid" "$i_minus_1"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
english.sh "$courseid" "$i_minus_1"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Không hỗ trợ siteid=$siteid"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Quay lại thư mục gốc
|
||||||
|
cd "$BASEDIR"
|
||||||
|
else
|
||||||
|
echo "Bỏ qua $dirname (không bắt đầu bằng số)"
|
||||||
|
fi
|
||||||
|
done
|
7
Moodle/PHP/create_course.sh
Normal file
7
Moodle/PHP/create_course.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Lấy thư mục hiện tại nơi người dùng chạy lệnh
|
||||||
|
CURRENT_DIR="$(pwd)"
|
||||||
|
|
||||||
|
# Gọi PHP script, truyền tham số đầu vào và thư mục hiện tại
|
||||||
|
/usr/local/lsws/lsphp82/bin/php /usr/bin/create_moodle_course.php "$@" "$CURRENT_DIR"
|
131
Moodle/PHP/create_moodle_course.php
Normal file
131
Moodle/PHP/create_moodle_course.php
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Bắt buộc để script CLI hoạt động với Moodle
|
||||||
|
define('CLI_SCRIPT', true);
|
||||||
|
$site = intval($argv[1]); // site ID
|
||||||
|
$categoryid = intval($argv[2]);
|
||||||
|
$format = $argv[3];
|
||||||
|
|
||||||
|
// ✅ Xác định DIRROOT theo site
|
||||||
|
switch ($site) {
|
||||||
|
case 1:
|
||||||
|
define('DIRROOT', '/home/online.huph.edu.vn/public_html/online');
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
define('DIRROOT', '/home/elearning.huph.edu.vn/public_html/elearning');
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
define('DIRROOT', '/home/english.huph.edu.vn/public_html/english');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
echo "❌ Site ID không hợp lệ: $site\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Đường dẫn đến config.php
|
||||||
|
require_once(DIRROOT . '/config.php');
|
||||||
|
require_once($CFG->libdir . '/clilib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/lib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/externallib.php');
|
||||||
|
|
||||||
|
// 🛠 Nhận tham số dòng lệnh
|
||||||
|
global $argv;
|
||||||
|
|
||||||
|
if (count($argv) < 4) {
|
||||||
|
echo "❗Cách dùng: create_course <categoryid> <format>\n";
|
||||||
|
echo " Ví dụ : create_course 3 topics\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$categoryid = intval($argv[2]);
|
||||||
|
$format = $argv[3];
|
||||||
|
$cwd = $argv[4]; // Thư mục làm việc thực tế
|
||||||
|
|
||||||
|
$coursefullname = basename($cwd);
|
||||||
|
$shortname = preg_replace('/\s+/', '', $coursefullname);
|
||||||
|
|
||||||
|
echo "📂 Thư mục hiện tại: $cwd\n";
|
||||||
|
echo "📘 Tên khóa học: $coursefullname\n";
|
||||||
|
echo "🔤 Shortname: $shortname\n";
|
||||||
|
echo "📁 Category ID: $categoryid\n";
|
||||||
|
echo "🧱 Course Format: $format\n";
|
||||||
|
|
||||||
|
// ✅ Lấy danh sách thư mục con để đếm số topic
|
||||||
|
$topics = array_filter(glob($cwd . '/*'), 'is_dir');
|
||||||
|
$numsections = count($topics);
|
||||||
|
echo "🧩 Số topic được tạo: $numsections\n";
|
||||||
|
|
||||||
|
// ✅ Kiểm tra shortname đã tồn tại chưa
|
||||||
|
if ($DB->record_exists('course', ['shortname' => $shortname])) {
|
||||||
|
echo "⚠️ Shortname '$shortname' đã tồn tại. Dừng lại để tránh trùng.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Tạo khóa học
|
||||||
|
$course = new stdClass();
|
||||||
|
$course->fullname = $coursefullname;
|
||||||
|
$course->shortname = $shortname;
|
||||||
|
$course->category = $categoryid;
|
||||||
|
$course->format = $format;
|
||||||
|
$course->numsections = $numsections;
|
||||||
|
$course->summary = $coursefullname;
|
||||||
|
$course->summaryformat = FORMAT_HTML;
|
||||||
|
$course->visible = 1;
|
||||||
|
|
||||||
|
$newcourse = create_course($course);
|
||||||
|
echo "✅ Đã tạo khóa học thành công với ID: {$newcourse->id}\n";
|
||||||
|
|
||||||
|
// ✅ Đặt tên cho từng topic/section
|
||||||
|
foreach ($topics as $dir) {
|
||||||
|
$dirname = basename($dir);
|
||||||
|
$index = 0;
|
||||||
|
|
||||||
|
if (preg_match('/^(\d+)/', $dirname, $matches)) {
|
||||||
|
$index = intval($matches[1]) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$topicname = preg_replace('/^\d+\.\s*/', '', $dirname);
|
||||||
|
|
||||||
|
$section = $DB->get_record('course_sections', [
|
||||||
|
'course' => $newcourse->id,
|
||||||
|
'section' => $index
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($section) {
|
||||||
|
$section->name = $topicname;
|
||||||
|
$DB->update_record('course_sections', $section);
|
||||||
|
echo "🔁 Đổi tên topic [$index] thành: $topicname\n";
|
||||||
|
} else {
|
||||||
|
$section = course_create_section($newcourse->id, $index);
|
||||||
|
$section->name = $topicname;
|
||||||
|
$section->summary = '';
|
||||||
|
$section->summaryformat = FORMAT_HTML;
|
||||||
|
$DB->update_record('course_sections', $section);
|
||||||
|
echo "🆕 Tạo topic mới [$index]: $topicname\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Ghi course ID vào file .env
|
||||||
|
$envFile = $cwd . '/.env';
|
||||||
|
$courseIdLine = "COURSEID={$newcourse->id}";
|
||||||
|
|
||||||
|
if (file_exists($envFile)) {
|
||||||
|
$lines = file($envFile, FILE_IGNORE_NEW_LINES);
|
||||||
|
$found = false;
|
||||||
|
foreach ($lines as &$line) {
|
||||||
|
if (str_starts_with(trim($line), 'COURSEID=')) {
|
||||||
|
$line = $courseIdLine;
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$found) {
|
||||||
|
$lines[] = $courseIdLine;
|
||||||
|
}
|
||||||
|
file_put_contents($envFile, implode(PHP_EOL, $lines) . PHP_EOL);
|
||||||
|
echo "📄 Đã cập nhật COURSEID vào .env\n";
|
||||||
|
} else {
|
||||||
|
file_put_contents($envFile, $courseIdLine . PHP_EOL);
|
||||||
|
echo "📄 Đã tạo file .env với COURSEID={$newcourse->id}\n";
|
||||||
|
}
|
114
Moodle/PHP/createcourse.php
Normal file
114
Moodle/PHP/createcourse.php
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Bắt buộc để script CLI hoạt động với Moodle
|
||||||
|
define('CLI_SCRIPT', true);
|
||||||
|
define('DIRROOT', '/home/online.huph.edu.vn/public_html/online');
|
||||||
|
|
||||||
|
// Đường dẫn đến config.php
|
||||||
|
require_once(DIRROOT . '/config.php');
|
||||||
|
require_once($CFG->libdir . '/clilib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/lib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/externallib.php');
|
||||||
|
|
||||||
|
// 🛠 Nhận tham số dòng lệnh
|
||||||
|
global $argv;
|
||||||
|
|
||||||
|
if (count($argv) < 4) {
|
||||||
|
echo "❗Cách dùng: create_course_list <categoryid> <format>\n";
|
||||||
|
echo " Ví dụ : create_course_list 3 topics\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$categoryid = intval($argv[1]);
|
||||||
|
$format = $argv[2];
|
||||||
|
$cwd = $argv[3]; // Thư mục đang thao tác (nơi chứa list.txt)
|
||||||
|
|
||||||
|
// 🧾 Đọc tên khóa học từ thư mục
|
||||||
|
$coursefullname = basename($cwd);
|
||||||
|
$shortname = preg_replace('/\s+/', '', $coursefullname);
|
||||||
|
|
||||||
|
echo "📂 Thư mục hiện tại: $cwd\n";
|
||||||
|
echo "📘 Tên khóa học: $coursefullname\n";
|
||||||
|
echo "🔤 Shortname: $shortname\n";
|
||||||
|
echo "📁 Category ID: $categoryid\n";
|
||||||
|
echo "🧱 Course Format: $format\n";
|
||||||
|
|
||||||
|
// 📄 Đọc danh sách topic từ file list.txt
|
||||||
|
$listfile = $cwd . '/list.txt';
|
||||||
|
|
||||||
|
if (!file_exists($listfile)) {
|
||||||
|
echo "❌ Không tìm thấy file list.txt trong thư mục: $cwd\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$lines = file($listfile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||||
|
$numsections = count($lines);
|
||||||
|
echo "🧩 Số topic từ list.txt: $numsections\n";
|
||||||
|
|
||||||
|
// ✅ Kiểm tra shortname đã tồn tại chưa
|
||||||
|
if ($DB->record_exists('course', ['shortname' => $shortname])) {
|
||||||
|
echo "⚠️ Shortname '$shortname' đã tồn tại. Dừng lại để tránh trùng.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Tạo khóa học
|
||||||
|
$course = new stdClass();
|
||||||
|
$course->fullname = $coursefullname;
|
||||||
|
$course->shortname = $shortname;
|
||||||
|
$course->category = $categoryid;
|
||||||
|
$course->format = $format;
|
||||||
|
$course->numsections = $numsections;
|
||||||
|
$course->summary = $coursefullname;
|
||||||
|
$course->summaryformat = FORMAT_HTML;
|
||||||
|
$course->visible = 1;
|
||||||
|
|
||||||
|
$newcourse = create_course($course);
|
||||||
|
echo "✅ Đã tạo khóa học thành công với ID: {$newcourse->id}\n";
|
||||||
|
|
||||||
|
// ✅ Tạo và đặt tên cho từng topic từ danh sách
|
||||||
|
foreach ($lines as $i => $topicname) {
|
||||||
|
$index = $i; // Bắt đầu từ 0
|
||||||
|
|
||||||
|
$section = $DB->get_record('course_sections', [
|
||||||
|
'course' => $newcourse->id,
|
||||||
|
'section' => $index
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($section) {
|
||||||
|
$section->name = trim($topicname);
|
||||||
|
$DB->update_record('course_sections', $section);
|
||||||
|
echo "🔁 Đặt tên topic [$index] thành: $topicname\n";
|
||||||
|
} else {
|
||||||
|
$section = course_create_section($newcourse->id, $index);
|
||||||
|
$section->name = trim($topicname);
|
||||||
|
$section->summary = '';
|
||||||
|
$section->summaryformat = FORMAT_HTML;
|
||||||
|
$DB->update_record('course_sections', $section);
|
||||||
|
echo "🆕 Tạo topic [$index]: $topicname\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Ghi course ID vào file .env
|
||||||
|
$envFile = $cwd . '/.env';
|
||||||
|
$courseIdLine = "COURSEID={$newcourse->id}";
|
||||||
|
|
||||||
|
if (file_exists($envFile)) {
|
||||||
|
$lines = file($envFile, FILE_IGNORE_NEW_LINES);
|
||||||
|
$found = false;
|
||||||
|
foreach ($lines as &$line) {
|
||||||
|
if (str_starts_with(trim($line), 'COURSEID=')) {
|
||||||
|
$line = $courseIdLine;
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$found) {
|
||||||
|
$lines[] = $courseIdLine;
|
||||||
|
}
|
||||||
|
file_put_contents($envFile, implode(PHP_EOL, $lines) . PHP_EOL);
|
||||||
|
echo "📄 Đã cập nhật COURSEID vào .env\n";
|
||||||
|
} else {
|
||||||
|
file_put_contents($envFile, $courseIdLine . PHP_EOL);
|
||||||
|
echo "📄 Đã tạo file .env với COURSEID={$newcourse->id}\n";
|
||||||
|
}
|
7
Moodle/PHP/createcourse.sh
Normal file
7
Moodle/PHP/createcourse.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Lấy thư mục hiện tại nơi người dùng chạy lệnh
|
||||||
|
CURRENT_DIR="$(pwd)"
|
||||||
|
|
||||||
|
# Gọi PHP script, truyền tham số đầu vào và thư mục hiện tại
|
||||||
|
php /usr/bin/createcourse.php "$@" "$CURRENT_DIR"
|
42
Moodle/PHP/delete_course.php
Normal file
42
Moodle/PHP/delete_course.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Bắt buộc để script CLI hoạt động với Moodle
|
||||||
|
define('CLI_SCRIPT', true);
|
||||||
|
define('DIRROOT', '/home/online.huph.edu.vn/public_html/online');
|
||||||
|
|
||||||
|
require_once(DIRROOT . '/config.php');
|
||||||
|
require_once($CFG->libdir . '/clilib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/lib.php');
|
||||||
|
|
||||||
|
// 🛠 Nhận tham số dòng lệnh
|
||||||
|
global $argv;
|
||||||
|
|
||||||
|
if ($argc < 2) {
|
||||||
|
echo "❗ Cách dùng: delete_course <courseid>\n";
|
||||||
|
echo " Ví dụ : delete_course 123\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$courseid = intval($argv[1]);
|
||||||
|
|
||||||
|
if (!$course = $DB->get_record('course', ['id' => $courseid])) {
|
||||||
|
echo "⚠️ Không tìm thấy khóa học với ID: $courseid\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($course->id == SITEID) {
|
||||||
|
echo "❌ Không thể xóa site course (ID = SITEID).\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "⚠️ Bạn sắp xóa khóa học: [{$course->id}] {$course->fullname}\n";
|
||||||
|
|
||||||
|
// ✅ Thực hiện xóa
|
||||||
|
try {
|
||||||
|
delete_course($course, false); // false = không tái sắp xếp category
|
||||||
|
echo "✅ Đã xóa khóa học thành công.\n";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "❌ Lỗi khi xóa khóa học: " . $e->getMessage() . "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
47
Moodle/PHP/ebatch_run.sh
Normal file
47
Moodle/PHP/ebatch_run.sh
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Kiểm tra có đối số không
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Usage: $(basename "$0") <courseid>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
courseid="$1"
|
||||||
|
|
||||||
|
# Lưu thư mục hiện tại (nơi chạy script, không phải nơi đặt script)
|
||||||
|
BASEDIR="$(pwd)"
|
||||||
|
|
||||||
|
for dir in */ ; do
|
||||||
|
# Bỏ dấu '/' cuối
|
||||||
|
dirname="${dir%/}"
|
||||||
|
|
||||||
|
# Cắt phần số trước dấu '.' và trim khoảng trắng
|
||||||
|
index=$(echo "$dirname" | cut -d'.' -f1 | tr -d '[:space:]')
|
||||||
|
|
||||||
|
# Bỏ số 0 đầu nếu có
|
||||||
|
index_nozero=$(echo "$index" | sed 's/^0*//')
|
||||||
|
|
||||||
|
# Nếu chuỗi rỗng (trường hợp '0')
|
||||||
|
if [ -z "$index_nozero" ]; then
|
||||||
|
index_nozero=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Kiểm tra là số hợp lệ
|
||||||
|
if [[ "$index_nozero" =~ ^[0-9]+$ ]]; then
|
||||||
|
# Tính i-1
|
||||||
|
i_minus_1=$((index_nozero - 1))
|
||||||
|
|
||||||
|
echo "===> Đang xử lý thư mục: $dirname (index: $index_nozero, i-1: $i_minus_1)"
|
||||||
|
|
||||||
|
# Chuyển vào thư mục
|
||||||
|
cd "$dirname" || { echo "Không thể vào thư mục $dirname"; exit 1; }
|
||||||
|
|
||||||
|
# Gọi online.sh với courseid từ dòng lệnh và i-1
|
||||||
|
elearning.sh "$courseid" "$i_minus_1"
|
||||||
|
|
||||||
|
# Quay lại thư mục gốc
|
||||||
|
cd "$BASEDIR"
|
||||||
|
else
|
||||||
|
echo "Bỏ qua $dirname (không bắt đầu bằng số)"
|
||||||
|
fi
|
||||||
|
done
|
7
Moodle/PHP/ecreate_course.sh
Normal file
7
Moodle/PHP/ecreate_course.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Lấy thư mục hiện tại nơi người dùng chạy lệnh
|
||||||
|
CURRENT_DIR="$(pwd)"
|
||||||
|
|
||||||
|
# Gọi PHP script, truyền tham số đầu vào và thư mục hiện tại
|
||||||
|
php /usr/bin/ecreate_moodle_course.php "$@" "$CURRENT_DIR"
|
113
Moodle/PHP/ecreate_moodle_course.php
Normal file
113
Moodle/PHP/ecreate_moodle_course.php
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Bắt buộc để script CLI hoạt động với Moodle
|
||||||
|
define('CLI_SCRIPT', true);
|
||||||
|
define('DIRROOT', '/home/elearning.huph.edu.vn/public_html/elearning');
|
||||||
|
|
||||||
|
// Đường dẫn đến config.php
|
||||||
|
require_once(DIRROOT . '/config.php');
|
||||||
|
require_once($CFG->libdir . '/clilib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/lib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/externallib.php');
|
||||||
|
|
||||||
|
// 🛠 Nhận tham số dòng lệnh
|
||||||
|
global $argv;
|
||||||
|
|
||||||
|
if (count($argv) < 4) {
|
||||||
|
echo "❗Cách dùng: create_course <categoryid> <format>\n";
|
||||||
|
echo " Ví dụ : create_course 3 topics\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$categoryid = intval($argv[1]);
|
||||||
|
$format = $argv[2];
|
||||||
|
$cwd = $argv[3]; // Thư mục làm việc thực tế
|
||||||
|
|
||||||
|
$coursefullname = basename($cwd);
|
||||||
|
$shortname = preg_replace('/\s+/', '', $coursefullname);
|
||||||
|
|
||||||
|
echo "📂 Thư mục hiện tại: $cwd\n";
|
||||||
|
echo "📘 Tên khóa học: $coursefullname\n";
|
||||||
|
echo "🔤 Shortname: $shortname\n";
|
||||||
|
echo "📁 Category ID: $categoryid\n";
|
||||||
|
echo "🧱 Course Format: $format\n";
|
||||||
|
|
||||||
|
// ✅ Lấy danh sách thư mục con để đếm số topic
|
||||||
|
$topics = array_filter(glob($cwd . '/*'), 'is_dir');
|
||||||
|
$numsections = count($topics);
|
||||||
|
echo "🧩 Số topic được tạo: $numsections\n";
|
||||||
|
|
||||||
|
// ✅ Kiểm tra shortname đã tồn tại chưa
|
||||||
|
if ($DB->record_exists('course', ['shortname' => $shortname])) {
|
||||||
|
echo "⚠️ Shortname '$shortname' đã tồn tại. Dừng lại để tránh trùng.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Tạo khóa học
|
||||||
|
$course = new stdClass();
|
||||||
|
$course->fullname = $coursefullname;
|
||||||
|
$course->shortname = $shortname;
|
||||||
|
$course->category = $categoryid;
|
||||||
|
$course->format = $format;
|
||||||
|
$course->numsections = $numsections;
|
||||||
|
$course->summary = $coursefullname;
|
||||||
|
$course->summaryformat = FORMAT_HTML;
|
||||||
|
$course->visible = 1;
|
||||||
|
|
||||||
|
$newcourse = create_course($course);
|
||||||
|
echo "✅ Đã tạo khóa học thành công với ID: {$newcourse->id}\n";
|
||||||
|
|
||||||
|
// ✅ Đặt tên cho từng topic/section
|
||||||
|
foreach ($topics as $dir) {
|
||||||
|
$dirname = basename($dir);
|
||||||
|
$index = 0;
|
||||||
|
|
||||||
|
if (preg_match('/^(\d+)/', $dirname, $matches)) {
|
||||||
|
$index = intval($matches[1]) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$topicname = preg_replace('/^\d+\.\s*/', '', $dirname);
|
||||||
|
|
||||||
|
$section = $DB->get_record('course_sections', [
|
||||||
|
'course' => $newcourse->id,
|
||||||
|
'section' => $index
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($section) {
|
||||||
|
$section->name = $topicname;
|
||||||
|
$DB->update_record('course_sections', $section);
|
||||||
|
echo "🔁 Đổi tên topic [$index] thành: $topicname\n";
|
||||||
|
} else {
|
||||||
|
$section = course_create_section($newcourse->id, $index);
|
||||||
|
$section->name = $topicname;
|
||||||
|
$section->summary = '';
|
||||||
|
$section->summaryformat = FORMAT_HTML;
|
||||||
|
$DB->update_record('course_sections', $section);
|
||||||
|
echo "🆕 Tạo topic mới [$index]: $topicname\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Ghi course ID vào file .env
|
||||||
|
$envFile = $cwd . '/.env';
|
||||||
|
$courseIdLine = "COURSEID={$newcourse->id}";
|
||||||
|
|
||||||
|
if (file_exists($envFile)) {
|
||||||
|
$lines = file($envFile, FILE_IGNORE_NEW_LINES);
|
||||||
|
$found = false;
|
||||||
|
foreach ($lines as &$line) {
|
||||||
|
if (str_starts_with(trim($line), 'COURSEID=')) {
|
||||||
|
$line = $courseIdLine;
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$found) {
|
||||||
|
$lines[] = $courseIdLine;
|
||||||
|
}
|
||||||
|
file_put_contents($envFile, implode(PHP_EOL, $lines) . PHP_EOL);
|
||||||
|
echo "📄 Đã cập nhật COURSEID vào .env\n";
|
||||||
|
} else {
|
||||||
|
file_put_contents($envFile, $courseIdLine . PHP_EOL);
|
||||||
|
echo "📄 Đã tạo file .env với COURSEID={$newcourse->id}\n";
|
||||||
|
}
|
42
Moodle/PHP/edelete_course.php
Normal file
42
Moodle/PHP/edelete_course.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Bắt buộc để script CLI hoạt động với Moodle
|
||||||
|
define('CLI_SCRIPT', true);
|
||||||
|
define('DIRROOT', '/home/elearning.huph.edu.vn/public_html/elearning');
|
||||||
|
|
||||||
|
require_once(DIRROOT . '/config.php');
|
||||||
|
require_once($CFG->libdir . '/clilib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/lib.php');
|
||||||
|
|
||||||
|
// 🛠 Nhận tham số dòng lệnh
|
||||||
|
global $argv;
|
||||||
|
|
||||||
|
if ($argc < 2) {
|
||||||
|
echo "❗ Cách dùng: delete_course <courseid>\n";
|
||||||
|
echo " Ví dụ : delete_course 123\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$courseid = intval($argv[1]);
|
||||||
|
|
||||||
|
if (!$course = $DB->get_record('course', ['id' => $courseid])) {
|
||||||
|
echo "⚠️ Không tìm thấy khóa học với ID: $courseid\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($course->id == SITEID) {
|
||||||
|
echo "❌ Không thể xóa site course (ID = SITEID).\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "⚠️ Bạn sắp xóa khóa học: [{$course->id}] {$course->fullname}\n";
|
||||||
|
|
||||||
|
// ✅ Thực hiện xóa
|
||||||
|
try {
|
||||||
|
delete_course($course, false); // false = không tái sắp xếp category
|
||||||
|
echo "✅ Đã xóa khóa học thành công.\n";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "❌ Lỗi khi xóa khóa học: " . $e->getMessage() . "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
20
Moodle/PHP/elearning.sh
Normal file
20
Moodle/PHP/elearning.sh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Kiểm tra số lượng tham số đầu vào
|
||||||
|
if [ "$#" -ne 2 ]; then
|
||||||
|
echo "Usage: $0 <courseid> <topicid>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gán tham số đầu vào vào biến
|
||||||
|
COURSEID=$1
|
||||||
|
TOPICID=$2
|
||||||
|
|
||||||
|
# Lấy đường dẫn thư mục hiện tại
|
||||||
|
CURRENT_DIR=$(pwd)
|
||||||
|
|
||||||
|
# Chạy lệnh PHP với các tham số, sử dụng mdl.media từ thư mục hiện tại
|
||||||
|
/usr/local/lsws/lsphp82/bin/php /home/elearning.huph.edu.vn/public_html/elearning/page.php "$CURRENT_DIR/mdl.media" "$COURSEID" "$TOPICID"
|
||||||
|
|
||||||
|
# Hiển thị thông báo hoàn tất
|
||||||
|
echo "Command executed with mdl.media from $CURRENT_DIR, courseid=$COURSEID, and topicid=$TOPICID"
|
47
Moodle/PHP/enbatch_run.sh
Normal file
47
Moodle/PHP/enbatch_run.sh
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Kiểm tra có đối số không
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Usage: $(basename "$0") <courseid>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
courseid="$1"
|
||||||
|
|
||||||
|
# Lưu thư mục hiện tại (nơi chạy script, không phải nơi đặt script)
|
||||||
|
BASEDIR="$(pwd)"
|
||||||
|
|
||||||
|
for dir in */ ; do
|
||||||
|
# Bỏ dấu '/' cuối
|
||||||
|
dirname="${dir%/}"
|
||||||
|
|
||||||
|
# Cắt phần số trước dấu '.' và trim khoảng trắng
|
||||||
|
index=$(echo "$dirname" | cut -d'.' -f1 | tr -d '[:space:]')
|
||||||
|
|
||||||
|
# Bỏ số 0 đầu nếu có
|
||||||
|
index_nozero=$(echo "$index" | sed 's/^0*//')
|
||||||
|
|
||||||
|
# Nếu chuỗi rỗng (trường hợp '0')
|
||||||
|
if [ -z "$index_nozero" ]; then
|
||||||
|
index_nozero=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Kiểm tra là số hợp lệ
|
||||||
|
if [[ "$index_nozero" =~ ^[0-9]+$ ]]; then
|
||||||
|
# Tính i-1
|
||||||
|
i_minus_1=$((index_nozero - 1))
|
||||||
|
|
||||||
|
echo "===> Đang xử lý thư mục: $dirname (index: $index_nozero, i-1: $i_minus_1)"
|
||||||
|
|
||||||
|
# Chuyển vào thư mục
|
||||||
|
cd "$dirname" || { echo "Không thể vào thư mục $dirname"; exit 1; }
|
||||||
|
|
||||||
|
# Gọi online.sh với courseid từ dòng lệnh và i-1
|
||||||
|
english.sh "$courseid" "$i_minus_1"
|
||||||
|
|
||||||
|
# Quay lại thư mục gốc
|
||||||
|
cd "$BASEDIR"
|
||||||
|
else
|
||||||
|
echo "Bỏ qua $dirname (không bắt đầu bằng số)"
|
||||||
|
fi
|
||||||
|
done
|
7
Moodle/PHP/encreate_course.sh
Normal file
7
Moodle/PHP/encreate_course.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Lấy thư mục hiện tại nơi người dùng chạy lệnh
|
||||||
|
CURRENT_DIR="$(pwd)"
|
||||||
|
|
||||||
|
# Gọi PHP script, truyền tham số đầu vào và thư mục hiện tại
|
||||||
|
php /usr/bin/encreate_moodle_course.php "$@" "$CURRENT_DIR"
|
113
Moodle/PHP/encreate_moodle_course.php
Normal file
113
Moodle/PHP/encreate_moodle_course.php
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Bắt buộc để script CLI hoạt động với Moodle
|
||||||
|
define('CLI_SCRIPT', true);
|
||||||
|
define('DIRROOT', '/home/english.huph.edu.vn/public_html/english');
|
||||||
|
|
||||||
|
// Đường dẫn đến config.php
|
||||||
|
require_once(DIRROOT . '/config.php');
|
||||||
|
require_once($CFG->libdir . '/clilib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/lib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/externallib.php');
|
||||||
|
|
||||||
|
// 🛠 Nhận tham số dòng lệnh
|
||||||
|
global $argv;
|
||||||
|
|
||||||
|
if (count($argv) < 4) {
|
||||||
|
echo "❗Cách dùng: create_course <categoryid> <format>\n";
|
||||||
|
echo " Ví dụ : create_course 3 topics\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$categoryid = intval($argv[1]);
|
||||||
|
$format = $argv[2];
|
||||||
|
$cwd = $argv[3]; // Thư mục làm việc thực tế
|
||||||
|
|
||||||
|
$coursefullname = basename($cwd);
|
||||||
|
$shortname = preg_replace('/\s+/', '', $coursefullname);
|
||||||
|
|
||||||
|
echo "📂 Thư mục hiện tại: $cwd\n";
|
||||||
|
echo "📘 Tên khóa học: $coursefullname\n";
|
||||||
|
echo "🔤 Shortname: $shortname\n";
|
||||||
|
echo "📁 Category ID: $categoryid\n";
|
||||||
|
echo "🧱 Course Format: $format\n";
|
||||||
|
|
||||||
|
// ✅ Lấy danh sách thư mục con để đếm số topic
|
||||||
|
$topics = array_filter(glob($cwd . '/*'), 'is_dir');
|
||||||
|
$numsections = count($topics);
|
||||||
|
echo "🧩 Số topic được tạo: $numsections\n";
|
||||||
|
|
||||||
|
// ✅ Kiểm tra shortname đã tồn tại chưa
|
||||||
|
if ($DB->record_exists('course', ['shortname' => $shortname])) {
|
||||||
|
echo "⚠️ Shortname '$shortname' đã tồn tại. Dừng lại để tránh trùng.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Tạo khóa học
|
||||||
|
$course = new stdClass();
|
||||||
|
$course->fullname = $coursefullname;
|
||||||
|
$course->shortname = $shortname;
|
||||||
|
$course->category = $categoryid;
|
||||||
|
$course->format = $format;
|
||||||
|
$course->numsections = $numsections;
|
||||||
|
$course->summary = $coursefullname;
|
||||||
|
$course->summaryformat = FORMAT_HTML;
|
||||||
|
$course->visible = 1;
|
||||||
|
|
||||||
|
$newcourse = create_course($course);
|
||||||
|
echo "✅ Đã tạo khóa học thành công với ID: {$newcourse->id}\n";
|
||||||
|
|
||||||
|
// ✅ Đặt tên cho từng topic/section
|
||||||
|
foreach ($topics as $dir) {
|
||||||
|
$dirname = basename($dir);
|
||||||
|
$index = 0;
|
||||||
|
|
||||||
|
if (preg_match('/^(\d+)/', $dirname, $matches)) {
|
||||||
|
$index = intval($matches[1]) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$topicname = preg_replace('/^\d+\.\s*/', '', $dirname);
|
||||||
|
|
||||||
|
$section = $DB->get_record('course_sections', [
|
||||||
|
'course' => $newcourse->id,
|
||||||
|
'section' => $index
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($section) {
|
||||||
|
$section->name = $topicname;
|
||||||
|
$DB->update_record('course_sections', $section);
|
||||||
|
echo "🔁 Đổi tên topic [$index] thành: $topicname\n";
|
||||||
|
} else {
|
||||||
|
$section = course_create_section($newcourse->id, $index);
|
||||||
|
$section->name = $topicname;
|
||||||
|
$section->summary = '';
|
||||||
|
$section->summaryformat = FORMAT_HTML;
|
||||||
|
$DB->update_record('course_sections', $section);
|
||||||
|
echo "🆕 Tạo topic mới [$index]: $topicname\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Ghi course ID vào file .env
|
||||||
|
$envFile = $cwd . '/.env';
|
||||||
|
$courseIdLine = "COURSEID={$newcourse->id}";
|
||||||
|
|
||||||
|
if (file_exists($envFile)) {
|
||||||
|
$lines = file($envFile, FILE_IGNORE_NEW_LINES);
|
||||||
|
$found = false;
|
||||||
|
foreach ($lines as &$line) {
|
||||||
|
if (str_starts_with(trim($line), 'COURSEID=')) {
|
||||||
|
$line = $courseIdLine;
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$found) {
|
||||||
|
$lines[] = $courseIdLine;
|
||||||
|
}
|
||||||
|
file_put_contents($envFile, implode(PHP_EOL, $lines) . PHP_EOL);
|
||||||
|
echo "📄 Đã cập nhật COURSEID vào .env\n";
|
||||||
|
} else {
|
||||||
|
file_put_contents($envFile, $courseIdLine . PHP_EOL);
|
||||||
|
echo "📄 Đã tạo file .env với COURSEID={$newcourse->id}\n";
|
||||||
|
}
|
114
Moodle/PHP/encreatecourse.php
Normal file
114
Moodle/PHP/encreatecourse.php
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Bắt buộc để script CLI hoạt động với Moodle
|
||||||
|
define('CLI_SCRIPT', true);
|
||||||
|
define('DIRROOT', '/home/english.huph.edu.vn/public_html/english');
|
||||||
|
|
||||||
|
// Đường dẫn đến config.php
|
||||||
|
require_once(DIRROOT . '/config.php');
|
||||||
|
require_once($CFG->libdir . '/clilib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/lib.php');
|
||||||
|
require_once($CFG->dirroot . '/course/externallib.php');
|
||||||
|
|
||||||
|
// 🛠 Nhận tham số dòng lệnh
|
||||||
|
global $argv;
|
||||||
|
|
||||||
|
if (count($argv) < 4) {
|
||||||
|
echo "❗Cách dùng: create_course_list <categoryid> <format>\n";
|
||||||
|
echo " Ví dụ : create_course_list 3 topics\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$categoryid = intval($argv[1]);
|
||||||
|
$format = $argv[2];
|
||||||
|
$cwd = $argv[3]; // Thư mục đang thao tác (nơi chứa list.txt)
|
||||||
|
|
||||||
|
// 🧾 Đọc tên khóa học từ thư mục
|
||||||
|
$coursefullname = basename($cwd);
|
||||||
|
$shortname = preg_replace('/\s+/', '', $coursefullname);
|
||||||
|
|
||||||
|
echo "📂 Thư mục hiện tại: $cwd\n";
|
||||||
|
echo "📘 Tên khóa học: $coursefullname\n";
|
||||||
|
echo "🔤 Shortname: $shortname\n";
|
||||||
|
echo "📁 Category ID: $categoryid\n";
|
||||||
|
echo "🧱 Course Format: $format\n";
|
||||||
|
|
||||||
|
// 📄 Đọc danh sách topic từ file list.txt
|
||||||
|
$listfile = $cwd . '/list.txt';
|
||||||
|
|
||||||
|
if (!file_exists($listfile)) {
|
||||||
|
echo "❌ Không tìm thấy file list.txt trong thư mục: $cwd\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$lines = file($listfile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||||
|
$numsections = count($lines);
|
||||||
|
echo "🧩 Số topic từ list.txt: $numsections\n";
|
||||||
|
|
||||||
|
// ✅ Kiểm tra shortname đã tồn tại chưa
|
||||||
|
if ($DB->record_exists('course', ['shortname' => $shortname])) {
|
||||||
|
echo "⚠️ Shortname '$shortname' đã tồn tại. Dừng lại để tránh trùng.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Tạo khóa học
|
||||||
|
$course = new stdClass();
|
||||||
|
$course->fullname = $coursefullname;
|
||||||
|
$course->shortname = $shortname;
|
||||||
|
$course->category = $categoryid;
|
||||||
|
$course->format = $format;
|
||||||
|
$course->numsections = $numsections;
|
||||||
|
$course->summary = $coursefullname;
|
||||||
|
$course->summaryformat = FORMAT_HTML;
|
||||||
|
$course->visible = 1;
|
||||||
|
|
||||||
|
$newcourse = create_course($course);
|
||||||
|
echo "✅ Đã tạo khóa học thành công với ID: {$newcourse->id}\n";
|
||||||
|
|
||||||
|
// ✅ Tạo và đặt tên cho từng topic từ danh sách
|
||||||
|
foreach ($lines as $i => $topicname) {
|
||||||
|
$index = $i; // Bắt đầu từ 0
|
||||||
|
|
||||||
|
$section = $DB->get_record('course_sections', [
|
||||||
|
'course' => $newcourse->id,
|
||||||
|
'section' => $index
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($section) {
|
||||||
|
$section->name = trim($topicname);
|
||||||
|
$DB->update_record('course_sections', $section);
|
||||||
|
echo "🔁 Đặt tên topic [$index] thành: $topicname\n";
|
||||||
|
} else {
|
||||||
|
$section = course_create_section($newcourse->id, $index);
|
||||||
|
$section->name = trim($topicname);
|
||||||
|
$section->summary = '';
|
||||||
|
$section->summaryformat = FORMAT_HTML;
|
||||||
|
$DB->update_record('course_sections', $section);
|
||||||
|
echo "🆕 Tạo topic [$index]: $topicname\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Ghi course ID vào file .env
|
||||||
|
$envFile = $cwd . '/.env';
|
||||||
|
$courseIdLine = "COURSEID={$newcourse->id}";
|
||||||
|
|
||||||
|
if (file_exists($envFile)) {
|
||||||
|
$lines = file($envFile, FILE_IGNORE_NEW_LINES);
|
||||||
|
$found = false;
|
||||||
|
foreach ($lines as &$line) {
|
||||||
|
if (str_starts_with(trim($line), 'COURSEID=')) {
|
||||||
|
$line = $courseIdLine;
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$found) {
|
||||||
|
$lines[] = $courseIdLine;
|
||||||
|
}
|
||||||
|
file_put_contents($envFile, implode(PHP_EOL, $lines) . PHP_EOL);
|
||||||
|
echo "📄 Đã cập nhật COURSEID vào .env\n";
|
||||||
|
} else {
|
||||||
|
file_put_contents($envFile, $courseIdLine . PHP_EOL);
|
||||||
|
echo "📄 Đã tạo file .env với COURSEID={$newcourse->id}\n";
|
||||||
|
}
|
7
Moodle/PHP/encreatecourse.sh
Normal file
7
Moodle/PHP/encreatecourse.sh
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Lấy thư mục hiện tại nơi người dùng chạy lệnh
|
||||||
|
CURRENT_DIR="$(pwd)"
|
||||||
|
|
||||||
|
# Gọi PHP script, truyền tham số đầu vào và thư mục hiện tại
|
||||||
|
php /usr/bin/encreatecourse.php "$@" "$CURRENT_DIR"
|
20
Moodle/PHP/english.sh
Normal file
20
Moodle/PHP/english.sh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Kiểm tra số lượng tham số đầu vào
|
||||||
|
if [ "$#" -ne 2 ]; then
|
||||||
|
echo "Usage: $0 <courseid> <topicid>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gán tham số đầu vào vào biến
|
||||||
|
COURSEID=$1
|
||||||
|
TOPICID=$2
|
||||||
|
|
||||||
|
# Lấy đường dẫn thư mục hiện tại
|
||||||
|
CURRENT_DIR=$(pwd)
|
||||||
|
|
||||||
|
# Chạy lệnh PHP với các tham số, sử dụng mdl.media từ thư mục hiện tại
|
||||||
|
/usr/local/lsws/lsphp82/bin/php /home/english.huph.edu.vn/public_html/english/page.php "$CURRENT_DIR/mdl.media" "$COURSEID" "$TOPICID"
|
||||||
|
|
||||||
|
# Hiển thị thông báo hoàn tất
|
||||||
|
echo "Command executed with mdl.media from $CURRENT_DIR, courseid=$COURSEID, and topicid=$TOPICID"
|
26
Moodle/PHP/genmedia.sh
Normal file
26
Moodle/PHP/genmedia.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Thư mục gốc của khóa học
|
||||||
|
COURSE_FOLDER="$(pwd)"
|
||||||
|
|
||||||
|
# Tìm tất cả các thư mục con
|
||||||
|
mapfile -t DIRS < <(find "$COURSE_FOLDER" -type d)
|
||||||
|
|
||||||
|
for dir in "${DIRS[@]}"; do
|
||||||
|
relative_path="${dir#*/vod/}"
|
||||||
|
OUTPUT_JSON="$dir/mdl.media"
|
||||||
|
|
||||||
|
# Xóa tệp mdl.media cũ nếu tồn tại
|
||||||
|
rm -f "$OUTPUT_JSON"
|
||||||
|
|
||||||
|
# Tìm và sắp xếp các file .mp3 và .mp4 theo thứ tự tự nhiên
|
||||||
|
mapfile -d '' -t FILES < <(find "$dir" -maxdepth 1 -type f \( -name '*.mp3' -o -name '*.mp4' \) -print0 | sort -z -V)
|
||||||
|
|
||||||
|
for file in "${FILES[@]}"; do
|
||||||
|
filename=$(basename "$file")
|
||||||
|
vid="$relative_path/$filename"
|
||||||
|
echo "[stream=$vid]" >> "$OUTPUT_JSON"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "File mdl.media created successfully in $dir."
|
||||||
|
done
|
32
Moodle/PHP/genmedia.v.1.sh
Normal file
32
Moodle/PHP/genmedia.v.1.sh
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Thư mục gốc của khóa học
|
||||||
|
COURSE_FOLDER="$(pwd)"
|
||||||
|
|
||||||
|
# Hàm xử lý tạo mdl.media cho mỗi thư mục
|
||||||
|
process_dir() {
|
||||||
|
local dir="$1"
|
||||||
|
local relative_path="${dir#*/vod/}"
|
||||||
|
|
||||||
|
# Đường dẫn đến tệp mdl.media trong thư mục
|
||||||
|
OUTPUT_JSON="$dir/mdl.media"
|
||||||
|
|
||||||
|
# Xóa tệp mdl.media cũ nếu tồn tại
|
||||||
|
rm -f "$OUTPUT_JSON"
|
||||||
|
|
||||||
|
# Tìm và sắp xếp các file .mp4 theo thứ tự tự nhiên (hỗ trợ cả số có hoặc không có leading zeros)
|
||||||
|
find "$dir" -maxdepth 1 -type f -name '*.mp4' -print0 | sort -z -V | while IFS= read -r -d '' file; do
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
filename=$(basename "$file")
|
||||||
|
vid="$relative_path/$filename"
|
||||||
|
echo "[stream=$vid]" >> "$OUTPUT_JSON"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "File mdl.media created successfully in $dir."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Duyệt qua thư mục gốc và tất cả các thư mục con, gọi hàm xử lý
|
||||||
|
find "$COURSE_FOLDER" -type d | while IFS= read -r dir; do
|
||||||
|
process_dir "$dir"
|
||||||
|
done
|
32
Moodle/PHP/genmp3.sh
Normal file
32
Moodle/PHP/genmp3.sh
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Thư mục gốc của khóa học
|
||||||
|
COURSE_FOLDER="$(pwd)"
|
||||||
|
|
||||||
|
# Hàm xử lý tạo mdl.media cho mỗi thư mục
|
||||||
|
process_dir() {
|
||||||
|
local dir="$1"
|
||||||
|
local relative_path="${dir#*/vod/}"
|
||||||
|
|
||||||
|
# Đường dẫn đến tệp mdl.media trong thư mục
|
||||||
|
OUTPUT_JSON="$dir/mdl.media"
|
||||||
|
|
||||||
|
# Xóa tệp mdl.media cũ nếu tồn tại
|
||||||
|
rm -f "$OUTPUT_JSON"
|
||||||
|
|
||||||
|
# Tìm và sắp xếp các file .mp3 theo thứ tự tự nhiên (hỗ trợ cả số có hoặc không có leading zeros)
|
||||||
|
find "$dir" -maxdepth 1 -type f -name '*.mp3' -print0 | sort -z -V | while IFS= read -r -d '' file; do
|
||||||
|
if [ -f "$file" ]; then
|
||||||
|
filename=$(basename "$file")
|
||||||
|
vid="$relative_path/$filename"
|
||||||
|
echo "[stream=$vid]" >> "$OUTPUT_JSON"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "File mdl.media created successfully in $dir."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Duyệt qua thư mục gốc và tất cả các thư mục con, gọi hàm xử lý
|
||||||
|
find "$COURSE_FOLDER" -type d | while IFS= read -r dir; do
|
||||||
|
process_dir "$dir"
|
||||||
|
done
|
2
Moodle/PHP/gets.sh
Normal file
2
Moodle/PHP/gets.sh
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
wget --no-check-certificate --content-disposition --max-redirect=10 --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36" --referer="https://en.git.ir/" -i list.txt
|
||||||
|
|
135
Moodle/PHP/gettext.sh
Normal file
135
Moodle/PHP/gettext.sh
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (C) 2003, 2005-2007, 2011, 2018-2020 Free Software Foundation, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Find a way to echo strings without interpreting backslash.
|
||||||
|
if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then
|
||||||
|
echo='echo'
|
||||||
|
else
|
||||||
|
if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then
|
||||||
|
echo='printf %s\n'
|
||||||
|
else
|
||||||
|
echo_func () {
|
||||||
|
cat <<EOT
|
||||||
|
$*
|
||||||
|
EOT
|
||||||
|
}
|
||||||
|
echo='echo_func'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# This script is primarily a shell function library. In order for
|
||||||
|
# ". gettext.sh" to find it, we install it in $PREFIX/bin (that is usually
|
||||||
|
# contained in $PATH), rather than in some other location such as
|
||||||
|
# $PREFIX/share/sh-scripts or $PREFIX/share/gettext. In order to not violate
|
||||||
|
# the Filesystem Hierarchy Standard when doing so, this script is executable.
|
||||||
|
# Therefore it needs to support the standard --help and --version.
|
||||||
|
if test -z "${ZSH_VERSION+set}"; then
|
||||||
|
# zsh is not POSIX compliant: By default, while ". gettext.sh" is executed,
|
||||||
|
# it sets $0 to "gettext.sh", defeating the purpose of this test. But
|
||||||
|
# fortunately we know that when running under zsh, this script is always
|
||||||
|
# being sourced, not executed, because hardly anyone is crazy enough to
|
||||||
|
# install zsh as /bin/sh.
|
||||||
|
case "$0" in
|
||||||
|
gettext.sh | */gettext.sh | *\\gettext.sh)
|
||||||
|
progname=$0
|
||||||
|
package=gettext-runtime
|
||||||
|
version=0.21
|
||||||
|
# func_usage
|
||||||
|
# outputs to stdout the --help usage message.
|
||||||
|
func_usage ()
|
||||||
|
{
|
||||||
|
echo "GNU gettext shell script function library version $version"
|
||||||
|
echo "Usage: . gettext.sh"
|
||||||
|
}
|
||||||
|
# func_version
|
||||||
|
# outputs to stdout the --version message.
|
||||||
|
func_version ()
|
||||||
|
{
|
||||||
|
echo "$progname (GNU $package) $version"
|
||||||
|
echo "Copyright (C) 2003-2020 Free Software Foundation, Inc.
|
||||||
|
License GPLv2+: GNU GPL version 2 or later <https://gnu.org/licenses/gpl.html>
|
||||||
|
This is free software: you are free to change and redistribute it.
|
||||||
|
There is NO WARRANTY, to the extent permitted by law."
|
||||||
|
echo "Written by" "Bruno Haible"
|
||||||
|
}
|
||||||
|
if test $# = 1; then
|
||||||
|
case "$1" in
|
||||||
|
--help | --hel | --he | --h )
|
||||||
|
func_usage; exit 0 ;;
|
||||||
|
--version | --versio | --versi | --vers | --ver | --ve | --v )
|
||||||
|
func_version; exit 0 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
func_usage 1>&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# eval_gettext MSGID
|
||||||
|
# looks up the translation of MSGID and substitutes shell variables in the
|
||||||
|
# result.
|
||||||
|
eval_gettext () {
|
||||||
|
gettext "$1" | (export PATH `envsubst --variables "$1"`; envsubst "$1")
|
||||||
|
}
|
||||||
|
|
||||||
|
# eval_ngettext MSGID MSGID-PLURAL COUNT
|
||||||
|
# looks up the translation of MSGID / MSGID-PLURAL for COUNT and substitutes
|
||||||
|
# shell variables in the result.
|
||||||
|
eval_ngettext () {
|
||||||
|
ngettext "$1" "$2" "$3" | (export PATH `envsubst --variables "$1 $2"`; envsubst "$1 $2")
|
||||||
|
}
|
||||||
|
|
||||||
|
# eval_pgettext MSGCTXT MSGID
|
||||||
|
# looks up the translation of MSGID in the context MSGCTXT and substitutes
|
||||||
|
# shell variables in the result.
|
||||||
|
eval_pgettext () {
|
||||||
|
gettext --context="$1" "$2" | (export PATH `envsubst --variables "$2"`; envsubst "$2")
|
||||||
|
}
|
||||||
|
|
||||||
|
# eval_npgettext MSGCTXT MSGID MSGID-PLURAL COUNT
|
||||||
|
# looks up the translation of MSGID / MSGID-PLURAL for COUNT in the context
|
||||||
|
# MSGCTXT and substitutes shell variables in the result.
|
||||||
|
eval_npgettext () {
|
||||||
|
ngettext --context="$1" "$2" "$3" "$4" | (export PATH `envsubst --variables "$2 $3"`; envsubst "$2 $3")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Note: This use of envsubst is much safer than using the shell built-in 'eval'
|
||||||
|
# would be.
|
||||||
|
# 1) The security problem with Chinese translations that happen to use a
|
||||||
|
# character such as \xe0\x60 is avoided.
|
||||||
|
# 2) The security problem with malevolent translators who put in command lists
|
||||||
|
# like "$(...)" or "`...`" is avoided.
|
||||||
|
# 3) The translations can only refer to shell variables that are already
|
||||||
|
# mentioned in MSGID or MSGID-PLURAL.
|
||||||
|
#
|
||||||
|
# Note: "export PATH" above is a dummy; this is for the case when
|
||||||
|
# `envsubst --variables ...` returns nothing.
|
||||||
|
#
|
||||||
|
# Note: In eval_ngettext above, "$1 $2" means a string whose variables set is
|
||||||
|
# the union of the variables set of "$1" and "$2".
|
||||||
|
#
|
||||||
|
# Note: The minimal use of backquote above ensures that trailing newlines are
|
||||||
|
# not dropped, not from the gettext invocation and not from the value of any
|
||||||
|
# shell variable.
|
||||||
|
#
|
||||||
|
# Note: Field splitting on the `envsubst --variables ...` result is desired,
|
||||||
|
# since envsubst outputs the variables, separated by newlines. Pathname
|
||||||
|
# wildcard expansion or tilde expansion has no effect here, since the words
|
||||||
|
# output by "envsubst --variables ..." consist solely of alphanumeric
|
||||||
|
# characters and underscore.
|
20
Moodle/PHP/hsphnline.sh
Normal file
20
Moodle/PHP/hsphnline.sh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Kiểm tra số lượng tham số đầu vào
|
||||||
|
if [ "$#" -ne 2 ]; then
|
||||||
|
echo "Usage: $0 <courseid> <topicid>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gán tham số đầu vào vào biến
|
||||||
|
COURSEID=$1
|
||||||
|
TOPICID=$2
|
||||||
|
|
||||||
|
# Lấy đường dẫn thư mục hiện tại
|
||||||
|
CURRENT_DIR=$(pwd)
|
||||||
|
|
||||||
|
# Chạy lệnh PHP với các tham số, sử dụng mdl.media từ thư mục hiện tại
|
||||||
|
php /home/online.linkvn.vn/public_html/online/objpage.php "$CURRENT_DIR/mdl.media" "$COURSEID" "$TOPICID"
|
||||||
|
|
||||||
|
# Hiển thị thông báo hoàn tất
|
||||||
|
echo "Command executed with mdl.media from $CURRENT_DIR, courseid=$COURSEID, and topicid=$TOPICID"
|
20
Moodle/PHP/hsphonline.sh
Normal file
20
Moodle/PHP/hsphonline.sh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Kiểm tra số lượng tham số đầu vào
|
||||||
|
if [ "$#" -ne 2 ]; then
|
||||||
|
echo "Usage: $0 <courseid> <topicid>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gán tham số đầu vào vào biến
|
||||||
|
COURSEID=$1
|
||||||
|
TOPICID=$2
|
||||||
|
|
||||||
|
# Lấy đường dẫn thư mục hiện tại
|
||||||
|
CURRENT_DIR=$(pwd)
|
||||||
|
|
||||||
|
# Chạy lệnh PHP với các tham số, sử dụng mdl.media từ thư mục hiện tại
|
||||||
|
php /home/online.linkvn.vn/public_html/online/page.php "$CURRENT_DIR/mdl.media" "$COURSEID" "$TOPICID"
|
||||||
|
|
||||||
|
# Hiển thị thông báo hoàn tất
|
||||||
|
echo "Command executed with mdl.media from $CURRENT_DIR, courseid=$COURSEID, and topicid=$TOPICID"
|
20
Moodle/PHP/objonline.sh
Normal file
20
Moodle/PHP/objonline.sh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Kiểm tra số lượng tham số đầu vào
|
||||||
|
if [ "$#" -ne 2 ]; then
|
||||||
|
echo "Usage: $0 <courseid> <topicid>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gán tham số đầu vào vào biến
|
||||||
|
COURSEID=$1
|
||||||
|
TOPICID=$2
|
||||||
|
|
||||||
|
# Lấy đường dẫn thư mục hiện tại
|
||||||
|
CURRENT_DIR=$(pwd)
|
||||||
|
|
||||||
|
# Chạy lệnh PHP với các tham số, sử dụng mdl.media từ thư mục hiện tại
|
||||||
|
php /home/online.linkvn.vn/public_html/online/objpage.php "$CURRENT_DIR/mdl.media" "$COURSEID" "$TOPICID"
|
||||||
|
|
||||||
|
# Hiển thị thông báo hoàn tất
|
||||||
|
echo "Command executed with mdl.media from $CURRENT_DIR, courseid=$COURSEID, and topicid=$TOPICID"
|
21
Moodle/PHP/online.sh
Normal file
21
Moodle/PHP/online.sh
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Kiểm tra số lượng tham số đầu vào
|
||||||
|
if [ "$#" -ne 2 ]; then
|
||||||
|
echo "Usage: $0 <courseid> <topicid>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gán tham số đầu vào vào biến
|
||||||
|
|
||||||
|
COURSEID=$1
|
||||||
|
TOPICID=$2
|
||||||
|
|
||||||
|
# Lấy đường dẫn thư mục hiện tại
|
||||||
|
CURRENT_DIR=$(pwd)
|
||||||
|
|
||||||
|
# Chạy lệnh PHP với các tham số, sử dụng mdl.media từ thư mục hiện tại
|
||||||
|
/usr/local/lsws/lsphp82/bin/php /home/online.huph.edu.vn/public_html/online/page.php "$CURRENT_DIR/mdl.media" "$COURSEID" "$TOPICID"
|
||||||
|
|
||||||
|
# Hiển thị thông báo hoàn tất
|
||||||
|
echo "Command executed with mdl.media from $CURRENT_DIR, courseid=$COURSEID, and topicid=$TOPICID"
|
22
Moodle/PHP/removeblank.sh
Normal file
22
Moodle/PHP/removeblank.sh
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
BASE_DIR="."
|
||||||
|
|
||||||
|
find "$BASE_DIR" -depth | while IFS= read -r path; do
|
||||||
|
current_name=$(basename "$path")
|
||||||
|
parent_dir=$(dirname "$path")
|
||||||
|
|
||||||
|
# Loại bỏ ký tự đặc biệt: # ! ' [ ] @ bằng tr + tr -d
|
||||||
|
new_name=$(echo "$current_name" | tr -d "#!'\[\]@")
|
||||||
|
|
||||||
|
# Loại bỏ khoảng trắng thừa (chỉ giữ 1 dấu cách, loại bỏ cuối)
|
||||||
|
new_name=$(echo "$new_name" | tr -s ' ' | sed 's/ *$//')
|
||||||
|
|
||||||
|
# Nếu tên thay đổi thì đổi tên
|
||||||
|
if [[ "$current_name" != "$new_name" ]]; then
|
||||||
|
mv -- "$path" "$parent_dir/$new_name"
|
||||||
|
echo "Đã đổi tên: '$path' → '$parent_dir/$new_name'"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "✅ Hoàn thành."
|
14
Moodle/PHP/rename_topics.sh
Normal file
14
Moodle/PHP/rename_topics.sh
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Lấy thư mục hiện hành nơi người dùng đang gọi lệnh
|
||||||
|
TARGET_DIR="$(pwd)"
|
||||||
|
|
||||||
|
# Đổi tên các thư mục con cấp 1 theo mẫu: 1 - ABC → 1. ABC
|
||||||
|
find "$TARGET_DIR" -mindepth 1 -maxdepth 1 -type d -regextype posix-extended -regex '.*/[0-9]+ - .+' | while read dir; do
|
||||||
|
base=$(basename "$dir")
|
||||||
|
newname=$(echo "$base" | sed -E 's/^([0-9]+) - (.+)$/\1. \2/')
|
||||||
|
if [ "$base" != "$newname" ]; then
|
||||||
|
echo "🔁 Đổi tên: $base → $newname"
|
||||||
|
mv "$TARGET_DIR/$base" "$TARGET_DIR/$newname"
|
||||||
|
fi
|
||||||
|
done
|
1401
Moodle/PHP/rescan-scsi-bus.sh
Normal file
1401
Moodle/PHP/rescan-scsi-bus.sh
Normal file
File diff suppressed because it is too large
Load Diff
70
Moodle/Tools/agets.sh
Normal file
70
Moodle/Tools/agets.sh
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
INPUT_LIST="list.txt"
|
||||||
|
TEMP_LIST="list_to_download.txt"
|
||||||
|
MIN_VALID_SIZE=1024 # Kích thước tối thiểu (bytes) để coi là file hợp lệ
|
||||||
|
|
||||||
|
# 🔍 Kiểm tra aria2c đã cài chưa
|
||||||
|
if ! command -v aria2c &> /dev/null; then
|
||||||
|
echo "❌ Lỗi: aria2c chưa được cài. Vui lòng cài aria2 trước."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 📂 Kiểm tra file list.txt có tồn tại không
|
||||||
|
if [ ! -f "$INPUT_LIST" ]; then
|
||||||
|
echo "❌ Không tìm thấy file $INPUT_LIST"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 📝 Tạo danh sách tạm các URL cần tải
|
||||||
|
> "$TEMP_LIST"
|
||||||
|
|
||||||
|
while read -r url; do
|
||||||
|
# 👉 Bỏ qua dòng trống hoặc comment
|
||||||
|
[[ -z "$url" || "$url" == \#* ]] && continue
|
||||||
|
|
||||||
|
# 🎯 Lấy tên file bỏ phần query string
|
||||||
|
filename=$(basename "${url%%\?*}")
|
||||||
|
|
||||||
|
# 📦 Kiểm tra trạng thái file
|
||||||
|
if [ ! -f "$filename" ]; then
|
||||||
|
echo "$url" >> "$TEMP_LIST"
|
||||||
|
echo "⬇️ Thiếu file: $filename"
|
||||||
|
elif [ -f "$filename.aria2" ]; then
|
||||||
|
echo "$url" >> "$TEMP_LIST"
|
||||||
|
echo "🔄 Đang tải dở: $filename"
|
||||||
|
elif [ "$(stat -c%s "$filename")" -lt "$MIN_VALID_SIZE" ]; then
|
||||||
|
echo "$url" >> "$TEMP_LIST"
|
||||||
|
echo "⚠️ File nhỏ bất thường (<$MIN_VALID_SIZE bytes), sẽ tải lại: $filename"
|
||||||
|
else
|
||||||
|
echo "✅ Đã có đầy đủ: $filename"
|
||||||
|
fi
|
||||||
|
done < "$INPUT_LIST"
|
||||||
|
|
||||||
|
# 🚫 Nếu không còn gì để tải
|
||||||
|
if [ ! -s "$TEMP_LIST" ]; then
|
||||||
|
echo "🎉 Tất cả file đã được tải đầy đủ."
|
||||||
|
rm -f "$TEMP_LIST"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 🚀 Bắt đầu tải bằng aria2c
|
||||||
|
aria2c \
|
||||||
|
-i "$TEMP_LIST" \
|
||||||
|
--dir=. \
|
||||||
|
--max-connection-per-server=16 \
|
||||||
|
--split=16 \
|
||||||
|
--min-split-size=1M \
|
||||||
|
--max-concurrent-downloads=10 \
|
||||||
|
--continue=true \
|
||||||
|
--remove-control-file=true \
|
||||||
|
--auto-file-renaming=false \
|
||||||
|
--file-allocation=none \
|
||||||
|
--summary-interval=0 \
|
||||||
|
--console-log-level=warn \
|
||||||
|
--log="aria2_download.log" \
|
||||||
|
--log-level=notice
|
||||||
|
|
||||||
|
# ✅ Hoàn tất
|
||||||
|
echo "✅ Hoàn tất tải các file chưa đầy đủ."
|
||||||
|
rm -f "$TEMP_LIST"
|
557
Moodle/Tools/dockerd-rootless-setuptool.sh
Normal file
557
Moodle/Tools/dockerd-rootless-setuptool.sh
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# dockerd-rootless-setuptool.sh: setup tool for dockerd-rootless.sh
|
||||||
|
# Needs to be executed as a non-root user.
|
||||||
|
#
|
||||||
|
# Typical usage: dockerd-rootless-setuptool.sh install --force
|
||||||
|
#
|
||||||
|
# Documentation: https://docs.docker.com/go/rootless/
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
# utility functions
|
||||||
|
INFO() {
|
||||||
|
/bin/echo -e "\e[104m\e[97m[INFO]\e[49m\e[39m $@"
|
||||||
|
}
|
||||||
|
|
||||||
|
WARNING() {
|
||||||
|
/bin/echo >&2 -e "\e[101m\e[97m[WARNING]\e[49m\e[39m $@"
|
||||||
|
}
|
||||||
|
|
||||||
|
ERROR() {
|
||||||
|
/bin/echo >&2 -e "\e[101m\e[97m[ERROR]\e[49m\e[39m $@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# constants
|
||||||
|
DOCKERD_ROOTLESS_SH="dockerd-rootless.sh"
|
||||||
|
SYSTEMD_UNIT="docker.service"
|
||||||
|
CLI_CONTEXT="rootless"
|
||||||
|
|
||||||
|
# CLI opt: --force
|
||||||
|
OPT_FORCE=""
|
||||||
|
# CLI opt: --skip-iptables
|
||||||
|
OPT_SKIP_IPTABLES=""
|
||||||
|
|
||||||
|
# global vars
|
||||||
|
ARG0="$0"
|
||||||
|
DOCKERD_ROOTLESS_SH_FLAGS=""
|
||||||
|
BIN=""
|
||||||
|
SYSTEMD=""
|
||||||
|
CFG_DIR=""
|
||||||
|
XDG_RUNTIME_DIR_CREATED=""
|
||||||
|
USERNAME=""
|
||||||
|
USERNAME_ESCAPED=""
|
||||||
|
|
||||||
|
# run checks and also initialize global vars
|
||||||
|
init() {
|
||||||
|
# OS verification: Linux only
|
||||||
|
case "$(uname)" in
|
||||||
|
Linux) ;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
ERROR "Rootless Docker cannot be installed on $(uname)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# User verification: deny running as root
|
||||||
|
if [ "$(id -u)" = "0" ]; then
|
||||||
|
ERROR "Refusing to install rootless Docker as the root user"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# set BIN
|
||||||
|
if ! BIN="$(command -v "$DOCKERD_ROOTLESS_SH" 2> /dev/null)"; then
|
||||||
|
ERROR "$DOCKERD_ROOTLESS_SH needs to be present under \$PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
BIN=$(dirname "$BIN")
|
||||||
|
|
||||||
|
# set SYSTEMD
|
||||||
|
if systemctl --user show-environment > /dev/null 2>&1; then
|
||||||
|
SYSTEMD=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# HOME verification
|
||||||
|
if [ -z "${HOME:-}" ] || [ ! -d "$HOME" ]; then
|
||||||
|
ERROR "HOME needs to be set"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ ! -w "$HOME" ]; then
|
||||||
|
ERROR "HOME needs to be writable"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set USERNAME from `id -un` and potentially protect backslash
|
||||||
|
# for windbind/samba domain users
|
||||||
|
USERNAME=$(id -un)
|
||||||
|
USERNAME_ESCAPED=$(echo $USERNAME | sed 's/\\/\\\\/g')
|
||||||
|
|
||||||
|
# set CFG_DIR
|
||||||
|
CFG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||||
|
|
||||||
|
# Existing rootful docker verification
|
||||||
|
if [ -w /var/run/docker.sock ] && [ -z "$OPT_FORCE" ]; then
|
||||||
|
ERROR "Aborting because rootful Docker (/var/run/docker.sock) is running and accessible. Set --force to ignore."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate XDG_RUNTIME_DIR and set XDG_RUNTIME_DIR_CREATED
|
||||||
|
if [ -z "${XDG_RUNTIME_DIR:-}" ] || [ ! -w "$XDG_RUNTIME_DIR" ]; then
|
||||||
|
if [ -n "$SYSTEMD" ]; then
|
||||||
|
ERROR "Aborting because systemd was detected but XDG_RUNTIME_DIR (\"$XDG_RUNTIME_DIR\") is not set, does not exist, or is not writable"
|
||||||
|
ERROR "Hint: this could happen if you changed users with 'su' or 'sudo'. To work around this:"
|
||||||
|
ERROR "- try again by first running with root privileges 'loginctl enable-linger <user>' where <user> is the unprivileged user and export XDG_RUNTIME_DIR to the value of RuntimePath as shown by 'loginctl show-user <user>'"
|
||||||
|
ERROR "- or simply log back in as the desired unprivileged user (ssh works for remote machines, machinectl shell works for local machines)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
export XDG_RUNTIME_DIR="$HOME/.docker/run"
|
||||||
|
mkdir -p -m 700 "$XDG_RUNTIME_DIR"
|
||||||
|
XDG_RUNTIME_DIR_CREATED=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
instructions=""
|
||||||
|
# instruction: uidmap dependency check
|
||||||
|
if ! command -v newuidmap > /dev/null 2>&1; then
|
||||||
|
if command -v apt-get > /dev/null 2>&1; then
|
||||||
|
instructions=$(
|
||||||
|
cat <<- EOI
|
||||||
|
${instructions}
|
||||||
|
# Install newuidmap & newgidmap binaries
|
||||||
|
apt-get install -y uidmap
|
||||||
|
EOI
|
||||||
|
)
|
||||||
|
elif command -v dnf > /dev/null 2>&1; then
|
||||||
|
instructions=$(
|
||||||
|
cat <<- EOI
|
||||||
|
${instructions}
|
||||||
|
# Install newuidmap & newgidmap binaries
|
||||||
|
dnf install -y shadow-utils
|
||||||
|
EOI
|
||||||
|
)
|
||||||
|
elif command -v yum > /dev/null 2>&1; then
|
||||||
|
instructions=$(
|
||||||
|
cat <<- EOI
|
||||||
|
${instructions}
|
||||||
|
# Install newuidmap & newgidmap binaries
|
||||||
|
yum install -y shadow-utils
|
||||||
|
EOI
|
||||||
|
)
|
||||||
|
else
|
||||||
|
ERROR "newuidmap binary not found. Please install with a package manager."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# instruction: iptables dependency check
|
||||||
|
faced_iptables_error=""
|
||||||
|
# Many OSs now use iptables-nft by default so, check for module nf_tables by default. But,
|
||||||
|
# if "iptables --version" worked and reported "legacy", check for module ip_tables instead.
|
||||||
|
iptables_module="nf_tables"
|
||||||
|
iptables_command=$(PATH=$PATH:/sbin:/usr/sbin command -v iptables 2> /dev/null) || :
|
||||||
|
if [ -n "$iptables_command" ]; then
|
||||||
|
iptables_version=$($iptables_command --version 2> /dev/null) || :
|
||||||
|
case $iptables_version in
|
||||||
|
*legacy*) iptables_module="ip_tables" ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
faced_iptables_error=1
|
||||||
|
if [ -z "$OPT_SKIP_IPTABLES" ]; then
|
||||||
|
if command -v apt-get > /dev/null 2>&1; then
|
||||||
|
instructions=$(
|
||||||
|
cat <<- EOI
|
||||||
|
${instructions}
|
||||||
|
# Install iptables
|
||||||
|
apt-get install -y iptables
|
||||||
|
EOI
|
||||||
|
)
|
||||||
|
elif command -v dnf > /dev/null 2>&1; then
|
||||||
|
instructions=$(
|
||||||
|
cat <<- EOI
|
||||||
|
${instructions}
|
||||||
|
# Install iptables
|
||||||
|
dnf install -y iptables
|
||||||
|
EOI
|
||||||
|
)
|
||||||
|
elif command -v yum > /dev/null 2>&1; then
|
||||||
|
instructions=$(
|
||||||
|
cat <<- EOI
|
||||||
|
${instructions}
|
||||||
|
# Install iptables
|
||||||
|
yum install -y iptables
|
||||||
|
EOI
|
||||||
|
)
|
||||||
|
else
|
||||||
|
ERROR "iptables binary not found. Please install with a package manager."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# instruction: ip_tables module dependency check
|
||||||
|
if ! grep -q $iptables_module /proc/modules 2> /dev/null && ! grep -q $iptables_module /lib/modules/$(uname -r)/modules.builtin 2> /dev/null; then
|
||||||
|
faced_iptables_error=1
|
||||||
|
if [ -z "$OPT_SKIP_IPTABLES" ]; then
|
||||||
|
instructions=$(
|
||||||
|
cat <<- EOI
|
||||||
|
${instructions}
|
||||||
|
# Load $iptables_module module
|
||||||
|
modprobe $iptables_module
|
||||||
|
EOI
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# set DOCKERD_ROOTLESS_SH_FLAGS
|
||||||
|
if [ -n "$faced_iptables_error" ] && [ -n "$OPT_SKIP_IPTABLES" ]; then
|
||||||
|
DOCKERD_ROOTLESS_SH_FLAGS="${DOCKERD_ROOTLESS_SH_FLAGS} --iptables=false"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# instruction: Debian and Arch require setting unprivileged_userns_clone
|
||||||
|
if [ -f /proc/sys/kernel/unprivileged_userns_clone ]; then
|
||||||
|
if [ "1" != "$(cat /proc/sys/kernel/unprivileged_userns_clone)" ]; then
|
||||||
|
instructions=$(
|
||||||
|
cat <<- EOI
|
||||||
|
${instructions}
|
||||||
|
# Set kernel.unprivileged_userns_clone
|
||||||
|
cat <<EOT > /etc/sysctl.d/50-rootless.conf
|
||||||
|
kernel.unprivileged_userns_clone = 1
|
||||||
|
EOT
|
||||||
|
sysctl --system
|
||||||
|
EOI
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# instruction: RHEL/CentOS 7 requires setting max_user_namespaces
|
||||||
|
if [ -f /proc/sys/user/max_user_namespaces ]; then
|
||||||
|
if [ "0" = "$(cat /proc/sys/user/max_user_namespaces)" ]; then
|
||||||
|
instructions=$(
|
||||||
|
cat <<- EOI
|
||||||
|
${instructions}
|
||||||
|
# Set user.max_user_namespaces
|
||||||
|
cat <<EOT > /etc/sysctl.d/51-rootless.conf
|
||||||
|
user.max_user_namespaces = 28633
|
||||||
|
EOT
|
||||||
|
sysctl --system
|
||||||
|
EOI
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# instructions: validate subuid for current user
|
||||||
|
if command -v "getsubids" > /dev/null 2>&1; then
|
||||||
|
getsubids "$USERNAME" > /dev/null 2>&1 || getsubids "$(id -u)" > /dev/null 2>&1
|
||||||
|
else
|
||||||
|
grep -q "^$USERNAME_ESCAPED:\|^$(id -u):" /etc/subuid 2> /dev/null
|
||||||
|
fi
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
instructions=$(
|
||||||
|
cat <<- EOI
|
||||||
|
${instructions}
|
||||||
|
# Add subuid entry for ${USERNAME}
|
||||||
|
echo "${USERNAME}:100000:65536" >> /etc/subuid
|
||||||
|
EOI
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# instructions: validate subgid for current user
|
||||||
|
if command -v "getsubids" > /dev/null 2>&1; then
|
||||||
|
getsubids -g "$USERNAME" > /dev/null 2>&1 || getsubids -g "$(id -u)" > /dev/null 2>&1
|
||||||
|
else
|
||||||
|
grep -q "^$USERNAME_ESCAPED:\|^$(id -u):" /etc/subgid 2> /dev/null
|
||||||
|
fi
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
instructions=$(
|
||||||
|
cat <<- EOI
|
||||||
|
${instructions}
|
||||||
|
# Add subgid entry for ${USERNAME}
|
||||||
|
echo "${USERNAME}:100000:65536" >> /etc/subgid
|
||||||
|
EOI
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# fail with instructions if requirements are not satisfied.
|
||||||
|
if [ -n "$instructions" ]; then
|
||||||
|
ERROR "Missing system requirements. Run the following commands to"
|
||||||
|
ERROR "install the requirements and run this tool again."
|
||||||
|
if [ -n "$faced_iptables_error" ] && [ -z "$OPT_SKIP_IPTABLES" ]; then
|
||||||
|
ERROR "Alternatively iptables checks can be disabled with --skip-iptables ."
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
echo "########## BEGIN ##########"
|
||||||
|
echo "sudo sh -eux <<EOF"
|
||||||
|
echo "$instructions" | sed -e '/^$/d'
|
||||||
|
echo "EOF"
|
||||||
|
echo "########## END ##########"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# TODO: support printing non-essential but recommended instructions:
|
||||||
|
# - sysctl: "net.ipv4.ping_group_range"
|
||||||
|
# - sysctl: "net.ipv4.ip_unprivileged_port_start"
|
||||||
|
# - external binary: slirp4netns
|
||||||
|
# - external binary: fuse-overlayfs
|
||||||
|
}
|
||||||
|
|
||||||
|
# CLI subcommand: "check"
|
||||||
|
cmd_entrypoint_check() {
|
||||||
|
init
|
||||||
|
# requirements are already checked in init()
|
||||||
|
INFO "Requirements are satisfied"
|
||||||
|
}
|
||||||
|
|
||||||
|
# CLI subcommand: "nsenter"
|
||||||
|
cmd_entrypoint_nsenter() {
|
||||||
|
# No need to call init()
|
||||||
|
pid=$(cat "$XDG_RUNTIME_DIR/dockerd-rootless/child_pid")
|
||||||
|
exec nsenter --no-fork --wd="$(pwd)" --preserve-credentials -m -n -U -t "$pid" -- "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_systemd_error() {
|
||||||
|
n="20"
|
||||||
|
ERROR "Failed to start ${SYSTEMD_UNIT}. Run \`journalctl -n ${n} --no-pager --user --unit ${SYSTEMD_UNIT}\` to show the error log."
|
||||||
|
ERROR "Before retrying installation, you might need to uninstall the current setup: \`$0 uninstall -f ; ${BIN}/rootlesskit rm -rf ${HOME}/.local/share/docker\`"
|
||||||
|
if journalctl -q -n ${n} --user --unit ${SYSTEMD_UNIT} | grep -qF "/run/xtables.lock: Permission denied"; then
|
||||||
|
ERROR "Failure likely related to https://github.com/moby/moby/issues/41230"
|
||||||
|
ERROR "This may work as a workaround: \`sudo dnf install -y policycoreutils-python-utils && sudo semanage permissive -a iptables_t\`"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# install (systemd)
|
||||||
|
install_systemd() {
|
||||||
|
mkdir -p "${CFG_DIR}/systemd/user"
|
||||||
|
unit_file="${CFG_DIR}/systemd/user/${SYSTEMD_UNIT}"
|
||||||
|
if [ -f "${unit_file}" ]; then
|
||||||
|
WARNING "File already exists, skipping: ${unit_file}"
|
||||||
|
else
|
||||||
|
INFO "Creating ${unit_file}"
|
||||||
|
cat <<- EOT > "${unit_file}"
|
||||||
|
[Unit]
|
||||||
|
Description=Docker Application Container Engine (Rootless)
|
||||||
|
Documentation=https://docs.docker.com/go/rootless/
|
||||||
|
Requires=dbus.socket
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Environment=PATH=$BIN:/sbin:/usr/sbin:$PATH
|
||||||
|
ExecStart=$BIN/dockerd-rootless.sh $DOCKERD_ROOTLESS_SH_FLAGS
|
||||||
|
ExecReload=/bin/kill -s HUP \$MAINPID
|
||||||
|
TimeoutSec=0
|
||||||
|
RestartSec=2
|
||||||
|
Restart=always
|
||||||
|
StartLimitBurst=3
|
||||||
|
StartLimitInterval=60s
|
||||||
|
LimitNOFILE=infinity
|
||||||
|
LimitNPROC=infinity
|
||||||
|
LimitCORE=infinity
|
||||||
|
TasksMax=infinity
|
||||||
|
Delegate=yes
|
||||||
|
Type=notify
|
||||||
|
NotifyAccess=all
|
||||||
|
KillMode=mixed
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
EOT
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
fi
|
||||||
|
if ! systemctl --user --no-pager status "${SYSTEMD_UNIT}" > /dev/null 2>&1; then
|
||||||
|
INFO "starting systemd service ${SYSTEMD_UNIT}"
|
||||||
|
(
|
||||||
|
set -x
|
||||||
|
if ! systemctl --user start "${SYSTEMD_UNIT}"; then
|
||||||
|
set +x
|
||||||
|
show_systemd_error
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 3
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
(
|
||||||
|
set -x
|
||||||
|
if ! systemctl --user --no-pager --full status "${SYSTEMD_UNIT}"; then
|
||||||
|
set +x
|
||||||
|
show_systemd_error
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
DOCKER_HOST="unix://$XDG_RUNTIME_DIR/docker.sock" $BIN/docker version
|
||||||
|
systemctl --user enable "${SYSTEMD_UNIT}"
|
||||||
|
)
|
||||||
|
INFO "Installed ${SYSTEMD_UNIT} successfully."
|
||||||
|
INFO "To control ${SYSTEMD_UNIT}, run: \`systemctl --user (start|stop|restart) ${SYSTEMD_UNIT}\`"
|
||||||
|
INFO "To run ${SYSTEMD_UNIT} on system startup, run: \`sudo loginctl enable-linger ${USERNAME}\`"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
# install (non-systemd)
|
||||||
|
install_nonsystemd() {
|
||||||
|
INFO "systemd not detected, ${DOCKERD_ROOTLESS_SH} needs to be started manually:"
|
||||||
|
echo
|
||||||
|
echo "PATH=$BIN:/sbin:/usr/sbin:\$PATH ${DOCKERD_ROOTLESS_SH} ${DOCKERD_ROOTLESS_SH_FLAGS}"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
cli_ctx_exists() {
|
||||||
|
name="$1"
|
||||||
|
"${BIN}/docker" --context=default context inspect -f "{{.Name}}" "${name}" > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
cli_ctx_create() {
|
||||||
|
name="$1"
|
||||||
|
host="$2"
|
||||||
|
description="$3"
|
||||||
|
"${BIN}/docker" --context=default context create "${name}" --docker "host=${host}" --description "${description}" > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
cli_ctx_use() {
|
||||||
|
name="$1"
|
||||||
|
"${BIN}/docker" --context=default context use "${name}" > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
cli_ctx_rm() {
|
||||||
|
name="$1"
|
||||||
|
"${BIN}/docker" --context=default context rm -f "${name}" > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# CLI subcommand: "install"
|
||||||
|
cmd_entrypoint_install() {
|
||||||
|
init
|
||||||
|
# Most requirements are already checked in init(), except the smoke test below for RootlessKit.
|
||||||
|
# https://github.com/docker/docker-install/issues/417
|
||||||
|
|
||||||
|
# check RootlessKit functionality. RootlessKit will print hints if something is still unsatisfied.
|
||||||
|
# (e.g., `kernel.apparmor_restrict_unprivileged_userns` constraint)
|
||||||
|
if ! rootlesskit true; then
|
||||||
|
if [ -z "$OPT_FORCE" ]; then
|
||||||
|
ERROR "RootlessKit failed, see the error messages and https://rootlesscontaine.rs/getting-started/common/ . Set --force to ignore."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
WARNING "RootlessKit failed, see the error messages and https://rootlesscontaine.rs/getting-started/common/ ."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$SYSTEMD" ]; then
|
||||||
|
install_nonsystemd
|
||||||
|
else
|
||||||
|
install_systemd
|
||||||
|
fi
|
||||||
|
|
||||||
|
if cli_ctx_exists "${CLI_CONTEXT}"; then
|
||||||
|
INFO "CLI context \"${CLI_CONTEXT}\" already exists"
|
||||||
|
else
|
||||||
|
INFO "Creating CLI context \"${CLI_CONTEXT}\""
|
||||||
|
cli_ctx_create "${CLI_CONTEXT}" "unix://${XDG_RUNTIME_DIR}/docker.sock" "Rootless mode"
|
||||||
|
fi
|
||||||
|
|
||||||
|
INFO "Using CLI context \"${CLI_CONTEXT}\""
|
||||||
|
cli_ctx_use "${CLI_CONTEXT}"
|
||||||
|
|
||||||
|
echo
|
||||||
|
INFO "Make sure the following environment variable(s) are set (or add them to ~/.bashrc):"
|
||||||
|
if [ -n "$XDG_RUNTIME_DIR_CREATED" ]; then
|
||||||
|
echo "# WARNING: systemd not found. You have to remove XDG_RUNTIME_DIR manually on every logout."
|
||||||
|
echo "export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR}"
|
||||||
|
fi
|
||||||
|
echo "export PATH=${BIN}:\$PATH"
|
||||||
|
echo
|
||||||
|
INFO "Some applications may require the following environment variable too:"
|
||||||
|
echo "export DOCKER_HOST=unix://${XDG_RUNTIME_DIR}/docker.sock"
|
||||||
|
echo
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# CLI subcommand: "uninstall"
|
||||||
|
cmd_entrypoint_uninstall() {
|
||||||
|
init
|
||||||
|
# requirements are already checked in init()
|
||||||
|
if [ -z "$SYSTEMD" ]; then
|
||||||
|
INFO "systemd not detected, ${DOCKERD_ROOTLESS_SH} needs to be stopped manually:"
|
||||||
|
else
|
||||||
|
unit_file="${CFG_DIR}/systemd/user/${SYSTEMD_UNIT}"
|
||||||
|
(
|
||||||
|
set -x
|
||||||
|
systemctl --user stop "${SYSTEMD_UNIT}"
|
||||||
|
) || :
|
||||||
|
(
|
||||||
|
set -x
|
||||||
|
systemctl --user disable "${SYSTEMD_UNIT}"
|
||||||
|
) || :
|
||||||
|
rm -f "${unit_file}"
|
||||||
|
INFO "Uninstalled ${SYSTEMD_UNIT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if cli_ctx_exists "${CLI_CONTEXT}"; then
|
||||||
|
cli_ctx_rm "${CLI_CONTEXT}"
|
||||||
|
INFO "Deleted CLI context \"${CLI_CONTEXT}\""
|
||||||
|
fi
|
||||||
|
unset DOCKER_HOST
|
||||||
|
unset DOCKER_CONTEXT
|
||||||
|
cli_ctx_use "default"
|
||||||
|
INFO 'Configured CLI to use the "default" context.'
|
||||||
|
INFO
|
||||||
|
INFO 'Make sure to unset or update the environment PATH, DOCKER_HOST, and DOCKER_CONTEXT environment variables if you have added them to `~/.bashrc`.'
|
||||||
|
INFO "This uninstallation tool does NOT remove Docker binaries and data."
|
||||||
|
INFO "To remove data, run: \`$BIN/rootlesskit rm -rf $HOME/.local/share/docker\`"
|
||||||
|
}
|
||||||
|
|
||||||
|
# text for --help
|
||||||
|
usage() {
|
||||||
|
echo "Usage: ${ARG0} [OPTIONS] COMMAND"
|
||||||
|
echo
|
||||||
|
echo "A setup tool for Rootless Docker (${DOCKERD_ROOTLESS_SH})."
|
||||||
|
echo
|
||||||
|
echo "Documentation: https://docs.docker.com/go/rootless/"
|
||||||
|
echo
|
||||||
|
echo "Options:"
|
||||||
|
echo " -f, --force Ignore rootful Docker (/var/run/docker.sock)"
|
||||||
|
echo " --skip-iptables Ignore missing iptables"
|
||||||
|
echo
|
||||||
|
echo "Commands:"
|
||||||
|
echo " check Check prerequisites"
|
||||||
|
echo " nsenter Enter into RootlessKit namespaces (mostly for debugging)"
|
||||||
|
echo " install Install systemd unit (if systemd is available) and show how to manage the service"
|
||||||
|
echo " uninstall Uninstall systemd unit"
|
||||||
|
}
|
||||||
|
|
||||||
|
# parse CLI args
|
||||||
|
if ! args="$(getopt -o hf --long help,force,skip-iptables -n "$ARG0" -- "$@")"; then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
eval set -- "$args"
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
arg="$1"
|
||||||
|
shift
|
||||||
|
case "$arg" in
|
||||||
|
-h | --help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-f | --force)
|
||||||
|
OPT_FORCE=1
|
||||||
|
;;
|
||||||
|
--skip-iptables)
|
||||||
|
OPT_SKIP_IPTABLES=1
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# XXX this means we missed something in our "getopt" arguments above!
|
||||||
|
ERROR "Scripting error, unknown argument '$arg' when parsing script arguments."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
command="${1:-}"
|
||||||
|
if [ -z "$command" ]; then
|
||||||
|
ERROR "No command was specified. Run with --help to see the usage. Maybe you want to run \`$ARG0 install\`?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v "cmd_entrypoint_${command}" > /dev/null 2>&1; then
|
||||||
|
ERROR "Unknown command: ${command}. Run with --help to see the usage."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# main
|
||||||
|
"cmd_entrypoint_${command}" "$@"
|
203
Moodle/Tools/dockerd-rootless.sh
Normal file
203
Moodle/Tools/dockerd-rootless.sh
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# dockerd-rootless.sh executes dockerd in rootless mode.
|
||||||
|
#
|
||||||
|
# Usage: dockerd-rootless.sh [DOCKERD_OPTIONS]
|
||||||
|
#
|
||||||
|
# External dependencies:
|
||||||
|
# * newuidmap and newgidmap needs to be installed.
|
||||||
|
# * /etc/subuid and /etc/subgid needs to be configured for the current user.
|
||||||
|
# * Either one of slirp4netns (>= v0.4.0), VPNKit, lxc-user-nic needs to be installed.
|
||||||
|
#
|
||||||
|
# Recognized environment variables:
|
||||||
|
# * DOCKERD_ROOTLESS_ROOTLESSKIT_STATE_DIR=DIR: the rootlesskit state dir. Defaults to "$XDG_RUNTIME_DIR/dockerd-rootless".
|
||||||
|
# * DOCKERD_ROOTLESS_ROOTLESSKIT_NET=(slirp4netns|vpnkit|pasta|lxc-user-nic): the rootlesskit network driver. Defaults to "slirp4netns" if slirp4netns (>= v0.4.0) is installed. Otherwise defaults to "vpnkit".
|
||||||
|
# * DOCKERD_ROOTLESS_ROOTLESSKIT_MTU=NUM: the MTU value for the rootlesskit network driver. Defaults to 65520 for slirp4netns, 1500 for other drivers.
|
||||||
|
# * DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=(builtin|slirp4netns|implicit): the rootlesskit port driver. Defaults to "builtin".
|
||||||
|
# * DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SANDBOX=(auto|true|false): whether to protect slirp4netns with a dedicated mount namespace. Defaults to "auto".
|
||||||
|
# * DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SECCOMP=(auto|true|false): whether to protect slirp4netns with seccomp. Defaults to "auto".
|
||||||
|
# * DOCKERD_ROOTLESS_ROOTLESSKIT_DISABLE_HOST_LOOPBACK=(true|false): prohibit connections to 127.0.0.1 on the host (including via 10.0.2.2, in the case of slirp4netns). Defaults to "true".
|
||||||
|
|
||||||
|
# To apply an environment variable via systemd, create ~/.config/systemd/user/docker.service.d/override.conf as follows,
|
||||||
|
# and run `systemctl --user daemon-reload && systemctl --user restart docker`:
|
||||||
|
# --- BEGIN ---
|
||||||
|
# [Service]
|
||||||
|
# Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_NET=pasta"
|
||||||
|
# Environment="DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=implicit"
|
||||||
|
# --- END ---
|
||||||
|
|
||||||
|
# Guide to choose the network driver and the port driver:
|
||||||
|
#
|
||||||
|
# Network driver | Port driver | Net throughput | Port throughput | Src IP | No SUID | Note
|
||||||
|
# ---------------|----------------|----------------|-----------------|--------|---------|---------------------------------------------------------
|
||||||
|
# slirp4netns | builtin | Slow | Fast ✅ | ❌ | ✅ | Default in typical setup
|
||||||
|
# vpnkit | builtin | Slow | Fast ✅ | ❌ | ✅ | Default when slirp4netns is not installed
|
||||||
|
# slirp4netns | slirp4netns | Slow | Slow | ✅ | ✅ |
|
||||||
|
# pasta | implicit | Slow | Fast ✅ | ✅ | ✅ | Experimental; Needs recent version of pasta (2023_12_04)
|
||||||
|
# lxc-user-nic | builtin | Fast ✅ | Fast ✅ | ❌ | ❌ | Experimental
|
||||||
|
# (bypass4netns) | (bypass4netns) | Fast ✅ | Fast ✅ | ✅ | ✅ | (Not integrated to RootlessKit)
|
||||||
|
|
||||||
|
# See the documentation for the further information: https://docs.docker.com/go/rootless/
|
||||||
|
|
||||||
|
set -e -x
|
||||||
|
case "$1" in
|
||||||
|
"check" | "install" | "uninstall")
|
||||||
|
echo "Did you mean 'dockerd-rootless-setuptool.sh $@' ?"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if ! [ -w "$XDG_RUNTIME_DIR" ]; then
|
||||||
|
echo "XDG_RUNTIME_DIR needs to be set and writable"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! [ -d "$HOME" ]; then
|
||||||
|
echo "HOME needs to be set and exist."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mount_directory() {
|
||||||
|
if [ -z "$_DOCKERD_ROOTLESS_CHILD" ]; then
|
||||||
|
echo "mount_directory should be called from the child context. Otherwise data loss is at risk" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DIRECTORY="$1"
|
||||||
|
if [ ! -d "$DIRECTORY" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Bind mount directory: this makes this directory visible to
|
||||||
|
# Dockerd, even if it is originally a symlink, given Dockerd does
|
||||||
|
# not always follow symlinks. Some directories might also be
|
||||||
|
# "copied-up", meaning that they will also be writable on the child
|
||||||
|
# namespace; this will be the case only if they are provided as
|
||||||
|
# --copy-up to the rootlesskit.
|
||||||
|
DIRECTORY_REALPATH=$(realpath "$DIRECTORY")
|
||||||
|
MOUNT_OPTIONS="${2:---bind}"
|
||||||
|
rm -rf "$DIRECTORY"
|
||||||
|
mkdir -p "$DIRECTORY"
|
||||||
|
mount $MOUNT_OPTIONS "$DIRECTORY_REALPATH" "$DIRECTORY"
|
||||||
|
}
|
||||||
|
|
||||||
|
rootlesskit=""
|
||||||
|
for f in docker-rootlesskit rootlesskit; do
|
||||||
|
if command -v $f > /dev/null 2>&1; then
|
||||||
|
rootlesskit=$f
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ -z "$rootlesskit" ]; then
|
||||||
|
echo "rootlesskit needs to be installed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
: "${DOCKERD_ROOTLESS_ROOTLESSKIT_STATE_DIR:=$XDG_RUNTIME_DIR/dockerd-rootless}"
|
||||||
|
: "${DOCKERD_ROOTLESS_ROOTLESSKIT_NET:=}"
|
||||||
|
: "${DOCKERD_ROOTLESS_ROOTLESSKIT_MTU:=}"
|
||||||
|
: "${DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER:=builtin}"
|
||||||
|
: "${DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SANDBOX:=auto}"
|
||||||
|
: "${DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SECCOMP:=auto}"
|
||||||
|
: "${DOCKERD_ROOTLESS_ROOTLESSKIT_DISABLE_HOST_LOOPBACK:=}"
|
||||||
|
net=$DOCKERD_ROOTLESS_ROOTLESSKIT_NET
|
||||||
|
mtu=$DOCKERD_ROOTLESS_ROOTLESSKIT_MTU
|
||||||
|
if [ -z "$net" ]; then
|
||||||
|
if command -v slirp4netns > /dev/null 2>&1; then
|
||||||
|
# If --netns-type is present in --help, slirp4netns is >= v0.4.0.
|
||||||
|
if slirp4netns --help | grep -qw -- --netns-type; then
|
||||||
|
net=slirp4netns
|
||||||
|
if [ -z "$mtu" ]; then
|
||||||
|
mtu=65520
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "slirp4netns found but seems older than v0.4.0. Falling back to VPNKit."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -z "$net" ]; then
|
||||||
|
if command -v vpnkit > /dev/null 2>&1; then
|
||||||
|
net=vpnkit
|
||||||
|
else
|
||||||
|
echo "Either slirp4netns (>= v0.4.0) or vpnkit needs to be installed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -z "$mtu" ]; then
|
||||||
|
mtu=1500
|
||||||
|
fi
|
||||||
|
|
||||||
|
host_loopback="--disable-host-loopback"
|
||||||
|
if [ "$DOCKERD_ROOTLESS_ROOTLESSKIT_DISABLE_HOST_LOOPBACK" = "false" ]; then
|
||||||
|
host_loopback=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
dockerd="${DOCKERD:-dockerd}"
|
||||||
|
|
||||||
|
if [ -z "$_DOCKERD_ROOTLESS_CHILD" ]; then
|
||||||
|
_DOCKERD_ROOTLESS_CHILD=1
|
||||||
|
export _DOCKERD_ROOTLESS_CHILD
|
||||||
|
if [ "$(id -u)" = "0" ]; then
|
||||||
|
echo "This script must be executed as a non-privileged user"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# `selinuxenabled` always returns false in RootlessKit child, so we execute `selinuxenabled` in the parent.
|
||||||
|
# https://github.com/rootless-containers/rootlesskit/issues/94
|
||||||
|
if command -v selinuxenabled > /dev/null 2>&1 && selinuxenabled; then
|
||||||
|
_DOCKERD_ROOTLESS_SELINUX=1
|
||||||
|
export _DOCKERD_ROOTLESS_SELINUX
|
||||||
|
fi
|
||||||
|
# Re-exec the script via RootlessKit, so as to create unprivileged {user,mount,network} namespaces.
|
||||||
|
#
|
||||||
|
# --copy-up allows removing/creating files in the directories by creating tmpfs and symlinks
|
||||||
|
# * /etc: copy-up is required so as to prevent `/etc/resolv.conf` in the
|
||||||
|
# namespace from being unexpectedly unmounted when `/etc/resolv.conf` is recreated on the host
|
||||||
|
# (by either systemd-networkd or NetworkManager)
|
||||||
|
# * /run: copy-up is required so that we can create /run/docker (hardcoded for plugins) in our namespace
|
||||||
|
exec $rootlesskit \
|
||||||
|
--state-dir=$DOCKERD_ROOTLESS_ROOTLESSKIT_STATE_DIR \
|
||||||
|
--net=$net --mtu=$mtu \
|
||||||
|
--slirp4netns-sandbox=$DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SANDBOX \
|
||||||
|
--slirp4netns-seccomp=$DOCKERD_ROOTLESS_ROOTLESSKIT_SLIRP4NETNS_SECCOMP \
|
||||||
|
$host_loopback --port-driver=$DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER \
|
||||||
|
--copy-up=/etc --copy-up=/run \
|
||||||
|
--propagation=rslave \
|
||||||
|
$DOCKERD_ROOTLESS_ROOTLESSKIT_FLAGS \
|
||||||
|
"$0" "$@"
|
||||||
|
else
|
||||||
|
[ "$_DOCKERD_ROOTLESS_CHILD" = 1 ]
|
||||||
|
|
||||||
|
# The Container Device Interface (CDI) specs can be found by default
|
||||||
|
# under {/etc,/var/run}/cdi. More information at:
|
||||||
|
# https://github.com/cncf-tags/container-device-interface
|
||||||
|
#
|
||||||
|
# In order to use the Container Device Interface (CDI) integration,
|
||||||
|
# the CDI paths need to exist before the Docker daemon is started in
|
||||||
|
# order for it to read the CDI specification files. Otherwise, a
|
||||||
|
# Docker daemon restart will be required for the daemon to discover
|
||||||
|
# them.
|
||||||
|
#
|
||||||
|
# If another set of CDI paths (other than the default /etc/cdi and
|
||||||
|
# /var/run/cdi) are configured through the Docker configuration file
|
||||||
|
# (using "cdi-spec-dirs"), they need to be bind mounted in rootless
|
||||||
|
# mode; otherwise the Docker daemon won't have access to the CDI
|
||||||
|
# specification files.
|
||||||
|
mount_directory /etc/cdi
|
||||||
|
mount_directory /var/run/cdi
|
||||||
|
|
||||||
|
# remove the symlinks for the existing files in the parent namespace if any,
|
||||||
|
# so that we can create our own files in our mount namespace.
|
||||||
|
rm -f /run/docker /run/containerd /run/xtables.lock
|
||||||
|
|
||||||
|
if [ -n "$_DOCKERD_ROOTLESS_SELINUX" ]; then
|
||||||
|
# iptables requires /run in the child to be relabeled. The actual /run in the parent is unaffected.
|
||||||
|
# https://github.com/containers/podman/blob/e6fc34b71aa9d876b1218efe90e14f8b912b0603/libpod/networking_linux.go#L396-L401
|
||||||
|
# https://github.com/moby/moby/issues/41230
|
||||||
|
chcon system_u:object_r:iptables_var_run_t:s0 /run
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(stat -c %T -f /etc)" = "tmpfs" ] && [ -L "/etc/ssl" ]; then
|
||||||
|
# Workaround for "x509: certificate signed by unknown authority" on openSUSE Tumbleweed.
|
||||||
|
# https://github.com/rootless-containers/rootlesskit/issues/225
|
||||||
|
mount_directory /etc/ssl "--rbind"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$dockerd" "$@"
|
||||||
|
fi
|
21
Moodle/Tools/entovi.sh
Normal file
21
Moodle/Tools/entovi.sh
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Tìm tất cả các tệp có tên kết thúc bằng .en.vtt nhưng không phải *.en.en.vtt hoặc *.vi.vi.vtt
|
||||||
|
find . -type f -name "*.en.vtt" ! -name "*.en.en.vtt" ! -name "*.vi.vi.vtt" -print0 | while IFS= read -r -d $'\0' file; do
|
||||||
|
# Lấy phần tên tệp trước .en.vtt
|
||||||
|
base_name=$(basename "$file" .en.vtt)
|
||||||
|
|
||||||
|
# Lấy thư mục chứa tệp
|
||||||
|
dir_name=$(dirname "$file")
|
||||||
|
|
||||||
|
# Tạo đường dẫn đầy đủ cho tệp đích
|
||||||
|
destination_file="${dir_name}/${base_name}.vi.vtt"
|
||||||
|
|
||||||
|
# Sao chép tệp
|
||||||
|
cp "$file" "$destination_file"
|
||||||
|
|
||||||
|
# In thông báo
|
||||||
|
echo "Đã sao chép: \"$file\" thành \"$destination_file\""
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Hoàn tất quá trình sao chép."
|
15
Moodle/Tools/envtt.sh
Normal file
15
Moodle/Tools/envtt.sh
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Quét toàn bộ file *.vtt (trừ *.en.vtt và *.vi.vtt) trong thư mục hiện tại và thư mục con
|
||||||
|
find . -type f -name "*.vtt" ! -name "*.en.vtt" ! -name "*.vi.vtt" | while read file; do
|
||||||
|
# Tạo tên file mới bằng cách thay .vtt thành .en.vtt
|
||||||
|
new_file="${file%.vtt}.en.vtt"
|
||||||
|
|
||||||
|
# Copy nội dung sang file mới
|
||||||
|
cp "$file" "$new_file"
|
||||||
|
|
||||||
|
# Hiển thị thông báo
|
||||||
|
echo "Đã tạo: $new_file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Hoàn thành sao chép tất cả các tệp .vtt thành .en.vtt (trừ *.en.vtt và *.vi.vtt)"
|
33
Moodle/Tools/getdesrt.sh
Normal file
33
Moodle/Tools/getdesrt.sh
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Set the custom cache directory for Whisper models
|
||||||
|
export XDG_CACHE_HOME="/backup/whisper"
|
||||||
|
|
||||||
|
# Create the cache directory if it does not exist
|
||||||
|
mkdir -p "$XDG_CACHE_HOME"
|
||||||
|
|
||||||
|
# Iterate through all MP4 files in the current directory and subdirectories
|
||||||
|
find . -type f -name "*.mp4" | while read -r file; do
|
||||||
|
# Extract the directory and file name without the .mp4 extension
|
||||||
|
dir=$(dirname "$file")
|
||||||
|
filename=$(basename "$file" .mp4)
|
||||||
|
srt_file="$dir/$filename.srt"
|
||||||
|
|
||||||
|
# Check if the corresponding SRT file exists
|
||||||
|
if [ ! -f "$srt_file" ]; then
|
||||||
|
# Run Whisper to generate SRT subtitles with English as the source language
|
||||||
|
echo "Generating subtitles for: $file"
|
||||||
|
whisper "$file" --model medium --output_format srt --task transcribe
|
||||||
|
|
||||||
|
# Rename the generated subtitle file to match the required format
|
||||||
|
if [ -f "$dir/$filename_en.srt" ]; then
|
||||||
|
mv "$dir/$filename_en.srt" "$srt_file"
|
||||||
|
else
|
||||||
|
echo "Warning: Expected $dir/$filename_en.srt not found."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Subtitle already exists for: $file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Process completed!"
|
2
Moodle/Tools/gets.sh
Normal file
2
Moodle/Tools/gets.sh
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
wget --no-check-certificate --content-disposition --max-redirect=10 --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36" --referer="https://en.git.ir/" -i list.txt
|
||||||
|
|
33
Moodle/Tools/getsrt.sh
Normal file
33
Moodle/Tools/getsrt.sh
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Set the custom cache directory for Whisper models
|
||||||
|
export XDG_CACHE_HOME="/backup/whisper"
|
||||||
|
|
||||||
|
# Create the cache directory if it does not exist
|
||||||
|
mkdir -p "$XDG_CACHE_HOME"
|
||||||
|
|
||||||
|
# Iterate through all MP4 files in the current directory and subdirectories
|
||||||
|
find . -type f -name "*.mp4" | while read -r file; do
|
||||||
|
# Extract the directory and file name without the .mp4 extension
|
||||||
|
dir=$(dirname "$file")
|
||||||
|
filename=$(basename "$file" .mp4)
|
||||||
|
srt_file="$dir/$filename.srt"
|
||||||
|
|
||||||
|
# Check if the corresponding SRT file exists
|
||||||
|
if [ ! -f "$srt_file" ]; then
|
||||||
|
# Run Whisper to generate SRT subtitles with English as the source language
|
||||||
|
echo "Generating subtitles for: $file"
|
||||||
|
whisper "$file" --model medium --output_format srt --task transcribe --language en --output_dir "$dir"
|
||||||
|
|
||||||
|
# Rename the generated subtitle file to match the required format
|
||||||
|
if [ -f "$dir/$filename_en.srt" ]; then
|
||||||
|
mv "$dir/$filename_en.srt" "$srt_file"
|
||||||
|
else
|
||||||
|
echo "Warning: Expected $dir/$filename_en.srt not found."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Subtitle already exists for: $file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Process completed!"
|
135
Moodle/Tools/gettext.sh
Normal file
135
Moodle/Tools/gettext.sh
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#!/usr/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (C) 2003, 2005-2007, 2011, 2018-2020 Free Software Foundation, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Find a way to echo strings without interpreting backslash.
|
||||||
|
if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then
|
||||||
|
echo='echo'
|
||||||
|
else
|
||||||
|
if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then
|
||||||
|
echo='printf %s\n'
|
||||||
|
else
|
||||||
|
echo_func () {
|
||||||
|
cat <<EOT
|
||||||
|
$*
|
||||||
|
EOT
|
||||||
|
}
|
||||||
|
echo='echo_func'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# This script is primarily a shell function library. In order for
|
||||||
|
# ". gettext.sh" to find it, we install it in $PREFIX/bin (that is usually
|
||||||
|
# contained in $PATH), rather than in some other location such as
|
||||||
|
# $PREFIX/share/sh-scripts or $PREFIX/share/gettext. In order to not violate
|
||||||
|
# the Filesystem Hierarchy Standard when doing so, this script is executable.
|
||||||
|
# Therefore it needs to support the standard --help and --version.
|
||||||
|
if test -z "${ZSH_VERSION+set}"; then
|
||||||
|
# zsh is not POSIX compliant: By default, while ". gettext.sh" is executed,
|
||||||
|
# it sets $0 to "gettext.sh", defeating the purpose of this test. But
|
||||||
|
# fortunately we know that when running under zsh, this script is always
|
||||||
|
# being sourced, not executed, because hardly anyone is crazy enough to
|
||||||
|
# install zsh as /bin/sh.
|
||||||
|
case "$0" in
|
||||||
|
gettext.sh | */gettext.sh | *\\gettext.sh)
|
||||||
|
progname=$0
|
||||||
|
package=gettext-runtime
|
||||||
|
version=0.21
|
||||||
|
# func_usage
|
||||||
|
# outputs to stdout the --help usage message.
|
||||||
|
func_usage ()
|
||||||
|
{
|
||||||
|
echo "GNU gettext shell script function library version $version"
|
||||||
|
echo "Usage: . gettext.sh"
|
||||||
|
}
|
||||||
|
# func_version
|
||||||
|
# outputs to stdout the --version message.
|
||||||
|
func_version ()
|
||||||
|
{
|
||||||
|
echo "$progname (GNU $package) $version"
|
||||||
|
echo "Copyright (C) 2003-2020 Free Software Foundation, Inc.
|
||||||
|
License GPLv2+: GNU GPL version 2 or later <https://gnu.org/licenses/gpl.html>
|
||||||
|
This is free software: you are free to change and redistribute it.
|
||||||
|
There is NO WARRANTY, to the extent permitted by law."
|
||||||
|
echo "Written by" "Bruno Haible"
|
||||||
|
}
|
||||||
|
if test $# = 1; then
|
||||||
|
case "$1" in
|
||||||
|
--help | --hel | --he | --h )
|
||||||
|
func_usage; exit 0 ;;
|
||||||
|
--version | --versio | --versi | --vers | --ver | --ve | --v )
|
||||||
|
func_version; exit 0 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
func_usage 1>&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# eval_gettext MSGID
|
||||||
|
# looks up the translation of MSGID and substitutes shell variables in the
|
||||||
|
# result.
|
||||||
|
eval_gettext () {
|
||||||
|
gettext "$1" | (export PATH `envsubst --variables "$1"`; envsubst "$1")
|
||||||
|
}
|
||||||
|
|
||||||
|
# eval_ngettext MSGID MSGID-PLURAL COUNT
|
||||||
|
# looks up the translation of MSGID / MSGID-PLURAL for COUNT and substitutes
|
||||||
|
# shell variables in the result.
|
||||||
|
eval_ngettext () {
|
||||||
|
ngettext "$1" "$2" "$3" | (export PATH `envsubst --variables "$1 $2"`; envsubst "$1 $2")
|
||||||
|
}
|
||||||
|
|
||||||
|
# eval_pgettext MSGCTXT MSGID
|
||||||
|
# looks up the translation of MSGID in the context MSGCTXT and substitutes
|
||||||
|
# shell variables in the result.
|
||||||
|
eval_pgettext () {
|
||||||
|
gettext --context="$1" "$2" | (export PATH `envsubst --variables "$2"`; envsubst "$2")
|
||||||
|
}
|
||||||
|
|
||||||
|
# eval_npgettext MSGCTXT MSGID MSGID-PLURAL COUNT
|
||||||
|
# looks up the translation of MSGID / MSGID-PLURAL for COUNT in the context
|
||||||
|
# MSGCTXT and substitutes shell variables in the result.
|
||||||
|
eval_npgettext () {
|
||||||
|
ngettext --context="$1" "$2" "$3" "$4" | (export PATH `envsubst --variables "$2 $3"`; envsubst "$2 $3")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Note: This use of envsubst is much safer than using the shell built-in 'eval'
|
||||||
|
# would be.
|
||||||
|
# 1) The security problem with Chinese translations that happen to use a
|
||||||
|
# character such as \xe0\x60 is avoided.
|
||||||
|
# 2) The security problem with malevolent translators who put in command lists
|
||||||
|
# like "$(...)" or "`...`" is avoided.
|
||||||
|
# 3) The translations can only refer to shell variables that are already
|
||||||
|
# mentioned in MSGID or MSGID-PLURAL.
|
||||||
|
#
|
||||||
|
# Note: "export PATH" above is a dummy; this is for the case when
|
||||||
|
# `envsubst --variables ...` returns nothing.
|
||||||
|
#
|
||||||
|
# Note: In eval_ngettext above, "$1 $2" means a string whose variables set is
|
||||||
|
# the union of the variables set of "$1" and "$2".
|
||||||
|
#
|
||||||
|
# Note: The minimal use of backquote above ensures that trailing newlines are
|
||||||
|
# not dropped, not from the gettext invocation and not from the value of any
|
||||||
|
# shell variable.
|
||||||
|
#
|
||||||
|
# Note: Field splitting on the `envsubst --variables ...` result is desired,
|
||||||
|
# since envsubst outputs the variables, separated by newlines. Pathname
|
||||||
|
# wildcard expansion or tilde expansion has no effect here, since the words
|
||||||
|
# output by "envsubst --variables ..." consist solely of alphanumeric
|
||||||
|
# characters and underscore.
|
37
Moodle/Tools/getvtt.sh
Normal file
37
Moodle/Tools/getvtt.sh
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Set the custom cache directory for Whisper models
|
||||||
|
export XDG_CACHE_HOME="/backup/whisper"
|
||||||
|
|
||||||
|
# Create the cache directory if it does not exist
|
||||||
|
mkdir -p "$XDG_CACHE_HOME"
|
||||||
|
|
||||||
|
# Function để xử lý từng file (chạy trong từng tiến trình riêng)
|
||||||
|
process_file() {
|
||||||
|
file="$1"
|
||||||
|
dir=$(dirname "$file")
|
||||||
|
filename=$(basename "$file" .mp4)
|
||||||
|
vtt_file="$dir/$filename.vtt"
|
||||||
|
|
||||||
|
if [ ! -f "$vtt_file" ]; then
|
||||||
|
echo "🔄 Generating subtitles for: $file"
|
||||||
|
whisper "$file" --model medium --output_format vtt --task transcribe --language en --output_dir "$dir"
|
||||||
|
|
||||||
|
if [ -f "$dir/${filename}_en.vtt" ]; then
|
||||||
|
mv "$dir/${filename}_en.vtt" "$vtt_file"
|
||||||
|
echo "✅ Created: $vtt_file"
|
||||||
|
else
|
||||||
|
echo "⚠️ Warning: Expected $dir/${filename}_en.vtt not found."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "⏩ Subtitle already exists for: $file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
export -f process_file
|
||||||
|
export XDG_CACHE_HOME
|
||||||
|
|
||||||
|
# Tìm tất cả file .mp4 và xử lý song song với tối đa 4 tiến trình
|
||||||
|
find . -type f -name "*.mp4" | xargs -n 1 -P 4 -I {} bash -c 'process_file "$@"' _ {}
|
||||||
|
|
||||||
|
echo "🎉 All done!"
|
65
Moodle/Tools/mkv2mp4.sh
Normal file
65
Moodle/Tools/mkv2mp4.sh
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Kịch bản chuyển đổi tất cả các file .mkv sang .mp4 một cách đệ quy.
|
||||||
|
# Phiên bản cải tiến: dễ cấu hình, ghi log cho từng file và dùng tùy chọn ffmpeg hiện đại.
|
||||||
|
|
||||||
|
# --- Phần cấu hình ---
|
||||||
|
# Bạn có thể thay đổi các giá trị này để phù hợp với nhu cầu.
|
||||||
|
# CRF (Constant Rate Factor) cho video: 0-51. Càng thấp chất lượng càng cao. 18-28 là khoảng hợp lý.
|
||||||
|
CRF="23"
|
||||||
|
# Preset cho tốc độ encode: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow.
|
||||||
|
# Càng chậm thì nén càng tốt (file nhỏ hơn) nhưng tốn thời gian hơn. 'medium' là mặc định cân bằng.
|
||||||
|
PRESET="medium"
|
||||||
|
# Bitrate cho audio. '128k' hoặc '192k' là phổ biến cho codec AAC.
|
||||||
|
AUDIO_BITRATE="128k"
|
||||||
|
|
||||||
|
# --- Bắt đầu kịch bản ---
|
||||||
|
|
||||||
|
# 1. Kiểm tra xem ffmpeg đã được cài đặt chưa
|
||||||
|
if ! command -v ffmpeg >/dev/null 2>&1; then
|
||||||
|
echo "❌ Lỗi: Lệnh 'ffmpeg' không tồn tại. Vui lòng cài đặt ffmpeg."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Xác định thư mục mục tiêu (lấy tham số đầu vào, mặc định là thư mục hiện tại)
|
||||||
|
DIR="${1:-.}"
|
||||||
|
echo "🔍 Bắt đầu quét thư mục '$DIR'..."
|
||||||
|
echo "---"
|
||||||
|
|
||||||
|
# 3. Tìm và lặp qua từng file .mkv
|
||||||
|
# -print0 và -d '' là cách an toàn nhất để xử lý tên file có chứa ký tự đặc biệt hoặc dấu cách.
|
||||||
|
find "$DIR" -type f -iname '*.mkv' -print0 | while IFS= read -r -d '' file; do
|
||||||
|
# Tạo tên file output bằng cách thay thế đuôi .mkv thành .mp4 một cách hiệu quả
|
||||||
|
out_file="${file%.mkv}.mp4"
|
||||||
|
log_file="${out_file}.log"
|
||||||
|
|
||||||
|
# Kiểm tra nếu file .mp4 đã tồn tại thì bỏ qua
|
||||||
|
if [ -f "$out_file" ]; then
|
||||||
|
echo "⚠️ Bỏ qua (đã tồn tại): '$out_file'"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "▶️ Đang chuyển đổi: '$file'"
|
||||||
|
|
||||||
|
# 4. Thực thi lệnh ffmpeg
|
||||||
|
# -hide_banner: Ẩn thông tin banner của ffmpeg cho log gọn hơn.
|
||||||
|
# -c:v libx264 -crf $CRF -preset $PRESET: Tùy chọn encode video H.264 chất lượng cao.
|
||||||
|
# -c:a aac -b:a $AUDIO_BITRATE: Tùy chọn encode audio AAC.
|
||||||
|
# Ghi log (cả stdout và stderr) vào một file riêng cho mỗi video để tránh ghi đè.
|
||||||
|
ffmpeg -nostdin -hide_banner -i "$file" \
|
||||||
|
-c:v libx264 -crf "$CRF" -preset "$PRESET" \
|
||||||
|
-c:a aac -b:a "$AUDIO_BITRATE" \
|
||||||
|
"$out_file" > "$log_file" 2>&1
|
||||||
|
|
||||||
|
# 5. Kiểm tra kết quả chuyển đổi và thông báo
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "✅ Hoàn thành: '$out_file'"
|
||||||
|
# Nếu muốn tự động xóa log khi thành công, bỏ comment dòng dưới
|
||||||
|
# rm "$log_file"
|
||||||
|
else
|
||||||
|
echo "❌ Lỗi khi chuyển đổi file trên. Xem chi tiết trong log: '$log_file'"
|
||||||
|
fi
|
||||||
|
echo "---"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "🎉 Tất cả đã xong!"
|
13
Moodle/Tools/removeeng.sh
Normal file
13
Moodle/Tools/removeeng.sh
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Qu<51>t c<>c file k?t th<74>c b?ng .en.vtt, .vi.vtt ho?c .srt
|
||||||
|
find . -type f \( -name "*.en.vtt" -o -name "*.vi.vtt" -o -name "*.srt" \) -print0 | while IFS= read -r -d $'\0' file; do
|
||||||
|
# Ki?m tra xem t<>n t?p c<> ch?a ' English' kh<6B>ng
|
||||||
|
if [[ "$file" == *" English."* ]]; then
|
||||||
|
# <20>?i t<>n file b?ng c<>ch x<>a ' English' tru?c ph?n m? r?ng
|
||||||
|
newfile=$(echo "$file" | sed 's/ English\././')
|
||||||
|
|
||||||
|
# <20>?i t<>n t?p
|
||||||
|
mv "$file" "$newfile"
|
||||||
|
|
||||||
|
echo "<EFBFBD><EFBFBD> d?i t<>n: \"$file\" ? \"$
|
14
Moodle/Tools/rename_topics.sh
Normal file
14
Moodle/Tools/rename_topics.sh
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Lấy thư mục hiện hành nơi người dùng đang gọi lệnh
|
||||||
|
TARGET_DIR="$(pwd)"
|
||||||
|
|
||||||
|
# Đổi tên các thư mục con cấp 1 theo mẫu: 1 - ABC → 1. ABC
|
||||||
|
find "$TARGET_DIR" -mindepth 1 -maxdepth 1 -type d -regextype posix-extended -regex '.*/[0-9]+ - .+' | while read dir; do
|
||||||
|
base=$(basename "$dir")
|
||||||
|
newname=$(echo "$base" | sed -E 's/^([0-9]+) - (.+)$/\1. \2/')
|
||||||
|
if [ "$base" != "$newname" ]; then
|
||||||
|
echo "🔁 Đổi tên: $base → $newname"
|
||||||
|
mv "$TARGET_DIR/$base" "$TARGET_DIR/$newname"
|
||||||
|
fi
|
||||||
|
done
|
1401
Moodle/Tools/rescan-scsi-bus.sh
Normal file
1401
Moodle/Tools/rescan-scsi-bus.sh
Normal file
File diff suppressed because it is too large
Load Diff
29
Moodle/Tools/srttovtt.sh
Normal file
29
Moodle/Tools/srttovtt.sh
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Kiểm tra xem ffmpeg đã được cài đặt chưa
|
||||||
|
if ! command -v ffmpeg &> /dev/null; then
|
||||||
|
echo "ffmpeg chưa được cài đặt. Vui lòng cài đặt ffmpeg trước khi chạy script."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tìm tất cả các tệp .srt và chuyển đổi chúng thành .vtt nếu chưa tồn tại
|
||||||
|
find . -type f -name "*.srt" | while read -r srt_file; do
|
||||||
|
# Xác định đường dẫn và tên tệp đích
|
||||||
|
vtt_file="${srt_file%.srt}.vtt"
|
||||||
|
|
||||||
|
# Nếu tệp .vtt đã tồn tại, bỏ qua
|
||||||
|
if [ -f "$vtt_file" ]; then
|
||||||
|
echo "Đã tồn tại: $vtt_file → bỏ qua."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Chuyển đổi tệp .srt sang .vtt bằng ffmpeg
|
||||||
|
ffmpeg -i "$srt_file" "$vtt_file"
|
||||||
|
|
||||||
|
# Kiểm tra nếu chuyển đổi thành công
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Chuyển đổi thành công: $srt_file -> $vtt_file"
|
||||||
|
else
|
||||||
|
echo "Lỗi khi chuyển đổi: $srt_file"
|
||||||
|
fi
|
||||||
|
done
|
1
Moodle/Tools/whisperenv.sh
Normal file
1
Moodle/Tools/whisperenv.sh
Normal file
@@ -0,0 +1 @@
|
|||||||
|
source /whisper-env/bin/activate
|
Reference in New Issue
Block a user