Skip to content

Commit 5c16790

Browse files
committed
修正
1 parent e725c2b commit 5c16790

9 files changed

Lines changed: 143 additions & 33 deletions

File tree

WebSite/assets/data/portfolioList.json

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@
33
"id": "portfolio_0003",
44
"title": "Develop Utility",
55
"date": "",
6-
"category": "",
6+
"category": "Tool",
77
"role": "Full Stack Engineer",
88
"description": "開発者用Discord Bot",
99
"tech": "Javascript / discord.js",
10-
"tags": [
11-
"discord",
12-
"bot"
13-
],
10+
"tags": ["discord", "bot"],
1411
"thumbnail": "assets/img/ogp.png",
1512
"links": [],
1613
"contentPath": "portfolio/portfolio_0003.html"
@@ -19,34 +16,26 @@
1916
"id": "portfolio_0002",
2017
"title": "クマの逆転プロ野球",
2118
"date": "",
22-
"category": "",
19+
"category": "Game",
2320
"role": "Full Stack Engineer",
2421
"description": "カジュアル",
2522
"tech": "Unity / C#",
26-
"tags": [
27-
"game",
28-
"ios",
29-
"android"
30-
],
31-
"thumbnail": "https://github.com/user-attachments/assets/f0314371-190f-43a0-981b-a94f501a2adc",
23+
"tags": ["game", "ios", "android"],
24+
"thumbnail": "assets/img/thumbnails/portfolio_0002.png",
3225
"links": [],
3326
"contentPath": "portfolio/portfolio_0002.html"
3427
},
3528
{
3629
"id": "portfolio_0001",
3730
"title": "霊迷の湯",
3831
"date": "",
39-
"category": "",
32+
"category": "Game",
4033
"role": "Full Stack Engineer",
4134
"description": "8番ライクホラー",
4235
"tech": "Unity / C#",
43-
"tags": [
44-
"game",
45-
"steam",
46-
"pc"
47-
],
48-
"thumbnail": "https://shared.fastly.steamstatic.com/store_item_assets/steam/apps/2806350/header_japanese.jpg?t=1710480483",
36+
"tags": ["game", "steam", "pc"],
37+
"thumbnail": "assets/img/thumbnails/portfolio_0001.jpg",
4938
"links": [],
5039
"contentPath": "portfolio/portfolio_0001.html"
5140
}
52-
]
41+
]

WebSite/assets/data/products.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
"id": "product_0001",
44
"title": "Develop Utility",
5-
"type": "Discord",
5+
"type": "Tool",
66
"platform": ["Android", "iOS", "PC<Client, Browser>"],
77
"description": "PanKUNがDiscordで開発コミュニケーションを扱う際に使用するボット",
88
"tags": ["tool", "discord", "bot"],
32.5 KB
Loading
86 KB
Loading

