File size: 15,378 Bytes
f4ce0ef
30933b5
0e9d836
 
 
 
c4395d2
 
 
 
 
 
 
 
 
 
 
0e9d836
c13064d
f4ce0ef
 
 
 
9e47428
 
 
 
 
 
bf90d9e
9e47428
f4ce0ef
9e47428
bf90d9e
9e47428
bf90d9e
9e47428
bf90d9e
9e47428
 
 
 
bf90d9e
 
9e47428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf90d9e
9e47428
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf90d9e
 
9e47428
bf90d9e
 
 
 
9e47428
 
bf90d9e
9e47428
 
 
 
 
0e9d836
 
 
 
 
 
 
 
 
c13064d
 
7071579
30933b5
 
 
 
 
 
 
 
 
9e47428
0e9d836
 
 
 
 
 
 
9e47428
bf90d9e
7071579
 
 
9e47428
 
 
bf90d9e
 
 
9e47428
7071579
 
 
 
 
 
 
 
 
 
 
30933b5
7071579
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30933b5
 
 
7071579
 
30933b5
bf90d9e
30933b5
 
 
 
bf90d9e
30933b5
 
 
 
 
 
 
 
 
0e9d836
 
 
 
 
 
 
 
30933b5
0e9d836
30933b5
c4395d2
 
 
30933b5
 
 
 
 
 
 
 
 
 
 
 
c13064d
30933b5
 
0e9d836
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c4395d2
 
 
0e9d836
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c4395d2
 
 
9e47428
c13064d
9e47428
7071579
 
 
9e47428
 
7071579
9e47428
 
 
 
 
c4395d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
import { Client } from "@gradio/client";

// Add these variables at the top of the file
let audioCache = {};
let currentTrack = null;

// // Add this function at the beginning of your script.js file
// function handleSharedUrl() {
//   const urlParams = new URLSearchParams(window.location.search);
//   const sharedUrl = urlParams.get('url');

//   if (sharedUrl) {
//     console.log('Shared URL detected:', sharedUrl);
//     fetchMp3(sharedUrl);
//   }
// }

