soyailabs / templates /admin_utils.html
GitHub Actions
Auto-deploy from GitHub Actions - 2025-12-11 09:58:40
21d20c7
raw
history blame
29.4 kB
<!DOCTYPE html>
<html lang="ko">
<head>
<script type="text/javascript">
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "ujskfvh0bu");
</script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>μœ ν‹Έλ¦¬ν‹° - SOY NV AI</title>
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f8f9fa;
color: #202124;
}
.header {
background: white;
border-bottom: 1px solid #dadce0;
padding: 16px 24px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.header-title {
font-size: 20px;
font-weight: 500;
display: flex;
align-items: center;
gap: 12px;
}
.header-actions {
display: flex;
gap: 12px;
align-items: center;
}
.menu-toggle {
display: none;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
padding: 8px;
color: #202124;
}
.mobile-menu {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
.mobile-menu.active {
display: block;
}
.mobile-menu-content {
position: fixed;
top: 0;
right: -100%;
width: 280px;
max-width: 80%;
height: 100%;
background: white;
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
transition: right 0.3s ease;
overflow-y: auto;
z-index: 1001;
}
.mobile-menu.active .mobile-menu-content {
right: 0;
}
.mobile-menu-header {
padding: 16px 20px;
border-bottom: 1px solid #dadce0;
display: flex;
justify-content: space-between;
align-items: center;
background: white;
position: sticky;
top: 0;
z-index: 10;
}
.mobile-menu-title {
font-size: 18px;
font-weight: 500;
}
.mobile-menu-close {
background: none;
border: none;
font-size: 28px;
cursor: pointer;
color: #202124;
padding: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.mobile-menu-user {
padding: 12px 20px;
background: #f8f9fa;
border-bottom: 1px solid #dadce0;
font-size: 14px;
color: #5f6368;
}
.mobile-menu-items {
padding: 8px 0;
}
.mobile-menu-item {
display: block;
padding: 12px 20px;
color: #202124;
text-decoration: none;
font-size: 14px;
transition: background 0.2s;
}
.mobile-menu-item:hover {
background: #f8f9fa;
}
.btn {
padding: 8px 16px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
text-decoration: none;
display: inline-block;
transition: all 0.2s;
}
.btn-primary {
background: #1a73e8;
color: white;
}
.btn-primary:hover {
background: #1557b0;
}
.btn-secondary {
background: #f1f3f4;
color: #202124;
}
.btn-secondary:hover {
background: #e8eaed;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 24px;
}
.page-header {
margin-bottom: 24px;
}
.page-header h1 {
font-size: 28px;
font-weight: 600;
margin-bottom: 8px;
}
.page-header p {
color: #5f6368;
}
.card {
background: white;
border-radius: 8px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
padding: 24px;
margin-bottom: 24px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.card-title {
font-size: 18px;
font-weight: 500;
}
.alert {
padding: 12px 16px;
border-radius: 6px;
margin-bottom: 16px;
font-size: 14px;
}
.alert.error {
background: #fce8e6;
color: #c5221f;
}
.alert.success {
background: #e8f5e9;
color: #137333;
}
.utils-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}
.util-card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.2s;
}
.util-card:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.util-card-title {
font-size: 16px;
font-weight: 500;
margin-bottom: 8px;
}
.util-card-description {
font-size: 14px;
color: #5f6368;
margin-bottom: 12px;
}
.form-group {
margin-bottom: 16px;
}
.form-group label {
display: block;
font-size: 14px;
font-weight: 500;
margin-bottom: 8px;
}
.form-group select,
.form-group textarea {
width: 100%;
padding: 10px 12px;
border: 1px solid #dadce0;
border-radius: 6px;
font-size: 14px;
font-family: inherit;
}
.form-group textarea {
min-height: 200px;
resize: vertical;
}
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: #1a73e8;
box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.1);
}
.form-group-checkbox {
display: flex;
align-items: center;
gap: 8px;
margin-top: 12px;
}
.form-group-checkbox input {
width: auto;
}
.preview-box {
background: #f8f9fa;
border: 1px solid #dadce0;
border-radius: 6px;
padding: 12px;
margin-top: 12px;
max-height: 300px;
overflow-y: auto;
font-size: 13px;
font-family: 'Courier New', monospace;
white-space: pre-wrap;
word-wrap: break-word;
}
.preview-box.empty {
color: #5f6368;
font-style: italic;
}
input[type="file"] {
cursor: pointer;
}
input[type="file"]:focus {
outline: none;
border-color: #1a73e8;
box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.1);
}
@media (max-width: 768px) {
.header {
padding: 12px 16px;
}
.header-title {
font-size: 18px;
}
.menu-toggle {
display: block;
}
.header-actions {
display: none;
}
.container {
padding: 16px;
}
.utils-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="header">
<div class="header-title">
<span>πŸ”§</span>
<span>μœ ν‹Έλ¦¬ν‹°</span>
</div>
<button class="menu-toggle" onclick="toggleMobileMenu()" aria-label="메뉴 μ—΄κΈ°">☰</button>
<div class="header-actions">
<span style="margin-right: 12px; color: #5f6368;">{{ current_user.nickname or current_user.username }}</span>
<a href="{{ url_for('main.admin') }}" class="btn btn-secondary">μ‚¬μš©μž 관리</a>
<a href="{{ url_for('main.admin_webnovels') }}" class="btn btn-secondary">μ›Ήμ†Œμ„€ 관리</a>
<a href="{{ url_for('main.admin_files') }}" class="btn btn-secondary">파일 λͺ©λ‘</a>
<a href="{{ url_for('main.admin_messages') }}" class="btn btn-secondary">λ©”μ‹œμ§€ 확인</a>
<a href="{{ url_for('main.admin_prompts') }}" class="btn btn-secondary">ν”„λ‘¬ν”„νŠΈ 관리</a>
<a href="{{ url_for('main.admin_settings') }}" class="btn btn-secondary">AI μ„€μ •</a>
<a href="{{ url_for('main.admin_utils') }}" class="btn btn-secondary">μœ ν‹Έ</a>
<a href="{{ url_for('main.admin_tokens') }}" class="btn btn-secondary">토큰 톡계</a>
<a href="{{ url_for('main.index') }}" class="btn btn-secondary">λ©”μΈμœΌλ‘œ</a>
<a href="{{ url_for('main.logout') }}" class="btn btn-secondary">λ‘œκ·Έμ•„μ›ƒ</a>
</div>
</div>
<!-- λͺ¨λ°”일 메뉴 -->
<div class="mobile-menu" id="mobileMenu" onclick="closeMobileMenuOnBackdrop(event)">
<div class="mobile-menu-content" onclick="event.stopPropagation()">
<div class="mobile-menu-header">
<div class="mobile-menu-title">메뉴</div>
<button class="mobile-menu-close" onclick="toggleMobileMenu()" aria-label="메뉴 λ‹«κΈ°">&times;</button>
</div>
<div class="mobile-menu-user">{{ current_user.nickname or current_user.username }}</div>
<div class="mobile-menu-items">
<a href="{{ url_for('main.admin') }}" class="mobile-menu-item" onclick="closeMobileMenu()">μ‚¬μš©μž 관리</a>
<a href="{{ url_for('main.admin_webnovels') }}" class="mobile-menu-item" onclick="closeMobileMenu()">μ›Ήμ†Œμ„€ 관리</a>
<a href="{{ url_for('main.admin_files') }}" class="mobile-menu-item" onclick="closeMobileMenu()">파일 λͺ©λ‘</a>
<a href="{{ url_for('main.admin_messages') }}" class="mobile-menu-item" onclick="closeMobileMenu()">λ©”μ‹œμ§€ 확인</a>
<a href="{{ url_for('main.admin_prompts') }}" class="mobile-menu-item" onclick="closeMobileMenu()">ν”„λ‘¬ν”„νŠΈ 관리</a>
<a href="{{ url_for('main.admin_settings') }}" class="mobile-menu-item" onclick="closeMobileMenu()">AI μ„€μ •</a>
<a href="{{ url_for('main.admin_utils') }}" class="mobile-menu-item" onclick="closeMobileMenu()">μœ ν‹Έ</a>
<a href="{{ url_for('main.admin_tokens') }}" class="mobile-menu-item" onclick="closeMobileMenu()">토큰 톡계</a>
<a href="{{ url_for('main.index') }}" class="mobile-menu-item" onclick="closeMobileMenu()">λ©”μΈμœΌλ‘œ</a>
<a href="{{ url_for('main.logout') }}" class="mobile-menu-item" onclick="closeMobileMenu()">λ‘œκ·Έμ•„μ›ƒ</a>
</div>
</div>
</div>
<div class="container">
<div class="page-header">
<h1>μœ ν‹Έλ¦¬ν‹°</h1>
<p>λ‹€μ–‘ν•œ λΆ€μˆ˜μ μΈ κΈ°λŠ₯듀을 관리할 수 μžˆμŠ΅λ‹ˆλ‹€.</p>
</div>
<div id="alertContainer"></div>
<!-- 회차 ꡬ뢄 방식 λ³€ν™˜ -->
<div class="card">
<div class="card-header">
<div class="card-title">회차 ꡬ뢄 방식 λ³€ν™˜</div>
</div>
<div style="padding: 16px 0;">
<p style="margin-bottom: 16px; color: #5f6368; font-size: 14px;">
λ‹€μ–‘ν•œ 회차 ꡬ뢄 방식(@n, #n, @ν”„λ‘€λ‘œκ·Έ λ“±)을 #nν™” ν˜•μ‹μœΌλ‘œ λ³€ν™˜ν•©λ‹ˆλ‹€. νŒŒμΌμ„ μ—…λ‘œλ“œν•˜κ±°λ‚˜ 직접 λ‚΄μš©μ„ μž…λ ₯ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
</p>
<div class="form-group">
<label for="episodeFileUpload">파일 μ—…λ‘œλ“œ</label>
<input type="file" id="episodeFileUpload" accept=".txt,.md" onchange="handleFileUpload(event)" style="width: 100%; padding: 8px; border: 1px solid #dadce0; border-radius: 6px; font-size: 14px;">
<small style="color: #5f6368; font-size: 12px; display: block; margin-top: 4px;">
ν…μŠ€νŠΈ 파일(.txt, .md)을 μ—…λ‘œλ“œν•˜μ„Έμš”
</small>
</div>
<div class="form-group" id="encodingGroup" style="display: none;">
<label for="fileEncoding">파일 인코딩</label>
<div style="display: flex; gap: 8px; align-items: center;">
<select id="fileEncoding" style="flex: 1; padding: 8px 12px; border: 1px solid #dadce0; border-radius: 6px; font-size: 14px;">
<option value="utf-8">UTF-8</option>
<option value="cp949">CP949 (EUC-KR)</option>
<option value="euc-kr">EUC-KR</option>
<option value="latin-1">Latin-1 (ISO-8859-1)</option>
<option value="utf-16">UTF-16</option>
<option value="utf-16-le">UTF-16 LE</option>
<option value="utf-16-be">UTF-16 BE</option>
</select>
<button class="btn btn-secondary" onclick="reloadFileWithEncoding()" style="padding: 8px 16px;">인코딩 적용</button>
</div>
<div id="encodingInfo" style="margin-top: 8px; padding: 8px; background: #f8f9fa; border-radius: 4px; font-size: 12px; color: #5f6368; display: none;">
<span id="encodingStatus"></span>
</div>
</div>
<div class="form-group">
<label for="episodeContentInput">λ˜λŠ” 직접 λ‚΄μš© μž…λ ₯</label>
<textarea id="episodeContentInput" placeholder="@1&#10;@2ν™”&#10;3ν™”&#10;... ν˜•μ‹μ˜ λ‚΄μš©μ„ μž…λ ₯ν•˜μ„Έμš”"></textarea>
</div>
<div style="display: flex; gap: 8px; margin-top: 16px;">
<button class="btn btn-primary" onclick="convertEpisodeFormat()">λ³€ν™˜ μ‹€ν–‰</button>
<button class="btn btn-secondary" onclick="previewConversion()">미리보기</button>
<button class="btn btn-secondary" onclick="clearEpisodeForm()">μ΄ˆκΈ°ν™”</button>
<button class="btn btn-secondary" id="downloadBtn" onclick="downloadConvertedFile()" style="display: none;">λ‹€μš΄λ‘œλ“œ</button>
</div>
<div id="episodePreview" class="preview-box empty" style="display: none;">
λ³€ν™˜ κ²°κ³Όκ°€ 여기에 ν‘œμ‹œλ©λ‹ˆλ‹€.
</div>
<div id="conversionInfo" style="margin-top: 12px; padding: 12px; background: #e8f0fe; border-radius: 6px; display: none;">
<div style="font-size: 14px; color: #1967d2;">
<strong>λ³€ν™˜ μ™„λ£Œ!</strong> μœ„μ˜ "λ‹€μš΄λ‘œλ“œ" λ²„νŠΌμ„ ν΄λ¦­ν•˜μ—¬ λ³€ν™˜λœ νŒŒμΌμ„ λ‹€μš΄λ‘œλ“œν•˜μ„Έμš”.
</div>
</div>
</div>
</div>
</div>
<script>
function toggleMobileMenu() {
const menu = document.getElementById('mobileMenu');
menu.classList.toggle('active');
document.body.style.overflow = menu.classList.contains('active') ? 'hidden' : '';
}
function closeMobileMenu() {
const menu = document.getElementById('mobileMenu');
menu.classList.remove('active');
document.body.style.overflow = '';
}
function closeMobileMenuOnBackdrop(event) {
if (event.target.id === 'mobileMenu') {
closeMobileMenu();
}
}
function showAlert(message, type = 'success') {
const container = document.getElementById('alertContainer');
container.innerHTML = `<div class="alert ${type}">${message}</div>`;
setTimeout(() => {
container.innerHTML = '';
}, 5000);
}
// ν˜„μž¬ μ„ νƒλœ 파일 μ €μž₯
let currentFile = null;
// 파일 μ—…λ‘œλ“œ 처리
async function handleFileUpload(event) {
const file = event.target.files[0];
if (!file) {
currentFile = null;
document.getElementById('encodingGroup').style.display = 'none';
return;
}
currentFile = file;
// 인코딩 감지 API 호좜
try {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/admin/utils/detect-encoding', {
method: 'POST',
credentials: 'include',
body: formData
});
const data = await response.json();
if (response.ok && data.detected_encoding) {
// 인코딩 정보 ν‘œμ‹œ
document.getElementById('fileEncoding').value = data.detected_encoding;
const encodingInfo = document.getElementById('encodingInfo');
const encodingStatus = document.getElementById('encodingStatus');
encodingStatus.innerHTML = `κ°μ§€λœ 인코딩: <strong>${data.detected_encoding}</strong> (신뒰도: ${Math.round(data.confidence * 100)}%)`;
encodingInfo.style.display = 'block';
document.getElementById('encodingGroup').style.display = 'block';
// κ°μ§€λœ μΈμ½”λ”©μœΌλ‘œ 파일 읽기
await loadFileWithEncoding(data.detected_encoding);
} else {
// 인코딩 감지 μ‹€νŒ¨ μ‹œ κΈ°λ³Έκ°’(UTF-8)으둜 μ‹œλ„
document.getElementById('fileEncoding').value = 'utf-8';
document.getElementById('encodingInfo').style.display = 'none';
document.getElementById('encodingGroup').style.display = 'block';
await loadFileWithEncoding('utf-8');
}
} catch (error) {
console.error('인코딩 감지 였λ₯˜:', error);
// 였λ₯˜ λ°œμƒ μ‹œ κΈ°λ³Έκ°’μœΌλ‘œ μ‹œλ„
document.getElementById('fileEncoding').value = 'utf-8';
document.getElementById('encodingGroup').style.display = 'block';
await loadFileWithEncoding('utf-8');
}
}
// μ§€μ •λœ μΈμ½”λ”©μœΌλ‘œ 파일 읽기
async function loadFileWithEncoding(encoding) {
if (!currentFile) {
return;
}
try {
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('episodeContentInput').value = e.target.result;
showAlert(`파일이 ${encoding} μΈμ½”λ”©μœΌλ‘œ λ‘œλ“œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.`, 'success');
};
reader.onerror = function() {
showAlert('파일 읽기 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', 'error');
};
reader.readAsText(currentFile, encoding);
} catch (error) {
console.error('파일 읽기 였λ₯˜:', error);
showAlert(`파일 읽기 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: ${error.message}`, 'error');
}
}
// 인코딩 λ³€κ²½ ν›„ 파일 λ‹€μ‹œ 읽기
function reloadFileWithEncoding() {
const encoding = document.getElementById('fileEncoding').value;
if (!currentFile) {
showAlert('νŒŒμΌμ„ λ¨Όμ € μ—…λ‘œλ“œν•΄μ£Όμ„Έμš”.', 'error');
return;
}
loadFileWithEncoding(encoding);
}
// λ‹€μš΄λ‘œλ“œ URL μ €μž₯
let downloadUrl = null;
let downloadFilename = null;
// 회차 ν˜•μ‹ λ³€ν™˜
async function convertEpisodeFormat() {
const fileInput = document.getElementById('episodeFileUpload');
const content = document.getElementById('episodeContentInput').value.trim();
const file = fileInput.files[0];
if (!file && !content) {
showAlert('νŒŒμΌμ„ μ—…λ‘œλ“œν•˜κ±°λ‚˜ λ‚΄μš©μ„ μž…λ ₯ν•΄μ£Όμ„Έμš”.', 'error');
return;
}
try {
const formData = new FormData();
if (file) {
formData.append('file', file);
// μ„ νƒλœ 인코딩 정보 μΆ”κ°€
const encoding = document.getElementById('fileEncoding').value || 'utf-8';
formData.append('encoding', encoding);
} else if (content) {
// JSON으둜 전솑
const response = await fetch('/api/admin/utils/convert-episode-format', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
content: content,
filename: file ? file.name : 'converted_file.txt'
})
});
const data = await response.json();
if (response.ok) {
showAlert(data.message, 'success');
if (data.converted_content) {
document.getElementById('episodePreview').textContent = data.converted_content;
document.getElementById('episodePreview').style.display = 'block';
document.getElementById('episodePreview').classList.remove('empty');
}
if (data.download_url) {
downloadUrl = data.download_url;
downloadFilename = data.filename;
document.getElementById('downloadBtn').style.display = 'inline-block';
document.getElementById('conversionInfo').style.display = 'block';
}
} else {
showAlert(data.error || 'λ³€ν™˜ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', 'error');
}
return;
}
// 파일 μ—…λ‘œλ“œμΈ 경우
const response = await fetch('/api/admin/utils/convert-episode-format', {
method: 'POST',
credentials: 'include',
body: formData
});
const data = await response.json();
if (response.ok) {
showAlert(data.message, 'success');
if (data.converted_content) {
document.getElementById('episodePreview').textContent = data.converted_content;
document.getElementById('episodePreview').style.display = 'block';
document.getElementById('episodePreview').classList.remove('empty');
}
if (data.download_url) {
downloadUrl = data.download_url;
downloadFilename = data.filename;
document.getElementById('downloadBtn').style.display = 'inline-block';
document.getElementById('conversionInfo').style.display = 'block';
}
} else {
showAlert(data.error || 'λ³€ν™˜ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', 'error');
}
} catch (error) {
console.error('λ³€ν™˜ 였λ₯˜:', error);
showAlert(`였λ₯˜: ${error.message}`, 'error');
}
}
// λ³€ν™˜λœ 파일 λ‹€μš΄λ‘œλ“œ
function downloadConvertedFile() {
if (!downloadUrl) {
showAlert('λ‹€μš΄λ‘œλ“œν•  파일이 μ—†μŠ΅λ‹ˆλ‹€.', 'error');
return;
}
// μƒˆ μ°½μ—μ„œ λ‹€μš΄λ‘œλ“œ 링크 μ—΄κΈ°
window.location.href = downloadUrl;
}
// 미리보기
async function previewConversion() {
const fileInput = document.getElementById('episodeFileUpload');
const content = document.getElementById('episodeContentInput').value.trim();
const file = fileInput.files[0];
if (!file && !content) {
showAlert('νŒŒμΌμ„ μ—…λ‘œλ“œν•˜κ±°λ‚˜ λ‚΄μš©μ„ μž…λ ₯ν•΄μ£Όμ„Έμš”.', 'error');
return;
}
try {
let response;
if (file) {
const formData = new FormData();
formData.append('file', file);
// μ„ νƒλœ 인코딩 정보 μΆ”κ°€
const encoding = document.getElementById('fileEncoding').value || 'utf-8';
formData.append('encoding', encoding);
response = await fetch('/api/admin/utils/convert-episode-format', {
method: 'POST',
credentials: 'include',
body: formData
});
} else {
response = await fetch('/api/admin/utils/convert-episode-format', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
content: content,
filename: 'preview.txt'
})
});
}
const data = await response.json();
if (response.ok && data.converted_content) {
const previewBox = document.getElementById('episodePreview');
previewBox.textContent = data.converted_content.substring(0, 2000) + (data.converted_content.length > 2000 ? '\n... (더 λ§Žμ€ λ‚΄μš©μ΄ μžˆμŠ΅λ‹ˆλ‹€)' : '');
previewBox.style.display = 'block';
previewBox.classList.remove('empty');
} else {
showAlert(data.error || '미리보기 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', 'error');
}
} catch (error) {
console.error('미리보기 였λ₯˜:', error);
showAlert(`였λ₯˜: ${error.message}`, 'error');
}
}
// 폼 μ΄ˆκΈ°ν™”
function clearEpisodeForm() {
document.getElementById('episodeFileUpload').value = '';
document.getElementById('episodeContentInput').value = '';
document.getElementById('episodePreview').style.display = 'none';
document.getElementById('episodePreview').textContent = 'λ³€ν™˜ κ²°κ³Όκ°€ 여기에 ν‘œμ‹œλ©λ‹ˆλ‹€.';
document.getElementById('episodePreview').classList.add('empty');
document.getElementById('downloadBtn').style.display = 'none';
document.getElementById('conversionInfo').style.display = 'none';
document.getElementById('encodingGroup').style.display = 'none';
document.getElementById('encodingInfo').style.display = 'none';
document.getElementById('fileEncoding').value = 'utf-8';
currentFile = null;
downloadUrl = null;
downloadFilename = null;
}
// νŽ˜μ΄μ§€ λ‘œλ“œ μ‹œ μ΄ˆκΈ°ν™”
document.addEventListener('DOMContentLoaded', function() {
console.log('μœ ν‹Έλ¦¬ν‹° νŽ˜μ΄μ§€ λ‘œλ“œ μ™„λ£Œ');
});
</script>
</body>
</html>