thejagstudio commited on
Commit
0373222
·
verified ·
1 Parent(s): 2c43524

Upload 6 files

Browse files
Files changed (6) hide show
  1. main copy 2.py +188 -0
  2. main copy.py +171 -0
  3. main.py +254 -219
  4. static/simple.css +84 -86
  5. templates/index copy.html +420 -0
  6. templates/index.html +487 -419
main copy 2.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import base64
4
+ from flask import Flask, request, render_template, jsonify
5
+ from google import genai
6
+ from google.genai import types
7
+ from werkzeug.utils import secure_filename
8
+ import requests
9
+ import time
10
+ import tempfile
11
+
12
+ # Initialize Flask app
13
+ app = Flask(__name__)
14
+
15
+ # Initialize Gemini client
16
+ client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY", "AIzaSyCg9NGsLygb0sVKpviMkgV4eMPLd9nXW7w"))
17
+
18
+ # Set up generation config
19
+ generate_content_config = types.GenerateContentConfig(
20
+ temperature=1.0,
21
+ top_p=0.95,
22
+ top_k=40,
23
+ max_output_tokens=8192,
24
+ )
25
+
26
+ # Initialize the model
27
+ model_name = "gemini-2.5-flash"
28
+
29
+
30
+ @app.route("/")
31
+ def index():
32
+ return render_template("index.html")
33
+
34
+
35
+ @app.route("/convert", methods=["POST"])
36
+ def convert_audio():
37
+ if "file" not in request.files:
38
+ return jsonify({"error": "No file part"}), 400
39
+
40
+ file = request.files["file"]
41
+ if file.filename == "":
42
+ return jsonify({"error": "No selected file"}), 400
43
+
44
+ if file:
45
+ try:
46
+ # Determine mime type based on file extension
47
+ mime_type = None
48
+ if file.filename.lower().endswith('.mp3'):
49
+ mime_type = 'audio/mpeg'
50
+ elif file.filename.lower().endswith('.wav'):
51
+ mime_type = 'audio/wav'
52
+ elif file.filename.lower().endswith('.m4a'):
53
+ mime_type = 'audio/x-m4a'
54
+ elif file.filename.lower().endswith('.ogg'):
55
+ mime_type = 'audio/ogg'
56
+ else:
57
+ return jsonify({"error": "Unsupported file type"}), 400
58
+
59
+ # Save the file temporarily
60
+ temp_dir = tempfile.mkdtemp()
61
+ temp_path = os.path.join(temp_dir, secure_filename(file.filename))
62
+ file.save(temp_path)
63
+
64
+ # Upload file to Gemini with mime type
65
+ with open(temp_path, 'rb') as f:
66
+ uploaded_file = client.files.upload(file=f,config={'mime_type': mime_type})
67
+
68
+ # Clean up temporary file
69
+ os.remove(temp_path)
70
+ os.rmdir(temp_dir)
71
+
72
+ # Create content for Gemini using uploaded file
73
+ prompt = """Extract the Gujarati lyrics from the provided audio file and output them using the following HTML template. Ensure that each line of the lyrics is correctly inserted into the template and that any placeholders are replaced with the appropriate content from the file.
74
+
75
+ ```
76
+ <html>
77
+ <head>
78
+ <title>Bhaktisudha</title>
79
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
80
+ <link href="/static/simple.css" rel="stylesheet" type="text/css" />
81
+ <style>
82
+ </style>
83
+ </head>
84
+ <body>
85
+ <div class="main">
86
+ <div class="gtitlev3">
87
+ {Title of the file}
88
+ </div>
89
+ <div class="gpara">
90
+ Line1 <br/>
91
+ Line2 <br/>
92
+ </div>
93
+ <div class="chend">
94
+ *****
95
+ </div>
96
+ </div>
97
+ </body>
98
+ </html>
99
+ ```"""
100
+
101
+ # Generate response
102
+ response = ""
103
+ for chunk in client.models.generate_content_stream(
104
+ model=model_name,
105
+ contents=[uploaded_file, prompt],
106
+ config=generate_content_config,
107
+ ):
108
+ response += chunk.text
109
+
110
+ # Clean up the response
111
+ lyrics = response.replace("```html", "").replace("```", "")
112
+ return jsonify({"lyrics": lyrics})
113
+
114
+ except Exception as e:
115
+ return jsonify({"error": str(e)}), 500
116
+
117
+
118
+ @app.route("/convert-youtube", methods=["POST"])
119
+ def convert_youtube():
120
+ try:
121
+ data = request.get_json()
122
+ url = data.get("url")
123
+ if not url:
124
+ return jsonify({"error": "No URL provided"}), 400
125
+
126
+ # Create content for Gemini
127
+ contents = [
128
+ types.Content(
129
+ role="user",
130
+ parts=[
131
+ types.Part(
132
+ file_data=types.FileData(
133
+ file_uri=url,
134
+ mime_type="video/*",
135
+ )
136
+ ),
137
+ types.Part.from_text(
138
+ text="""Extract the Gujarati lyrics from the provided video and output them using the following HTML template. Ensure that each line of the lyrics is correctly inserted into the template and that any placeholders are replaced with the appropriate content from the file.
139
+
140
+ ```
141
+ <html>
142
+ <head>
143
+ <title>Bhaktisudha</title>
144
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
145
+ <link href="/static/simple.css" rel="stylesheet" type="text/css" />
146
+ <style>
147
+ </style>
148
+ </head>
149
+ <body>
150
+ <div class="main">
151
+ <div class="gtitlev3">
152
+ {Title of the file}
153
+ </div>
154
+ <div class="gpara">
155
+ Line1 <br/>
156
+ Line2 <br/>
157
+ </div>
158
+ <div class="chend">
159
+ *****
160
+ </div>
161
+ </div>
162
+ </body>
163
+ </html>
164
+ ```"""
165
+ ),
166
+ ],
167
+ ),
168
+ ]
169
+
170
+ # Generate response
171
+ response = ""
172
+ for chunk in client.models.generate_content_stream(
173
+ model=model_name,
174
+ contents=contents,
175
+ config=generate_content_config,
176
+ ):
177
+ response += chunk.text
178
+
179
+ # Clean up the response
180
+ lyrics = response.replace("```html", "").replace("```", "")
181
+ return jsonify({"lyrics": lyrics})
182
+
183
+ except Exception as e:
184
+ return jsonify({"error": str(e)})
185
+
186
+
187
+ if __name__ == "__main__":
188
+ app.run(debug=True, host="0.0.0.0", port=7860)
main copy.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ from flask import Flask, request, render_template, jsonify
4
+ import google.generativeai as genai
5
+ from werkzeug.utils import secure_filename
6
+ import requests
7
+ import time
8
+ from google.generativeai import types
9
+
10
+ # Initialize Flask app
11
+ app = Flask(__name__)
12
+
13
+ # Configure Gemini API
14
+ genai.configure(api_key="AIzaSyCg9NGsLygb0sVKpviMkgV4eMPLd9nXW7w")
15
+
16
+ # Set up generation config
17
+ generation_config = {
18
+ "temperature": 1,
19
+ "top_p": 0.95,
20
+ "top_k": 40,
21
+ "max_output_tokens": 8192,
22
+ "response_mime_type": "text/plain",
23
+ }
24
+
25
+ # Initialize the model
26
+ model = genai.GenerativeModel(
27
+ model_name="gemini-2.0-flash",
28
+ generation_config=generation_config,
29
+ )
30
+
31
+ def upload_to_gemini(file_bytes, mime_type=None):
32
+ """Uploads the given file bytes to Gemini."""
33
+ file = genai.upload_file(file_bytes, mime_type=mime_type)
34
+ print(f"Uploaded file as: {file.uri}")
35
+ return file
36
+
37
+
38
+ @app.route('/')
39
+ def index():
40
+ return render_template('index.html')
41
+
42
+ @app.route('/convert', methods=['POST'])
43
+ def convert_audio():
44
+ if 'file' not in request.files:
45
+ return jsonify({'error': 'No file part'}), 400
46
+
47
+ file = request.files['file']
48
+ if file.filename == '':
49
+ return jsonify({'error': 'No selected file'}), 400
50
+
51
+ if file:
52
+ try:
53
+ # Read file bytes directly from the request
54
+ file_bytes = io.BytesIO(file.read())
55
+
56
+ # Determine mime type based on file extension
57
+ mime_type = 'audio/mpeg' if file.filename.endswith('.mp3') else \
58
+ 'audio/wav' if file.filename.endswith('.wav') else \
59
+ 'audio/x-m4a' if file.filename.endswith('.m4a') else \
60
+ 'audio/ogg' if file.filename.endswith('.ogg') else None
61
+
62
+ # Upload to Gemini
63
+ gemini_file = upload_to_gemini(file_bytes, mime_type=mime_type)
64
+
65
+ # Start chat session with the uploaded file
66
+ chat_session = model.start_chat(
67
+ history=[
68
+ {
69
+ "role": "user",
70
+ "parts": [gemini_file],
71
+ },
72
+ ]
73
+ )
74
+
75
+ # Generate lyrics
76
+ response = chat_session.send_message('''Extract the Gujarati lyrics from the provided audio file and output them using the following HTML template. Ensure that each line of the lyrics is correctly inserted into the template and that any placeholders are replaced with the appropriate content from the file.
77
+
78
+ ```
79
+ <html>
80
+ <head>
81
+ <title>Bhaktisudha</title>
82
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
83
+ <link href="/static/simple.css" rel="stylesheet" type="text/css" />
84
+ <style>
85
+ </style>
86
+ </head>
87
+ <body>
88
+ <div class="main">
89
+ <div class="gtitlev3">
90
+ {Title of the file}
91
+ </div>
92
+ <div class="gpara">
93
+ Line1 <br/>
94
+ Line2 <br/>
95
+ </div>
96
+ <div class="chend">
97
+ *****
98
+ </div>
99
+ </div>
100
+ </body>
101
+ </html>
102
+ ```''')
103
+ lyrics = response.text.replace("```html","").replace("```","")
104
+ return jsonify({'lyrics': lyrics})
105
+
106
+ except Exception as e:
107
+ return jsonify({'error': str(e)}), 500
108
+
109
+ @app.route('/convert-youtube', methods=['POST'])
110
+ def convert_youtube():
111
+ try:
112
+ data = request.get_json()
113
+ url = data.get('url')
114
+ if not url:
115
+ return jsonify({'error': 'No URL provided'}), 400
116
+
117
+ # Download audio from YouTube
118
+ audio_data = download_from_youtube(url)
119
+
120
+ # Create a file-like object from the audio data
121
+ file_bytes = io.BytesIO(audio_data)
122
+
123
+ # Upload to Gemini
124
+ gemini_file = upload_to_gemini(file_bytes, mime_type='audio/mpeg')
125
+
126
+ # Start chat session with the uploaded file
127
+ chat_session = model.start_chat(
128
+ history=[
129
+ {
130
+ "role": "user",
131
+ "parts": [gemini_file],
132
+ },
133
+ ]
134
+ )
135
+
136
+ # Generate lyrics
137
+ response = chat_session.send_message('''Extract the Gujarati lyrics from the provided audio file and output them using the following HTML template. Ensure that each line of the lyrics is correctly inserted into the template and that any placeholders are replaced with the appropriate content from the file.
138
+
139
+ ```
140
+ <html>
141
+ <head>
142
+ <title>Bhaktisudha</title>
143
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
144
+ <link href="/static/simple.css" rel="stylesheet" type="text/css" />
145
+ <style>
146
+ </style>
147
+ </head>
148
+ <body>
149
+ <div class="main">
150
+ <div class="gtitlev3">
151
+ {Title of the file}
152
+ </div>
153
+ <div class="gpara">
154
+ Line1 <br/>
155
+ Line2 <br/>
156
+ </div>
157
+ <div class="chend">
158
+ *****
159
+ </div>
160
+ </div>
161
+ </body>
162
+ </html>
163
+ ```''')
164
+ lyrics = response.text.replace("```html","").replace("```","")
165
+ return jsonify({'lyrics': lyrics})
166
+
167
+ except Exception as e:
168
+ return jsonify({'error': str(e)})
169
+
170
+ if __name__ == '__main__':
171
+ app.run(debug=True, host='0.0.0.0', port=7860)
main.py CHANGED
@@ -1,219 +1,254 @@
1
- import os
2
- import io
3
- import base64
4
- from flask import Flask, request, render_template, jsonify
5
- from google import genai
6
- from google.genai import types
7
- from werkzeug.utils import secure_filename
8
- import requests
9
- import time
10
-
11
- # Initialize Flask app
12
- app = Flask(__name__)
13
-
14
- # Initialize Gemini client
15
- client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY", "AIzaSyCg9NGsLygb0sVKpviMkgV4eMPLd9nXW7w"))
16
-
17
- # Set up generation config
18
- generate_content_config = types.GenerateContentConfig(
19
- temperature=1.0,
20
- top_p=0.95,
21
- top_k=40,
22
- max_output_tokens=8192,
23
- thinking_config=types.ThinkingConfig(
24
- thinking_budget=-1,
25
- ),
26
- )
27
-
28
- # Initialize the model
29
- model_name = "gemini-2.5-flash"
30
-
31
- @app.route('/')
32
- def index():
33
- return render_template('index.html')
34
-
35
- @app.route('/convert', methods=['POST'])
36
- def convert_audio():
37
- if 'file' not in request.files:
38
- return jsonify({'error': 'No file part'}), 400
39
-
40
- file = request.files['file']
41
- if file.filename == '':
42
- return jsonify({'error': 'No selected file'}), 400
43
-
44
- if file:
45
- try:
46
- # Read file bytes directly from the request
47
- file_bytes = file.read()
48
-
49
- # Determine mime type based on file extension
50
- mime_type = 'audio/mpeg' if file.filename.endswith('.mp3') else \
51
- 'audio/wav' if file.filename.endswith('.wav') else \
52
- 'audio/x-m4a' if file.filename.endswith('.m4a') else \
53
- 'audio/ogg' if file.filename.endswith('.ogg') else None
54
-
55
- # Encode file bytes to base64
56
- encoded_bytes = base64.b64encode(file_bytes).decode('utf-8')
57
- file_data_uri = f"data:{mime_type};base64,{encoded_bytes}"
58
-
59
- # Create content for Gemini
60
- contents = [
61
- types.Content(
62
- role="user",
63
- parts=[
64
- types.Part(
65
- file_data=types.FileData(
66
- file_uri=file_data_uri,
67
- mime_type=mime_type,
68
- )
69
- ),
70
- types.Part.from_text(
71
- text='''Extract the Gujarati lyrics from the provided audio file and output them using the following HTML template. Ensure that each line of the lyrics is correctly inserted into the template and that any placeholders are replaced with the appropriate content from the file.
72
-
73
- ```
74
- <html>
75
- <head>
76
- <title>Bhaktisudha</title>
77
- <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
78
- <link href="/static/simple.css" rel="stylesheet" type="text/css" />
79
- <style>
80
- </style>
81
- </head>
82
- <body>
83
- <div class="main">
84
- <div class="gtitlev3">
85
- {Title of the file}
86
- </div>
87
- <div class="gpara">
88
- Line1 <br/>
89
- Line2 <br/>
90
- </div>
91
- <div class="chend">
92
- *****
93
- </div>
94
- </div>
95
- </body>
96
- </html>
97
- ```'''
98
- ),
99
- ],
100
- ),
101
- ]
102
-
103
- # Generate response
104
- response = ""
105
- for chunk in client.models.generate_content_stream(
106
- model=model_name,
107
- contents=contents,
108
- config=generate_content_config,
109
- ):
110
- response += chunk.text
111
-
112
- # Clean up the response
113
- lyrics = response.replace("```html", "").replace("```", "")
114
- return jsonify({'lyrics': lyrics})
115
-
116
- # Generate lyrics
117
- response = chat_session.send_message('''Extract the Gujarati lyrics from the provided audio file and output them using the following HTML template. Ensure that each line of the lyrics is correctly inserted into the template and that any placeholders are replaced with the appropriate content from the file.
118
-
119
- ```
120
- <html>
121
- <head>
122
- <title>Bhaktisudha</title>
123
- <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
124
- <link href="/static/simple.css" rel="stylesheet" type="text/css" />
125
- <style>
126
- </style>
127
- </head>
128
- <body>
129
- <div class="main">
130
- <div class="gtitlev3">
131
- {Title of the file}
132
- </div>
133
- <div class="gpara">
134
- Line1 <br/>
135
- Line2 <br/>
136
- </div>
137
- <div class="chend">
138
- *****
139
- </div>
140
- </div>
141
- </body>
142
- </html>
143
- ```''')
144
- lyrics = response.text.replace("```html","").replace("```","")
145
- return jsonify({'lyrics': lyrics})
146
-
147
- except Exception as e:
148
- return jsonify({'error': str(e)}), 500
149
-
150
- @app.route('/convert-youtube', methods=['POST'])
151
- def convert_youtube():
152
- try:
153
- data = request.get_json()
154
- url = data.get('url')
155
- if not url:
156
- return jsonify({'error': 'No URL provided'}), 400
157
-
158
- # Create content for Gemini
159
- contents = [
160
- types.Content(
161
- role="user",
162
- parts=[
163
- types.Part(
164
- file_data=types.FileData(
165
- file_uri=url,
166
- mime_type="video/*",
167
- )
168
- ),
169
- types.Part.from_text(
170
- text='''Extract the Gujarati lyrics from the provided video and output them using the following HTML template. Ensure that each line of the lyrics is correctly inserted into the template and that any placeholders are replaced with the appropriate content from the file.
171
-
172
- ```
173
- <html>
174
- <head>
175
- <title>Bhaktisudha</title>
176
- <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
177
- <link href="/static/simple.css" rel="stylesheet" type="text/css" />
178
- <style>
179
- </style>
180
- </head>
181
- <body>
182
- <div class="main">
183
- <div class="gtitlev3">
184
- {Title of the file}
185
- </div>
186
- <div class="gpara">
187
- Line1 <br/>
188
- Line2 <br/>
189
- </div>
190
- <div class="chend">
191
- *****
192
- </div>
193
- </div>
194
- </body>
195
- </html>
196
- ```'''
197
- ),
198
- ],
199
- ),
200
- ]
201
-
202
- # Generate response
203
- response = ""
204
- for chunk in client.models.generate_content_stream(
205
- model=model_name,
206
- contents=contents,
207
- config=generate_content_config,
208
- ):
209
- response += chunk.text
210
-
211
- # Clean up the response
212
- lyrics = response.replace("```html", "").replace("```", "")
213
- return jsonify({'lyrics': lyrics})
214
-
215
- except Exception as e:
216
- return jsonify({'error': str(e)})
217
-
218
- if __name__ == '__main__':
219
- app.run(debug=True, host='0.0.0.0', port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import base64
4
+ from flask import Flask, request, render_template, jsonify
5
+ from google import genai
6
+ from google.genai import types
7
+ from werkzeug.utils import secure_filename
8
+ import requests
9
+ import time
10
+ import tempfile
11
+
12
+ # Initialize Flask app
13
+ app = Flask(__name__)
14
+
15
+ # Initialize Gemini client
16
+ client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY", "AIzaSyCg9NGsLygb0sVKpviMkgV4eMPLd9nXW7w"))
17
+
18
+ # Set up generation config
19
+ generate_content_config = types.GenerateContentConfig(
20
+ temperature=1.0,
21
+ top_p=0.95,
22
+ top_k=40,
23
+ max_output_tokens=8192,
24
+ )
25
+
26
+ # Initialize the model
27
+ model_name = "gemini-2.5-flash"
28
+
29
+
30
+ @app.route("/")
31
+ def index():
32
+ return render_template("index.html")
33
+
34
+
35
+ @app.route("/convert", methods=["POST"])
36
+ def convert_audio():
37
+ if "file" not in request.files:
38
+ return jsonify({"error": "No file part"}), 400
39
+
40
+ file = request.files["file"]
41
+ if file.filename == "":
42
+ return jsonify({"error": "No selected file"}), 400
43
+
44
+ # Get the mode type from form data
45
+ mode_type = request.form.get("type", "music") # Default to music mode
46
+
47
+ if file:
48
+ try:
49
+ # Determine mime type based on file extension
50
+ mime_type = None
51
+ if file.filename.lower().endswith('.mp3'):
52
+ mime_type = 'audio/mpeg'
53
+ elif file.filename.lower().endswith('.wav'):
54
+ mime_type = 'audio/wav'
55
+ elif file.filename.lower().endswith('.m4a'):
56
+ mime_type = 'audio/x-m4a'
57
+ elif file.filename.lower().endswith('.ogg'):
58
+ mime_type = 'audio/ogg'
59
+ else:
60
+ return jsonify({"error": "Unsupported file type"}), 400
61
+
62
+ # Save the file temporarily
63
+ temp_dir = tempfile.mkdtemp()
64
+ temp_path = os.path.join(temp_dir, secure_filename(file.filename))
65
+ file.save(temp_path)
66
+
67
+ # Upload file to Gemini with mime type
68
+ with open(temp_path, 'rb') as f:
69
+ uploaded_file = client.files.upload(file=f,config={'mime_type': mime_type})
70
+
71
+ # Clean up temporary file
72
+ os.remove(temp_path)
73
+ os.rmdir(temp_dir)
74
+
75
+ # Choose prompt based on mode
76
+ if mode_type == "speech":
77
+ prompt = """Extract the Gujarati speech content from the provided audio file and output it using the following HTML template. Format the content into meaningful paragraphs instead of line breaks. Ensure that each paragraph flows naturally and represents coherent thoughts or topics from the speech.
78
+
79
+ ```
80
+ <html>
81
+ <head>
82
+ <title>Pravachan</title>
83
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
84
+ <link href="/static/simple.css" rel="stylesheet" type="text/css" />
85
+ <style>
86
+ </style>
87
+ </head>
88
+ <body>
89
+ <div class="main">
90
+ <div class="gtitlev3">
91
+ {Title of the audio content}
92
+ </div>
93
+ <div class="gpara">
94
+ <p>First paragraph of the speech content...</p>
95
+ <p>Second paragraph of the speech content...</p>
96
+ <p>Continue with more paragraphs as needed...</p>
97
+ </div>
98
+ <div class="chend">
99
+ *****
100
+ </div>
101
+ </div>
102
+ </body>
103
+ </html>
104
+ ```"""
105
+ else: # Default to music mode
106
+ prompt = """Extract the Gujarati lyrics from the provided audio file and output them using the following HTML template. Ensure that each line of the lyrics is correctly inserted into the template and that any placeholders are replaced with the appropriate content from the file.
107
+
108
+ ```
109
+ <html>
110
+ <head>
111
+ <title>Bhaktisudha</title>
112
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
113
+ <link href="/static/simple.css" rel="stylesheet" type="text/css" />
114
+ <style>
115
+ </style>
116
+ </head>
117
+ <body>
118
+ <div class="main">
119
+ <div class="gtitlev3">
120
+ {Title of the file}
121
+ </div>
122
+ <div class="gpara">
123
+ Line1 <br/>
124
+ Line2 <br/>
125
+ </div>
126
+ <div class="chend">
127
+ *****
128
+ </div>
129
+ </div>
130
+ </body>
131
+ </html>
132
+ ```"""
133
+
134
+ # Generate response
135
+ response = ""
136
+ for chunk in client.models.generate_content_stream(
137
+ model=model_name,
138
+ contents=[uploaded_file, prompt],
139
+ config=generate_content_config,
140
+ ):
141
+ response += chunk.text
142
+
143
+ # Clean up the response
144
+ lyrics = response.replace("```html", "").replace("```", "")
145
+ return jsonify({"lyrics": lyrics})
146
+
147
+ except Exception as e:
148
+ return jsonify({"error": str(e)}), 500
149
+
150
+
151
+ @app.route("/convert-youtube", methods=["POST"])
152
+ def convert_youtube():
153
+ try:
154
+ data = request.get_json()
155
+ url = data.get("url")
156
+ mode_type = data.get("type", "music") # Get mode type, default to music
157
+
158
+ if not url:
159
+ return jsonify({"error": "No URL provided"}), 400
160
+
161
+ # Choose prompt based on mode
162
+ if mode_type == "speech":
163
+ prompt_text = """Extract the Gujarati speech content from the provided video and output it using the following HTML template. Format the content into meaningful paragraphs instead of line breaks. Ensure that each paragraph flows naturally and represents coherent thoughts or topics from the speech.
164
+
165
+ ```
166
+ <html>
167
+ <head>
168
+ <title>Pravachan</title>
169
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
170
+ <link href="/static/simple.css" rel="stylesheet" type="text/css" />
171
+ <style>
172
+ </style>
173
+ </head>
174
+ <body>
175
+ <div class="main">
176
+ <div class="gtitlev3">
177
+ {Title of the video content}
178
+ </div>
179
+ <div class="gpara">
180
+ <p>First paragraph of the speech content...</p>
181
+ <p>Second paragraph of the speech content...</p>
182
+ <p>Continue with more paragraphs as needed...</p>
183
+ </div>
184
+ <div class="chend">
185
+ *****
186
+ </div>
187
+ </div>
188
+ </body>
189
+ </html>
190
+ ```"""
191
+ else: # Default to music mode
192
+ prompt_text = """Extract the Gujarati lyrics from the provided video and output them using the following HTML template. Ensure that each line of the lyrics is correctly inserted into the template and that any placeholders are replaced with the appropriate content from the file.
193
+
194
+ ```
195
+ <html>
196
+ <head>
197
+ <title>Bhaktisudha</title>
198
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
199
+ <link href="/static/simple.css" rel="stylesheet" type="text/css" />
200
+ <style>
201
+ </style>
202
+ </head>
203
+ <body>
204
+ <div class="main">
205
+ <div class="gtitlev3">
206
+ {Title of the file}
207
+ </div>
208
+ <div class="gpara">
209
+ Line1 <br/>
210
+ Line2 <br/>
211
+ </div>
212
+ <div class="chend">
213
+ *****
214
+ </div>
215
+ </div>
216
+ </body>
217
+ </html>
218
+ ```"""
219
+
220
+ # Create content for Gemini
221
+ contents = [
222
+ types.Content(
223
+ role="user",
224
+ parts=[
225
+ types.Part(
226
+ file_data=types.FileData(
227
+ file_uri=url,
228
+ mime_type="video/*",
229
+ )
230
+ ),
231
+ types.Part.from_text(text=prompt_text),
232
+ ],
233
+ ),
234
+ ]
235
+
236
+ # Generate response
237
+ response = ""
238
+ for chunk in client.models.generate_content_stream(
239
+ model=model_name,
240
+ contents=contents,
241
+ config=generate_content_config,
242
+ ):
243
+ response += chunk.text
244
+
245
+ # Clean up the response
246
+ lyrics = response.replace("```html", "").replace("```", "")
247
+ return jsonify({"lyrics": lyrics})
248
+
249
+ except Exception as e:
250
+ return jsonify({"error": str(e)})
251
+
252
+
253
+ if __name__ == "__main__":
254
+ app.run(debug=True, host="0.0.0.0", port=7860)
static/simple.css CHANGED
@@ -1,87 +1,85 @@
1
- @import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
2
-
3
- body {
4
- font-family: 'Inter', sans-serif;
5
- }
6
-
7
- .dropzone {
8
- border: 2px dashed #e5e7eb;
9
- border-radius: 0.5rem;
10
- transition: all 0.3s ease;
11
- }
12
-
13
- .dropzone:hover,
14
- .dropzone.dragover {
15
- border-color: #3b82f6;
16
- background-color: rgba(59, 130, 246, 0.05);
17
- }
18
-
19
- .pulse {
20
- animation: pulse 2s infinite;
21
- }
22
-
23
- @keyframes pulse {
24
- 0% {
25
- box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
26
- }
27
-
28
- 70% {
29
- box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
30
- }
31
-
32
- 100% {
33
- box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
34
- }
35
- }
36
-
37
- .glass-morphism {
38
- background: rgba(255, 255, 255, 0.7);
39
- backdrop-filter: blur(10px);
40
- -webkit-backdrop-filter: blur(10px);
41
- border: 1px solid rgba(255, 255, 255, 0.18);
42
- }
43
-
44
- .gtitlev3 {
45
- padding-top: 0.25rem;
46
- padding-bottom: 0.25rem;
47
- padding-left: 0.5rem;
48
- padding-right: 0.5rem;
49
- margin-bottom: 0.75rem;
50
- border-radius: 0.375rem;
51
- font-size: 1.5rem;
52
- line-height: 2rem;
53
- font-weight: 700;
54
- text-align: center;
55
- box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
56
- background-color: rgb(255 248 220);
57
- }
58
-
59
- .chend {
60
- padding-left: 1.25rem;
61
- padding-right: 1.25rem;
62
- margin-top: 0.75rem;
63
- border-radius: 0.375rem;
64
- width: fit-content;
65
- height: 1.5rem;
66
- font-size: 1.875rem;
67
- line-height: 2.25rem;
68
- font-weight: 700;
69
- text-align: center;
70
- box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
71
- color: rgb(49 71 91);
72
- background-color: rgb(255 248 220);
73
- }
74
-
75
- .gpara {
76
- font-weight: 600;
77
- color: rgb(49 71 91)
78
- /* #31475b */
79
- ;
80
- }
81
-
82
- .gparabhajan3 {
83
- font-weight: 600;
84
- color: rgb(49 71 91)
85
- /* #31475b */
86
- ;
87
  }
 
1
+ body {
2
+ font-family: 'Inter', sans-serif;
3
+ }
4
+
5
+ .dropzone {
6
+ border: 2px dashed #e5e7eb;
7
+ border-radius: 0.5rem;
8
+ transition: all 0.3s ease;
9
+ }
10
+
11
+ .dropzone:hover,
12
+ .dropzone.dragover {
13
+ border-color: #3b82f6;
14
+ background-color: rgba(59, 130, 246, 0.05);
15
+ }
16
+
17
+ .pulse {
18
+ animation: pulse 2s infinite;
19
+ }
20
+
21
+ @keyframes pulse {
22
+ 0% {
23
+ box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
24
+ }
25
+
26
+ 70% {
27
+ box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
28
+ }
29
+
30
+ 100% {
31
+ box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
32
+ }
33
+ }
34
+
35
+ .glass-morphism {
36
+ background: rgba(255, 255, 255, 0.7);
37
+ backdrop-filter: blur(10px);
38
+ -webkit-backdrop-filter: blur(10px);
39
+ border: 1px solid rgba(255, 255, 255, 0.18);
40
+ }
41
+
42
+ .gtitlev3 {
43
+ padding-top: 0.25rem;
44
+ padding-bottom: 0.25rem;
45
+ padding-left: 0.5rem;
46
+ padding-right: 0.5rem;
47
+ margin-bottom: 0.75rem;
48
+ border-radius: 0.375rem;
49
+ font-size: 1.5rem;
50
+ line-height: 2rem;
51
+ font-weight: 700;
52
+ text-align: center;
53
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
54
+ background-color: rgb(255 248 220);
55
+ }
56
+
57
+ .chend {
58
+ padding-left: 1.25rem;
59
+ padding-right: 1.25rem;
60
+ margin-top: 0.75rem;
61
+ border-radius: 0.375rem;
62
+ width: fit-content;
63
+ height: 1.5rem;
64
+ font-size: 1.875rem;
65
+ line-height: 2.25rem;
66
+ font-weight: 700;
67
+ text-align: center;
68
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
69
+ color: rgb(49 71 91);
70
+ background-color: rgb(255 248 220);
71
+ }
72
+
73
+ .gpara {
74
+ font-weight: 600;
75
+ color: rgb(49 71 91)
76
+ /* #31475b */
77
+ ;
78
+ }
79
+
80
+ .gparabhajan3 {
81
+ font-weight: 600;
82
+ color: rgb(49 71 91)
83
+ /* #31475b */
84
+ ;
 
 
85
  }
templates/index copy.html ADDED
@@ -0,0 +1,420 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Audio to Lyrics Converter</title>
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
10
+ <script src="https://unpkg.com/feather-icons"></script>
11
+ <style>
12
+ * {
13
+ font-family: 'Inter', sans-serif;
14
+ }
15
+
16
+ .dropzone {
17
+ border: 2px dashed #e5e7eb;
18
+ border-radius: 0.5rem;
19
+ transition: all 0.3s ease;
20
+ }
21
+
22
+ .dropzone:hover,
23
+ .dropzone.dragover {
24
+ border-color: #3b82f6;
25
+ background-color: rgba(59, 130, 246, 0.05);
26
+ }
27
+
28
+ .pulse {
29
+ animation: pulse 2s infinite;
30
+ }
31
+
32
+ @keyframes pulse {
33
+ 0% {
34
+ box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
35
+ }
36
+
37
+ 70% {
38
+ box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
39
+ }
40
+
41
+ 100% {
42
+ box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
43
+ }
44
+ }
45
+
46
+ .glass-morphism {
47
+ background: rgba(255, 255, 255, 0.7);
48
+ backdrop-filter: blur(10px);
49
+ -webkit-backdrop-filter: blur(10px);
50
+ border: 1px solid rgba(255, 255, 255, 0.18);
51
+ }
52
+
53
+ .gtitlev3 {
54
+ padding-top: 0.25rem;
55
+ padding-bottom: 0.25rem;
56
+ padding-left: 0.5rem;
57
+ padding-right: 0.5rem;
58
+ margin-bottom: 0.75rem;
59
+ border-radius: 0.375rem;
60
+ font-size: 1.5rem;
61
+ line-height: 2rem;
62
+ font-weight: 700;
63
+ text-align: center;
64
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 #0000000f;
65
+ background-color: #fff8dc;
66
+ }
67
+
68
+ .chend {
69
+ padding-left: 1.25rem;
70
+ padding-right: 1.25rem;
71
+ margin-top: 0.75rem;
72
+ border-radius: 0.375rem;
73
+ width: fit-content;
74
+ height: 1.5rem;
75
+ font-size: 1.875rem;
76
+ line-height: 2.25rem;
77
+ font-weight: 700;
78
+ text-align: center;
79
+ box-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px 0 #0000000f;
80
+ color: rgb(49 71 91);
81
+ background-color: #fff8dc;
82
+ }
83
+
84
+ .gpara {
85
+ font-weight: 600;
86
+ color: rgb(49 71 91)
87
+ /* #31475b */
88
+ ;
89
+ }
90
+
91
+ .gparabhajan3 {
92
+ font-weight: 600;
93
+ color: rgb(49 71 91)
94
+ /* #31475b */
95
+ ;
96
+ }
97
+ </style>
98
+ </head>
99
+
100
+ <body class="bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen">
101
+ <div class="container mx-auto px-4 py-12 max-w-4xl md:max-w-[90%]">
102
+ <header class="text-center mb-12">
103
+ <h1 class="text-4xl font-bold text-gray-800 mb-2">
104
+ <span class="bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-indigo-600">Audio to
105
+ Lyrics</span>
106
+ Converter
107
+ </h1>
108
+ <p class="text-gray-600 max-w-xl mx-auto">Transform your audio files into beautifully formatted lyrics with
109
+ AI-powered transcription</p>
110
+ </header>
111
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-8">
112
+ <div class="glass-morphism col-span-1 md:col-span-1 rounded-xl shadow-xl p-8 mb-8 h-fit">
113
+ <!-- Add tab buttons -->
114
+ <div class="flex mb-6 bg-gray-100 rounded-lg p-1">
115
+ <button id="fileTabBtn" class="flex-1 py-2 px-4 rounded-md bg-white shadow-sm text-blue-600 font-medium">
116
+ <i data-feather="file" class="inline mr-2"></i>File
117
+ </button>
118
+ <button id="youtubeTabBtn" class="flex-1 py-2 px-4 rounded-md text-gray-600 font-medium">
119
+ <i data-feather="youtube" class="inline mr-2"></i>YouTube
120
+ </button>
121
+ </div>
122
+
123
+ <!-- File upload section -->
124
+ <div id="fileUploadSection">
125
+ <div class="dropzone p-8 mb-6 flex flex-col items-center justify-center" id="dropzone">
126
+ <i data-feather="music" class="text-blue-500 mb-4" style="width: 48px; height: 48px;"></i>
127
+ <label class="block text-gray-700 font-medium mb-2">Upload Audio File</label>
128
+ <p class="text-sm text-gray-500 mb-4">Drag & drop your file here or click to browse</p>
129
+ <input type="file" id="audioFile" accept="audio/*" class="hidden">
130
+ <button id="browseBtn"
131
+ class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-6 rounded-full transition duration-300 flex items-center">
132
+ <i data-feather="upload-cloud" class="mr-2"></i>
133
+ Browse Files
134
+ </button>
135
+ </div>
136
+ <div class="mb-6">
137
+ <div class="bg-gray-50 rounded-lg p-4 flex items-center" id="fileInfoContainer"
138
+ style="display: none;">
139
+ <div
140
+ class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mr-3 flex-shrink-0">
141
+ <i data-feather="file" class="text-blue-600"></i>
142
+ </div>
143
+ <div class="flex-grow">
144
+ <p class="font-medium text-gray-800" id="fileName">No file selected</p>
145
+ <p class="text-sm text-gray-500" id="fileSize"></p>
146
+ </div>
147
+ <button id="removeFileBtn"
148
+ class="text-gray-400 hover:text-red-500 transition-colors duration-300">
149
+ <i data-feather="x-circle"></i>
150
+ </button>
151
+ </div>
152
+
153
+ <audio id="audioPlayer" controls class="w-full mt-4 rounded-lg" hidden>
154
+ Your browser does not support the audio element.
155
+ </audio>
156
+ </div>
157
+ </div>
158
+
159
+ <!-- YouTube link section -->
160
+ <div id="youtubeLinkSection" class="hidden">
161
+ <div class="mb-6">
162
+ <label class="block text-gray-700 font-medium mb-2">YouTube URL</label>
163
+ <input type="text" id="youtubeUrl"
164
+ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
165
+ placeholder="https://www.youtube.com/watch?v=...">
166
+ <p id="youtubeError" class="text-red-500 text-sm mt-1 hidden">Please enter a valid YouTube URL</p>
167
+ </div>
168
+ </div>
169
+
170
+ <button id="convertBtn"
171
+ class="w-full bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white font-semibold py-3 px-4 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed transition duration-300 flex items-center justify-center pulse"
172
+ disabled>
173
+ <i data-feather="zap" class="mr-2"></i>
174
+ Convert to Lyrics
175
+ </button>
176
+
177
+ <div id="loadingIndicator" class="hidden mt-6 text-center">
178
+ <div class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
179
+ <p class="mt-2 text-blue-600 font-medium">Processing your audio file...</p>
180
+ <p class="text-sm text-gray-500">This might take a moment depending on file size</p>
181
+ </div>
182
+ </div>
183
+
184
+ <div class="glass-morphism col-span-1 md:col-span-3 rounded-xl shadow-xl p-8">
185
+ <div class="flex items-center justify-between mb-4">
186
+ <h3 class="font-bold text-gray-800 flex items-center">
187
+ <i data-feather="file-text" class="mr-2 text-blue-600"></i>
188
+ Lyrics Output
189
+ </h3>
190
+ <button id="downloadBtn"
191
+ class="hidden bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded-full transition duration-300 flex items-center text-sm">
192
+ <i data-feather="download-cloud" class="mr-1"></i>
193
+ Download
194
+ </button>
195
+ </div>
196
+
197
+ <iframe id="lyricsOutputIframe"
198
+ class="p-6 bg-white rounded-lg min-h-[300px] w-full whitespace-pre-line border border-gray-100 text-gray-700 overflow-auto max-h-[500px] md:max-h-[1000px] h-[500px] md:h-[500px]"
199
+ srcdoc="No lyrics available yet. Upload an audio file and click convert."
200
+ ></iframe>
201
+ </div>
202
+ </div>
203
+
204
+ <footer class="mt-12 text-center text-gray-500 text-sm">
205
+ <p>Created by Jagrat Patel</p>
206
+ <p class="mt-1">© 2025 Audio to Lyrics Converter</p>
207
+ </footer>
208
+ </div>
209
+
210
+ <script>
211
+ // Initialize Feather icons
212
+ feather.replace();
213
+
214
+ const audioFile = document.getElementById('audioFile');
215
+ const browseBtn = document.getElementById('browseBtn');
216
+ const audioPlayer = document.getElementById('audioPlayer');
217
+ const convertBtn = document.getElementById('convertBtn');
218
+ const lyricsOutput = document.getElementById('lyricsOutputIframe');
219
+ const loadingIndicator = document.getElementById('loadingIndicator');
220
+ const downloadBtn = document.getElementById('downloadBtn');
221
+ const dropzone = document.getElementById('dropzone');
222
+ const fileInfoContainer = document.getElementById('fileInfoContainer');
223
+ const fileName = document.getElementById('fileName');
224
+ const fileSize = document.getElementById('fileSize');
225
+ const removeFileBtn = document.getElementById('removeFileBtn');
226
+
227
+ // Handle browse button click
228
+ browseBtn.addEventListener('click', () => {
229
+ audioFile.click();
230
+ });
231
+
232
+ // Format file size
233
+ function formatFileSize(bytes) {
234
+ if (bytes === 0) return '0 Bytes';
235
+ const k = 1024;
236
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
237
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
238
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
239
+ }
240
+
241
+ // Display file info
242
+ function displayFileInfo(file) {
243
+ fileName.textContent = file.name;
244
+ fileSize.textContent = formatFileSize(file.size);
245
+ fileInfoContainer.style.display = 'flex';
246
+
247
+ // Enable convert button and show audio player
248
+ convertBtn.disabled = false;
249
+ convertBtn.classList.add('pulse');
250
+
251
+ const audioUrl = URL.createObjectURL(file);
252
+ audioPlayer.src = audioUrl;
253
+ audioPlayer.hidden = false;
254
+
255
+ // Reset output area
256
+ lyricsOutput.srcdoc = "No lyrics available yet. Upload an audio file and click convert.";
257
+ downloadBtn.classList.add('hidden');
258
+ }
259
+
260
+ // Handle file selection
261
+ audioFile.addEventListener('change', (e) => {
262
+ const file = e.target.files[0];
263
+ if (file) {
264
+ displayFileInfo(file);
265
+ }
266
+ });
267
+
268
+ // Handle remove file button
269
+ removeFileBtn.addEventListener('click', () => {
270
+ audioFile.value = '';
271
+ fileInfoContainer.style.display = 'none';
272
+ audioPlayer.hidden = true;
273
+ audioPlayer.src = '';
274
+ convertBtn.disabled = true;
275
+ convertBtn.classList.remove('pulse');
276
+ lyricsOutput.srcdoc = "No lyrics available yet. Upload an audio file and click convert.";
277
+ downloadBtn.classList.add('hidden');
278
+ });
279
+
280
+ // Handle drag and drop
281
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
282
+ dropzone.addEventListener(eventName, (e) => {
283
+ e.preventDefault();
284
+ e.stopPropagation();
285
+ }, false);
286
+ });
287
+
288
+ ['dragenter', 'dragover'].forEach(eventName => {
289
+ dropzone.addEventListener(eventName, () => {
290
+ dropzone.classList.add('dragover');
291
+ }, false);
292
+ });
293
+
294
+ ['dragleave', 'drop'].forEach(eventName => {
295
+ dropzone.addEventListener(eventName, () => {
296
+ dropzone.classList.remove('dragover');
297
+ }, false);
298
+ });
299
+
300
+ dropzone.addEventListener('drop', (e) => {
301
+ const file = e.dataTransfer.files[0];
302
+ if (file && file.type.startsWith('audio/')) {
303
+ audioFile.files = e.dataTransfer.files;
304
+ displayFileInfo(file);
305
+ }
306
+ }, false);
307
+
308
+ // Handle convert button click
309
+ convertBtn.addEventListener('click', async () => {
310
+ const isYoutubeMode = !youtubeLinkSection.classList.contains('hidden');
311
+
312
+ if (isYoutubeMode) {
313
+ const youtubeLink = youtubeUrl.value;
314
+ if (!isValidYouTubeUrl(youtubeLink)) {
315
+ alert('Please enter a valid YouTube URL');
316
+ return;
317
+ }
318
+ } else {
319
+ const file = audioFile.files[0];
320
+ if (!file) {
321
+ alert('Please select an audio file first');
322
+ return;
323
+ }
324
+ }
325
+
326
+ // Show loading indicator
327
+ loadingIndicator.classList.remove('hidden');
328
+ convertBtn.disabled = true;
329
+ convertBtn.classList.remove('pulse');
330
+
331
+ try {
332
+ let response;
333
+ if (isYoutubeMode) {
334
+ response = await fetch('/convert-youtube', {
335
+ method: 'POST',
336
+ headers: {
337
+ 'Content-Type': 'application/json',
338
+ },
339
+ body: JSON.stringify({ url: youtubeUrl.value })
340
+ });
341
+ } else {
342
+ const formData = new FormData();
343
+ formData.append('file', audioFile.files[0]);
344
+ response = await fetch('/convert', {
345
+ method: 'POST',
346
+ body: formData
347
+ });
348
+ }
349
+
350
+ const result = await response.json();
351
+
352
+ if (response.ok) {
353
+ lyricsOutput.srcdoc = result.lyrics;
354
+ downloadBtn.classList.remove('hidden');
355
+ } else {
356
+ lyricsOutput.srcdoc = `Error: ${result.error}`;
357
+ }
358
+ } catch (error) {
359
+ lyricsOutput.srcdoc = `Error: ${error.message}`;
360
+ } finally {
361
+ loadingIndicator.classList.add('hidden');
362
+ convertBtn.disabled = false;
363
+ }
364
+ });
365
+
366
+ // Handle download button click
367
+ downloadBtn.addEventListener('click', () => {
368
+ const lyrics = lyricsOutput.srcdoc;
369
+ const blob = new Blob([lyrics], { type: 'text/html' });
370
+ const url = URL.createObjectURL(blob);
371
+
372
+ const a = document.createElement('a');
373
+ a.href = url;
374
+ a.download = 'lyrics.html';
375
+ document.body.appendChild(a);
376
+ a.click();
377
+ document.body.removeChild(a);
378
+ URL.revokeObjectURL(url);
379
+ });
380
+
381
+ // Add new JavaScript for YouTube functionality
382
+ const fileTabBtn = document.getElementById('fileTabBtn');
383
+ const youtubeTabBtn = document.getElementById('youtubeTabBtn');
384
+ const fileUploadSection = document.getElementById('fileUploadSection');
385
+ const youtubeLinkSection = document.getElementById('youtubeLinkSection');
386
+ const youtubeUrl = document.getElementById('youtubeUrl');
387
+ const youtubeError = document.getElementById('youtubeError');
388
+
389
+ fileTabBtn.addEventListener('click', () => {
390
+ fileTabBtn.classList.add('bg-white', 'shadow-sm', 'text-blue-600');
391
+ youtubeTabBtn.classList.remove('bg-white', 'shadow-sm', 'text-blue-600');
392
+ fileUploadSection.classList.remove('hidden');
393
+ youtubeLinkSection.classList.add('hidden');
394
+ convertBtn.disabled = !audioFile.files.length;
395
+ });
396
+
397
+ youtubeTabBtn.addEventListener('click', () => {
398
+ youtubeTabBtn.classList.add('bg-white', 'shadow-sm', 'text-blue-600');
399
+ fileTabBtn.classList.remove('bg-white', 'shadow-sm', 'text-blue-600');
400
+ youtubeLinkSection.classList.remove('hidden');
401
+ fileUploadSection.classList.add('hidden');
402
+ convertBtn.disabled = !isValidYouTubeUrl(youtubeUrl.value);
403
+ });
404
+
405
+ function isValidYouTubeUrl(url) {
406
+ const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+/;
407
+ return youtubeRegex.test(url);
408
+ }
409
+
410
+ youtubeUrl.addEventListener('input', () => {
411
+ const isValid = isValidYouTubeUrl(youtubeUrl.value);
412
+ youtubeError.classList.toggle('hidden', isValid);
413
+ convertBtn.disabled = !isValid;
414
+ if (isValid) convertBtn.classList.add('pulse');
415
+ else convertBtn.classList.remove('pulse');
416
+ });
417
+ </script>
418
+ </body>
419
+
420
+ </html>
templates/index.html CHANGED
@@ -1,420 +1,488 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Audio to Lyrics Converter</title>
8
- <script src="https://cdn.tailwindcss.com"></script>
9
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
10
- <script src="https://unpkg.com/feather-icons"></script>
11
- <style>
12
- * {
13
- font-family: 'Inter', sans-serif;
14
- }
15
-
16
- .dropzone {
17
- border: 2px dashed #e5e7eb;
18
- border-radius: 0.5rem;
19
- transition: all 0.3s ease;
20
- }
21
-
22
- .dropzone:hover,
23
- .dropzone.dragover {
24
- border-color: #3b82f6;
25
- background-color: rgba(59, 130, 246, 0.05);
26
- }
27
-
28
- .pulse {
29
- animation: pulse 2s infinite;
30
- }
31
-
32
- @keyframes pulse {
33
- 0% {
34
- box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
35
- }
36
-
37
- 70% {
38
- box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
39
- }
40
-
41
- 100% {
42
- box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
43
- }
44
- }
45
-
46
- .glass-morphism {
47
- background: rgba(255, 255, 255, 0.7);
48
- backdrop-filter: blur(10px);
49
- -webkit-backdrop-filter: blur(10px);
50
- border: 1px solid rgba(255, 255, 255, 0.18);
51
- }
52
-
53
- .gtitlev3 {
54
- padding-top: 0.25rem;
55
- padding-bottom: 0.25rem;
56
- padding-left: 0.5rem;
57
- padding-right: 0.5rem;
58
- margin-bottom: 0.75rem;
59
- border-radius: 0.375rem;
60
- font-size: 1.5rem;
61
- line-height: 2rem;
62
- font-weight: 700;
63
- text-align: center;
64
- box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 #0000000f;
65
- background-color: #fff8dc;
66
- }
67
-
68
- .chend {
69
- padding-left: 1.25rem;
70
- padding-right: 1.25rem;
71
- margin-top: 0.75rem;
72
- border-radius: 0.375rem;
73
- width: fit-content;
74
- height: 1.5rem;
75
- font-size: 1.875rem;
76
- line-height: 2.25rem;
77
- font-weight: 700;
78
- text-align: center;
79
- box-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px 0 #0000000f;
80
- color: rgb(49 71 91);
81
- background-color: #fff8dc;
82
- }
83
-
84
- .gpara {
85
- font-weight: 600;
86
- color: rgb(49 71 91)
87
- /* #31475b */
88
- ;
89
- }
90
-
91
- .gparabhajan3 {
92
- font-weight: 600;
93
- color: rgb(49 71 91)
94
- /* #31475b */
95
- ;
96
- }
97
- </style>
98
- </head>
99
-
100
- <body class="bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen">
101
- <div class="container mx-auto px-4 py-12 max-w-4xl md:max-w-[90%]">
102
- <header class="text-center mb-12">
103
- <h1 class="text-4xl font-bold text-gray-800 mb-2">
104
- <span class="bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-indigo-600">Audio to
105
- Lyrics</span>
106
- Converter
107
- </h1>
108
- <p class="text-gray-600 max-w-xl mx-auto">Transform your audio files into beautifully formatted lyrics with
109
- AI-powered transcription</p>
110
- </header>
111
- <div class="grid grid-cols-1 md:grid-cols-4 gap-8">
112
- <div class="glass-morphism col-span-1 md:col-span-1 rounded-xl shadow-xl p-8 mb-8 h-fit">
113
- <!-- Add tab buttons -->
114
- <div class="flex mb-6 bg-gray-100 rounded-lg p-1">
115
- <button id="fileTabBtn" class="flex-1 py-2 px-4 rounded-md bg-white shadow-sm text-blue-600 font-medium">
116
- <i data-feather="file" class="inline mr-2"></i>File
117
- </button>
118
- <button id="youtubeTabBtn" class="flex-1 py-2 px-4 rounded-md text-gray-600 font-medium">
119
- <i data-feather="youtube" class="inline mr-2"></i>YouTube
120
- </button>
121
- </div>
122
-
123
- <!-- File upload section -->
124
- <div id="fileUploadSection">
125
- <div class="dropzone p-8 mb-6 flex flex-col items-center justify-center" id="dropzone">
126
- <i data-feather="music" class="text-blue-500 mb-4" style="width: 48px; height: 48px;"></i>
127
- <label class="block text-gray-700 font-medium mb-2">Upload Audio File</label>
128
- <p class="text-sm text-gray-500 mb-4">Drag & drop your file here or click to browse</p>
129
- <input type="file" id="audioFile" accept="audio/*" class="hidden">
130
- <button id="browseBtn"
131
- class="bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-6 rounded-full transition duration-300 flex items-center">
132
- <i data-feather="upload-cloud" class="mr-2"></i>
133
- Browse Files
134
- </button>
135
- </div>
136
- <div class="mb-6">
137
- <div class="bg-gray-50 rounded-lg p-4 flex items-center" id="fileInfoContainer"
138
- style="display: none;">
139
- <div
140
- class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mr-3 flex-shrink-0">
141
- <i data-feather="file" class="text-blue-600"></i>
142
- </div>
143
- <div class="flex-grow">
144
- <p class="font-medium text-gray-800" id="fileName">No file selected</p>
145
- <p class="text-sm text-gray-500" id="fileSize"></p>
146
- </div>
147
- <button id="removeFileBtn"
148
- class="text-gray-400 hover:text-red-500 transition-colors duration-300">
149
- <i data-feather="x-circle"></i>
150
- </button>
151
- </div>
152
-
153
- <audio id="audioPlayer" controls class="w-full mt-4 rounded-lg" hidden>
154
- Your browser does not support the audio element.
155
- </audio>
156
- </div>
157
- </div>
158
-
159
- <!-- YouTube link section -->
160
- <div id="youtubeLinkSection" class="hidden">
161
- <div class="mb-6">
162
- <label class="block text-gray-700 font-medium mb-2">YouTube URL</label>
163
- <input type="text" id="youtubeUrl"
164
- class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
165
- placeholder="https://www.youtube.com/watch?v=...">
166
- <p id="youtubeError" class="text-red-500 text-sm mt-1 hidden">Please enter a valid YouTube URL</p>
167
- </div>
168
- </div>
169
-
170
- <button id="convertBtn"
171
- class="w-full bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 text-white font-semibold py-3 px-4 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed transition duration-300 flex items-center justify-center pulse"
172
- disabled>
173
- <i data-feather="zap" class="mr-2"></i>
174
- Convert to Lyrics
175
- </button>
176
-
177
- <div id="loadingIndicator" class="hidden mt-6 text-center">
178
- <div class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
179
- <p class="mt-2 text-blue-600 font-medium">Processing your audio file...</p>
180
- <p class="text-sm text-gray-500">This might take a moment depending on file size</p>
181
- </div>
182
- </div>
183
-
184
- <div class="glass-morphism col-span-1 md:col-span-3 rounded-xl shadow-xl p-8">
185
- <div class="flex items-center justify-between mb-4">
186
- <h3 class="font-bold text-gray-800 flex items-center">
187
- <i data-feather="file-text" class="mr-2 text-blue-600"></i>
188
- Lyrics Output
189
- </h3>
190
- <button id="downloadBtn"
191
- class="hidden bg-green-500 hover:bg-green-600 text-white font-medium py-2 px-4 rounded-full transition duration-300 flex items-center text-sm">
192
- <i data-feather="download-cloud" class="mr-1"></i>
193
- Download
194
- </button>
195
- </div>
196
-
197
- <iframe id="lyricsOutputIframe"
198
- class="p-6 bg-white rounded-lg min-h-[300px] w-full whitespace-pre-line border border-gray-100 text-gray-700 overflow-auto max-h-[500px] md:max-h-[1000px] h-[500px] md:h-[500px]"
199
- srcdoc="No lyrics available yet. Upload an audio file and click convert."
200
- ></iframe>
201
- </div>
202
- </div>
203
-
204
- <footer class="mt-12 text-center text-gray-500 text-sm">
205
- <p>Powered by AI transcription technology</p>
206
- <p class="mt-1">© 2025 Audio to Lyrics Converter</p>
207
- </footer>
208
- </div>
209
-
210
- <script>
211
- // Initialize Feather icons
212
- feather.replace();
213
-
214
- const audioFile = document.getElementById('audioFile');
215
- const browseBtn = document.getElementById('browseBtn');
216
- const audioPlayer = document.getElementById('audioPlayer');
217
- const convertBtn = document.getElementById('convertBtn');
218
- const lyricsOutput = document.getElementById('lyricsOutputIframe');
219
- const loadingIndicator = document.getElementById('loadingIndicator');
220
- const downloadBtn = document.getElementById('downloadBtn');
221
- const dropzone = document.getElementById('dropzone');
222
- const fileInfoContainer = document.getElementById('fileInfoContainer');
223
- const fileName = document.getElementById('fileName');
224
- const fileSize = document.getElementById('fileSize');
225
- const removeFileBtn = document.getElementById('removeFileBtn');
226
-
227
- // Handle browse button click
228
- browseBtn.addEventListener('click', () => {
229
- audioFile.click();
230
- });
231
-
232
- // Format file size
233
- function formatFileSize(bytes) {
234
- if (bytes === 0) return '0 Bytes';
235
- const k = 1024;
236
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
237
- const i = Math.floor(Math.log(bytes) / Math.log(k));
238
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
239
- }
240
-
241
- // Display file info
242
- function displayFileInfo(file) {
243
- fileName.textContent = file.name;
244
- fileSize.textContent = formatFileSize(file.size);
245
- fileInfoContainer.style.display = 'flex';
246
-
247
- // Enable convert button and show audio player
248
- convertBtn.disabled = false;
249
- convertBtn.classList.add('pulse');
250
-
251
- const audioUrl = URL.createObjectURL(file);
252
- audioPlayer.src = audioUrl;
253
- audioPlayer.hidden = false;
254
-
255
- // Reset output area
256
- lyricsOutput.srcdoc = "No lyrics available yet. Upload an audio file and click convert.";
257
- downloadBtn.classList.add('hidden');
258
- }
259
-
260
- // Handle file selection
261
- audioFile.addEventListener('change', (e) => {
262
- const file = e.target.files[0];
263
- if (file) {
264
- displayFileInfo(file);
265
- }
266
- });
267
-
268
- // Handle remove file button
269
- removeFileBtn.addEventListener('click', () => {
270
- audioFile.value = '';
271
- fileInfoContainer.style.display = 'none';
272
- audioPlayer.hidden = true;
273
- audioPlayer.src = '';
274
- convertBtn.disabled = true;
275
- convertBtn.classList.remove('pulse');
276
- lyricsOutput.srcdoc = "No lyrics available yet. Upload an audio file and click convert.";
277
- downloadBtn.classList.add('hidden');
278
- });
279
-
280
- // Handle drag and drop
281
- ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
282
- dropzone.addEventListener(eventName, (e) => {
283
- e.preventDefault();
284
- e.stopPropagation();
285
- }, false);
286
- });
287
-
288
- ['dragenter', 'dragover'].forEach(eventName => {
289
- dropzone.addEventListener(eventName, () => {
290
- dropzone.classList.add('dragover');
291
- }, false);
292
- });
293
-
294
- ['dragleave', 'drop'].forEach(eventName => {
295
- dropzone.addEventListener(eventName, () => {
296
- dropzone.classList.remove('dragover');
297
- }, false);
298
- });
299
-
300
- dropzone.addEventListener('drop', (e) => {
301
- const file = e.dataTransfer.files[0];
302
- if (file && file.type.startsWith('audio/')) {
303
- audioFile.files = e.dataTransfer.files;
304
- displayFileInfo(file);
305
- }
306
- }, false);
307
-
308
- // Handle convert button click
309
- convertBtn.addEventListener('click', async () => {
310
- const isYoutubeMode = !youtubeLinkSection.classList.contains('hidden');
311
-
312
- if (isYoutubeMode) {
313
- const youtubeLink = youtubeUrl.value;
314
- if (!isValidYouTubeUrl(youtubeLink)) {
315
- alert('Please enter a valid YouTube URL');
316
- return;
317
- }
318
- } else {
319
- const file = audioFile.files[0];
320
- if (!file) {
321
- alert('Please select an audio file first');
322
- return;
323
- }
324
- }
325
-
326
- // Show loading indicator
327
- loadingIndicator.classList.remove('hidden');
328
- convertBtn.disabled = true;
329
- convertBtn.classList.remove('pulse');
330
-
331
- try {
332
- let response;
333
- if (isYoutubeMode) {
334
- response = await fetch('/convert-youtube', {
335
- method: 'POST',
336
- headers: {
337
- 'Content-Type': 'application/json',
338
- },
339
- body: JSON.stringify({ url: youtubeUrl.value })
340
- });
341
- } else {
342
- const formData = new FormData();
343
- formData.append('file', audioFile.files[0]);
344
- response = await fetch('/convert', {
345
- method: 'POST',
346
- body: formData
347
- });
348
- }
349
-
350
- const result = await response.json();
351
-
352
- if (response.ok) {
353
- lyricsOutput.srcdoc = result.lyrics;
354
- downloadBtn.classList.remove('hidden');
355
- } else {
356
- lyricsOutput.srcdoc = `Error: ${result.error}`;
357
- }
358
- } catch (error) {
359
- lyricsOutput.srcdoc = `Error: ${error.message}`;
360
- } finally {
361
- loadingIndicator.classList.add('hidden');
362
- convertBtn.disabled = false;
363
- }
364
- });
365
-
366
- // Handle download button click
367
- downloadBtn.addEventListener('click', () => {
368
- const lyrics = lyricsOutput.srcdoc;
369
- const blob = new Blob([lyrics], { type: 'text/html' });
370
- const url = URL.createObjectURL(blob);
371
-
372
- const a = document.createElement('a');
373
- a.href = url;
374
- a.download = 'lyrics.html';
375
- document.body.appendChild(a);
376
- a.click();
377
- document.body.removeChild(a);
378
- URL.revokeObjectURL(url);
379
- });
380
-
381
- // Add new JavaScript for YouTube functionality
382
- const fileTabBtn = document.getElementById('fileTabBtn');
383
- const youtubeTabBtn = document.getElementById('youtubeTabBtn');
384
- const fileUploadSection = document.getElementById('fileUploadSection');
385
- const youtubeLinkSection = document.getElementById('youtubeLinkSection');
386
- const youtubeUrl = document.getElementById('youtubeUrl');
387
- const youtubeError = document.getElementById('youtubeError');
388
-
389
- fileTabBtn.addEventListener('click', () => {
390
- fileTabBtn.classList.add('bg-white', 'shadow-sm', 'text-blue-600');
391
- youtubeTabBtn.classList.remove('bg-white', 'shadow-sm', 'text-blue-600');
392
- fileUploadSection.classList.remove('hidden');
393
- youtubeLinkSection.classList.add('hidden');
394
- convertBtn.disabled = !audioFile.files.length;
395
- });
396
-
397
- youtubeTabBtn.addEventListener('click', () => {
398
- youtubeTabBtn.classList.add('bg-white', 'shadow-sm', 'text-blue-600');
399
- fileTabBtn.classList.remove('bg-white', 'shadow-sm', 'text-blue-600');
400
- youtubeLinkSection.classList.remove('hidden');
401
- fileUploadSection.classList.add('hidden');
402
- convertBtn.disabled = !isValidYouTubeUrl(youtubeUrl.value);
403
- });
404
-
405
- function isValidYouTubeUrl(url) {
406
- const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+/;
407
- return youtubeRegex.test(url);
408
- }
409
-
410
- youtubeUrl.addEventListener('input', () => {
411
- const isValid = isValidYouTubeUrl(youtubeUrl.value);
412
- youtubeError.classList.toggle('hidden', isValid);
413
- convertBtn.disabled = !isValid;
414
- if (isValid) convertBtn.classList.add('pulse');
415
- else convertBtn.classList.remove('pulse');
416
- });
417
- </script>
418
- </body>
419
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Audio to Lyrics Converter</title>
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
10
+ <script src="https://unpkg.com/feather-icons"></script>
11
+ <style>
12
+ * {
13
+ font-family: 'Inter', sans-serif;
14
+ }
15
+
16
+ .glass-morphism {
17
+ background: rgba(255, 255, 255, 0.7);
18
+ backdrop-filter: blur(10px);
19
+ -webkit-backdrop-filter: blur(10px);
20
+ border: 1px solid rgba(255, 255, 255, 0.18);
21
+ }
22
+
23
+ .gtitlev3 {
24
+ padding-top: 0.25rem;
25
+ padding-bottom: 0.25rem;
26
+ padding-left: 0.5rem;
27
+ padding-right: 0.5rem;
28
+ margin-bottom: 0.75rem;
29
+ border-radius: 0.375rem;
30
+ font-size: 1.5rem;
31
+ line-height: 2rem;
32
+ font-weight: 700;
33
+ text-align: center;
34
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 #0000000f;
35
+ background-color: #fff8dc;
36
+ color: rgb(49 71 91)
37
+ }
38
+
39
+ .chend {
40
+ padding-left: 1.25rem;
41
+ padding-right: 1.25rem;
42
+ margin-top: 0.75rem;
43
+ border-radius: 0.375rem;
44
+ width: fit-content;
45
+ height: 1.5rem;
46
+ font-size: 1.875rem;
47
+ line-height: 2.25rem;
48
+ font-weight: 700;
49
+ text-align: center;
50
+ box-shadow: 0 1px 3px 0 #0000001a, 0 1px 2px 0 #0000000f;
51
+ color: rgb(49 71 91);
52
+ background-color: #fff8dc;
53
+ }
54
+
55
+ .gpara {
56
+ font-weight: 600;
57
+ color: rgb(49 71 91)
58
+ /* #31475b */
59
+ ;
60
+ }
61
+
62
+ .gparabhajan3 {
63
+ font-weight: 600;
64
+ color: rgb(49 71 91)
65
+ /* #31475b */
66
+ ;
67
+ }
68
+
69
+ .dropzone {
70
+ border: 2px dashed #374151;
71
+ border-radius: 0.5rem;
72
+ transition: all 0.3s ease;
73
+ }
74
+
75
+ .dropzone:hover,
76
+ .dropzone.dragover {
77
+ border-color: #f63b3b;
78
+ background-color: rgba(246, 59, 59, 0.1);
79
+ }
80
+
81
+ .pulse {
82
+ animation: pulse 2s infinite;
83
+ }
84
+
85
+ @keyframes pulse {
86
+ 0% {
87
+ box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
88
+ }
89
+
90
+ 70% {
91
+ box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
92
+ }
93
+
94
+ 100% {
95
+ box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
96
+ }
97
+ }
98
+
99
+ /* Custom scrollbar */
100
+ ::-webkit-scrollbar {
101
+ width: 8px;
102
+ }
103
+ ::-webkit-scrollbar-track {
104
+ background: #1f2937;
105
+ }
106
+ ::-webkit-scrollbar-thumb {
107
+ background: #4b5563;
108
+ border-radius: 4px;
109
+ }
110
+ ::-webkit-scrollbar-thumb:hover {
111
+ background: #6b7280;
112
+ }
113
+ </style>
114
+ </head>
115
+
116
+ <body class="bg-stone-900 text-stone-300">
117
+ <div class="flex flex-col h-screen">
118
+ <header class="flex items-center justify-between p-4 h-16 border-b border-stone-800 flex-shrink-0">
119
+ <div class="flex items-center space-x-4">
120
+ <svg class="w-8 h-8 text-stone-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 6l12-3"></path></svg>
121
+ <h1 class="text-xl font-bold text-stone-100">Audio to Lyrics</h1>
122
+ </div>
123
+ <div class="text-sm text-stone-500">v1.0.0</div>
124
+ </header>
125
+
126
+ <div class="flex flex-grow overflow-hidden">
127
+ <!-- Left Sidebar -->
128
+ <div class="w-96 bg-stone-900 p-6 overflow-y-auto flex-shrink-0 border-r border-stone-800">
129
+ <h2 class="text-lg font-semibold text-stone-200 mb-4">Select Source</h2>
130
+
131
+ <!-- Mode Toggle -->
132
+ <div class="mb-6">
133
+ <div class="flex items-center justify-between mb-2">
134
+ <span class="text-sm font-medium text-stone-300">Mode</span>
135
+ </div>
136
+ <div class="flex bg-stone-800 rounded-lg p-1">
137
+ <button id="musicModeBtn" class="flex-1 py-2 px-3 rounded-md bg-stone-700 shadow-sm text-stone-100 font-medium text-sm">
138
+ <i data-feather="music" class="inline mr-2 w-4 h-4"></i>Music
139
+ </button>
140
+ <button id="speechModeBtn" class="flex-1 py-2 px-3 rounded-md text-stone-400 font-medium text-sm">
141
+ <i data-feather="mic" class="inline mr-2 w-4 h-4"></i>Speech
142
+ </button>
143
+ </div>
144
+ </div>
145
+
146
+ <!-- Tab buttons -->
147
+ <div class="flex mb-6 bg-stone-800 rounded-lg p-1">
148
+ <button id="fileTabBtn" class="flex-1 py-2 px-4 rounded-md bg-stone-700 shadow-sm text-stone-100 font-medium">
149
+ <i data-feather="file" class="inline mr-2 w-4 h-4"></i>File
150
+ </button>
151
+ <button id="youtubeTabBtn" class="flex-1 py-2 px-4 rounded-md text-stone-400 font-medium">
152
+ <i data-feather="youtube" class="inline mr-2 w-4 h-4"></i>YouTube
153
+ </button>
154
+ </div>
155
+
156
+ <!-- File upload section -->
157
+ <div id="fileUploadSection">
158
+ <div class="dropzone p-6 mb-6 flex flex-col items-center justify-center text-center" id="dropzone">
159
+ <i data-feather="music" class="text-stone-500 mb-4" style="width: 40px; height: 40px;"></i>
160
+ <p class="text-sm text-stone-400 mb-4">Drag & drop or</p>
161
+ <input type="file" id="audioFile" accept="audio/*" class="hidden">
162
+ <button id="browseBtn"
163
+ class="bg-stone-600 hover:bg-stone-700 text-white font-medium py-2 px-4 rounded-md transition duration-300 flex items-center text-sm">
164
+ <i data-feather="folder" class="mr-2 w-4 h-4"></i>
165
+ Browse File
166
+ </button>
167
+ </div>
168
+ <div class="mb-6">
169
+ <div class="bg-stone-800 rounded-lg p-3 flex items-center" id="fileInfoContainer"
170
+ style="display: none;">
171
+ <div
172
+ class="w-10 h-10 bg-stone-700 rounded-lg flex items-center justify-center mr-3 flex-shrink-0">
173
+ <i data-feather="file" class="text-stone-400"></i>
174
+ </div>
175
+ <div class="flex-grow overflow-hidden">
176
+ <p class="font-medium text-stone-200 text-sm truncate" id="fileName">No file selected</p>
177
+ <p class="text-xs text-stone-400" id="fileSize"></p>
178
+ </div>
179
+ <button id="removeFileBtn"
180
+ class="text-stone-500 hover:text-red-500 transition-colors duration-300 ml-2 flex-shrink-0">
181
+ <i data-feather="x" class="w-5 h-5"></i>
182
+ </button>
183
+ </div>
184
+
185
+ <audio id="audioPlayer" controls class="w-full mt-4 rounded-lg" hidden>
186
+ Your browser does not support the audio element.
187
+ </audio>
188
+ </div>
189
+ </div>
190
+
191
+ <!-- YouTube link section -->
192
+ <div id="youtubeLinkSection" class="hidden">
193
+ <div class="mb-6">
194
+ <label class="block text-stone-400 font-medium mb-2 text-sm">YouTube URL</label>
195
+ <input type="text" id="youtubeUrl"
196
+ class="w-full px-4 py-2 bg-stone-800 border border-stone-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-stone-500 focus:border-transparent"
197
+ placeholder="https://www.youtube.com/watch?v=...">
198
+ <p id="youtubeError" class="text-red-500 text-sm mt-1 hidden">Please enter a valid YouTube URL</p>
199
+ </div>
200
+ </div>
201
+
202
+ <button id="convertBtn"
203
+ class="w-full bg-gradient-to-br from-stone-500 to-zinc-600 hover:from-red-600 hover:to-red-700 text-white font-semibold py-3 px-4 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed transition duration-300 flex items-center justify-center"
204
+ disabled>
205
+ <i data-feather="zap" class="mr-2"></i>
206
+ Convert to Lyrics
207
+ </button>
208
+
209
+ <div id="loadingIndicator" class="hidden mt-6 text-center">
210
+ <div class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-red-500"></div>
211
+ <p class="mt-2 text-stone-400 font-medium">Processing...</p>
212
+ <p class="text-sm text-stone-500">This might take a moment.</p>
213
+ </div>
214
+ </div>
215
+
216
+ <!-- Main Content -->
217
+ <div class="flex-grow h-[calc(100vh-4rem)] bg-stone-800/50 p-6 md:p-8 overflow-y-auto">
218
+ <div class="flex items-center justify-between mb-4">
219
+ <h3 class="text-lg font-semibold text-stone-200 flex items-center">
220
+ <i data-feather="file-text" class="mr-2 text-stone-400"></i>
221
+ Lyrics Output
222
+ </h3>
223
+ <button id="downloadBtn"
224
+ class="hidden bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-md transition duration-300 flex items-center text-sm">
225
+ <i data-feather="download" class="mr-1 w-4 h-4"></i>
226
+ Download
227
+ </button>
228
+ </div>
229
+
230
+ <iframe id="lyricsOutputIframe"
231
+ class="bg-stone-900 rounded-lg min-h-[300px] max-h-[calc(100vh-14rem)] h-full w-full whitespace-pre-line border border-stone-700 text-stone-300 overflow-auto"
232
+ srcdoc="<body style='background-color: #fffff6; color: #9ca3af; font-family: Inter, sans-serif; display: flex; justify-content: center; align-items: center; height: 100%;'>No lyrics available yet.</body>"
233
+ ></iframe>
234
+ </div>
235
+ </div>
236
+ </div>
237
+
238
+ <script>
239
+ // Initialize Feather icons
240
+ feather.replace();
241
+
242
+ const audioFile = document.getElementById('audioFile');
243
+ const browseBtn = document.getElementById('browseBtn');
244
+ const audioPlayer = document.getElementById('audioPlayer');
245
+ const convertBtn = document.getElementById('convertBtn');
246
+ const lyricsOutput = document.getElementById('lyricsOutputIframe');
247
+ const loadingIndicator = document.getElementById('loadingIndicator');
248
+ const downloadBtn = document.getElementById('downloadBtn');
249
+ const dropzone = document.getElementById('dropzone');
250
+ const fileInfoContainer = document.getElementById('fileInfoContainer');
251
+ const fileName = document.getElementById('fileName');
252
+ const fileSize = document.getElementById('fileSize');
253
+ const removeFileBtn = document.getElementById('removeFileBtn');
254
+
255
+ // Handle browse button click
256
+ browseBtn.addEventListener('click', () => {
257
+ audioFile.click();
258
+ });
259
+
260
+ // Format file size
261
+ function formatFileSize(bytes) {
262
+ if (bytes === 0) return '0 Bytes';
263
+ const k = 1024;
264
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
265
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
266
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
267
+ }
268
+
269
+ // Display file info
270
+ function displayFileInfo(file) {
271
+ fileName.textContent = file.name;
272
+ fileSize.textContent = formatFileSize(file.size);
273
+ fileInfoContainer.style.display = 'flex';
274
+
275
+ // Enable convert button and show audio player
276
+ convertBtn.disabled = false;
277
+ convertBtn.classList.add('pulse');
278
+
279
+ const audioUrl = URL.createObjectURL(file);
280
+ audioPlayer.src = audioUrl;
281
+ audioPlayer.hidden = false;
282
+
283
+ // Reset output area
284
+ lyricsOutput.srcdoc = "<body style='background-color: #fffff6; color: #9ca3af; font-family: Inter, sans-serif; display: flex; justify-content: center; align-items: center; height: 100%;'>Ready to convert.</body>";
285
+ downloadBtn.classList.add('hidden');
286
+ }
287
+
288
+ // Handle file selection
289
+ audioFile.addEventListener('change', (e) => {
290
+ const file = e.target.files[0];
291
+ if (file) {
292
+ displayFileInfo(file);
293
+ }
294
+ });
295
+
296
+ // Handle remove file button
297
+ removeFileBtn.addEventListener('click', () => {
298
+ audioFile.value = '';
299
+ fileInfoContainer.style.display = 'none';
300
+ audioPlayer.hidden = true;
301
+ audioPlayer.src = '';
302
+ convertBtn.disabled = true;
303
+ convertBtn.classList.remove('pulse');
304
+ lyricsOutput.srcdoc = "<body style='background-color: #fffff6; color: #9ca3af; font-family: Inter, sans-serif; display: flex; justify-content: center; align-items: center; height: 100%;'>No lyrics available yet.</body>";
305
+ downloadBtn.classList.add('hidden');
306
+ });
307
+
308
+ // Handle drag and drop
309
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
310
+ dropzone.addEventListener(eventName, (e) => {
311
+ e.preventDefault();
312
+ e.stopPropagation();
313
+ }, false);
314
+ });
315
+
316
+ ['dragenter', 'dragover'].forEach(eventName => {
317
+ dropzone.addEventListener(eventName, () => {
318
+ dropzone.classList.add('dragover');
319
+ }, false);
320
+ });
321
+
322
+ ['dragleave', 'drop'].forEach(eventName => {
323
+ dropzone.addEventListener(eventName, () => {
324
+ dropzone.classList.remove('dragover');
325
+ }, false);
326
+ });
327
+
328
+ dropzone.addEventListener('drop', (e) => {
329
+ const file = e.dataTransfer.files[0];
330
+ if (file && file.type.startsWith('audio/')) {
331
+ audioFile.files = e.dataTransfer.files;
332
+ displayFileInfo(file);
333
+ }
334
+ }, false);
335
+
336
+ // Handle convert button click
337
+ convertBtn.addEventListener('click', async () => {
338
+ const isYoutubeMode = !youtubeLinkSection.classList.contains('hidden');
339
+
340
+ if (isYoutubeMode) {
341
+ const youtubeLink = youtubeUrl.value;
342
+ if (!isValidYouTubeUrl(youtubeLink)) {
343
+ alert('Please enter a valid YouTube URL');
344
+ return;
345
+ }
346
+ } else {
347
+ const file = audioFile.files[0];
348
+ if (!file) {
349
+ alert('Please select an audio file first');
350
+ return;
351
+ }
352
+ }
353
+
354
+ // Show loading indicator
355
+ loadingIndicator.classList.remove('hidden');
356
+ convertBtn.disabled = true;
357
+ convertBtn.classList.remove('pulse');
358
+
359
+ try {
360
+ let response;
361
+ if (isYoutubeMode) {
362
+ response = await fetch('/convert-youtube', {
363
+ method: 'POST',
364
+ headers: {
365
+ 'Content-Type': 'application/json',
366
+ },
367
+ body: JSON.stringify({
368
+ url: youtubeUrl.value,
369
+ type: currentMode
370
+ })
371
+ });
372
+ } else {
373
+ const formData = new FormData();
374
+ formData.append('file', audioFile.files[0]);
375
+ formData.append('type', currentMode);
376
+ response = await fetch('/convert', {
377
+ method: 'POST',
378
+ body: formData
379
+ });
380
+ }
381
+
382
+ const result = await response.json();
383
+
384
+ if (response.ok) {
385
+ lyricsOutput.srcdoc = `<body style='background-color: #fffff6; color: #1e293b; font-family: Inter, sans-serif; padding: 1rem;'>${result.lyrics}</body>`;
386
+ downloadBtn.classList.remove('hidden');
387
+ } else {
388
+ lyricsOutput.srcdoc = `<body style='background-color: #fffff6; color: #f87171; font-family: Inter, sans-serif; padding: 1rem;'>Error: ${result.error}</body>`;
389
+ }
390
+ } catch (error) {
391
+ lyricsOutput.srcdoc = `<body style='background-color: #fffff6; color: #f87171; font-family: Inter, sans-serif; padding: 1rem;'>Error: ${error.message}</body>`;
392
+ } finally {
393
+ loadingIndicator.classList.add('hidden');
394
+ convertBtn.disabled = false;
395
+ }
396
+ });
397
+
398
+ // Handle download button click
399
+ downloadBtn.addEventListener('click', () => {
400
+ const lyrics = lyricsOutput.srcdoc;
401
+ const blob = new Blob([lyrics], { type: 'text/html' });
402
+ const url = URL.createObjectURL(blob);
403
+
404
+ const a = document.createElement('a');
405
+ a.href = url;
406
+ a.download = 'lyrics.html';
407
+ document.body.appendChild(a);
408
+ a.click();
409
+ document.body.removeChild(a);
410
+ URL.revokeObjectURL(url);
411
+ });
412
+
413
+ // Add new JavaScript for YouTube functionality
414
+ const fileTabBtn = document.getElementById('fileTabBtn');
415
+ const youtubeTabBtn = document.getElementById('youtubeTabBtn');
416
+ const fileUploadSection = document.getElementById('fileUploadSection');
417
+ const youtubeLinkSection = document.getElementById('youtubeLinkSection');
418
+ const youtubeUrl = document.getElementById('youtubeUrl');
419
+ const youtubeError = document.getElementById('youtubeError');
420
+
421
+ // Mode toggle elements
422
+ const musicModeBtn = document.getElementById('musicModeBtn');
423
+ const speechModeBtn = document.getElementById('speechModeBtn');
424
+ let currentMode = 'music'; // Default mode
425
+
426
+ // Mode toggle handlers
427
+ musicModeBtn.addEventListener('click', () => {
428
+ currentMode = 'music';
429
+ musicModeBtn.classList.add('bg-stone-700', 'text-stone-100');
430
+ musicModeBtn.classList.remove('text-stone-400');
431
+ speechModeBtn.classList.remove('bg-stone-700', 'text-stone-100');
432
+ speechModeBtn.classList.add('text-stone-400');
433
+ });
434
+
435
+ speechModeBtn.addEventListener('click', () => {
436
+ currentMode = 'speech';
437
+ speechModeBtn.classList.add('bg-stone-700', 'text-stone-100');
438
+ speechModeBtn.classList.remove('text-stone-400');
439
+ musicModeBtn.classList.remove('bg-stone-700', 'text-stone-100');
440
+ musicModeBtn.classList.add('text-stone-400');
441
+ });
442
+
443
+ fileTabBtn.addEventListener('click', () => {
444
+ fileTabBtn.classList.add('bg-stone-700', 'text-stone-100');
445
+ fileTabBtn.classList.remove('text-stone-400');
446
+ youtubeTabBtn.classList.remove('bg-stone-700', 'text-stone-100');
447
+ youtubeTabBtn.classList.add('text-stone-400');
448
+
449
+ fileUploadSection.classList.remove('hidden');
450
+ youtubeLinkSection.classList.add('hidden');
451
+
452
+ const hasFile = audioFile.files.length > 0;
453
+ convertBtn.disabled = !hasFile;
454
+ if (hasFile) convertBtn.classList.add('pulse');
455
+ else convertBtn.classList.remove('pulse');
456
+ });
457
+
458
+ youtubeTabBtn.addEventListener('click', () => {
459
+ youtubeTabBtn.classList.add('bg-stone-700', 'text-stone-100');
460
+ youtubeTabBtn.classList.remove('text-stone-400');
461
+ fileTabBtn.classList.remove('bg-stone-700', 'text-stone-100');
462
+ fileTabBtn.classList.add('text-stone-400');
463
+
464
+ youtubeLinkSection.classList.remove('hidden');
465
+ fileUploadSection.classList.add('hidden');
466
+
467
+ const isValid = isValidYouTubeUrl(youtubeUrl.value);
468
+ convertBtn.disabled = !isValid;
469
+ if (isValid) convertBtn.classList.add('pulse');
470
+ else convertBtn.classList.remove('pulse');
471
+ });
472
+
473
+ function isValidYouTubeUrl(url) {
474
+ const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+/;
475
+ return youtubeRegex.test(url);
476
+ }
477
+
478
+ youtubeUrl.addEventListener('input', () => {
479
+ const isValid = isValidYouTubeUrl(youtubeUrl.value);
480
+ youtubeError.classList.toggle('hidden', isValid);
481
+ convertBtn.disabled = !isValid;
482
+ if (isValid) convertBtn.classList.add('pulse');
483
+ else convertBtn.classList.remove('pulse');
484
+ });
485
+ </script>
486
+ </body>
487
+
488
  </html>