document.addEventListener("DOMContentLoaded", async function() {
    const audioPlayer = document.getElementById('player');
    const playButton = document.getElementById('playButton');
    const skipBackwardButton = document.getElementById('skipBackward');
    const skipForwardButton = document.getElementById('skipForward');
  
    const settingsBtn = document.getElementById('settingsBtn');
    const settingsModal = document.getElementById('settingsModal');
    const closeBtn = document.querySelector('.close');
    const saveSettingsBtn = document.getElementById('saveSettings');
    const apiKeyInput = document.getElementById('apiKey');
    const toggleApiKeyBtn = document.getElementById('toggleApiKey');
    const apiServerInput = document.getElementById('apiServer');


    let originalApiKey = '';
    let originalApiServer = '';

    // Load saved settings on page load
    const savedApiKey = localStorage.getItem('openaiApiKey');
    const savedApiServer = localStorage.getItem('apiServer') || 'http://127.0.0.1:7860';
    if (savedApiKey) {
        apiKeyInput.value = savedApiKey;
        originalApiKey = savedApiKey;
    }
    apiServerInput.value = savedApiServer;
    originalApiServer = savedApiServer;

    // Toggle API key visibility
    toggleApiKeyBtn.onclick = function() {
        if (apiKeyInput.type === "password") {
            apiKeyInput.type = "text";
            toggleApiKeyBtn.textContent = "🔒";
        } else {
            apiKeyInput.type = "password";
            toggleApiKeyBtn.textContent = "👁️";
        }
    }

    // Open settings modal
    settingsBtn.onclick = function() {
        originalApiKey = apiKeyInput.value;
        originalApiServer = apiServerInput.value;
        settingsModal.style.display = "block";
        apiKeyInput.focus();
    }

    // Close settings modal
    function closeModal() {
        settingsModal.style.display = "none";
        apiKeyInput.value = originalApiKey;  // Revert to original value
    }

    closeBtn.onclick = closeModal;

    // Close modal if clicked outside
    window.onclick = function(event) {
        if (event.target == settingsModal) {
            closeModal();
        }
    }

    // Handle keydown events
    document.addEventListener('keydown', function(event) {
        if (settingsModal.style.display === "block") {
            if (event.key === "Escape") {
                closeModal();
            } else if (event.key === "Enter") {
                saveSettings();
            }
        }
    });

    // Save settings
    function saveSettings() {
        const apiKey = apiKeyInput.value.trim();
        const apiServer = apiServerInput.value.trim();
        if (apiKey && apiServer) {
            localStorage.setItem('openaiApiKey', apiKey);
            localStorage.setItem('apiServer', apiServer);
            originalApiKey = apiKey;
            originalApiServer = apiServer;
            alert('Settings saved successfully!');
            closeModal();
        } else {
            alert('Please enter both a valid API key and API server.');
        }
    }

    saveSettingsBtn.onclick = saveSettings;

    const historyList = document.getElementById('historyList');
    const clearHistoryBtn = document.getElementById('clearHistory');

    // Load audio cache from localStorage and Cache API
    await loadAudioCache();

    // Update history list
    updateHistoryList();

    // Function to fetch MP3 from API endpoint when a link is shared
    async function fetchMp3(link) {
        console.log('Starting fetchMp3 function with link:', link);
        const loadingIndicator = document.getElementById('loadingIndicator');
        const audioPlayer = document.getElementById('player');
        const playButton = document.getElementById('playButton');
        const transcriptionContainer = document.getElementById('transcriptionContainer');
        const transcriptionElement = document.getElementById('transcription');

        if (loadingIndicator) loadingIndicator.style.display = 'block';
        if (transcriptionContainer) transcriptionContainer.style.display = 'none';

        try {
            // Check if the link is already in the cache
            if (audioCache[link]) {
                console.log('Loading audio from cache');
                await loadAudioFromCache(link);
                return;
            }

            const apiKey = localStorage.getItem('openaiApiKey');
            const apiServer = localStorage.getItem('apiServer');
            console.log('Retrieved API key and server from localStorage');
            console.log('API Server:', apiServer);

            if (!apiKey) {
                throw new Error("API key not set. Please set your OpenAI API key in the settings.");
            }
            if (!apiServer) {
                throw new Error("API server not set. Please set the API server in the settings.");
            }

            console.log('Attempting to connect to Gradio app...');

            // Connect to local Gradio app 
            const client = await Client.connect(apiServer);

            //connect to HF deployed one OK
            //const client = await Client.connect("Mightypeacock/webtoaudio");

            console.log('Gradio client created successfully');
            
            console.log(await client.view_api())
         
            console.log('Preparing to make prediction...');
            // Make the prediction

            const result = await client.predict("/generate_audio", { 
              url:link,
              openai_api_key: apiKey,
              text_model:  "gpt-4o-mini",
              audio_model:  "tts-1",
              speaker_1_voice:   "alloy",
              speaker_2_voice:  "echo",
              api_base: null, // api_base
              edited_transcript: "", // edited_transcript
              user_feedback:  "", // user_feedback
              original_text: "summary" // original_text
              // debug: true, 
        });


        console.log('Raw result from predict:', result);
        console.log('Result data:', result.data);


        console.log('Prediction made successfully');

        // Check if result.data is an array and has at least one element
        if (!Array.isArray(result.data) || result.data.length === 0) {
            throw new Error('Unexpected result format from server');
        }

        // Assuming the audio file URL is the second item in the result
        const audioFileUrl = result.data[0].url;
        console.log('Received audio file URL:', audioFileUrl);

        // Check if the URL is valid
        if (typeof audioFileUrl !== 'string' || !audioFileUrl.startsWith('http')) {
            throw new Error(`Invalid audio file URL received: ${audioFileUrl}`);
        }

        // After successful API call, add to cache
        audioCache[link] = {
            audioUrl: audioFileUrl,
            transcription: result.data[1],
            lastPosition: 0
        };
        await saveAudioCache(link, audioFileUrl);
        updateHistoryList();

        await loadAudioFromCache(link);

        // Update media session metadata
        updateMediaSessionMetadata(link, 'Web to Audio', 'Generated Audio');

    } catch (error) {
        console.error('Error in fetchMp3:', error);
        console.error('Error stack:', error.stack);
        alert(`Error fetching MP3: ${error.message}`);
        
        // Clear the audio player source and hide the play button
        if (audioPlayer) audioPlayer.src = '';
        if (playButton) playButton.style.display = 'none';
        if (transcriptionContainer) transcriptionContainer.style.display = 'none';
    } finally {
        if (loadingIndicator) 
            loadingIndicator.style.display = 'none';
    }
    }

    async function loadAudioFromCache(link) {
        const cachedAudio = audioCache[link];
        if (!cachedAudio) return;

        const audioPlayer = document.getElementById('player');
        const playButton = document.getElementById('playButton');
        const transcriptionContainer = document.getElementById('transcriptionContainer');
        const transcriptionElement = document.getElementById('transcription');

        // Fetch the audio file from the Cache API
        const cache = await caches.open('audio-cache');
        const response = await cache.match(cachedAudio.audioUrl);
        if (response) {
            const blob = await response.blob();
            audioPlayer.src = URL.createObjectURL(blob);
        } else {
            audioPlayer.src = cachedAudio.audioUrl;
        }

        audioPlayer.currentTime = cachedAudio.lastPosition;
        currentTrack = link;

        if (playButton) {
            playButton.style.display = 'block';
            playButton.onclick = () => audioPlayer.play();
        }

        if (transcriptionElement && transcriptionContainer) {
            transcriptionElement.textContent = cachedAudio.transcription;
            transcriptionContainer.style.display = 'block';
        }

        console.log('Audio loaded from cache and ready for playback');

        // Update media session metadata
        updateMediaSessionMetadata(link, 'Web to Audio', 'Generated Audio');
    }

    async function saveAudioCache(link, audioUrl) {
        // Save metadata to localStorage
        localStorage.setItem('audioCache', JSON.stringify(audioCache));

        // Save audio file to Cache API
        const cache = await caches.open('audio-cache');
        await cache.add(audioUrl);
    }

    async function loadAudioCache() {
        const savedCache = localStorage.getItem('audioCache');
        if (savedCache) {
            audioCache = JSON.parse(savedCache);
        }

        // Verify that all cached audio files are still in the Cache API
        const cache = await caches.open('audio-cache');
        for (const link in audioCache) {
            const response = await cache.match(audioCache[link].audioUrl);
            if (!response) {
                console.log(`Audio file for ${link} not found in cache, removing entry`);
                delete audioCache[link];
            }
        }

        // Save the cleaned-up cache back to localStorage
        localStorage.setItem('audioCache', JSON.stringify(audioCache));
    }

    async function updateHistoryList() {
        historyList.innerHTML = '';
        Object.keys(audioCache).forEach(link => {
            const li = document.createElement('li');
            const playBtn = document.createElement('button');
            playBtn.textContent = 'Play';
            playBtn.onclick = () => loadAudioFromCache(link);
            const removeBtn = document.createElement('button');
            removeBtn.textContent = 'Remove';
            removeBtn.onclick = () => removeFromCache(link);
            li.appendChild(document.createTextNode(link + ' '));
            li.appendChild(playBtn);
            li.appendChild(removeBtn);
            historyList.appendChild(li);
        });
    }

    async function removeFromCache(link) {
        const cache = await caches.open('audio-cache');
        await cache.delete(audioCache[link].audioUrl);
        delete audioCache[link];
        localStorage.setItem('audioCache', JSON.stringify(audioCache));
        updateHistoryList();
    }

    clearHistoryBtn.onclick = async function() {
        const cache = await caches.open('audio-cache');
        for (const link in audioCache) {
            await cache.delete(audioCache[link].audioUrl);
        }
        audioCache = {};
        localStorage.setItem('audioCache', JSON.stringify(audioCache));
        updateHistoryList();
    };

    // Save current position every 5 seconds
    setInterval(() => {
        if (currentTrack && audioPlayer.currentTime > 0) {
            audioCache[currentTrack].lastPosition = audioPlayer.currentTime;
            localStorage.setItem('audioCache', JSON.stringify(audioCache));
        }
    }, 5000);

    // Call handleSharedUrl instead of directly checking for the URL parameter
    // handleSharedUrl();

    // Get the link from the shared URL
    const queryParams = new URLSearchParams(window.location.search);
    const sharedLink = queryParams.get('url');

    console.log('Shared link from URL:', sharedLink);

    // Only call the API to get MP3 if a valid URL is provided
    if (sharedLink) {
        console.log('Valid URL provided, calling fetchMp3');
        fetchMp3(sharedLink);
    } else {
        console.log("No URL provided. Waiting for user input.");
        // You might want to update the UI here to indicate that the user needs to provide a URL
    }

    // Add this function to update media session metadata
    function updateMediaSessionMetadata(title, artist, album) {
        if ('mediaSession' in navigator) {
            navigator.mediaSession.metadata = new MediaMetadata({
                title: title || 'Unknown Title',
                artist: artist || 'Unknown Artist',
                album: album || 'Unknown Album',
                artwork: [
                    { src: '/icons/imagepodcast-transp500.png', sizes: '500x500', type: 'image/png' },
                    { src: '/icons/imagepodcast.png', sizes: '1024x1024', type: 'image/png' }
                ]
            });
        }
    }

    // Add this function to set up media session handlers
    function setupMediaSessionHandlers() {
        if ('mediaSession' in navigator) {
            navigator.mediaSession.setActionHandler('play', () => {
                audioPlayer.play();
                playButton.textContent = 'Pause';
            });
            
            navigator.mediaSession.setActionHandler('pause', () => {
                audioPlayer.pause();
                playButton.textContent = 'Play';
            });
            
            navigator.mediaSession.setActionHandler('seekbackward', (details) => {
                const skipTime = details.seekOffset || 10;
                audioPlayer.currentTime = Math.max(audioPlayer.currentTime - skipTime, 0);
            });
            
            navigator.mediaSession.setActionHandler('seekforward', (details) => {
                const skipTime = details.seekOffset || 10;
                audioPlayer.currentTime = Math.min(audioPlayer.currentTime + skipTime, audioPlayer.duration);
            });
            
            navigator.mediaSession.setActionHandler('seekto', (details) => {
                if (details.fastSeek && 'fastSeek' in audioPlayer) {
                    audioPlayer.fastSeek(details.seekTime);
                    return;
                }
                audioPlayer.currentTime = details.seekTime;
            });
            
            navigator.mediaSession.setActionHandler('previoustrack', () => {
                audioPlayer.currentTime = 0;
            });
        }
    }

    // Call this function to set up the media session handlers
    setupMediaSessionHandlers();
});