NoLev commited on
Commit
37f3c9b
·
verified ·
1 Parent(s): a1c3141

Create static/index.html

Browse files
Files changed (1) hide show
  1. static/index.html +201 -0
static/index.html ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Podcast to Novel Converter</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ </head>
9
+ <body class="bg-gray-100 flex items-center justify-center min-h-screen">
10
+ <div class="bg-white p-6 rounded-lg shadow-lg w-full max-w-4xl">
11
+ <h1 class="text-2xl font-bold mb-4 text-center">Podcast to Novel Converter</h1>
12
+
13
+ <!-- Model Selection -->
14
+ <div class="mb-4">
15
+ <label for="model" class="block text-sm font-medium text-gray-700">Select AI Model</label>
16
+ <select id="model" class="mt-1 block w-full p-2 border border-gray-300 rounded-md" required>
17
+ <option value="deepseek/deepseek-chat-v3-0324:free">DeepSeek Chat (Free)</option>
18
+ <option value="deepseek/deepseek-r1-0528:free">DeepSeek R1 (Free)</option>
19
+ <option value="tngtech/deepseek-r1t2-chimera:free">DeepSeek TNG (Free)</option>
20
+ <option value="x-ai/grok-4">Grok 4</option>
21
+ <option value="x-ai/grok-3">Grok 3</option>
22
+ <option value="anthropic/claude-3.7-sonnet">Claude Sonnet 3.7</option>
23
+ <option value="anthropic/claude-3.5-sonnet">Claude 3.5 Sonnet</option>
24
+ <option value="openai/gpt-4o-mini">GPT-4o Mini</option>
25
+ <option value="meta-ai/llama-3.1-8b-instruct">LLaMA 3.1 8B Instruct</option>
26
+ </select>
27
+ </div>
28
+
29
+ <!-- File Upload or Transcript Input -->
30
+ <div class="mb-4">
31
+ <label for="file" class="block text-sm font-medium text-gray-700">Upload Transcript File (TXT or PDF)</label>
32
+ <input type="file" id="file" accept=".txt,.pdf" class="mt-1 block w-full p-2 border border-gray-300 rounded-md">
33
+ <p class="text-sm text-gray-500 mt-1">Or paste below:</p>
34
+ <label for="transcript" class="block text-sm font-medium text-gray-700 mt-2">Podcast Transcript</label>
35
+ <textarea id="transcript" class="mt-1 block w-full p-2 border border-gray-300 rounded-md" rows="10" placeholder="Paste your podcast transcript here..."></textarea>
36
+ <p id="transcript_count" class="text-sm text-gray-500 mt-1">Characters: 0</p>
37
+ </div>
38
+
39
+ <!-- Generate Button -->
40
+ <div class="mb-4">
41
+ <button id="generate_novel" class="w-full bg-purple-600 text-white p-2 rounded-md hover:bg-purple-700">Generate Novel Structure</button>
42
+ </div>
43
+
44
+ <!-- Outputs -->
45
+ <div id="outputs" class="space-y-4 hidden">
46
+ <div>
47
+ <label class="block text-sm font-medium text-gray-700">Outline</label>
48
+ <textarea id="outline" class="mt-1 block w-full p-2 border border-gray-300 rounded-md" rows="4" readonly></textarea>
49
+ </div>
50
+ <div>
51
+ <label class="block text-sm font-medium text-gray-700">Characters</label>
52
+ <textarea id="characters" class="mt-1 block w-full p-2 border border-gray-300 rounded-md" rows="4" readonly></textarea>
53
+ </div>
54
+ <div>
55
+ <label class="block text-sm font-medium text-gray-700">Locations</label>
56
+ <textarea id="locations" class="mt-1 block w-full p-2 border border-gray-300 rounded-md" rows="3" readonly></textarea>
57
+ </div>
58
+ <div>
59
+ <label class="block text-sm font-medium text-gray-700">Objects</label>
60
+ <textarea id="objects" class="mt-1 block w-full p-2 border border-gray-300 rounded-md" rows="3" readonly></textarea>
61
+ </div>
62
+ <div>
63
+ <label class="block text-sm font-medium text-gray-700">Manuscript</label>
64
+ <textarea id="manuscript" class="mt-1 block w-full p-2 border border-gray-300 rounded-md" rows="20" readonly></textarea>
65
+ </div>
66
+ <button id="copy_all" class="mt-2 bg-green-600 text-white p-2 rounded-md hover:bg-green-700">Copy All Outputs</button>
67
+ </div>
68
+ </div>
69
+
70
+ <script>
71
+ function checkPassword() {
72
+ return new Promise((resolve) => {
73
+ const check = () => {
74
+ const enteredPassword = prompt("Please enter the password:");
75
+ if (!enteredPassword) {
76
+ check();
77
+ } else {
78
+ fetch('/check_password', {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ body: JSON.stringify({ password: enteredPassword })
82
+ })
83
+ .then(response => response.json())
84
+ .then(data => {
85
+ if (!data.success) {
86
+ alert("Incorrect password. Access denied.");
87
+ check();
88
+ } else {
89
+ resolve(true);
90
+ }
91
+ })
92
+ .catch(error => {
93
+ console.error('Error checking password:', error);
94
+ alert("An error occurred. Please try again.");
95
+ check();
96
+ });
97
+ }
98
+ };
99
+ check();
100
+ });
101
+ }
102
+
103
+ const transcriptTextarea = document.getElementById("transcript");
104
+ const fileInput = document.getElementById("file");
105
+ const countDisplay = document.getElementById("transcript_count");
106
+ const generateButton = document.getElementById("generate_novel");
107
+ const outputsDiv = document.getElementById("outputs");
108
+
109
+ // Handle file upload: read and populate textarea
110
+ fileInput.addEventListener("change", (event) => {
111
+ const file = event.target.files[0];
112
+ if (file) {
113
+ const reader = new FileReader();
114
+ reader.onload = (e) => {
115
+ transcriptTextarea.value = e.target.result;
116
+ updateCount();
117
+ };
118
+ reader.onerror = () => {
119
+ alert("Error reading file. Please paste manually.");
120
+ };
121
+ if (file.type === 'application/pdf') {
122
+ // For PDF, note: JS can't parse PDF easily, so warn and suggest manual paste
123
+ alert("PDF files will be processed on the server. Paste or upload will work, but preview not available in textarea.");
124
+ transcriptTextarea.value = "[PDF content will be extracted on server]";
125
+ } else {
126
+ reader.readAsText(file);
127
+ }
128
+ updateCount();
129
+ }
130
+ });
131
+
132
+ function updateCount() {
133
+ const charCount = transcriptTextarea.value.length;
134
+ countDisplay.textContent = `Characters: ${charCount}`;
135
+ }
136
+
137
+ transcriptTextarea.addEventListener("input", updateCount);
138
+
139
+ generateButton.addEventListener("click", async () => {
140
+ await checkPassword();
141
+ const model = document.getElementById("model").value;
142
+ const transcript = transcriptTextarea.value.trim();
143
+ const file = fileInput.files[0];
144
+ if (!transcript && !file) {
145
+ alert("Please enter a transcript or select a file.");
146
+ return;
147
+ }
148
+ const statusDiv = document.createElement("div");
149
+ statusDiv.textContent = "Generating novel structure... (This may take a few minutes)";
150
+ statusDiv.className = "text-center text-blue-600 mb-4";
151
+ outputsDiv.insertBefore(statusDiv, outputsDiv.firstChild);
152
+ outputsDiv.classList.remove("hidden");
153
+ try {
154
+ const formData = new FormData();
155
+ formData.append('model', model);
156
+ if (file) {
157
+ formData.append('file', file);
158
+ } else {
159
+ formData.append('transcript', transcript);
160
+ }
161
+
162
+ const response = await fetch("/generate_novel", {
163
+ method: "POST",
164
+ body: formData
165
+ });
166
+ if (!response.ok) {
167
+ const errorData = await response.json();
168
+ throw new Error(errorData.detail || `HTTP error! status: ${response.status}`);
169
+ }
170
+ const result = await response.json();
171
+ statusDiv.remove();
172
+ if (result.error) {
173
+ document.getElementById("manuscript").value = `Error: ${result.error}`;
174
+ } else {
175
+ document.getElementById("outline").value = result.outline || "";
176
+ document.getElementById("characters").value = result.characters || "";
177
+ document.getElementById("locations").value = result.locations || "";
178
+ document.getElementById("objects").value = result.objects || "";
179
+ document.getElementById("manuscript").value = result.manuscript || "";
180
+ }
181
+ } catch (error) {
182
+ statusDiv.textContent = `Error: ${error.message}`;
183
+ statusDiv.className = "text-center text-red-600 mb-4";
184
+ }
185
+ });
186
+
187
+ document.getElementById("copy_all").addEventListener("click", () => {
188
+ const outputs = [
189
+ document.getElementById("outline").value,
190
+ document.getElementById("characters").value,
191
+ document.getElementById("locations").value,
192
+ document.getElementById("objects").value,
193
+ document.getElementById("manuscript").value
194
+ ].join("\n\n---\n\n");
195
+ navigator.clipboard.writeText(outputs)
196
+ .then(() => alert("All outputs copied to clipboard! Paste into Novel Prompt Generator."))
197
+ .catch(err => console.error('Failed to copy: ', err));
198
+ });
199
+ </script>
200
+ </body>
201
+ </html>