diff --git a/src/pages/music/MusicStudio.css b/src/pages/music/MusicStudio.css
index 8ad99ff..d4b743a 100644
--- a/src/pages/music/MusicStudio.css
+++ b/src/pages/music/MusicStudio.css
@@ -2608,3 +2608,459 @@
background: var(--ms-surface); border: 1px solid var(--ms-line);
}
.ms-remix-submit { align-self: flex-start; margin-top: 8px; }
+
+/* ══════════════════════════════════════════
+ YouTube Tab — yt-* classes
+══════════════════════════════════════════ */
+
+.ms-tab--youtube.is-active {
+ color: #f59e0b;
+ border-bottom-color: #f59e0b;
+}
+
+.yt-container {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+}
+
+.yt-subtabs {
+ display: flex;
+ border-bottom: 1px solid #1f2937;
+ background: #0d1117;
+ padding: 0 16px;
+}
+
+.yt-subtab {
+ padding: 10px 18px;
+ font-size: 12px;
+ color: #6b7280;
+ background: none;
+ border: none;
+ border-bottom: 2px solid transparent;
+ cursor: pointer;
+ transition: color 0.15s, border-color 0.15s;
+ white-space: nowrap;
+}
+
+.yt-subtab:hover { color: #9ca3af; }
+
+.yt-subtab.is-active {
+ color: #22c55e;
+ border-bottom-color: #22c55e;
+ font-weight: 600;
+}
+
+.yt-content {
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ padding: 16px;
+}
+
+.yt-card {
+ background: #0d1117;
+ border: 1px solid #1f2937;
+ border-radius: 10px;
+ padding: 14px;
+}
+
+.yt-card--create { border-color: #22c55e33; }
+.yt-card--export { border-color: #3b82f633; border-style: dashed; }
+
+.yt-card__title {
+ font-size: 12px;
+ font-weight: 700;
+ color: #ccc;
+ margin: 0 0 12px;
+}
+
+.yt-card--create .yt-card__title { color: #86efac; }
+.yt-card--export .yt-card__title { color: #93c5fd; }
+
+.yt-row {
+ display: flex;
+ gap: 8px;
+ margin-bottom: 10px;
+ align-items: center;
+}
+
+.yt-row--bottom { margin-bottom: 0; margin-top: 8px; }
+
+.yt-form-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 8px;
+ margin-bottom: 0;
+}
+
+.yt-field { display: flex; flex-direction: column; gap: 4px; }
+.yt-field__label { font-size: 10px; color: #6b7280; }
+
+.yt-input {
+ background: #1f2937;
+ border: 1px solid #374151;
+ border-radius: 6px;
+ padding: 7px 10px;
+ color: #ccc;
+ font-size: 12px;
+ width: 100%;
+ box-sizing: border-box;
+}
+
+.yt-input:focus { outline: none; border-color: #22c55e; }
+.yt-input--sm { padding: 4px 8px; font-size: 11px; }
+
+.yt-select {
+ flex: 1;
+ background: #1f2937;
+ border: 1px solid #374151;
+ border-radius: 6px;
+ padding: 8px 10px;
+ color: #9ca3af;
+ font-size: 12px;
+}
+
+.yt-format-toggle { display: flex; gap: 4px; }
+
+.yt-format-btn {
+ background: #1f2937;
+ border: 1px solid #374151;
+ border-radius: 6px;
+ padding: 8px 10px;
+ color: #9ca3af;
+ font-size: 11px;
+ cursor: pointer;
+ white-space: nowrap;
+}
+
+.yt-format-btn.is-active {
+ background: #1a2e1a;
+ border-color: #22c55e;
+ color: #86efac;
+}
+
+.yt-country-label { font-size: 11px; color: #6b7280; margin-bottom: 6px; }
+
+.yt-country-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
+
+.yt-chip {
+ background: #1f2937;
+ border: 1px solid #374151;
+ border-radius: 4px;
+ padding: 3px 10px;
+ color: #6b7280;
+ font-size: 11px;
+ cursor: pointer;
+ transition: all 0.15s;
+}
+
+.yt-chip.is-active {
+ background: #1e3a2a;
+ border-color: #22c55e;
+ color: #86efac;
+}
+
+.yt-create-btn { width: 100%; margin-top: 2px; }
+
+.yt-project-list { display: flex; flex-direction: column; gap: 8px; }
+
+.yt-project-card {
+ background: #1f2937;
+ border-radius: 8px;
+ padding: 10px 12px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.yt-project-card__icon {
+ width: 40px;
+ height: 40px;
+ background: #111827;
+ border-radius: 6px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ flex-shrink: 0;
+}
+
+.yt-project-card__info { flex: 1; min-width: 0; }
+
+.yt-project-card__title {
+ font-size: 12px;
+ font-weight: 600;
+ color: #ccc;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.yt-project-card__meta { font-size: 10px; color: #6b7280; margin-top: 2px; }
+
+.yt-status {
+ font-size: 10px;
+ padding: 2px 8px;
+ border-radius: 4px;
+ white-space: nowrap;
+ flex-shrink: 0;
+}
+
+.yt-status--pending { background: #1f2937; color: #9ca3af; }
+.yt-status--rendering { background: #1a1500; color: #f59e0b; }
+.yt-status--done { background: #0a3d1a; color: #22c55e; }
+.yt-status--failed { background: #2d0a0a; color: #f87171; }
+
+.yt-progress-bar {
+ height: 3px;
+ background: #374151;
+ border-radius: 2px;
+ margin-top: 6px;
+ overflow: hidden;
+}
+
+.yt-progress-bar__fill {
+ height: 100%;
+ width: 65%;
+ background: linear-gradient(90deg, #f59e0b, #fbbf24);
+ border-radius: 2px;
+ animation: yt-progress-pulse 2s ease-in-out infinite;
+}
+
+@keyframes yt-progress-pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.6; }
+}
+
+.yt-export-links { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
+
+.yt-meta-preview { background: #111827; border-radius: 6px; padding: 8px; }
+.yt-meta-preview__label { font-size: 10px; color: #6b7280; margin-bottom: 4px; }
+.yt-meta-preview__content {
+ font-size: 11px;
+ color: #9ca3af;
+ font-family: monospace;
+ margin: 0;
+ white-space: pre-wrap;
+ word-break: break-all;
+}
+
+.yt-empty {
+ text-align: center;
+ color: #6b7280;
+ font-size: 11px;
+ padding: 8px 0;
+ margin: 0;
+}
+
+.yt-dash-cards {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ gap: 10px;
+}
+
+.yt-dash-card {
+ background: #0d1117;
+ border: 1px solid #1f2937;
+ border-radius: 8px;
+ padding: 12px;
+ text-align: center;
+}
+
+.yt-dash-card__label { font-size: 10px; color: #6b7280; margin-bottom: 4px; }
+.yt-dash-card__sub { font-size: 9px; color: #6b7280; margin-top: 2px; }
+
+.yt-dash-card__value { font-size: 18px; font-weight: 700; }
+.yt-dash-card__value--green { color: #22c55e; }
+.yt-dash-card__value--blue { color: #60a5fa; }
+.yt-dash-card__value--amber { color: #f59e0b; }
+
+.yt-bar-chart { display: flex; flex-direction: column; gap: 8px; }
+
+.yt-bar-row { display: flex; align-items: center; gap: 8px; }
+
+.yt-bar-row__label {
+ width: 80px;
+ font-size: 11px;
+ color: #9ca3af;
+ text-align: right;
+ flex-shrink: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.yt-bar-row__rank {
+ width: 24px;
+ font-size: 11px;
+ font-weight: 700;
+ color: #f59e0b;
+ text-align: center;
+ flex-shrink: 0;
+}
+
+.yt-bar-row__info { flex: 1; }
+
+.yt-bar-row__genre-header {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 3px;
+}
+
+.yt-bar-row__genre-name { font-size: 12px; color: #ccc; }
+.yt-bar-row__flags { font-size: 10px; color: #9ca3af; }
+
+.yt-bar-row__track {
+ flex: 1;
+ height: 6px;
+ background: #1f2937;
+ border-radius: 3px;
+ overflow: hidden;
+}
+
+.yt-bar-row__fill {
+ height: 100%;
+ background: linear-gradient(90deg, #22c55e, #4ade80);
+ border-radius: 3px;
+ transition: width 0.4s ease;
+}
+
+.yt-bar-row__fill--genre { background: linear-gradient(90deg, #f59e0b, #fbbf24); }
+
+.yt-bar-row__value {
+ width: 44px;
+ font-size: 11px;
+ color: #22c55e;
+ text-align: right;
+ flex-shrink: 0;
+}
+
+.yt-table { display: flex; flex-direction: column; gap: 2px; }
+
+.yt-table__header {
+ display: grid;
+ grid-template-columns: 2fr 1fr 1fr 1fr 1fr 28px;
+ gap: 4px;
+ padding: 0 4px 6px;
+ border-bottom: 1px solid #1f2937;
+ font-size: 10px;
+ color: #6b7280;
+}
+
+.yt-table__row {
+ display: grid;
+ grid-template-columns: 2fr 1fr 1fr 1fr 1fr 28px;
+ gap: 4px;
+ padding: 7px 4px;
+ border-bottom: 1px solid #111827;
+ align-items: center;
+}
+
+.yt-table__row--editing {
+ background: #111827;
+ border-radius: 6px;
+ padding: 8px;
+}
+
+.yt-table__row:last-child { border-bottom: none; }
+
+.yt-table__cell { font-size: 11px; color: #9ca3af; }
+.yt-table__cell--mono { font-family: monospace; }
+.yt-table__cell--green { color: #22c55e; }
+.yt-table__cell--amber { color: #f59e0b; }
+
+.yt-table__actions { display: flex; gap: 4px; grid-column: span 2; }
+
+.yt-status-bar {
+ background: #0d1117;
+ border: 1px solid #1f2937;
+ border-radius: 8px;
+ padding: 10px 14px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.yt-status-bar__left { display: flex; align-items: center; gap: 8px; }
+
+.yt-status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: #22c55e;
+ box-shadow: 0 0 6px #22c55e;
+ flex-shrink: 0;
+}
+
+.yt-status-bar__text { font-size: 11px; color: #9ca3af; }
+
+.yt-prompt-list { display: flex; flex-direction: column; gap: 8px; }
+
+.yt-prompt-card {
+ background: #1a0d2e;
+ border-radius: 8px;
+ padding: 10px 12px;
+}
+
+.yt-prompt-card__header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 5px;
+}
+
+.yt-prompt-card__genre { font-size: 11px; font-weight: 700; color: #c084fc; }
+.yt-prompt-card__countries { font-size: 10px; color: #6b7280; }
+
+.yt-prompt-card__text {
+ display: block;
+ width: 100%;
+ text-align: left;
+ background: #110820;
+ border: none;
+ border-radius: 4px;
+ padding: 6px 8px;
+ font-size: 11px;
+ font-family: monospace;
+ color: #e9d5ff;
+ line-height: 1.6;
+ cursor: pointer;
+ transition: background 0.15s;
+}
+
+.yt-prompt-card__text:hover { background: #1a0d30; }
+
+.yt-prompt-card__copied { font-size: 10px; color: #22c55e; margin-top: 4px; display: block; }
+.yt-prompt-card__reason { font-size: 10px; color: #6b7280; margin-top: 5px; }
+
+.yt-report-list { display: flex; flex-direction: column; gap: 4px; }
+
+.yt-report-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 6px 8px;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: background 0.15s;
+}
+
+.yt-report-row:hover { background: #1f2937; }
+.yt-report-row.is-selected { background: #1f2937; }
+
+.yt-report-row__date { font-size: 11px; color: #ccc; }
+.yt-report-row__today { font-size: 10px; color: #22c55e; margin-left: 4px; }
+.yt-report-row__meta { font-size: 10px; color: #9ca3af; }
+.yt-report-row__action { font-size: 11px; color: #60a5fa; }
+
+@media (max-width: 600px) {
+ .yt-dash-cards { grid-template-columns: 1fr 1fr; }
+ .yt-form-grid { grid-template-columns: 1fr; }
+ .yt-table__header,
+ .yt-table__row { grid-template-columns: 2fr 1fr 1fr 28px; }
+ .yt-table__header span:nth-child(4),
+ .yt-table__header span:nth-child(5),
+ .yt-table__row span:nth-child(4),
+ .yt-table__row span:nth-child(5) { display: none; }
+}
diff --git a/src/pages/music/MusicStudio.jsx b/src/pages/music/MusicStudio.jsx
index da55ed3..e7a89f5 100644
--- a/src/pages/music/MusicStudio.jsx
+++ b/src/pages/music/MusicStudio.jsx
@@ -27,6 +27,7 @@ import LyricsTab from './components/LyricsTab';
import StemModal from './components/StemModal';
import SyncedLyricsPlayer from './components/SyncedLyricsPlayer';
import RemixTab from './components/RemixTab';
+import YoutubeTab from './components/YoutubeTab';
/* ─────────────────────────────────────────────
데이터 상수
@@ -337,7 +338,7 @@ const TrackResult = ({ track, onDownload, onNew }) => {
/* ─────────────────────────────────────────────
Library Card
───────────────────────────────────────────── */
-const LibraryCard = ({ track, onDelete, onPlay, isPlaying, onExtend, onVocalRemoval, onCoverArt, onWavConvert, onStemSplit, onSyncedLyrics, onVideoGenerate, isGenerating }) => {
+const LibraryCard = ({ track, onDelete, onPlay, isPlaying, onExtend, onVocalRemoval, onCoverArt, onWavConvert, onStemSplit, onSyncedLyrics, onVideoGenerate, onVideoProject, isGenerating }) => {
const [menuOpen, setMenuOpen] = useState(false);
const genre = GENRES.find((g) => g.id === track.genre);
const totalSec = track.duration_sec ?? null;
@@ -425,6 +426,9 @@ const LibraryCard = ({ track, onDelete, onPlay, isPlaying, onExtend, onVocalRemo
disabled={isGenerating || !track.lyrics}>📝 Synced Lyrics
+
)}
@@ -447,7 +451,7 @@ const LibraryCard = ({ track, onDelete, onPlay, isPlaying, onExtend, onVocalRemo
/* ─────────────────────────────────────────────
Library Section
───────────────────────────────────────────── */
-const Library = ({ tracks, onDelete, onRefresh, onExtend, onVocalRemoval, onCoverArt, onWavConvert, onStemSplit, onSyncedLyrics, onVideoGenerate, isGenerating, loading }) => {
+const Library = ({ tracks, onDelete, onRefresh, onExtend, onVocalRemoval, onCoverArt, onWavConvert, onStemSplit, onSyncedLyrics, onVideoGenerate, onVideoProject, isGenerating, loading }) => {
const [playingId, setPlayingId] = useState(null);
const handlePlay = (track) => {
@@ -501,6 +505,7 @@ const Library = ({ tracks, onDelete, onRefresh, onExtend, onVocalRemoval, onCove
onStemSplit={onStemSplit}
onSyncedLyrics={onSyncedLyrics}
onVideoGenerate={onVideoGenerate}
+ onVideoProject={onVideoProject}
isGenerating={isGenerating}
/>
))}
@@ -515,6 +520,7 @@ const Library = ({ tracks, onDelete, onRefresh, onExtend, onVocalRemoval, onCove
export default function MusicStudio() {
/* ── 탭 ── */
const [tab, setTab] = useState('create');
+ const [initialTrackId, setInitialTrackId] = useState(null);
/* ── Provider 상태 ── */
const [providers, setProviders] = useState([]);
@@ -1058,6 +1064,11 @@ export default function MusicStudio() {
}
};
+ const handleVideoProject = (track) => {
+ setInitialTrackId(track.id);
+ setTab('youtube');
+ };
+
const handleNewTrack = () => {
setTrack(null);
setGenProgress(0);
@@ -1121,6 +1132,13 @@ export default function MusicStudio() {
>
🔄 Remix
+
{/* ═══ LIBRARY TAB ═══ */}
@@ -1138,6 +1156,7 @@ export default function MusicStudio() {
onStemSplit={handleStemSplit}
onSyncedLyrics={handleSyncedLyrics}
onVideoGenerate={handleVideoGenerate}
+ onVideoProject={handleVideoProject}
isGenerating={isGenerating}
/>
@@ -1166,6 +1185,15 @@ export default function MusicStudio() {
/>
)}
+ {/* ═══ YOUTUBE TAB ═══ */}
+ {tab === 'youtube' && (
+ setInitialTrackId(null)}
+ />
+ )}
+
{/* ═══ CREATE TAB ═══ */}
{tab === 'create' && (