WebSite/assets/js/portfolio.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,11 @@ document.addEventListener("DOMContentLoaded", () => {
225225
if (isEn) {
226226
return {
227227
...work,
228-
thumbnail: work.thumbnail
229-
? `${relativePrefix}${work.thumbnail}`
230-
: work.thumbnail,
228+
thumbnail:
229+
work.thumbnail &&
230+
!work.thumbnail.startsWith("http")
231+
? `${relativePrefix}${work.thumbnail}`
232+
: work.thumbnail,
231233
contentPath: work.contentPath
232234
? `${relativePrefix}${work.contentPath}`
233235
: work.contentPath,

WebSite/assets/js/products.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ document.addEventListener("DOMContentLoaded", () => {
2828
const rawProducts = await res.json();
2929

3030
allProducts = rawProducts.map((p) => {
31-
if (isEn && p.thumbnail) {
31+
if (
32+
isEn &&
33+
p.thumbnail &&
34+
!p.thumbnail.startsWith("http")
35+
) {
3236
return {
3337
...p,
3438
thumbnail: `${relativePrefix}${p.thumbnail}`,

WebSite/assets/js/recommend.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,17 @@ document.addEventListener("DOMContentLoaded", async () => {
1111
window.location.pathname.includes("/blog/") &&
1212
!window.location.pathname.endsWith("blog.html");
1313

14-
let basePath = isBlogDir ? "../" : "";
15-
if (isBlogDir && isEn) {
16-
basePath = "../../";
14+
let basePath = "";
15+
if (isBlogDir) {
16+
// ブログ詳細ページなど (/blog/xxx.html)
17+
// 日本語: /WebSite/blog/xxx.html -> ../assets/...
18+
// 英語: /WebSite/en/blog/xxx.html -> ../../assets/...
19+
basePath = isEn ? "../../" : "../";
20+
} else {
21+
// 一覧ページやトップページ
22+
// 日本語: /WebSite/index.html -> assets/...
23+
// 英語: /WebSite/en/index.html -> ../assets/...
24+
basePath = isEn ? "../" : "";
1725
}
1826

1927
// データパス

WebSite/assets/js/top.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ document.addEventListener("DOMContentLoaded", async () => {
2020

2121
if (!blogListEl && !portListEl && !slideshowEl) return;
2222

23+
const lang = document.documentElement.lang;
24+
const isEn = lang === "en";
25+
const relativePrefix = isEn ? "../" : "";
26+
2327
if (slideshowEl) {
2428
try {
2529
const res = await fetch(
26-
"assets/data/portfolioList.json",
30+
`${relativePrefix}assets/data/portfolioList.json`,
2731
);
2832
if (res.ok) {
2933
let items = await res.json();
@@ -41,7 +45,17 @@ document.addEventListener("DOMContentLoaded", async () => {
4145
const item = items[currentIndex];
4246
const slide = document.createElement("div");
4347
slide.className = "hero-slide";
44-
slide.style.backgroundImage = `url('${item.thumbnail}')`;
48+
49+
let thumbUrl = item.thumbnail;
50+
if (
51+
isEn &&
52+
thumbUrl &&
53+
!thumbUrl.startsWith("http")
54+
) {
55+
thumbUrl = relativePrefix + thumbUrl;
56+
}
57+
58+
slide.style.backgroundImage = `url('${thumbUrl}')`;
4559

4660
slideshowEl.appendChild(slide);
4761

@@ -74,7 +88,9 @@ document.addEventListener("DOMContentLoaded", async () => {
7488

7589
if (blogListEl) {
7690
try {
77-
const res = await fetch("assets/data/blogList.json");
91+
const res = await fetch(
92+
`${relativePrefix}assets/data/blogList.json`,
93+
);
7894
if (!res.ok) throw new Error(res.statusText);
7995
const posts = await res.json();
8096

@@ -126,7 +142,7 @@ document.addEventListener("DOMContentLoaded", async () => {
126142
if (portListEl) {
127143
try {
128144
const res = await fetch(
129-
"assets/data/portfolioList.json",
145+
`${relativePrefix}assets/data/portfolioList.json`,
130146
);
131147
if (!res.ok) throw new Error(res.statusText);
132148
const works = await res.json();

WebSite/tools/build-portfolio.js

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ const LIST_JSON = path.join(
1515
"portfolioList.json",
1616
);
1717

18+
const THUMBNAIL_DIR = path.join(
19+
ROOT,
20+
"assets",
21+
"img",
22+
"thumbnails",
23+
);
24+
1825
function escapeHtml(str = "") {
1926
return String(str).replace(/[&<>"]/g, (c) => {
2027
return {
@@ -145,12 +152,17 @@ ${bodyHtml}
145152
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
146153
}
147154

155+
if (!fs.existsSync(THUMBNAIL_DIR)) {
156+
fs.mkdirSync(THUMBNAIL_DIR, { recursive: true });
157+
}
158+
148159
const works = [];
149160
const files = fs.existsSync(CONTENT_DIR)
150161
? fs
151162
.readdirSync(CONTENT_DIR)
152163
.filter((f) => f.endsWith(".md"))
153164
: [];
165+
const usedThumbnails = new Set();
154166

155167
for (const file of files) {
156168
const id = path.basename(file, ".md"); // work_0001 など
@@ -166,7 +178,72 @@ ${bodyHtml}
166178
const category = data.category || "";
167179
const role = data.role || "";
168180
const tech = data.tech || "";
169-
const thumbnail = data.thumbnail || "";
181+
let thumbnail = data.thumbnail || "";
182+
183+
if (thumbnail) {
184+
try {
185+
if (thumbnail.startsWith("data:image")) {
186+
const matches = thumbnail.match(
187+
/^data:image\/([a-zA-Z0-9]+);base64,(.+)$/,
188+
);
189+
if (matches) {
190+
const ext =
191+
matches[1] === "jpeg" ? "jpg" : matches[1];
192+
const filename = `${id}.${ext}`;
193+
const thumbPath = path.join(
194+
THUMBNAIL_DIR,
195+
filename,
196+
);
197+
fs.writeFileSync(
198+
thumbPath,
199+
Buffer.from(matches[2], "base64"),
200+
);
201+
thumbnail = `assets/img/thumbnails/${filename}`;
202+
usedThumbnails.add(filename);
203+
}
204+
} else if (thumbnail.startsWith("http")) {
205+
const urlObj = new URL(thumbnail);
206+
let ext = path
207+
.extname(urlObj.pathname)
208+
.substring(1);
209+
if (!ext) ext = "png";
210+
211+
const filename = `${id}.${ext}`;
212+
const thumbPath = path.join(
213+
THUMBNAIL_DIR,
214+
filename,
215+
);
216+
217+
console.log(
218+
`Downloading thumbnail for ${id} from ${thumbnail}`,
219+
);
220+
const res = await fetch(thumbnail);
221+
if (res.ok) {
222+
const buffer = Buffer.from(
223+
await res.arrayBuffer(),
224+
);
225+
fs.writeFileSync(thumbPath, buffer);
226+
thumbnail = `assets/img/thumbnails/${filename}`;
227+
usedThumbnails.add(filename);
228+
} else {
229+
console.error(
230+
`Failed to fetch thumbnail for ${id}: ${res.statusText}`,
231+
);
232+
}
233+
} else if (
234+
thumbnail.includes("assets/img/thumbnails/")
235+
) {
236+
const filename = path.basename(thumbnail);
237+
usedThumbnails.add(filename);
238+
}
239+
} catch (e) {
240+
console.error(
241+
`Failed to process thumbnail for ${id}:`,
242+
e,
243+
);
244+
}
245+
}
246+
170247
const tagsRaw = data.tags || [];
171248
const tags = Array.isArray(tagsRaw)
172249
? tagsRaw
@@ -226,6 +303,20 @@ ${bodyHtml}
226303
"utf8",
227304
);
228305
console.log(`updated: assets/data/portfolioList.json`);
306+
307+
// Clean up unused portfolio thumbnails
308+
if (fs.existsSync(THUMBNAIL_DIR)) {
309+
const allThumbnails = fs.readdirSync(THUMBNAIL_DIR);
310+
for (const file of allThumbnails) {
311+
if (
312+
file.startsWith("portfolio_") &&
313+
!usedThumbnails.has(file)
314+
) {
315+
console.log(`Removing unused thumbnail: ${file}`);
316+
fs.unlinkSync(path.join(THUMBNAIL_DIR, file));
317+
}
318+
}
319+
}
229320
})().catch((err) => {
230321
console.error(err);
231322
process.exit(1);

0 commit comments

Comments
 (0)