Skip to content

Commit cf36c2d

Browse files
feats: convert png to webp
1 parent 030e489 commit cf36c2d

57 files changed

Lines changed: 2905 additions & 2654 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

convert_images.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import os
2+
import re
3+
from PIL import Image
4+
5+
def convert_to_webp(directory, quality=85):
6+
converted_files = {} # original_rel_path: new_rel_path
7+
8+
for root, dirs, files in os.walk(directory):
9+
for file in files:
10+
if file.lower().endswith(('.png', '.jpg', '.jpeg')):
11+
original_path = os.path.join(root, file)
12+
name, ext = os.path.splitext(file)
13+
new_file = f"{name}.webp"
14+
new_path = os.path.join(root, new_file)
15+
16+
try:
17+
with Image.open(original_path) as img:
18+
# Convert to RGB if necessary (e.g. for RGBA PNGs)
19+
# Actually WebP supports alpha, so we can keep it.
20+
img.save(new_path, "WEBP", quality=quality)
21+
22+
old_size = os.path.getsize(original_path)
23+
new_size = os.path.getsize(new_path)
24+
reduction = (old_size - new_size) / (1024 * 1024)
25+
print(f"Converted: {original_path} -> {new_path} (Saved {reduction:.2f} MB)")
26+
27+
# Store mapping for replacement
28+
rel_original = os.path.relpath(original_path, start=".")
29+
rel_new = os.path.relpath(new_path, start=".")
30+
# Use forward slashes for web paths
31+
rel_original_web = rel_original.replace("\\", "/")
32+
rel_new_web = rel_new.replace("\\", "/")
33+
converted_files[rel_original_web] = rel_new_web
34+
35+
except Exception as e:
36+
print(f"Error converting {original_path}: {e}")
37+
38+
return converted_files
39+
40+
def update_references(target_files, mapping):
41+
for file_path in target_files:
42+
if not os.path.exists(file_path):
43+
continue
44+
45+
try:
46+
with open(file_path, 'r', encoding='utf-8') as f:
47+
content = f.read()
48+
49+
original_content = content
50+
updated_count = 0
51+
52+
# Sort mapping keys by length (longest first) to avoid partial replacements
53+
# e.g. "image.png" vs "small_image.png"
54+
for old_path, new_path in sorted(mapping.items(), key=lambda x: len(x[0]), reverse=True):
55+
# Use regex to find the file extension and replace it if it matches our list
56+
# Specifically targeting paths that look like "images/.../*.png"
57+
# But simple string replacement for the specific filename is safer here
58+
if old_path in content:
59+
content = content.replace(old_path, new_path)
60+
updated_count += 1
61+
62+
# Generic replacement for extensions if they are used as part of dynamically built paths
63+
# e.g. some_path + ".png" -> some_path + ".webp"
64+
# But let's stick to the specific mapping first.
65+
66+
if content != original_content:
67+
with open(file_path, 'w', encoding='utf-8') as f:
68+
f.write(content)
69+
print(f"Updated references in: {file_path} ({updated_count} replacements)")
70+
except Exception as e:
71+
print(f"Error updating {file_path}: {e}")
72+
73+
def cleanup_originals(mapping):
74+
for old_path_web in mapping.keys():
75+
old_path = old_path_web.replace("/", os.sep)
76+
if os.path.exists(old_path):
77+
try:
78+
os.remove(old_path)
79+
print(f"Deleted: {old_path}")
80+
except Exception as e:
81+
print(f"Error deleting {old_path}: {e}")
82+
83+
if __name__ == "__main__":
84+
print("Starting Image Conversion to WebP...")
85+
mapping = convert_to_webp("images")
86+
87+
if not mapping:
88+
print("No images found to convert.")
89+
else:
90+
print(f"\nConverted {len(mapping)} images. Updating references...")
91+
92+
# Files to sweep for references
93+
target_files = [
94+
"index.html", "subject.html", "header.html", "footer.html",
95+
"js/courses.json", "js/game_dev.json", "js/computer_vision.json",
96+
"js/calculus.json", "js/computer_organization.json", "js/script.js", "js/main.js"
97+
]
98+
99+
update_references(target_files, mapping)
100+
101+
print("\nCleanup phase: deleting original image files...")
102+
# cleanup_originals(mapping) # Uncomment after verification if needed,
103+
# but I'll run it now since the user asked for it.
104+
cleanup_originals(mapping)
105+
106+
print("\nAll tasks completed!")

css/main.css

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,4 +433,89 @@ dl dd {
433433
}
434434

435435

436+
}
437+
438+
/* ==================
439+
WELCOME TOAST (Tutorial)
440+
================== */
441+
.welcome-toast {
442+
position: fixed;
443+
bottom: 80px; /* Nằm trên nút ? một chút */
444+
left: 50%;
445+
transform: translateX(-50%) translateY(100px);
446+
background-color: #ffffff;
447+
color: #333;
448+
padding: 16px 24px;
449+
border-radius: 12px;
450+
box-shadow: 0 10px 25px rgba(0,0,0,0.15);
451+
z-index: 1000;
452+
display: flex;
453+
align-items: center;
454+
gap: 15px;
455+
transition: transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.5s;
456+
opacity: 0;
457+
max-width: 90%;
458+
width: 450px;
459+
}
460+
461+
.welcome-toast.show {
462+
transform: translateX(-50%) translateY(0);
463+
opacity: 1;
464+
}
465+
466+
.welcome-toast-content {
467+
flex-grow: 1;
468+
font-size: 0.95em;
469+
line-height: 1.4;
470+
}
471+
472+
.welcome-toast-actions {
473+
display: flex;
474+
gap: 10px;
475+
}
476+
477+
.welcome-toast-btn {
478+
background-color: #5624d0;
479+
color: white;
480+
border: none;
481+
padding: 8px 16px;
482+
border-radius: 6px;
483+
cursor: pointer;
484+
font-weight: 600;
485+
font-size: 0.9em;
486+
line-height: 1.2;
487+
white-space: nowrap;
488+
transition: background-color 0.2s;
489+
}
490+
491+
.welcome-toast-btn:hover {
492+
background-color: #401b9c;
493+
}
494+
495+
.welcome-toast-close {
496+
background: transparent;
497+
border: none;
498+
color: #888;
499+
cursor: pointer;
500+
font-size: 1.2em;
501+
padding: 5px;
502+
line-height: 1;
503+
}
504+
505+
.welcome-toast-close:hover {
506+
color: #333;
507+
}
508+
509+
@media (max-width: 600px) {
510+
.welcome-toast {
511+
flex-direction: column;
512+
gap: 12px;
513+
text-align: center;
514+
bottom: 20px;
515+
}
516+
517+
.welcome-toast-actions {
518+
width: 100%;
519+
justify-content: center;
520+
}
436521
}

images/Android.png

-6.03 MB
Binary file not shown.

images/Android.webp

147 KB

images/Apple.png

-6.09 MB
Binary file not shown.

images/Apple.webp

135 KB

images/C#.png

-5.26 MB
Binary file not shown.

images/C#.webp

110 KB

images/C++.png

-5.66 MB
Binary file not shown.

images/C++.webp

163 KB

0 commit comments

Comments
 (0)