fix: chuẩn hóa tên ảnh tour về ASCII (chua-thai-lac/01..26.jpg)

Tên thư mục/file Unicode tiếng Việt bị lệch chuẩn hóa (thư mục NFC, file NFD)
khiến URL ảnh 404 khi phục vụ tĩnh. Đổi sang đường dẫn ASCII ổn định, tránh
lỗi mã hóa khi triển khai trên mọi máy chủ/CDN. Cập nhật content.ts và
cấu hình tour theo số thứ tự cảnh; dọn scaffold svg mặc định.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
tuyenio
2026-06-23 13:46:42 +07:00
parent dd8b4b20d1
commit 17a7866cd2
33 changed files with 42 additions and 46 deletions
-1
View File
@@ -1 +0,0 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 391 B

-1
View File
@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 22 MiB

After

Width:  |  Height:  |  Size: 22 MiB

Before

Width:  |  Height:  |  Size: 13 MiB

After

Width:  |  Height:  |  Size: 13 MiB

Before

Width:  |  Height:  |  Size: 16 MiB

After

Width:  |  Height:  |  Size: 16 MiB

Before

Width:  |  Height:  |  Size: 12 MiB

After

Width:  |  Height:  |  Size: 12 MiB

Before

Width:  |  Height:  |  Size: 15 MiB

After

Width:  |  Height:  |  Size: 15 MiB

Before

Width:  |  Height:  |  Size: 15 MiB

After

Width:  |  Height:  |  Size: 15 MiB

Before

Width:  |  Height:  |  Size: 23 MiB

After

Width:  |  Height:  |  Size: 23 MiB

Before

Width:  |  Height:  |  Size: 14 MiB

After

Width:  |  Height:  |  Size: 14 MiB

Before

Width:  |  Height:  |  Size: 15 MiB

After

Width:  |  Height:  |  Size: 15 MiB

Before

Width:  |  Height:  |  Size: 15 MiB

After

Width:  |  Height:  |  Size: 15 MiB

Before

Width:  |  Height:  |  Size: 16 MiB

After

Width:  |  Height:  |  Size: 16 MiB

Before

Width:  |  Height:  |  Size: 19 MiB

After

Width:  |  Height:  |  Size: 19 MiB

Before

Width:  |  Height:  |  Size: 12 MiB

After

Width:  |  Height:  |  Size: 12 MiB

Before

Width:  |  Height:  |  Size: 12 MiB

After

Width:  |  Height:  |  Size: 12 MiB

Before

Width:  |  Height:  |  Size: 14 MiB

After

Width:  |  Height:  |  Size: 14 MiB

Before

Width:  |  Height:  |  Size: 10 MiB

After

Width:  |  Height:  |  Size: 10 MiB

Before

Width:  |  Height:  |  Size: 10 MiB

After

Width:  |  Height:  |  Size: 10 MiB

Before

Width:  |  Height:  |  Size: 21 MiB

After

Width:  |  Height:  |  Size: 21 MiB

Before

Width:  |  Height:  |  Size: 16 MiB

After

Width:  |  Height:  |  Size: 16 MiB

Before

Width:  |  Height:  |  Size: 12 MiB

After

Width:  |  Height:  |  Size: 12 MiB

Before

Width:  |  Height:  |  Size: 14 MiB

After

Width:  |  Height:  |  Size: 14 MiB

Before

Width:  |  Height:  |  Size: 13 MiB

After

Width:  |  Height:  |  Size: 13 MiB

Before

Width:  |  Height:  |  Size: 15 MiB

After

Width:  |  Height:  |  Size: 15 MiB

Before

Width:  |  Height:  |  Size: 19 MiB

After

Width:  |  Height:  |  Size: 19 MiB

Before

Width:  |  Height:  |  Size: 16 MiB

After

Width:  |  Height:  |  Size: 16 MiB

Before

Width:  |  Height:  |  Size: 14 MiB

After

Width:  |  Height:  |  Size: 14 MiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

-1
View File
@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 128 B

-1
View File
@@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

Before

Width:  |  Height:  |  Size: 385 B

