fromis_9/backend/routes/albums.js
caadiq 79a3501ef1 feat(Album): PC 타이틀곡 텍스트 제거 + 모바일 상세 UI 개선
- PC: 앨범 상세에서 '타이틀곡: OOO' 텍스트 제거
- 모바일: 앨범 상세 UI 전면 개선
  - 티저 포토 섹션 추가
  - 수록곡 리스트 스타일 개선 (트랙번호, TITLE 뱃지)
  - 컨셉 포토 6장 프리뷰 + 전체보기 링크
  - 라이트박스 (다운로드, 이전/다음 네비게이션)
- 앨범 API에서 folder_name 반환 추가
2026-01-11 22:04:08 +09:00

123 lines
3.8 KiB
JavaScript

import express from "express";
import pool from "../lib/db.js";
const router = express.Router();
// 앨범 상세 정보 조회 헬퍼 함수 (트랙, 티저, 컨셉포토 포함)
async function getAlbumDetails(album) {
// 트랙 정보 조회 (가사 포함)
const [tracks] = await pool.query(
"SELECT * FROM tracks WHERE album_id = ? ORDER BY track_number",
[album.id]
);
album.tracks = tracks;
// 티저 이미지/비디오 조회 (3개 해상도 URL + media_type 포함)
const [teasers] = await pool.query(
"SELECT original_url, medium_url, thumb_url, media_type FROM album_teasers WHERE album_id = ? ORDER BY sort_order",
[album.id]
);
album.teasers = teasers;
// 컨셉 포토 조회 (멤버 정보 + 3개 해상도 URL + 크기 정보 포함)
const [photos] = await pool.query(
`SELECT
p.id, p.original_url, p.medium_url, p.thumb_url, p.photo_type, p.concept_name, p.sort_order,
p.width, p.height,
GROUP_CONCAT(m.name ORDER BY m.id SEPARATOR ', ') as members
FROM album_photos p
LEFT JOIN album_photo_members pm ON p.id = pm.photo_id
LEFT JOIN members m ON pm.member_id = m.id
WHERE p.album_id = ?
GROUP BY p.id
ORDER BY p.sort_order`,
[album.id]
);
// 컨셉별로 그룹화
const conceptPhotos = {};
for (const photo of photos) {
const concept = photo.concept_name || "Default";
if (!conceptPhotos[concept]) {
conceptPhotos[concept] = [];
}
conceptPhotos[concept].push({
id: photo.id,
original_url: photo.original_url,
medium_url: photo.medium_url,
thumb_url: photo.thumb_url,
width: photo.width,
height: photo.height,
type: photo.photo_type,
members: photo.members,
sortOrder: photo.sort_order,
});
}
album.conceptPhotos = conceptPhotos;
return album;
}
// 전체 앨범 조회 (트랙 포함)
router.get("/", async (req, res) => {
try {
const [albums] = await pool.query(
"SELECT id, title, folder_name, album_type, album_type_short, release_date, cover_original_url, cover_medium_url, cover_thumb_url FROM albums ORDER BY release_date DESC"
);
// 각 앨범에 트랙 정보 추가
for (const album of albums) {
const [tracks] = await pool.query(
"SELECT id, track_number, title, is_title_track, duration, lyricist, composer, arranger FROM tracks WHERE album_id = ? ORDER BY track_number",
[album.id]
);
album.tracks = tracks;
}
res.json(albums);
} catch (error) {
console.error("앨범 조회 오류:", error);
res.status(500).json({ error: "앨범 정보를 가져오는데 실패했습니다." });
}
});
// 앨범명으로 조회
router.get("/by-name/:name", async (req, res) => {
try {
const albumName = decodeURIComponent(req.params.name);
const [albums] = await pool.query("SELECT * FROM albums WHERE title = ?", [
albumName,
]);
if (albums.length === 0) {
return res.status(404).json({ error: "앨범을 찾을 수 없습니다." });
}
const album = await getAlbumDetails(albums[0]);
res.json(album);
} catch (error) {
console.error("앨범 조회 오류:", error);
res.status(500).json({ error: "앨범 정보를 가져오는데 실패했습니다." });
}
});
// ID로 앨범 조회
router.get("/:id", async (req, res) => {
try {
const [albums] = await pool.query("SELECT * FROM albums WHERE id = ?", [
req.params.id,
]);
if (albums.length === 0) {
return res.status(404).json({ error: "앨범을 찾을 수 없습니다." });
}
const album = await getAlbumDetails(albums[0]);
res.json(album);
} catch (error) {
console.error("앨범 조회 오류:", error);
res.status(500).json({ error: "앨범 정보를 가져오는데 실패했습니다." });
}
});
export default router;