Sodagraph's picture
스페이스 이동
eda02a7
raw
history blame
4.35 kB
<template>
<div id="app">
<h1>YouTube RAG Explorer</h1>
<p>영상 URL과 찾고 싶은 내용을 입력해주세요.</p>
<div class="input-section">
<label for="videoUrl">YouTube 영상 URL:</label>
<input type="text" id="videoUrl" v-model="videoUrl" placeholder="예: https://www.youtube.com/watch?v=..." />
</div>
<div class="input-section">
<label for="query">찾을 내용 (쿼리):</label>
<input type="text" id="query" v-model="query" placeholder="예: RAG 기술의 장점은?" />
</div>
<button @click="processVideo" :disabled="loading">
{{ loading ? '처리 중...' : '영상 탐색 시작' }}
</button>
<div v-if="errorMessage" class="error-message">
{{ errorMessage }}
</div>
<div v-if="results.length > 0 && !loading" class="results-section">
<h2>검색 결과:</h2>
<div v-for="(result, index) in results" :key="index" class="result-item">
<p><strong>시간:</strong> <a :href="videoUrl + '&t=' + result.timestamp.replace(/:/g, 'm') + 's'" target="_blank">{{ result.timestamp }}</a></p>
<p><strong>내용:</strong> {{ result.text }}</p>
</div>
</div>
<div v-else-if="!loading && responseMessage" class="info-message">
{{ responseMessage }}
</div>
</div>
</template>
<script>
import apiClient from './api'; // axios 인스턴스 import
export default {
name: 'App',
data() {
return {
videoUrl: '',
query: '',
loading: false,
results: [],
errorMessage: '',
responseMessage: ''
};
},
methods: {
async processVideo() {
this.errorMessage = '';
this.results = [];
this.responseMessage = '';
this.loading = true;
try {
const response = await apiClient.post(`/process_youtube_video`, {
video_url: this.videoUrl,
query: this.query
});
this.responseMessage = response.data.message;
if (response.data.results && response.data.results.length > 0) {
this.results = response.data.results;
} else {
this.responseMessage = response.data.message || "결과를 찾을 수 없습니다.";
}
} catch (error) {
console.error("API 호출 중 오류 발생:", error);
if (error.response) {
this.errorMessage = `오류: ${error.response.data.detail || error.response.statusText}`;
} else if (error.request) {
this.errorMessage = '서버 응답이 없습니다. 백엔드가 실행 중인지 확인하세요.';
} else {
this.errorMessage = '요청 설정 중 오류가 발생했습니다.';
}
} finally {
this.loading = false;
}
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
max-width: 800px;
margin-left: auto;
margin-right: auto;
padding: 20px;
border: 1px solid #eee;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.input-section {
margin-bottom: 20px;
}
label {
display: block;
font-weight: bold;
margin-bottom: 5px;
}
input[type="text"] {
width: calc(100% - 20px);
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
}
button {
background-color: #42b983;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s ease;
}
button:hover:not(:disabled) {
background-color: #368a68;
}
button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.error-message {
color: red;
margin-top: 20px;
font-weight: bold;
}
.info-message {
color: #3498db;
margin-top: 20px;
font-style: italic;
}
.results-section {
margin-top: 30px;
text-align: left;
border-top: 1px solid #eee;
padding-top: 20px;
}
.results-section h2 {
text-align: center;
color: #333;
}
.result-item {
background-color: #f9f9f9;
border: 1px solid #ddd;
border-radius: 6px;
padding: 15px;
margin-bottom: 15px;
}
.result-item p {
margin: 5px 0;
}
.result-item a {
color: #42b983;
text-decoration: none;
}
.result-item a:hover {
text-decoration: underline;
}
</style>