+7 -7
View File
@@ -1,4 +1,4 @@
const imgBase = "/images/Đình Thái Lạc"; // thư mục chứa 26 ảnh Chùa Thái Lạc const imgBase = "/images/chua-thai-lac"; // 26 ảnh panorama Chùa Thái Lạc, đặt tên 01..26.jpg
export const img = (file: string) => encodeURI(`${imgBase}/${file}`); export const img = (file: string) => encodeURI(`${imgBase}/${file}`);
export const commune = { export const commune = {
@@ -13,10 +13,10 @@ export const activeTour = {
title: "Chùa Thái Lạc (Pháp Vân tự)", title: "Chùa Thái Lạc (Pháp Vân tự)",
subtitle: "Di tích quốc gia — chạm khắc gỗ thời Trần", subtitle: "Di tích quốc gia — chạm khắc gỗ thời Trần",
href: "/tour/chua-thai-lac", href: "/tour/chua-thai-lac",
heroImage: img("Chính Diện Cổng Chùa.jpg"), heroImage: img("01.jpg"), // Chính diện cổng chùa
coverImage: img("Chính Sảnh.jpg"), coverImage: img("08.jpg"), // Chính sảnh
gateImage: img("Chính Diện Cổng Chùa.jpg"), gateImage: img("01.jpg"), // Chính diện cổng chùa
shrineImage: img("Chính Điện 3.jpg"), shrineImage: img("11.jpg"), // Chính điện
sceneCount: "26", sceneCount: "26",
duration: "15-20 phút", duration: "15-20 phút",
}; };
@@ -60,7 +60,7 @@ function stub(id: string, name: string, type: string, level: HeritageLevel, desc
level, level,
status: "Đang bổ sung tư liệu", status: "Đang bổ sung tư liệu",
shortDescription: desc, shortDescription: desc,
image: img("Chính Sảnh.jpg"), // ảnh tạm dùng chung cho các di tích chưa có tư liệu image: img("08.jpg"), // ảnh tạm (Chính sảnh) dùng chung cho các di tích chưa có tư liệu
detailUrl: `/di-tich/${id}`, detailUrl: `/di-tich/${id}`,
isAvailable: false, isAvailable: false,
location: "Xã Như Quỳnh, tỉnh Hưng Yên", location: "Xã Như Quỳnh, tỉnh Hưng Yên",
@@ -81,7 +81,7 @@ export const heritageSites: HeritageSite[] = [
"Giá trị đặc biệt của di tích nằm ở hệ thống ván chạm khắc gỗ có niên đại thời Trần (thế kỷ 14), khắc họa các nhạc công, vũ nữ, hình tượng Phật giáo với kỹ thuật điêu luyện — nguồn tư liệu quý cho nghiên cứu mỹ thuật cổ Việt Nam.", "Giá trị đặc biệt của di tích nằm ở hệ thống ván chạm khắc gỗ có niên đại thời Trần (thế kỷ 14), khắc họa các nhạc công, vũ nữ, hình tượng Phật giáo với kỹ thuật điêu luyện — nguồn tư liệu quý cho nghiên cứu mỹ thuật cổ Việt Nam.",
"Không gian chùa gồm cổng, sân, tiền đường, thượng điện cùng các ban thờ và cảnh quan vườn, ao, được số hóa đầy đủ trong tour VR360.", "Không gian chùa gồm cổng, sân, tiền đường, thượng điện cùng các ban thờ và cảnh quan vườn, ao, được số hóa đầy đủ trong tour VR360.",
], ],
image: img("Chính Diện Cổng Chùa.jpg"), image: img("01.jpg"), // Chính diện cổng chùa
vrUrl: "/tour/chua-thai-lac", vrUrl: "/tour/chua-thai-lac",
detailUrl: "/di-tich/chua-thai-lac", detailUrl: "/di-tich/chua-thai-lac",
isAvailable: true, isAvailable: true,
+35 -34
View File
@@ -1,26 +1,29 @@
// Cấu hình tour VR360 Chùa Thái Lạc (Pháp Vân tự) — Xã Như Quỳnh, Hưng Yên. // Cấu hình tour VR360 Chùa Thái Lạc (Pháp Vân tự) — Xã Như Quỳnh, Hưng Yên.
// File chỉ chứa DỮ LIỆU (type-only import từ engine để tránh circular import runtime). // File chỉ chứa DỮ LIỆU (type-only import từ engine để tránh circular import runtime).
// Engine `VirtualTour.tsx` import `chuaThaiLacTour` rồi bọc qua createTourConfig(). // Engine `VirtualTour.tsx` import `chuaThaiLacTour` rồi bọc qua createTourConfig().
//
// Ảnh panorama đặt tại public/images/chua-thai-lac/01.jpg … 26.jpg
// (đặt tên ASCII theo thứ tự cảnh để tránh lỗi mã hóa Unicode/đường dẫn khi triển khai).
import type { TourConfig, TourScene, InfoMarker } from "@/app/components/VirtualTour"; import type { TourConfig, TourScene, InfoMarker } from "@/app/components/VirtualTour";
const base = "/images/Đình Thái Lạc"; const base = "/images/chua-thai-lac";
const p = (file: string) => encodeURI(`${base}/${file}`); // Ảnh theo số thứ tự cảnh: scene-1 → 01.jpg, scene-2 → 02.jpg, …
const imageFor = (index: number) => `${base}/${String(index + 1).padStart(2, "0")}.jpg`;
type SceneSeed = { type SceneSeed = {
title: string; title: string;
location: string; location: string;
file: string;
initialYaw?: number; initialYaw?: number;
infoMarkers?: InfoMarker[]; infoMarkers?: InfoMarker[];
}; };
// Tuyến tham quan: cổng → đường/vòm → tiền đường & chính điện → khu thờ → ban tượng → cảnh quan. // Tuyến tham quan: cổng → đường/vòm → tiền đường & chính điện → khu thờ → ban tượng → cảnh quan.
// yaw/pitch của hotspot để mặc định (tiến: 0, lùi: 180); tinh chỉnh trực quan ở bước chạy dev. // Thứ tự mảng = thứ tự ảnh (01..26). yaw/pitch hotspot để mặc định (tiến: 0, lùi: 180);
// tinh chỉnh trực quan khi chạy dev.
const seeds: SceneSeed[] = [ const seeds: SceneSeed[] = [
{ {
title: "Chính diện cổng chùa", title: "Chính diện cổng chùa",
location: "Khu cổng", location: "Khu cổng",
file: "Chính Diện Cổng Chùa.jpg",
infoMarkers: [ infoMarkers: [
{ {
id: "gioi-thieu", id: "gioi-thieu",
@@ -36,19 +39,18 @@ const seeds: SceneSeed[] = [
}, },
], ],
}, },
{ title: "Cổng bên phải", location: "Khu cổng", file: "Cổng Hữu.jpg" }, { title: "Cổng bên phải", location: "Khu cổng" },
{ title: "Cổng bên trái", location: "Khu cổng", file: "Cổng Bên Trái.jpg" }, { title: "Cổng bên trái", location: "Khu cổng" },
{ title: "Lối vào bên trái", location: "Khu cổng", file: "Lối Vào Been Trái.jpg" }, { title: "Lối vào bên trái", location: "Khu cổng" },
{ title: "Đường bên phải", location: "Đường vào", file: "Đường Hữu.jpg" }, { title: "Đường bên phải", location: "Đường vào" },
{ title: "Đường bên trái qua vòm", location: "Đường vào", file: "Đường Tả Qua Vòm.jpg" }, { title: "Đường bên trái qua vòm", location: "Đường vào" },
{ title: "Đường tả qua vòm", location: "Đường vào", file: "Duong Tả Vòm.jpg" }, { title: "Đường tả qua vòm", location: "Đường vào" },
{ title: "Chính sảnh", location: "Tiền đường & Chính điện", file: "Chính Sảnh.jpg" }, { title: "Chính sảnh", location: "Tiền đường & Chính điện" },
{ title: "Gian giữa", location: "Tiền đường & Chính điện", file: "Chính.jpg" }, { title: "Gian giữa", location: "Tiền đường & Chính điện" },
{ title: "Trung tâm chính điện", location: "Tiền đường & Chính điện", file: "Giuawx.jpg" }, { title: "Trung tâm chính điện", location: "Tiền đường & Chính điện" },
{ {
title: "Chính điện", title: "Chính điện",
location: "Tiền đường & Chính điện", location: "Tiền đường & Chính điện",
file: "Chính Điện 3.jpg",
infoMarkers: [ infoMarkers: [
{ {
id: "cham-khac-go", id: "cham-khac-go",
@@ -64,18 +66,17 @@ const seeds: SceneSeed[] = [
}, },
], ],
}, },
{ title: "Gian trong chính điện", location: "Tiền đường & Chính điện", file: "4.jpg" }, { title: "Gian trong chính điện", location: "Tiền đường & Chính điện" },
{ title: "Ban thờ bên trái", location: "Khu thờ trong chùa", file: "Chùa 1 Tả.jpg" }, { title: "Ban thờ bên trái", location: "Khu thờ trong chùa" },
{ title: "Ban thờ chính giữa", location: "Khu thờ trong chùa", file: "Chùa 1 Giữa.jpg" }, { title: "Ban thờ chính giữa", location: "Khu thờ trong chùa" },
{ title: "Ban thờ bên phải", location: "Khu thờ trong chùa", file: "Chùa 1 Hữu.jpg" }, { title: "Ban thờ bên phải", location: "Khu thờ trong chùa" },
{ title: "Gian trong - giữa", location: "Khu thờ trong chùa", file: "Chùa 1 Trong - Giữa.jpg" }, { title: "Gian trong - giữa", location: "Khu thờ trong chùa" },
{ title: "Gian trong bên trái", location: "Khu thờ trong chùa", file: "Chùa 1 Trong Tả.jpg" }, { title: "Gian trong bên trái", location: "Khu thờ trong chùa" },
{ title: "Hành lang bên phải", location: "Khu thờ trong chùa", file: "Hữu 2.jpg" }, { title: "Hành lang bên phải", location: "Khu thờ trong chùa" },
{ title: "Hành lang bên phải (tiếp)", location: "Khu thờ trong chùa", file: "Hữu 3.jpg" }, { title: "Hành lang bên phải (tiếp)", location: "Khu thờ trong chùa" },
{ {
title: "Tượng Quan Âm", title: "Tượng Quan Âm",
location: "Ban thờ tượng", location: "Ban thờ tượng",
file: "Tượng Quan Âm.jpg",
infoMarkers: [ infoMarkers: [
{ {
id: "dai-quan-am", id: "dai-quan-am",
@@ -90,12 +91,12 @@ const seeds: SceneSeed[] = [
}, },
], ],
}, },
{ title: "Tượng La Hán bên trái", location: "Ban thờ tượng", file: "La Hán Tả.jpg" }, { title: "Tượng La Hán bên trái", location: "Ban thờ tượng" },
{ title: "Nhà thờ Phật bên phải", location: "Ban thờ tượng", file: "Nhà Phật Hữu.jpg" }, { title: "Nhà thờ Phật bên phải", location: "Ban thờ tượng" },
{ title: "Ao cá Koi", location: "Cảnh quan", file: "Ao Cá Koi.jpg" }, { title: "Ao cá Koi", location: "Cảnh quan" },
{ title: "Gốc ngõa bên trái", location: "Cảnh quan", file: "Gốc Ngoã Tả.jpg" }, { title: "Gốc ngõa bên trái", location: "Cảnh quan" },
{ title: "Gốc ngõa bên phải", location: "Cảnh quan", file: "Gốc Ngoã Hữu.jpg" }, { title: "Gốc ngõa bên phải", location: "Cảnh quan" },
{ title: "Phía sau chùa", location: "Cảnh quan", file: "Sau Chùa.jpg" }, { title: "Phía sau chùa", location: "Cảnh quan" },
]; ];
const sceneId = (index: number) => `scene-${index + 1}`; const sceneId = (index: number) => `scene-${index + 1}`;
@@ -131,7 +132,7 @@ const scenes: TourScene[] = seeds.map((seed, index) => {
order: String(index + 1).padStart(2, "0"), order: String(index + 1).padStart(2, "0"),
title: seed.title, title: seed.title,
location: seed.location, location: seed.location,
image: p(seed.file), image: imageFor(index),
initialYaw: seed.initialYaw ?? 0, initialYaw: seed.initialYaw ?? 0,
mapPosition, mapPosition,
hotspots, hotspots,
@@ -151,10 +152,10 @@ export const chuaThaiLacTour: Omit<TourConfig, "sceneById" | "locationGroups"> =
welcomeAccent: "Pháp Vân tự", welcomeAccent: "Pháp Vân tự",
welcomeSubtitle: welcomeSubtitle:
"Tham quan 360° ngôi chùa cổ thờ Pháp Vân, nổi tiếng với mảng chạm khắc gỗ thời Trần.", "Tham quan 360° ngôi chùa cổ thờ Pháp Vân, nổi tiếng với mảng chạm khắc gỗ thời Trần.",
welcomeBackgroundImage: p("Chính Sảnh.jpg"), welcomeBackgroundImage: imageFor(7), // 08.jpg — Chính sảnh
welcomeCards: [ welcomeCards: [
{ image: p("Chính Diện Cổng Chùa.jpg"), label: "Cổng chùa", rotate: "-rotate-6", sceneId: "scene-1" }, { image: imageFor(0), label: "Cổng chùa", rotate: "-rotate-6", sceneId: "scene-1" },
{ image: p("Tượng Quan Âm.jpg"), label: "Đài Quan Âm", rotate: "rotate-5", sceneId: "scene-20" }, { image: imageFor(19), label: "Đài Quan Âm", rotate: "rotate-5", sceneId: "scene-20" },
], ],
backgroundAudio: "", // chưa có nhạc nền; engine bỏ qua chuỗi rỗng (không render thẻ audio) backgroundAudio: "", // chưa có nhạc nền; engine bỏ qua chuỗi rỗng (không render thẻ audio)
practicalInfo: { practicalInfo: {