File size: 9,944 Bytes
e2c1f2e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
db7598d
e2c1f2e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
db7598d
e2c1f2e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Files - Media Encoder Dashboard</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script>
        tailwind.config = {
            darkMode: 'class',
            theme: {
                extend: {
                    colors: {
                        dark: {
                            900: '#0f172a',
                            800: '#1e293b',
                            700: '#334155',
                            600: '#475569',
                            500: '#64748b'
                        }
                    }
                }
            }
        }
    </script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <style>
        body { font-family: 'Inter', sans-serif; background-color: #0f172a; }
    </style>
</head>
<body class="text-gray-100">
    <nav class="bg-dark-800 border-b border-dark-700">
        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
            <div class="flex justify-between h-16">
                <div class="flex items-center">
                    <i class="fas fa-video text-indigo-500 text-2xl mr-2"></i>
                    <h1 class="text-xl font-semibold">Media Encoder Dashboard</h1>
                </div>
                <div class="flex items-center space-x-4">
                    <a href="/" class="text-gray-300 hover:text-gray-100">Home</a>
                    <a href="/files" class="text-indigo-400 hover:text-indigo-300">Files</a>
                </div>
            </div>
        </div>
    </nav>

    <main class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
        <div class="bg-dark-800 border border-dark-700 shadow rounded-lg p-6">
            <h2 class="text-lg font-medium mb-4">Encoded Files</h2>
            <div id="fileList" class="space-y-4">
                <!-- Files will be listed here -->
            </div>
        </div>
    </main>

    <!-- Video Preview Modal -->
    <div id="videoModal" class="hidden fixed inset-0 bg-black bg-opacity-75 backdrop-blur-sm flex items-center justify-center z-50">
        <div class="bg-dark-800 border border-dark-700 rounded-lg p-6 max-w-4xl w-full mx-4">
            <div class="flex justify-between items-center mb-4">
                <h3 class="text-lg font-medium">Video Preview</h3>
                <button onclick="closeVideoModal()" class="text-gray-400 hover:text-gray-300">
                    <i class="fas fa-times"></i>
                </button>
            </div>
            <div class="aspect-w-16 aspect-h-9">
                <video id="videoPlayer" controls class="w-full rounded-lg">
                    Your browser does not support the video tag.
                </video>
            </div>
        </div>
    </div>

    <!-- Confirmation Modal -->
    <div id="confirmModal" class="hidden fixed inset-0 bg-black bg-opacity-75 backdrop-blur-sm flex items-center justify-center z-50">
        <div class="bg-dark-800 border border-dark-700 rounded-lg p-6 max-w-md w-full mx-4">
            <h3 class="text-lg font-medium mb-4">Confirm Deletion</h3>
            <p class="text-gray-300 mb-6">Are you sure you want to delete this job and all its associated files? This action cannot be undone.</p>
            <div class="flex justify-end space-x-3">
                <button onclick="closeConfirmModal()" class="px-4 py-2 bg-dark-600 text-gray-300 rounded hover:bg-dark-500 transition-colors">
                    Cancel
                </button>
                <button id="confirmDeleteBtn" class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 transition-colors">
                    Delete
                </button>
            </div>
        </div>
    </div>

    <script>
        function formatFileSize(bytes) {
            if (bytes === 0) return '0 Bytes';
            const k = 1024;
            const sizes = ['Bytes', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }

        function formatDate(dateString) {
            const date = new Date(dateString);
            return date.toLocaleString();
        }

        async function loadFiles() {
            try {
                const response = await fetch('/api/files');
                const data = await response.json();
                
                const fileList = document.getElementById('fileList');
                fileList.innerHTML = '';

                data.files.forEach(file => {
                    const fileCard = document.createElement('div');
                    fileCard.className = 'bg-dark-700 rounded-lg p-4 hover:bg-dark-600 transition-colors duration-200';
                    fileCard.innerHTML = `
                        <div class="flex justify-between items-start">
                            <div class="space-y-1">
                                <h3 class="text-lg font-medium text-gray-100">${file.output_name}</h3>
                                <p class="text-sm text-gray-400">Job ID: ${file.job_id}</p>
                                <p class="text-sm text-gray-400">Created: ${formatDate(file.created_at)}</p>
                                ${file.completed_at ? `<p class="text-sm text-gray-400">Completed: ${formatDate(file.completed_at)}</p>` : ''}
                            </div>
                            <div class="flex items-start space-x-4">
                                <div class="flex flex-col items-end space-y-2">
                                    ${Object.entries(file.qualities).map(([quality, size]) => `
                                        <div class="flex items-center space-x-2">
                                            <button onclick="playVideo('${file.job_id}', '${quality}', '${file.output_name}')" 
                                                    class="text-xs bg-dark-600 hover:bg-dark-500 px-3 py-1.5 rounded transition-all duration-300">
                                                <i class="fas fa-play mr-1"></i>${quality}
                                            </button>
                                            <a href="/api/video/${file.job_id}/${quality}"
                                               download="${file.output_name}_${quality}.mp4"
                                               class="text-xs bg-indigo-900 hover:bg-indigo-800 text-indigo-300 px-2 py-1.5 rounded transition-all duration-300">
                                                <i class="fas fa-download"></i>
                                            </a>
                                            <span class="text-xs text-gray-400">${formatFileSize(size)}</span>
                                        </div>
                                    `).join('')}
                                </div>
                                <button onclick="showDeleteConfirmation('${file.job_id}')" 
                                        class="text-xs bg-red-900 hover:bg-red-800 text-red-300 px-3 py-1.5 rounded transition-all duration-300">
                                    <i class="fas fa-trash"></i>
                                </button>
                            </div>
                        </div>
                    `;
                    fileList.appendChild(fileCard);
                });
            } catch (error) {
                console.error('Error loading files:', error);
            }
        }

        function playVideo(jobId, quality, outputName) {
            const videoPlayer = document.getElementById('videoPlayer');
            const videoModal = document.getElementById('videoModal');
            videoPlayer.src = `/api/video/${jobId}/${quality}`;
            videoModal.classList.remove('hidden');
        }

        function closeVideoModal() {
            const videoModal = document.getElementById('videoModal');
            const videoPlayer = document.getElementById('videoPlayer');
            videoModal.classList.add('hidden');
            videoPlayer.pause();
            videoPlayer.src = '';
        }

        let jobToDelete = null;

        function showDeleteConfirmation(jobId) {
            jobToDelete = jobId;
            document.getElementById('confirmModal').classList.remove('hidden');
        }

        function closeConfirmModal() {
            jobToDelete = null;
            document.getElementById('confirmModal').classList.add('hidden');
        }

        document.getElementById('confirmDeleteBtn').addEventListener('click', async () => {
            if (!jobToDelete) return;

            try {
                const response = await fetch(`/api/jobs/${jobToDelete}/clean`, {
                    method: 'POST'
                });

                if (response.ok) {
                    closeConfirmModal();
                    loadFiles();
                } else {
                    alert('Failed to delete job');
                }
            } catch (error) {
                console.error('Error deleting job:', error);
                alert('Error deleting job');
            }
        });

        // Close modals when clicking outside
        window.addEventListener('click', (e) => {
            const videoModal = document.getElementById('videoModal');
            const confirmModal = document.getElementById('confirmModal');
            if (e.target === videoModal) closeVideoModal();
            if (e.target === confirmModal) closeConfirmModal();
        });

        // Load files on page load
        loadFiles();

        // Refresh files list every 30 seconds
        setInterval(loadFiles, 30000);
    </script>
</body>
</html>